diff --git a/.devcontainer/.gitignore b/.devcontainer/.gitignore deleted file mode 100644 index 6bf3b5d9e5..0000000000 --- a/.devcontainer/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.env -library \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 4ce7076011..0000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:7c2e711a4f7b02f32d2da16192d5e05aa7c95279be4ce889cff5df316f251c1d -FROM ${BASEIMAGE} - -# Flutter SDK -# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux -ENV FLUTTER_CHANNEL="stable" -ENV FLUTTER_VERSION="3.29.3" -ENV FLUTTER_HOME=/flutter -ENV PATH=${PATH}:${FLUTTER_HOME}/bin - -# Flutter SDK -RUN mkdir -p ${FLUTTER_HOME} \ - && curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ - && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \ - && rm flutter.tar.xz \ - && chown -R 1000:1000 ${FLUTTER_HOME} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2d567f033a..79126fd658 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,26 +1,67 @@ { - "name": "Immich", - "service": "immich-devcontainer", + "name": "Immich - Backend, Frontend and ML", + "service": "immich-server", + "runServices": [ + "immich-server", + "redis", + "database", + "immich-machine-learning" + ], "dockerComposeFile": [ - "docker-compose.yml", - "../docker/docker-compose.dev.yml" + "../docker/docker-compose.dev.yml", + "./server/container-compose-overrides.yml" ], "customizations": { "vscode": { "extensions": [ - "Dart-Code.dart-code", - "Dart-Code.flutter", "dbaeumer.vscode-eslint", - "dcmdev.dcm-vscode-extension", "esbenp.prettier-vscode", - "svelte.svelte-vscode" + "svelte.svelte-vscode", + "ms-vscode-remote.remote-containers", + "foxundermoon.shell-format", + "timonwong.shellcheck", + "rvest.vs-code-prettier-eslint", + "bluebrown.yamlfmt", + "vkrishna04.cspell-sync", + "vitest.explorer", + "ms-playwright.playwright", + "ms-azuretools.vscode-docker" ] } }, - "forwardPorts": [], - "initializeCommand": "bash .devcontainer/scripts/initializeCommand.sh", - "onCreateCommand": "bash .devcontainer/scripts/onCreateCommand.sh", + "forwardPorts": [3000, 9231, 9230, 2283], + "portsAttributes": { + "3000": { + "label": "Immich - Frontend HTTP", + "description": "The frontend of the Immich project", + "onAutoForward": "openBrowserOnce" + }, + "2283": { + "label": "Immich - API Server - HTTP", + "description": "The API server of the Immich project" + }, + "9231": { + "label": "Immich - API Server - DEBUG", + "description": "The API server of the Immich project" + }, + "9230": { + "label": "Immich - Workers - DEBUG", + "description": "The workers of the Immich project" + } + }, "overrideCommand": true, - "workspaceFolder": "/immich", - "remoteUser": "node" + "workspaceFolder": "/workspaces/immich", + "remoteUser": "node", + "userEnvProbe": "loginInteractiveShell", + "remoteEnv": { + // The location where your uploaded files are stored + "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./library}", + // Connection secret for postgres. You should change it to a random password + // Please use only the characters `A-Za-z0-9`, without special characters or spaces + "DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}", + // The database username + "DB_USERNAME": "${localEnv:DB_USERNAME:postgres}", + // The database name + "DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}" + } } diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml deleted file mode 100644 index 25719641d2..0000000000 --- a/.devcontainer/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - immich-devcontainer: - build: - dockerfile: Dockerfile - extra_hosts: - - 'host.docker.internal:host-gateway' - volumes: - - ..:/immich:cached diff --git a/.devcontainer/mobile/container-compose-overrides.yml b/.devcontainer/mobile/container-compose-overrides.yml new file mode 100644 index 0000000000..62a97a01eb --- /dev/null +++ b/.devcontainer/mobile/container-compose-overrides.yml @@ -0,0 +1,34 @@ +services: + immich-server: + build: + target: dev-container-mobile + environment: + - IMMICH_SERVER_URL=http://127.0.0.1:2283/ + volumes: !override # bind mount host to /workspaces/immich + - ..:/workspaces/immich + - cli_node_modules:/workspaces/immich/cli/node_modules + - e2e_node_modules:/workspaces/immich/e2e/node_modules + - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules + - server_node_modules:/workspaces/immich/server/node_modules + - web_node_modules:/workspaces/immich/web/node_modules + - ${UPLOAD_LOCATION}/photos:/workspaces/immich/server/upload + - ${UPLOAD_LOCATION}/photos/upload:/workspaces/immich/server/upload/upload + - /etc/localtime:/etc/localtime:ro + + database: + volumes: + - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data + +volumes: + # Node modules for each service to avoid conflicts and ensure consistent dependencies + cli_node_modules: + e2e_node_modules: + open_api_node_modules: + server_node_modules: + web_node_modules: + + # UPLOAD_LOCATION must be set to a absolute path or vol-upload + vol-upload: + + # DB_DATA_LOCATION must be set to a absolute path or vol-database + vol-database: diff --git a/.devcontainer/mobile/devcontainer.json b/.devcontainer/mobile/devcontainer.json new file mode 100644 index 0000000000..0dbcc8e9c8 --- /dev/null +++ b/.devcontainer/mobile/devcontainer.json @@ -0,0 +1,52 @@ +{ + "name": "Immich - Mobile", + "service": "immich-server", + "runServices": [ + "immich-server", + "redis", + "database", + "immich-machine-learning" + ], + "dockerComposeFile": [ + "../../docker/docker-compose.dev.yml", + "./container-compose-overrides.yml" + ], + "customizations": { + "vscode": { + "extensions": [ + "Dart-Code.dart-code", + "Dart-Code.flutter", + "dcmdev.dcm-vscode-extension", + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "svelte.svelte-vscode", + "ms-vscode-remote.remote-containers", + "foxundermoon.shell-format", + "timonwong.shellcheck", + "rvest.vs-code-prettier-eslint", + "bluebrown.yamlfmt", + "vkrishna04.cspell-sync", + "vitest.explorer", + "ms-playwright.playwright", + "ms-azuretools.vscode-docker" + ] + } + }, + "forwardPorts": [], + "overrideCommand": true, + "workspaceFolder": "/workspaces/immich", + "remoteUser": "node", + "userEnvProbe": "loginInteractiveShell", + "remoteEnv": { + // The location where your uploaded files are stored + "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}", + // Connection secret for postgres. You should change it to a random password + // Please use only the characters `A-Za-z0-9`, without special characters or spaces + "DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}", + // The database username + "DB_USERNAME": "${localEnv:DB_USERNAME:postgres}", + // The database name + "DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}" + } +} diff --git a/.devcontainer/scripts/initializeCommand.sh b/.devcontainer/scripts/initializeCommand.sh deleted file mode 100644 index 9d9d196696..0000000000 --- a/.devcontainer/scripts/initializeCommand.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -# If .env file does not exist, create it by copying example.env from the docker folder -if [ ! -f ".devcontainer/.env" ]; then - cp docker/example.env .devcontainer/.env -fi diff --git a/.devcontainer/scripts/onCreateCommand.sh b/.devcontainer/scripts/onCreateCommand.sh deleted file mode 100644 index 2f898ec32e..0000000000 --- a/.devcontainer/scripts/onCreateCommand.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -# Enable multiarch for arm64 if necessary -if [ "$(dpkg --print-architecture)" = "arm64" ]; then - sudo dpkg --add-architecture amd64 && \ - sudo apt-get update && \ - sudo apt-get install -y --no-install-recommends \ - qemu-user-static \ - libc6:amd64 \ - libstdc++6:amd64 \ - libgcc1:amd64 -fi - -# Install DCM -wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg -sudo echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list - -sudo apt-get update -sudo apt-get install dcm - -dart --disable-analytics - -# Install immich -cd /immich || exit -make install-all diff --git a/.devcontainer/server/container-common.sh b/.devcontainer/server/container-common.sh new file mode 100755 index 0000000000..e1e81a9b52 --- /dev/null +++ b/.devcontainer/server/container-common.sh @@ -0,0 +1,80 @@ +#!/bin/bash +export IMMICH_PORT="${DEV_SERVER_PORT:-2283}" +export DEV_PORT="${DEV_PORT:-3000}" + +# search for immich directory inside workspace. +# /workspaces/immich is the bind mount, but other directories can be mounted if runing +# Devcontainer: Clone [repository|pull request] in container volumne +WORKSPACES_DIR="/workspaces" +IMMICH_DIR="$WORKSPACES_DIR/immich" +IMMICH_DEVCONTAINER_LOG="$HOME/immich-devcontainer.log" + +log() { + # Display command on console, log with timestamp to file + echo "$*" + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >>"$IMMICH_DEVCONTAINER_LOG" +} + +run_cmd() { + # Ensure log directory exists + mkdir -p "$(dirname "$IMMICH_DEVCONTAINER_LOG")" + + log "$@" + + # Execute command: display normally on console, log with timestamps to file + "$@" 2>&1 | tee >(while IFS= read -r line; do + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $line" >>"$IMMICH_DEVCONTAINER_LOG" + done) + + # Preserve exit status + return "${PIPESTATUS[0]}" +} + +# Find directories excluding /workspaces/immich +mapfile -t other_dirs < <(find "$WORKSPACES_DIR" -mindepth 1 -maxdepth 1 -type d ! -path "$IMMICH_DIR" ! -name ".*") + +if [ ${#other_dirs[@]} -gt 1 ]; then + log "Error: More than one directory found in $WORKSPACES_DIR other than $IMMICH_DIR." + exit 1 +elif [ ${#other_dirs[@]} -eq 1 ]; then + export IMMICH_WORKSPACE="${other_dirs[0]}" +else + export IMMICH_WORKSPACE="$IMMICH_DIR" +fi + +log "Found immich workspace in $IMMICH_WORKSPACE" +log "" + +fix_permissions() { + + log "Fixing permissions for ${IMMICH_WORKSPACE}" + + run_cmd sudo find "${IMMICH_WORKSPACE}/server/upload" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres/*" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres" -exec chown node {} + + + # Change ownership for directories that exist + for dir in "${IMMICH_WORKSPACE}/.vscode" \ + "${IMMICH_WORKSPACE}/cli/node_modules" \ + "${IMMICH_WORKSPACE}/e2e/node_modules" \ + "${IMMICH_WORKSPACE}/open-api/typescript-sdk/node_modules" \ + "${IMMICH_WORKSPACE}/server/node_modules" \ + "${IMMICH_WORKSPACE}/server/dist" \ + "${IMMICH_WORKSPACE}/web/node_modules" \ + "${IMMICH_WORKSPACE}/web/dist"; do + if [ -d "$dir" ]; then + run_cmd sudo chown node -R "$dir" + fi + done + + log "" +} + +install_dependencies() { + + log "Installing dependencies" + ( + cd "${IMMICH_WORKSPACE}" || exit 1 + export CI=1 FROZEN=1 OFFLINE=1 + run_cmd make setup-dev + ) + log "" +} diff --git a/.devcontainer/server/container-compose-overrides.yml b/.devcontainer/server/container-compose-overrides.yml new file mode 100644 index 0000000000..d7efc92cb1 --- /dev/null +++ b/.devcontainer/server/container-compose-overrides.yml @@ -0,0 +1,49 @@ +services: + immich-server: + build: + target: dev-container-server + env_file: !reset [] + hostname: immich-dev + environment: + - IMMICH_SERVER_URL=http://127.0.0.1:2283/ + volumes: !override + - ..:/workspaces/immich + - cli_node_modules:/workspaces/immich/cli/node_modules + - e2e_node_modules:/workspaces/immich/e2e/node_modules + - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules + - server_node_modules:/workspaces/immich/server/node_modules + - web_node_modules:/workspaces/immich/web/node_modules + - ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/workspaces/immich/server/upload + - ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/workspaces/immich/server/upload/upload + - /etc/localtime:/etc/localtime:ro + + immich-web: + env_file: !reset [] + + immich-machine-learning: + env_file: !reset [] + + database: + env_file: !reset [] + environment: !override + POSTGRES_PASSWORD: ${DB_PASSWORD-postgres} + POSTGRES_USER: ${DB_USERNAME-postgres} + POSTGRES_DB: ${DB_DATABASE_NAME-immich} + POSTGRES_INITDB_ARGS: '--data-checksums' + POSTGRES_HOST_AUTH_METHOD: md5 + volumes: + - ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data + + redis: + env_file: !reset [] + +volumes: + # Node modules for each service to avoid conflicts and ensure consistent dependencies + cli_node_modules: + e2e_node_modules: + open_api_node_modules: + server_node_modules: + web_node_modules: + upload1-devcontainer-volume: + upload2-devcontainer-volume: + postgres-devcontainer-volume: diff --git a/.devcontainer/server/container-start-backend.sh b/.devcontainer/server/container-start-backend.sh new file mode 100755 index 0000000000..aeb70df72d --- /dev/null +++ b/.devcontainer/server/container-start-backend.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# shellcheck source=common.sh +# shellcheck disable=SC1091 +source /immich-devcontainer/container-common.sh + +log "Starting Nest API Server" +log "" +cd "${IMMICH_WORKSPACE}/server" || ( + log "Immich workspace not found" + exit 1 +) + +while true; do + run_cmd node ./node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch + log "Nest API Server crashed with exit code $?. Respawning in 3s ..." + sleep 3 +done diff --git a/.devcontainer/server/container-start-frontend.sh b/.devcontainer/server/container-start-frontend.sh new file mode 100755 index 0000000000..633dcc3a93 --- /dev/null +++ b/.devcontainer/server/container-start-frontend.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# shellcheck source=common.sh +# shellcheck disable=SC1091 +source /immich-devcontainer/container-common.sh + +log "Starting Immich Web Frontend" +log "" +cd "${IMMICH_WORKSPACE}/web" || ( + log "Immich Workspace not found" + exit 1 +) + +until curl --output /dev/null --silent --head --fail "http://127.0.0.1:${IMMICH_PORT}/api/server/config"; do + log "Waiting for api server..." + sleep 1 +done + +while true; do + run_cmd node ./node_modules/.bin/vite dev --host 0.0.0.0 --port "${DEV_PORT}" + log "Web crashed with exit code $?. Respawning in 3s ..." + sleep 3 +done diff --git a/.devcontainer/server/container-start.sh b/.devcontainer/server/container-start.sh new file mode 100755 index 0000000000..860b2826b0 --- /dev/null +++ b/.devcontainer/server/container-start.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# shellcheck source=common.sh +# shellcheck disable=SC1091 +source /immich-devcontainer/container-common.sh + +log "Setting up Immich dev container..." +fix_permissions + +log "Installing npm dependencies (node_modules)..." +install_dependencies + +log "Setup complete, please wait while backend and frontend services automatically start" +log +log "If necessary, the services may be manually started using" +log +log "$ /immich-devcontainer/container-start-backend.sh" +log "$ /immich-devcontainer/container-start-frontend.sh" +log +log "From different terminal windows, as these scripts automatically restart the server" +log "on error, and will continuously run in a loop" diff --git a/.dockerignore b/.dockerignore index e182865ae0..d152800d1b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,9 +4,14 @@ design/ docker/ +Dockerfile !docker/scripts docs/ +!docs/package.json +!docs/package-lock.json e2e/ +!e2e/package.json +!e2e/package-lock.json fastlane/ machine-learning/ misc/ @@ -15,6 +20,7 @@ mobile/ cli/coverage/ cli/dist/ cli/node_modules/ +cli/Dockerfile open-api/typescript-sdk/build/ open-api/typescript-sdk/node_modules/ @@ -25,9 +31,11 @@ server/upload/ server/src/queries server/dist/ server/www/ +server/Dockerfile web/node_modules/ web/coverage/ web/.svelte-kit web/build/ web/.env +web/Dockerfile diff --git a/.github/.nvmrc b/.github/.nvmrc index 5b540673a8..fc37597bcc 100644 --- a/.github/.nvmrc +++ b/.github/.nvmrc @@ -1 +1 @@ -22.16.0 +22.17.0 diff --git a/.github/.prettierignore b/.github/.prettierignore new file mode 100644 index 0000000000..cc41cea9b2 --- /dev/null +++ b/.github/.prettierignore @@ -0,0 +1,4 @@ +# Ignore files for PNPM, NPM and YARN +pnpm-lock.yaml +package-lock.json +yarn.lock diff --git a/.github/package-lock.json b/.github/package-lock.json index 423661befb..bea1c66e46 100644 --- a/.github/package-lock.json +++ b/.github/package-lock.json @@ -9,9 +9,9 @@ } }, "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { diff --git a/.github/workflows/build-mobile.yml b/.github/workflows/build-mobile.yml index 33912d687c..a048536b2f 100644 --- a/.github/workflows/build-mobile.yml +++ b/.github/workflows/build-mobile.yml @@ -58,7 +58,7 @@ jobs: 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 + runs-on: mich steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -66,24 +66,40 @@ jobs: ref: ${{ inputs.ref || github.sha }} persist-credentials: false + - name: Create the Keystore + env: + KEY_JKS: ${{ secrets.KEY_JKS }} + working-directory: ./mobile + run: printf "%s" $KEY_JKS | base64 -d > android/key.jks + - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'zulu' java-version: '17' - cache: 'gradle' + + - name: Restore Gradle Cache + id: cache-gradle-restore + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + ~/.android/sdk + mobile/android/.gradle + mobile/.dart_tool + key: build-mobile-gradle-${{ runner.os }}-main - name: Setup Flutter SDK - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0 + uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0 with: channel: 'stable' flutter-version-file: ./mobile/pubspec.yaml cache: true - - name: Create the Keystore - env: - KEY_JKS: ${{ secrets.KEY_JKS }} - working-directory: ./mobile - run: echo $KEY_JKS | base64 -d > android/key.jks + - name: Setup Android SDK + uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 + with: + packages: '' - name: Get Packages working-directory: ./mobile @@ -103,12 +119,30 @@ jobs: ALIAS: ${{ secrets.ALIAS }} ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }} + IS_MAIN: ${{ github.ref == 'refs/heads/main' }} run: | - flutter build apk --release - flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64 + if [[ $IS_MAIN == 'true' ]]; then + flutter build apk --release + flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64 + else + flutter build apk --debug --split-per-abi --target-platform android-arm64 + fi - name: Publish Android Artifact uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: release-apk-signed path: mobile/build/app/outputs/flutter-apk/*.apk + + - name: Save Gradle Cache + id: cache-gradle-save + uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4 + if: github.ref == 'refs/heads/main' + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + ~/.android/sdk + mobile/android/.gradle + mobile/.dart_tool + key: ${{ steps.cache-gradle-restore.outputs.cache-primary-key }} diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 74f5970139..9729450a91 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -38,6 +38,9 @@ jobs: with: node-version-file: './cli/.nvmrc' registry-url: 'https://registry.npmjs.org' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + - name: Prepare SDK run: npm ci --prefix ../open-api/typescript-sdk/ - name: Build SDK @@ -67,7 +70,7 @@ jobs: uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 - name: Login to GitHub Container Registry uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 @@ -96,7 +99,7 @@ jobs: type=raw,value=latest,enable=${{ github.event_name == 'release' }} - name: Build and push image - uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.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 fb6d686878..6f1e68afce 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -50,7 +50,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -63,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@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 # â„šī¸ 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 @@ -76,6 +76,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 20da0c5201..7bb7f72b51 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -177,7 +177,7 @@ jobs: runs-on: ubuntu-latest if: always() steps: - - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3 + - uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4 with: needs: ${{ toJSON(needs) }} @@ -188,6 +188,6 @@ jobs: runs-on: ubuntu-latest if: always() steps: - - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3 + - uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4 with: needs: ${{ toJSON(needs) }} diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index 32010728cf..93b6c8ad04 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -57,6 +57,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './docs/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Run npm install run: npm ci diff --git a/.github/workflows/fix-format.yml b/.github/workflows/fix-format.yml index 7a90747c12..7ef80306ba 100644 --- a/.github/workflows/fix-format.yml +++ b/.github/workflows/fix-format.yml @@ -32,6 +32,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Fix formatting run: make install-all && make format-all diff --git a/.github/workflows/org-checks.yml b/.github/workflows/org-checks.yml new file mode 100644 index 0000000000..55c5dfad5b --- /dev/null +++ b/.github/workflows/org-checks.yml @@ -0,0 +1,12 @@ +name: Org Checks + +on: + pull_request_review: + +jobs: + check-approvals: + name: Check for Team/Admin Review + uses: immich-app/devtools/.github/workflows/required-approval.yml@main + permissions: + pull-requests: read + contents: read diff --git a/.github/workflows/pr-label-validation.yml b/.github/workflows/pr-label-validation.yml index 19f90143e0..2c75be8653 100644 --- a/.github/workflows/pr-label-validation.yml +++ b/.github/workflows/pr-label-validation.yml @@ -14,7 +14,7 @@ jobs: pull-requests: write steps: - name: Require PR to have a changelog label - uses: mheap/github-action-required-labels@fb29a14a076b0f74099f6198f77750e8fc236016 # v5.5.0 + uses: mheap/github-action-required-labels@8afbe8ae6ab7647d0c9f0cfa7c2f939650d22509 # v5.5.1 with: mode: exactly count: 1 diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index f1995bb866..fa1152c336 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -100,7 +100,7 @@ jobs: name: release-apk-signed - name: Create draft release - uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 + uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2 with: draft: true tag_name: ${{ env.IMMICH_VERSION }} diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index bb3ae8f27f..c94ee14209 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -25,6 +25,8 @@ jobs: with: node-version-file: './open-api/typescript-sdk/.nvmrc' registry-url: 'https://registry.npmjs.org' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Install deps run: npm ci - name: Build diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 62e84ac957..0344189043 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -42,6 +42,9 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + defaults: + run: + working-directory: ./mobile steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -49,34 +52,30 @@ jobs: persist-credentials: false - name: Setup Flutter SDK - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0 + uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0 with: channel: 'stable' flutter-version-file: ./mobile/pubspec.yaml - name: Install dependencies run: dart pub get - working-directory: ./mobile - name: Install DCM - run: | - sudo apt-get update - wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg - echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list - sudo apt-get update - sudo apt-get install dcm + # TODO: Move to upstream after https://github.com/CQLabs/setup-dcm/pull/235 merges + uses: bo0tzz/setup-dcm@b4952ab813659c03513b57bd78bfe3f634171f8a + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + version: auto + working-directory: ./mobile - name: Generate translation file run: make translation - working-directory: ./mobile - name: Run Build Runner run: make build - working-directory: ./mobile - name: Generate platform API run: make pigeon - working-directory: ./mobile - name: Find file changes uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 @@ -98,19 +97,16 @@ jobs: - name: Run dart analyze run: dart analyze --fatal-infos - working-directory: ./mobile - name: Run dart format run: dart format lib/ --set-exit-if-changed - working-directory: ./mobile - name: Run dart custom_lint run: dart run custom_lint - working-directory: ./mobile + # TODO: Use https://github.com/CQLabs/dcm-action - name: Run DCM - run: dcm analyze lib - working-directory: ./mobile + run: dcm analyze lib --fatal-style --fatal-warnings zizmor: name: zizmor @@ -134,7 +130,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: sarif_file: results.sarif category: zizmor diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 186eb07761..47a11c8232 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -84,6 +84,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Run npm install run: npm ci @@ -101,7 +103,7 @@ jobs: if: ${{ !cancelled() }} - name: Run small tests & coverage - run: npm run test:cov + run: npm test if: ${{ !cancelled() }} cli-unit-tests: @@ -125,6 +127,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './cli/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Setup typescript-sdk run: npm ci && npm run build @@ -146,7 +150,7 @@ jobs: if: ${{ !cancelled() }} - name: Run unit tests & coverage - run: npm run test:cov + run: npm run test if: ${{ !cancelled() }} cli-unit-tests-win: @@ -170,6 +174,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './cli/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Setup typescript-sdk run: npm ci && npm run build @@ -184,7 +190,7 @@ jobs: if: ${{ !cancelled() }} - name: Run unit tests & coverage - run: npm run test:cov + run: npm run test if: ${{ !cancelled() }} web-lint: @@ -208,6 +214,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './web/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Run setup typescript-sdk run: npm ci && npm run build @@ -249,6 +257,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './web/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Run setup typescript-sdk run: npm ci && npm run build @@ -262,7 +272,7 @@ jobs: if: ${{ !cancelled() }} - name: Run unit tests & coverage - run: npm run test:cov + run: npm run test if: ${{ !cancelled() }} i18n-tests: @@ -282,6 +292,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './web/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Install dependencies run: npm --prefix=web ci @@ -326,6 +338,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './e2e/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Run setup typescript-sdk run: npm ci && npm run build @@ -369,6 +383,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Run npm install run: npm ci @@ -402,6 +418,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './e2e/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Run setup typescript-sdk run: npm ci && npm run build @@ -450,6 +468,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './e2e/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Run setup typescript-sdk run: npm ci && npm run build @@ -461,7 +481,7 @@ jobs: if: ${{ !cancelled() }} - name: Install Playwright Browsers - run: npx playwright install --with-deps chromium + run: npx playwright install chromium --only-shell if: ${{ !cancelled() }} - name: Docker build @@ -479,7 +499,7 @@ jobs: runs-on: ubuntu-latest if: always() steps: - - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3 + - uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4 with: needs: ${{ toJSON(needs) }} @@ -496,7 +516,7 @@ jobs: persist-credentials: false - name: Setup Flutter SDK - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0 + uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0 with: channel: 'stable' flutter-version-file: ./mobile/pubspec.yaml @@ -568,6 +588,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './.github/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Run npm install run: npm ci @@ -587,7 +609,7 @@ jobs: persist-credentials: false - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master + uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 with: ignore_paths: >- **/open-api/** @@ -609,6 +631,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Install server dependencies run: npm --prefix=server ci @@ -644,7 +668,7 @@ jobs: contents: read services: postgres: - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:1f5583fe3397210a0fbc7f11b0cec18bacc4a99e3e8ea0548e9bd6bcf26ec37a env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres @@ -670,6 +694,8 @@ jobs: uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Install server dependencies run: npm ci @@ -722,6 +748,7 @@ jobs: run: | echo "ERROR: Generated SQL files not up to date!" echo "Changed files: ${CHANGED_FILES}" + git diff exit 1 # mobile-integration-tests: diff --git a/.github/workflows/weblate-lock.yml b/.github/workflows/weblate-lock.yml index 0758ef4dc9..084e1a97d6 100644 --- a/.github/workflows/weblate-lock.yml +++ b/.github/workflows/weblate-lock.yml @@ -52,6 +52,6 @@ jobs: permissions: {} if: always() steps: - - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3 + - uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4 with: needs: ${{ toJSON(needs) }} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..119d6a961b --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,72 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Fix Permissions, Install Dependencies", + "type": "shell", + "command": "[ -f /immich-devcontainer/container-start.sh ] && /immich-devcontainer/container-start.sh || exit 0", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": true, + "clear": false, + "group": "Devcontainer tasks", + "close": true + }, + "runOptions": { + "runOn": "default" + }, + "problemMatcher": [] + }, + { + "label": "Immich API Server (Nest)", + "dependsOn": ["Fix Permissions, Install Dependencies"], + "type": "shell", + "command": "[ -f /immich-devcontainer/container-start-backend.sh ] && /immich-devcontainer/container-start-backend.sh || exit 0", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": true, + "clear": false, + "group": "Devcontainer tasks", + "close": true + }, + "runOptions": { + "runOn": "default" + }, + "problemMatcher": [] + }, + { + "label": "Immich Web Server (Vite)", + "dependsOn": ["Fix Permissions, Install Dependencies"], + "type": "shell", + "command": "[ -f /immich-devcontainer/container-start-frontend.sh ] && /immich-devcontainer/container-start-frontend.sh || exit 0", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": true, + "clear": false, + "group": "Devcontainer tasks", + "close": true + }, + "runOptions": { + "runOn": "default" + }, + "problemMatcher": [] + }, + { + "label": "Immich Server and Web", + "dependsOn": ["Immich Web Server (Vite)", "Immich API Server (Nest)"], + "runOptions": { + "runOn": "folderOpen" + }, + "problemMatcher": [] + } + ] +} diff --git a/Makefile b/Makefile index 1e7760ae68..207665d31c 100644 --- a/Makefile +++ b/Makefile @@ -1,27 +1,33 @@ dev: - docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans || make dev-down + @trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans dev-down: docker compose -f ./docker/docker-compose.dev.yml down --remove-orphans dev-update: - docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans + @trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans dev-scale: - docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans + @trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans .PHONY: e2e e2e: - docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans + @trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans + +e2e-update: + @trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans + +e2e-down: + docker compose -f ./e2e/docker-compose.yml down --remove-orphans prod: - docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans + @trap 'make prod-down' EXIT; COMPOSE_BAKE=true 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 + @trap 'make prod-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans .PHONY: open-api open-api: @@ -48,6 +54,8 @@ audit-%: npm --prefix $(subst sdk,open-api/typescript-sdk,$*) audit fix install-%: npm --prefix $(subst sdk,open-api/typescript-sdk,$*) i +ci-%: + npm --prefix $(subst sdk,open-api/typescript-sdk,$*) ci build-cli: build-sdk build-web: build-sdk build-%: install-% @@ -81,7 +89,8 @@ test-medium-dev: docker exec -it immich_server /bin/sh -c "npm run test:medium" build-all: $(foreach M,$(filter-out e2e .github,$(MODULES)),build-$M) ; -install-all: $(foreach M,$(MODULES),install-$M) ; +install-all: $(foreach M,$(MODULES),install-$M) ; +ci-all: $(foreach M,$(filter-out .github,$(MODULES)),ci-$M) ; check-all: $(foreach M,$(filter-out sdk cli docs .github,$(MODULES)),check-$M) ; lint-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),lint-$M) ; format-all: $(foreach M,$(filter-out sdk,$(MODULES)),format-$M) ; @@ -90,9 +99,11 @@ hygiene-all: lint-all format-all check-all sql audit-all; test-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),test-$M) ; clean: - find . -name "node_modules" -type d -prune -exec rm -rf '{}' + + find . -name "node_modules" -type d -prune -exec rm -rf {} + find . -name "dist" -type d -prune -exec rm -rf '{}' + find . -name "build" -type d -prune -exec rm -rf '{}' + find . -name "svelte-kit" -type d -prune -exec rm -rf '{}' + - docker compose -f ./docker/docker-compose.dev.yml rm -v -f || true - docker compose -f ./e2e/docker-compose.yml rm -v -f || true + command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml rm -v -f || true + command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml rm -v -f || true + +setup-dev: install-server install-sdk build-sdk install-web \ No newline at end of file diff --git a/cli/.nvmrc b/cli/.nvmrc index 5b540673a8..fc37597bcc 100644 --- a/cli/.nvmrc +++ b/cli/.nvmrc @@ -1 +1 @@ -22.16.0 +22.17.0 diff --git a/cli/bin/immich b/cli/bin/immich new file mode 100755 index 0000000000..924fff1230 --- /dev/null +++ b/cli/bin/immich @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import '../dist/index.js'; diff --git a/cli/package-lock.json b/cli/package-lock.json index 7732e3fb71..0bb42db17c 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.68", + "version": "2.2.72", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.68", + "version": "2.2.72", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -16,7 +16,7 @@ "micromatch": "^4.0.8" }, "bin": { - "immich": "dist/index.js" + "immich": "bin/immich" }, "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -27,7 +27,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.29", + "@types/node": "^22.15.33", "@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.134.0", + "version": "1.135.3", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.29", + "@types/node": "^22.15.33", "typescript": "^5.3.3" } }, @@ -607,9 +607,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -622,9 +622,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", - "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -682,9 +682,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.27.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", - "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", + "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", "dev": true, "license": "MIT", "engines": { @@ -1276,6 +1276,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, "node_modules/@types/cli-progress": { "version": "3.11.6", "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.6.tgz", @@ -1286,6 +1296,13 @@ "@types/node": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", @@ -1338,9 +1355,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", - "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", + "version": "22.15.34", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", + "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", "dev": true, "license": "MIT", "dependencies": { @@ -1348,17 +1365,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "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==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz", + "integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@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", + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/type-utils": "8.35.1", + "@typescript-eslint/utils": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1372,7 +1389,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "@typescript-eslint/parser": "^8.35.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -1388,16 +1405,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", - "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz", + "integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==", "dev": true, "license": "MIT", "dependencies": { - "@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", + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/typescript-estree": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "debug": "^4.3.4" }, "engines": { @@ -1412,15 +1429,37 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", - "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz", + "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.1", - "@typescript-eslint/visitor-keys": "8.32.1" + "@typescript-eslint/tsconfig-utils": "^8.35.1", + "@typescript-eslint/types": "^8.35.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz", + "integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1430,15 +1469,32 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz", + "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/@typescript-eslint/type-utils": { - "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==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz", + "integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.32.1", - "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/typescript-estree": "8.35.1", + "@typescript-eslint/utils": "8.35.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1455,9 +1511,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", - "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz", + "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==", "dev": true, "license": "MIT", "engines": { @@ -1469,14 +1525,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", - "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz", + "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.1", - "@typescript-eslint/visitor-keys": "8.32.1", + "@typescript-eslint/project-service": "8.35.1", + "@typescript-eslint/tsconfig-utils": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1496,9 +1554,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1522,16 +1580,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", - "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz", + "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==", "dev": true, "license": "MIT", "dependencies": { "@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" + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/typescript-estree": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1546,14 +1604,14 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", - "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz", + "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.1", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.35.1", + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1564,15 +1622,16 @@ } }, "node_modules/@vitest/coverage-v8": { - "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==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", + "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^1.0.2", - "debug": "^4.4.0", + "ast-v8-to-istanbul": "^0.3.3", + "debug": "^4.4.1", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", @@ -1587,8 +1646,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.4", - "vitest": "3.1.4" + "@vitest/browser": "3.2.4", + "vitest": "3.2.4" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1597,14 +1656,15 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz", - "integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.4", - "@vitest/utils": "3.1.4", + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -1613,13 +1673,13 @@ } }, "node_modules/@vitest/mocker": { - "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==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.4", + "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -1628,7 +1688,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "peerDependenciesMeta": { "msw": { @@ -1640,9 +1700,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz", - "integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, "license": "MIT", "dependencies": { @@ -1653,27 +1713,28 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz", - "integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.4", - "pathe": "^2.0.3" + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz", - "integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.4", + "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -1682,27 +1743,27 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz", - "integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^3.0.2" + "tinyspy": "^4.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz", - "integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.4", - "loupe": "^3.1.3", + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" }, "funding": { @@ -1710,9 +1771,9 @@ } }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -1792,6 +1853,18 @@ "node": ">=12" } }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", + "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "estree-walker": "^3.0.3", + "js-tokens": "^9.0.1" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2105,9 +2178,9 @@ } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2232,19 +2305,19 @@ } }, "node_modules/eslint": { - "version": "9.27.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", - "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", + "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", "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.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.27.0", + "@eslint/js": "9.30.1", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -2256,9 +2329,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2309,14 +2382,14 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz", - "integrity": "sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", + "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", "dev": true, "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.11.0" + "synckit": "^0.11.7" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -2402,9 +2475,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2419,9 +2492,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2432,15 +2505,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2749,9 +2822,9 @@ } }, "node_modules/globals": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", - "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", "dev": true, "license": "MIT", "engines": { @@ -2975,6 +3048,13 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3076,9 +3156,9 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", + "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", "dev": true, "license": "MIT" }, @@ -3347,9 +3427,9 @@ "license": "MIT" }, "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, "license": "MIT", "engines": { @@ -3425,9 +3505,9 @@ } }, "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { @@ -3799,6 +3879,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3813,14 +3906,13 @@ } }, "node_modules/synckit": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz", - "integrity": "sha512-Q/XQKRaJiLiFIBNN+mndW7S/RHxvwzuZS6ZwmRzUBqJBv/5QIKCEwkBC8GBf8EQJKYnaFs0wOZbKTXBPj8L9oQ==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", "dev": true, "license": "MIT", "dependencies": { - "@pkgr/core": "^0.2.3", - "tslib": "^2.8.1" + "@pkgr/core": "^0.2.4" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -3885,9 +3977,9 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "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": { @@ -3930,9 +4022,9 @@ } }, "node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", "engines": { @@ -3950,9 +4042,9 @@ } }, "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", "dev": true, "license": "MIT", "engines": { @@ -4005,13 +4097,6 @@ } } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4040,15 +4125,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz", - "integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.1.tgz", + "integrity": "sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.32.1", - "@typescript-eslint/parser": "8.32.1", - "@typescript-eslint/utils": "8.32.1" + "@typescript-eslint/eslint-plugin": "8.35.1", + "@typescript-eslint/parser": "8.35.1", + "@typescript-eslint/utils": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4186,17 +4271,17 @@ } }, "node_modules/vite-node": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz", - "integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.4.0", + "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" @@ -4257,32 +4342,34 @@ } }, "node_modules/vitest": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz", - "integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", "dependencies": { - "@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", + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", "chai": "^5.2.0", - "debug": "^4.4.0", + "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", + "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.13", - "tinypool": "^1.0.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.4", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -4298,8 +4385,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.4", - "@vitest/ui": "3.1.4", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, @@ -4340,6 +4427,19 @@ "vitest": ">=2.0.0" } }, + "node_modules/vitest/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/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/cli/package.json b/cli/package.json index 97c3448e31..ca4ada2f65 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,11 +1,11 @@ { "name": "@immich/cli", - "version": "2.2.68", + "version": "2.2.72", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", "bin": { - "immich": "dist/index.js" + "immich": "./bin/immich" }, "license": "GNU Affero General Public License version 3", "keywords": [ @@ -21,7 +21,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.29", + "@types/node": "^22.15.33", "@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.16.0" + "node": "22.17.0" } } diff --git a/cli/src/commands/asset.ts b/cli/src/commands/asset.ts index edb24e2f67..2af3cf8d5e 100644 --- a/cli/src/commands/asset.ts +++ b/cli/src/commands/asset.ts @@ -70,7 +70,10 @@ const uploadBatch = async (files: string[], options: UploadOptionsDto) => { console.log(JSON.stringify({ newFiles, duplicates, newAssets }, undefined, 4)); } await updateAlbums([...newAssets, ...duplicates], options); - await deleteFiles(newFiles, options); + await deleteFiles( + newAssets.map(({ filepath }) => filepath), + options, + ); }; export const startWatch = async ( diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index c6fb8e40d2..0694bf57f2 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 @@ -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:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2 + image: docker.io/valkey/valkey:8-bookworm@sha256:fec42f399876eb6faf9e008570597741c87ff7662a54185593e74b09ce83d177 healthcheck: test: redis-cli ping || exit 1 database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 env_file: - .env environment: @@ -134,6 +134,7 @@ services: - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data ports: - 5432:5432 + shm_size: 128mb # 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 1406062f55..bf70c43e4a 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:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2 + image: docker.io/valkey/valkey:8-bookworm@sha256:fec42f399876eb6faf9e008570597741c87ff7662a54185593e74b09ce83d177 healthcheck: test: redis-cli ping || exit 1 restart: always database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 env_file: - .env environment: @@ -75,6 +75,7 @@ services: - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data ports: - 5432:5432 + shm_size: 128mb restart: always # set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics @@ -82,7 +83,7 @@ services: container_name: immich_prometheus ports: - 9090:9090 - image: prom/prometheus@sha256:9abc6cf6aea7710d163dbb28d8eeb7dc5baef01e38fa4cd146a406dd9f07f70d + image: prom/prometheus@sha256:7a34573f0b9c952286b33d537f233cd5b708e12263733aa646e50c33f598f16c volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus @@ -91,10 +92,10 @@ services: # add data source for http://immich-prometheus:9090 to get started immich-grafana: container_name: immich_grafana - command: [ './run.sh', '-disable-reporting' ] + command: ['./run.sh', '-disable-reporting'] ports: - 3000:3000 - image: grafana/grafana:12.0.1-ubuntu@sha256:65575bb9c761335e2ff30e364f21d38632e3b2e75f5f81d83cc92f44b9bbc055 + image: grafana/grafana:12.0.2-ubuntu@sha256:0512d81cdeaaff0e370a9aa66027b465d1f1f04379c3a9c801a905fabbdbc7a5 volumes: - grafana-data:/var/lib/grafana diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 3536879145..95d375ccd6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -49,14 +49,14 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2 + image: docker.io/valkey/valkey:8-bookworm@sha256:fec42f399876eb6faf9e008570597741c87ff7662a54185593e74b09ce83d177 healthcheck: test: redis-cli ping || exit 1 restart: always database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} @@ -67,6 +67,7 @@ services: 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 + shm_size: 128mb restart: always volumes: diff --git a/docs/.nvmrc b/docs/.nvmrc index 5b540673a8..fc37597bcc 100644 --- a/docs/.nvmrc +++ b/docs/.nvmrc @@ -1 +1 @@ -22.16.0 +22.17.0 diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx index 24aa8e5d1b..9e14d10a0e 100644 --- a/docs/docs/FAQ.mdx +++ b/docs/docs/FAQ.mdx @@ -490,7 +490,7 @@ You can also scan the Postgres database file structure for errors:
Scan for file structure errors ```bash -docker exec -it immich_postgres pg_amcheck --username=postgres --heapallindexed --parent-check --rootdescend --progress --all --install-missing +docker exec -it immich_postgres pg_amcheck --username= --heapallindexed --parent-check --rootdescend --progress --all --install-missing ``` A normal result will end something like this and return with an exit code of `0`: diff --git a/docs/docs/administration/backup-and-restore.md b/docs/docs/administration/backup-and-restore.md index 7b90c3b19a..deeefa5635 100644 --- a/docs/docs/administration/backup-and-restore.md +++ b/docs/docs/administration/backup-and-restore.md @@ -57,7 +57,7 @@ Then please follow the steps in the following section for restoring the database ```bash title='Backup' -docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres | gzip > "/path/to/backup/dump.sql.gz" +docker exec -t immich_postgres pg_dumpall --clean --if-exists --username= | gzip > "/path/to/backup/dump.sql.gz" ``` ```bash title='Restore' @@ -79,7 +79,7 @@ docker compose up -d # Start remainder of Immich apps ```powershell title='Backup' -[System.IO.File]::WriteAllLines("C:\absolute\path\to\backup\dump.sql", (docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres)) +[System.IO.File]::WriteAllLines("C:\absolute\path\to\backup\dump.sql", (docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=)) ``` ```powershell title='Restore' @@ -150,12 +150,10 @@ for more info read the [release notes](https://github.com/immich-app/immich/rele - Preview images (small thumbnails and large previews) for each asset and thumbnails for recognized faces. - Stored in `UPLOAD_LOCATION/thumbs/`. - **Encoded Assets:** - - Videos that have been re-encoded from the original for wider compatibility. The original is not removed. - Stored in `UPLOAD_LOCATION/encoded-video/`. - **Postgres** - - The Immich database containing all the information to allow the system to function properly. **Note:** This folder will only appear to users who have made the changes mentioned in [v1.102.0](https://github.com/immich-app/immich/discussions/8930) (an optional, non-mandatory change) or who started with this version. - Stored in `DB_DATA_LOCATION`. @@ -201,7 +199,6 @@ When you turn off the storage template engine, it will leave the assets in `UPLO - Temporarily located in `UPLOAD_LOCATION/upload/`. - Transferred to `UPLOAD_LOCATION/library/` upon successful upload. - **Postgres** - - The Immich database containing all the information to allow the system to function properly. **Note:** This folder will only appear to users who have made the changes mentioned in [v1.102.0](https://github.com/immich-app/immich/discussions/8930) (an optional, non-mandatory change) or who started with this version. - Stored in `DB_DATA_LOCATION`. diff --git a/docs/docs/administration/oauth.md b/docs/docs/administration/oauth.md index b60b5dbb8b..833b70f77a 100644 --- a/docs/docs/administration/oauth.md +++ b/docs/docs/administration/oauth.md @@ -20,7 +20,6 @@ Immich supports 3rd party authentication via [OpenID Connect][oidc] (OIDC), an i Before enabling OAuth in Immich, a new client application needs to be configured in the 3rd-party authentication server. While the specifics of this setup vary from provider to provider, the general approach should be the same. 1. Create a new (Client) Application - 1. The **Provider** type should be `OpenID Connect` or `OAuth2` 2. The **Client type** should be `Confidential` 3. The **Application** type should be `Web` @@ -29,7 +28,6 @@ Before enabling OAuth in Immich, a new client application needs to be configured 2. Configure Redirect URIs/Origins The **Sign-in redirect URIs** should include: - - `app.immich:///oauth-callback` - for logging in with OAuth from the [Mobile App](/docs/features/mobile-app.mdx) - `http://DOMAIN:PORT/auth/login` - for logging in with OAuth from the Web Client - `http://DOMAIN:PORT/user-settings` - for manually linking OAuth in the Web Client @@ -37,21 +35,17 @@ Before enabling OAuth in Immich, a new client application needs to be configured Redirect URIs should contain all the domains you will be using to access Immich. Some examples include: Mobile - - `app.immich:///oauth-callback` (You **MUST** include this for iOS and Android mobile apps to work properly) Localhost - - `http://localhost:2283/auth/login` - `http://localhost:2283/user-settings` Local IP - - `http://192.168.0.200:2283/auth/login` - `http://192.168.0.200:2283/user-settings` Hostname - - `https://immich.example.com/auth/login` - `https://immich.example.com/user-settings` @@ -68,6 +62,7 @@ Once you have a new OAuth client application configured, Immich can be configure | Scope | string | openid email profile | Full list of scopes to send with the request (space delimited) | | Signing Algorithm | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) | | Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**š** | +| Role Claim | string | immich_role | Claim mapping for the user's role. (should return "user" or "admin")**š** | | Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**š** | | Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (Enter 0 for unlimited quota) | | Button Text | string | Login with OAuth | Text for the OAuth button on the web | diff --git a/docs/docs/administration/postgres-standalone.md b/docs/docs/administration/postgres-standalone.md index d9ad331810..fd9b8a5e4d 100644 --- a/docs/docs/administration/postgres-standalone.md +++ b/docs/docs/administration/postgres-standalone.md @@ -64,7 +64,13 @@ COMMIT; ### Updating VectorChord -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;`. +When installing a new version of VectorChord, you will need to manually update the extension and reindex by connecting to the Immich database and running: + +``` +ALTER EXTENSION vchord UPDATE; +REINDEX INDEX face_index; +REINDEX INDEX clip_index; +``` ## Migrating to VectorChord @@ -76,6 +82,8 @@ Support for pgvecto.rs will be dropped in a later release, hence we recommend al The easiest option is to have both extensions installed during the migration: +
+Migration steps (automatic) 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] @@ -89,8 +97,12 @@ The easiest option is to have both extensions installed during the migration: 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: +
+Migration steps (manual) 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 @@ -123,14 +135,20 @@ 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 +
+Migration steps 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 diff --git a/docs/docs/developer/devcontainers.md b/docs/docs/developer/devcontainers.md new file mode 100644 index 0000000000..1aab5ad327 --- /dev/null +++ b/docs/docs/developer/devcontainers.md @@ -0,0 +1,476 @@ +--- +title: Devcontainers +sidebar_position: 3 +--- + +# Development with Dev Containers + +Dev Containers provide a consistent, reproducible development environment using Docker containers. With a single click, you can get started with an Immich development environment on Mac, Linux, Windows, or in the cloud using GitHub Codespaces. + +[![Open in VSCode Containers](https://img.shields.io/static/v1?label=VSCode%20DevContainer&message=Immich&color=blue)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/immich-app/immich/) + +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/immich-app/immich/) + +[Learn more about Dev Containers](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers) + +## Prerequisites + +Before getting started, ensure you have: + +- **Docker Desktop** (latest version) + - [Mac](https://docs.docker.com/desktop/install/mac-install/) + - [Windows](https://docs.docker.com/desktop/install/windows-install/) (with WSL2 backend recommended) + - [Linux](https://docs.docker.com/desktop/install/linux-install/) +- **Visual Studio Code** with the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) +- **Git** for cloning the repository +- At least **8GB of RAM** (16GB recommended) +- **20GB of free disk space** + +:::tip Alternative Development Environments +While this guide focuses on VS Code, you have many options for Dev Container development: + +**Local Editors:** + +- [IntelliJ IDEA](https://www.jetbrains.com/help/idea/connect-to-devcontainer.html) - Full JetBrains IDE support +- [neovim](https://github.com/jamestthompson3/nvim-remote-containers) - Lightweight terminal-based editor +- [Emacs](https://github.com/emacs-lsp/lsp-docker) - Extensible text editor +- [DevContainer CLI](https://github.com/devcontainers/cli) - Command-line interface + +**Cloud-Based Solutions:** + +- [GitHub Codespaces](https://github.com/features/codespaces) - Fully integrated with GitHub, excellent devcontainer.json support +- [GitPod](https://www.gitpod.io) - SaaS platform with recent Dev Container support (historically used gitpod.yml) + +**Self-Hostable Options:** + +- [Coder](https://coder.com) - Enterprise-focused, requires Terraform knowledge, self-managed +- [DevPod](https://devpod.sh) - Client-only tool with excellent devcontainer.json support, works with any provider (local, cloud, or on-premise) + ::: + +## Dev Container Services + +The Dev Container environment consists of the following services: + +| Service | Container Name | Description | Ports | +| ---------------- | ------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------- | +| Server & Web | `immich-server` | Runs both API server and web frontend in development mode | 2283 (API)
3000 (Web)
9230 (Workers Debug)
9231 (API Debug) | +| Database | `database` | PostgreSQL database | 5432 | +| Cache | `redis` | Valkey cache server | 6379 | +| Machine Learning | `immich-machine-learning` | Immich ML model inference server | 3003 | + +## Getting Started + +### Step 1: Clone the Repository + +```bash +git clone https://github.com/immich-app/immich.git +cd immich +``` + +### Step 2: Configure Environment Variables + +The immich dev containers read environment variables from your shell environment, not from `.env` files. This allows them to work in cloud environments without pre-configuration. + +:::important Required Configuration +When running locally, and if you want to create (or use an existing) DB and/or photo storage folder, you must set the `UPLOAD_LOCATION` variable in your shell environment before launching the Dev Container. This determines where uploaded files are stored and also where the DB stores it data. + +```bash +# Set temporarily for current session +export UPLOAD_LOCATION=/opt/dev_upload_folder + +# Or add to your shell profile for persistence +# (~/.bashrc, ~/.zshrc, ~/.bash_profile, etc.) +echo 'export UPLOAD_LOCATION=/opt/dev_upload_folder' >> ~/.bashrc +source ~/.bashrc +``` + +::: + +### Step 3: Launch the Dev Container + +#### Using VS Code UI: + +1. Open the cloned repository in VS Code +2. Press `F1` or `Ctrl/Cmd+Shift+P` to open the command palette +3. Type and select "Dev Containers: Rebuild and Reopen in Container" +4. Select "Immich - Backend, Frontend and ML" from the list +5. Wait for the container to build and start (this may take several minutes on first run) + +#### Using VS Code Quick Actions: + +1. Open the repository in VS Code +2. You should see a popup asking if you want to reopen in a container +3. Click "Reopen in Container" + +#### Using Command Line: + +```bash +# Using the DevContainer CLI +devcontainer up --workspace-folder . +``` + +## Environment Variable Details + +### How Dev Containers Handle Environment Variables + +Unlike the Immich developer setup based on Docker Compose which uses `.env` files, Immich Dev Containers read environment variables from your shell environment. This is configured in `.devcontainer/devcontainer.json`: + +```json +"remoteEnv": { + "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}", + "DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}", + "DB_USERNAME": "${localEnv:DB_USERNAME:postgres}", + "DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}" +} +``` + +The `${localEnv:VARIABLE:default}` syntax reads from your shell environment with optional defaults. + +### Upload Location Path Resolution + +The `UPLOAD_LOCATION` environment variable controls where files are stored: + +**Default:** `./Library` (relative to the `docker` directory) +**Resolved to:** `/docker/Library` + +**Bind Mounts Created:** + +```yaml +# From .devcontainer/server/container-compose-overrides.yml +- ${UPLOAD_LOCATION-./Library}/photos:/workspaces/immich/server/upload +- ${UPLOAD_LOCATION-./Library}/postgres:/var/lib/postgresql/data +``` + +### Database Configuration + +These variables have sensible defaults (for development) but can be customized: + +| Variable | Default | Description | +| ------------------ | ---------- | ------------------- | +| `DB_PASSWORD` | `postgres` | PostgreSQL password | +| `DB_USERNAME` | `postgres` | PostgreSQL username | +| `DB_DATABASE_NAME` | `immich` | Database name | + +### Setting Environment Variables + +Add these to your shell profile (`~/.bashrc`, `~/.zshrc`, `~/.bash_profile`, etc.): + +```bash +# Required +export UPLOAD_LOCATION=./Library # or absolute path + +# Optional (only if using non-default values) +export DB_PASSWORD=your_password +export DB_USERNAME=your_username +export DB_DATABASE_NAME=your_database +``` + +Remember to reload your shell configuration: + +```bash +source ~/.bashrc # or ~/.zshrc, etc. +``` + +## Git Configuration + +### SSH Keys and Authentication + +To use your SSH keys for GitHub access inside the Dev Container: + +1. **Start SSH Agent** on your host machine: + + ```bash + eval "$(ssh-agent -s)" + ssh-add ~/.ssh/id_rsa # or your key path + ``` + +2. **VS Code automatically forwards your SSH agent** to the container + +For detailed instructions, see the [VS Code guide on sharing Git credentials](https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials). + +### Commit Signing + +To use your SSH key for commit signing, see the [GitHub guide on SSH commit signing](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key#telling-git-about-your-ssh-key). + +## Development Workflow + +### Automatic Setup + +When the Dev Container starts, it automatically: + +1. **Runs post-create script** (`container-server-post-create.sh`): + - Adjusts file permissions for the `node` user + - Installs dependencies: `npm install` in all packages + - Builds TypeScript SDK: `npm run build` in `open-api/typescript-sdk` + +2. **Starts development servers** via VS Code tasks: + - `Immich API Server (Nest)` - API server with hot-reloading on port 2283 + - `Immich Web Server (Vite)` - Web frontend with hot-reloading on port 3000 + - Both servers watch for file changes and recompile automatically + +3. **Configures port forwarding**: + - Web UI: http://localhost:3000 (opens automatically) + - API: http://localhost:2283 + - Debug ports: 9230 (workers), 9231 (API) + +:::info +The Dev Container setup replaces the `make dev` command from the traditional setup. All services start automatically when you open the container. +::: + +### Accessing Services + +Once running, you can access: + +| Service | URL | Description | +| -------- | --------------------- | ---------------------------------------------------------------------------------------------- | +| Web UI | http://localhost:3000 | Main web interface | +| API | http://localhost:2283 | REST API endpoints (Not used directly, web UI will expose this over http://localhost:3000/api) | +| Database | localhost:5432 | PostgreSQL (username: `postgres`) (Not used directly) | + +### Connecting Mobile Apps + +To connect the mobile app to your Dev Container: + +1. Find your machine's IP address +2. In the mobile app, use: `http://YOUR_IP:3000/api` +3. Ensure your firewall allows connections on port 2283 + +### Making Code Changes + +- **Server code** (`/server`): Changes trigger automatic restart +- **Web code** (`/web`): Changes trigger hot module replacement +- **Database migrations**: Run `npm run sync:sql` in the server directory +- **API changes**: Regenerate TypeScript SDK with `make open-api` + +## Testing + +### Running Tests + +The Dev Container supports multiple ways to run tests: + +#### Using Make Commands (Recommended) + +```bash +# Run tests for specific components +make test-server # Server unit tests +make test-web # Web unit tests +make test-e2e # End-to-end tests +make test-cli # CLI tests + +# Run all tests +make test-all # Runs tests for all components + +# Medium tests (integration tests) +make test-medium-dev # End-to-end tests +``` + +#### Using NPM Directly + +```bash +# Server tests +cd /workspaces/immich/server +npm test # Run all tests +npm run test:watch # Watch mode +npm run test:cov # Coverage report + +# Web tests +cd /workspaces/immich/web +npm test # Run all tests +npm run test:watch # Watch mode + +# E2E tests +cd /workspaces/immich/e2e +npm run test # Run API tests +npm run test:web # Run web UI tests +``` + +### Code Quality Commands + +```bash +# Linting +make lint-server # Lint server code +make lint-web # Lint web code +make lint-all # Lint all components + +# Formatting +make format-server # Format server code +make format-web # Format web code +make format-all # Format all code + +# Type checking +make check-server # Type check server +make check-web # Type check web +make check-all # Check all components + +# Complete hygiene check +make hygiene-all # Runs lint, format, check, SQL sync, and audit +``` + +### Additional Make Commands + +```bash +# Build commands +make build-server # Build server +make build-web # Build web app +make build-all # Build everything + +# API generation +make open-api # Generate OpenAPI specs +make open-api-typescript # Generate TypeScript SDK +make open-api-dart # Generate Dart SDK + +# Database +make sql # Sync database schema + +# Dependencies +make install-server # Install server dependencies +make install-web # Install web dependencies +make install-all # Install all dependencies +``` + +### Debugging + +The Dev Container is pre-configured for debugging: + +1. **API Server Debugging**: + - Set breakpoints in VS Code + - Press `F5` or use "Run and Debug" panel + - Select "Attach to Server" configuration + - Debug port: 9231 + +2. **Worker Debugging**: + - Use "Attach to Workers" configuration + - Debug port: 9230 + +3. **Web Debugging**: + - Use browser DevTools + - VS Code debugger for Chrome/Edge extensions supported + +## Troubleshooting + +### Common Issues + +#### Permission Errors + +**Problem**: `EACCES` or permission denied errors +**Solution**: + +- The Dev Container runs as the `node` user (UID 1000) +- If your host UID differs, you may see permission issues +- Try rebuilding the container: "Dev Containers: Rebuild Container" + +#### Container Won't Start + +**Problem**: Dev Container fails to start or build +**Solution**: + +1. Check Docker is running: `docker ps` +2. Clean Docker resources: `docker system prune -a` +3. Check available disk space +4. Review Docker Desktop resource limits + +#### Port Already in Use + +**Problem**: "Port 3000/2283 is already in use" +**Solution**: + +1. Check for conflicting services: `lsof -i :3000` (macOS/Linux) +2. Stop conflicting services or change port mappings +3. Restart Docker Desktop + +#### Upload Location Not Set + +**Problem**: Errors about missing UPLOAD_LOCATION +**Solution**: + +1. Set the environment variable: `export UPLOAD_LOCATION=./Library` +2. Add to your shell profile for persistence +3. Restart your terminal and VS Code + +#### Database Connection Failed + +**Problem**: Cannot connect to PostgreSQL +**Solution**: + +1. Ensure all containers are running: `docker ps` +2. Check logs: "Dev Containers: Show Container Log" +3. Verify database credentials match environment variables + +### Getting Help + +If you encounter issues: + +1. Check container logs: View → Output → Select "Dev Containers" +2. Rebuild without cache: "Dev Containers: Rebuild Container Without Cache" +3. Review [common Docker issues](https://docs.docker.com/desktop/troubleshoot/) +4. Ask in [Discord](https://discord.immich.app) `#help-desk-support` channel + +## Mobile Development + +While the Dev Container focuses on server and web development, you can connect mobile apps for testing: + +### Connecting iOS/Android Apps + +1. **Ensure API is accessible**: + + ```bash + # Find your machine's IP + # macOS + ipconfig getifaddr en0 + # Linux + hostname -I + # Windows (in WSL2) + ip addr show eth0 + ``` + +2. **Configure mobile app**: + - Server URL: `http://YOUR_IP:2283/api` + - Ensure firewall allows port 2283 + +3. **For full mobile development**, see the [mobile development guide](/docs/developer/setup) which covers: + - Flutter setup + - Running on simulators/devices + - Mobile-specific debugging + +## Advanced Configuration + +### Custom VS Code Extensions + +Add extensions to `.devcontainer/devcontainer.json`: + +```json +"customizations": { + "vscode": { + "extensions": [ + "your.extension-id" + ] + } +} +``` + +### Additional Services + +To add services (e.g., Redis Commander), modify: + +1. `/docker/docker-compose.dev.yml` - Add service definition +2. `/.devcontainer/server/container-compose-overrides.yml` - Add overrides if needed + +### Resource Limits + +Adjust Docker Desktop resources: + +- **macOS/Windows**: Docker Desktop → Settings → Resources +- **Linux**: Modify Docker daemon configuration + +Recommended minimums: + +- CPU: 4 cores +- Memory: 8GB +- Disk: 20GB + +## Next Steps + +- Read the [architecture overview](/docs/developer/architecture) +- Learn about [database migrations](/docs/developer/database-migrations) +- Explore [API documentation](/docs/api) +- Join `#immich` on [Discord](https://discord.immich.app) diff --git a/docs/docs/features/folder-view.md b/docs/docs/features/folder-view.md index 3534a22081..3d8613a042 100644 --- a/docs/docs/features/folder-view.md +++ b/docs/docs/features/folder-view.md @@ -2,7 +2,7 @@ Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template. -You can enable this feature under [`Account Settings > Features > Folder View`](https://my.immich.app/user-settings?isOpen=feature+folders) +You can enable this feature under [`Account Settings > Features > Folders`](https://my.immich.app/user-settings?isOpen=feature+folders) ## Enable folder view diff --git a/docs/docs/features/libraries.md b/docs/docs/features/libraries.md index 9f094509f4..ad76eb44b2 100644 --- a/docs/docs/features/libraries.md +++ b/docs/docs/features/libraries.md @@ -56,7 +56,7 @@ Internally, Immich uses the [glob](https://www.npmjs.com/package/glob) package t ### Automatic watching (EXPERIMENTAL) -This feature - currently hidden in the config file - is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan. +This feature is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan. If your photos are on a network drive, automatic file watching likely won't work. In that case, you will have to rely on a periodic library refresh to pull in your changes. @@ -112,12 +112,15 @@ _Remember to run `docker compose up -d` to register the changes. Make sure you c These actions must be performed by the Immich administrator. -- Click on Administration -> Libraries -- Click on Create External Library +- Click on your avatar in the upper right corner +- Click on Administration -> External Libraries +- Click on Create an external libraryâ€Ļ - Select which user owns the library, this can not be changed later - Enter `/mnt/media/christmas-trip` then click Add - Click on Save - Click the drop-down menu on the newly created library +- Click on Scan +- Click the drop-down menu on the newly created library - Click on Rename Library and rename it to "Christmas Trip" NOTE: We have to use the `/mnt/media/christmas-trip` path and not the `/mnt/nas/christmas-trip` path since all paths have to be what the Docker containers see. @@ -156,9 +159,7 @@ Within seconds, the assets from the old-pics and videos folders should show up i Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template. -You can enable this feature under [`Account Settings > Features > Folder View`](https://my.immich.app/user-settings?isOpen=feature+folders) - -The UI is currently only available for the web; mobile will come in a subsequent release. +You can enable this feature under [`Account Settings > Features > Folders`](https://my.immich.app/user-settings?isOpen=feature+folders) @@ -168,7 +169,7 @@ The UI is currently only available for the web; mobile will come in a subsequent Only an admin can do this. ::: -You can define a custom interval for the trigger external library rescan under Administration -> Settings -> Library. +You can define a custom interval for the trigger external library rescan under Administration -> Settings -> External Library. You can set the scanning interval using the preset or cron format. For more information you can refer to [Crontab Guru](https://crontab.guru/). diff --git a/docs/docs/features/supported-formats.md b/docs/docs/features/supported-formats.md index e6fb2c8f00..16f1ab0b6b 100644 --- a/docs/docs/features/supported-formats.md +++ b/docs/docs/features/supported-formats.md @@ -16,7 +16,7 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a | `HEIC` | `.heic` | :white_check_mark: | | | `HEIF` | `.heif` | :white_check_mark: | | | `JPEG 2000` | `.jp2` | :white_check_mark: | | -| `JPEG` | `.webp` `.jpg` `.jpe` `.insp` | :white_check_mark: | | +| `JPEG` | `.jpeg` `.jpg` `.jpe` `.insp` | :white_check_mark: | | | `JPEG XL` | `.jxl` | :white_check_mark: | | | `PNG` | `.png` | :white_check_mark: | | | `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop | diff --git a/docs/docs/guides/img/administration-link.webp b/docs/docs/guides/img/administration-link.webp index cbed69782a..22bc4e4c87 100644 Binary files a/docs/docs/guides/img/administration-link.webp and b/docs/docs/guides/img/administration-link.webp differ diff --git a/docs/docs/guides/template-backup-script.md b/docs/docs/guides/template-backup-script.md index dd0b94ebb1..34381dd0ee 100644 --- a/docs/docs/guides/template-backup-script.md +++ b/docs/docs/guides/template-backup-script.md @@ -52,9 +52,9 @@ REMOTE_BACKUP_PATH="/path/to/remote/backup/directory" ### Local # Backup Immich database -docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres > "$UPLOAD_LOCATION"/database-backup/immich-database.sql +docker exec -t immich_postgres pg_dumpall --clean --if-exists --username= > "$UPLOAD_LOCATION"/database-backup/immich-database.sql # For deduplicating backup programs such as Borg or Restic, compressing the content can increase backup size by making it harder to deduplicate. If you are using a different program or still prefer to compress, you can use the following command instead: -# docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres | /usr/bin/gzip --rsyncable > "$UPLOAD_LOCATION"/database-backup/immich-database.sql.gz +# docker exec -t immich_postgres pg_dumpall --clean --if-exists --username= | /usr/bin/gzip --rsyncable > "$UPLOAD_LOCATION"/database-backup/immich-database.sql.gz ### Append to local Borg repository borg create "$BACKUP_PATH/immich-borg::{now}" "$UPLOAD_LOCATION" --exclude "$UPLOAD_LOCATION"/thumbs/ --exclude "$UPLOAD_LOCATION"/encoded-video/ diff --git a/docs/docs/install/config-file.md b/docs/docs/install/config-file.md index 9806af760a..54d7c61bb3 100644 --- a/docs/docs/install/config-file.md +++ b/docs/docs/install/config-file.md @@ -123,7 +123,7 @@ The default configuration looks like this: "buttonText": "Login with OAuth", "clientId": "", "clientSecret": "", - "defaultStorageQuota": 0, + "defaultStorageQuota": null, "enabled": false, "issuerUrl": "", "mobileOverrideEnabled": false, diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index d3ca49a0a4..b9dab5b9c8 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -72,22 +72,25 @@ 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_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 | +| 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 | +| `DB_STORAGE_TYPE` | Optimize concurrent IO on SSDs or sequential IO on HDDs ([`SSD`, `HDD`])\*3 | `SSD` | 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: 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. +\*3: Uses either [`postgresql.ssd.conf`](https://github.com/immich-app/base-images/blob/main/postgres/postgresql.ssd.conf) or [`postgresql.hdd.conf`](https://github.com/immich-app/base-images/blob/main/postgres/postgresql.hdd.conf) which mainly controls the Postgres `effective_io_concurrency` setting to allow for concurrenct IO on SSDs and sequential IO on HDDs. + :::info All `DB_` variables must be provided to all Immich workers, including `api` and `microservices`. diff --git a/docs/docs/install/portainer.md b/docs/docs/install/portainer.md index 5ff324e282..916d89a0d5 100644 --- a/docs/docs/install/portainer.md +++ b/docs/docs/install/portainer.md @@ -39,8 +39,8 @@ alt="Dot Env Example" /> - Change the default `DB_PASSWORD`, and add custom database connection information if necessary. -- Change `DB_DATA_LOCATION` to a folder where the database will be saved to disk. -- Change `UPLOAD_LOCATION` to a folder where media (uploaded and generated) will be stored. +- Change `DB_DATA_LOCATION` to a folder (absolute path) where the database will be saved to disk. +- Change `UPLOAD_LOCATION` to a folder (absolute path) where media (uploaded and generated) will be stored. 11. Click on "**Deploy the stack**". diff --git a/docs/docs/install/synology.md b/docs/docs/install/synology.md index e8bca61489..c61513b7f6 100644 --- a/docs/docs/install/synology.md +++ b/docs/docs/install/synology.md @@ -25,7 +25,7 @@ When you're all done, you should have the following: - `./docker/immich-app/postgres` - `./docker/immich-app/library` -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) to your computer. Upload the files to the `./docker/immich-app` directory. +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) to your computer. Upload the files to the `./docker/immich-app` directory, and rename `example.env` to `.env`. ## Step 2 - Populate the .env file with custom values diff --git a/docs/docs/install/truenas.md b/docs/docs/install/truenas.md index bd0dc7af5f..de110e00a1 100644 --- a/docs/docs/install/truenas.md +++ b/docs/docs/install/truenas.md @@ -9,7 +9,7 @@ This is a community contribution and not officially supported by the Immich team Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/). -**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/charts/tree/master/community/immich).** +**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/apps/tree/master/trains/community/immich).** ::: Immich can easily be installed on TrueNAS Community Edition via the **Community** train application. diff --git a/docs/docs/install/unraid.md b/docs/docs/install/unraid.md index 344b912aea..efb493f267 100644 --- a/docs/docs/install/unraid.md +++ b/docs/docs/install/unraid.md @@ -75,7 +75,6 @@ alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich" 5. Click "**Save Changes**", you will be prompted to edit stack UI labels, just leave this blank and click "**Ok**" 6. Select the cog âš™ī¸ next to Immich, click "**Edit Stack**", then click "**Env File**" 7. Paste the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following: - - `UPLOAD_LOCATION`: Create a folder in your Images Unraid share and place the **absolute** location here > For example my _"images"_ share has a folder within it called _"immich"_. If I browse to this directory in the terminal and type `pwd` the output is `/mnt/user/images/immich`. This is the exact value I need to enter as my `UPLOAD_LOCATION` - `DB_DATA_LOCATION`: Change this to use an Unraid share (preferably a cache pool, e.g. `/mnt/user/appdata/postgresql/data`). This uses the `appdata` share. Do also create the `postgresql` folder, by running `mkdir /mnt/user/{share_location}/postgresql/data`. If left at default it will try to use Unraid's `/boot/config/plugins/compose.manager/projects/[stack_name]/postgres` folder which it doesn't have permissions to, resulting in this container continuously restarting. diff --git a/docs/package-lock.json b/docs/package-lock.json index 602232da07..53ac997e3b 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,8 +8,9 @@ "name": "documentation", "version": "0.0.0", "dependencies": { - "@docusaurus/core": "~3.7.0", - "@docusaurus/preset-classic": "~3.7.0", + "@docusaurus/core": "~3.8.0", + "@docusaurus/preset-classic": "~3.8.0", + "@docusaurus/theme-common": "~3.8.0", "@mdi/js": "^7.3.67", "@mdi/react": "^1.6.1", "@mdx-js/react": "^3.0.0", @@ -18,6 +19,7 @@ "clsx": "^2.0.0", "docusaurus-lunr-search": "^3.3.2", "docusaurus-preset-openapi": "^0.7.5", + "lunr": "^2.3.9", "postcss": "^8.4.25", "prism-react-renderer": "^2.3.1", "raw-loader": "^4.0.2", @@ -27,7 +29,7 @@ "url": "^0.11.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "~3.7.0", + "@docusaurus/module-type-aliases": "~3.8.0", "@docusaurus/tsconfig": "^3.7.0", "@docusaurus/types": "^3.7.0", "prettier": "^3.2.4", @@ -1980,9 +1982,9 @@ } }, "node_modules/@csstools/cascade-layer-name-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.4.tgz", - "integrity": "sha512-7DFHlPuIxviKYZrOiwVU/PiHLm3lLUR23OMuEEtfEOQTOp9hzQ2JjdY6X5H18RVuUPJqSCI+qNnD5iOLMVE0bA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz", + "integrity": "sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==", "funding": [ { "type": "github", @@ -1998,8 +2000,8 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/color-helpers": { @@ -2022,9 +2024,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz", - "integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", "funding": [ { "type": "github", @@ -2040,14 +2042,14 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-color-parser": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz", - "integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", + "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", "funding": [ { "type": "github", @@ -2061,20 +2063,20 @@ "license": "MIT", "dependencies": { "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.2" + "@csstools/css-calc": "^2.1.4" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "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==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", "funding": [ { "type": "github", @@ -2090,13 +2092,13 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-tokenizer": "^3.0.4" } }, "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==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", "funding": [ { "type": "github", @@ -2113,9 +2115,9 @@ } }, "node_modules/@csstools/media-query-list-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz", - "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", + "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", "funding": [ { "type": "github", @@ -2131,14 +2133,14 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/postcss-cascade-layers": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.1.tgz", - "integrity": "sha512-XOfhI7GShVcKiKwmPAnWSqd2tBR0uxt+runAxttbSp/LY2U16yAVPmAf7e9q4JJ0d+xMNmpwNDLBXnmRCl3HMQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.2.tgz", + "integrity": "sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==", "funding": [ { "type": "github", @@ -2197,9 +2199,9 @@ } }, "node_modules/@csstools/postcss-color-function": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.8.tgz", - "integrity": "sha512-9dUvP2qpZI6PlGQ/sob+95B3u5u7nkYt9yhZFCC7G9HBRHBxj+QxS/wUlwaMGYW0waf+NIierI8aoDTssEdRYw==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.10.tgz", + "integrity": "sha512-4dY0NBu7NVIpzxZRgh/Q/0GPSz/jLSw0i/u3LTUor0BkQcz/fNhN10mSWBDsL0p9nDb0Ky1PD6/dcGbhACuFTQ==", "funding": [ { "type": "github", @@ -2212,10 +2214,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2226,9 +2228,9 @@ } }, "node_modules/@csstools/postcss-color-mix-function": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.8.tgz", - "integrity": "sha512-yuZpgWUzqZWQhEqfvtJufhl28DgO9sBwSbXbf/59gejNuvZcoUTRGQZhzhwF4ccqb53YAGB+u92z9+eSKoB4YA==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.10.tgz", + "integrity": "sha512-P0lIbQW9I4ShE7uBgZRib/lMTf9XMjJkFl/d6w4EMNHu2qvQ6zljJGEcBkw/NsBtq/6q3WrmgxSS8kHtPMkK4Q==", "funding": [ { "type": "github", @@ -2241,10 +2243,39 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-color-mix-variadic-function-arguments": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-variadic-function-arguments/-/postcss-color-mix-variadic-function-arguments-1.0.0.tgz", + "integrity": "sha512-Z5WhouTyD74dPFPrVE7KydgNS9VvnjB8qcdes9ARpCOItb4jTnm7cHp4FhxCRUoyhabD0WVv43wbkJ4p8hLAlQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2255,9 +2286,9 @@ } }, "node_modules/@csstools/postcss-content-alt-text": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.4.tgz", - "integrity": "sha512-YItlZUOuZJCBlRaCf8Aucc1lgN41qYGALMly0qQllrxYJhiyzlI6RxOTMUvtWk+KhS8GphMDsDhKQ7KTPfEMSw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.6.tgz", + "integrity": "sha512-eRjLbOjblXq+byyaedQRSrAejKGNAFued+LcbzT+LCL78fabxHkxYjBbxkroONxHHYu2qxhFK2dBStTLPG3jpQ==", "funding": [ { "type": "github", @@ -2270,9 +2301,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2283,9 +2314,9 @@ } }, "node_modules/@csstools/postcss-exponential-functions": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.7.tgz", - "integrity": "sha512-XTb6Mw0v2qXtQYRW9d9duAjDnoTbBpsngD7sRNLmYDjvwU2ebpIHplyxgOeo6jp/Kr52gkLi5VaK5RDCqzMzZQ==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.9.tgz", + "integrity": "sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw==", "funding": [ { "type": "github", @@ -2298,9 +2329,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -2336,9 +2367,9 @@ } }, "node_modules/@csstools/postcss-gamut-mapping": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.8.tgz", - "integrity": "sha512-/K8u9ZyGMGPjmwCSIjgaOLKfic2RIGdFHHes84XW5LnmrvdhOTVxo255NppHi3ROEvoHPW7MplMJgjZK5Q+TxA==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.10.tgz", + "integrity": "sha512-QDGqhJlvFnDlaPAfCYPsnwVA6ze+8hhrwevYWlnUeSjkkZfBpcCO42SaUD8jiLlq7niouyLgvup5lh+f1qessg==", "funding": [ { "type": "github", @@ -2351,9 +2382,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -2363,9 +2394,9 @@ } }, "node_modules/@csstools/postcss-gradients-interpolation-method": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.8.tgz", - "integrity": "sha512-CoHQ/0UXrvxLovu0ZeW6c3/20hjJ/QRg6lyXm3dZLY/JgvRU6bdbQZF/Du30A4TvowfcgvIHQmP1bNXUxgDrAw==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.10.tgz", + "integrity": "sha512-HHPauB2k7Oits02tKFUeVFEU2ox/H3OQVrP3fSOKDxvloOikSal+3dzlyTZmYsb9FlY9p5EUpBtz0//XBmy+aw==", "funding": [ { "type": "github", @@ -2378,10 +2409,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2392,9 +2423,9 @@ } }, "node_modules/@csstools/postcss-hwb-function": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.8.tgz", - "integrity": "sha512-LpFKjX6hblpeqyych1cKmk+3FJZ19QmaJtqincySoMkbkG/w2tfbnO5oE6mlnCTXcGUJ0rCEuRHvTqKK0nHYUQ==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.10.tgz", + "integrity": "sha512-nOKKfp14SWcdEQ++S9/4TgRKchooLZL0TUFdun3nI4KPwCjETmhjta1QT4ICQcGVWQTvrsgMM/aLB5We+kMHhQ==", "funding": [ { "type": "github", @@ -2407,10 +2438,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2421,9 +2452,9 @@ } }, "node_modules/@csstools/postcss-ic-unit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.0.tgz", - "integrity": "sha512-9QT5TDGgx7wD3EEMN3BSUG6ckb6Eh5gSPT5kZoVtUuAonfPmLDJyPhqR4ntPpMYhUKAMVKAg3I/AgzqHMSeLhA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.2.tgz", + "integrity": "sha512-lrK2jjyZwh7DbxaNnIUjkeDmU8Y6KyzRBk91ZkI5h8nb1ykEfZrtIVArdIjX4DHMIBGpdHrgP0n4qXDr7OHaKA==", "funding": [ { "type": "github", @@ -2436,7 +2467,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" }, @@ -2470,9 +2501,9 @@ } }, "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.1.tgz", - "integrity": "sha512-JLp3POui4S1auhDR0n8wHd/zTOWmMsmK3nQd3hhL6FhWPaox5W7j1se6zXOG/aP07wV2ww0lxbKYGwbBszOtfQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz", + "integrity": "sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==", "funding": [ { "type": "github", @@ -2531,9 +2562,9 @@ } }, "node_modules/@csstools/postcss-light-dark-function": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.7.tgz", - "integrity": "sha512-ZZ0rwlanYKOHekyIPaU+sVm3BEHCe+Ha0/px+bmHe62n0Uc1lL34vbwrLYn6ote8PHlsqzKeTQdIejQCJ05tfw==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.9.tgz", + "integrity": "sha512-1tCZH5bla0EAkFAI2r0H33CDnIBeLUaJh1p+hvvsylJ4svsv2wOmJjJn+OXwUZLXef37GYbRIVKX+X+g6m+3CQ==", "funding": [ { "type": "github", @@ -2546,9 +2577,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2650,9 +2681,9 @@ } }, "node_modules/@csstools/postcss-logical-viewport-units": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.3.tgz", - "integrity": "sha512-OC1IlG/yoGJdi0Y+7duz/kU/beCwO+Gua01sD6GtOtLi7ByQUpcIqs7UE/xuRPay4cHgOMatWdnDdsIDjnWpPw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.4.tgz", + "integrity": "sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ==", "funding": [ { "type": "github", @@ -2665,7 +2696,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-tokenizer": "^3.0.3", + "@csstools/css-tokenizer": "^3.0.4", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2676,9 +2707,9 @@ } }, "node_modules/@csstools/postcss-media-minmax": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.7.tgz", - "integrity": "sha512-LB6tIP7iBZb5CYv8iRenfBZmbaG3DWNEziOnPjGoQX5P94FBPvvTBy68b/d9NnS5PELKwFmmOYsAEIgEhDPCHA==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.9.tgz", + "integrity": "sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig==", "funding": [ { "type": "github", @@ -2691,10 +2722,10 @@ ], "license": "MIT", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" }, "engines": { "node": ">=18" @@ -2704,9 +2735,9 @@ } }, "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.4.tgz", - "integrity": "sha512-AnGjVslHMm5xw9keusQYvjVWvuS7KWK+OJagaG0+m9QnIjZsrysD2kJP/tr/UJIyYtMCtu8OkUd+Rajb4DqtIQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.5.tgz", + "integrity": "sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg==", "funding": [ { "type": "github", @@ -2719,9 +2750,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" }, "engines": { "node": ">=18" @@ -2782,9 +2813,9 @@ } }, "node_modules/@csstools/postcss-oklab-function": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.8.tgz", - "integrity": "sha512-+5aPsNWgxohXoYNS1f+Ys0x3Qnfehgygv3qrPyv+Y25G0yX54/WlVB+IXprqBLOXHM1gsVF+QQSjlArhygna0Q==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.10.tgz", + "integrity": "sha512-ZzZUTDd0fgNdhv8UUjGCtObPD8LYxMH+MJsW9xlZaWTV8Ppr4PtxlHYNMmF4vVWGl0T6f8tyWAKjoI6vePSgAg==", "funding": [ { "type": "github", @@ -2797,10 +2828,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2811,9 +2842,9 @@ } }, "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.0.0.tgz", - "integrity": "sha512-XQPtROaQjomnvLUSy/bALTR5VCtTVUFwYs1SblvYgLSeTo2a/bMNwUwo2piXw5rTv/FEYiy5yPSXBqg9OKUx7Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.1.0.tgz", + "integrity": "sha512-YrkI9dx8U4R8Sz2EJaoeD9fI7s7kmeEBfmO+UURNeL6lQI7VxF6sBE+rSqdCBn4onwqmxFdBU3lTwyYb/lCmxA==", "funding": [ { "type": "github", @@ -2836,9 +2867,9 @@ } }, "node_modules/@csstools/postcss-random-function": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-1.0.3.tgz", - "integrity": "sha512-dbNeEEPHxAwfQJ3duRL5IPpuD77QAHtRl4bAHRs0vOVhVbHrsL7mHnwe0irYjbs9kYwhAHZBQTLBgmvufPuRkA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz", + "integrity": "sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w==", "funding": [ { "type": "github", @@ -2851,9 +2882,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -2863,9 +2894,9 @@ } }, "node_modules/@csstools/postcss-relative-color-syntax": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.8.tgz", - "integrity": "sha512-eGE31oLnJDoUysDdjS9MLxNZdtqqSxjDXMdISpLh80QMaYrKs7VINpid34tWQ+iU23Wg5x76qAzf1Q/SLLbZVg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.10.tgz", + "integrity": "sha512-8+0kQbQGg9yYG8hv0dtEpOMLwB9M+P7PhacgIzVzJpixxV4Eq9AUQtQw8adMmAJU1RBBmIlpmtmm3XTRd/T00g==", "funding": [ { "type": "github", @@ -2878,10 +2909,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2930,9 +2961,9 @@ } }, "node_modules/@csstools/postcss-sign-functions": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.2.tgz", - "integrity": "sha512-4EcAvXTUPh7n6UoZZkCzgtCf/wPzMlTNuddcKg7HG8ozfQkUcHsJ2faQKeLmjyKdYPyOUn4YA7yDPf8K/jfIxw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.4.tgz", + "integrity": "sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg==", "funding": [ { "type": "github", @@ -2945,9 +2976,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -2957,9 +2988,9 @@ } }, "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.7.tgz", - "integrity": "sha512-rdrRCKRnWtj5FyRin0u/gLla7CIvZRw/zMGI1fVJP0Sg/m1WGicjPVHRANL++3HQtsiXKAbPrcPr+VkyGck0IA==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.9.tgz", + "integrity": "sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA==", "funding": [ { "type": "github", @@ -2972,9 +3003,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -3010,9 +3041,9 @@ } }, "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.7.tgz", - "integrity": "sha512-qTrZgLju3AV7Djhzuh2Bq/wjFqbcypnk0FhHjxW8DWJQcZLS1HecIus4X2/RLch1ukX7b+YYCdqbEnpIQO5ccg==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz", + "integrity": "sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==", "funding": [ { "type": "github", @@ -3025,9 +3056,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -3128,9 +3159,9 @@ } }, "node_modules/@docusaurus/babel": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.7.0.tgz", - "integrity": "sha512-0H5uoJLm14S/oKV3Keihxvh8RV+vrid+6Gv+2qhuzbqHanawga8tYnsdpjEyt36ucJjqlby2/Md2ObWjA02UXQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.8.1.tgz", + "integrity": "sha512-3brkJrml8vUbn9aeoZUlJfsI/GqyFcDgQJwQkmBtclJgWDEQBKKeagZfOgx0WfUQhagL1sQLNW0iBdxnI863Uw==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.9", @@ -3143,8 +3174,8 @@ "@babel/runtime": "^7.25.9", "@babel/runtime-corejs3": "^7.25.9", "@babel/traverse": "^7.25.9", - "@docusaurus/logger": "3.7.0", - "@docusaurus/utils": "3.7.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/utils": "3.8.1", "babel-plugin-dynamic-import-node": "^2.3.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -3154,31 +3185,30 @@ } }, "node_modules/@docusaurus/bundler": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.7.0.tgz", - "integrity": "sha512-CUUT9VlSGukrCU5ctZucykvgCISivct+cby28wJwCC/fkQFgAHRp/GKv2tx38ZmXb7nacrKzFTcp++f9txUYGg==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.8.1.tgz", + "integrity": "sha512-/z4V0FRoQ0GuSLToNjOSGsk6m2lQUG4FRn8goOVoZSRsTrU8YR2aJacX5K3RG18EaX9b+52pN4m1sL3MQZVsQA==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.9", - "@docusaurus/babel": "3.7.0", - "@docusaurus/cssnano-preset": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", + "@docusaurus/babel": "3.8.1", + "@docusaurus/cssnano-preset": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", "babel-loader": "^9.2.1", - "clean-css": "^5.3.2", + "clean-css": "^5.3.3", "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.8.1", + "css-loader": "^6.11.0", "css-minimizer-webpack-plugin": "^5.0.1", "cssnano": "^6.1.2", "file-loader": "^6.2.0", "html-minifier-terser": "^7.2.0", - "mini-css-extract-plugin": "^2.9.1", + "mini-css-extract-plugin": "^2.9.2", "null-loader": "^4.0.1", - "postcss": "^8.4.26", - "postcss-loader": "^7.3.3", - "postcss-preset-env": "^10.1.0", - "react-dev-utils": "^12.0.1", + "postcss": "^8.5.4", + "postcss-loader": "^7.3.4", + "postcss-preset-env": "^10.2.1", "terser-webpack-plugin": "^5.3.9", "tslib": "^2.6.0", "url-loader": "^4.1.1", @@ -3198,18 +3228,18 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.7.0.tgz", - "integrity": "sha512-b0fUmaL+JbzDIQaamzpAFpTviiaU4cX3Qz8cuo14+HGBCwa0evEK0UYCBFY3n4cLzL8Op1BueeroUD2LYAIHbQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.8.1.tgz", + "integrity": "sha512-ENB01IyQSqI2FLtOzqSI3qxG2B/jP4gQPahl2C3XReiLebcVh5B5cB9KYFvdoOqOWPyr5gXK4sjgTKv7peXCrA==", "license": "MIT", "dependencies": { - "@docusaurus/babel": "3.7.0", - "@docusaurus/bundler": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/babel": "3.8.1", + "@docusaurus/bundler": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", @@ -3217,19 +3247,19 @@ "combine-promises": "^1.1.0", "commander": "^5.1.0", "core-js": "^3.31.1", - "del": "^6.1.1", "detect-port": "^1.5.1", "escape-html": "^1.0.3", "eta": "^2.2.0", "eval": "^0.1.8", + "execa": "5.1.1", "fs-extra": "^11.1.1", "html-tags": "^3.3.1", "html-webpack-plugin": "^5.6.0", "leven": "^3.1.0", "lodash": "^4.17.21", + "open": "^8.4.0", "p-map": "^4.0.0", "prompts": "^2.4.2", - "react-dev-utils": "^12.0.1", "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", "react-loadable-ssr-addon-v5-slorber": "^1.0.1", @@ -3238,7 +3268,7 @@ "react-router-dom": "^5.3.4", "semver": "^7.5.4", "serve-handler": "^6.1.6", - "shelljs": "^0.8.5", + "tinypool": "^1.0.2", "tslib": "^2.6.0", "update-notifier": "^6.0.2", "webpack": "^5.95.0", @@ -3259,13 +3289,13 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.7.0.tgz", - "integrity": "sha512-X9GYgruZBSOozg4w4dzv9uOz8oK/EpPVQXkp0MM6Tsgp/nRIU9hJzJ0Pxg1aRa3xCeEQTOimZHcocQFlLwYajQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.8.1.tgz", + "integrity": "sha512-G7WyR2N6SpyUotqhGznERBK+x84uyhfMQM2MmDLs88bw4Flom6TY46HzkRkSEzaP9j80MbTN8naiL1fR17WQug==", "license": "MIT", "dependencies": { "cssnano-preset-advanced": "^6.1.2", - "postcss": "^8.4.38", + "postcss": "^8.5.4", "postcss-sort-media-queries": "^5.2.0", "tslib": "^2.6.0" }, @@ -3274,9 +3304,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.7.0.tgz", - "integrity": "sha512-z7g62X7bYxCYmeNNuO9jmzxLQG95q9QxINCwpboVcNff3SJiHJbGrarxxOVMVmAh1MsrSfxWkVGv4P41ktnFsA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.8.1.tgz", + "integrity": "sha512-2wjeGDhKcExEmjX8k1N/MRDiPKXGF2Pg+df/bDDPnnJWHXnVEZxXj80d6jcxp1Gpnksl0hF8t/ZQw9elqj2+ww==", "license": "MIT", "dependencies": { "chalk": "^4.1.2", @@ -3287,21 +3317,21 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.7.0.tgz", - "integrity": "sha512-OFBG6oMjZzc78/U3WNPSHs2W9ZJ723ewAcvVJaqS0VgyeUfmzUV8f1sv+iUHA0DtwiR5T5FjOxj6nzEE8LY6VA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.8.1.tgz", + "integrity": "sha512-DZRhagSFRcEq1cUtBMo4TKxSNo/W6/s44yhr8X+eoXqCLycFQUylebOMPseHi5tc4fkGJqwqpWJLz6JStU9L4w==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", "estree-util-value-to-estree": "^3.0.1", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", - "image-size": "^1.0.2", + "image-size": "^2.0.2", "mdast-util-mdx": "^3.0.0", "mdast-util-to-string": "^4.0.0", "rehype-raw": "^7.0.0", @@ -3326,17 +3356,17 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.7.0.tgz", - "integrity": "sha512-g7WdPqDNaqA60CmBrr0cORTrsOit77hbsTj7xE2l71YhBn79sxdm7WMK7wfhcaafkbpIh7jv5ef5TOpf1Xv9Lg==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.8.1.tgz", + "integrity": "sha512-6xhvAJiXzsaq3JdosS7wbRt/PwEPWHr9eM4YNYqVlbgG1hSK3uQDXTVvQktasp3VO6BmfYWPozueLWuj4gB+vg==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.7.0", + "@docusaurus/types": "3.8.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", "@types/react-router-dom": "*", - "react-helmet-async": "npm:@slorber/react-helmet-async@*", + "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" }, "peerDependencies": { @@ -3345,24 +3375,24 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.7.0.tgz", - "integrity": "sha512-EFLgEz6tGHYWdPU0rK8tSscZwx+AsyuBW/r+tNig2kbccHYGUJmZtYN38GjAa3Fda4NU+6wqUO5kTXQSRBQD3g==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.8.1.tgz", + "integrity": "sha512-vNTpMmlvNP9n3hGEcgPaXyvTljanAKIUkuG9URQ1DeuDup0OR7Ltvoc8yrmH+iMZJbcQGhUJF+WjHLwuk8HSdw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/theme-common": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "cheerio": "1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", "lodash": "^4.17.21", - "reading-time": "^1.5.0", + "schema-dts": "^1.1.2", "srcset": "^4.0.0", "tslib": "^2.6.0", "unist-util-visit": "^5.0.0", @@ -3379,25 +3409,26 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.7.0.tgz", - "integrity": "sha512-GXg5V7kC9FZE4FkUZA8oo/NrlRb06UwuICzI6tcbzj0+TVgjq/mpUXXzSgKzMS82YByi4dY2Q808njcBCyy6tQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.8.1.tgz", + "integrity": "sha512-oByRkSZzeGNQByCMaX+kif5Nl2vmtj2IHQI2fWjCfCootsdKZDPFLonhIp5s3IGJO7PLUfe0POyw0Xh/RrGXJA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/module-type-aliases": "3.7.0", - "@docusaurus/theme-common": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/module-type-aliases": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", "lodash": "^4.17.21", + "schema-dts": "^1.1.2", "tslib": "^2.6.0", "utility-types": "^3.10.0", "webpack": "^5.88.1" @@ -3411,16 +3442,16 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.7.0.tgz", - "integrity": "sha512-YJSU3tjIJf032/Aeao8SZjFOrXJbz/FACMveSMjLyMH4itQyZ2XgUIzt4y+1ISvvk5zrW4DABVT2awTCqBkx0Q==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.8.1.tgz", + "integrity": "sha512-a+V6MS2cIu37E/m7nDJn3dcxpvXb6TvgdNI22vJX8iUTp8eoMoPa0VArEbWvCxMY/xdC26WzNv4wZ6y0iIni/w==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -3433,17 +3464,33 @@ "react-dom": "^18.0.0 || ^19.0.0" } }, - "node_modules/@docusaurus/plugin-debug": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.7.0.tgz", - "integrity": "sha512-Qgg+IjG/z4svtbCNyTocjIwvNTNEwgRjSXXSJkKVG0oWoH0eX/HAPiu+TS1HBwRPQV+tTYPWLrUypYFepfujZA==", + "node_modules/@docusaurus/plugin-css-cascade-layers": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.8.1.tgz", + "integrity": "sha512-VQ47xRxfNKjHS5ItzaVXpxeTm7/wJLFMOPo1BkmoMG4Cuz4nuI+Hs62+RMk1OqVog68Swz66xVPK8g9XTrBKRw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@docusaurus/plugin-debug": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.8.1.tgz", + "integrity": "sha512-nT3lN7TV5bi5hKMB7FK8gCffFTBSsBsAfV84/v293qAmnHOyg1nr9okEw8AiwcO3bl9vije5nsUvP0aRl2lpaw==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", "fs-extra": "^11.1.1", - "react-json-view-lite": "^1.2.0", + "react-json-view-lite": "^2.3.0", "tslib": "^2.6.0" }, "engines": { @@ -3455,14 +3502,14 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.7.0.tgz", - "integrity": "sha512-otIqiRV/jka6Snjf+AqB360XCeSv7lQC+DKYW+EUZf6XbuE8utz5PeUQ8VuOcD8Bk5zvT1MC4JKcd5zPfDuMWA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.8.1.tgz", + "integrity": "sha512-Hrb/PurOJsmwHAsfMDH6oVpahkEGsx7F8CWMjyP/dw1qjqmdS9rcV1nYCGlM8nOtD3Wk/eaThzUB5TSZsGz+7Q==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "tslib": "^2.6.0" }, "engines": { @@ -3474,14 +3521,14 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.7.0.tgz", - "integrity": "sha512-M3vrMct1tY65ModbyeDaMoA+fNJTSPe5qmchhAbtqhDD/iALri0g9LrEpIOwNaoLmm6lO88sfBUADQrSRSGSWA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.8.1.tgz", + "integrity": "sha512-tKE8j1cEZCh8KZa4aa80zpSTxsC2/ZYqjx6AAfd8uA8VHZVw79+7OTEP2PoWi0uL5/1Is0LF5Vwxd+1fz5HlKg==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -3494,14 +3541,14 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.7.0.tgz", - "integrity": "sha512-X8U78nb8eiMiPNg3jb9zDIVuuo/rE1LjGDGu+5m5CX4UBZzjMy+klOY2fNya6x8ACyE/L3K2erO1ErheP55W/w==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.8.1.tgz", + "integrity": "sha512-iqe3XKITBquZq+6UAXdb1vI0fPY5iIOitVjPQ581R1ZKpHr0qe+V6gVOrrcOHixPDD/BUKdYwkxFjpNiEN+vBw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "tslib": "^2.6.0" }, "engines": { @@ -3513,17 +3560,17 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.7.0.tgz", - "integrity": "sha512-bTRT9YLZ/8I/wYWKMQke18+PF9MV8Qub34Sku6aw/vlZ/U+kuEuRpQ8bTcNOjaTSfYsWkK4tTwDMHK2p5S86cA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.8.1.tgz", + "integrity": "sha512-+9YV/7VLbGTq8qNkjiugIelmfUEVkTyLe6X8bWq7K5qPvGXAjno27QAfFq63mYfFFbJc7z+pudL63acprbqGzw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -3537,15 +3584,15 @@ } }, "node_modules/@docusaurus/plugin-svgr": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.7.0.tgz", - "integrity": "sha512-HByXIZTbc4GV5VAUkZ2DXtXv1Qdlnpk3IpuImwSnEzCDBkUMYcec5282hPjn6skZqB25M1TYCmWS91UbhBGxQg==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.8.1.tgz", + "integrity": "sha512-rW0LWMDsdlsgowVwqiMb/7tANDodpy1wWPwCcamvhY7OECReN3feoFwLjd/U4tKjNY3encj0AJSTxJA+Fpe+Gw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@svgr/core": "8.1.0", "@svgr/webpack": "^8.1.0", "tslib": "^2.6.0", @@ -3560,25 +3607,26 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.7.0.tgz", - "integrity": "sha512-nPHj8AxDLAaQXs+O6+BwILFuhiWbjfQWrdw2tifOClQoNfuXDjfjogee6zfx6NGHWqshR23LrcN115DmkHC91Q==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.8.1.tgz", + "integrity": "sha512-yJSjYNHXD8POMGc2mKQuj3ApPrN+eG0rO1UPgSx7jySpYU+n4WjBikbrA2ue5ad9A7aouEtMWUoiSRXTH/g7KQ==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/plugin-content-blog": "3.7.0", - "@docusaurus/plugin-content-docs": "3.7.0", - "@docusaurus/plugin-content-pages": "3.7.0", - "@docusaurus/plugin-debug": "3.7.0", - "@docusaurus/plugin-google-analytics": "3.7.0", - "@docusaurus/plugin-google-gtag": "3.7.0", - "@docusaurus/plugin-google-tag-manager": "3.7.0", - "@docusaurus/plugin-sitemap": "3.7.0", - "@docusaurus/plugin-svgr": "3.7.0", - "@docusaurus/theme-classic": "3.7.0", - "@docusaurus/theme-common": "3.7.0", - "@docusaurus/theme-search-algolia": "3.7.0", - "@docusaurus/types": "3.7.0" + "@docusaurus/core": "3.8.1", + "@docusaurus/plugin-content-blog": "3.8.1", + "@docusaurus/plugin-content-docs": "3.8.1", + "@docusaurus/plugin-content-pages": "3.8.1", + "@docusaurus/plugin-css-cascade-layers": "3.8.1", + "@docusaurus/plugin-debug": "3.8.1", + "@docusaurus/plugin-google-analytics": "3.8.1", + "@docusaurus/plugin-google-gtag": "3.8.1", + "@docusaurus/plugin-google-tag-manager": "3.8.1", + "@docusaurus/plugin-sitemap": "3.8.1", + "@docusaurus/plugin-svgr": "3.8.1", + "@docusaurus/theme-classic": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/theme-search-algolia": "3.8.1", + "@docusaurus/types": "3.8.1" }, "engines": { "node": ">=18.0" @@ -3589,31 +3637,31 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.7.0.tgz", - "integrity": "sha512-MnLxG39WcvLCl4eUzHr0gNcpHQfWoGqzADCly54aqCofQX6UozOS9Th4RK3ARbM9m7zIRv3qbhggI53dQtx/hQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.8.1.tgz", + "integrity": "sha512-bqDUCNqXeYypMCsE1VcTXSI1QuO4KXfx8Cvl6rYfY0bhhqN6d2WZlRkyLg/p6pm+DzvanqHOyYlqdPyP0iz+iw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/module-type-aliases": "3.7.0", - "@docusaurus/plugin-content-blog": "3.7.0", - "@docusaurus/plugin-content-docs": "3.7.0", - "@docusaurus/plugin-content-pages": "3.7.0", - "@docusaurus/theme-common": "3.7.0", - "@docusaurus/theme-translations": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/module-type-aliases": "3.8.1", + "@docusaurus/plugin-content-blog": "3.8.1", + "@docusaurus/plugin-content-docs": "3.8.1", + "@docusaurus/plugin-content-pages": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/theme-translations": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", "infima": "0.2.0-alpha.45", "lodash": "^4.17.21", "nprogress": "^0.2.0", - "postcss": "^8.4.26", + "postcss": "^8.5.4", "prism-react-renderer": "^2.3.0", "prismjs": "^1.29.0", "react-router-dom": "^5.3.4", @@ -3630,15 +3678,15 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.7.0.tgz", - "integrity": "sha512-8eJ5X0y+gWDsURZnBfH0WabdNm8XMCXHv8ENy/3Z/oQKwaB/EHt5lP9VsTDTf36lKEp0V6DjzjFyFIB+CetL0A==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.8.1.tgz", + "integrity": "sha512-UswMOyTnPEVRvN5Qzbo+l8k4xrd5fTFu2VPPfD6FcW/6qUtVLmJTQCktbAL3KJ0BVXGm5aJXz/ZrzqFuZERGPw==", "license": "MIT", "dependencies": { - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/module-type-aliases": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/module-type-aliases": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -3658,19 +3706,19 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.7.0.tgz", - "integrity": "sha512-Al/j5OdzwRU1m3falm+sYy9AaB93S1XF1Lgk9Yc6amp80dNxJVplQdQTR4cYdzkGtuQqbzUA8+kaoYYO0RbK6g==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.8.1.tgz", + "integrity": "sha512-NBFH5rZVQRAQM087aYSRKQ9yGEK9eHd+xOxQjqNpxMiV85OhJDD4ZGz6YJIod26Fbooy54UWVdzNU0TFeUUUzQ==", "license": "MIT", "dependencies": { - "@docsearch/react": "^3.8.1", - "@docusaurus/core": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/plugin-content-docs": "3.7.0", - "@docusaurus/theme-common": "3.7.0", - "@docusaurus/theme-translations": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docsearch/react": "^3.9.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/plugin-content-docs": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/theme-translations": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "algoliasearch": "^5.17.1", "algoliasearch-helper": "^3.22.6", "clsx": "^2.0.0", @@ -3689,9 +3737,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.7.0.tgz", - "integrity": "sha512-Ewq3bEraWDmienM6eaNK7fx+/lHMtGDHQyd1O+4+3EsDxxUmrzPkV7Ct3nBWTuE0MsoZr3yNwQVKjllzCMuU3g==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.8.1.tgz", + "integrity": "sha512-OTp6eebuMcf2rJt4bqnvuwmm3NVXfzfYejL+u/Y1qwKhZPrjPoKWfk1CbOP5xH5ZOPkiAsx4dHdQBRJszK3z2g==", "license": "MIT", "dependencies": { "fs-extra": "^11.1.1", @@ -3702,16 +3750,16 @@ } }, "node_modules/@docusaurus/tsconfig": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.7.0.tgz", - "integrity": "sha512-vRsyj3yUZCjscgfgcFYjIsTcAru/4h4YH2/XAE8Rs7wWdnng98PgWKvP5ovVc4rmRpRg2WChVW0uOy2xHDvDBQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.8.1.tgz", + "integrity": "sha512-XBWCcqhRHhkhfolnSolNL+N7gj3HVE3CoZVqnVjfsMzCoOsuQw2iCLxVVHtO+rePUUfouVZHURDgmqIySsF66A==", "dev": true, "license": "MIT" }, "node_modules/@docusaurus/types": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.7.0.tgz", - "integrity": "sha512-kOmZg5RRqJfH31m+6ZpnwVbkqMJrPOG5t0IOl4i/+3ruXyNfWzZ0lVtVrD0u4ONc/0NOsS9sWYaxxWNkH1LdLQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.8.1.tgz", + "integrity": "sha512-ZPdW5AB+pBjiVrcLuw3dOS6BFlrG0XkS2lDGsj8TizcnREQg3J8cjsgfDviszOk4CweNfwo1AEELJkYaMUuOPg==", "license": "MIT", "dependencies": { "@mdx-js/mdx": "^3.0.0", @@ -3744,15 +3792,16 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.7.0.tgz", - "integrity": "sha512-e7zcB6TPnVzyUaHMJyLSArKa2AG3h9+4CfvKXKKWNx6hRs+p0a+u7HHTJBgo6KW2m+vqDnuIHK4X+bhmoghAFA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.8.1.tgz", + "integrity": "sha512-P1ml0nvOmEFdmu0smSXOqTS1sxU5tqvnc0dA4MTKV39kye+bhQnjkIKEE18fNOvxjyB86k8esoCIFM3x4RykOQ==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils-common": "3.7.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils-common": "3.8.1", "escape-string-regexp": "^4.0.0", + "execa": "5.1.1", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "github-slugger": "^1.5.0", @@ -3762,9 +3811,9 @@ "js-yaml": "^4.1.0", "lodash": "^4.17.21", "micromatch": "^4.0.5", + "p-queue": "^6.6.2", "prompts": "^2.4.2", "resolve-pathname": "^3.0.0", - "shelljs": "^0.8.5", "tslib": "^2.6.0", "url-loader": "^4.1.1", "utility-types": "^3.10.0", @@ -3775,12 +3824,12 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.7.0.tgz", - "integrity": "sha512-IZeyIfCfXy0Mevj6bWNg7DG7B8G+S6o6JVpddikZtWyxJguiQ7JYr0SIZ0qWd8pGNuMyVwriWmbWqMnK7Y5PwA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.8.1.tgz", + "integrity": "sha512-zTZiDlvpvoJIrQEEd71c154DkcriBecm4z94OzEE9kz7ikS3J+iSlABhFXM45mZ0eN5pVqqr7cs60+ZlYLewtg==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.7.0", + "@docusaurus/types": "3.8.1", "tslib": "^2.6.0" }, "engines": { @@ -3788,14 +3837,14 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.7.0.tgz", - "integrity": "sha512-w8eiKk8mRdN+bNfeZqC4nyFoxNyI1/VExMKAzD9tqpJfLLbsa46Wfn5wcKH761g9WkKh36RtFV49iL9lh1DYBA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.8.1.tgz", + "integrity": "sha512-gs5bXIccxzEbyVecvxg6upTwaUbfa0KMmTj7HhHzc016AGyxH2o73k1/aOD0IFrdCsfJNt37MqNI47s2MgRZMA==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", @@ -4757,12 +4806,6 @@ "@types/node": "*" } }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "license": "MIT" - }, "node_modules/@types/parse5": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", @@ -5437,15 +5480,6 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/autocomplete.js": { "version": "0.37.1", "resolved": "https://registry.npmjs.org/autocomplete.js/-/autocomplete.js-0.37.1.tgz", @@ -5775,9 +5809,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "funding": [ { "type": "opencollective", @@ -5794,10 +5828,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -5978,9 +6012,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001713", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001713.tgz", - "integrity": "sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==", + "version": "1.0.30001726", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", "funding": [ { "type": "opencollective", @@ -7061,9 +7095,9 @@ } }, "node_modules/cssdb": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.2.5.tgz", - "integrity": "sha512-leAt8/hdTCtzql9ZZi86uYAmCLzVKpJMMdjbvOGVnXFXz/BWFpBmM1MHEHU/RqtPyRYmabVmEW1DtX3YGLuuLA==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.3.1.tgz", + "integrity": "sha512-XnDRQMXucLueX92yDe0LPKupXetWoFOgawr4O4X41l5TltgK2NVbJJVDnnOywDYfW1sTJ28AcXGKOqdRKwCcmQ==", "funding": [ { "type": "opencollective", @@ -7369,28 +7403,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", - "license": "MIT", - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -7463,38 +7475,6 @@ "node": ">= 4.0.0" } }, - "node_modules/detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "license": "MIT", - "dependencies": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "bin": { - "detect": "bin/detect-port", - "detect-port": "bin/detect-port" - }, - "engines": { - "node": ">= 4.2.1" - } - }, - "node_modules/detect-port-alt/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/detect-port-alt/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/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -7936,9 +7916,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.137", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", - "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", + "version": "1.5.178", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.178.tgz", + "integrity": "sha512-wObbz/ar3Bc6e4X5vf0iO8xTN8YAjN/tgiAOJLr7yjYFtP9wAjq8Mb5h0yn6kResir+VYx2DXBj9NNobs0ETSA==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -8685,15 +8665,6 @@ "node": ">=0.10.0" } }, - "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -8834,134 +8805,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", - "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=10", - "yarn": ">=1.0.0" - }, - "peerDependencies": { - "eslint": ">= 6", - "typescript": ">= 2.7", - "vue-template-compiler": "*", - "webpack": ">= 4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - }, - "vue-template-compiler": { - "optional": true - } - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/form-data": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", @@ -9281,44 +9124,6 @@ "node": ">=10" } }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "license": "MIT", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "license": "MIT", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -10644,13 +10449,10 @@ } }, "node_modules/image-size": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", - "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", + "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", "license": "MIT", - "dependencies": { - "queue": "6.0.2" - }, "bin": { "image-size": "bin/image-size.js" }, @@ -10994,15 +10796,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -11045,15 +10838,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -14650,6 +14434,15 @@ "node": ">=12.20" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", @@ -14695,6 +14488,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", @@ -14708,13 +14517,16 @@ "node": ">=8" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/package-json": { @@ -15012,83 +14824,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "license": "MIT", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "license": "MIT", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -15105,7 +14844,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -15183,9 +14922,9 @@ } }, "node_modules/postcss-color-functional-notation": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.8.tgz", - "integrity": "sha512-S/TpMKVKofNvsxfau/+bw+IA6cSfB6/kmzFj5szUofHOVnFFMB2WwK+Zu07BeMD8T0n+ZnTO5uXiMvAKe2dPkA==", + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.10.tgz", + "integrity": "sha512-k9qX+aXHBiLTRrWoCJuUFI6F1iF6QJQUXNVWJVSbqZgj57jDhBlOvD8gNUGl35tgqDivbGLhZeW3Ongz4feuKA==", "funding": [ { "type": "github", @@ -15198,10 +14937,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -15298,9 +15037,9 @@ } }, "node_modules/postcss-custom-media": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.5.tgz", - "integrity": "sha512-SQHhayVNgDvSAdX9NQ/ygcDQGEY+aSF4b/96z7QUX6mqL5yl/JgG/DywcF6fW9XbnCRE+aVYk+9/nqGuzOPWeQ==", + "version": "11.0.6", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.6.tgz", + "integrity": "sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw==", "funding": [ { "type": "github", @@ -15313,10 +15052,10 @@ ], "license": "MIT", "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.4", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2" + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" }, "engines": { "node": ">=18" @@ -15326,9 +15065,9 @@ } }, "node_modules/postcss-custom-properties": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.4.tgz", - "integrity": "sha512-QnW8FCCK6q+4ierwjnmXF9Y9KF8q0JkbgVfvQEMa93x1GT8FvOiUevWCN2YLaOWyByeDX8S6VFbZEeWoAoXs2A==", + "version": "14.0.6", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz", + "integrity": "sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==", "funding": [ { "type": "github", @@ -15341,9 +15080,9 @@ ], "license": "MIT", "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.4", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" }, @@ -15355,9 +15094,9 @@ } }, "node_modules/postcss-custom-selectors": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.4.tgz", - "integrity": "sha512-ASOXqNvDCE0dAJ/5qixxPeL1aOVGHGW2JwSy7HyjWNbnWTQCl+fDc968HY1jCmZI0+BaYT5CxsOiUhavpG/7eg==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.5.tgz", + "integrity": "sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg==", "funding": [ { "type": "github", @@ -15370,9 +15109,9 @@ ], "license": "MIT", "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.4", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", "postcss-selector-parser": "^7.0.0" }, "engines": { @@ -15497,9 +15236,9 @@ } }, "node_modules/postcss-double-position-gradients": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.0.tgz", - "integrity": "sha512-JkIGah3RVbdSEIrcobqj4Gzq0h53GG4uqDPsho88SgY84WnpkTpI0k50MFK/sX7XqVisZ6OqUfFnoUO6m1WWdg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.2.tgz", + "integrity": "sha512-7qTqnL7nfLRyJK/AHSVrrXOuvDDzettC+wGoienURV8v2svNbu6zJC52ruZtHaO6mfcagFmuTGFdzRsJKB3k5Q==", "funding": [ { "type": "github", @@ -15512,7 +15251,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" }, @@ -15693,9 +15432,9 @@ } }, "node_modules/postcss-lab-function": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.8.tgz", - "integrity": "sha512-plV21I86Hg9q8omNz13G9fhPtLopIWH06bt/Cb5cs1XnaGU2kUtEitvVd4vtQb/VqCdNUHK5swKn3QFmMRbpDg==", + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.10.tgz", + "integrity": "sha512-tqs6TCEv9tC1Riq6fOzHuHcZyhg4k3gIAMB8GGY/zA1ssGdm6puHMVE7t75aOSoFg7UD2wyrFFhbldiCMyyFTQ==", "funding": [ { "type": "github", @@ -15708,10 +15447,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -16040,9 +15779,9 @@ } }, "node_modules/postcss-nesting": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz", - "integrity": "sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz", + "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==", "funding": [ { "type": "github", @@ -16055,7 +15794,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/selector-resolve-nested": "^3.0.0", + "@csstools/selector-resolve-nested": "^3.1.0", "@csstools/selector-specificity": "^5.0.0", "postcss-selector-parser": "^7.0.0" }, @@ -16067,9 +15806,9 @@ } }, "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz", - "integrity": "sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", + "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", "funding": [ { "type": "github", @@ -16354,9 +16093,9 @@ } }, "node_modules/postcss-preset-env": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.1.5.tgz", - "integrity": "sha512-LQybafF/K7H+6fAs4SIkgzkSCixJy0/h0gubDIAP3Ihz+IQBRwsjyvBnAZ3JUHD+A/ITaxVRPDxn//a3Qy4pDw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.2.4.tgz", + "integrity": "sha512-q+lXgqmTMdB0Ty+EQ31SuodhdfZetUlwCA/F0zRcd/XdxjzI+Rl2JhZNz5US2n/7t9ePsvuhCnEN4Bmu86zXlA==", "funding": [ { "type": "github", @@ -16369,62 +16108,63 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/postcss-cascade-layers": "^5.0.1", - "@csstools/postcss-color-function": "^4.0.8", - "@csstools/postcss-color-mix-function": "^3.0.8", - "@csstools/postcss-content-alt-text": "^2.0.4", - "@csstools/postcss-exponential-functions": "^2.0.7", + "@csstools/postcss-cascade-layers": "^5.0.2", + "@csstools/postcss-color-function": "^4.0.10", + "@csstools/postcss-color-mix-function": "^3.0.10", + "@csstools/postcss-color-mix-variadic-function-arguments": "^1.0.0", + "@csstools/postcss-content-alt-text": "^2.0.6", + "@csstools/postcss-exponential-functions": "^2.0.9", "@csstools/postcss-font-format-keywords": "^4.0.0", - "@csstools/postcss-gamut-mapping": "^2.0.8", - "@csstools/postcss-gradients-interpolation-method": "^5.0.8", - "@csstools/postcss-hwb-function": "^4.0.8", - "@csstools/postcss-ic-unit": "^4.0.0", + "@csstools/postcss-gamut-mapping": "^2.0.10", + "@csstools/postcss-gradients-interpolation-method": "^5.0.10", + "@csstools/postcss-hwb-function": "^4.0.10", + "@csstools/postcss-ic-unit": "^4.0.2", "@csstools/postcss-initial": "^2.0.1", - "@csstools/postcss-is-pseudo-class": "^5.0.1", - "@csstools/postcss-light-dark-function": "^2.0.7", + "@csstools/postcss-is-pseudo-class": "^5.0.3", + "@csstools/postcss-light-dark-function": "^2.0.9", "@csstools/postcss-logical-float-and-clear": "^3.0.0", "@csstools/postcss-logical-overflow": "^2.0.0", "@csstools/postcss-logical-overscroll-behavior": "^2.0.0", "@csstools/postcss-logical-resize": "^3.0.0", - "@csstools/postcss-logical-viewport-units": "^3.0.3", - "@csstools/postcss-media-minmax": "^2.0.7", - "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.4", + "@csstools/postcss-logical-viewport-units": "^3.0.4", + "@csstools/postcss-media-minmax": "^2.0.9", + "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.5", "@csstools/postcss-nested-calc": "^4.0.0", "@csstools/postcss-normalize-display-values": "^4.0.0", - "@csstools/postcss-oklab-function": "^4.0.8", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", - "@csstools/postcss-random-function": "^1.0.3", - "@csstools/postcss-relative-color-syntax": "^3.0.8", + "@csstools/postcss-oklab-function": "^4.0.10", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", + "@csstools/postcss-random-function": "^2.0.1", + "@csstools/postcss-relative-color-syntax": "^3.0.10", "@csstools/postcss-scope-pseudo-class": "^4.0.1", - "@csstools/postcss-sign-functions": "^1.1.2", - "@csstools/postcss-stepped-value-functions": "^4.0.7", + "@csstools/postcss-sign-functions": "^1.1.4", + "@csstools/postcss-stepped-value-functions": "^4.0.9", "@csstools/postcss-text-decoration-shorthand": "^4.0.2", - "@csstools/postcss-trigonometric-functions": "^4.0.7", + "@csstools/postcss-trigonometric-functions": "^4.0.9", "@csstools/postcss-unset-value": "^4.0.0", - "autoprefixer": "^10.4.19", - "browserslist": "^4.24.4", + "autoprefixer": "^10.4.21", + "browserslist": "^4.25.0", "css-blank-pseudo": "^7.0.1", "css-has-pseudo": "^7.0.2", "css-prefers-color-scheme": "^10.0.0", - "cssdb": "^8.2.3", + "cssdb": "^8.3.0", "postcss-attribute-case-insensitive": "^7.0.1", "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^7.0.8", + "postcss-color-functional-notation": "^7.0.10", "postcss-color-hex-alpha": "^10.0.0", "postcss-color-rebeccapurple": "^10.0.0", - "postcss-custom-media": "^11.0.5", - "postcss-custom-properties": "^14.0.4", - "postcss-custom-selectors": "^8.0.4", + "postcss-custom-media": "^11.0.6", + "postcss-custom-properties": "^14.0.6", + "postcss-custom-selectors": "^8.0.5", "postcss-dir-pseudo-class": "^9.0.1", - "postcss-double-position-gradients": "^6.0.0", + "postcss-double-position-gradients": "^6.0.2", "postcss-focus-visible": "^10.0.1", "postcss-focus-within": "^9.0.1", "postcss-font-variant": "^5.0.0", "postcss-gap-properties": "^6.0.0", "postcss-image-set-function": "^7.0.0", - "postcss-lab-function": "^7.0.8", + "postcss-lab-function": "^7.0.10", "postcss-logical": "^8.1.0", - "postcss-nesting": "^13.0.1", + "postcss-nesting": "^13.0.2", "postcss-opacity-percentage": "^3.0.0", "postcss-overflow-shorthand": "^6.0.0", "postcss-page-break": "^3.0.4", @@ -16719,9 +16459,9 @@ } }, "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { @@ -16894,15 +16634,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "license": "MIT", - "dependencies": { - "inherits": "~2.0.3" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -17094,132 +16825,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-dev-utils/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", - "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/react-dev-utils/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dev-utils/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -17233,12 +16838,6 @@ "react": "^18.3.1" } }, - "node_modules/react-error-overlay": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.1.0.tgz", - "integrity": "sha512-SN/U6Ytxf1QGkw/9ve5Y+NxBbZM6Ht95tuXNMKs8EJyFa/Vy/+Co3stop3KBHARfn/giv+Lj1uUnTfOJ3moFEQ==", - "license": "MIT" - }, "node_modules/react-fast-compare": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", @@ -17270,15 +16869,15 @@ "license": "MIT" }, "node_modules/react-json-view-lite": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.5.0.tgz", - "integrity": "sha512-nWqA1E4jKPklL2jvHWs6s+7Na0qNgw9HCP6xehdQJeg6nPBTFZgGwyko9Q0oj+jQWKTTVRS30u0toM5wiuL3iw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-2.4.1.tgz", + "integrity": "sha512-fwFYknRIBxjbFm0kBDrzgBy1xa5tDg2LyXXBepC5f1b+MY3BUClMCsvanMPn089JbV1Eg3nZcrp0VCuH43aXnA==", "license": "MIT", "engines": { - "node": ">=14" + "node": ">=18" }, "peerDependencies": { - "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + "react": "^18.0.0 || ^19.0.0" } }, "node_modules/react-loadable": { @@ -17433,12 +17032,6 @@ "node": ">=8.10.0" } }, - "node_modules/reading-time": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", - "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==", - "license": "MIT" - }, "node_modules/rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -17514,18 +17107,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "license": "MIT", - "dependencies": { - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/redux": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", @@ -18208,6 +17789,12 @@ "loose-envify": "^1.1.0" } }, + "node_modules/schema-dts": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/schema-dts/-/schema-dts-1.1.5.tgz", + "integrity": "sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==", + "license": "Apache-2.0" + }, "node_modules/schema-utils": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", @@ -19472,12 +19059,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "license": "MIT" }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "license": "MIT" - }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -19517,6 +19098,15 @@ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "license": "MIT" }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -19686,6 +19276,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/docs/package.json b/docs/package.json index e13e85ecb3..b1faa4cfcb 100644 --- a/docs/package.json +++ b/docs/package.json @@ -16,8 +16,9 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "~3.7.0", - "@docusaurus/preset-classic": "~3.7.0", + "@docusaurus/core": "~3.8.0", + "@docusaurus/preset-classic": "~3.8.0", + "@docusaurus/theme-common": "~3.8.0", "@mdi/js": "^7.3.67", "@mdi/react": "^1.6.1", "@mdx-js/react": "^3.0.0", @@ -26,6 +27,7 @@ "clsx": "^2.0.0", "docusaurus-lunr-search": "^3.3.2", "docusaurus-preset-openapi": "^0.7.5", + "lunr": "^2.3.9", "postcss": "^8.4.25", "prism-react-renderer": "^2.3.1", "raw-loader": "^4.0.2", @@ -35,7 +37,7 @@ "url": "^0.11.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "~3.7.0", + "@docusaurus/module-type-aliases": "~3.8.0", "@docusaurus/tsconfig": "^3.7.0", "@docusaurus/types": "^3.7.0", "prettier": "^3.2.4", @@ -57,6 +59,6 @@ "node": ">=20" }, "volta": { - "node": "22.16.0" + "node": "22.17.0" } } diff --git a/docs/src/components/community-guides.tsx b/docs/src/components/community-guides.tsx index f5331a9163..49ba7a8a08 100644 --- a/docs/src/components/community-guides.tsx +++ b/docs/src/components/community-guides.tsx @@ -58,6 +58,12 @@ const guides: CommunityGuidesProps[] = [ description: 'Access Immich with an end-to-end encrypted connection.', url: 'https://meshnet.nordvpn.com/how-to/remote-files-media-access/immich-remote-access', }, + { + title: 'Trust Self Signed Certificates with Immich - OAuth Setup', + description: + 'Set up Certificate Authority trust with Immich, and your private OAuth2/OpenID service, while using a private CA for HTTPS commication.', + url: 'https://github.com/immich-app/immich/discussions/18614', + }, ]; function CommunityGuide({ title, description, url }: CommunityGuidesProps): JSX.Element { diff --git a/docs/src/pages/roadmap.tsx b/docs/src/pages/roadmap.tsx index 1258000052..e002c4d032 100644 --- a/docs/src/pages/roadmap.tsx +++ b/docs/src/pages/roadmap.tsx @@ -85,6 +85,7 @@ import React from 'react'; import { Item, Timeline } from '../components/timeline'; const releases = { + 'v1.135.0': new Date(2025, 5, 18), 'v1.133.0': new Date(2025, 4, 21), 'v1.130.0': new Date(2025, 2, 25), 'v1.127.0': new Date(2025, 1, 26), @@ -196,14 +197,6 @@ const roadmap: Item[] = [ description: 'Automate tasks with workflows', getDateLabel: () => 'Planned for 2025', }, - { - done: false, - icon: mdiTableKey, - iconColor: 'gray', - title: 'Fine grained access controls', - description: 'Granular access controls for users and api keys', - getDateLabel: () => 'Planned for 2025', - }, { done: false, icon: mdiImageEdit, @@ -239,12 +232,26 @@ const roadmap: Item[] = [ ]; const milestones: Item[] = [ + { + icon: mdiStar, + iconColor: 'gold', + title: '70,000 Stars', + description: 'Reached 70K Stars on GitHub!', + getDateLabel: withLanguage(new Date(2025, 6, 9)), + }, + withRelease({ + icon: mdiTableKey, + iconColor: 'gray', + title: 'Fine grained access controls', + description: 'Granular access controls for api keys', + release: 'v1.135.0', + }), withRelease({ icon: mdiCast, iconColor: 'aqua', - title: 'Google Cast (web)', + title: 'Google Cast (web and mobile)', description: 'Cast assets to Google Cast/Chromecast compatible devices', - release: 'v1.133.0', + release: 'v1.135.0', }), withRelease({ icon: mdiLockOutline, diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index 31543e7abb..adbd9aa717 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,20 @@ [ + { + "label": "v1.135.3", + "url": "https://v1.135.3.archive.immich.app" + }, + { + "label": "v1.135.2", + "url": "https://v1.135.2.archive.immich.app" + }, + { + "label": "v1.135.1", + "url": "https://v1.135.1.archive.immich.app" + }, + { + "label": "v1.135.0", + "url": "https://v1.135.0.archive.immich.app" + }, { "label": "v1.134.0", "url": "https://v1.134.0.archive.immich.app" diff --git a/e2e/.nvmrc b/e2e/.nvmrc index 5b540673a8..fc37597bcc 100644 --- a/e2e/.nvmrc +++ b/e2e/.nvmrc @@ -1 +1 @@ -22.16.0 +22.17.0 diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 5188a2d017..6ec5402360 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -36,10 +36,10 @@ services: - 2285:2285 redis: - image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa + image: redis:6.2-alpine@sha256:03fd052257735b41cd19f3d8ae9782926bf9b704fb6a9dc5e29f9ccfbe8827f0 database: - image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:9c704fb49ce27549df00f1b096cc93f8b0c959ef087507704d74954808f78a82 + image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:3aef84a0a4fabbda17ef115c3019ba0c914ec73e9f6e59203674322d858b8eea command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf environment: POSTGRES_PASSWORD: postgres diff --git a/e2e/package-lock.json b/e2e/package-lock.json index c49f9a761d..2987223fe0 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.134.0", + "version": "1.135.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.134.0", + "version": "1.135.3", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -15,7 +15,7 @@ "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.44.1", "@types/luxon": "^3.4.2", - "@types/node": "^22.15.29", + "@types/node": "^22.15.33", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", @@ -34,6 +34,7 @@ "pngjs": "^7.0.0", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^4.0.0", + "sharp": "^0.34.0", "socket.io-client": "^4.7.4", "supertest": "^7.0.0", "typescript": "^5.3.3", @@ -44,7 +45,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.68", + "version": "2.2.72", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -55,7 +56,7 @@ "micromatch": "^4.0.8" }, "bin": { - "immich": "dist/index.js" + "immich": "bin/immich" }, "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -66,7 +67,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.29", + "@types/node": "^22.15.33", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -93,14 +94,14 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.134.0", + "version": "1.135.3", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.29", + "@types/node": "^22.15.33", "typescript": "^5.3.3" } }, @@ -178,6 +179,17 @@ "node": ">=18" } }, + "node_modules/@emnapi/runtime": { + "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, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", @@ -646,9 +658,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -661,9 +673,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", - "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -721,9 +733,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.27.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", - "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", + "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", "dev": true, "license": "MIT", "engines": { @@ -823,6 +835,423 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@img/sharp-darwin-arm64": { + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "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.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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", + "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.4.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "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" + ], + "dev": true, + "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.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" + ], + "dev": true, + "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-x64": { + "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" + ], + "dev": true, + "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/@immich/cli": { "resolved": "../cli", "link": true @@ -1080,13 +1509,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", - "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", + "version": "1.53.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz", + "integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.52.0" + "playwright": "1.53.2" }, "bin": { "playwright": "cli.js" @@ -1403,6 +1832,16 @@ "@types/node": "*" } }, + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -1440,6 +1879,13 @@ "@types/node": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", @@ -1549,9 +1995,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", - "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", + "version": "22.15.34", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", + "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", "dev": true, "license": "MIT", "dependencies": { @@ -1559,9 +2005,9 @@ } }, "node_modules/@types/oidc-provider": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@types/oidc-provider/-/oidc-provider-9.1.0.tgz", - "integrity": "sha512-UoC3ZQur+TtVL5hiUN8LoCbXocS2WI2eAPBtZtv1Y5F3vW0QTBawFAgDoctPqCQF73kah/Nzb5Gd3m5GtxFxiA==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/oidc-provider/-/oidc-provider-9.1.1.tgz", + "integrity": "sha512-sG4UcE4AbUwAsEpyrcyoqZ383wJiQObZU+gTa1Iv288+l09HwSr88hBZE2IBLlXS+RKmLId0i4B430PBFO/XRA==", "dev": true, "license": "MIT", "dependencies": { @@ -1571,15 +2017,15 @@ } }, "node_modules/@types/pg": { - "version": "8.15.2", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.2.tgz", - "integrity": "sha512-+BKxo5mM6+/A1soSHBI7ufUglqYXntChLDyTbvcAn1Lawi9J7J9Ok3jt6w7I0+T/UDJ4CyhHk66+GZbwmkYxSg==", + "version": "8.15.4", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.4.tgz", + "integrity": "sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", "pg-protocol": "*", - "pg-types": "^4.0.1" + "pg-types": "^2.2.0" } }, "node_modules/@types/pngjs": { @@ -1654,17 +2100,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "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==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz", + "integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@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", + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/type-utils": "8.35.1", + "@typescript-eslint/utils": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1678,15 +2124,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "@typescript-eslint/parser": "^8.35.1", "eslint": "^8.57.0 || ^9.0.0", "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==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -1694,16 +2140,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", - "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz", + "integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==", "dev": true, "license": "MIT", "dependencies": { - "@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", + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/typescript-estree": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "debug": "^4.3.4" }, "engines": { @@ -1718,15 +2164,37 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", - "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz", + "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.1", - "@typescript-eslint/visitor-keys": "8.32.1" + "@typescript-eslint/tsconfig-utils": "^8.35.1", + "@typescript-eslint/types": "^8.35.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz", + "integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1736,15 +2204,32 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz", + "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/@typescript-eslint/type-utils": { - "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==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz", + "integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.32.1", - "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/typescript-estree": "8.35.1", + "@typescript-eslint/utils": "8.35.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1761,9 +2246,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", - "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz", + "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==", "dev": true, "license": "MIT", "engines": { @@ -1775,14 +2260,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", - "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz", + "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.1", - "@typescript-eslint/visitor-keys": "8.32.1", + "@typescript-eslint/project-service": "8.35.1", + "@typescript-eslint/tsconfig-utils": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1802,9 +2289,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1828,16 +2315,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", - "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz", + "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==", "dev": true, "license": "MIT", "dependencies": { "@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" + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/typescript-estree": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1852,14 +2339,14 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", - "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz", + "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.1", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.35.1", + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1870,15 +2357,16 @@ } }, "node_modules/@vitest/coverage-v8": { - "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==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", + "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^1.0.2", - "debug": "^4.4.0", + "ast-v8-to-istanbul": "^0.3.3", + "debug": "^4.4.1", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", @@ -1893,8 +2381,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.4", - "vitest": "3.1.4" + "@vitest/browser": "3.2.4", + "vitest": "3.2.4" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1903,14 +2391,15 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz", - "integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.4", - "@vitest/utils": "3.1.4", + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -1919,13 +2408,13 @@ } }, "node_modules/@vitest/mocker": { - "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==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.4", + "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -1934,7 +2423,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "peerDependenciesMeta": { "msw": { @@ -1946,9 +2435,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz", - "integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, "license": "MIT", "dependencies": { @@ -1959,27 +2448,28 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz", - "integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.4", - "pathe": "^2.0.3" + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz", - "integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.4", + "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -1988,27 +2478,27 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz", - "integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^3.0.2" + "tinyspy": "^4.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz", - "integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.4", - "loupe": "^3.1.3", + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" }, "funding": { @@ -2037,9 +2527,9 @@ } }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -2164,6 +2654,18 @@ "node": ">=12" } }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", + "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "estree-walker": "^3.0.3", + "js-tokens": "^9.0.1" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2447,6 +2949,20 @@ "node": ">=0.8.0" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2467,6 +2983,17 @@ "dev": true, "license": "MIT" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -2668,9 +3195,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==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2911,19 +3438,19 @@ } }, "node_modules/eslint": { - "version": "9.27.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", - "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", + "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", "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.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.27.0", + "@eslint/js": "9.30.1", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -2935,9 +3462,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2988,14 +3515,14 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz", - "integrity": "sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", + "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", "dev": true, "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.11.0" + "synckit": "^0.11.7" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -3081,9 +3608,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3098,9 +3625,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3111,15 +3638,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3692,9 +4219,9 @@ } }, "node_modules/globals": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", - "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", "dev": true, "license": "MIT", "engines": { @@ -3964,6 +4491,13 @@ "dev": true, "license": "ISC" }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-builtin-module": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", @@ -4110,6 +4644,13 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -4256,9 +4797,9 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", + "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", "dev": true, "license": "MIT" }, @@ -4611,17 +5152,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true, - "license": "MIT" - }, "node_modules/oidc-provider": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.1.3.tgz", - "integrity": "sha512-DaiiCllAr+y33M2HzTRRpV7/jmcZBSfIXaDv0eHURIV85N0O+QUqtjVtPY+viCwHKBRGWwfShWWFKddPUxpAWw==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.2.0.tgz", + "integrity": "sha512-L0JL1ymI/hLKzDRqYzhKluNfRRQUR0++q5fTTziniKmJgNrJ6DnI5h5SP6w8Z0U/3wZrCndpVmbbu0VpKpY0CA==", "dev": true, "license": "MIT", "dependencies": { @@ -4836,23 +5370,23 @@ } }, "node_modules/pg": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.0.tgz", - "integrity": "sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg==", + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "dev": true, "license": "MIT", "dependencies": { - "pg-connection-string": "^2.9.0", - "pg-pool": "^3.10.0", - "pg-protocol": "^1.10.0", + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 16.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.2.5" + "pg-cloudflare": "^1.2.7" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -4864,17 +5398,17 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz", - "integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", "dev": true, "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.0.tgz", - "integrity": "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", "dev": true, "license": "MIT" }, @@ -4888,20 +5422,10 @@ "node": ">=4.0.0" } }, - "node_modules/pg-numeric": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", - "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=4" - } - }, "node_modules/pg-pool": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.0.tgz", - "integrity": "sha512-DzZ26On4sQ0KmqnO34muPcmKbhrjmyiO4lCCR0VwEd7MjmiKf5NTg/6+apUEu0NF7ESa37CGzFxH513CoUmWnA==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -4909,32 +5433,13 @@ } }, "node_modules/pg-protocol": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.0.tgz", - "integrity": "sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", "dev": true, "license": "MIT" }, "node_modules/pg-types": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", - "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", - "dev": true, - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "pg-numeric": "1.0.2", - "postgres-array": "~3.0.1", - "postgres-bytea": "~3.0.0", - "postgres-date": "~2.1.0", - "postgres-interval": "^3.0.0", - "postgres-range": "^1.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pg/node_modules/pg-types": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", @@ -4951,49 +5456,6 @@ "node": ">=4" } }, - "node_modules/pg/node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/pg/node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pg/node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pg/node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pgpass": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", @@ -5025,13 +5487,13 @@ } }, "node_modules/playwright": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", - "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", + "version": "1.53.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz", + "integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.52.0" + "playwright-core": "1.53.2" }, "bin": { "playwright": "cli.js" @@ -5044,9 +5506,9 @@ } }, "node_modules/playwright-core": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", - "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", + "version": "1.53.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz", + "integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5125,55 +5587,48 @@ } }, "node_modules/postgres-array": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", - "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=4" } }, "node_modules/postgres-bytea": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", - "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", "dev": true, "license": "MIT", - "dependencies": { - "obuf": "~1.1.2" - }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, "node_modules/postgres-date": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", - "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, "node_modules/postgres-interval": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", - "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", "dev": true, "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/postgres-range": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", - "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", - "dev": true, - "license": "MIT" - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5185,9 +5640,9 @@ } }, "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { @@ -5510,9 +5965,9 @@ "license": "MIT" }, "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==", "dev": true, "license": "ISC", "bin": { @@ -5536,6 +5991,48 @@ "dev": true, "license": "ISC" }, + "node_modules/sharp": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", + "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.4", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@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": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5655,6 +6152,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/socket.io-client": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", @@ -5908,6 +6415,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/superagent": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.1.tgz", @@ -5957,14 +6477,13 @@ } }, "node_modules/synckit": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz", - "integrity": "sha512-Q/XQKRaJiLiFIBNN+mndW7S/RHxvwzuZS6ZwmRzUBqJBv/5QIKCEwkBC8GBf8EQJKYnaFs0wOZbKTXBPj8L9oQ==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", "dev": true, "license": "MIT", "dependencies": { - "@pkgr/core": "^0.2.3", - "tslib": "^2.8.1" + "@pkgr/core": "^0.2.4" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -6102,9 +6621,9 @@ } }, "node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", "engines": { @@ -6122,9 +6641,9 @@ } }, "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", "dev": true, "license": "MIT", "engines": { @@ -6179,7 +6698,8 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "0BSD" + "license": "0BSD", + "optional": true }, "node_modules/tsscmp": { "version": "1.0.6", @@ -6257,15 +6777,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz", - "integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.1.tgz", + "integrity": "sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.32.1", - "@typescript-eslint/parser": "8.32.1", - "@typescript-eslint/utils": "8.32.1" + "@typescript-eslint/eslint-plugin": "8.35.1", + "@typescript-eslint/parser": "8.35.1", + "@typescript-eslint/utils": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6445,17 +6965,17 @@ } }, "node_modules/vite-node": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz", - "integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.4.0", + "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" @@ -6511,32 +7031,34 @@ } }, "node_modules/vitest": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz", - "integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", "dependencies": { - "@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", + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", "chai": "^5.2.0", - "debug": "^4.4.0", + "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", + "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.13", - "tinypool": "^1.0.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.4", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -6552,8 +7074,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.4", - "@vitest/ui": "3.1.4", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, @@ -6581,6 +7103,19 @@ } } }, + "node_modules/vitest/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/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/e2e/package.json b/e2e/package.json index 9c69199f65..4669ca0a1a 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.134.0", + "version": "1.135.3", "description": "", "main": "index.js", "type": "module", @@ -25,7 +25,7 @@ "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.44.1", "@types/luxon": "^3.4.2", - "@types/node": "^22.15.29", + "@types/node": "^22.15.33", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", @@ -44,6 +44,7 @@ "pngjs": "^7.0.0", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^4.0.0", + "sharp": "^0.34.0", "socket.io-client": "^4.7.4", "supertest": "^7.0.0", "typescript": "^5.3.3", @@ -52,6 +53,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.16.0" + "node": "22.17.0" } } diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index 4673db5426..c1e9f9dfb8 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -15,6 +15,7 @@ import { DateTime } from 'luxon'; import { randomBytes } from 'node:crypto'; import { readFile, writeFile } from 'node:fs/promises'; import { basename, join } from 'node:path'; +import sharp from 'sharp'; import { Socket } from 'socket.io-client'; import { createUserDto, uuidDto } from 'src/fixtures'; import { makeRandomImage } from 'src/generators'; @@ -40,6 +41,40 @@ const today = DateTime.fromObject({ }) as DateTime; const yesterday = today.minus({ days: 1 }); +const createTestImageWithExif = async (filename: string, exifData: Record) => { + // Generate unique color to ensure different checksums for each image + const r = Math.floor(Math.random() * 256); + const g = Math.floor(Math.random() * 256); + const b = Math.floor(Math.random() * 256); + + // Create a 100x100 solid color JPEG using Sharp + const imageBytes = await sharp({ + create: { + width: 100, + height: 100, + channels: 3, + background: { r, g, b }, + }, + }) + .jpeg({ quality: 90 }) + .toBuffer(); + + // Add random suffix to filename to avoid collisions + const uniqueFilename = filename.replace('.jpg', `-${randomBytes(4).toString('hex')}.jpg`); + const filepath = join(tempDir, uniqueFilename); + await writeFile(filepath, imageBytes); + + // Filter out undefined values before writing EXIF + const cleanExifData = Object.fromEntries(Object.entries(exifData).filter(([, value]) => value !== undefined)); + + await exiftool.write(filepath, cleanExifData); + + // Re-read the image bytes after EXIF has been written + const finalImageBytes = await readFile(filepath); + + return { filepath, imageBytes: finalImageBytes, filename: uniqueFilename }; +}; + describe('/asset', () => { let admin: LoginResponseDto; let websocket: Socket; @@ -1190,6 +1225,411 @@ describe('/asset', () => { }); }); + describe('EXIF metadata extraction', () => { + describe('Additional date tag extraction', () => { + describe('Date-time vs time-only tag handling', () => { + it('should fall back to file timestamps when only time-only tags are available', async () => { + const { imageBytes, filename } = await createTestImageWithExif('time-only-fallback.jpg', { + TimeCreated: '2023:11:15 14:30:00', // Time-only tag, should not be used for dateTimeOriginal + // Exclude all date-time tags to force fallback to file timestamps + SubSecDateTimeOriginal: undefined, + DateTimeOriginal: undefined, + SubSecCreateDate: undefined, + SubSecMediaCreateDate: undefined, + CreateDate: undefined, + MediaCreateDate: undefined, + CreationDate: undefined, + DateTimeCreated: undefined, + GPSDateTime: undefined, + DateTimeUTC: undefined, + SonyDateTime2: undefined, + GPSDateStamp: undefined, + }); + + const oldDate = new Date('2020-01-01T00:00:00.000Z'); + const asset = await utils.createAsset(admin.accessToken, { + assetData: { + filename, + bytes: imageBytes, + }, + fileCreatedAt: oldDate.toISOString(), + fileModifiedAt: oldDate.toISOString(), + }); + + await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id }); + + const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) }); + + expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined(); + // Should fall back to file timestamps, which we set to 2020-01-01 + expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe( + new Date('2020-01-01T00:00:00.000Z').getTime(), + ); + }); + + it('should prefer DateTimeOriginal over time-only tags', async () => { + const { imageBytes, filename } = await createTestImageWithExif('datetime-over-time.jpg', { + DateTimeOriginal: '2023:10:10 10:00:00', // Should be preferred + TimeCreated: '2023:11:15 14:30:00', // Should be ignored (time-only) + }); + + const asset = await utils.createAsset(admin.accessToken, { + assetData: { + filename, + bytes: imageBytes, + }, + }); + + await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id }); + + const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) }); + + expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined(); + // Should use DateTimeOriginal, not TimeCreated + expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe( + new Date('2023-10-10T10:00:00.000Z').getTime(), + ); + }); + }); + + describe('GPSDateTime tag extraction', () => { + it('should extract GPSDateTime with GPS coordinates', async () => { + const { imageBytes, filename } = await createTestImageWithExif('gps-datetime.jpg', { + GPSDateTime: '2023:11:15 12:30:00Z', + GPSLatitude: 37.7749, + GPSLongitude: -122.4194, + // Exclude other date tags + SubSecDateTimeOriginal: undefined, + DateTimeOriginal: undefined, + SubSecCreateDate: undefined, + SubSecMediaCreateDate: undefined, + CreateDate: undefined, + MediaCreateDate: undefined, + CreationDate: undefined, + DateTimeCreated: undefined, + TimeCreated: undefined, + }); + + const asset = await utils.createAsset(admin.accessToken, { + assetData: { + filename, + bytes: imageBytes, + }, + }); + + await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id }); + + const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) }); + + expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined(); + expect(assetInfo.exifInfo?.latitude).toBeCloseTo(37.7749, 4); + expect(assetInfo.exifInfo?.longitude).toBeCloseTo(-122.4194, 4); + expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe( + new Date('2023-11-15T12:30:00.000Z').getTime(), + ); + }); + }); + + describe('CreateDate tag extraction', () => { + it('should extract CreateDate when available', async () => { + const { imageBytes, filename } = await createTestImageWithExif('create-date.jpg', { + CreateDate: '2023:11:15 10:30:00', + // Exclude other higher priority date tags + SubSecDateTimeOriginal: undefined, + DateTimeOriginal: undefined, + SubSecCreateDate: undefined, + SubSecMediaCreateDate: undefined, + MediaCreateDate: undefined, + CreationDate: undefined, + DateTimeCreated: undefined, + TimeCreated: undefined, + GPSDateTime: undefined, + }); + + const asset = await utils.createAsset(admin.accessToken, { + assetData: { + filename, + bytes: imageBytes, + }, + }); + + await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id }); + + const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) }); + + expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined(); + expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe( + new Date('2023-11-15T10:30:00.000Z').getTime(), + ); + }); + }); + + describe('GPSDateStamp tag extraction', () => { + it('should fall back to file timestamps when only date-only tags are available', async () => { + const { imageBytes, filename } = await createTestImageWithExif('gps-datestamp.jpg', { + GPSDateStamp: '2023:11:15', // Date-only tag, should not be used for dateTimeOriginal + // Note: NOT including GPSTimeStamp to avoid automatic GPSDateTime creation + GPSLatitude: 51.5074, + GPSLongitude: -0.1278, + // Explicitly exclude all testable date-time tags to force fallback to file timestamps + DateTimeOriginal: undefined, + CreateDate: undefined, + CreationDate: undefined, + GPSDateTime: undefined, + }); + + const oldDate = new Date('2020-01-01T00:00:00.000Z'); + const asset = await utils.createAsset(admin.accessToken, { + assetData: { + filename, + bytes: imageBytes, + }, + fileCreatedAt: oldDate.toISOString(), + fileModifiedAt: oldDate.toISOString(), + }); + + await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id }); + + const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) }); + + expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined(); + expect(assetInfo.exifInfo?.latitude).toBeCloseTo(51.5074, 4); + expect(assetInfo.exifInfo?.longitude).toBeCloseTo(-0.1278, 4); + // Should fall back to file timestamps, which we set to 2020-01-01 + expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe( + new Date('2020-01-01T00:00:00.000Z').getTime(), + ); + }); + }); + + /* + * NOTE: The following EXIF date tags are NOT effectively usable with JPEG test files: + * + * NOT WRITABLE to JPEG: + * - MediaCreateDate: Can be read from video files but not written to JPEG + * - DateTimeCreated: Read-only tag in JPEG format + * - DateTimeUTC: Cannot be written to JPEG files + * - SonyDateTime2: Proprietary Sony tag, not writable to JPEG + * - SubSecMediaCreateDate: Tag not defined for JPEG format + * - SourceImageCreateTime: Non-standard insta360 tag, not writable to JPEG + * + * WRITABLE but NOT READABLE from JPEG: + * - SubSecDateTimeOriginal: Can be written but not read back from JPEG + * - SubSecCreateDate: Can be written but not read back from JPEG + * + * EFFECTIVELY TESTABLE TAGS (writable and readable): + * - DateTimeOriginal ✓ + * - CreateDate ✓ + * - CreationDate ✓ + * - GPSDateTime ✓ + * + * The metadata service correctly handles non-readable tags and will fall back to + * file timestamps when only non-readable tags are present. + */ + + describe('Date tag priority order', () => { + it('should respect the complete date tag priority order', async () => { + // Test cases using only EFFECTIVELY TESTABLE tags (writable AND readable from JPEG) + const testCases = [ + { + name: 'DateTimeOriginal has highest priority among testable tags', + exifData: { + DateTimeOriginal: '2023:04:04 04:00:00', // TESTABLE - highest priority among readable tags + CreateDate: '2023:05:05 05:00:00', // TESTABLE + CreationDate: '2023:07:07 07:00:00', // TESTABLE + GPSDateTime: '2023:10:10 10:00:00', // TESTABLE + }, + expectedDate: '2023-04-04T04:00:00.000Z', + }, + { + name: 'CreateDate when DateTimeOriginal missing', + exifData: { + CreateDate: '2023:05:05 05:00:00', // TESTABLE + CreationDate: '2023:07:07 07:00:00', // TESTABLE + GPSDateTime: '2023:10:10 10:00:00', // TESTABLE + }, + expectedDate: '2023-05-05T05:00:00.000Z', + }, + { + name: 'CreationDate when standard EXIF tags missing', + exifData: { + CreationDate: '2023:07:07 07:00:00', // TESTABLE + GPSDateTime: '2023:10:10 10:00:00', // TESTABLE + }, + expectedDate: '2023-07-07T07:00:00.000Z', + }, + { + name: 'GPSDateTime when no other testable date tags present', + exifData: { + GPSDateTime: '2023:10:10 10:00:00', // TESTABLE + Make: 'SONY', + }, + expectedDate: '2023-10-10T10:00:00.000Z', + }, + ]; + + for (const testCase of testCases) { + const { imageBytes, filename } = await createTestImageWithExif( + `${testCase.name.replaceAll(/\s+/g, '-').toLowerCase()}.jpg`, + testCase.exifData, + ); + + const asset = await utils.createAsset(admin.accessToken, { + assetData: { + filename, + bytes: imageBytes, + }, + }); + + await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id }); + + const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) }); + + expect(assetInfo.exifInfo?.dateTimeOriginal, `Failed for: ${testCase.name}`).toBeDefined(); + expect( + new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime(), + `Date mismatch for: ${testCase.name}`, + ).toBe(new Date(testCase.expectedDate).getTime()); + } + }); + }); + + describe('Edge cases for date tag handling', () => { + it('should fall back to file timestamps with GPSDateStamp alone', async () => { + const { imageBytes, filename } = await createTestImageWithExif('gps-datestamp-only.jpg', { + GPSDateStamp: '2023:08:08', // Date-only tag, should not be used for dateTimeOriginal + // Intentionally no GPSTimeStamp + // Exclude all other date tags + SubSecDateTimeOriginal: undefined, + DateTimeOriginal: undefined, + SubSecCreateDate: undefined, + SubSecMediaCreateDate: undefined, + CreateDate: undefined, + MediaCreateDate: undefined, + CreationDate: undefined, + DateTimeCreated: undefined, + TimeCreated: undefined, + GPSDateTime: undefined, + DateTimeUTC: undefined, + }); + + const oldDate = new Date('2020-01-01T00:00:00.000Z'); + const asset = await utils.createAsset(admin.accessToken, { + assetData: { + filename, + bytes: imageBytes, + }, + fileCreatedAt: oldDate.toISOString(), + fileModifiedAt: oldDate.toISOString(), + }); + + await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id }); + + const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) }); + + expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined(); + // Should fall back to file timestamps, which we set to 2020-01-01 + expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe( + new Date('2020-01-01T00:00:00.000Z').getTime(), + ); + }); + + it('should handle all testable date tags present to verify complete priority order', async () => { + const { imageBytes, filename } = await createTestImageWithExif('all-testable-date-tags.jpg', { + // All TESTABLE date tags to JPEG format (writable AND readable) + DateTimeOriginal: '2023:04:04 04:00:00', // TESTABLE - highest priority among readable tags + CreateDate: '2023:05:05 05:00:00', // TESTABLE + CreationDate: '2023:07:07 07:00:00', // TESTABLE + GPSDateTime: '2023:10:10 10:00:00', // TESTABLE + // Note: Excluded non-testable tags: + // SubSec tags: writable but not readable from JPEG + // Non-writable tags: MediaCreateDate, DateTimeCreated, DateTimeUTC, SonyDateTime2, etc. + // Time-only/date-only tags: already excluded from EXIF_DATE_TAGS + }); + + const asset = await utils.createAsset(admin.accessToken, { + assetData: { + filename, + bytes: imageBytes, + }, + }); + + await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id }); + + const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) }); + + expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined(); + // Should use DateTimeOriginal as it has the highest priority among testable tags + expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe( + new Date('2023-04-04T04:00:00.000Z').getTime(), + ); + }); + + it('should use CreationDate when SubSec tags are missing', async () => { + const { imageBytes, filename } = await createTestImageWithExif('creation-date-priority.jpg', { + CreationDate: '2023:07:07 07:00:00', // WRITABLE + GPSDateTime: '2023:10:10 10:00:00', // WRITABLE + // Note: DateTimeCreated, DateTimeUTC, SonyDateTime2 are NOT writable to JPEG + // Note: TimeCreated and GPSDateStamp are excluded from EXIF_DATE_TAGS (time-only/date-only) + // Exclude SubSec and standard EXIF tags + SubSecDateTimeOriginal: undefined, + DateTimeOriginal: undefined, + SubSecCreateDate: undefined, + CreateDate: undefined, + }); + + const asset = await utils.createAsset(admin.accessToken, { + assetData: { + filename, + bytes: imageBytes, + }, + }); + + await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id }); + + const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) }); + + expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined(); + // Should use CreationDate when available + expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe( + new Date('2023-07-07T07:00:00.000Z').getTime(), + ); + }); + + it('should skip invalid date formats and use next valid tag', async () => { + const { imageBytes, filename } = await createTestImageWithExif('invalid-date-handling.jpg', { + // Note: Testing invalid date handling with only WRITABLE tags + GPSDateTime: '2023:10:10 10:00:00', // WRITABLE - Valid date + CreationDate: '2023:13:13 13:00:00', // WRITABLE - Valid date + // Note: TimeCreated excluded (time-only), DateTimeCreated not writable to JPEG + // Exclude other date tags + SubSecDateTimeOriginal: undefined, + DateTimeOriginal: undefined, + SubSecCreateDate: undefined, + CreateDate: undefined, + }); + + const asset = await utils.createAsset(admin.accessToken, { + assetData: { + filename, + bytes: imageBytes, + }, + }); + + await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id }); + + const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) }); + + expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined(); + // Should skip invalid dates and use the first valid one (GPSDateTime) + expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe( + new Date('2023-10-10T10:00:00.000Z').getTime(), + ); + }); + }); + }); + }); + describe('POST /assets/exist', () => { it('ignores invalid deviceAssetIds', async () => { const response = await utils.checkExistingAssets(user1.accessToken, { diff --git a/e2e/src/api/specs/auth.e2e-spec.ts b/e2e/src/api/specs/auth.e2e-spec.ts deleted file mode 100644 index 0f407f4ba7..0000000000 --- a/e2e/src/api/specs/auth.e2e-spec.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { LoginResponseDto, login, signUpAdmin } from '@immich/sdk'; -import { loginDto, signupDto } from 'src/fixtures'; -import { errorDto, loginResponseDto, signupResponseDto } from 'src/responses'; -import { app, utils } from 'src/utils'; -import request from 'supertest'; -import { beforeEach, describe, expect, it } from 'vitest'; - -const { email, password } = signupDto.admin; - -describe(`/auth/admin-sign-up`, () => { - beforeEach(async () => { - await utils.resetDatabase(); - }); - - describe('POST /auth/admin-sign-up', () => { - it(`should sign up the admin`, async () => { - const { status, body } = await request(app).post('/auth/admin-sign-up').send(signupDto.admin); - expect(status).toBe(201); - expect(body).toEqual(signupResponseDto.admin); - }); - - it('should not allow a second admin to sign up', async () => { - await signUpAdmin({ signUpDto: signupDto.admin }); - - const { status, body } = await request(app).post('/auth/admin-sign-up').send(signupDto.admin); - - expect(status).toBe(400); - expect(body).toEqual(errorDto.alreadyHasAdmin); - }); - }); -}); - -describe('/auth/*', () => { - let admin: LoginResponseDto; - - beforeEach(async () => { - await utils.resetDatabase(); - await signUpAdmin({ signUpDto: signupDto.admin }); - admin = await login({ loginCredentialDto: loginDto.admin }); - }); - - describe(`POST /auth/login`, () => { - it('should reject an incorrect password', async () => { - const { status, body } = await request(app).post('/auth/login').send({ email, password: 'incorrect' }); - expect(status).toBe(401); - expect(body).toEqual(errorDto.incorrectLogin); - }); - - it('should accept a correct password', async () => { - const { status, body, headers } = await request(app).post('/auth/login').send({ email, password }); - expect(status).toBe(201); - expect(body).toEqual(loginResponseDto.admin); - - const token = body.accessToken; - expect(token).toBeDefined(); - - const cookies = headers['set-cookie']; - expect(cookies).toHaveLength(3); - expect(cookies[0].split(';').map((item) => item.trim())).toEqual([ - `immich_access_token=${token}`, - 'Max-Age=34560000', - 'Path=/', - expect.stringContaining('Expires='), - 'HttpOnly', - 'SameSite=Lax', - ]); - expect(cookies[1].split(';').map((item) => item.trim())).toEqual([ - 'immich_auth_type=password', - 'Max-Age=34560000', - 'Path=/', - expect.stringContaining('Expires='), - 'HttpOnly', - 'SameSite=Lax', - ]); - expect(cookies[2].split(';').map((item) => item.trim())).toEqual([ - 'immich_is_authenticated=true', - 'Max-Age=34560000', - 'Path=/', - expect.stringContaining('Expires='), - 'SameSite=Lax', - ]); - }); - }); - - describe('POST /auth/validateToken', () => { - it('should reject an invalid token', async () => { - const { status, body } = await request(app).post(`/auth/validateToken`).set('Authorization', 'Bearer 123'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.invalidToken); - }); - - it('should accept a valid token', async () => { - const { status, body } = await request(app) - .post(`/auth/validateToken`) - .send({}) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(200); - expect(body).toEqual({ authStatus: true }); - }); - }); - - describe('POST /auth/change-password', () => { - it('should require the current password', async () => { - const { status, body } = await request(app) - .post(`/auth/change-password`) - .send({ password: 'wrong-password', newPassword: 'Password1234' }) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorDto.wrongPassword); - }); - - it('should change the password', async () => { - const { status } = await request(app) - .post(`/auth/change-password`) - .send({ password, newPassword: 'Password1234' }) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(200); - - await login({ - loginCredentialDto: { - email: 'admin@immich.cloud', - password: 'Password1234', - }, - }); - }); - }); - - describe('POST /auth/logout', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).post(`/auth/logout`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should logout the user', async () => { - const { status, body } = await request(app) - .post(`/auth/logout`) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(200); - expect(body).toEqual({ - successful: true, - redirectUri: '/auth/login?autoLaunch=0', - }); - }); - }); -}); diff --git a/e2e/src/api/specs/memory.e2e-spec.ts b/e2e/src/api/specs/memory.e2e-spec.ts index d91a570f77..e5e2351738 100644 --- a/e2e/src/api/specs/memory.e2e-spec.ts +++ b/e2e/src/api/specs/memory.e2e-spec.ts @@ -6,7 +6,7 @@ import { createMemory, getMemory, } 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'; @@ -17,7 +17,6 @@ describe('/memories', () => { let user: LoginResponseDto; let adminAsset: AssetMediaResponseDto; let userAsset1: AssetMediaResponseDto; - let userAsset2: AssetMediaResponseDto; let userMemory: MemoryResponseDto; beforeAll(async () => { @@ -25,10 +24,9 @@ describe('/memories', () => { admin = await utils.adminSetup(); user = await utils.userSetup(admin.accessToken, createUserDto.user1); - [adminAsset, userAsset1, userAsset2] = await Promise.all([ + [adminAsset, userAsset1] = await Promise.all([ utils.createAsset(admin.accessToken), utils.createAsset(user.accessToken), - utils.createAsset(user.accessToken), ]); userMemory = await createMemory( { @@ -43,121 +41,7 @@ describe('/memories', () => { ); }); - describe('GET /memories', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get('/memories'); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - }); - - describe('POST /memories', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).post('/memories'); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should validate data when type is on this day', async () => { - const { status, body } = await request(app) - .post('/memories') - .set('Authorization', `Bearer ${user.accessToken}`) - .send({ - type: 'on_this_day', - data: {}, - memoryAt: new Date(2021).toISOString(), - }); - - expect(status).toBe(400); - expect(body).toEqual( - errorDto.badRequest(['data.year must be a positive number', 'data.year must be an integer number']), - ); - }); - - it('should create a new memory', async () => { - const { status, body } = await request(app) - .post('/memories') - .set('Authorization', `Bearer ${user.accessToken}`) - .send({ - type: 'on_this_day', - data: { year: 2021 }, - memoryAt: new Date(2021).toISOString(), - }); - - expect(status).toBe(201); - expect(body).toEqual({ - id: expect.any(String), - type: 'on_this_day', - data: { year: 2021 }, - createdAt: expect.any(String), - updatedAt: expect.any(String), - isSaved: false, - memoryAt: expect.any(String), - ownerId: user.userId, - assets: [], - }); - }); - - it('should create a new memory (with assets)', async () => { - const { status, body } = await request(app) - .post('/memories') - .set('Authorization', `Bearer ${user.accessToken}`) - .send({ - type: 'on_this_day', - data: { year: 2021 }, - memoryAt: new Date(2021).toISOString(), - assetIds: [userAsset1.id, userAsset2.id], - }); - - expect(status).toBe(201); - expect(body).toMatchObject({ - id: expect.any(String), - assets: expect.arrayContaining([ - expect.objectContaining({ id: userAsset1.id }), - expect.objectContaining({ id: userAsset2.id }), - ]), - }); - expect(body.assets).toHaveLength(2); - }); - - it('should create a new memory and ignore assets the user does not have access to', async () => { - const { status, body } = await request(app) - .post('/memories') - .set('Authorization', `Bearer ${user.accessToken}`) - .send({ - type: 'on_this_day', - data: { year: 2021 }, - memoryAt: new Date(2021).toISOString(), - assetIds: [userAsset1.id, adminAsset.id], - }); - - expect(status).toBe(201); - expect(body).toMatchObject({ - id: expect.any(String), - assets: [expect.objectContaining({ id: userAsset1.id })], - }); - expect(body.assets).toHaveLength(1); - }); - }); - describe('GET /memories/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get(`/memories/${uuidDto.invalid}`); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should require a valid id', async () => { - const { status, body } = await request(app) - .get(`/memories/${uuidDto.invalid}`) - .set('Authorization', `Bearer ${user.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(`/memories/${userMemory.id}`) @@ -176,22 +60,6 @@ describe('/memories', () => { }); describe('PUT /memories/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).put(`/memories/${uuidDto.invalid}`).send({ isSaved: true }); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should require a valid id', async () => { - const { status, body } = await request(app) - .put(`/memories/${uuidDto.invalid}`) - .send({ isSaved: true }) - .set('Authorization', `Bearer ${user.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(`/memories/${userMemory.id}`) @@ -218,23 +86,6 @@ describe('/memories', () => { }); describe('PUT /memories/:id/assets', () => { - it('should require authentication', async () => { - const { status, body } = await request(app) - .put(`/memories/${userMemory.id}/assets`) - .send({ ids: [userAsset1.id] }); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should require a valid id', async () => { - const { status, body } = await request(app) - .put(`/memories/${uuidDto.invalid}/assets`) - .send({ ids: [userAsset1.id] }) - .set('Authorization', `Bearer ${user.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(`/memories/${userMemory.id}/assets`) @@ -244,15 +95,6 @@ describe('/memories', () => { expect(body).toEqual(errorDto.noPermission); }); - it('should require a valid asset id', async () => { - const { status, body } = await request(app) - .put(`/memories/${userMemory.id}/assets`) - .send({ ids: [uuidDto.invalid] }) - .set('Authorization', `Bearer ${user.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(['each value in ids must be a UUID'])); - }); - it('should require asset access', async () => { const { status, body } = await request(app) .put(`/memories/${userMemory.id}/assets`) @@ -279,23 +121,6 @@ describe('/memories', () => { }); describe('DELETE /memories/:id/assets', () => { - it('should require authentication', async () => { - const { status, body } = await request(app) - .delete(`/memories/${userMemory.id}/assets`) - .send({ ids: [userAsset1.id] }); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should require a valid id', async () => { - const { status, body } = await request(app) - .delete(`/memories/${uuidDto.invalid}/assets`) - .send({ ids: [userAsset1.id] }) - .set('Authorization', `Bearer ${user.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) .delete(`/memories/${userMemory.id}/assets`) @@ -305,15 +130,6 @@ describe('/memories', () => { expect(body).toEqual(errorDto.noPermission); }); - it('should require a valid asset id', async () => { - const { status, body } = await request(app) - .delete(`/memories/${userMemory.id}/assets`) - .send({ ids: [uuidDto.invalid] }) - .set('Authorization', `Bearer ${user.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(['each value in ids must be a UUID'])); - }); - it('should only remove assets in the memory', async () => { const { status, body } = await request(app) .delete(`/memories/${userMemory.id}/assets`) @@ -340,21 +156,6 @@ describe('/memories', () => { }); describe('DELETE /memories/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).delete(`/memories/${uuidDto.invalid}`); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should require a valid id', async () => { - const { status, body } = await request(app) - .delete(`/memories/${uuidDto.invalid}`) - .set('Authorization', `Bearer ${user.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) .delete(`/memories/${userMemory.id}`) diff --git a/e2e/src/api/specs/oauth.e2e-spec.ts b/e2e/src/api/specs/oauth.e2e-spec.ts index 9e4d64892e..58fc43a2d5 100644 --- a/e2e/src/api/specs/oauth.e2e-spec.ts +++ b/e2e/src/api/specs/oauth.e2e-spec.ts @@ -227,6 +227,21 @@ describe(`/oauth`, () => { expect(user.storageLabel).toBe('user-username'); }); + it('should set the admin status from a role claim', async () => { + const callbackParams = await loginWithOAuth(OAuthUser.WITH_ROLE); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); + expect(status).toBe(201); + expect(body).toMatchObject({ + accessToken: expect.any(String), + userId: expect.any(String), + userEmail: 'oauth-with-role@immich.app', + isAdmin: true, + }); + + const user = await getMyUser({ headers: asBearerAuth(body.accessToken) }); + expect(user.isAdmin).toBe(true); + }); + it('should work with RS256 signed tokens', async () => { await setupOAuth(admin.accessToken, { enabled: true, diff --git a/e2e/src/api/specs/person.e2e-spec.ts b/e2e/src/api/specs/person.e2e-spec.ts index 1826002af6..20290cd941 100644 --- a/e2e/src/api/specs/person.e2e-spec.ts +++ b/e2e/src/api/specs/person.e2e-spec.ts @@ -11,11 +11,32 @@ describe('/people', () => { let hiddenPerson: PersonResponseDto; let multipleAssetsPerson: PersonResponseDto; + let nameAlicePerson: PersonResponseDto; + let nameBobPerson: PersonResponseDto; + let nameCharliePerson: PersonResponseDto; + let nameNullPerson4Assets: PersonResponseDto; + let nameNullPerson3Assets: PersonResponseDto; + let nameNullPerson1Asset: PersonResponseDto; + let nameBillPersonFavourite: PersonResponseDto; + let nameFreddyPersonFavourite: PersonResponseDto; + beforeAll(async () => { await utils.resetDatabase(); admin = await utils.adminSetup(); - [visiblePerson, hiddenPerson, multipleAssetsPerson] = await Promise.all([ + [ + visiblePerson, + hiddenPerson, + multipleAssetsPerson, + nameCharliePerson, + nameBobPerson, + nameAlicePerson, + nameNullPerson4Assets, + nameNullPerson3Assets, + nameNullPerson1Asset, + nameBillPersonFavourite, + nameFreddyPersonFavourite, + ] = await Promise.all([ utils.createPerson(admin.accessToken, { name: 'visible_person', }), @@ -26,10 +47,39 @@ describe('/people', () => { utils.createPerson(admin.accessToken, { name: 'multiple_assets_person', }), + // --- Setup for the specific sorting test --- + utils.createPerson(admin.accessToken, { + name: 'Charlie', + }), + utils.createPerson(admin.accessToken, { + name: 'Bob', + }), + utils.createPerson(admin.accessToken, { + name: 'Alice', + }), + utils.createPerson(admin.accessToken, { + name: '', + }), + utils.createPerson(admin.accessToken, { + name: '', + }), + utils.createPerson(admin.accessToken, { + name: '', + }), + utils.createPerson(admin.accessToken, { + name: 'Bill', + isFavorite: true, + }), + utils.createPerson(admin.accessToken, { + name: 'Freddy', + isFavorite: true, + }), ]); const asset1 = await utils.createAsset(admin.accessToken); const asset2 = await utils.createAsset(admin.accessToken); + const asset3 = await utils.createAsset(admin.accessToken); + const asset4 = await utils.createAsset(admin.accessToken); await Promise.all([ utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }), @@ -37,6 +87,27 @@ describe('/people', () => { utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }), utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }), utils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }), + utils.createFace({ assetId: asset3.id, personId: multipleAssetsPerson.id }), // 4 assets + // Named persons + utils.createFace({ assetId: asset1.id, personId: nameCharliePerson.id }), // 1 asset + utils.createFace({ assetId: asset1.id, personId: nameBobPerson.id }), + utils.createFace({ assetId: asset2.id, personId: nameBobPerson.id }), // 2 assets + utils.createFace({ assetId: asset1.id, personId: nameAlicePerson.id }), // 1 asset + // Null-named person 4 assets + utils.createFace({ assetId: asset1.id, personId: nameNullPerson4Assets.id }), + utils.createFace({ assetId: asset2.id, personId: nameNullPerson4Assets.id }), + utils.createFace({ assetId: asset3.id, personId: nameNullPerson4Assets.id }), + utils.createFace({ assetId: asset4.id, personId: nameNullPerson4Assets.id }), // 4 assets + // Null-named person 3 assets + utils.createFace({ assetId: asset1.id, personId: nameNullPerson3Assets.id }), + utils.createFace({ assetId: asset2.id, personId: nameNullPerson3Assets.id }), + utils.createFace({ assetId: asset3.id, personId: nameNullPerson3Assets.id }), // 3 assets + // Null-named person 1 asset + utils.createFace({ assetId: asset3.id, personId: nameNullPerson1Asset.id }), + // Favourite People + utils.createFace({ assetId: asset1.id, personId: nameFreddyPersonFavourite.id }), + utils.createFace({ assetId: asset2.id, personId: nameFreddyPersonFavourite.id }), + utils.createFace({ assetId: asset1.id, personId: nameBillPersonFavourite.id }), ]); }); @@ -51,27 +122,66 @@ describe('/people', () => { expect(status).toBe(200); expect(body).toEqual({ hasNextPage: false, - total: 3, + total: 11, hidden: 1, people: [ + expect.objectContaining({ name: 'Freddy' }), + expect.objectContaining({ name: 'Bill' }), expect.objectContaining({ name: 'multiple_assets_person' }), + expect.objectContaining({ name: 'Bob' }), + expect.objectContaining({ name: 'Alice' }), + expect.objectContaining({ name: 'Charlie' }), expect.objectContaining({ name: 'visible_person' }), - expect.objectContaining({ name: 'hidden_person' }), + expect.objectContaining({ id: nameNullPerson4Assets.id, name: '' }), + expect.objectContaining({ id: nameNullPerson3Assets.id, name: '' }), + expect.objectContaining({ name: 'hidden_person' }), // Should really be before the null names ], }); }); + it('should sort visible people by asset count (desc), then by name (asc, nulls last)', async () => { + const { status, body } = await request(app).get('/people').set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body.hasNextPage).toBe(false); + expect(body.total).toBe(11); // All persons + expect(body.hidden).toBe(1); // 'hidden_person' + + const people = body.people as PersonResponseDto[]; + + expect(people.map((p) => p.id)).toEqual([ + nameFreddyPersonFavourite.id, // name: 'Freddy', count: 2 + nameBillPersonFavourite.id, // name: 'Bill', count: 1 + multipleAssetsPerson.id, // name: 'multiple_assets_person', count: 3 + nameBobPerson.id, // name: 'Bob', count: 2 + nameAlicePerson.id, // name: 'Alice', count: 1 + nameCharliePerson.id, // name: 'Charlie', count: 1 + visiblePerson.id, // name: 'visible_person', count: 1 + nameNullPerson4Assets.id, // name: '', count: 4 + nameNullPerson3Assets.id, // name: '', count: 3 + ]); + + expect(people.some((p) => p.id === hiddenPerson.id)).toBe(false); + }); + it('should return only visible people', async () => { const { status, body } = await request(app).get('/people').set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toBe(200); expect(body).toEqual({ hasNextPage: false, - total: 3, + total: 11, hidden: 1, people: [ + expect.objectContaining({ name: 'Freddy' }), + expect.objectContaining({ name: 'Bill' }), expect.objectContaining({ name: 'multiple_assets_person' }), + expect.objectContaining({ name: 'Bob' }), + expect.objectContaining({ name: 'Alice' }), + expect.objectContaining({ name: 'Charlie' }), expect.objectContaining({ name: 'visible_person' }), + expect.objectContaining({ id: nameNullPerson4Assets.id, name: '' }), + expect.objectContaining({ id: nameNullPerson3Assets.id, name: '' }), ], }); }); @@ -80,14 +190,14 @@ describe('/people', () => { const { status, body } = await request(app) .get('/people') .set('Authorization', `Bearer ${admin.accessToken}`) - .query({ withHidden: true, page: 2, size: 1 }); + .query({ withHidden: true, page: 5, size: 1 }); expect(status).toBe(200); expect(body).toEqual({ hasNextPage: true, - total: 3, + total: 11, hidden: 1, - people: [expect.objectContaining({ name: 'visible_person' })], + people: [expect.objectContaining({ name: 'Alice' })], }); }); }); @@ -128,7 +238,7 @@ describe('/people', () => { .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toBe(200); - expect(body).toEqual(expect.objectContaining({ assets: 2 })); + expect(body).toEqual(expect.objectContaining({ assets: 3 })); }); }); diff --git a/e2e/src/api/specs/shared-link.e2e-spec.ts b/e2e/src/api/specs/shared-link.e2e-spec.ts index 04ed8ca0a4..d9f8672c66 100644 --- a/e2e/src/api/specs/shared-link.e2e-spec.ts +++ b/e2e/src/api/specs/shared-link.e2e-spec.ts @@ -117,8 +117,25 @@ describe('/shared-links', () => { const resp = await request(shareUrl).get(`/${linkWithAssets.key}`); expect(resp.status).toBe(200); expect(resp.header['content-type']).toContain('text/html'); + expect(resp.text).toContain(` tag.includes('Date') || tag.includes('Time') || tag.includes('Created'), + ); + if (firstDateTag && metadata[firstDateTag]) { + console.log(` ✓ Verified ${firstDateTag}: ${metadata[firstDateTag]}`); + } + } catch (error) { + console.error(`Failed to create ${image.filename}:`, (error as Error).message); + } + } + + console.log('\nTest image generation complete!'); + console.log('Files created in:', targetDir); + console.log('\nTo test these images:'); + console.log(`cd ${targetDir} && exiftool -time:all -gps:all *.jpg`); +}; + +export { generateTestImages }; + +// Run the generator if this file is executed directly +if (import.meta.url === `file://${process.argv[1]}`) { + generateTestImages().catch(console.error); +} diff --git a/e2e/src/immich-admin/specs/immich-admin.e2e-spec.ts b/e2e/src/immich-admin/specs/immich-admin.e2e-spec.ts index cf0558883a..24699cda30 100644 --- a/e2e/src/immich-admin/specs/immich-admin.e2e-spec.ts +++ b/e2e/src/immich-admin/specs/immich-admin.e2e-spec.ts @@ -7,6 +7,44 @@ describe(`immich-admin`, () => { await utils.adminSetup(); }); + describe('revoke-admin', () => { + it('should revoke admin privileges from a user', async () => { + const { child, promise } = immichAdmin(['revoke-admin']); + + let data = ''; + child.stdout.on('data', (chunk) => { + data += chunk; + if (data.includes('Please enter the user email:')) { + child.stdin.end('admin@immich.cloud\n'); + } + }); + + const { stdout, exitCode } = await promise; + expect(exitCode).toBe(0); + + expect(stdout).toContain('Admin access has been revoked from'); + }); + }); + + describe('grant-admin', () => { + it('should grant admin privileges to a user', async () => { + const { child, promise } = immichAdmin(['grant-admin']); + + let data = ''; + child.stdout.on('data', (chunk) => { + data += chunk; + if (data.includes('Please enter the user email:')) { + child.stdin.end('admin@immich.cloud\n'); + } + }); + + const { stdout, exitCode } = await promise; + expect(exitCode).toBe(0); + + expect(stdout).toContain('Admin access has been granted to'); + }); + }); + describe('list-users', () => { it('should list the admin user', async () => { const { stdout, exitCode } = await immichAdmin(['list-users']).promise; diff --git a/e2e/src/setup/auth-server.ts b/e2e/src/setup/auth-server.ts index 575e97d291..489bda2ee4 100644 --- a/e2e/src/setup/auth-server.ts +++ b/e2e/src/setup/auth-server.ts @@ -12,6 +12,7 @@ export enum OAuthUser { NO_NAME = 'no-name', WITH_QUOTA = 'with-quota', WITH_USERNAME = 'with-username', + WITH_ROLE = 'with-role', } const claims = [ @@ -34,6 +35,12 @@ const claims = [ preferred_username: 'user-quota', immich_quota: 25, }, + { + sub: OAuthUser.WITH_ROLE, + email: 'oauth-with-role@immich.app', + email_verified: true, + immich_role: 'admin', + }, ]; const withDefaultClaims = (sub: string) => ({ @@ -64,7 +71,15 @@ const setup = async () => { claims: { openid: ['sub'], email: ['email', 'email_verified'], - profile: ['name', 'given_name', 'family_name', 'preferred_username', 'immich_quota', 'immich_username'], + profile: [ + 'name', + 'given_name', + 'family_name', + 'preferred_username', + 'immich_quota', + 'immich_username', + 'immich_role', + ], }, features: { jwtUserinfo: { diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index 1d5004d385..cf9eafbf23 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -60,6 +60,7 @@ import { io, type Socket } from 'socket.io-client'; import { loginDto, signupDto } from 'src/fixtures'; import { makeRandomImage } from 'src/generators'; import request from 'supertest'; +export type { Emitter } from '@socket.io/component-emitter'; type CommandResponse = { stdout: string; stderr: string; exitCode: number | null }; type EventType = 'assetUpload' | 'assetUpdate' | 'assetDelete' | 'userDelete' | 'assetHidden'; @@ -84,10 +85,10 @@ export const immichAdmin = (args: string[]) => export const specialCharStrings = ["'", '"', ',', '{', '}', '*']; export const TEN_TIMES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; -const executeCommand = (command: string, args: string[]) => { +const executeCommand = (command: string, args: string[], options?: { cwd?: string }) => { let _resolve: (value: CommandResponse) => void; const promise = new Promise((resolve) => (_resolve = resolve)); - const child = spawn(command, args, { stdio: 'pipe' }); + const child = spawn(command, args, { stdio: 'pipe', cwd: options?.cwd }); let stdout = ''; let stderr = ''; diff --git a/e2e/src/web/specs/user-admin.e2e-spec.ts b/e2e/src/web/specs/user-admin.e2e-spec.ts new file mode 100644 index 0000000000..3d64e47aef --- /dev/null +++ b/e2e/src/web/specs/user-admin.e2e-spec.ts @@ -0,0 +1,89 @@ +import { getUserAdmin } from '@immich/sdk'; +import { expect, test } from '@playwright/test'; +import { asBearerAuth, utils } from 'src/utils'; + +test.describe('User Administration', () => { + test.beforeAll(() => { + utils.initSdk(); + }); + + test.beforeEach(async () => { + await utils.resetDatabase(); + }); + + test('validate admin/users link', async ({ context, page }) => { + const admin = await utils.adminSetup(); + await utils.setAuthCookies(context, admin.accessToken); + + // Navigate to user management page and verify title and header + await page.goto(`/admin/users`); + await expect(page).toHaveTitle(/User Management/); + await expect(page.getByText('User Management')).toBeVisible(); + }); + + test('create user', async ({ context, page }) => { + const admin = await utils.adminSetup(); + await utils.setAuthCookies(context, admin.accessToken); + + // Create a new user + await page.goto('/admin/users'); + await page.getByRole('button', { name: 'Create user' }).click(); + await page.getByLabel('Email').fill('user@immich.cloud'); + await page.getByLabel('Password', { exact: true }).fill('password'); + await page.getByLabel('Confirm Password').fill('password'); + await page.getByLabel('Name').fill('Immich User'); + await page.getByRole('button', { name: 'Create', exact: true }).click(); + + // Verify the user exists in the user list + await page.getByRole('row', { name: 'user@immich.cloud' }); + }); + + test('promote to admin', async ({ context, page }) => { + const admin = await utils.adminSetup(); + await utils.setAuthCookies(context, admin.accessToken); + + const user = await utils.userSetup(admin.accessToken, { + name: 'Admin 2', + email: 'admin2@immich.cloud', + password: 'password', + }); + + expect(user.isAdmin).toBe(false); + + await page.goto(`/admin/users/${user.userId}`); + + await page.getByRole('button', { name: 'Edit user' }).click(); + await expect(page.getByLabel('Admin User')).not.toBeChecked(); + await page.getByText('Admin User').click(); + await expect(page.getByLabel('Admin User')).toBeChecked(); + await page.getByRole('button', { name: 'Confirm' }).click(); + + const updated = await getUserAdmin({ id: user.userId }, { headers: asBearerAuth(admin.accessToken) }); + expect(updated.isAdmin).toBe(true); + }); + + test('revoke admin access', async ({ context, page }) => { + const admin = await utils.adminSetup(); + await utils.setAuthCookies(context, admin.accessToken); + + const user = await utils.userSetup(admin.accessToken, { + name: 'Admin 2', + email: 'admin2@immich.cloud', + password: 'password', + isAdmin: true, + }); + + expect(user.isAdmin).toBe(true); + + await page.goto(`/admin/users/${user.userId}`); + + await page.getByRole('button', { name: 'Edit user' }).click(); + await expect(page.getByLabel('Admin User')).toBeChecked(); + await page.getByText('Admin User').click(); + await expect(page.getByLabel('Admin User')).not.toBeChecked(); + await page.getByRole('button', { name: 'Confirm' }).click(); + + const updated = await getUserAdmin({ id: user.userId }, { headers: asBearerAuth(admin.accessToken) }); + expect(updated.isAdmin).toBe(false); + }); +}); diff --git a/e2e/test-assets b/e2e/test-assets index 8885d6d01c..18736fc27a 160000 --- a/e2e/test-assets +++ b/e2e/test-assets @@ -1 +1 @@ -Subproject commit 8885d6d01c12242785b6ea68f4a277334f60bc90 +Subproject commit 18736fc27a80c99c68e856cdb4f842bc81ed3445 diff --git a/i18n/af.json b/i18n/af.json index 8c02dcee02..55555c8398 100644 --- a/i18n/af.json +++ b/i18n/af.json @@ -40,7 +40,6 @@ "backup_keep_last_amount": "Aantal vorige rugsteune om te hou", "backup_settings": "Rugsteun instellings", "backup_settings_description": "Bestuur databasis rugsteun instellings", - "check_all": "Kies Alles", "cleared_jobs": "Poste gevee vir: {job}", "config_set_by_file": "Config word tans deur 'n konfigurasielÃĒer gestel", "confirm_delete_library": "Is jy seker jy wil {library}-biblioteek uitvee?", @@ -55,12 +54,10 @@ "disable_login": "Deaktiveer aanmelding", "duplicate_detection_job_description": "Begin masjienleer op bates om soortgelyke beelde op te spoor. Maak staat op Smart Search", "exclusion_pattern_description": "Met uitsluitingspatrone kan jy lÃĒers en vouers ignoreer wanneer jy jou biblioteek skandeer. Dit is nuttig as jy vouers het wat lÃĒers bevat wat jy nie wil invoer nie, soos RAW-lÃĒers.", - "external_library_created_at": "Eksterne biblioteek (geskep op {date})", "external_library_management": "Eksterne Biblioteekbestuur", "face_detection": "Gesig deteksie", "failed_job_command": "Opdrag {command} het misluk vir werk: {job}", "force_delete_user_warning": "WAARSKUWING: Dit sal onmiddellik die gebruiker en alle bates verwyder. Dit kan nie ontdoen word nie en die lÃĒers kan nie herstel word nie.", - "forcing_refresh_library_files": "Forseer herlaai van alle biblioteeklÃĒers", "image_format": "Formaat", "image_format_description": "WebP produseer kleiner lÃĒers as JPEG, maar is stadiger om te enkodeer.", "image_prefer_embedded_preview": "Verkies ingebedde voorskou", diff --git a/i18n/ar.json b/i18n/ar.json index 5dedbd2ea5..67f7752ae1 100644 --- a/i18n/ar.json +++ b/i18n/ar.json @@ -43,8 +43,6 @@ "backup_keep_last_amount": "Ų…Ų‚Ø¯Ø§Øą Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ Ø§Ų„ØŗØ§Ø¨Ų‚ØŠ Ų„Ų„Ø§Ø­ØĒŲØ§Ø¸ Ø¨Ų‡Ø§", "backup_settings": "ØĨؚداداØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", "backup_settings_description": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", - "check_all": "ا؎ØĒØą Ø§Ų„ŲƒŲ„", - "cleanup": "ØĒŲ†Ø¸ŲŠŲ", "cleared_jobs": "ØĒŲ… ØĨØŽŲ„Ø§ØĄ Ų…Ų‡Ø§Ų…: {job}", "config_set_by_file": "Ø§Ų„ØĨؚداداØĒ Ø­Ø§Ų„ŲŠŲ‹Ø§ Ų…ØšŲŠŲ†ØŠ ØšŲ† ØˇØąŲŠŲ‚ ؅؄؁ Ø§Ų„Ø§ØšØ¯Ø§Ø¯Ø§ØĒ", "confirm_delete_library": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø­Ø°Ų Ų…ŲƒØĒب؊ {library}؟", @@ -59,14 +57,12 @@ "disable_login": "ØĒØšØˇŲŠŲ„ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„", "duplicate_detection_job_description": "Ø¨Ø¯ØĄ Ø§Ų„ØĒØšŲ„Ų… Ø§Ų„ØĸŲ„ŲŠ ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ Ų„Ų„ØšØĢŲˆØą ØšŲ„Ų‰ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĒØ´Ø§Ø¨Ų‡ØŠ. ŲŠØšØĒŲ…Ø¯ ØšŲ„Ų‰ Ø§Ų„Ø¨Ø­ØĢ Ø§Ų„Ø°ŲƒŲŠ", "exclusion_pattern_description": "ØĒØĒŲŠØ­ Ų„Ųƒ ØŖŲ†Ų…Ø§Øˇ Ø§Ų„Ø§ØŗØĒبؚاد ØĒØŦØ§Ų‡Ų„ Ø§Ų„Ų…Ų„ŲØ§ØĒ ŲˆØ§Ų„Ų…ØŦŲ„Ø¯Ø§ØĒ ØšŲ†Ø¯ ŲØ­Øĩ Ų…ŲƒØĒبØĒ؃. ŲŠØšØ¯ Ų‡Ø°Ø§ Ų…ŲŲŠØ¯Ų‹Ø§ ØĨذا ŲƒØ§Ų† Ų„Ø¯ŲŠŲƒ Ų…ØŦŲ„Ø¯Ø§ØĒ ØĒØ­ØĒ؈؊ ØšŲ„Ų‰ Ų…Ų„ŲØ§ØĒ Ų„Ø§ ØĒØąŲŠØ¯ Ø§ØŗØĒŲŠØąØ§Ø¯Ų‡Ø§ØŒ Ų…ØĢŲ„ Ų…Ų„ŲØ§ØĒ RAW.", - "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_prefer_embedded_preview": "ØĒ؁ØļŲŠŲ„ Ø§Ų„Ų…ØšØ§ŲŠŲ†ØŠ Ø§Ų„Ų…Ø¯Ų…ØŦØŠ", @@ -189,7 +185,7 @@ "oauth_enable_description": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… OAuth", "oauth_mobile_redirect_uri": "ØšŲ†ŲˆØ§Ų† URI Ų„ØĨؚاد؊ Ø§Ų„ØĒ؈ØŦŲŠŲ‡ ØšŲ„Ų‰ Ø§Ų„Ų‡Ø§ØĒ؁", "oauth_mobile_redirect_uri_override": "ØĒØŦØ§ŲˆØ˛ ØšŲ†ŲˆØ§Ų† URI Ų„ØĨؚاد؊ Ø§Ų„ØĒ؈ØŦŲŠŲ‡ ØšŲ„Ų‰ Ø§Ų„Ų‡Ø§ØĒ؁", - "oauth_mobile_redirect_uri_override_description": "Ų‚Ų… بØĒŲØšŲŠŲ„Ų‡ ØšŲ†Ø¯Ų…Ø§ Ų„Ø§ ŲŠØŗŲ…Ø­ Ų…ŲˆŲØą OAuth Ø¨Ų…ØšØąŲ URI Ų„Ų„ØŦŲˆØ§Ų„ØŒ Ų…ØĢŲ„ '{callback}'", + "oauth_mobile_redirect_uri_override_description": "Ų‚Ų… بØĒŲØšŲŠŲ„Ų‡ ØšŲ†Ø¯Ų…Ø§ Ų„Ø§ ŲŠØŗŲ…Ø­ Ų…ŲˆŲØą OAuth Ø¨Ų…ØšØąŲ URI Ų„Ų„ØŦŲˆØ§Ų„ØŒ Ų…ØĢŲ„ ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ OAuth", "oauth_settings_more_details": "Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„ØĒŲØ§ØĩŲŠŲ„ Ø­ŲˆŲ„ Ų‡Ø°Ų‡ Ø§Ų„Ų…ŲŠØ˛ØŠØŒ ŲŠØąØŦŲ‰ Ø§Ų„ØąØŦŲˆØš ØĨŲ„Ų‰ Ø§Ų„ŲˆØĢاØĻŲ‚.", @@ -199,8 +195,6 @@ "oauth_storage_quota_claim_description": "Ų‚Ų… ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ بØĒØšŲŠŲŠŲ† Ø­ØĩØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ų„Ų„Ų…ØŗØĒØŽØ¯Ų… ØšŲ„Ų‰ Ų‚ŲŠŲ…ØŠ Ų‡Ø°Ų‡ Ø§Ų„Ų…ØˇØ§Ų„Ø¨ØŠ.", "oauth_storage_quota_default": "Ø­ØĩØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ø§ŲØĒØąØ§ØļŲŠØŠ (ØŦ؊ØŦØ§Ø¨Ø§ŲŠØĒ)", "oauth_storage_quota_default_description": "Ø§Ų„Ø­ØĩØŠ Ø¨Ø§Ų„ØŦ؊ØŦØ§Ø¨Ø§ŲŠØĒ Ø§Ų„ØĒ؊ ØŗŲŠØĒŲ… Ø§ØŗØĒØŽØ¯Ø§Ų…Ų‡Ø§ ØšŲ†Ø¯Ų…Ø§ Ų„Ø§ ؊ØĒŲ… ØĒŲˆŲŲŠØą Ų…ØˇØ§Ų„Ø¨ØŠ (ØŖØ¯ØŽŲ„ 0 Ų„Ø­ØĩØŠ ØēŲŠØą Ų…Ø­Ø¯ŲˆØ¯ØŠ).", - "offline_paths": "Ų…ØŗØ§ØąØ§ØĒ ØēŲŠØą Ų…ØĒØĩŲ„ØŠ", - "offline_paths_description": "Ų‚Ø¯ ØĒŲƒŲˆŲ† Ų‡Ø°Ų‡ Ø§Ų„Ų†ØĒاØĻØŦ Ų†Ø§ØĒØŦØŠ ØšŲ† Ø­Ø°Ų ŲŠØ¯ŲˆŲŠ Ų„Ų…Ų„ŲØ§ØĒ Ų„Ø§ ØĒØĒبؚ Ų„Ų…ŲƒØĒب؊ ØŽØ§ØąØŦŲŠØŠ.", "password_enable_description": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ŲƒØĒØąŲˆŲ†ŲŠ ŲˆŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", "password_settings": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", "password_settings_description": "ØĨØ¯Ø§ØąØŠ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", @@ -210,9 +204,6 @@ "refreshing_all_libraries": "ØĒØ­Ø¯ŲŠØĢ ŲƒØ§ŲØŠ Ø§Ų„Ų…ŲƒØĒباØĒ", "registration": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ų…Ø¯ŲŠØą", "registration_description": "Ø¨Ų…Ø§ ØŖŲ†Ųƒ ØŖŲˆŲ„ Ų…ØŗØĒØŽØ¯Ų… ؁؊ Ø§Ų„Ų†Ø¸Ø§Ų…ØŒ ØŗŲŠØĒŲ… ØĒØšŲŠŲŠŲ†Ųƒ ŲƒŲ…ØŗØ¤ŲˆŲ„ ŲˆØŗØĒŲƒŲˆŲ† Ų…ØŗØ¤ŲˆŲ„Ų‹Ø§ ØšŲ† Ø§Ų„Ų…Ų‡Ø§Ų… Ø§Ų„ØĨØ¯Ø§ØąŲŠØŠØŒ ŲˆØŗŲŠØĒŲ… ØĨŲ†Ø´Ø§ØĄ Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† ØĨØļØ§ŲŲŠŲŠŲ† Ø¨ŲˆØ§ØŗØˇØĒ؃.", - "repair_all": "ØĨØĩŲ„Ø§Ø­ Ø§Ų„ŲƒŲ„", - "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": "ØĨؚاد؊ ØļØ¨Øˇ Ø§Ų„ØĨؚداداØĒ ØĨŲ„Ų‰ Ø§Ų„ØĨؚداداØĒ Ø§Ų„Ų…Ø­ŲŲˆØ¸ØŠ Ų…Ø¤ØŽØąŲ‹Ø§", @@ -241,7 +232,6 @@ "storage_template_migration_info": "ØĒØēŲŠŲŠØąØ§ØĒ Ø§Ų„Ų‚Ø§Ų„Ø¨ ØŗØĒŲ†ØˇØ¨Ų‚ ŲŲ‚Øˇ ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ. Ų„ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„Ų‚Ø§Ų„Ø¨ ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØĒ؊ ØĒŲ… ØąŲØšŲ‡Ø§ ØŗØ§Ø¨Ų‚Ų‹Ø§ØŒ Ų‚Ų… بØĒØ´ØēŲŠŲ„ {job}.", "storage_template_migration_job": "ŲˆØ¸ŲŠŲØŠ ØĒŲ‡ØŦŲŠØą Ų‚Ø§Ų„Ø¨ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", "storage_template_more_details": "Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„ØĒŲØ§ØĩŲŠŲ„ Ø­ŲˆŲ„ Ų‡Ø°Ų‡ Ø§Ų„Ų…ŲŠØ˛ØŠØŒ ŲŠØąØŦŲ‰ Ø§Ų„ØąØŦŲˆØš ØĨŲ„Ų‰ Storage Template ؈implications", - "storage_template_onboarding_description": "ØšŲ†Ø¯ ØĒŲØšŲŠŲ„ Ų‡Ø°Ų‡ Ø§Ų„Ų…ŲŠØ˛ØŠØŒ ØŗŲŠŲ‚ŲˆŲ… بØĒŲ†Ø¸ŲŠŲ… Ø§Ų„Ų…Ų„ŲØ§ØĒ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ Ø¨Ų†Ø§ØĄŲ‹ ØšŲ„Ų‰ Ų‚Ø§Ų„Ø¨ Ų…Ø­Ø¯Ø¯ Ų…Ų† Ų‚Ø¨Ų„ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…. Ø¨ØŗØ¨Ø¨ Ų…Ø´Ø§ŲƒŲ„ Ø§Ų„Ø§ØŗØĒŲ‚ØąØ§ØąØŒ ØĒŲ… ØĒØšØˇŲŠŲ„ Ø§Ų„Ų…ŲŠØ˛ØŠ Ø§ŲØĒØąØ§ØļŲŠŲ‹Ø§. Ų„Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„Ų…ØšŲ„ŲˆŲ…Ø§ØĒ، ŲŠØąØŦŲ‰ Ø§Ų„ØąØŦŲˆØš ØĨŲ„Ų‰ Ø§Ų„ŲˆØĢاØĻŲ‚.", "storage_template_path_length": "Ø§Ų„Ø­Ø¯ Ø§Ų„ØĒŲ‚ØąŲŠØ¨ŲŠ Ų„ØˇŲˆŲ„ Ø§Ų„Ų…ØŗØ§Øą: {length, number}/{limit, number}", "storage_template_settings": "Ų‚Ø§Ų„Ø¨ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", "storage_template_settings_description": "ØĨØ¯Ø§ØąØŠ Ų‡ŲŠŲƒŲ„ Ø§Ų„Ų…ØŦŲ„Ø¯ ŲˆØ§ØŗŲ… Ø§Ų„Ų…Ų„Ų Ų„Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…ØąŲŲˆØšØŠ", @@ -253,7 +243,6 @@ "template_email_invite_album": "Ų‚Ø§Ų„Ø¨ Ø¯ØšŲˆØŠ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "template_email_preview": "ØšØąØļ Ų…ØŗØ¨Ų‚", "template_email_settings": "Ų†Ų…Ø§Ø°ØŦ Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„Ø§Ų„ŲƒØĒØąŲˆŲ†ŲŠ", - "template_email_settings_description": "ØĨØ¯Ø§ØąØŠ Ų‚ŲˆØ§Ų„Ø¨ ØĨØ´ØšØ§ØąØ§ØĒ Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ Ø§Ų„Ų…ØŽØĩØĩØŠ", "template_email_update_album": "ØĒØ­Ø¯ŲŠØĢ Ų‚Ø§Ų„Ø¨ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "template_email_welcome": "Ų‚Ø§Ų„Ø¨ Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ Ø§Ų„ØĒØąØ­ŲŠØ¨ŲŠ", "template_settings": "Ų‚ŲˆØ§Ų„Ø¨ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ", @@ -262,7 +251,6 @@ "theme_custom_css_settings_description": "ØŖŲˆØąØ§Ų‚ Ø§Ų„ØŖŲ†Ų…Ø§Øˇ Ø§Ų„Ų…ØĒØĒØ§Ų„ŲŠØŠ ØĒØŗŲ…Ø­ بØĒØŽØĩ؊Øĩ ØĒØĩŲ…ŲŠŲ… Immich.", "theme_settings": "ØĨؚداداØĒ Ø§Ų„ØŗŲ…ØŠ", "theme_settings_description": "ØĨØ¯Ø§ØąØŠ ØĒØŽØĩ؊Øĩ ŲˆØ§ØŦŲ‡ØŠ ŲˆŲŠØ¨ Immich", - "these_files_matched_by_checksum": "ØĒØĒŲ… Ų…ØˇØ§Ø¨Ų‚ØŠ Ų‡Ø°Ų‡ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ų…Ų† ØŽŲ„Ø§Ų„ Ø§Ų„Ų…ØŦØ§Ų…ŲŠØš Ø§Ų„Ø§ØŽØĒØ¨Ø§ØąŲŠØŠ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ų‡Ų…", "thumbnail_generation_job": "ØĨŲ†Ø´Ø§ØĄ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąØŠ", "thumbnail_generation_job_description": "ØĨŲ†Ø´Ø§ØĄ ØĩŲˆØą Ų…ØĩØēØąØŠ ŲƒØ¨ŲŠØąØŠ ؈ØĩØēŲŠØąØŠ ؈ØēŲŠØą ŲˆØ§Øļح؊ Ų„ŲƒŲ„ ØŖØĩŲ„ØŒ Ø¨Ø§Ų„ØĨØļØ§ŲØŠ ØĨŲ„Ų‰ ØĩŲˆØą Ų…ØĩØēØąØŠ Ų„ŲƒŲ„ Ø´ØŽØĩ", "transcoding_acceleration_api": "ŲˆØ§ØŦŲ‡ØŠ Ø¨ØąŲ…ØŦØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚Ø§ØĒ Ų„Ų„ØĒØŗØąŲŠØš", @@ -293,7 +281,6 @@ "transcoding_hardware_acceleration_description": "ØĒØŦØąŲŠØ¨ŲŠØ› ØŖØŗØąØš Ø¨ŲƒØĢŲŠØąØŒ ŲˆŲ„ŲƒŲ† ØŗØĒŲƒŲˆŲ† ØŦŲˆØ¯ØĒŲ‡Ø§ ØŖŲ‚Ų„ ØšŲ†Ø¯ Ų†ŲØŗ Ų…ØšØ¯Ų„ Ø§Ų„Ø¨ØĒ", "transcoding_hardware_decoding": "؁؃ ØĒØ´ŲŲŠØą Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ", "transcoding_hardware_decoding_setting_description": "ŲŠŲ†ØˇØ¨Ų‚ Ø°Ų„Ųƒ ŲŲ‚Øˇ ØšŲ„Ų‰ NVENC، QSV، ؈ RKMPP. ŲŠŲ…ŲƒŲ† Ø§Ų„ØĒØŗØąŲŠØš Ų…Ų† ØˇØąŲ Ų„ØˇØąŲ Ø¨Ø¯Ų„Ø§Ų‹ Ų…Ų† ØĒØŗØąŲŠØš Ø§Ų„ØĒØąŲ…ŲŠØ˛ ŲŲ‚Øˇ. Ų‚Ø¯ Ų„Ø§ ŲŠØšŲ…Ų„ ØšŲ„Ų‰ ØŦŲ…ŲŠØš Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ.", - "transcoding_hevc_codec": "ŲƒŲˆØ¯ HEVC", "transcoding_max_b_frames": "ØŖŲ‚ØĩŲ‰ ؚدد Ų…Ų† Ø§Ų„ØĨØˇØ§ØąØ§ØĒ B", "transcoding_max_b_frames_description": "Ø§Ų„Ų‚ŲŠŲ… Ø§Ų„ØŖØšŲ„Ų‰ ØĒØšØ˛Ø˛ ŲƒŲØ§ØĄØŠ Ø§Ų„ØļØēØˇØŒ ŲˆŲ„ŲƒŲ†Ų‡Ø§ ØĒØ¨ØˇØĻ ØšŲ…Ų„ŲŠØŠ Ø§Ų„ØĒØąŲ…ŲŠØ˛. Ų‚Ø¯ Ų„Ø§ ØĒŲƒŲˆŲ† Ų…ØĒŲˆØ§ŲŲ‚ØŠ Ų…Øš Ø§Ų„ØĒØŗØąŲŠØš Ø§Ų„ØšØĒØ§Ø¯ŲŠ ØšŲ„Ų‰ Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ. Ų‚ŲŠŲ…ØŠ 0 ØĒØšØˇŲ„ ØĨØˇØ§ØąØ§ØĒ B، Ø¨ŲŠŲ†Ų…Ø§ ØĒØļØ¨Øˇ Ø§Ų„Ų‚ŲŠŲ…ØŠ -1 Ų‡Ø°Ø§ Ø§Ų„Ų‚ŲŠŲ…ØŠ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§.", "transcoding_max_bitrate": "Ø§Ų„Ø­Ø¯ Ø§Ų„ØŖŲ‚ØĩŲ‰ Ų„Ų…ØšØ¯Ų„ Ø§Ų„Ø¨ØĒ", @@ -331,8 +318,6 @@ "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": "؁ØĒØąØŠ Ø§Ų„ØĒØŖØŽŲŠØą Ų‚Ø¨Ų„ Ø§Ų„Ø­Ø°Ų", @@ -382,8 +367,6 @@ "album_remove_user": "Ų‡Ų„ ØĒØąØēب ؁؊ ØĨØ˛Ø§Ų„ØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ØŸ", "album_remove_user_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĨØ˛Ø§Ų„ØŠ {user}؟", "album_share_no_users": "ŲŠØ¨Ø¯Ųˆ ØŖŲ†Ųƒ Ų‚Ų…ØĒ Ø¨Ų…Ø´Ø§ØąŲƒØŠ Ų‡Ø°Ø§ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… Ų…Øš ØŦŲ…ŲŠØš Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† ØŖŲˆ Ų„ŲŠØŗ Ų„Ø¯ŲŠŲƒ ØŖŲŠ Ų…ØŗØĒØŽØ¯Ų… Ų„Ų„Ų…Ø´Ø§ØąŲƒØŠ Ų…ØšŲ‡.", - "album_thumbnail_card_item": "ØšŲ†ØĩØą ŲˆØ§Ø­Ø¯", - "album_thumbnail_card_shared": " ¡ . Ų…Ø´ØĒØąŲƒ", "album_updated": "ØĒŲ… ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "album_updated_setting_description": "ØĒŲ„Ų‚ŲŠ ØĨØ´ØšØ§ØąŲ‹Ø§ ØšØ¨Øą Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ ØšŲ†Ø¯Ų…Ø§ ŲŠØ­ØĒ؈؊ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… Ø§Ų„Ų…Ø´ØĒØąŲƒ ØšŲ„Ų‰ Ų…Ø­ØĒŲˆŲŠØ§ØĒ ØŦØ¯ŲŠØ¯ØŠ", "album_user_left": "ØĒŲ… ØĒØąŲƒ {album}", @@ -563,7 +546,6 @@ "change_pin_code": "ØĒØēŲŠŲŠØą Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ", "change_your_password": "ØēŲŠØą ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ", "changed_visibility_successfully": "ØĒŲ… ØĒØēŲŠŲŠØą Ø§Ų„ØąØ¤ŲŠØŠ Ø¨Ų†ØŦاح", - "check_all": "ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„ŲƒŲ„", "check_logs": "ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„ØŗØŦŲ„Ø§ØĒ", "choose_matching_people_to_merge": "ا؎ØĒØą Ø§Ų„ØŖØ´ØŽØ§Øĩ Ø§Ų„Ų…ØĒØˇØ§Ø¨Ų‚ŲŠŲ† Ų„Ø¯Ų…ØŦŲ‡Ų…", "city": "Ø§Ų„Ų…Ø¯ŲŠŲ†ØŠ", @@ -749,7 +731,6 @@ "cant_get_number_of_comments": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų„Ø­ØĩŲˆŲ„ ØšŲ„Ų‰ ؚدد Ø§Ų„ØĒØšŲ„ŲŠŲ‚Ø§ØĒ", "cant_search_people": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų„Ø¨Ø­ØĢ ØšŲ† Ø§Ų„Ų†Ø§Øŗ", "cant_search_places": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų„Ø¨Ø­ØĢ ØšŲ† Ø§Ų„ØŖŲ…Ø§ŲƒŲ†", - "cleared_jobs": "Ø§ŲØŽŲ„ŲŠØĒ Ø§Ų„Ų…Ų‡Ø§Ų… Ų„Ų€: {job}", "error_adding_assets_to_album": "حدØĢ ØŽØˇØŖŲŒ ØŖØĢŲ†Ø§ØĄ ØĨØļØ§ŲØŠ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "error_adding_users_to_album": "حدØĢ ØŽØˇØŖŲŒ ØŖØĢŲ†Ø§ØĄ ØĨØļØ§ŲØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "error_deleting_shared_user": "حدØĢ ØŽØˇØŖŲŒ ØŖØĢŲ†Ø§ØĄ Ø­Ø°Ų Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… Ø§Ų„Ų…Ø´ØĒØąŲƒ", @@ -758,7 +739,6 @@ "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": "ŲØ´Ų„ ØĒØšØ¯ŲŠŲ„ Ø§Ų„ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒ", @@ -775,7 +755,6 @@ "paths_validation_failed": "ŲØ´Ų„ ؁؊ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† {paths, plural, one {# Ų…ØŗØ§Øą} 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": "ØĒØšØ°Øą ØĨØļØ§ŲØŠ Ø§Ų„ØĒØšŲ„ŲŠŲ‚", @@ -793,7 +772,6 @@ "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", "unable_to_create_admin_account": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĨŲ†Ø´Ø§ØĄ Ø­ØŗØ§Ø¨ Ø§Ų„Ų…ØŗØ¤ŲˆŲ„", "unable_to_create_api_key": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĨŲ†Ø´Ø§ØĄ ؅؁ØĒاح API ØŦØ¯ŲŠØ¯", @@ -817,10 +795,6 @@ "unable_to_hide_person": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĨØŽŲØ§ØĄ Ø§Ų„Ø´ØŽØĩ", "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", @@ -831,11 +805,9 @@ "unable_to_remove_album_users": "ØĒØšØ°Øą ØĨØ˛Ø§Ų„ØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ų…Ų† Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "unable_to_remove_api_key": "ØĒØšØ°Øą ØĨØ˛Ø§Ų„ØŠ ؅؁ØĒاح API", "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_resolve_duplicate": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ Ø­Ų„ Ø§Ų„ØĒŲƒØąØ§ØąØ§ØĒ", "unable_to_restore_assets": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ Ø§ØŗØĒؚاد؊ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ", @@ -1150,14 +1122,11 @@ "notifications_setting_description": "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ", "official_immich_resources": "Ø§Ų„Ų…ŲˆØ§ØąØ¯ Ø§Ų„ØąØŗŲ…ŲŠØŠ Ų„Ø´ØąŲƒØŠ Immich", "offline": "ØēŲŠØą Ų…ØĒØĩŲ„", - "offline_paths": "Ų…ØŗØ§ØąØ§ØĒ ØēŲŠØą Ų…ØĒØĩŲ„ØŠ", - "offline_paths_description": "Ų‚Ø¯ ØĒŲƒŲˆŲ† Ų‡Ø°Ų‡ Ø§Ų„Ų†ØĒاØĻØŦ Ø¨ØŗØ¨Ø¨ Ø§Ų„Ø­Ø°Ų Ø§Ų„ŲŠØ¯ŲˆŲŠ Ų„Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„ØĒ؊ Ų„Ø§ ØĒØ´ŲƒŲ„ ØŦØ˛ØĄŲ‹Ø§ Ų…Ų† Ų…ŲƒØĒب؊ ØŽØ§ØąØŦŲŠØŠ.", "ok": "Ų†ØšŲ…", "oldest_first": "Ø§Ų„ØŖŲ‚Ø¯Ų… ØŖŲˆŲ„Ø§", "onboarding": "Ø§Ų„ØĨؚداد Ø§Ų„ØŖŲˆŲ„ŲŠ", "onboarding_privacy_description": "ØĒØšØĒŲ…Ø¯ Ø§Ų„Ų…ŲŠØ˛Ø§ØĒ Ø§Ų„ØĒØ§Ų„ŲŠØŠ (ا؎ØĒŲŠØ§ØąŲŠ) ØšŲ„Ų‰ ØŽØ¯Ų…Ø§ØĒ ØŽØ§ØąØŦŲŠØŠØŒ ŲˆŲŠŲ…ŲƒŲ† ØĒØšØˇŲŠŲ„Ų‡Ø§ ؁؊ ØŖŲŠ ŲˆŲ‚ØĒ ؁؊ ØĨؚداداØĒ Ø§Ų„ØĨØ¯Ø§ØąØŠ.", "onboarding_theme_description": "ا؎ØĒØą Ų†ØŗŲ‚ Ø§Ų„ØŖŲ„ŲˆØ§Ų† Ų„Ų„Ų†ØŗØŽØŠ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ. ŲŠŲ…ŲƒŲ†Ųƒ ØĒØēŲŠŲŠØą Ø°Ų„Ųƒ Ų„Ø§Ø­Ų‚Ų‹Ø§ ؁؊ ØĨؚداداØĒ؃.", - "onboarding_welcome_description": "Ų„Ų†Ų‚Ų… بØĨؚداد Ų†ØŗØŽØĒ؃ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… بؚØļ Ø§Ų„ØĨؚداداØĒ Ø§Ų„Ø´Ø§ØĻؚ؊.", "onboarding_welcome_user": "Ų…ØąØ­Ø¨Ø§ØŒ {user}", "online": "Ų…ØĒØĩŲ„", "only_favorites": "Ø§Ų„Ų…ŲØļŲ„ØŠ ŲŲ‚Øˇ", @@ -1450,7 +1419,6 @@ "setting_image_viewer_preview_title": "ØĒØ­Ų…ŲŠŲ„ ØĩŲˆØąØŠ Ų…ØšØ§ŲŠŲ†ØŠ", "setting_image_viewer_title": "Ø§Ų„ØĩŲˆØą", "setting_languages_apply": "ØĒØēŲŠŲŠØą Ø§Ų„ØĨؚداداØĒ", - "setting_languages_title": "Ø§Ų„Ų„ØēاØĒ", "setting_notifications_notify_immediately": "؁؊ Ø§Ų„Ø­Ø§Ų„", "setting_notifications_notify_never": "ØŖØ¨Ø¯Ø§Ų‹", "setting_notifications_single_progress_subtitle": "Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„ØĒŲ‚Ø¯Ų… Ø§Ų„ØĒ؁ØĩŲŠŲ„ŲŠØŠ ØĒØ­Ų…ŲŠŲ„ Ų„ŲƒŲ„ ØŖØĩŲ„", @@ -1601,7 +1569,6 @@ "to_parent": "Ø§Ų†ØĒŲ‚Ų„ ØĨŲ„Ų‰ Ø§Ų„ŲˆØ§Ų„Ø¯", "to_trash": "Ø­Ø°Ų", "toggle_settings": "Ø§Ų„ØĨؚداداØĒ", - "toggle_theme": "ØĒØ¨Ø¯ŲŠŲ„ Ø§Ų„Ų…Ø¸Ų‡Øą Ø§Ų„Ø¯Ø§ŲƒŲ†", "total": "Ø§Ų„ØĨØŦŲ…Ø§Ų„ŲŠ", "total_usage": "Ø§Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„ØĨØŦŲ…Ø§Ų„ŲŠ", "trash": "Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", @@ -1638,8 +1605,6 @@ "unselect_all_duplicates": "ØĨŲ„ØēØ§ØĄ ØĒØ­Ø¯ŲŠØ¯ ŲƒØ§ŲØŠ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ų…ŲƒØąØąØŠ", "unstack": "؁؃ Ø§Ų„ŲƒŲˆŲ…Ų‡", "unstacked_assets_count": "ØĒŲ… ØĨØŽØąØ§ØŦ {count, plural, one {# Ø§Ų„ØŖØĩŲ„} other {# Ø§Ų„ØŖØĩŲˆŲ„}} Ų…Ų† Ø§Ų„ØĒŲƒØ¯ŲŠØŗ", - "untracked_files": "Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„ØĒ؊ Ų„Ų… ؊ØĒŲ… ØĒØšŲ‚Ø¨Ų‡Ø§", - "untracked_files_decription": "Ų„Ø§ ؊ØĒŲ… ØĒØšŲ‚Ø¨ Ų‡Ø°Ų‡ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø¨ŲˆØ§ØŗØˇØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚. ŲŠŲ…ŲƒŲ† ØŖŲ† ØĒŲƒŲˆŲ† Ų†ØĒ؊ØŦØŠŲ‹ Ų„ØšŲ…Ų„ŲŠØ§ØĒ Ų†Ų‚Ų„ ŲØ§Ø´Ų„ØŠØŒ ØŖŲˆ ØšŲ…Ų„ŲŠØ§ØĒ ØąŲØš Ų…ØĒŲ‚ØˇØšØŠØŒ ØŖŲˆ ؊ØĒŲ… ØĒØąŲƒŲ‡Ø§ ؁؊ Ø§Ų„ØŽŲ„Ų Ø¨ØŗØ¨Ø¨ ØŽŲ„Ų„Ø§Ų‹ Ų…Ø§", "up_next": "Ø§Ų„ØĒØ§Ų„ŲŠ", "updated_password": "ØĒŲ… ØĒØ­Ø¯ŲŠØĢ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", "upload": "ØąŲØš", @@ -1675,10 +1640,6 @@ "version": "Ø§Ų„ØĨØĩØ¯Ø§Øą", "version_announcement_closing": "ØĩØ¯ŲŠŲ‚ŲƒØŒ ØŖŲ„ŲŠŲƒØŗ", "version_announcement_message": "Ų…ØąØ­Ø¨Ų‹Ø§! ؊ØĒŲˆŲØą ØĨØĩØ¯Ø§Øą ØŦØ¯ŲŠØ¯ Ų…Ų† Immich. ŲŠŲØąØŦŲ‰ ØĒØŽØĩ؊Øĩ بؚØļ Ø§Ų„ŲˆŲ‚ØĒ Ų„Ų‚ØąØ§ØĄØŠ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„ØĨØĩØ¯Ø§Øą Ų„Ų„ØĒØŖŲƒØ¯ Ų…Ų† ØĒØ­Ø¯ŲŠØĢ ØĨؚداداØĒ؃ Ų„Ų…Ų†Øš ØŖŲŠ ØŖØŽØˇØ§ØĄ ؁؊ Ø§Ų„ØĒŲƒŲˆŲŠŲ†ØŒ ؎اØĩØŠ ØĨذا ŲƒŲ†ØĒ ØĒØŗØĒØŽØ¯Ų… WatchTower ØŖŲˆ ØŖŲŠ ØĸŲ„ŲŠØŠ ØĒØĒŲˆŲ„Ų‰ ØĒØ­Ø¯ŲŠØĢ Ų…ØĢŲŠŲ„ Immich Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§.", - "version_announcement_overlay_release_notes": "Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„ØĨØĩØ¯Ø§Øą", - "version_announcement_overlay_text_1": "Ų…ØąØ­Ø¨Ų‹Ø§ ŲŠØ§ ØĩØ¯ŲŠŲ‚ŲŠ ، Ų‡Ų†Ø§Ųƒ ØĨØĩØ¯Ø§Øą ØŦØ¯ŲŠØ¯", - "version_announcement_overlay_text_2": "Ų…Ų† ؁ØļŲ„Ųƒ ؎ذ ŲˆŲ‚ØĒ؃ Ų„Ø˛ŲŠØ§ØąØŠ", - "version_announcement_overlay_title": "Ų†ØŗØŽŲ‡ ØŦØ¯ŲŠØ¯Ų‡ Ų…ØĒØ§Ø­Ų‡ Ų„Ų„ØŽØ§Ø¯Ų… ", "version_history": "ØĒØ§ØąŲŠØŽ Ø§Ų„ØĨØĩØ¯Ø§Øą", "version_history_item": "ØĒŲ… ØĒØĢØ¨ŲŠØĒ {version} ؁؊ {date}", "video": "ŲŲŠØ¯ŲŠŲˆ", diff --git a/i18n/az.json b/i18n/az.json index 0b787b317f..19ca4aa08d 100644 --- a/i18n/az.json +++ b/i18n/az.json @@ -34,18 +34,15 @@ "backup_database_enable_description": "Verilənlər bazasÄąnÄąn ehtiyat nÃŧsxələrini aktiv et", "backup_settings": "Ehtiyat NÃŧsxə Parametrləri", "backup_settings_description": "Verilənlər bazasÄąnÄąn ehtiyat nÃŧsxə parametrlərini idarə et", - "check_all": "HamÄąsÄąnÄą yoxla", "config_set_by_file": "Konfiqurasiya hal-hazÄąrda konfiqurasiya faylÄą ilə təyin olunub", "confirm_delete_library": "{library} kitabxanasÄąnÄą silmək istədiyinizdən əminmisiniz?", "confirm_email_below": "Təsdiqləmək ÃŧçÃŧn aşağıya {email} yazÄąn", "confirm_user_password_reset": "{user} adlÄą istifadəçinin şifrəsini sÄąfÄąrlamaq istədiyinizdən əminmisiniz?", "disable_login": "Giriş etməni sÃļndÃŧr", "duplicate_detection_job_description": "Bənzər şəkilləri tapmaq ÃŧçÃŧn maÅŸÄąn Ãļyrənməsini işə salÄąn. Bu prosses Smart Search funksiyasÄąna əsaslanÄąr", - "external_library_created_at": "Xarici kitabxana ({date} (tarixində yaradÄąldÄą)", "external_library_management": "Xarici kitabxana idarəetməsi", "face_detection": "Üz tanÄąma", "force_delete_user_warning": "XƏBƏRDARLIQ: Bu əməliyyat istifadəçi və bÃŧtÃŧn məlumatlarÄą siləcəkdir. Bu prossesi və silinən fayllarÄą geri qaytarmaq olmaz.", - "forcing_refresh_library_files": "BÃŧtÃŧn kitabxana fayllarÄąnÄą məcburi yeniləmə", "image_format_description": "WebP, JPEG faylÄąna gÃļrə daha kiçik həcmə sahibdir, lakin onu kodlaşdÄąrmaq daha çox vaxt alÄąr.", "image_preview_title": "Önizləmə parametrləri", "image_quality": "Keyfiyyət", diff --git a/i18n/be.json b/i18n/be.json index b8898d8aaf..470030b6f6 100644 --- a/i18n/be.json +++ b/i18n/be.json @@ -44,8 +44,6 @@ "backup_keep_last_amount": "КоĐģҌĐēĐ°ŅŅ†ŅŒ ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅ–Ņ… Ņ€ŅĐˇĐĩŅ€Đ˛ĐžĐ˛Ņ‹Ņ… ĐēĐžĐŋŅ–Đš Đ´ĐģŅ ĐˇĐ°Ņ…Đ°Đ˛Đ°ĐŊĐŊŅ", "backup_settings": "НаĐģĐ°Đ´Ņ‹ Ņ€ŅĐˇĐĩŅ€Đ˛ĐžĐ˛Đ°ĐŗĐ° ĐēаĐŋŅ–ŅĐ˛Đ°ĐŊĐŊŅ", "backup_settings_description": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŊаĐģадаĐŧŅ– даĐŧĐŋа ĐąĐ°ĐˇŅ‹ дадСĐĩĐŊҋ҅. Đ—Đ°ŅžĐ˛Đ°ĐŗĐ°: ĐŗŅŅ‚Ņ‹Ņ ĐˇĐ°Đ´Đ°Ņ‡Ņ‹ ĐŊĐĩ ĐēаĐŊŅ‚Ņ€Đ°ĐģŅŽŅŽŅ†Ņ†Đ°, Ņ– Ņž Đ˛Ņ‹ĐŋадĐē҃ ĐŊŅŅžĐ´Đ°Ņ‡Ņ‹ ĐŋавĐĩдаĐŧĐģĐĩĐŊĐŊĐĩ адĐŋŅ€Đ°ŅžĐģĐĩĐŊа ĐŊĐĩ ĐąŅƒĐ´ĐˇĐĩ.", - "check_all": "ĐŸŅ€Đ°Đ˛ĐĩŅ€Ņ‹Ņ†ŅŒ ҃ҁĐĩ", - "cleanup": "ĐŅ‡Ņ‹ŅŅ‚Đēа", "cleared_jobs": "ĐŅ‡Ņ‹ŅˆŅ‡Đ°ĐŊŅ‹ СадаĐŊĐŊŅ– Đ´ĐģŅ: {job}", "config_set_by_file": "КаĐŊŅ„Ņ–ĐŗŅƒŅ€Đ°Ņ†Ņ‹Ņ Ņž ĐˇĐ°Ņ€Đ°Đˇ ŅƒŅŅ‚Đ°ĐģŅĐ˛Đ°ĐŊа ĐŋŅ€Đ°Đˇ Ņ„Đ°ĐšĐģ ĐēаĐŊŅ„Ņ–ĐŗŅƒŅ€Đ°Ņ†Ņ‹Ņ–", "confirm_delete_library": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹ ŅˆŅ‚Đž ĐļадаĐĩ҆Đĩ Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ {library} ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐē҃?", @@ -60,14 +58,12 @@ "disable_login": "АдĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ŅƒĐ˛Đ°Ņ…ĐžĐ´", "duplicate_detection_job_description": "ЗаĐŋŅƒŅŅ†Ņ–Ņ†ŅŒ ĐŧĐ°ŅˆŅ‹ĐŊĐŊаĐĩ ĐŊĐ°Đ˛ŅƒŅ‡Đ°ĐŊĐŊĐĩ ĐŊа аĐēŅ‚Ņ‹Đ˛Đ°Ņ… Đ´ĐģŅ Đ˛Ņ‹ŅŅžĐģĐĩĐŊĐŊŅ ĐŋадОйĐŊҋ҅ Đ˛Ņ‹ŅŅž. ЗаĐģĐĩĐļŅ‹Ņ†ŅŒ ад Smart Search", "exclusion_pattern_description": "ШайĐģĐžĐŊŅ‹ Đ˛Ņ‹ĐēĐģŅŽŅ‡ŅĐŊĐŊŅ даСваĐģŅŅŽŅ†ŅŒ Ņ–ĐŗĐŊĐ°Ņ€Đ°Đ˛Đ°Ņ†ŅŒ Ņ„Đ°ĐšĐģŅ‹ Ņ– ĐŋаĐŋĐēŅ– ĐŋҀҋ ҁĐēаĐŊаваĐŊĐŊŅ– Đ˛Đ°ŅˆĐ°Đš ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēŅ–. Đ“ŅŅ‚Đ° ĐēĐ°Ņ€Ņ‹ŅĐŊа, ĐēаĐģŅ– Ņž Đ˛Đ°Ņ Ņ‘ŅŅ†ŅŒ ĐŋаĐŋĐēŅ–, ŅĐēŅ–Ņ СĐŧŅŅˆŅ‡Đ°ŅŽŅ†ŅŒ Ņ„Đ°ĐšĐģŅ‹, ŅĐēŅ–Ņ Đ˛Ņ‹ ĐŊĐĩ Ņ…ĐžŅ‡Đ°Ņ†Đĩ Ņ–ĐŧĐŋĐ°Ņ€Ņ‚Đ°Đ˛Đ°Ņ†ŅŒ, ĐŊаĐŋҀҋĐēĐģад, Ņ„Đ°ĐšĐģŅ‹ RAW.", - "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": "Đ’Ņ‹ŅĐ˛Đ° Ņž ĐŋĐžŅžĐŊŅ‹Đŧ ĐŋаĐŧĐĩҀҋ ĐąĐĩС ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊҋ҅, Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°ĐĩŅ†Ņ†Đ° ĐŋҀҋ ĐŋавĐĩĐģŅ–Ņ‡ŅĐŊĐŊŅ–", diff --git a/i18n/bg.json b/i18n/bg.json index 7f0fca55d6..2df3dd927d 100644 --- a/i18n/bg.json +++ b/i18n/bg.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,11 @@ "add_partner": "ДобавĐĩŅ‚Đĩ ĐŋĐ°Ņ€Ņ‚ĐŊŅŒĐžŅ€", "add_path": "Добави ĐŋŅŠŅ‚", "add_photos": "ДобавĐĩŅ‚Đĩ ҁĐŊиĐŧĐēи", + "add_tag": "Добави ĐŧĐ°Ņ€ĐēĐĩŅ€", "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": "ДобавĐĩĐŊĐž ĐēҊĐŧ Đ°Ņ€Ņ…Đ¸Đ˛Đ°", @@ -29,19 +34,18 @@ "added_to_favorites_count": "ДобавĐĩĐŊи {count, number} ĐēҊĐŧ ĐģŅŽĐąĐ¸Đŧи", "admin": { "add_exclusion_pattern_description": "Добави ĐŧОдĐĩĐģи Са иСĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ. ĐŸĐžĐ´Đ´ŅŠŅ€Đļа ҁĐĩ \"globbing\" ҁ ĐŋĐžĐŧĐžŅ‰Ņ‚Đ° ĐŊа *, ** и ?. За да Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€Đ°Ņ‚Đĩ Đ˛ŅĐ¸Ņ‡Đēи Ņ„Đ°ĐšĐģОвĐĩ в Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐžŅ€Đ¸Ņ ҁ иĐŧĐĩ \"Raw\", иСĐŋĐžĐģĐˇĐ˛Đ°ĐšŅ‚Đĩ \"**/Raw/**\". За да Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€Đ°Ņ‚Đĩ Đ˛ŅĐ¸Ņ‡Đēи Ņ„Đ°ĐšĐģОвĐĩ, ĐˇĐ°Đ˛ŅŠŅ€ŅˆĐ˛Đ°Ņ‰Đ¸ ĐŊа \".tif\", иСĐŋĐžĐģĐˇĐ˛Đ°ĐšŅ‚Đĩ \"**/*.tif\". За да Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€Đ°Ņ‚Đĩ Đ°ĐąŅĐžĐģŅŽŅ‚ĐĩĐŊ ĐŋŅŠŅ‚, иСĐŋĐžĐģĐˇĐ˛Đ°ĐšŅ‚Đĩ \"/path/to/ignore/**\".", + "admin_user": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€", "asset_offline_description": "ĐĸОСи Đ˛ŅŠĐŊ҈ĐĩĐŊ йийĐģĐ¸ĐžŅ‚Đĩ҇ĐĩĐŊ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐąŅŠĐ´Đĩ ĐžŅ‚ĐēŅ€Đ¸Ņ‚ ĐŊа Đ´Đ¸ŅĐēа и Đĩ ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩĐŊ в ĐēĐžŅˆŅ‡ĐĩŅ‚Đž Са йОĐēĐģ҃Đē. АĐēĐž Ņ„Đ°ĐšĐģŅŠŅ‚ Đĩ ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩĐŊ в йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ°, ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐĩŅ‚Đĩ Đ˛Đ°ŅˆĐ°Ņ‚Đ° Đ¸ŅŅ‚ĐžŅ€Đ¸Ņ Са ĐŊОв ŅŅŠĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛Đ°Ņ‰ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚. За да Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°, ĐŧĐžĐģŅ ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐĩŅ‚Đĩ даĐģи Ņ„Đ°ĐšĐģĐžĐ˛Đ¸ŅŅ‚ ĐŋŅŠŅ‚ ĐžŅ‚Đ´ĐžĐģ҃ ĐŧĐžĐļĐĩ да ĐąŅŠĐ´Đĩ Đ´ĐžŅŅ‚ŅŠĐŋĐĩĐŊ ĐžŅ‚ Immich и ҁĐēаĐŊĐ¸Ņ€Đ°ĐšŅ‚Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ°.", "authentication_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Са ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ", "authentication_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŋĐ°Ņ€ĐžĐģа, OAuth и Đ´Ņ€ŅƒĐŗĐ¸ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи Са ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ", "authentication_settings_disable_all": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Ņ‚Đĩ Đ˛ŅĐ¸Ņ‡Đēи ĐŧĐĩŅ‚ĐžĐ´Đ¸ Са вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ? ВĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩŅ‚Đž ҉Đĩ ĐąŅŠĐ´Đĩ ĐŊаĐŋҊĐģĐŊĐž Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž.", "authentication_settings_reenable": "За да Ņ€ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Ņ‚Đĩ, иСĐŋĐžĐģĐˇĐ˛Đ°ĐšŅ‚Đĩ Server Command.", "background_task_job": "ĐŸŅ€ĐžŅ†ĐĩŅĐ¸ ĐŊа СадĐĩĐŊ Ņ„ĐžĐŊ", - "backup_database": "Đ ĐĩСĐĩŅ€Đ˛ĐŊа База даĐŊĐŊи", - "backup_database_enable_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ°Đ˛Đ°ĐŊĐĩ ĐŊа Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи", - "backup_keep_last_amount": "Đ‘Ņ€ĐžĐš Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ Са СаĐŋаСваĐŊĐĩ", - "backup_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŊа Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ", - "backup_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐž ĐēĐžĐŋиĐĩ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи", - "check_all": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸ Đ˛ŅĐ¸Ņ‡ĐēĐž", - "cleanup": "ĐŸĐžŅ‡Đ¸ŅŅ‚Đ˛Đ°ĐŊĐĩ", + "backup_database": "ĐĄŅŠĐˇĐ´Đ°Đš Ņ€ĐĩСĐĩŅ€Đ˛ĐŊа йаСа даĐŊĐŊи", + "backup_database_enable_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи", + "backup_keep_last_amount": "Đ‘Ņ€ĐžĐš СаĐŋаСĐĩĐŊи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ", + "backup_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŊа Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи", + "backup_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐž ĐēĐžĐŋиĐĩ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи.", "cleared_jobs": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚ĐĩĐŊи ĐˇĐ°Đ´Đ°Ņ‡Đ¸ ĐžŅ‚ Ņ‚Đ¸Đŋ: {job}", "config_set_by_file": "КоĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸ŅŅ‚Đ° Đĩ СададĐĩĐŊа ĐžŅ‚ Ņ„Đ°ĐšĐģ", "confirm_delete_library": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ° - {library} ?", @@ -49,6 +53,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", @@ -56,18 +61,21 @@ "disable_login": "ИСĐēĐģŅŽŅ‡ĐĩŅ‚Đĩ вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩŅ‚Đž", "duplicate_detection_job_description": "ĐĄŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐž ĐžĐąŅƒŅ‡ĐĩĐŊиĐĩ Đ˛ŅŠŅ€Ņ…Ņƒ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸, Са ĐžŅ‚ĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋОдОйĐŊи Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ. Đ Đ°ĐˇŅ‡Đ¸Ņ‚Đ° ĐŊа ИĐŊŅ‚ĐĩĐģĐ¸ĐŗĐĩĐŊŅ‚ĐŊĐž ĐĸŅŠŅ€ŅĐĩĐŊĐĩ", "exclusion_pattern_description": "МодĐĩĐģи Са иСĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŋОСвОĐģŅĐ˛Đ°Ņ‚ да Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€Đ°Ņ‚Đĩ Ņ„Đ°ĐšĐģОвĐĩ и ĐŋаĐŋĐēи, ĐēĐžĐŗĐ°Ņ‚Đž ҁĐēаĐŊĐ¸Ņ€Đ°Ņ‚Đĩ Đ˛Đ°ŅˆĐ°Ņ‚Đ° йийĐģĐ¸ĐžŅ‚ĐĩĐēа. ĐĸОва Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊĐž, аĐēĐž иĐŧĐ°Ņ‚Đĩ ĐŋаĐŋĐēи, ĐēĐžĐ¸Ņ‚Đž ŅŅŠĐ´ŅŠŅ€ĐļĐ°Ņ‚ Ņ„Đ°ĐšĐģОвĐĩ, ĐēĐžĐ¸Ņ‚Đž ĐŊĐĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да иĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°Ņ‚Đĩ. ĐŸŅ€Đ¸ĐŧĐĩŅ€ĐŊĐž - RAW Ņ„Đ°ĐšĐģОвĐĩ.", - "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_prefer_embedded_preview": "ĐŸŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐĩ ĐŊа Đ˛ĐŗŅ€Đ°Đ´ĐĩĐŊĐ¸Ņ‚Đĩ ĐŋŅ€ĐĩĐŗĐģĐĩди", - "image_prefer_embedded_preview_setting_description": "ИСĐŋĐžĐģСваĐŊĐĩ ĐŊа Đ˛ĐŗŅ€Đ°Đ´ĐĩĐŊĐ¸Ņ‚Đĩ ĐŋŅ€ĐĩĐŗĐģĐĩди в RAW ҁĐŊиĐŧĐēĐ¸Ņ‚Đĩ ĐēĐ°Ņ‚Đž Đ˛Ņ…ĐžĐ´ Са ĐžĐąŅ€Đ°ĐąĐžŅ‚Đēа ĐŊа Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸ŅŅ‚Đ°, ĐēĐžĐŗĐ°Ņ‚Đž иĐŧа Ņ‚Đ°Đēива. ĐĸОва ĐŧĐžĐļĐĩ да дОвĐĩĐ´Đĩ Đ´Đž ĐŋĐž-Ņ‚ĐžŅ‡ĐŊи Ņ†Đ˛ĐĩŅ‚ĐžĐ˛Đĩ Са ĐŊŅĐēОи Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ, ĐŊĐž ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛ĐžŅ‚Đž ĐŊа ĐŋŅ€ĐĩĐŗĐģĐĩĐ´Đ¸Ņ‚Đĩ ĐˇĐ°Đ˛Đ¸ŅĐ¸ ĐžŅ‚ ĐēаĐŧĐĩŅ€Đ°Ņ‚Đ° и Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩŅ‚Đž ĐŧĐžĐļĐĩ да иĐŧа ĐŋОвĐĩ҇Đĩ ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸ĐžĐŊĐŊи Đ°Ņ€Ņ‚ĐĩŅ„Đ°ĐēŅ‚Đ¸.", + "image_prefer_embedded_preview_setting_description": "ИСĐŋĐžĐģСваĐŊĐĩ ĐŊа Đ˛ĐŗŅ€Đ°Đ´ĐĩĐŊĐ¸Ņ‚Đĩ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸ в RAW ҁĐŊиĐŧĐēĐ¸Ņ‚Đĩ ĐēĐ°Ņ‚Đž Đ˛Ņ…ĐžĐ´ĐŊи Са ĐžĐąŅ€Đ°ĐąĐžŅ‚Đēа ĐŊа Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸ŅŅ‚Đ°, ĐēĐžĐŗĐ°Ņ‚Đž иĐŧа Ņ‚Đ°Đēива. ĐĸОва ĐŧĐžĐļĐĩ да дОвĐĩĐ´Đĩ Đ´Đž ĐŋĐž-Ņ‚ĐžŅ‡ĐŊи Ņ†Đ˛ĐĩŅ‚ĐžĐ˛Đĩ Са ĐŊŅĐēОи Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ, ĐŊĐž ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛ĐžŅ‚Đž ĐŊа ĐŋŅ€ĐĩĐŗĐģĐĩĐ´Đ¸Ņ‚Đĩ ĐˇĐ°Đ˛Đ¸ŅĐ¸ ĐžŅ‚ ĐēаĐŧĐĩŅ€Đ°Ņ‚Đ° и Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩŅ‚Đž ĐŧĐžĐļĐĩ да иĐŧа ĐŋОвĐĩ҇Đĩ ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸ĐžĐŊĐŊи Đ°Ņ€Ņ‚ĐĩŅ„Đ°ĐēŅ‚Đ¸.", "image_prefer_wide_gamut": "ĐŸŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐĩ ĐŊа ŅˆĐ¸Ņ€ĐžĐēа ĐŗĐ°Đŧа", "image_prefer_wide_gamut_setting_description": "ИСĐŋĐžĐģСваĐŊĐĩ ĐŊа Display P3 Са ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸. ĐĸОва СаĐŋаСва ĐŋĐž-Đ´ĐžĐąŅ€Đĩ ĐļиСĐŊĐĩĐŊĐžŅŅ‚Ņ‚Đ° ĐŊа Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸ŅŅ‚Đ° ҁ ŅˆĐ¸Ņ€ĐžĐēи Ņ†Đ˛ĐĩŅ‚ĐžĐ˛Đ¸ ĐŋŅ€ĐžŅŅ‚Ņ€Đ°ĐŊŅŅ‚Đ˛Đ°, ĐŊĐž Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸ŅŅ‚Đ° ĐŧĐžĐļĐĩ да Đ¸ĐˇĐŗĐģĐĩĐļĐ´Đ°Ņ‚ ĐŋĐž Ņ€Đ°ĐˇĐģĐ¸Ņ‡ĐĩĐŊ ĐŊĐ°Ņ‡Đ¸ĐŊ ĐŊа ŅŅ‚Đ°Ņ€Đ¸ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ° ҁҊҁ ŅŅ‚Đ°Ņ€Đ° вĐĩŅ€ŅĐ¸Ņ ĐŊа ĐąŅ€Đ°ŅƒĐˇŅŠŅ€Đ°. sRGB Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸ŅŅ‚Đ° ҁĐĩ СаĐŋĐ°ĐˇĐ˛Đ°Ņ‚ ĐēĐ°Ņ‚Đž sRGB, Са да ҁĐĩ иСйĐĩĐŗĐŊĐ°Ņ‚ Ņ†Đ˛ĐĩŅ‚ĐžĐ˛Đ¸ ĐŋŅ€ĐžĐŧĐĩĐŊи.", "image_preview_description": "ĐĄŅ€ĐĩĐ´ĐĩĐŊ Ņ€Đ°ĐˇĐŧĐĩŅ€ ĐŊа Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩŅ‚Đž ҁ ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐ°Ņ‚Đ¸ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊи, иСĐŋĐžĐģСваĐŊĐž ĐŋŅ€Đ¸ ĐŋŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ĐĩдиĐŊ аĐēŅ‚Đ¸Đ˛ и Са ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐž ĐžĐąŅƒŅ‡ĐĩĐŊиĐĩ", @@ -163,18 +171,18 @@ "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_from_address_description": "ЕĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊа ĐŋĐžŅ‰Đ° ĐŊа иСĐŋŅ€Đ°Ņ‰Đ°Ņ‡Đ°, ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€: \"Immich Photo Server \". ИСĐŋĐžĐģĐˇĐ˛Đ°ĐšŅ‚Đĩ Đ°Đ´Ņ€Đĩҁ, ĐžŅ‚ ĐēĐžĐšŅ‚Đž ĐŧĐžĐļĐĩ да иСĐŋŅ€Đ°Ņ‰Đ°Ņ‚Đĩ иĐŧĐĩĐšĐģи.", "notification_email_host_description": "ĐĨĐžŅŅ‚ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° Са ĐĩĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊа ĐŋĐžŅ‰Đ° (ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€: smtp.immich.app)", "notification_email_ignore_certificate_errors": "Đ˜ĐŗĐŊĐžŅ€Đ¸Ņ€Đ°ĐšŅ‚Đĩ ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸ĐžĐŊĐŊи ĐŗŅ€Đĩ҈Đēи", "notification_email_ignore_certificate_errors_description": "Đ˜ĐŗĐŊĐžŅ€Đ¸Ņ€Đ°Đš ĐŗŅ€Đĩ҈Đēи ŅĐ˛ŅŠŅ€ĐˇĐ°ĐŊи ҁ ваĐģĐ¸Đ´Đ°Ņ†Đ¸Ņ ĐŊа TLS ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ (ĐŊĐĩ ҁĐĩ ĐŋŅ€ĐĩĐŋĐžŅ€ŅŠŅ‡Đ˛Đ°)", - "notification_email_password_description": "ĐŸĐ°Ņ€ĐžĐģа иСĐŋĐžĐģСваĐŊа Са идĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ ҁҊҁ ŅŅŠŅ€Đ˛ŅŠŅ€Đ° Са ĐĩĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊа ĐŋĐžŅ‰Đ°", + "notification_email_password_description": "ĐŸĐ°Ņ€ĐžĐģа иСĐŋĐžĐģСваĐŊа Са ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ ĐŋŅ€ĐĩĐ´ ŅŅŠŅ€Đ˛ŅŠŅ€Đ° Са ĐĩĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊа ĐŋĐžŅ‰Đ°", "notification_email_port_description": "ĐŸĐžŅ€Ņ‚ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° Са ĐĩĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊа ĐŋĐžŅ‰Đ° (ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€ 25, 465 иĐģи 587)", "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}. ĐŸŅ€ĐžĐ˛ĐĩŅ€ĐĩŅ‚Đĩ Đ˛Ņ…ĐžĐ´ŅŅ‰Đ°Ņ‚Đ° ŅĐ¸ ĐŋĐžŅ‰a.", - "notification_email_username_description": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģҁĐēĐž иĐŧĐĩ Са идĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ ҁ иĐŧĐĩĐšĐģ ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", + "notification_email_username_description": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģҁĐēĐž иĐŧĐĩ Са ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ ĐŋŅ€ĐĩĐ´ иĐŧĐĩĐšĐģ ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "notification_enable_email_notifications": "ВĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа иĐŧĐĩĐšĐģ иСвĐĩŅŅ‚Đ¸ŅŅ‚Đ°", "notification_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊа иСвĐĩŅŅ‚Đ¸ŅŅ‚Đ°", "notification_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са иСвĐĩŅŅ‚Đ¸Ņ, вĐēĐģ. иĐŧĐĩĐšĐģ", @@ -183,10 +191,11 @@ "oauth_auto_register": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊа Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ", "oauth_auto_register_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŊОви ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи ҁĐģĐĩĐ´ вĐģиСаĐŊĐĩ ҁ OAuth", "oauth_button_text": "ĐĸĐĩĐēҁ҂ ĐŊа ĐąŅƒŅ‚ĐžĐŊа", + "oauth_client_secret_description": "Đ˜ĐˇĐ¸ŅĐēва ҁĐĩ, ĐēĐžĐŗĐ°Ņ‚Đž Đ´ĐžŅŅ‚Đ°Đ˛Ņ‡Đ¸Đēа ĐŊа OAuth ĐŊĐĩ ĐŋĐžĐ´Đ´ŅŠŅ€Đļа PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "ВĐģиСаĐŊĐĩ ҁ OAuth", "oauth_mobile_redirect_uri": "URI Са ĐŧОйиĐģĐŊĐž ĐŋŅ€ĐĩĐŊĐ°ŅĐžŅ‡Đ˛Đ°ĐŊĐĩ", "oauth_mobile_redirect_uri_override": "URI ĐŋŅ€ĐĩĐŊĐ°ŅĐžŅ‡Đ˛Đ°ĐŊĐĩ Са ĐŧОйиĐģĐŊи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", - "oauth_mobile_redirect_uri_override_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸ ĐēĐžĐŗĐ°Ņ‚Đž Đ´ĐžŅŅ‚Đ°Đ˛Ņ‡Đ¸Đēа Са OAuth ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ ĐŊĐĩ ĐŋОСвОĐģŅĐ˛Đ° Са ĐŧОйиĐģĐŊи URI идĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€Đ¸, ĐēĐ°Ņ‚Đž '{callback}'", + "oauth_mobile_redirect_uri_override_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸ ĐēĐžĐŗĐ°Ņ‚Đž Đ´ĐžŅŅ‚Đ°Đ˛Ņ‡Đ¸Đēа Са OAuth ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ ĐŊĐĩ ĐŋОСвОĐģŅĐ˛Đ° Са ĐŧОйиĐģĐŊи URI идĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€Đ¸, ĐēĐ°Ņ‚Đž ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са Đ˛Ņ…ĐžĐ´ ҁ OAuth", "oauth_settings_more_details": "За ĐŋОвĐĩ҇Đĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Са Ņ„ŅƒĐŊĐēŅ†Đ¸ĐžĐŊаĐģĐŊĐžŅŅ‚Ņ‚Đ°, ҁĐĩ ĐŋĐžŅ‚ŅŠŅ€ŅĐĩŅ‚Đĩ в docs.", @@ -195,9 +204,9 @@ "oauth_storage_quota_claim": "Đ—Đ°ŅĐ˛Đēа Са ĐēĐ˛ĐžŅ‚Đ° Са ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ", "oauth_storage_quota_claim_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐˇĐ°Đ´Đ°ĐšŅ‚Đĩ ĐēĐ˛ĐžŅ‚Đ°Ņ‚Đ° Са ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ ҁҊҁ ŅŅ‚ĐžĐšĐŊĐžŅŅ‚Ņ‚Đ° ĐžŅ‚ Ņ‚Đ°ĐˇĐ¸ ĐˇĐ°ŅĐ˛Đēа.", "oauth_storage_quota_default": "ĐĄŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊа ĐēĐ˛ĐžŅ‚Đ° Са ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ (GiB)", - "oauth_storage_quota_default_description": "ĐšĐ˛ĐžŅ‚Đ° в GiB, ĐēĐžŅŅ‚Đž да ҁĐĩ иСĐŋĐžĐģСва, ĐēĐžĐŗĐ°Ņ‚Đž ĐŊĐĩ Đĩ ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ĐĩĐŊа ĐˇĐ°ŅĐ˛Đēа (Đ’ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ 0 Са ĐŊĐĩĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊа ĐēĐ˛ĐžŅ‚Đ°).", - "offline_paths": "ĐžŅ„ĐģаКĐŊ ĐŋŅŠŅ‚Đ¸Ņ‰Đ°", - "offline_paths_description": "ĐĸĐĩСи Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸ ĐŧĐžĐļĐĩ да ҁĐĩ Đ´ŅŠĐģĐļĐ°Ņ‚ ĐŊа Ņ€ŅŠŅ‡ĐŊĐž Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Ņ„Đ°ĐšĐģОвĐĩ, ĐēĐžĐ¸Ņ‚Đž ĐŊĐĩ ŅĐ° Ņ‡Đ°ŅŅ‚ ĐžŅ‚ Đ˛ŅŠĐŊ҈ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēа.", + "oauth_storage_quota_default_description": "ĐšĐ˛ĐžŅ‚Đ° в GiB, ĐēĐžŅŅ‚Đž да ҁĐĩ иСĐŋĐžĐģСва, ĐēĐžĐŗĐ°Ņ‚Đž ĐŊĐĩ Đĩ ĐŋĐžŅĐžŅ‡ĐĩĐŊĐž Đ´Ņ€ŅƒĐŗĐž.", + "oauth_timeout": "Đ’Ņ€ĐĩĐŧĐĩ ĐŊа Đ¸ĐˇŅ‡Đ°ĐēваĐŊĐĩ ĐŋŅ€Đ¸ ĐˇĐ°ŅĐ˛Đēа", + "oauth_timeout_description": "Đ’Ņ€ĐĩĐŧĐĩ Са Đ¸ĐˇŅ‡Đ°ĐēваĐŊĐĩ ĐŊа ĐžŅ‚ĐŗĐžĐ˛ĐžŅ€ ĐŊа ĐˇĐ°ŅĐ˛Đēа, в ĐŧиĐģĐ¸ŅĐĩĐē҃ĐŊди", "password_enable_description": "ВĐģиСаĐŊĐĩ ҁ иĐŧĐĩĐšĐģ и ĐŋĐ°Ņ€ĐžĐģа", "password_settings": "ВĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ ҁ ĐŋĐ°Ņ€ĐžĐģа", "password_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са вĐģиСаĐŊĐĩ ҁ ĐŋĐ°Ņ€ĐžĐģа", @@ -207,9 +216,6 @@ "refreshing_all_libraries": "ОĐŋŅ€ĐĩҁĐŊŅĐ˛Đ°ĐŊĐĩ ĐŊа Đ˛ŅĐ¸Ņ‡Đēи йийĐģĐ¸ĐžŅ‚ĐĩĐēи", "registration": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ŅĐēа Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ", "registration_description": "ĐĸŅŠĐš ĐēĐ°Ņ‚Đž ҁ҂Đĩ ĐŋŅŠŅ€Đ˛Đ¸ŅŅ‚ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ в ŅĐ¸ŅŅ‚ĐĩĐŧĐ°Ņ‚Đ°, ҉Đĩ ĐąŅŠĐ´ĐĩŅ‚Đĩ ĐŊаСĐŊĐ°Ņ‡ĐĩĐŊ ĐēĐ°Ņ‚Đž адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ и ҉Đĩ ĐžŅ‚ĐŗĐžĐ˛Đ°Ņ€ŅŅ‚Đĩ Са адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚Đ¸Đ˛ĐŊĐ¸Ņ‚Đĩ ĐˇĐ°Đ´Đ°Ņ‡Đ¸, а Đ´ĐžĐŋҊĐģĐŊĐ¸Ņ‚ĐĩĐģĐŊĐ¸Ņ‚Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи ҉Đĩ ĐąŅŠĐ´Đ°Ņ‚ ŅŅŠĐˇĐ´Đ°Đ´ĐĩĐŊи ĐžŅ‚ Đ˛Đ°Ņ.", - "repair_all": "ПоĐŋŅ€Đ°Đ˛ŅĐŊĐĩ ĐŊа Đ˛ŅĐ¸Ņ‡ĐēĐž", - "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": "Đ’ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Đ´Đž ĐŋĐžŅĐģĐĩĐ´ĐŊĐ¸Ņ‚Đĩ СаĐŋаСĐĩĐŊи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", @@ -235,10 +241,10 @@ "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 и ĐŊĐĩĐŗĐžĐ˛Đ¸Ņ‚Đĩ ĐŋĐžŅĐģĐĩĐ´ŅŅ‚Đ˛Đ¸Ņ ", - "storage_template_onboarding_description": "ĐšĐžĐŗĐ°Ņ‚Đž Đĩ аĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊа, Ņ‚Đ°ĐˇĐ¸ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ ҉Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ¸Ņ€Đ° Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Ņ„Đ°ĐšĐģОвĐĩ Đ˛ŅŠĐˇ ĐžŅĐŊОва ĐŊа Đ´ĐĩŅ„Đ¸ĐŊĐ¸Ņ€Đ°ĐŊ ĐžŅ‚ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ ŅˆĐ°ĐąĐģĐžĐŊ. ĐŸĐžŅ€Đ°Đ´Đ¸ ĐŋŅ€ĐžĐąĐģĐĩĐŧи ҁҊҁ ŅŅ‚Đ°ĐąĐ¸ĐģĐŊĐžŅŅ‚Ņ‚Đ°, Ņ„ŅƒĐŊĐēŅ†Đ¸ŅŅ‚Đ° Đĩ иСĐēĐģŅŽŅ‡ĐĩĐŊа ĐŋĐž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ. За ĐŋОвĐĩ҇Đĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ, ĐŧĐžĐģŅ, виĐļŅ‚Đĩ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸ŅŅ‚Đ°.", + "storage_template_onboarding_description_v2": "ĐšĐžĐŗĐ°Ņ‚Đž Đĩ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊа, Ņ‚Đ°ĐˇĐ¸ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ ҉Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ¸Ņ€Đ° Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Ņ„Đ°ĐšĐģОвĐĩŅ‚Đĩ, ҁĐŋĐžŅ€ĐĩĐ´ ŅˆĐ°ĐąĐģĐžĐŊ, Đ´ĐĩŅ„Đ¸ĐŊĐ¸Ņ€Đ°ĐŊ ĐžŅ‚ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ. За Đ´ĐžĐŋҊĐģĐŊĐ¸Ņ‚ĐĩĐģĐŊа иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ, ĐŧĐžĐģŅ виĐļŅ‚Đĩ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸ŅŅ‚Đ°.", "storage_template_path_length": "ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊиĐĩ ĐŊа Đ´ŅŠĐģĐļиĐŊĐ°Ņ‚Đ° ĐŊа ĐŋŅŠŅ‚Ņ: {length, number}/{limit, number}", "storage_template_settings": "ШайĐģĐžĐŊ Са ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ", "storage_template_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Đ°Ņ‚Đ° ĐŊа ĐŋаĐŋĐēĐ¸Ņ‚Đĩ и иĐŧĐĩŅ‚Đž ĐŊа Ņ„Đ°ĐšĐģа Са ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐĩ", @@ -250,16 +256,14 @@ "template_email_invite_album": "ШайĐģĐžĐŊ Са ĐŋĐžĐēаĐŊа Са аĐģĐąŅƒĐŧ", "template_email_preview": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´", "template_email_settings": "ШайĐģĐžĐŊи Са иĐŧĐĩĐšĐģи", - "template_email_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ŅˆĐ°ĐąĐģĐžĐŊи Са иĐŧĐĩĐšĐģ иСвĐĩŅŅ‚Đ¸Ņ", "template_email_update_album": "ШайĐģĐžĐŊ Са аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ°Ņ†Đ¸Ņ ĐŊа аĐģĐąŅƒĐŧ", "template_email_welcome": "ШайĐģĐžĐŊ Са ĐŋŅ€Đ¸Đ˛ĐĩŅ‚ŅŅ‚Đ˛Đ°Ņ‰ иĐŧĐĩĐšĐģ", "template_settings": "ШайĐģĐžĐŊи Са иСвĐĩŅŅ‚Đ¸Ņ", - "template_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ŅˆĐ°ĐąĐģĐžĐŊи Са иСвĐĩŅŅ‚Đ¸Ņ.", + "template_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ŅˆĐ°ĐąĐģĐžĐŊи Са иСвĐĩŅŅ‚Đ¸Ņ", "theme_custom_css_settings": "ПĐĩŅ€ŅĐžĐŊаĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ CSS", "theme_custom_css_settings_description": "ĐšĐ°ŅĐēадĐŊĐ¸Ņ‚Đĩ ŅŅ‚Đ¸ĐģОви Ņ‚Đ°ĐąĐģĐ¸Ņ†Đ¸ ĐŋОСвОĐģŅĐ˛Đ°Ņ‚ ĐŋĐĩŅ€ŅĐžĐŊаĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа диСаКĐŊа ĐŊа Immich.", "theme_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊа Ņ‚ĐĩĐŧĐ°Ņ‚Đ°", "theme_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŋĐĩŅ€ŅĐžĐŊаĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩŅ‚Đž ĐŊа ҃ĐĩĐą иĐŊŅ‚ĐĩҀ҄ĐĩĐšŅĐ° ĐŊа Immich", - "these_files_matched_by_checksum": "ĐĸĐĩСи Ņ„Đ°ĐšĐģОвĐĩ ҁĐĩ ŅŅ€Đ°Đ˛ĐŊŅĐ˛Đ°Ņ‚ ĐŋĐž ĐēĐžĐŊŅ‚Ņ€ĐžĐģĐŊĐ¸Ņ‚Đĩ иĐŧ ҁ҃Đŧи (checksums)", "thumbnail_generation_job": "ГĐĩĐŊĐĩŅ€Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸", "thumbnail_generation_job_description": "ГĐĩĐŊĐĩŅ€Đ¸Ņ€Đ°ĐšŅ‚Đĩ ĐŗĐžĐģĐĩĐŧи, ĐŧаĐģĐēи и СаĐŧŅŠĐŗĐģĐĩĐŊи ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸ Са Đ˛ŅĐĩĐēи аĐēŅ‚Đ¸Đ˛, ĐēаĐēŅ‚Đž и ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸ Са Đ˛ŅĐĩĐēи Ņ‡ĐžĐ˛ĐĩĐē", "transcoding_acceleration_api": "API Са ҃ҁĐēĐžŅ€ŅĐ˛Đ°ĐŊĐĩ", @@ -287,10 +291,9 @@ "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": "ĐŸŅ€Đ¸ĐģĐ°ĐŗĐ° ҁĐĩ ŅĐ°ĐŧĐž Са NVENC, QSV и RKMPP. АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ° ҃ҁĐēĐžŅ€ĐĩĐŊиĐĩ ĐžŅ‚ ĐēŅ€Đ°Đš Đ´Đž ĐēŅ€Đ°Đš, вĐŧĐĩŅŅ‚Đž ŅĐ°ĐŧĐž да ҃ҁĐēĐžŅ€ŅĐ˛Đ° ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩŅ‚Đž. МоĐļĐĩ да ĐŊĐĩ Ņ€Đ°ĐąĐžŅ‚Đ¸ ҁ Đ˛ŅĐ¸Ņ‡Đēи видĐĩĐžĐēĐģиĐŋОвĐĩ.", - "transcoding_hevc_codec": "HEVC ĐēОдĐĩĐē", "transcoding_max_b_frames": "МаĐēŅĐ¸ĐŧаĐģĐŊи B-҄ҀĐĩĐšĐŧа", "transcoding_max_b_frames_description": "По-Đ˛Đ¸ŅĐžĐēĐ¸Ņ‚Đĩ ŅŅ‚ĐžĐšĐŊĐžŅŅ‚Đ¸ ĐŋĐžĐ´ĐžĐąŅ€ŅĐ˛Đ°Ņ‚ ĐĩŅ„ĐĩĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚Ņ‚Đ° ĐŊа ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸ŅŅ‚Đ°, ĐŊĐž ĐˇĐ°ĐąĐ°Đ˛ŅŅ‚ Ņ€Đ°ĐˇĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩŅ‚Đž. МоĐļĐĩ да ĐŊĐĩ Đĩ ŅŅŠĐ˛ĐŧĐĩŅŅ‚Đ¸Đŧ ҁ Ņ…Đ°Ņ€Đ´ŅƒĐĩŅ€ĐŊĐžŅ‚Đž ҃ҁĐēĐžŅ€ĐĩĐŊиĐĩ ĐŊа ĐŋĐž-ŅŅ‚Đ°Ņ€Đ¸ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°. 0 Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ° B-҄ҀĐĩĐšĐŧа, Đ´ĐžĐēĐ°Ņ‚Đž -1 Садава Ņ‚Đ°ĐˇĐ¸ ŅŅ‚ĐžĐšĐŊĐžŅŅ‚ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž.", "transcoding_max_bitrate": "МаĐēŅĐ¸ĐŧаĐģĐĩĐŊ ĐąĐ¸Ņ‚Ņ€ĐĩĐšŅ‚", @@ -328,14 +331,13 @@ "trash_number_of_days_description": "Đ‘Ņ€ĐžĐš Đ´ĐŊи, в ĐēĐžĐ¸Ņ‚Đž Ņ„Đ°ĐšĐģОвĐĩŅ‚Đĩ да ҁĐĩ ŅŅŠŅ…Ņ€Đ°ĐŊŅĐ˛Đ°Ņ‚ ĐŊа йОĐēĐģ҃Đēа, ĐŋŅ€Đĩди да ĐąŅŠĐ´Đ°Ņ‚ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģĐŊĐž ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐ°Ņ‚Đ¸", "trash_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊа ĐēĐžŅˆŅ‡ĐĩŅ‚Đž", "trash_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ ĐŊа ĐēĐžŅˆŅ‡ĐĩŅ‚Đž", - "untracked_files": "НĐĩĐŋŅ€ĐžŅĐģĐĩĐ´ĐĩĐŊи Ņ„Đ°ĐšĐģОвĐĩ", - "untracked_files_description": "ĐĸĐĩСи Ņ„Đ°ĐšĐģОвĐĩ ĐŊĐĩ ҁĐĩ ĐŋŅ€ĐžŅĐģĐĩĐ´ŅĐ˛Đ°Ņ‚ ĐžŅ‚ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž. ĐĸĐĩ ĐŧĐžĐŗĐ°Ņ‚ да ĐąŅŠĐ´Đ°Ņ‚ Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚ ĐžŅ‚ ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊи ĐŋŅ€ĐĩĐŧĐĩŅŅ‚Đ˛Đ°ĐŊĐ¸Ņ, ĐŋŅ€ĐĩĐēҊҁĐŊĐ°Ņ‚Đ¸ ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐ¸Ņ иĐģи ĐžŅŅ‚Đ°Đ˛ĐĩĐŊи ĐŋĐžŅ€Đ°Đ´Đ¸ ĐŗŅ€Đĩ҈Đēа", "user_cleanup_job": "ĐŸĐžŅ‡Đ¸ŅŅ‚Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи", "user_delete_delay": "{user} aĐēĐ°ŅƒĐŊŅ‚ŅŠŅ‚ и Ņ„Đ°ĐšĐģОвĐĩŅ‚Đĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ ҉Đĩ ĐąŅŠĐ´Đ°Ņ‚ ĐŋĐģаĐŊĐ¸Ņ€Đ°ĐŊи Са ĐŋĐžŅŅ‚ĐžŅĐŊĐŊĐž Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ ҁĐģĐĩĐ´ {delay, plural, one {# Đ´ĐĩĐŊ} other {# Đ´ĐŊи}}.", "user_delete_delay_settings": "Đ—Đ°ĐąĐ°Đ˛ŅĐŊĐĩ ĐŊа Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ", "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": "МоĐģŅ, ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ĐĩŅ‚Đĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊĐ°Ņ‚Đ° ĐŋĐ°Ņ€ĐžĐģа ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ и ĐŗĐž иĐŊŅ„ĐžŅ€ĐŧĐ¸Ņ€Đ°ĐšŅ‚Đĩ, ҇Đĩ ҉Đĩ Ņ‚Ņ€ŅĐąĐ˛Đ° да Ņ ҁĐŧĐĩĐŊи ĐŋŅ€Đ¸ ҁĐģĐĩĐ´Đ˛Đ°Ņ‰ĐžŅ‚Đž ŅĐ¸ вĐģиСаĐŊĐĩ в ŅĐ¸ŅŅ‚ĐĩĐŧĐ°Ņ‚Đ°.", @@ -355,6 +357,20 @@ "admin_password": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ŅĐēа ĐŋĐ°Ņ€ĐžĐģа", "administration": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ", "advanced": "Đ Đ°ĐˇŅˆĐ¸Ņ€ĐĩĐŊĐž", + "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 {ГодиĐŊа #}}", @@ -363,6 +379,8 @@ "album_cover_updated": "ОбĐģĐžĐļĐēĐ°Ņ‚Đ° ĐŊа аĐģĐąŅƒĐŧа Đĩ аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊа", "album_delete_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ аĐģĐąŅƒĐŧа {album}?", "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}?", @@ -375,9 +393,20 @@ "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} АĐģĐąŅƒĐŧи}}", + "albums_default_sort_order": "Đ ĐĩĐ´ ĐŋĐž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ Са ŅĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа аĐģĐąŅƒĐŧи", + "albums_default_sort_order_description": "ĐŸŅŠŅ€Đ˛ĐžĐŊĐ°Ņ‡Đ°ĐģĐĩĐŊ Ņ€ĐĩĐ´ ĐŊа ŅĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŋŅ€Đ¸ ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐŊОв аĐģĐąŅƒĐŧ.", + "albums_feature_description": "КоĐģĐĩĐēŅ†Đ¸Đ¸ ĐžŅ‚ ОйĐĩĐēŅ‚Đ¸, ĐēĐžĐ¸Ņ‚Đž ĐŧĐžĐŗĐ°Ņ‚ да ĐąŅŠĐ´Đ°Ņ‚ ҁĐŋОдĐĩĐģŅĐŊи ҁ Đ´Ņ€ŅƒĐŗĐ¸ ĐŋĐžŅ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи.", "all": "Đ’ŅĐ¸Ņ‡Đēи", "all_albums": "Đ’ŅĐ¸Ņ‡Đēи аĐģĐąŅƒĐŧи", "all_people": "Đ’ŅĐ¸Ņ‡Đēи Ņ…ĐžŅ€Đ°", @@ -392,43 +421,142 @@ "api_key_description": "ĐĸаСи ŅŅ‚ĐžĐšĐŊĐžŅŅ‚ ҉Đĩ ĐąŅŠĐ´Đĩ ĐŋĐžĐēаСаĐŊа ŅĐ°ĐŧĐž вĐĩĐ´ĐŊҊĐļ. МоĐģŅ, ĐŊĐĩ ĐˇĐ°ĐąŅ€Đ°Đ˛ŅĐšŅ‚Đĩ да ĐŗĐž ĐēĐžĐŋĐ¸Ņ€Đ°Ņ‚Đĩ, ĐŋŅ€Đĩди да ĐˇĐ°Ņ‚Đ˛ĐžŅ€Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐˇĐžŅ€ĐĩŅ†Đ°.", "api_key_empty": "ИĐŧĐĩŅ‚Đž ĐŊа Đ˛Đ°ŅˆĐ¸Ņ API ĐēĐģŅŽŅ‡ ĐŊĐĩ Ņ‚Ņ€ŅĐąĐ˛Đ° да Đĩ ĐŋŅ€Đ°ĐˇĐŊĐž", "api_keys": "API ĐēĐģŅŽŅ‡ĐžĐ˛Đĩ", + "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": "НĐĩ ŅĐ° ĐŊаĐŧĐĩŅ€ĐĩĐŊи ОйĐĩĐēŅ‚Đ¸ в Đ°Ņ€Ņ…Đ¸Đ˛Đ°", + "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_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_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_uploaded": "ĐšĐ°Ņ‡ĐĩĐŊĐž", "asset_uploading": "ĐšĐ°Ņ‡Đ˛Đ°ĐŊĐĩâ€Ļ", + "asset_viewer_settings_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са Đ¸ĐˇĐŗĐģĐĩĐ´", + "asset_viewer_settings_title": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "assets": "ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "assets_added_count": "ДобавĐĩĐŊĐž {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "ДобавĐĩĐŊ(и) ŅĐ° {count, plural, one {# аĐēŅ‚Đ¸Đ˛} other {# аĐēŅ‚Đ¸Đ˛Đ°}} в аĐģĐąŅƒĐŧа", "assets_added_to_name_count": "ДобавĐĩĐŊ(и) ŅĐ° {count, plural, one {# аĐēŅ‚Đ¸Đ˛} other {# аĐēŅ‚Đ¸Đ˛Đ°}} ĐēҊĐŧ {hasName, select, true {{name}} other {ĐŊОв аĐģĐąŅƒĐŧ}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {ОбĐĩĐēŅ‚Đ° ĐŊĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ дОйави} other {ОбĐĩĐēŅ‚Đ¸Ņ‚Đĩ ĐŊĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ Đ´ĐžĐąĐ°Đ˛ŅŅ‚}} в аĐģĐąŅƒĐŧа", "assets_count": "{count, plural, one {# аĐēŅ‚Đ¸Đ˛} other {# аĐēŅ‚Đ¸Đ˛Đ°}}", + "assets_deleted_permanently": "{count} ОйĐĩĐēŅ‚Đ° ŅĐ° Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸ СавиĐŊĐ°ĐŗĐ¸", + "assets_deleted_permanently_from_server": "{count} ОйĐĩĐēŅ‚Đ° ŅĐ° Đ¸ĐˇŅ‚Đ¸Ņ‚Đ¸ ĐžŅ‚ Immich ŅŅŠŅ€Đ˛ŅŠŅ€Đ° СавиĐŊĐ°ĐŗĐ¸", + "assets_downloaded_failed": "{count, plural, one {Đ—Đ°Ņ€ĐĩĐ´ĐĩĐŊ # Ņ„Đ°ĐšĐģ} many {Đ—Đ°Ņ€ĐĩĐ´ĐĩĐŊи # Ņ„Đ°ĐšĐģа} other {ĐˇĐ°Ņ€ĐĩĐ´ĐĩĐŊи # Ņ„Đ°ĐšĐģа}}, {error} - ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐž", + "assets_downloaded_successfully": "ĐŖŅĐŋĐĩ҈ĐŊĐž {count, plural, one {Đĩ ĐēĐ°Ņ‡ĐĩĐŊ # Ņ„Đ°ĐšĐģ} many {ŅĐ° ĐēĐ°Ņ‡ĐĩĐŊи # Ņ„Đ°ĐšĐģа} other {ŅĐ° ĐēĐ°Ņ‡ĐĩĐŊи # Ņ„Đ°ĐšĐģа}}", "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} ОйĐĩĐēŅ‚Đ° ŅĐ° ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩĐŊи в ĐēĐžŅˆĐ° ĐŊа Immich ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "assets_were_part_of_album_count": "{count, plural, one {ФаКĐģŅŠŅ‚ Đĩ} other {ФаКĐģОвĐĩŅ‚Đĩ ŅĐ°}} вĐĩ҇Đĩ Ņ‡Đ°ŅŅ‚ ĐžŅ‚ аĐģĐąŅƒĐŧа", "authorized_devices": "ĐŖĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ĐĩĐŊи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", + "automatic_endpoint_switching_subtitle": "ĐšĐžĐŗĐ°Ņ‚Đž Đĩ Đ´ĐžŅŅ‚ŅŠĐŋĐŊа, иСĐŋĐžĐģСваК ĐŋĐžŅĐžŅ‡ĐĩĐŊĐ°Ņ‚Đ° Wi-Fi ĐŧŅ€ĐĩĐļа, иĐŊĐ°Ņ‡Đĩ иСĐŋĐžĐģСваК аĐģŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊи Đ˛Ņ€ŅŠĐˇĐēи", + "automatic_endpoint_switching_title": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа URL", + "autoplay_slideshow": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊа ҁĐŧŅĐŊа ĐŊа ҁĐģаКдОвĐĩŅ‚Đĩ", "back": "Назад", "back_close_deselect": "Назад, ĐˇĐ°Ņ‚Đ˛Đ°Ņ€ŅĐŊĐĩ иĐģи ĐŋŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐŊа Đ¸ĐˇĐąĐžŅ€Đ°", + "background_location_permission": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ Са Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž Đ˛ŅŠĐ˛ Ņ„ĐžĐŊОв Ņ€ĐĩĐļиĐŧ", + "background_location_permission_content": "За да ĐŧĐžĐļĐĩ да ҇ĐĩŅ‚Đĩ иĐŧĐĩĐŊĐ°Ņ‚Đ° ĐŊа Wi-Fi ĐŧŅ€ĐĩĐļĐ¸Ņ‚Đĩ и да ĐŗĐ¸ ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ˛Đ° ĐŋŅ€Đ¸ Ņ€Đ°ĐąĐžŅ‚Đ° Đ˛ŅŠĐ˛ Ņ„ĐžĐŊОв Ņ€ĐĩĐļиĐŧ, Immich Ņ‚Ņ€ŅĐąĐ˛Đ° *виĐŊĐ°ĐŗĐ¸* да иĐŧа Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž Ņ‚ĐžŅ‡ĐŊĐžŅ‚Đž ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", + "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: {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": "Назад", + "biometric_auth_enabled": "ВĐēĐģŅŽŅ‡ĐĩĐŊа йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊĐž ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ", + "biometric_locked_out": "ĐŅĐŧа Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊĐž ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ", + "biometric_no_options": "ĐŅĐŧа йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊа Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸ĐēĐ°Ņ†Đ¸Ņ", + "biometric_not_available": "На Ņ‚ĐžĐ˛Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž ĐŊĐĩ Đĩ ĐŊаĐģĐ¸Ņ‡ĐŊĐž йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊĐž ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ", "birthdate_saved": "Đ”Đ°Ņ‚Đ°Ņ‚Đ° ĐŊа Ņ€Đ°ĐļдаĐŊĐĩ Đĩ СаĐŋаСĐĩĐŊа ҃ҁĐŋĐĩ҈ĐŊĐž", "birthdate_set_description": "Đ”Đ°Ņ‚Đ°Ņ‚Đ° ĐŊа Ņ€Đ°ĐļдаĐŊĐĩ ҁĐĩ иСĐŋĐžĐģСва Са Đ¸ĐˇŅ‡Đ¸ŅĐģŅĐ˛Đ°ĐŊĐĩ ĐŊа Đ˛ŅŠĐˇŅ€Đ°ŅŅ‚Ņ‚Đ° ĐŊа Ņ‚ĐžĐˇĐ¸ Ņ‡ĐžĐ˛ĐĩĐē ĐēҊĐŧ ĐŧĐžĐŧĐĩĐŊŅ‚Đ° ĐŊа ҁĐŊиĐŧĐēĐ°Ņ‚Đ°.", "blurred_background": "ЗаĐŧŅŠĐŗĐģĐĩĐŊ СадĐĩĐŊ Ņ„ĐžĐŊ", @@ -439,24 +567,51 @@ "bulk_keep_duplicates_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да СаĐŋĐ°ĐˇĐ¸Ņ‚Đĩ {count, plural, one {# Đ´ŅƒĐąĐģĐ¸Ņ€Đ°ĐŊ Ņ„Đ°ĐšĐģ} other {# Đ´ŅƒĐąĐģĐ¸Ņ€Đ°ĐŊи Ņ„Đ°ĐšĐģа}}? ĐĸОва ҉Đĩ ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´Đ¸ Đ˛ŅĐ¸Ņ‡Đēи ĐŗŅ€ŅƒĐŋи Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸, ĐąĐĩС да Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ° ĐŊĐ¸Ņ‰Đž.", "bulk_trash_duplicates_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да ĐŋŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚Đĩ в ĐēĐžŅˆŅ‡ĐĩŅ‚Đž ĐŧĐ°ŅĐžĐ˛Đž {count, plural, one {# Đ´ŅƒĐąĐģĐ¸Ņ€Đ°ĐŊ Ņ„Đ°ĐšĐģ} other {# Đ´ŅƒĐąĐģĐ¸Ņ€Đ°ĐŊи Ņ„Đ°ĐšĐģа}}? ĐĸОва ҉Đĩ СаĐŋаСи ĐŊаК-ĐŗĐžĐģĐĩĐŧĐ¸Ņ Ņ„Đ°ĐšĐģ ĐžŅ‚ Đ˛ŅŅĐēа ĐŗŅ€ŅƒĐŋа и ҉Đĩ ĐŋŅ€ĐĩĐŧĐĩŅŅ‚Đ¸ в ĐēĐžŅˆŅ‡ĐĩŅ‚Đž Đ˛ŅĐ¸Ņ‡Đēи Đ´Ņ€ŅƒĐŗĐ¸ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸.", "buy": "ĐšŅƒĐŋĐĩŅ‚Đĩ Immich", + "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_statistics_album": "БибĐģĐ¸ĐžŅ‚ĐĩĐēа ҁ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸", + "cache_settings_statistics_full": "ĐŸŅŠĐģĐŊи Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", + "cache_settings_statistics_shared": "МиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊĐ¸Ņ‚Đĩ аĐģĐąŅƒĐŧи", + "cache_settings_statistics_thumbnail": "МиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸", + "cache_settings_statistics_title": "ИСĐŋĐžĐģСваĐŊĐĩ ĐŊа ĐēĐĩŅˆĐ°", + "cache_settings_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐēĐĩŅˆĐ¸Ņ€Đ°ĐŊĐĩŅ‚Đž в ĐŧОйиĐģĐŊĐžŅ‚Đž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ Immich", + "cache_settings_tile_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐģĐžĐēаĐģĐŊĐžŅ‚Đž Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ", + "cache_settings_tile_title": "ЛоĐēаĐģĐŊĐž Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ", + "cache_settings_title": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊа ĐēĐĩŅˆĐ¸Ņ€Đ°ĐŊĐĩ", "camera": "КаĐŧĐĩŅ€Đ°", "camera_brand": "ĐœĐ°Ņ€Đēа ĐŊа ĐēаĐŧĐĩŅ€Đ°Ņ‚Đ°", "camera_model": "МодĐĩĐģ ĐŊа ĐēаĐŧĐĩŅ€Đ°Ņ‚Đ°", "cancel": "ĐžŅ‚ĐēаĐļи", "cancel_search": "ĐžŅ‚ĐŧĐĩĐŊи Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž", + "canceled": "ĐžŅ‚ĐŧĐĩĐŊĐĩĐŊĐž", "cannot_merge_people": "НĐĩ ĐŧĐžĐļĐĩ да ОйĐĩдиĐŊŅĐ˛Đ° Ņ…ĐžŅ€Đ°", "cannot_undo_this_action": "НĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ да ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚Đĩ Ņ‚ĐžĐ˛Đ° Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ!", "cannot_update_the_description": "ОĐŋĐ¸ŅĐ°ĐŊиĐĩŅ‚Đž ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐąŅŠĐ´Đĩ аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐž", + "cast": "ĐŸĐžŅ‚ĐžŅ‡ĐŊĐž ĐŋŅ€ĐĩдаваĐŊĐĩ", + "cast_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŊа ĐŊаĐģĐ¸Ņ‡ĐŊĐ¸Ņ‚Đĩ ҆ĐĩĐģи Са ĐŋŅ€ĐĩдаваĐŊĐĩ", "change_date": "ĐŸŅ€ĐžĐŧĐĩĐŊи Đ´Đ°Ņ‚Đ°Ņ‚Đ°", + "change_description": "ĐŸŅ€ĐžĐŧĐĩĐŊи ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩŅ‚Đž", + "change_display_order": "ĐŸŅ€ĐžĐŧĐĩĐŊи Ņ€Đĩда ĐŊа ĐŋĐžĐēаСваĐŊĐĩ", "change_expiration_time": "ĐŸŅ€ĐžĐŧĐĩĐŊĐĩŅ‚Đĩ Đ˛Ņ€ĐĩĐŧĐĩŅ‚Đž ĐŊа Đ¸ĐˇŅ‚Đ¸Ņ‡Đ°ĐŊĐĩ", "change_location": "ĐŸŅ€ĐžĐŧĐĩĐŊи ĐģĐžĐēĐ°Ņ†Đ¸ŅŅ‚Đ°", "change_name": "ĐŸŅ€ĐžĐŧĐĩĐŊи иĐŧĐĩŅ‚Đž", - "change_name_successfully": "ИĐŧĐĩŅ‚Đž Đĩ ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋŅ€ĐžĐŧĐĩĐŊĐĩĐŊĐž", + "change_name_successfully": "ĐŖŅĐŋĐĩ҈ĐŊа ĐŋŅ€ĐžĐŧŅĐŊа ĐŊа иĐŧĐĩŅ‚Đž", "change_password": "ĐŸŅ€ĐžĐŧĐĩĐŊи ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ°", "change_password_description": "ĐĸОва Đĩ иĐģи ĐŋŅŠŅ€Đ˛Đ¸ŅŅ‚ ĐŋŅŠŅ‚, ĐēĐžĐŗĐ°Ņ‚Đž вĐģĐ¸ĐˇĐ°Ņ‚Đĩ в ŅĐ¸ŅŅ‚ĐĩĐŧĐ°Ņ‚Đ°, иĐģи Đĩ ĐŊаĐŋŅ€Đ°Đ˛ĐĩĐŊа ĐˇĐ°ŅĐ˛Đēа Са ĐŋŅ€ĐžĐŧŅĐŊа ĐŊа ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ° ви. МоĐģŅ, Đ˛ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ ĐŊĐžĐ˛Đ°Ņ‚Đ° ĐŋĐ°Ņ€ĐžĐģа ĐŋĐž-Đ´ĐžĐģ҃.", + "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": "ĐĄĐŧĐĩĐŊи 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_logs": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸ ĐģĐžĐŗĐžĐ˛ĐĩŅ‚Đĩ", "choose_matching_people_to_merge": "ИСйĐĩŅ€ĐĩŅ‚Đĩ ĐŋĐžĐ´Ņ…ĐžĐ´ŅŅ‰Đ¸ Ņ…ĐžŅ€Đ° Са ҁĐģиваĐŊĐĩ", "city": "Đ“Ņ€Đ°Đ´", @@ -465,6 +620,14 @@ "clear_all_recent_searches": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚ĐĩŅ‚Đĩ Đ˛ŅĐ¸Ņ‡Đēи ҁĐēĐžŅ€ĐžŅˆĐŊи Ņ‚ŅŠŅ€ŅĐĩĐŊĐ¸Ņ", "clear_message": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ¸ ŅŅŠĐžĐąŅ‰ĐĩĐŊиĐĩŅ‚Đž", "clear_value": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ¸ ŅŅ‚ĐžĐšĐŊĐžŅŅ‚Ņ‚Đ°", + "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_title": "КĐģиĐĩĐŊ҂ҁĐēи SSL ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚", "clockwise": "По Ņ‡Đ°ŅĐžĐ˛ĐŊиĐēĐžĐ˛Đ°Ņ‚Đ° ҁ҂ҀĐĩĐģĐēа", "close": "Đ—Đ°Ņ‚Đ˛ĐžŅ€Đ¸", "collapse": "ХвиваĐŊĐĩ", @@ -475,15 +638,31 @@ "comment_options": "ОĐŋŅ†Đ¸Đ¸ Са ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€", "comments_and_likes": "КоĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸ и Ņ…Đ°Ņ€ĐĩŅĐ˛Đ°ĐŊĐ¸Ņ", "comments_are_disabled": "КоĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ ŅĐ° Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊи", + "common_create_new_album": "ĐĄŅŠĐˇĐ´Đ°Đš ĐŊОв аĐģĐąŅƒĐŧ", + "common_server_error": "МоĐģŅ, ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐĩŅ‚Đĩ ĐŧŅ€ĐĩĐļĐžĐ˛Đ°Ņ‚Đ° Đ˛Ņ€ŅŠĐˇĐēа, ŅƒĐąĐĩĐ´ĐĩŅ‚Đĩ ҁĐĩ, ҇Đĩ ŅŅŠŅ€Đ˛ŅŠŅ€Đ° Đĩ Đ´ĐžŅŅ‚ŅŠĐŋĐĩĐŊ и вĐĩŅ€ŅĐ¸Đ¸Ņ‚Đĩ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° и ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž ŅĐ° ŅŅŠĐ˛ĐŧĐĩŅŅ‚Đ¸Đŧи.", + "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": "ĐŸĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ°", + "confirm_tag_face": "Đ˜ŅĐēĐ°Ņ‚Đĩ Đģи да ĐžŅ‚ĐąĐĩĐģĐĩĐļĐ¸Ņ‚Đĩ Ņ‚ĐžĐ˛Đ° ĐģĐ¸Ņ†Đĩ ĐēĐ°Ņ‚Đž {name}?", + "confirm_tag_face_unnamed": "Đ˜ŅĐēĐ°Ņ‚Đĩ Đģи да ĐžŅ‚ĐąĐĩĐģĐĩĐļĐ¸Ņ‚Đĩ Ņ‚ĐžĐ˛Đ° ĐģĐ¸Ņ†Đĩ?", + "connected_device": "ĐĄĐ˛ŅŠŅ€ĐˇĐ°ĐŊĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž", + "connected_to": "ĐĄĐ˛ŅŠŅ€ĐˇĐ°ĐŊ ĐēҊĐŧ", "contain": "В Ņ€Đ°ĐŧĐēĐ¸Ņ‚Đĩ ĐŊа", "context": "КоĐŊŅ‚ĐĩĐēҁ҂", "continue": "ĐŸŅ€ĐžĐ´ŅŠĐģĐļи", + "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": "ĐĄĐŋОдĐĩĐģи Đ˛Ņ€ŅŠĐˇĐēа", + "control_bottom_app_bar_share_to": "ĐĄĐŋОдĐĩĐģи ҁ", + "control_bottom_app_bar_trash_from_immich": "ĐŸŅ€ĐĩĐŧĐĩŅŅ‚Đ¸ в ĐēĐžŅˆĐ°", "copied_image_to_clipboard": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩŅ‚Đž Đĩ ĐēĐžĐŋĐ¸Ņ€Đ°ĐŊĐž в ĐēĐģиĐŋĐąĐžŅ€Đ´Đ°.", "copied_to_clipboard": "КоĐŋĐ¸Ņ€Đ°ĐŊĐž в ĐēĐģиĐŋĐąĐžŅ€Đ´Đ°!", "copy_error": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐēĐžĐŋĐ¸Ņ€Đ°ĐŊĐĩŅ‚Đž", @@ -498,24 +677,37 @@ "covers": "ОбĐģĐžĐļĐēа", "create": "ĐĄŅŠĐˇĐ´Đ°Đš", "create_album": "ĐĄŅŠĐˇĐ´Đ°Đš аĐģĐąŅƒĐŧ", + "create_album_page_untitled": "БĐĩС ĐˇĐ°ĐŗĐģавиĐĩ", "create_library": "ĐĄŅŠĐˇĐ´Đ°Đš йийĐģĐ¸ĐžŅ‚ĐĩĐēа", "create_link": "ĐĄŅŠĐˇĐ´Đ°Đš ĐģиĐŊĐē", "create_link_to_share": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐģиĐŊĐē Са ҁĐŋОдĐĩĐģŅĐŊĐĩ", "create_link_to_share_description": "ПозвоĐģĐĩŅ‚Đĩ ĐŊа Đ˛ŅĐĩĐēи, ĐēĐžĐšŅ‚Đž иĐŧа ĐģиĐŊĐē, да види Đ¸ĐˇĐąŅ€Đ°ĐŊĐ°Ņ‚Đ°(Đ¸Ņ‚Đĩ) ҁĐŊиĐŧĐēа(и)", + "create_new": "ĐĄĐĒЗДАЙ НОВ", "create_new_person": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐŊОвО ĐģĐ¸Ņ†Đĩ", "create_new_person_hint": "ĐŸŅ€Đ¸ŅĐ˛ĐžĐšŅ‚Đĩ Đ¸ĐˇĐąŅ€Đ°ĐŊĐ¸Ņ‚Đĩ Ņ„Đ°ĐšĐģОвĐĩ ĐŊа ĐŊОв Ņ‡ĐžĐ˛ĐĩĐē", "create_new_user": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐŊОв ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ", + "create_shared_album_page_share_add_assets": "ДОБАВИ ОБЕКĐĸИ", + "create_shared_album_page_share_select_photos": "ИСйĐĩŅ€Đ¸ ҁĐŊиĐŧĐēи", "create_tag": "ĐĄŅŠĐˇĐ´Đ°Đš Ņ‚Đ°Đŗ", "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, dd MMM", + "daily_title_text_date_year": "E, dd MMM yyyy", "dark": "ĐĸҊĐŧĐĩĐŊ", + "darkTheme": "ĐŸŅ€ĐĩвĐēĐģŅŽŅ‡Đ¸ ĐŊа Ņ‚ŅŠĐŧĐŊа Ņ‚ĐĩĐŧа", "date_after": "Đ”Đ°Ņ‚Đ° ҁĐģĐĩĐ´", "date_and_time": "Đ”Đ°Ņ‚Đ° и Ņ‡Đ°Ņ", "date_before": "Đ”Đ°Ņ‚Đ° ĐŋŅ€Đĩди", + "date_format": "E, d LLL y â€ĸ h:mm a", "date_of_birth_saved": "Đ”Đ°Ņ‚Đ° ĐŊа Ņ€Đ°ĐļдаĐŊĐĩ Đĩ СаĐŋĐ¸ŅĐ°ĐŊа ҃ҁĐŋĐĩ҈ĐŊĐž", "date_range": "ПĐĩŅ€Đ¸ĐžĐ´ ĐžŅ‚ Đ˛Ņ€ĐĩĐŧĐĩ", "day": "ДĐĩĐŊ", @@ -525,28 +717,41 @@ "deduplication_info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Са Đ´ĐĩĐ´ŅƒĐŋĐģиĐēĐ°Ņ†Đ¸ŅŅ‚Đ°", "deduplication_info_description": "За Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋŅ€ĐĩĐ´Đ˛Đ°Ņ€Đ¸Ņ‚ĐĩĐģĐŊĐž Đ¸ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа Ņ€ĐĩŅŅƒŅ€ŅĐ¸ и ĐŋŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐŊа Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸ ĐŊа ĐĩĐ´Ņ€Đž, Ņ€Đ°ĐˇĐŗĐģĐĩĐļдаĐŧĐĩ:", "default_locale": "ЛоĐēаĐģĐ¸ĐˇĐ°Ņ†Đ¸Ņ ĐŋĐž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ", - "default_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ´Đ°Ņ‚Đ¸ и Ņ‡Đ¸ŅĐģа в ĐˇĐ°Đ˛Đ¸ŅĐ¸ĐŧĐžŅŅ‚ ĐžŅ‚ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž ĐŊа ĐąŅ€Đ°ŅƒĐˇŅŠŅ€Đ°", + "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": "ĐŅĐēОи ĐžŅ‚ Ņ‚ĐĩСи ОйĐĩĐēŅ‚Đ¸ ĐŊĐĩ ŅĐ° Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊи ĐŊа Immich ŅŅŠŅ€Đ˛ŅŠŅ€Đ° и ҉Đĩ ĐąŅŠĐ´Đ°Ņ‚ ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐ°Ņ‚Đ¸ СавиĐŊĐ°ĐŗĐ¸ ĐžŅ‚ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ĐžŅ‚Đž", + "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_local_dialog_ok_force": "Đ’ŅŠĐŋŅ€ĐĩĐēи Ņ‚ĐžĐ˛Đ° Đ¸ĐˇŅ‚Ņ€Đ¸Đš", "delete_others": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš ĐžŅŅ‚Đ°ĐŊаĐģĐ¸Ņ‚Đĩ", "delete_shared_link": "Đ˜ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊ ĐģиĐŊĐē", + "delete_shared_link_dialog_title": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš ҁĐŋОдĐĩĐģĐĩĐŊĐ°Ņ‚Đ° Đ˛Ņ€ŅŠĐˇĐēа", "delete_tag": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš Ņ‚Đ°Đŗ", "delete_tag_confirmation_prompt": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ Ņ‚Đ°Đŗ {tagName}?", "delete_user": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ", "deleted_shared_link": "Đ˜ĐˇŅ‚Ņ€Đ¸Ņ‚ ҁĐŋОдĐĩĐģĐĩĐŊ ĐģиĐŊĐē", "deletes_missing_assets": "Đ˜ĐˇŅ‚Ņ€Đ¸Đ˛Đ° Ņ„Đ°ĐšĐģОвĐĩ, ĐēĐžĐ¸Ņ‚Đž ĐģиĐŋŅĐ˛Đ°Ņ‚ ĐŊа Đ´Đ¸ŅĐēа", "description": "ОĐŋĐ¸ŅĐ°ĐŊиĐĩ", + "description_input_hint_text": "Добави ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ...", + "description_input_submit_error": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ОйĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩŅ‚Đž. За ĐŋĐžĐ´Ņ€ĐžĐąĐŊĐžŅŅ‚Đ¸ виĐļ в Đ´ĐŊĐĩвĐŊиĐēа", "details": "ДĐĩŅ‚Đ°ĐšĐģи", "direction": "ĐŸĐžŅĐžĐēа", "disabled": "ИСĐēĐģŅŽŅ‡ĐĩĐŊĐž", "disallow_edits": "Đ—Đ°ĐąŅ€Đ°ĐŊŅĐ˛Đ°ĐŊĐĩ ĐŊа Ņ€ĐĩдаĐēŅ†Đ¸Đ¸Ņ‚Đĩ", + "discord": "НаĐŧĐĩŅ€Đ¸ ĐŊи в Discord", "discover": "ĐžŅ‚ĐēŅ€Đ¸Đš", + "discovered_devices": "ĐžŅ‚ĐēŅ€Đ¸Ņ‚Đ¸ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "dismiss_all_errors": "ĐžŅ‚Ņ…Đ˛ŅŠŅ€ĐģŅĐŊĐĩ ĐŊа Đ˛ŅĐ¸Ņ‡Đēи ĐŗŅ€Đĩ҈Đēи", "dismiss_error": "ĐžŅ‚Ņ…Đ˛ŅŠŅ€ĐģŅĐŊĐĩ ĐŊа ĐŗŅ€Đĩ҈Đēа", "display_options": "ОĐŋŅ†Đ¸Đ¸ Са ĐŋĐžĐēаСваĐŊĐĩ", @@ -557,12 +762,25 @@ "documentation": "ДоĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ", "done": "Đ“ĐžŅ‚ĐžĐ˛Đž", "download": "Đ˜ĐˇŅ‚ĐĩĐŗĐģи", + "download_canceled": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩŅ‚Đž Đĩ ĐžŅ‚ĐŧĐĩĐŊĐĩĐŊĐž", + "download_complete": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩŅ‚Đž ĐˇĐ°Đ˛ŅŠŅ€ŅˆĐ¸", + "download_enqueue": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩŅ‚Đž Đĩ дОйавĐĩĐŊĐž в ĐžĐŋĐ°ŅˆĐēĐ°Ņ‚Đ°", + "download_error": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ¸ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ", + "download_failed": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž Đ¸ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ", + "download_finished": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩŅ‚Đž ĐˇĐ°Đ˛ŅŠŅ€ŅˆĐ¸", "download_include_embedded_motion_videos": "Đ’ĐŗŅ€Đ°Đ´ĐĩĐŊи видĐĩа", "download_include_embedded_motion_videos_description": "ВĐēĐģŅŽŅ‡ĐĩŅ‚Đĩ видĐĩĐ°Ņ‚Đ°, Đ˛ĐŗŅ€Đ°Đ´ĐĩĐŊи в диĐŊаĐŧĐ¸Ņ‡ĐŊи ҁĐŊиĐŧĐēи, ĐēĐ°Ņ‚Đž ĐžŅ‚Đ´ĐĩĐģĐĩĐŊ Ņ„Đ°ĐšĐģ", + "download_notfound": "НĐĩ Đĩ ĐŊаĐŧĐĩŅ€ĐĩĐŊĐž Са Đ¸ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ", + "download_paused": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩŅ‚Đž Đĩ ĐŊа ĐŋĐ°ŅƒĐˇĐ°", "download_settings": "Đ˜ĐˇŅ‚ĐĩĐŗĐģи", "download_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ, ŅĐ˛ŅŠŅ€ĐˇĐ°ĐŊи ҁ Đ¸ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩŅ‚Đž ĐŊа Ņ„Đ°ĐšĐģОвĐĩ", + "download_started": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩŅ‚Đž СаĐŋĐžŅ‡ĐŊа", + "download_sucess": "ĐŖŅĐŋĐĩ҈ĐŊĐž Đ¸ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ", + "download_sucess_android": "ФаКĐģОвĐĩŅ‚Đĩ ŅĐ° Đ¸ĐˇŅ‚ĐĩĐŗĐģĐĩĐŊи в DCIM/Immich", + "download_waiting_to_retry": "Đ˜ĐˇŅ‡Đ°ĐēваĐŊĐĩ Са ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐĩĐŊиĐĩ", "downloading": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ", "downloading_asset_filename": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ ĐŊа Ņ„Đ°ĐšĐģ {filename}", + "downloading_media": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ ĐŊа ĐŧĐĩĐ´Đ¸Ņ", "drop_files_to_upload": "ĐŸŅƒŅĐŊĐĩŅ‚Đĩ Ņ„Đ°ĐšĐģОвĐĩŅ‚Đĩ, Са да ĐŗĐ¸ ĐēĐ°Ņ‡Đ¸Ņ‚Đĩ", "duplicates": "Đ”ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸", "duplicates_description": "ИСйĐĩŅ€ĐĩŅ‚Đĩ Đ˛ŅŅĐēа ĐŗŅ€ŅƒĐŋа, ĐēĐ°Ņ‚Đž ĐŋĐžŅĐžŅ‡Đ¸Ņ‚Đĩ ĐēОи, аĐēĐž иĐŧа Ņ‚Đ°Đēива, ŅĐ° Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸", @@ -572,6 +790,8 @@ "edit_avatar": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ°Đ˛Đ°Ņ‚Đ°Ņ€", "edit_date": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ´Đ°Ņ‚Đ°", "edit_date_and_time": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ´Đ°Ņ‚Đ° и Ņ‡Đ°Ņ", + "edit_description": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°Đš ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", + "edit_description_prompt": "МоĐģŅ, иСйĐĩŅ€Đ¸ ĐŊОвО ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ:", "edit_exclusion_pattern": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ŅˆĐ°ĐąĐģĐžĐŊ Са иСĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ", "edit_faces": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐģĐ¸Ņ†Đ°", "edit_import_path": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŋŅŠŅ‚Ņ Са иĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ", @@ -579,6 +799,7 @@ "edit_key": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐēĐģŅŽŅ‡", "edit_link": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐģиĐŊĐē", "edit_location": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", + "edit_location_dialog_title": "МĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", "edit_name": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа иĐŧĐĩ", "edit_people": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Ņ…ĐžŅ€Đ°", "edit_tag": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°Đš Ņ‚Đ°Đŗ", @@ -591,14 +812,24 @@ "editor_crop_tool_h2_aspect_ratios": "ĐĄŅŠĐžŅ‚ĐŊĐžŅˆĐĩĐŊĐ¸Ņ ĐŊа ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ‚Đĩ", "editor_crop_tool_h2_rotation": "Đ—Đ°Đ˛ŅŠŅ€Ņ‚Đ°ĐŊĐĩ", "email": "ИĐŧĐĩĐšĐģ", + "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}", + "error_tag_face_bounding_box": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐžŅ‚ĐąĐĩĐģŅĐˇĐ˛Đ°ĐŊĐĩ ĐŊа ĐģĐ¸Ņ†Đĩ - ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋĐžĐģŅƒŅ‡Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ¸ ĐŊа Ņ€Đ°ĐŧĐēĐ°Ņ‚Đ°", "error_title": "Đ“Ņ€Đĩ҈Đēа - ĐŊĐĩŅ‰Đž ҁĐĩ ĐžĐąŅŠŅ€Đēа", "errors": { "cannot_navigate_next_asset": "НĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ да ĐŋŅ€ĐĩĐŧиĐŊĐĩŅ‚Đĩ ĐēҊĐŧ ҁĐģĐĩĐ´Đ˛Đ°Ņ‰Đ¸Ņ Ņ„Đ°ĐšĐģ", @@ -611,7 +842,6 @@ "cant_get_number_of_comments": "НĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ĐŋĐžĐģŅƒŅ‡Đ¸ ĐąŅ€ĐžŅŅ‚ ĐŊа ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ", "cant_search_people": "НĐĩ ĐŧĐžĐļĐĩ да Ņ‚ŅŠŅ€ŅĐ¸ Ņ…ĐžŅ€Đ°", "cant_search_places": "НĐĩ ĐŧĐžĐļĐĩ да Ņ‚ŅŠŅ€ŅĐ¸ ĐŧĐĩŅŅ‚Đ°", - "cleared_jobs": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚ĐĩĐŊи ĐˇĐ°Đ´Đ°Ņ‡Đ¸ Са: {job}", "error_adding_assets_to_album": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ´ĐžĐąĐ°Đ˛ŅĐŊĐĩŅ‚Đž ĐŊа Ņ„Đ°ĐšĐģОвĐĩŅ‚Đĩ в аĐģĐąŅƒĐŧа", "error_adding_users_to_album": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ´ĐžĐąĐ°Đ˛ŅĐŊĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи в аĐģĐąŅƒĐŧ", "error_deleting_shared_user": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ", @@ -620,7 +850,6 @@ "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": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊа Đ˛Ņ€ŅŠĐˇĐēа", @@ -628,16 +857,17 @@ "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 {# ĐŋŅŠŅ‚Đ¸Ņ‰Đ°}} ĐŊĐĩ ĐŋŅ€ĐĩĐŧиĐŊĐ°Ņ…Đ° ваĐģĐ¸Đ´Đ°Ņ†Đ¸Ņ", "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": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž Đ´ĐžĐąĐ°Đ˛ŅĐŊĐĩ ĐŊа ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€", @@ -649,13 +879,13 @@ "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 {# Ņ‡ĐžĐ˛ĐĩĐē} other {# Ņ‡ĐžĐ˛ĐĩĐēа}}", "unable_to_complete_oauth_login": "НĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ĐˇĐ°Đ˛ŅŠŅ€ŅˆĐ¸ OAuth вĐģиСаĐŊĐĩ", "unable_to_connect": "НĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ŅĐ˛ŅŠŅ€ĐļĐĩ", - "unable_to_connect_to_server": "НĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ŅĐ˛ŅŠŅ€ĐļĐĩ ҁҊҁ ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "unable_to_copy_to_clipboard": "НĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ĐēĐžĐŋĐ¸Ņ€Đ° в ĐēĐģиĐŋĐąĐžŅ€Đ´Đ°, ŅƒĐ˛ĐĩŅ€ĐĩŅ‚Đĩ ҁĐĩ, ҇Đĩ иĐŧĐ°Ņ‚Đĩ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ°Ņ‚Đ° ĐŋŅ€ĐĩС https", "unable_to_create_admin_account": "НĐĩ ĐŧĐžĐļĐĩ да ŅŅŠĐˇĐ´Đ°Đ´Đĩ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ŅĐēи аĐēĐ°ŅƒĐŊŅ‚", "unable_to_create_api_key": "НĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ŅŅŠĐˇĐ´Đ°Đ´Đĩ ĐŊОв API ĐēĐģŅŽŅ‡", @@ -679,10 +909,6 @@ "unable_to_hide_person": "НĐĩ ĐŧĐžĐļĐĩ да ҁĐēŅ€Đ¸Đĩ Ņ‡ĐžĐ˛ĐĩĐē", "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", @@ -693,12 +919,11 @@ "unable_to_remove_album_users": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи ĐžŅ‚ аĐģĐąŅƒĐŧа", "unable_to_remove_api_key": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐŊа API Key", "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_trash": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐžŅ‚ ĐēĐžŅˆŅ‡ĐĩŅ‚Đž", @@ -726,8 +951,21 @@ "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_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": "В Ņ€Đ°ĐˇĐ˛Đ¸Ņ‚Đ¸Đĩ", + "experimental_settings_new_asset_list_title": "ВĐēĐģŅŽŅ‡Đ¸ ĐĩĐēҁĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģĐŊа ĐŋĐžĐ´Ņ€Đĩдйа ĐŊа ҁĐŊиĐŧĐēи", + "experimental_settings_subtitle": "ИСĐŋĐžĐģĐˇĐ˛Đ°ĐšŅ‚Đĩ ĐŊа ŅĐ˛ĐžĐš Ņ€Đ¸ŅĐē!", + "experimental_settings_title": "ЕĐēҁĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģĐŊи", "expire_after": "Đ˜ĐˇŅ‚Đ¸Ņ‡Đ° ҁĐģĐĩĐ´", "expired": "Đ˜ĐˇŅ‚ĐĩĐēĐģĐž", "expires_date": "Đ˜ĐˇŅ‚Đ¸Ņ‡Đ° {date}", @@ -738,11 +976,17 @@ "extension": "Đ Đ°ĐˇŅˆĐ¸Ņ€ĐĩĐŊиĐĩ", "external": "Đ’ŅŠĐŊ҈ĐŊĐž", "external_libraries": "Đ’ŅŠĐŊ҈ĐŊи йийĐģĐ¸ĐžŅ‚ĐĩĐēи", + "external_network": "Đ’ŅŠĐŊ҈ĐŊа ĐŧŅ€ĐĩĐļа", + "external_network_sheet_info": "ĐšĐžĐŗĐ°Ņ‚Đž ĐŊŅĐŧа Đ˛Ņ€ŅŠĐˇĐēа ҁ ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐ°Ņ‚Đ° Wi-Fi ĐŧŅ€ĐĩĐļа, ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž ҉Đĩ ĐžĐŋĐ¸Ņ‚Đ˛Đ° да ҁĐĩ ŅĐ˛ŅŠŅ€ĐļĐĩ ҁҊҁ ŅŅŠŅ€Đ˛ŅŠŅ€Đ° ҇ҀĐĩС ĐŋŅŠŅ€Đ˛Đ¸Ņ Đ´ĐžŅŅ‚ŅŠĐŋĐĩĐŊ URL Đ°Đ´Ņ€Đĩҁ, СаĐŋĐžŅ‡Đ˛Đ°ĐšĐēи ĐžŅ‚ĐŗĐžŅ€Đĩ ĐŊадОĐģ҃", "face_unassigned": "НĐĩСададĐĩĐŊĐž", + "failed": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž", + "failed_to_authenticate": "НĐĩ҃ҁĐŋĐĩ҈ĐŊа Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸ĐēĐ°Ņ†Đ¸Ņ", "failed_to_load_assets": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", + "failed_to_load_folder": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа ĐŋаĐŋĐēа", "favorite": "Đ›ŅŽĐąĐ¸Đŧ", "favorite_or_unfavorite_photo": "Добави иĐģи ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊи ҁĐŊиĐŧĐēа ĐžŅ‚ Đ›ŅŽĐąĐ¸Đŧи", "favorites": "Đ›ŅŽĐąĐ¸Đŧи", + "favorites_page_no_favorites": "НĐĩ ŅĐ° ĐŊаĐŧĐĩŅ€ĐĩĐŊи ĐģŅŽĐąĐ¸Đŧи ОйĐĩĐēŅ‚Đ¸", "feature_photo_updated": "ĐŸŅ€ĐĩĐ´ŅŅ‚Đ°Đ˛Đ¸Ņ‚ĐĩĐģĐŊĐ°Ņ‚Đ° ҁĐŊиĐŧĐēа Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐĩĐŊа", "features": "Đ¤ŅƒĐŊĐēŅ†Đ¸Đ¸", "features_setting_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸Ņ‚Đĩ ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", @@ -750,25 +994,41 @@ "file_name_or_extension": "ИĐŧĐĩ ĐŊа Ņ„Đ°ĐšĐģ иĐģи Ņ€Đ°ĐˇŅˆĐ¸Ņ€ĐĩĐŊиĐĩ", "filename": "ИĐŧĐĩ ĐŊа Ņ„Đ°ĐšĐģ", "filetype": "ĐĸиĐŋ ĐŊа Ņ„Đ°ĐšĐģ", + "filter": "ФиĐģŅ‚ŅŠŅ€", "filter_people": "ФиĐģŅ‚Ņ€Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Ņ…ĐžŅ€Đ°", + "filter_places": "ФиĐģŅ‚ŅŠŅ€ ĐŋĐž ĐŧŅŅŅ‚Đž", "find_them_fast": "НаĐŧĐĩŅ€ĐĩŅ‚Đĩ ĐŗĐ¸ ĐąŅŠŅ€ĐˇĐž ĐŋĐž иĐŧĐĩ ҁ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ", "fix_incorrect_match": "ПоĐŋŅ€Đ°Đ˛ŅĐŊĐĩ ĐŊа ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģĐŊĐž ŅŅŠĐ˛ĐŋадĐĩĐŊиĐĩ", + "folder": "ПаĐŋĐēа", + "folder_not_found": "ПаĐŋĐēĐ°Ņ‚Đ° ĐŊĐĩ Đĩ ĐŊаĐŧĐĩŅ€ĐĩĐŊа", "folders": "ПаĐŋĐēи", "folders_feature_description": "ĐŸŅ€ĐĩĐŗĐģĐĩĐļдаĐŊĐĩ ĐŊа ĐŋаĐŋĐēĐ°Ņ‚Đ° Са ҁĐŊиĐŧĐēĐ¸Ņ‚Đĩ и видĐĩĐžĐēĐģиĐŋОвĐĩŅ‚Đĩ в Ņ„Đ°ĐšĐģĐžĐ˛Đ°Ņ‚Đ° ŅĐ¸ŅŅ‚ĐĩĐŧа", "forward": "НаĐŋŅ€ĐĩĐ´", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "За да Ņ€Đ°ĐąĐžŅ‚Đ¸ Ņ‚Đ°ĐˇĐ¸ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ ĐˇĐ°Ņ€ĐĩĐļда Đ˛ŅŠĐŊ҈ĐŊи Ņ€ĐĩŅŅƒŅ€ŅĐ¸ ĐžŅ‚ Google.", "general": "ĐžĐąŅ‰Đ¸", "get_help": "ПоĐŧĐžŅ‰", + "get_wifiname_error": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋĐžĐģŅƒŅ‡Đ°Đ˛Đ°ĐŊĐĩ иĐŧĐĩŅ‚Đž ĐŊа Wi-Fi ĐŧŅ€ĐĩĐļĐ°Ņ‚Đ°. МоĐģŅ, ŅƒĐąĐĩĐ´ĐĩŅ‚Đĩ ҁĐĩ, ҇Đĩ ŅĐ° ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ĐĩĐŊи ĐŊ҃ĐļĐŊĐ¸Ņ‚Đĩ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐ¸Ņ ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž и иĐŧа Đ˛Ņ€ŅŠĐˇĐēа ҁ Wi-Fi", "getting_started": "КаĐē да СаĐŋĐžŅ‡ĐŊĐĩĐŧ", "go_back": "Đ’Ņ€ŅŠŅ‰Đ°ĐŊĐĩ ĐŊаСад", "go_to_folder": "ĐžŅ‚Đ¸Đ´Đ¸ в ĐŋаĐŋĐēĐ°Ņ‚Đ°", "go_to_search": "ĐŸŅ€ĐĩĐŧиĐŊаваĐŊĐĩ ĐēҊĐŧ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ", + "grant_permission": "Дай Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ", "group_albums_by": "Đ“Ņ€ŅƒĐŋĐ¸Ņ€Đ°Đš аĐģĐąŅƒĐŧ ĐŋĐž...", "group_country": "Đ“Ņ€ŅƒĐŋĐ¸Ņ€Đ°Đš ĐŋĐž Đ´ŅŠŅ€Đļава", "group_no": "ĐŅĐŧа ĐŗŅ€ŅƒĐŋа", "group_owner": "Đ“Ņ€ŅƒĐŋĐ¸Ņ€Đ°ĐŊĐĩ ĐŋĐž ŅĐžĐąŅŅ‚Đ˛ĐĩĐŊиĐē", "group_places_by": "Đ“Ņ€ŅƒĐŋĐ¸Ņ€Đ°Đš ĐŧĐĩŅŅ‚Đ° ĐŋĐžâ€Ļ", "group_year": "Đ“Ņ€ŅƒĐŋĐ¸Ņ€Đ°ĐŊĐĩ ĐŋĐž ĐŗĐžĐ´Đ¸ĐŊа", + "haptic_feedback_switch": "ВĐēĐģŅŽŅ‡Đ¸ Ņ‚Đ°ĐēŅ‚Đ¸ĐģĐŊа ĐžĐąŅ€Đ°Ņ‚ĐŊа Đ˛Ņ€ŅŠĐˇĐēа", + "haptic_feedback_title": "ĐĸаĐēŅ‚Đ¸ĐģĐŊа ĐžĐąŅ€Đ°Ņ‚ĐŊа Đ˛Ņ€ŅŠĐˇĐēа", "has_quota": "ЛиĐŧĐ¸Ņ‚", + "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": "ĐĄĐēŅ€Đ¸Đš ĐŗĐ°ĐģĐĩŅ€Đ¸Ņ", @@ -776,8 +1036,27 @@ "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_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_locked_error_local": "ЛоĐēаĐģĐŊи ОйĐĩĐēŅ‚Đ¸ ĐŊĐĩ ĐŧĐžĐŗĐ°Ņ‚ да ҁĐĩ ĐŋŅ€ĐĩĐŧĐĩŅŅ‚ŅŅ‚ в СаĐēĐģŅŽŅ‡ĐĩĐŊа ĐŋаĐŋĐēа, ĐŋŅ€ĐžĐŋ҃ҁĐēаĐŊĐĩ", + "home_page_locked_error_partner": "ĐŸĐ°Ņ€Ņ‚ĐŊŅŒĐžŅ€ŅĐēи ОйĐĩĐēŅ‚Đ¸ ĐŊĐĩ ĐŧĐžĐŗĐ°Ņ‚ да ҁĐĩ ĐŋŅ€ĐĩĐŧĐĩŅŅ‚ŅŅ‚ в СаĐēĐģŅŽŅ‡ĐĩĐŊа ĐŋаĐŋĐēа, ĐŋŅ€ĐžĐŋ҃ҁĐēаĐŊĐĩ", + "home_page_share_err_local": "ЛоĐēаĐģĐŊи ОйĐĩĐēŅ‚Đ¸ ĐŊĐĩ ĐŧĐžĐŗĐ°Ņ‚ да ҁĐĩ ҁĐŋОдĐĩĐģŅŅ‚ ҇ҀĐĩС Đ˛Ņ€ŅŠĐˇĐēа, ĐŋŅ€ĐžĐŋ҃ҁĐēаĐŊĐĩ", + "home_page_upload_err_limit": "МоĐļĐĩ да ĐēĐ°Ņ‡Đ˛Đ°Ņ‚Đĩ ĐŧаĐēŅĐ¸Đŧ҃Đŧ 30 ОйĐĩĐēŅ‚Đ° ĐĩĐ´ĐŊĐžĐ˛Ņ€ĐĩĐŧĐĩĐŊĐŊĐž, ĐŋŅ€ĐžĐŋ҃ҁĐēаĐŊĐĩ", "host": "ĐĨĐžŅŅ‚", "hour": "Đ§Đ°Ņ", + "id": "ID", + "ignore_icloud_photos": "ĐŸŅ€ĐžĐŋ҃ҁĐŊи ҁĐŊиĐŧĐēи ĐžŅ‚ iCloud", + "ignore_icloud_photos_description": "ĐĄĐŊиĐŧĐēи, ĐēĐžĐ¸Ņ‚Đž ŅĐ° СаĐŋаСĐĩĐŊи в iCloud ĐŊŅĐŧа да ҁĐĩ ĐēĐ°Ņ‡Đ˛Đ°Ņ‚ в Immich ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "image": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ", "image_alt_text_date": "{isVideo, select, true {ВидĐĩĐž} other {ĐĄĐŊиĐŧĐēа}} ĐˇĐ°ŅĐŊĐĩŅ‚Đž ĐŊа {date}", "image_alt_text_date_1_person": "{isVideo, select, true {ВидĐĩĐž} other {ĐĄĐŊиĐŧĐēа}} ĐˇĐ°ŅĐŊĐĩŅ‚Đž ҁ {person1} ĐŊа {date}", @@ -789,6 +1068,10 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {ВидĐĩĐž} other {ĐĄĐŊиĐŧĐēа}} ĐˇĐ°ŅĐŊĐĩŅ‚Đž в {city}, {country} ҁ {person1} и {person2} ĐŊа {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {ВидĐĩĐž} other {ĐĄĐŊиĐŧĐēа}} ĐˇĐ°ŅĐŊĐĩŅ‚Đž ҁ {city}, {country} и {person1}, {person2}, и {person3} ĐŊа {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {ВидĐĩĐž} other {ĐĄĐŊиĐŧĐēа}} ĐˇĐ°ŅĐŊĐĩŅ‚Đž в {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 ĐģĐžĐŗĐž", "immich_web_interface": "ĐŖĐĩĐą иĐŊŅ‚ĐĩҀ҄ĐĩĐšŅ ĐŊа Immich", "import_from_json": "ИĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐžŅ‚ JSON", @@ -807,8 +1090,16 @@ "night_at_midnight": "Đ’ŅŅĐēа вĐĩ҇ĐĩŅ€ в ĐŋĐžĐģ҃ĐŊĐžŅ‰", "night_at_twoam": "Đ’ŅŅĐēа ĐŊĐžŅ‰ в 2 Ņ‡Đ°ŅĐ° ŅŅƒŅ‚Ņ€Đ¸ĐŊŅ‚Đ°" }, + "invalid_date": "НĐĩваĐģидĐŊа Đ´Đ°Ņ‚Đ°", + "invalid_date_format": "НĐĩваĐģидĐĩĐŊ Ņ„ĐžŅ€ĐŧĐ°Ņ‚ Са Đ´Đ°Ņ‚Đ°", "invite_people": "ПоĐēаĐŊи Ņ…ĐžŅ€Đ°", "invite_to_album": "ПоĐēаĐŊи в аĐģĐąŅƒĐŧ", + "ios_debug_info_fetch_ran_at": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ ĐŊа {dateTime}", + "ios_debug_info_last_sync_at": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐž ĐŊа {dateTime}", + "ios_debug_info_no_processes_queued": "ĐŅĐŧа Ņ„ĐžĐŊОви ĐŋŅ€ĐžŅ†ĐĩŅĐ¸ в ĐžĐŋĐ°ŅˆĐēĐ°Ņ‚Đ°", + "ios_debug_info_no_sync_yet": "Đ’ŅĐĩ ĐžŅ‰Đĩ ĐŊĐĩ Đĩ иСĐŋҊĐģĐŊŅĐ˛Đ°ĐŊа ĐˇĐ°Đ´Đ°Ņ‡Đ° Са Ņ„ĐžĐŊОва ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ", + "ios_debug_info_processes_queued": "{count, plural, one {{count} Ņ„ĐžĐŊОв ĐŋŅ€ĐžŅ†Đĩҁ} many {{count} Ņ„ĐžĐŊОви ĐŋŅ€ĐžŅ†ĐĩŅĐ°} other {{count} Ņ„ĐžĐŊОви ĐŋŅ€ĐžŅ†ĐĩŅĐ°}} в ĐžĐŋĐ°ŅˆĐēĐ°Ņ‚Đ°", + "ios_debug_info_processing_ran_at": "ЗаĐŋĐžŅ‡ĐŊĐ°Ņ‚Đ° ĐžĐąŅ€Đ°ĐąĐžŅ‚Đēа ĐŊа {dateTime}", "items_count": "{count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸}}", "jobs": "Đ—Đ°Đ´Đ°Ņ‡Đ¸", "keep": "Đ—Đ°Đ´Ņ€ŅŠĐļ", @@ -817,6 +1108,9 @@ "kept_this_deleted_others": "ЗаĐŋаСи Ņ‚ĐžĐˇĐ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ и Đ´Ņ€ŅƒĐŗĐ¸Ņ‚Đĩ Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸ {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°}}", "keyboard_shortcuts": "Đ‘ŅŠŅ€ĐˇĐ¸ ĐēĐģĐ°Đ˛Đ¸ŅˆĐŊи ĐēĐžĐŧйиĐŊĐ°Ņ†Đ¸Đ¸", "language": "ЕзиĐē", + "language_no_results_subtitle": "ОĐŋĐ¸Ņ‚Đ°ĐšŅ‚Đĩ да ĐēĐžŅ€Đ¸ĐŗĐ¸Ņ€Đ°Ņ‚Đĩ Ņ‚ĐĩŅ€ĐŧиĐŊа ŅĐ¸ Са Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ", + "language_no_results_title": "НĐĩ ŅĐ° ĐŊаĐŧĐĩŅ€ĐĩĐŊи ĐĩĐˇĐ¸Ņ†Đ¸", + "language_search_hint": "ĐĸŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа ĐĩĐˇĐ¸Ņ†Đ¸...", "language_setting_description": "ИСйĐĩŅ€ĐĩŅ‚Đĩ ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊ ĐĩСиĐē", "last_seen": "ĐŸĐžŅĐģĐĩĐ´ĐŊĐž Đ˛Đ¸Đ´ŅĐŊĐž", "latest_version": "ĐŸĐžŅĐģĐĩĐ´ĐŊа вĐĩŅ€ŅĐ¸Ņ", @@ -827,6 +1121,12 @@ "level": "Ниво", "library": "БибĐģĐ¸ĐžŅ‚ĐĩĐēа", "library_options": "ОĐŋŅ†Đ¸Đ¸ ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ°", + "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": "ЛиĐŊĐē ĐēҊĐŧ видĐĩĐž", @@ -836,12 +1136,46 @@ "list": "Đ›Đ¸ŅŅ‚", "loading": "Đ—Đ°Ņ€ĐĩĐļдаĐŊĐĩ", "loading_search_results_failed": "Đ—Đ°Ņ€ĐĩĐļдаĐŊĐĩŅ‚Đž ĐŊа Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž Đĩ ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐž", + "local_asset_cast_failed": "НĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ĐŋŅ€Đĩдава ОйĐĩĐēŅ‚, ĐēĐžĐšŅ‚Đž ĐžŅ‰Đĩ ĐŊĐĩ Đĩ ĐēĐ°Ņ‡ĐĩĐŊ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", + "local_network": "ЛоĐēаĐģĐŊа ĐŧŅ€ĐĩĐļа", + "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": "Đ’ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ ĐŗĐĩĐžĐŗŅ€Đ°Ņ„ŅĐēа ŅˆĐ¸Ņ€Đ¸ĐŊа Ņ‚ŅƒĐē", + "location_picker_longitude_error": "Đ’ŅŠĐ˛Đĩди ĐŋŅ€Đ°Đ˛Đ¸ĐģĐŊа Đ´ŅŠĐģĐļиĐŊа", + "location_picker_longitude_hint": "Đ’ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ ĐŗĐĩĐžĐŗŅ€Đ°Ņ„ŅĐēа Đ´ŅŠĐģĐļиĐŊа Ņ‚ŅƒĐē", + "lock": "ЗаĐēĐģŅŽŅ‡Đ¸", + "locked_folder": "ЗаĐēĐģŅŽŅ‡ĐĩĐŊа ĐŋаĐŋĐēа", "log_out": "ИСĐģиСаĐŊĐĩ", "log_out_all_devices": "ИСĐģиСаĐŊĐĩ ҁ Đ˛ŅĐ¸Ņ‡Đēи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", + "logged_in_as": "ВĐŋĐ¸ŅĐ°ĐŊ ĐēĐ°Ņ‚Đž {user}", "logged_out_all_devices": "ĐŖŅĐŋĐĩ҈ĐŊĐž иСĐģиСаĐŊĐĩ ĐžŅ‚ Đ˛ŅĐ¸Ņ‡Đēи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "logged_out_device": "ĐŖŅĐŋĐĩ҈ĐŊĐž иСĐģиСаĐŊĐĩ ĐžŅ‚ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž", "login": "ВĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ", + "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 Đ°Đ´Ņ€Đĩҁ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", + "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_failed_get_oauth_server_disable": "На Ņ‚ĐžĐˇĐ¸ ŅŅŠŅ€Đ˛ŅŠŅ€ OAuth ĐŊĐĩ Đĩ Đ´ĐžŅŅ‚ŅŠĐŋĐŊа", + "login_form_failed_login": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ, ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐĩŅ‚Đĩ URL, иĐŧĐĩĐšĐģа и ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ°", + "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": "ВĐģиСаĐŊĐĩŅ‚Đž Đĩ Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž.", + "login_password_changed_error": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ОйĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ°", + "login_password_changed_success": "ĐŖŅĐŋĐĩ҈ĐŊĐž ОйĐŊОвĐĩĐŊа ĐŋĐ°Ņ€ĐžĐģа", "logout_all_device_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да иСĐģĐĩСĐĩŅ‚Đĩ ĐžŅ‚ Đ˛ŅĐ¸Ņ‡Đēи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°?", "logout_this_device_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да иСĐģĐĩСĐĩŅ‚Đĩ ĐžŅ‚ Ņ‚ĐžĐ˛Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž?", "longitude": "Đ”ŅŠĐģĐļиĐŊа", @@ -849,6 +1183,7 @@ "loop_videos": "ĐŸĐžĐ˛Ņ‚Đ°Ņ€ŅĐŊĐĩ ĐŊа видĐĩĐ°Ņ‚Đ°", "loop_videos_description": "ПозвоĐģи Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋĐžĐ˛Ņ‚Đ°Ņ€ŅĐŊĐĩ ĐŊа видĐĩĐžŅ‚Đž в Đ¸ĐˇĐŗĐģĐĩда ĐŊа Đ´ĐĩŅ‚Đ°ĐšĐģĐ¸Ņ‚Đĩ.", "main_branch_warning": "ИСĐŋĐžĐģĐˇĐ˛Đ°Ņ‚Đĩ вĐĩŅ€ŅĐ¸Ņ Са Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Ņ‡Đ¸Ņ†Đ¸, ŅĐ¸ĐģĐŊĐž ĐŋŅ€ĐĩĐŋĐžŅ€ŅŠŅ‡Đ˛Đ°ĐŧĐĩ да иСĐŋĐžĐģĐˇĐ˛Đ°Ņ‚Đĩ ĐžŅ„Đ¸Ņ†Đ¸Đ°ĐģĐŊа вĐĩŅ€ŅĐ¸Ņ!", + "main_menu": "ГĐģавĐŊĐž ĐŧĐĩĐŊŅŽ", "make": "ĐœĐ°Ņ€Đēа", "manage_shared_links": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊи Đ˛Ņ€ŅŠĐˇĐēи", "manage_sharing_with_partners": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ҁĐŋОдĐĩĐģŅĐŊĐĩŅ‚Đž ҁ ĐŋĐ°Ņ€Ņ‚ĐŊŅŒĐžŅ€Đ¸", @@ -858,13 +1193,41 @@ "manage_your_devices": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа вĐģĐĩСĐģĐ¸Ņ‚Đĩ в ŅĐ¸ŅŅ‚ĐĩĐŧĐ°Ņ‚Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "manage_your_oauth_connection": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа OAuth Đ˛Ņ€ŅŠĐˇĐēĐ°Ņ‚Đ°", "map": "ĐšĐ°Ņ€Ņ‚Đ°", + "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": "ĐŖŅĐģŅƒĐŗĐ°Ņ‚Đ° Са ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ Đĩ иСĐēĐģŅŽŅ‡ĐĩĐŊа", "map_marker_for_images": "ĐœĐ°Ņ€ĐēĐĩŅ€Đ¸ ĐŊа ĐēĐ°Ņ€Ņ‚Đ°Ņ‚Đ° Са ҁĐŊиĐŧĐēи ĐŊаĐŋŅ€Đ°Đ˛ĐĩĐŊи в {city}, {country}", "map_marker_with_image": "ĐœĐ°Ņ€ĐēĐĩŅ€ ĐŊа ĐēĐ°Ņ€Ņ‚Đ°Ņ‚Đ° ҁ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ", + "map_no_assets_in_bounds": "ĐŅĐŧа ҁĐŊиĐŧĐēи ĐžŅ‚ Ņ‚ĐžĐˇĐ¸ Ņ€Đ°ĐšĐžĐŊ", + "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": "ĐŸĐžŅĐģĐĩĐ´ĐŊĐ¸Ņ‚Đĩ {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": "ĐĸОва Đĩ Đ˛ŅĐ¸Ņ‡ĐēĐž Са Đ´ĐŊĐĩҁ", + "memories_check_back_tomorrow": "За ĐžŅ‰Đĩ ҁĐŋĐžĐŧĐĩĐŊи ĐŋĐžĐŗĐģĐĩĐ´ĐŊи ŅƒŅ‚Ņ€Đĩ ĐŋаĐē", "memories_setting_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа Ņ‚ĐžĐ˛Đ°, ĐēĐžĐĩŅ‚Đž виĐļĐ´Đ°Ņ‚Đĩ в ҁĐŋĐžĐŧĐĩĐŊи", + "memories_start_over": "ЗаĐŋĐžŅ‡ĐŊи ĐžŅ‚ĐŊОвО", + "memories_swipe_to_close": "За да ĐˇĐ°Ņ‚Đ˛ĐžŅ€Đ¸Ņˆ ĐŋĐģŅŠĐˇĐŊи ĐŊĐ°ĐŗĐžŅ€Đĩ", "memory": "ПаĐŧĐĩŅ‚", "memory_lane_title": "ĐĄĐŋĐžĐŧĐĩĐŊи Са {title}", "menu": "МĐĩĐŊŅŽ", @@ -879,60 +1242,87 @@ "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": "НĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€Đ° ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž ĐŊа ОйĐĩĐēŅ‚ ŅĐ°ĐŧĐž Са ҇ĐĩŅ‚ĐĩĐŊĐĩ, ĐŋŅ€ĐžĐŋ҃ҁĐēаĐŊĐĩ", "mute_memories": "ИСĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа ĐˇĐ˛ŅƒĐēа ĐŊа ҁĐŋĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ", "my_albums": "Мои аĐģĐąŅƒĐŧи", "name": "ИĐŧĐĩ", "name_or_nickname": "ИĐŧĐĩ иĐģи ĐŋŅ€ŅĐēĐžŅ€", + "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": "Най-ĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐŋŅŠŅ€Đ˛Đ¸", "next": "ĐĄĐģĐĩĐ´Đ˛Đ°Ņ‰Đž", "next_memory": "ĐĄĐģĐĩĐ´Đ˛Đ°Ņ‰ ҁĐŋĐžĐŧĐĩĐŊ", "no": "НĐĩ", - "no_albums_message": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа аĐģĐąŅƒĐŧ Са ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа ҁĐŊиĐŧĐēи и видĐĩĐžĐēĐģиĐŋОвĐĩ", + "no_albums_message": "ĐĄŅŠĐˇĐ´Đ°ĐšŅ‚Đĩ аĐģĐąŅƒĐŧ Са ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа ҁĐŊиĐŧĐēи и видĐĩĐžĐēĐģиĐŋОвĐĩ", "no_albums_with_name_yet": "Đ˜ĐˇĐŗĐģĐĩĐļда, ҇Đĩ Đ˛ŅĐĩ ĐžŅ‰Đĩ ĐŊŅĐŧĐ°Ņ‚Đĩ аĐģĐąŅƒĐŧи ҁ Ņ‚ĐžĐ˛Đ° иĐŧĐĩ.", "no_albums_yet": "Đ˜ĐˇĐŗĐģĐĩĐļда, ҇Đĩ Đ˛ŅĐĩ ĐžŅ‰Đĩ ĐŊŅĐŧĐ°Ņ‚Đĩ аĐģĐąŅƒĐŧи.", "no_archived_assets_message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐšŅ‚Đĩ ҁĐŊиĐŧĐēи и видĐĩĐžĐēĐģиĐŋОвĐĩ, Са да ĐŗĐ¸ ҁĐēŅ€Đ¸ĐĩŅ‚Đĩ ĐžŅ‚ Đ¸ĐˇĐŗĐģĐĩда ĐŊа ĐĄĐŊиĐŧĐēи", "no_assets_message": "КЛИКНЕĐĸЕ, ЗА ДА КАЧИĐĸЕ ПĐĒРВАĐĸА ХИ СНИМКА", + "no_assets_to_show": "ĐŅĐŧа ОйĐĩĐēŅ‚Đ¸ Са ĐŋĐžĐēаСваĐŊĐĩ", + "no_cast_devices_found": "ĐŅĐŧа ĐŊаĐŧĐĩŅ€ĐĩĐŊи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ° Са ĐŋŅ€ĐĩдаваĐŊĐĩ", "no_duplicates_found": "НĐĩ ĐąŅŅ…Đ° ĐžŅ‚ĐēŅ€Đ¸Ņ‚Đ¸ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸.", "no_exif_info_available": "ĐŅĐŧа exif иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ", "no_explore_results_message": "ĐšĐ°Ņ‡ĐĩŅ‚Đĩ ĐžŅ‰Đĩ ҁĐŊиĐŧĐēи, Са да Ņ€Đ°ĐˇĐŗĐģĐĩĐ´Đ°Ņ‚Đĩ ĐēĐžĐģĐĩĐēŅ†Đ¸ŅŅ‚Đ° ŅĐ¸.", - "no_favorites_message": "Đ”ĐžĐąĐ°Đ˛ŅĐŊĐĩ ĐŊа ĐģŅŽĐąĐ¸Đŧи, Са да ĐŊаĐŧĐĩŅ€Đ¸Ņ‚Đĩ ĐąŅŠŅ€ĐˇĐž ĐŊаК-Đ´ĐžĐąŅ€Đ¸Ņ‚Đĩ ŅĐ¸ ҁĐŊиĐŧĐēи и видĐĩĐžĐēĐģиĐŋОвĐĩ", - "no_libraries_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": "НĐĩ Đĩ Đ¸ĐˇĐąŅ€Đ°ĐŊĐž", "note_apply_storage_label_to_previously_uploaded assets": "ЗабĐĩĐģĐĩĐļĐēа: За да ĐŋŅ€Đ¸ĐģĐžĐļĐ¸Ņ‚Đĩ ĐĩŅ‚Đ¸ĐēĐĩŅ‚Đ° Са ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ ĐēҊĐŧ ĐŋŅ€ĐĩĐ´Đ˛Đ°Ņ€Đ¸Ņ‚ĐĩĐģĐŊĐž ĐēĐ°Ņ‡ĐĩĐŊи аĐēŅ‚Đ¸Đ˛Đ¸, ŅŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°ĐšŅ‚Đĩ", "notes": "БĐĩĐģĐĩĐļĐēи", - "notification_toggle_setting_description": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа иĐŧĐĩĐšĐģ иСвĐĩŅŅ‚Đ¸Ņ", + "nothing_here_yet": "Đ—Đ°ŅĐĩĐŗĐ° Ņ‚ŅƒĐē ĐŊŅĐŧа ĐŊĐ¸Ņ‰Đž", + "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", "offline": "ĐžŅ„ĐģаКĐŊ", - "offline_paths": "ĐžŅ„ĐģаКĐŊ ĐŋŅŠŅ‚Đ¸Ņ‰Đ°", - "offline_paths_description": "ĐĸĐĩСи Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸ ĐŧĐžĐļĐĩ да ҁĐĩ Đ´ŅŠĐģĐļĐ°Ņ‚ ĐŊа Ņ€ŅŠŅ‡ĐŊĐž Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Ņ„Đ°ĐšĐģОвĐĩ, ĐēĐžĐ¸Ņ‚Đž ĐŊĐĩ ŅĐ° Ņ‡Đ°ŅŅ‚ ĐžŅ‚ Đ˛ŅŠĐŊ҈ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēа.", "ok": "Đ”ĐžĐąŅ€Đĩ", "oldest_first": "Най-ŅŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ ĐŋŅŠŅ€Đ˛Đ¸", + "on_this_device": "На Ņ‚ĐžĐ˛Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž", "onboarding": "Đ’ŅŠĐ˛ĐĩĐļдаĐŊĐĩ", - "onboarding_privacy_description": "ĐĄĐģĐĩĐ´ĐŊĐ¸Ņ‚Đĩ (ĐŊĐĩĐˇĐ°Đ´ŅŠĐģĐļĐ¸Ņ‚ĐĩĐģĐŊи) Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ Ņ€Đ°ĐˇŅ‡Đ¸Ņ‚Đ°Ņ‚ ĐŊа Đ˛ŅŠĐŊ҈ĐŊи ҃ҁĐģŅƒĐŗĐ¸ и ĐŧĐžĐŗĐ°Ņ‚ да ĐąŅŠĐ´Đ°Ņ‚ Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊи ĐŋĐž Đ˛ŅŅĐēĐž Đ˛Ņ€ĐĩĐŧĐĩ в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ¸Ņ€Đ°ĐŊĐĩ.", - "onboarding_theme_description": "ИСйĐĩŅ€ĐĩŅ‚Đĩ Ņ†Đ˛ĐĩŅ‚ĐžĐ˛Đ° Ņ‚ĐĩĐŧа Са Đ˛Đ°ŅˆĐ¸Ņ ĐŋŅ€Đ¸ĐŧĐĩŅ€. МоĐļĐĩŅ‚Đĩ да Ņ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ ĐŋĐž-ĐēҊҁĐŊĐž в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ ŅĐ¸.", - "onboarding_welcome_description": "НĐĩĐēа ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Đŧ Đ˛Đ°ŅˆĐ°Ņ‚Đ° иĐŊŅŅ‚Đ°ĐŊŅ†Đ¸Ņ ҁ ĐŊŅĐēОи ĐžĐąŅ‰Đ¸ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи.", + "onboarding_locale_description": "ИСйĐĩŅ€ĐĩŅ‚Đĩ ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊ ĐĩСиĐē. По-ĐēҊҁĐŊĐž ĐŧĐžĐļĐĩ да ĐŗĐž ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ в ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи.", + "onboarding_privacy_description": "ĐĄĐģĐĩĐ´ĐŊĐ¸Ņ‚Đĩ (ĐŊĐĩĐˇĐ°Đ´ŅŠĐģĐļĐ¸Ņ‚ĐĩĐģĐŊи) Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ Ņ€Đ°ĐˇŅ‡Đ¸Ņ‚Đ°Ņ‚ ĐŊа Đ˛ŅŠĐŊ҈ĐŊи ҃ҁĐģŅƒĐŗĐ¸ и ĐŧĐžĐŗĐ°Ņ‚ да ĐąŅŠĐ´Đ°Ņ‚ Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊи ĐŋĐž Đ˛ŅŅĐēĐž Đ˛Ņ€ĐĩĐŧĐĩ в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ.", + "onboarding_server_welcome_description": "Да ĐŊаĐŋŅ€Đ°Đ˛Đ¸Đŧ ĐžĐąŅ‰Đ¸Ņ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊа Đ˛Đ°ŅˆĐ°Ņ‚Đ° иĐŊŅŅ‚Đ°ĐģĐ°Ņ†Đ¸Ņ.", + "onboarding_theme_description": "ИСйĐĩŅ€ĐĩŅ‚Đĩ Ņ†Đ˛ĐĩŅ‚ĐžĐ˛Đ° Ņ‚ĐĩĐŧа. МоĐļĐĩ да Ņ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ ĐŋĐž-ĐēҊҁĐŊĐž в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ.", + "onboarding_user_welcome_description": "Да СаĐŋĐžŅ‡Đ˛Đ°ĐŧĐĩ!", "onboarding_welcome_user": "Đ”ĐžĐąŅ€Đĩ Đ´ĐžŅˆŅŠĐģ, {user}", "online": "ОĐŊĐģаКĐŊ", "only_favorites": "ХаĐŧĐž ĐģŅŽĐąĐ¸Đŧи", - "open_in_map_view": "ĐžŅ‚Đ˛ĐžŅ€Đ¸ в Đ¸ĐˇĐŗĐģĐĩĐ´ ĐŊа ĐēĐ°Ņ€Ņ‚Đ°Ņ‚Đ°", + "open": "ĐžŅ‚Đ˛ĐžŅ€Đ¸", + "open_in_map_view": "ĐžŅ‚Đ˛ĐžŅ€Đ¸ Đ¸ĐˇĐŗĐģĐĩĐ´ ĐŊа ĐēĐ°Ņ€Ņ‚Đ°", "open_in_openstreetmap": "ĐžŅ‚Đ˛ĐžŅ€Đ¸ в OpenStreetMap", - "open_the_search_filters": "ĐžŅ‚Đ˛Đ°Ņ€ŅĐŊĐĩ ĐŊа Ņ„Đ¸ĐģŅ‚Ņ€Đ¸Ņ‚Đĩ Са Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ", + "open_the_search_filters": "ĐžŅ‚Đ˛Đ°Ņ€Đ¸ Ņ„Đ¸ĐģŅ‚Ņ€Đ¸Ņ‚Đĩ Са Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ", "options": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "or": "иĐģи", "organize_your_library": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ˛Đ°ŅˆĐ°Ņ‚Đ° йийĐģĐ¸ĐžŅ‚ĐĩĐēа", @@ -940,12 +1330,20 @@ "other": "Đ”Ņ€ŅƒĐŗĐ¸", "other_devices": "Đ”Ņ€ŅƒĐŗĐ¸ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "other_variables": "Đ”Ņ€ŅƒĐŗĐ¸ ĐŋŅ€ĐžĐŧĐĩĐŊĐģиви", - "owned": "ĐŸŅ€Đ¸Ņ‚ĐĩĐļаваĐŊ", + "owned": "ĐœĐžĐ¸Ņ‚Đĩ", "owner": "ĐĄĐžĐąŅŅ‚Đ˛ĐĩĐŊиĐē", "partner": "ĐŸĐ°Ņ€Ņ‚ĐŊŅŒĐžŅ€", "partner_can_access": "{partner} иĐŧа Đ´ĐžŅŅ‚ŅŠĐŋ", "partner_can_access_assets": "Đ’ŅĐ¸Ņ‡Đēи Đ˛Đ°ŅˆĐ¸ ҁĐŊиĐŧĐēи и видĐĩĐžĐēĐģиĐŋОвĐĩ, ҁ иСĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ ĐŊа Ņ‚ĐĩСи в ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊи и Đ˜ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸", - "partner_can_access_location": "ĐœŅŅŅ‚ĐžŅ‚Đž, ĐēŅŠĐ´ĐĩŅ‚Đž ŅĐ° ĐŊаĐŋŅ€Đ°Đ˛ĐĩĐŊи ҁĐŊиĐŧĐēĐ¸Ņ‚Đĩ ви", + "partner_can_access_location": "ĐœŅŅŅ‚ĐžŅ‚Đž, ĐēŅŠĐ´ĐĩŅ‚Đž ŅĐ° ĐŊаĐŋŅ€Đ°Đ˛ĐĩĐŊи ҁĐŊиĐŧĐēĐ¸Ņ‚Đĩ", + "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": "ĐŸĐ°Ņ€ĐžĐģа", @@ -974,7 +1372,18 @@ "permanently_delete_assets_prompt": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ СавиĐŊĐ°ĐŗĐ¸ {count, plural, one {Ņ‚ĐžĐˇĐ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚?} other {Ņ‚ĐĩСи # ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸?}} ĐĸОва ҉Đĩ ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐĩ {count, plural, one {ĐžŅ‚ ĐŊĐĩĐŗĐžĐ˛Đ¸Ņ} other {ĐžŅ‚ Ņ‚ĐĩŅ…ĐŊĐ¸Ņ‚Đĩ}} аĐģĐąŅƒĐŧ(и).", "permanently_deleted_asset": "Đ˜ĐˇŅ‚Ņ€Đ¸Ņ‚ СавиĐŊĐ°ĐŗĐ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚", "permanently_deleted_assets_count": "Đ˜ĐˇŅ‚Ņ€Đ¸Ņ‚(и) СавиĐŊĐ°ĐŗĐ¸{count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°}}", + "permission": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐ¸Ņ", + "permission_empty": "ĐĸвОĐĩŅ‚Đž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ ĐŊĐĩ Ņ‚Ņ€ŅĐąĐ˛Đ° да Đĩ ĐŋŅ€Đ°ĐˇĐŊĐž", + "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 да Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ° и ҃ĐŋŅ€Đ°Đ˛ĐģŅĐ˛Đ° ĐŗĐ°ĐģĐĩŅ€Đ¸ŅŅ‚Đ°, ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ĐĩŅ‚Đĩ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž ҁĐŊиĐŧĐēи и видĐĩĐž в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ.", + "permission_onboarding_request": "Immich ҁĐĩ ĐŊ҃ĐļдаĐĩ ĐžŅ‚ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ Са ĐŋŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ҁĐŊиĐŧĐēи и видĐĩĐž.", "person": "ЧОвĐĩĐē", + "person_birthdate": "Đ”Đ°Ņ‚Đ° ĐŊа Ņ€Đ°ĐļдаĐŊĐĩ {date}", "person_hidden": "{name}{hidden, select, true { (ҁĐēŅ€Đ¸Ņ‚)} other {}}", "photo_shared_all_users": "Đ˜ĐˇĐŗĐģĐĩĐļда, ҇Đĩ ҁ҂Đĩ ҁĐŋОдĐĩĐģиĐģи ҁĐŊиĐŧĐēĐ¸Ņ‚Đĩ ŅĐ¸ ҁ Đ˛ŅĐ¸Ņ‡Đēи ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи иĐģи ĐŊŅĐŧĐ°Ņ‚Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи, ҁ ĐēĐžĐ¸Ņ‚Đž да ҁĐŋОдĐĩĐģŅŅ‚Đĩ.", "photos": "ĐĄĐŊиĐŧĐēи", @@ -982,6 +1391,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, one {{count, number} ĐœŅŅŅ‚Đž} other {{count, number} МĐĩŅŅ‚Đ°}}", @@ -989,14 +1402,28 @@ "play_memories": "Đ’ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ ĐŊа ҁĐŋĐžĐŧĐĩĐŊи", "play_motion_photo": "Đ’ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛Đĩди Motion Photo", "play_or_pause_video": "Đ’ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ иĐģи ĐŋĐ°ŅƒĐˇĐ° ĐŊа видĐĩĐž", + "please_auth_to_access": "МоĐģŅ, ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€Đ¸ Са Đ´ĐžŅŅ‚ŅŠĐŋ", "port": "ĐŸĐžŅ€Ņ‚", + "preferences_settings_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐ¸ŅŅ‚Đ° ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", + "preferences_settings_title": "ĐŸŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐ¸Ņ", "preset": "ШайĐģĐžĐŊ", "preview": "ĐŸŅ€ĐĩĐŗĐģĐĩди", "previous": "ĐŸŅ€ĐĩĐ´Đ¸ŅˆĐŊĐž", "previous_memory": "ĐŸŅ€ĐĩĐ´Ņ…ĐžĐ´ĐĩĐŊ ҁĐŋĐžĐŧĐĩĐŊ", - "previous_or_next_photo": "ĐŸŅ€ĐĩĐ´Ņ…ĐžĐ´ĐŊа иĐģи ҁĐģĐĩĐ´Đ˛Đ°Ņ‰Đ° ҁĐŊиĐŧĐēа", + "previous_or_next_day": "ДĐĩĐŊ ĐŊаĐŋŅ€ĐĩĐ´/ĐŊаСад", + "previous_or_next_month": "МĐĩҁĐĩ҆ ĐŊаĐŋŅ€ĐĩĐ´/ĐŊаСад", + "previous_or_next_photo": "ĐŸŅ€ĐĩĐ´Đ¸ŅˆĐŊа иĐģи ҁĐģĐĩĐ´Đ˛Đ°Ņ‰Đ° ҁĐŊиĐŧĐēа", + "previous_or_next_year": "ГодиĐŊа ĐŊаĐŋŅ€ĐĩĐ´/ĐŊаСад", "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}", "profile_picture_set": "ĐŸŅ€ĐžŅ„Đ¸ĐģĐŊĐ°Ņ‚Đ° ҁĐŊиĐŧĐēа Đĩ ҁĐģĐžĐļĐĩĐŊа.", "public_album": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐĩĐŊ аĐģĐąŅƒĐŧ", @@ -1021,7 +1448,7 @@ "purchase_lifetime_description": "ПоĐē҃ĐŋĐēа Са Ņ†ŅĐģ ĐļĐ¸Đ˛ĐžŅ‚", "purchase_option_title": "ОПĐĻИИ ЗА Đ—ĐĐšĐŖĐŸĐŖĐ’ĐĐĐ•", "purchase_panel_info_1": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩŅ‚Đž ĐŊа Immich ĐžŅ‚ĐŊĐĩĐŧа ĐŧĐŊĐžĐŗĐž Đ˛Ņ€ĐĩĐŧĐĩ и ŅƒŅĐ¸ĐģĐ¸Ņ, и иĐŧаĐŧĐĩ иĐŊĐļĐĩĐŊĐĩŅ€Đ¸ ĐŊа ĐŋҊĐģĐŊĐž Ņ€Đ°ĐąĐžŅ‚ĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩ, ĐēĐžĐ¸Ņ‚Đž Ņ€Đ°ĐąĐžŅ‚ŅŅ‚ ĐŋĐž ĐŊĐĩĐŗĐž, Са да ĐŗĐž ĐŊаĐŋŅ€Đ°Đ˛Đ¸Đŧ Đ˛ŅŠĐˇĐŧĐžĐļĐŊĐž ĐŊаК-Đ´ĐžĐąŅ€Đž. ĐĐ°ŅˆĐ°Ņ‚Đ° ĐŧĐ¸ŅĐ¸Ņ Đĩ ŅĐžŅ„Ņ‚ŅƒĐĩŅ€ŅŠŅ‚ ҁ ĐžŅ‚Đ˛ĐžŅ€ĐĩĐŊ ĐēОд и ĐĩŅ‚Đ¸Ņ‡ĐŊĐ¸Ņ‚Đĩ йиСĐŊĐĩҁ ĐŋŅ€Đ°ĐēŅ‚Đ¸Đēи да ҁĐĩ ĐŋŅ€ĐĩĐ˛ŅŠŅ€ĐŊĐ°Ņ‚ в ŅƒŅŅ‚ĐžĐšŅ‡Đ¸Đ˛ Đ¸ĐˇŅ‚ĐžŅ‡ĐŊиĐē ĐŊа Đ´ĐžŅ…ĐžĐ´Đ¸ Са Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Ņ‡Đ¸Ņ†Đ¸Ņ‚Đĩ и да ŅŅŠĐˇĐ´Đ°Đ´ĐĩĐŧ ĐĩĐēĐžŅĐ¸ŅŅ‚ĐĩĐŧа, ĐēĐžŅŅ‚Đž ŅƒĐ˛Đ°Đļава ĐģĐ¸Ņ‡ĐŊĐ°Ņ‚Đ° ĐŊĐĩĐŋŅ€Đ¸ĐēĐžŅĐŊОвĐĩĐŊĐžŅŅ‚ и ĐŋŅ€ĐĩĐ´ĐģĐ°ĐŗĐ° Đ¸ŅŅ‚Đ¸ĐŊҁĐēи аĐģŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛Đ¸ ĐŊа ĐĩĐēҁĐŋĐģĐžĐ°Ņ‚Đ°Ņ‚ĐžŅ€ŅĐēĐ¸Ņ‚Đĩ ОйĐģĐ°Ņ‡ĐŊи ҃ҁĐģŅƒĐŗĐ¸.", - "purchase_panel_info_2": "ĐĸŅŠĐš ĐēĐ°Ņ‚Đž ҁĐŧĐĩ аĐŊĐŗĐ°ĐļĐ¸Ņ€Đ°ĐŊи да ĐŊĐĩ Đ´ĐžĐąĐ°Đ˛ŅĐŧĐĩ ĐŋĐģĐ°Ņ‚ĐĩĐŊи ҁ҂ĐĩĐŊи, Ņ‚Đ°ĐˇĐ¸ ĐŋĐžĐē҃ĐŋĐēа ĐŊŅĐŧа да ви ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛Đ¸ Đ´ĐžĐŋҊĐģĐŊĐ¸Ņ‚ĐĩĐģĐŊи Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ в Immich. НиĐĩ Ņ€Đ°ĐˇŅ‡Đ¸Ņ‚Đ°ĐŧĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи ĐēĐ°Ņ‚Đž Đ˛Đ°Ņ, Са да ĐŋОдĐēŅ€ĐĩĐŋŅŅ‚Đĩ ĐŋŅ€ĐžĐ´ŅŠĐģĐļĐ°Đ˛Đ°Ņ‰ĐžŅ‚Đž Ņ€Đ°ĐˇĐ˛Đ¸Ņ‚Đ¸Đĩ ĐŊа Immich.", + "purchase_panel_info_2": "ĐĸŅŠĐš ĐēĐ°Ņ‚Đž ҁĐŧĐĩ ОйĐĩŅ‰Đ°Đģи да ĐŊĐĩ Đ´ĐžĐąĐ°Đ˛ŅĐŧĐĩ ĐŋĐģĐ°Ņ‚ĐĩĐŊи Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸, Ņ‚Đ°ĐˇĐ¸ ĐŋĐžĐē҃ĐŋĐēа ĐŊŅĐŧа да ви ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛Đ¸ Đ´ĐžĐŋҊĐģĐŊĐ¸Ņ‚ĐĩĐģĐŊи Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ в Immich. НиĐĩ Ņ€Đ°ĐˇŅ‡Đ¸Ņ‚Đ°ĐŧĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи ĐēĐ°Ņ‚Đž Đ˛Đ°Ņ, Са да ĐŋОдĐēŅ€ĐĩĐŋĐ¸Ņ‚Đĩ ĐŋŅ€ĐžĐ´ŅŠĐģĐļаваĐŊĐĩ ĐŊа Ņ€Đ°ĐˇĐ˛Đ¸Ņ‚Đ¸ĐĩŅ‚Đž ĐŊа Immich.", "purchase_panel_title": "ĐŸĐžĐ´Đ´ŅŠŅ€ĐļĐ°ĐšŅ‚Đĩ ĐŋŅ€ĐžĐĩĐēŅ‚Đ°", "purchase_per_server": "ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€", "purchase_per_user": "ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ", @@ -1046,6 +1473,10 @@ "recent": "ĐĄĐēĐžŅ€ĐžŅˆĐŊи", "recent-albums": "ĐĄĐēĐžŅ€ĐžŅˆĐŊи АĐģĐąŅƒĐŧи", "recent_searches": "ĐĄĐēĐžŅ€ĐžŅˆĐŊи Ņ‚ŅŠŅ€ŅĐĩĐŊĐ¸Ņ", + "recently_added": "ĐĐ°ŅĐēĐžŅ€Đž дОйавĐĩĐŊĐž", + "recently_added_page_title": "ĐĐ°ŅĐēĐžŅ€Đž дОйавĐĩĐŊĐž", + "recently_taken": "ĐĐ°ŅĐēĐžŅ€Đž ҁĐŊиĐŧаĐŊĐž", + "recently_taken_page_title": "ĐĐ°ŅĐēĐžŅ€Đž ҁĐŊиĐŧаĐŊĐž", "refresh": "ОĐŋŅ€ĐĩҁĐŊŅĐ˛Đ°ĐŊĐĩ", "refresh_encoded_videos": "ОĐŋŅ€ĐĩҁĐŊи ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊи видĐĩĐžĐēĐģиĐŋОвĐĩ", "refresh_faces": "ОĐŋŅ€ĐĩҁĐŊи ĐģĐ¸Ņ†Đ°Ņ‚Đ°", @@ -1065,7 +1496,12 @@ "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": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи ҁĐŊиĐŧĐēĐ¸Ņ‚Đĩ ĐžŅ‚ ҁĐŋĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ", + "remove_tag": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи ĐŧĐ°Ņ€ĐēĐĩŅ€Đ°", "remove_url": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи URL", "remove_user": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ", "removed_api_key": "ĐŸŅ€ĐĩĐŧĐ°Ņ…Đ°Ņ‚ API ĐēĐģŅŽŅ‡: {name}", @@ -1086,6 +1522,7 @@ "reset": "ĐŅƒĐģĐ¸Ņ€Đ°ĐŊĐĩ", "reset_password": "ĐŅƒĐģĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ°", "reset_people_visibility": "ĐŅƒĐģĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа видиĐŧĐžŅŅ‚Ņ‚Đ° ĐŊа Ņ…ĐžŅ€Đ°Ņ‚Đ°", + "reset_pin_code": "ĐŅƒĐģĐ¸Ņ€Đ°Đš PIN ĐēОда", "reset_to_default": "Đ’Ņ€ŅŠŅ‰Đ°ĐŊĐĩ ĐŊа Ņ„Đ°ĐąŅ€Đ¸Ņ‡ĐŊи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "resolve_duplicates": "Đ ĐĩŅˆĐ¸ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸Ņ‚Đĩ", "resolved_all_duplicates": "Đ’ŅĐ¸Ņ‡Đēи Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸ ŅĐ° Ņ€Đĩ҈ĐĩĐŊи", @@ -1100,10 +1537,12 @@ "role_editor": "Đ ĐĩдаĐēŅ‚ĐžŅ€", "role_viewer": "Đ—Ņ€Đ¸Ņ‚ĐĩĐģ", "save": "ЗаĐŋаСи", + "save_to_gallery": "ЗаĐŋаСи в ĐŗĐ°ĐģĐĩŅ€Đ¸ŅŅ‚Đ°", "saved_api_key": "ЗаĐŋаСĐĩĐŊ API Key", "saved_profile": "ЗаĐŋаСĐĩĐŊ ĐŋŅ€ĐžŅ„Đ¸Đģ", "saved_settings": "ЗаĐŋаСĐĩĐŊи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "say_something": "КаĐļи ĐŊĐĩŅ‰Đž", + "scaffold_body_error_occurred": "Đ’ŅŠĐˇĐŊиĐēĐŊа ĐŗŅ€Đĩ҈Đēа", "scan_all_libraries": "ĐĄĐēаĐŊĐ¸Ņ€Đ°Đš Đ˛ŅĐ¸Ņ‡Đēи йийĐģĐ¸ĐžŅ‚ĐĩĐēи", "scan_library": "ĐĄĐēаĐŊĐ¸Ņ€Đ°Đš", "scan_settings": "ĐĄĐēаĐŊĐ¸Ņ€Đ°Đš ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ", @@ -1119,16 +1558,45 @@ "search_camera_model": "ĐĸŅŠŅ€ŅĐ¸ ĐŧОдĐĩĐģ ĐŊа ĐēаĐŧĐĩŅ€Đ°Ņ‚Đ°...", "search_city": "ĐĸŅŠŅ€ŅĐ¸ ĐŗŅ€Đ°Đ´...", "search_country": "ĐĸŅŠŅ€ŅĐ¸ Đ´ŅŠŅ€Đļава...", + "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": "ĐĸиĐŋ ĐŊа Ņ„Đ°ĐšĐģа", + "search_filter_media_type_title": "ИСйĐĩŅ€Đ¸ Ņ‚Đ¸Đŋ ĐŊа Ņ„Đ°ĐšĐģа", + "search_filter_people_title": "ИСйĐĩŅ€Đ¸ Ņ…ĐžŅ€Đ°", "search_for": "ĐĸŅŠŅ€ŅĐ¸ Са", "search_for_existing_person": "ĐĸŅŠŅ€ŅĐ¸ ŅŅŠŅ‰ĐĩŅŅ‚Đ˛ŅƒĐ˛Đ°Ņ‰ Ņ‡ĐžĐ˛ĐĩĐē", + "search_no_more_result": "ĐŅĐŧа Đ´Ņ€ŅƒĐŗĐ¸ Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸", "search_no_people": "ĐŅĐŧа Ņ…ĐžŅ€Đ°", "search_no_people_named": "ĐŅĐŧа Ņ…ĐžŅ€Đ° ĐŊа иĐŧĐĩ \"{name}\"", + "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_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": "Ново Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ", "search_settings": "ĐĸŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "search_state": "ĐĸŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа Ņ‰Đ°Ņ‚...", + "search_suggestion_list_smart_search_hint_1": "ĐŖĐŧĐŊĐžŅ‚Đž Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ Đĩ вĐēĐģŅŽŅ‡ĐĩĐŊĐž ĐŋĐž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ, Са Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ в ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊĐ¸Ņ‚Đĩ иСĐŋĐžĐģСваК ҁĐŋĐĩŅ†Đ¸Đ°ĐģĐĩĐŊ ŅĐ¸ĐŊŅ‚Đ°Đēҁ ", + "search_suggestion_list_smart_search_hint_2": "m:Đ˛Đ°ŅˆĐ¸Ņ-Ņ‚ĐĩŅ€ĐŧиĐŊ-Са-Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ", "search_tags": "ĐĸŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа ĐĩŅ‚Đ¸ĐēĐĩŅ‚Đ¸...", "search_timezone": "ĐĸŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа Ņ‡Đ°ŅĐžĐ˛Đ° СОĐŊа...", "search_type": "ĐĸиĐŋ ĐŊа Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ", @@ -1136,9 +1604,11 @@ "searching_locales": "ĐĸŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа ĐģĐžĐēаĐģи...", "second": "ĐĄĐĩĐē҃ĐŊда", "see_all_people": "ВиĐļŅ‚Đĩ Đ˛ŅĐ¸Ņ‡Đēи Ņ…ĐžŅ€Đ°", + "select": "ИСйĐĩŅ€Đ¸", "select_album_cover": "ИСйĐĩŅ€ĐĩŅ‚Đĩ ОйĐģĐžĐļĐēа ĐŊа аĐģĐąŅƒĐŧ", "select_all": "ИСйĐĩŅ€ĐĩŅ‚Đĩ Đ˛ŅĐ¸Ņ‡Đēи", "select_all_duplicates": "ИСйĐĩŅ€Đ¸ Đ˛ŅĐ¸Ņ‡Đēи Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸", + "select_all_in": "ИСйĐĩŅ€Đ¸ Đ˛ŅĐ¸Ņ‡Đēи ĐžŅ‚ ĐŗŅ€ŅƒĐŋĐ°Ņ‚Đ° {group}", "select_avatar_color": "ИСйĐĩŅ€ĐĩŅ‚Đĩ Ņ†Đ˛ŅŅ‚ ĐŊа Đ°Đ˛Đ°Ņ‚Đ°Ņ€Đ°", "select_face": "ИСйĐĩŅ€ĐĩŅ‚Đĩ ĐģĐ¸Ņ†Đĩ", "select_featured_photo": "ИСйĐĩŅ€Đ¸ ĐŋŅ€ĐĩĐ´ŅŅ‚Đ°Đ˛Đ¸Ņ‚ĐĩĐģĐŊа ҁĐŊиĐŧĐēа", @@ -1146,14 +1616,20 @@ "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 {# Đ¸ĐˇĐąŅ€Đ°ĐŊи}}", "send_message": "ИСĐŋŅ€Đ°Ņ‚ĐĩŅ‚Đĩ ŅŅŠĐžĐąŅ‰ĐĩĐŊиĐĩ", "send_welcome_email": "ИСĐŋŅ€Đ°Ņ‚ĐĩŅ‚Đĩ иĐŧĐĩĐšĐģ Са Đ´ĐžĐąŅ€Đĩ Đ´ĐžŅˆĐģи", + "server_endpoint": "ĐĐ´Ņ€Đĩҁ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", + "server_info_box_app_version": "Đ’Ņ€ŅĐ¸Ņ ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", + "server_info_box_server_url": "URL ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "server_offline": "ĐĄŅŠŅ€Đ˛ŅŠŅ€ ĐžŅ„ĐģаКĐŊ", "server_online": "ĐĄŅŠŅ€Đ˛ŅŠŅ€ ĐžĐŊĐģаКĐŊ", + "server_privacy": "ПовĐĩŅ€Đ¸Ņ‚ĐĩĐģĐŊĐžŅŅ‚ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "server_stats": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "server_version": "ВĐĩŅ€ŅĐ¸Ņ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "set": "Задай", @@ -1163,22 +1639,93 @@ "set_date_of_birth": "Đ—Đ°Đ´Đ°ĐšŅ‚Đĩ Đ´Đ°Ņ‚Đ° ĐŊа Ņ€Đ°ĐļдаĐŊĐĩ", "set_profile_picture": "Đ—Đ°Đ´Đ°ĐšŅ‚Đĩ ĐŋŅ€ĐžŅ„Đ¸ĐģĐŊа ҁĐŊиĐŧĐēа", "set_slideshow_to_fullscreen": "Đ—Đ°Đ´Đ°ĐšŅ‚Đĩ ĐĄĐģĐ°ĐšĐ´ŅˆĐžŅƒ ĐŊа Ņ†ŅĐģ ĐĩĐēŅ€Đ°ĐŊ", + "set_stack_primary_asset": "Задай ĐēĐ°Ņ‚Đž ĐžŅĐŊОвĐŊи ОйĐĩĐēŅ‚Đ¸", + "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_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": "МоĐģŅ, Са да ҁĐĩ ĐŋŅ€Đ¸ĐģĐžĐļи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ‚Đ° Ņ€ĐĩŅŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°Đš Immich", "settings_saved": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ ŅĐ° СаĐŋаСĐĩĐŊи", + "setup_pin_code": "Задай PIN ĐēОд", "share": "ĐĄĐŋОдĐĩĐģŅĐŊĐĩ", + "share_add_photos": "Добави ҁĐŊиĐŧĐēи", + "share_assets_selected": "{count} Đ¸ĐˇĐąŅ€Đ°ĐŊи", + "share_dialog_preparing": "ĐŸĐžĐ´ĐŗĐžŅ‚ĐžĐ˛Đēа...", + "share_link": "Đ’Ņ€ŅŠĐˇĐēа Са ҁĐŋОдĐĩĐģŅĐŊĐĩ", "shared": "ĐĄĐŋОдĐĩĐģĐĩĐŊĐž", + "shared_album_activities_input_disable": "КоĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ ŅĐ° иСĐēĐģŅŽŅ‡ĐĩĐŊи", + "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_by_you": "ĐĄĐŋОдĐĩĐģĐĩĐŊĐž ĐžŅ‚ Đ˛Đ°Ņ", "shared_from_partner": "ĐĄĐŊиĐŧĐēи ĐžŅ‚ {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} Đ—Đ°Ņ€ĐĩĐ´ĐĩĐŊĐž", + "shared_link_app_bar_title": "ĐĄĐŋОдĐĩĐģĐĩĐŊи Đ˛Ņ€ŅŠĐˇĐēи", + "shared_link_clipboard_copied_massage": "КоĐŋĐ¸Ņ€Đ°ĐŊĐž в ĐēĐģиĐŋĐąĐžŅ€Đ´Đ°", + "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": "{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": "ОбĐŊОви Đ˛Ņ€ŅŠĐˇĐēĐ°Ņ‚Đ°", + "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_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_partner": "ĐĄĐŋОдĐĩĐģĐĩĐŊĐž ҁ {partner}", "sharing": "ĐĄĐŋОдĐĩĐģĐĩĐŊи", "sharing_enter_password": "МоĐģŅ, Đ˛ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ°, Са да Đ˛Đ¸Đ´Đ¸Ņ‚Đĩ Ņ‚Đ°ĐˇĐ¸ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ°.", + "sharing_page_album": "ĐĄĐŋОдĐĩĐģĐĩĐŊи аĐģĐąŅƒĐŧи", + "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": "ĐĐ°Ņ‚Đ¸ŅĐŊĐĩŅ‚Đĩ ⇧, Са да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ СавиĐŊĐ°ĐŗĐ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°", "show_album_options": "ПоĐēаСваĐŊĐĩ ĐžĐŋŅ†Đ¸Đ¸ Са аĐģĐąŅƒĐŧ", "show_albums": "ПоĐēаĐļи аĐģĐąŅƒĐŧи", @@ -1230,12 +1777,14 @@ "start_date": "ĐĐ°Ņ‡Đ°ĐģĐŊа Đ´Đ°Ņ‚Đ°", "state": "ĐŠĐ°Ņ‚", "status": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ", + "stop_casting": "ĐĄĐŋŅ€Đ¸ ĐŋŅ€ĐĩдаваĐŊĐĩŅ‚Đž", "stop_motion_photo": "ĐĄĐŊиĐŧĐēа ҁҊҁ ŅŅ‚ĐžĐŋ ĐēĐ°Đ´ŅŠŅ€", "stop_photo_sharing": "Да ҁĐŋŅ€Đ° Đģи ҁĐŋОдĐĩĐģŅĐŊĐĩŅ‚Đž ĐŊа Đ˛Đ°ŅˆĐ¸Ņ‚Đĩ ҁĐŊиĐŧĐēи?", "stop_photo_sharing_description": "{partner} вĐĩ҇Đĩ ĐŊŅĐŧа Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž Đ˛Đ°ŅˆĐ¸Ņ‚Đĩ ҁĐŊиĐŧĐēи.", "stop_sharing_photos_with_user": "ĐŸŅ€ĐĩĐēŅ€Đ°Ņ‚ĐĩŅ‚Đĩ ҁĐŋОдĐĩĐģŅĐŊĐĩŅ‚Đž ĐŊа ҁĐŊиĐŧĐēи ҁ Ņ‚ĐžĐˇĐ¸ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ", "storage": "ĐŸŅ€ĐžŅŅ‚Ņ€Đ°ĐŊŅŅ‚Đ˛Đž ĐŊа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰ĐĩŅ‚Đž", "storage_label": "НаиĐŧĐĩĐŊОваĐŊиĐĩ ĐŊа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰ĐĩŅ‚Đž", + "storage_quota": "ĐšĐ˛ĐžŅ‚Đ° ĐŊа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰ĐĩŅ‚Đž", "storage_usage": "ИСĐŋĐžĐģСваĐŊи {used} ĐžŅ‚ {available}", "submit": "ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩ", "suggestions": "ĐŸŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐ¸Ņ", @@ -1245,6 +1794,9 @@ "support_third_party_description": "Đ’Đ°ŅˆĐ°Ņ‚Đ° иĐŊŅŅ‚Đ°ĐģĐ°Ņ†Đ¸Ņ ĐŊа Immich Đĩ ĐŋаĐēĐĩŅ‚Đ¸Ņ€Đ°ĐŊа ĐžŅ‚ ҂ҀĐĩŅ‚Đ° ŅŅ‚Ņ€Đ°ĐŊа. ĐŸŅ€ĐžĐąĐģĐĩĐŧĐ¸Ņ‚Đĩ, ĐēĐžĐ¸Ņ‚Đž иСĐŋĐ¸Ņ‚Đ˛Đ°Ņ‚Đĩ, ĐŧĐžĐļĐĩ да ŅĐ° ĐŋŅ€Đ¸Ņ‡Đ¸ĐŊĐĩĐŊи ĐžŅ‚ Ņ‚ĐžĐˇĐ¸ ĐŋаĐēĐĩŅ‚, ĐˇĐ°Ņ‚ĐžĐ˛Đ° ĐŧĐžĐģŅ, ĐŋŅŠŅ€Đ˛Đž ĐŋĐžĐ´Đ°Đ˛Đ°ĐšŅ‚Đĩ ĐŋŅ€ĐžĐąĐģĐĩĐŧĐ¸Ņ‚Đĩ ŅĐ¸ ĐēҊĐŧ Ņ‚ŅŅ… ҇ҀĐĩС ĐģиĐŊĐēОвĐĩŅ‚Đĩ ĐŋĐž-Đ´ĐžĐģ҃.", "swap_merge_direction": "РаСĐŧŅĐŊа ĐŋĐžŅĐžĐēĐ°Ņ‚Đ° ĐŊа ҁĐģиваĐŊĐĩ", "sync": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ", + "sync_albums": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа аĐģĐąŅƒĐŧи", + "sync_albums_manual_subtitle": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°Đš Đ˛ŅĐ¸Ņ‡Đēи ĐˇĐ°Ņ€ĐĩĐ´ĐĩĐŊи видĐĩа и ҁĐŊиĐŧĐēи в Đ¸ĐˇĐąŅ€Đ°ĐŊĐ¸Ņ‚Đĩ Đ°Ņ€Ņ…Đ¸Đ˛ĐŊи аĐģĐąŅƒĐŧи", + "sync_upload_album_setting_subtitle": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐšŅ‚Đĩ и ĐˇĐ°Ņ€ĐĩĐļĐ´Đ°ĐšŅ‚Đĩ ҁĐŊиĐŧĐēи и видĐĩа в Đ¸ĐˇĐąŅ€Đ°ĐŊи аĐģĐąŅƒĐŧи в Immich", "tag": "ĐĸĐ°Đŗ", "tag_assets": "ĐĸĐ°ĐŗĐŊи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "tag_created": "ĐĄŅŠĐˇĐ´Đ°Đ´ĐĩĐŊ ĐĩŅ‚Đ¸ĐēĐĩŅ‚: {tag}", @@ -1258,6 +1810,19 @@ "theme": "ĐĸĐĩĐŧа", "theme_selection": "Đ˜ĐˇĐąĐžŅ€ ĐŊа Ņ‚ĐĩĐŧа", "theme_selection_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž СадаваĐŊĐĩ ĐŊа ŅĐ˛ĐĩŅ‚Đģа иĐģи Ņ‚ŅŠĐŧĐŊа Ņ‚ĐĩĐŧа Đ˛ŅŠĐˇ ĐžŅĐŊОва ĐŊа ŅĐ¸ŅŅ‚ĐĩĐŧĐŊĐ¸Ņ‚Đĩ ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐ¸Ņ ĐŊа Đ˛Đ°ŅˆĐ¸Ņ ĐąŅ€Đ°ŅƒĐˇŅŠŅ€", + "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": "ĐĄĐŋĐžĐŧĐĩĐŊи, ĐąĐ°ĐˇĐ¸Ņ€Đ°ĐŊи ĐŊа Đ˛Ņ€ĐĩĐŧĐĩŅ‚Đž", @@ -1270,18 +1835,28 @@ "to_parent": "ĐžŅ‚Đ¸Đ´Đ¸ ĐēҊĐŧ Ņ€ĐžĐ´Đ¸Ņ‚ĐĩĐģҁĐēĐ¸Ņ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚", "to_trash": "ĐšĐžŅˆŅ‡Đĩ", "toggle_settings": "ĐŸŅ€ĐĩвĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ", - "toggle_theme": "ĐŸŅ€ĐĩвĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа Ņ‚ĐĩĐŧа", "total": "ĐžĐąŅ‰Đž", "total_usage": "ĐžĐąŅ‰Đž иСĐŋĐžĐģСваĐŊĐž", "trash": "ĐšĐžŅˆŅ‡Đĩ", "trash_all": "Đ˜ĐˇŅ…Đ˛ŅŠŅ€Đģи Đ˛ŅĐ¸Ņ‡Đēи", "trash_count": "В ĐšĐžŅˆŅ‡ĐĩŅ‚Đž {count, number}", "trash_delete_asset": "ВĐēĐ°Ņ€Đ°Đš в ĐšĐžŅˆŅ‡ĐĩŅ‚Đž/Đ˜ĐˇŅ‚Ņ€Đ¸Đš ĐĩĐģĐĩĐŧĐĩĐŊŅ‚", + "trash_emptied": "ĐšĐžŅˆĐ° Đĩ иСĐŋŅ€Đ°ĐˇĐŊĐĩĐŊ", "trash_no_results_message": "Đ˜ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸Ņ‚Đĩ ҁĐŊиĐŧĐēи и видĐĩĐžĐēĐģиĐŋОвĐĩ ҉Đĩ ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚ Ņ‚ŅƒĐē.", + "trash_page_delete_all": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš Đ˛ŅĐ¸Ņ‡ĐēĐž", + "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": "В ĐēĐžŅˆĐ° ({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 {НĐĩĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊи #}}", + "undo": "ĐžŅ‚ĐŧĐĩĐŊи", "unfavorite": "ĐŸŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐžŅ‚ ĐģŅŽĐąĐ¸ĐŧĐ¸Ņ‚Đĩ", "unhide_person": "ПоĐēаĐļи ĐžŅ‚ĐŊОвО Ņ‡ĐžĐ˛ĐĩĐēа", "unknown": "НĐĩиСвĐĩҁ҂ĐŊĐž", @@ -1298,14 +1873,16 @@ "unsaved_change": "НĐĩСаĐŋаСĐĩĐŊа ĐŋŅ€ĐžĐŧŅĐŊа", "unselect_all": "ДĐĩҁĐĩĐģĐĩĐēŅ‚Đ¸Ņ€Đ°ĐšŅ‚Đĩ Đ˛ŅĐ¸Ņ‡Đēи", "unselect_all_duplicates": "ĐžŅ‚ ĐŧĐ°Ņ€ĐēĐ¸Ņ€Đ°Đš Đ˛ŅĐ¸Ņ‡Đēи Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸", + "unselect_all_in": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи Đ¸ĐˇĐąĐžŅ€Đ° ĐŊа Đ˛ŅĐ¸Ņ‡Đēи ĐžŅ‚ ĐŗŅ€ŅƒĐŋĐ°Ņ‚Đ° {group}", "unstack": "РаСĐēĐ°Ņ‡Đ¸", "unstacked_assets_count": "РаСĐēĐ°Ņ‡ĐĩĐŊи {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸}}", - "untracked_files": "НĐĩĐŋОСĐŊĐ°Ņ‚Đ¸ Ņ„Đ°ĐšĐģОвĐĩ", - "untracked_files_decription": "ĐĸĐĩСи Ņ„Đ°ĐšĐģОвĐĩ ŅĐ° ĐŊĐĩ Ņ€Đ°ĐˇĐŋОСĐŊĐ°Ņ‚Đ¸ ĐžŅ‚ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž. ĐĸĐĩ ĐŧĐžĐŗĐ°Ņ‚ да ĐąŅŠĐ´Đ°Ņ‚ Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚ ĐžŅ‚ ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊи ĐŋŅ€ĐĩŅ…Đ˛ŅŠŅ€ĐģŅ ĐŊĐ¸Ņ, ĐŋŅ€ĐĩĐēҊҁĐŊĐ°Ņ‚Đ¸ ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐ¸Ņ иĐģи ĐŊĐĩĐˇĐ°Đ˛ŅŠŅ€ŅˆĐĩĐŊи ĐŋĐžŅ€Đ°Đ´Đ¸ ĐŗŅ€Đĩ҈Đēа", "up_next": "ĐĄĐģĐĩĐ´Đ˛Đ°Ņ‰", + "updated_at": "ОбĐŊОвĐĩĐŊĐž", "updated_password": "ĐŸĐ°Ņ€ĐžĐģĐ°Ņ‚Đ° Đĩ аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊа", "upload": "ĐšĐ°Ņ‡Đ˛Đ°ĐŊĐĩ", "upload_concurrency": "ĐŖŅĐŋĐžŅ€ĐĩĐ´ĐŊи ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐ¸Ņ", + "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 {# Đ´ŅƒĐąĐģĐ¸Ņ€Đ°ĐŊи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸}}", @@ -1313,13 +1890,22 @@ "upload_status_errors": "Đ“Ņ€Đĩ҈Đēи", "upload_status_uploaded": "ĐšĐ°Ņ‡ĐĩĐŊĐž", "upload_success": "ĐšĐ°Ņ‡Đ˛Đ°ĐŊĐĩŅ‚Đž Đĩ ҃ҁĐŋĐĩ҈ĐŊĐž, ĐžĐŋŅ€ĐĩҁĐŊĐĩŅ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ°Ņ‚Đ°, Са да Đ˛Đ¸Đ´Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Đ¸Ņ‚Đĩ Ņ„Đ°ĐšĐģОвĐĩ.", + "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": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ ИД", "user_liked": "{user} Ņ…Đ°Ņ€ĐĩŅĐ° {type, select, photo {Ņ‚Đ°ĐˇĐ¸ ҁĐŊиĐŧĐēа} video {Ņ‚ĐžĐ˛Đ° видĐĩĐž} asset {Ņ‚ĐžĐˇĐ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {}}", + "user_pin_code_settings": "PIN ĐēОд", + "user_pin_code_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģŅĐ˛Đ°ĐšŅ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ ĐŊа PIN ĐēОда", + "user_privacy": "ПовĐĩŅ€Đ¸Ņ‚ĐĩĐģĐŊĐžŅŅ‚ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģĐ¸Ņ‚Đĩ", "user_purchase_settings": "ПоĐē҃ĐŋĐēа", - "user_purchase_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģŅĐ˛Đ°Đš ĐŋĐžĐē҃ĐŋĐēĐ°Ņ‚Đ° ŅĐ¸", + "user_purchase_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģŅĐ˛Đ°ĐšŅ‚Đĩ ĐŋĐžĐē҃ĐŋĐēĐ°Ņ‚Đ° ŅĐ¸", "user_role_set": "Задай {user} ĐēĐ°Ņ‚Đž {role}", "user_usage_detail": "ĐŸĐžĐ´Ņ€ĐžĐąĐŊĐžŅŅ‚Đ¸ Са иСĐŋĐžĐģСваĐŊĐĩŅ‚Đž ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ", "user_usage_stats": "ĐĄŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đēа Са иСĐŋĐžĐģСваĐŊĐĩŅ‚Đž ĐŊа аĐēĐ°ŅƒĐŊŅ‚Đ°", @@ -1328,6 +1914,7 @@ "users": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи", "utilities": "ИĐŊŅŅ‚Ņ€ŅƒĐŧĐĩĐŊŅ‚Đ¸", "validate": "ВаĐģĐ¸Đ´Đ¸Ņ€Đ°ĐŊĐĩ", + "validate_endpoint_error": "МоĐģŅ, Đ˛ŅŠĐ˛Đĩди ĐŋŅ€Đ°Đ˛Đ¸ĐģĐĩĐŊ URL", "variables": "ĐŸŅ€ĐžĐŧĐĩĐŊĐģиви", "version": "ВĐĩŅ€ŅĐ¸Ņ", "version_announcement_closing": "ĐĸвОК ĐŋŅ€Đ¸ŅŅ‚ĐĩĐģ, АĐģĐĩĐēҁ", @@ -1349,16 +1936,24 @@ "view_name": "ĐŸŅ€ĐĩĐŗĐģĐĩдаК", "view_next_asset": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ҁĐģĐĩĐ´Đ˛Đ°Ņ‰Đ¸Ņ Ņ„Đ°ĐšĐģ", "view_previous_asset": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ĐŋŅ€ĐĩĐ´Đ¸ŅˆĐŊĐ¸Ņ Ņ„Đ°ĐšĐģ", + "view_qr_code": "ВиĐļ QR ĐēОда", "view_stack": "ПоĐēаĐļи в ҁ҂ĐĩĐē", + "view_user": "ВиĐļ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ", + "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": "Wi-Fi ĐŧŅ€ĐĩĐļа", + "wrong_pin_code": "Đ“Ņ€Đĩ҈ĐĩĐŊ PIN ĐēОд", "year": "ГодиĐŊа", "years_ago": "ĐŋŅ€Đĩди {years, plural, one {# ĐŗĐžĐ´Đ¸ĐŊа} other {# ĐŗĐžĐ´Đ¸ĐŊи}}", "yes": "Да", "you_dont_have_any_shared_links": "ĐŅĐŧĐ°Ņ‚Đĩ ҁĐŋОдĐĩĐģĐĩĐŊи Đ˛Ņ€ŅŠĐˇĐēи", + "your_wifi_name": "Đ’Đ°ŅˆĐ°Ņ‚Đ° Wi-Fi ĐŧŅ€ĐĩĐļа", "zoom_image": "ĐŖĐ˛ĐĩĐģĐ¸Ņ‡Đ°Đ˛Đ°ĐŊĐĩ ĐŊа Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩŅ‚Đž" } diff --git a/i18n/bn.json b/i18n/bn.json index 966d474111..ed108652b7 100644 --- a/i18n/bn.json +++ b/i18n/bn.json @@ -13,5 +13,7 @@ "add_a_location": "āĻāĻ•āϟāĻŋ āĻ…āĻŦāĻ¸ā§āĻĨāĻžāύ āϝ⧋āĻ— āĻ•āϰ⧁āύ", "add_a_name": "āĻāĻ•āϟāĻŋ āύāĻžāĻŽ āϝ⧋āĻ— āĻ•āϰ⧁āύ", "add_a_title": "āĻāĻ•āϟāĻŋ āĻļāĻŋāϰ⧋āύāĻžāĻŽ āϝ⧋āĻ— āĻ•āϰ⧁āύ", - "add_endpoint": "āĻāĻ¨ā§āĻĄāĻĒāϝāĻŧ⧇āĻ¨ā§āϟ āϝ⧋āĻ— āĻ•āϰ⧁āύ" + "add_endpoint": "āĻāĻ¨ā§āĻĄāĻĒāϝāĻŧ⧇āĻ¨ā§āϟ āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "add_exclusion_pattern": "āĻŦāĻšāĻŋāĻ°ā§āĻ­ā§‚āϤāĻ•āϰāĻŖ āύāĻŽā§āύāĻž", + "add_url": "āϞāĻŋāĻ™ā§āĻ• āϝ⧋āĻ— āĻ•āϰ⧁āύ" } diff --git a/i18n/ca.json b/i18n/ca.json index cd9cc4e0b4..5c53311a02 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -22,6 +22,7 @@ "add_partner": "Afegir company/a", "add_path": "Afegir una ruta", "add_photos": "Afegir fotografies", + "add_tag": "Afegir una etiqueta", "add_to": "Afegir aâ€Ļ", "add_to_album": "Afegir a un l'àlbum", "add_to_album_bottom_sheet_added": "Afegit a {album}", @@ -33,6 +34,7 @@ "added_to_favorites_count": "{count, number} afegits als preferits", "admin": { "add_exclusion_pattern_description": "Afegeix patrons d'exclusiÃŗ. Es permet englobar fent Ãēs de *, **, i ?. Per a ignorar els fitxers de qualsevol directori anomenat \"Raw\" introduïu \"**/Raw/**\". Per a ignorar els fitxers acabats en \".tif\" introduïu \"**/*.tif\". Per a ignorar una ruta absoluta, utilitzeu \"/ruta/a/ignorar/**\".", + "admin_user": "Administrador", "asset_offline_description": "Aquest recurs de la biblioteca externa ja no es troba al disc i s'ha mogut a la paperera. Si el fitxer s'ha mogut dins de la biblioteca, comproveu la vostra línia de temps per trobar el nou recurs corresponent. Per restaurar aquest recurs, assegureu-vos que Immich pugui accedir a la ruta del fitxer segÃŧent i escanegeu la biblioteca.", "authentication_settings": "ConfiguraciÃŗ de l'autenticaciÃŗ", "authentication_settings_description": "Gestiona la contrasenya, OAuth i altres configuracions de l'autenticaciÃŗ", @@ -43,9 +45,7 @@ "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", + "backup_settings_description": "Gestionar la configuraciÃŗ de bolcats de la base de dades. Nota: els treballs no es monitoritzen ni es notifiquen els errors.", "cleared_jobs": "Tasques esborrades per a: {job}", "config_set_by_file": "La configuraciÃŗ està definida per un fitxer de configuraciÃŗ", "confirm_delete_library": "Esteu segurs que voleu eliminar la llibreria {library}?", @@ -59,16 +59,14 @@ "cron_expression_description": "Estableix l'interval d'escaneig amb el format cron. Per obtenir mÊs informaciÃŗ, consulteu, p.e Crontab Guru", "cron_expression_presets": "Ajustos predefinits d'expressions Cron", "disable_login": "Deshabiliteu l'inici de sessiÃŗ", - "duplicate_detection_job_description": "Executa l'aprenentatge automàtic en els elements per a detectar imatges semblants. Fa servir l'Smart Search", + "duplicate_detection_job_description": "Executa l'aprenentatge automàtic en els elements per a detectar imatges semblants. Fa servir la cerca intel¡ligent", "exclusion_pattern_description": "Els patrons d'exclusiÃŗ permeten ignorar fitxers i carpetes quan escanegeu una llibreria. AixÃ˛ Ês Ãētil si teniu carpetes que contenen fitxer que no voleu importar, com els fitxers RAW.", - "external_library_created_at": "Llibreria externa (creada el {date})", "external_library_management": "GestiÃŗ de llibreries externes", "face_detection": "DetecciÃŗ de cares", "face_detection_description": "Detecta les cares fent servir aprenentatge automàtic. Per a videos nomÊs Ês tÊ en compte la miniatura. \"Actualitzar\" reprocessa tots els elements. \"Resetejar\" esborra tota la informaciÃŗ de cares actuals. \"Pendent\" afegeix a la cua els elements que encara no han estat processats. Les cares detectades s'afegiran a la cua per al Reconeixement Facial desprÊs de completar la DetecciÃŗ Facial, tot agrupant-les entre noves persones o les ja existents.", "facial_recognition_job_description": "Agrupa les cares detectades per persona. Aquest pas s'executa desprÊs de completar la detecciÃŗ de cares. \"Resetejar\" reagrupa totes les cares. \"Pendent\" afegeix a la cua les cares que no tenen cap persona assignada.", "failed_job_command": "La comanda {command} ha fallat per la tasca: {job}", "force_delete_user_warning": "COMPTE: Aquesta acciÃŗ eliminara immediatament l'usuari i els seus elements. Aquesta acciÃŗ Ês irreversible i els fitxers no es poden recuperar.", - "forcing_refresh_library_files": "Força l'actualitzaciÃŗ de tots els fitxers de les biblioteques", "image_format": "Format", "image_format_description": "WebP genera fitxers mÊs petits que JPEG, perÃ˛ codifica mÊs lentament.", "image_fullsize_description": "Imatges a tamany complet sense metadades, utilitzades quan es fa zoom", @@ -91,7 +89,7 @@ "image_thumbnail_description": "Miniatura petita amb metadades eliminades, que s'utilitza quan es visualitzen grups de fotos com la línia de temps principal", "image_thumbnail_quality_description": "Qualitat de miniatura d'1 a 100. MÊs alt Ês millor, perÃ˛ produeix fitxers mÊs grans i pot reduir la capacitat de resposta de l'aplicaciÃŗ.", "image_thumbnail_title": "ConfiguraciÃŗ de miniatures", - "job_concurrency": "{job} concurrència", + "job_concurrency": "{job} simultàniament", "job_created": "Tasca creada", "job_not_concurrency_safe": "Aquesta tasca no Ês segura per a la conconcurrència.", "job_settings": "ConfiguraciÃŗ de les tasques", @@ -107,7 +105,7 @@ "library_scanning_enable_description": "Habilita l'escaneig periÃ˛dic de biblioteques", "library_settings": "Llibreria externes", "library_settings_description": "Gestiona la configuraciÃŗ de les llibreries externes", - "library_tasks_description": "Escaneja biblioteques externes per arxius nous o canviats", + "library_tasks_description": "Escaneja les biblioteques externes per trobar arxius nous o canviats", "library_watching_enable_description": "Consultar llibreries externes per detectar canvis en fitxers", "library_watching_settings": "Monitoratge de la llibreria (EXPERIMENTAL)", "library_watching_settings_description": "MonitoritzaciÃŗ automàtica de fitxers modificats", @@ -115,19 +113,19 @@ "logging_level_description": "Quan està habilitat, quin nivell de registre es vol emprar.", "logging_settings": "Registre", "machine_learning_clip_model": "Model CLIP", - "machine_learning_clip_model_description": "El nom d'un model CLIP que apareix a aquí. Tingues en compte que has de tornar a executar l'Smart Search' per a totes les imatges quan es canvia de model.", + "machine_learning_clip_model_description": "El nom d'un model CLIP que apareix a aquí. Tingues en compte que has de tornar a executar la cerca intel¡ligent per a totes les imatges quan es canvia de model.", "machine_learning_duplicate_detection": "DetecciÃŗ de duplicats", - "machine_learning_duplicate_detection_enabled": "Activa detecciÃŗ de duplicats", - "machine_learning_duplicate_detection_enabled_description": "Si es deshabilitat, els elements exactament idèntics encara es desduplicaran.", + "machine_learning_duplicate_detection_enabled": "Activa la detecciÃŗ de duplicats", + "machine_learning_duplicate_detection_enabled_description": "Si està desactivada, els elements idèntics encara es desduplicaran.", "machine_learning_duplicate_detection_setting_description": "Usa incrustacions CLIP per a trobar prossibles duplicats", "machine_learning_enabled": "Activa l'aprenentatge automàtic", - "machine_learning_enabled_description": "Si està deshabilitat, totes les funcions ML es deshabilitaran sense tenir en compte la configuraciÃŗ segÃŧent.", + "machine_learning_enabled_description": "Si està desactivat, totes les funcions ML es deshabilitaran sense tenir en compte la configuraciÃŗ segÃŧent.", "machine_learning_facial_recognition": "Reconeixement facial", "machine_learning_facial_recognition_description": "Detecta, reconeix i agrupa cares a les imatges", "machine_learning_facial_recognition_model": "Model de reconeixement facial", "machine_learning_facial_recognition_model_description": "Els models es llisten en ordre descent segons la mida. Els models mÊs grans sÃŗn mÊs lents i usen mÊs memÃ˛ria perÃ˛ produeixen millors resultats. Tingueu en compte que desprÊs de canviar un model haureu de tornar a executar la tasca de detecciÃŗ de cares per a totes les imatges.", - "machine_learning_facial_recognition_setting": "Activa reconeixement facial", - "machine_learning_facial_recognition_setting_description": "Si està deshabilitat, les imatges no es codificaran pel reconeixement facial i no s'afegiran a la secciÃŗ Persones de la pàgina Explorar.", + "machine_learning_facial_recognition_setting": "Activa el reconeixement facial", + "machine_learning_facial_recognition_setting_description": "Si està desactivat, les imatges no es codificaran pel reconeixement facial i no s'afegiran a la secciÃŗ Persones de la pàgina Explorar.", "machine_learning_max_detection_distance": "Distància màxima de detecciÃŗ", "machine_learning_max_detection_distance_description": "Diferència màxima entre dues imatges per a considerar-les duplicades, en un rang d'entre 0.001-0.1. Com mÊs elevat el valor mÊs detecciÃŗ de duplicats, perÃ˛ pot resultar en falsos positius.", "machine_learning_max_recognition_distance": "Màxima diferència de reconeixement", @@ -138,17 +136,17 @@ "machine_learning_min_recognized_faces_description": "El nombre mínim de cares reconegudes per crear una persona. Augmentar aquest valor fa que el reconeixement facial sigui mÊs precís, perÃ˛ augmenta la possibilitat que una cara no sigui assignada a una persona.", "machine_learning_settings": "ConfiguraciÃŗ d'aprenentatge automàtic", "machine_learning_settings_description": "Gestiona funcions i configuraciÃŗ d'aprenentatge automàtic", - "machine_learning_smart_search": "Cerca Intel¡ligent", + "machine_learning_smart_search": "Cerca intel¡ligent", "machine_learning_smart_search_description": "Cerca imatges semànticament emprant incrustacions CLIP", "machine_learning_smart_search_enabled": "Activa la cerca intel¡ligent", - "machine_learning_smart_search_enabled_description": "Si està deshabilitat les imatges no es codificaran per la cerca intel¡ligent.", + "machine_learning_smart_search_enabled_description": "Si està desactivada, les imatges no es codificaran per la cerca intel¡ligent.", "machine_learning_url_description": "L'URL del servidor d'aprenentatge automàtic. Si es proporciona mÊs d'un URL, s'intentarà accedir a cada servidor en ordre fins que un d'ells respongui correctament.", "manage_concurrency": "Gestiona la concurrència", "manage_log_settings": "Gestiona la configuraciÃŗ del registre", "map_dark_style": "Tema fosc", "map_enable_description": "Habilita característiques del mapa", - "map_gps_settings": "ConfiguraciÃŗ de mapa i GPS", - "map_gps_settings_description": "Gestiona la configuraciÃŗ de mapa i GPS (GeocodificaciÃŗ inversa)", + "map_gps_settings": "ConfiguraciÃŗ del mapa i GPS", + "map_gps_settings_description": "Gestiona la configuraciÃŗ del mapa i GPS (GeocodificaciÃŗ inversa)", "map_implications": "La funciÃŗ mapa depèn del servei extern de tesel¡les (tiles.immich.cloud)", "map_light_style": "Tema clar", "map_manage_reverse_geocoding_settings": "Gestiona els paràmetres de geocodificaciÃŗ inversa", @@ -173,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Nota: Per aplicar l'etiquetatge d'emmagatzematge a elements pujats prèviament, executeu la", "note_cannot_be_changed_later": "NOTA: AixÃ˛ Ês irreversible!", "notification_email_from_address": "Des de l'adreça", - "notification_email_from_address_description": "Adreça de correu electrÃ˛nic del remitent, per exemple: \"Immich Photo Server \"", + "notification_email_from_address_description": "Adreça de correu electrÃ˛nic del remitent, per exemple: \"Immich Photo Server \". Assegureu-vos d'utilitzar una adreça des de la qual tingueu permís per enviar correus electrÃ˛nics.", "notification_email_host_description": "AmfitriÃŗ del servidor de correu electrÃ˛nic (p.ex. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignora els errors de certificat", "notification_email_ignore_certificate_errors_description": "Ignora els errors de validaciÃŗ de certificat TLS (no recomanat)", @@ -197,7 +195,7 @@ "oauth_enable_description": "Iniciar sessiÃŗ amb OAuth", "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_mobile_redirect_uri_override_description": "Habilita quan el proveïdor d'OAuth no permet una URI mÃ˛bil, com ara ''{callback}''", "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Ãŗ.", @@ -209,8 +207,6 @@ "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", "password_settings": "Inici de sessiÃŗ amb contrasenya", "password_settings_description": "Gestiona la configuraciÃŗ de l'inici de sessiÃŗ amb contrasenya", @@ -220,9 +216,6 @@ "refreshing_all_libraries": "Actualitzant totes les biblioteques", "registration": "Registre d'administrador", "registration_description": "Com que ets el primer usuari del sistema, seràs designat com a administrador i seràs responsable de les tasques administratives. TambÊ seràs l'encarregat de crear usuaris addicionals.", - "repair_all": "Reparar tot", - "repair_matched_items": "Coincidència {count, plural, one {# element} other {# elements}}", - "repaired_items": "Corregit {count, plural, one {# element} other {# elements}}", "require_password_change_on_login": "Requerir que l'usuari canviï la contrasenya en el primer inici de sessiÃŗ", "reset_settings_to_default": "Restablir configuracions per defecte", "reset_settings_to_recent_saved": "Restablir la configuraciÃŗ guardada mÊs recent", @@ -251,7 +244,6 @@ "storage_template_migration_info": "Les extensions es convertiran a minÃēscules. Els canvis de plantilla nomÊs s'aplicaran a nous elements. Per aplicar la plantilla rectroactivament a elements pujats prèviament, executeu la {job}.", "storage_template_migration_job": "Tasca de migraciÃŗ de la plantilla d'emmagatzematge", "storage_template_more_details": "Per obtenir mÊs detalls sobre aquesta funciÃŗ, consulteu la Storage Template i les seves implications", - "storage_template_onboarding_description": "Quan està activada, aquesta funciÃŗ organitzarà automàticament els fitxers en funciÃŗ d'una plantilla definida per l'usuari. A causa de problemes d'estabilitat, la funciÃŗ s'ha desactivat de manera predeterminada. Per obtenir mÊs informaciÃŗ, consulteu la documentation.", "storage_template_path_length": "Límit aproximat de longitud de la ruta: {length, number}/{limit, number}", "storage_template_settings": "Plantilla d'emmagatzematge", "storage_template_settings_description": "Gestiona l'estructura de les carpetes i el nom del fitxers dels elements pujats", @@ -263,16 +255,14 @@ "template_email_invite_album": "Plantilla per l'àlbum d'invitacions", "template_email_preview": "Vista prèvia", "template_email_settings": "Plantilles de correu electrÃ˛nic", - "template_email_settings_description": "Gestionar les plantilles de notificaciÃŗ per correu electrÃ˛nic personalitzades", "template_email_update_album": "Actualitzar la plantilla de l'àlbum", "template_email_welcome": "Plantilla del correu de benvinguda", "template_settings": "Plantilles de notificaciÃŗ", - "template_settings_description": "Gestiona les plantilles personalitzades per les notificacions.", + "template_settings_description": "Gestiona les plantilles personalitzades per les notificacions", "theme_custom_css_settings": "CSS personalitzat", - "theme_custom_css_settings_description": "Els Fulls d'Estil en Cascada permeten personalitzar el disseny d'Immich.", + "theme_custom_css_settings_description": "Els fulls d'estil en cascada permeten personalitzar el disseny d'Immich.", "theme_settings": "ConfiguraciÃŗ del tema", "theme_settings_description": "Gestiona la personalitzaciÃŗ de la interfície web Immich", - "these_files_matched_by_checksum": "Aquests fitxers coincideixen amb els seus checksums", "thumbnail_generation_job": "Generar miniatures", "thumbnail_generation_job_description": "Genera miniatures grans, petites i borroses per a cada element, així com miniatures per a cada persona", "transcoding_acceleration_api": "API d'acceleraciÃŗ", @@ -300,10 +290,9 @@ "transcoding_encoding_options": "Opcions de codificaciÃŗ", "transcoding_encoding_options_description": "Establiu cÃ˛decs, resoluciÃŗ, qualitat i altres opcions per als vídeos codificats", "transcoding_hardware_acceleration": "AcceleraciÃŗ de maquinari", - "transcoding_hardware_acceleration_description": "Experimental. Molt mÊs ràpid, perÃ˛ tindrà una qualitat mÊs baixa amb la mateixa taxa de bits", + "transcoding_hardware_acceleration_description": "Experimental: transcodificaciÃŗ mÊs ràpida perÃ˛ pot perdre qualitat amb la mateixa tasa de bits", "transcoding_hardware_decoding": "DescodificaciÃŗ de maquinari", "transcoding_hardware_decoding_setting_description": "Habilita l'acceleraciÃŗ d'extrem a extrem en lloc d'accelerar nomÊs la codificaciÃŗ. És possible que no funcioni en tots els vídeos.", - "transcoding_hevc_codec": "CÃ˛dec HEVC", "transcoding_max_b_frames": "Nombre màxim de B-frames", "transcoding_max_b_frames_description": "Els valors mÊs alts milloren l'eficiència de la compressiÃŗ, perÃ˛ alenteixen la codificaciÃŗ. És possible que no sigui compatible amb l'acceleraciÃŗ de maquinari en dispositius antics. 0 desactiva els B-frames, mentre que -1 estableix aquest valor automàticament.", "transcoding_max_bitrate": "Taxa de bits màxima", @@ -341,8 +330,6 @@ "trash_number_of_days_description": "Nombre de dies per mantenir els recursos a la paperera abans de suprimir-los permanentment", "trash_settings": "ConfiguraciÃŗ de la paperera", "trash_settings_description": "Gestiona la configuraciÃŗ de la paperera", - "untracked_files": "Fitxers sense seguiment", - "untracked_files_description": "L'aplicaciÃŗ no fa un seguiment d'aquests fitxers. Poden ser el resultat de moviments fallits, càrregues interrompudes o deixades enrere a causa d'un error", "user_cleanup_job": "Neteja d'usuari", "user_delete_delay": "El compte i els recursos de {user} es programaran per a la supressiÃŗ permanent en {delay, plural, one {# dia} other {# dies}}.", "user_delete_delay_settings": "Retard de la supressiÃŗ", @@ -367,7 +354,7 @@ }, "admin_email": "Correu de l'administrador", "admin_password": "Contrasenya de l'administrador", - "administration": "Administrador", + "administration": "AdministraciÃŗ", "advanced": "Avançat", "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", @@ -401,10 +388,6 @@ "album_remove_user": "Eliminar l'usuari?", "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": "{count} elements", - "album_thumbnail_card_shared": " ¡ Compartit", - "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}", @@ -420,6 +403,9 @@ "album_with_link_access": "Permet que qualsevol persona que tingui l'enllaç vegi fotos i persones d'aquest àlbum.", "albums": "Àlbums", "albums_count": "{count, plural, one {{count, number} Àlbum} other {{count, number} Àlbums}}", + "albums_default_sort_order": "Ordre per defecte de l'àlbum", + "albums_default_sort_order_description": "Ordre de classificaciÃŗ inicial dels recursos al crear àlbums nous.", + "albums_feature_description": "Col¡leccions d'actius que es poden compartir amb altres usuaris.", "all": "Tots", "all_albums": "Tots els àlbum", "all_people": "Tota la gent", @@ -478,9 +464,12 @@ "assets_added_count": "{count, plural, one {Afegit un element} other {Afegits # elements}}", "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_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no es pot afegir a l'àlbum", "assets_count": "{count, plural, one {# recurs} other {# recursos}}", "assets_deleted_permanently": "{count} element(s) esborrats permanentment", "assets_deleted_permanently_from_server": "{count} element(s) esborrats permanentment del servidor d'Immich", + "assets_downloaded_failed": "{count, plural, one {S'ha baixat un arxiu - {error} l'arxiu ha fallat} other {S'han baixat # arxius - {error} els arxius han fallat}}", + "assets_downloaded_successfully": "{count, plural, one {S'ha baixat un arxiu amb èxit} other {S'han baixat # arxius amb èxit}}", "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}}", @@ -495,6 +484,7 @@ "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", "automatic_endpoint_switching_title": "Canvi automàtic d'URL", + "autoplay_slideshow": "Reprodueix automàticament les diapositives", "back": "Enrere", "back_close_deselect": "Tornar, tancar o anul¡lar la selecciÃŗ", "background_location_permission": "Permís d'ubicaciÃŗ en segon pla", @@ -538,6 +528,7 @@ "backup_controller_page_excluded": "Exclosos: ", "backup_controller_page_failed": "Fallats ({count})", "backup_controller_page_filename": "Nom de l'arxiu: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "InformaciÃŗ de la cÃ˛pia", "backup_controller_page_none_selected": "Cap seleccionat", "backup_controller_page_remainder": "Restant", @@ -561,6 +552,10 @@ "backup_options_page_title": "Opcions de cÃ˛pia de seguretat", "backup_setting_subtitle": "Gestiona la configuraciÃŗ de càrrega en segon pla i en primer pla", "backward": "Enrere", + "biometric_auth_enabled": "AutentificaciÃŗ biomètrica activada", + "biometric_locked_out": "Esteu bloquejats fora de l'autenticaciÃŗ biomètrica", + "biometric_no_options": "No hi ha opcions biomètriques disponibles", + "biometric_not_available": "L'autenticaciÃŗ biomètrica no està disponible en aquest dispositiu", "birthdate_saved": "Data de naixement guardada amb èxit", "birthdate_set_description": "La data de naixement s'utilitza per calcular l'edat d'aquesta persona en el moment d'una foto.", "blurred_background": "Fons difuminat", @@ -571,21 +566,17 @@ "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 ({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 ({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": "{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 ({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", @@ -598,7 +589,10 @@ "cannot_merge_people": "No es pot fusionar gent", "cannot_undo_this_action": "Aquesta acciÃŗ no es pot desfer!", "cannot_update_the_description": "No es pot actualitzar la descripciÃŗ", + "cast": "Emet", + "cast_description": "Configurar les destinacions de transmissiÃŗ disponibles", "change_date": "Canvia la data", + "change_description": "Canvia la descripciÃŗ", "change_display_order": "Canvia l'ordre de visualitzaciÃŗ", "change_expiration_time": "Canvia la data d'expiraciÃŗ", "change_location": "Canvia la ubicaciÃŗ", @@ -614,7 +608,6 @@ "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", "check_corrupt_asset_backup": "Comprovar les cÃ˛pies de seguretat corruptes", "check_corrupt_asset_backup_button": "Realitzar comprovaciÃŗ", "check_corrupt_asset_backup_description": "Executeu aquesta comprovaciÃŗ nomÊs mitjançant Wi-Fi i un cop s'hagi fet una cÃ˛pia de seguretat de tots els actius. El procediment pot trigar uns minuts.", @@ -626,6 +619,7 @@ "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", @@ -637,6 +631,7 @@ "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", @@ -652,9 +647,13 @@ "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", + "confirm_tag_face": "Vols etiquetar aquesta cara com a {name}?", + "confirm_tag_face_unnamed": "Com vols etiquetar aquesta cara?", + "connected_device": "Dispositiu connectat", + "connected_to": "Connectat a", "contain": "Contingut", + "context": "Context", "continue": "Continuar", - "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", @@ -703,6 +702,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Fosc", + "darkTheme": "Activa/desactiva el tema fosc", "date_after": "Data posterior a", "date_and_time": "Data i hora", "date_before": "Data anterior a", @@ -748,7 +748,9 @@ "direction": "DirecciÃŗ", "disabled": "Desactivat", "disallow_edits": "No permetre les edicions", + "discord": "Discord", "discover": "Descobreix", + "discovered_devices": "Dispositius descoberts", "dismiss_all_errors": "Descarta tots els errors", "dismiss_error": "Descarta l'error", "display_options": "Opcions de visualitzaciÃŗ", @@ -764,7 +766,6 @@ "download_enqueue": "Descàrrega en cua", "download_error": "Error de descàrrega", "download_failed": "Descàrrega ha fallat", - "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", @@ -788,6 +789,8 @@ "edit_avatar": "Edita l'avatar", "edit_date": "Edita la data", "edit_date_and_time": "Edita data i hora", + "edit_description": "Edita la descripciÃŗ", + "edit_description_prompt": "Si us plau, selecciona una nova descripciÃŗ:", "edit_exclusion_pattern": "Edita patrÃŗ d'exclusiÃŗ", "edit_faces": "Edita les cares", "edit_import_path": "Edita la ruta d'importaciÃŗ", @@ -802,6 +805,7 @@ "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", @@ -812,13 +816,19 @@ "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Ãŗ!", "enable": "Activar", + "enable_biometric_auth_description": "Introduïu el codi PIN per a habilitar l'autenticaciÃŗ biomètrica", "enabled": "Activat", "end_date": "Data final", "enqueued": "En cua", "enter_wifi_name": "Introdueix el nom de Wi-Fi", + "enter_your_pin_code": "Introduïu el codi PIN", + "enter_your_pin_code_subtitle": "Introduïu el codi PIN per a accedir a la carpeta protegida", + "error": "Error", "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}", + "error_tag_face_bounding_box": "Error a l'etiquetar la cara - no s'han pogut obtenir les coordenades de l'àrea", "error_title": "Error - Quelcom ha anat malament", "errors": { "cannot_navigate_next_asset": "No es pot navegar a l'element segÃŧent", @@ -831,7 +841,6 @@ "cant_get_number_of_comments": "No es pot obtenir el nombre de comentaris", "cant_search_people": "No es poden cercar persones", "cant_search_places": "No es poden cercar llocs", - "cleared_jobs": "Tasques buides per a: {job}", "error_adding_assets_to_album": "Error afegint elements a l'àlbum", "error_adding_users_to_album": "Error afegint usuaris a l'àlbum", "error_deleting_shared_user": "S'ha produït un error en suprimir l'usuari compartit", @@ -840,7 +849,6 @@ "error_removing_assets_from_album": "Error eliminant els elements de l'àlbum, consulteu la consola per obtenir mÊs detalls", "error_selecting_all_assets": "Error seleccionant tots els elements", "exclusion_pattern_already_exists": "Aquest patrÃŗ d’exclusiÃŗ ja existeix.", - "failed_job_command": "L'ordre {command} ha fallat per a la tasca: {job}", "failed_to_create_album": "No s'ha pogut crear l'àlbum", "failed_to_create_shared_link": "No s'ha pogut crear l'enllaç compartit", "failed_to_edit_shared_link": "No s'ha pogut editar l'enllaç compartit", @@ -859,7 +867,6 @@ "paths_validation_failed": "{paths, plural, one {# ruta} other {# rutes}} no ha pogut validar", "profile_picture_transparent_pixels": "Les fotos de perfil no poden tenir píxels transparents. Per favor, feu zoom in, mogueu la imatge o ambdues.", "quota_higher_than_disk_size": "Heu establert una quota mÊs gran que la mida de disc", - "repair_unable_to_check_items": "No es pot comprovar {count, select, one {l'element} other {els elements}}", "unable_to_add_album_users": "No es poden afegir usuaris a l'àlbum", "unable_to_add_assets_to_shared_link": "No s'han pogut afegir els elements a l'enllaç compartit", "unable_to_add_comment": "No es pot afegir el comentari", @@ -871,13 +878,13 @@ "unable_to_archive_unarchive": "No es pot {archived, select, true {arxivar} other {desarxivar}}", "unable_to_change_album_user_role": "No es pot canviar el rol d'usuari de l'àlbum", "unable_to_change_date": "No es pot canviar la data", + "unable_to_change_description": "No s'ha pogut canviar la descripciÃŗ", "unable_to_change_favorite": "No es pot canviar el favorit per a aquest recurs", "unable_to_change_location": "No es pot canviar la ubicaciÃŗ", "unable_to_change_password": "No es pot canviar la contrasenya", "unable_to_change_visibility": "No es pot canviar la visibilitat de {count, plural, one {# persona} other {# persones}}", "unable_to_complete_oauth_login": "No es pot completar l'inici de sessiÃŗ OAuth", "unable_to_connect": "No pot connectar", - "unable_to_connect_to_server": "No es pot connectar al servidor", "unable_to_copy_to_clipboard": "No es pot copiar al porta-retalls, assegureu-vos que esteu accedint a la pàgina mitjançant https", "unable_to_create_admin_account": "No es pot crear un compte d'administrador", "unable_to_create_api_key": "No es pot crear una clau d'API nova", @@ -901,10 +908,6 @@ "unable_to_hide_person": "No es pot amagar la persona", "unable_to_link_motion_video": "No es pot enllaçar el vídeo en moviment", "unable_to_link_oauth_account": "No es pot enllaçar el compte OAuth", - "unable_to_load_album": "No es pot carregar l'àlbum", - "unable_to_load_asset_activity": "No es pot carregar l'activitat dels recursos", - "unable_to_load_items": "No es poden carregar els elements", - "unable_to_load_liked_status": "No es pot carregar l'estat de m'agrada", "unable_to_log_out_all_devices": "No es poden tancar la sessiÃŗ de tots els dispositius", "unable_to_log_out_device": "No es pot tancar la sessiÃŗ del dispositiu", "unable_to_login_with_oauth": "No es pot iniciar sessiÃŗ amb OAuth", @@ -915,11 +918,9 @@ "unable_to_remove_album_users": "No es poden eliminar usuaris de l'àlbum", "unable_to_remove_api_key": "No es pot eliminar la clau de l'API", "unable_to_remove_assets_from_shared_link": "No es poden eliminar recursos de l'enllaç compartit", - "unable_to_remove_deleted_assets": "No es poden eliminar els fitxers fora de línia", "unable_to_remove_library": "No es pot eliminar la biblioteca", "unable_to_remove_partner": "No es pot eliminar company/a", "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", @@ -949,12 +950,12 @@ "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_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 {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}", @@ -963,6 +964,7 @@ "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}", @@ -977,6 +979,7 @@ "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_authenticate": "No s'ha pogut autenticar", "failed_to_load_assets": "Error carregant recursos", "failed_to_load_folder": "No s'ha pogut carregar la carpeta", "favorite": "Preferit", @@ -1000,6 +1003,9 @@ "folders": "Carpetes", "folders_feature_description": "Explorar la vista de carpetes per les fotos i vídeos del sistema d'arxius", "forward": "Endavant", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Aquesta funciÃŗ carrega recursos externs de Google per funcionar.", + "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", @@ -1041,10 +1047,13 @@ "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_locked_error_local": "No s'han pogut moure els recursos locals a la carpeta bloquejada, saltant", + "home_page_locked_error_partner": "No s'han pogut moure els recursos de la parella a la carpeta bloquejada, saltant", "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Ãŗ", "hour": "Hora", + "id": "ID", "ignore_icloud_photos": "Ignora fotos d'iCloud", "ignore_icloud_photos_description": "Les fotos emmagatzemades a iCloud no es penjaran al servidor Immich", "image": "Imatge", @@ -1084,6 +1093,12 @@ "invalid_date_format": "Format de data invàlid", "invite_people": "Convida gent", "invite_to_album": "Convida a l'àlbum", + "ios_debug_info_fetch_ran_at": "La recuperaciÃŗ s'ha executat {dateTime}", + "ios_debug_info_last_sync_at": "Darrera sincronitzaciÃŗ {dateTime}", + "ios_debug_info_no_processes_queued": "No hi ha processos en segon pla en cua", + "ios_debug_info_no_sync_yet": "Encara no s'ha executat cap tasca de sincronitzaciÃŗ en segon pla", + "ios_debug_info_processes_queued": "{count, plural, one {Un procÊs en segon pla a la cua} other {{count} processos en segon pla a la cua}}", + "ios_debug_info_processing_ran_at": "El processament s'ha executat {dateTime}", "items_count": "{count, plural, one {# element} other {# elements}}", "jobs": "Tasques", "keep": "Mantenir", @@ -1092,6 +1107,9 @@ "kept_this_deleted_others": "S'ha conservat aquest element i s'han suprimit {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Dreceres de teclat", "language": "Idioma", + "language_no_results_subtitle": "Prova d'ajustar el terme de cerca", + "language_no_results_title": "No s'han trobat idiomes", + "language_search_hint": "Cerca idiomes...", "language_setting_description": "Seleccioneu el vostre idioma", "last_seen": "Vist per Ãēltim cop", "latest_version": "Última versiÃŗ", @@ -1117,6 +1135,7 @@ "list": "Llista", "loading": "Carregant", "loading_search_results_failed": "No s'han pogut carregar els resultats de la cerca", + "local_asset_cast_failed": "No es pot convertir un actiu que no s'ha penjat al servidor", "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Ãŗ", @@ -1126,6 +1145,8 @@ "location_picker_latitude_hint": "Introdueix aquí la latitud", "location_picker_longitude_error": "Introdueix una longitud vàlida", "location_picker_longitude_hint": "Introdueix aquí la longitud", + "lock": "Bloqueja", + "locked_folder": "Carpeta bloquejada", "log_out": "Tanca la sessiÃŗ", "log_out_all_devices": "Tanqueu la sessiÃŗ de tots els dispositius", "logged_out_all_devices": "S'ha tancat la sessiÃŗ de tots els dispositius", @@ -1159,7 +1180,7 @@ "look": "Aspecte", "loop_videos": "Vídeos en bucle", "loop_videos_description": "Habilita la reproducciÃŗ en bucle del vídeo en els detalls.", - "main_branch_warning": "Esteu usant una versiÃŗ de desenvolupaent. Recomanem fer servir una versiÃŗ publicada!", + "main_branch_warning": "Esteu utilitzant una versiÃŗ en desenvolupament; Recomanem fer servir una versiÃŗ publicada!", "main_menu": "MenÃē principal", "make": "Fabricant", "manage_shared_links": "Administrar enllaços compartits", @@ -1217,8 +1238,14 @@ "minimize": "Minimitza", "minute": "Minut", "missing": "Restants", + "model": "Model", "month": "Mes", + "monthly_title_text_date_format": "MMMM y", "more": "MÊs", + "move": "Moure", + "move_off_locked_folder": "Moure fora de la carpeta bloquejada", + "move_to_locked_folder": "Moure a la carpeta bloquejada", + "move_to_locked_folder_confirmation": "Aquestes fotos i vídeos seran eliminades de tots els àlbums, i nomÊs podran ser vistes des de la carpeta bloquejada", "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", @@ -1236,22 +1263,26 @@ "new_password": "Nova contrasenya", "new_person": "Persona nova", "new_pin_code": "Nou codi PIN", + "new_pin_code_subtitle": "Aquesta Ês la primera vegada que accedeixes a la carpeta bloquejada. Crea una codi PIN i accedeix de manera segura a aquesta pàgina", "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.", "no_archived_assets_message": "Arxiveu fotos i vídeos per ocultar-los de Fotos", "no_assets_message": "FEU CLIC PER PUJAR LA VOSTRA PRIMERA FOTO", "no_assets_to_show": "No hi ha elements per mostrar", + "no_cast_devices_found": "No s'han trobat dispositius per transmetre", "no_duplicates_found": "No s'han trobat duplicats.", "no_exif_info_available": "No hi ha informaciÃŗ d'exif disponible", "no_explore_results_message": "Penja mÊs fotos per explorar la teva col¡lecciÃŗ.", "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_locked_photos_message": "Les fotos i vídeos d'aquesta carpeta estan ocultes, i no es mostraran a mesura que navegues o cerques a la teva biblioteca.", "no_name": "Sense nom", "no_notifications": "No hi ha notificacions", "no_people_found": "No s'han trobat coincidències de persones", @@ -1262,6 +1293,8 @@ "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", + "nothing_here_yet": "No hi ha res encara", "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", @@ -1269,17 +1302,18 @@ "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", - "offline_paths_description": "Aquests resultats poden ser deguts a la supressiÃŗ manual de fitxers que no formen part d'una biblioteca externa.", "ok": "D'acord", "oldest_first": "El mÊs vell primer", "on_this_device": "En aquest dispositiu", "onboarding": "IncorporaciÃŗ", - "onboarding_privacy_description": "Les segÃŧents funcions (opcionals) depenen de serveis externs i poden desactivarse en qualsevol moment de dels ajustos.", + "onboarding_locale_description": "Tria el teu llenguatge preferit. Pots canviar aquesta opciÃŗ mes tard a la configuraciÃŗ.", + "onboarding_privacy_description": "Les segÃŧents funcions (opcionals) depenen de serveis externs i poden desactivarse en qualsevol moment des de la configuraciÃŗ.", + "onboarding_server_welcome_description": "Configurem la instància amb alguns paràmetres comuns.", "onboarding_theme_description": "Trieu un tema de color per a la vostra instància. Podeu canviar-ho mÊs endavant a la vostra configuraciÃŗ.", - "onboarding_welcome_description": "Configurem la vostra instància amb alguns paràmetres habituals.", + "onboarding_user_welcome_description": "Comencem!", "onboarding_welcome_user": "Benvingut, {user}", "online": "En línia", "only_favorites": "NomÊs preferits", @@ -1290,6 +1324,7 @@ "options": "Opcions", "or": "o", "organize_your_library": "Organitzeu la llibreria", + "original": "original", "other": "Altres", "other_devices": "Altres dispositius", "other_variables": "Altres variables", @@ -1335,6 +1370,8 @@ "permanently_delete_assets_prompt": "Esteu segur que voleu suprimir permanentment {count, plural, one {aquest recurs?} other {aquests # recursos?}} AixÃ˛ tambÊ {count, plural, one {el} other {els}} suprimirà del seu àlbum.", "permanently_deleted_asset": "Element eliminat permanentment", "permanently_deleted_assets_count": "{count, plural, one {S'ha eliminat # element} other {S'han eliminat # elements}} permanentment", + "permission": "Permís", + "permission_empty": "El seu permís no hauria d'estar buit", "permission_onboarding_back": "Torna", "permission_onboarding_continue_anyway": "Continua de totes maneres", "permission_onboarding_get_started": "Comença", @@ -1355,6 +1392,7 @@ "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", + "pin_verification": "VerificaciÃŗ de codi PIN", "place": "Lloc", "places": "Llocs", "places_count": "{count, plural, one {{count, number} Lloc} other {{count, number} Llocs}}", @@ -1362,13 +1400,18 @@ "play_memories": "Reproduir records", "play_motion_photo": "Reproduir Fotos en Moviment", "play_or_pause_video": "Reproduir o posar en pausa el vídeo", + "please_auth_to_access": "Per favor, autentica't per accedir", + "port": "Port", "preferences_settings_subtitle": "Gestiona les preferències de l'aplicaciÃŗ", "preferences_settings_title": "Preferències", "preset": "Preestablert", "preview": "PrevisualitzaciÃŗ", "previous": "Anterior", "previous_memory": "MemÃ˛ria anterior", - "previous_or_next_photo": "Foto anterior o segÃŧent", + "previous_or_next_day": "Dia endavant/enrere", + "previous_or_next_month": "Mes endavant/enrere", + "previous_or_next_photo": "Foto endavant/enrere", + "previous_or_next_year": "Any endavant/enrere", "primary": "Primària", "privacy": "Privacitat", "profile": "Perfil", @@ -1376,8 +1419,9 @@ "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_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_drawer_github": "GitHub", + "profile_drawer_server_out_of_date_major": "El servidor està desactualitzat. Si us plau, actualitzeu a l'Ãēltima versiÃŗ major.", + "profile_drawer_server_out_of_date_minor": "El servidor està desactualitzat. Si us plau, actualitzeu a l'Ãēltima versiÃŗ menor.", "profile_image_of_user": "Imatge de perfil de {user}", "profile_picture_set": "Imatge de perfil configurada.", "public_album": "Àlbum pÃēblic", @@ -1396,12 +1440,13 @@ "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", "purchase_option_title": "OPCIONS DE COMPRA", "purchase_panel_info_1": "Crear Immich requereix molt de temps i esforç, tenim enginyers a temps complet treballant-hi per fer-ho tan bo com sigui possible. La nostra missiÃŗ Ês que el programari de codi obert i les pràctiques empresarials ètiques es converteixin en una font d'ingressos sostenible per als desenvolupadors i creïn un ecosistema que respecti la privacitat amb alternatives reals als serveis cloud explotadors.", - "purchase_panel_info_2": "Com que estem compromesos a no afegir murs de pagament, aquesta compra no us atorgarà cap funciÃŗ addicional a Immich. Confiem en usuaris com tu per donar suport al desenvolupament continu d'Immich.", + "purchase_panel_info_2": "Estem compromesos a no afegir murs de pagament, aquesta compra no us otorgarà cap funciÃŗ addicional a Immich. Confiem en usuaris com tu per donar suport al desenvolupament continuat d'Immich.", "purchase_panel_title": "Donar suport al projecte", "purchase_per_server": "Per servidor", "purchase_per_user": "Per usuari", @@ -1423,6 +1468,7 @@ "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", @@ -1448,9 +1494,12 @@ "remove_deleted_assets": "Suprimeix fitxers fora de línia", "remove_from_album": "Treu de l'àlbum", "remove_from_favorites": "Eliminar dels preferits", + "remove_from_locked_folder": "Elimina de la carpeta bloquejada", + "remove_from_locked_folder_confirmation": "Segur que vols moure aquestes fotos i vídeos fora de la carpeta bloquejada? Seran visibles a la teva biblioteca.", "remove_from_shared_link": "Eliminar de l'enllaç compartit", "remove_memory": "Eliminar memÃ˛ria", "remove_photo_from_memory": "Traieu la foto d'aquesta memÃ˛ria", + "remove_tag": "Elimina l'etiqueta", "remove_url": "Eliminar URL", "remove_user": "Eliminar l'usuari", "removed_api_key": "Eliminada la clau d'API: {name}", @@ -1483,6 +1532,7 @@ "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", @@ -1526,6 +1576,7 @@ "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", @@ -1575,6 +1626,7 @@ "server_info_box_server_url": "URL del servidor", "server_offline": "Servidor fora de línia", "server_online": "Servidor en línia", + "server_privacy": "Privadesa del servidor", "server_stats": "Estadístiques del servidor", "server_version": "VersiÃŗ del servidor", "set": "Establir", @@ -1584,6 +1636,7 @@ "set_date_of_birth": "Establir data de naixement", "set_profile_picture": "Establir imatge de perfil", "set_slideshow_to_fullscreen": "Mostra Diapositives en pantalla completa", + "set_stack_primary_asset": "Estableix com a actiu principal", "setting_image_viewer_help": "El visor de detalls carrega primer la miniatura petita, desprÊs carrega la vista prèvia de mida mitjana (si està habilitada), finalment carrega l'original (si està habilitada).", "setting_image_viewer_original_subtitle": "Activa per carregar la imatge en resoluciÃŗ original (molt gran!). Desactiva per reduir el consum de dades (tant de xarxa com de memÃ˛ria cau).", "setting_image_viewer_original_title": "Carrega la imatge original", @@ -1592,7 +1645,6 @@ "setting_image_viewer_title": "Imatges", "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: {duration}", "setting_notifications_notify_hours": "{count} hores", "setting_notifications_notify_immediately": "immediatament", @@ -1615,6 +1667,7 @@ "share_add_photos": "Afegeix fotografies", "share_assets_selected": "{count} seleccionats", "share_dialog_preparing": "S'està preparant...", + "share_link": "Compartir Enllaç", "shared": "Compartit", "shared_album_activities_input_disable": "Els comentaris estan desactivats", "shared_album_activity_remove_content": "Voleu eliminar aquesta activitat?", @@ -1654,6 +1707,7 @@ "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", @@ -1720,6 +1774,7 @@ "start_date": "Data inicial", "state": "RegiÃŗ", "status": "Estat", + "stop_casting": "Atura la transmisiÃŗ", "stop_motion_photo": "Atura foto en moviment", "stop_photo_sharing": "Deixar de compartir les teves fotos?", "stop_photo_sharing_description": "{partner} no podrà tornar a accedir a les vostres fotos.", @@ -1777,7 +1832,7 @@ "to_parent": "Anar als pares", "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", @@ -1798,6 +1853,7 @@ "unable_to_setup_pin_code": "No s'ha pogut configurar el codi PIN", "unarchive": "Desarxivar", "unarchived_count": "{count, plural, other {# elements desarxivats}}", + "undo": "Desfer", "unfavorite": "Reverteix preferit", "unhide_person": "Mostra persona", "unknown": "Desconegut", @@ -1816,8 +1872,6 @@ "unselect_all_duplicates": "Desmarqueu tots els duplicats", "unstack": "Desapila", "unstacked_assets_count": "No apilat {count, plural, one {# recurs} other {# recursos}}", - "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", @@ -1829,11 +1883,14 @@ "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 ({count})", "uploading": "Pujant", + "url": "URL", "usage": "Ús", + "use_biometric": "Empra biometria", "use_current_connection": "utilitzar la connexiÃŗ actual", "use_custom_date_range": "Fes servir un rang de dates personalitzat", "user": "Usuari", @@ -1842,6 +1899,7 @@ "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_privacy": "Privadesa d'Usuari", "user_purchase_settings": "Compra", "user_purchase_settings_description": "Gestiona la teva compra", "user_role_set": "Establir {user} com a {role}", @@ -1853,14 +1911,10 @@ "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.", - "version_announcement_overlay_release_notes": "notes de llançament", - "version_announcement_overlay_text_1": "Hola amic, hi ha una nova versiÃŗ d'", - "version_announcement_overlay_text_2": "si us plau, pren-te una estona per visitar les ", - "version_announcement_overlay_text_3": " i assegura't que la teva configuraciÃŗ de docker-compose i .env estiguin actualitzades per evitar qualsevol error de configuraciÃŗ, especialment si utilitzes WatchTower o qualsevol mecanisme que gestioni l'actualitzaciÃŗ automàtica de l'aplicaciÃŗ del servidor.", - "version_announcement_overlay_title": "Nova versiÃŗ del servidor disponible 🎉", "version_history": "Historial de versions", "version_history_item": "Instal¡lat {version} el {date}", "video": "Vídeo", @@ -1880,6 +1934,7 @@ "view_previous_asset": "Mostra l'element anterior", "view_qr_code": "Veure codi QR", "view_stack": "Veure la pila", + "view_user": "Veure Usuari", "viewer_remove_from_stack": "Elimina de la pila", "viewer_stack_use_as_main_asset": "Fes servir com a element principal", "viewer_unstack": "Desapila", @@ -1890,6 +1945,7 @@ "welcome": "Benvingut", "welcome_to_immich": "Benvingut a immich", "wifi_name": "Nom Wi-Fi", + "wrong_pin_code": "Codi PIN incorrecte", "year": "Any", "years_ago": "Fa {years, plural, one {# any} other {# anys}}", "yes": "Sí", diff --git a/i18n/cs.json b/i18n/cs.json index 7bfca25c01..82e72cab46 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -22,11 +22,11 @@ "add_partner": "Přidat partnera", "add_path": "Přidat cestu", "add_photos": "Přidat fotky", + "add_tag": "Přidat značku", "add_to": "Přidat doâ€Ļ", "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", @@ -34,6 +34,7 @@ "added_to_favorites_count": "PřidÃĄno {count, number} do oblíbenÃŊch", "admin": { "add_exclusion_pattern_description": "PřidÃĄní vzorů vyloučení. PodporovÃĄno je globovÃĄní pomocí *, ** a ?. Chcete-li ignorovat vÅĄechny soubory v jakÊmkoli adresÃĄÅ™i s nÃĄzvem \"Raw\", pouÅžijte \"**/Raw/**\". Chcete-li ignorovat vÅĄechny soubory končící na \".tif\", pouÅžijte \"**/*.tif\". Chcete-li ignorovat absolutní cestu, pouÅžijte příkaz \"/path/to/ignore/**\".", + "admin_user": "AdministrÃĄtor", "asset_offline_description": "Tato poloÅžka externí knihovny se jiÅž na disku nenachÃĄzí a byla přesunuta do koÅĄe. Pokud byl soubor přesunut v rÃĄmci knihovny, zkontrolujte časovou osu a vyhledejte novÊ odpovídající poloÅžku. Chcete-li tuto poloÅžku obnovit, ujistěte se, Åže je cesta k níŞe uvedenÊmu souboru přístupnÃĄ pomocí aplikace Immich a prohledejte knihovnu.", "authentication_settings": "PřihlaÅĄovÃĄní", "authentication_settings_description": "SprÃĄva hesel, OAuth a dalÅĄÃ­ch nastavení ověření", @@ -44,9 +45,7 @@ "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í", + "backup_settings_description": "SprÃĄva nastavení vÃŊpisu databÃĄze.", "cleared_jobs": "HotovÊ Ãēlohy pro: {job}", "config_set_by_file": "Konfigurace je aktuÃĄlně provÃĄděna konfiguračním souborem", "confirm_delete_library": "Opravdu chcete odstranit knihovnu {library}?", @@ -62,14 +61,12 @@ "disable_login": "ZakÃĄzat přihlÃĄÅĄení", "duplicate_detection_job_description": "SpuÅĄtění strojovÊho učení na poloÅžkÃĄch za Ãēčelem detekce podobnÃŊch obrÃĄzků. SpolÊhÃĄ na ChytrÊ vyhledÃĄvÃĄní", "exclusion_pattern_description": "Vzory vyloučení umoŞňují při prohledÃĄvÃĄní knihovny ignorovat soubory a sloÅžky. To je uÅžitečnÊ, pokud mÃĄte sloÅžky obsahující soubory, kterÊ nechcete importovat, například RAW soubory.", - "external_library_created_at": "Externí knihovna (vytvořena {date})", "external_library_management": "SprÃĄva externích knihoven", "face_detection": "Detekce obličejů", "face_detection_description": "Detekce obličejů v obrÃĄzcích pomocí strojovÊho učení. U videí se bere v Ãēvahu pouze miniatura. „Obnovit“ znovu zpracuje vÅĄechny poloÅžky. „Resetovat“ navíc vymaÅže vÅĄechna aktuÃĄlní data obličejů. „Chybějící“ zařadí do fronty poloÅžky, kterÊ jeÅĄtě nebyly zpracovÃĄny. ZjiÅĄtěnÊ obličeje budou po dokončení funkce RozpoznÃĄvÃĄní obličejů zařazeny do fronty a seskupeny do stÃĄvajících nebo novÃŊch osob.", "facial_recognition_job_description": "Seskupí nalezenÊ obličeje do osob. Tento krok se spustí po dokončení detekce obličejů. „Resetovat“ znovu seskupí vÅĄechny obličeje. „Chybějící“ zpracuje obličeje, kterÊ nemají přiřazenou osobu.", "failed_job_command": "Příkaz {command} se nezdařil pro Ãēlohu: {job}", "force_delete_user_warning": "UPOZORNĚNÍ: Tímto okamÅžitě odstraníte uÅživatele a vÅĄechny jeho poloÅžky. Tento krok nelze vrÃĄtit zpět a soubory nelze obnovit.", - "forcing_refresh_library_files": "Vynucení obnovy vÅĄech souborů knihovny", "image_format": "FormÃĄt", "image_format_description": "WebP vytvÃĄÅ™Ã­ menÅĄÃ­ soubory neÅž JPEG, ale je pomalejÅĄÃ­ při kÃŗdovÃĄní.", "image_fullsize_description": "ObrÃĄzek v plnÊ velikosti s odstraněnÃŊmi metadaty, pouÅžito při přiblíŞení", @@ -174,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Upozornění: Pro uplatnění Å títku ÃēloÅžiÅĄtě na dříve nahranÊ poloÅžky spusÅĨte", "note_cannot_be_changed_later": "UPOZORNĚNÍ: Toto nelze později změnit!", "notification_email_from_address": "Adresa Od", - "notification_email_from_address_description": "E-mailovÃĄ adresa odesílatele, např.: „Immich Photo Server “", + "notification_email_from_address_description": "E-mailovÃĄ adresa odesílatele, např.: „Immich Photo Server “. Ujistěte se, Åže pouŞívÃĄte adresu, ze kterÊ smíte odesílat e-maily.", "notification_email_host_description": "Adresa e-mailovÊho serveru (např. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorovat chyby certifikÃĄtů", "notification_email_ignore_certificate_errors_description": "Ignorovat chyby ověření certifikÃĄtu TLS (nedoporučuje se)", @@ -198,7 +195,7 @@ "oauth_enable_description": "PřihlÃĄsit pomocí OAuth", "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_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například ''{callback}''", "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.", @@ -207,11 +204,9 @@ "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_storage_quota_default_description": "KvÃŗta v GiB, kterÃĄ se pouÅžije, pokud není poskytnuta ÅžÃĄdnÃĄ deklarace.", "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", "password_settings": "PřihlÃĄÅĄení heslem", "password_settings_description": "SprÃĄva nastavení přihlaÅĄovÃĄní pomocí hesla", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "Obnovení vÅĄech knihoven", "registration": "Registrace sprÃĄvce", "registration_description": "Vzhledem k tomu, Åže jste prvním uÅživatelem v systÊmu, budete přiřazen jako sprÃĄvce a budete zodpovědnÃŊ za Ãēkoly sprÃĄvy a dalÅĄÃ­ uÅživatelÊ budou vytvořeni vÃĄmi.", - "repair_all": "Opravit vÅĄe", - "repair_matched_items": "Shoda {count, plural, one {# poloÅžky} other {# poloÅžek}}", - "repaired_items": "{count, plural, one {Opravena # poloÅžka} few {Opraveny # poloÅžky} other {Opraveno # poloÅžek}}", "require_password_change_on_login": "PoÅžadovat, aby si uÅživatel při prvním přihlÃĄÅĄení změnil heslo", "reset_settings_to_default": "Obnovení vÃŊchozího nastavení", "reset_settings_to_recent_saved": "Obnovit poslední uloÅženÊ nastavení", @@ -252,7 +244,7 @@ "storage_template_migration_info": "Å ablona ÃēloÅžiÅĄtě převede vÅĄechny přípony na malÃĄ písmena. Změny ÅĄablon se uplatní pouze u novÃŊch poloÅžek. Chcete-li ÅĄablonu zpětně pouŞít na dříve nahranÊ poloÅžky, spusÅĨte {job}.", "storage_template_migration_job": "Úloha migrace ÅĄablony ÃēloÅžiÅĄtě", "storage_template_more_details": "DalÅĄÃ­ podrobnosti o tÊto funkci naleznete v sekci Å ablona ÃēloÅžiÅĄtě včetně jejích důsledků", - "storage_template_onboarding_description": "Je-li tato funkce povolena, automaticky uspoÅ™ÃĄdÃĄ soubory na zÃĄkladě uÅživatelem definovanÊ ÅĄablony. Z důvodu problÊmů se stabilitou byla tato funkce ve vÃŊchozím nastavení vypnuta. DalÅĄÃ­ informace naleznete v dokumentaci.", + "storage_template_onboarding_description_v2": "Pokud je tato funkce povolena, automaticky uspoÅ™ÃĄdÃĄ soubory na zÃĄkladě uÅživatelem definovanÊ ÅĄablony. DalÅĄÃ­ informace naleznete v dokumentaci.", "storage_template_path_length": "PřibliÅžnÃŊ limit dÊlky cesty: {length, number}/{limit, number}", "storage_template_settings": "Å ablona ÃēloÅžiÅĄtě", "storage_template_settings_description": "SprÃĄva struktury sloÅžek a nÃĄzvů nahranÃŊch souborů", @@ -264,16 +256,14 @@ "template_email_invite_album": "Å ablona pozvÃĄnky do alba", "template_email_preview": "NÃĄhled", "template_email_settings": "Å ablony e-mailů", - "template_email_settings_description": "SprÃĄva vlastních ÅĄablon e-mailovÃŊch oznÃĄmení", "template_email_update_album": "Å ablona aktualizace alba", "template_email_welcome": "Å ablona uvítacího e-mailu", "template_settings": "Å ablony oznÃĄmení", - "template_settings_description": "SprÃĄva vlastních ÅĄablon oznÃĄmení.", + "template_settings_description": "SprÃĄva vlastních ÅĄablon oznÃĄmení", "theme_custom_css_settings": "Vlastní CSS", "theme_custom_css_settings_description": "KaskÃĄdovÊ styly umoŞňují přizpůsobit design aplikace Immich.", "theme_settings": "Motivy", "theme_settings_description": "SprÃĄva přizpůsobení webovÊho rozhraní Immich", - "these_files_matched_by_checksum": "Tyto soubory jsou porovnÃĄvÃĄny podle jejich kontrolních součtů", "thumbnail_generation_job": "GenerovÃĄní miniatur", "thumbnail_generation_job_description": "GenerovÃĄní velkÃŊch, malÃŊch a rozmazanÃŊch miniatur pro kaÅždÃŊ obrÃĄzek a miniatur pro kaÅždou osobu", "transcoding_acceleration_api": "API pro akceleraci", @@ -301,10 +291,9 @@ "transcoding_encoding_options": "MoÅžnosti kÃŗdovÃĄní", "transcoding_encoding_options_description": "Nastavte kodeky, rozliÅĄení, kvalitu a dalÅĄÃ­ moÅžnosti pro kÃŗdovanÃĄ videa", "transcoding_hardware_acceleration": "HardwarovÃĄ akcelerace", - "transcoding_hardware_acceleration_description": "ExperimentÃĄlní; mnohem rychlejÅĄÃ­, ale při stejnÊm datovÊm toku bude mít niÅžÅĄÃ­ kvalitu", + "transcoding_hardware_acceleration_description": "ExperimentÃĄlní: rychlejÅĄÃ­ kÃŗdovÃĄní, ale při stejnÊm datovÊm toku můŞe mít niÅžÅĄÃ­ kvalitu", "transcoding_hardware_decoding": "HardwarovÊ dekÃŗdovÃĄní", "transcoding_hardware_decoding_setting_description": "Povoluje kompletní akceleraci namísto akcelerace pouze kÃŗdovÃĄní. Nemusí fungovat u vÅĄech videí.", - "transcoding_hevc_codec": "Kodek HEVC", "transcoding_max_b_frames": "MaximÃĄlní počet B-snímků", "transcoding_max_b_frames_description": "VyÅĄÅĄÃ­ hodnoty zvyÅĄují Ãēčinnost komprese, ale zpomalují kÃŗdovÃĄní. Nemusí bÃŊt kompatibilní s hardwarovou akcelerací na starÅĄÃ­ch zařízeních. Hodnota 0 zakÃĄÅže B-snímky, zatímco -1 tuto hodnotu nastaví automaticky.", "transcoding_max_bitrate": "MaximÃĄlní datovÃŊ tok", @@ -342,8 +331,6 @@ "trash_number_of_days_description": "Počet dní, po kterÊ je třeba poloÅžku ponechat v koÅĄi, neÅž bude trvale odstraněna", "trash_settings": "KoÅĄ", "trash_settings_description": "SprÃĄva nastavení koÅĄe", - "untracked_files": "NeznÃĄmÊ soubory", - "untracked_files_description": "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ě", "user_cleanup_job": "PromazÃĄní uÅživatelů", "user_delete_delay": "Účet a poloÅžky uÅživatele {user} budou trvale smazÃĄny za {delay, plural, one {# den} few {# dny} other {# dní}}.", "user_delete_delay_settings": "OdloÅžení odstranění", @@ -384,8 +371,8 @@ "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": "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_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_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", @@ -402,10 +389,6 @@ "album_remove_user": "Odebrat uÅživatele?", "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": "{count} poloÅžek", - "album_thumbnail_card_shared": " ¡ Sdíleno", - "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}", @@ -421,6 +404,9 @@ "album_with_link_access": "Nechte kohokoli s odkazem zobrazit fotografie a lidi v tomto albu.", "albums": "Alba", "albums_count": "{count, plural, one {{count, number} album} few {{count, number} alba} other {{count, number} alb}}", + "albums_default_sort_order": "VÃŊchozí řazení alb", + "albums_default_sort_order_description": "VÃŊchozí řazení poloÅžek při vytvÃĄÅ™ení novÃŊch alb.", + "albums_feature_description": "Sbírky poloÅžek, kterÊ lze sdílet s ostatními uÅživateli.", "all": "VÅĄe", "all_albums": "VÅĄechna alba", "all_people": "VÅĄichni lidÊ", @@ -479,9 +465,12 @@ "assets_added_count": "{count, plural, one {PřidÃĄna # poloÅžka} few {PřidÃĄny # poloÅžky} other {PřidÃĄno # poloÅžek}}", "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_cannot_be_added_to_album_count": "{count, plural, one {PoloÅžku} other {PoloÅžky}} nelze přidat do alba", "assets_count": "{count, plural, one {# poloÅžka} few {# poloÅžky} other {# poloÅžek}}", "assets_deleted_permanently": "{count} poloÅžek trvale odstraněno", "assets_deleted_permanently_from_server": "{count} poloÅžek trvale odstraněno z Immich serveru", + "assets_downloaded_failed": "{count, plural, one {StaÅžen # soubor - {error} souborů selhalo} few {StaÅženy # soubory - {error} souborů selhalo} other {StaÅženo # souborů - {error} souborů selhalo}}", + "assets_downloaded_successfully": "{count, plural, one {ÚspÄ›ÅĄně staÅžen # soubor} few {ÚspÄ›ÅĄně staÅženy # soubory} other {ÚspÄ›ÅĄně staÅženo # souborů}}", "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}}", @@ -496,6 +485,7 @@ "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", "automatic_endpoint_switching_title": "AutomatickÊ přepínÃĄní URL", + "autoplay_slideshow": "AutomatickÊ přehrÃĄvÃĄní prezentace", "back": "Zpět", "back_close_deselect": "Zpět, zavřít nebo zruÅĄit vÃŊběr", "background_location_permission": "Povolení polohy na pozadí", @@ -520,6 +510,7 @@ "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í", @@ -538,6 +529,7 @@ "backup_controller_page_excluded": "Vyloučeno: ", "backup_controller_page_failed": "Nepodařilo se ({count})", "backup_controller_page_filename": "NÃĄzev souboru: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informace o zÃĄlohovÃĄní", "backup_controller_page_none_selected": "ÅŊÃĄdnÊ vybranÊ", "backup_controller_page_remainder": "ZbÃŊvÃĄ", @@ -575,21 +567,17 @@ "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 ({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 ({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": "{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ů ({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", @@ -602,14 +590,15 @@ "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", + "cast": "Odeslat do zařízení", + "cast_description": "Nastavení dostupnÃŊch cílů přenosu", "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", "change_name": "Změnit jmÊno", - "change_name_successfully": "Změna jmÊna proběhla ÃēspÄ›ÅĄně", + "change_name_successfully": "JmÊno bylo ÃēspÄ›ÅĄně změněno", "change_password": "Změna hesla", "change_password_description": "Buď se do systÊmu přihlaÅĄujete poprvÊ, nebo jste byli poÅžÃĄdÃĄni o změnu hesla. Zadejte prosím novÊ heslo níŞe.", "change_password_form_confirm_password": "Potvrďte heslo", @@ -620,7 +609,6 @@ "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", "check_corrupt_asset_backup": "Kontrola poÅĄkozenÃŊch zÃĄloh poloÅžek", "check_corrupt_asset_backup_button": "ProvÊst kontrolu", "check_corrupt_asset_backup_description": "Tuto kontrolu provÃĄdějte pouze přes Wi-Fi a po zÃĄlohovÃĄní vÅĄech prostředků. Takto operace můŞe trvat několik minut.", @@ -632,6 +620,7 @@ "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", @@ -659,11 +648,13 @@ "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", + "confirm_tag_face": "Opravdu chcete označit tento obličej jako {name}?", + "confirm_tag_face_unnamed": "Opravdu chcete označit tento obličej?", + "connected_device": "PřipojenÊ zařízení", "connected_to": "Připojeno k", "contain": "Obsah", "context": "Kontext", "continue": "Pokračovat", - "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í", @@ -712,6 +703,7 @@ "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", "dark": "TmavÃŊ", + "darkTheme": "Přepnout tmavÃŊ motiv", "date_after": "Datum po", "date_and_time": "Datum a čas", "date_before": "Datum před", @@ -757,7 +749,9 @@ "direction": "Směr", "disabled": "ZakÃĄzÃĄno", "disallow_edits": "ZakÃĄzat Ãēpravy", + "discord": "Discord", "discover": "Objevit", + "discovered_devices": "NalezenÃĄ zařízení", "dismiss_all_errors": "ZruÅĄit vÅĄechny chyby", "dismiss_error": "ZruÅĄit chybu", "display_options": "MoÅžnosti zobrazení", @@ -773,7 +767,6 @@ "download_enqueue": "StahovÃĄní ve frontě", "download_error": "Chyba při stahovÃĄní", "download_failed": "StahovÃĄní selhalo", - "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", @@ -813,6 +806,7 @@ "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", @@ -835,6 +829,7 @@ "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}", + "error_tag_face_bounding_box": "Chyba při označovÃĄní obličeje - nelze získat souřadnice ohraničujícího rÃĄmečku", "error_title": "Chyba - Něco se pokazilo", "errors": { "cannot_navigate_next_asset": "Nelze přejít na dalÅĄÃ­ poloÅžku", @@ -847,7 +842,6 @@ "cant_get_number_of_comments": "Nelze načíst počet komentÃĄÅ™Å¯", "cant_search_people": "Nelze vyhledÃĄvat lidi", "cant_search_places": "Nelze vyhledÃĄvat místa", - "cleared_jobs": "VyřízenÊ Ãēlohy pro: {job}", "error_adding_assets_to_album": "Chyba při přidÃĄvÃĄní poloÅžek do alba", "error_adding_users_to_album": "Chyba při přidÃĄvÃĄní uÅživatelů do alba", "error_deleting_shared_user": "Chyba při odstraňovÃĄní sdílenÊho uÅživatele", @@ -856,7 +850,6 @@ "error_removing_assets_from_album": "Chyba při odstraňovÃĄní poloÅžek z alba, dalÅĄÃ­ podrobnosti najdete v konzoli", "error_selecting_all_assets": "Chyba při vÃŊběru vÅĄech poloÅžek", "exclusion_pattern_already_exists": "Tento vzor vyloučení jiÅž existuje.", - "failed_job_command": "Příkaz {command} se nezdařil pro Ãēlohu: {job}", "failed_to_create_album": "Nepodařilo se vytvořit album", "failed_to_create_shared_link": "Nepodařilo se vytvořit sdílenÃŊ odkaz", "failed_to_edit_shared_link": "Nepodařilo se upravit sdílenÃŊ odkaz", @@ -875,7 +868,6 @@ "paths_validation_failed": "{paths, plural, one {# cesta neproÅĄla} few {# cesty neproÅĄly} other {# cest neproÅĄlo}} kontrolou", "profile_picture_transparent_pixels": "ProfilovÊ obrÃĄzky nemohou mít průhlednÊ pixely. ObrÃĄzek si prosím zvětÅĄete nebo posuňte.", "quota_higher_than_disk_size": "Nastavili jste kvÃŗtu vyÅĄÅĄÃ­, neÅž je velikost disku", - "repair_unable_to_check_items": "Nelze zkontrolovat {count, select, one {poloÅžku} other {poloÅžky}}", "unable_to_add_album_users": "Nelze přidat uÅživatele do alba", "unable_to_add_assets_to_shared_link": "Nelze přidat poloÅžky do sdílenÊho odkazu", "unable_to_add_comment": "Nelze přidat komentÃĄÅ™", @@ -894,7 +886,6 @@ "unable_to_change_visibility": "Nelze změnit viditelnost u {count, plural, one {# osoby} few {# osob} other {# lidí}}", "unable_to_complete_oauth_login": "Nelze dokončit OAuth přihlÃĄÅĄení", "unable_to_connect": "Nelze se připojit", - "unable_to_connect_to_server": "Nepodařilo se připojit k serveru", "unable_to_copy_to_clipboard": "Nelze zkopírovat do schrÃĄnky, ujistěte se, Åže na strÃĄnku přistupujete přes https", "unable_to_create_admin_account": "Nelze vytvořit Ãēčet sprÃĄvce", "unable_to_create_api_key": "Nelze vytvořit novÃŊ API klíč", @@ -918,14 +909,9 @@ "unable_to_hide_person": "Nelze skrÃŊt osobu", "unable_to_link_motion_video": "Nelze připojit pohyblivÊ video", "unable_to_link_oauth_account": "Nelze propojit OAuth Ãēčet", - "unable_to_load_album": "Nelze načíst album", - "unable_to_load_asset_activity": "Nelze načíst aktivitu poloÅžky", - "unable_to_load_items": "Nelze načíst poloÅžky", - "unable_to_load_liked_status": "Nelze načíst stav oblíbenÃŊch", "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", @@ -933,11 +919,9 @@ "unable_to_remove_album_users": "Nelze odebrat uÅživatele z alba", "unable_to_remove_api_key": "Nelze odstranit API klíč", "unable_to_remove_assets_from_shared_link": "Nelze odstranit poloÅžky ze sdílenÊho odkazu", - "unable_to_remove_deleted_assets": "Nelze odstranit offline soubory", "unable_to_remove_library": "Nelze odstranit knihovnu", "unable_to_remove_partner": "Nelze odebrat partnera", "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", @@ -967,12 +951,12 @@ "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": "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", @@ -987,6 +971,7 @@ "expires_date": "Platnost končí {date}", "explore": "Prozkoumat", "explorer": "Průzkumník", + "export": "Export", "export_as_json": "Exportovat jako JSON", "extension": "Přípona", "external": "Externí", @@ -1019,6 +1004,8 @@ "folders": "SloÅžky", "folders_feature_description": "ProchÃĄzení zobrazení sloÅžek s fotografiemi a videi v souborovÊm systÊmu", "forward": "Dopředu", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Tato funkce načítÃĄ externí zdroje z Googlu, aby mohla fungovat.", "general": "ObecnÊ", "get_help": "Získat pomoc", "get_wifiname_error": "Nepodařilo se získat nÃĄzev Wi-Fi. Zkontrolujte, zda jste udělili potřebnÃĄ oprÃĄvnění a zda jste připojeni k Wi-Fi síti", @@ -1067,6 +1054,7 @@ "home_page_upload_err_limit": "Lze nahrÃĄt nejvÃŊÅĄe 30 poloÅžek najednou, přeskakuji", "host": "Hostitel", "hour": "Hodina", + "id": "ID", "ignore_icloud_photos": "Ignorovat fotografie na iCloudu", "ignore_icloud_photos_description": "Fotografie uloÅženÊ na iCloudu se nebudou nahrÃĄvat na Immich server", "image": "ObrÃĄzek", @@ -1084,6 +1072,7 @@ "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", @@ -1105,6 +1094,12 @@ "invalid_date_format": "ChybnÃŊ formÃĄt data", "invite_people": "Pozvat lidi", "invite_to_album": "Pozvat do alba", + "ios_debug_info_fetch_ran_at": "Data načtena {dateTime}", + "ios_debug_info_last_sync_at": "Poslední synchronizace {dateTime}", + "ios_debug_info_no_processes_queued": "ÅŊÃĄdnÊ procesy na pozadí ve frontě", + "ios_debug_info_no_sync_yet": "Dosud nebyla spuÅĄtěna ÅžÃĄdnÃĄ Ãēloha synchronizace na pozadí", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proces na pozadí ve frontě} few {{count} procesy na pozadí ve frontě} other {{count} procesů na pozadí ve frontě}}", + "ios_debug_info_processing_ran_at": "ZpracovÃĄní spuÅĄtěno {dateTime}", "items_count": "{count, plural, one {# poloÅžka} few {# poloÅžky} other {# poloÅžek}}", "jobs": "Úlohy", "keep": "Ponechat", @@ -1113,6 +1108,9 @@ "kept_this_deleted_others": "PonechÃĄna tato poloÅžka a {count, plural, one {odstraněna # poloÅžka} few {odstraněny # poloÅžky} other {odstraněno # poloÅžek}}", "keyboard_shortcuts": "KlÃĄvesovÊ zkratky", "language": "Jazyk", + "language_no_results_subtitle": "Zkuste upravit hledanÃŊ vÃŊraz", + "language_no_results_title": "Nebyly nalezeny ÅžÃĄdnÊ jazyky", + "language_search_hint": "Vyhledat jazyk...", "language_setting_description": "Vyberte upřednostňovanÃŊ jazyk", "last_seen": "Naposledy viděno", "latest_version": "NejnovějÅĄÃ­ verze", @@ -1138,6 +1136,7 @@ "list": "Seznam", "loading": "NačítÃĄní", "loading_search_results_failed": "NačítÃĄní vÃŊsledků vyhledÃĄvÃĄní se nezdařilo", + "local_asset_cast_failed": "Nelze odeslat poloÅžku, kterÃĄ není nahranÃĄ na serveru", "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", @@ -1151,6 +1150,7 @@ "locked_folder": "UzamčenÃĄ sloÅžka", "log_out": "OdhlÃĄsit", "log_out_all_devices": "OdhlÃĄsit vÅĄechna zařízení", + "logged_in_as": "PřihlÃĄÅĄen jako {user}", "logged_out_all_devices": "VÅĄechna zařízení odhlÃĄÅĄena", "logged_out_device": "Zařízení odhlÃĄÅĄeno", "login": "PřihlÃĄÅĄení", @@ -1240,6 +1240,7 @@ "minimize": "Minimalizovat", "minute": "Minuta", "missing": "Chybějící", + "model": "Model", "month": "Měsíc", "monthly_title_text_date_format": "LLLL y", "more": "Více", @@ -1277,12 +1278,13 @@ "no_archived_assets_message": "Archivujte fotografie a videa a skryjte je ze zobrazení v sekci Fotky", "no_assets_message": "KLIKNĚTE PRO NAHRÁNÍ PRVNÍ FOTOGRAFIE", "no_assets_to_show": "ÅŊÃĄdnÊ poloÅžky k zobrazení", + "no_cast_devices_found": "Nebyla nalezena ÅžÃĄdnÃĄ zařízení", "no_duplicates_found": "Nebyly nalezeny ÅžÃĄdnÊ duplicity.", "no_exif_info_available": "Exif není k dispozici", "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_locked_photos_message": "Fotky a videa v uzamčenÊ sloÅžce jsou skrytÊ a při prochÃĄzení nebo vyhledÃĄvÃĄní v knihovně se nezobrazují.", "no_name": "Bez jmÊna", "no_notifications": "ÅŊÃĄdnÃĄ oznÃĄmení", "no_people_found": "Nebyli nalezeni ÅžÃĄdní odpovídající lidÊ", @@ -1302,16 +1304,20 @@ "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_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.", + "offline": "Offline", + "ok": "Ok", "oldest_first": "NejstarÅĄÃ­ první", "on_this_device": "V tomto zařízení", "onboarding": "ZahÃĄjení", - "onboarding_privacy_description": "NÃĄsledující (volitelnÊ) funkce jsou zÃĄvislÊ na externích sluÅžbÃĄch a lze je kdykoli zakÃĄzat v nastavení sprÃĄvy.", + "onboarding_locale_description": "Vyberte preferovanÃŊ jazyk. Tento vÃŊběr můŞete později změnit v nastavení.", + "onboarding_privacy_description": "NÃĄsledující (volitelnÊ) funkce jsou zÃĄvislÊ na externích sluÅžbÃĄch a lze je kdykoli zakÃĄzat v nastavení.", + "onboarding_server_welcome_description": "Pojďme nastavit vaÅĄi instanci pomocí několika běŞnÃŊch nastavení.", "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_user_welcome_description": "Pojďme na to!", "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", @@ -1326,6 +1332,7 @@ "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", @@ -1365,6 +1372,8 @@ "permanently_delete_assets_prompt": "Opravdu chcete trvale smazat {count, plural, one {tuto poloÅžku} few {tyto # poloÅžky} other {těchto # poloÅžek}}? Tím {count, plural, one {ji takÊ odstraníte z jejích} other {je takÊ odstraníte z jejich}} alb.", "permanently_deleted_asset": "PoloÅžka trvale odstraněna", "permanently_deleted_assets_count": "{count, plural, one {PoloÅžka trvale vymazÃĄna} other {PoloÅžky trvale vymazÃĄny}}", + "permission": "OprÃĄvnění", + "permission_empty": "VaÅĄe oprÃĄvnění by nemělo bÃŊt prÃĄzdnÊ", "permission_onboarding_back": "Zpět", "permission_onboarding_continue_anyway": "Přesto pokračovat", "permission_onboarding_get_started": "Začít", @@ -1394,13 +1403,17 @@ "play_motion_photo": "PřehrÃĄt pohybovou fotografii", "play_or_pause_video": "PřehrÃĄt nebo pozastavit video", "please_auth_to_access": "Pro přístup se prosím ověřte", + "port": "Port", "preferences_settings_subtitle": "SprÃĄva předvoleb aplikace", "preferences_settings_title": "Předvolby", "preset": "Přednastavení", "preview": "NÃĄhled", "previous": "Předchozí", "previous_memory": "Předchozí vzpomínka", - "previous_or_next_photo": "Předchozí nebo dalÅĄÃ­ fotka", + "previous_or_next_day": "NÃĄsledující/předchozí den", + "previous_or_next_month": "NÃĄsledující/předchozí měsíc", + "previous_or_next_photo": "NÃĄsledující/předchozí fotografie", + "previous_or_next_year": "NÃĄsledující/předchozí rok", "primary": "PrimÃĄrní", "privacy": "Soukromí", "profile": "Profil", @@ -1408,6 +1421,7 @@ "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}", @@ -1444,6 +1458,7 @@ "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í", @@ -1486,6 +1501,7 @@ "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", + "remove_tag": "Odstranit značku", "remove_url": "Odstranit URL", "remove_user": "Odebrat uÅživatele", "removed_api_key": "Odstraněn API klíč: {name}", @@ -1517,6 +1533,8 @@ "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", @@ -1590,6 +1608,7 @@ "select_album_cover": "Vybrat obal alba", "select_all": "Vybrat vÅĄe", "select_all_duplicates": "Vybrat vÅĄechny duplicity", + "select_all_in": "Vybrat vÅĄe ve skupině {group}", "select_avatar_color": "Vyberte barvu avatara", "select_face": "Vybrat obličej", "select_featured_photo": "Vybrat hlavní fotografii", @@ -1610,6 +1629,7 @@ "server_info_box_server_url": "URL serveru", "server_offline": "Server offline", "server_online": "Server online", + "server_privacy": "Ochrana soukromí serveru", "server_stats": "Statistiky serveru", "server_version": "Verze serveru", "set": "Nastavit", @@ -1619,6 +1639,7 @@ "set_date_of_birth": "Nastavit datum narození", "set_profile_picture": "Nastavit profilovÃŊ obrÃĄzek", "set_slideshow_to_fullscreen": "Nastavit prezentaci na celou obrazovku", + "set_stack_primary_asset": "Nastavit jako hlavní poloÅžku", "setting_image_viewer_help": "V prohlíŞeči detailů se nejprve načte malÃĄ miniatura, potÊ se načte nÃĄhled střední velikosti (je-li povolen) a nakonec se načte originÃĄl (je-li povolen).", "setting_image_viewer_original_subtitle": "UmoŞňuje načíst původní obrÃĄzek v plnÊm rozliÅĄení (velkÃŊ!). ZakaÅžte pro sníŞení vyuÅžití dat (v síti i v mezipaměti zařízení).", "setting_image_viewer_original_title": "Načíst původní obrÃĄzek", @@ -1627,7 +1648,6 @@ "setting_image_viewer_title": "ObrÃĄzky", "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í: {duration}", "setting_notifications_notify_hours": "{count} hodin", "setting_notifications_notify_immediately": "okamÅžitě", @@ -1690,6 +1710,7 @@ "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", @@ -1752,9 +1773,11 @@ "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", + "stop_casting": "Zastavit odesílÃĄní", "stop_motion_photo": "Zastavit pohyblivou fotografii", "stop_photo_sharing": "Přestat sdílet svÊ fotografie?", "stop_photo_sharing_description": "{partner} jiÅž nebude mít přístup k vaÅĄim fotkÃĄm.", @@ -1812,7 +1835,6 @@ "to_parent": "Přejít k rodiči", "to_trash": "Vyhodit", "toggle_settings": "Přepnout nastavení", - "toggle_theme": "Přepnout tmavÃŊ motiv", "total": "Celkem", "total_usage": "CelkovÊ vyuÅžití", "trash": "KoÅĄ", @@ -1834,6 +1856,7 @@ "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 #}}", + "undo": "VrÃĄtit zpět", "unfavorite": "ZruÅĄit oblíbení", "unhide_person": "ZruÅĄit skrytí osoby", "unknown": "NeznÃĄmÃŊ", @@ -1850,10 +1873,9 @@ "unsaved_change": "NeuloÅženÃĄ změna", "unselect_all": "ZruÅĄit vÃŊběr vÅĄech", "unselect_all_duplicates": "ZruÅĄit vÃŊběr vÅĄech duplicit", + "unselect_all_in": "ZruÅĄit vÃŊběr ve skupině {group}", "unstack": "ZruÅĄit seskupení", "unstacked_assets_count": "{count, plural, one {RozloÅženÃĄ # poloÅžka} few {RozloÅženÊ # poloÅžky} other {RozloÅženÃŊch # poloÅžiek}}", - "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", @@ -1870,6 +1892,7 @@ "upload_success": "NahrÃĄní proběhlo ÃēspÄ›ÅĄně, obnovením strÃĄnky se zobrazí nově nahranÊ poloÅžky.", "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í", @@ -1880,6 +1903,7 @@ "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_privacy": "Ochrana soukromí uÅživatelů", "user_purchase_settings": "NÃĄkup", "user_purchase_settings_description": "SprÃĄva vaÅĄeho nÃĄkupu", "user_role_set": "UÅživatel {user} nastaven jako {role}", @@ -1895,13 +1919,9 @@ "version": "Verze", "version_announcement_closing": "VÃĄÅĄ přítel Alex", "version_announcement_message": "Ahoj! K dispozici je novÃĄ verze aplikace Immich. Věnujte prosím chvíli přečtení poznÃĄmek k vydÃĄní a ujistěte se, Åže je vaÅĄe nastavení aktuÃĄlní, abyste předeÅĄli případnÃŊm chybnÃŊm konfiguracím, zejmÊna pokud pouŞívÃĄte WatchTower nebo jinÃŊ mechanismus, kterÃŊ se starÃĄ o automatickou aktualizaci instance aplikace Immich.", - "version_announcement_overlay_release_notes": "poznÃĄmky k vydÃĄní", - "version_announcement_overlay_text_1": "Ahoj, k dispozici je novÃĄ verze", - "version_announcement_overlay_text_2": "najděte si čas na nÃĄvÅĄtěvu ", - "version_announcement_overlay_text_3": " a ujistěte se, Åže vaÅĄe konfigurace docker-compose a .env je aktuÃĄlní, abyste předeÅĄli nesprÃĄvnÊ konfiguraci, zvlÃĄÅĄtě pokud pouŞívÃĄte WatchTower nebo jakÃŊkoli mechanismus, kterÃŊ podporuje automatickÊ aktualizace serverovÃŊch aplikací.", - "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", @@ -1918,6 +1938,7 @@ "view_previous_asset": "Zobrazit předchozí poloÅžku", "view_qr_code": "Zobrazit QR kÃŗd", "view_stack": "Zobrazit seskupení", + "view_user": "Zobrazit uÅživatele", "viewer_remove_from_stack": "Odstranit ze zÃĄsobníku", "viewer_stack_use_as_main_asset": "PouŞít jako hlavní poloÅžku", "viewer_unstack": "Rozbalit zÃĄsobník", diff --git a/i18n/cv.json b/i18n/cv.json index 9010911c25..89f71c9632 100644 --- a/i18n/cv.json +++ b/i18n/cv.json @@ -31,7 +31,6 @@ "asset_offline_description": "БибĐģĐ¸ĐžŅ‚ĐĩĐēĶ‘ĐŊ ŌĢаĐē Ņ‚ŅƒĐģĐ°Ņˆ Ņ„Đ°ĐšĐģĐŊĐĩ Đ´Đ¸ŅĐēŅ€Đ° ŅƒŅ€Ķ‘Ņ… Ņ‚ŅƒĐŋаКĐŧаĐŊ, ĐēĐ°Ņ€ŌĢиĐŊĐēĐēаĐŊа Đē҃ŌĢĐ°Ņ€ĐŊĶ‘. ЕĐŊ҇ĐĩĐŊ Ņ‚Đĩ Ņ„Đ°ĐšĐģа Đ˛ŅƒĐģĐ°Đ˛Ķ‘Ņˆ Ķ‘ŅˆĐŊĐĩ Đē҃ŌĢĐ°Ņ€ĐŊĶ‘ Đŋ҃ĐģŅĐ°ĐŊ, Ņ‚Đ¸Đ˛Ķ—ŌĢĐģĶ— ŌĢĶ—ĐŊĶ— Ņ€ĐĩŅŅƒŅ€Ņ Ņ‚ŅƒĐŋĐ°Ņ Ņ‚ĐĩҁĐĩĐŊ Ņ…Ķ‘Đ˛Ķ‘Ņ€Ķ‘ĐŊ Đ˛Ķ‘Ņ…Ķ‘Ņ‚Đģ͑҅ ҈ĐēаĐģĶ‘ĐŊа ҂͗Ҁ͗ҁĐģĶ—Ņ€. ŌĒаĐē Ņ„Đ°ĐšĐģа ŌĢĶ—ĐŊĶ—Ņ€ĐĩĐŊ ҇͗Ҁ҂Đĩҁ Ņ‚ĐĩҁĐĩĐŊ Ņ„Đ°ĐšĐģ ĐŋĐ°Ņ‚ĐŊĐĩ ĐēаКĐŧаĐģĐģи ŌĢ҃Đģа Immich ваĐģĐģи Đ°ŅĐģĐ°Ņ€Đ°Ņ… ŌĢĐ¸Ņ‚ĐĩŅ€ĐŊиĐŊĐĩ ĐēŅƒŅ€ŅĐ° Ķ—ĐŊĐĩĐŊĶ—Ņ€, йийĐģĐ¸ĐžŅ‚ĐĩĐēĶ‘ĐŊа ҁĐēаĐŊĐĩŅ€ĐģаĐŊиĐŊĐĩ ĐŋŅƒŅ€ĐŊĶ‘ŌĢĐģ͑Ҁ.", "authentication_settings_disable_all": "Đ­ŅĐ¸Ņ€ ĐēĶ—ĐŧĐĩĐģĐģи ĐŋŅƒŅ€ ĐŧĐĩҁĐģĐĩ҂ҁĐĩĐŊĐĩ Ņ‚Đĩ Ņ‡Đ°Ņ€ŅĐ° ĐģĐ°Ņ€Ņ‚Đ°ŅŅˆĶ‘ĐŊ Ņ‚ĐĩҁĐĩ ŅˆŅƒŅ‚ĐģĐ°Ņ‚Ķ‘Ņ€-и? ĐšĶ—ĐŧĐĩĐģĐģи ŅˆĶ‘Ņ‚Ķ‘Đēа Đŋ͗҂͗ĐŧĐŋĐĩŅ… ҃ŌĢаŌĢŌĢĶ—.", "background_task_job": "ĐšŅƒŅ€ÄƒĐŊĐŧаĐŊ Ķ—ŌĢҁĐĩĐŧ", - "check_all": "ĐŸŅƒŅ€ĐŊĐĩ Ņ‚Đĩ ҂͗Ҁ͗ҁĐģĶ—Ņ€", "cleared_jobs": "Ķ–ŌĢҁĐĩĐŊĐĩ Ņ‚Đ°ŅĐ°Ņ‚ĐŊĶ‘:{job}", "confirm_email_below": "ŌĒĐ¸Ņ€Ķ—ĐŋĐģĐĩŅ‚Đĩҁ Ņ‚ĐĩҁĐĩĐŊ, Đ°ŅĐģĐ°Ņ€Đ°Ņ… ÂĢ{email}Âģ Đē͗Ҁ҂͗Ҁ", "confirm_reprocess_all_faces": "ĐŸĶ—Ņ‚Ķ—Đŧ ҁ͑ĐŊ͗ҁĐĩĐŊĐĩ Ņ‚ĐĩĐŋĶ—Ņ€ Ņ…ŅƒŅ‚ ĐŋаĐģĶ‘Ņ€Ņ‚Đ°Ņ ĐēиĐģĐĩŅ‚ Ņ‚ĐĩҁĐĩ ŅˆĐ°ĐŊĐ°Ņ‚Ķ‘Ņ€-и? ŌĒĐ°Đ˛Ķ‘ĐŊ ĐŋĐĩĐēĐĩŅ… ŅŅ‚ŅĐĩĐŊĐĩ ĐŋŅƒŅ€ ŌĢŅ‹ĐŊŅ€Đ°ĐŊ Ņ‚Đ° Ņ…ŅƒŅ€Đ°Ņ‚Ķ—ŌĢ.", diff --git a/i18n/da.json b/i18n/da.json index eeec0ce036..3273a2d553 100644 --- a/i18n/da.json +++ b/i18n/da.json @@ -22,11 +22,11 @@ "add_partner": "Tilføj partner", "add_path": "Tilføj sti", "add_photos": "Tilføj billeder", + "add_tag": "Tilføj tag", "add_to": "Tilføj tilâ€Ļ", "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", @@ -44,9 +44,7 @@ "backup_database_enable_description": "SlÃĨ database-backup til", "backup_keep_last_amount": "MÃĻngde af tidligere backups, der skal gemmes", "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", + "backup_settings_description": "Administrer backupindstillinger for database.", "cleared_jobs": "Ryddet jobs til: {job}", "config_set_by_file": "konfigurationen er i øjeblikket indstillet af en konfigurations fil", "confirm_delete_library": "Er du sikker pÃĨ, at du vil slette {library} bibliotek?", @@ -62,14 +60,12 @@ "disable_login": "Deaktiver login", "duplicate_detection_job_description": "Kør maskinlÃĻring pÃĨ mediefiler for at opdage lignende billeder. Er afhÃĻngig af Smart Søgning", "exclusion_pattern_description": "Ekskluderingsmønstre lader dig ignorere filer og mapper, nÃĨr du scanner dit bibliotek. Dette er nyttigt, hvis du har mapper, der indeholder filer, du ikke vil importere, sÃĨsom RAW-filer.", - "external_library_created_at": "Eksternt bibliotek (oprettet {date})", "external_library_management": "Ekstern biblioteksstyring", "face_detection": "Ansigtsopdagelse", "face_detection_description": "Genkend ansigterne i mediefiler via maskinlÃĻring. For videoer er det kun miniaturebilledet som tages hensyn til. \"Alle\" (gen-)behandler alle mediefiler. \"Mangler\" sÃĻtter mediefiler i kø, som ikke er blevet behandlet endnu. Opdagede ansigter vil blive sat i kø til Ansigtsgenkendelse efter Ansigtsopdagelse er fÃĻrdig, hvilket grupperer dem til eksisterende eller nye personer.", "facial_recognition_job_description": "Grupper opdagede ansigter i personer. Dette trin kører efter Ansigtsopdagelse er fÃĻrdig. \"Alle\" (gen-)klumper alle ansigter sammen. \"Mangler\" sÃĻtter ansigter i kø, som ikke har en person tildelt.", "failed_job_command": "Kommando {command} mislykkedes for job: {job}", "force_delete_user_warning": "ADVARSEL: Dette vil øjeblikkeligt fjerne brugeren og alle Billeder/Videoer. Dette kan ikke fortrydes, og filerne kan ikke gendannes.", - "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", @@ -174,7 +170,7 @@ "note_apply_storage_label_previous_assets": "BemÃĻrk: For at anvende LagringsmÃĻrkatet pÃĨ tidligere uploadede mediefiler, kør", "note_cannot_be_changed_later": "BEMÆRK: Dette kan ikke ÃĻndres senere!", "notification_email_from_address": "Fra adressse", - "notification_email_from_address_description": "Afsenderemailadresse, for eksempel: \"Immich Billedserver \"", + "notification_email_from_address_description": "Afsenderemailadresse, for eksempel: \"Immich Billedserver \". VÃĻr sikker pÃĨ du bruger en email du kan sende emails fra.", "notification_email_host_description": "Host af emailserver (fx smtp.immich.app)", "notification_email_ignore_certificate_errors": "IgnorÊr certifikatfejl", "notification_email_ignore_certificate_errors_description": "IgnorÊr TLS-certifikatgodkendelsesfejl (ikke anbefalet)", @@ -198,7 +194,7 @@ "oauth_enable_description": "Log ind med OAuth", "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_mobile_redirect_uri_override_description": "Aktiver, nÃĨr OAuth-udbyderen ikke tillader en mobil URI, som ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "Administrer OAuth login-indstillinger", "oauth_settings_more_details": "LÃĻs flere detaljer om funktionen i dokumentationen.", @@ -210,8 +206,6 @@ "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", "password_settings": "Adgangskodelogin", "password_settings_description": "Administrer indstillinger for adgangskodelogin", @@ -221,9 +215,6 @@ "refreshing_all_libraries": "Opdaterer alle biblioteker", "registration": "Administratorregistrering", "registration_description": "Da du er den første bruger i systemet, fÃĨr du tildelt rollen som administrator og ansvar for administration og oprettelsen af nye brugere.", - "repair_all": "ReparÊr alle", - "repair_matched_items": "Har parret {count, plural, one {# element} other {# elementer}}", - "repaired_items": "Reparerede {count, plural, one {# element} other {# elementer}}", "require_password_change_on_login": "KrÃĻv at brugeren skifter adgangskode ved første login", "reset_settings_to_default": "Nulstil indstillingerne til standard", "reset_settings_to_recent_saved": "Nulstil indstillinger til de senest gemte indstillinger", @@ -252,7 +243,6 @@ "storage_template_migration_info": "Lager-skabelonen vil konvertere alle filendelser til smÃĨ bogstaver. SkabelonÃĻndringer vil kun gÃĻlde for nye mediefiler. For at anvende skabelonen retroaktivt pÃĨ tidligere uploadede mediefiler skal du køre {job}.", "storage_template_migration_job": "Lager Skabelon Migreringsjob", "storage_template_more_details": "For flere detaljer om denne funktion, referer til Lager Skabelonen og dens implikationer", - "storage_template_onboarding_description": "NÃĨr denne funktion er aktiveret, vil den automatisk organisere filer baseret pÃĨ en brugerdefineret skabelon. PÃĨ grund af stabilitetsproblemer er funktionen som standard slÃĨet fra. For mere information, se dokumentation.", "storage_template_path_length": "AnslÃĨet sti-lÃĻngde begrÃĻnsning {length, number}/{limit, number}", "storage_template_settings": "Lagringsskabelon", "storage_template_settings_description": "Administrer mappestrukturen og filnavnet for den uploadede mediefil", @@ -264,16 +254,14 @@ "template_email_invite_album": "Inviterings albumskabelon", "template_email_preview": "ForhÃĨndsvisning", "template_email_settings": "Email skabeloner", - "template_email_settings_description": "Administrer tilpassede e-mailmeddelelsesskabeloner", "template_email_update_album": "Opdater albumskabelon", "template_email_welcome": "Velkomst e-mail skabelon", "template_settings": "Notifikations skabeloner", - "template_settings_description": "Administrer tilpassede skabeloner for notifikationer.", + "template_settings_description": "Administrer tilpassede skabeloner for notifikationer", "theme_custom_css_settings": "Brugerdefineret CSS", "theme_custom_css_settings_description": "Cascading Style Sheets tillader at give Immich et brugerdefineret look.", "theme_settings": "Temaindstillinger", "theme_settings_description": "AdministrÊr brugertilpasningen af Immich's webinterface", - "these_files_matched_by_checksum": "Disse filer er blevet matchet med deres checksummer", "thumbnail_generation_job": "GenerÊr miniaturebilleder", "thumbnail_generation_job_description": "GenerÊr store, smÃĨ og slørede miniaturebilleder for hver mediefil, sÃĨvel som miniaturebilleder for hver person", "transcoding_acceleration_api": "Accelerations-API", @@ -301,10 +289,9 @@ "transcoding_encoding_options": "Kodningsmuligheder", "transcoding_encoding_options_description": "Indstil codecs, opløsning, kvalitet og andre muligheder for de kodede videoer", "transcoding_hardware_acceleration": "Hardwareacceleration", - "transcoding_hardware_acceleration_description": "Eksperimentel; meget hurtigere, men vil have lavere kvalitet ved samme bitrate", + "transcoding_hardware_acceleration_description": "Eksperimentel: hurtigere transkodning men kan sÃĻnke kvaliteten ved samme bitrate", "transcoding_hardware_decoding": "Hardware-afkodning", "transcoding_hardware_decoding_setting_description": "GÃĻlder kun NVENC, QSV og RKMPP. SlÃĨr ende-til-ende acceleration til i stedet for kun at accelerere indkodning. Virker mÃĨske ikke pÃĨ alle videoer.", - "transcoding_hevc_codec": "HEVC-codec", "transcoding_max_b_frames": "Maksimum B-frames", "transcoding_max_b_frames_description": "Højere vÃĻrdier forbedrer kompressionseffektivitet, men kan gøre indkodning langsommere. Er mÃĨske ikke kompatibelt med hardware-acceleration pÃĨ ÃĻldre enheder. 0 slÃĨr B-frames fra, mens -1 sÃĻtter denne vÃĻrdi automatisk.", "transcoding_max_bitrate": "Maksimal bitrate", @@ -342,8 +329,6 @@ "trash_number_of_days_description": "Antal dage aktiver i skraldespanden skal beholdes inden de fjernes permanent", "trash_settings": "Skraldeindstillinger", "trash_settings_description": "AdministrÊr skraldeindstillinger", - "untracked_files": "Usporede filer", - "untracked_files_description": "Applikationen holder ikke styr pÃĨ disse filer. De kan vÃĻre resultatet af mislykkede flytninger, afbrudte uploads eller vÃĻre efterladt pÃĨ grund af en fejl", "user_cleanup_job": "Bruger-oprydning", "user_delete_delay": "{user}'s konto og mediefiler vil blive planlagt til permanent sletning om {delay, plural, one {# dag} other {# dage}}.", "user_delete_delay_settings": "Slet forsinkelse", @@ -368,6 +353,7 @@ }, "admin_email": "Administrator-email", "admin_password": "Administratoradgangskode", + "administration": "Administration", "advanced": "Avanceret", "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", @@ -375,6 +361,7 @@ "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": "Definer proxy headers Immich skal sende med hver netvÃĻrks forespørgsel", + "advanced_settings_proxy_headers_title": "Proxy Headers", "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", @@ -400,10 +387,6 @@ "album_remove_user": "Fjern bruger?", "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": "{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}", @@ -419,6 +402,9 @@ "album_with_link_access": "Lad alle med linket se billeder og personer i dette album.", "albums": "Albummer", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albummer}}", + "albums_default_sort_order": "Standard album sortering", + "albums_default_sort_order_description": "GrundlÃĻggende sortering ved oprettelse af nyt album.", + "albums_feature_description": "Samling af billeder der kan deles med andre brugere.", "all": "Alt", "all_albums": "Alle albummer", "all_people": "Alle personer", @@ -455,11 +441,13 @@ "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_month_day": "MÃĨned + dag", + "asset_list_layout_sub_title": "Udseende", "asset_list_settings_subtitle": "Indstillinger for billedgitterlayout", "asset_list_settings_title": "Billedgitter", "asset_offline": "Mediefil offline", @@ -475,9 +463,12 @@ "assets_added_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}}", "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_cannot_be_added_to_album_count": "{count, plural, one {Billed} other {Billeder}} kan ikke blive tilføjet til album", "assets_count": "{count, plural, one {# mediefil} other {# mediefiler}}", "assets_deleted_permanently": "{count} element(er) blev fjernet permanent", "assets_deleted_permanently_from_server": "{count} element(er) blev fjernet permanent fra Immich serveren", + "assets_downloaded_failed": "{count, plural, one {Downloaded # fil - {error} fil fejlede} other {Downloaded # filer - {error} filer fejlede}}", + "assets_downloaded_successfully": "{count, plural, one {Downloaded # fil med succes} other {Downloaded # filer med succes}}", "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}}", @@ -492,6 +483,7 @@ "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", "automatic_endpoint_switching_title": "Automatisk skift af URL", + "autoplay_slideshow": "Afspil slideshow automatisk", "back": "Tilbage", "back_close_deselect": "Tilbage, luk eller fravÃĻlg", "background_location_permission": "Tilladelse til baggrundsplacering", @@ -516,6 +508,7 @@ "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", @@ -572,21 +565,17 @@ "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 ({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 ({count})", - "cache_settings_image_cache_size": "Størrelse af billedecache ({count} elementer)", "cache_settings_statistics_album": "Biblioteksminiaturer", - "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 ({count} elementer)", "cache_settings_tile_subtitle": "Kontroller den lokale lagerplads", "cache_settings_tile_title": "Lokal lagerplads", "cache_settings_title": "Cache-indstillinger", @@ -599,13 +588,15 @@ "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", + "cast": "Cast", + "cast_description": "Konfigurer tilgÃĻngelige cast destinationer", "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", "change_name": "Ændr navn", - "change_name_successfully": "Navn er ÃĻndret", + "change_name_successfully": "NavneÃĻndring lykkedes", "change_password": "Skift kodeord", "change_password_description": "Dette er enten første gang du tilmelder dig, eller en ÃĻndring af kodeordet blev bestilt. Indtast dit nye kodeord herunder.", "change_password_form_confirm_password": "BekrÃĻft kodeord", @@ -616,7 +607,6 @@ "change_pin_code": "Skift PIN kode", "change_your_password": "Skift dit kodeord", "changed_visibility_successfully": "Synlighed blev ÃĻndret", - "check_all": "MarkÊr alle", "check_corrupt_asset_backup": "Tjek for korrupte sikkerhedskopier af elementer", "check_corrupt_asset_backup_button": "Foretag kontrol", "check_corrupt_asset_backup_description": "Kør kun denne kontrol via Wi-Fi, og nÃĨr alle elementer er blevet sikkerhedskopieret. Proceduren kan tage et par minutter.", @@ -628,6 +618,9 @@ "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": "Indtast Kodeord", + "client_cert_import": "Importer", "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", @@ -653,10 +646,13 @@ "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", + "confirm_tag_face": "Vil du markere dette ansigt som {name}?", + "confirm_tag_face_unnamed": "Vil du markere dette ansigt?", + "connected_device": "Forbundne enheder", + "connected_to": "Forbundet til", "contain": "InddÃĻm", "context": "Kontekst", "continue": "FortsÃĻt", - "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", @@ -705,6 +701,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Mørk", + "darkTheme": "Skift til mørkt tema", "date_after": "Dato efter", "date_and_time": "Dato og klokkeslÃĻt", "date_before": "Dato før", @@ -750,7 +747,9 @@ "direction": "Retning", "disabled": "Deaktiveret", "disallow_edits": "DeaktivÊr redigeringer", + "discord": "Discord", "discover": "Opdag", + "discovered_devices": "Opdaget enheder", "dismiss_all_errors": "Afvis alle fejl", "dismiss_error": "Afvis fejl", "display_options": "Display-indstillinger", @@ -766,12 +765,12 @@ "download_enqueue": "Donload sat i kø", "download_error": "Fejl med download", "download_failed": "Download mislykkes", - "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", @@ -809,6 +808,7 @@ "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": "Rotere", "email": "E-mail", "email_notifications": "Email notifikationer", "empty_folder": "Denne mappe er tom", @@ -827,6 +827,7 @@ "error_delete_face": "Fejl ved sletning af ansigt fra mediefil", "error_loading_image": "Fejl ved indlÃĻsning af billede", "error_saving_image": "Fejl: {error}", + "error_tag_face_bounding_box": "Fejl ved tagging af ansigt - kan ikke finde koordinator for afgrÃĻnsningskasse", "error_title": "Fejl - Noget gik galt", "errors": { "cannot_navigate_next_asset": "Kan ikke navigere til nÃĻste mediefil", @@ -839,7 +840,6 @@ "cant_get_number_of_comments": "Kan ikke fÃĨ antallet af kommentarer", "cant_search_people": "Kan ikke søge efter personer", "cant_search_places": "Kan ikke søge efter steder", - "cleared_jobs": "Ryddede opgaver for: {job}", "error_adding_assets_to_album": "Fejl i tilføjelse af mediefiler til album", "error_adding_users_to_album": "Fejl i tilføjelse af brugere til album", "error_deleting_shared_user": "Fejl i sletning af delt bruger", @@ -848,7 +848,6 @@ "error_removing_assets_from_album": "Fejl i fjernelse af mediefiler fra album. Tjek konsol for flere detaljer", "error_selecting_all_assets": "Fejl ved valg af alle mediefiler", "exclusion_pattern_already_exists": "Denne udelukkelsesmønster findes allerede.", - "failed_job_command": "Kommando {command} slog fejl for opgave: {job}", "failed_to_create_album": "Oprettelse af album mislykkedes", "failed_to_create_shared_link": "Oprettelse af delt link mislykkedes", "failed_to_edit_shared_link": "Redigering af delt link mislykkedes", @@ -867,7 +866,6 @@ "paths_validation_failed": "{paths, plural, one {# sti} other {# stier}} slog fejl ved validering", "profile_picture_transparent_pixels": "Profilbilleder kan ikke have gennemsigtige pixels. Zoom venligst ind og/eller flyt billedet.", "quota_higher_than_disk_size": "Du har sat en kvote der er større end disken", - "repair_unable_to_check_items": "Kunne ikke tjekke {count, select, one {element} other {elementer}}", "unable_to_add_album_users": "Ikke i stand til at tilføje brugere til album", "unable_to_add_assets_to_shared_link": "Kan ikke tilføje mediefiler til det delte link", "unable_to_add_comment": "Ikke i stand til at tilføje kommentar", @@ -886,7 +884,6 @@ "unable_to_change_visibility": "Kan ikke ÃĻndre synligheden for {count, plural, one {# person} other {# personer}}", "unable_to_complete_oauth_login": "Kan ikke fuldføre OAuth-login", "unable_to_connect": "Kan ikke oprette forbindelse", - "unable_to_connect_to_server": "Kan ikke oprette forbindelse til serveren", "unable_to_copy_to_clipboard": "Kan ikke kopiere til udklipsholder, sørg for at du tilgÃĨr siden gennem https", "unable_to_create_admin_account": "Kan ikke oprette en administratorkonto", "unable_to_create_api_key": "Kunne ikke oprette ny API-nøgle", @@ -910,14 +907,9 @@ "unable_to_hide_person": "Ikke i stand til at gemme person", "unable_to_link_motion_video": "Kan ikke linke bevÃĻgelsesvideo", "unable_to_link_oauth_account": "Kunne ikke tilkoble OAuth-konto", - "unable_to_load_album": "Ikke i stand til hente album", - "unable_to_load_asset_activity": "Kunne ikke hente aktivitet for mediet", - "unable_to_load_items": "Ikke i stand til at hente ting", - "unable_to_load_liked_status": "Ikke i stand til hente synes-om-status", "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", @@ -925,11 +917,9 @@ "unable_to_remove_album_users": "Ikke i stand til at fjerne brugere fra album", "unable_to_remove_api_key": "Kunne ikke fjerne API-nøgle", "unable_to_remove_assets_from_shared_link": "Kan ikke fjerne aktiver fra delt link", - "unable_to_remove_deleted_assets": "Kunne ikke fjerne offlinefiler", "unable_to_remove_library": "Ikke i stand til at fjerne bibliotek", "unable_to_remove_partner": "Ikke i stand til at fjerne partner", "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", @@ -959,12 +949,12 @@ "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": "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}", @@ -1002,6 +992,7 @@ "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", @@ -1011,6 +1002,8 @@ "folders": "Mapper", "folders_feature_description": "Gennemse mappevisningen efter fotos og videoer pÃĨ filsystemet", "forward": "Fremad", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Denne funktion indlÃĻser eksterne ressourcer fra Google for at virke.", "general": "Generel", "get_help": "FÃĨ hjÃĻlp", "get_wifiname_error": "Kunne ikke hente Wi-Fi-navn. Sørg for, at du har givet de nødvendige tilladelser og er forbundet til et Wi-Fi-netvÃĻrk", @@ -1050,13 +1043,14 @@ "home_page_building_timeline": "Bygger tidslinjen", "home_page_delete_err_partner": "Kan endnu ikke slette partners elementer. Springer over", "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_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_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", @@ -1087,6 +1081,7 @@ "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}}", @@ -1097,6 +1092,12 @@ "invalid_date_format": "Ugyldigt dato format", "invite_people": "Inviter personer", "invite_to_album": "Inviter til album", + "ios_debug_info_fetch_ran_at": "Hent kørte {dateTime}", + "ios_debug_info_last_sync_at": "Sidste sync skete {dateTime}", + "ios_debug_info_no_processes_queued": "Ingen baggrundsprocesser i kø", + "ios_debug_info_no_sync_yet": "Der er endnu ikke kørt noget baggrundssynkroniseringsjob", + "ios_debug_info_processes_queued": "{count, plural, one {{count} baggrundsproces i kø} other {{count} baggrundsprocesser i kø}}", + "ios_debug_info_processing_ran_at": "Behandlingen kørte {dateTime}", "items_count": "{count, plural, one {# element} other {# elementer}}", "jobs": "Opgaver", "keep": "Behold", @@ -1105,6 +1106,9 @@ "kept_this_deleted_others": "Beholdt denne mediefil og slettede {count, plural, one {# aktiv} other {# aktiver}}", "keyboard_shortcuts": "Tastaturgenveje", "language": "Sprog", + "language_no_results_subtitle": "Prøv at justere dit søgeord", + "language_no_results_title": "Ingen sprog fundet", + "language_search_hint": "VÃĻlg sprog...", "language_setting_description": "VÃĻlg dit foretrukne sprog", "last_seen": "Sidst set", "latest_version": "Seneste version", @@ -1130,6 +1134,7 @@ "list": "Liste", "loading": "IndlÃĻser", "loading_search_results_failed": "IndlÃĻsning af søgeresultater fejlede", + "local_asset_cast_failed": "Kan ikke caste et aktiv, der ikke er uploadet til serveren", "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", @@ -1151,6 +1156,7 @@ "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", @@ -1221,6 +1227,7 @@ "memories_swipe_to_close": "Stryg op for at lukke", "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", @@ -1230,7 +1237,9 @@ "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", @@ -1266,6 +1275,7 @@ "no_archived_assets_message": "ArkivÊr billeder og videoer for at gemme dem vÃĻk fra din Billede oversigt", "no_assets_message": "KLIK FOR AT UPLOADE DIT FØRSTE BILLEDE", "no_assets_to_show": "Ingen elementer at vise", + "no_cast_devices_found": "Ingen Cast-enheder fundet", "no_duplicates_found": "Ingen duplikater fundet.", "no_exif_info_available": "Ingen tilgÃĻngelig exif information", "no_explore_results_message": "Upload flere billeder for at udforske din samling.", @@ -1291,16 +1301,20 @@ "notification_toggle_setting_description": "AktivÊr emailnotifikationer", "notifications": "Notifikationer", "notifications_setting_description": "AdministrÊr notifikationer", + "oauth": "OAuth", "official_immich_resources": "Officielle Immich-ressourcer", - "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.", + "offline": "Offline", + "ok": "Ok", "oldest_first": "Ældste først", "on_this_device": "PÃĨ denne enhed", "onboarding": "Introduktion", - "onboarding_privacy_description": "Følgende (valgfrie) funktioner er afhÃĻngige af eksterne tjenester, og kan til enhver tid deaktiveres i administrationsindstillingerne.", + "onboarding_locale_description": "VÃĻlg dit foretrukne sprog. Du kan ÃĻndre dette senere i dine indstillinger.", + "onboarding_privacy_description": "Følgende (valgfrie) funktioner er afhÃĻngige af eksterne tjenester, og kan til enhver tid deaktiveres i indstillingerne.", + "onboarding_server_welcome_description": "Lad os konfigurere din instans med nogle almindelige indstillinger.", "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_user_welcome_description": "Lad os komme i gang!", "onboarding_welcome_user": "Velkommen, {user}", + "online": "Online", "only_favorites": "Kun favoritter", "open": "Åben", "open_in_map_view": "Åben i kortvisning", @@ -1309,11 +1323,13 @@ "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", @@ -1353,6 +1369,8 @@ "permanently_delete_assets_prompt": "Er du sikker pÃĨ, at du permanent vil slette {count, plural, one {dette aktiv?} other {disse # aktiver?}} Dette vil ogsÃĨ fjerne {count, plural, one {det fra dets} other {dem fra deres}} album(er).", "permanently_deleted_asset": "Permanent slettet medie", "permanently_deleted_assets_count": "{count, plural, one {# aktiv} other {# aktiver}} permanent slettet", + "permission": "Tilladelse", + "permission_empty": "Din tilladelse mÃĨ ikke vÃĻre tom", "permission_onboarding_back": "Tilbage", "permission_onboarding_continue_anyway": "FortsÃĻt alligevel", "permission_onboarding_get_started": "Kom i gang", @@ -1361,6 +1379,7 @@ "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.", @@ -1381,13 +1400,17 @@ "play_motion_photo": "Afspil bevÃĻgelsesbillede", "play_or_pause_video": "Afspil eller pause video", "please_auth_to_access": "Log venligst ind for at tilgÃĨ", + "port": "Port", "preferences_settings_subtitle": "Administrer app-prÃĻferencer", "preferences_settings_title": "PrÃĻferencer", "preset": "Forudindstilling", "preview": "ForhÃĨndsvisning", "previous": "Forrige", "previous_memory": "Forrige minde", + "previous_or_next_day": "Dag frem/tilbage", + "previous_or_next_month": "MÃĨned frem/tilbage", "previous_or_next_photo": "Forrige eller nÃĻste billede", + "previous_or_next_year": "År frem/tilbage", "primary": "PrimÃĻre", "privacy": "Privatliv", "profile": "Profil", @@ -1395,12 +1418,14 @@ "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_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}", "purchase_activated_title": "Din nøgle er blevet aktiveret", @@ -1423,12 +1448,14 @@ "purchase_panel_info_2": "Da vi er forpligtet til ikke at tilføje betalingsvÃĻgge, vil dette køb ikke give dig yderligere funktioner i Immich. Vi er afhÃĻngige af, at brugere som dig støtter Immichs løbende udvikling.", "purchase_panel_title": "Støt projektet", "purchase_per_server": "Pr. server", - "purchase_per_user": "Per bruger", + "purchase_per_user": "Pr. bruger", "purchase_remove_product_key": "Fjern produktnøgle", "purchase_remove_product_key_prompt": "Er du sikker pÃĨ, at du vil fjerne produktnøglen?", "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", @@ -1467,10 +1494,11 @@ "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_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", + "remove_tag": "Fjern tag", "remove_url": "Fjern URL", "remove_user": "Fjern bruger", "removed_api_key": "Fjernede API-nøgle: {name}", @@ -1594,7 +1622,12 @@ "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_privacy": "Serverens privatliv", "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", @@ -1602,6 +1635,7 @@ "set_date_of_birth": "Indstil fødselsdato", "set_profile_picture": "Indstil profilbillede", "set_slideshow_to_fullscreen": "SÃĻt diasshow til fuldskÃĻrmsvisning", + "set_stack_primary_asset": "Angiv som primÃĻrt billede", "setting_image_viewer_help": "Detaljeret visning indlÃĻser miniaturebilleder først. Herefter indlÃĻses mediumstørrelse forhÃĨndsvisning af billedet (hvis dette er slÃĨet til), for til sidst at vise originalen (hvis dette er slÃĨet til).", "setting_image_viewer_original_subtitle": "SlÃĨ indlÃĻsning af originalbillede i fuld størrelse til (stort!). Deaktiver for at reducere dataforbruget (bÃĨde pÃĨ netvÃĻrket og for enhedscache).", "setting_image_viewer_original_title": "IndlÃĻs originalbillede", @@ -1610,7 +1644,6 @@ "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: {duration}", "setting_notifications_notify_hours": "{count} timer", "setting_notifications_notify_immediately": "med det samme", @@ -1673,6 +1706,7 @@ "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", @@ -1734,8 +1768,12 @@ "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_casting": "Stop casting", "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.", @@ -1747,12 +1785,15 @@ "submit": "Indsend", "suggestions": "Anbefalinger", "sunrise_on_the_beach": "Solopgang pÃĨ stranden", + "support": "Support", + "support_and_feedback": "Support og 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", @@ -1760,6 +1801,7 @@ "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", @@ -1785,10 +1827,11 @@ "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", @@ -1804,10 +1847,12 @@ "trash_page_select_assets_btn": "VÃĻlg elementer", "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 #}}", + "undo": "Fortryd", "unfavorite": "Fjern favorit", "unhide_person": "Stop med at skjule person", "unknown": "Ukendt", @@ -1826,11 +1871,10 @@ "unselect_all_duplicates": "Fjern markeringen af alle dubletter", "unstack": "Fjern fra stak", "unstacked_assets_count": "Ikke-stablet {count, plural, one {# aktiv} other {# aktiver}}", - "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", @@ -1854,6 +1898,7 @@ "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_privacy": "Brugerprivatliv", "user_purchase_settings": "Køb", "user_purchase_settings_description": "Administrer dit køb", "user_role_set": "Indstil {user} som {role}", @@ -1866,15 +1911,12 @@ "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", - "version_announcement_overlay_text_1": "Hej ven, der er en ny version af", - "version_announcement_overlay_text_2": ". Besøg venligst ", - "version_announcement_overlay_text_3": " for at sikre dig, at din dockercompose- og .env-fil er opdateret, sÃĨ der undgÃĨs fejlkonfiguration, specielt hvis du bruger WatchTower eller lignede.", - "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", @@ -1891,6 +1933,7 @@ "view_previous_asset": "Se forrige medie", "view_qr_code": "Vis QR kode", "view_stack": "Vis stak", + "view_user": "Vis bruger", "viewer_remove_from_stack": "Fjern fra stak", "viewer_stack_use_as_main_asset": "Brug som hovedelement", "viewer_unstack": "Fjern fra stak", diff --git a/i18n/de.json b/i18n/de.json index 34cda82c81..bbcd9c569c 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -22,11 +22,11 @@ "add_partner": "Partner hinzufÃŧgen", "add_path": "Pfad hinzufÃŧgen", "add_photos": "Fotos hinzufÃŧgen", + "add_tag": "Tag hinzufÃŧgen", "add_to": "HinzufÃŧgen zu â€Ļ", "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", @@ -34,6 +34,7 @@ "added_to_favorites_count": "{count, number} zu Favoriten hinzugefÃŧgt", "admin": { "add_exclusion_pattern_description": "Ausschlussmuster hinzufÃŧgen. Platzhalter, wie *, **, und ? werden unterstÃŧtzt. Um alle Dateien in einem Verzeichnis namens „Raw\" zu ignorieren, „**/Raw/**“ verwenden. Um alle Dateien zu ignorieren, die auf „.tif“ enden, „**/*.tif“ verwenden. Um einen absoluten Pfad zu ignorieren, „/pfad/zum/ignorieren/**“ verwenden.", + "admin_user": "Administrator", "asset_offline_description": "Diese Datei einer externen Bibliothek befindet sich nicht mehr auf der Festplatte und wurde in den Papierkorb verschoben. Falls die Datei innerhalb der Bibliothek verschoben wurde, ÃŧberprÃŧfe deine Zeitleiste auf die neue entsprechende Datei. Um diese Datei wiederherzustellen, stelle bitte sicher, dass Immich auf den unten stehenden Dateipfad zugreifen kann und scanne die Bibliothek.", "authentication_settings": "Authentifizierungseinstellungen", "authentication_settings_description": "Passwort-, OAuth- und sonstige Authentifizierungseinstellungen verwalten", @@ -41,12 +42,10 @@ "authentication_settings_reenable": "Nutze einen Server-Befehl zur Reaktivierung.", "background_task_job": "Hintergrundaufgaben", "backup_database": "Datenbanksicherung regelmäßig erstellen", - "backup_database_enable_description": "Datenbank regeläßig sichern", + "backup_database_enable_description": "Datenbank regelmäß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", + "backup_settings": "Einstellungen fÃŧr Datenbanksicherung", + "backup_settings_description": "Einstellungen zur regelmäßigen Sicherung der Datenbank. Hinweis: Diese Jobs werden nicht Ãŧberwacht und du wirst nicht Ãŧber Fehler informiert.", "cleared_jobs": "Folgende Aufgaben zurÃŧckgesetzt: {job}", "config_set_by_file": "Ist derzeit in einer Konfigurationsdatei festgelegt", "confirm_delete_library": "Bist du sicher, dass du die Bibliothek {library} lÃļschen willst?", @@ -62,14 +61,12 @@ "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.", - "external_library_created_at": "Externe Bibliothek (erstellt am {date})", "external_library_management": "Verwaltung externer Bibliotheken", "face_detection": "Gesichtserkennung", "face_detection_description": "Diese Aufgabe erkennt Gesichter in Dateien mittels maschinellen Lernens. Bei Videos wird nur die Miniaturansicht verwendet. „Aktualisieren“ verarbeitet alle Dateien neu. „ZurÃŧcksetzen“ setzt zusätzlich alle Gesichter zurÃŧck. „Fehlende“ stellt nur nicht verarbeitete Dateien in die Warteschlange. Erkannte Gesichter werden zur Gruppierung in bestehende oder neue Personen in die Warteschlange gestellt.", "facial_recognition_job_description": "Diese Aufgabe gruppiert im Anschluss an die Gesichtserkennung die erkannten Gesichter zu Personen. „ZurÃŧcksetzen“ gruppiert alle Gesichter neu, während „Fehlende“ Gesichter ohne Zuordnung in die Warteschlange stellt.", "failed_job_command": "Befehl {command} ist fÃŧr Aufgabe {job} fehlgeschlagen", "force_delete_user_warning": "WARNUNG: Diese Aktion lÃļscht sofort den Benutzer und all seine Dateien. Dies kann nicht rÃŧckgängig gemacht werden und die Dateien kÃļnnen nicht wiederhergestellt werden.", - "forcing_refresh_library_files": "Erneutes Laden aller Bibliotheksdateien erzwingen", "image_format": "Format", "image_format_description": "WebP erzeugt kleinere Dateien als JPEG, ist aber etwas langsamer in der Erstellung.", "image_fullsize_description": "HochauflÃļsendes Bild mit entfernten Metadaten, das beim Zoomen verwendet wird", @@ -174,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Hinweis: Um den Speicherpfad auf die vorher hochgeladenen Dateien anzuwenden, starte den", "note_cannot_be_changed_later": "HINWEIS: Dies kann später nicht mehr geändert werden!", "notification_email_from_address": "Absenderadresse", - "notification_email_from_address_description": "E-Mail-Adresse des Senders, zum Beispiel: \"Immich Photo Server \"", + "notification_email_from_address_description": "E-Mail-Adresse des Senders, zum Beispiel: \"Immich Photo Server \". Stelle sicher, dass du eine Adresse nutzt, die du berechtigt bist zu nutzen.", "notification_email_host_description": "Host des E-Mail-Servers (z.B. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignoriere Zertifikats-Fehler", "notification_email_ignore_certificate_errors_description": "TLS-Zertifikatsvalidierungsfehler ignorieren (nicht empfohlen)", @@ -198,7 +195,7 @@ "oauth_enable_description": "Anmeldung mit OAuth", "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_mobile_redirect_uri_override_description": "Einschalten, wenn der OAuth-Anbieter keine mobile URI wie ''{callback}'' erlaubt", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth-Anmeldeeinstellungen verwalten", "oauth_settings_more_details": "Weitere Informationen zu dieser Funktion findest du in der Dokumentation.", @@ -207,11 +204,9 @@ "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_storage_quota_default_description": "Kontingent in GiB, das verwendet werden soll, wenn keines Ãŧbermittelt wird.", "oauth_timeout": "ZeitÃŧberschreitung bei Anfrage", "oauth_timeout_description": "ZeitÃŧberschreitung fÃŧr Anfragen in Millisekunden", - "offline_paths": "Offline-Pfade", - "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", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "Alle Bibliotheken aktualisieren", "registration": "Admin-Registrierung", "registration_description": "Da du der erste Benutzer im System bist, wird dir die Rolle des Administrators zugewiesen, womit du fÃŧr die Verwaltungsaufgaben verantwortlich bist. Weitere Benutzer werden von dir erstellt.", - "repair_all": "Alle reparieren", - "repair_matched_items": "{count, plural, one {# Eintrag} other {# Einträge}} gefunden", - "repaired_items": "{count, plural, one {# Eintrag} other {# Einträge}} repariert", "require_password_change_on_login": "Benutzer muss das Passwort beim ersten Login ändern", "reset_settings_to_default": "Einstellungen auf Standard zurÃŧcksetzen", "reset_settings_to_recent_saved": "Einstellungen auf die zuletzt gespeicherten Einstellungen zurÃŧcksetzen", @@ -252,7 +244,6 @@ "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.", "storage_template_path_length": "Ungefähres Pfadlängen-Limit: {length, number}/{limit, number}", "storage_template_settings": "Speichervorlage", "storage_template_settings_description": "Die Ordnerstruktur und den Dateinamen der hochgeladenen Datei verwalten", @@ -264,16 +255,14 @@ "template_email_invite_album": "E-Mail-Vorlage: Einladung zu Album", "template_email_preview": "Vorschau", "template_email_settings": "E-Mail-Vorlagen", - "template_email_settings_description": "Benutzerdefinierte E-Mail-Benachrichtigungsvorlagen verwalten", "template_email_update_album": "Album-Vorlage aktualisieren", "template_email_welcome": "Willkommen bei den E-Mail-Vorlagen", "template_settings": "Benachrichtigungsvorlagen", - "template_settings_description": "Benutzerdefinierte Vorlagen fÃŧr Benachrichtigungen verwalten.", + "template_settings_description": "Benutzerdefinierte Vorlagen fÃŧr Benachrichtigungen verwalten", "theme_custom_css_settings": "Benutzerdefiniertes CSS", "theme_custom_css_settings_description": "Mit Cascading Style Sheets (CSS) kann das Design von Immich angepasst werden.", "theme_settings": "Theme-Einstellungen", "theme_settings_description": "Anpassung der Immich-Web-Oberfläche", - "these_files_matched_by_checksum": "Diese Dateien wurden anhand ihrer PrÃŧfsummen abgeglichen", "thumbnail_generation_job": "Miniaturansichten generieren", "thumbnail_generation_job_description": "Diese Aufgabe erzeugt große, kleine und unscharfe Miniaturansichten fÃŧr jede einzelne Datei, sowie Miniaturansichten fÃŧr jede Person", "transcoding_acceleration_api": "Beschleunigungs-API", @@ -301,10 +290,9 @@ "transcoding_encoding_options": "Kodierungsoptionen", "transcoding_encoding_options_description": "Setze Codec, AuflÃļsung, Qualität und andere Optionen fÃŧr die kodierten Videos", "transcoding_hardware_acceleration": "Hardware-Beschleunigung", - "transcoding_hardware_acceleration_description": "Experimentell; viel schneller, aber bei gleicher Bitrate mit geringerer Qualität", + "transcoding_hardware_acceleration_description": "Experimentell: schnellere Transcodierung, kann aber die Qualität bei gleicher Bitrate verringern", "transcoding_hardware_decoding": "Hardware-Dekodierung", "transcoding_hardware_decoding_setting_description": "ErmÃļglicht eine Ende-zu-Ende-Beschleunigung, anstatt nur die Codierung zu beschleunigen. Dies funktioniert mÃļglicherweise nicht bei allen Videos.", - "transcoding_hevc_codec": "HEVC-Codec", "transcoding_max_b_frames": "Maximale B-Frames", "transcoding_max_b_frames_description": "HÃļhere Werte verbessern die Komprimierungseffizienz, verlangsamen aber die Kodierung. Ist mÃļglicherweise nicht mit der Hardware-Beschleunigung älterer Geräte kompatibel. 0 deaktiviert die B-Frames, während -1 diesen Wert automatisch setzt.", "transcoding_max_bitrate": "Maximale Bitrate", @@ -342,8 +330,6 @@ "trash_number_of_days_description": "Anzahl der Tage, welche die Objekte im Papierkorb verbleiben, bevor sie endgÃŧltig entfernt werden", "trash_settings": "Papierkorbeinstellungen", "trash_settings_description": "Papierkorbeinstellungen verwalten", - "untracked_files": "Unverfolgte Dateien", - "untracked_files_description": "Diese Dateien werden nicht von der Anwendung getrackt. Sie kÃļnnen das Ergebnis fehlgeschlagener Verschiebungen, unterbrochener Uploads oder aufgrund eines Fehlers sein", "user_cleanup_job": "Benutzer aufräumen", "user_delete_delay": "Das Konto und die Dateien von {user} werden in {delay, plural, one {einem Tag} other {# Tagen}} fÃŧr eine permanente LÃļschung geplant.", "user_delete_delay_settings": "VerzÃļgerung fÃŧr das LÃļschen von Benutzern", @@ -402,10 +388,6 @@ "album_remove_user": "Nutzer entfernen?", "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": "{count} Elemente", - "album_thumbnail_card_shared": " ¡ Geteilt", - "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", @@ -421,6 +403,9 @@ "album_with_link_access": "Lass jeden mit dem Link die Fotos und Personen in diesem Album sehen.", "albums": "Alben", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Alben}}", + "albums_default_sort_order": "Standard Album Sortierung", + "albums_default_sort_order_description": "Sortierreihenfolge der Dateien bei der Erstellung neuer Alben.", + "albums_feature_description": "Sammlung an Alben die mit anderen Benutzern geteilt werden kÃļnnen.", "all": "Alle", "all_albums": "Alle Alben", "all_people": "Alle Personen", @@ -463,6 +448,7 @@ "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", @@ -478,9 +464,12 @@ "assets_added_count": "{count, plural, one {# Datei} other {# Dateien}} hinzugefÃŧgt", "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_cannot_be_added_to_album_count": "{count, plural, one {Datei kann}other {Dateien kÃļnnen}} nicht zum Album hinzugefÃŧgt werden", "assets_count": "{count, plural, one {# Datei} other {# Dateien}}", "assets_deleted_permanently": "{count} Element(e) permanent gelÃļscht", "assets_deleted_permanently_from_server": "{count} Element(e) permanent vom Immich-Server gelÃļscht", + "assets_downloaded_failed": "{count, plural, one {# Datei heruntergeladen - {error} fehlgeschlagen} other {# Dateien heruntergeladen - {error} fehlgeschlagen}}", + "assets_downloaded_successfully": "{count, plural, one {# Datei erfolgreich heruntergeladen} other {# Dateien erfolgreich heruntergeladen}}", "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", @@ -495,6 +484,7 @@ "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_title": "Automatische URL-Umschaltung", + "autoplay_slideshow": "Automatische Diashow", "back": "ZurÃŧck", "back_close_deselect": "ZurÃŧck, Schließen oder Abwählen", "background_location_permission": "Hintergrund Standortfreigabe", @@ -519,6 +509,7 @@ "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", @@ -537,6 +528,7 @@ "backup_controller_page_excluded": "Ausgeschlossen: ", "backup_controller_page_failed": "Fehlgeschlagen ({count})", "backup_controller_page_filename": "Dateiname: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informationen zur Sicherung", "backup_controller_page_none_selected": "Keine ausgewählt", "backup_controller_page_remainder": "Verbleibend", @@ -568,26 +560,23 @@ "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 ({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 ({count})", - "cache_settings_image_cache_size": "Bilder im Zwischenspeicher ({count} Bilder)", "cache_settings_statistics_album": "Vorschaubilder der Bibliothek", - "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 ({count} Bilder)", "cache_settings_tile_subtitle": "Lokalen Speicher verwalten", "cache_settings_tile_title": "Lokaler Speicher", "cache_settings_title": "Zwischenspeicher Einstellungen", @@ -601,6 +590,7 @@ "cannot_undo_this_action": "Diese Aktion kann nicht rÃŧckgängig gemacht werden!", "cannot_update_the_description": "Beschreibung kann nicht aktualisiert werden", "cast": "Übertragen", + "cast_description": "Konfiguration verfÃŧgbarer Ziele", "change_date": "Datum ändern", "change_description": "Beschreibung anpassen", "change_display_order": "Anzeigereihenfolge ändern", @@ -618,7 +608,6 @@ "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", "check_corrupt_asset_backup": "Auf beschädigte Asset-Backups ÃŧberprÃŧfen", "check_corrupt_asset_backup_button": "ÜberprÃŧfung durchfÃŧhren", "check_corrupt_asset_backup_description": "FÃŧhre diese PrÃŧfung nur mit aktivierten WLAN durch, nachdem alle Dateien gesichert worden sind. Dieser Vorgang kann ein paar Minuten dauern.", @@ -630,6 +619,7 @@ "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", @@ -657,11 +647,13 @@ "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", + "confirm_tag_face": "Wollen Sie dieses Gesicht mit {name} markieren?", + "confirm_tag_face_unnamed": "MÃļchten Sie dieses Gesicht markieren?", + "connected_device": "Verbundenes Gerät", "connected_to": "Verbunden mit", "contain": "Vollständig", "context": "Kontext", "continue": "Fortsetzen", - "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", @@ -710,6 +702,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Dunkel", + "darkTheme": "Dunkles Theme umschalten", "date_after": "Datum nach", "date_and_time": "Datum und Zeit", "date_before": "Datum vor", @@ -751,10 +744,13 @@ "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", "direction": "Richtung", "disabled": "Deaktiviert", "disallow_edits": "Bearbeitungen verbieten", + "discord": "Discord", "discover": "Entdecken", + "discovered_devices": "Gefundene Geräte", "dismiss_all_errors": "Alle Fehler ignorieren", "dismiss_error": "Fehler ignorieren", "display_options": "Anzeigeoptionen", @@ -770,12 +766,12 @@ "download_enqueue": "Download in die Warteschlange gesetzt", "download_error": "Download fehlerhaft", "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_settings_description": "Einstellungen fÃŧr das Herunterladen von Dateien verwalten", "download_started": "Download gestartet", "download_sucess": "Download erfolgreich", @@ -832,6 +828,7 @@ "error_delete_face": "Fehler beim LÃļschen des Gesichts", "error_loading_image": "Fehler beim Laden des Bildes", "error_saving_image": "Fehler: {error}", + "error_tag_face_bounding_box": "Fehler beim Markieren des Gesichts - Begrenzungen kÃļnnen nicht abgerufen werden", "error_title": "Fehler - Etwas ist schief gelaufen", "errors": { "cannot_navigate_next_asset": "Kann nicht zur nächsten Datei navigieren", @@ -844,7 +841,6 @@ "cant_get_number_of_comments": "Anzahl der Kommentare konnte nicht abgerufen werden", "cant_search_people": "Personen konnten nicht gesucht werden", "cant_search_places": "Orte konnten nicht gesucht werden", - "cleared_jobs": "Folgende Aufgaben zurÃŧckgesetzt: {job}", "error_adding_assets_to_album": "Fehler beim HinzufÃŧgen von Dateien zum Album", "error_adding_users_to_album": "Fehler beim HinzufÃŧgen von Benutzern zum Album", "error_deleting_shared_user": "Fehler beim LÃļschen des geteilten Benutzers", @@ -853,7 +849,6 @@ "error_removing_assets_from_album": "Fehler beim Entfernen von Dateien aus dem Album, siehe Konsole fÃŧr weitere Details", "error_selecting_all_assets": "Fehler beim Auswählen aller Dateien", "exclusion_pattern_already_exists": "Dieses Ausschlussmuster existiert bereits.", - "failed_job_command": "Befehl {command} ist fÃŧr Aufgabe {job} fehlgeschlagen", "failed_to_create_album": "Album konnte nicht erstellt werden", "failed_to_create_shared_link": "Geteilter Link konnte nicht erstellt werden", "failed_to_edit_shared_link": "Geteilter Link konnte nicht bearbeitet werden", @@ -872,7 +867,6 @@ "paths_validation_failed": "{paths, plural, one {# Pfad konnte} other {# Pfade konnten}} nicht validiert werden", "profile_picture_transparent_pixels": "Profilbilder dÃŧrfen keine transparenten Pixel haben. Bitte zoome heran und/oder verschiebe das Bild.", "quota_higher_than_disk_size": "Dein festgelegtes Kontingent ist grÃļßer als der verfÃŧgbare Speicher", - "repair_unable_to_check_items": "{count, select, one {Eintrag konnte} other {Einträge konnten}} nicht ÃŧberprÃŧft werden", "unable_to_add_album_users": "Benutzer konnten nicht zum Album hinzugefÃŧgt werden", "unable_to_add_assets_to_shared_link": "Datei konnte nicht zum geteilten Link hinzugefÃŧgt werden", "unable_to_add_comment": "Es kann kein Kommentar hinzufÃŧgt werden", @@ -891,7 +885,6 @@ "unable_to_change_visibility": "Sichtbarkeit von {count, plural, one {einer Person} other {# Personen}} konnte nicht geändert werden", "unable_to_complete_oauth_login": "OAuth-Anmeldung konnte nicht abgeschlossen werden", "unable_to_connect": "Verbindung konnte nicht hergestellt werden", - "unable_to_connect_to_server": "Verbindung zum Server konnte nicht hergestellt werden", "unable_to_copy_to_clipboard": "Konnte nicht in die Zwischenablage kopieren, stelle sicher, dass du per https auf die Seite zugreifst", "unable_to_create_admin_account": "Administratorkonto konnte nicht erstellt werden", "unable_to_create_api_key": "Es konnte kein API-SchlÃŧssel erstellt werden", @@ -915,14 +908,9 @@ "unable_to_hide_person": "Person kann nicht versteckt werden", "unable_to_link_motion_video": "Bewegungsvideo kann nicht verknÃŧpft werden", "unable_to_link_oauth_account": "OAuth-Konto kann nicht verknÃŧpft werden", - "unable_to_load_album": "Album kann nicht geladen werden", - "unable_to_load_asset_activity": "Foto-Aktivität konnte nicht geladen werden", - "unable_to_load_items": "Elemente konnten nicht geladen werden", - "unable_to_load_liked_status": "GewÃŧnschter Status kann nicht geladen werden", "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", @@ -930,11 +918,9 @@ "unable_to_remove_album_users": "Mitglieder der Alben kÃļnnen nicht entfernt werden", "unable_to_remove_api_key": "API-SchlÃŧssel konnte nicht entfernt werden", "unable_to_remove_assets_from_shared_link": "Dateien konnten nicht von geteiltem Link entfernt werden", - "unable_to_remove_deleted_assets": "Offline-Dateien konnten nicht entfernt werden", "unable_to_remove_library": "Bibliothek kann nicht entfernt werden", "unable_to_remove_partner": "Partner kann nicht entfernt werden", "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", @@ -966,10 +952,10 @@ }, "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 {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}", @@ -1007,6 +993,7 @@ "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", @@ -1016,6 +1003,8 @@ "folders": "Ordner", "folders_feature_description": "Durchsuchen der Ordneransicht fÃŧr Fotos und Videos im Dateisystem", "forward": "Vorwärts", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Diese Funktion lädt externe Quellen von Google, um zu funktionieren.", "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", @@ -1062,7 +1051,9 @@ "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", "hour": "Stunde", + "id": "ID", "ignore_icloud_photos": "iCloud Fotos ignorieren", "ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen", "image": "Bild", @@ -1091,6 +1082,7 @@ "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}}", @@ -1101,6 +1093,12 @@ "invalid_date_format": "UngÃŧltiges Datumsformat", "invite_people": "Personen einladen", "invite_to_album": "Zum Album einladen", + "ios_debug_info_fetch_ran_at": "Abruf läuft {dateTime}", + "ios_debug_info_last_sync_at": "Zuletzt aktualisiert {dateTime}", + "ios_debug_info_no_processes_queued": "Keine Hintergrundprozesse in der Warteschlange", + "ios_debug_info_no_sync_yet": "Noch kein Hintergrundsynchronisierungsauftrag ausgefÃŧhrt", + "ios_debug_info_processes_queued": "{count, plural, one {{count} Hintergrundprozess in der Warteschlange} other {{count} Hintergrundprozesse in der Warteschlange}}", + "ios_debug_info_processing_ran_at": "Prozess läuft {dateTime}", "items_count": "{count, plural, one {# Eintrag} other {# Einträge}}", "jobs": "Aufgaben", "keep": "Behalten", @@ -1109,6 +1107,9 @@ "kept_this_deleted_others": "Diese Datei behalten und {count, plural, one {# Datei} other {# Dateien}} gelÃļscht", "keyboard_shortcuts": "TastenkÃŧrzel", "language": "Sprache", + "language_no_results_subtitle": "Probiere es mit einem anderen Suchbegriff", + "language_no_results_title": "Keine Sprachen gefunden", + "language_search_hint": "Sprachen durchsuchen...", "language_setting_description": "Wähle deine bevorzugte Sprache", "last_seen": "Zuletzt gesehen", "latest_version": "Aktuellste Version", @@ -1116,6 +1117,7 @@ "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", @@ -1133,6 +1135,7 @@ "list": "Liste", "loading": "Laden", "loading_search_results_failed": "Laden von Suchergebnissen fehlgeschlagen", + "local_asset_cast_failed": "Eine Datei, die nicht auf den Server hochgeladen wurde, kann nicht gecastet werden", "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", @@ -1146,6 +1149,7 @@ "locked_folder": "Gesperrter Ordner", "log_out": "Abmelden", "log_out_all_devices": "Alle Geräte abmelden", + "logged_in_as": "Angemeldet als {user}", "logged_out_all_devices": "Alle Geräte abgemeldet", "logged_out_device": "Gerät abgemeldet", "login": "Anmelden", @@ -1233,9 +1237,11 @@ "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", @@ -1248,6 +1254,7 @@ "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", @@ -1270,6 +1277,7 @@ "no_archived_assets_message": "Archiviere Fotos und Videos, um sie aus deiner Fotoansicht zu entfernen", "no_assets_message": "KLICKE, UM DEIN ERSTES FOTO HOCHZULADEN", "no_assets_to_show": "Keine Vorschau vorhanden", + "no_cast_devices_found": "Keine Geräte zum Übertragen gefunden", "no_duplicates_found": "Es wurden keine Duplikate gefunden.", "no_exif_info_available": "Keine EXIF-Informationen vorhanden", "no_explore_results_message": "Lade weitere Fotos hoch, um deine Sammlung zu erkunden.", @@ -1295,16 +1303,20 @@ "notification_toggle_setting_description": "E-Mail-Benachrichtigungen aktivieren", "notifications": "Benachrichtigungen", "notifications_setting_description": "Benachrichtigungen verwalten", + "oauth": "OAuth", "official_immich_resources": "Offizielle Immich Quellen", - "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.", + "offline": "Offline", + "ok": "Ok", "oldest_first": "Älteste zuerst", "on_this_device": "Auf diesem Gerät", "onboarding": "Einstieg", - "onboarding_privacy_description": "Die folgenden (optionalen) Funktionen hängen von externen Diensten ab und kÃļnnen jederzeit in den Administrationseinstellungen deaktiviert werden.", + "onboarding_locale_description": "Wählen Sie Ihre bevorzugte Sprache. Sie kÃļnnen diese auch später in Ihren Einstellungen ändern.", + "onboarding_privacy_description": "Die folgenden (optionalen) Funktionen hängen von externen Diensten ab und kÃļnnen jederzeit in den Einstellungen deaktiviert werden.", + "onboarding_server_welcome_description": "Lassen Sie uns diese Instanz mit einigen allgemeinen Einstellungen einrichten.", "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_user_welcome_description": "Fangen wir an!", "onboarding_welcome_user": "Willkommen, {user}", + "online": "Online", "only_favorites": "Nur Favoriten", "open": "Öffnen", "open_in_map_view": "In Kartenansicht Ãļffnen", @@ -1319,6 +1331,7 @@ "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", @@ -1343,6 +1356,7 @@ }, "path": "Pfad", "pattern": "Muster", + "pause": "Pause", "pause_memories": "Erinnerungen pausieren", "paused": "Pausiert", "pending": "Ausstehend", @@ -1357,6 +1371,8 @@ "permanently_delete_assets_prompt": "Bist du sicher, dass {count, plural, one {diese Datei} other {diese # Dateien}} endgÃŧltig gelÃļscht werden soll? Dadurch {count, plural, one {wird} other {werden}} diese auch aus deinen Alben entfernt.", "permanently_deleted_asset": "EndgÃŧltig gelÃļschtes Objekt", "permanently_deleted_assets_count": "{count, plural, one {# Datei} other {# Dateien}} endgÃŧltig gelÃļscht", + "permission": "Berechtigung", + "permission_empty": "Ihre Berechtigung sollte nicht leer sein", "permission_onboarding_back": "ZurÃŧck", "permission_onboarding_continue_anyway": "Trotzdem fortfahren", "permission_onboarding_get_started": "Jetzt starten", @@ -1365,6 +1381,7 @@ "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.", @@ -1385,19 +1402,25 @@ "play_motion_photo": "Bewegte Bilder abspielen", "play_or_pause_video": "Video abspielen oder pausieren", "please_auth_to_access": "FÃŧr den Zugriff bitte Authentifizieren", + "port": "Port", "preferences_settings_subtitle": "App-Einstellungen verwalten", "preferences_settings_title": "Voreinstellungen", "preset": "Voreinstellung", "preview": "Vorschau", "previous": "Vorherige", "previous_memory": "Vorherige Erinnerung", - "previous_or_next_photo": "Vorheriges oder nächstes Foto", + "previous_or_next_day": "Tag vorwärts/rÃŧckwärts", + "previous_or_next_month": "Monat vorwärts/rÃŧckwärts", + "previous_or_next_photo": "Foto vorwärts/rÃŧckwärts", + "previous_or_next_year": "Jahr vorwärts/rÃŧckwärts", "primary": "Primär", "privacy": "Privatsphäre", "profile": "Profil", + "profile_drawer_app_logs": "Logs", "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- und Server-Versionen sind aktuell", + "profile_drawer_github": "GitHub", "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}", @@ -1424,7 +1447,7 @@ "purchase_lifetime_description": "Lebenslange GÃŧltigkeit", "purchase_option_title": "KAUFOPTIONEN", "purchase_panel_info_1": "Die Entwicklung von Immich erfordert viel Zeit und MÃŧhe, und wir haben Vollzeit-Entwickler, die daran arbeiten es mÃļglichst perfekt zu machen. Unser Ziel ist es, dass Open-Source-Software und moralische Geschäftsmethoden zu einer nachhaltigen Einkommensquelle fÃŧr Entwickler werden und ein datenschutzfreundliches Ökosystem mit echten Alternativen zu ausbeuterischen Cloud-Diensten geschaffen wird.", - "purchase_panel_info_2": "Weil wir davon Ãŧberzeugt sind keine Paywalls zu haben, wird dieser Kauf keine zusätzlichen Funktionen in Immich freischalten. Wir verlassen uns auf Nutzende wie dich, um die Entwicklung von Immich zu unterstÃŧtzen.", + "purchase_panel_info_2": "Weil wir uns dagegen entschieden haben, eine Bezahlschranke einzusetzen, wird dieser Kauf keine zusätzlichen Funktionen in Immich freischalten. Wir verlassen uns auf Nutzende wie dich, um die Entwicklung von Immich zu unterstÃŧtzen.", "purchase_panel_title": "Das Projekt unterstÃŧtzen", "purchase_per_server": "Pro Server", "purchase_per_user": "Pro Benutzer", @@ -1434,6 +1457,7 @@ "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", @@ -1472,10 +1496,11 @@ "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_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", + "remove_tag": "Tag entfernen", "remove_url": "URL entfernen", "remove_user": "Nutzer entfernen", "removed_api_key": "API-SchlÃŧssel {name} wurde entfernt", @@ -1489,6 +1514,7 @@ "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", @@ -1557,6 +1583,7 @@ "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", @@ -1580,6 +1607,7 @@ "select_album_cover": "Album-Cover auswählen", "select_all": "Alles auswählen", "select_all_duplicates": "Alle Duplikate auswählen", + "select_all_in": "Alle in {group} auswählen", "select_avatar_color": "Avatar-Farbe auswählen", "select_face": "Gesicht auswählen", "select_featured_photo": "Anzeigebild auswählen", @@ -1600,6 +1628,7 @@ "server_info_box_server_url": "Server-URL", "server_offline": "Server offline", "server_online": "Server online", + "server_privacy": "Privatsphäre auf dem Server", "server_stats": "Server-Statistiken", "server_version": "Server-Version", "set": "Speichern", @@ -1609,6 +1638,7 @@ "set_date_of_birth": "Geburtsdatum festlegen", "set_profile_picture": "Profilbild einstellen", "set_slideshow_to_fullscreen": "Diashow auf Vollbild einstellen", + "set_stack_primary_asset": "Als primäre Datei festlegen", "setting_image_viewer_help": "Der Detailbildbetrachter lädt zuerst ein (kleines) Vorschaubild, dann ein Vorschaubild in mittlerer GrÃļße (falls aktiviert) und schließlich das Original (falls aktiviert).", "setting_image_viewer_original_subtitle": "Aktivieren, um das Originalbild in voller AuflÃļsung (groß!) zu laden. Deaktivieren, um den Datenverbrauch zu reduzieren (sowohl im Netzwerk als auch im Gerätespeicher).", "setting_image_viewer_original_title": "Original laden", @@ -1617,7 +1647,6 @@ "setting_image_viewer_title": "Bilder", "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: {duration}", "setting_notifications_notify_hours": "{count} Stunden", "setting_notifications_notify_immediately": "sofort", @@ -1680,6 +1709,7 @@ "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", @@ -1741,9 +1771,12 @@ "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": "Stapelaufgaben", "start": "Starten", "start_date": "Anfangsdatum", "state": "Bundesland / Provinz", + "status": "Status", + "stop_casting": "Übertragung stoppen", "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.", @@ -1763,6 +1796,7 @@ "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", @@ -1770,7 +1804,9 @@ "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", @@ -1798,7 +1834,6 @@ "to_parent": "Gehe zum Übergeordneten", "to_trash": "In den Papierkorb verschieben", "toggle_settings": "Einstellungen umschalten", - "toggle_theme": "Dunkles Theme umschalten", "total": "Gesamt", "total_usage": "Gesamtnutzung", "trash": "Papierkorb", @@ -1820,6 +1855,7 @@ "unable_to_setup_pin_code": "PIN Code konnte nicht festgelegt werden", "unarchive": "Entarchivieren", "unarchived_count": "{count, plural, other {# entarchiviert}}", + "undo": "RÃŧckgängig", "unfavorite": "Entfavorisieren", "unhide_person": "Person einblenden", "unknown": "Unbekannt", @@ -1836,10 +1872,9 @@ "unsaved_change": "Ungespeicherte Änderung", "unselect_all": "Alles abwählen", "unselect_all_duplicates": "Alle Duplikate abwählen", + "unselect_all_in": "Alle in {group} abwählen", "unstack": "Entstapeln", "unstacked_assets_count": "{count, plural, one {# Datei} other {# Dateien}} entstapelt", - "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", @@ -1856,6 +1891,7 @@ "upload_success": "Hochladen erfolgreich. Aktualisiere die Seite, um neue hochgeladene Dateien zu sehen.", "upload_to_immich": "Auf Immich hochladen ({count})", "uploading": "Wird hochgeladen", + "url": "URL", "usage": "Verwendung", "use_biometric": "Biometrie verwenden", "use_current_connection": "aktuelle Verbindung verwenden", @@ -1864,7 +1900,9 @@ "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": "PIN Code", "user_pin_code_settings_description": "Verwalte deinen PIN Code", + "user_privacy": "Datenschutzeinstellungen Nutzer", "user_purchase_settings": "Kauf", "user_purchase_settings_description": "Kauf verwalten", "user_role_set": "{user} als {role} festlegen", @@ -1877,17 +1915,16 @@ "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", - "version_announcement_overlay_text_1": "Hallo mein Freund! Es gibt eine neue Version von", - "version_announcement_overlay_text_2": "Bitte nehme dir die Zeit und lies das ", - "version_announcement_overlay_text_3": " und achte darauf, dass deine docker-compose und .env Dateien aktuell sind, vor allem wenn du ein System fÃŧr automatische Updates benutzt (z.B. Watchtower).", - "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", @@ -1900,6 +1937,7 @@ "view_previous_asset": "Vorherige Datei anzeigen", "view_qr_code": "QR code anzeigen", "view_stack": "Stapel anzeigen", + "view_user": "Benutzer anzeigen", "viewer_remove_from_stack": "Aus Stapel entfernen", "viewer_stack_use_as_main_asset": "An Stapelanfang", "viewer_unstack": "Stapel aufheben", diff --git a/i18n/el.json b/i18n/el.json index 613be44e3e..62d0481ec6 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -12,7 +12,7 @@ "add": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ", "add_a_description": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚", "add_a_location": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŧÎ¯ÎąĪ‚ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", - "add_a_name": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŋÎŊΌÎŧÎąĪ„ÎŋĪ‚", + "add_a_name": "Î ĪÎŋĪƒÎ¸Î­ĪƒĪ„Îĩ έÎŊÎą ΌÎŊÎŋÎŧÎą", "add_a_title": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ Ī„Î¯Ī„ÎģÎŋĪ…", "add_endpoint": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ Ī„ÎĩÎģΚÎēÎŋĪ ĪƒÎˇÎŧÎĩίÎŋĪ…", "add_exclusion_pattern": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŧÎŋĪ„Î¯Î˛ÎŋĪ… ÎąĪ€ÎŋÎēÎģÎĩÎšĪƒÎŧÎŋĪ", @@ -22,11 +22,11 @@ "add_partner": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ĪƒĪ…ÎŊÎĩĪÎŗÎŦĪ„Îˇ", "add_path": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ Î´ÎšÎąÎ´ĪÎŋÎŧÎŽĪ‚", "add_photos": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ", + "add_tag": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎĩĪ„ÎšÎēÎ­Ī„ÎąĪ‚", "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": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŖĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "added_to_archive": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎĩ ĪƒĪ„Îŋ ÎąĪĪ‡ÎĩίÎŋ", @@ -34,6 +34,7 @@ "added_to_favorites_count": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎąÎŊ {count, number} ĪƒĪ„Îą ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą", "admin": { "add_exclusion_pattern_description": "Î ĪÎŋĪƒÎ¸Î­ĪƒĪ„Îĩ ÎŧÎŋĪ„Î¯Î˛Îą ÎąĪ€ÎŋÎēÎģÎĩÎšĪƒÎŧÎŋĪ. ÎĨĪ€ÎŋĪƒĪ„ÎˇĪÎ¯ÎļÎĩĪ„ÎąÎš Ρ ÎĩĪ€ÎšÎģÎŋÎŗÎŽ Ī€ÎŋÎģÎģĪŽÎŊ ÎŧÎĩ *, **, ÎēιΚ ?. Για ÎŊÎą ÎąÎŗÎŊÎŋΡθÎŋĪÎŊ ΌÎģÎą Ī„Îą ÎąĪĪ‡ÎĩÎ¯Îą ΃Îĩ έÎŊÎąÎŊ ΆÎŦÎēÎĩÎģÎŋ ÎŧÎĩ Ī„Îŋ ΌÎŊÎŋÎŧÎą \"Raw\", Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ \"**/Raw/**\". Για ÎŊÎą ÎąÎŗÎŊÎŋΡθÎŋĪÎŊ ΌÎģÎą Ī„Îą ÎąĪĪ‡ÎĩÎ¯Îą ÎŧÎĩ ÎēÎąĪ„ÎŦÎģΡΞΡ \".tif\", Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ \"**/*.tif\". Για ÎŊÎą ÎąÎŗÎŊÎŋΡθÎĩί ÎŧÎ¯Îą ÎąĪ€ĪŒÎģĪ…Ī„Îˇ Î´ÎšÎąÎ´ĪÎŋÎŧÎŽ, Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ \"/path/to/ignore/**\".", + "admin_user": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎŽĪ‚", "asset_offline_description": "Î‘Ī…Ī„ĪŒ Ī„Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ ÎĩÎžĪ‰Ī„ÎĩĪÎšÎēÎŽĪ‚ βΚβÎģΚÎŋθΎÎēÎˇĪ‚ δÎĩ Î˛ĪÎ¯ĪƒÎēÎĩĪ„ÎąÎš Ī€ÎģέÎŋÎŊ ĪƒĪ„Îŋ Î´Î¯ĪƒÎēÎŋ ÎēιΚ Î­Ī‡ÎĩΚ ÎŧÎĩĪ„ÎąĪ†ÎĩĪÎ¸Îĩί ĪƒĪ„Îą ÎąĪ€ÎŋĪĪÎ¯ÎŧÎŧÎąĪ„Îą. ΕÎŦÎŊ Ī„Îŋ ÎąĪĪ‡ÎĩίÎŋ Î­Ī‡ÎĩΚ ÎŧÎĩĪ„ÎąÎēΚÎŊΡθÎĩί ÎĩÎŊĪ„ĪŒĪ‚ Ī„ÎˇĪ‚ βΚβÎģΚÎŋθΎÎēÎˇĪ‚, ÎĩÎģÎ­ÎŗÎžĪ„Îĩ Ī„Îŋ ·΁ÎŋÎŊÎŋÎģĪŒÎŗÎšÎŋ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ ĪƒÎąĪ‚ ÎŗÎšÎą Ī„Îŋ ÎŊέÎŋ ÎąÎŊĪ„Î¯ĪƒĪ„ÎŋÎšĪ‡Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ. Για ÎŊÎą ÎĩĪ€ÎąÎŊÎąĪ†Î­ĪÎĩĪ„Îĩ ÎąĪ…Ī„ĪŒ Ī„Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ, βÎĩÎ˛ÎąÎšĪ‰Î¸ÎĩÎ¯Ī„Îĩ ĪŒĪ„Îš Ī„Îŋ Ī€ÎąĪÎąÎēÎŦ΄Ή ÎŧÎŋÎŊÎŋĪ€ÎŦĪ„Îš ÎąĪĪ‡ÎĩίÎŋĪ… ÎĩίÎŊιΚ ΀΁ÎŋĪƒÎ˛ÎŦĪƒÎšÎŧÎŋ ÎąĪ€ĪŒ Ī„Îŋ Immich ÎēιΚ ĪƒÎąĪĪŽĪƒĪ„Îĩ Ī„Îˇ βΚβÎģΚÎŋθΎÎēΡ.", "authentication_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ΕÎģÎ­ÎŗĪ‡ÎŋĪ… Î¤ÎąĪ…Ī„ĪŒĪ„ÎˇĪ„ÎąĪ‚", "authentication_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ÎēĪ‰Î´ÎšÎēÎŋĪ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚, OAuth ÎēιΚ ÎŦÎģÎģΉÎŊ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ÎĩÎģÎ­ÎŗĪ‡ÎŋĪ… Ī„ÎąĪ…Ī„ĪŒĪ„ÎˇĪ„ÎąĪ‚", @@ -44,9 +45,7 @@ "backup_database_enable_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ dumps βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", "backup_keep_last_amount": "ΠÎŋĪƒĪŒĪ„ÎˇĪ„Îą ΀΁ÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊΉÎŊ dumps Ī€ÎŋĪ… Ī€ĪÎ­Ī€ÎĩΚ ÎŊÎą Î´ÎšÎąĪ„ÎˇĪÎˇÎ¸ÎŋĪÎŊ", "backup_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ dump βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", - "backup_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ dump Ī„ÎˇĪ‚ βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ. ÎŖÎˇÎŧÎĩÎ¯Ī‰ĪƒÎˇ: Î‘Ī…Ī„Î­Ī‚ ÎŋΚ ÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚ δÎĩÎŊ Ī€ÎąĪÎąÎēÎŋÎģÎŋĪ…Î¸ÎŋĪÎŊĪ„ÎąÎš ÎēιΚ δÎĩÎŊ θι ÎĩΚδÎŋĪ€ÎŋΚΡθÎĩÎ¯Ī„Îĩ ÎŗÎšÎą ÎąĪ€ÎŋĪ„Ī…Ī‡Î¯Îą.", - "check_all": "ΈÎģÎĩÎŗĪ‡ÎŋĪ‚ ΌÎģΉÎŊ", - "cleanup": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ", + "backup_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ dump Ī„ÎˇĪ‚ βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ.", "cleared_jobs": "ΕÎēÎēÎąÎ¸ÎąĪÎ¯ĪƒĪ„ÎˇÎēÎąÎŊ ÎŋΚ ÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚ ÎŗÎšÎą: {job}", "config_set_by_file": "Η Ī€ÎąĪÎąÎŧÎĩ΄΁ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎŗÎ¯ÎŊÎĩĪ„ÎąÎš, ΀΁ÎŋĪ‚ Ī„Îŋ Ī€ÎąĪĪŒÎŊ, ÎŧÎ­ĪƒĪ‰ ÎĩÎŊĪŒĪ‚ ÎąĪĪ‡ÎĩίÎŋĪ… Ī€ÎąĪÎąÎŧÎ­Ī„ĪĪ‰ÎŊ", "confirm_delete_library": "Î•Î¯ĪƒĪ„Îĩ βέβιΚÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩĪ„Îĩ Ī„Îˇ βΚβÎģΚÎŋθΎÎēΡ {library};", @@ -62,14 +61,12 @@ "disable_login": "Î‘Ī€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚", "duplicate_detection_job_description": "ΕÎēĪ„ÎĩÎģÎ­ĪƒĪ„Îĩ ÎŧÎˇĪ‡ÎąÎŊΚÎēÎŽ ÎŧÎŦÎ¸ÎˇĪƒÎˇ ΃Îĩ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŗÎšÎą ÎŊÎą ÎĩÎŊĪ„ÎŋĪ€Î¯ĪƒÎĩĪ„Îĩ Ī€ÎąĪĪŒÎŧÎŋΚÎĩĪ‚ ÎĩΚÎēΌÎŊÎĩĪ‚. Î’ÎąĪƒÎ¯ÎļÎĩĪ„ÎąÎš ĪƒĪ„ÎˇÎŊ ÎˆÎžĪ…Ī€ÎŊΡ ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ", "exclusion_pattern_description": "Τι ÎŧÎŋĪ„Î¯Î˛Îą ÎąĪ€ÎŋÎēÎģÎĩÎšĪƒÎŧÎŋĪ ĪƒÎąĪ‚ ÎĩĪ€ÎšĪ„ĪÎ­Ī€ÎŋĪ…ÎŊ ÎŊÎą ÎąÎŗÎŊÎŋÎĩÎ¯Ī„ÎąÎš ÎąĪĪ‡ÎĩÎ¯Îą ÎēιΚ Ī†ÎąÎēέÎģÎŋĪ…Ī‚ ÎēÎąĪ„ÎŦ Ī„Îˇ ΃ÎŦĪĪ‰ĪƒÎˇ Ī„ÎˇĪ‚ βΚβÎģΚÎŋθΎÎēÎˇĪ‚ ĪƒÎąĪ‚. Î‘Ī…Ī„ĪŒ ÎĩίÎŊιΚ Ī‡ĪÎŽĪƒÎšÎŧÎŋ ÎĩÎŦÎŊ Îĩ·ÎĩĪ„Îĩ Ī†ÎąÎēέÎģÎŋĪ…Ī‚ Ī€ÎŋĪ… Ī€ÎĩĪÎšÎ­Ī‡ÎŋĪ…ÎŊ ÎąĪĪ‡ÎĩÎ¯Îą Ī€ÎŋĪ… δÎĩÎŊ θέÎģÎĩĪ„Îĩ ÎŊÎą ÎĩÎšĪƒÎŦÎŗÎĩĪ„Îĩ, ĪŒĪ€Ī‰Ī‚ ÎąĪĪ‡ÎĩÎ¯Îą RAW.", - "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": "ΕιÎēΌÎŊÎą Ī€ÎģÎŽĪÎŋĪ…Ī‚ ÎŧÎĩÎŗÎ­Î¸ÎŋĪ…Ī‚ ÎŧÎĩ ÎąĪ€ÎŋÎŗĪ…ÎŧÎŊΉÎŧέÎŊÎą ÎŧÎĩĪ„ÎąÎ´ÎĩδÎŋÎŧέÎŊÎą, Ī€ÎŋĪ… Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„ÎąÎš ÎēÎąĪ„ÎŦ Ī„Îˇ ÎŧÎĩÎŗÎ­Î¸Ī…ÎŊĪƒÎˇ", @@ -174,7 +171,7 @@ "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_from_address_description": "ΔιÎĩĪÎ¸Ī…ÎŊĪƒÎˇ ÎąĪ€ÎŋĪƒĪ„ÎŋÎģέι, ΀·: \"Immich Photo Server \". ΒÎĩÎ˛ÎąÎšĪ‰Î¸ÎĩÎ¯Ī„Îĩ ĪŒĪ„Îš Î­Ī‡ÎĩĪ„Îĩ δΚÎēÎąÎ¯Ī‰ÎŧÎą Ī‡ĪÎŽĪƒÎˇĪ‚ Ī„ÎˇĪ‚ δΚÎĩĪÎ¸Ī…ÎŊĪƒÎˇĪ‚ Ī€ÎŋĪ… Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„Îĩ.", "notification_email_host_description": "ΠÎŦ΁Îŋ·ÎŋĪ‚ Ī„ÎŋĪ… email server (΀· smtp.immich.app)", "notification_email_ignore_certificate_errors": "Î ÎąĪÎŦβÎģÎĩĪˆÎˇ ΄ΉÎŊ ĪƒĪ†ÎąÎģÎŧÎŦ΄ΉÎŊ Ī€ÎšĪƒĪ„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚", "notification_email_ignore_certificate_errors_description": "Î ÎąĪÎŦβÎģÎĩĪˆÎˇ ĪƒĪ†ÎąÎģÎŧÎŦ΄ΉÎŊ ÎĩĪ€ÎšÎēĪĪĪ‰ĪƒÎˇĪ‚ Ī„ÎˇĪ‚ Ī€ÎšĪƒĪ„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚ TLS (δÎĩÎŊ ΀΁ÎŋĪ„ÎĩίÎŊÎĩĪ„ÎąÎš)", @@ -198,7 +195,7 @@ "oauth_enable_description": "ÎŖĪÎŊδÎĩĪƒÎˇ ÎŧÎĩ OAuth", "oauth_mobile_redirect_uri": "URI ΑÎŊÎąÎēÎąĪ„ÎĩĪÎ¸Ī…ÎŊĪƒÎˇĪ‚ ÎŗÎšÎą ÎēΚÎŊÎˇĪ„ÎŦ Ī„ÎˇÎģÎ­Ī†Ī‰ÎŊÎą", "oauth_mobile_redirect_uri_override": "Î ĪÎŋĪƒĪ€Î­ÎģÎąĪƒÎˇ URI ÎąÎŊÎąÎēÎąĪ„ÎĩĪÎ¸Ī…ÎŊĪƒÎˇĪ‚ ÎŗÎšÎą ÎēΚÎŊÎˇĪ„ÎŦ Ī„ÎˇÎģÎ­Ī†Ī‰ÎŊÎą", - "oauth_mobile_redirect_uri_override_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„Îŋ ĪŒĪ„ÎąÎŊ Îŋ Ī€ÎŦ΁Îŋ·ÎŋĪ‚ OAuth δÎĩÎŊ ÎĩĪ€ÎšĪ„ĪÎ­Ī€ÎĩΚ ÎŧΚι URI ÎŗÎšÎą ÎēΚÎŊÎˇĪ„ÎŦ, ĪŒĪ€Ī‰Ī‚ Ī„Îŋ '{callback}'", + "oauth_mobile_redirect_uri_override_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„Îŋ ĪŒĪ„ÎąÎŊ Îŋ Ī€ÎŦ΁Îŋ·ÎŋĪ‚ OAuth δÎĩÎŊ ÎĩĪ€ÎšĪ„ĪÎ­Ī€ÎĩΚ ÎŧΚι URI ÎŗÎšÎą ÎēΚÎŊÎˇĪ„ÎŦ, ĪŒĪ€Ī‰Ī‚ Ī„Îŋ ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚ OAuth", "oauth_settings_more_details": "Για Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ÎĩĪ‚ ÎģÎĩ΀΄ÎŋÎŧÎ­ĪÎĩΚÎĩĪ‚ ĪƒĪ‡ÎĩĪ„ÎšÎēÎŦ ÎŧÎĩ ÎąĪ…Ī„ÎŽÎŊ Ī„Îˇ Î´Ī…ÎŊÎąĪ„ĪŒĪ„ÎˇĪ„Îą, ÎąÎŊÎąĪ„ĪÎ­ÎžĪ„Îĩ ĪƒĪ„ÎˇÎŊ Ī„ÎĩÎēÎŧÎˇĪÎ¯Ī‰ĪƒÎˇ.", @@ -207,11 +204,9 @@ "oauth_storage_quota_claim": "ΔήÎģĪ‰ĪƒÎˇ Ī€Îŋ΃ÎŋĪƒĪ„ÎŋĪ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚", "oauth_storage_quota_claim_description": "ÎŸĪÎ¯ÎļÎĩΚ ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îą Ī„Îŋ Ī€Îŋ΃ÎŋĪƒĪ„ĪŒ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ ĪƒĪ„Îˇ δΡÎģΉÎŧέÎŊΡ Ī„ÎšÎŧÎŽ.", "oauth_storage_quota_default": "Î ĪÎŋÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎŋ ĪŒĪÎšÎŋ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚ (GiB)", - "oauth_storage_quota_default_description": "ΠÎŋ΃ÎŋĪƒĪ„ĪŒ ΃Îĩ GiB Ī€ÎŋĪ… θι Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚΡθÎĩί ĪŒĪ„ÎąÎŊ δÎĩÎŊ ÎŋĪÎ¯ÎļÎĩĪ„ÎąÎš ÎąĪ€ĪŒ Ī„Îˇ δΡÎģΉÎŧέÎŊΡ Ī„ÎšÎŧÎŽ (Î•ÎšĪƒÎŦÎŗÎĩĪ„Îĩ 0 ÎŗÎšÎą ÎąĪ€ÎĩĪÎšĪŒĪÎšĪƒĪ„Îŋ Ī€Îŋ΃ÎŋĪƒĪ„ĪŒ).", + "oauth_storage_quota_default_description": "ΠÎŋ΃ÎŋĪƒĪ„ĪŒ ΃Îĩ GiB Ī€ÎŋĪ… θι Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚΡθÎĩί ĪŒĪ„ÎąÎŊ δÎĩÎŊ ÎŋĪÎ¯ÎļÎĩĪ„ÎąÎš ÎąĪ€ĪŒ Ī„Îˇ δΡÎģΉÎŧέÎŊΡ Ī„ÎšÎŧÎŽ.", "oauth_timeout": "Î§ĪÎŋÎŊΚÎēΌ ĪŒĪÎšÎŋ Î‘ÎšĪ„ÎŽÎŧÎąĪ„ÎŋĪ‚", "oauth_timeout_description": "Î§ĪÎŋÎŊΚÎēΌ ĪŒĪÎšÎŋ Î‘ÎšĪ„ÎŽÎŧÎąĪ„ÎŋĪ‚ ΃Îĩ milliseconds", - "offline_paths": "Î”ÎšÎąÎ´ĪÎŋÎŧÎ­Ī‚ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ ÎĩÎēĪ„ĪŒĪ‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚", - "offline_paths_description": "Î‘Ī…Ī„ÎŦ Ī„Îą ÎąĪ€ÎŋĪ„ÎĩÎģÎ­ĪƒÎŧÎąĪ„Îą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ÎŋΆÎĩίÎģÎŋÎŊĪ„ÎąÎš ΃Îĩ ·ÎĩÎšĪÎŋÎēίÎŊÎˇĪ„Îˇ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ Ī€ÎŋĪ… δÎĩÎŊ ÎąÎŊÎŽÎēÎŋĪ…ÎŊ ΃Îĩ ÎĩÎžĪ‰Ī„ÎĩĪÎšÎēÎŽ βΚβÎģΚÎŋθΎÎēΡ.", "password_enable_description": "ÎŖĪÎŊδÎĩĪƒÎˇ ÎŧÎĩ ΡÎģÎĩÎē΄΁ÎŋÎŊΚÎēΌ Ī„ÎąĪ‡Ī…Î´ĪÎŋÎŧÎĩίÎŋ", "password_settings": "ÎŖĪÎŊδÎĩĪƒÎˇ ÎŧÎĩ ÎēĪ‰Î´ÎšÎēΌ", "password_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚ ÎŧÎ­ĪƒĪ‰ ÎēĪ‰Î´ÎšÎēÎŋĪ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "Î•Ī€ÎąÎŊÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ ΌÎģΉÎŊ ΄ΉÎŊ βΚβÎģΚÎŋθΡÎēĪŽÎŊ", "registration": "Î•ÎŗÎŗĪÎąĪ†ÎŽ Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎŽ", "registration_description": "ΔÎĩδÎŋÎŧέÎŊÎŋĪ… ĪŒĪ„Îš ÎĩÎ¯ĪƒĪ„Îĩ Îŋ Ī€ĪĪŽĪ„ÎŋĪ‚ Ī‡ĪÎŽĪƒĪ„ÎˇĪ‚ ĪƒĪ„Îŋ ĪƒĪĪƒĪ„ÎˇÎŧÎą, θι ÎąÎŊÎąĪ„ÎĩθÎĩÎ¯Ī„Îĩ Ή΂ Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎŽĪ‚ ÎēιΚ θι ÎĩÎ¯ĪƒĪ„Îĩ Ī…Ī€ÎĩĪÎ¸Ī…ÎŊÎŋĪ‚ ÎŗÎšÎą Ī„ÎšĪ‚ Î´ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎšÎēÎ­Ī‚ ÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚, ÎĩÎŊĪŽ ÎŋΚ ÎĩĪ€ÎšĪ€ÎģέÎŋÎŊ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚ θι δΡÎŧΚÎŋĪ…ĪÎŗÎŋĪÎŊĪ„ÎąÎš ÎąĪ€ĪŒ Îĩ΃ÎŦĪ‚.", - "repair_all": "Î•Ī€ÎšÎ´ÎšĪŒĪÎ¸Ī‰ĪƒÎˇ ΌÎģΉÎŊ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", - "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": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ĪƒĪ„ÎšĪ‚ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îą ÎąĪ€ÎŋθΡÎēÎĩĪ…ÎŧέÎŊÎĩĪ‚ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚", @@ -252,7 +244,7 @@ "storage_template_migration_info": "ΤÎŋ Ī€ĪĪŒĪ„Ī…Ī€Îŋ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚ θι ÎŧÎĩĪ„ÎąĪ„ĪÎ­ĪˆÎĩΚ ΌÎģÎĩĪ‚ Ī„ÎšĪ‚ ÎĩĪ€ÎĩÎēĪ„ÎŦ΃ÎĩÎšĪ‚ ΃Îĩ Ī€ÎĩÎļÎŦ ÎŗĪÎŦÎŧÎŧÎąĪ„Îą. Οι ÎąÎģÎģÎąÎŗÎ­Ī‚ ĪƒĪ„Îŋ Ī€ĪĪŒĪ„Ī…Ī€Îŋ θι ÎšĪƒĪ‡ĪÎŋĪ…ÎŊ ÎŧΌÎŊÎŋ ÎŗÎšÎą ÎŊέι Ī€ÎĩĪÎšÎŋĪ…ĪƒÎšÎąÎēÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą. Για ÎŊÎą ÎĩĪ†ÎąĪÎŧΌ΃ÎĩĪ„Îĩ ÎąÎŊÎąÎ´ĪÎŋÎŧΚÎēÎŦ Ī„Îŋ Ī€ĪĪŒĪ„Ī…Ī€Îŋ ΃Îĩ Ī€ÎĩĪÎšÎŋĪ…ĪƒÎšÎąÎēÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī€ÎŋĪ… Î­Ī‡ÎŋĪ…ÎŊ ÎŧÎĩĪ„ÎąĪ†ÎŋĪĪ„Ī‰Î¸Îĩί ΀΁ÎŋÎˇÎŗÎŋĪ…ÎŧέÎŊΉ΂, ÎĩÎēĪ„ÎĩÎģÎ­ĪƒĪ„Îĩ Ī„Îŋ {job}.", "storage_template_migration_job": "Î•ĪÎŗÎąĪƒÎ¯Îą ΜÎĩĪ„ÎąĪ†Îŋ΁ÎŦĪ‚ Î ĪÎŋĪ„ĪĪ€Ī‰ÎŊ Î‘Ī€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚", "storage_template_more_details": "Για Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ÎĩĪ‚ ÎģÎĩ΀΄ÎŋÎŧÎ­ĪÎĩΚÎĩĪ‚ ĪƒĪ‡ÎĩĪ„ÎšÎēÎŦ ÎŧÎĩ ÎąĪ…Ī„ÎŽÎŊ Ī„Îˇ Î´Ī…ÎŊÎąĪ„ĪŒĪ„ÎˇĪ„Îą, ÎąÎŊÎąĪ„ĪÎ­ÎžĪ„Îĩ ĪƒĪ„Îŋ Î ĪĪŒĪ„Ī…Ī€Îŋ Î‘Ī€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚ ÎēιΚ ĪƒĪ„ÎšĪ‚ ĪƒĪ…ÎŊÎ­Ī€ÎĩÎšÎ­Ī‚ Ī„ÎŋĪ…", - "storage_template_onboarding_description": "ÎŒĪ„ÎąÎŊ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡθÎĩί, ÎąĪ…Ī„ÎŽ Ρ Î´Ī…ÎŊÎąĪ„ĪŒĪ„ÎˇĪ„Îą θι ÎŋĪÎŗÎąÎŊĪŽÎŊÎĩΚ ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îą Ī„Îą ÎąĪĪ‡ÎĩÎ¯Îą ÎŧÎĩ βÎŦĪƒÎˇ έÎŊÎą Ī€ĪĪŒĪ„Ī…Ī€Îŋ Ī€ÎŋĪ… ÎēιθÎŋĪÎ¯ÎļÎĩĪ„ÎąÎš ÎąĪ€ĪŒ Ī„ÎŋÎŊ Ī‡ĪÎŽĪƒĪ„Îˇ. Î›ĪŒÎŗĪ‰ θÎĩÎŧÎŦ΄ΉÎŊ ĪƒĪ„ÎąÎ¸ÎĩĪĪŒĪ„ÎˇĪ„ÎąĪ‚, Ρ Î´Ī…ÎŊÎąĪ„ĪŒĪ„ÎˇĪ„Îą ÎĩίÎŊιΚ ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ ÎąĪ€ĪŒ ΀΁ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ. Για Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ÎĩĪ‚ Ī€ÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚, Ī€ÎąĪÎąÎēÎąÎģĪŽ δÎĩÎ¯Ī„Îĩ Ī„ÎˇÎŊ Ī„ÎĩÎēÎŧÎˇĪÎ¯Ī‰ĪƒÎˇ.", + "storage_template_onboarding_description_v2": "ÎŒĪ„ÎąÎŊ ÎĩίÎŊιΚ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ, ÎąĪ…Ī„ÎŽ Ρ ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯Îą θι ÎŋĪÎŗÎąÎŊĪŽÎŊÎĩΚ ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îą Ī„Îą ÎąĪĪ‡ÎĩÎ¯Îą ÎŧÎĩ βÎŦĪƒÎˇ έÎŊÎą Ī€ĪĪŒĪ„Ī…Ī€Îŋ Ī€ÎŋĪ… ÎŋĪÎ¯ÎļÎĩĪ„ÎąÎš ÎąĪ€ĪŒ Ī„Îŋ Ī‡ĪÎŽĪƒĪ„Îˇ. Για Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ÎĩĪ‚ Ī€ÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚, Ī€ÎąĪÎąÎēÎąÎģĪŽ ÎąÎŊÎąĪ„ĪÎ­ÎžĪ„Îĩ ĪƒĪ„ÎšĪ‚ ÎŋÎ´ÎˇÎŗÎ¯ÎĩĪ‚ Ī‡ĪÎŽĪƒÎˇĪ‚.", "storage_template_path_length": "ÎŒĪÎšÎŋ ÎŧÎŽÎēÎŋĪ…Ī‚ Î´ÎšÎąÎ´ĪÎŋÎŧÎŽĪ‚: {length, number}/{limit, number}, ÎēÎąĪ„ÎŦ ΀΁ÎŋĪƒÎ­ÎŗÎŗÎšĪƒÎˇ", "storage_template_settings": "Î ĪĪŒĪ„Ī…Ī€Îŋ Î‘Ī€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚", "storage_template_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ Ī„ÎˇĪ‚ δÎŋÎŧÎŽĪ‚ Ī†ÎąÎēέÎģÎŋĪ… ÎēιΚ Ī„ÎŋĪ… ÎŋÎŊΌÎŧÎąĪ„ÎŋĪ‚, Ī„ÎŋĪ… ÎąÎŊÎĩÎ˛ÎąĪƒÎŧέÎŊÎŋĪ… ÎąĪĪ‡ÎĩίÎŋĪ…", @@ -264,16 +256,14 @@ "template_email_invite_album": "Î ĪĪŒĪ„Ī…Ī€Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ Ī€ĪĪŒĪƒÎēÎģÎˇĪƒÎˇĪ‚", "template_email_preview": "Î ĪÎŋÎĩĪ€ÎšĪƒÎēĪŒĪ€ÎˇĪƒÎˇ", "template_email_settings": "Î ĪĪŒĪ„Ī…Ī€Îą Email", - "template_email_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ΀΁ÎŋĪƒÎąĪÎŧÎŋ΃ÎŧέÎŊΉÎŊ ΀΁ÎŋĪ„ĪĪ€Ī‰ÎŊ ÎĩΚδÎŋĪ€ÎŋÎšÎŽĪƒÎĩΉÎŊ email", "template_email_update_album": "ΕÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ Ī€ĪĪŒĪ„Ī…Ī€ÎŋĪ… ΆÎģÎŧĪ€ÎŋĪ…Îŧ", "template_email_welcome": "Î ĪĪŒĪ„Ī…Ī€Îŋ email ÎēÎąÎģĪ‰ĪƒÎŋĪÎ¯ĪƒÎŧÎąĪ„ÎŋĪ‚", "template_settings": "Î ĪĪŒĪ„Ī…Ī€Îą ÎĩΚδÎŋĪ€ÎŋÎšÎŽĪƒÎĩΉÎŊ", - "template_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ΀΁ÎŋĪƒÎąĪÎŧÎŋ΃ÎŧέÎŊΉÎŊ ΀΁ÎŋĪ„ĪĪ€Ī‰ÎŊ ÎŗÎšÎą ÎĩΚδÎŋĪ€ÎŋÎšÎŽĪƒÎĩÎšĪ‚.", + "template_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ΀΁ÎŋĪƒÎąĪÎŧÎŋ΃ÎŧέÎŊΉÎŊ ΀΁ÎŋĪ„ĪĪ€Ī‰ÎŊ ÎŗÎšÎą ÎĩΚδÎŋĪ€ÎŋÎšÎŽĪƒÎĩÎšĪ‚", "theme_custom_css_settings": "Î ĪÎŋĪƒÎąĪÎŧÎŋ΃ÎŧέÎŊÎŋ CSS", "theme_custom_css_settings_description": "Τι Cascading Style Sheets(CSS) ÎĩĪ€ÎšĪ„ĪÎ­Ī€ÎĩΚ Ī„ÎˇÎŊ ΀΁ÎŋĪƒÎąĪÎŧÎŋÎŗÎŽ Ī„ÎŋĪ… ĪƒĪ‡ÎĩÎ´ÎšÎąĪƒÎŧÎŋĪ Ī„ÎŋĪ… Immich.", "theme_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ΘέÎŧÎąĪ„ÎŋĪ‚", "theme_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ Ī„ÎˇĪ‚ ΀΁ÎŋĪƒÎąĪÎŧÎŋÎŗÎŽĪ‚ Ī„ÎŋĪ… ÎšĪƒĪ„ĪŒĪ„ÎŋĪ€ÎŋĪ… Ī„ÎŋĪ… Immich", - "these_files_matched_by_checksum": "Î‘Ī…Ī„ÎŦ Ī„Îą ÎąĪĪ‡ÎĩÎ¯Îą ÎąÎŊĪ„ÎšĪƒĪ„ÎŋÎšĪ‡Î¯ÎļÎŋÎŊĪ„ÎąÎš ÎŧÎĩ βÎŦĪƒÎˇ Ī„Îą checksums(ÎŧÎŋÎŊιδΚÎēÎ­Ī‚ ÎąÎģÎŗÎŋĪÎšÎ¸ÎŧΚÎēÎ­Ī‚ Ī„ÎšÎŧÎ­Ī‚ ΄ΉÎŊ Ī€ÎĩĪÎšÎĩ·ÎŋÎŧέÎŊΉÎŊ ÎĩÎŊĪŒĪ‚ ÎąĪĪ‡ÎĩίÎŋĪ…) Ī„ÎŋĪ…Ī‚", "thumbnail_generation_job": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ΜιÎē΁ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ", "thumbnail_generation_job_description": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎŧÎĩÎŗÎŦÎģΉÎŊ, ÎŧΚÎēĪĪŽÎŊ ÎēιΚ θÎŋÎģĪŽÎŊ ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ ÎŗÎšÎą ÎēÎŦθÎĩ ÎąĪĪ‡ÎĩίÎŋ, ÎēÎąÎ¸ĪŽĪ‚ ÎēιΚ ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ ÎŗÎšÎą ÎēÎŦθÎĩ ÎŦĪ„ÎŋÎŧÎŋ", "transcoding_acceleration_api": "Î•Ī€ÎšĪ„ÎŦ·΅ÎŊĪƒÎˇ API", @@ -301,10 +291,9 @@ "transcoding_encoding_options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ÎēĪ‰Î´ÎšÎēÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚", "transcoding_encoding_options_description": "ÎŸĪÎ¯ĪƒĪ„Îĩ Ī„ÎŋĪ…Ī‚ ÎēĪ‰Î´ÎšÎēÎŋĪ€ÎŋÎšÎˇĪ„Î­Ī‚, Ī„ÎˇÎŊ ÎąÎŊÎŦÎģĪ…ĪƒÎˇ, Ī„ÎˇÎŊ Ī€ÎŋÎšĪŒĪ„ÎˇĪ„Îą ÎēιΚ ÎŦÎģÎģÎĩĪ‚ ÎĩĪ€ÎšÎģÎŋÎŗÎ­Ī‚ ÎŗÎšÎą Ī„Îą ÎēĪ‰Î´ÎšÎēÎŋĪ€ÎŋΚΡÎŧέÎŊÎą Î˛Î¯ÎŊĪ„ÎĩÎŋ", "transcoding_hardware_acceleration": "Î•Ī€ÎšĪ„ÎŦ·΅ÎŊĪƒÎˇ Ī…ÎģΚÎēÎŋĪ", - "transcoding_hardware_acceleration_description": "ΠÎĩÎšĪÎąÎŧÎąĪ„ÎšÎēĪŒÎ‡ Ī€ÎŋÎģĪ Ī€ÎšÎŋ ÎŗĪÎŽÎŗÎŋ΁Îŋ, ÎąÎģÎģÎŦ θι Î­Ī‡ÎĩΚ Ī‡ÎąÎŧΡÎģĪŒĪ„ÎĩĪÎˇ Ī€ÎŋÎšĪŒĪ„ÎˇĪ„Îą ÎŧÎĩ Ī„ÎŋÎŊ ίδΚÎŋ ĪĪ…Î¸ÎŧΌ ÎŧÎĩĪ„ÎŦδÎŋĪƒÎˇĪ‚ (bitrate)", + "transcoding_hardware_acceleration_description": "ΠÎĩÎšĪÎąÎŧÎąĪ„ÎšÎēΌ: ÎŗĪÎŽÎŗÎŋĪÎˇ ÎēĪ‰Î´ÎšÎēÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎąÎģÎģÎŦ ÎĩÎŊÎ´Î­Ī‡ÎĩĪ„ÎąÎš ÎŊÎą ÎŧÎĩÎšĪ‰Î¸Îĩί Ρ Ī€ÎŋÎšĪŒĪ„ÎˇĪ„Îą ĪƒĪ„ÎŋÎŊ ίδΚÎŋ ĪĪ…Î¸ÎŧΌ ÎŧÎĩĪ„ÎŦδÎŋĪƒÎˇĪ‚ (bitrate)", "transcoding_hardware_decoding": "Î‘Ī€ÎŋÎēĪ‰Î´ÎšÎēÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎŧÎ­ĪƒĪ‰ Ī…ÎģΚÎēÎŋĪ", "transcoding_hardware_decoding_setting_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋΚÎĩί Ī„ÎˇÎŊ ÎĩĪ€ÎšĪ„ÎŦ·΅ÎŊĪƒÎˇ ÎąĪ€ĪŒ ÎŦÎēĪÎˇ ΃Îĩ ÎŦÎēĪÎˇ ÎąÎŊĪ„Î¯ ÎŗÎšÎą ÎŧΌÎŊÎŋ ÎĩĪ€ÎšĪ„ÎŦ·΅ÎŊĪƒÎˇ Ī„ÎˇĪ‚ ÎēĪ‰Î´ÎšÎēÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚. ÎœĪ€Îŋ΁Îĩί ÎŊÎą ÎŧΡÎŊ ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎĩί ΃Îĩ ΌÎģÎą Ī„Îą Î˛Î¯ÎŊĪ„ÎĩÎŋ.", - "transcoding_hevc_codec": "ÎšĪ‰Î´ÎšÎēÎŋĪ€ÎŋÎšÎˇĪ„ÎŽĪ‚ HEVC", "transcoding_max_b_frames": "ÎœÎ­ÎŗÎšĪƒĪ„ÎŋĪ‚ ÎąĪÎšÎ¸ÎŧĪŒĪ‚ B-frames(Bidirectional Predictive Frames)", "transcoding_max_b_frames_description": "Οι Ī…ĪˆÎˇÎģĪŒĪ„Îĩ΁ÎĩĪ‚ Ī„ÎšÎŧÎ­Ī‚ βÎĩÎģĪ„ÎšĪŽÎŊÎŋĪ…ÎŊ Ī„ÎˇÎŊ ÎąĪ€ÎŋδÎŋĪ„ÎšÎēĪŒĪ„ÎˇĪ„Îą Ī„ÎˇĪ‚ ĪƒĪ…ÎŧĪ€Î¯ÎĩĪƒÎˇĪ‚, ÎąÎģÎģÎŦ ÎĩĪ€ÎšÎ˛ĪÎąÎ´ĪÎŊÎŋĪ…ÎŊ Ī„ÎˇÎŊ ÎēĪ‰Î´ÎšÎēÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ. ΕÎŊÎ´Î­Ī‡ÎĩĪ„ÎąÎš ÎŊÎą ÎŧΡÎŊ ÎĩίÎŊιΚ ĪƒĪ…ÎŧÎ˛ÎąĪ„Î­Ī‚ ÎŧÎĩ Ī„ÎˇÎŊ ÎĩĪ€ÎšĪ„ÎŦ·΅ÎŊĪƒÎˇ Ī…ÎģΚÎēÎŋĪ ΃Îĩ Ī€ÎąÎģÎąÎšĪŒĪ„Îĩ΁ÎĩĪ‚ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚. Η Ī„ÎšÎŧÎŽ 0 ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚÎĩί Ī„Îą B-frames, ÎĩÎŊĪŽ Ρ -1, Ī„Îˇ ĪĪ…Î¸ÎŧίÎļÎĩΚ ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îą.", "transcoding_max_bitrate": "ÎœÎ­ÎŗÎšĪƒĪ„ÎŋĪ‚ ĪĪ…Î¸ÎŧĪŒĪ‚ ÎŧÎĩĪ„ÎŦδÎŋĪƒÎˇĪ‚ (bitrate)", @@ -342,8 +331,6 @@ "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": "ÎšÎąÎ¸Ī…ĪƒĪ„Î­ĪÎˇĪƒÎˇ Î´ÎšÎąÎŗĪÎąĪ†ÎŽĪ‚", @@ -402,10 +389,6 @@ "album_remove_user": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ Ī‡ĪÎŽĪƒĪ„Îˇ;", "album_remove_user_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎąĪ†ÎąÎšĪÎ­ĪƒÎĩĪ„Îĩ Ī„ÎŋÎŊ/Ī„ÎˇÎŊ {user};", "album_share_no_users": "ÎĻÎąÎ¯ÎŊÎĩĪ„ÎąÎš ĪŒĪ„Îš Î­Ī‡ÎĩĪ„Îĩ ÎēÎŋΚÎŊÎŋĪ€ÎŋÎšÎŽĪƒÎĩΚ ÎąĪ…Ī„ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ΃Îĩ ΌÎģÎŋĪ…Ī‚ Ī„ÎŋĪ…Ī‚ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚ ÎŽ δÎĩÎŊ Î­Ī‡ÎĩĪ„Îĩ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚ ÎŗÎšÎą ÎŊÎą Ī„Îŋ ÎēÎŋΚÎŊÎŋĪ€ÎŋÎšÎŽĪƒÎĩĪ„Îĩ.", - "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": "ΛÎŦβÎĩĪ„Îĩ ÎĩΚδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎŧÎ­ĪƒĪ‰ email ĪŒĪ„ÎąÎŊ έÎŊÎą ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ Î­Ī‡ÎĩΚ ÎŊέι ÎąĪĪ‡ÎĩÎ¯Îą", "album_user_left": "Î‘Ī€ÎŋĪ‡Ī‰ĪÎŽĪƒÎąĪ„Îĩ ÎąĪ€ĪŒ Ī„Îŋ {album}", @@ -421,6 +404,9 @@ "album_with_link_access": "Î•Ī€ÎšĪ„ĪÎ­ĪˆĪ„Îĩ ΃Îĩ ÎŋĪ€ÎŋΚÎŋÎŊÎ´ÎŽĪ€ÎŋĪ„Îĩ Î­Ī‡ÎĩΚ Ī„ÎŋÎŊ ĪƒĪÎŊδÎĩ΃ÎŧÎŋ, ÎŊÎą δÎĩΚ Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą ÎŦĪ„ÎŋÎŧÎą ΃Îĩ ÎąĪ…Ī„ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ.", "albums": "ΆÎģÎŧĪ€ÎŋĪ…Îŧ", "albums_count": "{count, plural, one {{count, number} ΆÎģÎŧĪ€ÎŋĪ…Îŧ} other {{count, number} ΆÎģÎŧĪ€ÎŋĪ…Îŧ}}", + "albums_default_sort_order": "Î ĪÎŋÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊΡ Ī„ÎąÎžÎšÎŊΌÎŧÎˇĪƒÎˇ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "albums_default_sort_order_description": "Î‘ĪĪ‡ÎšÎēÎŽ Ī„ÎąÎžÎšÎŊΌÎŧÎˇĪƒÎˇ ÎēÎąĪ„ÎŦ Ī„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎŊÎ­Ī‰ÎŊ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ.", + "albums_feature_description": "ÎŖĪ…ÎģÎģÎŋÎŗÎ­Ī‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ Ī€ÎŋĪ… ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą ÎēÎŋΚÎŊÎŋĪ€ÎŋΚΡθÎŋĪÎŊ ΃Îĩ ÎŦÎģÎģÎŋĪ…Ī‚ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚.", "all": "ΌÎģÎą", "all_albums": "ΌÎģÎą Ī„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "all_people": "ΌÎģÎą Ī„Îą ÎŦĪ„ÎŋÎŧÎą", @@ -479,9 +465,12 @@ "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_cannot_be_added_to_album_count": "{count, plural, one {ÎŖĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {ÎŖĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}} δÎĩÎŊ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą ΀΁ÎŋĪƒĪ„ÎĩθÎŋĪÎŊ ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "assets_count": "{count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}}", "assets_deleted_permanently": "{count} Ī„Îą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ", "assets_deleted_permanently_from_server": "{count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎąĪ€ĪŒ Ī„Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ Immich", + "assets_downloaded_failed": "{count, plural, one {ÎˆÎŗÎšÎŊÎĩ ÎģÎŽĪˆÎˇ # ÎąĪĪ‡ÎĩίÎŋĪ… - {error} ÎąĪĪ‡ÎĩίÎŋ ÎąĪ€Î­Ī„Ī…Ī‡Îĩ} other {ÎˆÎŗÎšÎŊÎĩ ÎģÎŽĪˆÎˇ # ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ - {error} ÎąĪĪ‡ÎĩÎ¯Îą ÎąĪ€Î­Ī„Ī…Ī‡ÎąÎŊ}}", + "assets_downloaded_successfully": "{count, plural, one {ÎˆÎŗÎšÎŊÎĩ ÎģÎŽĪˆÎˇ # ÎąĪĪ‡ÎĩίÎŋĪ… ÎĩĪ€ÎšĪ„Ī…Ī‡ĪŽĪ‚} other {ÎˆÎŗÎšÎŊÎĩ ÎģÎŽĪˆÎˇ # ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ ÎĩĪ€ÎšĪ„Ī…Ī‡ĪŽĪ‚}}", "assets_moved_to_trash_count": "ΜÎĩĪ„ÎąÎēΚÎŊΎθΡÎēÎąÎŊ {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}} ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", "assets_permanently_deleted_count": "Î”ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ ÎŧΌÎŊΚÎŧÎą {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}}", "assets_removed_count": "Î‘Ī†ÎąÎšĪÎ­Î¸ÎˇÎēÎąÎŊ {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}}", @@ -496,6 +485,7 @@ "authorized_devices": "ΕξÎŋĪ…ĪƒÎšÎŋδÎŋĪ„ÎˇÎŧέÎŊÎĩĪ‚ ÎŖĪ…ĪƒÎēÎĩĪ…Î­Ī‚", "automatic_endpoint_switching_subtitle": "ÎŖĪÎŊδÎĩĪƒÎˇ Ī„ÎŋĪ€ÎšÎēÎŦ ÎŧÎ­ĪƒĪ‰ Ī„ÎŋĪ… ÎēιθÎŋĪÎšĪƒÎŧέÎŊÎŋĪ… Wi-Fi ĪŒĪ„ÎąÎŊ ÎĩίÎŊιΚ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎŋ ÎēιΚ Ī‡ĪÎŽĪƒÎˇ ÎĩÎŊÎąÎģÎģÎąÎēĪ„ÎšÎēĪŽÎŊ ĪƒĪ…ÎŊÎ´Î­ĪƒÎĩΉÎŊ ÎąÎģÎģÎŋĪ", "automatic_endpoint_switching_title": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„Îˇ ÎĩÎŊÎąÎģÎģÎąÎŗÎŽ URL", + "autoplay_slideshow": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„Îˇ ÎąÎŊÎąĪ€ÎąĪÎąÎŗĪ‰ÎŗÎŽ Ī€ÎąĪÎŋĪ…ĪƒÎ¯ÎąĪƒÎˇĪ‚", "back": "Î Î¯ĪƒĪ‰", "back_close_deselect": "Î Î¯ĪƒĪ‰, ÎēÎģÎĩÎ¯ĪƒÎšÎŧÎŋ ÎŽ ÎąĪ€ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ", "background_location_permission": "ΆδÎĩΚι Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ", @@ -539,6 +529,7 @@ "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": "ÎĨĪ€ĪŒÎģÎŋÎšĪ€Îŋ", @@ -576,21 +567,17 @@ "bulk_keep_duplicates_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎēĪÎąĪ„ÎŽĪƒÎĩĪ„Îĩ {count, plural, one {# Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îŋ ÎąĪĪ‡ÎĩίÎŋ} other {# Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą ÎąĪĪ‡ÎĩÎ¯Îą}}; Î‘Ī…Ī„ĪŒ θι ÎĩĪ€ÎšÎģĪĪƒÎĩΚ ΌÎģÎĩĪ‚ Ī„ÎšĪ‚ ÎŋÎŧÎŦδÎĩĪ‚ Î´ÎšĪ€ÎģÎŋĪ„ĪĪ€Ī‰ÎŊ Ī‡Ī‰ĪÎ¯Ī‚ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩΚ Ī„Î¯Ī€ÎŋĪ„Îą.", "bulk_trash_duplicates_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą βÎŦÎģÎĩĪ„Îĩ ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ {count, plural, one {# Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îŋ ÎąĪĪ‡ÎĩίÎŋ} other {# Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą ÎąĪĪ‡ÎĩÎ¯Îą}}; Î‘Ī…Ī„ĪŒ θι ÎēĪÎąĪ„ÎŽĪƒÎĩΚ Ī„Îŋ ÎŧÎĩÎŗÎąÎģĪĪ„Îĩ΁Îŋ ÎąĪĪ‡ÎĩίÎŋ ÎąĪ€ĪŒ ÎēÎŦθÎĩ ÎŋÎŧÎŦδι ÎēιΚ θι βÎŦÎģÎĩΚ ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ ΌÎģÎą Ī„Îą ÎŦÎģÎģÎą Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą.", "buy": "Î‘ÎŗÎŋ΁ÎŦĪƒĪ„Îĩ Ī„Îŋ Immich", - "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": "Î”ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ({count})", - "cache_settings_image_cache_size": "ÎœÎ­ÎŗÎĩθÎŋĪ‚ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ÎŧÎŊÎŽÎŧÎˇĪ‚ ÎĩΚÎēΌÎŊΉÎŊ ({count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą)", "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": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎˇĪƒÎˇ ĪƒĪ…ÎŧĪ€ÎĩĪÎšĪ†Îŋ΁ÎŦĪ‚ Ī„ÎˇĪ‚ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ÎŧÎŊÎŽÎŧÎˇĪ‚", - "cache_settings_thumbnail_size": "ÎœÎ­ÎŗÎĩθÎŋĪ‚ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ÎŧÎŊÎŽÎŧÎˇĪ‚ ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ ({count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą)", "cache_settings_tile_subtitle": "ΧÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„Îˇ ĪƒĪ…ÎŧĪ€ÎĩĪÎšĪ†Îŋ΁ÎŦ Ī„ÎˇĪ‚ Ī„ÎŋĪ€ÎšÎēÎŽĪ‚ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚", "cache_settings_tile_title": "ΤÎŋĪ€ÎšÎēÎŽ Î‘Ī€ÎŋθΎÎēÎĩĪ…ĪƒÎˇ", "cache_settings_title": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ Î ĪÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ΜÎŊÎŽÎŧÎˇĪ‚", @@ -604,13 +591,14 @@ "cannot_undo_this_action": "ΔÎĩÎŊ ÎŧĪ€Îŋ΁ÎĩÎ¯Ī„Îĩ ÎŊÎą ÎąÎŊÎąÎšĪÎ­ĪƒÎĩĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„ÎˇÎŊ ÎĩÎŊÎ­ĪÎŗÎĩΚι!", "cannot_update_the_description": "Î‘Î´ĪÎŊÎąĪ„Îˇ Ρ ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ Ī„ÎˇĪ‚ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚", "cast": "Î ĪÎŋβÎŋÎģÎŽ", + "cast_description": "ÎĄĪÎ¸ÎŧÎšĪƒÎˇ ΄ΉÎŊ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧΉÎŊ ΀΁ÎŋÎŋĪÎšĪƒÎŧĪŽÎŊ casting", "change_date": "ΑÎģÎģÎąÎŗÎŽ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯ÎąĪ‚", "change_description": "ΑÎģÎģÎąÎŗÎŽ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚", "change_display_order": "ΑÎģÎģÎąÎŗÎŽ ΃ÎĩÎšĪÎŦĪ‚ ÎĩÎŧΆÎŦÎŊÎšĪƒÎˇĪ‚", "change_expiration_time": "ΑÎģÎģÎąÎŗÎŽ Ī‡ĪĪŒÎŊÎŋĪ… ÎģÎŽÎžÎˇĪ‚", "change_location": "ΑÎģÎģÎąÎŗÎŽ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", "change_name": "ΑÎģÎģÎąÎŗÎŽ ÎŋÎŊÎŋÎŧÎąĪƒÎ¯ÎąĪ‚", - "change_name_successfully": "Î•Ī€ÎšĪ„Ī…Ī‡ÎŽĪ‚ ÎąÎģÎģÎąÎŗÎŽ ÎŋÎŊÎŋÎŧÎąĪƒÎ¯ÎąĪ‚", + "change_name_successfully": "Î•Ī€ÎšĪ„Ī…Ī‡ÎŽĪ‚ ÎąÎģÎģÎąÎŗÎŽ ÎŋÎŊΌÎŧÎąĪ„ÎŋĪ‚", "change_password": "ΑÎģÎģÎąÎŗÎŽ ÎšĪ‰Î´ÎšÎēÎŋĪ", "change_password_description": "Î‘Ī…Ī„ÎŽ ÎĩίÎŊιΚ ÎŽ Ρ Ī€ĪĪŽĪ„Îˇ ΆÎŋ΁ÎŦ Ī€ÎŋĪ… ĪƒĪ…ÎŊδέÎĩĪƒĪ„Îĩ ĪƒĪ„Îŋ ĪƒĪĪƒĪ„ÎˇÎŧÎą ÎŽ Î­Ī‡ÎĩΚ ÎŗÎ¯ÎŊÎĩΚ ÎąÎ¯Ī„ÎˇÎŧÎą ÎŗÎšÎą ÎąÎģÎģÎąÎŗÎŽ Ī„ÎŋĪ… ÎēĪ‰Î´ÎšÎēÎŋĪ ĪƒÎąĪ‚. Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎšĪƒÎŦÎŗÎĩĪ„Îĩ Ī„ÎŋÎŊ ÎŊέÎŋ ÎēĪ‰Î´ÎšÎēΌ, Ī€ÎąĪÎąÎēÎŦ΄Ή.", "change_password_form_confirm_password": "Î•Ī€ÎšÎ˛ÎĩÎ˛ÎąÎ¯Ī‰ĪƒÎˇ ÎšĪ‰Î´ÎšÎēÎŋĪ", @@ -621,7 +609,6 @@ "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 ÎēιΚ ÎąĪ†ÎŋĪ Î­Ī‡ÎŋĪ…ÎŊ ÎąĪ€ÎŋθΡÎēÎĩĪ…Ī„Îĩί ΌÎģÎą Ī„Îą ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ. Η δΚιδΚÎēÎąĪƒÎ¯Îą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą Î´ÎšÎąĪÎēÎ­ĪƒÎĩΚ ÎŧÎĩĪÎšÎēÎŦ ÎģÎĩ΀΄ÎŦ.", @@ -661,11 +648,13 @@ "confirm_keep_this_delete_others": "ΌÎģÎą Ī„Îą ÎŦÎģÎģÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī„ÎˇĪ‚ ĪƒĪ„ÎŋÎ¯Î˛ÎąĪ‚ θι Î´ÎšÎąÎŗĪÎąĪ†ÎŋĪÎŊ, ÎĩÎēĪ„ĪŒĪ‚ ÎąĪ€ĪŒ ÎąĪ…Ī„ĪŒ Ī„Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ. Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ĪƒĪ…ÎŊÎĩĪ‡Î¯ĪƒÎĩĪ„Îĩ;", "confirm_new_pin_code": "Î•Ī€ÎšÎ˛ÎĩÎ˛ÎąÎ¯Ī‰ĪƒÎˇ ÎŊέÎŋĪ… ÎēĪ‰Î´ÎšÎēÎŋĪ PIN", "confirm_password": "Î•Ī€ÎšÎ˛ÎĩÎ˛ÎąÎ¯Ī‰ĪƒÎˇ ÎēĪ‰Î´ÎšÎēÎŋĪ", + "confirm_tag_face": "ΘέÎģÎĩĪ„Îĩ ÎŊÎą ÎĩĪ€ÎšĪƒÎˇÎŧÎŦÎŊÎĩĪ„Îĩ ÎąĪ…Ī„ĪŒ Ī„Îŋ Ī€ĪĪŒĪƒĪ‰Ī€Îŋ Ή΂ {name};", + "confirm_tag_face_unnamed": "ΘέÎģÎĩĪ„Îĩ ÎŊÎą ÎĩĪ€ÎšĪƒÎˇÎŧÎŦÎŊÎĩĪ„Îĩ ÎąĪ…Ī„ĪŒ Ī„Îŋ Ī€ĪĪŒĪƒĪ‰Ī€Îŋ;", + "connected_device": "ÎŖĪ…ÎŊδÎĩδÎĩÎŧέÎŊΡ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ", "connected_to": "ÎŖĪ…ÎŊδÎĩδÎĩÎŧέÎŊÎŋ ÎŧÎĩ", "contain": "ΠÎĩĪÎšÎ­Ī‡ÎĩΚ", "context": "ÎŖĪ…ÎŧĪ†ĪÎąÎļΌÎŧÎĩÎŊÎą", "continue": "ÎŖĪ…ÎŊÎ­Ī‡ÎĩΚι", - "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": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ÎąĪ€ĪŒ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ", @@ -714,6 +703,7 @@ "daily_title_text_date": "Ε, MMM dd", "daily_title_text_date_year": "Ε, MMM dd, yyyy", "dark": "ÎŖÎēÎŋĪĪÎŋ", + "darkTheme": "ΕÎŊÎąÎģÎģÎąÎŗÎŽ ΃ÎēÎŋĪĪÎŋĪ… θέÎŧÎąĪ„ÎŋĪ‚", "date_after": "ΗÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ÎŧÎĩĪ„ÎŦ", "date_and_time": "ΗÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ÎēιΚ ĪŽĪÎą", "date_before": "ΗÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą Ī€ĪÎšÎŊ", @@ -761,6 +751,7 @@ "disallow_edits": "Î‘Ī€ÎąÎŗĪŒĪÎĩĪ…ĪƒÎˇ ÎĩĪ€ÎĩΞÎĩĪÎŗÎąĪƒÎšĪŽÎŊ", "discord": "ΠÎģÎąĪ„Ī†ĪŒĪÎŧÎą Discord", "discover": "ΑÎŊÎ¯Ī‡ÎŊÎĩĪ…ĪƒÎˇ", + "discovered_devices": "Î”ÎšÎąÎ¸Î­ĪƒÎšÎŧÎĩĪ‚ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚", "dismiss_all_errors": "Î ÎąĪÎŦβÎģÎĩĪˆÎˇ ΌÎģΉÎŊ ΄ΉÎŊ ĪƒĪ†ÎąÎģÎŧÎŦ΄ΉÎŊ", "dismiss_error": "Î ÎąĪÎŦβÎģÎĩĪˆÎˇ ĪƒĪ†ÎŦÎģÎŧÎąĪ„ÎŋĪ‚", "display_options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ÎĩÎŧΆÎŦÎŊÎšĪƒÎˇĪ‚", @@ -776,7 +767,6 @@ "download_enqueue": "Η ÎģÎŽĪˆÎˇ Ī„Î­Î¸ÎˇÎēÎĩ ΃Îĩ Îŋ΅΁ÎŦ", "download_error": "ÎŖĪ†ÎŦÎģÎŧÎą ÎģÎŽĪˆÎˇĪ‚", "download_failed": "Η ÎģÎŽĪˆÎˇ ÎąĪ€Î­Ī„Ī…Ī‡Îĩ", - "download_filename": "ÎąĪĪ‡ÎĩίÎŋ: {filename}", "download_finished": "Η ÎģÎŽĪˆÎˇ ÎŋÎģÎŋÎēÎģÎˇĪĪŽÎ¸ÎˇÎēÎĩ", "download_include_embedded_motion_videos": "ΕÎŊĪƒĪ‰ÎŧÎąĪ„Ī‰ÎŧέÎŊÎą Î˛Î¯ÎŊĪ„ÎĩÎŋ", "download_include_embedded_motion_videos_description": "ÎŖĪ…ÎŧĪ€ÎĩĪÎšÎģÎŦβÎĩĪ„Îĩ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩÎŋ Ī€ÎŋĪ… ÎĩίÎŊιΚ ÎĩÎŊĪƒĪ‰ÎŧÎąĪ„Ī‰ÎŧέÎŊÎą ΃Îĩ ÎēΚÎŊÎŋĪÎŧÎĩÎŊÎĩĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ Ή΂ ΞÎĩĪ‡Ī‰ĪÎšĪƒĪ„ĪŒ ÎąĪĪ‡ÎĩίÎŋ", @@ -821,6 +811,7 @@ "editor_close_without_save_title": "ΚÎģÎĩÎ¯ĪƒÎšÎŧÎŋ ÎĩĪ€ÎĩΞÎĩĪÎŗÎąĪƒĪ„ÎŽ;", "editor_crop_tool_h2_aspect_ratios": "ΑÎŊÎąÎģÎŋÎŗÎ¯ÎĩĪ‚ Î´ÎšÎąĪƒĪ„ÎŦ΃ÎĩΉÎŊ", "editor_crop_tool_h2_rotation": "ΠÎĩĪÎšĪƒĪ„ĪÎŋĪ†ÎŽ", + "email": "Email", "email_notifications": "ΕιδÎŋĪ€ÎŋÎšÎŽĪƒÎĩÎšĪ‚ email", "empty_folder": "Î‘Ī…Ī„ĪŒĪ‚ Îŋ ΆÎŦÎēÎĩÎģÎŋĪ‚ ÎĩίÎŊιΚ ÎēÎĩÎŊĪŒĪ‚", "empty_trash": "ΆδÎĩÎšÎąĪƒÎŧÎą ÎēÎŦδÎŋĪ… ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", @@ -838,6 +829,7 @@ "error_delete_face": "ÎŖĪ†ÎŦÎģÎŧÎą Î´ÎšÎąÎŗĪÎąĪ†ÎŽĪ‚ ΀΁ÎŋĪƒĪŽĪ€ÎŋĪ… ÎąĪ€ĪŒ Ī„Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ", "error_loading_image": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„Îˇ Ī†ĪŒĪĪ„Ī‰ĪƒÎˇ Ī„ÎˇĪ‚ ÎĩΚÎēΌÎŊÎąĪ‚", "error_saving_image": "ÎŖĪ†ÎŦÎģÎŧÎą: {error}", + "error_tag_face_bounding_box": "ÎŖĪ†ÎŦÎģÎŧÎą ÎĩĪ€ÎšĪƒÎŽÎŧÎąÎŊĪƒÎˇĪ‚ ΀΁ÎŋĪƒĪŽĪ€ÎŋĪ… - δÎĩÎŊ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą ÎģÎˇĪ†Î¸ÎŋĪÎŊ ÎŋΚ ĪƒĪ…ÎŊĪ„ÎĩĪ„ÎąÎŗÎŧέÎŊÎĩĪ‚ Ī„ÎŋĪ… Ī€ÎģÎąÎšĪƒÎ¯ÎŋĪ… ÎŋĪÎšÎŋÎ¸Î­Ī„ÎˇĪƒÎˇĪ‚", "error_title": "ÎŖĪ†ÎŦÎģÎŧÎą - ΚÎŦĪ„Îš Ī€ÎŽÎŗÎĩ ĪƒĪ„ĪÎąÎ˛ÎŦ", "errors": { "cannot_navigate_next_asset": "ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ Ī€ÎģÎŋÎŽÎŗÎˇĪƒÎˇ ĪƒĪ„Îŋ ÎĩĪ€ĪŒÎŧÎĩÎŊÎŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ", @@ -850,7 +842,6 @@ "cant_get_number_of_comments": "ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎąÎŊÎŦÎēĪ„ÎˇĪƒÎˇ Ī„ÎŋĪ… ÎąĪÎšÎ¸ÎŧÎŋĪ ΄ΉÎŊ ĪƒĪ‡ÎŋÎģÎ¯Ī‰ÎŊ", "cant_search_people": "Î‘Î´ĪÎŊÎąĪ„Îˇ Ρ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎąĪ„ĪŒÎŧΉÎŊ", "cant_search_places": "ΔÎĩÎŊ ÎŧĪ€Îŋ΁ÎĩÎ¯Ī„Îĩ ÎŊÎą ÎąÎŊÎąÎļÎˇĪ„ÎŽĪƒÎĩĪ„Îĩ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎĩĪ‚", - "cleared_jobs": "ΕÎēÎēÎąÎ¸ÎąĪÎšĪƒÎŧέÎŊÎĩĪ‚ ÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚ ÎŗÎšÎą: {job}", "error_adding_assets_to_album": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ ΀΁ÎŋĪƒÎ¸ÎŽÎēΡ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "error_adding_users_to_album": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ ΀΁ÎŋĪƒÎ¸ÎŽÎēΡ Ī‡ĪÎˇĪƒĪ„ĪŽÎŊ ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "error_deleting_shared_user": "ÎŖĪ†ÎŦÎģÎŧÎą Î´ÎšÎąÎŗĪÎąĪ†ÎŽĪ‚ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ", @@ -859,7 +850,6 @@ "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": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ÎĩĪ€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯ÎąĪ‚ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋĪ… ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", @@ -878,7 +868,6 @@ "paths_validation_failed": "{paths, plural, one {# Î´ÎšÎąÎ´ĪÎŋÎŧÎŽ} 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": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ΀΁ÎŋĪƒÎ¸ÎŽÎēÎˇĪ‚ ĪƒĪ‡ÎŋÎģίÎŋĪ…", @@ -897,7 +886,6 @@ "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", "unable_to_create_admin_account": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧÎŋĪ Î´ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎŽ", "unable_to_create_api_key": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎĩÎŊĪŒĪ‚ ÎŊέÎŋĪ… ÎēÎģÎĩΚδΚÎŋĪ API", @@ -921,14 +909,9 @@ "unable_to_hide_person": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąĪ€ĪŒÎēĪĪ…ĪˆÎˇĪ‚ Ī„ÎŋĪ… ÎąĪ„ĪŒÎŧÎŋĪ…", "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_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": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎĩĪ€ÎąÎŊÎąÎēÎąĪ„ÎˇÎŗÎŋĪÎšÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ΃Îĩ έÎŊÎą ÎŊέÎŋ ÎŦĪ„ÎŋÎŧÎŋ", @@ -936,11 +919,9 @@ "unable_to_remove_album_users": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą Î´ÎšÎąÎŗĪÎąĪ†ÎŽĪ‚ Ī‡ĪÎˇĪƒĪ„ĪŽÎŊ ÎąĪ€ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "unable_to_remove_api_key": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą Î´ÎšÎąÎŗĪÎąĪ†ÎŽĪ‚ Ī„ÎŋĪ… ÎēÎģÎĩΚδΚÎŋĪ API", "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": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎĩĪ€Î¯ÎģĪ…ĪƒÎˇĪ‚ Ī„ÎŋĪ… Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€ÎŋĪ…", @@ -976,7 +957,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": "ΗÎģΚÎēÎ¯Îą {months} ÎŧÎŽÎŊÎĩĪ‚", "exif_bottom_sheet_person_age_year_months": "ΗÎģΚÎēÎ¯Îą 1 Î­Ī„ÎŋĪ…Ī‚, {months} ÎŧΡÎŊĪŽÎŊ", "exif_bottom_sheet_person_age_years": "ΗÎģΚÎēÎ¯Îą {years}", @@ -1024,6 +1004,8 @@ "folders": "ÎĻÎŦÎēÎĩÎģÎŋΚ", "folders_feature_description": "ΠÎĩĪÎšÎŽÎŗÎˇĪƒÎˇ ĪƒĪ„ÎˇÎŊ ΀΁ÎŋβÎŋÎģÎŽ Ī†ÎąÎēέÎģÎŋĪ… ÎŗÎšÎą Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩÎŋ ĪƒĪ„Îŋ ĪƒĪĪƒĪ„ÎˇÎŧÎą ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ", "forward": "Î ĪÎŋĪ‚ Ī„Îą ÎĩÎŧĪ€ĪĪŒĪ‚", + "gcast_enabled": "ΜÎĩĪ„ÎŦδÎŋĪƒÎˇ Ī€ÎĩĪÎšÎĩ·ÎŋÎŧέÎŊÎŋĪ… Google Cast", + "gcast_enabled_description": "Î‘Ī…Ī„ĪŒ Ī„Îŋ Ī‡ÎąĪÎąÎēĪ„ÎˇĪÎšĪƒĪ„ÎšÎēΌ ΆÎŋĪĪ„ĪŽÎŊÎĩΚ ÎĩÎžĪ‰Ī„ÎĩĪÎšÎēÎŋĪĪ‚ Ī€ĪŒĪÎŋĪ…Ī‚ ÎąĪ€ĪŒ Ī„Îˇ Google ÎŗÎšÎą ÎŊÎą ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎŽĪƒÎĩΚ.", "general": "ΓÎĩÎŊΚÎēÎŦ", "get_help": "Î–ÎˇĪ„ÎŽĪƒĪ„Îĩ βÎŋΎθÎĩΚι", "get_wifiname_error": "ΔÎĩÎŊ ÎŽĪ„ÎąÎŊ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎģÎŽĪˆÎˇ Ī„ÎŋĪ… ÎŋÎŊΌÎŧÎąĪ„ÎŋĪ‚ Wi-Fi. ΒÎĩÎ˛ÎąÎšĪ‰Î¸ÎĩÎ¯Ī„Îĩ ĪŒĪ„Îš Î­Ī‡ÎĩĪ„Îĩ Î´ĪŽĪƒÎĩΚ Ī„ÎšĪ‚ ÎąĪ€ÎąĪÎąÎ¯Ī„ÎˇĪ„ÎĩĪ‚ ÎŦδÎĩΚÎĩĪ‚ ÎēιΚ ĪŒĪ„Îš ÎĩÎ¯ĪƒĪ„Îĩ ĪƒĪ…ÎŊδÎĩδÎĩÎŧέÎŊÎŋΚ ΃Îĩ δίÎē΄΅Îŋ Wi-Fi", @@ -1067,10 +1049,12 @@ "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": "ÎœĪ€Îŋ΁ÎĩÎ¯Ī„Îĩ ÎŊÎą ÎąÎŊÎĩβÎŦ΃ÎĩĪ„Îĩ ÎŧΌÎŊÎŋ 30 ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎēÎŦθÎĩ ΆÎŋ΁ÎŦ, Ī€ÎąĪÎąÎģÎĩÎ¯Ī€ÎĩĪ„ÎąÎš", "host": "ÎĻΚÎģÎŋΞÎĩÎŊÎ¯Îą", "hour": "ÎĪÎą", + "id": "ID", "ignore_icloud_photos": "Î‘ÎŗÎŊÎŋÎŽĪƒĪ„Îĩ Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ iCloud", "ignore_icloud_photos_description": "Οι ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ Ī€ÎŋĪ… ÎĩίÎŊιΚ ÎąĪ€ÎŋθΡÎēÎĩĪ…ÎŧέÎŊÎĩĪ‚ ĪƒĪ„Îŋ iCloud δÎĩÎŊ θι ÎŧÎĩĪ„ÎąĪ†ÎŋĪĪ„Ī‰Î¸ÎŋĪÎŊ ĪƒĪ„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ Immich", "image": "ΕιÎēΌÎŊÎą", @@ -1110,6 +1094,12 @@ "invalid_date_format": "Μη Î­ÎŗÎēĪ…ĪÎˇ ÎŧÎŋĪĪ†ÎŽ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯ÎąĪ‚", "invite_people": "Î ĪĪŒĪƒÎēÎģÎˇĪƒÎˇ Î‘Ī„ĪŒÎŧΉÎŊ", "invite_to_album": "Î ĪĪŒĪƒÎēÎģÎˇĪƒÎˇ ΃Îĩ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "ios_debug_info_fetch_ran_at": "Η ÎąÎŊÎŦÎēĪ„ÎˇĪƒÎˇ ÎĩÎēĪ„ÎĩÎģÎ­ĪƒĪ„ÎˇÎēÎĩ ĪƒĪ„ÎšĪ‚ {dateTime}", + "ios_debug_info_last_sync_at": "ΤÎĩÎģÎĩĪ…Ī„ÎąÎ¯ÎŋĪ‚ ĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚ ĪƒĪ„ÎšĪ‚ {dateTime}", + "ios_debug_info_no_processes_queued": "ΔÎĩÎŊ Ī…Ī€ÎŦ΁·ÎŋĪ…ÎŊ δΚÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚ Ī€ÎąĪÎąĪƒÎēΡÎŊίÎŋĪ… ΃Îĩ Îŋ΅΁ÎŦ", + "ios_debug_info_no_sync_yet": "ΔÎĩÎŊ Î­Ī‡ÎĩΚ ÎĩÎēĪ„ÎĩÎģÎĩĪƒĪ„Îĩί, ÎŗÎšÎą Ī„ÎˇÎŊ ĪŽĪÎą, ÎēÎąÎŧÎ¯Îą ÎĩĪÎŗÎąĪƒÎ¯Îą ĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧÎŋĪ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ", + "ios_debug_info_processes_queued": "{count, plural, one {{count} δΚÎĩĪÎŗÎąĪƒÎ¯Îą Ī€ÎąĪÎąĪƒÎēΡÎŊίÎŋĪ… ΃Îĩ ÎąÎŊÎąÎŧÎŋÎŊÎŽ} other {{count} δΚÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚ Ī€ÎąĪÎąĪƒÎēΡÎŊίÎŋĪ… ΃Îĩ ÎąÎŊÎąÎŧÎŋÎŊÎŽ}}", + "ios_debug_info_processing_ran_at": "Η ÎĩĪ€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ÎĩÎēĪ„ÎĩÎģÎ­ĪƒĪ„ÎˇÎēÎĩ ĪƒĪ„ÎšĪ‚ {dateTime}", "items_count": "{count, plural, one {# ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎŋ} other {# ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎą}}", "jobs": "Î•ĪÎŗÎąĪƒÎ¯ÎĩĪ‚", "keep": "Î”ÎšÎąĪ„ÎŽĪÎˇĪƒÎˇ", @@ -1118,6 +1108,9 @@ "kept_this_deleted_others": "Î”ÎšÎąĪ„ÎˇĪÎŽÎ¸ÎˇÎēÎĩ ÎąĪ…Ī„ĪŒ Ī„Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ ÎēιΚ Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎĩ/ÎēÎąÎŊ {count, plural, one {# ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {# ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}}", "keyboard_shortcuts": "ÎŖĪ…ÎŊĪ„ÎŋÎŧÎĩĪĪƒÎĩÎšĪ‚ Ī€ÎģΡÎē΄΁ÎŋÎģÎŋÎŗÎ¯ÎŋĪ…", "language": "ΓÎģĪŽĪƒĪƒÎą", + "language_no_results_subtitle": "ΔÎŋÎēΚÎŧÎŦĪƒĪ„Îĩ ÎŊÎą ÎąÎģÎģÎŦΞÎĩĪ„Îĩ Ī„ÎŋÎŊ Ό΁Îŋ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚", + "language_no_results_title": "ΔÎĩ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ ÎŗÎģĪŽĪƒĪƒÎĩĪ‚", + "language_search_hint": "ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎŗÎģĪ‰ĪƒĪƒĪŽÎŊ...", "language_setting_description": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ Ī„Îˇ ÎŗÎģĪŽĪƒĪƒÎą Ī€ÎŋĪ… ΀΁ÎŋĪ„ÎšÎŧÎŦĪ„Îĩ", "last_seen": "ΤÎĩÎģÎĩĪ…Ī„ÎąÎ¯Îą ΀΁ÎŋβÎŋÎģÎŽ", "latest_version": "ΤÎĩÎģÎĩĪ…Ī„ÎąÎ¯Îą ΈÎēδÎŋĪƒÎˇ", @@ -1143,6 +1136,8 @@ "list": "Î›Î¯ĪƒĪ„Îą", "loading": "ÎĻĪŒĪĪ„Ī‰ĪƒÎˇ", "loading_search_results_failed": "Η Ī†ĪŒĪĪ„Ī‰ĪƒÎˇ ÎąĪ€ÎŋĪ„ÎĩÎģÎĩ΃ÎŧÎŦ΄ΉÎŊ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚ ÎąĪ€Î­Ī„Ī…Ī‡Îĩ", + "local_asset_cast_failed": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎŧÎĩĪ„ÎŦδÎŋĪƒÎˇĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ… Ī€ÎŋĪ… δÎĩÎŊ Î­Ī‡ÎĩΚ ÎąÎŊέβÎĩΚ ĪƒĪ„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ", + "local_network": "ΤÎŋĪ€ÎšÎēΌ δίÎē΄΅Îŋ", "local_network_sheet_info": "Η ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ θι ĪƒĪ…ÎŊδÎĩθÎĩί ÎŧÎĩ Ī„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎŧÎ­ĪƒĪ‰ ÎąĪ…Ī„ÎŋĪ Ī„ÎŋĪ… URL ĪŒĪ„ÎąÎŊ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„ÎąÎš Ī„Îŋ ÎēιθÎŋĪÎšĪƒÎŧέÎŊÎŋ δίÎē΄΅Îŋ Wi-Fi", "location_permission": "ΆδÎĩΚι Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", "location_permission_content": "Για ÎŊÎą Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚΡθÎĩί Ρ ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯Îą ÎąĪ…Ī„ĪŒÎŧÎąĪ„ÎˇĪ‚ ÎĩÎŊÎąÎģÎģÎąÎŗÎŽĪ‚, Ī„Îŋ Immich ·΁ÎĩΚÎŦÎļÎĩĪ„ÎąÎš ÎŦδÎĩΚι ÎŗÎšÎą Ī„ÎˇÎŊ ÎąÎēĪÎšÎ˛ÎŽ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą Ī„ÎˇĪ‚ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽĪ‚ ĪŽĪƒĪ„Îĩ ÎŊÎą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą δΚιβÎŦÎļÎĩΚ Ī„Îŋ ΌÎŊÎŋÎŧÎą Ī„ÎŋĪ… Ī„ĪÎ­Ī‡ÎŋÎŊĪ„ÎŋĪ‚ δΚÎēĪ„ĪÎŋĪ… Wi-Fi", @@ -1151,8 +1146,11 @@ "location_picker_latitude_hint": "Î•ÎšĪƒÎąÎŗÎŦÎŗÎĩĪ„Îĩ Ī„Îŋ ÎŗÎĩĪ‰ÎŗĪÎąĪ†ÎšÎēΌ Ī€ÎģÎŦĪ„ÎŋĪ‚ ĪƒÎąĪ‚ ÎĩÎ´ĪŽ", "location_picker_longitude_error": "Î•ÎšĪƒÎąÎŗÎŦÎŗÎĩĪ„Îĩ έÎŊÎą Î­ÎŗÎē΅΁Îŋ ÎŗÎĩĪ‰ÎŗĪÎąĪ†ÎšÎēΌ ÎŧÎŽÎēÎŋĪ‚", "location_picker_longitude_hint": "Î•ÎšĪƒÎąÎŗÎŦÎŗÎĩĪ„Îĩ ÎĩÎ´ĪŽ Ī„Îŋ ÎŗÎĩĪ‰ÎŗĪÎąĪ†ÎšÎēΌ ĪƒÎąĪ‚ ÎŧÎŽÎēÎŋĪ‚", + "lock": "ΚÎģÎĩÎ¯Î´Ī‰ÎŧÎą", + "locked_folder": "ΚÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋĪ‚ ΆÎŦÎēÎĩÎģÎŋĪ‚", "log_out": "Î‘Ī€ÎŋĪƒĪÎŊδÎĩĪƒÎˇ", "log_out_all_devices": "Î‘Ī€ÎŋĪƒĪÎŊδÎĩĪƒÎˇ ÎąĪ€ĪŒ ΌÎģÎĩĪ‚ Ī„ÎšĪ‚ ÎŖĪ…ĪƒÎēÎĩĪ…Î­Ī‚", + "logged_in_as": "ÎŖĪ…ÎŊδÎĩδÎĩÎŧέÎŊÎŋĪ‚ Ή΂ {user}", "logged_out_all_devices": "ΌÎģÎĩĪ‚ ÎŋΚ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚ ÎąĪ€ÎŋĪƒĪ…ÎŊδέθΡÎēÎąÎŊ", "logged_out_device": "Î‘Ī€ÎŋĪƒĪ…ÎŊδÎĩδÎĩÎŧέÎŊΡ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ", "login": "Î•Î¯ĪƒÎŋδÎŋĪ‚", @@ -1184,7 +1182,7 @@ "look": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ", "loop_videos": "Î•Ī€ÎąÎŊÎŦÎģÎˇĪˆÎˇ Î˛Î¯ÎŊĪ„ÎĩÎŋ", "loop_videos_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„ÎˇÎŊ ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îˇ ÎĩĪ€ÎąÎŊÎŦÎģÎˇĪˆÎˇ ÎĩÎŊĪŒĪ‚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ĪƒĪ„Îŋ Ī€ĪĪŒÎŗĪÎąÎŧÎŧÎą ΀΁ÎŋβÎŋÎģÎŽĪ‚ ÎģÎĩ΀΄ÎŋÎŧÎĩ΁ÎĩÎšĪŽÎŊ.", - "main_branch_warning": "Î§ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„Îĩ ÎŧΚι έÎēδÎŋĪƒÎˇ ΃Îĩ ÎąÎŊÎŦĪ€Ī„Ī…ÎžÎˇÎ‡ ĪƒĪ…ÎŊÎšĪƒĪ„ÎŋĪÎŧÎĩ ÎąÎŊÎĩĪ€ÎšĪ†ĪÎģÎąÎēĪ„Îą Ī„Îˇ Ī‡ĪÎŽĪƒÎˇ ÎŧÎšÎąĪ‚ ÎĩĪ€Î¯ĪƒÎˇÎŧÎˇĪ‚ έÎēδÎŋĪƒÎˇĪ‚!", + "main_branch_warning": "Î§ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„Îĩ ÎŧΚι έÎēδÎŋĪƒÎˇ ΃Îĩ ÎąÎŊÎŦĪ€Ī„Ī…ÎžÎˇÎ‡ ĪƒĪ…ÎŊÎšĪƒĪ„ÎŋĪÎŧÎĩ ÎąÎŊÎĩĪ€ÎšĪ†ĪÎģÎąÎēĪ„Îą Ī„Îˇ Ī‡ĪÎŽĪƒÎˇ ÎŧÎšÎąĪ‚ Ī„ÎĩÎģΚÎēÎŽĪ‚ έÎēδÎŋĪƒÎˇĪ‚!", "main_menu": "ÎšĪĪÎšÎŋ ÎŧÎĩÎŊÎŋĪ", "make": "ÎšÎąĪ„ÎąĪƒÎēÎĩĪ…ÎąĪƒĪ„ÎŽĪ‚", "manage_shared_links": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Ī‰ÎŊ ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧΉÎŊ", @@ -1246,6 +1244,12 @@ "month": "ΜήÎŊÎąĪ‚", "monthly_title_text_date_format": "ΜΜΜΜ 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": "ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎĩĪ€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą Ī„ÎˇĪ‚ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ÎŧΌÎŊÎŋ ÎŗÎšÎą ÎąÎŊÎŦÎŗÎŊĪ‰ĪƒÎˇ, Ī€ÎąĪÎąÎģÎĩÎ¯Ī€ÎĩĪ„ÎąÎš", @@ -1260,6 +1264,8 @@ "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": "Τι ÎŊÎĩĪŒĪ„ÎĩĪÎą Ī€ĪĪŽĪ„Îą", @@ -1272,13 +1278,16 @@ "no_archived_assets_message": "Î‘ĪĪ‡ÎĩΚÎŋθÎĩĪ„ÎŽĪƒĪ„Îĩ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎŗÎšÎą ÎŊÎą Ī„Îą ÎąĪ€ÎŋÎēĪĪĪˆÎĩĪ„Îĩ ÎąĪ€ĪŒ Ī„ÎˇÎŊ Î ĪÎŋβÎŋÎģÎŽ ÎĻΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ", "no_assets_message": "ΚΑΝΤΕ ΚΛΙΚ ΓΙΑ ΝΑ Î‘ÎÎ•Î’Î‘ÎŖÎ•Î¤Î• ΤΗΝ ΠΡΩΤΗ ÎŖÎ‘ÎŖ ÎĻΩΤΟΓΡΑÎĻΙΑ", "no_assets_to_show": "ΔÎĩÎŊ Ī…Ī€ÎŦ΁·ÎŋĪ…ÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ΀΁ÎŋĪ‚ ÎĩÎŧΆÎŦÎŊÎšĪƒÎˇ", + "no_cast_devices_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": "ΔÎŋÎēΚÎŧÎŦĪƒĪ„Îĩ έÎŊÎą ĪƒĪ…ÎŊĪŽÎŊĪ…ÎŧÎŋ ÎŽ Ī€ÎšÎŋ ÎŗÎĩÎŊΚÎēÎŽ ÎģέΞΡ-ÎēÎģÎĩΚδί", @@ -1287,6 +1296,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": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ΕιδÎŋĪ€ÎŋÎšÎŽĪƒÎĩΉÎŊ", @@ -1294,17 +1304,18 @@ "notification_toggle_setting_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎĩΚδÎŋĪ€ÎŋÎšÎŽĪƒÎĩΉÎŊ ÎŧÎ­ĪƒĪ‰ email", "notifications": "ΕιδÎŋĪ€ÎŋÎšÎŽĪƒÎĩÎšĪ‚", "notifications_setting_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ÎĩΚδÎŋĪ€ÎŋÎšÎŽĪƒÎĩΉÎŊ", + "oauth": "OAuth", "official_immich_resources": "Î•Ī€Î¯ĪƒÎˇÎŧÎŋΚ Î ĪŒĪÎŋΚ Ī„ÎŋĪ… Immich", "offline": "ΕÎēĪ„ĪŒĪ‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚", - "offline_paths": "Î”ÎšÎąÎ´ĪÎŋÎŧÎ­Ī‚ ÎĩÎēĪ„ĪŒĪ‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚", - "offline_paths_description": "Î‘Ī…Ī„ÎŦ Ī„Îą ÎąĪ€ÎŋĪ„ÎĩÎģÎ­ĪƒÎŧÎąĪ„Îą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ÎŋΆÎĩίÎģÎŋÎŊĪ„ÎąÎš ĪƒĪ„Îˇ ÎŧΡ ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îˇ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ Ī€ÎŋĪ… δÎĩÎŊ ÎąĪ€ÎŋĪ„ÎĩÎģÎŋĪÎŊ ÎŧÎ­ĪÎŋĪ‚ ÎŧÎšÎąĪ‚ ÎĩÎžĪ‰Ī„ÎĩĪÎšÎēÎŽĪ‚ βΚβÎģΚÎŋθΎÎēÎˇĪ‚.", "ok": "ÎˆÎŗÎšÎŊÎĩ", "oldest_first": "Τι Ī€ÎąÎģÎąÎšĪŒĪ„ÎĩĪÎą Ī€ĪĪŽĪ„Îą", "on_this_device": "ÎŖÎĩ ÎąĪ…Ī„ÎŽ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ", "onboarding": "ÎŸÎ´ÎˇÎŗĪŒĪ‚ ÎĩÎēÎēίÎŊÎˇĪƒÎˇĪ‚", - "onboarding_privacy_description": "Οι Ī€ÎąĪÎąÎēÎŦ΄Ή (΀΁ÎŋÎąÎšĪÎĩĪ„ÎšÎēÎ­Ī‚) ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯ÎĩĪ‚ Î˛ÎąĪƒÎ¯ÎļÎŋÎŊĪ„ÎąÎš ΃Îĩ ÎĩÎžĪ‰Ī„ÎĩĪÎšÎēÎ­Ī‚ Ī…Ī€ÎˇĪÎĩĪƒÎ¯ÎĩĪ‚ ÎēιΚ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡθÎŋĪÎŊ ÎąÎŊÎŦ Ī€ÎŦĪƒÎą ĪƒĪ„ÎšÎŗÎŧÎŽ ÎąĪ€ĪŒ Ī„ÎšĪ‚ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ Î´ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇĪ‚.", + "onboarding_locale_description": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ Ī„ÎˇÎŊ ÎŗÎģĪŽĪƒĪƒÎą Ī€ÎŋĪ… ΀΁ÎŋĪ„ÎšÎŧÎŦĪ„Îĩ. ÎœĪ€Îŋ΁ÎĩÎ¯Ī„Îĩ ÎŊÎą Ī„ÎˇÎŊ ÎąÎģÎģÎŦΞÎĩĪ„Îĩ ÎąĪÎŗĪŒĪ„ÎĩĪÎą ÎąĪ€ĪŒ Ī„ÎšĪ‚ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚.", + "onboarding_privacy_description": "Οι Ī€ÎąĪÎąÎēÎŦ΄Ή (΀΁ÎŋÎąÎšĪÎĩĪ„ÎšÎēÎ­Ī‚) ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯ÎĩĪ‚ Î˛ÎąĪƒÎ¯ÎļÎŋÎŊĪ„ÎąÎš ΃Îĩ ÎĩÎžĪ‰Ī„ÎĩĪÎšÎēÎ­Ī‚ Ī…Ī€ÎˇĪÎĩĪƒÎ¯ÎĩĪ‚ ÎēιΚ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡθÎŋĪÎŊ ÎąÎŊÎŦ Ī€ÎŦĪƒÎą ĪƒĪ„ÎšÎŗÎŧÎŽ ÎąĪ€ĪŒ Ī„ÎšĪ‚ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚.", + "onboarding_server_welcome_description": "Î‘Ī‚ ΞÎĩÎēΚÎŊÎŽĪƒÎŋĪ…ÎŧÎĩ ÎŧÎĩ ÎŧÎĩĪÎšÎēÎ­Ī‚ ĪƒĪ…ÎŊÎˇÎ¸ÎšĪƒÎŧέÎŊÎĩĪ‚ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚.", "onboarding_theme_description": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ έÎŊÎą θέÎŧÎą Ī‡ĪĪŽÎŧÎąĪ„ÎŋĪ‚ ÎŗÎšÎą Ī„Îŋ ΀΁ÎŋĪ†Î¯Îģ ĪƒÎąĪ‚. ÎœĪ€Îŋ΁ÎĩÎ¯Ī„Îĩ ÎŊÎą Ī„Îŋ ÎąÎģÎģÎŦΞÎĩĪ„Îĩ ÎąĪÎŗĪŒĪ„ÎĩĪÎą ĪƒĪ„ÎšĪ‚ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ĪƒÎąĪ‚.", - "onboarding_welcome_description": "Î‘Ī‚ ĪĪ…Î¸ÎŧÎ¯ĪƒÎŋĪ…ÎŧÎĩ Ī„Îŋ ΀΁ÎŋĪ†Î¯Îģ ĪƒÎąĪ‚ ÎŧÎĩ ÎŋĪÎšĪƒÎŧέÎŊÎĩĪ‚ ÎēÎŋΚÎŊÎ­Ī‚ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚.", + "onboarding_user_welcome_description": "Î‘Ī‚ ΞÎĩÎēΚÎŊÎŽĪƒÎŋĪ…ÎŧÎĩ!", "onboarding_welcome_user": "ΚαÎģĪ‰ĪƒĪŒĪÎšĪƒÎĩĪ‚, {user}", "online": "ÎŖÎĩ ĪƒĪÎŊδÎĩĪƒÎˇ", "only_favorites": "ÎœĪŒÎŊÎŋ ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą", @@ -1361,6 +1372,8 @@ "permanently_delete_assets_prompt": "Î•Î¯ĪƒĪ„Îĩ βέβιΚÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩĪ„Îĩ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ {count, plural, one {ÎąĪ…Ī„ĪŒ Ī„Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ;} other {ÎąĪ…Ī„ÎŦ Ī„Îą # ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą;}} Î‘Ī…Ī„ĪŒ θι {count, plural, one {Ī„Îŋ} other {Ī„Îą}} ÎąĪ†ÎąÎšĪÎ­ĪƒÎĩΚ ÎĩĪ€Î¯ĪƒÎˇĪ‚ ÎąĪ€ĪŒ Ī„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ĪƒĪ„Îą ÎŋĪ€ÎŋÎ¯Îą {count, plural, one {ÎąÎŊÎŽÎēÎĩΚ} other {ÎąÎŊÎŽÎēÎŋĪ…ÎŊ}} .", "permanently_deleted_asset": "ÎŸĪÎšĪƒĪ„ÎšÎēÎŦ Î´ÎšÎąÎŗĪÎąÎŧÎŧέÎŊÎŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ", "permanently_deleted_assets_count": "ÎŸĪÎšĪƒĪ„ÎšÎēÎŽ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ {count, plural, one {# ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ…} other {# ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ}}", + "permission": "ΆδÎĩΚι", + "permission_empty": "Η ÎŦδÎĩΚÎŦ ΃ÎŋĪ… δÎĩÎŊ Ī€ĪÎ­Ī€ÎĩΚ ÎŊÎą ÎĩίÎŊιΚ ÎēÎĩÎŊÎŽ", "permission_onboarding_back": "Î Î¯ĪƒĪ‰", "permission_onboarding_continue_anyway": "ÎŖĪ…ÎŊÎ­Ī‡ÎĩΚι", "permission_onboarding_get_started": "ΞÎĩÎēΚÎŊÎŽĪƒĪ„Îĩ", @@ -1378,6 +1391,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, one {{count} ΤÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą} other {{count} ΤÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎĩĪ‚}}", @@ -1385,6 +1402,7 @@ "play_memories": "ΑÎŊÎąĪ€ÎąĪÎąÎŗĪ‰ÎŗÎŽ ÎąÎŊÎąÎŧÎŊÎŽĪƒÎĩΉÎŊ", "play_motion_photo": "ΑÎŊÎąĪ€ÎąĪÎąÎŗĪ‰ÎŗÎŽ ΚιÎŊÎŋĪÎŧÎĩÎŊÎˇĪ‚ ÎĻΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎąĪ‚", "play_or_pause_video": "ΑÎŊÎąĪ€ÎąĪÎąÎŗĪ‰ÎŗÎŽ ÎŽ Ī€ÎąĪĪƒÎˇ Î˛Î¯ÎŊĪ„ÎĩÎŋ", + "please_auth_to_access": "Î ÎąĪÎąÎēÎąÎģĪŽ Ī€ÎšĪƒĪ„ÎŋĪ€ÎŋΚΡθÎĩÎ¯Ī„Îĩ ÎŗÎšÎą ÎŊÎą ÎąĪ€ÎŋÎēĪ„ÎŽĪƒÎĩĪ„Îĩ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ", "port": "Î˜ĪĪÎą", "preferences_settings_subtitle": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„ÎšĪ‚ ΀΁ÎŋĪ„ÎšÎŧÎŽĪƒÎĩÎšĪ‚ Ī„ÎˇĪ‚ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚", "preferences_settings_title": "Î ĪÎŋĪ„ÎšÎŧÎŽĪƒÎĩÎšĪ‚", @@ -1392,13 +1410,18 @@ "preview": "Î ĪÎŋÎĩĪ€ÎšĪƒÎēĪŒĪ€ÎˇĪƒÎˇ", "previous": "Î ĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊÎŋ", "previous_memory": "Î ĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊΡ ÎąÎŊÎŦÎŧÎŊÎˇĪƒÎˇ", - "previous_or_next_photo": "Î ĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊΡ ÎŽ ÎĩĪ€ĪŒÎŧÎĩÎŊΡ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą", + "previous_or_next_day": "ΗÎŧÎ­ĪÎą Îŧ΀΁ÎŋĪƒĪ„ÎŦ/Ī€Î¯ĪƒĪ‰", + "previous_or_next_month": "ΜήÎŊÎąĪ‚ Îŧ΀΁ÎŋĪƒĪ„ÎŦ/Ī€Î¯ĪƒĪ‰", + "previous_or_next_photo": "ÎĻΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą Îŧ΀΁ÎŋĪƒĪ„ÎŦ/Ī€Î¯ĪƒĪ‰", + "previous_or_next_year": "ÎˆĪ„ÎŋĪ‚ Îŧ΀΁ÎŋĪƒĪ„ÎŦ/Ī€Î¯ĪƒĪ‰", "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}", @@ -1473,9 +1496,12 @@ "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": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎąĪ‚ ÎąĪ€ĪŒ Ī„ÎˇÎŊ ÎąÎŊÎŦÎŧÎŊÎˇĪƒÎˇ", + "remove_tag": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ ÎĩĪ„ÎšÎēÎ­Ī„ÎąĪ‚", "remove_url": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ ÎŖĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "remove_user": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ Ī‡ĪÎŽĪƒĪ„Îˇ", "removed_api_key": "Î‘Ī†ÎąÎšĪÎ­Î¸ÎˇÎēÎĩ Ī„Îŋ API Key: {name}", @@ -1496,6 +1522,7 @@ "reset": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ", "reset_password": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ÎēĪ‰Î´ÎšÎēÎŋĪ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚", "reset_people_visibility": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ΀΁ÎŋβÎŋÎģÎŽĪ‚ ÎąĪ„ĪŒÎŧΉÎŊ", + "reset_pin_code": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ÎēĪ‰Î´ÎšÎēÎŋĪ PIN", "reset_to_default": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ĪƒĪ„ÎšĪ‚ ΀΁ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎ­Ī‚", "resolve_duplicates": "Î•Ī€Î¯ÎģĪ…ĪƒÎˇ Î´ÎšĪ€ÎģÎŋĪ„ĪĪ€Ī‰ÎŊ", "resolved_all_duplicates": "Î•Ī€ÎšÎģĪÎ¸ÎˇÎēÎąÎŊ ΌÎģÎą Ī„Îą Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą", @@ -1581,6 +1608,7 @@ "select_album_cover": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ ÎĩÎžĪŽĪ†Ī…ÎģÎģÎŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "select_all": "Î•Ī€ÎšÎģÎŋÎŗÎŽ ΌÎģΉÎŊ", "select_all_duplicates": "Î•Ī€ÎšÎģÎŋÎŗÎŽ ΌÎģΉÎŊ ΄ΉÎŊ Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Ī‰ÎŊ", + "select_all_in": "Î•Ī€ÎšÎģÎŋÎŗÎŽ ΌÎģΉÎŊ ĪƒĪ„Îŋ {group}", "select_avatar_color": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ Ī‡ĪĪŽÎŧÎą avatar", "select_face": "Î•Ī€ÎšÎģÎŋÎŗÎŽ ΀΁ÎŋĪƒĪŽĪ€ÎŋĪ…", "select_featured_photo": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą ÎŗÎšÎą ΀΁ÎŋβÎŋÎģÎŽ", @@ -1588,6 +1616,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": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎŦÎģĪ€ÎŋĪ…Îŧ", @@ -1600,6 +1629,7 @@ "server_info_box_server_url": "URL δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ", "server_offline": "ΔιαÎēÎŋÎŧÎšĪƒĪ„ÎŽĪ‚ ΕÎēĪ„ĪŒĪ‚ ÎŖĪÎŊδÎĩĪƒÎˇĪ‚", "server_online": "ΔιαÎēÎŋÎŧÎšĪƒĪ„ÎŽĪ‚ ÎŖÎĩ ÎŖĪÎŊδÎĩĪƒÎˇ", + "server_privacy": "Î‘Ī€ĪŒĪĪÎˇĪ„Îŋ ΔιαÎēÎŋÎŧÎšĪƒĪ„ÎŽ", "server_stats": "ÎŖĪ„ÎąĪ„ÎšĪƒĪ„ÎšÎēÎŦ ΔιαÎēÎŋÎŧÎšĪƒĪ„ÎŽ", "server_version": "ΈÎēδÎŋĪƒÎˇ ΔιαÎēÎŋÎŧÎšĪƒĪ„ÎŽ", "set": "ÎŸĪÎšĪƒÎŧĪŒĪ‚", @@ -1609,6 +1639,7 @@ "set_date_of_birth": "ÎŸĪÎšĪƒÎŧĪŒĪ‚ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯ÎąĪ‚ ÎŗÎ­ÎŊÎŊÎˇĪƒÎˇĪ‚", "set_profile_picture": "ÎŸĪÎšĪƒÎŧĪŒĪ‚ ÎĩΚÎēΌÎŊÎąĪ‚ ΀΁ÎŋĪ†Î¯Îģ", "set_slideshow_to_fullscreen": "ÎŸĪÎ¯ĪƒĪ„Îĩ Ī„ÎˇÎŊ Ī€ÎąĪÎŋĪ…ĪƒÎ¯ÎąĪƒÎˇ ΃Îĩ Ī€ÎģÎŽĪÎˇ ÎŋÎ¸ĪŒÎŊΡ", + "set_stack_primary_asset": "ÎŸĪÎšĪƒÎŧĪŒĪ‚ Ή΂ ÎēĪĪÎšÎŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ", "setting_image_viewer_help": "ΤÎŋ Ī€ĪĪŒÎŗĪÎąÎŧÎŧÎą ΀΁ÎŋβÎŋÎģÎŽĪ‚ ÎģÎĩ΀΄ÎŋÎŧÎĩ΁ÎĩÎšĪŽÎŊ ΆÎŋĪĪ„ĪŽÎŊÎĩΚ Ī€ĪĪŽĪ„Îą Ī„Îˇ ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†Î¯Îą, ĪƒĪ„Îˇ ĪƒĪ…ÎŊÎ­Ī‡ÎĩΚι ΆÎŋĪĪ„ĪŽÎŊÎĩΚ Ī„ÎˇÎŊ ΀΁ÎŋÎĩĪ€ÎšĪƒÎēĪŒĪ€ÎˇĪƒÎˇ ÎŧÎĩĪƒÎąÎ¯ÎŋĪ… ÎŧÎĩÎŗÎ­Î¸ÎŋĪ…Ī‚ (ÎąÎŊ ÎĩίÎŊιΚ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ), Ī„Î­ÎģÎŋĪ‚ ΆÎŋĪĪ„ĪŽÎŊÎĩΚ Ī„Îŋ Ī€ĪĪ‰Ī„ĪŒĪ„Ī…Ī€Îŋ (ÎąÎŊ ÎĩίÎŊιΚ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ).", "setting_image_viewer_original_subtitle": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„Îˇ Ī†ĪŒĪĪ„Ī‰ĪƒÎˇ Ī„ÎˇĪ‚ Ī€ĪĪ‰Ī„ĪŒĪ„Ī…Ī€ÎˇĪ‚ ÎĩΚÎēΌÎŊÎąĪ‚ Ī€ÎģÎŽĪÎŋĪ…Ī‚ ÎąÎŊÎŦÎģĪ…ĪƒÎˇĪ‚ (ÎŧÎĩÎŗÎŦÎģΡ!). Î‘Ī€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ ÎŗÎšÎą ÎŊÎą ÎŧÎĩÎšĪŽĪƒÎĩĪ„Îĩ Ī„Îˇ Ī‡ĪÎŽĪƒÎˇ δÎĩδÎŋÎŧέÎŊΉÎŊ (Ī„ĪŒĪƒÎŋ ĪƒĪ„Îŋ δίÎē΄΅Îŋ Ό΃Îŋ ÎēιΚ ĪƒĪ„ÎˇÎŊ ÎēĪĪ…Ī†ÎŽ ÎŧÎŊÎŽÎŧΡ Ī„ÎˇĪ‚ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽĪ‚).", "setting_image_viewer_original_title": "ÎĻĪŒĪĪ„Ī‰ĪƒÎˇ Ī€ĪĪ‰Ī„ĪŒĪ„Ī…Ī€ÎˇĪ‚ ÎĩΚÎēΌÎŊÎąĪ‚", @@ -1617,7 +1648,6 @@ "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": "ÎąÎŧÎ­ĪƒĪ‰Ī‚", @@ -1635,10 +1665,12 @@ "settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚", "settings_require_restart": "Î•Ī€ÎąÎŊÎĩÎēÎēΚÎŊÎŽĪƒĪ„Îĩ Ī„Îŋ Immich ÎŗÎšÎą ÎŊÎą ÎĩĪ†ÎąĪÎŧΌ΃ÎĩĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„Îˇ ĪĪÎ¸ÎŧÎšĪƒÎˇ", "settings_saved": "Οι ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ÎąĪ€ÎŋθΡÎēÎĩĪĪ„ÎˇÎēÎąÎŊ", + "setup_pin_code": "ÎĄĪÎ¸ÎŧÎšĪƒÎˇ ÎēĪ‰Î´ÎšÎēÎŋĪ PIN", "share": "ΚÎŋΚÎŊÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", "share_add_photos": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ", "share_assets_selected": "{count} ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎą", "share_dialog_preparing": "Î ĪÎŋÎĩĪ„ÎŋΚÎŧÎąĪƒÎ¯Îą...", + "share_link": "ÎŖĪÎŊδÎĩ΃ÎŧÎŋĪ‚ ÎēÎŋΚÎŊÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚", "shared": "ÎŖÎĩ ÎēÎŋΚÎŊÎŽ Ī‡ĪÎŽĪƒÎˇ", "shared_album_activities_input_disable": "ΤÎŋ ĪƒĪ‡ĪŒÎģΚÎŋ ÎĩίÎŊιΚ ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ", "shared_album_activity_remove_content": "ΘέÎģÎĩĪ„Îĩ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„Îˇ Î´ĪÎąĪƒĪ„ÎˇĪÎšĪŒĪ„ÎˇĪ„Îą;", @@ -1678,6 +1710,7 @@ "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": "ΚÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋΚ ĪƒĪÎŊδÎĩ΃ÎŧÎŋΚ", @@ -1744,12 +1777,14 @@ "start_date": "Î‘Ī€ĪŒ", "state": "ΝÎŋÎŧĪŒĪ‚", "status": "ÎšÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇ", + "stop_casting": "ΔιαÎēÎŋĪ€ÎŽ ÎŧÎĩĪ„ÎŦδÎŋĪƒÎˇĪ‚", "stop_motion_photo": "ΔιέÎēÎŋΈÎĩ Ī„ÎˇÎŊ ÎĻΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą ΚίÎŊÎˇĪƒÎˇĪ‚", "stop_photo_sharing": "ΔιαÎēÎŋĪ€ÎŽ ÎēÎŋΚÎŊÎŽĪ‚ Ī‡ĪÎŽĪƒÎˇĪ‚ ΄ΉÎŊ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ ĪƒÎąĪ‚;", "stop_photo_sharing_description": "Ο Ī‡ĪÎŽĪƒĪ„ÎˇĪ‚ {partner} δÎĩÎŊ θι Î­Ī‡ÎĩΚ Ī€ÎģέÎŋÎŊ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ ĪƒĪ„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ĪƒÎąĪ‚.", "stop_sharing_photos_with_user": "ΔιαÎēÎŋĪ€ÎŽ ÎēÎŋΚÎŊÎŽĪ‚ Ī‡ĪÎŽĪƒÎˇĪ‚ ΄ΉÎŊ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ ĪƒÎąĪ‚ ÎŧÎĩ ÎąĪ…Ī„ĪŒ Ī„Îŋ Ī‡ĪÎŽĪƒĪ„Îˇ", "storage": "Î§ĪŽĪÎŋĪ‚ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚", "storage_label": "Î•Ī„ÎšÎēÎ­Ī„Îą ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚", + "storage_quota": "ΠÎŋ΃ÎŋĪƒĪ„ĪŒ ÎąĪ€ÎŋθΡÎēÎĩĪ…Ī„ÎšÎēÎŋĪ Ī‡ĪŽĪÎŋĪ…", "storage_usage": "{used} ÎąĪ€ĪŒ {available} ΃Îĩ Ī‡ĪÎŽĪƒÎˇ", "submit": "ÎĨĪ€ÎŋβÎŋÎģÎŽ", "suggestions": "Î ĪÎŋĪ„ÎŦ΃ÎĩÎšĪ‚", @@ -1800,9 +1835,8 @@ "to_parent": "ΜÎĩĪ„ÎąÎ˛ÎĩÎ¯Ī„Îĩ ĪƒĪ„Îŋ ÎŗÎŋÎŊΚÎēΌ ΆÎŦÎēÎĩÎģÎŋ", "to_trash": "ΚÎŦδÎŋĪ‚ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", "toggle_settings": "ΕÎŊÎąÎģÎģÎąÎŗÎŽ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ", - "toggle_theme": "ΕÎŊÎąÎģÎģÎąÎŗÎŽ θέÎŧÎąĪ„ÎŋĪ‚", "total": "ÎŖĪÎŊÎŋÎģÎŋ", - "total_usage": "ÎŖĪ…ÎŊÎŋÎģΚÎēÎŽ Ī‡ĪÎŽĪƒÎˇ", + "total_usage": "ÎŖĪ…ÎŊÎŋÎģΚÎēΡ Ī‡ĪÎˇĪƒÎˇ", "trash": "ΚÎŦδÎŋĪ‚ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", "trash_all": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ΌÎģΉÎŊ", "trash_count": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ {count, number}", @@ -1818,8 +1852,11 @@ "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 {Î‘ĪĪ‡ÎĩΚÎŋθÎĩĪ„ÎŽĪƒÎĩÎšĪ‚ ÎąÎŊÎąÎšĪÎ­Î¸ÎˇÎēÎąÎŊ #}}", + "undo": "ΑÎŊÎąÎ¯ĪÎĩĪƒÎˇ", "unfavorite": "Î‘Ī€ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ ÎąĪ€ĪŒ Ī„Îą ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą", "unhide_person": "ΑÎŊÎąÎ¯ĪÎĩĪƒÎˇ ÎąĪ€ĪŒÎēĪĪ…ĪˆÎˇĪ‚ ÎąĪ„ĪŒÎŧÎŋĪ…", "unknown": "Î†ÎŗÎŊĪ‰ĪƒĪ„Îŋ", @@ -1836,11 +1873,11 @@ "unsaved_change": "Μη ÎąĪ€ÎŋθΡÎēÎĩĪ…ÎŧέÎŊΡ ÎąÎģÎģÎąÎŗÎŽ", "unselect_all": "Î‘Ī€ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ ΌÎģΉÎŊ", "unselect_all_duplicates": "Î‘Ī€ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ ΌÎģΉÎŊ ΄ΉÎŊ Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Ī‰ÎŊ", + "unselect_all_in": "Î‘Ī€ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ ΌÎģΉÎŊ ĪƒĪ„Îŋ {group}", "unstack": "Î‘Ī€ÎŋĪƒĪ„ÎŋÎ¯Î˛ÎąÎžÎˇ", "unstacked_assets_count": "Î‘Ī€ÎŋĪƒĪ„ÎŋΚβÎŦÎžÎąĪ„Îĩ {count, plural, one {# ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {# ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}}", - "untracked_files": "Μη Ī€ÎąĪÎąÎēÎŋÎģÎŋĪ…Î¸ÎŋĪÎŧÎĩÎŊÎą ÎąĪĪ‡ÎĩÎ¯Îą", - "untracked_files_decription": "Î‘Ī…Ī„ÎŦ Ī„Îą ÎąĪĪ‡ÎĩÎ¯Îą δÎĩÎŊ Ī€ÎąĪÎąÎēÎŋÎģÎŋĪ…Î¸ÎŋĪÎŊĪ„ÎąÎš ÎąĪ€ĪŒ Ī„ÎˇÎŊ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ. ÎœĪ€Îŋ΁Îĩί ÎŊÎą ÎĩίÎŊιΚ ÎąĪ€ÎŋĪ„ÎĩÎģÎ­ĪƒÎŧÎąĪ„Îą ÎąĪ€ÎŋĪ„Ī…Ī‡ÎˇÎŧέÎŊΉÎŊ ÎŧÎĩĪ„ÎąÎēΚÎŊÎŽĪƒÎĩΉÎŊ, ÎąĪ€ÎŋĪ„Ī…Ī‡ÎˇÎŧέÎŊÎĩĪ‚ ÎŧÎĩĪ„ÎąĪ†ÎŋĪĪ„ĪŽĪƒÎĩÎšĪ‚ ÎŽ ÎĩÎŊÎąĪ€ÎŋÎŧÎĩίÎŊÎąÎŊĪ„Îą ÎģĪŒÎŗĪ‰ ĪƒĪ†ÎŦÎģÎŧÎąĪ„ÎŋĪ‚", "up_next": "ΑÎēÎŋÎģÎŋĪ…Î¸Îĩί", + "updated_at": "ΕÎŊΡÎŧÎĩ΁ΉÎŧέÎŊÎŋ", "updated_password": "Ο ÎēĪ‰Î´ÎšÎēĪŒĪ‚ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚ ÎĩÎŊΡÎŧÎĩĪĪŽÎ¸ÎˇÎēÎĩ", "upload": "ΜÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ", "upload_concurrency": "Î¤ÎąĪ…Ī„ĪŒĪ‡ĪÎŋÎŊΡ ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ", @@ -1855,16 +1892,22 @@ "upload_success": "Η ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ ÎŋÎģÎŋÎēÎģÎˇĪĪŽÎ¸ÎˇÎēÎĩ, ÎąÎŊÎąÎŊÎĩĪŽĪƒĪ„Îĩ Ī„Îˇ ΃ÎĩÎģÎ¯Î´Îą ÎŗÎšÎą ÎŊÎą δÎĩÎ¯Ī„Îĩ Ī„Îą ÎŊέι ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎą.", "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 {it}}", + "user_pin_code_settings": "ÎšĪ‰Î´ÎšÎēĪŒĪ‚ PIN", + "user_pin_code_settings_description": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ PIN ĪƒÎąĪ‚", + "user_privacy": "Î‘Ī€ĪŒĪĪÎˇĪ„Îŋ Î§ĪÎŽĪƒĪ„Îˇ", "user_purchase_settings": "Î‘ÎŗÎŋ΁ÎŦ", "user_purchase_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ Î‘ÎŗÎŋ΁ÎŦĪ‚", "user_role_set": "ÎŸĪÎšĪƒÎŧĪŒĪ‚ {user} Ή΂ {role}", - "user_usage_detail": "ΛÎĩ΀΄ÎŋÎŧÎ­ĪÎĩΚÎĩĪ‚ Ī‡ĪÎŽĪƒÎˇĪ‚ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ", + "user_usage_detail": "ΛÎĩ΀΄ÎŋÎŧÎĩ΁ÎĩΚÎĩĪ‚ Ī‡ĪÎˇĪƒÎˇĪ‚ Ī„ÎŋĪ… Ī‡ĪÎˇĪƒĪ„Îˇ", "user_usage_stats": "ÎŖĪ„ÎąĪ„ÎšĪƒĪ„ÎšÎēÎŦ Ī‡ĪÎŽĪƒÎˇĪ‚ ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧÎŋĪ", "user_usage_stats_description": "Î ĪÎŋβÎŋÎģÎŽ ĪƒĪ„ÎąĪ„ÎšĪƒĪ„ÎšÎēĪŽÎŊ Ī‡ĪÎŽĪƒÎˇĪ‚ ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧÎŋĪ", "username": "ΌÎŊÎŋÎŧÎą Î§ĪÎŽĪƒĪ„Îˇ", @@ -1876,11 +1919,6 @@ "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_3": " ÎēιΚ βÎĩÎ˛ÎąÎšĪ‰Î¸ÎĩÎ¯Ī„Îĩ ĪŒĪ„Îš Ī„Îŋ docker-compose ÎēιΚ Ī„Îŋ .env ĪƒÎąĪ‚ ÎĩίÎŊιΚ ÎĩÎŊΡÎŧÎĩ΁ΉÎŧέÎŊΡ ÎŗÎšÎą Ī„ÎˇÎŊ ÎąĪ€ÎŋĪ†Ī…ÎŗÎŽ Ī„Ī…Ī‡ĪŒÎŊ ÎĩĪƒĪ†ÎąÎģÎŧέÎŊΉÎŊ δΚιÎŧÎŋĪĪ†ĪŽĪƒÎĩΉÎŊ, ÎĩΚδΚÎēÎŦ ÎĩÎŦÎŊ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„Îĩ Ī„Îŋ WatchTower ÎŽ ÎŋĪ€ÎŋΚÎŋÎŊÎ´ÎŽĪ€ÎŋĪ„Îĩ ÎŧÎˇĪ‡ÎąÎŊÎšĪƒÎŧΌ Ī€ÎŋĪ… ·ÎĩÎšĪÎ¯ÎļÎĩĪ„ÎąÎš Ī„ÎˇÎŊ ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îˇ ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ Ī„ÎŋĪ… δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ĪƒÎąĪ‚.", - "version_announcement_overlay_title": "Î”ÎšÎąÎ¸Î­ĪƒÎšÎŧΡ ÎŊέι έÎēδÎŋĪƒÎˇ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ 🎉", "version_history": "Î™ĪƒĪ„ÎŋĪÎšÎēΌ ΕÎēÎ´ĪŒĪƒÎĩΉÎŊ", "version_history_item": "Î•ÎŗÎēÎąĪ„ÎąĪƒĪ„ÎŦθΡÎēÎĩ {version} ĪƒĪ„ÎšĪ‚ {date}", "video": "ΒίÎŊĪ„ÎĩÎŋ", @@ -1900,6 +1938,7 @@ "view_previous_asset": "Î ĪÎŋβÎŋÎģÎŽ ΀΁ÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊÎŋĪ… ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ…", "view_qr_code": "Î ĪÎŋβÎŋÎģÎŽ ÎēĪ‰Î´ÎšÎēÎŋĪ QR", "view_stack": "Î ĪÎŋβÎŋÎģÎŽ Ī„ÎˇĪ‚ ĪƒĪ„ÎŋÎ¯Î˛ÎąĪ‚", + "view_user": "Î ĪÎŋβÎŋÎģÎŽ Î§ĪÎŽĪƒĪ„Îˇ", "viewer_remove_from_stack": "ÎšÎąĪ„ÎŦĪÎŗÎˇĪƒÎˇ ÎąĪ€ĪŒ Ī„Îˇ ÎŖĪ„ÎŋÎ¯Î˛Îą", "viewer_stack_use_as_main_asset": "Î§ĪÎŽĪƒÎˇ Ή΂ ÎšĪĪÎšÎŋ ÎŖĪ„ÎŋÎšĪ‡ÎĩίÎŋ", "viewer_unstack": "Î‘Ī€ÎŋĪƒĪ„ÎŋÎ¯Î˛ÎąÎžÎĩ", diff --git a/i18n/en.json b/i18n/en.json index 2fa37114c7..64e9fce58b 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "Added {count, number} to favorites", "admin": { "add_exclusion_pattern_description": "Add exclusion patterns. Globbing using *, **, and ? is supported. To ignore all files in any directory named \"Raw\", use \"**/Raw/**\". To ignore all files ending in \".tif\", use \"**/*.tif\". To ignore an absolute path, use \"/path/to/ignore/**\".", + "admin_user": "Admin User", "asset_offline_description": "This external library asset is no longer found on disk and has been moved to trash. If the file was moved within the library, check your timeline for the new corresponding asset. To restore this asset, please ensure that the file path below can be accessed by Immich and scan the library.", "authentication_settings": "Authentication Settings", "authentication_settings_description": "Manage password, OAuth, and other authentication settings", @@ -194,7 +195,9 @@ "oauth_enable_description": "Login with OAuth", "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_mobile_redirect_uri_override_description": "Enable when OAuth provider does not allow a mobile URI, like ''{callback}''", + "oauth_role_claim": "Role Claim", + "oauth_role_claim_description": "Automatically grant admin access based on the presence of this claim. The claim may have either 'user' or 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Manage OAuth login settings", "oauth_settings_more_details": "For more details about this feature, refer to the docs.", @@ -203,7 +206,7 @@ "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_storage_quota_default_description": "Quota in GiB to be used when no claim is provided.", "oauth_timeout": "Request Timeout", "oauth_timeout_description": "Timeout for requests in milliseconds", "password_enable_description": "Login with email and password", @@ -243,7 +246,7 @@ "storage_template_migration_info": "The storage template will convert all extensions to lowercase. Template changes will only apply to new assets. To retroactively apply the template to previously uploaded assets, run the {job}.", "storage_template_migration_job": "Storage Template Migration Job", "storage_template_more_details": "For more details about this feature, refer to the Storage Template and its implications", - "storage_template_onboarding_description": "When enabled, this feature will auto-organize files based on a user-defined template. Due to stability issues the feature has been turned off by default. For more information, please see the documentation.", + "storage_template_onboarding_description_v2": "When enabled, this feature will auto-organize files based on a user-defined template. For more information, please see the documentation.", "storage_template_path_length": "Approximate path length limit: {length, number}/{limit, number}", "storage_template_settings": "Storage Template", "storage_template_settings_description": "Manage the folder structure and file name of the upload asset", @@ -426,6 +429,7 @@ "app_settings": "App Settings", "appears_in": "Appears in", "archive": "Archive", + "archive_action_prompt": "{count} added to Archive", "archive_or_unarchive_photo": "Archive or unarchive photo", "archive_page_no_archived_assets": "No archived assets found", "archive_page_title": "Archive ({count})", @@ -463,7 +467,6 @@ "assets": "Assets", "assets_added_count": "Added {count, plural, one {# asset} other {# assets}}", "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_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} cannot be added to the album", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} asset(s) deleted permanently", @@ -702,7 +705,7 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Dark", - "darkTheme": "Toggle dark theme", + "dark_theme": "Toggle dark theme", "date_after": "Date after", "date_and_time": "Date and Time", "date_before": "Date before", @@ -718,6 +721,7 @@ "default_locale": "Default Locale", "default_locale_description": "Format dates and numbers based on your browser locale", "delete": "Delete", + "delete_action_prompt": "{count} deleted permanently", "delete_album": "Delete album", "delete_api_key_prompt": "Are you sure you want to delete this API key?", "delete_dialog_alert": "These items will be permanently deleted from Immich and from your device", @@ -798,6 +802,7 @@ "edit_key": "Edit key", "edit_link": "Edit link", "edit_location": "Edit location", + "edit_location_action_prompt": "{count} location edited", "edit_location_dialog_title": "Location", "edit_name": "Edit name", "edit_people": "Edit people", @@ -983,6 +988,7 @@ "failed_to_load_assets": "Failed to load assets", "failed_to_load_folder": "Failed to load folder", "favorite": "Favorite", + "favorite_action_prompt": "{count} added to Favorites", "favorite_or_unfavorite_photo": "Favorite or unfavorite photo", "favorites": "Favorites", "favorites_page_no_favorites": "No favorite assets found", @@ -1149,6 +1155,7 @@ "locked_folder": "Locked Folder", "log_out": "Log out", "log_out_all_devices": "Log Out All Devices", + "logged_in_as": "Logged in as {user}", "logged_out_all_devices": "Logged out all devices", "logged_out_device": "Logged out device", "login": "Login", @@ -1244,6 +1251,7 @@ "more": "More", "move": "Move", "move_off_locked_folder": "Move out of locked folder", + "move_to_lock_folder_action_prompt": "{count} added to the 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", @@ -1495,11 +1503,13 @@ "remove_deleted_assets": "Remove Deleted Assets", "remove_from_album": "Remove from album", "remove_from_favorites": "Remove from favorites", + "remove_from_lock_folder_action_prompt": "{count} removed from the locked folder", "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", + "remove_tag": "Remove tag", "remove_url": "Remove URL", "remove_user": "Remove user", "removed_api_key": "Removed API Key: {name}", @@ -1606,6 +1616,7 @@ "select_album_cover": "Select album cover", "select_all": "Select all", "select_all_duplicates": "Select all duplicates", + "select_all_in": "Select all in {group}", "select_avatar_color": "Select avatar color", "select_face": "Select face", "select_featured_photo": "Select featured photo", @@ -1835,6 +1846,7 @@ "total": "Total", "total_usage": "Total usage", "trash": "Trash", + "trash_action_prompt": "{count} moved to trash", "trash_all": "Trash All", "trash_count": "Trash {count, number}", "trash_delete_asset": "Trash/Delete Asset", @@ -1852,9 +1864,11 @@ "unable_to_change_pin_code": "Unable to change PIN code", "unable_to_setup_pin_code": "Unable to setup PIN code", "unarchive": "Unarchive", + "unarchive_action_prompt": "{count} removed from Archive", "unarchived_count": "{count, plural, other {Unarchived #}}", "undo": "Undo", "unfavorite": "Unfavorite", + "unfavorite_action_prompt": "{count} removed from Favorites", "unhide_person": "Unhide person", "unknown": "Unknown", "unknown_country": "Unknown Country", @@ -1870,8 +1884,10 @@ "unsaved_change": "Unsaved change", "unselect_all": "Unselect all", "unselect_all_duplicates": "Unselect all duplicates", + "unselect_all_in": "Unselect all in {group}", "unstack": "Un-stack", "unstacked_assets_count": "Un-stacked {count, plural, one {# asset} other {# assets}}", + "untagged": "Untagged", "up_next": "Up next", "updated_at": "Updated", "updated_password": "Updated password", diff --git a/i18n/es.json b/i18n/es.json index 13d242f9f8..7bfdc678df 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -22,11 +22,11 @@ "add_partner": "Agregar compaÃąero", "add_path": "Agregar carpeta", "add_photos": "Agregar fotos", + "add_tag": "Agregar etiqueta", "add_to": "Agregar aâ€Ļ", "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", @@ -34,6 +34,7 @@ "added_to_favorites_count": "Agregado {count, number} a favoritos", "admin": { "add_exclusion_pattern_description": "Agrega patrones de exclusiÃŗn. Puedes utilizar los caracteres *, ** y ? (globbing). Ejemplos: para ignorar todos los archivos en cualquier directorio llamado \"Raw\", utiliza \"**/Raw/**\". Para ignorar todos los archivos que terminan en \".tif\", utiliza \"**/*.tif\". Para ignorar una ruta absoluta, utiliza \"/carpeta/a/ignorar/**\".", + "admin_user": "Usuario admin", "asset_offline_description": "Este recurso externo de la biblioteca ya no se encuentra en el disco y se ha movido a la papelera. Si el archivo se moviÃŗ dentro de la biblioteca, comprueba la línea temporal para el nuevo recurso correspondiente. Para restaurar este recurso, asegÃērate de que Immich puede acceder a la siguiente ruta de archivo y escanear la biblioteca.", "authentication_settings": "ParÃĄmetros de autenticaciÃŗn", "authentication_settings_description": "Gestionar contraseÃąas, OAuth y otros parÃĄmetros de autenticaciÃŗn", @@ -44,9 +45,7 @@ "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", + "backup_settings_description": "Administrar configuraciÃŗn de volcado de base de datos.", "cleared_jobs": "Trabajos borrados para: {job}", "config_set_by_file": "La configuraciÃŗn estÃĄ definida por un archivo de configuraciÃŗn", "confirm_delete_library": "ÂŋEstÃĄs seguro de que quieres eliminar la biblioteca {library}?", @@ -62,14 +61,12 @@ "disable_login": "Deshabilitar inicio de sesiÃŗn", "duplicate_detection_job_description": "Lanza el aprendizaje automÃĄtico para detectar imÃĄgenes similares. Necesita tener activado \"BÃēsqueda Inteligente\"", "exclusion_pattern_description": "Los patrones de exclusiÃŗn te permiten ignorar archivos y carpetas al escanear tu biblioteca. Es Ãētil si tienes carpetas que contienen archivos que no deseas importar, por ejemplo archivos RAW.", - "external_library_created_at": "Biblioteca externa (creada el {date})", "external_library_management": "GestiÃŗn de bibliotecas externas", "face_detection": "DetecciÃŗn de caras", "face_detection_description": "Detecta las caras en los activos mediante aprendizaje automÃĄtico. En el caso de los vídeos, solo se tiene en cuenta la miniatura. \"Actualizar\" (re)procesarÃĄ todos los elementos. \"Restablecer\" borra ademÃĄs todos los datos de caras actuales. \"Falta\" pone en cola los elementos que aÃēn no se han procesado. Las caras detectadas se pondrÃĄn en cola para el reconocimiento facial una vez finalizada la detecciÃŗn, agrupÃĄndolos en personas existentes o nuevas.", "facial_recognition_job_description": "Agrupa las caras detectadas en personas. Este paso se ejecuta una vez finalizada la detecciÃŗn de caras. \"Restablecer\" (re)agrupa todas las caras. \"Falta\" pone en cola las caras que no tienen asignada una persona.", "failed_job_command": "El comando {command} ha fallado para la tarea: {job}", "force_delete_user_warning": "CUIDADO: Esta acciÃŗn eliminarÃĄ inmediatamente el usuario y todos los elementos. Esta accion no se puede deshacer y los archivos no pueden ser recuperados.", - "forcing_refresh_library_files": "Forzando la recarga de todos los elementos en la biblioteca", "image_format": "Formato", "image_format_description": "WebP genera archivos mÃĄs pequeÃąos que JPEG, pero es mÃĄs lento al codificarlos.", "image_fullsize_description": "Imagen de tamaÃąo completo con metadatos removidos, usado cuando se hace zoom", @@ -174,7 +171,7 @@ "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 \"", + "notification_email_from_address_description": "DirecciÃŗn de correo electrÃŗnico del remitente, por ejemplo: \"Immich Photo Server \". AsegÃērate de utilizar una direcciÃŗn desde la que puedas enviar correos electrÃŗnicos.", "notification_email_host_description": "Host del servidor de correo electrÃŗnico (por ejemplo: smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorar errores de certificado", "notification_email_ignore_certificate_errors_description": "Ignorar los errores de validaciÃŗn del certificado TLS (no recomendado)", @@ -198,7 +195,7 @@ "oauth_enable_description": "Iniciar sesiÃŗn con OAuth", "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_mobile_redirect_uri_override_description": "Habilitar cuando el proveedor de OAuth no permite una URI mÃŗvil, como ''{callback}''", "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.", @@ -207,11 +204,9 @@ "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_storage_quota_default_description": "Cuota en GiB que se utilizarÃĄ cuando no se proporcione ninguna por defecto.", "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", "password_settings": "ContraseÃąa de Acceso", "password_settings_description": "Administrar la configuraciÃŗn de inicio de sesiÃŗn con contraseÃąa", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "Actualizar todas las bibliotecas", "registration": "Registrar administrador", "registration_description": "Dado que eres el primer usuario del sistema, se te asignarÃĄ como Admin y serÃĄs responsable de las tareas administrativas, y de crear a los usuarios adicionales.", - "repair_all": "Reparar todo", - "repair_matched_items": "Coincidencia {count, plural, one {# elemento} other {# elementos}}", - "repaired_items": "Reparado {count, plural, one {# elemento} other {# elementos}}", "require_password_change_on_login": "Requerir que el usuario cambie la contraseÃąa en el primer inicio de sesiÃŗn", "reset_settings_to_default": "Restablecer la configuraciÃŗn predeterminada", "reset_settings_to_recent_saved": "Restablecer la configuraciÃŗn a la configuraciÃŗn guardada recientemente", @@ -252,7 +244,7 @@ "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": "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_onboarding_description_v2": "Al habilitar esta funciÃŗn, los archivos se organizarÃĄn automÃĄticamente segÃēn la plantilla definida por el usuario. Para 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": "Administrar la estructura de carpetas y el nombre de archivo del recurso cargado", @@ -264,16 +256,14 @@ "template_email_invite_album": "Plantilla de ÃĄlbum de invitaciones", "template_email_preview": "Vista previa", "template_email_settings": "Modelos de correo electrÃŗnico", - "template_email_settings_description": "Gestionar plantillas de notificaciÃŗn por correo electrÃŗnico personalizadas", "template_email_update_album": "Actualizar plantilla del ÃĄlbum", "template_email_welcome": "Plantilla de correo electrÃŗnico de bienvenida", "template_settings": "Plantillas de notificaciÃŗn", - "template_settings_description": "Gestione plantillas personalizadas para las notificaciones.", + "template_settings_description": "Gestione plantillas personalizadas para las notificaciones", "theme_custom_css_settings": "CSS Personalizado", "theme_custom_css_settings_description": "Las Hojas de Estilo (CSS) permiten personalizar el diseÃąo de Immich.", "theme_settings": "Ajustes Tema", "theme_settings_description": "Gestionar la personalizaciÃŗn de la interfaz web de Immich", - "these_files_matched_by_checksum": "Estos archivos coinciden con sus checksums", "thumbnail_generation_job": "Generar Miniaturas", "thumbnail_generation_job_description": "Genere miniaturas grandes, pequeÃąas y borrosas para cada archivo, así como miniaturas para cada persona", "transcoding_acceleration_api": "API AceleraciÃŗn", @@ -301,10 +291,9 @@ "transcoding_encoding_options": "Opciones de codificaciÃŗn", "transcoding_encoding_options_description": "Establecer cÃŗdecs, resoluciÃŗn, calidad y otras opciones para los vídeos codificados", "transcoding_hardware_acceleration": "AceleraciÃŗn por Hardware", - "transcoding_hardware_acceleration_description": "Experimental; mucho mÃĄs rÃĄpido, pero tendrÃĄ menor calidad con la misma tasa de bits", + "transcoding_hardware_acceleration_description": "Experimental: transcodificaciÃŗn mÃĄs rÃĄpida, pero puede reducir la calidad con la misma tasa de bits", "transcoding_hardware_decoding": "DecodificaciÃŗn por hardware", "transcoding_hardware_decoding_setting_description": "Permite la aceleraciÃŗn de extremo a extremo en lugar de acelerar Ãēnicamente la codificaciÃŗn. Puede que no funcione en todos los vídeos.", - "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "Maximos B-frames", "transcoding_max_b_frames_description": "Los valores mÃĄs altos mejoran la eficiencia de la compresiÃŗn, pero ralentizan la codificaciÃŗn. Puede que no sea compatible con la aceleraciÃŗn de hardware en dispositivos mÃĄs antiguos. 0 desactiva los fotogramas B, mientras que -1 establece este valor automÃĄticamente.", "transcoding_max_bitrate": "MÃĄxima tasa de bits", @@ -342,8 +331,6 @@ "trash_number_of_days_description": "NÃēmero de días para mantener los archivos en la papelera antes de eliminarlos permanentemente", "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, 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", @@ -402,10 +389,6 @@ "album_remove_user": "ÂŋEliminar usuario?", "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": "{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}", @@ -421,6 +404,9 @@ "album_with_link_access": "Permita que cualquier persona con el enlace vea fotos y personas en este ÃĄlbum.", "albums": "Álbumes", "albums_count": "{count, plural, one {{count, number} Álbum} other {{count, number} Álbumes}}", + "albums_default_sort_order": "OrdenaciÃŗn por defecto de los ÃĄlbumes", + "albums_default_sort_order_description": "Orden de clasificaciÃŗn inicial de los activos al crear nuevos ÃĄlbumes.", + "albums_feature_description": "Colecciones de activos que pueden compartirse con otros usuarios.", "all": "Todos", "all_albums": "Todos los albums", "all_people": "Todas las personas", @@ -479,9 +465,12 @@ "assets_added_count": "AÃąadido {count, plural, one {# asset} other {# assets}}", "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_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no pueden ser aÃąadidos al album", "assets_count": "{count, plural, one {# activo} other {# activos}}", "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_downloaded_failed": "{count, plural, one {Descargado archivo # - {error} archivo fallido} other {Descargados # archivos - {error} archivos fallidos}}", + "assets_downloaded_successfully": "{count, plural, one {Archivo # descargado correctamente} other {Archivos # descargados correctamente}}", "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}}", @@ -496,6 +485,7 @@ "authorized_devices": "Dispositivos Autorizados", "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", + "autoplay_slideshow": "PresentaciÃŗn con reproducciÃŗn automÃĄtica", "back": "AtrÃĄs", "back_close_deselect": "AtrÃĄs, cerrar o anular la selecciÃŗn", "background_location_permission": "Permiso de ubicaciÃŗn en segundo plano", @@ -539,6 +529,7 @@ "backup_controller_page_excluded": "Excluido: ", "backup_controller_page_failed": "Fallidos ({count})", "backup_controller_page_filename": "Nombre del archivo: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "InformaciÃŗn de la Copia de Seguridad", "backup_controller_page_none_selected": "Ninguno seleccionado", "backup_controller_page_remainder": "Restante", @@ -576,21 +567,17 @@ "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 ({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 ({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": "{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 ({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Ê", @@ -604,13 +591,14 @@ "cannot_undo_this_action": "ÂĄNo puedes deshacer esta acciÃŗn!", "cannot_update_the_description": "No se puede actualizar la descripciÃŗn", "cast": "Convertir", + "cast_description": "Configura los posibles destinos de retransmisiÃŗn", "change_date": "Cambiar fecha", "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", - "change_name_successfully": "Nombre cambiado correctamente", + "change_name_successfully": "Nombre cambiado exitosamente", "change_password": "Cambiar ContraseÃąa", "change_password_description": "Esta es la primera vez que inicia sesiÃŗn en el sistema o se ha realizado una solicitud para cambiar su contraseÃąa. Por favor ingrese la nueva contraseÃąa a continuaciÃŗn.", "change_password_form_confirm_password": "Confirmar ContraseÃąa", @@ -621,7 +609,6 @@ "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": "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.", @@ -633,6 +620,7 @@ "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": "El certificado de cliente estÃĄ importado", @@ -644,6 +632,7 @@ "close": "Cerrar", "collapse": "Agrupar", "collapse_all": "Desplegar todo", + "color": "Color", "color_theme": "Color del tema", "comment_deleted": "Comentario borrado", "comment_options": "Opciones de comentarios", @@ -659,11 +648,13 @@ "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", + "confirm_tag_face": "ÂŋQuieres etiquetar esta cara como {name}?", + "confirm_tag_face_unnamed": "ÂŋQuieres etiquetar esta cara?", + "connected_device": "Dispositivo conectado", "connected_to": "Conectado a", "contain": "Incluido", "context": "Contexto", "continue": "Continuar", - "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", @@ -712,6 +703,7 @@ "daily_title_text_date": "E dd, MMM", "daily_title_text_date_year": "E dd de MMM, yyyy", "dark": "Oscuro", + "darkTheme": "Activar tema oscuro", "date_after": "Fecha posterior", "date_and_time": "Fecha y Hora", "date_before": "Fecha anterior", @@ -758,6 +750,7 @@ "disabled": "Deshabilitado", "disallow_edits": "Bloquear ediciÃŗn", "discover": "Descubrir", + "discovered_devices": "Dispositivos descubiertos", "dismiss_all_errors": "Descartar todos los errores", "dismiss_error": "Descartar error", "display_options": "Opciones de pantalla", @@ -773,7 +766,6 @@ "download_enqueue": "Descarga en cola", "download_error": "Error al descargar", "download_failed": "Descarga fallida", - "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", @@ -813,6 +805,7 @@ "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", @@ -830,9 +823,12 @@ "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": "Error", "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}", + "error_tag_face_bounding_box": "Error etiquetando cara - no se pueden obtener las coordenadas del marco delimitante", "error_title": "Error: algo saliÃŗ mal", "errors": { "cannot_navigate_next_asset": "No puedes navegar al siguiente archivo", @@ -845,7 +841,6 @@ "cant_get_number_of_comments": "No se puede obtener la cantidad de comentarios", "cant_search_people": "No se puede buscar a personas", "cant_search_places": "No se pueden buscar lugares", - "cleared_jobs": "Tareas listas para: {job}", "error_adding_assets_to_album": "Error al aÃąadir archivos al ÃĄlbum", "error_adding_users_to_album": "Error al aÃąadir usuarios al ÃĄlbum", "error_deleting_shared_user": "Error al eliminar usuario compartido", @@ -854,7 +849,6 @@ "error_removing_assets_from_album": "Error al eliminar archivos del ÃĄlbum; consulte la consola para obtener mÃĄs detalles", "error_selecting_all_assets": "Error al seleccionar todos los archivos", "exclusion_pattern_already_exists": "Este patrÃŗn de exclusiÃŗn ya existe.", - "failed_job_command": "El comando {command} ha fallado para la tarea: {job}", "failed_to_create_album": "Error al crear el ÃĄlbum", "failed_to_create_shared_link": "Error al crear el enlace compartido", "failed_to_edit_shared_link": "Error al editar el enlace compartido", @@ -873,7 +867,6 @@ "paths_validation_failed": "FallÃŗ la validaciÃŗn en {paths, plural, one {# carpeta} other {# carpetas}}", "profile_picture_transparent_pixels": "Las imÃĄgenes de perfil no pueden tener píxeles transparentes. Por favor amplíe y/o mueva la imagen.", "quota_higher_than_disk_size": "Se ha establecido una cuota superior al tamaÃąo del disco", - "repair_unable_to_check_items": "No se puede verificar {count, select, one {elemento} other {elementos}}", "unable_to_add_album_users": "No se pueden agregar usuarios al ÃĄlbum", "unable_to_add_assets_to_shared_link": "No se pueden agregar archivos al enlace compartido", "unable_to_add_comment": "No se puede agregar comentario", @@ -892,7 +885,6 @@ "unable_to_change_visibility": "No se puede cambiar la visibilidad de {count, plural, one {# persona} other {# personas}}", "unable_to_complete_oauth_login": "No se puede completar el inicio de sesiÃŗn de OAuth", "unable_to_connect": "No puede conectarse", - "unable_to_connect_to_server": "Error al conectar al servidor", "unable_to_copy_to_clipboard": "No se puede copiar al portapapeles, asegÃērese de acceder a la pÃĄgina a travÊs de https", "unable_to_create_admin_account": "No se puede crear una cuenta de administrador", "unable_to_create_api_key": "No se puede crear una nueva clave API", @@ -916,14 +908,9 @@ "unable_to_hide_person": "No se puede ocultar a la persona", "unable_to_link_motion_video": "No se puede enlazar el vídeo en movimiento", "unable_to_link_oauth_account": "No se puede vincular la cuenta OAuth", - "unable_to_load_album": "No se puede cargar el ÃĄlbum", - "unable_to_load_asset_activity": "No se puede cargar la actividad de los archivos", - "unable_to_load_items": "No se pueden cargar items", - "unable_to_load_liked_status": "No se puede cargar el estado \"Me gusta\"", "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", @@ -931,11 +918,9 @@ "unable_to_remove_album_users": "No se pueden eliminar usuarios del ÃĄlbum", "unable_to_remove_api_key": "No se puede eliminar la clave API", "unable_to_remove_assets_from_shared_link": "No se pueden eliminar archivos desde el enlace compartido", - "unable_to_remove_deleted_assets": "No se pueden eliminar archivos sin conexiÃŗn", "unable_to_remove_library": "No se puede eliminar la biblioteca", "unable_to_remove_partner": "No se puede eliminar el invitado", "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", @@ -971,7 +956,6 @@ "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": "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}", @@ -980,6 +964,7 @@ "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 propia responsabilidad!", + "experimental_settings_title": "Experimental", "expire_after": "Expirar despuÊs de", "expired": "Caducado", "expires_date": "Expira el {date}", @@ -987,6 +972,7 @@ "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar a JSON", + "extension": "ExtensiÃŗn", "external": "Externo", "external_libraries": "Bibliotecas Externas", "external_network": "Red externa", @@ -1017,6 +1003,9 @@ "folders": "Carpetas", "folders_feature_description": "Explorar la vista de carpetas para las fotos y los videos en el sistema de archivos", "forward": "Reenviar", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Esta funcionalidad carga recursos externos desde Google para poder funcionar.", + "general": "General", "get_help": "Solicitar ayuda", "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", @@ -1062,7 +1051,9 @@ "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", + "id": "ID", "ignore_icloud_photos": "Ignorar fotos de iCloud", "ignore_icloud_photos_description": "Las fotos almacenadas en iCloud no se subirÃĄn a Immich", "image": "Imagen", @@ -1102,6 +1093,12 @@ "invalid_date_format": "Formato de fecha incorrecto", "invite_people": "Invitar a Personas", "invite_to_album": "Invitar al ÃĄlbum", + "ios_debug_info_fetch_ran_at": "Busca ejecuciÃŗn en {dateTime}", + "ios_debug_info_last_sync_at": "Última sincronizaciÃŗn en {dateTime}", + "ios_debug_info_no_processes_queued": "NingÃēn proceso de fondo encolado", + "ios_debug_info_no_sync_yet": "Todavía no se ha ejecutado ningÃēn trabajo de sincronizaciÃŗn en segundo plano", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proceso encolado de fondo} other {{count} procesos encolados de fondo}}", + "ios_debug_info_processing_ran_at": "El procesamiento se ejecutÃŗ el {dateTime}", "items_count": "{count, plural, one {# elemento} other {# elementos}}", "jobs": "Tareas", "keep": "Conservar", @@ -1110,6 +1107,9 @@ "kept_this_deleted_others": "Mantuvo este activo y eliminÃŗ {count, plural, one {# activo} other {# activos}}", "keyboard_shortcuts": "Atajos de teclado", "language": "Idioma", + "language_no_results_subtitle": "Intente ajustar el tÊrmino de bÃēsqueda", + "language_no_results_title": "No se han encontrado idiomas", + "language_search_hint": "Buscar idiomas...", "language_setting_description": "Selecciona tu idioma preferido", "last_seen": "Ultima vez visto", "latest_version": "Última versiÃŗn", @@ -1135,6 +1135,8 @@ "list": "Listar", "loading": "Cargando", "loading_search_results_failed": "Error al cargar los resultados de la bÃēsqueda", + "local_asset_cast_failed": "No se puede emitir un activo que no estÃĄ cargado en el servidor", + "local_network": "Red local", "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", @@ -1144,10 +1146,11 @@ "location_picker_longitude_error": "Introduce una longitud vÃĄlida", "location_picker_longitude_hint": "Introduce tu longitud aquí", "lock": "Bloquear", - "locked_folder": "Bloquear carpeta", + "locked_folder": "Carpeta bloqueada", "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", + "logged_in_as": "SesiÃŗn iniciada como {user}", + "logged_out_all_devices": "Se ha cerrado la sesiÃŗn en todos los dispositivos", "logged_out_device": "Dispositivo desconectado", "login": "Inicio de sesiÃŗn", "login_disabled": "El inicio de sesiÃŗn ha sido desactivado", @@ -1178,7 +1181,7 @@ "look": "Mirar", "loop_videos": "Vídeos en bucle", "loop_videos_description": "Habilite la reproducciÃŗn automÃĄtica de un video en el visor de detalles.", - "main_branch_warning": "EstÃĄs ejecutando una compilaciÃŗn desde la rama principal. ÂĄRecomendamos encarecidamente usar una versiÃŗn de lanzamiento!", + "main_branch_warning": "EstÃĄ utilizando una versiÃŗn de desarrollo; ÂĄle recomendamos encarecidamente que utilice una versiÃŗn de lanzamiento!", "main_menu": "MenÃē principal", "make": "Marca", "manage_shared_links": "Administrar enlaces compartidos", @@ -1238,10 +1241,11 @@ "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_off_locked_folder": "Mover fuera de la carpeta protegida", + "move_to_locked_folder": "Mover a la 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", @@ -1266,18 +1270,20 @@ "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.", "no_archived_assets_message": "Archive fotos y videos para ocultarlos de su vista de Fotos", "no_assets_message": "HAZ CLIC PARA SUBIR TU PRIMERA FOTO", "no_assets_to_show": "No hay elementos a mostrar", + "no_cast_devices_found": "Dispositivos de difusiÃŗn no encontrados", "no_duplicates_found": "No se encontraron duplicados.", "no_exif_info_available": "No hay informaciÃŗn exif disponible", "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_locked_photos_message": "Fotos y vídeos en la carpeta protegida estÃĄn ocultos y no se mostrarÃĄn en las bÃēsquedas de tu librería.", "no_name": "Sin nombre", "no_notifications": "Ninguna notificaciÃŗn", "no_people_found": "No se encontraron personas coincidentes", @@ -1297,17 +1303,18 @@ "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", - "offline_paths_description": "Estos resultados pueden deberse a la eliminaciÃŗn manual de archivos que no forman parte de una biblioteca externa.", "ok": "Sí", "oldest_first": "Los mÃĄs antiguos primero", "on_this_device": "En este dispositivo", "onboarding": "Incorporando", - "onboarding_privacy_description": "Las siguientes funciones (opcionales) dependen de servicios externos y pueden desactivarse en cualquier momento en los ajustes.", + "onboarding_locale_description": "Selecciona tu idioma preferido. PodrÃĄs cambiarlo despuÊs desde tu configuraciÃŗn.", + "onboarding_privacy_description": "Las siguientes funciones (opcionales) dependen de servicios externos y pueden desactivarse en cualquier momento desde los ajustes.", + "onboarding_server_welcome_description": "Empecemos a configurar tu instancia con algunos ajustes comunes.", "onboarding_theme_description": "Elija un color de tema para su instancia. Puedes cambiar esto mÃĄs tarde en tu configuraciÃŗn.", - "onboarding_welcome_description": "Configuremos su instancia con algunas configuraciones comunes.", + "onboarding_user_welcome_description": "ÂĄEmpecemos!", "onboarding_welcome_user": "Bienvenido, {user}", "online": "En línea", "only_favorites": "Solo favoritos", @@ -1318,6 +1325,7 @@ "options": "Opciones", "or": "o", "organize_your_library": "Organiza tu biblioteca", + "original": "original", "other": "Otro", "other_devices": "Otro dispositivo", "other_variables": "Otras variables", @@ -1363,6 +1371,8 @@ "permanently_delete_assets_prompt": "ÂŋEstÃĄ seguro de que desea eliminar permanentemente {count, plural, one {este activo?} other {estos # activos?}} Esto tambiÊn eliminarÃĄ {count, plural, one {de tu} other {de tus}} ÃĄlbum(es).", "permanently_deleted_asset": "Archivo eliminado permanentemente", "permanently_deleted_assets_count": "Eliminado permanentemente {count, plural, one {# elemento} other {# elementos}}", + "permission": "Permiso", + "permission_empty": "Tus permisos no deben estar vacíos", "permission_onboarding_back": "Volver", "permission_onboarding_continue_anyway": "Continuar de todos modos", "permission_onboarding_get_started": "Empezar", @@ -1399,14 +1409,18 @@ "preview": "Posterior", "previous": "Anterior", "previous_memory": "Recuerdo anterior", - "previous_or_next_photo": "Foto anterior o siguiente", + "previous_or_next_day": "Día posterior/anterior", + "previous_or_next_month": "Mes posterior/anterior", + "previous_or_next_photo": "Foto posterior/anterior", + "previous_or_next_year": "AÃąo posterior/anterior", "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_client_server_up_to_date": "Cliente y 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}", @@ -1427,6 +1441,7 @@ "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", @@ -1480,11 +1495,12 @@ "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_locked_folder": "Eliminar de la carpeta protegida", + "remove_from_locked_folder_confirmation": "ÂŋEstÃĄs seguro de que deseas mover estas fotos y vídeos fuera 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", + "remove_tag": "Quitar etiqueta", "remove_url": "Eliminar URL", "remove_user": "Eliminar usuario", "removed_api_key": "Clave API eliminada: {name}", @@ -1517,6 +1533,7 @@ "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", @@ -1566,6 +1583,7 @@ "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", @@ -1589,6 +1607,7 @@ "select_album_cover": "Seleccionar portada del ÃĄlbum", "select_all": "Seleccionar todo", "select_all_duplicates": "Seleccionar todos los duplicados", + "select_all_in": "Selecciona todos en {group}", "select_avatar_color": "Seleccionar color del avatar", "select_face": "Seleccionar cara", "select_featured_photo": "Seleccionar foto principal", @@ -1609,6 +1628,7 @@ "server_info_box_server_url": "URL del servidor", "server_offline": "Servidor desconectado", "server_online": "Servidor en línea", + "server_privacy": "Privacidad del Servidor", "server_stats": "Estadísticas del servidor", "server_version": "VersiÃŗn del servidor", "set": "Establecer", @@ -1618,6 +1638,7 @@ "set_date_of_birth": "Establecer fecha de nacimiento", "set_profile_picture": "Establecer foto de perfil", "set_slideshow_to_fullscreen": "Mostrar diapositivas en pantalla completa", + "set_stack_primary_asset": "Establecer como activo principal", "setting_image_viewer_help": "El visor de detalles carga primero la miniatura pequeÃąa, luego carga la vista previa de tamaÃąo mediano (si estÃĄ habilitada), finalmente carga la original (si estÃĄ habilitada).", "setting_image_viewer_original_subtitle": "Activar para cargar la imagen en resoluciÃŗn original (ÂĄmuy grande!). Deshabilitar para reducir el consumo de datos (de red y cachÊ).", "setting_image_viewer_original_title": "Cargar imagen original", @@ -1626,7 +1647,6 @@ "setting_image_viewer_title": "ImÃĄgenes", "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: {duration}", "setting_notifications_notify_hours": "{count} horas", "setting_notifications_notify_immediately": "inmediatamente", @@ -1689,6 +1709,7 @@ "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", @@ -1755,6 +1776,7 @@ "start_date": "Fecha de inicio", "state": "Estado", "status": "Estado", + "stop_casting": "Parar difusiÃŗn", "stop_motion_photo": "Parar foto en movimiento", "stop_photo_sharing": "ÂŋDejar de compartir tus fotos?", "stop_photo_sharing_description": "{partner} ya no podrÃĄ acceder a tus fotos.", @@ -1812,7 +1834,7 @@ "to_parent": "Ir a los padres", "to_trash": "Descartar", "toggle_settings": "Alternar ajustes", - "toggle_theme": "Alternar tema oscuro", + "total": "Total", "total_usage": "Uso total", "trash": "Papelera", "trash_all": "Descartar todo", @@ -1833,6 +1855,7 @@ "unable_to_setup_pin_code": "No se ha podido establecer el PIN", "unarchive": "Desarchivar", "unarchived_count": "{count, plural, one {# No archivado} other {# No archivados}}", + "undo": "Deshacer", "unfavorite": "Retirar favorito", "unhide_person": "Mostrar persona", "unknown": "Desconocido", @@ -1849,10 +1872,9 @@ "unsaved_change": "Cambio no guardado", "unselect_all": "Limpiar selecciÃŗn", "unselect_all_duplicates": "Deseleccionar todos los duplicados", + "unselect_all_in": "Deselecciona todos en {group}", "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, subidas interrumpidas o por un fallo de la aplicaciÃŗn", "up_next": "A continuaciÃŗn", "updated_at": "Actualizado", "updated_password": "ContraseÃąa actualizada", @@ -1869,6 +1891,7 @@ "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", + "url": "URL", "usage": "Uso", "use_biometric": "Uso biomÊtrico", "use_current_connection": "Usar conexiÃŗn actual", @@ -1879,6 +1902,7 @@ "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_privacy": "Privacidad del Usuario", "user_purchase_settings": "Compra", "user_purchase_settings_description": "Gestiona tu compra", "user_role_set": "Establecer {user} como {role}", @@ -1890,14 +1914,10 @@ "utilities": "Utilidades", "validate": "Validar", "validate_endpoint_error": "Por favor, introduce una URL vÃĄlida", + "variables": "Variables", "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.", - "version_announcement_overlay_release_notes": "notas de versiÃŗn", - "version_announcement_overlay_text_1": "Hola amigo, hay una nueva versiÃŗn de", - "version_announcement_overlay_text_2": "por favor, tÃŗmate tu tiempo para visitar las ", - "version_announcement_overlay_text_3": " y asegÃērate de que la configuraciÃŗn de docker-compose y .env estÊn actualizadas para evitar cualquier error de configuraciÃŗn, especialmente si utilizas WatchTower o cualquier mecanismo que actualice automÃĄticamente la aplicaciÃŗn del servidor.", - "version_announcement_overlay_title": "Nueva versiÃŗn del servidor disponible 🎉", "version_history": "Historial de versiones", "version_history_item": "Instalada {version} el {date}", "video": "Vídeo", @@ -1917,6 +1937,7 @@ "view_previous_asset": "Mostrar elemento anterior", "view_qr_code": "Ver cÃŗdigo QR", "view_stack": "Ver Pila", + "view_user": "Ver Usuario", "viewer_remove_from_stack": "Quitar de la pila", "viewer_stack_use_as_main_asset": "Usar como elemento principal", "viewer_unstack": "Desapilar", diff --git a/i18n/et.json b/i18n/et.json index fa621118d1..a2b17c6138 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -22,11 +22,11 @@ "add_partner": "Lisa partner", "add_path": "Lisa tee", "add_photos": "Lisa fotosid", + "add_tag": "Lisa silt", "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", @@ -34,6 +34,7 @@ "added_to_favorites_count": "{count, number} pilti lisatud lemmikutesse", "admin": { "add_exclusion_pattern_description": "Lisa välistamismustreid. Toetatud on metamärgid *, ** ja ?. KÃĩikide kataloogis nimega \"Raw\" olevate failide ignoreerimiseks kasuta \"**/Raw/**\". KÃĩikide .tif failide ignoreerimiseks kasuta \"**/*.tif\". Absouutse tee ignoreerimiseks kasuta \"/path/to/ignore/**\".", + "admin_user": "Administraator", "asset_offline_description": "Seda välise kogu Ãŧksust ei leitud kettalt ning see liigutati prÃŧgikasti. Kui faili asukoht kogu siseselt muutus, leiad vastava uue Ãŧksuse oma ajajoonelt. Üksuse taastamiseks veendu, et allpool toodud failitee on Immich'ile kättesaadav ning skaneeri kogu uuesti.", "authentication_settings": "Autentimise seaded", "authentication_settings_description": "Halda parooli, OAuth ja muid autentimise seadeid", @@ -44,9 +45,7 @@ "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", + "backup_settings_description": "Halda andmebaasi tÃĩmmiste seadeid.", "cleared_jobs": "TÃļÃļted eemaldatud: {job}", "config_set_by_file": "Konfiguratsioon on määratud konfifaili abil", "confirm_delete_library": "Kas oled kindel, et soovid kustutada {library} kogu?", @@ -62,14 +61,12 @@ "disable_login": "Keela sisselogimine", "duplicate_detection_job_description": "Rakenda Ãŧksustele masinÃĩpet, et leida sarnaseid pilte. Kasutab nutiotsingut", "exclusion_pattern_description": "Välistamismustrid vÃĩimaldavad ignoreerida faile ja kaustu kogu skaneerimisel. See on kasulik, kui sul on kaustu, mis sisaldavad faile, mida sa ei soovi importida, nagu RAW failid.", - "external_library_created_at": "Väline kogu (lisatud {date})", "external_library_management": "Väliste kogude haldus", "face_detection": "Näoavastus", "face_detection_description": "Avasta Ãŧksustest nägusid masinÃĩppe abil. Videote puhul kasutatakse ainult pisipilti. \"Värskenda\" tÃļÃļtleb kÃĩik Ãŧksused uuesti. \"Lähtesta\" kustutab lisaks kÃĩik seni leitud näed. \"Puuduvad\" vÃĩtab ette Ãŧksused, mida pole veel tÃļÃļdeldud. Avastatud näod suunatakse näotuvastusse, et grupeerida nad olemasolevateks vÃĩi uuteks isikuteks.", "facial_recognition_job_description": "Grupeeri avastatud näod inimesteks. See samm käivitub siis, kui näoavastus on lÃĩppenud. \"Lähtesta\" grupeerib kÃĩik näod uuesti. \"Puuduvad\" vÃĩtab ette näod, mida pole isikuga seostatud.", "failed_job_command": "Käsk {command} ebaÃĩnnestus tÃļÃļtes: {job}", "force_delete_user_warning": "HOIATUS: See kustutab koheselt kasutaja ja kÃĩik Ãŧksused. Seda ei saa tagasi vÃĩtta ja faile ei saa taastada.", - "forcing_refresh_library_files": "Kogu kÃĩigi failide sundvärskendamine", "image_format": "Formaat", "image_format_description": "WebP failid on väiksemad kui JPEG, aga kodeerimine on aeglasem.", "image_fullsize_description": "TäismÃĩÃĩdus pilt ilma metaandmeteta, kasutatakse sisse suumimisel", @@ -174,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Märkus: Et rakendada talletussilt varem Ãŧleslaaditud Ãŧksustele, käivita", "note_cannot_be_changed_later": "MÄRKUS: Seda ei saa hiljem muuta!", "notification_email_from_address": "Saatja aadress", - "notification_email_from_address_description": "Saatja e-posti aadress, näiteks: \"Immich Photo Server \"", + "notification_email_from_address_description": "Saatja e-posti aadress, näiteks: \"Immich Photo Server \". Kasuta kindlasti aadressi, millelt sul on luba e-kirju saata.", "notification_email_host_description": "E-posti serveri host (nt. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignoreeri sertifikaadi vigu", "notification_email_ignore_certificate_errors_description": "Ignoreeri TLS sertifikaadi valideerimise vigu (mittesoovituslik)", @@ -198,7 +195,7 @@ "oauth_enable_description": "Sisene OAuth abil", "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_mobile_redirect_uri_override_description": "LÃŧlita sisse, kui OAuth pakkuja ei luba mobiilset URI-d, näiteks ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "Halda OAuth sisselogimise seadeid", "oauth_settings_more_details": "Selle funktsiooni kohta rohkem teada saamiseks loe dokumentatsiooni.", @@ -207,11 +204,9 @@ "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_storage_quota_default_description": "Kvoot (GiB), mida kasutada, kui Ãŧhtegi väidet pole esitatud.", "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", "password_settings": "Parooliga sisselogimine", "password_settings_description": "Halda parooliga sisselogimise seadeid", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "KÃĩikide kogude värskendamine", "registration": "Administraatori registreerimine", "registration_description": "Kuna sa oled sÃŧsteemis esimene kasutaja, määratakse sind administraatoriks, ning sa saad lisada täiendavaid kasutajaid.", - "repair_all": "Paranda kÃĩik", - "repair_matched_items": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} leitud", - "repaired_items": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} parandatud", "require_password_change_on_login": "NÃĩua kasutajalt esmakordsel sisenemisel parooli muutmist", "reset_settings_to_default": "Lähtesta seaded", "reset_settings_to_recent_saved": "Taasta hiljuti salvestatud seaded", @@ -252,7 +244,7 @@ "storage_template_migration_info": "Talletusmall teeb kÃĩik faililaiendid väiketähtedeks. Malli muudatused rakenduvad ainult uutele Ãŧksustele. Et rakendada malli tagasiulatuvalt varem Ãŧleslaaditud Ãŧksustele, käivita {job}.", "storage_template_migration_job": "Talletusmallide migreerimise tÃļÃļde", "storage_template_more_details": "Et selle funktsiooni kohta rohkem teada saada, loe talletusmallide ja nende tagajärgede kohta", - "storage_template_onboarding_description": "Kui sisse lÃŧlitatud, vÃĩimaldab see faile kasutaja määratud malli alusel automaatselt organiseerida. Stabiilsusprobleemide tÃĩttu on see funktsioon vaikimisi välja lÃŧlitatud. Rohkem infot leiad dokumentatsioonist.", + "storage_template_onboarding_description_v2": "Kui lubatud, organiseeritakse failid automaatselt kasutaja määratud malli alusel. Rohkem infot leiad dokumentatsioonist.", "storage_template_path_length": "Tee pikkuse umbkaudne limiit: {length, number}/{limit, number}", "storage_template_settings": "Talletusmall", "storage_template_settings_description": "Halda Ãŧleslaaditud Ãŧksuse kaustastruktuuri ja failinime", @@ -264,7 +256,6 @@ "template_email_invite_album": "Albumisse kutse mall", "template_email_preview": "Eelvaade", "template_email_settings": "E-posti mallid", - "template_email_settings_description": "Halda e-posti teavitusmalle", "template_email_update_album": "Albumi muutmise mall", "template_email_welcome": "Tervituskirja mall", "template_settings": "Teavituse mallid", @@ -273,7 +264,6 @@ "theme_custom_css_settings_description": "Cascading Style Sheets lubab Immich'i kujunduse kohandamist.", "theme_settings": "Teema seaded", "theme_settings_description": "Halda Immich'i veebiliidese kohandamist", - "these_files_matched_by_checksum": "Need failid Ãŧhtivad kontrollsumma alusel", "thumbnail_generation_job": "Pisipiltide genereerimine", "thumbnail_generation_job_description": "Genereeri iga Ãŧksuse kohta suur, väike ja udustatud pisipilt ning iga isiku kohta pisipilt", "transcoding_acceleration_api": "Kiirenduse API", @@ -301,10 +291,9 @@ "transcoding_encoding_options": "Kodeerimise valikud", "transcoding_encoding_options_description": "Määra kodeeritud videote koodek, resolutsioon, kvaliteet ja muud valikud", "transcoding_hardware_acceleration": "Riistvaraline kiirendus", - "transcoding_hardware_acceleration_description": "Eksperimentaalne; palju kiirem, aga sama bitisageduse juures madalam kvaliteet", + "transcoding_hardware_acceleration_description": "Eksperimentaalne: kiirem transkodeerimine, aga vÃĩib vähendada kvaliteeti sama bitisageduse juures", "transcoding_hardware_decoding": "Riistvaraline dekodeerimine", "transcoding_hardware_decoding_setting_description": "VÃĩimaldab protsessi läbivalt kiirendada, mitte ainult kodeerimist. Ei pruugi kÃĩigi videote puhul tÃļÃļtada.", - "transcoding_hevc_codec": "HEVC koodek", "transcoding_max_b_frames": "Maksimaalne B-kaadrite arv", "transcoding_max_b_frames_description": "KÃĩrgemad väärtused parandavad pakkimise efektiivsust, aga aeglustavad kodeerimist. See valik ei pruugi olla Ãŧhilduv riistvaralise kiirendusega vanematel seadmetel. 0 lÃŧlitab B-kaadrid välja, -1 määrab väärtuse automaatselt.", "transcoding_max_bitrate": "Maksimaalne bitisagedus", @@ -342,8 +331,6 @@ "trash_number_of_days_description": "Päevade arv, kui kaua hoida Ãŧksusi prÃŧgikastis enne nende lÃĩplikku kustutamist", "trash_settings": "PrÃŧgikasti seaded", "trash_settings_description": "Halda prÃŧgikasti seadeid", - "untracked_files": "Mittejälgitavad failid", - "untracked_files_description": "Rakendus ei jälgi neid faile. Need vÃĩivad olla pÃĩhjustatud ebaÃĩnnestunud liigutamisest, katkestatud Ãŧleslaadimisest vÃĩi rakenduse veast", "user_cleanup_job": "Kasutajate korrastamine", "user_delete_delay": "Kasutaja {user} konto ja Ãŧksuste lÃĩplik kustutamine on planeeritud {delay, plural, one {# päeva} other {# päeva}} pärast.", "user_delete_delay_settings": "Kustutamise viivitus", @@ -402,10 +389,6 @@ "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}", @@ -421,6 +404,9 @@ "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}}", + "albums_default_sort_order": "Vaikimisi albumi järjestus", + "albums_default_sort_order_description": "Uute albumite lisamisel Ãŧksuste esialgne järjekord.", + "albums_feature_description": "Üksuste kollektsioonid, mida saab teiste kasutajatega jagada.", "all": "KÃĩik", "all_albums": "KÃĩik albumid", "all_people": "KÃĩik isikud", @@ -479,9 +465,12 @@ "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_cannot_be_added_to_album_count": "{count, plural, one {Üksust} other {Üksuseid}} ei saa albumisse lisada", "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_downloaded_failed": "{count, plural, one {# fail allalaaditud - {error} fail ebaÃĩnnestus} other {# faili allalaaditud - {error} faili ebaÃĩnnestus}}", + "assets_downloaded_successfully": "{count, plural, one {# fail edukalt allalaaditud} other {# faili edukalt allalaaditud}}", "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", @@ -496,6 +485,7 @@ "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", + "autoplay_slideshow": "Esita slaidiesitlus automaatselt", "back": "Tagasi", "back_close_deselect": "Tagasi, sulge vÃĩi tÃŧhista valik", "background_location_permission": "Taustal asukoha luba", @@ -520,6 +510,7 @@ "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_ok": "OK", "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", @@ -538,6 +529,7 @@ "backup_controller_page_excluded": "Välistatud: ", "backup_controller_page_failed": "EbaÃĩnnestunud ({count})", "backup_controller_page_filename": "Failinimi: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Varunduse info", "backup_controller_page_none_selected": "Ühtegi pole valitud", "backup_controller_page_remainder": "Ootel", @@ -575,21 +567,17 @@ "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", @@ -603,6 +591,7 @@ "cannot_undo_this_action": "Sa ei saa seda tagasi vÃĩtta!", "cannot_update_the_description": "Kirjelduse muutmine ebaÃĩnnestus", "cast": "Edasta", + "cast_description": "Seadista saadavalolevaid voogedastuse sihtpunkte", "change_date": "Muuda kuupäeva", "change_description": "Muuda kirjeldust", "change_display_order": "Muuda kuva järjekorda", @@ -620,7 +609,6 @@ "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.", @@ -632,6 +620,7 @@ "clear_all_recent_searches": "TÃŧhjenda hiljutised otsingud", "clear_message": "TÃŧhjenda sÃĩnum", "clear_value": "TÃŧhjenda väärtus", + "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Sisesta parool", "client_cert_import": "Impordi", "client_cert_import_success_msg": "Klientsertifikaat on imporditud", @@ -659,11 +648,13 @@ "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", + "confirm_tag_face": "Kas tahad sildistada selle näo kui {name}?", + "confirm_tag_face_unnamed": "Kas tahad selle näo sildistada?", + "connected_device": "Ühendatud seade", "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", @@ -712,6 +703,7 @@ "daily_title_text_date": "d. MMMM", "daily_title_text_date_year": "d. MMMM yyyy", "dark": "Tume", + "darkTheme": "LÃŧlita tume teema", "date_after": "Kuupäev pärast", "date_and_time": "Kuupäev ja kellaaeg", "date_before": "Kuupäev enne", @@ -757,7 +749,9 @@ "direction": "Suund", "disabled": "Välja lÃŧlitatud", "disallow_edits": "Keela muutmine", + "discord": "Discord", "discover": "Avasta", + "discovered_devices": "Avastatud seadmed", "dismiss_all_errors": "Peida kÃĩik veateated", "dismiss_error": "Peida veateade", "display_options": "Kuva valikud", @@ -773,7 +767,6 @@ "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", @@ -836,6 +829,7 @@ "error_delete_face": "Viga näo kustutamisel", "error_loading_image": "Viga pildi laadimisel", "error_saving_image": "Viga: {error}", + "error_tag_face_bounding_box": "Viga näo sildistamisel - Ãŧmbritseva kasti koordinaate ei Ãĩnnestunud leida", "error_title": "Viga - midagi läks valesti", "errors": { "cannot_navigate_next_asset": "Järgmise Ãŧksuse juurde liikumine ebaÃĩnnestus", @@ -848,7 +842,6 @@ "cant_get_number_of_comments": "Kommentaare ei Ãĩnnestunud leida", "cant_search_people": "Isikuid ei Ãĩnnestunud otsida", "cant_search_places": "Kohti ei Ãĩnnestunud otsida", - "cleared_jobs": "TÃļÃļted eemaldatud: {job}", "error_adding_assets_to_album": "Viga Ãŧksuste albumisse lisamisel", "error_adding_users_to_album": "Viga kasutajate albumisse lisamisel", "error_deleting_shared_user": "Viga jagatud kasutaja kustutamisel", @@ -857,7 +850,6 @@ "error_removing_assets_from_album": "Viga Ãŧksuste albumist eemaldamisel, rohkem infot leiad konsoolilt", "error_selecting_all_assets": "Viga kÃĩigi Ãŧksuste valimisel", "exclusion_pattern_already_exists": "See välistamismuster on juba olemas.", - "failed_job_command": "Käsk {command} ebaÃĩnnestus tÃļÃļtes: {job}", "failed_to_create_album": "Albumi lisamine ebaÃĩnnestus", "failed_to_create_shared_link": "Jagatud lingi lisamine ebaÃĩnnestus", "failed_to_edit_shared_link": "Jagatud lingi muutmine ebaÃĩnnestus", @@ -876,7 +868,6 @@ "paths_validation_failed": "{paths, plural, one {# tee} other {# teed}} ei valideerunud", "profile_picture_transparent_pixels": "Profiilipildis ei tohi olla läbipaistvaid piksleid. Palun suumi sisse ja/vÃĩi liiguta pilti.", "quota_higher_than_disk_size": "Määratud kvoot on suurem kui kettamaht", - "repair_unable_to_check_items": "{count, select, one {Üksuse} other {Üksuste}} kontrollimine ebaÃĩnnestus", "unable_to_add_album_users": "Kasutajate lisamine albumisse ebaÃĩnnestus", "unable_to_add_assets_to_shared_link": "Üksuste jagatud lingile lisamine ebaÃĩnnestus", "unable_to_add_comment": "Kommentaari lisamine ebaÃĩnnestus", @@ -895,7 +886,6 @@ "unable_to_change_visibility": "{count, plural, one {# isiku} other {# isiku}} nähtavuse muutmine ebaÃĩnnestus", "unable_to_complete_oauth_login": "OAuth sisselogimine ebaÃĩnnestus", "unable_to_connect": "Ühendumine ebaÃĩnnestus", - "unable_to_connect_to_server": "Serveriga Ãŧhendumine ebaÃĩnnestus", "unable_to_copy_to_clipboard": "Ei saanud kopeerida lÃĩikelauale, kontrolli, kas kasutad lehte Ãŧle https-i", "unable_to_create_admin_account": "Administraatori konto loomine ebaÃĩnnestus", "unable_to_create_api_key": "Uue API vÃĩtme lisamine ebaÃĩnnestus", @@ -919,14 +909,9 @@ "unable_to_hide_person": "Isiku peitmine ebaÃĩnnestus", "unable_to_link_motion_video": "Liikuva video linkimine ebaÃĩnnestus", "unable_to_link_oauth_account": "OAuth konto Ãŧhendamine ebaÃĩnnestus", - "unable_to_load_album": "Albumi laadimine ebaÃĩnnestus", - "unable_to_load_asset_activity": "Üksuse aktiivsuse laadimine ebaÃĩnnestus", - "unable_to_load_items": "Üksuste laadimine ebaÃĩnnestus", - "unable_to_load_liked_status": "Meeldimise staatuse laadimine ebaÃĩnnestus", "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", @@ -934,11 +919,9 @@ "unable_to_remove_album_users": "Kasutajate albumist eemaldamine ebaÃĩnnestus", "unable_to_remove_api_key": "API vÃĩtme eemaldamine ebaÃĩnnestus", "unable_to_remove_assets_from_shared_link": "Üksuste jagatud lingilt eemaldamine ebaÃĩnnestus", - "unable_to_remove_deleted_assets": "Ühenduseta failide eemaldamine ebaÃĩnnestus", "unable_to_remove_library": "Kogu eemaldamine ebaÃĩnnestus", "unable_to_remove_partner": "Partneri eemaldamine ebaÃĩnnestus", "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", @@ -968,12 +951,12 @@ "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}", @@ -1011,6 +994,7 @@ "file_name_or_extension": "Failinimi vÃĩi -laiend", "filename": "Failinimi", "filetype": "FailitÃŧÃŧp", + "filter": "Filter", "filter_people": "Filtreeri isikuid", "filter_places": "Filtreeri kohti", "find_them_fast": "Leia teda kiiresti nime järgi otsides", @@ -1020,6 +1004,8 @@ "folders": "Kaustad", "folders_feature_description": "Kaustavaate abil failisÃŧsteemis olevate fotode ja videote sirvimine", "forward": "Edasi", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "See funktsionaalsus laadib tÃļÃļtamiseks Google'st väliseid ressursse.", "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", @@ -1066,7 +1052,9 @@ "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", + "host": "Host", "hour": "Tund", + "id": "ID", "ignore_icloud_photos": "Ignoreeri iCloud fotosid", "ignore_icloud_photos_description": "Fotosid, mis on iCloud'is, ei laadita Ãŧles Immich'i serverisse", "image": "Pilt", @@ -1095,6 +1083,7 @@ "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", @@ -1105,6 +1094,12 @@ "invalid_date_format": "Vigane kuupäevaformaat", "invite_people": "Kutsu inimesi", "invite_to_album": "Kutsu albumisse", + "ios_debug_info_fetch_ran_at": "Andmed laaditi {dateTime}", + "ios_debug_info_last_sync_at": "Viimane sÃŧnkroonimine {dateTime}", + "ios_debug_info_no_processes_queued": "Taustaprotsesse pole järjekorras", + "ios_debug_info_no_sync_yet": "Taustal sÃŧnkroonimise tÃļÃļde pole veel käinud", + "ios_debug_info_processes_queued": "{count, plural, one {{count} taustaprotsess järjekorras} other {{count} taustaprotsessi järjekorras}}", + "ios_debug_info_processing_ran_at": "TÃļÃļtlemine käis {dateTime}", "items_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}}", "jobs": "TÃļÃļted", "keep": "Jäta alles", @@ -1113,6 +1108,9 @@ "kept_this_deleted_others": "See Ãŧksus säilitatud ning {count, plural, one {# Ãŧksus} other {# Ãŧksust}} kustutatud", "keyboard_shortcuts": "Kiirklahvid", "language": "Keel", + "language_no_results_subtitle": "Proovi otsinguterminit muuta", + "language_no_results_title": "Ühtegi keelt ei leitud", + "language_search_hint": "Otsi keeli...", "language_setting_description": "Vali oma eelistatud keel", "last_seen": "Viimati nähtud", "latest_version": "Uusim versioon", @@ -1138,6 +1136,7 @@ "list": "Loend", "loading": "Laadimine", "loading_search_results_failed": "Otsitulemuste laadimine ebaÃĩnnestus", + "local_asset_cast_failed": "Ei saa edastada Ãŧksust, mis pole serverisse Ãŧles laaditud", "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", @@ -1151,6 +1150,7 @@ "locked_folder": "Lukustatud kaust", "log_out": "Logi välja", "log_out_all_devices": "Logi kÃĩigist seadmetest välja", + "logged_in_as": "Logitud sisse kasutajana {user}", "logged_out_all_devices": "KÃĩigist seadmetest välja logitud", "logged_out_device": "Seadmest välja logitud", "login": "Logi sisse", @@ -1242,6 +1242,7 @@ "missing": "Puuduvad", "model": "Mudel", "month": "Kuu", + "monthly_title_text_date_format": "MMMM y", "more": "Rohkem", "move": "Liiguta", "move_off_locked_folder": "Liiguta lukustatud kaustast välja", @@ -1277,6 +1278,7 @@ "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_cast_devices_found": "Edastamise seadmeid ei leitud", "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.", @@ -1302,16 +1304,18 @@ "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_locale_description": "Vali oma eelistatud keel. Saad seda hiljem seadetes muuta.", + "onboarding_privacy_description": "Järgnevad (valikulised) funktsioonid sÃĩltuvad välistest teenustest ning neid saab igal ajal seadetest välja lÃŧlitada.", + "onboarding_server_welcome_description": "Alustamiseks rakendame su serveril mÃĩned levinumad seaded.", "onboarding_theme_description": "Vali oma serverile värviteema. Saad seda hiljem seadetes muuta.", - "onboarding_welcome_description": "Algväärtustame mÃĩned levinumad seaded.", + "onboarding_user_welcome_description": "Alustame!", "onboarding_welcome_user": "Tere tulemast, {user}", "online": "Ühendatud", "only_favorites": "Ainult lemmikud", @@ -1328,6 +1332,7 @@ "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", @@ -1367,6 +1372,8 @@ "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": "Luba", + "permission_empty": "Luba ei tohiks olla tÃŧhi", "permission_onboarding_back": "Tagasi", "permission_onboarding_continue_anyway": "Jätka sellegipoolest", "permission_onboarding_get_started": "Alusta", @@ -1396,13 +1403,17 @@ "play_motion_photo": "Esita liikuv foto", "play_or_pause_video": "Esita vÃĩi peata video", "please_auth_to_access": "Ligipääsemiseks palun autendi", + "port": "Port", "preferences_settings_subtitle": "Halda rakenduse eelistusi", "preferences_settings_title": "Eelistused", "preset": "Eelseadistus", "preview": "Eelvaade", "previous": "Eelmine", "previous_memory": "Eelmine mälestus", - "previous_or_next_photo": "Eelmine vÃĩi järgmine foto", + "previous_or_next_day": "Päev edasi/tagasi", + "previous_or_next_month": "Kuu edasi/tagasi", + "previous_or_next_photo": "Foto edasi/tagasi", + "previous_or_next_year": "Aasta edasi/tagasi", "primary": "Peamine", "privacy": "Privaatsus", "profile": "Profiil", @@ -1410,6 +1421,7 @@ "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_github": "GitHub", "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", @@ -1446,6 +1458,7 @@ "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", @@ -1488,6 +1501,7 @@ "remove_from_shared_link": "Eemalda jagatud lingist", "remove_memory": "Eemalda mälestus", "remove_photo_from_memory": "Eemalda foto sellest mälestusest", + "remove_tag": "Eemalda silt", "remove_url": "Eemalda URL", "remove_user": "Eemalda kasutaja", "removed_api_key": "API vÃĩti eemaldatud: {name}", @@ -1594,6 +1608,7 @@ "select_album_cover": "Vali albumi kaanepilt", "select_all": "Vali kÃĩik", "select_all_duplicates": "Vali kÃĩik duplikaadid", + "select_all_in": "Vali kÃĩik grupis {group}", "select_avatar_color": "Vali avatari värv", "select_face": "Vali nägu", "select_featured_photo": "Vali esiletÃĩstetud foto", @@ -1614,6 +1629,7 @@ "server_info_box_server_url": "Serveri URL", "server_offline": "Serveriga Ãŧhendus puudub", "server_online": "Server Ãŧhendatud", + "server_privacy": "Serveri privaatsus", "server_stats": "Serveri statistika", "server_version": "Serveri versioon", "set": "Määra", @@ -1623,6 +1639,7 @@ "set_date_of_birth": "Määra sÃŧnnikuupäev", "set_profile_picture": "Sea profiilipilt", "set_slideshow_to_fullscreen": "Kuva slaidiesitlus täisekraanil", + "set_stack_primary_asset": "Sea peamiseks Ãŧksuseks", "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", @@ -1631,7 +1648,6 @@ "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", @@ -1694,6 +1710,7 @@ "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_info_chip_metadata": "EXIF", "shared_link_manage_links": "Halda jagatud linke", "shared_link_options": "Jagatud lingi valikud", "shared_links": "Jagatud lingid", @@ -1760,6 +1777,7 @@ "start_date": "Alguskuupäev", "state": "Osariik", "status": "Staatus", + "stop_casting": "LÃĩpeta edastamine", "stop_motion_photo": "Peata liikuv foto", "stop_photo_sharing": "LÃĩpeta oma fotode jagamine?", "stop_photo_sharing_description": "{partner} ei pääse rohkem su fotodele ligi.", @@ -1817,7 +1835,6 @@ "to_parent": "Tase Ãŧles", "to_trash": "PrÃŧgikasti", "toggle_settings": "Kuva/peida seaded", - "toggle_theme": "LÃŧlita tume teema", "total": "Kokku", "total_usage": "Kogukasutus", "trash": "PrÃŧgikast", @@ -1839,6 +1856,7 @@ "unable_to_setup_pin_code": "PIN-koodi seadistamine ebaÃĩnnestus", "unarchive": "Taasta arhiivist", "unarchived_count": "{count, plural, other {# arhiivist taastatud}}", + "undo": "VÃĩta tagasi", "unfavorite": "Eemalda lemmikutest", "unhide_person": "Ära peida isikut", "unknown": "Teadmata", @@ -1855,10 +1873,9 @@ "unsaved_change": "Salvestamata muudatus", "unselect_all": "Ära vali Ãŧhtegi", "unselect_all_duplicates": "Ära vali duplikaate", + "unselect_all_in": "Ära vali Ãŧhtegi grupis {group}", "unstack": "Eralda", "unstacked_assets_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} eraldatud", - "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", @@ -1875,6 +1892,7 @@ "upload_success": "Üleslaadimine Ãĩnnestus, uute Ãŧksuste nägemiseks värskenda lehte.", "upload_to_immich": "Laadi Immich'isse ({count})", "uploading": "Üleslaadimine", + "url": "URL", "usage": "Kasutus", "use_biometric": "Kasuta biomeetriat", "use_current_connection": "kasuta praegust Ãŧhendust", @@ -1885,6 +1903,7 @@ "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_privacy": "Kasutaja privaatsus", "user_purchase_settings": "Ost", "user_purchase_settings_description": "Halda oma ostu", "user_role_set": "Määra kasutajale {user} roll {role}", @@ -1900,13 +1919,9 @@ "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 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", @@ -1923,6 +1938,7 @@ "view_previous_asset": "Vaata eelmist Ãŧksust", "view_qr_code": "Vaata QR-koodi", "view_stack": "Vaata virna", + "view_user": "Vaata kasutajat", "viewer_remove_from_stack": "Eemalda virnast", "viewer_stack_use_as_main_asset": "Kasuta peamise Ãŧksusena", "viewer_unstack": "Eralda", diff --git a/i18n/eu.json b/i18n/eu.json index f2840bfebd..f7f695a2d2 100644 --- a/i18n/eu.json +++ b/i18n/eu.json @@ -12,7 +12,6 @@ "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 f0b99a81ca..84857479b3 100644 --- a/i18n/fa.json +++ b/i18n/fa.json @@ -33,7 +33,6 @@ "authentication_settings_disable_all": "Øĸیا Ų…ØˇŲ…ØĻŲ† Ų‡ØŗØĒید ÚŠŲ‡ Ų…ÛŒâ€ŒØŽŲˆØ§Ų‡ÛŒØ¯ ØĒŲ…Ø§Ų… ØąŲˆØ´â€ŒŲ‡Ø§ÛŒ ŲˆØąŲˆØ¯ ØąØ§ ØēÛŒØąŲØšØ§Ų„ ÚŠŲ†ÛŒØ¯ØŸ ŲˆØąŲˆØ¯ Ø¨Ų‡ ØˇŲˆØą ÚŠØ§Ų…Ų„ ØēÛŒØąŲØšØ§Ų„ ØŽŲˆØ§Ų‡Ø¯ شد.", "authentication_settings_reenable": "Ø¨ØąØ§ÛŒ ŲØšØ§Ų„ ØŗØ§Ø˛ÛŒ Ų…ØŦدد Ø§Ø˛ Ø¯ØŗØĒŲˆØą ØŗØąŲˆØą Ø§ØŗØĒŲØ§Ø¯Ų‡ ÚŠŲ†ÛŒØ¯.", "background_task_job": "ŲˆØ¸Ø§ÛŒŲ ŲžØŗâ€ŒØ˛Ų…ÛŒŲ†Ų‡", - "check_all": "Ø¨ØąØąØŗÛŒ Ų‡Ų…Ų‡", "cleared_jobs": "ŲˆØ¸Ø§ÛŒŲ ŲžØ§ÚŠ Ø´Ø¯Ų‡ Ø¨ØąØ§ÛŒ:{job}", "config_set_by_file": "ØĒŲ†Ø¸ÛŒŲ… ŲØšŲ„ÛŒ ØĒŲˆØŗØˇ یڊ ŲØ§ÛŒŲ„ ŲžÛŒÚŠØąØ¨Ų†Ø¯ÛŒ Ø§Ų†ØŦØ§Ų… Ø´Ø¯Ų‡ Ø§ØŗØĒ", "confirm_delete_library": "Øĸیا Ų…ØˇŲ…ØĻŲ† Ų‡ØŗØĒید ÚŠŲ‡ Ų…ÛŒâ€ŒØŽŲˆØ§Ų‡ÛŒØ¯ ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡ {library} ØąØ§ Ø­Ø°Ų ÚŠŲ†ÛŒØ¯ØŸ", @@ -44,14 +43,12 @@ "disable_login": "ØēÛŒØąŲØšØ§Ų„ ÚŠØąØ¯Ų† ŲˆØąŲˆØ¯", "duplicate_detection_job_description": "اØŦØąØ§ÛŒ ÛŒØ§Ø¯Ú¯ÛŒØąÛŒ Ų…Ø§Ø´ÛŒŲ† Ø¨Øą ØąŲˆÛŒ ŲØ§ÛŒŲ„â€ŒŲ‡Ø§ Ø¨ØąØ§ÛŒ Ø´Ų†Ø§ØŗØ§ÛŒÛŒ ØĒØĩØ§ŲˆÛŒØą Ų…Ø´Ø§Ø¨Ų‡. Ø§ÛŒŲ† ŲˆØ§Ø¨ØŗØĒŲ‡ Ø¨Ų‡ ØŦØŗØĒØŦŲˆÛŒ Ų‡ŲˆØ´Ų…Ų†Ø¯ Ø§ØŗØĒ", "exclusion_pattern_description": "Ø§Ų„Ú¯ŲˆŲ‡Ø§ÛŒ Ø§ØŗØĒØĢŲ†Ø§ Ø¨Ų‡ Ø´Ų…Ø§ Ø§Ų…ÚŠØ§Ų† Ų…ÛŒâ€ŒØ¯Ų‡Ø¯ Ų‡Ų†Ú¯Ø§Ų… Ø§ØŗÚŠŲ† ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡ ØŽŲˆØ¯ ŲØ§ÛŒŲ„â€ŒŲ‡Ø§ ؈ ŲžŲˆØ´Ų‡â€ŒŲ‡Ø§ ØąØ§ Ų†Ø§Ø¯ÛŒØ¯Ų‡ Ø¨Ú¯ÛŒØąÛŒØ¯ . Ø§ÛŒŲ† Ų…ŲÛŒØ¯ Ø§ØŗØĒ Ø§Ú¯Øą ŲžŲˆØ´Ų‡â€ŒŲ‡Ø§ÛŒÛŒ Ø¯Ø§ØąÛŒØ¯ ÚŠŲ‡ ŲØ§ÛŒŲ„â€ŒŲ‡Ø§ÛŒÛŒ ØąØ§ Ø´Ø§Ų…Ų„ Ų…ÛŒâ€ŒØ´ŲˆŲ†Ø¯ ÚŠŲ‡ Ų†Ų…ÛŒâ€ŒØŽŲˆØ§Ų‡ÛŒØ¯ ŲˆØ§ØąØ¯ ÚŠŲ†ÛŒØ¯ØŒ Ų…Ø§Ų†Ų†Ø¯ ŲØ§ÛŒŲ„â€ŒŲ‡Ø§ÛŒ RAW.", - "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_description": "ŲØąŲ…ØĒ WebP ŲØ§ÛŒŲ„â€ŒŲ‡Ø§ÛŒ ÚŠŲˆÚ†ÚŠØĒØąÛŒ Ų†ØŗØ¨ØĒ Ø¨Ų‡ JPEG ایØŦاد Ų…ÛŒâ€ŒÚŠŲ†Ø¯ØŒ Ø§Ų…Ø§ Ø˛Ų…Ø§Ų† ÚŠØ¯Ú¯Ø°Ø§ØąÛŒ ØĸŲ† ÚŠŲ†Ø¯ØĒØą Ø§ØŗØĒ.", "image_prefer_embedded_preview": "ØĒØąØŦیحاØĒ ŲžÛŒØ´â€ŒŲ†Ų…Ø§ÛŒØ´ ØĒØšØ¨ÛŒŲ‡â€ŒØ´Ø¯Ų‡", "image_prefer_embedded_preview_setting_description": "Ø§ØŗØĒŲØ§Ø¯Ų‡ Ø§Ø˛ ŲžÛŒØ´â€ŒŲ†Ų…Ø§ÛŒØ´ Ø¯Ø§ØŽŲ„ÛŒ Ø¯Øą ØšÚŠØŗâ€ŒŲ‡Ø§ÛŒ RAW Ø¨Ų‡ ØšŲ†ŲˆØ§Ų† ŲˆØąŲˆØ¯ÛŒ ŲžØąØ¯Ø§Ø˛Ø´ ØĒØĩŲˆÛŒØą Ų‡Ų†Ú¯Ø§Ų…ÛŒ ÚŠŲ‡ Ø¯Øą Ø¯ØŗØĒØąØŗ باشد. Ø§ÛŒŲ† Ų…ÛŒâ€ŒØĒŲˆØ§Ų†Ø¯ ØąŲ†Ú¯â€ŒŲ‡Ø§ÛŒ Ø¯Ų‚ÛŒŲ‚â€ŒØĒØąÛŒ ØąØ§ Ø¨ØąØ§ÛŒ Ø¨ØąØŽÛŒ ØĒØĩØ§ŲˆÛŒØą ØĒŲˆŲ„ÛŒØ¯ ÚŠŲ†Ø¯ØŒ Ø§Ų…Ø§ ÚŠÛŒŲÛŒØĒ ŲžÛŒØ´â€ŒŲ†Ų…Ø§ÛŒØ´ Ø¨Ų‡ Ø¯ŲˆØąØ¨ÛŒŲ† Ø¨ØŗØĒگی Ø¯Ø§ØąØ¯ ؈ Ų…Ų…ÚŠŲ† Ø§ØŗØĒ ØĒØĩŲˆÛŒØą ØĸØĢØ§Øą ŲØ´ØąØ¯Ų‡â€ŒØŗØ§Ø˛ÛŒ بیشØĒØąÛŒ داشØĒŲ‡ باشد.", @@ -164,8 +161,6 @@ "oauth_storage_quota_claim_description": "ØĒŲ†Ø¸ÛŒŲ… ØŽŲˆØ¯ÚŠØ§Øą ØŗŲ‡Ų…ÛŒŲ‡ Ø°ØŽÛŒØąŲ‡â€ŒØŗØ§Ø˛ÛŒ ÚŠØ§ØąØ¨Øą Ø¨Ų‡ Ų…Ų‚Ø¯Ø§Øą Ø¯ØąØŽŲˆØ§ØŗØĒ Ø´Ø¯Ų‡.", "oauth_storage_quota_default": "Ų…Ų‚Ø¯Ø§Øą ØŗŲ‡Ų…ÛŒŲ‡ Ø°ØŽÛŒØąŲ‡â€ŒØŗØ§Ø˛ÛŒ ŲžÛŒØ´â€ŒŲØąØļ (گیگابایØĒ)", "oauth_storage_quota_default_description": "ØŗŲ‡Ų…ÛŒŲ‡ Ø¨Ų‡ گیگابایØĒ Ų‡Ų†Ú¯Ø§Ų…ÛŒ ÚŠŲ‡ Ø¯ØąØŽŲˆØ§ØŗØĒی Ø§ØąØ§ØĻŲ‡ Ų†Ø´Ø¯Ų‡ باشد (Ø¨ØąØ§ÛŒ ØŗŲ‡Ų…ÛŒŲ‡ Ų†Ø§Ų…Ø­Ø¯ŲˆØ¯ ؚدد 0 ØąØ§ ŲˆØ§ØąØ¯ ÚŠŲ†ÛŒØ¯).", - "offline_paths": "Ų…ØŗÛŒØąŲ‡Ø§ÛŒ ØĸŲŲ„Ø§ÛŒŲ†", - "offline_paths_description": "Ø§ÛŒŲ† Ų†ØĒایØŦ Ų…Ų…ÚŠŲ† Ø§ØŗØĒ Ų†Ø§Ø´ÛŒ Ø§Ø˛ Ø­Ø°Ų Ø¯ØŗØĒی ŲØ§ÛŒŲ„â€ŒŲ‡Ø§ÛŒÛŒ باشد ÚŠŲ‡ Ų‚ØŗŲ…ØĒی Ø§Ø˛ یڊ ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡ ØŽØ§ØąØŦی Ų†ÛŒØŗØĒŲ†Ø¯.", "password_enable_description": "ŲˆØąŲˆØ¯ با Ø§ÛŒŲ…ÛŒŲ„ ؈ Ú¯Ø°ØąŲˆØ§Ú˜Ų‡", "password_settings": "Ú¯Ø°ØąŲˆØ§Ú˜Ų‡ ŲˆØąŲˆØ¯", "password_settings_description": "Ų…Ø¯ÛŒØąÛŒØĒ ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ú¯Ø°ØąŲˆØ§Ú˜Ų‡ ŲˆØąŲˆØ¯", @@ -174,7 +169,6 @@ "refreshing_all_libraries": "Ø¨ØąŲˆØ˛ ØąØŗØ§Ų†ÛŒ Ų‡Ų…Ų‡ ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡ Ų‡Ø§", "registration": "ØĢبØĒ Ų†Ø§Ų… Ų…Ø¯ÛŒØą", "registration_description": "Ø§Ø˛ ØĸŲ†ØŦایی ÚŠŲ‡ Ø´Ų…Ø§ Ø§ŲˆŲ„ÛŒŲ† ÚŠØ§ØąØ¨Øą Ø¯Øą ØŗÛŒØŗØĒŲ… Ų‡ØŗØĒید، Ø¨Ų‡ ØšŲ†ŲˆØ§Ų† Ų…Ø¯ÛŒØą ØĒØšÛŒÛŒŲ† Ø´Ø¯Ų‡â€ŒØ§ÛŒØ¯ ؈ Ų…ØŗØĻŲˆŲ„ÛŒØĒ Ø§Ų†ØŦØ§Ų… ŲˆØ¸Ø§ÛŒŲ Ų…Ø¯ÛŒØąÛŒØĒی Ø¨Øą ØšŲ‡Ø¯Ų‡ Ø´Ų…Ø§ ØŽŲˆØ§Ų‡Ø¯ Ø¨ŲˆØ¯ ؈ ÚŠØ§ØąØ¨ØąØ§Ų† اØļØ§ŲÛŒ ØĒŲˆØŗØˇ Ø´Ų…Ø§ ایØŦاد ØŽŲˆØ§Ų‡Ų†Ø¯ شد.", - "repair_all": "Ø¨Ø§Ø˛ØŗØ§Ø˛ÛŒ Ų‡Ų…Ų‡", "require_password_change_on_login": "Ø§Ų„Ø˛Ø§Ų… ÚŠØ§ØąØ¨Øą Ø¨Ų‡ ØĒØēÛŒÛŒØą Ú¯Ø°ØąŲˆØ§Ú˜Ų‡ Ø¯Øą Ø§ŲˆŲ„ÛŒŲ† ŲˆØąŲˆØ¯", "reset_settings_to_default": "Ø¨Ø§Ø˛Ų†Ø´Ø§Ų†ÛŒ ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø¨Ų‡ Ø­Ø§Ų„ØĒ ŲžÛŒØ´â€ŒŲØąØļ", "reset_settings_to_recent_saved": "Ø¨Ø§Ø˛Ų†Ø´Ø§Ų†ÛŒ ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø¨Ų‡ ØĸØŽØąÛŒŲ† ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø°ØŽÛŒØąŲ‡ Ø´Ø¯Ų‡", @@ -198,7 +192,6 @@ "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": "Ų…Ø¯ÛŒØąÛŒØĒ ØŗØ§ØŽØĒØ§Øą ŲžŲˆØ´Ų‡ ؈ Ų†Ø§Ų… ŲØ§ÛŒŲ„ Ø¯Ø§ØąØ§ÛŒÛŒ Ø¨Ø§ØąÚ¯Ø°Ø§ØąÛŒ Ø´Ø¯Ų‡", @@ -208,7 +201,6 @@ "theme_custom_css_settings_description": "Ø¨ØąÚ¯Ų‡â€ŒŲ‡Ø§ÛŒ ØŗØ¨ÚŠ ØĸØ¨Ø´Ø§ØąÛŒ (CSS) Ø§Ų…ÚŠØ§Ų† ØŗŲØ§ØąØ´ÛŒâ€ŒØŗØ§Ø˛ÛŒ ØˇØąØ§Ø­ÛŒ Immich ØąØ§ ŲØąØ§Ų‡Ų… Ų…ÛŒâ€ŒÚŠŲ†Ų†Ø¯.", "theme_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ ŲžŲˆØŗØĒŲ‡", "theme_settings_description": "Ų…Ø¯ÛŒØąÛŒØĒ ØŗŲØ§ØąØ´ÛŒâ€ŒØŗØ§Ø˛ÛŒ ØąØ§Ø¨Øˇ ÚŠØ§ØąØ¨ØąÛŒ ŲˆØ¨ Immich", - "these_files_matched_by_checksum": "Ø§ÛŒŲ† ŲØ§ÛŒŲ„â€ŒŲ‡Ø§ با Ø§ØŗØĒŲØ§Ø¯Ų‡ Ø§Ø˛ Ú†ÚŠâ€ŒØŗØ§Ų…â€ŒŲ‡Ø§ÛŒØ´Ø§Ų† Ų…ØˇØ§Ø¨Ų‚ØĒ Ø¯Ø§ØąŲ†Ø¯", "thumbnail_generation_job": "ایØŦاد ØĒØĩØ§ŲˆÛŒØą Ø¨Ų†Ø¯Ø§Ų†Ú¯Ø´ØĒی", "thumbnail_generation_job_description": "ایØŦاد ØĒØĩØ§ŲˆÛŒØą Ø¨Ų†Ø¯Ø§Ų†Ú¯Ø´ØĒی Ø¨Ø˛ØąÚ¯ØŒ ÚŠŲˆÚ†ÚŠ ؈ ØĒØ§Øą Ø¨ØąØ§ÛŒ Ų‡Øą Ø¯Ø§ØąØ§ÛŒÛŒØŒ Ų‡Ų…Ú†Ų†ÛŒŲ† ØĒØĩØ§ŲˆÛŒØą Ø¨Ų†Ø¯Ø§Ų†Ú¯Ø´ØĒی Ø¨ØąØ§ÛŒ Ų‡Øą ŲØąØ¯", "transcoding_acceleration_api": "API Ø´ØĒØ§Ø¨â€ŒØ¯Ų‡Ų†Ø¯Ų‡", @@ -236,7 +228,6 @@ "transcoding_hardware_acceleration": "Ø´ØĒاب Ø¯Ų‡Ų†Ø¯Ų‡ ØŗØŽØĒ Ø§ŲØ˛Ø§ØąÛŒ", "transcoding_hardware_acceleration_description": "ØĸØ˛Ų…Ø§ÛŒØ´ÛŒØ› Ø¨ØŗÛŒØ§Øą ØŗØąÛŒØšâ€ŒØĒØą Ø§ØŗØĒ، Ø§Ų…Ø§ Ø¯Øą Ų‡Ų…Ø§Ų† بیØĒâ€ŒØąÛŒØĒ ÚŠÛŒŲÛŒØĒ ÚŠŲ…ØĒØąÛŒ ØŽŲˆØ§Ų‡Ø¯ داشØĒ", "transcoding_hardware_decoding": "ØąŲ…Ø˛Ú¯Ø´Ø§ÛŒÛŒ ØŗØŽØĒ Ø§ŲØ˛Ø§ØąÛŒ", - "transcoding_hevc_codec": "ڊدڊ HEVC", "transcoding_max_b_frames": "بیشØĒØąÛŒŲ† B-frames", "transcoding_max_b_frames_description": "Ų…Ų‚Ø§Ø¯ÛŒØą Ø¨Ø§Ų„Ø§ØĒØą ÚŠØ§ØąØ§ÛŒÛŒ ŲØ´ØąØ¯Ų‡ ØŗØ§Ø˛ÛŒ ØąØ§ Ø¨Ų‡Ø¨ŲˆØ¯ Ų…ÛŒâ€ŒØ¨ØŽØ´Ų†Ø¯ØŒ Ø§Ų…Ø§ ÚŠØ¯Ú¯Ø°Ø§ØąÛŒ ØąØ§ ÚŠŲ†Ø¯ Ų…ÛŒâ€ŒÚŠŲ†Ų†Ø¯. Ų…Ų…ÚŠŲ† Ø§ØŗØĒ با Ø´ØĒاب Ø¯Ų‡ÛŒ ØŗØŽØĒâ€ŒØ§ŲØ˛Ø§ØąÛŒ Ø¯Øą Ø¯ØŗØĒÚ¯Ø§Ų‡â€ŒŲ‡Ø§ÛŒ Ų‚Ø¯ÛŒŲ…ÛŒ ØŗØ§Ø˛Ú¯Ø§Øą Ų†Ø¨Ø§Ø´Ø¯. Ų…Ų‚Ø¯Ø§Øą( 0 ) B-frames ØąØ§ ØēÛŒØąŲØšØ§Ų„ Ų…ÛŒâ€ŒÚŠŲ†Ø¯ØŒ Ø¯Øą Ø­Ø§Ų„ÛŒ ÚŠŲ‡ Ų…Ų‚Ø¯Ø§Øą ( 1 ) Ø§ÛŒŲ† Ų…Ų‚Ø¯Ø§Øą ØąØ§ Ø¨Ų‡ ØĩŲˆØąØĒ ØŽŲˆØ¯ÚŠØ§Øą ØĒŲ†Ø¸ÛŒŲ… Ų…ÛŒâ€ŒÚŠŲ†Ø¯.", "transcoding_max_bitrate": "بیشØĒØąÛŒŲ† بیØĒ ØąÛŒØĒ", @@ -271,8 +262,6 @@ "trash_number_of_days_description": "ØĒؚداد ØąŲˆØ˛Ų‡Ø§ÛŒÛŒ ÚŠŲ‡ Ø¯Ø§ØąØ§ÛŒÛŒ Ų‡Ø§(ØšÚŠØŗŲ‡Ø§ ؈ ŲÛŒŲ…Ų„Ų‡Ø§) Ø¯Øą Ø˛Ø¨Ø§Ų„Ų‡ Ø¯Ø§Ų†(ØŗØˇŲ„ Ø¨Ø§Ø˛ÛŒØ§ŲØĒ) Ų‚Ø¨Ų„ Ø§Ø˛ Ø­Ø°Ų داØĻŲ…ÛŒ Ų†Ú¯Ų‡Ø¯Ø§ØąÛŒ Ų…ÛŒØ´ŲˆŲ†Ø¯", "trash_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ ØŗØˇŲ„ Ø¨Ø§Ø˛ÛŒØ§ŲØĒ (ØŗØˇŲ„ Ø˛Ø¨Ø§Ų„Ų‡)", "trash_settings_description": "Ų…Ø¯ÛŒØąÛŒØĒ ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ ØŗØˇŲ„ Ø¨Ø§Ø˛ÛŒØ§ŲØĒ (ØŗØˇŲ„ Ø˛Ø¨Ø§Ų„Ų‡)", - "untracked_files": "ŲØ§ÛŒŲ„ Ų‡Ø§ÛŒ ØąØ¯ÛŒØ§Ø¨ÛŒ Ų†Ø´Ø¯Ų‡", - "untracked_files_description": "Ø§ÛŒŲ† ŲØ§ÛŒŲ„ Ų‡Ø§ ØĒŲˆØŗØˇ Ø¨ØąŲ†Ø§Ų…Ų‡ ØąØ¯ÛŒØ§Ø¨ÛŒ Ų†Ų…ÛŒ Ø´ŲˆŲ†Ø¯. Ų…ÛŒ ØĒŲˆØ§Ų†Ų†Ø¯ Ų†ØĒیØŦŲ‡ Ø§Ų†ØĒŲ‚Ø§Ų„ Ų†Ø§Ų…ŲˆŲŲ‚ØŒ Ø¨Ø§ØąÚ¯Ø°Ø§ØąÛŒ Ų…ØĒŲˆŲ‚Ų Ø´Ø¯Ų‡ یا Ø¨Ų‡ Ø¯Ų„ÛŒŲ„ یڊ باگ Ø¨Ø§Ų‚ÛŒ Ų…Ø§Ų†Ø¯Ų‡ Ø¨Ø§Ø´Ų†Ø¯", "user_delete_delay": "{user}'s Ø­ØŗØ§Ø¨ ÚŠØ§ØąØ¨ØąÛŒ ؈ Ø¯Ø§ØąØ§ÛŒÛŒ Ų‡Ø§(ØšÚŠØŗ ؈ ŲÛŒŲ„Ų…) Ø¨ØąØ§ÛŒ Ø­Ø°Ų داØĻŲ…ÛŒ Ø¯Øą {delay, plural, one {# ØąŲˆØ˛} other {# ØąŲˆØ˛}} Ø¨ØąŲ†Ø§Ų…Ų‡ ØąÛŒØ˛ÛŒ ØŽŲˆØ§Ų‡Ų†Ø¯ شد.", "user_delete_delay_settings": "ØĒØŖØŽÛŒØą Ø¯Øą Ø­Ø°Ų", "user_delete_delay_settings_description": "ØĒؚداد ØąŲˆØ˛Ų‡Ø§ÛŒÛŒ ÚŠŲ‡ ŲžØŗ Ø§Ø˛ Ø­Ø°ŲØŒ Ø­ØŗØ§Ø¨ ÚŠØ§ØąØ¨ØąÛŒ ؈ Ø¯Ø§ØąØ§ÛŒÛŒ Ų‡Ø§ÛŒ(ØšÚŠØŗ ؈ ŲÛŒŲ„Ų…) ÚŠØ§ØąØ¨Øą Ø¨Ų‡ ØˇŲˆØą داØĻŲ…ÛŒ Ø­Ø°Ų Ų…ÛŒ Ø´ŲˆŲ†Ø¯. ÚŠØ§Øą Ø­Ø°Ų ÚŠØ§ØąØ¨Øą Ø¯Øą Ų†ÛŒŲ…Ų‡ شب اØŦØąØ§ Ų…ÛŒ Ø´ŲˆØ¯ ØĒا ÚŠØ§ØąØ¨ØąØ§Ų†ÛŒ ÚŠŲ‡ ØĸŲ…Ø§Ø¯Ų‡ Ø­Ø°Ų Ų‡ØŗØĒŲ†Ø¯ ØąØ§ Ø¨ØąØąØŗÛŒ ÚŠŲ†Ø¯. ØĒØēÛŒÛŒØąØ§ØĒ Ø¯Øą Ø§ÛŒŲ† ØĒŲ†Ø¸ÛŒŲ… Ø¯Øą اØŦØąØ§ÛŒ بؚدی Ø§ØąØ˛ÛŒØ§Ø¨ÛŒ ØŽŲˆØ§Ų‡Ų†Ø¯ شد.", @@ -334,7 +323,6 @@ "change_name_successfully": "Ų†Ø§Ų… با Ų…ŲˆŲŲ‚ÛŒØĒ ØĒØēÛŒÛŒØą ÛŒØ§ŲØĒ", "change_password": "ØĒØēÛŒÛŒØą ØąŲ…Ø˛ ØšØ¨ŲˆØą", "change_your_password": "ØąŲ…Ø˛ ØšØ¨ŲˆØą ØŽŲˆØ¯ ØąØ§ ØĒØēÛŒÛŒØą Ø¯Ų‡ÛŒØ¯", - "check_all": "Ø§Ų†ØĒ؎اب Ų‡Ų…Ų‡", "check_logs": "Ø¨ØąØąØŗÛŒ Ų„Ø§Ú¯â€ŒŲ‡Ø§", "city": "Ø´Ų‡Øą", "clear": "ŲžØ§ÚŠ ÚŠØąØ¯Ų†", @@ -494,15 +482,38 @@ "login_has_been_disabled": "ŲˆØąŲˆØ¯ ØēÛŒØąŲØšØ§Ų„ Ø´Ø¯Ų‡ Ø§ØŗØĒ.", "look": "Ų†Ú¯Ø§Ų‡ ÚŠØąØ¯Ų†", "loop_videos": "ŲžØŽØ´ Ų…Ø¯Ø§ŲˆŲ… ŲˆÛŒØ¯ØĻŲˆŲ‡Ø§", + "main_branch_warning": "Ø´Ų…Ø§ Ø¯Øą Ø­Ø§Ų„ Ø§ØŗØĒŲØ§Ø¯Ų‡ Ø§Ø˛ Ų†ØŗØŽŲ‡ ØĒŲˆØŗØšŲ‡â€ŒØ¯Ų‡Ų†Ø¯Ú¯Ø§Ų† Ų‡ØŗØĒید ÚŠŲ‡ ØĸØ˛Ų…Ø§ÛŒØ´ÛŒ ؈ Ų†Ø§ŲžØ§ÛŒØ¯Ø§Øą Ø§ØŗØĒ. Ø§ÚŠÛŒØ¯Ø§Ų‹ ØĒ؈ØĩÛŒŲ‡ Ų…ÛŒâ€ŒÚŠŲ†ÛŒŲ… Ø§Ø˛ Ų†ØŗØŽŲ‡ ØąØŗŲ…ÛŒ Ø§ØŗØĒŲØ§Ø¯Ų‡ ÚŠŲ†ÛŒØ¯!", + "main_menu": "Ų…Ų†ŲˆÛŒ اØĩŲ„ÛŒ", "make": "ØŗØ§ØŽØĒŲ†", "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": "{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": "ØŗØąŲˆÛŒØŗ Ų…ÚŠØ§Ų†â€ŒÛŒØ§Ø¨ÛŒ ØēÛŒØąŲØšØ§Ų„ Ø§ØŗØĒ", + "map_marker_for_images": "Ų†Ø´Ø§Ų†Ú¯Øą ØąŲˆÛŒ Ų†Ų‚Ø´Ų‡ Ø¨ØąØ§ÛŒ ØšÚŠØŗâ€ŒŲ‡Ø§ÛŒ Ú¯ØąŲØĒŲ‡â€ŒØ´Ø¯Ų‡ Ø¯Øą {city}, {country}", + "map_marker_with_image": "ØšŲ„Ø§Ų…ØĒâ€ŒÚ¯Ø°Ø§ØąÛŒ Ų†Ų‚Ø´Ų‡ با ØšÚŠØŗ", + "map_no_assets_in_bounds": "Ų‡ÛŒÚ† ØšÚŠØŗÛŒ Ø¯Øą Ø§ÛŒŲ† Ų…Ø­Ø¯ŲˆØ¯Ų‡ Ų†ÛŒØŗØĒ", + "map_no_location_permission_content": "Ø¨ØąØ§ÛŒ Ų†Ų…Ø§ÛŒØ´ ØšÚŠØŗâ€ŒŲ‡Ø§ÛŒ Ø§ØˇØąØ§ŲØĒØ§Ų†ØŒ Ø¨ØąŲ†Ø§Ų…Ų‡ Ų†ÛŒØ§Ø˛ Ø¨Ų‡ Ø¯ØŗØĒØąØŗÛŒ Ø¨Ų‡ Ų…ŲˆŲ‚ØšÛŒØĒ Ų…ÚŠØ§Ų†ÛŒ Ø¯Ø§ØąØ¯. اØŦØ§Ø˛Ų‡ Ø¯ØŗØĒØąØŗÛŒ Ų…ÛŒâ€ŒØ¯Ų‡ÛŒØ¯ØŸ", + "map_no_location_permission_title": "Ø¯ØŗØĒØąØŗÛŒ Ø¨Ų‡ Ų…ŲˆŲ‚ØšÛŒØĒ Ø´Ų…Ø§ ŲØšØ§Ų„ Ų†ÛŒØŗØĒ", "map_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ų†Ų‚Ø´Ų‡", + "map_settings_dark_mode": "Ø­Ø§Ų„ØĒ ØĒØ§ØąÛŒÚŠ", + "map_settings_date_range_option_day": "Û˛Û´ ØŗØ§ØšØĒ گذشØĒŲ‡", + "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": "Ø´Ø§Ų…Ų„ Ų‡Ų…ØŗØą", "matches": "ØĒØˇØ§Ø¨Ų‚â€ŒŲ‡Ø§", "media_type": "Ų†ŲˆØš ØąØŗØ§Ų†Ų‡", "memories": "ØŽØ§ØˇØąØ§ØĒ", @@ -541,7 +552,6 @@ "notifications": "Ø§ØšŲ„Ø§Ų†â€ŒŲ‡Ø§", "notifications_setting_description": "Ų…Ø¯ÛŒØąÛŒØĒ Ø§ØšŲ„Ø§Ų†â€ŒŲ‡Ø§", "offline": "ØĸŲŲ„Ø§ÛŒŲ†", - "offline_paths": "Ų…ØŗÛŒØąŲ‡Ø§ÛŒ ØĸŲŲ„Ø§ÛŒŲ†", "ok": "ØĒØŖÛŒÛŒØ¯", "oldest_first": "Ų‚Ø¯ÛŒŲ…ÛŒâ€ŒØĒØąÛŒŲ† ابØĒدا", "online": "ØĸŲ†Ų„Ø§ÛŒŲ†", @@ -693,7 +703,6 @@ "to_archive": "Ø¨Ø§ÛŒÚ¯Ø§Ų†ÛŒ", "to_favorite": "Ø¨Ų‡ ØšŲ„Ø§Ų‚Ų‡â€ŒŲ…Ų†Ø¯ÛŒâ€ŒŲ‡Ø§", "toggle_settings": "ØĒØēÛŒÛŒØą ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ", - "toggle_theme": "ØĒØēÛŒÛŒØą ØĒŲ… ØĒØ§ØąÛŒÚŠ", "total_usage": "Ø§ØŗØĒŲØ§Ø¯Ų‡ ÚŠŲ„ÛŒ", "trash": "ØŗØˇŲ„ Ø˛Ø¨Ø§Ų„Ų‡", "type": "Ų†ŲˆØš", diff --git a/i18n/fi.json b/i18n/fi.json index 5aaf614342..f41d0cf631 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -22,6 +22,7 @@ "add_partner": "Lisää kumppani", "add_path": "Lisää polku", "add_photos": "Lisää kuvia", + "add_tag": "Lisää tunniste", "add_to": "Lisääâ€Ļ", "add_to_album": "Lisää albumiin", "add_to_album_bottom_sheet_added": "Lisätty albumiin {album}", @@ -33,6 +34,7 @@ "added_to_favorites_count": "{count, number} lisätty suosikkeihin", "admin": { "add_exclusion_pattern_description": "Lisää mallit, jonka mukaan jätetään tiedostoja pois. Jokerimerkit *, ** ja ? ovat tuettuna. Jättääksesi pois kaikki tiedostot mistä tahansa lÃļytyvästä kansiosta \"Raw\" käytä \"**/Raw/**\". Jättääksesi pois kaikki \". tif\" päätteiset tiedot, käytä \"**/*.tif\". Jättääksesi pois tarkan tiedostopolun, käytä \"/path/to/ignore/**\".", + "admin_user": "Ylläpitäjä", "asset_offline_description": "Ulkoista kirjaston resurssia ei enää lÃļydy levyltä, ja se on siirretty roskakoriin. Jos tiedosto siirrettiin kirjaston sisällä, tarkista aikajanaltasi uusi vastaava resurssi. Palautaaksesi tämän resurssin, varmista, että alla oleva tiedostopolku on Immichin käytettävissä ja skannaa kirjasto uudelleen.", "authentication_settings": "Autentikointiasetukset", "authentication_settings_description": "Hallitse salasana-, OAuth- ja muut autentikoinnin asetukset", @@ -43,9 +45,7 @@ "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", + "backup_settings_description": "Hallitse tietokannan vedosasetuksia.", "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}?", @@ -61,14 +61,12 @@ "disable_login": "Poista kirjautuminen käytÃļstä", "duplicate_detection_job_description": "Tunnista samankaltaiset kuvat käyttäen koneoppimista. Tukeutuu Smart Search:iin", "exclusion_pattern_description": "Poissulkemismallit mahdollistavat tiettyjen tiedostojen ja kansioiden jättämisen pois kirjastoasi skannatessa. Tästä on hyÃļtyä jos kansiot sisältävät tiedostoja mitä et halua tuoda, kuten RAW-tiedostot.", - "external_library_created_at": "Ulkoinen kirjasto (luotu {date})", "external_library_management": "Ulkoisen kirjaston hallinta", "face_detection": "Kasvojen havaitseminen", "face_detection_description": "Tunnista sisällÃļn kasvoja käyttäen koneoppimista. Videoiden osalta vain pikkukuva tunnistetaan. \"Päivitä\" (uudelleen)prosessoi koko sisällÃļn.\"Nollaa\" lisäksi puhdistaa kaiken kasvo-datan. \"Puuttuvat\" prosessoi sisällÃļn, jota ei vielä ole käyty läpi. Havaitut kasvot ryhmitellään jo tunnistettujen kanssa, tai lisätään uusina henkilÃļinä.", "facial_recognition_job_description": "Ryhmitä havaitut kasvot henkilÃļihin. Tämä vaihe suoritetaan, kun kasvot on ensin havaittu. \"Nollaus\" (uudelleen-)ryhmittelee kaikki kasvot. \"Puuttuvat\" vain ne, joille ei ole määritetty henkilÃļä.", "failed_job_command": "Komento {command} epäonnistui tyÃļlle {job}", "force_delete_user_warning": "VAROITUS: Tämä poistaa käyttäjän ja kaikki mediat. Tätä ei voi perua, eikä tiedostoja voi palauttaa.", - "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", @@ -173,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Huom: Asettaaksesi nimikkeen aiemmin ladatulle aineistolle, aja", "note_cannot_be_changed_later": "Huom: Tätä ei voi enää myÃļhemmin vaihtaa!", "notification_email_from_address": "Lähettäjän osoite", - "notification_email_from_address_description": "Lähettäjän sähkÃļpostiosoite. Esimerkiksi \"Immich-kuvapalvelin \"", + "notification_email_from_address_description": "Lähettäjän sähkÃļpostiosoite. Esimerkiksi \"Immich-kuvapalvelin \". Varmista, että käytetystä osoiteesta on lupa lähettää sähkÃļposteja.", "notification_email_host_description": "SähkÃļpostipalvelin (esim. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Älä huomioi varmennevirheitä", "notification_email_ignore_certificate_errors_description": "Älä huomioi TLS-varmenteiden validointivirheitä (ei suositeltu)", @@ -197,7 +195,7 @@ "oauth_enable_description": "Kirjaudu käyttäen OAuthia", "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_mobile_redirect_uri_override_description": "Ota käyttÃļÃļn kun OAuth tarjoaja ei salli mobiili URI:a, kuten ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "Hallitse OAuth-kirjautumisen asetuksia", "oauth_settings_more_details": "Saadaksesi lisätietoja tästä toiminnosta, katso dokumentaatio.", @@ -206,11 +204,9 @@ "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_storage_quota_default_description": "Käytettävä kiintiÃļn määrä gigatavuissa, kun väittämää ei ole annettu.", "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", "password_settings": "Kirjaudu salasanalla", "password_settings_description": "Hallitse salasanakirjautumisen asetuksia", @@ -220,9 +216,6 @@ "refreshing_all_libraries": "Virkistetään kaikki kirjastot", "registration": "Pääkäyttäjän rekisterÃļinti", "registration_description": "Pääkäyttäjänä olet vastuussa järjestelmän hallinnallisista tehtävistä ja uusien käyttäjien luomisesta.", - "repair_all": "Korjaa kaikki", - "repair_matched_items": "LÃļytyi {count, plural, one {# osuma} other {# osumaa}}", - "repaired_items": "Korjattiin {count, plural, one {# kohta} other {# kohtaa}}", "require_password_change_on_login": "Vaadi käyttäjää vaihtamaan salasana ensimmäisellä kirjautumiskerralla", "reset_settings_to_default": "Nollaa asetukset oletuksille", "reset_settings_to_recent_saved": "Palauta aiemmin tallennetut asetukset", @@ -251,7 +244,6 @@ "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.", "storage_template_path_length": "Arvioitu tiedostopolun pituusrajoitus: {length, number}/{limit, number}", "storage_template_settings": "Tallennustilan malli", "storage_template_settings_description": "Hallitse palvelimelle ladatun aineiston kansiorakennetta ja tiedostonimiä", @@ -263,16 +255,14 @@ "template_email_invite_album": "Albumikutsun malli", "template_email_preview": "Esikatselu", "template_email_settings": "SähkÃļpostimalli", - "template_email_settings_description": "Hallitse yksilÃļllisten sähkÃļposti-ilmoitusten malleja", "template_email_update_album": "Albumipäivityksen malli", "template_email_welcome": "TervetulosähkÃļpostin malli", "template_settings": "Ilmoitusmallit", - "template_settings_description": "Hallitse yksilÃļllisten ilmoitusten malleja.", + "template_settings_description": "Hallitse yksilÃļllisten ilmoitusten malleja", "theme_custom_css_settings": "Mukautettu CSS", "theme_custom_css_settings_description": "Mukauta Immichin ulkoasua CSS:llä.", "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": "Luo pikkukuvat", "thumbnail_generation_job_description": "Generoi isot, pienet sekä sumeat pikkukuvat jokaisesta aineistosta, kuten myÃļs henkilÃļistä", "transcoding_acceleration_api": "Kiihdytysrajapinta", @@ -300,10 +290,9 @@ "transcoding_encoding_options": "Enkoodausasetukset", "transcoding_encoding_options_description": "Aseta koodekit, tarkkuus, laatu ja muut asetukset enkoodatuille videoille", "transcoding_hardware_acceleration": "Laitteistokiihdytys", - "transcoding_hardware_acceleration_description": "Kokeellinen. Paljon nopeampi, mutta huonompaa laatua samalla bittinopeudella", + "transcoding_hardware_acceleration_description": "Kokeellinen: Mahdollistaa nopeamman transkoodauksen, mutta saattaa alentaa laatua samalla tiedonsiirtonopeudella", "transcoding_hardware_decoding": "Laitteiston dekoodaus", "transcoding_hardware_decoding_setting_description": "Ottaa käyttÃļÃļn end-to-end kiihdytyksen pelkän muuntamisen sijasta. Ei välttämättä toimi kaikissa videoissa.", - "transcoding_hevc_codec": "HEVC-koodekki", "transcoding_max_b_frames": "B-kehysten enimmäismäärä", "transcoding_max_b_frames_description": "Korkeampi arvo parantaa pakkausta, mutta hidastaa enkoodausta. Ei välttämättä ole yhteensopiva vanhempien laitteiden kanssa. 0 poistaa B-kehykset käytÃļstä, -1 määrittää arvon automaattisesti.", "transcoding_max_bitrate": "Suurin bittinopeus", @@ -341,8 +330,6 @@ "trash_number_of_days_description": "Kuinka monta päivää aineistoja pidetään roskakorissa ennen pysyvää poistamista", "trash_settings": "Roskakorin asetukset", "trash_settings_description": "Hallitse roskakoriasetuksia", - "untracked_files": "Tiedostot joita ei seurata", - "untracked_files_description": "Nämä tiedostot eivät ole ohjelman hallitsemia. Ne voivat olla virheellisten siirtojen tai keskeytyneiden latausten tulosta, tai bugista johtuvia jälkeen jääneitä", "user_cleanup_job": "Käyttäjien puhdistus", "user_delete_delay": "Käyttäjän {user} tili ja aineistot aikataulutetaan poistettavaksi ajan kuluttua: {delay, plural, one {# day} other {# days}}.", "user_delete_delay_settings": "Poiston viive", @@ -401,10 +388,6 @@ "album_remove_user": "Poista käyttäjä?", "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": "{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}", @@ -519,6 +502,7 @@ "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", @@ -537,6 +521,7 @@ "backup_controller_page_excluded": "Poissuljettu: ", "backup_controller_page_failed": "Epäonnistui ({count})", "backup_controller_page_filename": "Tiedostonimi: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Varmuuskopioinnin tiedot", "backup_controller_page_none_selected": "Ei mitään", "backup_controller_page_remainder": "Jäljellä", @@ -560,6 +545,10 @@ "backup_options_page_title": "Varmuuskopioinnin asetukset", "backup_setting_subtitle": "Hallinnoi aktiivisia ja taustalla olevia lähetysasetuksia", "backward": "Taaksepäin", + "biometric_auth_enabled": "Biometrinen tunnistautuminen käytÃļssä", + "biometric_locked_out": "Sinulta on evätty pääsy biometriseen tunnistautumiseen", + "biometric_no_options": "Ei biometrisiä vaihtoehtoja", + "biometric_not_available": "Biometrinen tunnistautuminen ei ole käytettävissä tässä laitteessa", "birthdate_saved": "Syntymäaika tallennettu", "birthdate_set_description": "Syntymäaikaa käytetään laskemaan henkilÃļn ikä kuvanottohetkellä.", "blurred_background": "Sumennettu tausta", @@ -570,21 +559,17 @@ "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 ({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 ({count})", - "cache_settings_image_cache_size": "Kuvavälimuistin koko ({count} kohdetta)", "cache_settings_statistics_album": "Kirjaston esikatselukuvat", - "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 ({count} kohdetta)", "cache_settings_tile_subtitle": "Hallitse paikallista tallenustilaa", "cache_settings_tile_title": "Paikallinen tallennustila", "cache_settings_title": "Välimuistin asetukset", @@ -597,7 +582,9 @@ "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ää", + "cast": "Lähettää", "change_date": "Vaihda päiväys", + "change_description": "Muuta kuvausta", "change_display_order": "Muuta näyttÃļjärjestystä", "change_expiration_time": "Muuta erääntymisaikaa", "change_location": "Vaihda sijainti", @@ -613,7 +600,6 @@ "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": "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.", @@ -625,6 +611,9 @@ "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": "Anna salasana", + "client_cert_import": "Tuo", "client_cert_import_success_msg": "Asiakasvarmenne tuotu", "client_cert_invalid_msg": "Virheellinen varmennetiedosto tai väärä salasana", "client_cert_remove_msg": "Asiakassertifikaatti on poistettu", @@ -650,10 +639,12 @@ "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", + "confirm_tag_face": "Haluatko merkitä nämä kasvot nimellä {name}?", + "confirm_tag_face_unnamed": "MerkitäänkÃļ nämä kasvot?", + "connected_to": "Yhdistetty", "contain": "Mahduta", "context": "Konteksti", "continue": "Jatka", - "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", @@ -681,6 +672,7 @@ "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": "LUO UUSI", "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ä", @@ -746,6 +738,7 @@ "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", @@ -760,8 +753,8 @@ "download_canceled": "Lataus peruutettu", "download_complete": "Lataus valmis", "download_enqueue": "Latausjonossa", + "download_error": "Latausvirhe", "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", @@ -785,6 +778,8 @@ "edit_avatar": "Muokkaa avataria", "edit_date": "Muokkaa päiväystä", "edit_date_and_time": "Muokkaa päivämäärää ja kellonaikaa", + "edit_description": "Muokkaa kuvausta", + "edit_description_prompt": "Valitse uusi kuvaus:", "edit_exclusion_pattern": "Muokkaa poissulkemismallia", "edit_faces": "Muokkaa kasvoja", "edit_import_path": "Muokkaa tuontipolkua", @@ -810,15 +805,19 @@ "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", + "enable_biometric_auth_description": "SyÃļtä PIN-koodisi ottaaksesi biometrisen tunnistautumisen käyttÃļÃļn", "enabled": "KäytÃļssä", "end_date": "Päättymispäivä", "enqueued": "Lisätty jonoon", "enter_wifi_name": "Anna Wi-Fi-verkon nimi", + "enter_your_pin_code": "SyÃļtä PIN-koodi", + "enter_your_pin_code_subtitle": "SyÃļtä PIN-koodi päästäksesi lukittuun kansioon", "error": "Virhe", "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": "Virhe: {error}", + "error_tag_face_bounding_box": "Kasvojen merkitseminen epäonnistui – rajausruudun koordinaatteja ei lÃļydy", "error_title": "Virhe - Jotain meni pieleen", "errors": { "cannot_navigate_next_asset": "Seuraavaan mediaan ei voi siirtyä", @@ -831,7 +830,6 @@ "cant_get_number_of_comments": "Kommenttien määrää ei voitu hakea", "cant_search_people": "Ihmisiä ei voitu hakea", "cant_search_places": "Sijainteja ei voitu hakea", - "cleared_jobs": "{job} tyÃļt tyhjätty", "error_adding_assets_to_album": "Medioita ei voitu lisätä albumiin", "error_adding_users_to_album": "Käyttäjiä ei voitu lisätä albumiin", "error_deleting_shared_user": "Jaettua käyttäjää ei voitu poistaa", @@ -840,7 +838,6 @@ "error_removing_assets_from_album": "Medioiden poisto epäonnistui. Katso konsolista lisätietoja", "error_selecting_all_assets": "Kaikkia medioita ei voitu valita", "exclusion_pattern_already_exists": "Tämä poissulkemismalli on jo olemassa.", - "failed_job_command": "Komento {command} tyÃļlle {job} epäonnistui", "failed_to_create_album": "Albumin luonti epäonnistui", "failed_to_create_shared_link": "Jaetun linkin luonti epäonnistui", "failed_to_edit_shared_link": "Jaetun linkin muokkaus epäonnistui", @@ -859,7 +856,6 @@ "paths_validation_failed": "{paths, plural, one {# polun} other {# polun}} validointi epäonnistui", "profile_picture_transparent_pixels": "Profiilikuvassa ei voi olla läpinäkyviä pikseleitä. Zoomaa lähemmäs ja/tai siirrä kuvaa.", "quota_higher_than_disk_size": "Asettamasi kiintiÃļ on suurempi kuin levyn koko", - "repair_unable_to_check_items": "Ei voida tarkistaa {count, select, one {kohdetta} other {kohteita}}", "unable_to_add_album_users": "Käyttäjiä ei voi lisätä albumiin", "unable_to_add_assets_to_shared_link": "Medioiden lisääminen jaettuun linkkiin epäonnistui", "unable_to_add_comment": "Kommentin lisääminen epäonnistui", @@ -871,13 +867,13 @@ "unable_to_archive_unarchive": "Ei voida {archived, select, true {arkistoida} other {poistaa arkistosta}}", "unable_to_change_album_user_role": "Albumin käyttäjän roolia ei voitu muuttaa", "unable_to_change_date": "Päivämäärää ei voitu muuttaa", + "unable_to_change_description": "Kuvausta ei voi muuttaa", "unable_to_change_favorite": "Ei voida muuttaa suosikkia kohteelle", "unable_to_change_location": "Sijainnin muuttaminen epäonnistui", "unable_to_change_password": "Salasanan vaihto epäonnistui", "unable_to_change_visibility": "Ei voida muuttaa näkyvyyttä {count, plural, one {# henkilÃļlle} other {# henkilÃļlle}}", "unable_to_complete_oauth_login": "OAuth-kirjautumista ei voitu suorittaa loppuun", "unable_to_connect": "Yhteyttä ei voitu muodostaa", - "unable_to_connect_to_server": "Palvelimeen ei saatu yhteyttä", "unable_to_copy_to_clipboard": "LeikepÃļydälle ei voitu kopioida, varmista että käytät sivua https-yhteyden kautta", "unable_to_create_admin_account": "Pääkäyttäjän luominen epäonnistui", "unable_to_create_api_key": "Uuden API-avaimen luominen epäonnistui", @@ -901,10 +897,6 @@ "unable_to_hide_person": "HenkilÃļn piilottaminen epäonnistui", "unable_to_link_motion_video": "Liikekuvan linkitys epäonnistui", "unable_to_link_oauth_account": "OAuth-tilin linkittäminen epäonnistui", - "unable_to_load_album": "Albumin lataaminen epäonnistui", - "unable_to_load_asset_activity": "Ei voitu ladata kohteen toimintaa", - "unable_to_load_items": "Kohteiden lataaminen epäonnistui", - "unable_to_load_liked_status": "Ei voitu ladata tykkäyksen tilaa", "unable_to_log_out_all_devices": "Kaikkien laitteiden uloskirjautuminen epäonnistui", "unable_to_log_out_device": "Laitteen uloskirjautuminen epäonnistui", "unable_to_login_with_oauth": "OAuth-kirjautuminen epäonnistui", @@ -915,11 +907,9 @@ "unable_to_remove_album_users": "Käyttäjien poistaminen albumista epäonnistui", "unable_to_remove_api_key": "API-avaimen poistaminen epäonnistui", "unable_to_remove_assets_from_shared_link": "kohteiden poistaminen jaetusta linkistä epäonnistui", - "unable_to_remove_deleted_assets": "Offline-tiedostoja ei voitu poistaa", "unable_to_remove_library": "Kirjaston poistaminen epäonnistui", "unable_to_remove_partner": "Kumppanin poistaminen epäonnistui", "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_reset_pin_code": "PIN-koodin nollaaminen epäonnistui", "unable_to_resolve_duplicate": "Kaksoiskappaleen ratkaiseminen epäonnistui", @@ -949,12 +939,12 @@ "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": "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}", @@ -978,6 +968,7 @@ "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": "Epäonnistui", + "failed_to_authenticate": "Todennus epäonnistui", "failed_to_load_assets": "Kohteiden lataus epäonnistui", "failed_to_load_folder": "Kansion lataaminen epäonnistui", "favorite": "Suosikki", @@ -991,6 +982,7 @@ "file_name_or_extension": "Tiedostonimi tai tiedostopääte", "filename": "Tiedostonimi", "filetype": "Tiedostotyyppi", + "filter": "Suodatin", "filter_people": "Suodata henkilÃļt", "filter_places": "Suodata paikkoja", "find_them_fast": "LÃļydä nopeasti hakemalla nimellä", @@ -1000,6 +992,8 @@ "folders": "Kansiot", "folders_feature_description": "Käytetään kansionäkymää valokuvien ja videoiden selaamiseen järjestelmässä", "forward": "Eteenpäin", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Ominaisuus lataa ulkoisia resursseja Googlelta toimiakseen.", "general": "Yleinen", "get_help": "Hae apua", "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", @@ -1042,10 +1036,13 @@ "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_locked_error_local": "Paikallisten kohteiden siirto lukittuun kansioon ei onnistu, ohitetaan", + "home_page_locked_error_partner": "Kumppanin kohteita ei voi siirtää lukittuun kansioon, ohitetaan", "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", + "id": "ID", "ignore_icloud_photos": "Ohita iCloud-kuvat", "ignore_icloud_photos_description": "iCloudiin tallennettuja kuvia ei ladata Immich-palvelimelle", "image": "Kuva", @@ -1085,6 +1082,10 @@ "invalid_date_format": "Virheellinen päivämäärämuoto", "invite_people": "Kutsu ihmisiä", "invite_to_album": "Kutsu albumiin", + "ios_debug_info_fetch_ran_at": "Noudettu {dateTime}", + "ios_debug_info_last_sync_at": "Viimeisin synkronisointi {dateTime}", + "ios_debug_info_no_processes_queued": "Ei taustaprosesseja jonossa", + "ios_debug_info_no_sync_yet": "Taustasynkronisointia ei ole suoritettu vielä", "items_count": "{count, plural, one {# kpl} other {# kpl}}", "jobs": "Taustatehtävät", "keep": "Säilytä", @@ -1118,6 +1119,7 @@ "list": "Lista", "loading": "Ladataan", "loading_search_results_failed": "Hakutulosten lataaminen epäonnistui", + "local_network": "Lähiverkko", "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", @@ -1126,6 +1128,8 @@ "location_picker_latitude_hint": "SyÃļtä leveysaste", "location_picker_longitude_error": "Lisää kelvollinen pituusaste", "location_picker_longitude_hint": "SyÃļtä pituusaste", + "lock": "Lukitse", + "locked_folder": "Lukittu kansio", "log_out": "Kirjaudu ulos", "log_out_all_devices": "Kirjaudu ulos kaikilta laitteilta", "logged_out_all_devices": "Kaikki laitteet kirjattu ulos", @@ -1219,7 +1223,12 @@ "missing": "Puuttuu", "model": "Malli", "month": "Kuukauden mukaan", + "monthly_title_text_date_format": "MMMM y", "more": "Enemmän", + "move": "Siirrä", + "move_off_locked_folder": "Siirrä pois lukitusta kansiosta", + "move_to_locked_folder": "Siirrä lukittuun kansioon", + "move_to_locked_folder_confirmation": "Nämä kuvat ja videot poistetaan kaikista albumeista, ja ne ovat nähtävissä vain lukitussa kansiossa", "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", @@ -1237,6 +1246,7 @@ "new_password": "Uusi salasana", "new_person": "Uusi henkilÃļ", "new_pin_code": "Uusi PIN-koodi", + "new_pin_code_subtitle": "Tämä on ensimmäinen kerta, kun käytät lukittua kansiota. Luo PIN-koodi päästäksesi tähän sisältÃļÃļn turvallisesti", "new_user_created": "Uusi käyttäjä lisätty", "new_version_available": "UUSI VERSIO SAATAVILLA", "newest_first": "Uusin ensin", @@ -1254,6 +1264,7 @@ "no_explore_results_message": "Lataa lisää kuvia tutkiaksesi kokoelmaasi.", "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_locked_photos_message": "Kuvat ja videot lukitussa kansiossa ovat piilotettuja, eivätkä ne näy selatessasi tai etsiessäsi kirjastoasi.", "no_name": "Ei nimeä", "no_notifications": "Ei ilmoituksia", "no_people_found": "Ei vastaavia henkilÃļitä", @@ -1265,6 +1276,7 @@ "not_selected": "Ei valittu", "note_apply_storage_label_to_previously_uploaded assets": "Huom: Jotta voit soveltaa tallennustunnistetta aiemmin ladattuihin kohteisiin, suorita", "notes": "Muistiinpanot", + "nothing_here_yet": "Ei vielä mitään", "notification_permission_dialog_content": "Ottaaksesi ilmoitukset käyttÃļÃļn, siirry asetuksiin ja valitse 'salli'.", "notification_permission_list_tile_content": "MyÃļnnä käyttÃļoikeus ottaaksesi ilmoitukset käyttÃļÃļn.", "notification_permission_list_tile_enable_button": "Ota ilmoitukset käyttÃļÃļn", @@ -1272,16 +1284,17 @@ "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_paths": "Offline-polut", - "offline_paths_description": "Nämä tulokset voivat johtua tiedostojen manuaalisesta poistamisesta, jotka eivät ole osa ulkoista kirjastoa.", + "offline": "Offline", + "ok": "Ok", "oldest_first": "Vanhin ensin", "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ä", @@ -1336,6 +1349,8 @@ "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": "KäyttÃļoikeus", + "permission_empty": "KäyttÃļoikeus ei saa olla tyhjä", "permission_onboarding_back": "Takaisin", "permission_onboarding_continue_anyway": "Jatka silti", "permission_onboarding_get_started": "Aloittaminen", @@ -1356,6 +1371,7 @@ "pin_code_changed_successfully": "PIN-koodin vaihto onnistui", "pin_code_reset_successfully": "PIN-koodin nollaus onnistui", "pin_code_setup_successfully": "PIN-koodin asettaminen onnistui", + "pin_verification": "PIN-koodin vahvistus", "place": "Sijainti", "places": "Paikat", "places_count": "{count, plural, one {{count, number} Paikka} other {{count, number} Paikkaa}}", @@ -1363,6 +1379,7 @@ "play_memories": "Toista muistot", "play_motion_photo": "Toista Liikekuva", "play_or_pause_video": "Toista tai keskeytä video", + "please_auth_to_access": "Ole hyvä ja kirjaudu sisään", "port": "Portti", "preferences_settings_subtitle": "Hallitse sovelluksen asetuksia", "preferences_settings_title": "Asetukset", @@ -1370,14 +1387,18 @@ "preview": "Esikatselu", "previous": "Edellinen", "previous_memory": "Edellinen muisto", - "previous_or_next_photo": "Edellinen tai seuraava kuva", + "previous_or_next_day": "Päivä seuraava/edellinen", + "previous_or_next_month": "Kuukausi seuraava/edellinen", + "previous_or_next_photo": "Kuva seuraava/edellinen", + "previous_or_next_year": "Vuosi seuraava/edellinen", "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_client_server_up_to_date": "Asiakasohjelma 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", @@ -1452,6 +1473,8 @@ "remove_deleted_assets": "Poista Offline-tiedostot", "remove_from_album": "Poista albumista", "remove_from_favorites": "Poista suosikeista", + "remove_from_locked_folder": "Poista lukitusta kansiosta", + "remove_from_locked_folder_confirmation": "Haluatko varmasti siirtää nämä kuvat ja videot pois lukitusta kansiosta? Ne näkyvät sen jälkeen kirjastossasi.", "remove_from_shared_link": "Poista jakolinkistä", "remove_memory": "Tyhjennä muisti", "remove_photo_from_memory": "Poista kuva muistista", @@ -1513,12 +1536,15 @@ "search_country": "Etsi maata...", "search_filter_apply": "Käytä", "search_filter_camera_title": "Valitse kameratyyppi", + "search_filter_date": "Päivämäärä", + "search_filter_date_interval": "{start} – {end}", "search_filter_date_title": "Valitse aikaväli", "search_filter_display_option_not_in_album": "Ei kuulu albumiin", "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": "Mediatyyppi", "search_filter_media_type_title": "Valitse mediatyyppi", "search_filter_people_title": "Valitse ihmiset", "search_for": "Hae", @@ -1595,7 +1621,6 @@ "setting_image_viewer_title": "Kuvat", "setting_languages_apply": "Käytä", "setting_languages_subtitle": "Vaihda sovelluksen kieli", - "setting_languages_title": "Kieli", "setting_notifications_notify_failures_grace_period": "Ilmoita taustalla tapahtuvista varmuuskopiointivirheistä: {duration}", "setting_notifications_notify_hours": "{count} tuntia", "setting_notifications_notify_immediately": "heti", @@ -1618,6 +1643,7 @@ "share_add_photos": "Lisää kuvia", "share_assets_selected": "{count} valittu", "share_dialog_preparing": "Valmistellaan...", + "share_link": "Jaa linkki", "shared": "Jaettu", "shared_album_activities_input_disable": "Kommentointi on kytketty pois päältä", "shared_album_activity_remove_content": "Haluatko poistaa tämän aktiviteetin?", @@ -1657,6 +1683,7 @@ "shared_link_expires_second": "Vanhenee {count} sekunnissa", "shared_link_expires_seconds": "Vanhenee {count} sekunnissa", "shared_link_individual_shared": "YksilÃļllisesti jaettu", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Hallitse jaettuja linkkejä", "shared_link_options": "Jaetun linkin vaihtoehdot", "shared_links": "Jaetut linkit", @@ -1751,6 +1778,7 @@ "tag_updated": "Päivitetty tunniste: {tag}", "tagged_assets": "Tunnistettu {count, plural, one {# kohde} other {# kohdetta}}", "tags": "Tunnisteet", + "template": "Nimeämismalli", "theme": "Teema", "theme_selection": "Teeman valinta", "theme_selection_description": "Aseta vaalea tai tumma tila automaattisesti perustuen selaimesi asetuksiin", @@ -1779,7 +1807,6 @@ "to_parent": "Siirry vanhempaan", "to_trash": "Roskakoriin", "toggle_settings": "Määritä asetukset", - "toggle_theme": "Aseta tumma teema", "total": "Yhteensä", "total_usage": "KäyttÃļ yhteensä", "trash": "Roskakori", @@ -1819,8 +1846,6 @@ "unselect_all_duplicates": "Poista kaikkien kaksoiskappaleiden valinta", "unstack": "Pura pino", "unstacked_assets_count": "Poistettu pinosta {count, plural, one {# kohde} other {# kohdetta}}", - "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", @@ -1837,7 +1862,9 @@ "upload_success": "Lataus onnistui. Päivitä sivu jotta näet latauksesi.", "upload_to_immich": "Lähetä Immichiin ({count})", "uploading": "Lähettään", + "url": "URL", "usage": "KäyttÃļ", + "use_biometric": "Käytä biometriikkaa", "use_current_connection": "käytä nykyistä yhteyttä", "use_custom_date_range": "Käytä omaa aikaväliä", "user": "Käyttäjä", @@ -1861,13 +1888,9 @@ "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_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", @@ -1884,6 +1907,7 @@ "view_previous_asset": "Näytä edellinen", "view_qr_code": "Näytä QR-koodi", "view_stack": "Näytä pinona", + "view_user": "Näytä käyttäjä", "viewer_remove_from_stack": "Poista pinosta", "viewer_stack_use_as_main_asset": "Käytä pääkohteena", "viewer_unstack": "Pura pino", @@ -1894,6 +1918,7 @@ "welcome": "Tervetuloa", "welcome_to_immich": "Tervetuloa Immichiin", "wifi_name": "Wi-Fi-verkon nimi", + "wrong_pin_code": "Väärä PIN-koodi", "year": "Vuosi", "years_ago": "{years, plural, one {# vuosi} other {# vuotta}} sitten", "yes": "Kyllä", diff --git a/i18n/fil.json b/i18n/fil.json index 7368a1bccb..12e74f7bad 100644 --- a/i18n/fil.json +++ b/i18n/fil.json @@ -1,8 +1,10 @@ { - "about": "Tungkol sa app na ito", + "about": "Tungkol sa app", + "account": "Account", "account_settings": "Mga Setting ng Account", "acknowledge": "Tanggapin", "action": "Aksyon", + "action_common_update": "I-update", "actions": "Mga Aksyon", "active": "Tumatakbo", "activity": "Mga Aktibidad", @@ -14,27 +16,49 @@ "add_a_title": "Dagdagan ng pamagat", "add_location": "Magdagdag ng lugar", "add_more_users": "Magdagdag ng mga user", + "add_partner": "Magdagdag ng kasangga", "add_photos": "Magdagdag ng litrato", - "add_to": "Idagdag sa...", + "add_to": "Idagdag saâ€Ļ", "add_to_album": "Idagdag sa album", + "add_to_album_bottom_sheet_added": "Naidagdag sa {album}", + "add_to_album_bottom_sheet_already_exists": "Nasa {album} na", "add_to_shared_album": "Idagdag sa shared album", - "added_to_archive": "Idinagdag sa archive", - "added_to_favorites": "Idinagdag sa mga paborito", - "added_to_favorites_count": "Idinagdag ang {count, number} sa mga paborito", + "add_url": "Magdagdag ng URL", + "added_to_archive": "Naidagdag sa archive", + "added_to_favorites": "Naidagdag sa mga paborito", + "added_to_favorites_count": "Naidagdag ang {count, number} sa mga paborito", "admin": { - "asset_offline_description": "Ang external library asset na ito ay hindi na makikita sa disk at nailipat na sa trash. Kung ang file ay nailipat sa loob ng library, tignan ang iyong timeline para sa kaukulang asset. Para maibalik ang asset na ito, siguraduhin na ang file path ay maa-access ng Immich para iscan ang library.", - "authentication_settings_disable_all": "Sigurado ka bang gusto mo patayin lahat ng paraan ng pag-login? Ang pag-login ay ganap na idi-disable.", - "authentication_settings_reenable": "Para i-enable ulit, gamitin ang Server Command.", - "cleared_jobs": "Lahat nang mga trabaho para sa {job} ay tinanggal na", - "confirm_delete_library": "Sigurado ka na gusto mo tanggalin ang {library} library?", + "add_exclusion_pattern_description": "Dagdagan ng pattern para maibukod. Supportado ang pag-tutugma gamit ang *, **, at ?. Para hindi maisama ang mga file sa direktoryo na may pangalang \"Raw\", gamitin ang \"**/Raw/**\". Para hindi maisama ang lahat ng mga file na nagtatapos sa \".tif\", gamitin ang \"**/*.tif\". Para hindi maisama ang isang tiyak na folder, gamitin ang \"/path/to/ignore/**\".", + "asset_offline_description": "Ang external library asset na ito ay hindi na makikita sa disk at nailipat na sa basurahan. Kung ang file ay nailipat sa loob ng library, tignan ang iyong timeline para sa kaukulang asset. Para maibalik ang asset na ito, siguraduhin na ang file ay maa-access ng Immich at muling i-scan ang library.", + "authentication_settings": "Setting ng mga Pagkakakilanlan", + "authentication_settings_description": "Pamahalaan ang password, OAuth, and iba pang setting ng pagkakakilanlan", + "authentication_settings_disable_all": "Sigurado ka bang gusto mong i-disable lahat ng paraan ng pag-login? Ang pag-login ay ganap na idi-disable.", + "authentication_settings_reenable": "Para i-enable muli, gamitin ang Server Command.", + "background_task_job": "Mga Backround na Gawain", + "backup_database": "Gumawa ng Dump ng Database", + "backup_database_enable_description": "Paganahin ang Database Dumps", + "backup_keep_last_amount": "Bilang ng mga itatagong nakaraang dump", + "backup_settings": "Setting ng mga Database Dump", + "backup_settings_description": "Pamahalaan ang mga setting ng database dump.", + "cleared_jobs": "Tinanggal na ang mga trabaho para sa {job}", + "config_set_by_file": "Ang mga setting ay kasalukuyang naka-set mula sa config file", + "confirm_delete_library": "Sigurado ka na gusto mo burahin ang {library} library?", + "confirm_delete_library_assets": "Sigurado ka bang gusto mong burahin ang library na ito? Ang {count, plural, one {# na lamang asset} other {lahat ng # na nilalamang asset}} mula sa Immich ay mabubura at hindi maibabalik. Ang mga file ay mananatili sa disk.", "confirm_email_below": "Para isigurado, i-type ito sa baba: \"{email}\"", - "confirm_user_password_reset": "Sigurado ka na gusto mo i-reset ang password ni {user}?", + "confirm_reprocess_all_faces": "Sigurado ka bang gusto mong i-process muli lahat ng mga mukha? Mabubura nito ang mga taong napangalanan na.", + "confirm_user_password_reset": "Sigurado ka bang gusto mo i-reset ang password ni {user}?", + "confirm_user_pin_code_reset": "Sigurado ka bang gusto mo i-reset ang PIN code ni {user}?", + "create_job": "Gumawa ng trabaho", + "cron_expression": "Ekspresyon na Cron", + "cron_expression_description": "I-set ang pagitan ng pag-scan gamit ang cron na format. Maaaring basahin ang Crontab Guru para sa karagdagang impormasyon", + "cron_expression_presets": "Mga preset na ekspresyong Cron", "disable_login": "I-disable ang login", + "duplicate_detection_job_description": "Hanapin ang mga magkakatulad na imahe gamit ang machine learning. Umaasa sa Smart Search", + "exclusion_pattern_description": "Maaaring gamitin ang mga pattern na pangbukod para hindi pansinin ang ilang file o folder habang binabasa ang iyong library. Mainam itong solusyon para sa mga folder na may file na ayaw niyong ma-import, tulad ng mga RAW na file.", "force_delete_user_warning": "BABALA: Tatanggalin itong user at lahat ng asset nila, Hindi ito mababawi at ang kanilang files ay hindi na mababalik", "image_format": "Format", "library_import_path_description": "Tukuyin ang folder na i-import. Ang folder na ito, kasama ang subfolders, ay mag sa-scan para sa mga imahe at mga videos.", "note_cannot_be_changed_later": "TANDAAN: Hindi na ito pwede baguhin sa susunod!", - "repair_all": "Ayusin lahat", "server_welcome_message_description": "Mensahe na ipapakita sa login page.", "user_restore_description": "Ang account ni {user} ay maibabalik." }, diff --git a/i18n/fr.json b/i18n/fr.json index 4b5a09c664..bf57469944 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -3,7 +3,9 @@ "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}}", @@ -20,11 +22,11 @@ "add_partner": "Ajouter un partenaire", "add_path": "Ajouter un chemin", "add_photos": "Ajouter des photos", + "add_tag": "Ajouter une Êtiquette", "add_to": "Ajouter àâ€Ļ", "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", @@ -32,6 +34,7 @@ "added_to_favorites_count": "{count, number} ajoutÊ(s) aux favoris", "admin": { "add_exclusion_pattern_description": "Ajouter des schÊmas d'exclusion. Les caractères gÊnÊriques *, ** et ? sont pris en charge. Pour ignorer tous les fichiers dans un rÊpertoire nommÊ ÂĢ Raw Âģ, utilisez ÂĢ **/Raw/** Âģ. Pour ignorer tous les fichiers se terminant par ÂĢ .tif Âģ, utilisez ÂĢ **/*.tif Âģ. Pour ignorer un chemin absolu, utilisez ÂĢ /chemin/à/ignorer/** Âģ.", + "admin_user": "Administrateur", "asset_offline_description": "Ce mÊdia de la bibliothèque externe n'est plus prÊsent sur le disque et a ÊtÊ dÊplacÊ vers la corbeille. Si le fichier a ÊtÊ dÊplacÊ dans la bibliothèque, vÊrifiez votre chronologie pour le nouveau mÊdia correspondant. Pour restaurer ce mÊdia, veuillez vous assurer que le chemin du fichier ci-dessous peut ÃĒtre accÊdÊ par Immich et lancez l'analyse de la bibliothèque.", "authentication_settings": "Paramètres d'authentification", "authentication_settings_description": "GÊrer le mot de passe, l'authentification OAuth et d'autres paramètres d'authentification", @@ -42,9 +45,7 @@ "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", + "backup_settings_description": "GÊrer les paramètres de crÊation d'images de la base de donnÊes.", "cleared_jobs": "TÃĸches supprimÊes pour : {job}", "config_set_by_file": "La configuration est actuellement dÊfinie par un fichier de configuration", "confirm_delete_library": "Êtes-vous sÃģr de vouloir supprimer la bibliothèque {library} ?", @@ -60,14 +61,12 @@ "disable_login": "DÊsactiver la connexion", "duplicate_detection_job_description": "Lancement de l'apprentissage automatique sur les mÊdias pour dÊtecter les images similaires. Se base sur la recherche intelligente", "exclusion_pattern_description": "Les schÊmas d'exclusion vous permettent d'ignorer des fichiers et des dossiers lors de l'analyse de votre bibliothèque. Cette fonction est utile si des dossiers contiennent des fichiers que vous ne souhaitez pas importer, tels que des fichiers RAW.", - "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 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.", - "forcing_refresh_library_files": "Forcer le rafraÃŽchissement de tous les fichiers de la bibliothèque", "image_format": "Format", "image_format_description": "WebP produit des fichiers plus petits que JPEG, mais son encodage est plus lent.", "image_fullsize_description": "Image en taille rÊelle, sans mÊtadonnÊes, utilisÊe lors d'un zoom", @@ -169,10 +168,10 @@ "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 tÊlÊversÊs, exÊcutez", + "note_apply_storage_label_previous_assets": "Remarque : pour appliquer l'Êtiquette de stockage à des mÊdias prÊcÊdemment envoyÊ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  Âģ", + "notification_email_from_address_description": "Adresse courriel de l'expÊditeur, par exemple : ÂĢ Serveur de photos Immich  Âģ. Assurez-vous d'utiliser une adresse à partir de laquelle vous pouvez envoyer des courriels.", "notification_email_host_description": "Hôte du serveur de messagerie Êlectronique (par exemple, smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorer les erreurs de certificat", "notification_email_ignore_certificate_errors_description": "Ignorer les erreurs de validation du certificat TLS (non recommandÊ)", @@ -196,7 +195,7 @@ "oauth_enable_description": "Connexion avec OAuth", "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_mobile_redirect_uri_override_description": "Activer quand le fournisseur d'OAuth ne permet pas un URI mobile, comme ''{callback}''", "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.", @@ -205,11 +204,9 @@ "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_storage_quota_default_description": "Quota en Gio à utiliser lorsqu'aucune valeur n'est prÊcisÊe.", "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", "password_settings": "Connexion par mot de passe", "password_settings_description": "GÊrer les paramètres de connexion par mot de passe", @@ -219,9 +216,6 @@ "refreshing_all_libraries": "Actualisation de toutes les bibliothèques", "registration": "Enregistrement de l'administrateur", "registration_description": "Puisque vous ÃĒtes le premier utilisateur sur le système, vous serez dÊsignÊ en tant qu'administrateur et responsable des tÃĸches administratives, et vous pourrez alors crÊer d'autres utilisateurs.", - "repair_all": "RÊparer tout", - "repair_matched_items": "{count, plural, one {# ÉlÊment correspondant} other {# ÉlÊments correspondants}}", - "repaired_items": "{count, plural, one {# ÉlÊment corrigÊ} other {# ÉlÊments corrigÊs}}", "require_password_change_on_login": "Demander à l'utilisateur de changer son mot de passe lors de sa première connexion", "reset_settings_to_default": "RÊinitialiser les paramètres par dÊfaut", "reset_settings_to_recent_saved": "Paramètres rÊinitialisÊs avec les derniers paramètres enregistrÊs", @@ -246,14 +240,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 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_description": "Appliquer le modèle courant {template} aux mÊdias prÊcÊdemment envoyÊ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 envoyÊ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_onboarding_description_v2": "Quand elle est activÊe, cette fonctionnalitÊ organise automatiquement les fichiers, sur base d'un modèle dÊfini par l'utilisateur. Pour plus d'informations, se rÊpÊter à 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 tÊlÊversÊ", + "storage_template_settings_description": "GÊrer la structure des dossiers et le nom des fichiers du mÊdia envoyÊ", "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", @@ -262,16 +256,14 @@ "template_email_invite_album": "Modèle d'invitation à un album", "template_email_preview": "PrÊvisualiser", "template_email_settings": "Modèles de courriel", - "template_email_settings_description": "GÊrer les modèles de notifications par courriel personnalisÊs", "template_email_update_album": "Mettre à jour le modèle d’album", "template_email_welcome": "Modèle de courriel de bienvenue", "template_settings": "Modèles de notifications", - "template_settings_description": "GÊrer les modèles personnalisÊs pour les notifications.", + "template_settings_description": "GÊrer les modèles personnalisÊs pour les notifications", "theme_custom_css_settings": "CSS personnalisÊ", "theme_custom_css_settings_description": "Les feuilles de style en cascade (CSS) permettent de personnaliser l'apparence d'Immich.", "theme_settings": "Paramètres du thème", "theme_settings_description": "GÊrer la personnalisation de l'interface web d'Immich", - "these_files_matched_by_checksum": "Ces fichiers sont identiques d'après leur somme de contrôle", "thumbnail_generation_job": "GÊnÊration des miniatures", "thumbnail_generation_job_description": "GÊnÊration des miniatures pour chaque mÊdia ainsi que pour les visages dÊtectÊs", "transcoding_acceleration_api": "API d'accÊlÊration", @@ -299,10 +291,9 @@ "transcoding_encoding_options": "Options d'encodage", "transcoding_encoding_options_description": "Configure codecs, rÊsolution, qualitÊ et autres options pour les vidÊos encodÊes", "transcoding_hardware_acceleration": "AccÊlÊration matÊrielle", - "transcoding_hardware_acceleration_description": "ExpÊrimental ; beaucoup plus rapide, mais aura une qualitÊ infÊrieure pour un mÃĒme dÊbit binaire", + "transcoding_hardware_acceleration_description": "ExpÊrimental  : transcodage plus rapide, mais peut rÊduire la qualitÊ pour un mÃĒme dÊbit binaire", "transcoding_hardware_decoding": "DÊcodage matÊriel", "transcoding_hardware_decoding_setting_description": "Active l'accÊlÊration de bout en bout au lieu d'accÊlÊrer uniquement l'encodage. Peut ne pas fonctionner sur toutes les vidÊos.", - "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "Nombre maximum de trames B", "transcoding_max_b_frames_description": "Des valeurs plus ÊlevÊes amÊliorent l'efficacitÊ de la compression, mais ralentissent l'encodage. Elles peuvent ne pas ÃĒtre compatibles avec l'accÊlÊration matÊrielle sur les anciens appareils. Une valeur de 0 dÊsactive les trames B, tandis qu'une valeur de -1 dÊfinit automatiquement ce paramètre.", "transcoding_max_bitrate": "DÊbit binaire maximal", @@ -340,8 +331,6 @@ "trash_number_of_days_description": "Nombre de jours de rÊtention des mÊdias dans la corbeille avant leur suppression dÊfinitive", "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'Ê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", @@ -366,6 +355,7 @@ }, "admin_email": "Courriel Admin", "admin_password": "Mot de passe Admin", + "administration": "Administration", "advanced": "AvancÊ", "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", @@ -399,10 +389,6 @@ "album_remove_user": "Supprimer l'utilisateur ?", "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": "{count} ÊlÊments", - "album_thumbnail_card_shared": " ¡ PartagÊ", - "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,6 +402,11 @@ "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}}", + "albums_default_sort_order": "Ordre de tri par dÊfaut des albums", + "albums_default_sort_order_description": "Ordre de tri des mÊdias pour les nouveaux albums crÊÊs.", + "albums_feature_description": "Bibliothèques de mÊdias pouvant ÃĒtre partagÊs avec d'autres utilisateurs.", "all": "Tout", "all_albums": "Tous les albums", "all_people": "Toutes les personnes", @@ -423,7 +414,7 @@ "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 le tÊlÊversement aux utilisateurs non connectÊs", + "allow_public_user_to_upload": "Autoriser l'envoi aux utilisateurs non connectÊs", "alt_text_qr_code": "Image du code QR", "anti_clockwise": "Sens anti-horaire", "api_key": "ClÊ API", @@ -435,8 +426,10 @@ "app_bar_signout_dialog_title": "Se dÊconnecter", "app_settings": "Paramètres de l'application", "appears_in": "ApparaÃŽt dans", + "archive": "Archiver", "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": "Archiver ({count})", "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", @@ -464,17 +457,20 @@ "asset_restored_successfully": "ÉlÊment restaurÊ avec succès", "asset_skipped": "SautÊ", "asset_skipped_in_trash": "À la corbeille", - "asset_uploaded": "TÊlÊversÊ", - "asset_uploading": "TÊlÊversementâ€Ļ", + "asset_uploaded": "EnvoyÊ", + "asset_uploading": "Envoiâ€Ļ", "asset_viewer_settings_subtitle": "Modifier les paramètres du visualiseur photos", "asset_viewer_settings_title": "Visualiseur d'ÊlÊments", "assets": "MÊdias", "assets_added_count": "{count, plural, one {# mÊdia ajoutÊ} other {# mÊdias ajoutÊs}}", "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_cannot_be_added_to_album_count": "{count, plural, one {Le mÊdia ne peut pas ÃĒtre ajoutÊ} other {Les mÊdias ne peuvent pas ÃĒtre ajoutÊs}} à l'album", "assets_count": "{count, plural, one {# mÊdia} other {# mÊdias}}", "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_downloaded_failed": "{count, plural, one {# fichier tÊlÊchargÊ - Êchec du fichier {error}} other {# fichiers tÊlÊchargÊs - Êchec des fichiers {error}}}", + "assets_downloaded_successfully": "{count, plural, one {# fichier tÊlÊchargÊ avec succès} other {# fichiers tÊlÊchargÊs avec succès}}", "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}}", @@ -489,6 +485,7 @@ "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", "automatic_endpoint_switching_title": "Changement automatique d'adresse", + "autoplay_slideshow": "Lecture automatique d'un diaporama", "back": "Retour", "back_close_deselect": "Retournez en arrière, fermez ou dÊsÊlectionnez", "background_location_permission": "Permission de localisation en arrière plan", @@ -502,17 +499,18 @@ "backup_all": "Tout", "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_current_upload_notification": "Envoi 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 mÊdiasâ€Ļ", - "backup_background_service_upload_failure_notification": "Échec lors du tÊlÊversement de {filename}", + "backup_background_service_upload_failure_notification": "Échec lors de l'envoi 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", @@ -527,7 +525,7 @@ "backup_controller_page_backup_selected": "SÊlectionnÊ : ", "backup_controller_page_backup_sub": "Photos et vidÊos sauvegardÊes", "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_desc_backup": "Activez la sauvegarde au premier plan pour envoyer 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}]", @@ -545,15 +543,15 @@ "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": "TÊlÊversement des informations du fichier", + "backup_controller_page_uploading_file_info": "Envoi 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Êversement dÊjà en cours. RÊessayez plus tard", + "backup_manual_in_progress": "Envoi dÊjà en cours. RÊessayez plus tard", "backup_manual_success": "Succès", - "backup_manual_title": "Statut du tÊlÊversement", + "backup_manual_title": "Statut de l'envoi", "backup_options_page_title": "Options de sauvegarde", - "backup_setting_subtitle": "Ajuster les paramètres de tÊlÊversement au premier et en arrière-plan", + "backup_setting_subtitle": "Ajuster les paramètres d'envoi 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Ê", @@ -569,21 +567,17 @@ "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": "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": "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": "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 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", @@ -596,7 +590,8 @@ "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", + "cast": "Diffusion", + "cast_description": "Configurer les destinations disponibles de diffusion", "change_date": "Changer la date", "change_description": "Changer la description", "change_display_order": "Modifier l'ordre d'affichage", @@ -614,7 +609,6 @@ "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.", @@ -654,11 +648,13 @@ "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", + "confirm_tag_face": "Voulez-vous identifier ce visage en tant que {name} ?", + "confirm_tag_face_unnamed": "Voulez-vous identifier ce visage ?", + "connected_device": "Appareil connectÊ", "connected_to": "ConnectÊ à", "contain": "Contenu", "context": "Contexte", "continue": "Continuer", - "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", @@ -707,9 +703,11 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Sombre", + "darkTheme": "Basculer sur le thème sombre", "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", @@ -744,12 +742,16 @@ "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": "Ordre", "disabled": "DÊsactivÊ", "disallow_edits": "Ne pas autoriser les modifications", + "discord": "Discord", "discover": "DÊcouvrir", + "discovered_devices": "Appareils identifiÊs", "dismiss_all_errors": "Ignorer toutes les erreurs", "dismiss_error": "Ignorer l'erreur", "display_options": "Afficher les options", @@ -757,6 +759,7 @@ "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Ê", @@ -764,7 +767,6 @@ "download_enqueue": "TÊlÊchargement en attente", "download_error": "Erreur de tÊlÊchargement", "download_failed": "TÊlÊchargement ÊchouÊ", - "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Ê", @@ -779,7 +781,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 tÊlÊverser", + "drop_files_to_upload": "DÊposez les fichiers n'importe oÚ pour envoyer", "duplicates": "Doublons", "duplicates_description": "Examiner chaque groupe et indiquer s'il y a des doublons", "duration": "DurÊe", @@ -808,6 +810,7 @@ "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", @@ -826,6 +829,7 @@ "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}", + "error_tag_face_bounding_box": "Erreur lors de l'identification de visage - impossible de rÊcupÊrer les coordonnÊes du cadre entourant le visage", "error_title": "Erreur - Quelque chose s'est mal passÊ", "errors": { "cannot_navigate_next_asset": "Impossible de naviguer jusqu'au prochain mÊdia", @@ -833,12 +837,11 @@ "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, une {# asset} autre {# assets}}", + "cant_change_metadata_assets_count": "Impossible de modifier les mÊtadonnÊes {count, plural, one {d'un mÊdia} other {de # mÊdias}}", "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", "cant_search_places": "Impossible de rechercher des lieux", - "cleared_jobs": "TÃĸches supprimÊes pour : {job}", "error_adding_assets_to_album": "Erreur lors de l'ajout des mÊdias à l'album", "error_adding_users_to_album": "Erreur lors de l'ajout d'utilisateurs à l'album", "error_deleting_shared_user": "Erreur lors de la suppression de l'utilisateur partagÊ", @@ -847,7 +850,6 @@ "error_removing_assets_from_album": "Erreur lors de la suppression des mÊdias de l'album, vÊrifier la console pour plus de dÊtails", "error_selecting_all_assets": "Erreur lors de la sÊlection de tous les mÊdias", "exclusion_pattern_already_exists": "Ce modèle d'exclusion existe dÊjà.", - "failed_job_command": "La commande {command} a ÊchouÊ pour la tÃĸche : {job}", "failed_to_create_album": "Impossible de crÊer l'album", "failed_to_create_shared_link": "Impossible de crÊer le lien partagÊ", "failed_to_edit_shared_link": "Impossible de modifier le lien partagÊ", @@ -866,7 +868,6 @@ "paths_validation_failed": "Validation ÊchouÊe pour {paths, plural, one {# un chemin} other {# plusieurs chemins}}", "profile_picture_transparent_pixels": "Les images de profil ne peuvent pas avoir de pixels transparents. Veuillez agrandir et/ou dÊplacer l'image.", "quota_higher_than_disk_size": "Le quota saisi est supÊrieur à l'espace disponible", - "repair_unable_to_check_items": "Impossible de vÊrifier {count, select, one {un ÊlÊment} other {plusieurs ÊlÊments}}", "unable_to_add_album_users": "Impossible d'ajouter des utilisateurs à l'album", "unable_to_add_assets_to_shared_link": "Impossible d'ajouter des mÊdias au lien partagÊ", "unable_to_add_comment": "Impossible d'ajouter un commentaire", @@ -885,7 +886,6 @@ "unable_to_change_visibility": "Impossible de changer la visibilitÊ pour {count, plural, one {# personne} other {# personnes}}", "unable_to_complete_oauth_login": "Impossible de terminer la connexion OAuth", "unable_to_connect": "Impossible de se connecter", - "unable_to_connect_to_server": "Impossible de se connecter au serveur", "unable_to_copy_to_clipboard": "Impossible de copier dans le presse-papiers, assurez-vous que vous accÊdez à la page via https", "unable_to_create_admin_account": "Impossible de crÊer le compte administrateur", "unable_to_create_api_key": "Impossible de crÊer une nouvelle clÊ API", @@ -909,14 +909,9 @@ "unable_to_hide_person": "Impossible de cacher la personne", "unable_to_link_motion_video": "Impossible de lier la photo animÊe", "unable_to_link_oauth_account": "Impossible de lier le compte OAuth", - "unable_to_load_album": "Impossible de charger l'album", - "unable_to_load_asset_activity": "Impossible de charger l'activitÊ du mÊdia", - "unable_to_load_items": "Impossible de charger les ÊlÊments", - "unable_to_load_liked_status": "Impossible de charger le statut aimÊ", "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_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", @@ -924,11 +919,9 @@ "unable_to_remove_album_users": "Impossible de supprimer les utilisateurs de l'album", "unable_to_remove_api_key": "Impossible de supprimer la clÊ API", "unable_to_remove_assets_from_shared_link": "Impossible de supprimer des mÊdias du lien partagÊ", - "unable_to_remove_deleted_assets": "Impossible de supprimer les fichiers hors ligne", "unable_to_remove_library": "Impossible de supprimer la bibliothèque", "unable_to_remove_partner": "Impossible de supprimer le partenaire", "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", @@ -956,14 +949,14 @@ "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 de tÊlÊverser le fichier" + "unable_to_upload_file": "Impossible d'envoyer 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 {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}", @@ -980,6 +973,7 @@ "explorer": "Explorateur", "export": "Exporter", "export_as_json": "Exporter en JSON", + "extension": "Extension", "external": "Externe", "external_libraries": "Bibliothèques externes", "external_network": "RÊseau externe", @@ -1010,6 +1004,8 @@ "folders": "Dossiers", "folders_feature_description": "Parcourir l'affichage par dossiers pour les photos et les vidÊos sur le système de fichiers", "forward": "Avant", + "gcast_enabled": "Diffusion Google Cast", + "gcast_enabled_description": "Cette fonctionnalitÊ charge des ressources externes depuis Google pour fonctionner.", "general": "GÊnÊral", "get_help": "Obtenir de l'aide", "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", @@ -1055,11 +1051,13 @@ "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", + "home_page_upload_err_limit": "Impossible d'envoyer plus de 30 mÊdias en mÃĒme temps, demande ignorÊe", "host": "Hôte", "hour": "Heure", + "id": "ID", "ignore_icloud_photos": "Ignorer les photos iCloud", - "ignore_icloud_photos_description": "Les photos stockÊes sur iCloud ne sont pas tÊlÊversÊes sur le serveur Immich", + "ignore_icloud_photos_description": "Les photos stockÊes sur iCloud ne seront pas envoyÊes sur le serveur Immich", + "image": "Image", "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}", @@ -1096,6 +1094,12 @@ "invalid_date_format": "Format de date invalide", "invite_people": "Inviter une personne", "invite_to_album": "Inviter à l'album", + "ios_debug_info_fetch_ran_at": "RÊcupÊration lancÊe {dateTime}", + "ios_debug_info_last_sync_at": "Dernière synchronisation {dateTime}", + "ios_debug_info_no_processes_queued": "Aucun processus en arrière plan en attente de traitement", + "ios_debug_info_no_sync_yet": "Le traitement de synchronisation en arrière plan n'a jamais ÊtÊ lancÊ", + "ios_debug_info_processes_queued": "{count, plural, one {{count} processus en arrière plan en attente de traitement} other {{count} processus en arrière plan en attente de traitement}}", + "ios_debug_info_processing_ran_at": "Le traitement a ÊtÊ lancÊ {dateTime}", "items_count": "{count, plural, one {# ÊlÊment} other {# ÊlÊments}}", "jobs": "TÃĸches", "keep": "Conserver", @@ -1104,9 +1108,13 @@ "kept_this_deleted_others": "Ce mÊdia a ÊtÊ conservÊ, et {count, plural, one {un autre a ÊtÊ supprimÊ} other {# autres ont ÊtÊ supprimÊs}}", "keyboard_shortcuts": "Raccourcis clavier", "language": "Langue", + "language_no_results_subtitle": "Essayer d'affiner vos critères de recherche", + "language_no_results_title": "Aucune langue trouvÊe", + "language_search_hint": "Recherche de langues...", "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", @@ -1128,6 +1136,7 @@ "list": "Liste", "loading": "Chargement", "loading_search_results_failed": "Chargement des rÊsultats ÊchouÊ", + "local_asset_cast_failed": "Impossible de caster un mÊdia qui n'a pas envoyÊ vers le serveur", "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", @@ -1141,6 +1150,7 @@ "locked_folder": "Dossier verrouillÊ", "log_out": "Se dÊconnecter", "log_out_all_devices": "DÊconnecter tous les appareils", + "logged_in_as": "ConnectÊ en tant que {user}", "logged_out_all_devices": "DÊconnectÊ de tous les appareils", "logged_out_device": "DÊconnectÊ de l'appareil", "login": "Connexion", @@ -1168,6 +1178,7 @@ "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Ê.", @@ -1182,6 +1193,8 @@ "manage_your_devices": "GÊrer vos appareils", "manage_your_oauth_connection": "GÊrer votre connexion OAuth", "map": "Carte", + "map_assets_in_bound": "{count} photo", + "map_assets_in_bounds": "{count} 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", @@ -1217,6 +1230,7 @@ "memories_swipe_to_close": "Balayez vers le haut pour fermer", "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", @@ -1224,9 +1238,11 @@ "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Ê", @@ -1260,14 +1276,15 @@ "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 TÉLÉVERSER VOTRE PREMIÈRE PHOTO", + "no_assets_message": "CLIQUEZ POUR ENVOYER VOTRE PREMIÈRE PHOTO", "no_assets_to_show": "Aucun ÊlÊment à afficher", + "no_cast_devices_found": "Aucun appareil de diffusion trouvÊ", "no_duplicates_found": "Aucun doublon n'a ÊtÊ trouvÊ.", "no_exif_info_available": "Aucune information exif disponible", - "no_explore_results_message": "TÊlÊversez plus de photos pour explorer votre collection.", + "no_explore_results_message": "Envoyez plus de photos pour explorer votre bibliothèque.", "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_locked_photos_message": "Les photos et vidÊos du dossier verrouillÊ sont masquÊs et ne s'afficheront pas dans votre galerie ou la recherche.", "no_name": "Pas de nom", "no_notifications": "Pas de notification", "no_people_found": "Aucune personne correspondante trouvÊe", @@ -1277,24 +1294,28 @@ "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à tÊlÊversÊs, exÊcutez", + "note_apply_storage_label_to_previously_uploaded assets": "Note : Pour appliquer l'Êtiquette de stockage aux mÊdias prÊcÊdemment envoyÊs, exÊcutez", + "notes": "Notes", "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", - "onboarding_privacy_description": "Les fonctions suivantes (optionnelles) dÊpendent de services externes et peuvent ÃĒtre dÊsactivÊes à tout moment dans les paramètres d'administration.", + "onboarding_locale_description": "Choisissez vote langue prÊfÊrÊe. Vous pourrez en changer par la suite dans les paramètres.", + "onboarding_privacy_description": "Les fonctions suivantes (optionnelles) dÊpendent de services externes et peuvent ÃĒtre dÊsactivÊes à tout moment dans les paramètres.", + "onboarding_server_welcome_description": "DÊmarrons votre instance avec quelques paramètres courants.", "onboarding_theme_description": "Choisissez un thème de couleur pour votre instance. Vous pouvez le changer plus tard dans vos paramètres.", - "onboarding_welcome_description": "Mettons votre instance en place avec quelques paramètres communs.", + "onboarding_user_welcome_description": "Commençons !", "onboarding_welcome_user": "Bienvenue {user}", "online": "En ligne", "only_favorites": "Uniquement les favoris", @@ -1302,8 +1323,10 @@ "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", @@ -1334,6 +1357,7 @@ }, "path": "Chemin", "pattern": "SchÊma", + "pause": "Pause", "pause_memories": "Mettre en pause les souvenirs", "paused": "En pause", "pending": "En attente", @@ -1348,6 +1372,8 @@ "permanently_delete_assets_prompt": "Êtes-vous sÃģr de vouloir supprimer dÊfinitivement {count, plural, one {ce mÊdia ?} other {ces # mÊdias ?}} Cela {count, plural, one {le} other {les}} supprimera aussi de {count, plural, one {son (ses)} other {leur(s)}} album(s).", "permanently_deleted_asset": "MÊdia supprimÊ dÊfinitivement", "permanently_deleted_assets_count": "{count, plural, one {# mÊdia dÊfinitivement supprimÊ} other {# mÊdias dÊfinitivement supprimÊs}}", + "permission": "Autorisation", + "permission_empty": "Votre autorisation ne doit pas ÃĒtre vide", "permission_onboarding_back": "Retour", "permission_onboarding_continue_anyway": "Continuer quand mÃĒme", "permission_onboarding_get_started": "Commencer", @@ -1360,7 +1386,9 @@ "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", @@ -1375,19 +1403,25 @@ "play_motion_photo": "Jouer la photo animÊe", "play_or_pause_video": "Lancer ou mettre en pause la vidÊo", "please_auth_to_access": "Merci de vous authentifier pour accÊder", + "port": "Port", "preferences_settings_subtitle": "GÊrer les prÊfÊrences de l'application", "preferences_settings_title": "PrÊfÊrences", "preset": "PrÊrÊglage", "preview": "Aperçu", "previous": "PrÊcÊdent", "previous_memory": "Souvenir prÊcÊdent", - "previous_or_next_photo": "Photo prÊcÊdente ou suivante", + "previous_or_next_day": "Jour prÊcÊdent / suivant", + "previous_or_next_month": "Mois prÊcÊdent / suivant", + "previous_or_next_photo": "Photo prÊcÊdente / suivante", + "previous_or_next_year": "AnnÊe prÊcÊdente / suivante", "primary": "Primaire", "privacy": "Vie privÊe", + "profile": "Profil", "profile_drawer_app_logs": "Journaux", "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}", @@ -1463,10 +1497,11 @@ "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_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", + "remove_tag": "Supprimer l'Êtiquette", "remove_url": "Supprimer l'URL", "remove_user": "Supprimer l'utilisateur", "removed_api_key": "ClÊ API supprimÊe : {name}", @@ -1479,7 +1514,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Êversement", + "replace_with_upload": "Remplacer avec l'envoi", "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", @@ -1496,7 +1531,7 @@ "restore_user": "Restaurer l'utilisateur", "restored_asset": "MÊdia restaurÊ", "resume": "Reprendre", - "retry_upload": "RÊessayer le tÊlÊversement", + "retry_upload": "RÊessayer l'envoi", "review_duplicates": "Consulter les doublons", "role": "Rôle", "role_editor": "Éditeur", @@ -1525,6 +1560,7 @@ "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", @@ -1572,6 +1608,7 @@ "select_album_cover": "SÊlectionner la couverture d'album", "select_all": "Tout sÊlectionner", "select_all_duplicates": "SÊlectionner tous les doublons", + "select_all_in": "Tout sÊlectionner dans {group}", "select_avatar_color": "SÊlectionner la couleur de l'avatar", "select_face": "SÊlectionner le visage", "select_featured_photo": "SÊlectionner la photo de profil de cette personne", @@ -1592,6 +1629,7 @@ "server_info_box_server_url": "URL du serveur", "server_offline": "Serveur hors ligne", "server_online": "Serveur en ligne", + "server_privacy": "Vie privÊe pour le serveur", "server_stats": "Statistiques du serveur", "server_version": "Version du serveur", "set": "DÊfinir", @@ -1601,23 +1639,25 @@ "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", + "set_stack_primary_asset": "Marquer comme mÊdia principal", "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 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 : {duration}", "setting_notifications_notify_hours": "{count} heures", "setting_notifications_notify_immediately": "immÊdiatement", + "setting_notifications_notify_minutes": "{count} minutes", "setting_notifications_notify_never": "jamais", "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_subtitle": "Informations dÊtaillÊes sur la progression de l'envoi 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 tÊlÊversement (effectuÊ/total des mÊdias)", + "setting_notifications_total_progress_subtitle": "Progression globale de l'envoi (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.", @@ -1643,7 +1683,7 @@ "shared_by_user": "PartagÊ par {user}", "shared_by_you": "PartagÊ par vous", "shared_from_partner": "Photos de {partner}", - "shared_intent_upload_button_progress_text": "{current} / {total} TÊlÊversÊ(s)", + "shared_intent_upload_button_progress_text": "{current} / {total} EnvoyÊ(s)", "shared_link_app_bar_title": "Liens partagÊs", "shared_link_clipboard_copied_massage": "CopiÊ dans le presse-papier", "shared_link_clipboard_text": "Lien : {link}\nMot de passe : {password}", @@ -1653,6 +1693,8 @@ "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": "{count} heures", + "shared_link_edit_expire_after_option_minute": "1 minute", + "shared_link_edit_expire_after_option_minutes": "{count} minutes", "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", @@ -1668,6 +1710,7 @@ "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", @@ -1723,6 +1766,7 @@ "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", @@ -1733,6 +1777,7 @@ "start_date": "Date de dÊbut", "state": "RÊgion", "status": "Statut", + "stop_casting": "ArrÃĒter la diffusion", "stop_motion_photo": "Photo Stop Motion", "stop_photo_sharing": "ArrÃĒter de partager vos photos ?", "stop_photo_sharing_description": "{partner} ne pourra plus accÊder à vos photos.", @@ -1742,14 +1787,16 @@ "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": "Soutenir", "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 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", + "sync_albums_manual_subtitle": "Synchroniser toutes les vidÊos et photos envoyÊes dans les albums sÊlectionnÊs", + "sync_upload_album_setting_subtitle": "CrÊez et envoyez 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}", @@ -1788,7 +1835,7 @@ "to_parent": "Aller au dossier parent", "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", @@ -1804,10 +1851,12 @@ "trash_page_select_assets_btn": "SÊlectionner les ÊlÊments", "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", + "undo": "Annuler", "unfavorite": "Enlever des favoris", "unhide_person": "Afficher la personne", "unknown": "Inconnu", @@ -1824,26 +1873,26 @@ "unsaved_change": "Modification non enregistrÊe", "unselect_all": "Annuler la sÊlection", "unselect_all_duplicates": "DÊsÊlectionner tous les doublons", + "unselect_all_in": "Tout dÊsÊlectionner dans {group}", "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, 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": "TÊlÊverser", - "upload_concurrency": "TÊlÊversements simultanÊs", + "upload": "Envoyer", + "upload_concurrency": "Envois 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_dialog_title": "Envoyer le mÊdia", + "upload_errors": "L'envoi s'est complÊtÊ avec {count, plural, one {# erreur} other {# erreurs}}. RafraÃŽchissez la page pour voir les nouveaux mÊdias envoyÊ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": "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", + "upload_status_uploaded": "EnvoyÊ", + "upload_success": "Envoi rÊussi. RafraÃŽchissez la page pour voir les nouveaux mÊdias envoyÊs.", + "upload_to_immich": "Envoyer vers Immich ({count})", + "uploading": "Envoi", + "url": "URL", "usage": "Utilisation", "use_biometric": "Utiliser l'authentification biomÊtrique", "use_current_connection": "Utiliser le rÊseau actuel", @@ -1854,6 +1903,7 @@ "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_privacy": "Vie privÊe pour l'utilisateur", "user_purchase_settings": "Achat", "user_purchase_settings_description": "GÊrer votre achat", "user_role_set": "DÊfinir {user} comme {role}", @@ -1865,13 +1915,10 @@ "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", - "version_announcement_overlay_text_1": "Bonjour, une nouvelle version de", - "version_announcement_overlay_text_2": "veuillez prendre le temps de visiter le ", - "version_announcement_overlay_text_3": " et assurez-vous que votre configuration docker-compose et .env est à jour pour Êviter toute erreur de configuration, en particulier si vous utilisez WatchTower ou tout autre mÊcanisme qui gère la mise à jour automatique de votre application serveur.", - "version_announcement_overlay_title": "Nouvelle version serveur disponible 🎉", "version_history": "Historique de version", "version_history_item": "Version {version} installÊe le {date}", "video": "VidÊo", @@ -1891,6 +1938,7 @@ "view_previous_asset": "Voir le mÊdia prÊcÊdent", "view_qr_code": "Voir le QR code", "view_stack": "Afficher la pile", + "view_user": "Voir l'utilisateur", "viewer_remove_from_stack": "Retirer de la pile", "viewer_stack_use_as_main_asset": "Utiliser comme ÊlÊment principal", "viewer_unstack": "DÊsempiler", diff --git a/i18n/gl.json b/i18n/gl.json index af2841481d..558cc48900 100644 --- a/i18n/gl.json +++ b/i18n/gl.json @@ -26,7 +26,6 @@ "add_to_album": "Engadir ao ÃĄlbum", "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", @@ -45,8 +44,6 @@ "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": "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}?", @@ -62,14 +59,12 @@ "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 da biblioteca externa", "face_detection": "DetecciÃŗn de caras", "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", @@ -197,7 +192,7 @@ "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_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.", @@ -207,8 +202,6 @@ "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", @@ -218,9 +211,6 @@ "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", @@ -249,7 +239,6 @@ "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", @@ -261,7 +250,6 @@ "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", @@ -270,7 +258,6 @@ "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", @@ -301,7 +288,6 @@ "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", @@ -339,14 +325,13 @@ "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_details": "Detalles do usuario", "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.", @@ -398,10 +383,6 @@ "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}", @@ -535,6 +516,7 @@ "backup_controller_page_excluded": "Excluído: ", "backup_controller_page_failed": "Fallado ({count})", "backup_controller_page_filename": "Nome do ficheiro: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "InformaciÃŗn da Copia de Seguridade", "backup_controller_page_none_selected": "NingÃēn seleccionado", "backup_controller_page_remainder": "Restante", @@ -558,6 +540,7 @@ "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", + "biometric_auth_enabled": "AutenticaciÃŗn biomÊtrica activada", "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", @@ -568,21 +551,17 @@ "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Ê", @@ -610,7 +589,6 @@ "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.", @@ -652,7 +630,6 @@ "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", @@ -760,7 +737,6 @@ "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", @@ -828,7 +804,6 @@ "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", @@ -837,7 +812,6 @@ "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", @@ -856,7 +830,6 @@ "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", @@ -874,7 +847,6 @@ "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", @@ -898,10 +870,6 @@ "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", @@ -912,11 +880,9 @@ "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", @@ -950,7 +916,6 @@ "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}", @@ -1265,15 +1230,12 @@ "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", @@ -1583,7 +1545,6 @@ "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", @@ -1766,7 +1727,6 @@ "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", @@ -1803,8 +1763,6 @@ "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", @@ -1840,11 +1798,6 @@ "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", diff --git a/i18n/he.json b/i18n/he.json index a6fc7fcbfd..737c307f31 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -22,6 +22,7 @@ "add_partner": "הוספ×Ē ×Š×•×Ē×Ŗ", "add_path": "הוספ×Ē × ×Ēיב", "add_photos": "הוספ×Ē ×Ēמונו×Ē", + "add_tag": "הוספ×Ē ×Ēג", "add_to": "הוספה לâ€Ļ", "add_to_album": "הוספה לאלבום", "add_to_album_bottom_sheet_added": "× ×•×Ą×Ŗ ל {album}", @@ -43,9 +44,7 @@ "backup_database_enable_description": "אפ׊ר גיבויי מסד × ×Ēונים", "backup_keep_last_amount": "כמו×Ē ×Š×œ גיבויים קודמים שיש לשמור", "backup_settings": "הגדרו×Ē ×’×™×‘×•×™", - "backup_settings_description": "ניהול הגדרו×Ē ×’×™×‘×•×™ מסד × ×Ēונים", - "check_all": "סימון הכל", - "cleanup": "ניקוי", + "backup_settings_description": "ניהול הגדרו×Ē ×’×™×‘×•×™ מסד × ×Ēונים.", "cleared_jobs": "נוקו משימו×Ē ×ĸבור: {job}", "config_set_by_file": "ה×Ē×Ļורה מוגדר×Ē ×›×ĸ×Ē ×ĸל ידי קוב×Ĩ ×Ē×Ļורה", "confirm_delete_library": "האם באמ×Ē ×‘×¨×Ļונך למחוק א×Ē ×”×Ą×¤×¨×™×™×” {library}?", @@ -61,14 +60,12 @@ "disable_login": "השב×Ē ×›× ×™×Ą×”", "duplicate_detection_job_description": "הפ×ĸל למיד×Ē ×ž×›×•× ×” ×ĸל ×Ēמונו×Ē ×›×“×™ לזהו×Ē ×Ēמונו×Ē ×“×•×ž×•×Ē. נ׊×ĸן ×ĸל חיפוש חכם", "exclusion_pattern_description": "דפוסי החרגה מאפשרים לך לה×Ē×ĸלם מקב×Ļים ומ×Ēיקיו×Ē ×‘×ĸ×Ē ×Ą×¨×™×§×Ē ×”×Ą×¤×¨×™×™×” שלך. זה שימושי אם יש לך ×Ēיקיו×Ē ×”×ž×›×™×œ×•×Ē ×§×‘×Ļים שאינך רו×Ļה לייבא, כגון קוב×Ļי RAW.", - "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": "×Ēמונה בגודל מלא ×ĸם מטא-× ×Ēונים שהוסרו, מ׊מ׊×Ē ×›××Š×¨ ה×Ēמונה מו×Ļג×Ē ×ž×§×¨×•×‘", @@ -173,7 +170,7 @@ "note_apply_storage_label_previous_assets": "ה×ĸרה: כדי להחיל א×Ē ×Ēווי×Ē ×”××—×Ą×•×Ÿ ×ĸל ×Ēמונו×Ē ×Š×”×•×ĸלו ב×ĸבר, הפ×ĸל א×Ē", "note_cannot_be_changed_later": "ה×ĸרה: אי אפ׊ר לשנו×Ē ×–××Ē ×ž××•×—×¨ יו×Ēר!", "notification_email_from_address": "מכ×Ēוב×Ē", - "notification_email_from_address_description": "כ×Ēוב×Ē ×“×•×\"ל של השולח, לדוגמה: \"Immich ׊ר×Ē ×Ēמונו×Ē \"", + "notification_email_from_address_description": "כ×Ēוב×Ē ×“×•×\"ל של השולח, לדוגמה: \"Immich ׊ר×Ē ×Ēמונו×Ē \". יש לוודא ׊נ×ĸשה שימוש בכ×Ēוב×Ē ×ž×ž× ×” הנך מורשה לשלוח דוא\"ל.", "notification_email_host_description": "מארח ׊ר×Ē ×”×“×•×\"ל (למשל smtp.immich.app)", "notification_email_ignore_certificate_errors": "ה×Ē×ĸלם משגיאו×Ē ×Ē×ĸודה", "notification_email_ignore_certificate_errors_description": "ה×Ē×ĸלם משגיאו×Ē ××™×ž×•×Ē ×Ē×ĸוד×Ē TLS (לא מומל×Ĩ)", @@ -197,7 +194,7 @@ "oauth_enable_description": "ה×Ēחבר ×ĸם OAuth", "oauth_mobile_redirect_uri": "URI להפניה מחדש בנייד", "oauth_mobile_redirect_uri_override": "×ĸקיפ×Ē URI להפניה מחדש בנייד", - "oauth_mobile_redirect_uri_override_description": "אפ׊ר כאשר ץפק OAuth לא מאפ׊ר כ×Ēוב×Ē URI לנייד, כמו '{callback}'", + "oauth_mobile_redirect_uri_override_description": "אפ׊ר כאשר ץפק OAuth לא מאפ׊ר כ×Ēוב×Ē URI לנייד, כמו ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "ניהול הגדרו×Ē ×”×Ēחברו×Ē ×ĸם OAuth", "oauth_settings_more_details": "למיד×ĸ × ×•×Ą×Ŗ אודו×Ē ×Ēכונה זו, בדוק א×Ē ×”×Ēי×ĸוד.", @@ -209,8 +206,6 @@ "oauth_storage_quota_default_description": "מכסה ב-GiB לשימוש כאשר לא מסופק×Ē ×“×¨×™×Š×” (הזן 0 ×ĸבור מכסה בל×Ēי מוגבל×Ē).", "oauth_timeout": "הבקשה נכשלה – הזמן הק×Ļוב הס×Ēיים", "oauth_timeout_description": "זמן ×§×Ļוב לבקשו×Ē (במילישניו×Ē)", - "offline_paths": "× ×Ēיבים לא מקוונים", - "offline_paths_description": "×Ēו×Ļאו×Ē ××œ×• ×ĸשויו×Ē ×œ×”×™×•×Ē ×ĸקב מחיקה ידני×Ē ×Š×œ קב×Ļים שאינם חלק מספרייה חי×Ļוני×Ē.", "password_enable_description": "ה×Ēחבר ×ĸם דוא\"ל וסיסמה", "password_settings": "סיסמ×Ē ×”×Ēחברו×Ē", "password_settings_description": "ניהול הגדרו×Ē ×Ą×™×Ą×ž×Ē ×”×Ēחברו×Ē", @@ -220,9 +215,6 @@ "refreshing_all_libraries": "מר×ĸנן א×Ē ×›×œ הספריו×Ē", "registration": "רישום מנהל מ×ĸרכ×Ē", "registration_description": "מכיוון ׊א×Ēה המש×Ēמ׊ הראשון במ×ĸרכ×Ē, א×Ēה ×Ēוק×Ļה כמנהל וא×Ēה אחראי ×ĸל משימו×Ē × ×™×”×•×œ, ומש×Ēמשים נוספים ייוו×Ļרו ×ĸל ידך.", - "repair_all": "×Ēקן הכל", - "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": "אפץ הגדרו×Ē ×œ×”×’×“×¨×•×Ē ×Š× ×Š×ž×¨×• לאחרונה", @@ -251,7 +243,6 @@ "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": "ניהול מבנה ה×Ēיקיו×Ē ×•××Ē ×Š× הקוב×Ĩ של ה×Ēמונה שהו×ĸל×Ēה", @@ -263,16 +254,14 @@ "template_email_invite_album": "×Ēבני×Ē ×”×–×ž× ×Ē ××œ×‘×•×", "template_email_preview": "×Ē×Ļוגה מקדימה", "template_email_settings": "×Ēבניו×Ē ×“×•×\"ל", - "template_email_settings_description": "ניהול ×Ēבניו×Ē ×”×Ēרא×Ē ×“×•×\"ל מו×Ēאמו×Ē ××™×Š×™×Ē", "template_email_update_album": "×ĸדכון ×Ēבני×Ē ××œ×‘×•×", "template_email_welcome": "×Ēבני×Ē ×“×•×\"ל ברוכים הבאים", "template_settings": "×Ēבניו×Ē ×”×Ēראה", - "template_settings_description": "ניהול ×Ēבניו×Ē ×ž×•×Ēאמו×Ē ××™×Š×™×Ē ×ĸבור ה×Ēראו×Ē.", + "template_settings_description": "ניהול ×Ēבניו×Ē ×ž×•×Ēאמו×Ē ××™×Š×™×Ē ×ĸבור ה×Ēראו×Ē", "theme_custom_css_settings": "CSS בה×Ēאמה אישי×Ē", "theme_custom_css_settings_description": "גיליונו×Ē ×Ą×’× ×•×Ÿ מדורגים (CSS) מאפשרים ה×Ēאמה אישי×Ē ×Š×œ ה×ĸי×Ļוב של Immich.", "theme_settings": "הגדרו×Ē ×ĸרכ×Ē × ×•×Š×", "theme_settings_description": "ניהול ה×Ēאמה אישי×Ē ×Š×œ ממ׊ק האינטרנט של Immich", - "these_files_matched_by_checksum": "קב×Ļים אלה ×Ēואמים לפי סיכומי הביקור×Ē ×Š×œ×”×", "thumbnail_generation_job": "×Ļור ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē", "thumbnail_generation_job_description": "יו×Ļר ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×’×“×•×œ×•×Ē, קטנו×Ē ×•×ž×˜×•×Š×˜×Š×•×Ē ×ĸבור כל ×Ēמונה, כמו גם ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×ĸבור כל אדם", "transcoding_acceleration_api": "API הא×Ļה", @@ -300,10 +289,9 @@ "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": "קידוד HEVC", "transcoding_max_b_frames": "B-פריימים מרביים", "transcoding_max_b_frames_description": "×ĸרכים גבוהים יו×Ēר משפרים א×Ē ×™×ĸילו×Ē ×”×“×—×™×Ą×”, אך מאטים א×Ē ×”×§×™×“×•×“. יי×Ēכן שלא יהיה ×Ēואם ×ĸם הא×Ļ×Ē ×—×•×ž×¨×” במכשירים ישנים יו×Ēר. 0 משבי×Ē ××Ē B-פריימים, ב×ĸוד ׊1- מגדיר א×Ē ×”×ĸרך זה באופן אוטומטי.", "transcoding_max_bitrate": "×§×Ļב סיביו×Ē ×ž×¨×‘×™", @@ -341,14 +329,13 @@ "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_immediately": "החשבון וה×Ēמונו×Ē ×Š×œ {user} י×ĸמדו ב×Ēור למחיקה ל×Ļמי×Ēו×Ē ×‘××•×¤×Ÿ מיידי.", "user_delete_immediately_checkbox": "ה×Ļב מ׊×Ēמ׊ ו×Ēמונו×Ē ×‘×Ēור למחיקה מיידי×Ē", + "user_details": "פרטי מ׊×Ēמ׊", "user_management": "ניהול מ׊×Ēמשים", "user_password_has_been_reset": "סיסמ×Ē ×”×ž×Š×Ēמ׊ אופסה:", "user_password_reset_description": "אנא ץפק א×Ē ×”×Ą×™×Ą×ž×” הזמני×Ē ×œ×ž×Š×Ēמ׊ והוד×ĸ לו שיש ×Ļורך לשנו×Ē ××Ē ×”×Ą×™×Ą×ž×” בכניסה הבאה שלו.", @@ -400,10 +387,6 @@ "album_remove_user": "להסיר מ׊×Ēמ׊?", "album_remove_user_confirmation": "האם באמ×Ē ×‘×¨×Ļונך להסיר א×Ē {user}?", "album_share_no_users": "נראה ששי×Ēפ×Ē ××Ē ×”××œ×‘×•× הזה ×ĸם כל המש×Ēמשים או שאין לך את מ׊×Ēמ׊ לש×Ē×Ŗ אי×Ēו.", - "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}", @@ -419,6 +402,9 @@ "album_with_link_access": "אפ׊ר לכל אחד ×ĸם הקישור לראו×Ē ×Ēמונו×Ē ×•×× ×Š×™× באלבום הזה.", "albums": "אלבומים", "albums_count": "{count, plural, one {אלבום {count, number}} other {{count, number} אלבומים}}", + "albums_default_sort_order": "סדר מיון אלבומים בריר×Ē ×ž×—×“×œ", + "albums_default_sort_order_description": "סדר מיון ×Ēמונו×Ē ×¨××Š×•× ×™ ב×ĸ×Ē ×™×Ļיר×Ē ××œ×‘×•×ž×™× חדשים.", + "albums_feature_description": "אוספים של ×Ēמונו×Ē ××Š×¨ ני×Ēנים לשי×Ēות ×ĸם מ׊×Ēמשים אחרים.", "all": "הכל", "all_albums": "כל האלבומים", "all_people": "כל האנשים", @@ -477,9 +463,12 @@ "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_cannot_be_added_to_album_count": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ א×Ē ×”{count, plural, one {×Ēמונה} other {×Ēמונו×Ē}} לאלבום", "assets_count": "{count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}}", "assets_deleted_permanently": "{count} ×Ēמונו×Ē × ×ž×—×§×• ל×Ļמי×Ēו×Ē", "assets_deleted_permanently_from_server": "{count} ×Ēמונו×Ē × ×ž×—×§×• ל×Ļמי×Ēו×Ē ×ž×Š×¨×Ē ×”-Immich", + "assets_downloaded_failed": "{count, plural, one {הורד קוב×Ĩ # - קוב×Ĩ {error} נכשל} other {ירדו # קב×Ļים - {error} קב×Ļים נכשלו}}", + "assets_downloaded_successfully": "{count, plural, one {קוב×Ĩ # ירד בה×Ļלחה} other {# קב×Ļים ירדו בה×Ļלחה}}", "assets_moved_to_trash_count": "{count, plural, one {×Ēמונה # הו×ĸברה} other {# ×Ēמונו×Ē ×”×•×ĸברו}} לאשפה", "assets_permanently_deleted_count": "{count, plural, one {×Ēמונה # נמחקה} other {# ×Ēמונו×Ē × ×ž×—×§×•}} ל×Ļמי×Ēו×Ē", "assets_removed_count": "{count, plural, one {×Ēמונה # הוסרה} other {# ×Ēמונו×Ē ×”×•×Ą×¨×•}}", @@ -494,6 +483,7 @@ "authorized_devices": "מכשירים מורשים", "automatic_endpoint_switching_subtitle": "ה×Ēחבר מקומי×Ē ×“×¨×š אינטרנט אלחוטי יי×ĸודי כאשר זמין והש×Ēמ׊ בחיבורים חלופיים במקומו×Ē ××—×¨×™×", "automatic_endpoint_switching_title": "החלפ×Ē ×›×Ēוב×Ē ××•×˜×•×ž×˜×™×Ē", + "autoplay_slideshow": "מ×Ļג×Ē ×Ēמונו×Ē ××•×˜×•×ž×˜×™×Ē", "back": "חזרה", "back_close_deselect": "חזור, סגור, או בטל בחירה", "background_location_permission": "הרשא×Ē ×ž×™×§×•× ברק×ĸ", @@ -561,6 +551,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": "רק×ĸ מטושטש", @@ -571,21 +565,17 @@ "bulk_keep_duplicates_confirmation": "האם באמ×Ē ×‘×¨×Ļונך להשאיר {count, plural, one {×Ēמונה # כפולה} other {# ×Ēמונו×Ē ×›×¤×•×œ×•×Ē}}? זה יסגור א×Ē ×›×œ הקבו×Ļו×Ē ×”×›×¤×•×œ×•×Ē ×ž×‘×œ×™ למחוק דבר.", "bulk_trash_duplicates_confirmation": "האם באמ×Ē ×‘×¨×Ļונך לה×ĸביר לאשפה בכמו×Ē ×’×“×•×œ×” {count, plural, one {×Ēמונה # כפולה} other {# ×Ēמונו×Ē ×›×¤×•×œ×•×Ē}}? זה ישמור ×ĸל ה×Ēמונה הגדולה ביו×Ēר של כל קבו×Ļה וי×ĸביר לאשפה א×Ē ×›×œ ׊אר הכפילויו×Ē.", "buy": "רכוש א×Ē Immich", - "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": "({count}) ×Ēמונו×Ē ×ž×Š×•×›×¤×œ×•×Ē", - "cache_settings_image_cache_size": "גודל מטמון ה×Ēמונה ({count} ×Ēמונו×Ē)", "cache_settings_statistics_album": "×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×Š×œ ספרייה", - "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": "הגדר כי×Ļד אפליק×Ļיי×Ē Immich שומר×Ē × ×Ēונים באופן זמני", - "cache_settings_thumbnail_size": "גודל מטמון ×Ēמונה ממוז×ĸר×Ē ({count} ×Ēמונו×Ē)", "cache_settings_tile_subtitle": "שלוט בה×Ēנהגו×Ē ×”××—×Ą×•×Ÿ המקומי", "cache_settings_tile_title": "אחסון מקומי", "cache_settings_title": "הגדרו×Ē ×Š×ž×™×¨×Ē ×ž×˜×ž×•×Ÿ", @@ -598,7 +588,10 @@ "cannot_merge_people": "לא ני×Ēן למזג אנשים", "cannot_undo_this_action": "אין באפשרו×Ēך לבטל א×Ē ×”×¤×ĸולה הזו!", "cannot_update_the_description": "לא ני×Ēן ל×ĸדכן א×Ē ×”×Ēיאור", + "cast": "שידור למסך", + "cast_description": "הגדר×Ē ×”×Ēקנים זמינים לשידור", "change_date": "שנה ×Ēאריך", + "change_description": "שנה ×Ēיאור", "change_display_order": "שנה סדר ×Ē×Ļוגה", "change_expiration_time": "שנה א×Ē ×–×ž×Ÿ ה×Ēפוגה", "change_location": "שנה מיקום", @@ -614,7 +607,6 @@ "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 ולאחר שכל ה×Ēמונו×Ē ×’×•×‘×•. ההליך ×ĸשוי לקח×Ē ×›×ž×” דקו×Ē.", @@ -654,10 +646,13 @@ "confirm_keep_this_delete_others": "כל ׊אר ×Ēמונו×Ē ×Š×‘×ĸרימה יימחקו למ×ĸט ×Ēמונה זא×Ē. האם באמ×Ē ×‘×¨×Ļונך להמשיך?", "confirm_new_pin_code": "א׊ר קוד PIN חדש", "confirm_password": "א׊ר סיסמה", + "confirm_tag_face": "האם א×Ēה רו×Ļה ל×Ēייג א×Ē ×”×¤× ×™× האלה כ־{name}?", + "confirm_tag_face_unnamed": "האם בר×Ļונך ל×Ēייג א×Ē ×”×¤× ×™× האלה?", + "connected_device": "מכשיר מחובר", + "connected_to": "מחובר אל", "contain": "מכיל", "context": "הקשר", "continue": "המשך", - "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": "מחק מהמכשיר", @@ -695,6 +690,7 @@ "create_tag_description": "×Ļור ×Ēג חדש. ×ĸבור ×Ēגים מקוננים, נא להזין א×Ē ×”× ×Ēיב המלא של ה×Ēג כולל קווים נטויים.", "create_user": "×Ļור מ׊×Ēמ׊", "created": "נו×Ļר", + "created_at": "נו×Ļר", "crop": "ח×Ēוך", "curated_object_page_title": "דברים", "current_device": "מכשיר נוכחי", @@ -702,10 +698,14 @@ "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": "כהה", + "darkTheme": "החלפה למ×Ļב חושך", "date_after": "×Ēאריך אחרי", "date_and_time": "×Ēאריך וש×ĸה", "date_before": "×Ēאריך לפני", + "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "×Ēאריך לידה נ׊מר בה×Ļלחה", "date_range": "טווח ×Ēאריכים", "day": "יום", @@ -749,6 +749,7 @@ "disallow_edits": "אל ×Ēאפ׊ר ×ĸריכו×Ē", "discord": "דיסקורד", "discover": "גילוי", + "discovered_devices": "מכשירים שה×Ēגלו", "dismiss_all_errors": "ה×Ē×ĸלמו×Ē ×ž×›×œ השגיאו×Ē", "dismiss_error": "ה×Ē×ĸלמו×Ē ×ž×”×Š×’×™××”", "display_options": "ה×Ļג×Ē ××¤×Š×¨×•×™×•×Ē", @@ -764,7 +765,6 @@ "download_enqueue": "הורדה נוספה ל×Ēור", "download_error": "שגיא×Ē ×”×•×¨×“×”", "download_failed": "הורדה נכשלה", - "download_filename": "קוב×Ĩ: {filename}", "download_finished": "הורדה הס×Ēיימה", "download_include_embedded_motion_videos": "סרטונים מוטמ×ĸים", "download_include_embedded_motion_videos_description": "כלול סרטונים מוט×ĸמים ב×Ēמונו×Ē ×ĸם ×Ēנו×ĸה כקוב×Ĩ נפרד", @@ -788,6 +788,8 @@ "edit_avatar": "×ĸרוך ×Ēמונ×Ē ×¤×¨×•×¤×™×œ", "edit_date": "×ĸרוך ×Ēאריך", "edit_date_and_time": "×ĸרוך ×Ēאריך וש×ĸה", + "edit_description": "×ĸרוך ×Ēיאור", + "edit_description_prompt": "אנא בחר ×Ēיאור חדש:", "edit_exclusion_pattern": "×ĸרוך דפוס החרגה", "edit_faces": "×ĸרוך פנים", "edit_import_path": "×ĸרוך × ×Ēיב יבוא", @@ -808,19 +810,24 @@ "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 שלך כדי להפ×ĸיל אימו×Ē ×‘×™×•×ž×˜×¨×™", "enabled": "מופ×ĸל", "end_date": "×Ēאריך סיום", "enqueued": "הו×Ļבו ב×Ēור", "enter_wifi_name": "הזן ׊ם אינטרנט אלחוטי", + "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}", + "error_tag_face_bounding_box": "שגיאה ב×Ēיוג הפנים – לא ני×Ēן לקבל א×Ē ×§×•××•×¨×“×™× ×˜×•×Ē ×”×ž×Ą×’×¨×Ē", "error_title": "שגיאה - משהו הש×Ēבש", "errors": { "cannot_navigate_next_asset": "לא ני×Ēן לנווט ל×Ēמונה הבאה", @@ -833,7 +840,6 @@ "cant_get_number_of_comments": "לא ני×Ēן לקבל א×Ē ×ž×Ą×¤×¨ ה×Ēגובו×Ē", "cant_search_people": "לא ני×Ēן לחפש אנשים", "cant_search_places": "לא ני×Ēן לחפש מקומו×Ē", - "cleared_jobs": "משימו×Ē × ×•×§×• ×ĸבור: {job}", "error_adding_assets_to_album": "שגיאה בהוספ×Ē ×Ēמונו×Ē ×œ××œ×‘×•×", "error_adding_users_to_album": "שגיאה בהוספ×Ē ×ž×Š×Ēמשים לאלבום", "error_deleting_shared_user": "שגיאה במחיק×Ē ×ž×Š×Ēמ׊ משו×Ē×Ŗ", @@ -842,7 +848,6 @@ "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": "×ĸריכ×Ē ×§×™×Š×•×¨ משו×Ē×Ŗ נכשלה", @@ -861,7 +866,6 @@ "paths_validation_failed": "{paths, plural, one {× ×Ēיב # נכשל} 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": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ ×Ēגובה", @@ -873,13 +877,13 @@ "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 {אדם #} other {# אנשים}}", "unable_to_complete_oauth_login": "לא ני×Ēן להשלים ה×Ēחברו×Ē OAuth", "unable_to_connect": "לא ני×Ēן לה×Ēחבר", - "unable_to_connect_to_server": "לא ני×Ēן לה×Ēחבר לשר×Ē", "unable_to_copy_to_clipboard": "לא ני×Ēן לה×ĸ×Ēיק ללוח, יש לודא שניגש×Ē ×œ×“×Ŗ דרך https", "unable_to_create_admin_account": "לא ני×Ēן לי×Ļור חשבון מנהל", "unable_to_create_api_key": "לא ני×Ēן לי×Ļור מפ×Ēח API חדש", @@ -903,10 +907,6 @@ "unable_to_hide_person": "לא ני×Ēן להס×Ēיר אדם", "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", @@ -917,11 +917,9 @@ "unable_to_remove_album_users": "לא ני×Ēן להסיר מ׊×Ēמשים מהאלבום", "unable_to_remove_api_key": "לא ני×Ēן להסיר מפ×Ēח API", "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": "לא ני×Ēן לפ×Ēור כפילו×Ē", @@ -951,12 +949,12 @@ "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": "גיל {months} חודשים", "exif_bottom_sheet_person_age_year_months": "גיל שנה ו-{months} חודשים", "exif_bottom_sheet_person_age_years": "גיל {years}", @@ -977,9 +975,10 @@ "external": "חי×Ļוני", "external_libraries": "ספריו×Ē ×—×™×Ļוניו×Ē", "external_network": "ר׊×Ē ×—×™×Ļוני×Ē", - "external_network_sheet_info": "כאשר לא ×ĸל ר׊×Ē ×”××™× ×˜×¨× ×˜ האלחוטי המו×ĸדפ×Ē, היישום י×Ēחבר לשר×Ē ×“×¨×š הכ×Ēוב×Ē ×”×¨××Š×•× ×” שני×Ēן להשיג מהכ×Ēובו×Ē ×Š×œ×”×œ×Ÿ, החל מלמ×ĸלה למטה", + "external_network_sheet_info": "אם אינך מחובר ל־Wi‑Fi מו×ĸדת, האפליק×Ļיה ×Ēנסה לה×Ēחבר לשר×Ē ×œ×¤×™ הסדר מהרשימה שלמטה (מלמ×ĸלה למטה)", "face_unassigned": "לא מוק×Ļה", "failed": "נכשלו", + "failed_to_authenticate": "ההזדהו×Ē × ×›×Š×œ×”", "failed_to_load_assets": "ט×ĸינ×Ē ×Ēמונו×Ē × ×›×Š×œ×”", "failed_to_load_folder": "ט×ĸינ×Ē ×Ēיקיה נכשלה", "favorite": "מו×ĸדת", @@ -1003,6 +1002,8 @@ "folders": "×Ēיקיו×Ē", "folders_feature_description": "×ĸיון ב×Ē×Ļוג×Ē ×”×Ēיקייה ×ĸבור ה×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× שבמ×ĸרכ×Ē ×”×§×‘×Ļים", "forward": "קדימה", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "×Ēכונה זא×Ē ×˜×•×ĸ× ×Ē ×ž×Š××‘×™× חי×Ļוניים מגוגל בכדי לפ×ĸול.", "general": "כללי", "get_help": "קבל ×ĸזרה", "get_wifiname_error": "לא היה ני×Ēן לקבל א×Ē ×Š× האינטרנט האלחוטי שלך. יש לודא שה×ĸ× ×§×Ē ××Ē ×”×”×¨×Š××•×Ē ×”×“×¨×•×Š×•×Ē ×•×Š××Ē/ה מחובר/×Ē ×œ×¨×Š×Ē ××™× ×˜×¨× ×˜ אלחוטי", @@ -1045,10 +1046,13 @@ "home_page_favorite_err_local": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ למו×ĸדפים ×Ēמונו×Ē ×ž×§×•×ž×™×•×Ē ×ĸדיין, מדלג", "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": "ני×Ēן לה×ĸלו×Ē ×¨×§ מקסימום של 30 ×Ēמונו×Ē ×‘×›×œ פ×ĸם, מדלג", "host": "מארח", "hour": "׊×ĸה", + "id": "מזהה", "ignore_icloud_photos": "ה×Ē×ĸלם מ×Ēמונו×Ē iCloud", "ignore_icloud_photos_description": "×Ēמונו×Ē ×Š×ž××•×—×Ą× ×•×Ē ×‘-iCloud לא יו×ĸלו לשר×Ē", "image": "×Ēמונה", @@ -1088,6 +1092,12 @@ "invalid_date_format": "פורמט ×Ēאריך לא ×Ēקין", "invite_people": "הזמן אנשים", "invite_to_album": "הזמן לאלבום", + "ios_debug_info_fetch_ran_at": "השליפה בו×Ļ×ĸה ב־{dateTime}", + "ios_debug_info_last_sync_at": "סנכרון אחרון ב- {dateTime}", + "ios_debug_info_no_processes_queued": "אין ×Ēהליכי רק×ĸ הממ×Ēינים ב×Ēור", + "ios_debug_info_no_sync_yet": "טרם ה×Ēב×Ļ×ĸ ×Ēהליך סנכרון ברק×ĸ", + "ios_debug_info_processes_queued": "{count, plural, one {×Ēהליך רק×ĸ {count} ב×Ēור} other {{count} ×Ēהליכי רק×ĸ ב×Ēור}}", + "ios_debug_info_processing_ran_at": "ה×ĸיבוד ה×Ēב×Ļ×ĸ ב־{dateTime}", "items_count": "{count, plural, one {פריט #} other {# פריטים}}", "jobs": "משימו×Ē", "keep": "שמור", @@ -1096,6 +1106,9 @@ "kept_this_deleted_others": "×Ēמונה זו נשמרה ונמחקו {count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}}", "keyboard_shortcuts": "קי×Ļורי מקלד×Ē", "language": "שפה", + "language_no_results_subtitle": "נסה לשנו×Ē ××Ē â€Ēמונח החיפוש שלך", + "language_no_results_title": "לא נמ×Ļאה שפה", + "language_search_hint": "חפש שפו×Ē...", "language_setting_description": "בחר א×Ē ×”×Š×¤×” המו×ĸדפ×Ē ×ĸליך", "last_seen": "נראה לאחרונה", "latest_version": "גרסה ×ĸדכני×Ē ×‘×™×•×Ēר", @@ -1121,6 +1134,7 @@ "list": "רשימה", "loading": "טו×ĸן", "loading_search_results_failed": "ט×ĸינ×Ē ×Ēו×Ļאו×Ē ×”×—×™×¤×•×Š נכשלה", + "local_asset_cast_failed": "לא ני×Ēן לשדר ×Ēמונה שלא הו×ĸל×Ēה לשר×Ē", "local_network": "ר׊×Ē ×ž×§×•×ž×™×Ē", "local_network_sheet_info": "היישום י×Ēחבר לשר×Ē ×“×¨×š הכ×Ēוב×Ē ×”×–××Ē ×›××Š×¨ מ׊×Ēמשים ברש×Ē ×”××™× ×˜×¨× ×˜ האלחוטי ׊מ×Ļוינ×Ē", "location_permission": "הרשא×Ē ×ž×™×§×•×", @@ -1130,6 +1144,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": "מנו×Ē×§ מכל המכשירים", @@ -1139,6 +1155,7 @@ "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": "דוא\"ל שגוי", @@ -1222,7 +1239,14 @@ "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": "לא ני×Ēן ל×ĸרוך מיקום של ×Ēמונו×Ē ×œ×§×¨×™××” בלבד, מדלג", @@ -1238,6 +1262,7 @@ "new_password": "סיסמה חדשה", "new_person": "אדם חדש", "new_pin_code": "קוד PIN חדש", + "new_pin_code_subtitle": "זא×Ē ×”×¤×ĸם הראשונה שנכנס×Ē ×œ×Ēיקיה הנ×ĸולה. ×Ļור קוד PIN כדי לאבטח א×Ē ×”×’×™×Š×” ×œ×“×Ŗ זה", "new_user_created": "מ׊×Ēמ׊ חדש נו×Ļר", "new_version_available": "גרסה חדשה זמינה", "newest_first": "החדש ביו×Ēר ראשון", @@ -1250,11 +1275,13 @@ "no_archived_assets_message": "ה×ĸבר ×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× לארכיון כדי להס×Ēיר או×Ēם מ×Ē×Ļוג×Ē ×”×Ēמונו×Ē ×Š×œ×š", "no_assets_message": "לח×Ĩ כדי לה×ĸלו×Ē ××Ē ×”×Ēמונה הראשונה שלך", "no_assets_to_show": "אין ×Ēמונו×Ē ×œ×”×Ļגה", + "no_cast_devices_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": "לא נמ×Ļאו אנשים ×Ēואמים", @@ -1266,6 +1293,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": "אפ׊ר ה×Ēראו×Ē", @@ -1273,17 +1301,18 @@ "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": "במכשיר הזה", "onboarding": "היכרו×Ē", - "onboarding_privacy_description": "ה×Ēכונו×Ē (האופ×Ļיונליו×Ē) הבאו×Ē ×ž×Ą×Ēמכו×Ē ×ĸל שירו×Ēים חי×Ļוניים, וני×Ēנו×Ē ×œ×‘×™×˜×•×œ בכל ×ĸ×Ē ×‘×”×’×“×¨×•×Ē ×”× ×™×”×•×œ.", + "onboarding_locale_description": "יש לבחור שפה מו×ĸדפ×Ē, ני×Ēן לשנו×Ē ×–××Ē ×ž××•×—×¨ יו×Ēר בהגדרו×Ē.", + "onboarding_privacy_description": "ה×Ēכונו×Ē (האופ×Ļיונליו×Ē) הבאו×Ē ×ž×Ą×Ēמכו×Ē ×ĸל שירו×Ēים חי×Ļוניים, וני×Ēנו×Ē ×œ×‘×™×˜×•×œ בכל ×ĸ×Ē ×‘×”×’×“×¨×•×Ē.", + "onboarding_server_welcome_description": "בואו נא×Ēחל א×Ē ×”×Ą×¤×¨×™×” שלך ×ĸם כמה הגדרו×Ē × ×¤×•×Ļו×Ē.", "onboarding_theme_description": "יש לבחור א×Ē ×Ļב×ĸ ×ĸרכ×Ē ×”× ×•×Š× ×ĸבור הה×Ēקנה שלך. באפשרו×Ēך לשנו×Ē ××Ē ×–×” מאוחר יו×Ēר בהגדרו×Ē ×Š×œ×š.", - "onboarding_welcome_description": "בואו נכין א×Ē ×”×”×Ēקנה שלכם ×ĸם כמה הגדרו×Ē × ×¤×•×Ļו×Ē.", + "onboarding_user_welcome_description": "בואו × ×Ēחיל!", "onboarding_welcome_user": "ברוך בואך, {user}", "online": "מקוון", "only_favorites": "רק מו×ĸדפים", @@ -1340,6 +1369,8 @@ "permanently_delete_assets_prompt": "האם באמ×Ē ×‘×¨×Ļונך למחוק ל×Ļמי×Ēו×Ē {count, plural, one {×Ēמונה זא×Ē?} other {# ×Ēמונו×Ē ××œ×•?}}זה גם יסיר {count, plural, one {או×Ēו מאלבומו} other {או×Ēם מאלבומים}}.", "permanently_deleted_asset": "ה×Ēמונה נמחקה ל×Ļמי×Ēו×Ē", "permanently_deleted_assets_count": "{count, plural, one {×Ēמונה # נמחקה} other {# ×Ēמונו×Ē × ×ž×—×§×•}} ל×Ļמי×Ēו×Ē", + "permission": "הרשאו×Ē", + "permission_empty": "ההרשאו×Ē ×Š×œ×š לא יכולו×Ē ×œ×”×™×•×Ē ×¨×™×§×•×Ē", "permission_onboarding_back": "חזרה", "permission_onboarding_continue_anyway": "המשך בכל זא×Ē", "permission_onboarding_get_started": "לה×Ēחיל", @@ -1360,6 +1391,7 @@ "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} מקומו×Ē}}", @@ -1367,6 +1399,7 @@ "play_memories": "נגן זכרונו×Ē", "play_motion_photo": "הפ×ĸל ×Ēמונה ×ĸם ×Ēנו×ĸה", "play_or_pause_video": "הפ×ĸל או השהה סרטון", + "please_auth_to_access": "אנא אמ×Ē ××Ē ×–×”×•×Ēך כדי לגש×Ē", "port": "י×Ļיאה", "preferences_settings_subtitle": "ניהול ה×ĸדפו×Ē ×™×™×Š×•×", "preferences_settings_title": "ה×ĸדפו×Ē", @@ -1374,13 +1407,18 @@ "preview": "×Ē×Ļוגה מקדימה", "previous": "הקודם", "previous_memory": "זיכרון קודם", - "previous_or_next_photo": "ה×Ēמונה הקודמ×Ē ××• הבאה", + "previous_or_next_day": "ה×Ēקדם יום / חזור יום", + "previous_or_next_month": "חודש קדימה / חודש אחורה", + "previous_or_next_photo": "×Ēמונה הבאה / ה×Ēמונה הקודמ×Ē", + "previous_or_next_year": "שנה קדימה / שנה אחורה", "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}", @@ -1389,7 +1427,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": "קנה", @@ -1455,9 +1493,12 @@ "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": "הסר ×Ēמונה מזיכרון זה", + "remove_tag": "הסר×Ē ×Ēג", "remove_url": "הסר URL", "remove_user": "הסר מ׊×Ēמ׊", "removed_api_key": "מפ×Ēח API הוסר: {name}", @@ -1584,6 +1625,7 @@ "server_info_box_server_url": "כ×Ēוב×Ē ×Š×¨×Ē", "server_offline": "השר×Ē ×ž× ×•×Ē×§", "server_online": "החיבור לשר×Ē ×¤×ĸיל", + "server_privacy": "פרטיו×Ē ×”×Š×¨×Ē", "server_stats": "סטטיסטיקו×Ē ×Š×¨×Ē", "server_version": "גרס×Ē ×Š×¨×Ē", "set": "הגדר", @@ -1593,6 +1635,7 @@ "set_date_of_birth": "הגדר ×Ēאריך לידה", "set_profile_picture": "הגדר ×Ēמונ×Ē ×¤×¨×•×¤×™×œ", "set_slideshow_to_fullscreen": "הגדר מ×Ļג×Ē ×Š×§×•×¤×™×•×Ē ×œ×ž×Ą×š מלא", + "set_stack_primary_asset": "הגדרה כ×Ēמונה ראשי×Ē", "setting_image_viewer_help": "מ×Ļיג הפרטים טו×ĸן א×Ē ×”×Ēמונה הממוז×ĸר×Ē ×”×§×˜× ×” קודם, לאחר מכן טו×ĸן א×Ē ×”×Ē×Ļוגה המקדימה בגודל בינוני (אם מופ×ĸל), ×œ×‘×Ą×•×Ŗ טו×ĸן א×Ē ×”×ž×§×•×¨×™×Ē (אם מופ×ĸל).", "setting_image_viewer_original_subtitle": "אפ׊ר לט×ĸון א×Ē ×”×Ēמונה המקורי×Ē ×‘×¨×–×œ×•×Ļיה מלאה (גדולה!). השב×Ē ×›×“×™ להקטין שימוש בנ×Ēונים (גם בשר×Ē ×•×’× בזיכרון המטמון שבמכשיר).", "setting_image_viewer_original_title": "ט×ĸן ×Ēמונה מקורי×Ē", @@ -1601,7 +1644,6 @@ "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": "באופן מיידי", @@ -1624,6 +1666,7 @@ "share_add_photos": "×”×•×Ą×Ŗ ×Ēמונו×Ē", "share_assets_selected": "{count} נבחרו", "share_dialog_preparing": "מכין...", + "share_link": "׊×Ē×Ŗ קישור", "shared": "משו×Ē×Ŗ", "shared_album_activities_input_disable": "ה×Ēגובה מושב×Ē×Ē", "shared_album_activity_remove_content": "האם בר×Ļונך למחוק א×Ē ×”×¤×ĸילו×Ē ×”×–××Ē?", @@ -1663,6 +1706,7 @@ "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": "קישורים משו×Ēפים", @@ -1729,12 +1773,14 @@ "start_date": "×Ēאריך ה×Ēחלה", "state": "מדינה", "status": "מ×Ļב", + "stop_casting": "הפסק×Ē ×Š×™×“×•×¨", "stop_motion_photo": "×ĸ×Ļור ×Ēמונה ×ĸם ×Ēנו×ĸה", "stop_photo_sharing": "להפסיק לש×Ē×Ŗ א×Ē ×”×Ēמונו×Ē ×Š×œ×š?", "stop_photo_sharing_description": "{partner} לא יוכל לגש×Ē ×œ×Ēמונו×Ē ×Š×œ×š יו×Ēר.", "stop_sharing_photos_with_user": "הפסק לש×Ē×Ŗ א×Ē ×”×Ēמונו×Ē ×Š×œ×š ×ĸם מ׊×Ēמ׊ זה", "storage": "שטח אחסון", "storage_label": "×Ēווי×Ē ××—×Ą×•×Ÿ", + "storage_quota": "מכס×Ē ×”××—×Ą×•×Ÿ", "storage_usage": "{used} בשימוש מ×Ēוך {available}", "submit": "שלח", "suggestions": "ה×Ļ×ĸו×Ē", @@ -1785,7 +1831,6 @@ "to_parent": "לך להורה", "to_trash": "אשפה", "toggle_settings": "×”×—×œ×Ŗ מ×Ļב הגדרו×Ē", - "toggle_theme": "×”×—×œ×Ŗ ×ĸרכ×Ē × ×•×Š× כהה", "total": "סה\"כ", "total_usage": "שימוש כולל", "trash": "אשפה", @@ -1807,6 +1852,7 @@ "unable_to_setup_pin_code": "לא ני×Ēן להגדיר קוד PIN", "unarchive": "הו×Ļא מארכיון", "unarchived_count": "{count, plural, other {# הו×Ļאו מהארכיון}}", + "undo": "לבטל", "unfavorite": "לא מו×ĸדת", "unhide_person": "בטל הס×Ēר×Ē ××“×", "unknown": "לא ידו×ĸ", @@ -1825,9 +1871,8 @@ "unselect_all_duplicates": "בטל בחיר×Ē ×›×œ הכפילויו×Ē", "unstack": "בטל ×ĸרימה", "unstacked_assets_count": "{count, plural, one {×Ēמונה # הוסרה} other {# ×Ēמונו×Ē ×”×•×Ą×¨×•}} מה×ĸרימה", - "untracked_files": "קב×Ļים ללא מ×ĸקב", - "untracked_files_decription": "קב×Ļים אלה אינם נמ×Ļאים במ×ĸקב של היישום. הם יכולים להיו×Ē ×Ēו×Ļאו×Ē ×Š×œ ה×ĸברו×Ē ×›×•×Š×œ×•×Ē, ה×ĸלאו×Ē ×Š× ×§×˜×ĸו, או שנו×Ēרו מאחור בגלל שיבוש ב×Ēוכנה", "up_next": "הבא ב×Ēור", + "updated_at": "×ĸודכן", "updated_password": "סיסמה ×ĸודכנה", "upload": "ה×ĸלאה", "upload_concurrency": "בו-זמניו×Ē ×Š×œ ה×ĸלאה", @@ -1842,14 +1887,18 @@ "upload_success": "הה×ĸלאה בו×Ļ×ĸה בה×Ļלחה. ר×ĸנן א×Ē ×”×“×Ŗ כדי ל×Ļפו×Ē ×‘×Ēמונו×Ē ×Š×”×•×ĸלו.", "upload_to_immich": "ה×ĸלה לשר×Ē ({count})", "uploading": "מ×ĸלה", + "url": "URL", "usage": "שימוש", + "use_biometric": "הש×Ēמ׊ באימו×Ē ×‘×™×•×ž×˜×¨×™", "use_current_connection": "הש×Ēמ׊ בחיבור נוכחי", "use_custom_date_range": "הש×Ēמ׊ בטווח ×Ēאריכים מו×Ēאם במקום", "user": "מ׊×Ēמ׊", + "user_has_been_deleted": "מ׊×Ēמ׊ זה נמחק.", "user_id": "מזהה מ׊×Ēמ׊", "user_liked": "{user} אהב א×Ē {type, select, photo {ה×Ēמונה הזא×Ē} video {הסרטון הזה} asset {ה×Ēמונה הזא×Ē} other {זה}}", "user_pin_code_settings": "קוד PIN", "user_pin_code_settings_description": "נהל א×Ē ×§×•×“ ה PIN שלך", + "user_privacy": "פרטיו×Ē ×”×ž×Š×Ēמ׊", "user_purchase_settings": "רכישה", "user_purchase_settings_description": "ניהול הרכישה שלך", "user_role_set": "הגדר א×Ē {user} ב×Ēור {role}", @@ -1865,11 +1914,6 @@ "version": "גרסה", "version_announcement_closing": "החבר שלך, אלכס", "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_3": " ולוודא שמבנה ה docker-compose וה env. שלך ×ĸדכני כדי למנו×ĸ ×Ē×Ļורו×Ē ×Š×’×•×™×•×Ē, במיוחד אם א×Ēה מ׊×Ēמ׊ ב WatchTower או במנגנון שמטפל ב×ĸדכון השר×Ē ×‘××•×¤×Ÿ אוטומטי.", - "version_announcement_overlay_title": "גרס×Ē ×Š×¨×Ē ×—×“×Š×” זמינה 🎉", "version_history": "היסטוריי×Ē ×’×¨×Ą××•×Ē", "version_history_item": "{version} הו×Ēקנה ב-{date}", "video": "סרטון", @@ -1889,6 +1933,7 @@ "view_previous_asset": "ה×Ļג א×Ē ×”×Ēמונה הקודמ×Ē", "view_qr_code": "ה×Ļג ברקוד", "view_stack": "ה×Ļג ×ĸרימה", + "view_user": "ה×Ļג מ׊×Ēמ׊", "viewer_remove_from_stack": "הסר מ×ĸרימה", "viewer_stack_use_as_main_asset": "הש×Ēמ׊ כ×Ēמונה ראשי×Ē", "viewer_unstack": "ביטול ×ĸרימה", @@ -1899,6 +1944,7 @@ "welcome": "ברוכים הבאים", "welcome_to_immich": "ברוכים הבאים אל immich", "wifi_name": "׊ם הרש×Ē ×”××œ×—×•×˜×™×Ē", + "wrong_pin_code": "קוד PIN שגוי", "year": "שנה", "years_ago": "לפני {years, plural, one {שנה #} other {# שנים}}", "yes": "כן", diff --git a/i18n/hi.json b/i18n/hi.json index 823380c24f..abb1552154 100644 --- a/i18n/hi.json +++ b/i18n/hi.json @@ -4,6 +4,7 @@ "account_settings": "⤅⤭ā¤ŋ⤞āĨ‡ā¤– ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤ž", "acknowledge": "⤏āĨā¤ĩāĨ€ā¤•ā¤žā¤° ⤕⤰āĨ‡ā¤‚", "action": "ā¤•ā¤žā¤°āĨā¤°ā¤ĩā¤žā¤ˆ", + "action_common_update": "⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨", "actions": "ā¤•ā¤žā¤°āĨā¤¯ā¤ĩā¤žā¤šā¤ŋā¤¯ā¤žā¤‚", "active": "⤏⤕āĨā¤°ā¤ŋ⤝", "activity": "⤗⤤ā¤ŋā¤ĩā¤ŋ⤧ā¤ŋ", @@ -25,7 +26,6 @@ "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,13 +39,11 @@ "authentication_settings_disable_all": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤏⤭āĨ€ ⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤ĩā¤ŋ⤧ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ⤅⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤ĒāĨ‚⤰āĨ€ ā¤¤ā¤°ā¤š ⤏āĨ‡ ⤅⤕āĨā¤ˇā¤Ž ⤕⤰ ā¤Ļā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤žāĨ¤", "authentication_settings_reenable": "ā¤ĒāĨā¤¨ā¤ƒ ⤏⤕āĨā¤ˇā¤Ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, Server Command ā¤•ā¤ž ā¤ĒāĨā¤°ā¤¯āĨ‹ā¤— ⤕⤰āĨ‡āĨ¤", "background_task_job": "ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ā¤•ā¤žā¤°āĨā¤¯", - "backup_database": "ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤Ąā¤žā¤Ÿā¤žā¤ŦāĨ‡ā¤¸", - "backup_database_enable_description": "ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ⤏⤕āĨā¤°ā¤ŋ⤝ ⤕⤰āĨ‡ā¤‚", - "backup_keep_last_amount": "ā¤ĒāĨ‚⤰āĨā¤ĩ ā¤ŦāĨˆā¤•⤅ā¤Ē ⤕āĨā¤ˇā¤Žā¤¤ā¤ž", - "backup_settings": "ā¤ŦāĨˆā¤•⤅ā¤Ē ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", - "backup_settings_description": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤ŦāĨˆā¤•⤅ā¤Ē ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧⤍", - "check_all": "⤏⤭āĨ€ ⤚āĨ‡ā¤• ⤕⤰āĨ‡ā¤‚", - "cleanup": "ā¤¸ā¤žā¤Ģā¤ŧ-⤏ā¤Ģā¤ŧā¤žā¤ˆ", + "backup_database": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤Ąā¤‚ā¤Ē ā¤Ŧā¤¨ā¤žā¤ā¤‚", + "backup_database_enable_description": "Enable database dumps", + "backup_keep_last_amount": "⤰⤖⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ēā¤ŋ⤛⤞āĨ‡ ā¤Ąā¤‚ā¤Ē ⤕āĨ€ ā¤Žā¤žā¤¤āĨā¤°ā¤ž", + "backup_settings": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤Ąā¤‚ā¤Ē ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", + "backup_settings_description": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤Ąā¤‚ā¤Ē ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚āĨ¤ ⤧āĨā¤¯ā¤žā¤¨ ā¤ĻāĨ‡ā¤‚: ⤇⤍ ā¤•ā¤žā¤°āĨā¤¯āĨ‹ā¤‚ ⤕āĨ€ ⤍ā¤ŋā¤—ā¤°ā¤žā¤¨āĨ€ ā¤¨ā¤šāĨ€ā¤‚ ⤕āĨ€ ā¤œā¤žā¤¤āĨ€ ā¤šāĨˆ ⤔⤰ ā¤ĩā¤ŋā¤Ģā¤˛ā¤¤ā¤ž ⤕āĨ€ ⤏āĨā¤Ĩā¤ŋ⤤ā¤ŋ ā¤ŽāĨ‡ā¤‚ ⤆ā¤Ē⤕āĨ‹ ⤏āĨ‚ā¤šā¤ŋ⤤ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤žāĨ¤", "cleared_jobs": "{job}: ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤•ā¤žā¤°āĨā¤¯ ā¤¸ā¤žā¤Ģā¤ŧ ⤕⤰ ā¤Ļā¤ŋā¤ ā¤—ā¤", "config_set_by_file": "Config ā¤ĩ⤰āĨā¤¤ā¤Žā¤žā¤¨ ā¤ŽāĨ‡ā¤‚ ā¤ā¤• config ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ⤏āĨ‡ā¤Ÿ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆ", "confirm_delete_library": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ {library} ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", @@ -53,32 +51,44 @@ "confirm_email_below": "ā¤ĒāĨā¤ˇāĨā¤Ÿā¤ŋ ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤍āĨ€ā¤šāĨ‡ \"{email}\" ā¤Ÿā¤žā¤‡ā¤Ē ⤕⤰āĨ‡ā¤‚", "confirm_reprocess_all_faces": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤏⤭āĨ€ ⤚āĨ‡ā¤šā¤°āĨ‹ā¤‚ ⤕āĨ‹ ā¤ĻāĨ‹ā¤Ŧā¤žā¤°ā¤ž ā¤¸ā¤‚ā¤¸ā¤žā¤§ā¤ŋ⤤ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ⤇⤏⤏āĨ‡ ā¤¨ā¤žā¤Žā¤ŋ⤤ ⤞āĨ‹ā¤— ⤭āĨ€ ā¤¸ā¤žā¤Ģ ā¤šāĨ‹ ā¤œā¤žā¤¯āĨ‡ā¤‚⤗āĨ‡āĨ¤", "confirm_user_password_reset": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ {user} ā¤•ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", + "confirm_user_pin_code_reset": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ {user} ā¤•ā¤ž ā¤Ēā¤ŋ⤍ ⤕āĨ‹ā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", "create_job": "⤜āĨ‰ā¤Ŧ ā¤Ŧā¤¨ā¤žā¤ā¤", "cron_expression": "⤕āĨā¤°āĨ‰ā¤¨ ⤅⤭ā¤ŋā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ", "cron_expression_description": "⤕āĨā¤°āĨ‰ā¤¨ ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ē ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰⤕āĨ‡ ⤏āĨā¤•āĨˆā¤¨ā¤ŋ⤂⤗ ā¤…ā¤‚ā¤¤ā¤°ā¤žā¤˛ ⤏āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚āĨ¤ ⤅⤧ā¤ŋ⤕ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤕āĨƒā¤Ēā¤¯ā¤ž ⤕āĨā¤°āĨ‹ā¤¨ā¤ŸāĨˆā¤Ŧ ⤗āĨā¤°āĨ ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚", + "cron_expression_presets": "⤕āĨā¤°āĨ‰ā¤¨ ⤅⤭ā¤ŋā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ā¤ĒāĨā¤°āĨ€ā¤¸āĨ‡ā¤Ÿ", "disable_login": "⤞āĨ‰ā¤—ā¤ŋ⤍ ⤅⤕āĨā¤ˇā¤Ž ⤕⤰āĨ‡ā¤‚", "duplicate_detection_job_description": "ā¤¸ā¤Žā¤žā¤¨ ⤛ā¤ĩā¤ŋ⤝āĨ‹ā¤‚ ā¤•ā¤ž ā¤Ēā¤¤ā¤ž ā¤˛ā¤—ā¤žā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤Ē⤰ ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ā¤šā¤˛ā¤žā¤ā¤‚āĨ¤ ā¤¯ā¤š ā¤•ā¤žā¤°āĨā¤¯ā¤•āĨā¤ˇā¤Žā¤¤ā¤ž ⤏āĨā¤Žā¤žā¤°āĨā¤Ÿ ⤖āĨ‹ā¤œ ā¤Ē⤰ ⤍ā¤ŋ⤰āĨā¤­ā¤° ⤕⤰⤤āĨ€ ā¤šāĨˆ", "exclusion_pattern_description": "Exclusion ā¤ĒāĨˆā¤Ÿā¤°āĨā¤¨ ⤆ā¤Ē⤕āĨ‹ ⤅ā¤Ē⤍āĨ€ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ⤕āĨ‹ ⤏āĨā¤•āĨˆā¤¨ ⤕⤰⤤āĨ‡ ā¤¸ā¤Žā¤¯ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤔⤰ ā¤Ģā¤ŧāĨ‹ā¤˛āĨā¤Ąā¤°āĨ‹ā¤‚ ⤕āĨ‹ ⤅⤍ā¤ĻāĨ‡ā¤–ā¤ž ⤕⤰⤍āĨ‡ ā¤ĻāĨ‡ā¤¤ā¤ž ā¤šāĨˆāĨ¤ ā¤¯ā¤š ⤉ā¤Ē⤝āĨ‹ā¤—āĨ€ ā¤šāĨˆ ⤝ā¤Ļā¤ŋ ⤆ā¤Ē⤕āĨ‡ ā¤Ēā¤žā¤¸ ⤐⤏āĨ‡ ā¤Ģā¤ŧāĨ‹ā¤˛āĨā¤Ąā¤° ā¤šāĨˆā¤‚ ⤜ā¤ŋā¤¨ā¤ŽāĨ‡ā¤‚ ⤐⤏āĨ€ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‡ā¤‚ ā¤šāĨˆā¤‚ ⤜ā¤ŋ⤍āĨā¤šāĨ‡ā¤‚ ⤆ā¤Ē ā¤†ā¤¯ā¤žā¤¤ ā¤¨ā¤šāĨ€ā¤‚ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚, ⤜āĨˆā¤¸āĨ‡ RAW ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‡ā¤‚āĨ¤", - "external_library_created_at": "ā¤Ŧā¤žā¤šā¤°āĨ€ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ({date} ⤕āĨ‹ ā¤Ŧā¤¨ā¤žā¤ˆ ā¤—ā¤ˆ)", "external_library_management": "ā¤Ŧā¤žā¤šā¤°āĨ€ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧⤍", "face_detection": "ā¤ŽāĨā¤– ⤏⤂ā¤ļāĨ‹ā¤§ā¤¨", "face_detection_description": "ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰⤕āĨ‡ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤ŽāĨ‡ā¤‚ ⤚āĨ‡ā¤šā¤°āĨ‹ā¤‚ ā¤•ā¤ž ā¤Ēā¤¤ā¤ž ā¤˛ā¤—ā¤žā¤ā¤‚āĨ¤ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ⤕āĨ‡ ⤞ā¤ŋā¤, ⤕āĨ‡ā¤ĩ⤞ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ā¤Ē⤰ ā¤ĩā¤ŋā¤šā¤žā¤° ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤¤ā¤ž ā¤šāĨˆāĨ¤ \"⤏⤭āĨ€\" ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ (ā¤ĒāĨā¤¨ā¤ƒ) ā¤¸ā¤‚ā¤¸ā¤žā¤§ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ā¤šāĨˆāĨ¤ \"ā¤˛ā¤žā¤Ēā¤¤ā¤ž\" ⤉⤍ ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤•ā¤¤ā¤žā¤°ā¤Ŧā¤ĻāĨā¤§ ā¤•ā¤°ā¤¤ā¤ž ā¤šāĨˆ ⤜ā¤ŋ⤍āĨā¤šāĨ‡ā¤‚ ⤅⤭āĨ€ ⤤⤕ ā¤¸ā¤‚ā¤¸ā¤žā¤§ā¤ŋ⤤ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆāĨ¤ ā¤ĢāĨ‡ā¤¸ ā¤Ąā¤ŋ⤟āĨ‡ā¤•āĨā¤ļ⤍ ā¤ĒāĨ‚ā¤°ā¤ž ā¤šāĨ‹ā¤¨āĨ‡ ⤕āĨ‡ ā¤Ŧā¤žā¤Ļ ā¤Ēā¤šā¤šā¤žā¤¨āĨ‡ ā¤—ā¤ ⤚āĨ‡ā¤šā¤°āĨ‹ā¤‚ ⤕āĨ‹ ⤚āĨ‡ā¤šā¤°āĨ‡ ⤕āĨ€ ā¤Ēā¤šā¤šā¤žā¤¨ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤•ā¤¤ā¤žā¤°ā¤Ŧā¤ĻāĨā¤§ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤ž, ⤉⤍āĨā¤šāĨ‡ā¤‚ ā¤ŽāĨŒā¤œāĨ‚ā¤Ļā¤ž ā¤¯ā¤ž ā¤¨ā¤ ⤞āĨ‹ā¤—āĨ‹ā¤‚ ā¤ŽāĨ‡ā¤‚ ā¤¸ā¤ŽāĨ‚ā¤šā¤ŋ⤤ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤žāĨ¤", "facial_recognition_job_description": "ā¤¸ā¤ŽāĨ‚ā¤š ⤍āĨ‡ ⤞āĨ‹ā¤—āĨ‹ā¤‚ ā¤ŽāĨ‡ā¤‚ ⤚āĨ‡ā¤šā¤°āĨ‹ā¤‚ ā¤•ā¤ž ā¤Ēā¤¤ā¤ž ā¤˛ā¤—ā¤žā¤¯ā¤žāĨ¤ ā¤¯ā¤š ⤚⤰⤪ ā¤ĢāĨ‡ā¤¸ ā¤Ąā¤ŋ⤟āĨ‡ā¤•āĨā¤ļ⤍ ā¤ĒāĨ‚ā¤°ā¤ž ā¤šāĨ‹ā¤¨āĨ‡ ⤕āĨ‡ ā¤Ŧā¤žā¤Ļ ā¤šā¤˛ā¤¤ā¤ž ā¤šāĨˆāĨ¤ \"⤏⤭āĨ€\" ⤚āĨ‡ā¤šā¤°āĨ‹ā¤‚ ⤕āĨ‹ (ā¤ĒāĨā¤¨ā¤ƒ) ā¤¸ā¤ŽāĨ‚ā¤šā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ā¤šāĨˆāĨ¤ \"ā¤˛ā¤žā¤Ēā¤¤ā¤ž\" ā¤•ā¤¤ā¤žā¤° ā¤ŽāĨ‡ā¤‚ ā¤ĩāĨ‡ ⤚āĨ‡ā¤šā¤°āĨ‡ ā¤šāĨˆā¤‚ ⤜ā¤ŋ⤍⤕āĨ‡ ⤞ā¤ŋā¤ ⤕āĨ‹ā¤ˆ ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤍ā¤ŋ⤝āĨā¤•āĨā¤¤ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆāĨ¤", "failed_job_command": "ā¤•ā¤žā¤°āĨā¤¯ {job} ⤕āĨ‡ ⤞ā¤ŋā¤ ⤆ā¤ĻāĨ‡ā¤ļ {command} ā¤ĩā¤ŋā¤Ģ⤞", "force_delete_user_warning": "⤚āĨ‡ā¤¤ā¤žā¤ĩ⤍āĨ€: ⤇⤏⤏āĨ‡ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤔⤰ ā¤¸ā¤žā¤°ā¤ž ā¤ĄāĨ‡ā¤Ÿā¤ž ⤤āĨā¤°ā¤‚⤤ ā¤šā¤Ÿ ā¤œā¤žā¤ā¤—ā¤žāĨ¤ ⤇⤏āĨ‡ ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž ⤔⤰ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‡ā¤‚ ā¤ĒāĨā¤¨ā¤°āĨā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ ā¤¨ā¤šāĨ€ā¤‚ ⤕āĨ€ ā¤œā¤ž ⤏⤕⤤āĨ€ā¤‚āĨ¤", - "forcing_refresh_library_files": "⤏⤭āĨ€ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‹ ⤜ā¤Ŧ⤰⤍ ā¤¸ā¤žā¤Žā¤¯ā¤ŋ⤕ ⤕⤰āĨ‡ā¤‚", "image_format": "ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ē", "image_format_description": "ā¤ĩāĨ‡ā¤Ŧā¤ĒāĨ€, ⤜āĨ‡ā¤ĒāĨ€ā¤ˆā¤œāĨ€ ⤕āĨ€ ⤤āĨā¤˛ā¤¨ā¤ž ā¤ŽāĨ‡ā¤‚ ⤛āĨ‹ā¤ŸāĨ€ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‡ā¤‚ ā¤Ŧā¤¨ā¤žā¤¤ā¤ž ā¤šāĨˆ, ⤞āĨ‡ā¤•ā¤ŋ⤍ ā¤ā¤¨ā¤•āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤧āĨ€ā¤Žā¤ž ā¤šāĨˆāĨ¤", + "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_embedded_preview_setting_description": "RAW ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ā¤ŽāĨ‡ā¤‚ ā¤ā¤ŽāĨā¤ŦāĨ‡ā¤ĄāĨ‡ā¤Ą ā¤ĒāĨā¤°āĨ€ā¤ĩāĨā¤¯āĨ‚ ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ā¤‡ā¤ŽāĨ‡ā¤œ ā¤ĒāĨā¤°āĨ‹ā¤¸āĨ‡ā¤¸ā¤ŋ⤂⤗ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤇⤍ā¤ĒāĨā¤Ÿ ⤕āĨ‡ ⤰āĨ‚ā¤Ē ā¤ŽāĨ‡ā¤‚ ⤔⤰ ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤šāĨ‹ā¤¨āĨ‡ ā¤Ē⤰ ⤕⤰āĨ‡ā¤‚āĨ¤ ā¤¯ā¤š ⤕āĨā¤› ⤛ā¤ĩā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤅⤧ā¤ŋ⤕ ⤏⤟āĨ€ā¤• ⤰⤂⤗ ā¤ĒāĨā¤°ā¤Ļā¤žā¤¨ ⤕⤰ ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆ, ⤞āĨ‡ā¤•ā¤ŋ⤍ ā¤ĒāĨā¤°āĨ€ā¤ĩāĨā¤¯āĨ‚ ⤕āĨ€ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ⤕āĨˆā¤Žā¤°āĨ‡ ā¤Ē⤰ ⤍ā¤ŋ⤰āĨā¤­ā¤° ⤕⤰⤤āĨ€ ā¤šāĨˆ ⤔⤰ ⤛ā¤ĩā¤ŋ ā¤ŽāĨ‡ā¤‚ ⤅⤧ā¤ŋ⤕ ⤕⤂ā¤ĒāĨā¤°āĨ‡ā¤ļ⤍ ⤆⤰āĨā¤Ÿā¤ŋā¤ĢāĨˆā¤•āĨā¤Ÿ ā¤šāĨ‹ ⤏⤕⤤āĨ‡ ā¤šāĨˆā¤‚āĨ¤", "image_prefer_wide_gamut": "ā¤ĩā¤ŋ⤏āĨā¤¤āĨƒā¤¤ ā¤¸ā¤°ā¤—ā¤Ž ⤕āĨ‹ ā¤ĒāĨā¤°ā¤žā¤Ĩā¤Žā¤ŋā¤•ā¤¤ā¤ž ā¤ĻāĨ‡ā¤‚", "image_prefer_wide_gamut_setting_description": "ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ąā¤ŋ⤏āĨā¤ĒāĨā¤˛āĨ‡ P3 ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚āĨ¤ ā¤¯ā¤š ā¤ĩā¤ŋ⤏āĨā¤¤āĨƒā¤¤ ⤕⤞⤰⤏āĨā¤ĒāĨ‡ā¤¸ ā¤ĩā¤žā¤˛āĨ€ ⤛ā¤ĩā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ€ ⤜āĨ€ā¤ĩā¤‚ā¤¤ā¤¤ā¤ž ⤕āĨ‹ ā¤ŦāĨ‡ā¤šā¤¤ā¤° ā¤ĸ⤂⤗ ⤏āĨ‡ ⤏⤂⤰⤕āĨā¤ˇā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ā¤šāĨˆ, ⤞āĨ‡ā¤•ā¤ŋ⤍ ā¤ĒāĨā¤°ā¤žā¤¨āĨ‡ ā¤ŦāĨā¤°ā¤žā¤‰ā¤œā¤ŧ⤰ ⤏⤂⤏āĨā¤•⤰⤪ ā¤ĩā¤žā¤˛āĨ‡ ā¤ĒāĨā¤°ā¤žā¤¨āĨ‡ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ā¤Ē⤰ ⤛ā¤ĩā¤ŋā¤¯ā¤žā¤‚ ⤅⤞⤗-⤅⤞⤗ ā¤Ļā¤ŋā¤–ā¤žā¤ˆ ā¤ĻāĨ‡ ⤏⤕⤤āĨ€ ā¤šāĨˆā¤‚āĨ¤ ⤰⤂⤗ ā¤Ē⤰ā¤ŋā¤ĩ⤰āĨā¤¤ā¤¨ ⤏āĨ‡ ā¤Ŧ⤚⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ sRGB ⤛ā¤ĩā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ sRGB ⤕āĨ‡ ⤰āĨ‚ā¤Ē ā¤ŽāĨ‡ā¤‚ ā¤°ā¤–ā¤ž ā¤œā¤žā¤¤ā¤ž ā¤šāĨˆāĨ¤", "image_preview_description": "ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤°ā¤šā¤ŋ⤤ ā¤Žā¤§āĨā¤¯ā¤Ž ā¤†ā¤•ā¤žā¤° ⤕āĨ€ ⤛ā¤ĩā¤ŋ, ⤜ā¤ŋā¤¸ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ā¤ā¤•ā¤˛ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤ĻāĨ‡ā¤–⤍āĨ‡ ⤔⤰ ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤šāĨ‹ā¤¤ā¤ž ā¤šāĨˆ", + "image_preview_quality_description": "ā¤ĒāĨ‚⤰āĨā¤ĩā¤žā¤ĩ⤞āĨ‹ā¤•⤍ ⤕āĨ€ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž (1 ⤏āĨ‡ 100 ⤤⤕)āĨ¤ ⤅⤧ā¤ŋ⤕ ā¤Žā¤žā¤¨ ā¤ŦāĨ‡ā¤šā¤¤ā¤° ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ā¤ĻāĨ‡ā¤¤ā¤ž ā¤šāĨˆ, ⤞āĨ‡ā¤•ā¤ŋ⤍ ⤇⤏⤏āĨ‡ ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤•ā¤ž ā¤†ā¤•ā¤žā¤° ā¤Ŧā¤ĸā¤ŧā¤¤ā¤ž ā¤šāĨˆ ⤔⤰ ⤐ā¤Ē ⤕āĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤕āĨā¤ˇā¤Žā¤¤ā¤ž ā¤•ā¤Ž ā¤šāĨ‹ ⤏⤕⤤āĨ€ ā¤šāĨˆāĨ¤ ā¤Ŧā¤šāĨā¤¤ ā¤•ā¤Ž ā¤Žā¤žā¤¨ ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ⤕āĨ€ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ⤕āĨ‹ ā¤ĒāĨā¤°ā¤­ā¤žā¤ĩā¤ŋ⤤ ⤕⤰ ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆāĨ¤", "image_preview_title": "ā¤ĒāĨ‚⤰āĨā¤ĩā¤Ļ⤰āĨā¤ļ⤍ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", "image_quality": "⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž", + "image_resolution": "⤰ā¤ŋ⤜ā¤ŧāĨ‰ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍", + "image_resolution_description": "ā¤‰ā¤šāĨā¤šā¤¤ā¤° ⤰ā¤ŋ⤜ā¤ŧāĨ‰ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ⤅⤧ā¤ŋ⤕ ā¤ĩā¤ŋā¤ĩ⤰⤪ ⤏āĨā¤°ā¤•āĨā¤ˇā¤ŋ⤤ ⤰⤖ ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆ, ⤞āĨ‡ā¤•ā¤ŋ⤍ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤅⤧ā¤ŋ⤕ ā¤¸ā¤Žā¤¯ ⤞āĨ‡ā¤¤ā¤ž ā¤šāĨˆ, ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤†ā¤•ā¤žā¤° ā¤Ŧā¤Ąā¤ŧā¤ž ā¤šāĨ‹ā¤¤ā¤ž ā¤šāĨˆ ⤔⤰ ⤐ā¤Ē ⤕āĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤕āĨā¤°ā¤ŋā¤¯ā¤žā¤ļāĨ€ā¤˛ā¤¤ā¤ž ā¤•ā¤Ž ā¤šāĨ‹ ⤏⤕⤤āĨ€ ā¤šāĨˆāĨ¤", "image_settings": "⤛ā¤ĩā¤ŋ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", "image_settings_description": "⤉⤤āĨā¤Ē⤍āĨā¤¨ ⤛ā¤ĩā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ€ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ⤔⤰ ⤰ā¤ŋ⤜ā¤ŧāĨ‰ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "image_thumbnail_description": "ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤šā¤Ÿā¤žā¤ˆ ā¤—ā¤ˆ ⤛āĨ‹ā¤ŸāĨ€ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛, ⤜ā¤ŋā¤¸ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤¸ā¤ŽāĨ‚ā¤šāĨ‹ā¤‚ ⤕āĨ‹ ā¤ĻāĨ‡ā¤–⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤜āĨˆā¤¸āĨ‡ ā¤ŽāĨā¤–āĨā¤¯ ā¤Ÿā¤žā¤‡ā¤Žā¤˛ā¤žā¤‡ā¤¨ ā¤ŽāĨ‡ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤¤ā¤ž ā¤šāĨˆ", + "image_thumbnail_quality_description": "ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤕āĨ€ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž 1-100 ⤤⤕āĨ¤ ā¤‰ā¤šāĨā¤šā¤¤ā¤° ā¤ŦāĨ‡ā¤šā¤¤ā¤° ā¤šāĨˆ, ⤞āĨ‡ā¤•ā¤ŋ⤍ ā¤Ŧā¤Ąā¤ŧāĨ€ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‡ā¤‚ ā¤Ŧā¤¨ā¤žā¤¤ā¤ž ā¤šāĨˆ ⤔⤰ ⤐ā¤Ē ⤕āĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤕āĨā¤°ā¤ŋā¤¯ā¤žā¤ļāĨ€ā¤˛ā¤¤ā¤ž ⤕āĨ‹ ā¤•ā¤Ž ⤕⤰ ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆāĨ¤", + "image_thumbnail_title": "ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", "job_concurrency": "{job} ā¤¸ā¤Žā¤°āĨ‚ā¤Ēā¤¤ā¤ž", + "job_created": "⤍āĨŒā¤•⤰āĨ€ ā¤Ŧā¤¨ā¤žā¤ˆ ā¤—ā¤ˆ", "job_not_concurrency_safe": "ā¤¯ā¤š ā¤•ā¤žā¤°āĨā¤¯ (⤜āĨ‰ā¤Ŧ) ā¤¸ā¤Žā¤ĩ⤰āĨā¤¤āĨ€-⤏āĨā¤°ā¤•āĨā¤ˇā¤ŋ⤤ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆāĨ¤", "job_settings": "ā¤•ā¤žā¤°āĨā¤¯ (⤜āĨ‰ā¤Ŧ) ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", "job_settings_description": "ā¤•ā¤žā¤°āĨā¤¯ (⤜āĨ‰ā¤Ŧ) ā¤¸ā¤Žā¤ĩ⤰āĨā¤¤āĨ€ā¤¤ā¤ž ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", @@ -93,7 +103,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": "ā¤Ē⤰ā¤ŋā¤ĩ⤰āĨā¤¤ā¤ŋ⤤ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ā¤Ē⤰ ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ⤍⤜ā¤ŧ⤰ ⤰⤖āĨ‡ā¤‚", @@ -128,13 +138,14 @@ "machine_learning_smart_search_description": "CLIP ā¤ā¤ŽāĨā¤ŦāĨ‡ā¤Ąā¤ŋ⤂⤗ ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰⤕āĨ‡ ā¤ļā¤ŦāĨā¤Ļā¤žā¤°āĨā¤Ĩ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ⤛ā¤ĩā¤ŋā¤¯ā¤žā¤‚ ⤖āĨ‹ā¤œāĨ‡ā¤‚", "machine_learning_smart_search_enabled": "⤏āĨā¤Žā¤žā¤°āĨā¤Ÿ ⤖āĨ‹ā¤œ ⤏⤕āĨā¤ˇā¤Ž ⤕⤰āĨ‡ā¤‚", "machine_learning_smart_search_enabled_description": "⤝ā¤Ļā¤ŋ ⤅⤕āĨā¤ˇā¤Ž ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆ, ⤤āĨ‹ ⤏āĨā¤Žā¤žā¤°āĨā¤Ÿ ⤖āĨ‹ā¤œ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤛ā¤ĩā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ą ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤žāĨ¤", - "machine_learning_url_description": "ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ⤏⤰āĨā¤ĩ⤰ ā¤•ā¤ž ⤝āĨ‚ā¤†ā¤°ā¤ā¤˛", + "machine_learning_url_description": "ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ⤏⤰āĨā¤ĩ⤰ ā¤•ā¤ž URLāĨ¤ ⤝ā¤Ļā¤ŋ ā¤ā¤• ⤏āĨ‡ ⤅⤧ā¤ŋ⤕ URL ā¤Ļā¤ŋā¤ ā¤—ā¤ ā¤šāĨˆā¤‚, ⤤āĨ‹ ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ⤏⤰āĨā¤ĩ⤰ ⤕āĨ‹ ā¤ā¤•-ā¤ā¤• ⤕⤰⤕āĨ‡ ⤕āĨ‹ā¤ļā¤ŋā¤ļ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤ž, ā¤Ēā¤šā¤˛āĨ‡ ⤏āĨ‡ ⤆⤖ā¤ŋ⤰āĨ€ ⤤⤕, ⤜ā¤Ŧ ⤤⤕ ⤕āĨ‹ā¤ˆ ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤍ ā¤ĻāĨ‡āĨ¤ ⤜āĨ‹ ⤏⤰āĨā¤ĩ⤰ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤ĻāĨ‡ā¤¤āĨ‡, ⤉⤍āĨā¤šāĨ‡ā¤‚ ⤅⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤¨ā¤œā¤°ā¤…ā¤‚ā¤Ļā¤žā¤œ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤ž ⤜ā¤Ŧ ⤤⤕ ā¤ĩāĨ‡ ā¤Ģā¤ŋ⤰ ⤏āĨ‡ ā¤‘ā¤¨ā¤˛ā¤žā¤‡ā¤¨ ⤍ ā¤šāĨ‹ā¤‚āĨ¤", "manage_concurrency": "ā¤¸ā¤Žā¤ĩ⤰āĨā¤¤āĨ€ā¤¤ā¤ž ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "manage_log_settings": "⤞āĨ‰ā¤— ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "map_dark_style": "ā¤Ąā¤žā¤°āĨā¤• ā¤ļāĨˆā¤˛āĨ€", "map_enable_description": "ā¤Žā¤žā¤¨ā¤šā¤ŋ⤤āĨā¤° ⤏āĨā¤ĩā¤ŋā¤§ā¤žā¤ā¤ ⤏⤕āĨā¤ˇā¤Ž ⤕⤰āĨ‡ā¤‚", "map_gps_settings": "ā¤Žā¤žā¤¨ā¤šā¤ŋ⤤āĨā¤° ā¤ā¤ĩ⤂ ⤜āĨ€ā¤ĒāĨ€ā¤ā¤¸ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", "map_gps_settings_description": "ā¤Žā¤žā¤¨ā¤šā¤ŋ⤤āĨā¤° ⤔⤰ ⤜āĨ€ā¤ĒāĨ€ā¤ā¤¸ (⤰ā¤ŋā¤ĩ⤰āĨā¤¸ ⤜ā¤ŋ⤝āĨ‹ā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗) ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "map_implications": "ā¤Žā¤žā¤¨ā¤šā¤ŋ⤤āĨā¤° ⤏āĨā¤ĩā¤ŋā¤§ā¤ž ā¤ā¤• ā¤Ŧā¤žā¤šā¤°āĨ€ ā¤Ÿā¤žā¤‡ā¤˛ ⤏āĨ‡ā¤ĩā¤ž (tiles.immich.cloud) ā¤Ē⤰ ⤍ā¤ŋ⤰āĨā¤­ā¤° ⤕⤰⤤āĨ€ ā¤šāĨˆ", "map_light_style": "ā¤šā¤˛āĨā¤•āĨ€ ā¤ļāĨˆā¤˛āĨ€", "map_manage_reverse_geocoding_settings": "ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ā¤•ā¤°ā¤¨ā¤ž ⤰ā¤ŋā¤ĩ⤰āĨā¤¸ ⤜ā¤ŋ⤝āĨ‹ā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ā¤¸ā¤Žā¤žā¤¯āĨ‹ā¤œā¤¨", "map_reverse_geocoding": "⤰ā¤ŋā¤ĩ⤰āĨā¤¸ ⤜ā¤ŋ⤝āĨ‹ā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗", @@ -143,8 +154,14 @@ "map_settings": "ā¤Žā¤žā¤¨ā¤šā¤ŋ⤤āĨā¤° ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗", "map_settings_description": "ā¤Žā¤žā¤¨ā¤šā¤ŋ⤤āĨā¤° ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "map_style_description": "style.json ā¤ŽāĨˆā¤Ē ā¤ĨāĨ€ā¤Ž ā¤•ā¤ž URL", + "memory_cleanup_job": "⤏āĨā¤ŽāĨƒā¤¤ā¤ŋ ⤏ā¤Ģā¤ŧā¤žā¤ˆ", + "memory_generate_job": "⤏āĨā¤ŽāĨƒā¤¤ā¤ŋ ā¤ĒāĨ€ā¤ĸā¤ŧāĨ€", "metadata_extraction_job": "ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ⤍ā¤ŋā¤•ā¤žā¤˛āĨ‡ā¤‚", "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": "⤕āĨ‹ā¤ˆ ā¤Ēā¤Ĩ ā¤¨ā¤šāĨ€ā¤‚ ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", @@ -172,6 +189,7 @@ "oauth_auto_register": "ā¤‘ā¤ŸāĨ‹ ⤰⤜ā¤ŋ⤏āĨā¤Ÿā¤°", "oauth_auto_register_description": "OAuth ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤¸ā¤žā¤‡ā¤¨ ⤇⤍ ⤕⤰⤍āĨ‡ ⤕āĨ‡ ā¤Ŧā¤žā¤Ļ ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤¨ā¤ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‹ ā¤Ēā¤‚ā¤œāĨ€ā¤•āĨƒā¤¤ ⤕⤰āĨ‡ā¤‚", "oauth_button_text": "⤟āĨ‡ā¤•āĨā¤¸āĨā¤Ÿ ā¤Ŧ⤟⤍", + "oauth_client_secret_description": "⤝ā¤Ļā¤ŋ PKCE (⤕āĨ‹ā¤Ą ā¤ā¤•āĨā¤¸ā¤šāĨ‡ā¤‚ā¤œ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ĒāĨā¤°āĨ‚ā¤Ģā¤ŧ ⤕āĨā¤‚ā¤œāĨ€) OAuth ā¤ĒāĨā¤°ā¤Ļā¤žā¤¤ā¤ž ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ā¤¸ā¤Žā¤°āĨā¤Ĩā¤ŋ⤤ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆ ⤤āĨ‹ ā¤¯ā¤š ⤆ā¤ĩā¤ļāĨā¤¯ā¤• ā¤šāĨˆ", "oauth_enable_description": "OAuth ⤏āĨ‡ ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤕⤰āĨ‡ā¤‚", "oauth_mobile_redirect_uri": "ā¤ŽāĨ‹ā¤Ŧā¤žā¤‡ā¤˛ ⤰āĨ€ā¤Ąā¤žā¤¯ā¤°āĨ‡ā¤•āĨā¤Ÿ ⤝āĨ‚ā¤†ā¤°ā¤†ā¤ˆ", "oauth_mobile_redirect_uri_override": "ā¤ŽāĨ‹ā¤Ŧā¤žā¤‡ā¤˛ ⤰āĨ€ā¤Ąā¤žā¤¯ā¤°āĨ‡ā¤•āĨā¤Ÿ ⤝āĨ‚ā¤†ā¤°ā¤†ā¤ˆ ⤓ā¤ĩā¤°ā¤°ā¤žā¤‡ā¤Ą", @@ -185,23 +203,27 @@ "oauth_storage_quota_claim_description": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤕āĨ‡ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤕āĨ‹ā¤Ÿā¤ž ⤕āĨ‹ ⤇⤏ ā¤Ļā¤žā¤ĩāĨ‡ ⤕āĨ‡ ā¤ŽāĨ‚⤞āĨā¤¯ ā¤Ē⤰ ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ⤏āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚āĨ¤", "oauth_storage_quota_default": "ā¤Ąā¤ŋā¤Ģā¤ŧāĨ‰ā¤˛āĨā¤Ÿ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤕āĨ‹ā¤Ÿā¤ž (GiB)", "oauth_storage_quota_default_description": "GiB ā¤ŽāĨ‡ā¤‚ ⤕āĨ‹ā¤Ÿā¤ž ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤤ā¤Ŧ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤ž ⤜ā¤Ŧ ⤕āĨ‹ā¤ˆ ā¤Ļā¤žā¤ĩā¤ž ā¤ĒāĨā¤°ā¤Ļā¤žā¤¨ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨ‹ (⤅⤏āĨ€ā¤Žā¤ŋ⤤ ⤕āĨ‹ā¤Ÿā¤ž ⤕āĨ‡ ⤞ā¤ŋā¤ 0 ā¤Ļ⤰āĨā¤œ ⤕⤰āĨ‡ā¤‚)āĨ¤", - "offline_paths": "⤑ā¤Ģā¤ŧā¤˛ā¤žā¤‡ā¤¨ ā¤Ēā¤Ĩ", - "offline_paths_description": "⤝āĨ‡ ā¤Ē⤰ā¤ŋā¤Ŗā¤žā¤Ž ⤉⤍ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‹ ā¤ŽāĨˆā¤¨āĨā¤¯āĨā¤…⤞ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ⤕āĨ‡ ā¤•ā¤žā¤°ā¤Ŗ ā¤šāĨ‹ ⤏⤕⤤āĨ‡ ā¤šāĨˆā¤‚ ⤜āĨ‹ ā¤Ŧā¤žā¤šā¤°āĨ€ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤•ā¤ž ā¤šā¤ŋ⤏āĨā¤¸ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆā¤‚āĨ¤", + "oauth_timeout": "ā¤ŦāĨā¤°āĨ‡ā¤• ā¤•ā¤ž ⤅⤍āĨā¤°āĨ‹ā¤§", + "oauth_timeout_description": "⤅⤍āĨā¤°āĨ‹ā¤§āĨ‹ā¤‚ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤¸ā¤Žā¤¯-⤏āĨ€ā¤Žā¤ž ā¤Žā¤ŋ⤞āĨ€ā¤¸āĨ‡ā¤•ā¤‚ā¤Ą ā¤ŽāĨ‡ā¤‚", "password_enable_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤔⤰ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤏āĨ‡ ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤕⤰āĨ‡ā¤‚", "password_settings": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤞āĨ‰ā¤— ⤇⤍", "password_settings_description": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "paths_validated_successfully": "⤏⤭āĨ€ ā¤Ēā¤Ĩ ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤Žā¤žā¤¨āĨā¤¯ ⤕ā¤ŋā¤ ā¤—ā¤", + "person_cleanup_job": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤏ā¤Ģā¤ŧā¤žā¤ˆ", "quota_size_gib": "⤕āĨ‹ā¤Ÿā¤ž ā¤†ā¤•ā¤žā¤° (GiB)", "refreshing_all_libraries": "⤏⤭āĨ€ ā¤ĒāĨā¤¸āĨā¤¤ā¤•ā¤žā¤˛ā¤¯āĨ‹ā¤‚ ⤕āĨ‹ ā¤¤ā¤žā¤œā¤ŧā¤ž ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤°ā¤šā¤ž ā¤šāĨˆ", "registration": "ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤕ ā¤Ēā¤‚ā¤œāĨ€ā¤•⤰⤪", "registration_description": "⤚āĨ‚⤂⤕ā¤ŋ ⤆ā¤Ē ⤏ā¤ŋ⤏āĨā¤Ÿā¤Ž ā¤Ē⤰ ā¤Ēā¤šā¤˛āĨ‡ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤šāĨˆā¤‚, ⤇⤏⤞ā¤ŋā¤ ⤆ā¤Ē⤕āĨ‹ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤕ ⤕āĨ‡ ⤰āĨ‚ā¤Ē ā¤ŽāĨ‡ā¤‚ ⤍ā¤ŋ⤝āĨā¤•āĨā¤¤ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤ž ⤔⤰ ⤆ā¤Ē ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤¨ā¤ŋ⤕ ā¤•ā¤žā¤°āĨā¤¯āĨ‹ā¤‚ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤜ā¤ŋā¤ŽāĨā¤ŽāĨ‡ā¤Ļā¤žā¤° ā¤šāĨ‹ā¤‚⤗āĨ‡, ⤔⤰ ⤅⤤ā¤ŋ⤰ā¤ŋ⤕āĨā¤¤ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤆ā¤Ē⤕āĨ‡ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ā¤Ŧā¤¨ā¤žā¤ ā¤œā¤žā¤ā¤‚ā¤—āĨ‡āĨ¤", - "repair_all": "⤏⤭āĨ€ ⤕āĨ€ ā¤Žā¤°ā¤ŽāĨā¤Žā¤¤", "require_password_change_on_login": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤕āĨ‹ ā¤Ēā¤šā¤˛āĨ‡ ⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤Ē⤰ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞⤍āĨ‡ ⤕āĨ€ ⤆ā¤ĩā¤ļāĨā¤¯ā¤•ā¤¤ā¤ž ā¤šāĨˆ", "reset_settings_to_default": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ⤕āĨ‹ ā¤Ąā¤ŋā¤Ģā¤ŧāĨ‰ā¤˛āĨā¤Ÿ ā¤Ē⤰ ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚", "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_settings": "⤏⤰āĨā¤ĩ⤰ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", "server_settings_description": "⤏⤰āĨā¤ĩ⤰ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "server_welcome_message": "⤏āĨā¤ĩā¤žā¤—ā¤¤ ⤏⤂ā¤ĻāĨ‡ā¤ļ", @@ -211,21 +233,34 @@ "slideshow_duration_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_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_user_label": "{label} ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤•ā¤ž ⤏āĨā¤ŸāĨ‹ā¤°āĨ‡ā¤œ ⤞āĨ‡ā¤Ŧ⤞ ā¤šāĨˆ", "system_settings": "ā¤ĒāĨā¤°ā¤Ŗā¤žā¤˛āĨ€ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤ž", + "tag_cleanup_job": "⤟āĨˆā¤— ⤏ā¤Ģā¤ŧā¤žā¤ˆ", + "template_email_available_tags": "⤆ā¤Ē ⤅ā¤Ē⤍āĨ‡ ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ ā¤ŽāĨ‡ā¤‚ ⤍ā¤ŋā¤ŽāĨā¤¨ā¤˛ā¤ŋ⤖ā¤ŋ⤤ ā¤ĩāĨ‡ā¤°ā¤ŋā¤ā¤Ŧ⤞ ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰ ⤏⤕⤤āĨ‡ ā¤šāĨˆā¤‚: {tags}", + "template_email_if_empty": "⤝ā¤Ļā¤ŋ ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ ⤰ā¤ŋ⤕āĨā¤¤ ā¤šāĨˆ, ⤤āĨ‹ ā¤Ąā¤ŋā¤Ģā¤ŧāĨ‰ā¤˛āĨā¤Ÿ ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤žāĨ¤", + "template_email_invite_album": "ā¤†ā¤Žā¤‚ā¤¤āĨā¤°ā¤Ŗ ā¤ā¤˛āĨā¤Ŧā¤Ž ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ", + "template_email_preview": "ā¤ĒāĨ‚⤰āĨā¤ĩ ā¤Ļ⤰āĨā¤ļ⤍", + "template_email_settings": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ", + "template_email_update_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚", + "template_email_welcome": "⤏āĨā¤ĩā¤žā¤—ā¤¤ ā¤ˆā¤ŽāĨ‡ā¤˛ ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ", + "template_settings": "⤅⤧ā¤ŋ⤏āĨ‚ā¤šā¤¨ā¤ž ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤ŸāĨā¤¸", + "template_settings_description": "⤏āĨ‚ā¤šā¤¨ā¤žā¤“ā¤‚ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤕⤏āĨā¤Ÿā¤Ž ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "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": "⤤āĨā¤ĩ⤰⤪ ā¤ā¤ĒāĨ€ā¤†ā¤ˆ", @@ -250,18 +285,21 @@ "transcoding_constant_rate_factor": "⤏āĨā¤Ĩā¤ŋ⤰ ā¤Ļ⤰ ā¤•ā¤žā¤°ā¤• (-⤏āĨ€ā¤†ā¤°ā¤ā¤Ģ)", "transcoding_constant_rate_factor_description": "ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ⤏āĨā¤¤ā¤°āĨ¤", "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_max_b_frames": "⤅⤧ā¤ŋā¤•ā¤¤ā¤Ž ā¤ŦāĨ€-ā¤ĢāĨā¤°āĨ‡ā¤Ž", "transcoding_max_b_frames_description": "ā¤‰ā¤šāĨā¤š ā¤Žā¤žā¤¨ ⤏⤂ā¤ĒāĨ€ā¤Ąā¤ŧ⤍ ā¤Ļ⤕āĨā¤ˇā¤¤ā¤ž ā¤ŽāĨ‡ā¤‚ ⤏āĨā¤§ā¤žā¤° ⤕⤰⤤āĨ‡ ā¤šāĨˆā¤‚, ⤞āĨ‡ā¤•ā¤ŋ⤍ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ⤕āĨ‹ ⤧āĨ€ā¤Žā¤ž ⤕⤰ ā¤ĻāĨ‡ā¤¤āĨ‡ ā¤šāĨˆā¤‚āĨ¤", "transcoding_max_bitrate": "⤅⤧ā¤ŋā¤•ā¤¤ā¤Ž ā¤Ŧā¤ŋ⤟⤰āĨ‡ā¤Ÿ", - "transcoding_max_bitrate_description": "⤅⤧ā¤ŋā¤•ā¤¤ā¤Ž ā¤Ŧā¤ŋ⤟⤰āĨ‡ā¤Ÿ ⤏āĨ‡ā¤Ÿ ⤕⤰⤍āĨ‡ ⤏āĨ‡ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ⤕āĨ€ ā¤Žā¤žā¤ŽāĨ‚⤞āĨ€ ā¤˛ā¤žā¤—ā¤¤ ā¤Ē⤰ ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤†ā¤•ā¤žā¤° ⤕āĨ‹ ⤅⤧ā¤ŋ⤕ ā¤ĒāĨ‚⤰āĨā¤ĩā¤žā¤¨āĨā¤Žā¤žā¤¨ā¤ŋ⤤ ā¤Ŧā¤¨ā¤žā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆāĨ¤", + "transcoding_max_bitrate_description": "⤅⤧ā¤ŋā¤•ā¤¤ā¤Ž ā¤Ŧā¤ŋ⤟⤰āĨ‡ā¤Ÿ ⤏āĨ‡ā¤Ÿ ⤕⤰⤍āĨ‡ ⤏āĨ‡ ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤†ā¤•ā¤žā¤° ⤕āĨ‹ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ā¤Ē⤰ ā¤Žā¤žā¤ŽāĨ‚⤞āĨ€ ā¤˛ā¤žā¤—ā¤¤ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ⤅⤧ā¤ŋ⤕ ā¤ĒāĨ‚⤰āĨā¤ĩā¤žā¤¨āĨā¤Žā¤žā¤¨ā¤ŋ⤤ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆāĨ¤ 720p ā¤Ē⤰, ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯ ā¤Žā¤žā¤¨ VP9 ā¤¯ā¤ž HEVC ⤕āĨ‡ ⤞ā¤ŋā¤ 2600k kbit/s ā¤¯ā¤ž H.264 ⤕āĨ‡ ⤞ā¤ŋā¤ 4500k kbit/s ā¤šāĨˆā¤‚āĨ¤ 0 ā¤Ē⤰ ⤏āĨ‡ā¤Ÿ ā¤šāĨ‹ā¤¨āĨ‡ ā¤Ē⤰ ⤅⤕āĨā¤ˇā¤ŽāĨ¤", "transcoding_max_keyframe_interval": "⤅⤧ā¤ŋā¤•ā¤¤ā¤Ž ā¤ŽāĨā¤–āĨā¤¯ā¤Ģā¤ŧāĨā¤°āĨ‡ā¤Ž ā¤…ā¤‚ā¤¤ā¤°ā¤žā¤˛", "transcoding_max_keyframe_interval_description": "ā¤ŽāĨā¤–āĨā¤¯ā¤Ģā¤ŧāĨā¤°āĨ‡ā¤Ž ⤕āĨ‡ ā¤ŦāĨ€ā¤š ⤅⤧ā¤ŋā¤•ā¤¤ā¤Ž ā¤Ģā¤ŧāĨā¤°āĨ‡ā¤Ž ā¤ĻāĨ‚⤰āĨ€ ⤍ā¤ŋ⤰āĨā¤§ā¤žā¤°ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ā¤šāĨˆāĨ¤", "transcoding_optimal_description": "⤞⤕āĨā¤ˇāĨā¤¯ ⤰ā¤ŋ⤜ā¤ŧāĨ‰ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ⤏āĨ‡ ⤅⤧ā¤ŋ⤕ ā¤Šā¤‚ā¤šāĨ‡ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ā¤¯ā¤ž ⤏āĨā¤ĩāĨ€ā¤•āĨƒā¤¤ ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ē ā¤ŽāĨ‡ā¤‚ ā¤¨ā¤šāĨ€ā¤‚", + "transcoding_policy": "⤟āĨā¤°ā¤žā¤‚⤏⤕āĨ‹ā¤Ą ⤍āĨ€ā¤¤ā¤ŋ", + "transcoding_policy_description": "⤏āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚ ⤕ā¤ŋ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ⤕ā¤Ŧ ⤟āĨā¤°ā¤žā¤‚⤏⤕āĨ‹ā¤Ą ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤ž", "transcoding_preferred_hardware_device": "ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ā¤šā¤žā¤°āĨā¤Ąā¤ĩāĨ‡ā¤¯ā¤° ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸", "transcoding_preferred_hardware_device_description": "⤕āĨ‡ā¤ĩ⤞ VAAPI ⤔⤰ QSV ā¤Ē⤰ ā¤˛ā¤žā¤—āĨ‚ ā¤šāĨ‹ā¤¤ā¤ž ā¤šāĨˆāĨ¤", "transcoding_preset_preset": "ā¤ĒāĨā¤°āĨ€ā¤¸āĨ‡ā¤Ÿ (-preset)", @@ -270,7 +308,7 @@ "transcoding_reference_frames_description": "⤕ā¤ŋ⤏āĨ€ ā¤Ļā¤ŋā¤ ā¤—ā¤ ā¤Ģā¤ŧāĨā¤°āĨ‡ā¤Ž ⤕āĨ‹ ⤏⤂ā¤ĒāĨ€ā¤Ąā¤ŧā¤ŋ⤤ ⤕⤰⤤āĨ‡ ā¤¸ā¤Žā¤¯ ⤏⤂ā¤Ļ⤰āĨā¤­ā¤ŋ⤤ ⤕ā¤ŋā¤ ā¤œā¤žā¤¨āĨ‡ ā¤ĩā¤žā¤˛āĨ‡ ā¤Ģā¤ŧāĨā¤°āĨ‡ā¤ŽāĨ‹ā¤‚ ⤕āĨ€ ⤏⤂⤖āĨā¤¯ā¤žāĨ¤", "transcoding_required_description": "⤕āĨ‡ā¤ĩ⤞ ā¤ĩāĨ‡ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ⤜āĨ‹ ⤏āĨā¤ĩāĨ€ā¤•āĨƒā¤¤ ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ē ā¤ŽāĨ‡ā¤‚ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆā¤‚", "transcoding_settings": "ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ⤟āĨā¤°ā¤žā¤‚⤏⤕āĨ‹ā¤Ąā¤ŋ⤂⤗ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", - "transcoding_settings_description": "ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‡ ⤰ā¤ŋ⤜ā¤ŧāĨ‰ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ⤔⤰ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤕āĨ‹ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "transcoding_settings_description": "ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‡ ⤰ā¤ŋ⤜ā¤ŧāĨ‰ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ⤔⤰ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤕āĨ‹ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚, ⤕ā¤ŋ⤍ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ⤕āĨ‹ ⤟āĨā¤°ā¤žā¤‚⤏⤕āĨ‹ā¤Ą ā¤•ā¤°ā¤¨ā¤ž ā¤šāĨˆ ⤔⤰ ⤉⤍āĨā¤šāĨ‡ā¤‚ ⤕āĨˆā¤¸āĨ‡ ā¤ĒāĨā¤°āĨ‹ā¤¸āĨ‡ā¤¸ ā¤•ā¤°ā¤¨ā¤ž ā¤šāĨˆ", "transcoding_target_resolution": "⤞⤕āĨā¤ˇāĨā¤¯ ⤏⤂⤕⤞āĨā¤Ē", "transcoding_target_resolution_description": "ā¤‰ā¤šāĨā¤š ⤰ā¤ŋ⤜ā¤ŧāĨ‰ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ⤅⤧ā¤ŋ⤕ ā¤ĩā¤ŋā¤ĩ⤰⤪ ⤏⤂⤰⤕āĨā¤ˇā¤ŋ⤤ ⤕⤰ ⤏⤕⤤āĨ‡ ā¤šāĨˆā¤‚ ⤞āĨ‡ā¤•ā¤ŋ⤍ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤅⤧ā¤ŋ⤕ ā¤¸ā¤Žā¤¯ ⤞āĨ‡ā¤¤āĨ‡ ā¤šāĨˆā¤‚, ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤†ā¤•ā¤žā¤° ā¤Ŧā¤Ąā¤ŧāĨ‡ ā¤šāĨ‹ā¤¤āĨ‡ ā¤šāĨˆā¤‚, ⤔⤰ ⤐ā¤Ē ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤕āĨā¤°ā¤ŋā¤¯ā¤žā¤ļāĨ€ā¤˛ā¤¤ā¤ž ⤕āĨ‹ ā¤•ā¤Ž ⤕⤰ ⤏⤕⤤āĨ‡ ā¤šāĨˆā¤‚āĨ¤", "transcoding_temporal_aq": "⤅⤏āĨā¤Ĩā¤žā¤¯āĨ€ AQ", @@ -290,17 +328,23 @@ "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 {# day} other {# days}} ā¤ŽāĨ‡ā¤‚ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ļāĨ‡ā¤ĄāĨā¤¯āĨ‚⤞ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤žāĨ¤", "user_delete_delay_settings": "ā¤šā¤Ÿā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĻāĨ‡ā¤°āĨ€", "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_restore_scheduled_removal": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤕āĨ‹ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚ - {date, date, long} ā¤Ē⤰ ā¤šā¤Ÿā¤žā¤¯ā¤ž ā¤œā¤žā¤¨ā¤ž ⤍ā¤ŋ⤰āĨā¤§ā¤žā¤°ā¤ŋ⤤ ā¤šāĨˆ", "user_settings": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗", "user_settings_description": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "user_successfully_removed": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž {email} ⤕āĨ‹ ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆāĨ¤", "version_check_enabled_description": "⤍⤈ ⤰ā¤ŋ⤞āĨ€ā¤œā¤ŧ ⤕āĨ€ ā¤œā¤žā¤ā¤š ⤕āĨ‡ ⤞ā¤ŋā¤ GitHub ā¤Ē⤰ ⤆ā¤ĩ⤧ā¤ŋ⤕ ⤅⤍āĨā¤°āĨ‹ā¤§ ⤏⤕āĨā¤ˇā¤Ž ⤕⤰āĨ‡ā¤‚", + "version_check_implications": "⤏⤂⤏āĨā¤•⤰⤪ ā¤œā¤žā¤ā¤š ⤏āĨā¤ĩā¤ŋā¤§ā¤ž github.com ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ⤆ā¤ĩ⤧ā¤ŋ⤕ ā¤¸ā¤‚ā¤šā¤žā¤° ā¤Ē⤰ ⤍ā¤ŋ⤰āĨā¤­ā¤° ⤕⤰⤤āĨ€ ā¤šāĨˆ", "version_check_settings": "⤏⤂⤏āĨā¤•⤰⤪ ⤚āĨ‡ā¤•", "version_check_settings_description": "ā¤¨ā¤ ⤏⤂⤏āĨā¤•⤰⤪ ⤅⤧ā¤ŋ⤏āĨ‚ā¤šā¤¨ā¤ž ⤕āĨ‹ ⤏⤕āĨā¤ˇā¤Ž/⤅⤕āĨā¤ˇā¤Ž ⤕⤰āĨ‡ā¤‚", "video_conversion_job": "⤟āĨā¤°ā¤žā¤‚⤏⤕āĨ‹ā¤Ą ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹", @@ -310,20 +354,53 @@ "admin_password": "ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤕ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", "administration": "ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤¨", "advanced": "ā¤ĩā¤ŋ⤕⤏ā¤ŋ⤤", + "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": "⤏āĨā¤ĩ-ā¤šā¤¸āĨā¤¤ā¤žā¤•āĨā¤ˇā¤°ā¤ŋ⤤ 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 {# month} other {# months}}", + "age_year_months": "⤆⤝āĨ 1 ā¤ĩ⤰āĨā¤ˇ, {months, plural, one {# month} other {# months}}", + "age_years": "{years, plural, other {Age #}}", "album_added": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", "album_added_notification_setting_description": "⤜ā¤Ŧ ⤆ā¤Ē⤕āĨ‹ ⤕ā¤ŋ⤏āĨ€ ā¤¸ā¤žā¤ā¤ž ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤œā¤žā¤ ⤤āĨ‹ ā¤ā¤• ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤ž ā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ ⤕⤰āĨ‡ā¤‚", "album_cover_updated": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤕ā¤ĩ⤰ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", + "album_delete_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ā¤ā¤˛āĨā¤Ŧā¤Ž {album} ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", + "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} ⤛āĨ‹ā¤Ąā¤ŧā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", "album_name": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤•ā¤ž ā¤¨ā¤žā¤Ž", "album_options": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", "album_remove_user": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤šā¤Ÿā¤žā¤ā¤‚?", + "album_remove_user_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ {user} ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", "album_share_no_users": "ā¤ā¤¸ā¤ž ā¤˛ā¤—ā¤¤ā¤ž ā¤šāĨˆ ⤕ā¤ŋ ⤆ā¤Ē⤍āĨ‡ ā¤¯ā¤š ā¤ā¤˛āĨā¤Ŧā¤Ž ⤏⤭āĨ€ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤¸ā¤žā¤ā¤ž ⤕⤰ ā¤Ļā¤ŋā¤¯ā¤ž ā¤šāĨˆ ā¤¯ā¤ž ⤆ā¤Ē⤕āĨ‡ ā¤Ēā¤žā¤¸ ā¤¸ā¤žā¤ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤕āĨ‹ā¤ˆ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆāĨ¤", "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_with_link_access": "⤞ā¤ŋ⤂⤕ ā¤ĩā¤žā¤˛āĨ‡ ⤕ā¤ŋ⤏āĨ€ ⤭āĨ€ ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤇⤏ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤔⤰ ⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ā¤ĻāĨ‡ā¤–⤍āĨ‡ ā¤ĻāĨ‡ā¤‚āĨ¤", "albums": "ā¤ā¤˛ā¤Ŧā¤Ž", + "albums_count": "{⤗ā¤ŋ⤍⤤āĨ€, ā¤Ŧā¤šāĨā¤ĩ⤚⤍, ā¤ā¤• {{count, number} ā¤ā¤˛āĨā¤Ŧā¤Ž} ⤅⤍āĨā¤¯ {{count, number} ā¤ā¤˛āĨā¤Ŧā¤Ž}}", "all": "⤏⤭āĨ€", "all_albums": "⤏⤭āĨ€ ā¤ā¤˛ā¤Ŧā¤Ž", "all_people": "⤏⤭āĨ€ ⤞āĨ‹ā¤—", @@ -332,6 +409,8 @@ "allow_edits": "⤏⤂ā¤Ēā¤žā¤Ļ⤍ ⤕āĨ€ ⤅⤍āĨā¤Žā¤¤ā¤ŋ ā¤ĻāĨ‡ā¤‚", "allow_public_user_to_download": "ā¤¸ā¤žā¤°āĨā¤ĩ⤜⤍ā¤ŋ⤕ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤕āĨ‹ ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ⤕āĨ€ ⤅⤍āĨā¤Žā¤¤ā¤ŋ ā¤ĻāĨ‡ā¤‚", "allow_public_user_to_upload": "ā¤¸ā¤žā¤°āĨā¤ĩ⤜⤍ā¤ŋ⤕ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤕āĨ‹ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ⤕āĨ€ ⤅⤍āĨā¤Žā¤¤ā¤ŋ ā¤ĻāĨ‡ā¤‚", + "alt_text_qr_code": "⤕āĨā¤¯āĨ‚⤆⤰ ⤕āĨ‹ā¤Ą ⤛ā¤ĩā¤ŋ", + "anti_clockwise": "ā¤ĩā¤žā¤Žā¤žā¤ĩ⤰āĨā¤¤", "api_key": "ā¤ā¤ĒāĨ€ā¤†ā¤ˆ ⤕āĨ€", "api_key_description": "ā¤¯ā¤š ⤕āĨ€ ⤕āĨ‡ā¤ĩ⤞ ā¤ā¤• ā¤Ŧā¤žā¤° ā¤Ļā¤ŋā¤–ā¤žā¤ˆ ā¤œā¤žā¤ā¤—āĨ€āĨ¤ ā¤ĩā¤ŋā¤‚ā¤ĄāĨ‹ ā¤Ŧ⤂ā¤Ļ ⤕⤰⤍āĨ‡ ⤏āĨ‡ ā¤Ēā¤šā¤˛āĨ‡ ⤕āĨƒā¤Ēā¤¯ā¤ž ⤇⤏āĨ‡ ⤕āĨ‰ā¤ĒāĨ€ ā¤•ā¤°ā¤¨ā¤ž ⤏āĨā¤¨ā¤ŋā¤ļāĨā¤šā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚āĨ¤āĨ¤", "api_key_empty": "⤆ā¤Ēā¤•ā¤ž ā¤ā¤ĒāĨ€ā¤†ā¤ˆ ⤕āĨā¤‚ā¤œāĨ€ ā¤¨ā¤žā¤Ž ā¤–ā¤žā¤˛āĨ€ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨ‹ā¤¨ā¤ž ā¤šā¤žā¤šā¤ŋā¤", @@ -343,16 +422,24 @@ "appears_in": "ā¤ĒāĨā¤°ā¤•ā¤Ÿ ā¤šāĨ‹ā¤¤ā¤ž ā¤šāĨˆ", "archive": "⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯", "archive_or_unarchive_photo": "ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤕āĨ‹ ⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ā¤¯ā¤ž ⤅⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ⤕⤰āĨ‡ā¤‚", + "archive_page_no_archived_assets": "⤕āĨ‹ā¤ˆ ⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤¨ā¤šāĨ€ā¤‚ ā¤Žā¤ŋ⤞āĨ€", + "archive_page_title": "ā¤ĒāĨā¤°ā¤žā¤˛āĨ‡ā¤– ({count})", "archive_size": "ā¤ĒāĨā¤°ā¤žā¤˛āĨ‡ā¤– ā¤†ā¤•ā¤žā¤°", "archive_size_description": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕āĨ‡ ⤞ā¤ŋā¤ ⤏⤂⤗āĨā¤°ā¤š ā¤†ā¤•ā¤žā¤° ⤕āĨ‰ā¤¨āĨā¤Ģā¤ŧā¤ŋ⤗⤰ ⤕⤰āĨ‡ā¤‚ (GiB ā¤ŽāĨ‡ā¤‚)", "archived": "⤏⤂⤗āĨā¤°ā¤šā¤ŋ⤤", + "archived_count": "{ā¤—ā¤Ŗā¤¨ā¤ž, ā¤Ŧā¤šāĨā¤ĩ⤚⤍, ⤅⤍āĨā¤¯ {⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ #}}", "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_hashing": "ā¤šāĨˆā¤ļā¤ŋ⤂⤗...āĨ¤", + "asset_list_group_by_sub_title": "ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ā¤¸ā¤ŽāĨ‚ā¤š ā¤Ŧā¤¨ā¤žā¤ā¤‚", + "asset_list_layout_settings_dynamic_layout_title": "⤗⤤ā¤ŋā¤ļāĨ€ā¤˛ ⤞āĨ‡ā¤†ā¤‰ā¤Ÿ", "asset_offline": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤑ā¤Ģā¤ŧā¤˛ā¤žā¤‡ā¤¨", "asset_offline_description": "ā¤¯ā¤š ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤑ā¤Ģā¤ŧā¤˛ā¤žā¤‡ā¤¨ ā¤šāĨˆāĨ¤", "asset_restored_successfully": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕āĨ€ ā¤—ā¤ˆā¤‚", @@ -397,7 +484,6 @@ "change_password_description": "ā¤¯ā¤š ā¤¯ā¤ž ⤤āĨ‹ ā¤Ēā¤šā¤˛āĨ€ ā¤Ŧā¤žā¤° ā¤šāĨˆ ⤜ā¤Ŧ ⤆ā¤Ē ⤏ā¤ŋ⤏āĨā¤Ÿā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤¸ā¤žā¤‡ā¤¨ ⤇⤍ ⤕⤰ ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚ ā¤¯ā¤ž ⤆ā¤Ēā¤•ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤•ā¤ž ⤅⤍āĨā¤°āĨ‹ā¤§ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆāĨ¤", "change_your_password": "⤅ā¤Ēā¤¨ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "changed_visibility_successfully": "ā¤ĻāĨƒā¤ļāĨā¤¯ā¤¤ā¤ž ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤Ē⤰ā¤ŋā¤ĩ⤰āĨā¤¤ā¤ŋ⤤", - "check_all": "⤏⤭āĨ€ ⤚āĨ‡ā¤• ⤕⤰āĨ‡ā¤‚", "check_logs": "⤞āĨ‰ā¤— ā¤œā¤žā¤‚ā¤šāĨ‡ā¤‚", "choose_matching_people_to_merge": "ā¤Žā¤°āĨā¤œ ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Žā¤ŋ⤞⤤āĨ‡-⤜āĨā¤˛ā¤¤āĨ‡ ⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ⤚āĨā¤¨āĨ‡ā¤‚", "city": "ā¤ļā¤šā¤°", @@ -490,7 +576,6 @@ "download_enqueue": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤•ā¤¤ā¤žā¤° ā¤ŽāĨ‡ā¤‚ ā¤šāĨˆ", "download_error": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤤āĨā¤°āĨā¤Ÿā¤ŋ", "download_failed": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤ĩā¤ŋā¤Ģ⤞", - "download_filename": "ā¤Ģā¤ŧā¤žā¤‡ā¤˛: {filename}", "download_finished": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤¸ā¤Žā¤žā¤ĒāĨā¤¤", "download_notfound": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤¨ā¤šāĨ€ā¤‚ ā¤Žā¤ŋā¤˛ā¤ž", "download_paused": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤏āĨā¤Ĩ⤗ā¤ŋ⤤", @@ -577,7 +662,6 @@ "unable_to_change_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_complete_oauth_login": "OAuth ⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤ĒāĨ‚⤰āĨā¤Ŗ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_connect": "⤕⤍āĨ‡ā¤•āĨā¤Ÿ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", - "unable_to_connect_to_server": "⤏⤰āĨā¤ĩ⤰ ⤏āĨ‡ ⤕⤍āĨ‡ā¤•āĨā¤Ÿ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ ā¤šāĨˆ", "unable_to_copy_to_clipboard": "⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ą ā¤Ē⤰ ⤕āĨ‰ā¤ĒāĨ€ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž, ⤏āĨā¤¨ā¤ŋā¤ļāĨā¤šā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚ ⤕ā¤ŋ ⤆ā¤Ē https ⤕āĨ‡ ā¤Žā¤žā¤§āĨā¤¯ā¤Ž ⤏āĨ‡ ā¤ĒāĨ‡ā¤œ ⤤⤕ ā¤Ēā¤šāĨā¤‚ā¤š ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚", "unable_to_create_admin_account": "ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤕ ā¤–ā¤žā¤¤ā¤ž ā¤Ŧā¤¨ā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_create_api_key": "⤍⤈ API ⤕āĨā¤‚ā¤œāĨ€ ā¤Ŧā¤¨ā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", @@ -600,10 +684,6 @@ "unable_to_get_shared_link": "ā¤¸ā¤žā¤ā¤ž ⤞ā¤ŋ⤂⤕ ā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", "unable_to_hide_person": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤛āĨā¤Ēā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "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 ⤏āĨ‡ ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", @@ -613,11 +693,9 @@ "unable_to_remove_album_users": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‹ ā¤ā¤˛āĨā¤Ŧā¤Ž ⤏āĨ‡ ⤍ā¤ŋā¤•ā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_remove_api_key": "API ⤕āĨā¤‚ā¤œāĨ€ ⤍ā¤ŋā¤•ā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "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_resolve_duplicate": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤•ā¤ž ā¤¸ā¤Žā¤žā¤§ā¤žā¤¨ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_restore_assets": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", @@ -820,14 +898,11 @@ "notifications": "⤏āĨ‚ā¤šā¤¨ā¤žā¤ā¤‚", "notifications_setting_description": "⤏āĨ‚ā¤šā¤¨ā¤žā¤ā¤‚ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "offline": "⤑ā¤Ģā¤˛ā¤žā¤‡ā¤¨", - "offline_paths": "⤑ā¤Ģā¤ŧā¤˛ā¤žā¤‡ā¤¨ ā¤Ēā¤Ĩ", - "offline_paths_description": "⤝āĨ‡ ā¤Ē⤰ā¤ŋā¤Ŗā¤žā¤Ž ⤉⤍ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‹ ā¤ŽāĨˆā¤¨āĨā¤¯āĨā¤…⤞ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ⤕āĨ‡ ā¤•ā¤žā¤°ā¤Ŗ ā¤šāĨ‹ ⤏⤕⤤āĨ‡ ā¤šāĨˆā¤‚ ⤜āĨ‹ ā¤Ŧā¤žā¤šā¤°āĨ€ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤•ā¤ž ā¤šā¤ŋ⤏āĨā¤¸ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆā¤‚āĨ¤", "ok": "⤠āĨ€ā¤• ā¤šāĨˆ", "oldest_first": "⤏ā¤Ŧ⤏āĨ‡ ā¤ĒāĨā¤°ā¤žā¤¨ā¤ž ā¤Ēā¤šā¤˛āĨ‡", "on_this_device": "⤇⤏ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ā¤Ē⤰", "onboarding": "⤜āĨā¤žā¤žā¤¨ā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ā¤ŋ", "onboarding_theme_description": "⤅ā¤Ē⤍āĨ‡ ⤉ā¤Ļā¤žā¤šā¤°ā¤Ŗ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ā¤• ⤰⤂⤗ ā¤ĨāĨ€ā¤Ž ⤚āĨā¤¨āĨ‡ā¤‚āĨ¤", - "onboarding_welcome_description": "ā¤†ā¤‡ā¤ ⤕āĨā¤› ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ⤅ā¤Ēā¤¨ā¤ž ⤇⤂⤏āĨā¤ŸāĨ‡ā¤‚⤏ ⤏āĨ‡ā¤Ÿ ⤅ā¤Ē ⤕⤰āĨ‡ā¤‚āĨ¤", "online": "ā¤‘ā¤¨ā¤˛ā¤žā¤‡ā¤¨", "only_favorites": "⤕āĨ‡ā¤ĩ⤞ ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž", "open_in_openstreetmap": "OpenStreetMap ā¤ŽāĨ‡ā¤‚ ⤖āĨ‹ā¤˛āĨ‡ā¤‚", @@ -1124,7 +1199,6 @@ "to_login": "⤞āĨ‰ā¤— ⤇⤍ ⤕⤰āĨ‡ā¤‚", "to_trash": "ā¤•ā¤šā¤°ā¤ž", "toggle_settings": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ⤟āĨ‰ā¤—⤞ ⤕⤰āĨ‡ā¤‚", - "toggle_theme": "ā¤ĨāĨ€ā¤Ž ⤟āĨ‰ā¤—⤞ ⤕⤰āĨ‡ā¤‚", "total_usage": "⤕āĨā¤˛ ⤉ā¤Ē⤝āĨ‹ā¤—", "trash": "ā¤•ā¤šā¤°ā¤ž", "trash_all": "⤏ā¤Ŧ ā¤•ā¤šā¤°ā¤ž", @@ -1149,8 +1223,6 @@ "unselect_all": "⤏⤭āĨ€ ⤕āĨ‹ ā¤…ā¤šā¤¯ā¤¨ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "unselect_all_duplicates": "⤏⤭āĨ€ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤕āĨ‹ ā¤…ā¤šā¤¯ā¤¨ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "unstack": "⤏āĨā¤ŸāĨˆā¤• ⤰ā¤ĻāĨā¤Ļ ⤕⤰āĨ‡ā¤‚", - "untracked_files": "⤟āĨā¤°āĨˆā¤• ⤍ ⤕āĨ€ ā¤—ā¤ˆ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‡ā¤‚", - "untracked_files_decription": "⤇⤍ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‹ ā¤ā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ļ⤍ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ⤟āĨā¤°āĨˆā¤• ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤¤ā¤ž ā¤šāĨˆ. ā¤ĩāĨ‡ ⤅⤏ā¤Ģ⤞ ā¤šā¤žā¤˛āĨ‹ā¤‚, ā¤Ŧā¤žā¤§ā¤ŋ⤤ ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤¯ā¤ž ⤕ā¤ŋ⤏āĨ€ ā¤Ŧ⤗ ⤕āĨ‡ ā¤•ā¤žā¤°ā¤Ŗ ā¤ĒāĨ€ā¤›āĨ‡ ⤛āĨ‚ā¤Ÿ ā¤œā¤žā¤¨āĨ‡ ā¤•ā¤ž ā¤Ē⤰ā¤ŋā¤Ŗā¤žā¤Ž ā¤šāĨ‹ ⤏⤕⤤āĨ‡ ā¤šāĨˆā¤‚", "up_next": "⤅ā¤Ŧ ā¤…ā¤—ā¤˛ā¤ž", "updated_password": "⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", "upload": "ā¤Ąā¤žā¤˛ā¤¨ā¤ž", diff --git a/i18n/hr.json b/i18n/hr.json index 5a2d259eb2..d2edfc9a8e 100644 --- a/i18n/hr.json +++ b/i18n/hr.json @@ -22,11 +22,11 @@ "add_partner": "Dodaj partnera", "add_path": "Dodaj putanju", "add_photos": "Dodaj slike", + "add_tag": "Dodaj oznaku", "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", @@ -44,9 +44,7 @@ "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": "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", + "backup_settings_description": "Upravljajte postavkama izvoza baze podataka.", "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}?", @@ -62,14 +60,12 @@ "disable_login": "Onemogući prijavu", "duplicate_detection_job_description": "Pokrenite strojno učenje na materijalima kako biste otkrili slične slike. Oslanja se na Pametno PretraÅživanje", "exclusion_pattern_description": "Uzorci izuzimanja omogućuju vam da zanemarite datoteke i mape prilikom skeniranja svoje biblioteke. Ovo je korisno ako imate mape koje sadrÅže datoteke koje ne Åželite uvesti, kao ÅĄto su RAW datoteke.", - "external_library_created_at": "Vanjska biblioteka (stvorena: {date})", "external_library_management": "Upravljanje vanjskom knjiÅžnicom", "face_detection": "Detekcija lica", "face_detection_description": "Prepoznajte lica u sredstvima pomoću strojnog učenja. Za videozapise u obzir se uzima samo minijaturni prikaz. \"Sve\" (ponovno) obrađuje svu imovinu. \"Nedostaje\" stavlja u red čekanja sredstva koja joÅĄ nisu obrađena. Otkrivena lica bit će stavljena u red čekanja za prepoznavanje lica nakon dovrÅĄetka prepoznavanja lica, grupirajući ih u postojeće ili nove osobe.", "facial_recognition_job_description": "Grupirajte otkrivena lica u osobe. Ovaj se korak pokreće nakon dovrÅĄetka prepoznavanja lica. \"Sve\" (ponovno) grupira sva lica. \"Nedostajuća\" lica u redovima kojima nije dodijeljena osoba.", "failed_job_command": "Naredba {command} nije uspjela za posao: {job}", "force_delete_user_warning": "UPOZORENJE: Ovo će odmah ukloniti korisnika i sve pripadajuće podatke. Ovo se ne moÅže poniÅĄtiti i datoteke se ne mogu vratiti.", - "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", @@ -174,7 +170,7 @@ "note_apply_storage_label_previous_assets": "Napomena: da biste primijenili Oznaku Pohrane na prethodno prenesena sredstva, pokrenite", "note_cannot_be_changed_later": "NAPOMENA: Ovo se ne moÅže promijeniti kasnije!", "notification_email_from_address": "Od adrese", - "notification_email_from_address_description": "E-mail adresa poÅĄiljatelja, na primjer: \"Immich Photo Server \"", + "notification_email_from_address_description": "E-mail adresa poÅĄiljatelja, na primjer: \"Immich Photo Server \". Obavezno koristite adresu s koje vam je dopuÅĄteno slanje e-poÅĄte.", "notification_email_host_description": "PosluÅžitelja e-poÅĄte (npr. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignoriraj pogreÅĄke certifikata", "notification_email_ignore_certificate_errors_description": "Ignoriraj pogreÅĄke provjere valjanosti TLS certifikata (nije preporučeno)", @@ -194,10 +190,11 @@ "oauth_auto_register": "Automatska registracija", "oauth_auto_register_description": "Automatski registrirajte nove korisnike nakon prijave s OAuth", "oauth_button_text": "Tekst gumba", + "oauth_client_secret_description": "Obavezno ukoliko PKCE (Proof Key for Code Exchange) nije podrÅžan od strane OAuth pruÅžatelja", "oauth_enable_description": "Prijavite se putem OAutha", "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_mobile_redirect_uri_override_description": "Omogući kada pruÅžatelj OAuth ne dopuÅĄta mobilni URI, poput ''{callback}''", "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.", @@ -209,8 +206,6 @@ "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", "password_settings": "Prijava zaporkom", "password_settings_description": "Upravljanje postavkama za prijavu zaporkom", @@ -220,9 +215,6 @@ "refreshing_all_libraries": "OsvjeÅžavanje svih biblioteka", "registration": "Registracija administratora", "registration_description": "Budući da ste prvi korisnik na sustavu, bit ćete dodijeljeni administratorsku ulogu i odgovorni ste za administrativne poslove, a dodatne korisnike kreirat ćete sami.", - "repair_all": "Popravi sve", - "repair_matched_items": "Podudaranje {count, plural, one {# item} other {# items}}", - "repaired_items": "Popravljeno {count, plural, one {# item} other {# items}}", "require_password_change_on_login": "Zahtijevajte od korisnika promjenu lozinke pri prvoj prijavi", "reset_settings_to_default": "Vrati postavke na zadane", "reset_settings_to_recent_saved": "Resetirajte postavke na nedavno spremljene postavke", @@ -251,7 +243,6 @@ "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.", "storage_template_path_length": "PribliÅžno ograničenje duljine putanje: {length, number}/{limit, number}", "storage_template_settings": "PredloÅžak pohrane", "storage_template_settings_description": "Upravljajte strukturom mape i nazivom datoteke učitanog sredstva", @@ -263,16 +254,14 @@ "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.", + "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", "theme_settings_description": "Upravljajte prilagodbom Immich web sučelja", - "these_files_matched_by_checksum": "Ove datoteke se podudaraju prema njihovim kontrolnim zbrojevima", "thumbnail_generation_job": "Generirajte sličice", "thumbnail_generation_job_description": "Generirajte velike, male i zamućene sličice za svaki materijal, kao i sličice za svaku osobu", "transcoding_acceleration_api": "API ubrzanja", @@ -300,10 +289,9 @@ "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_acceleration_description": "Eksperimentalno: brÅže transkodiranje, ali moÅže smanjiti kvalitetu pri istoj brzini prijenosa", "transcoding_hardware_decoding": "Hardversko dekodiranje", "transcoding_hardware_decoding_setting_description": "Odnosi se samo na NVENC, QSV i RKMPP. Omogućuje ubrzanje s kraja na kraj umjesto samo ubrzavanja kodiranja. MoÅžda neće raditi na svim videozapisima.", - "transcoding_hevc_codec": "HEVC kodek", "transcoding_max_b_frames": "Maksimalni B-frameovi", "transcoding_max_b_frames_description": "ViÅĄe vrijednosti poboljÅĄavaju učinkovitost kompresije, ali usporavaju kodiranje. MoÅžda nije kompatibilan s hardverskim ubrzanjem na starijim uređajima. 0 onemogućuje B-frameove, dok -1 automatski postavlja ovu vrijednost.", "transcoding_max_bitrate": "Maksimalne brzina prijenosa (bitrate)", @@ -341,8 +329,6 @@ "trash_number_of_days_description": "Broj dana za drÅžanje sredstava u smeću prije njihovog trajnog uklanjanja", "trash_settings": "Postavke Smeća", "trash_settings_description": "Upravljanje postavkama smeća", - "untracked_files": "Nepraćene datoteke", - "untracked_files_description": "Aplikacija ne prati ove datoteke. Mogu biti rezultat neuspjelih premjeÅĄtanja, prekinutih prijenosa ili izostale zbog pogreÅĄke", "user_cleanup_job": "ČiÅĄÄ‡enje korisnika", "user_delete_delay": "Račun i sredstva korisnika {user} bit će zakazani za trajno brisanje za {delay, plural, one {# day} other {# days}}.", "user_delete_delay_settings": "Brisanje odgode", @@ -401,10 +387,6 @@ "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}", @@ -420,6 +402,9 @@ "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}}", + "albums_default_sort_order": "Zadani redoslijed sortiranja albuma", + "albums_default_sort_order_description": "Početni redoslijed sortiranja elemenata prilikom izrade novih albuma.", + "albums_feature_description": "Zbirke resursa koje se mogu dijeliti s drugim korisnicima.", "all": "Sve", "all_albums": "Svi albumi", "all_people": "Svi ljudi", @@ -446,6 +431,7 @@ "archive_size": "Veličina arhive", "archive_size_description": "Konfigurirajte veličinu arhive za preuzimanja (u GiB)", "archived": "Ahrivirano", + "archived_count": "{count, plural, other {Archived #}}", "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", @@ -477,6 +463,8 @@ "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_cannot_be_added_to_album_count": "{count, plural,\n one {Nije moguće dodati medij u album}\n few {Nije moguće dodati # medija u album}\n other {Nije moguće dodati # medija u album}\n}", + "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", @@ -536,6 +524,7 @@ "backup_controller_page_excluded": "Izuzeto: ", "backup_controller_page_failed": "NeuspjeÅĄno ({count})", "backup_controller_page_filename": "Naziv datoteke: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informacije o sigurnosnom kopiranju", "backup_controller_page_none_selected": "Nema odabranih", "backup_controller_page_remainder": "Podsjetnik", @@ -559,6 +548,10 @@ "backup_options_page_title": "Opcije sigurnosnog kopiranja", "backup_setting_subtitle": "Upravljajte postavkama učitavanja u pozadini i prvom planu", "backward": "Unazad", + "biometric_auth_enabled": "Biometrijska autentikacija omogućena", + "biometric_locked_out": "Zaključani ste iz biometrijske autentikacije", + "biometric_no_options": "Nema dostupnih biometrijskih opcija", + "biometric_not_available": "Biometrijska autentikacija nije dostupna na ovom uređaju", "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.", "blurred_background": "Zamućena pozadina", @@ -569,21 +562,17 @@ "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", @@ -596,7 +585,10 @@ "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", + "cast": "Prijenos na uređaj (Cast)", + "cast_description": "Konfigurirajte dostupna odrediÅĄta za prijenos (cast)", "change_date": "Promjena datuma", + "change_description": "Promijeni opis", "change_display_order": "Promijeni redoslijed prikaza", "change_expiration_time": "Promjena vremena isteka", "change_location": "Promjena lokacije", @@ -609,9 +601,9 @@ "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_pin_code": "Promijeni PIN kod", "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.", @@ -649,11 +641,14 @@ "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_new_pin_code": "Potvrdi novi PIN kod", "confirm_password": "Potvrdite lozinku", + "confirm_tag_face": "ÅŊelite li označiti ovo lice kao {name}?", + "confirm_tag_face_unnamed": "ÅŊelite li označiti ovo lice?", + "connected_to": "Povezano s", "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", @@ -691,16 +686,21 @@ "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", + "created_at": "Kreirano", "crop": "ObreÅži", "curated_object_page_title": "Stvari", "current_device": "Trenutačni uređaj", + "current_pin_code": "Trenutni PIN kod", "current_server_address": "Trenutna adresa posluÅžitelja", "custom_locale": "Prilagođena Lokalizacija", "custom_locale_description": "Formatiranje datuma i brojeva na temelju jezika i regije", + "daily_title_text_date": "E, MMM dd", + "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Tamno", "date_after": "Datum nakon", "date_and_time": "Datum i Vrijeme", "date_before": "Datum prije", + "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "Datum rođenja uspjeÅĄno spremljen", "date_range": "Razdoblje", "day": "Dan", @@ -742,6 +742,7 @@ "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", @@ -758,7 +759,6 @@ "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", @@ -782,6 +782,8 @@ "edit_avatar": "Uredi avatar", "edit_date": "Uredi datum", "edit_date_and_time": "Uredite datum i vrijeme", + "edit_description": "Uredi opis", + "edit_description_prompt": "Molimo odaberite novi opis:", "edit_exclusion_pattern": "Uredi uzorak izuzimanja", "edit_faces": "Uređivanje lica", "edit_import_path": "Uredi put uvoza", @@ -802,32 +804,36 @@ "editor_crop_tool_h2_aspect_ratios": "Omjeri stranica", "editor_crop_tool_h2_rotation": "Rotacija", "email": "E-poÅĄta", + "email_notifications": "Obavijesti putem e-maila", "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", + "enable_biometric_auth_description": "Unesite svoj PIN kod za omogućavanje biometrijske autentikacije", "enabled": "Omogućeno", "end_date": "Datum zavrÅĄetka", "enqueued": "Dodano u red", "enter_wifi_name": "Unesite naziv Wi-Fi mreÅže", + "enter_your_pin_code": "Unesite svoj PIN kod", + "enter_your_pin_code_subtitle": "Unesite svoj PIN kod za pristup zaključanoj mapi", "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_tag_face_bounding_box": "PogreÅĄka pri označavanju lica – nije moguće dohvatiti koordinate granica (bounding box)", "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 moÅže se {enabled, select, true {onemogućiti} ostalo {omogućiti}} aktivnost", + "cant_change_activity": "Nije moguće {enabled, select, true {onemogućiti} other {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", "cant_get_number_of_comments": "Ne mogu dobiti broj komentara", "cant_search_people": "Ne mogu pretraÅživati ljude", "cant_search_places": "Ne mogu pretraÅživati mjesta", - "cleared_jobs": "Izbrisani poslovi za: {job}", "error_adding_assets_to_album": "PogreÅĄka pri dodavanju materijala u album", "error_adding_users_to_album": "PogreÅĄka pri dodavanju korisnika u album", "error_deleting_shared_user": "PogreÅĄka pri brisanju dijeljenog korisnika", @@ -836,7 +842,6 @@ "error_removing_assets_from_album": "PogreÅĄka prilikom uklanjanja materijala iz albuma, provjerite konzolu za viÅĄe pojedinosti", "error_selecting_all_assets": "PogreÅĄka pri odabiru svih sredstava", "exclusion_pattern_already_exists": "Ovaj uzorak izuzimanja već postoji.", - "failed_job_command": "Naredba {command} nije uspjela za posao: {job}", "failed_to_create_album": "Izrada albuma nije uspjela", "failed_to_create_shared_link": "Stvaranje dijeljene veze nije uspjelo", "failed_to_edit_shared_link": "Nije uspjelo uređivanje dijeljene poveznice", @@ -844,34 +849,35 @@ "failed_to_keep_this_delete_others": "ZadrÅžavanje ovog sredstva i brisanje ostalih sredstava nije uspjelo", "failed_to_load_asset": "Učitavanje sredstva nije uspjelo", "failed_to_load_assets": "Učitavanje sredstava nije uspjelo", + "failed_to_load_notifications": "NeuspjeÅĄno učitavanje obavijesti", "failed_to_load_people": "Učitavanje ljudi nije uspjelo", "failed_to_remove_product_key": "Uklanjanje ključa proizvoda nije uspjelo", "failed_to_stack_assets": "Slaganje sredstava nije uspjelo", "failed_to_unstack_assets": "Nije uspjelo uklanjanje snopa sredstava", + "failed_to_update_notification_status": "NeuspjeÅĄno aÅžuriranje statusa obavijesti", "import_path_already_exists": "Ovaj uvozni put već postoji.", "incorrect_email_or_password": "Netočna adresa e-poÅĄte ili lozinka", "paths_validation_failed": "{paths, plural, one {# putanja nije proÅĄla} other {# putanje nisu proÅĄle}} provjeru valjanosti", "profile_picture_transparent_pixels": "Profilne slike ne smiju imati prozirne piksele. Povećajte i/ili pomaknite sliku.", "quota_higher_than_disk_size": "Postavili ste kvotu veću od veličine diska", - "repair_unable_to_check_items": "Nije moguće provjeriti {count, select, one {item} other {items}}", "unable_to_add_album_users": "Nije moguće dodati korisnike u album", "unable_to_add_assets_to_shared_link": "Nije moguće dodati sredstva na dijeljenu poveznicu", "unable_to_add_comment": "Nije moguće dodati komentar", "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": "Ne moÅže se {archived, select, true {ukloniti resurs iz} ostalo {dodati resurs u}} arhivu", + "unable_to_add_remove_archive": "Nije moguće {archived, select, true {ukloniti resurs iz} other {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_archive_unarchive": "Nije moguće {archived, select, true {arhivirati} other {dearhivirati}}", "unable_to_change_album_user_role": "Nije moguće promijeniti ulogu korisnika albuma", "unable_to_change_date": "Nije moguće promijeniti datum", + "unable_to_change_description": "Nije moguće promijeniti opis", "unable_to_change_favorite": "Nije moguće promijeniti favorita za sredstvo", "unable_to_change_location": "Nije moguće promijeniti lokaciju", "unable_to_change_password": "Nije moguće promijeniti lozinku", "unable_to_change_visibility": "Nije moguće promijeniti vidljivost za {count, plural, one {# osobu} other {# osobe}}", "unable_to_complete_oauth_login": "Nije moguće dovrÅĄiti OAuth prijavu", "unable_to_connect": "Povezivanje nije moguće", - "unable_to_connect_to_server": "Nije moguće spojiti se na posluÅžitelj", "unable_to_copy_to_clipboard": "Nije moguće kopirati u međuspremnik, provjerite pristupate li stranici putem https-a", "unable_to_create_admin_account": "Nije moguće stvoriti administratorski račun", "unable_to_create_api_key": "Nije moguće izraditi novi API ključ", @@ -895,10 +901,6 @@ "unable_to_hide_person": "Nije moguće sakriti osobu", "unable_to_link_motion_video": "Nije moguće povezati videozapis pokreta", "unable_to_link_oauth_account": "Nije moguće povezati OAuth račun", - "unable_to_load_album": "Nije moguće učitati album", - "unable_to_load_asset_activity": "Nije moguće učitati aktivnost sredstva", - "unable_to_load_items": "Nije moguće učitati stavke", - "unable_to_load_liked_status": "Nije moguće učitati status sviđanja", "unable_to_log_out_all_devices": "Nije moguće odjaviti sve uređaje", "unable_to_log_out_device": "Nije moguće odjaviti uređaj", "unable_to_login_with_oauth": "Nije moguće prijaviti se pomoću OAutha", @@ -909,12 +911,11 @@ "unable_to_remove_album_users": "Nije moguće ukloniti korisnike iz albuma", "unable_to_remove_api_key": "Nije moguće ukloniti API ključ", "unable_to_remove_assets_from_shared_link": "Nije moguće ukloniti sredstva iz dijeljene poveznice", - "unable_to_remove_deleted_assets": "Nije moguće ukloniti izvanmreÅžne datoteke", "unable_to_remove_library": "Nije moguće ukloniti biblioteku", "unable_to_remove_partner": "Nije moguće ukloniti partnera", "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 ponovno postaviti lozinku", + "unable_to_reset_pin_code": "Nije moguće poniÅĄtiti PIN kod", "unable_to_resolve_duplicate": "Nije moguće razrijeÅĄiti duplikat", "unable_to_restore_assets": "Nije moguće vratiti imovinu", "unable_to_restore_trash": "Nije moguće vratiti otpad", @@ -942,12 +943,12 @@ "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}", @@ -971,6 +972,7 @@ "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_authenticate": "NeuspjeÅĄna autentikacija", "failed_to_load_assets": "Neuspjelo učitavanje stavki", "failed_to_load_folder": "Neuspjelo učitavanje mape", "favorite": "Omiljeno", @@ -994,6 +996,8 @@ "folders": "Mape", "folders_feature_description": "Pregledavanje prikaza mape za fotografije i videozapise u sustavu datoteka", "forward": "Naprijed", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Ova značajka učitava vanjske resurse s Googlea kako bi radila.", "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", @@ -1036,10 +1040,13 @@ "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_locked_error_local": "Nije moguće premjestiti lokalne resurse u zaključanu mapu, preskačem", + "home_page_locked_error_partner": "Nije moguće premjestiti partnerske resurse u zaključanu mapu, preskačem", "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", + "id": "ID", "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", @@ -1057,6 +1064,7 @@ "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_logo": "Immich Logo", "immich_web_interface": "Immich Web Sučelje", "import_from_json": "Uvoz iz JSON-a", "import_path": "Putanja uvoza", @@ -1078,6 +1086,12 @@ "invalid_date_format": "Neispravan format datuma", "invite_people": "Pozovite ljude", "invite_to_album": "Pozovi u album", + "ios_debug_info_fetch_ran_at": "Dohvaćanje pokrenuto {dateTime}", + "ios_debug_info_last_sync_at": "Zadnja sinkronizacija {dateTime}", + "ios_debug_info_no_processes_queued": "Nema pozadinskih procesa u redu čekanja", + "ios_debug_info_no_sync_yet": "JoÅĄ nije pokrenut nijedan zadatak pozadinske sinkronizacije", + "ios_debug_info_processes_queued": "{count, plural, one {{count} pozadinski proces u redu čekanja} other {{count} pozadinskih procesa u redu čekanja}}", + "ios_debug_info_processing_ran_at": "Obrada pokrenuta {dateTime}", "items_count": "{count, plural, one {# datoteka} other {# datoteke}}", "jobs": "Poslovi", "keep": "ZadrÅži", @@ -1120,6 +1134,8 @@ "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", + "lock": "Zaključaj", + "locked_folder": "Zaključana Mapa", "log_out": "Odjavi se", "log_out_all_devices": "Odjava sa svih uređaja", "logged_out_all_devices": "Odjavljeni su svi uređaji", @@ -1129,6 +1145,7 @@ "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_hint": "http://vaÅĄ-server-ip:port", "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", @@ -1187,6 +1204,9 @@ "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", + "mark_all_as_read": "Označi sve kao pročitano", + "mark_as_read": "Označi kao pročitano", + "marked_all_as_read": "Označeno sve kao pročitano", "matches": "Podudaranja", "media_type": "Vrsta medija", "memories": "Sjećanja", @@ -1207,8 +1227,16 @@ "minimize": "Minimiziraj", "minute": "Minuta", "missing": "Nedostaje", + "model": "Model", "month": "Mjesec", + "monthly_title_text_date_format": "MMMM y", "more": "ViÅĄe", + "move": "Pomakni", + "move_off_locked_folder": "Premjesti iz zaključane mape", + "move_to_locked_folder": "Premjesti u zaključanu mapu", + "move_to_locked_folder_confirmation": "Ove fotografije i videozapis bit će uklonjeni iz svih albuma i bit će vidljivi samo iz zaključane mape", + "moved_to_archive": "PremjeÅĄteno {count, plural, one {# resurs} other {# resursa}} u arhivu", + "moved_to_library": "PremjeÅĄteno {count, plural, one {# resurs} other {# resursa}} u biblioteku", "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", @@ -1223,6 +1251,8 @@ "new_api_key": "Novi API ključ", "new_password": "Nova lozinka", "new_person": "Nova osoba", + "new_pin_code": "Novi PIN kod", + "new_pin_code_subtitle": "Ovo je vaÅĄ prvi pristup zaključanoj mapi. Kreirajte PIN kod za siguran pristup ovoj stranici", "new_user_created": "Stvoren novi korisnik", "new_version_available": "DOSTUPNA NOVA VERZIJA", "newest_first": "Prvo najnovije", @@ -1240,7 +1270,10 @@ "no_explore_results_message": "Prenesite viÅĄe fotografija da istraÅžite svoju zbirku.", "no_favorites_message": "Dodajte favorite kako biste brzo pronaÅĄli svoje najbolje slike i videozapise", "no_libraries_message": "Stvorite vanjsku biblioteku za pregled svojih fotografija i videozapisa", + "no_locked_photos_message": "Fotografije i videozapisi u zaključanoj mapi su skriveni i neće se prikazivati dok pregledavate ili pretraÅžujete svoju biblioteku.", "no_name": "Bez imena", + "no_notifications": "Nema notifikacija", + "no_people_found": "Nema pronađenih odgovarajućih osoba", "no_places": "Nema mjesta", "no_results": "Nema rezultata", "no_results_description": "PokuÅĄajte sa sinonimom ili općenitijom ključnom riječi", @@ -1249,6 +1282,7 @@ "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", + "nothing_here_yet": "Ovdje joÅĄ nema ničega", "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", @@ -1256,16 +1290,15 @@ "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.", - "onboarding_welcome_description": "Postavimo vaÅĄu instancu s nekim uobičajenim postavkama.", "onboarding_welcome_user": "Dobro doÅĄli, {user}", "online": "Dostupan (Online)", "only_favorites": "Samo omiljeno", @@ -1276,11 +1309,13 @@ "options": "Opcije", "or": "ili", "organize_your_library": "Organizirajte svoju knjiÅžnicu", + "original": "originalno", "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", @@ -1320,6 +1355,8 @@ "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": "Dozvola", + "permission_empty": "VaÅĄa dozvola ne smije biti prazna", "permission_onboarding_back": "Natrag", "permission_onboarding_continue_anyway": "Nastavi svejedno", "permission_onboarding_get_started": "Započni", @@ -1337,6 +1374,10 @@ "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": "Odaberite lokaciju", + "pin_code_changed_successfully": "PIN kod je uspjeÅĄno promijenjen", + "pin_code_reset_successfully": "PIN kod je uspjeÅĄno poniÅĄten", + "pin_code_setup_successfully": "PIN kod je uspjeÅĄno postavljen", + "pin_verification": "Provjera PIN koda", "place": "Mjesto", "places": "Mjesta", "places_count": "{count, plural, =1 {{count, number} Mjesto} few {{count, number} Mjesta} other {{count, number} Mjesta}}", @@ -1344,19 +1385,26 @@ "play_memories": "Pokreni sjećanja", "play_motion_photo": "Reproduciraj Pokretnu fotografiju", "play_or_pause_video": "Reproducirajte ili pauzirajte video", + "please_auth_to_access": "Molimo autentificirajte se za pristup", + "port": "Port", "preferences_settings_subtitle": "Upravljajte postavkama aplikacije", "preferences_settings_title": "Postavke", "preset": "Unaprijed postavljeno", "preview": "Pregled", "previous": "Prethodno", "previous_memory": "Prethodno sjećanje", - "previous_or_next_photo": "Prethodna ili sljedeća fotografija", + "previous_or_next_day": "Dan naprijed/natrag", + "previous_or_next_month": "Mjesec naprijed/natrag", + "previous_or_next_photo": "Fotografija naprijed/natrag", + "previous_or_next_year": "Godina naprijed/natrag", "primary": "Primarna (Primary)", "privacy": "Privatnost", + "profile": "Profil", "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_github": "GitHub", "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}", @@ -1431,6 +1479,8 @@ "remove_deleted_assets": "Ukloni izbrisana sredstva", "remove_from_album": "Ukloni iz albuma", "remove_from_favorites": "Ukloni iz favorita", + "remove_from_locked_folder": "Ukloni iz zaključane mape", + "remove_from_locked_folder_confirmation": "Jeste li sigurni da Åželite premjestiti ove fotografije i videozapise iz zaključane mape? Bit će vidljivi u vaÅĄoj biblioteci.", "remove_from_shared_link": "Ukloni iz dijeljene poveznice", "remove_memory": "Ukloni uspomenu", "remove_photo_from_memory": "Ukloni fotografiju iz ove uspomene", @@ -1451,8 +1501,10 @@ "require_password": "Zahtijevaj lozinku", "require_user_to_change_password_on_first_login": "Zahtijevajte od korisnika promjenu lozinke pri prvoj prijavi", "rescan": "Ponovno skeniraj", + "reset": "Resetiraj", "reset_password": "Resetiraj lozinku", "reset_people_visibility": "PoniÅĄti vidljivost ljudi", + "reset_pin_code": "Resetiraj PIN kod", "reset_to_default": "Vrati na zadano", "resolve_duplicates": "RijeÅĄite duplikate", "resolved_all_duplicates": "RazrijeÅĄi sve duplikate", @@ -1545,6 +1597,7 @@ "select_keep_all": "Odaberi zadrÅži sve", "select_library_owner": "Odaberi vlasnika knjiÅžnice", "select_new_face": "Odaberi novo lice", + "select_person_to_tag": "Odaberite osobu za označavanje", "select_photos": "Odaberi fotografije", "select_trash_all": "Odaberi izbriÅĄi sve", "select_user_for_sharing_page_err_album": "Nije uspjelo kreiranje albuma", @@ -1574,7 +1627,6 @@ "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", @@ -1592,10 +1644,12 @@ "settings": "Postavke", "settings_require_restart": "Ponovno pokrenite Immich da biste primijenili ovu postavku", "settings_saved": "Postavke su spremljene", + "setup_pin_code": "Postavi PIN kod", "share": "Podijeli", "share_add_photos": "Dodaj fotografije", "share_assets_selected": "{count} odabrano", "share_dialog_preparing": "Priprema...", + "share_link": "Podijeli Link", "shared": "Podijeljeno", "shared_album_activities_input_disable": "Komentiranje je onemogućeno", "shared_album_activity_remove_content": "ÅŊelite li izbrisati ovu aktivnost?", @@ -1635,6 +1689,7 @@ "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_info_chip_metadata": "EXIF", "shared_link_manage_links": "Upravljanje dijeljenim poveznicama", "shared_link_options": "Opcije dijeljene poveznice", "shared_links": "Dijeljene poveznice", @@ -1700,15 +1755,18 @@ "start": "Početak", "start_date": "Datum početka", "state": "Stanje", + "status": "Status", "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_quota": "Kvota Pohrane", "storage_usage": "{used} od {available} iskoriÅĄteno", "submit": "PoÅĄalji", "suggestions": "Prijedlozi", + "sunrise_on_the_beach": "Izlazak sunca na plaÅži", "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.", @@ -1755,7 +1813,6 @@ "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", @@ -1773,6 +1830,8 @@ "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", + "unable_to_change_pin_code": "Nije moguće promijeniti PIN kod", + "unable_to_setup_pin_code": "Nije moguće postaviti PIN kod", "unarchive": "PoniÅĄti arhiviranje", "unarchived_count": "{count, plural, =1 {PoniÅĄteno arhiviranje #} few {PoniÅĄteno arhiviranje #} other {PoniÅĄteno arhiviranje #}}", "unfavorite": "Ukloni iz omiljenih", @@ -1793,9 +1852,8 @@ "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_at": "AÅžurirano", "updated_password": "Lozinka aÅžurirana", "upload": "Prijenos", "upload_concurrency": "Istovremeni prijenosi", @@ -1810,12 +1868,17 @@ "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", + "url": "URL", "usage": "KoriÅĄtenje", + "use_biometric": "Koristi biometriju", "use_current_connection": "koristi trenutnu vezu", "use_custom_date_range": "Koristi prilagođeni raspon datuma", "user": "Korisnik", + "user_has_been_deleted": "Ovaj korisnik je izbrisan.", "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_pin_code_settings": "PIN kod", + "user_pin_code_settings_description": "Upravljajte svojim PIN kodom", "user_purchase_settings": "Kupnja", "user_purchase_settings_description": "Upravljajte svojom kupnjom", "user_role_set": "Postavi {user} kao {role}", @@ -1831,11 +1894,6 @@ "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", @@ -1855,6 +1913,7 @@ "view_previous_asset": "PrikaÅži prethodnu stavku", "view_qr_code": "PrikaÅži QR kod", "view_stack": "PrikaÅži sloÅžene", + "view_user": "PrikaÅži korisnika", "viewer_remove_from_stack": "Ukloni iz sloÅženih", "viewer_stack_use_as_main_asset": "Koristi kao glavnu stavku", "viewer_unstack": "Razdvoji", @@ -1865,6 +1924,7 @@ "welcome": "DobrodoÅĄli", "welcome_to_immich": "DobrodoÅĄli u Immich", "wifi_name": "Naziv Wi-Fi mreÅže", + "wrong_pin_code": "Krivi PIN kod", "year": "Godina", "years_ago": "prije {years, plural, =1 {# godinu} few {# godine} other {# godina}}", "yes": "Da", diff --git a/i18n/hu.json b/i18n/hu.json index fb2d895e3d..22ec6f22a1 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -44,8 +44,6 @@ "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", "config_set_by_file": "A konfigurÃĄciÃŗt jelenleg egy konfigurÃĄciÃŗs fÃĄjl ÃĄllítja be", "confirm_delete_library": "Biztosan ki szeretnÊd tÃļrÃļlni a {library} kÊptÃĄrat?", @@ -61,14 +59,12 @@ "disable_login": "BelÊpÊs letiltÃĄsa", "duplicate_detection_job_description": "GÊpi tanulÃĄs futtatÃĄsa a hasonlÃŗ elemek megtalÃĄlÃĄsa cÊljÃĄbÃŗl. Ez az Okos KeresÊs funkciÃŗt hasznÃĄlja", "exclusion_pattern_description": "A kihagyÃĄsi mintÃĄk (pattern) hasznÃĄlatakor a mintÃĄnak megfelelő fÃĄjlok vagy mappÃĄk ÃĄt lesznek ugorva a kÊptÃĄr ÃĄtfÊsÃŧlÊsekor. Akkor hasznos, ha a mappÃĄkban vannak olyan fÃĄjlok is, amelyeket nem szeretnÊl importÃĄlni, pl. nyers (RAW) fÃĄjlok.", - "external_library_created_at": "KÃŧlső kÊptÃĄr (lÊtrehozva: {date})", "external_library_management": "KÃŧlső KÊptÃĄrak KezelÊse", "face_detection": "ArckeresÊs", "face_detection_description": "GÊpi tanulÃĄs segítsÊgÊvel megkeresi, hogy hol talÃĄlhatÃŗak arcok az elemeken. VideÃŗk esetÊben csak a bÊlyegkÊpeken keres. \"FrissítÊs\" (Ãējra) feldolgozza az Ãļsszes elemet. \"VisszaÃĄllítÃĄs\" ezen felÃŧl tÃļrli az Ãļsszes aktuÃĄlis arcadatot. \"HiÃĄnyzÃŗk\" sorba ÃĄllítja azokat az elemeket, amelyek eddig mÊg nem lettek feldolgozva. A megtalÃĄlt arcok ezutÃĄn sorba lesznek ÃĄllítva az ArcfelismerÊshez, ami ezutÃĄn az arcokat csoportosítja Ês meglevő vagy Ãēj szemÊlyekhez rendeli.", "facial_recognition_job_description": "A megtalÃĄlt arcokat szemÊlyekhez csoportosítja. Ez a lÊpÊs azutÃĄn kÃļvetkezik, amikor az ArckeresÊs lefutott. \"VisszaÃĄllítÃĄs\" (Ãējra)csoportosítja az Ãļsszes arcot. \"HiÃĄnyzÃŗk\" csak azokkal az arcokkal foglalkozik, amelyekhez mÊg nincsen ember rendelve.", "failed_job_command": "A(z) {command} parancs nem sikerÃŧlt a kÃļvetkező feladathoz: {job}", "force_delete_user_warning": "FIGYELEM: Ez azonnal eltÃĄvolítja a felhasznÃĄlÃŗt Ês az Ãļsszes hozzÃĄ tartozÃŗ elemet. A mÅąvelet nem visszavonhatÃŗ, Ês a fÃĄjlokat sem lehet kÊsőbb visszanyerni.", - "forcing_refresh_library_files": "A kÊptÃĄr Ãļsszes fÃĄjljÃĄnak frissítÊse", "image_format": "FormÃĄtum", "image_format_description": "WebP a JPEG-nÊl kisebb fÃĄjlokat kÊszít, de lassabban.", "image_fullsize_description": "Teljes mÊretÅą kÊp eltÃĄvolított metaadatokkal, nagyítÃĄskor hasznÃĄlva", @@ -197,7 +193,7 @@ "oauth_enable_description": "BejelentkezÊs OAuth hasznÃĄlatÃĄval", "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_mobile_redirect_uri_override_description": "EngedÊlyezd, ha az OAuth szolgÃĄltatÃŗ tiltja a mobil URI-t, mint pÊldÃĄul ''{callback}''", "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.", @@ -209,8 +205,6 @@ "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", "password_settings": "Jelszavas BejelentkezÊs", "password_settings_description": "Jelszavas bejelentkezÊs beÃĄllítÃĄsok kezelÊse", @@ -220,9 +214,6 @@ "refreshing_all_libraries": "Összes kÊptÃĄr frissítÊse", "registration": "Admin RegisztrÃĄciÃŗ", "registration_description": "Mivel ez az első felhasznÃĄlÃŗ a rendszerben, ezÊrt te leszel az Admin, aki az adminisztratív teendőkÊrt felelős Ês tovÃĄbbi felhasznÃĄlÃŗkat tud lÊtrehozni.", - "repair_all": "Összes JavítÃĄsa", - "repair_matched_items": "{count, plural, one {# egyezÊs} other {# egyezÊs}}", - "repaired_items": "Javítva {count, plural, one {# fÃĄjl} other {# fÃĄjl}}", "require_password_change_on_login": "KÃļtelező jelszÃŗmÃŗdosítÃĄs az első bejelentkezÊskor", "reset_settings_to_default": "BeÃĄllítÃĄsok visszaÃĄllítÃĄsa az alapÊrtelmezettre", "reset_settings_to_recent_saved": "BeÃĄllítÃĄsok visszaÃĄllítÃĄsa a legutÃŗbb mentettre", @@ -251,7 +242,6 @@ "storage_template_migration_info": "A sablon az Ãļsszes kiterjesztÊst kisbetÅąssÊ alakítja ÃĄt. A megvÃĄltozott sablon csak az Ãējonnan feltÃļltÃļtt elemekre vonatkozik. A korÃĄbbi elemek visszamenőleges ÃĄthelyezÊsÊhez ezt futtasd: {job}.", "storage_template_migration_job": "TÃĄrhely Sablon MigrÃĄciÃŗja", "storage_template_more_details": "TovÃĄbbi rÊszletekÊrt erről a funkciÃŗrÃŗl lÃĄsd a TÃĄrhely Sablon Ês annak kÃļvetkezmÊnyeit a dokumentÃĄciÃŗban", - "storage_template_onboarding_description": "Ha ez a funkciÃŗ engedÊlyezve van, akkor a fÃĄjlokat automatikusan az egyÊni sablon alapjÃĄn rendszerezi el. StabilitÃĄsi problÊmÃĄk miatt a funkciÃŗ alapÊrtelmezÊs szerint ki van kapcsolva. TovÃĄbbi informÃĄciÃŗkÊrt lÃĄsd a dokumentÃĄciÃŗt.", "storage_template_path_length": "Útvonal hozzÃĄvetőleges maximÃĄlis hossza: {length, number}{limit, number}", "storage_template_settings": "TÃĄrhely Sablon", "storage_template_settings_description": "A feltÃļltÃļtt elemek mappaszerkezetÊnek Ês fÃĄjl elnevezÊsÊnek kezelÊse", @@ -263,7 +253,6 @@ "template_email_invite_album": "Album meghívÃĄs sablon", "template_email_preview": "ElőnÊzet", "template_email_settings": "Email sablonok", - "template_email_settings_description": "SajÃĄt email ÊrtesítÊsek kezelÊse", "template_email_update_album": "Album frissítve sablon", "template_email_welcome": "ÜdvÃļzlő email sablon", "template_settings": "ÉrtesítÊs sablon", @@ -272,7 +261,6 @@ "theme_custom_css_settings_description": "CSS Stíluslapokkal az Immich stílusa megvÃĄltoztathatÃŗ.", "theme_settings": "TÊma BeÃĄllítÃĄsok", "theme_settings_description": "Az Immich webes felÃŧlet testreszabÃĄsÃĄnak kezelÊse", - "these_files_matched_by_checksum": "Ezek a fÃĄjlok egyeznek az ellenőrző ÃļsszegÃŧk alapjÃĄn", "thumbnail_generation_job": "BÊlyegkÊpek GenerÃĄlÃĄsa", "thumbnail_generation_job_description": "Nagy, kicsi Ês elmosÃŗdott bÊlyegkÊpek lÊtrehozÃĄsa minden elemhez, valamint bÊlyegkÊpek generÃĄlÃĄsa minden szemÊlyhez", "transcoding_acceleration_api": "GyorsítÃŗ API", @@ -303,7 +291,6 @@ "transcoding_hardware_acceleration_description": "KísÊrleti funkciÃŗ. Sokkal gyorsabb, viszont azonos bitrÃĄtÃĄn is alacsonyabb minősÊghez vezet", "transcoding_hardware_decoding": "Hardveres dekÃŗdolÃĄs", "transcoding_hardware_decoding_setting_description": "LehetővÊ teszi az egÊsz folyamat gyorsítÃĄsÃĄt a pusztÃĄn kÃŗdolÃĄs gyorsítÃĄsa helyett. Nem biztos, hogy minden videÃŗ esetÊn mÅąkÃļdik.", - "transcoding_hevc_codec": "HEVC kodek", "transcoding_max_b_frames": "B-kÊpkockÃĄk maximum szÃĄma", "transcoding_max_b_frames_description": "Nagyobb ÊrtÊkek megnÃļvelik a tÃļmÃļrítÊs hatÊkonysÃĄgÃĄt, de lelassítjÃĄk a kÃŗdolÃĄst. Nem minden hardvereszkÃļz tÃĄmogatja. A 0 ÊrtÊk kikapcsolja a B-kÊpkockÃĄkat, míg -1 esetÊn a szoftver magÃĄnak vÃĄlaszt ÊrtÊket.", "transcoding_max_bitrate": "Maximum bitrÃĄta", @@ -341,8 +328,6 @@ "trash_number_of_days_description": "HÃĄny napig legyenek a lomtÃĄrban az elemek a vÊgleges tÃļrlÊs előtt", "trash_settings": "LomtÃĄr BeÃĄllítÃĄsok", "trash_settings_description": "LomtÃĄr beÃĄllítÃĄsok kezelÊse", - "untracked_files": "Nem NyilvÃĄntartott FÃĄjlok", - "untracked_files_description": "Ezeket a fÃĄjlokat az alkalmazÃĄs nem tartja nyilvÃĄn. Ez lehetsÊges pÊldÃĄul meghiÃēsult ÃĄthelyezÊs vagy megszakított feltÃļltÊs miatt, illetve valamilyen alkalmazÃĄshiba kÃļvetkeztÊben", "user_cleanup_job": "FelhasznÃĄlÃŗk kipucolÃĄsa", "user_delete_delay": "{user} felhasznÃĄlÃŗi fiÃŗkja Ês elemei vÊglegesen tÃļrÃļlve lesznek {delay, plural, one {# nap} other {# nap}} mÃēlva.", "user_delete_delay_settings": "TÃļrlÊsi kÊsleltetÊs", @@ -398,10 +383,6 @@ "album_remove_user": "FelhasznÃĄlÃŗ tÃļrlÊse?", "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": "{count} elem", - "album_thumbnail_card_shared": "¡ Megosztott", - "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", @@ -567,21 +548,17 @@ "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 ({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 ({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": "{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 ({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,7 +587,6 @@ "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", "check_corrupt_asset_backup": "SÊrÃŧlt elemek keresÊse a mentÊsben", "check_corrupt_asset_backup_button": "EllenőrzÊs", "check_corrupt_asset_backup_description": "Ezt az ellenőtzÊst csak Wi-Fi hÃĄlÃŗzaton futtasd Ês csak akkot, ha mÃĄr az Ãļsszes elem feltÃļltÊsre kerÃŧlt. A folyamat nÊhÃĄny percig is eltarthat.", @@ -652,7 +628,6 @@ "contain": "BelÃŧl", "context": "Kontextus", "continue": "FolytatÃĄs", - "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", @@ -760,7 +735,6 @@ "download_enqueue": "LetÃļltÊs sorba ÃĄllítva", "download_error": "LetÃļltÊsi Hiba", "download_failed": "Sikertelen letÃļltÊs", - "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", @@ -827,7 +801,6 @@ "cant_get_number_of_comments": "HozzÃĄszÃŗlÃĄsok szÃĄmÃĄnak lekÊrdezÊse sikertelen", "cant_search_people": "SzemÊlyek keresÊse sikertelen", "cant_search_places": "Helyek keresÊse sikertelen", - "cleared_jobs": "A(z) {job} feladat tÃļrÃļlve", "error_adding_assets_to_album": "Elemek albumhoz adÃĄsa sikertelen", "error_adding_users_to_album": "FelhasznÃĄlÃŗk albumhoz adÃĄsa sikertelen", "error_deleting_shared_user": "Megosztott felhasznÃĄlÃŗ tÃļrlÊse sikertelen", @@ -836,7 +809,6 @@ "error_removing_assets_from_album": "Az elemek albumbÃŗl valÃŗ eltÃĄvolítÃĄsa sikertelen - tovÃĄbbi informÃĄciÃŗÃŠrt ellenőrizd a konzol kimenetet", "error_selecting_all_assets": "Az Ãļsszes elem kijelÃļlÊse sikertelen", "exclusion_pattern_already_exists": "Ez a kizÃĄrÃĄsi minta (pattern) mÃĄr lÊtezik.", - "failed_job_command": "A(z) {job} feladat {command} parancsa hibÃĄval zÃĄrult", "failed_to_create_album": "Album kÊszítÊse sikertelen", "failed_to_create_shared_link": "Megosztott link kÊszítÊse sikertelen", "failed_to_edit_shared_link": "Megosztott link mÃŗdosítÃĄsa sikertelen", @@ -853,7 +825,6 @@ "paths_validation_failed": "A(z) {paths, plural, one {# elÊrÊsi Ãētvonal} other {# elÊrÊsi Ãētvonal}} ÊrvÊnyesítÊse sikertelen", "profile_picture_transparent_pixels": "ProfilkÊpek nem tartalmazhatnak ÃĄtlÃĄtszÃŗ pixeleket. KÃļzelíts rÃĄ Ês/vagy mozgasd a kÊpet.", "quota_higher_than_disk_size": "Az elÊrhető lemezmÊretnÊl nagyobb kvÃŗtÃĄt ÃĄllítottÃĄl be", - "repair_unable_to_check_items": "{count, select, one {elem} other {elem}} ellenőrzÊse sikertelen", "unable_to_add_album_users": "FelhasznÃĄlÃŗk albumhoz adÃĄsa sikertelen", "unable_to_add_assets_to_shared_link": "Elemeket megosztott linkhez adÃĄsa sikertelen", "unable_to_add_comment": "HozzÃĄszÃŗlÃĄs sikertelen", @@ -871,7 +842,6 @@ "unable_to_change_visibility": "{count, plural, other {# szemÊly}} lÃĄthatÃŗsÃĄgÃĄnak megvÃĄltoztatÃĄsa sikertelen", "unable_to_complete_oauth_login": "OAuth bejelentkezÊs befejezÊse sikertelen", "unable_to_connect": "CsatlakozÃĄs sikertelen", - "unable_to_connect_to_server": "Szerverhez csatlakozÃĄs sikertelen", "unable_to_copy_to_clipboard": "Nem lehet a vÃĄgÃŗlapra mÃĄsolni. Ellenőrizd, hogy az oldalt https-en keresztÃŧl hasznÃĄlod-e", "unable_to_create_admin_account": "Admin felhasznÃĄlÃŗ lÊtrehozÃĄsa sikertelen", "unable_to_create_api_key": "Új API kulcs lÊtrehozÃĄsa sikertelen", @@ -895,10 +865,6 @@ "unable_to_hide_person": "SzemÊly elrejtÊse sikertelen", "unable_to_link_motion_video": "Motion videÃŗ ÃļsszekapcsolÃĄsa sikertelen", "unable_to_link_oauth_account": "OAuth felhasznÃĄlÃŗ hozzÃĄrendelÊse sikertelen", - "unable_to_load_album": "Album betÃļltÊse sikertelen", - "unable_to_load_asset_activity": "Elem aktivitÃĄsÃĄnak betÃļltÊse sikertelen", - "unable_to_load_items": "Elemek betÃļltÊse sikertelen", - "unable_to_load_liked_status": "ReakciÃŗ-ÃĄllapot betÃļltÊse sikertelen", "unable_to_log_out_all_devices": "KijelentkezÊs az Ãļsszes eszkÃļzből sikertelen", "unable_to_log_out_device": "KijelentkezÊs az eszkÃļzről sikertelen", "unable_to_login_with_oauth": "OAuth bejelentkezÊs sikertelen", @@ -909,11 +875,9 @@ "unable_to_remove_album_users": "FelhasznÃĄlÃŗ eltÃĄvolítÃĄsa az albumbÃŗl sikertelen", "unable_to_remove_api_key": "API kulcs eltÃĄvolítÃĄsa sikertelen", "unable_to_remove_assets_from_shared_link": "Elemek eltÃĄvolítÃĄsa a megosztott linkből sikertelen", - "unable_to_remove_deleted_assets": "Offline fÃĄjlok eltÃĄvolítÃĄsa sikertelen", "unable_to_remove_library": "KÊptÃĄr eltÃĄvolítÃĄsa sikertelen", "unable_to_remove_partner": "Partner eltÃĄvolítÃĄsa sikertelen", "unable_to_remove_reaction": "ReakciÃŗ eltÃĄvolítÃĄsa sikertelen", - "unable_to_repair_items": "Elemek javítÃĄsa sikertelen", "unable_to_reset_password": "JelszÃŗ visszaÃĄllítÃĄsa sikertelen", "unable_to_resolve_duplicate": "DuplikÃĄtum feloldÃĄsa sikertelen", "unable_to_restore_assets": "Elemek visszaÃĄllítÃĄsa sikertelen", @@ -1254,15 +1218,12 @@ "notifications": "ÉrtesítÊsek", "notifications_setting_description": "ÉrtesítÊsek kezelÊse", "official_immich_resources": "Hivatalos Immich ForrÃĄsok", - "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", "oldest_first": "LegrÊgebbi előszÃļr", "on_this_device": "Ezen az eszkÃļzÃļn", "onboarding": "Első lÊpÊsek", "onboarding_privacy_description": "Az alÃĄbbi (nem kÃļtelező) funkciÃŗk kÃŧlsős szolgÃĄltatÃĄsokon alapulnak Ês bÃĄrmikor kikapcsolhatÃŗak az adminisztrÃĄciÃŗs beÃĄllítÃĄsokban.", "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}", "only_favorites": "Csak kedvencek", "open_in_map_view": "MegnyitÃĄs tÊrkÊp nÊzetben", @@ -1533,7 +1494,7 @@ "searching_locales": "Helyszín keresÊse...", "second": "MÃĄsodperc", "see_all_people": "Minden szemÊly megtekintÊse", - "select": "KivÃĄlsztÃĄs", + "select": "KivÃĄlasztÃĄs", "select_album_cover": "AlbumborítÃŗ kivÃĄlasztÃĄsa", "select_all": "Összes kijelÃļlÊse", "select_all_duplicates": "Minden duplikÃĄtum kijelÃļlÊse", @@ -1573,7 +1534,6 @@ "setting_image_viewer_title": "KÊpek", "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: {duration}", "setting_notifications_notify_hours": "{count} Ãŗra", "setting_notifications_notify_immediately": "azonnal", @@ -1756,7 +1716,6 @@ "to_parent": "Egy szinttel feljebb", "to_trash": "LomtÃĄrba helyezÊs", "toggle_settings": "BeÃĄllítÃĄsok ÃĄtÃĄllítÃĄsa", - "toggle_theme": "SÃļtÊt tÊma ÃĄtvÃĄltÃĄsa", "total": "Összesen", "total_usage": "Összesen hasznÃĄlatban", "trash": "LomtÃĄr", @@ -1796,8 +1755,6 @@ "unselect_all_duplicates": "DuplikÃĄtumok kijelÃļlÊsÊnek megszÃŧntetÊse", "unstack": "Csoport SzÊtszedÊse", "unstacked_assets_count": "{count, plural, other {# elemből}} ÃĄllÃŗ csoport szÊtszedve", - "untracked_files": "Nem nyilvÃĄntartott fÃĄjlok", - "untracked_files_decription": "Ezeket a fÃĄjlokat az alkalmazÃĄs nem tartja nyilvÃĄn. Ez lehetsÊges pÊldÃĄul meghiÃēsult ÃĄthelyezÊs vagy megszakított feltÃļltÊs miatt, illetve valamilyen alkalmazÃĄshiba kÃļvetkeztÊben", "up_next": "KÃļvetkezik", "updated_password": "JelszÃŗ megvÃĄltoztatva", "upload": "FeltÃļltÊs", @@ -1836,11 +1793,6 @@ "version": "VerziÃŗ", "version_announcement_closing": "BarÃĄtsÃĄggal, Alex", "version_announcement_message": "Szia! Az Immich-nek elÊrhető egy Ãēj verziÃŗja. KÊrjÃŧk, szÃĄnj időt a verziÃŗinformÃĄciÃŗ elolvasÃĄsÃĄra, hogy meggyőződj rÃŗla, hogy a beÃĄllítÃĄsaid naprakÊszek, így elkerÃŧlj egy esetleges fÊlrekonfigurÃĄlÃĄst. KÃŧlÃļnÃļsen, ha WatchTower-t vagy mÃĄs automatikus frissítÊsi megoldÃĄst hasznÃĄlsz.", - "version_announcement_overlay_release_notes": "kiadÃĄsi megjegyzÊsek ÃĄttekintÊsÊre", - "version_announcement_overlay_text_1": "Szia barÃĄtom, ennek az alkalmazÃĄsnak van egy Ãēj verziÃŗja: ", - "version_announcement_overlay_text_2": "KÊrjÃŧk, szÃĄnj időt a", - "version_announcement_overlay_text_3": ", Ês győződj meg rÃŗla, hogy a docker-compose.yml Ês az .env beÃĄllítÃĄsaid naprakÊszek, hogy elkerÃŧld a hibÃĄs konfigurÃĄciÃŗkat, kÃŧlÃļnÃļsen, ha a WatchTower-t vagy bÃĄrmilyen automatikus frissítÊsi megoldÃĄst hasznÃĄlsz.", - "version_announcement_overlay_title": "ElÊrhető Új SzerververziÃŗ 🎉", "version_history": "VerziÃŗtÃļrtÊnet", "version_history_item": "{version} telepítve: {date}", "video": "VideÃŗ", @@ -1858,17 +1810,20 @@ "view_name": "MegtekintÊs", "view_next_asset": "KÃļvetkező elem megtekintÊse", "view_previous_asset": "Előző elem megtekintÊse", + "view_qr_code": "QR kÃŗd megtekintÊse", "view_stack": "Csoport MegtekintÊse", + "view_user": "FelhasznÃĄlÃŗ MegtekintÊse", "viewer_remove_from_stack": "EltÃĄvolít a CsoportbÃŗl", "viewer_stack_use_as_main_asset": "Fő Elemnek BeÃĄllít", - "viewer_unstack": "Csoport MegszÅąntetÊse", + "viewer_unstack": "Csoport MegszÃŧntetÊse", "visibility_changed": "{count, plural, other {# szemÊly}} lÃĄthatÃŗsÃĄga megvÃĄltozott", "waiting": "VÃĄrakozÃĄs", "warning": "FigyelmeztetÊs", "week": "HÊt", "welcome": "ÜdvÃļzlÃŧnk", - "welcome_to_immich": "ÜdvÃļzlÃŧnk az Immich-ben", + "welcome_to_immich": "ÜdvÃļzÃļl az Immich", "wifi_name": "WiFi Neve", + "wrong_pin_code": "HibÃĄs PIN kÃŗd", "year": "Év", "years_ago": "{years, plural, one {# Êvvel} other {# Êvvel}} ezelőtt", "yes": "Igen", diff --git a/i18n/hy.json b/i18n/hy.json index 76adfb3778..86cbcd5554 100644 --- a/i18n/hy.json +++ b/i18n/hy.json @@ -26,7 +26,6 @@ "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} ÕļÕ¯ÕĄÖ€", diff --git a/i18n/id.json b/i18n/id.json index 36dd6a953e..4d15ef01e9 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -22,11 +22,11 @@ "add_partner": "Tambahkan partner", "add_path": "Tambahkan jalur", "add_photos": "Tambahkan foto", + "add_tag": "Tambahkan tag", "add_to": "Tambahkan keâ€Ļ", "add_to_album": "Tambahkan ke 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", @@ -45,8 +45,6 @@ "backup_keep_last_amount": "Jumlah cadangan untuk disimpan", "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}", "config_set_by_file": "Konfigurasi saat ini ditetapkan oleh berkas konfigurasi", "confirm_delete_library": "Apakah Anda yakin ingin menghapus pustaka {library}?", @@ -62,14 +60,12 @@ "disable_login": "Nonaktifkan log masuk", "duplicate_detection_job_description": "Jalankan pembelajaran mesin pada aset untuk mendeteksi gambar yang serupa. Bergantung pada Pencarian Pintar", "exclusion_pattern_description": "Pola pengecualian memungkinkan Anda mengabaikan berkas dan folder ketika memindai pustaka Anda. Ini berguna jika Anda memiliki folder yang berisi berkas yang tidak ingin diimpor, seperti berkas RAW.", - "external_library_created_at": "Pustaka eksternal (dibuat pada {date})", "external_library_management": "Pengelolaan Pustaka Eksternal", "face_detection": "Deteksi wajah", "face_detection_description": "Deteksikan wajah dalam aset menggunakan pembelajaran mesin. Untuk video, hanya gambar kecilnya yang disertakan. \"Segarkan\" memproses (ulang) semua aset. \"Segarkan\" juga menghapus data wajah terkini. \"Hilang\" mengantrekan aset yang belum diproses. Wajah yang dideteksi akan diantrekan untuk Pengenalan Wajah setelah Pendeteksian Wajah selesai, mengelompokkan mereka dalam orang yang sudah ada atau yang baru.", "facial_recognition_job_description": "Kelompokkan wajah yang telah dideteksi menjadi orang. Langkah ini berjalan setelah Deteksi Wajah selesai. \"Segarkan\" mengelompokkan (ulang) semua wajah. \"Hilang\" mengantrekan wajah yang belum ditetapkan dengan seseorang.", "failed_job_command": "Perintah {command} gagal untuk tugas: {job}", "force_delete_user_warning": "PERINGATAN: Ini akan segera menghapus pengguna dan semua asetnya. Ini tidak dapat diurungkan dan semua berkasnya tidak dapat dipulihkan.", - "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", @@ -198,7 +194,7 @@ "oauth_enable_description": "Log masuk dengan OAuth", "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_mobile_redirect_uri_override_description": "Aktifkan ketika provider OAuth tidak mengizinkan tautan mobile, seperti ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "Kelola pengaturan log masuk OAuth", "oauth_settings_more_details": "Untuk detail lanjut tentang fitur ini, lihat docs.", @@ -210,8 +206,6 @@ "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", "password_settings": "Log Masuk Kata Sandi", "password_settings_description": "Kelola pengaturan log masuk kata sandi", @@ -221,9 +215,6 @@ "refreshing_all_libraries": "Menyegarkan semua pustaka", "registration": "Pendaftaran Admin", "registration_description": "Karena Anda merupakan pengguna pertama dalam sistem, Anda akan ditetapkan sebagai Admin dan bertanggung jawab atas tugas administratif dan pengguna tambahan akan dibuat oleh Anda.", - "repair_all": "Perbaiki Semua", - "repair_matched_items": "{count, plural, one {# item} other {# item}} dicocokkan", - "repaired_items": "{count, plural, one {# item} other {# item}} diperbaiki", "require_password_change_on_login": "Memerlukan pengguna untuk mengubah kata sandi pada log masuk pertama", "reset_settings_to_default": "Atur ulang pengaturan ke bawaan", "reset_settings_to_recent_saved": "Atur ulang pengaturan ke pengaturan tersimpan terkini", @@ -252,7 +243,6 @@ "storage_template_migration_info": "Templat penyimpanan akan mengubah semua ekstensi ke huruf kecil. Perubahan templat hanya akan diterapkan pada aset baru. Untuk menerapkan templat pada setiap aset yang sebelumnya telah diunggah, jalankan {job}.", "storage_template_migration_job": "Tugas Migrasi Templat Ruang Penyimpanan", "storage_template_more_details": "Untuk detail lebih lanjut tentang fitur ini, pergi ke Templat Penyimpanan dan kekurangannya", - "storage_template_onboarding_description": "Ketika diaktifkan, fitur ini akan mengelola berkas secara otomatis berdasarkan templat pengguna. Karena masalah stabilitas, fitur ini telah dimatikan secara bawaan. Untuk informasi lebih lanjut, silakan lihat dokumentasi.", "storage_template_path_length": "Batas panjang jalur: {length, number}{limit, number}", "storage_template_settings": "Templat Penyimpanan", "storage_template_settings_description": "Kelola struktur folder dan nama berkas dari aset yang diunggah", @@ -264,7 +254,6 @@ "template_email_invite_album": "Templat Undangan Album", "template_email_preview": "Pratinjau", "template_email_settings": "Templat Surel", - "template_email_settings_description": "Kelola templat notifikasi surel kustom", "template_email_update_album": "Perbarui Templat Album", "template_email_welcome": "Templat surel selamat datang", "template_settings": "Templat Notifikasi", @@ -273,7 +262,6 @@ "theme_custom_css_settings_description": "CSS memungkinkan desain Immich untuk diubah.", "theme_settings": "Pengaturan Tema", "theme_settings_description": "Kelola kustomisasi antarmuka web Immich", - "these_files_matched_by_checksum": "Berkas ini cocok dengan checksum-nya", "thumbnail_generation_job": "Buat Gambar Kecil", "thumbnail_generation_job_description": "Buat aset besar, kecil, dan buram untuk setiap aset, termasuk gambar kecil untuk setiap orang", "transcoding_acceleration_api": "API Akselerasi", @@ -304,7 +292,6 @@ "transcoding_hardware_acceleration_description": "Uji coba; lebih cepat, tetapi akan memiliki kualitas lebih rendah pada kecepatan bit yang sama", "transcoding_hardware_decoding": "Dekode perangkat keras", "transcoding_hardware_decoding_setting_description": "Mengaktifkan akselerasi ujung ke ujung daripada hanya mengakselerasi pengodean. Mungkin tidak berfungsi pada semua video.", - "transcoding_hevc_codec": "Kodek HEVC", "transcoding_max_b_frames": "Bingkai B maksimum", "transcoding_max_b_frames_description": "Nilai yang lebih tinggi meningkatkan efisiensi kompresi, tetapi membuat pengodean lebih lambat. Mungkin tidak kompatibel dengan akselerasi perangkat keras pada perangkat lawas. 0 menonaktifkan bingkai B, sedangkan -1 mengatur nilai ini secara otomatis.", "transcoding_max_bitrate": "Kecepatan bit maksimum", @@ -342,8 +329,6 @@ "trash_number_of_days_description": "Jumlah hari untuk menyimpan aset dalam sampah sebelum dihapus secara permanen", "trash_settings": "Pengaturan Sampah", "trash_settings_description": "Kelola pengaturan sampah", - "untracked_files": "Berkas yang Belum Dilacak", - "untracked_files_description": "Berkas ini tidak dilacak oleh aplikasi. Mereka dapat diakibatkan oleh pemindahan gagal, pengunggahan terganggu, atau tertinggal karena oleh kutu", "user_cleanup_job": "Pembersihan data pengguna", "user_delete_delay": "Akun dan aset {user} akan dijadwalkan untuk penghapusan permanen dalam {delay, plural, one {# hari} other {# hari}}.", "user_delete_delay_settings": "Jeda penghapusan", @@ -395,9 +380,6 @@ "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_items": "{count} item", - "album_thumbnail_card_shared": " ¡ Dibagikan", - "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}", @@ -552,21 +534,17 @@ "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": "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": "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": "{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 ({count} aset)", "cache_settings_tile_subtitle": "Mengatur perilaku penyimpanan lokal", "cache_settings_tile_title": "Penyimpanan Lokal", "cache_settings_title": "Setelan Cache", @@ -597,7 +575,6 @@ "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": "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.", @@ -641,7 +618,6 @@ "contain": "Berisi", "context": "Konteks", "continue": "Lanjutkan", - "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", @@ -751,7 +727,6 @@ "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", @@ -822,7 +797,6 @@ "cant_get_number_of_comments": "Tidak bisa mendapatkan jumlah komentar", "cant_search_people": "Tidak dapat mencari orang", "cant_search_places": "Tidak dapat mencari tempat", - "cleared_jobs": "Tugas dihapus untuk: {job}", "error_adding_assets_to_album": "Terjadi eror menambahkan aset ke album", "error_adding_users_to_album": "Terjadi kesalahan menambahkan pengguna ke album", "error_deleting_shared_user": "Terjadi eror menghapus pengguna terbagi", @@ -831,7 +805,6 @@ "error_removing_assets_from_album": "Terjadi eror menghapus aset dari album, lihat konsol untuk detail lebih lanjut", "error_selecting_all_assets": "Terjadi eror memilih semua aset", "exclusion_pattern_already_exists": "Pola pengecualian ini sudah ada.", - "failed_job_command": "Perintah {command} gagal untuk tugas: {job}", "failed_to_create_album": "Gagal membuat album", "failed_to_create_shared_link": "Gagal membuat tautan terbagi", "failed_to_edit_shared_link": "Gagal menyunting tautan terbagi", @@ -850,7 +823,6 @@ "paths_validation_failed": "{paths, plural, one {# jalur} other {# jalur}} gagal validasi", "profile_picture_transparent_pixels": "Foto profil tidak dapat memiliki piksel transparan. Silakan perbesar dan/atau pindah posisi gambar.", "quota_higher_than_disk_size": "Anda menetapkan kuota lebih tinggi dari ukuran diska", - "repair_unable_to_check_items": "Tidak dapat memeriksa {count, select, one {item} other {item}}", "unable_to_add_album_users": "Tidak dapat menambahkan pengguna ke album", "unable_to_add_assets_to_shared_link": "Tidak dapat menambahkan aset ke tautan terbagi", "unable_to_add_comment": "Tidak dapat menambahkan komentar", @@ -869,7 +841,6 @@ "unable_to_change_visibility": "Tidak dapat mengubah keterlihatan untuk {count, plural, one {# orang} other {# orang}}", "unable_to_complete_oauth_login": "Tidak dapat menyelesaikan log masuk OAuth", "unable_to_connect": "Tidak dapat menghubungkan", - "unable_to_connect_to_server": "Tidak bisa menghubungi server", "unable_to_copy_to_clipboard": "Tidak dapat menyalin ke papan klip, pastikan Anda mengakses laman ini melalui HTTPS", "unable_to_create_admin_account": "Tidak dapat membuat akun admin", "unable_to_create_api_key": "Tidak dapat membuat Kunci API baru", @@ -893,14 +864,9 @@ "unable_to_hide_person": "Tidak dapat menyembunyikan orang", "unable_to_link_motion_video": "Tidak dapat menautkan video gerak", "unable_to_link_oauth_account": "Tidak dapat menautkan akun OAuth", - "unable_to_load_album": "Tidak dapat memuat album", - "unable_to_load_asset_activity": "Tidak dapat memuat aktivitas aset", - "unable_to_load_items": "Tidak dapat memuat item", - "unable_to_load_liked_status": "Tidak dapat memuat status suka", "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", @@ -908,11 +874,9 @@ "unable_to_remove_album_users": "Tidak dapat mengeluarkan pengguna dari album", "unable_to_remove_api_key": "Tidak dapat menghapus Kunci API", "unable_to_remove_assets_from_shared_link": "Tidak dapat menghapus aset dari tautan terbagi", - "unable_to_remove_deleted_assets": "Tidak dapat menghapus berkas luring", "unable_to_remove_library": "Tidak dapat menghapus pustaka", "unable_to_remove_partner": "Tidak dapat menghapus partner", "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", @@ -948,7 +912,6 @@ "exif_bottom_sheet_location": "LOKASI", "exif_bottom_sheet_people": "ORANG", "exif_bottom_sheet_person_add_person": "Tambah nama", - "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}", @@ -1240,14 +1203,11 @@ "notifications_setting_description": "Kelola notifikasi", "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", "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.", - "onboarding_welcome_description": "Mari menyiapkan server Anda dengan beberapa pengaturan umum.", "onboarding_welcome_user": "Selamat datang, {user}", "online": "Daring", "only_favorites": "Hanya favorit", @@ -1546,7 +1506,6 @@ "setting_image_viewer_preview_title": "Muat gambar preview", "setting_image_viewer_title": "Foto", "setting_languages_apply": "Terapkan", - "setting_languages_title": "Bahasa", "setting_notifications_notify_failures_grace_period": "Beritahu kegagalan cadangan latar belakang: {duration}", "setting_notifications_notify_hours": "{count} jam", "setting_notifications_notify_immediately": "segera", @@ -1712,7 +1671,6 @@ "to_parent": "Ke induk", "to_trash": "Sampah", "toggle_settings": "Saklar pengaturan", - "toggle_theme": "Beralih tema gelap", "total": "Jumlah", "total_usage": "Jumlah penggunaan", "trash": "Sampah", @@ -1751,8 +1709,6 @@ "unselect_all_duplicates": "Batal pilih semua duplikat", "unstack": "Batalkan penumpukan", "unstacked_assets_count": "Penumpukan {count, plural, one {# aset} other {# aset}} dibatalkan", - "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", @@ -1791,7 +1747,6 @@ "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_title": "Server Versi Baru Tersedia 🎉", "version_history": "Riwayat Versi", "version_history_item": "Terpasang {version} pada {date}", "video_hover_setting": "Putar gambar kecil video saat kursor di atas", diff --git a/i18n/it.json b/i18n/it.json index 8721cefc61..07e19736a7 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -22,6 +22,7 @@ "add_partner": "Aggiungi partner", "add_path": "Aggiungi percorso", "add_photos": "Aggiungi foto", + "add_tag": "Aggiungi tag", "add_to": "Aggiungi aâ€Ļ", "add_to_album": "Aggiungi all'album", "add_to_album_bottom_sheet_added": "Aggiunto in {album}", @@ -33,6 +34,7 @@ "added_to_favorites_count": "Aggiunto {count, number} ai preferiti", "admin": { "add_exclusion_pattern_description": "Aggiungi modelli di esclusione. È supportato il globbing utilizzando *, ** e ?. Per ignorare tutti i file in qualsiasi directory denominata \"Raw\", usa \"**/Raw/**\". Per ignorare tutti i file con estensione \".tif\", usa \"**/*.tif\". Per ignorare un percorso assoluto, usa \"/percorso/da/ignorare/**\".", + "admin_user": "Utente amministratore", "asset_offline_description": "Questa risorsa della libreria esterna non si trova piÚ sul disco ed è stata spostata nel cestino. Se il file è stato spostato all'interno della libreria, controlla la timeline per la nuova risorsa corrispondente. Per ripristinare questa risorsa, assicurati che Immich possa accedere al percorso del file ed esegui la scansione della libreria.", "authentication_settings": "Impostazioni di Autenticazione", "authentication_settings_description": "Gestisci password, OAuth e altre impostazioni di autenticazione", @@ -43,9 +45,7 @@ "backup_database_enable_description": "Abilita i backup del database", "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", + "backup_settings_description": "Gestisci le impostazioni dei backup.", "cleared_jobs": "Cancellati i processi per: {job}", "config_set_by_file": "La configurazione è attualmente impostata da un file di configurazione", "confirm_delete_library": "Sei sicuro di voler cancellare la libreria {library}?", @@ -61,14 +61,12 @@ "disable_login": "Disabilita login", "duplicate_detection_job_description": "Esegui il machine learning sugli assets per rilevare immagini simili. Basato su Ricerca Intelligente", "exclusion_pattern_description": "I modelli di esclusione ti permettono di ignorare file e cartelle durante la scansione della tua libreria. Questo è utile se hai cartelle che contengono file che non vuoi importare, come ad esempio, i file RAW.", - "external_library_created_at": "Libreria esterna (creata il {date})", "external_library_management": "Gestione Librerie Esterne", "face_detection": "Rilevamento Volti", "face_detection_description": "Rileva i volti presenti negli assets utilizzando il machine learning. Per i video, viene presa in considerazione solo la miniatura. \"Aggiorna\" (ri-)processerà tutti gli assets. \"Reset\" inoltre elimina tutti i dati dei volti correnti. \"Mancanti\" seleziona solo gli assets che non sono ancora stati processati. I volti rilevati verranno selezionati per il riconoscimento facciale dopo che il rilevamento dei volti sarà stato completato, raggruppandoli in persone esistenti e/o nuove.", "facial_recognition_job_description": "Raggruppa i volti rilevati in persone. Questo processo viene eseguito dopo che il rilevamento volti è stato completato. \"Reset\" (ri-)unisce tutti i volti. \"Mancanti\" processa i volti che non hanno una persona assegnata.", "failed_job_command": "Il comando {command} è fallito per il processo: {job}", "force_delete_user_warning": "ATTENZIONE: Questo rimuoverà immediatamente l'utente e tutti i suoi assets. Non è possibile tornare indietro e i file non potranno essere recuperati.", - "forcing_refresh_library_files": "Forzando l'aggiornamento completo della libreria", "image_format": "Formato", "image_format_description": "WebP produce file piÚ piccoli rispetto a JPEG, ma l'encoding è piÚ lento.", "image_fullsize_description": "Le immagini con dimensioni reali senza metadati sono utilizzate durante lo zoom", @@ -173,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Nota: Per assegnare l'etichetta storage ad asset precedentemente caricati, esegui", "note_cannot_be_changed_later": "NOTA: Non potrà essere modificato in futuro!", "notification_email_from_address": "Indirizzo mittente", - "notification_email_from_address_description": "Indirizzo email mittente, ad esempio: \"Server Foto Immich \"", + "notification_email_from_address_description": "Indirizzo email del mittente, ad esempio: \"Immich Photo Server \". Assicurati di utilizzare un indirizzo da cui sei autorizzato a inviare email.", "notification_email_host_description": "Host del server email (es. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignora errori di certificato", "notification_email_ignore_certificate_errors_description": "Ignora errori di validazione del certificato TLS (sconsigliato)", @@ -197,7 +195,7 @@ "oauth_enable_description": "Login con OAuth", "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_mobile_redirect_uri_override_description": "Abilita quando il gestore OAuth non consente un URL come ''{callback}''", "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.", @@ -206,11 +204,9 @@ "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_storage_quota_default_description": "Limite in GiB da usare quanto nessuna dichiarazione di ambito(claim) è stata fornita.", "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", "password_settings": "Login con password", "password_settings_description": "Gestisci impostazioni del login con password", @@ -220,9 +216,6 @@ "refreshing_all_libraries": "Aggiorna tutte le librerie", "registration": "Registrazione amministratore", "registration_description": "PoichÊ sei il primo utente del sistema, sarai assegnato come Amministratore e sarai responsabile dei task amministrativi, e utenti aggiuntivi saranno creati da te.", - "repair_all": "Ripara Tutto", - "repair_matched_items": "{count, plural, one {Rilevato # elemento} other {Rilevati # elementi}}", - "repaired_items": "{count, plural, one {Riparato # elemento} other {Riparati # elementi}}", "require_password_change_on_login": "Richiedi all'utente di cambiare password al primo accesso", "reset_settings_to_default": "Ripristina impostazioni predefinite", "reset_settings_to_recent_saved": "Ripristina impostazioni alle impostazioni salvate di recente", @@ -251,7 +244,7 @@ "storage_template_migration_info": "Le modifiche al modello di archiviazione verranno applicate solo agli asset nuovi. Per applicare le modifiche retroattivamente esegui {job}.", "storage_template_migration_job": "Processo Migrazione Modello di Archiviazione", "storage_template_more_details": "Per maggiori informazioni riguardo a questa funzionalità, consulta il Modello Archiviazione e le sue conseguenze", - "storage_template_onboarding_description": "Quando attivata, questa funzionalità organizzerà automaticamente i file utilizzando il modello di archiviazione definito dall'utente. Per ragioni di stabilità, questa funzionalità è disabilitata per impostazione predefinita. Per piÚ informazioni, consulta la documentazione.", + "storage_template_onboarding_description_v2": "Se attiva, questa funzionalità organizzerà automaticamente i file utilizzando un modello definito dall'utente. Per maggiori informazioni, consultare la documentazione.", "storage_template_path_length": "Limite approssimativo lunghezza percorso: {length, number}/{limit, number}", "storage_template_settings": "Modello Archiviazione", "storage_template_settings_description": "Gestisci la struttura delle cartelle e il nome degli asset caricati", @@ -263,16 +256,14 @@ "template_email_invite_album": "Modello di invito all'album", "template_email_preview": "Anteprima", "template_email_settings": "Template Email", - "template_email_settings_description": "Gestisci i modelli personalizzati di notifiche email", "template_email_update_album": "Modello di aggiornamento dell'album", "template_email_welcome": "Modello di email di benvenuto", "template_settings": "Templates Notifiche", - "template_settings_description": "Gestisci i modelli personalizzati per le notifiche.", + "template_settings_description": "Gestisci i modelli personalizzati per le notifiche", "theme_custom_css_settings": "CSS Personalizzato", "theme_custom_css_settings_description": "I Cascading Style Sheets (CSS) permettono di personalizzare l'interfaccia di Immich.", "theme_settings": "Impostazioni Tema", "theme_settings_description": "Gestisci la personalizzazione dell'interfaccia web di Immich", - "these_files_matched_by_checksum": "File abbinati per checksum", "thumbnail_generation_job": "Generazione Miniature", "thumbnail_generation_job_description": "Genera miniature grandi, piccole e sfocate per ogni asset, oltre a miniature per ogni persona", "transcoding_acceleration_api": "API di accelerazione", @@ -300,10 +291,9 @@ "transcoding_encoding_options": "Opzioni di codifica", "transcoding_encoding_options_description": "Imposta codecs, risoluzione, qualità ed altre opzioni per i video codificati", "transcoding_hardware_acceleration": "Accelerazione Hardware", - "transcoding_hardware_acceleration_description": "Sperimentale; molto piÚ veloce, ma avrà una qualità inferiore allo stesso bitrate", + "transcoding_hardware_acceleration_description": "Sperimentale: codifica piÚ veloce ma con qualità inferiore a parità bitrate", "transcoding_hardware_decoding": "Decodifica hardware", "transcoding_hardware_decoding_setting_description": "Abilita l'accelerazione end-to-end anzichÊ accelerare solo la codifica. Potrebbe non funzionare su tutti i video.", - "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "B-frames Massimi", "transcoding_max_b_frames_description": "Valori piÚ alti migliorano l'efficienza di compressione, ma rallentano l'encoding. Potrebbero non essere compatibili con l'accelerazione hardware su dispositivi piÚ vecchi. 0 disabilita i B-frames, mentre -1 imposta questo valore automaticamente.", "transcoding_max_bitrate": "Bitrate massimo", @@ -341,8 +331,6 @@ "trash_number_of_days_description": "Numero di giorni per cui mantenere gli asset nel cestino prima di rimuoverli definitivamente", "trash_settings": "Impostazioni cestino", "trash_settings_description": "Gestisci impostazioni cestino", - "untracked_files": "File non tracciati", - "untracked_files_description": "Questi file non sono tracciati dall'applicazione. Potrebbero essere il risultato di spostamenti falliti, caricamenti interrotti o abbandonati a causa di un bug", "user_cleanup_job": "Pulizia Utente", "user_delete_delay": "L'account e gli asset dell'utente {user} verranno programmati per la cancellazione definitiva tra {delay, plural, one {# giorno} other {# giorni}}.", "user_delete_delay_settings": "Ritardo eliminazione", @@ -375,6 +363,7 @@ "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": "Definisci gli header per i proxy che Immich dovrebbe inviare con ogni richiesta di rete", + "advanced_settings_proxy_headers_title": "Header Proxy", "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", @@ -400,10 +389,6 @@ "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": "{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", @@ -419,6 +404,9 @@ "album_with_link_access": "Permetti a chiunque possieda il link di visualizzare le foto e le persone dell'album.", "albums": "Album", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", + "albums_default_sort_order": "Ordinamento album predefinito", + "albums_default_sort_order_description": "Ordine iniziale degli asset alla creazione di nuovi album.", + "albums_feature_description": "Collezione di asset che possono essere condivisi con altri utenti.", "all": "Tutti", "all_albums": "Tutti gli album", "all_people": "Tutte le persone", @@ -461,6 +449,7 @@ "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", @@ -476,9 +465,12 @@ "assets_added_count": "{count, plural, one {# asset aggiunto} other {# asset aggiunti}}", "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_cannot_be_added_to_album_count": "{count, plural, one {L'asset} other {Gli asset}} non possono essere aggiunti all'album", "assets_count": "{count, plural, other {# asset}}", "assets_deleted_permanently": "{count} elementi cancellati definitivamente", "assets_deleted_permanently_from_server": "{count} elementi cancellati definitivamente dal server Immich", + "assets_downloaded_failed": "{count, plural, one {Scaricato # file - {error} file non riusciti} other {Scaricati # file - {error} file non riusciti}}", + "assets_downloaded_successfully": "{count, plural, one {Scaricato # file con successo} other {Scaricati # file con successo}}", "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}}", @@ -493,6 +485,7 @@ "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", "automatic_endpoint_switching_title": "Cambio automatico di URL", + "autoplay_slideshow": "Avvio automatico presentazione", "back": "Indietro", "back_close_deselect": "Indietro, chiudi o deseleziona", "background_location_permission": "Permesso di localizzazione in background", @@ -506,7 +499,7 @@ "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 di {filename} in corso", + "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â€Ļ", @@ -517,6 +510,7 @@ "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", @@ -527,6 +521,7 @@ "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 Wi-Fi", + "backup_controller_page_backup": "Backup", "backup_controller_page_backup_selected": "Selezionati: ", "backup_controller_page_backup_sub": "Foto e video caricati", "backup_controller_page_created": "Creato il: {date}", @@ -534,6 +529,7 @@ "backup_controller_page_excluded": "Esclusi: ", "backup_controller_page_failed": "Falliti: ({count})", "backup_controller_page_filename": "Nome file: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informazioni sul backup", "backup_controller_page_none_selected": "Nessuna selezione", "backup_controller_page_remainder": "Rimanenti", @@ -557,6 +553,10 @@ "backup_options_page_title": "Opzioni di Backup", "backup_setting_subtitle": "Gestisci le impostazioni di upload in primo piano e in background", "backward": "Indietro", + "biometric_auth_enabled": "Autenticazione biometrica attivata", + "biometric_locked_out": "Sei stato bloccato dall'autenticazione biometrica", + "biometric_no_options": "Nessuna opzione biometrica disponibile", + "biometric_not_available": "L'autenticazione biometrica non è disponibile su questo dispositivo", "birthdate_saved": "Data di nascita salvata con successo", "birthdate_set_description": "La data di nascita è usata per calcolare l'età di questa persona al momento dello scatto della foto.", "blurred_background": "Sfondo sfocato", @@ -567,21 +567,17 @@ "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 ({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 ({count})", - "cache_settings_image_cache_size": "Dimensione cache delle immagini ({count} elementi)", "cache_settings_statistics_album": "Anteprime librerie", - "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 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", @@ -594,7 +590,10 @@ "cannot_merge_people": "Impossibile unire le persone", "cannot_undo_this_action": "Non puoi annullare questa azione!", "cannot_update_the_description": "Impossibile aggiornare la descrizione", + "cast": "Trasmetti", + "cast_description": "Configura le destinazioni di trasmissione disponibili", "change_date": "Modifica data", + "change_description": "Modifica descrizione", "change_display_order": "Cambia l'ordine di visualizzazione", "change_expiration_time": "Modifica tempo di scadenza", "change_location": "Modifica posizione", @@ -610,7 +609,6 @@ "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_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.", @@ -622,6 +620,9 @@ "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": "Inserire la password", + "client_cert_import": "Importa", "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", @@ -647,10 +648,13 @@ "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", + "confirm_tag_face": "Vuoi taggare questo volto come {name}?", + "confirm_tag_face_unnamed": "Vuoi taggare questo volto?", + "connected_device": "Dispositivo connesso", + "connected_to": "Connesso", "contain": "Adatta alla finestra", "context": "Contesto", "continue": "Continua", - "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", @@ -699,6 +703,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Scuro", + "darkTheme": "Attiva/Disattiva tema scuro", "date_after": "Data dopo", "date_and_time": "Data e ora", "date_before": "Data prima", @@ -744,7 +749,9 @@ "direction": "Direzione", "disabled": "Disabilitato", "disallow_edits": "Blocca modifiche", + "discord": "Discord", "discover": "Scopri", + "discovered_devices": "Dispositivi trovati", "dismiss_all_errors": "Ignora tutti gli errori", "dismiss_error": "Ignora errore", "display_options": "Impostazioni visualizzazione", @@ -783,6 +790,8 @@ "edit_avatar": "Modifica avatar", "edit_date": "Modifica data", "edit_date_and_time": "Modifica data e ora", + "edit_description": "Modifica la descrizione", + "edit_description_prompt": "Selezionare una nuova descrizione:", "edit_exclusion_pattern": "Modifica pattern di esclusione", "edit_faces": "Modifica volti", "edit_import_path": "Modifica percorso di importazione", @@ -797,24 +806,30 @@ "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", "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", + "enable_biometric_auth_description": "Inserire il codice PIN per abilitare l'autenticazione biometrica", "enabled": "Abilitato", "end_date": "Data Fine", "enqueued": "Accodato", "enter_wifi_name": "Inserisci il nome della rete Wi-Fi", + "enter_your_pin_code": "Inserisci il tuo codice PIN", + "enter_your_pin_code_subtitle": "Inserire il codice PIN per accedere alla cartella protetta", "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": "Errore: {error}", + "error_tag_face_bounding_box": "Errore durante il tag del volto - impossibile ricavare le coordinate del riquadro", "error_title": "Errore - Qualcosa è andato storto", "errors": { "cannot_navigate_next_asset": "Impossibile passare alla risorsa successiva", @@ -827,7 +842,6 @@ "cant_get_number_of_comments": "Impossibile ottenere il numero di commenti", "cant_search_people": "Impossibile cercare persone", "cant_search_places": "Impossibile cercare luoghi", - "cleared_jobs": "Eliminati i processi per: {job}", "error_adding_assets_to_album": "Errore aggiungendo le risorse all'album", "error_adding_users_to_album": "Errore aggiungendo gli utenti all'album", "error_deleting_shared_user": "Errore durante la cancellazione dell'utente condiviso", @@ -836,7 +850,6 @@ "error_removing_assets_from_album": "Errore rimuovendo le risorse dall'album, controlla la console per ulteriori dettagli", "error_selecting_all_assets": "Errore selezionando tutte le risorse", "exclusion_pattern_already_exists": "Questo pattern di esclusione è già presente.", - "failed_job_command": "Il comando {command} è fallito per il processo: {job}", "failed_to_create_album": "Creazione dell'album non riuscita", "failed_to_create_shared_link": "Creazione del link condivisibile non riuscita", "failed_to_edit_shared_link": "Errore durante la modifica del link condivisibile", @@ -855,7 +868,6 @@ "paths_validation_failed": "{paths, plural, one {# percorso} other {# percorsi}} hanno fallito la validazione", "profile_picture_transparent_pixels": "Le foto profilo non possono avere pixel trasparenti. Riprova ingrandendo e/o muovendo l'immagine.", "quota_higher_than_disk_size": "Hai impostato un limite piÚ alto della dimensione del disco", - "repair_unable_to_check_items": "Impossibile verificare {count, select, one {elemento} other {elementi}}", "unable_to_add_album_users": "Impossibile aggiungere utenti all'album", "unable_to_add_assets_to_shared_link": "Impossibile aggiungere gli assets al link condiviso", "unable_to_add_comment": "Impossibile aggiungere commento", @@ -867,13 +879,13 @@ "unable_to_archive_unarchive": "Impossible {archived, select, true {archiviare} other {rimuovere dall'archivio}}", "unable_to_change_album_user_role": "Impossibile modificare il ruolo dell'utente nell'album", "unable_to_change_date": "Impossibile modificare la data", + "unable_to_change_description": "Impossibile modificare la descrizione", "unable_to_change_favorite": "Errore durante il cambio dello stato preferito dell'asset", "unable_to_change_location": "Impossibile modificare posizione", "unable_to_change_password": "Impossibile modificare password", "unable_to_change_visibility": "Errore durante la modifica della visibilità per {count, plural, one {# persona} other {# persone}}", "unable_to_complete_oauth_login": "Errore durante l'accesso tramite OAuth", "unable_to_connect": "Impossibile connettersi", - "unable_to_connect_to_server": "Impossibile connettersi al server", "unable_to_copy_to_clipboard": "Impossibile copiare negli appunti, assicurati di aver aperto la pagina in https", "unable_to_create_admin_account": "Impossibile creare un account admin", "unable_to_create_api_key": "Impossibile creare una nuova chiave API", @@ -897,10 +909,6 @@ "unable_to_hide_person": "Impossibile nascondere persona", "unable_to_link_motion_video": "Impossibile collegare video in movimento", "unable_to_link_oauth_account": "Impossibile collegare l'account OAuth", - "unable_to_load_album": "Impossibile caricare l'album", - "unable_to_load_asset_activity": "Impossibile caricare l'attività dell'asset", - "unable_to_load_items": "Impossibile caricare gli elementi", - "unable_to_load_liked_status": "Impossibile caricare lo stato dei preferiti", "unable_to_log_out_all_devices": "Impossibile eseguire il logout da tutti i dispositivi", "unable_to_log_out_device": "Impossibile eseguire il logout dal dispositivo", "unable_to_login_with_oauth": "Impossibile effettuare l'accesso tramite OAuth", @@ -911,11 +919,9 @@ "unable_to_remove_album_users": "Impossibile rimuovere gli utenti dall'album", "unable_to_remove_api_key": "Impossibile rimuovere la chiave API", "unable_to_remove_assets_from_shared_link": "Errore durante la rimozione degli assets da un link condiviso", - "unable_to_remove_deleted_assets": "Impossibile rimuovere i file offline", "unable_to_remove_library": "Impossibile rimuovere libreria", "unable_to_remove_partner": "Impossibile rimuovere compagno", "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", @@ -945,12 +951,12 @@ "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": "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}", @@ -974,6 +980,7 @@ "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": "Fallito", + "failed_to_authenticate": "Autenticazione non riuscita", "failed_to_load_assets": "Impossibile caricare gli asset", "failed_to_load_folder": "Impossibile caricare la cartella", "favorite": "Preferito", @@ -997,6 +1004,8 @@ "folders": "Cartelle", "folders_feature_description": "Navigare la visualizzazione a cartelle per le foto e i video sul file system", "forward": "Avanti", + "gcast_enabled": "Google Cast Abilitato", + "gcast_enabled_description": "Questa funzione carica risorse esterne da Google per poter funzionare.", "general": "Generale", "get_help": "Chiedi Aiuto", "get_wifiname_error": "Non sono riuscito a recuperare il nome della rete Wi-Fi. Accertati di aver concesso i permessi necessari e di essere connesso ad una rete Wi-Fi", @@ -1039,9 +1048,13 @@ "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_locked_error_local": "Non puoi spostare la risorsa locale nella cartella privata, azione ignorata", + "home_page_locked_error_partner": "Non puoi spostare le risorse del partner nella cartella privata, azione ignorata", "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", + "id": "ID", "ignore_icloud_photos": "Ignora foto iCloud", "ignore_icloud_photos_description": "Le foto che sono memorizzate su iCloud non verranno caricate sul server Immich", "image": "Immagine", @@ -1070,6 +1083,7 @@ "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}}", @@ -1080,6 +1094,12 @@ "invalid_date_format": "Formato data invalido", "invite_people": "Invita Persone", "invite_to_album": "Invita nell'album", + "ios_debug_info_fetch_ran_at": "Recupero dati eseguito {dateTime}", + "ios_debug_info_last_sync_at": "Ultima sincronizzazione {dateTime}", + "ios_debug_info_no_processes_queued": "Nessun processo in background in coda", + "ios_debug_info_no_sync_yet": "Nessun processo di sincronizzazione in background è stato ancora avviato", + "ios_debug_info_processes_queued": "{count, plural, one {{count} processo in background messo in coda} other {{count} processi in background messi in coda}}", + "ios_debug_info_processing_ran_at": "Processo eseguito {dateTime}", "items_count": "{count, plural, one {# elemento} other {# elementi}}", "jobs": "Processi", "keep": "Mantieni", @@ -1088,6 +1108,9 @@ "kept_this_deleted_others": "Mantenuto questo asset ed eliminati {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Scorciatoie da tastiera", "language": "Lingua", + "language_no_results_subtitle": "Prova a cambiare i tuoi termini di ricerca", + "language_no_results_title": "Linguaggi non trovati", + "language_search_hint": "Cerca linguaggi...", "language_setting_description": "Seleziona la tua lingua predefinita", "last_seen": "Ultimo accesso", "latest_version": "Ultima Versione", @@ -1113,6 +1136,7 @@ "list": "Lista", "loading": "Caricamento", "loading_search_results_failed": "Impossibile caricare i risultati della ricerca", + "local_asset_cast_failed": "Impossibile trasmettere una risorsa che non è caricata sul server", "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", @@ -1122,10 +1146,14 @@ "location_picker_latitude_hint": "Inserisci la tua latitudine qui", "location_picker_longitude_error": "Inserisci una longitudine valida", "location_picker_longitude_hint": "Inserisci la longitudine qui", + "lock": "Rendi privato", + "locked_folder": "Cartella Privata", "log_out": "Esci", "log_out_all_devices": "Disconnetti tutti i dispositivi", + "logged_in_as": "Effettuato l'accesso come {user}", "logged_out_all_devices": "Disconnesso da tutti i dispositivi", "logged_out_device": "Disconnesso dal dispositivo", + "login": "Accesso", "login_disabled": "L'accesso è stato disattivato", "login_form_api_exception": "API error, per favore ricontrolli URL del server e riprovi.", "login_form_back_button_text": "Indietro", @@ -1134,13 +1162,14 @@ "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_invalid_url": "URL non valido", "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.", @@ -1153,7 +1182,7 @@ "look": "Guarda", "loop_videos": "Riproduci video in loop", "loop_videos_description": "Abilita per riprodurre automaticamente un video in loop nella vista dettagli.", - "main_branch_warning": "Stai usando una versione di sviluppo. Consigliamo vivamente di utilizzare una versione di rilascio!", + "main_branch_warning": "Stai utilizzando una versione di sviluppo. Ti consigliamo vivamente di utilizzare una versione di rilascio!", "main_menu": "Menu Principale", "make": "Produttore", "manage_shared_links": "Gestisci link condivisi", @@ -1201,6 +1230,7 @@ "memories_swipe_to_close": "Scorri sopra per chiudere", "memory": "Memoria", "memory_lane_title": "Sentiero dei Ricordi {title}", + "menu": "MenÚ", "merge": "Unisci", "merge_people": "Unisci persone", "merge_people_limit": "Puoi unire al massimo 5 volti alla volta", @@ -1212,7 +1242,12 @@ "missing": "Mancanti", "model": "Modello", "month": "Mese", + "monthly_title_text_date_format": "MMMM y", "more": "Di piÚ", + "move": "Sposta", + "move_off_locked_folder": "Sposta al di fuori della cartella privata", + "move_to_locked_folder": "Sposta nella cartella privata", + "move_to_locked_folder_confirmation": "Queste foto e video verranno rimossi da tutti gli album, e saranno visibili solo dalla cartella privata", "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", @@ -1230,22 +1265,26 @@ "new_password": "Nuova password", "new_person": "Nuova persona", "new_pin_code": "Nuovo codice PIN", + "new_pin_code_subtitle": "Questa è la prima volta che accedi alla cartella privata. Crea un codice PIN per accedere in modo sicuro a questa pagina", "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.", "no_archived_assets_message": "Archivia foto e video per nasconderli dalla galleria di foto", "no_assets_message": "CLICCA PER CARICARE LA TUA PRIMA FOTO", "no_assets_to_show": "Nessuna risorsa da mostrare", + "no_cast_devices_found": "Nessun dispositivo di trasmissione trovato", "no_duplicates_found": "Nessun duplicato trovato.", "no_exif_info_available": "Nessuna informazione exif disponibile", "no_explore_results_message": "Carica piÚ foto per esplorare la tua collezione.", "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_locked_photos_message": "Le foto e i video nella cartella privata sono nascosti e non vengono visualizzati mentre navighi o cerchi nella tua libreria.", "no_name": "Nessun nome", "no_notifications": "Nessuna notifica", "no_people_found": "Nessuna persona trovata", @@ -1257,6 +1296,7 @@ "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", + "nothing_here_yet": "Ancora nulla qui", "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", @@ -1264,16 +1304,20 @@ "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_paths": "Percorsi offline", - "offline_paths_description": "Questi risultati potrebbero essere causati dall'eliminazione manuale di file che non fanno parte di una libreria esterna.", + "offline": "Offline", + "ok": "Ok", "oldest_first": "Prima vecchi", "on_this_device": "Su questo dispositivo", "onboarding": "Inserimento", - "onboarding_privacy_description": "Le seguenti funzioni (opzionali) fanno uso di servizi esterni, e possono essere disabilitate in qualsiasi momento nelle impostazioni di amministrazione.", + "onboarding_locale_description": "Seleziona il tuo linguaggio di preferenza. Puoi cambiarlo successivamente nelle impostazioni.", + "onboarding_privacy_description": "Le seguenti funzioni (opzionali) fanno uso di servizi esterni, e possono essere disabilitate in qualsiasi momento nelle impostazioni.", + "onboarding_server_welcome_description": "Configuriamo la tua istanza con alcune impostazioni standard.", "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_user_welcome_description": "Iniziamo!", "onboarding_welcome_user": "Benvenuto, {user}", + "online": "Online", "only_favorites": "Solo preferiti", "open": "Apri", "open_in_map_view": "Apri nella visualizzazione mappa", @@ -1288,6 +1332,7 @@ "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", @@ -1301,6 +1346,7 @@ "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", @@ -1326,6 +1372,8 @@ "permanently_delete_assets_prompt": "Sei sicuro di voler cancellare definitivamente {count, plural, one {questo asset?} other {# assets?}} Questa operazione {count, plural, one {lo cancellerà dal suo} other {li cancellerà dai loro}} album.", "permanently_deleted_asset": "Asset eliminato definitivamente", "permanently_deleted_assets_count": "Cancellati {count, plural, one {# asset} other {# assets}} definitivamente", + "permission": "Autorizzazione", + "permission_empty": "La tua autorizzazione non puÃ˛ essere vuota", "permission_onboarding_back": "Indietro", "permission_onboarding_continue_anyway": "Continua lo stesso", "permission_onboarding_get_started": "Inizia", @@ -1346,6 +1394,7 @@ "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", + "pin_verification": "Verifica del codice PIN", "place": "Posizione", "places": "Luoghi", "places_count": "{count, plural, one {{count, number} Luogo} other {{count, number} Places}}", @@ -1353,6 +1402,7 @@ "play_memories": "Avvia ricordi", "play_motion_photo": "Avvia Foto in movimento", "play_or_pause_video": "Avvia o metti in pausa il video", + "please_auth_to_access": "Si prega di autenticarsi per accedere", "port": "Porta", "preferences_settings_subtitle": "Gestisci le preferenze dell'app", "preferences_settings_title": "Preferenze", @@ -1360,13 +1410,18 @@ "preview": "Anteprima", "previous": "Precedente", "previous_memory": "Ricordo precedente", - "previous_or_next_photo": "Precedente o prossima foto", + "previous_or_next_day": "Giorno avanti/indietro", + "previous_or_next_month": "Mese successivo/precedente", + "previous_or_next_photo": "Foto successiva/precedente", + "previous_or_next_year": "Anno successivo/precedente", "primary": "Primario", + "privacy": "Privacy", "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}", @@ -1393,8 +1448,9 @@ "purchase_lifetime_description": "Acquisto a vita", "purchase_option_title": "OPZIONI DI ACQUISTO", "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_info_2": "PoichÊ ci impegniamo a non aggiungere paywall, questo acquisto non ti garantirà funzionalità aggiuntive in Immich. Contiamo su utenti come te per supportare 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?", @@ -1402,6 +1458,7 @@ "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", @@ -1439,9 +1496,12 @@ "remove_deleted_assets": "Rimuovi file offline", "remove_from_album": "Rimuovere dall'album", "remove_from_favorites": "Rimuovi dai preferiti", + "remove_from_locked_folder": "Rimuovi dalla cartella privata", + "remove_from_locked_folder_confirmation": "Sei sicuro di voler spostare queste foto e questi video dalla cartella privata? Diventeranno visibili nella vostra libreria.", "remove_from_shared_link": "Rimuovi dal link condiviso", "remove_memory": "Rimuovi ricordo", "remove_photo_from_memory": "Rimuovi foto da questo ricordo", + "remove_tag": "Rimuovi tag", "remove_url": "Rimuovi URL", "remove_user": "Rimuovi utente", "removed_api_key": "Rimossa chiave API: {name}", @@ -1455,6 +1515,7 @@ "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", @@ -1473,6 +1534,7 @@ "retry_upload": "Riprova caricamento", "review_duplicates": "Esamina duplicati", "role": "Ruolo", + "role_editor": "Editor", "role_viewer": "Visualizzatore", "save": "Salva", "save_to_gallery": "Salva in galleria", @@ -1498,12 +1560,15 @@ "search_country": "Cerca paese...", "search_filter_apply": "Applica filtro", "search_filter_camera_title": "Seleziona il tipo di camera", + "search_filter_date": "Data", + "search_filter_date_interval": "{start} a {end}", "search_filter_date_title": "Scegli un range di date", "search_filter_display_option_not_in_album": "Non nell'album", "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": "Tipo di media", "search_filter_media_type_title": "Seleziona il tipo di media", "search_filter_people_title": "Seleziona persone", "search_for": "Cerca per", @@ -1543,6 +1608,7 @@ "select_album_cover": "Seleziona copertina album", "select_all": "Seleziona tutto", "select_all_duplicates": "Seleziona tutti i duplicati", + "select_all_in": "Seleziona tutto in {group}", "select_avatar_color": "Seleziona colore avatar", "select_face": "Seleziona volto", "select_featured_photo": "Seleziona foto in evidenza", @@ -1560,6 +1626,10 @@ "send_welcome_email": "Invia email di benvenuto", "server_endpoint": "Server endpoint", "server_info_box_app_version": "Versione App", + "server_info_box_server_url": "URL del Server", + "server_offline": "Server Offline", + "server_online": "Server Online", + "server_privacy": "Privacy del Server", "server_stats": "Statistiche Server", "server_version": "Versione Server", "set": "Imposta", @@ -1569,6 +1639,7 @@ "set_date_of_birth": "Imposta data di nascita", "set_profile_picture": "Imposta foto profilo", "set_slideshow_to_fullscreen": "Imposta presentazione a schermo intero", + "set_stack_primary_asset": "Imposta come risorsa primaria", "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", @@ -1577,7 +1648,6 @@ "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: {duration}", "setting_notifications_notify_hours": "{count} ore", "setting_notifications_notify_immediately": "immediatamente", @@ -1589,6 +1659,7 @@ "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", @@ -1599,6 +1670,7 @@ "share_add_photos": "Aggiungi foto", "share_assets_selected": "{count} selezionati", "share_dialog_preparing": "Preparoâ€Ļ", + "share_link": "Link di condivisione", "shared": "Condivisi", "shared_album_activities_input_disable": "I commenti sono disabilitati", "shared_album_activity_remove_content": "Vuoi eliminare questa attività?", @@ -1614,6 +1686,7 @@ "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: {link}\nPassword: {password}", "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", @@ -1637,6 +1710,7 @@ "shared_link_expires_second": "Scade tra {count} secondo", "shared_link_expires_seconds": "Scade tra {count} secondi", "shared_link_individual_shared": "Condiviso individualmente", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gestisci link condivisi", "shared_link_options": "Opzioni link condiviso", "shared_links": "Link condivisi", @@ -1703,6 +1777,7 @@ "start_date": "Data di inizio", "state": "Provincia", "status": "Stato", + "stop_casting": "Interrompi trasmissione", "stop_motion_photo": "Ferma Foto in Movimento", "stop_photo_sharing": "Interrompere la condivisione delle tue foto?", "stop_photo_sharing_description": "{partner} non potrà piÚ accedere alle tue foto.", @@ -1722,6 +1797,7 @@ "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": "Tag", "tag_assets": "Tagga risorse", "tag_created": "Tag creata: {tag}", "tag_feature_description": "Navigazione foto e video raggruppati per argomenti tag logici", @@ -1755,10 +1831,10 @@ "to_archive": "Archivio", "to_change_password": "Modifica password", "to_favorite": "Preferito", + "to_login": "Accedi", "to_parent": "Sali di un livello", "to_trash": "Cancella", "toggle_settings": "Attiva/disattiva impostazioni", - "toggle_theme": "Abilita tema scuro", "total": "Totale", "total_usage": "Utilizzo totale", "trash": "Cestino", @@ -1780,6 +1856,7 @@ "unable_to_setup_pin_code": "Impossibile configurare il codice PIN", "unarchive": "Annulla l'archiviazione", "unarchived_count": "{count, plural, other {Non archiviati #}}", + "undo": "Annulla", "unfavorite": "Rimuovi preferito", "unhide_person": "Mostra persona", "unknown": "Sconosciuto", @@ -1796,10 +1873,9 @@ "unsaved_change": "Modifica non salvata", "unselect_all": "Deseleziona tutto", "unselect_all_duplicates": "Deseleziona tutti i duplicati", + "unselect_all_in": "Deseleziona tutto in {group}", "unstack": "Rimuovi dal gruppo", "unstacked_assets_count": "{count, plural, one {Separato # asset} other {Separati # asset}}", - "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", @@ -1816,7 +1892,9 @@ "upload_success": "Caricamento completato con successo, aggiorna la pagina per vedere i nuovi asset caricati.", "upload_to_immich": "Carica su Immich ({count})", "uploading": "Caricamento", + "url": "URL", "usage": "Utilizzo", + "use_biometric": "Usa biometrica", "use_current_connection": "usa la connessione attuale", "use_custom_date_range": "Altrimenti utilizza un intervallo date personalizzato", "user": "Utente", @@ -1825,6 +1903,7 @@ "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_privacy": "Privacy dell'utente", "user_purchase_settings": "Acquisto", "user_purchase_settings_description": "Gestisci il tuo acquisto", "user_role_set": "Imposta {user} come {role}", @@ -1840,13 +1919,9 @@ "version": "Versione", "version_announcement_closing": "Il tuo amico, Alex", "version_announcement_message": "Ehilà! È stata rilasciata una nuova versione di Immich. Leggi le release notes e assicurati che i tuoi file di configurazione siano aggiornati per evitare problemi e incongruenze, soprattutto se utilizzi WatchTower o altri strumenti per aggiornare Immich in automatico.", - "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_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", @@ -1863,6 +1938,7 @@ "view_previous_asset": "Visualizza risorsa precedente", "view_qr_code": "Visualizza Codice QR", "view_stack": "Visualizza Raggruppamento", + "view_user": "Visualizza Utente", "viewer_remove_from_stack": "Rimuovi dalla pila", "viewer_stack_use_as_main_asset": "Usa come risorsa principale", "viewer_unstack": "Rimuovi dal gruppo", @@ -1873,6 +1949,7 @@ "welcome": "Benvenuto", "welcome_to_immich": "Benvenuto in Immich", "wifi_name": "Nome rete Wi-Fi", + "wrong_pin_code": "Codice PIN errato", "year": "Anno", "years_ago": "{years, plural, one {# anno} other {# anni}} fa", "yes": "Si", diff --git a/i18n/ja.json b/i18n/ja.json index 0bccd9f867..1649f978d8 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -22,11 +22,11 @@ "add_partner": "パãƒŧトナãƒŧをčŋŊ加", "add_path": "パ゚をčŋŊ加", "add_photos": "å†™įœŸã‚’čŋŊ加", + "add_tag": "ã‚ŋグをčŋŊ加", "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": "ã‚ĸãƒŧã‚ĢイブãĢしぞした", @@ -34,6 +34,7 @@ "added_to_favorites_count": "{count, number} 枚ぎį”ģ像をお気ãĢå…ĨりãĢčŋŊ加しぞした", "admin": { "add_exclusion_pattern_description": "除外パã‚ŋãƒŧãƒŗã‚’čŋŊ加しぞす。ワイãƒĢドã‚Ģãƒŧド「*」「**」「?」をäŊŋį”¨ã§ããžã™ã€‚ã™ãšãĻãŽãƒ‡ã‚ŖãƒŦクトãƒĒで「Raw」と名前がäģ˜ã„ãŸãƒ•ã‚Ąã‚¤ãƒĢã‚’į„ĄčĻ–ã™ã‚‹ãĢは、「**/Raw/**」をäŊŋį”¨ã—ãžã™ã€‚ãžãŸã€ã€Œ.tif」でįĩ‚ã‚ã‚‹ãƒ•ã‚Ąã‚¤ãƒĢをすずãĻį„ĄčĻ–ã™ã‚‹ãĢは、「**/*.tif」をäŊŋį”¨ã—ãžã™ã€‚ã•ã‚‰ãĢ、įĩļå¯žãƒ‘ã‚šã‚’į„ĄčĻ–ã™ã‚‹ãĢは「/path/to/ignore/**」をäŊŋį”¨ã—ãžã™ã€‚", + "admin_user": "įŽĄį†ãƒĻãƒŧã‚ļãƒŧ", "asset_offline_description": "こぎ外部ナイブナãƒĒぎã‚ĸã‚ģãƒƒãƒˆã¯ãƒ‡ã‚Ŗã‚šã‚¯ä¸ŠãĢčĻ‹ã¤ã‹ã‚‰ãĒくãĒãŖãĻã‚´ãƒŸįŽąãĢį§ģå‹•ã•ã‚Œãžã—ãŸã€‚ãƒ•ã‚Ąã‚¤ãƒĢがナイブナãƒĒぎ中でį§ģ動された場合はã‚ŋã‚¤ãƒ ãƒŠã‚¤ãƒŗã§æ–°ã—ã„å¯žåŋœã™ã‚‹ã‚ĸã‚ģットをįĸēčĒã—ãĻください。こぎã‚ĸã‚ģットを垊元するãĢはäģĨä¸‹ãŽãƒ•ã‚Ąã‚¤ãƒĢパ゚がImmichからã‚ĸクã‚ģ゚できるかįĸēčĒã—ãĻナイブナãƒĒã‚’ã‚šã‚­ãƒŖãƒŗã—ãĻください。", "authentication_settings": "čĒč¨ŧč¨­åŽš", "authentication_settings_description": "čĒč¨ŧč¨­åŽšãŽįŽĄį†īŧˆãƒ‘゚ワãƒŧド、OAuth、そぎäģ–īŧ‰", @@ -45,12 +46,10 @@ "backup_keep_last_amount": "過åŽģぎバックã‚ĸップぎäŋæŒæ•°", "backup_settings": "デãƒŧã‚ŋベãƒŧ゚バックã‚ĸップäŊœæˆãŽč¨­åޚ", "backup_settings_description": "デãƒŧã‚ŋベãƒŧ゚ぎバックã‚ĸップäŊœæˆč¨­åŽšãŽįŽĄį† (こぎジョブはãƒĸニã‚ŋãƒĒãƒŗã‚°ã•ã‚Œãžã›ã‚“ã—ã€å¤ąæ•—ãŒį™ēį”Ÿã—ãĻもあãĒたãĢ通įŸĨãŒčĄŒãã“ã¨ã¯ã‚ã‚Šãžã›ã‚“)", - "check_all": "すずãĻを選択", - "cleanup": "クãƒĒãƒŧãƒŗã‚ĸップ", "cleared_jobs": "{job}ぎジョブをクãƒĒã‚ĸしぞした", "config_set_by_file": "č¨­åŽšã¯įžåœ¨ Config 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} ぎパ゚ワãƒŧドをãƒĒã‚ģットしぞすかīŧŸ", @@ -62,14 +61,12 @@ "disable_login": "ãƒ­ã‚°ã‚¤ãƒŗã‚’į„ĄåŠšãĢする", "duplicate_detection_job_description": "抟æĸ°å­Ļįŋ’ã‚’į”¨ã„ãĻ類äŧŧį”ģ像ぎ検å‡ēã‚’čĄŒã„ãžã™ã€‚īŧˆã‚šãƒžãƒŧトã‚ĩãƒŧチãĢ䞝存īŧ‰", "exclusion_pattern_description": "除外パã‚ŋãƒŧãƒŗã‚’äŊŋį”¨ã™ã‚‹ã¨ã€ãƒŠã‚¤ãƒ–ãƒŠãƒĒã‚’ã‚šã‚­ãƒŖãƒŗã™ã‚‹éš›ãĢãƒ•ã‚Ąã‚¤ãƒĢやフりãƒĢãƒ€ã‚’į„ĄčĻ–ã™ã‚‹ã“ã¨ãŒã§ããžã™ã€‚RAWãƒ•ã‚Ąã‚¤ãƒĢãĒãŠã€ã‚¤ãƒŗãƒãƒŧトしたくãĒã„ãƒ•ã‚Ąã‚¤ãƒĢをåĢむフりãƒĢダがある場合ãĢäžŋ刊です。", - "external_library_created_at": "外部ナイブナãƒĒīŧˆäŊœæˆæ—Ĩīŧš{date}īŧ‰", "external_library_management": "外部ナイブナãƒĒįŽĄį†", "face_detection": "éĄ”æ¤œå‡ē", "face_detection_description": "抟æĸ°å­Ļįŋ’ã‚’äŊŋį”¨ã—ãĻã‚ĸã‚ģãƒƒãƒˆå†…ãŽéĄ”ã‚’æ¤œå‡ēしぞす。動į”ģぎ場合は、ã‚ĩムネイãƒĢぎãŋãŒå¯žčąĄã¨ãĒりぞす。\"すずãĻ\" はすずãĻぎã‚ĸã‚ģットをīŧˆå†īŧ‰å‡Ļį†ã—ãžã™ã€‚ \"æŦ čŊ\" はぞだå‡Ļį†ã•ã‚ŒãĻいãĒいã‚ĸã‚ģットをキãƒĨãƒŧãĢå…Ĩã‚Œãžã™ã€‚éĄ”æ¤œå‡ēぎ厌äē†åžŒã€æ¤œå‡ēã•ã‚ŒãŸéĄ”ã¯éĄ”čĒč­˜ãŽã‚­ãƒĨãƒŧへå…Ĩれられ、æ—ĸ存ぞたは新čĻãŽäēēį‰ŠãĢグãƒĢãƒŧプ化されぞす。", "facial_recognition_job_description": "検å‡ēã•ã‚ŒãŸéĄ”ã‚’äēēį‰ŠãĢグãƒĢãƒŧãƒ—åŒ–ã—ãžã™ã€‚ã“ãŽã‚šãƒ†ãƒƒãƒ—ã¯éĄ”æ¤œå‡ēが厌äē†ã—た垌ãĢåŽŸčĄŒã•ã‚Œãžã™ã€‚ \"すずãĻ\" はすずãĻãŽéĄ”ã‚’īŧˆå†īŧ‰ã‚¯ãƒŠã‚šã‚ŋãƒĒãƒŗã‚°ã—ã€ \"æŦ čŊ\" はäēēį‰ŠãŒå‰˛ã‚ŠåŊ“ãĻられãĻいãĒã„éĄ”ã‚’ã‚­ãƒĨãƒŧãĢå…Ĩれぞす。", "failed_job_command": "ジョブ {job}ãŽã‚ŗãƒžãƒŗãƒ‰ {command}ãŒå¤ąæ•—ã—ãžã—ãŸ", "force_delete_user_warning": "č­Ļ告īŧšã“ぎ操äŊœã‚’čĄŒã†ã¨ã€ãƒĻãƒŧã‚ļãƒŧとすずãĻぎã‚ĸã‚ģãƒƒãƒˆãŒį›´ãĄãĢ削除されぞす。これは元ãĢæˆģã›ãšã€ãƒ•ã‚Ąã‚¤ãƒĢも垊元できぞせん。", - "forcing_refresh_library_files": "すずãĻぎナイブナãƒĒãƒ•ã‚Ąã‚¤ãƒĢをåŧˇåˆļ更新", "image_format": "フりãƒŧマット", "image_format_description": "WebPはJPEGã‚ˆã‚Šã‚‚ãƒ•ã‚Ąã‚¤ãƒĢã‚ĩイã‚ēãŒå°ã•ã„ã§ã™ãŒã€ã‚¨ãƒŗã‚ŗãƒŧドãĢ時間がかかりぞす。", "image_fullsize_description": "į”ģåƒã‚’æ‹Ąå¤§ã™ã‚‹æ™‚ãĢäŊŋã‚ã‚Œã‚‹ãƒĄã‚ŋデãƒŧã‚ŋを取り除いた原寸大į”ģ像", @@ -174,7 +171,7 @@ "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_from_address_description": "送äŋĄãƒĄãƒŧãƒĢã‚ĸドãƒŦã‚šã‚’č¨­åŽšã—ãžã™īŧˆäž‹īŧš \"Immich Photo Server \" īŧ‰. åŋ…ãšãƒĄãƒŧãƒĢ送äŋĄãŒč¨ąå¯ã•れãĻいるã‚ĸドãƒŦ゚をäŊŋį”¨ã—ãĻください.", "notification_email_host_description": "送äŋĄãƒĄãƒŧãƒĢã‚ĩãƒŧバãƒŧã‚’č¨­åŽšã—ãžã™īŧˆäž‹īŧšsmtp.immich.appīŧ‰", "notification_email_ignore_certificate_errors": "č¨ŧ明書エナãƒŧã‚’į„ĄčĻ–", "notification_email_ignore_certificate_errors_description": "TLSč¨ŧ明書ぎ検č¨ŧエナãƒŧã‚’į„ĄčĻ–ã—ãžã™īŧˆéžæŽ¨åĨ¨īŧ‰", @@ -198,7 +195,7 @@ "oauth_enable_description": "OAuthã§ãƒ­ã‚°ã‚¤ãƒŗ", "oauth_mobile_redirect_uri": "ãƒĸバイãƒĢᔍãƒĒダイãƒŦクトURI", "oauth_mobile_redirect_uri_override": "ãƒĸバイãƒĢᔍãƒĒダイãƒŦクトURIīŧˆä¸Šæ›¸ãīŧ‰", - "oauth_mobile_redirect_uri_override_description": "'{callback}'ãĒお、ãƒĸバイãƒĢURIがOAuthプロバイダãƒŧãĢã‚ˆãŖãĻč¨ąå¯ã•ã‚ŒãĻいãĒい場合ãĢ有劚ãĢしãĻください", + "oauth_mobile_redirect_uri_override_description": "''{callback}''ãĒお、ãƒĸバイãƒĢURIがOAuthプロバイダãƒŧãĢã‚ˆãŖãĻč¨ąå¯ã•ã‚ŒãĻいãĒい場合ãĢ有劚ãĢしãĻください", "oauth_settings": "OAuth", "oauth_settings_description": "OAuthãƒ­ã‚°ã‚¤ãƒŗč¨­åŽšã‚’įŽĄį†ã—ãžã™", "oauth_settings_more_details": "こぎ抟čƒŊãŽčŠŗį´°ãĢついãĻは、ドキãƒĨãƒĄãƒŗãƒˆã‚’å‚į…§ã—ãĻください。", @@ -207,11 +204,9 @@ "oauth_storage_quota_claim": "゚トãƒŦãƒŧジクりãƒŧã‚ŋ クãƒŦãƒŧム", "oauth_storage_quota_claim_description": "ãƒĻãƒŧã‚ļãƒŧぎ゚トãƒŦãƒŧジクりãƒŧã‚ŋをこぎクãƒŦãƒŧムぎ値ãĢč‡Ēå‹•įš„ãĢč¨­åŽšã—ãžã™ã€‚", "oauth_storage_quota_default": "デフりãƒĢトぎ゚トãƒŦãƒŧã‚¸å‰˛ã‚ŠåŊ“ãĻ(GiB)", - "oauth_storage_quota_default_description": "クãƒŦãƒŧムが提䞛されãĻいãĒい場合ãĢäŊŋį”¨ã•ã‚Œã‚‹ã‚¯ã‚Šãƒŧã‚ŋをGiB単äŊã§č¨­åŽšã—ãžã™īŧˆį„Ąåˆļ限ãĢする場合は0をå…Ĩ力しãĻくださいīŧ‰ã€‚", + "oauth_storage_quota_default_description": "クãƒŦãƒŧムが提䞛されãĻいãĒい場合ãĢäŊŋį”¨ã•ã‚Œã‚‹ã‚¯ã‚Šãƒŧã‚ŋをGiB単äŊã§č¨­åŽšã—ãžã™ã€‚", "oauth_timeout": "ãƒĒクエ゚トã‚ŋイムã‚ĸã‚Ļト", "oauth_timeout_description": "ãƒĒクエ゚トぎã‚ŋイムã‚ĸã‚Ļトぞでぎ時間(ms)", - "offline_paths": "ã‚Ēãƒ•ãƒŠã‚¤ãƒŗãŽãƒ‘ã‚š", - "offline_paths_description": "これらぎįĩæžœã¯ã€å¤–部ナイブナãƒĒãĢåąžã•ãĒã„ãƒ•ã‚Ąã‚¤ãƒĢを手動で削除したことãĢよる可čƒŊ性がありぞす。", "password_enable_description": "ãƒĄãƒŧãƒĢã‚ĸドãƒŦ゚とパ゚ワãƒŧãƒ‰ã§ãƒ­ã‚°ã‚¤ãƒŗ", "password_settings": "パ゚ワãƒŧド ãƒ­ã‚°ã‚¤ãƒŗ", "password_settings_description": "パ゚ワãƒŧド ãƒ­ã‚°ã‚¤ãƒŗč¨­åŽšã‚’įŽĄį†ã—ãžã™", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "すずãĻぎナイブナãƒĒを更新", "registration": "įŽĄį†č€…į™ģ錞", "registration_description": "あãĒãŸã¯ã‚ˇã‚šãƒ†ãƒ ãŽæœ€åˆãŽãƒĻãƒŧã‚ļãƒŧã§ã‚ã‚‹ãŸã‚ã€įŽĄį†č€…ã¨ã—ãĻå‰˛ã‚ŠåŊ“ãĻã‚‰ã‚Œã€įŽĄį†ã‚ŋ゚クを担åŊ“し、čŋŊ加ぎãƒĻãƒŧã‚ļãƒŧはあãĒたãĢã‚ˆãŖãĻäŊœæˆã•れぞす。", - "repair_all": "すずãĻをäŋŽåžŠ", - "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": "å‰å›žãŽč¨­åŽšå€¤ãĢæˆģす", @@ -252,7 +244,7 @@ "storage_template_migration_info": "゚トãƒŦãƒŧã‚¸ãƒ†ãƒŗãƒ—ãƒŦãƒŧトは全ãĻãŽæ‹Ąåŧĩ子を小文字ãĢå¤‰æ›ã—ãžã™ã€‚ãƒ†ãƒŗãƒ—ãƒŦãƒŧトぎ変更は新しいã‚ĸã‚ģットãĢぎãŋéŠį”¨ã•ã‚Œãžã™ã€‚ äģĨ前ãĢã‚ĸップロãƒŧドしたã‚ĸã‚ģットãĢãƒ†ãƒŗãƒ—ãƒŦãƒŧãƒˆã‚’éĄãŖãĻéŠį”¨ã™ã‚‹ãĢは、{job} ã‚’åŽŸčĄŒã—ãĻください。", "storage_template_migration_job": "゚トãƒŦãƒŧã‚¸ãƒ†ãƒŗãƒ—ãƒŦãƒŧトį§ģčĄŒã‚¸ãƒ§ãƒ–", "storage_template_more_details": "こぎ抟čƒŊãŽčŠŗį´°ãĢついãĻは、゚トãƒŦãƒŧã‚¸ãƒ†ãƒŗãƒ—ãƒŦãƒŧトとそぎåŊąéŸŋã‚’å‚į…§ã—ãĻください", - "storage_template_onboarding_description": "こぎ抟čƒŊを有劚ãĢすると、ãƒĻãƒŧã‚ļãƒŧåŽšįžŠãŽãƒ†ãƒŗãƒ—ãƒŦãƒŧトãĢåŸēãĨいãĻãƒ•ã‚Ąã‚¤ãƒĢがč‡Ēå‹•ã§æ•´į†ã•ã‚Œãžã™ã€‚ åŽ‰åŽšæ€§ãŽå•éĄŒãŽãŸã‚ã€ã“ãŽæŠŸčƒŊはデフりãƒĢトでã‚ĒフãĢãĒãŖãĻいぞす。 čŠŗį´°ãĢついãĻは、ドキãƒĨãƒĄãƒŗãƒˆã‚’å‚į…§ã—ãĻください。", + "storage_template_onboarding_description_v2": "ã“ãŽč¨­åŽšã‚’ã‚ĒãƒŗãĢすると、ãƒĻãƒŧã‚ļãƒŧãŽåŽšįžŠã—ãŸãƒ†ãƒŗãƒ—ãƒŦãƒŧトãĢåž“ãŖãĻč‡Ēå‹•ã§ãƒ•ã‚Ąã‚¤ãƒĢãŒæ•´į†ã•ã‚Œãžã™ã€‚čŠŗã—ã„æƒ…å ąã¯ãƒ‰ã‚­ãƒĨãƒĄãƒŗãƒ†ãƒŧã‚ˇãƒ§ãƒŗã§įĸēčĒã—ãĻください。", "storage_template_path_length": "ãŠãŠã‚ˆããŽãƒ‘ã‚šé•ˇãŽåˆļ限: {length, number}/{limit, number}", "storage_template_settings": "゚トãƒŦãƒŧジ ãƒ†ãƒŗãƒ—ãƒŦãƒŧト", "storage_template_settings_description": "ã‚ĸップロãƒŧドしたã‚ĸã‚ģットぎフりãƒĢãƒ€æ§‹é€ ã¨ãƒ•ã‚Ąã‚¤ãƒĢåã‚’įŽĄį†ã—ãžã™", @@ -264,16 +256,14 @@ "template_email_invite_album": "ã‚ĸãƒĢãƒãƒ æ‹›åž…ãŽãƒ†ãƒŗãƒ—ãƒŦãƒŧト", "template_email_preview": "プãƒŦビãƒĨãƒŧ", "template_email_settings": "ãƒĄãƒŧãƒĢãƒ†ãƒŗãƒ—ãƒŦãƒŧト", - "template_email_settings_description": "通įŸĨãŽãƒĄãƒŧãƒĢãƒ†ãƒŗãƒ—ãƒŦãƒŧトぎã‚Ģ゚ã‚ŋãƒ ã‚’įŽĄį†ã—ãžã™", "template_email_update_album": "ã‚ĸãƒĢãƒãƒ æ›´æ–°ãŽãƒ†ãƒŗãƒ—ãƒŦãƒŧト", "template_email_welcome": "ã‚ĻェãƒĢã‚Ģãƒ ãƒĄãƒŧãƒĢãŽãƒ†ãƒŗãƒ—ãƒŦãƒŧト", "template_settings": "通įŸĨãƒ†ãƒŗãƒ—ãƒŦãƒŧト", - "template_settings_description": "通įŸĨぎためぎã‚Ģ゚ã‚ŋãƒ ãƒ†ãƒŗãƒ—ãƒŦãƒŧãƒˆã‚’įŽĄį†ã—ãžã™ã€‚", + "template_settings_description": "通įŸĨぎためぎã‚Ģ゚ã‚ŋãƒ ãƒ†ãƒŗãƒ—ãƒŦãƒŧãƒˆã‚’įŽĄį†ã—ãžã™", "theme_custom_css_settings": "ã‚Ģ゚ã‚ŋムCSS", "theme_custom_css_settings_description": "CSS をäŊŋãŖãĻ Immich ぎデã‚ļã‚¤ãƒŗã‚’ã‚Ģ゚ã‚ŋマイã‚ēできぞす。", "theme_settings": "テãƒŧãƒžč¨­åŽš", "theme_settings_description": "Web ã‚¤ãƒŗã‚ŋãƒŧフェãƒŧ゚ぎã‚Ģ゚ã‚ŋマイã‚ēã‚’įŽĄį†ã—ãžã™", - "these_files_matched_by_checksum": "ã“ã‚Œã‚‰ãŽãƒ•ã‚Ąã‚¤ãƒĢはチェックã‚ĩムãĢã‚ˆãŖãĻį…§åˆã•ã‚Œãžã™", "thumbnail_generation_job": "ã‚ĩムネイãƒĢį”Ÿæˆ", "thumbnail_generation_job_description": "各ã‚ĸã‚ģットぎã‚ĩムネイãƒĢīŧˆå¤§ã€å°ã€ãŧかしīŧ‰ã¨ã€å„äēēį‰ŠãŽã‚ĩムネイãƒĢã‚’į”Ÿæˆã—ãžã™", "transcoding_acceleration_api": "ã‚ĸクã‚ģナãƒŦãƒŧã‚ˇãƒ§ãƒŗ API", @@ -304,7 +294,6 @@ "transcoding_hardware_acceleration_description": "よりéĢ˜é€Ÿã§ã™ãŒã€åŒã˜ãƒ“ãƒƒãƒˆãƒŦãƒŧトではよりäŊŽå“čŗĒãĢãĒりぞすīŧˆåŽŸé¨“įš„īŧ‰", "transcoding_hardware_decoding": "ハãƒŧドã‚Ļェã‚ĸãƒ‡ã‚ŗãƒŧド", "transcoding_hardware_decoding_setting_description": "NVEnc、QSV、RKMPP ãĢぎãŋéŠį”¨ã•ã‚Œãžã™ã€‚ã‚¨ãƒŗã‚ŗãƒŧド ã‚ĸクã‚ģナãƒŦãƒŧã‚ˇãƒ§ãƒŗã ã‘ã§ãĒãã€ã‚¨ãƒŗãƒ‰ãƒ„ãƒŧã‚¨ãƒŗãƒ‰ ã‚ĸクã‚ģナãƒŦãƒŧã‚ˇãƒ§ãƒŗã‚’å¯čƒŊãĢしぞす。すずãĻぎ動į”ģで動äŊœã™ã‚‹ã¨ã¯é™ã‚Šãžã›ã‚“。", - "transcoding_hevc_codec": "HEVC ã‚ŗãƒŧデック", "transcoding_max_b_frames": "最大BフãƒŦãƒŧム", "transcoding_max_b_frames_description": "値をéĢ˜ãã™ã‚‹ã¨åœ§į¸ŽåŠšįŽ‡ãŒå‘ä¸Šã—ãžã™ãŒã€ã‚¨ãƒŗã‚ŗãƒŧド速åēĻが遅くãĒりぞす。古いデバイ゚ぎハãƒŧドã‚Ļェã‚ĸã‚ĸクã‚ģナãƒŦãƒŧã‚ˇãƒ§ãƒŗã§ã¯å¯žåŋœã—ãĻいãĒい場合がありぞす。\"0\" はBフãƒŦãƒŧãƒ ã‚’į„ĄåŠšãĢし、\"-1\" はこぎ値をč‡Ēå‹•įš„ãĢč¨­åŽšã—ãžã™ã€‚", "transcoding_max_bitrate": "最大ビットãƒŦãƒŧト", @@ -342,8 +331,6 @@ "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": "遅åģļ削除", @@ -384,7 +371,7 @@ "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, one {#æ­ŗ} other {#æ­ŗ}}", "album_added": "ã‚ĸãƒĢバムčŋŊ加", @@ -402,10 +389,6 @@ "album_remove_user": "ãƒĻãƒŧã‚ļãƒŧを削除しぞすか?", "album_remove_user_confirmation": "æœŦåŊ“ãĢ{user}を削除しぞすか?", "album_share_no_users": "こぎã‚ĸãƒĢバムを全ãĻぎãƒĻãƒŧã‚ļãƒŧã¨å…ąæœ‰ã—ãŸã‹ã€å…ąæœ‰ã™ã‚‹ãƒĻãƒŧã‚ļãƒŧがいãĒいようです。", - "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} をåŽģりぞした", @@ -421,6 +404,9 @@ "album_with_link_access": "ãƒĒãƒŗã‚¯ã‚’įŸĨãŖãĻいるäēēはčĒ°ã§ã‚‚ã€ã“ãŽã‚ĸãƒĢãƒãƒ ä¸­ãŽå†™įœŸã¨äēēį‰Šã‚’é–˛čĻ§ã§ãã‚‹ã‚ˆã†ãĢãĒりぞす。", "albums": "ã‚ĸãƒĢバム", "albums_count": "{count, plural, one {{count, number} äģļ} other {{count, number} äģļ}}ぎã‚ĸãƒĢバム", + "albums_default_sort_order": "デフりãƒĢトぎã‚ĸãƒĢãƒãƒ čĄ¨į¤ē順", + "albums_default_sort_order_description": "新čĻã‚ĸãƒĢバムäŊœæˆæ™‚ãŽåˆæœŸčĄ¨į¤ē順.", + "albums_feature_description": "äģ–ぎãƒĻãƒŧã‚ļãƒŧã¨å…ąæœ‰ã§ãã‚‹ã‚ĸã‚ģãƒƒãƒˆãŽã‚ŗãƒŦã‚¯ã‚ˇãƒ§ãƒŗ.", "all": "すずãĻ", "all_albums": "全ãĻぎã‚ĸãƒĢバム", "all_people": "全ãĻぎäēēį‰Š", @@ -479,9 +465,12 @@ "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_cannot_be_added_to_album_count": "{count, plural, one {ã‚ĸã‚ģット} other {ã‚ĸã‚ģット}} はã‚ĸãƒĢバムãĢčŋŊ加できぞせん", "assets_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģット", "assets_deleted_permanently": "{count}é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", "assets_deleted_permanently_from_server": "ã‚ĩãƒŧバãƒŧ上ぎ{count}é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", + "assets_downloaded_failed": "{count, plural, other {# ãƒ•ã‚Ąã‚¤ãƒĢ中 {error} ãƒ•ã‚Ąã‚¤ãƒĢぎダã‚Ļãƒŗãƒ­ãƒŧドãĢå¤ąæ•—ã—ãžã—ãŸ}}", + "assets_downloaded_successfully": "{count, plural, other {# ãƒ•ã‚Ąã‚¤ãƒĢぎダã‚Ļãƒŗãƒ­ãƒŧドãĢ成功しぞした}}", "assets_moved_to_trash_count": "{count, plural, one {#é …į›Ž} other {#é …į›Ž}}ã‚’ã‚´ãƒŸįŽąãĢį§ģ動しぞした", "assets_permanently_deleted_count": "{count, plural, one {#é …į›Ž} other {#é …į›Ž}}を厌全ãĢ削除しぞした", "assets_removed_count": "{count, plural, one {#é …į›Ž} other {#é …į›Ž}}を削除しぞした", @@ -496,6 +485,7 @@ "authorized_devices": "čĒå¯æ¸ˆãŋデバイ゚", "automatic_endpoint_switching_subtitle": "指厚されたWi-FiãĢæŽĨį™‚ぎãŋロãƒŧã‚ĢãƒĢæŽĨįļšã‚’čĄŒã„ã€äģ–ぎネットワãƒŧク下では通常通りぎæŽĨįļšã‚’čĄŒã„ãžã™", "automatic_endpoint_switching_title": "č‡Ē動URL切りæ›ŋえ", + "autoplay_slideshow": "ã‚šãƒŠã‚¤ãƒ‰ã‚ˇãƒ§ãƒŧをč‡Ēå‹•å†į”Ÿ", "back": "æˆģる", "back_close_deselect": "æˆģã‚‹ã€é–‰ã˜ã‚‹ã€é¸æŠžč§Ŗé™¤", "background_location_permission": "バックグナã‚Ļãƒŗãƒ‰äŊįŊŽæƒ…å ąã‚ĸクã‚ģ゚", @@ -507,19 +497,19 @@ "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_backup_failed_message": "ã‚ĸップロãƒŧドãĢå¤ąæ•—ã—ãžã—ãŸã€‚ãƒĒトナイ中â€Ļ", + "backup_background_service_connection_failed_message": "ã‚ĩãƒŧバãƒŧãĢæŽĨįļšã§ããžã›ã‚“。ãƒĒトナイ中â€Ļ", "backup_background_service_current_upload_notification": "{filename}をã‚ĸップロãƒŧド中", - "backup_background_service_default_notification": "æ–°ã—ã„å†™įœŸã‚’įĸēčĒä¸­", + "backup_background_service_default_notification": "æ–°ã—ã„å†™įœŸã‚’įĸēčĒä¸­â€Ļ", "backup_background_service_error_title": "バックã‚ĸップエナãƒŧ", - "backup_background_service_in_progress_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": "バックグナã‚Ļãƒŗãƒ‰ã§å†™įœŸãŽãƒãƒƒã‚¯ã‚ĸãƒƒãƒ—ã‚’čĄŒã„ãŸã„å ´åˆã¯ã€ãƒãƒƒã‚¯ã‚°ãƒŠã‚Ļãƒŗãƒ‰æ›´æ–°ã‚’\nč¨­åŽš > 一čˆŦ > Appぎバックグナã‚Ļãƒŗãƒ‰æ›´æ–°\nからã‚ĒãƒŗãĢしãĻください。", "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_message": "バックグナã‚Ļãƒŗãƒ‰å‡Ļį†ã‚’æ­Ŗå¸¸ãĢ動äŊœã•せるためãĢは、Immichã‚ĸプãƒĒãĢéŠį”¨ã•ã‚ŒãĻいるバッテãƒĒãƒŧぎ最遊化をã‚ĒフãĢしãĻください。\n\nデバイ゚ãĢã‚ˆãŖãĻč¨­åŽšæ–šæŗ•ãŒį•°ãĒりぞすぎで各々čĒŋずãĻください。", "backup_controller_page_background_battery_info_ok": "äē†č§Ŗ", "backup_controller_page_background_battery_info_title": "バッテãƒĒãƒŧぎ最遊化", "backup_controller_page_background_charging": "充é›ģ中ぎãŋ", @@ -532,11 +522,11 @@ "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": "äŊœæˆæ—Ĩ: {date}", - "backup_controller_page_desc_backup": "ã‚ĸプãƒĒを開いãĻいるときãĢå†™įœŸã¨å‹•į”ģをバックã‚ĸップしぞす", - "backup_controller_page_excluded": "除外中ぎã‚ĸãƒĢバム:", + "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: {id}", @@ -577,21 +567,17 @@ "bulk_keep_duplicates_confirmation": "æœŦåŊ“ãĢ{count, plural, one {#個} other {#個}}ãŽé‡č¤‡ã‚ĸã‚ģットをäŋæŒã—ぞすか?これãĢよりäŊ•も削除されずãĢé‡č¤‡ã‚°ãƒĢãƒŧãƒ—ãŒč§Ŗæąēされぞす。", "bulk_trash_duplicates_confirmation": "æœŦåŊ“ãĢ{count, plural, one {#個} other {#個}}ãŽé‡č¤‡ã—ãŸã‚ĸã‚ģットを一æ‹ŦでごãŋįŽąãĢį§ģ動しぞすか?これãĢã‚ˆã‚Šå„é‡č¤‡ä¸­ãŽæœ€å¤§ãŽã‚ĸã‚ģットがäŋæŒã•れ、äģ–ぎ全ãĻãŽé‡č¤‡ã¯ã”ãŋįŽąãĢį§ģ動されぞす。", "buy": "Immichをčŗŧå…Ĩ", - "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": "é‡č¤‡ã—ãŸé …į›Žæ•°: ({count})", - "cache_settings_image_cache_size": "į”ģåƒã‚­ãƒŖãƒƒã‚ˇãƒĨぎã‚ĩイã‚ē ({count}é …į›Ž)", "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": "ã‚­ãƒŖãƒƒã‚ˇãƒĨぎ動äŊœã‚’変更する", - "cache_settings_thumbnail_size": "ã‚ĩムネイãƒĢãŽã‚­ãƒŖãƒƒã‚ˇãƒĨぎã‚ĩイã‚ē ({count}é …į›Ž)", "cache_settings_tile_subtitle": "ロãƒŧã‚ĢãƒĢ゚トãƒŦãƒŧジぎ挙動をįĸēčĒã™ã‚‹", "cache_settings_tile_title": "ロãƒŧã‚ĢãƒĢ゚トãƒŦãƒŧジ", "cache_settings_title": "ã‚­ãƒŖãƒƒã‚ˇãƒĨãŽč¨­åŽš", @@ -605,6 +591,7 @@ "cannot_undo_this_action": "こぎ操äŊœã¯å…ƒãĢæˆģせぞせんīŧ", "cannot_update_the_description": "čĒŦ明を更新できぞせん", "cast": "ã‚­ãƒŖã‚šãƒˆ", + "cast_description": "ã‚­ãƒŖã‚šãƒˆå…ˆãŽå€™čŖœã‚’č¨­åŽš", "change_date": "æ—Ĩ時を変更", "change_description": "čĒŦ明文を変更", "change_display_order": "襨į¤ē順を変更", @@ -615,14 +602,13 @@ "change_password": "パ゚ワãƒŧドを変更", "change_password_description": "これは、初めãĻぎã‚ĩã‚¤ãƒŗã‚¤ãƒŗã§ã‚ã‚‹ã‹ã€ãƒ‘ã‚šãƒ¯ãƒŧドぎ変更čĻæą‚ãŒčĄŒã‚ã‚ŒãŸã‹ãŽã„ãšã‚Œã‹ã§ã™ã€‚ 新しいパ゚ワãƒŧドを下ãĢå…Ĩ力しãĻください。", "change_password_form_confirm_password": "įĸē厚", - "change_password_form_description": "{name}さん こんãĢãĄã¯\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_corrupt_asset_backup_button": "ãƒã‚§ãƒƒã‚¯ã‚’čĄŒã†", "check_corrupt_asset_backup_description": "å†™įœŸã‚„å‹•į”ģãĒおが全ãĻã‚ĸップロãƒŧドしįĩ‚えãĻからWi-FiãĢæŽĨį™‚ぎãŋãƒã‚§ãƒƒã‚¯ã‚’čĄŒãĒãŖãĻください。äŊœæĨ­ãŒåތäē†ã™ã‚‹ãĢは数分かかる場合がありぞす", @@ -662,16 +648,18 @@ "confirm_keep_this_delete_others": "ã“ãŽé …į›ŽäģĨå¤–ãŽé …į›ŽãŒã‚šã‚ŋックから削除されぞす。æœŦåŊ“ãĢ削除しぞすか?", "confirm_new_pin_code": "こぎPINã‚ŗãƒŧドをäŊŋう", "confirm_password": "įĸēčĒ", + "confirm_tag_face": "ã“ãŽéĄ”ã‚’{name}としãĻį™ģéŒ˛ã—ãžã™ã‹īŧŸ", + "confirm_tag_face_unnamed": "ã“ãŽéĄ”ã‚’į™ģéŒ˛ã—ãžã™ã‹īŧŸ", + "connected_device": "æŽĨįļšã•れたデバイ゚", "connected_to": "æŽĨįļš:", "contain": "収める", "context": "įŠļæŗ", "continue": "įļšã‘ã‚‹", - "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": "デバイ゚上から削除", "control_bottom_app_bar_edit_location": "äŊįŊŽæƒ…å ąã‚’įˇ¨é›†", - "control_bottom_app_bar_edit_time": "æ—Ĩ時を変更", + "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": "ã‚´ãƒŸįŽąãĢå…Ĩれる", @@ -715,6 +703,7 @@ "daily_title_text_date": "MM DD, EE", "daily_title_text_date_year": "yyyy MM DD, EE", "dark": "ダãƒŧクãƒĸãƒŧド", + "darkTheme": "ダãƒŧクãƒĸãƒŧドを切りæ›ŋえ", "date_after": "こぎæ—ĨäģĨ降", "date_and_time": "æ—Ĩäģ˜ã¨æ™‚é–“", "date_before": "こぎæ—ĨäģĨ前", @@ -729,7 +718,7 @@ "deduplication_info_description": "ã‚ĸã‚ģットをč‡Ēå‹•įš„ãĢ選択しãĻé‡č¤‡ã‚’ä¸€æ‹Ŧで削除するãĢはæŦĄãŽã‚ˆã†ãĢしぞす:", "default_locale": "デフりãƒĢãƒˆãŽãƒ­ã‚ąãƒŧãƒĢ", "default_locale_description": "ブナã‚Ļã‚ļãŽãƒ­ã‚ąãƒŧãƒĢãĢåŸēãĨいãĻæ—Ĩäģ˜ã¨æ•°å€¤ã‚’フりãƒŧマットしぞす", - "delete": "デバイ゚とã‚ĩãƒŧバãƒŧから削除", + "delete": "削除", "delete_album": "ã‚ĸãƒĢバムを削除", "delete_api_key_prompt": "æœŦåŊ“ãĢこぎAPI キãƒŧを削除しぞすか?", "delete_dialog_alert": "ã‚ĩãƒŧバãƒŧã¨ãƒ‡ãƒã‚¤ã‚šãŽä¸Ąæ–šã‹ã‚‰åŽŒå…¨ãĢ削除されぞす", @@ -760,7 +749,9 @@ "direction": "斚向", "disabled": "į„ĄåŠš", "disallow_edits": "įˇ¨é›†ã‚’č¨ąå¯ã—ãĒい", + "discord": "Discord", "discover": "æŽĸį´ĸ", + "discovered_devices": "į™ēčĻ‹ã•ã‚ŒãŸãƒ‡ãƒã‚¤ã‚š", "dismiss_all_errors": "全ãĻぎエナãƒŧã‚’į„ĄčĻ–", "dismiss_error": "エナãƒŧã‚’į„ĄčĻ–", "display_options": "襨į¤ēã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", @@ -776,12 +767,11 @@ "download_enqueue": "ダã‚Ļãƒŗãƒ­ãƒŧド垅抟中", "download_error": "ダã‚Ļãƒŗãƒ­ãƒŧドエナãƒŧ", "download_failed": "ダã‚Ļãƒŗãƒ­ãƒŧãƒ‰å¤ąæ•—", - "download_filename": "ãƒ•ã‚Ąã‚¤ãƒĢ名: {filename}", "download_finished": "ダã‚Ļãƒŗãƒ­ãƒŧドįĩ‚äē†", "download_include_embedded_motion_videos": "埋めčžŧぞれた動į”ģ", "download_include_embedded_motion_videos_description": "åˆĨãƒ•ã‚Ąã‚¤ãƒĢとしãĻ、ãƒĸãƒŧã‚ˇãƒ§ãƒŗãƒ•ã‚ŠãƒˆãĢ埋めčžŧぞれた動į”ģをåĢめる", "download_notfound": "ダã‚Ļãƒŗãƒ­ãƒŧドがčĻ‹ã¤ã‹ã‚Šãžã›ã‚“", - "download_paused": "ダã‚Ļãƒŗãƒ­ãƒŧド停æ­ĸ", + "download_paused": "ダã‚Ļãƒŗãƒ­ãƒŧド一時停æ­ĸ中", "download_settings": "ダã‚Ļãƒŗãƒ­ãƒŧド", "download_settings_description": "ã‚ĸã‚ģットぎダã‚Ļãƒŗãƒ­ãƒŧドãĢé–ĸé€Ŗã™ã‚‹č¨­åŽšã‚’įŽĄį†ã—ãžã™", "download_started": "ダã‚Ļãƒŗãƒ­ãƒŧド開始", @@ -799,7 +789,7 @@ "edit_album": "ã‚ĸãƒĢãƒãƒ ã‚’įˇ¨é›†", "edit_avatar": "ã‚ĸバã‚ŋãƒŧã‚’įˇ¨é›†", "edit_date": "æ—Ĩäģ˜ã‚’ᎍ集", - "edit_date_and_time": "æ—Ĩæ™‚ã‚’įˇ¨é›†", + "edit_date_and_time": "æ’ŽåŊąæ—Ĩæ™‚ã‚’įˇ¨é›†", "edit_description": "čĒŦæ˜Žæ–‡ã‚’įˇ¨é›†", "edit_description_prompt": "新しいčĒŦ明文を選んでください:", "edit_exclusion_pattern": "除外パã‚ŋãƒŧãƒŗã‚’įˇ¨é›†", @@ -824,7 +814,7 @@ "email": "ãƒĄãƒŧãƒĢã‚ĸドãƒŦ゚", "email_notifications": "EãƒĄãƒŧãƒĢ通įŸĨ", "empty_folder": "こぎフりãƒĢダãƒŧはįŠēです", - "empty_trash": "ã‚ŗãƒŸįŽąã‚’įŠēãĢする", + "empty_trash": "ã‚´ãƒŸįŽąã‚’įŠēãĢする", "empty_trash_confirmation": "æœŦåŊ“ãĢã‚´ãƒŸįŽąã‚’įŠēãĢしぞすか? これãĢã‚ˆã‚Šã€ã‚´ãƒŸįŽąå†…ãŽã™ãšãĻぎã‚ĸã‚ģットが Immich から永䚅ãĢ削除されぞす。\nこぎ操äŊœã‚’å…ƒãĢæˆģすことはできぞせん!", "enable": "有劚化", "enable_biometric_auth_description": "į”ŸäŊ“čĒč¨ŧを有劚化するためãĢ、PINã‚ŗãƒŧドをå…Ĩ力しãĻください", @@ -839,6 +829,7 @@ "error_delete_face": "ã‚ĸã‚ģãƒƒãƒˆã‹ã‚‰éĄ”ãŽå‰Šé™¤ãŒã§ããžã›ã‚“ã§ã—ãŸ", "error_loading_image": "į”ģ像ぎčĒ­ãŋčžŧãŋエナãƒŧ", "error_saving_image": "エナãƒŧ: {error}", + "error_tag_face_bounding_box": "éĄ”ãŽį™ģ錞ãĢå¤ąæ•—ã—ãžã—ãŸ - éĄ”ã‚’å›˛ã‚€å››č§’åŊĸぎåē§æ¨™å–åž—ãĢå¤ąæ•—", "error_title": "エナãƒŧ - å•éĄŒãŒį™ēį”Ÿã—ãžã—ãŸ", "errors": { "cannot_navigate_next_asset": "æŦĄãŽã‚ĸã‚ģットãĢį§ģ動できぞせん", @@ -851,7 +842,6 @@ "cant_get_number_of_comments": "ã‚ŗãƒĄãƒŗãƒˆæ•°ã‚’å–åž—ã§ããžã›ã‚“", "cant_search_people": "äēēį‰Šã‚’æ¤œį´ĸできぞせん", "cant_search_places": "場所を検į´ĸできぞせん", - "cleared_jobs": "{job} ぎジョブをクãƒĒã‚ĸしぞした", "error_adding_assets_to_album": "ã‚ĸã‚ģットをã‚ĸãƒĢバムãĢčŋŊ加中ぎエナãƒŧ", "error_adding_users_to_album": "ãƒĻãƒŧã‚ļãƒŧをã‚ĸãƒĢバムãĢčŋŊ加中ぎエナãƒŧ", "error_deleting_shared_user": "å…ąæœ‰ãƒĻãƒŧã‚ļを削除中ぎエナãƒŧ", @@ -860,7 +850,6 @@ "error_removing_assets_from_album": "ã‚ĸãƒĢバムからã‚ĸã‚ģットを削除中ぎエナãƒŧã€čŠŗį´°ãĢついãĻã¯ã‚ŗãƒŗã‚ŊãƒŧãƒĢをįĸēčĒã—ãĻください", "error_selecting_all_assets": "全ã‚ĸã‚ģット選択ぎエナãƒŧ", "exclusion_pattern_already_exists": "こぎ除外パã‚ŋãƒŧãƒŗã¯æ—ĸãĢ存在しぞす。", - "failed_job_command": "ジョブ{job} ãŽã‚ŗãƒžãƒŗãƒ‰{command} ãŒå¤ąæ•—ã—ãžã—ãŸ", "failed_to_create_album": "ã‚ĸãƒĢバムをäŊœæˆã§ããžã›ã‚“でした", "failed_to_create_shared_link": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‚’äŊœæˆã§ããžã›ã‚“でした", "failed_to_edit_shared_link": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‚’įˇ¨é›†ã§ããžã›ã‚“ã§ã—ãŸ", @@ -879,7 +868,6 @@ "paths_validation_failed": "{paths, plural, one {#個} 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": "ã‚ŗãƒĄãƒŗãƒˆã‚’čŋŊ加できぞせん", @@ -898,7 +886,6 @@ "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 įĩŒį”ąã§ãƒšãƒŧジãĢã‚ĸクã‚ģ゚しãĻいることをįĸēčĒã—ãĻください", "unable_to_create_admin_account": "įŽĄį†č€…ã‚ĸã‚Ģã‚Ļãƒŗãƒˆã‚’äŊœæˆã§ããžã›ã‚“", "unable_to_create_api_key": "新しいAPI キãƒŧをäŊœæˆã§ããžã›ã‚“", @@ -922,14 +909,9 @@ "unable_to_hide_person": "äēēį‰Šã‚’éžčĄ¨į¤ēãĢできぞせん", "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_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": "ã‚ĸã‚ģットを新しいäēēį‰ŠãĢå†å‰˛ã‚ŠåŊ“ãĻできぞせん", @@ -937,11 +919,9 @@ "unable_to_remove_album_users": "ã‚ĸãƒĢバムからãƒĻãƒŧã‚ļãƒŧを削除できぞせん", "unable_to_remove_api_key": "API キãƒŧを削除できぞせん", "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": "é‡č¤‡ã‚’č§Ŗæąēできぞせん", @@ -971,12 +951,12 @@ "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": "į”ŸåžŒ{months}ãƒļ月", "exif_bottom_sheet_person_age_year_months": "1æ­ŗ{months}ãƒļ月", "exif_bottom_sheet_person_age_years": "{years}æ­ŗ", @@ -1024,6 +1004,8 @@ "folders": "フりãƒĢダ", "folders_feature_description": "ãƒ•ã‚Ąã‚¤ãƒĢã‚ˇã‚šãƒ†ãƒ ä¸ŠãŽå†™įœŸã¨å‹•į”ģぎフりãƒĢダビãƒĨãƒŧã‚’é–˛čĻ§ã™ã‚‹", "forward": "前へ", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "こぎ抟čƒŊは動äŊœãŽãŸã‚ãĢGoogleぎãƒĒã‚Ŋãƒŧ゚をčĒ­ãŋčžŧãŋぞす。", "general": "一čˆŦ", "get_help": "åŠŠã‘ã‚’æą‚ã‚ã‚‹", "get_wifiname_error": "Wi-Fiぎ名前(SSID)がå…Ĩ手できぞせんでした。Wi-FiãĢįš‹ãŒãŖãĻるぎとåŋ…čρãĒæ¨Šé™ã‚’č¨ąå¯ã—ãŸã‹įĸēčĒã—ãĻください", @@ -1112,6 +1094,12 @@ "invalid_date_format": "æ—Ĩäģ˜ãŽãƒ•りãƒŧãƒžãƒƒãƒˆãŒį„ĄåŠšã§ã™", "invite_people": "äēē々を招垅", "invite_to_album": "ã‚ĸãƒĢバムãĢ招垅", + "ios_debug_info_fetch_ran_at": "{dateTime}ãĢ取垗をčŠĻãŋぞした", + "ios_debug_info_last_sync_at": "前回ぎ同期時åˆģ: {dateTime}", + "ios_debug_info_no_processes_queued": "バックグナã‚Ļãƒŗãƒ‰ã§å‡Ļį†åž…ãĄãŽé …į›Žã¯ã‚ã‚Šãžã›ã‚“", + "ios_debug_info_no_sync_yet": "バックグナã‚Ļãƒŗãƒ‰åŒæœŸã¯åŽŸčĄŒã•ã‚ŒãĻいぞせん", + "ios_debug_info_processes_queued": "バックグナã‚Ļãƒŗãƒ‰å‡Ļį†åž…ãĄ: {count, plural, one {{count}} other {{count}}}", + "ios_debug_info_processing_ran_at": "å‡Ļį†åŽŸčĄŒæ¸ˆãŋ: {dateTime}", "items_count": "{count, plural, one {#個} other {#個}}ãŽé …į›Ž", "jobs": "ジョブ", "keep": "äŋæŒ", @@ -1120,6 +1108,9 @@ "kept_this_deleted_others": "こぎã‚ĸã‚ģットを掋しãĻ{count, plural, other {#äģļぎã‚ĸã‚ģット}}を削除する", "keyboard_shortcuts": "キãƒŧボãƒŧãƒ‰ã‚ˇãƒ§ãƒŧトã‚Ģット", "language": "言čĒž", + "language_no_results_subtitle": "検į´ĸワãƒŧドを変えãĻãŋãĻください", + "language_no_results_title": "言čĒžãŒčĻ‹ã¤ã‹ã‚Šãžã›ã‚“", + "language_search_hint": "言čĒžã‚’æ¤œį´ĸ", "language_setting_description": "å„Ēå…ˆč¨€čĒžã‚’é¸æŠžã—ãĻください", "last_seen": "最新ぎæ´ģ動", "latest_version": "最新バãƒŧã‚¸ãƒ§ãƒŗ", @@ -1145,6 +1136,7 @@ "list": "ãƒĒ゚ト", "loading": "čĒ­ãŋčžŧãŋ中", "loading_search_results_failed": "検į´ĸįĩæžœã‚’čĒ­ãŋčžŧめぞせんでした", + "local_asset_cast_failed": "ã‚ĩãƒŧバãƒŧãĢã‚ĸップロãƒŧドされãĻいãĒã„é …į›Žã¯ã‚­ãƒŖã‚šãƒˆã§ããžã›ã‚“", "local_network": "ロãƒŧã‚ĢãƒĢネットワãƒŧク", "local_network_sheet_info": "ã‚ĸプãƒĒは指厚されたWi-FiãĢįš‹ãŒãŖãĻいる時ã‚ĩãƒŧバãƒŧへぎæŽĨįļšã‚’ä¸‹č¨˜ãŽURLã§čĄŒã„ãžã™", "location_permission": "äŊįŊŽæƒ…å ąæ¨Šé™", @@ -1158,6 +1150,7 @@ "locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧ", "log_out": "ログã‚ĸã‚Ļト", "log_out_all_devices": "全ãĻぎデバイ゚からログã‚ĸã‚Ļト", + "logged_in_as": "{user}としãĻãƒ­ã‚°ã‚¤ãƒŗä¸­", "logged_out_all_devices": "全ãĻぎデバイ゚からログã‚ĸã‚Ļトしぞした", "logged_out_device": "デバイ゚からログã‚ĸã‚Ļトしぞした", "login": "ãƒ­ã‚°ã‚¤ãƒŗ", @@ -1165,6 +1158,7 @@ "login_form_api_exception": "APIエナãƒŧがį™ēį”Ÿã—ãžã—ãŸã€‚URLをチェックしãĻもう一åēĻおčŠĻしください。", "login_form_back_button_text": "æˆģる", "login_form_email_hint": "hoge@email.com", + "login_form_endpoint_hint": "http://ã‚ĩãƒŧバãƒŧぎã‚ĸドãƒŦ゚:ポãƒŧトį•Ēåˇ", "login_form_endpoint_url": "ã‚ĩãƒŧバãƒŧãŽã‚¨ãƒŗãƒ‰ãƒã‚¤ãƒŗãƒˆURL", "login_form_err_http": "http://かhttps://かを指厚しãĻください", "login_form_err_invalid_email": "ãƒĄãƒŧãƒĢã‚ĸドãƒŦã‚šãŒį„ĄåŠšã§ã™", @@ -1284,12 +1278,13 @@ "no_archived_assets_message": "å†™įœŸã‚„å‹•į”ģをã‚ĸãƒŧã‚ĢイブしãĻã€å†™įœŸä¸€čĻ§ã‹ã‚‰éžčĄ¨į¤ēãĢしぞす", "no_assets_message": "クãƒĒックしãĻæœ€åˆãŽå†™įœŸã‚’ã‚ĸップロãƒŧド", "no_assets_to_show": "襨į¤ēã™ã‚‹é …į›ŽãŒã‚ã‚Šãžã›ã‚“", + "no_cast_devices_found": "ã‚­ãƒŖã‚šãƒˆå…ˆãŽãƒ‡ãƒã‚¤ã‚šãŒčĻ‹ã¤ã‹ã‚Šãžã›ã‚“", "no_duplicates_found": "é‡č¤‡ã¯čĻ‹ã¤ã‹ã‚Šãžã›ã‚“ã§ã—ãŸã€‚", "no_exif_info_available": "exifæƒ…å ąãŒåˆŠį”¨ã§ããžã›ã‚“", "no_explore_results_message": "ã‚ŗãƒŦã‚¯ã‚ˇãƒ§ãƒŗã‚’æŽĸį´ĸするãĢはさらãĢå†™įœŸã‚’ã‚ĸップロãƒŧドしãĻください。", "no_favorites_message": "お気ãĢå…Ĩりį™ģéŒ˛ã™ã‚‹ã¨åĨŊきãĒå†™įœŸã‚„å‹•į”ģをすぐãĢčĻ‹ã¤ã‘ã‚‰ã‚Œãžã™", "no_libraries_message": "あãĒãŸãŽå†™įœŸã‚„å‹•į”ģã‚’čĄ¨į¤ēするためぎ外部ナイブナãƒĒをäŊœæˆã—ぞしょう", - "no_locked_photos_message": "éĩäģ˜ããƒ•りãƒĢダãƒŧå†…ãŽå†™įœŸã‚„å‹•į”ģは通常ぎナイブナãƒĒから隠されぞす。", + "no_locked_photos_message": "éĩäģ˜ããƒ•りãƒĢダãƒŧå†…ãŽå†™įœŸã‚„å‹•į”ģは通常ぎナイブナãƒĒãĢ襨į¤ēされãĒくãĒりぞす。", "no_name": "名前ãĒし", "no_notifications": "通įŸĨãĒし", "no_people_found": "ä¸€č‡´ã™ã‚‹äēēį‰ŠãŒčĻ‹ã¤ã‹ã‚Šãžã›ã‚“", @@ -1309,17 +1304,18 @@ "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": "ãƒ‡ãƒã‚¤ã‚šä¸ŠãŽé …į›Ž", "onboarding": "はじめãĢ", - "onboarding_privacy_description": "æŦĄãŽ(äģģæ„ãŽ)抟čƒŊは外部ã‚ĩãƒŧビ゚ãĢäžå­˜ã—ã€ã„ã¤ã§ã‚‚įŽĄį†č€…į”¨č¨­åŽšã§į„ĄåŠšãĢできぞす。", + "onboarding_locale_description": "おåĨŊãŋãŽč¨€čĒžã‚’é¸æŠžã—ãĻください、あとから変更もできぞす。", + "onboarding_privacy_description": "æŦĄãŽ(äģģæ„ãŽ)抟čƒŊは外部ã‚ĩãƒŧビ゚ãĢäžå­˜ã—ã€įŽĄį†č€…į”¨č¨­åŽšã§ã„ã¤ã§ã‚‚į„ĄåŠšãĢできぞす。", + "onboarding_server_welcome_description": "あãĒãŸãŽã‚¤ãƒŗã‚šã‚ŋãƒŗã‚šã‚’ã‚ˆãã‚ã‚‹č¨­åŽšã¨ã¨ã‚‚ãĢ始めãĻãŋぞしょう。", "onboarding_theme_description": "ã‚¤ãƒŗã‚šã‚ŋãƒŗã‚šãŽã‚Ģナãƒŧテãƒŧマを選択しãĻãã ã•ã„ã€‚ã“ã‚Œã¯åžŒã‹ã‚‰č¨­åŽšã§å¤‰æ›´ã§ããžã™ã€‚", - "onboarding_welcome_description": "いくつかぎ一čˆŦįš„ãĒč¨­åŽšã‚’äŊŋį”¨ã—ãĻã‚¤ãƒŗã‚šã‚ŋãƒŗã‚šã‚’ã‚ģットã‚ĸップしぞしょう。", + "onboarding_user_welcome_description": "はじめぞしょうīŧ", "onboarding_welcome_user": "ようこそ、{user} さん", "online": "ã‚ĒãƒŗãƒŠã‚¤ãƒŗ", "only_favorites": "お気ãĢå…Ĩりぎãŋ", @@ -1376,6 +1372,8 @@ "permanently_delete_assets_prompt": "æœŦåŊ“ãĢ{count, plural, one {こぎã‚ĸã‚ģット} other {これらぎ#個ぎã‚ĸã‚ģット}}を厌全ãĢ削除しぞすか? これãĢより {count, plural, one {こぎã‚ĸã‚ģット} other {これらぎã‚ĸã‚ģット}}はã‚ĸãƒĢバムからも削除されぞす。", "permanently_deleted_asset": "é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", "permanently_deleted_assets_count": "{count, plural, one {#個} other {#個}}ãŽé …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", + "permission": "樊限", + "permission_empty": "樊限をįŠēį™ŊãĢはå‡ēæĨぞせん", "permission_onboarding_back": "æˆģる", "permission_onboarding_continue_anyway": "į„ĄčĻ–ã—ãĻįļščĄŒ", "permission_onboarding_get_started": "はじめる", @@ -1412,7 +1410,10 @@ "preview": "プãƒŦビãƒĨãƒŧ", "previous": "前", "previous_memory": "å‰ãŽãƒĄãƒĸãƒĒãƒŧ", + "previous_or_next_day": "前ぞたはæŦĄãŽæ—Ĩ", + "previous_or_next_month": "前ぞたはæŦĄãŽæœˆ", "previous_or_next_photo": "前ぞたはæŦĄãŽå†™įœŸ", + "previous_or_next_year": "前ぞたはæŦĄãŽåš´", "primary": "最å„Ē先", "privacy": "ãƒ—ãƒŠã‚¤ãƒã‚ˇãƒŧ", "profile": "ãƒ—ãƒ­ãƒ•ã‚ŖãƒŧãƒĢ", @@ -1420,6 +1421,7 @@ "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} ãŽãƒ—ãƒ­ãƒ•ã‚ŖãƒŧãƒĢį”ģ像", @@ -1491,7 +1493,7 @@ "remove_assets_shared_link_confirmation": "æœŦåŊ“ãĢã“ãŽå…ąæœ‰ãƒĒãƒŗã‚¯ã‹ã‚‰{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットを削除しぞすか?", "remove_assets_title": "ã‚ĸã‚ģットを削除しぞすか?", "remove_custom_date_range": "ã‚Ģ゚ã‚ŋムæ—Ĩäģ˜į¯„å›˛ã‚’å‰Šé™¤", - "remove_deleted_assets": "ã‚Ēãƒ•ãƒŠã‚¤ãƒŗãŽãƒ•ã‚Ąã‚¤ãƒĢを削除", + "remove_deleted_assets": "ã‚Ēãƒ•ãƒŠã‚¤ãƒŗãŽã‚ĸã‚ģットを削除", "remove_from_album": "ã‚ĸãƒĢバムから削除", "remove_from_favorites": "お気ãĢå…Ĩã‚Šč§Ŗé™¤", "remove_from_locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧから取り除く", @@ -1499,6 +1501,7 @@ "remove_from_shared_link": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‹ã‚‰å‰Šé™¤", "remove_memory": "ãƒĄãƒĸãƒĒãƒŧぎ削除", "remove_photo_from_memory": "ãƒĄãƒĸãƒĒãƒŧã‹ã‚‰å†™įœŸã‚’å‰Šé™¤", + "remove_tag": "ã‚ŋグを削除", "remove_url": "URLぎ削除", "remove_user": "ãƒĻãƒŧã‚ļãƒŧを削除", "removed_api_key": "削除されたAPI キãƒŧ: {name}", @@ -1605,6 +1608,7 @@ "select_album_cover": "ã‚ĸãƒĢバムã‚Ģバãƒŧを選択", "select_all": "全ãĻ選択", "select_all_duplicates": "全ãĻãŽé‡č¤‡ã‚’é¸æŠž", + "select_all_in": "{group}ぎすずãĻを選択", "select_avatar_color": "ã‚ĸバã‚ŋãƒŧãŽč‰˛ã‚’é¸æŠž", "select_face": "éĄ”ã‚’é¸æŠž", "select_featured_photo": "äēēį‰Šå†™įœŸã‚’é¸æŠž", @@ -1625,6 +1629,7 @@ "server_info_box_server_url": " ã‚ĩãƒŧバãƒŧぎURL", "server_offline": "ã‚ĩãƒŧバãƒŧがã‚Ēãƒ•ãƒŠã‚¤ãƒŗã§ã™", "server_online": "ã‚ĩãƒŧバãƒŧがã‚ĒãƒŗãƒŠã‚¤ãƒŗã§ã™", + "server_privacy": "ã‚ĩãƒŧバãƒŧãƒ—ãƒŠã‚¤ãƒã‚ˇãƒŧ", "server_stats": "ã‚ĩãƒŧバãƒŧįĩąč¨ˆ", "server_version": "ã‚ĩãƒŧバãƒŧバãƒŧã‚¸ãƒ§ãƒŗ", "set": "č¨­åŽš", @@ -1634,6 +1639,7 @@ "set_date_of_birth": "į”Ÿåš´æœˆæ—Ĩã‚’č¨­åŽš", "set_profile_picture": "ãƒ—ãƒ­ãƒ•ã‚ŖãƒŧãƒĢį”ģåƒã‚’č¨­åŽš", "set_slideshow_to_fullscreen": "ã‚šãƒŠã‚¤ãƒ‰ã‚ˇãƒ§ãƒŧをフãƒĢ゚クãƒĒãƒŧãƒŗãĢする", + "set_stack_primary_asset": "プナイマãƒĒã‚ĸã‚ģットとしãĻč¨­åŽš", "setting_image_viewer_help": "å†™įœŸã‚’ã‚ŋップするとã‚ĩムネイãƒĢãƒģ中į”ģčŗĒãƒģã‚ĒãƒĒジナãƒĢぎ順ãĢčĒ­ãŋčžŧãŋぞす", "setting_image_viewer_original_subtitle": "ã‚ĒãƒĒジナãƒĢぎį”ģåƒã‚’čĄ¨į¤ēしたいときãĢã‚ĒãƒŗãĢしãĻください。(最大į”ģčŗĒã§čĄ¨į¤ēされるぎで、デãƒŧã‚ŋとį̝æœĢぎ゚トãƒŦãƒŧジぎæļˆč˛ģ量がåĸ—えぞす)", "setting_image_viewer_original_title": "ã‚ĒãƒĒジナãƒĢをčĒ­ãŋčžŧむ", @@ -1642,7 +1648,6 @@ "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": "すぐãĢčĄŒã†", @@ -1705,6 +1710,7 @@ "shared_link_expires_second": "{count}į§’åžŒãĢ有劚期限切れ", "shared_link_expires_seconds": "{count}į§’åžŒãĢ有劚期限切れ", "shared_link_individual_shared": "1æžšãšã¤å…ąæœ‰ã•ã‚ŒãĻいぞす", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "å…ąæœ‰æ¸ˆãŋぎãƒĒãƒŗã‚¯ã‚’įŽĄį†", "shared_link_options": "å…ąæœ‰ãƒĒãƒŗã‚¯ãŽã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", "shared_links": "å…ąæœ‰ãƒĒãƒŗã‚¯", @@ -1771,6 +1777,7 @@ "start_date": "開始æ—Ĩ", "state": "éƒŊ道åēœįœŒ", "status": "゚テãƒŧã‚ŋ゚", + "stop_casting": "ã‚­ãƒŖã‚šãƒˆã‚’åœæ­ĸ", "stop_motion_photo": "゚トップãƒĸãƒŧã‚ˇãƒ§ãƒŗå†™įœŸ", "stop_photo_sharing": "å†™įœŸãŽå…ąæœ‰ã‚’į„ĄåŠšåŒ–ã—ãžã™ã‹īŧŸ", "stop_photo_sharing_description": "{partner} はあãĒãŸãŽå†™įœŸãĢã‚ĸクã‚ģ゚できãĒくãĒりぞす。", @@ -1818,7 +1825,7 @@ "theme_setting_three_stage_loading_title": "三æŽĩ階čĒ­ãŋčžŧãŋをã‚ĒãƒŗãĢする", "they_will_be_merged_together": "ã“ã‚Œã‚‰ã¯ä¸€įˇ’ãĢįĩąåˆã•れぞす", "third_party_resources": "ã‚ĩãƒŧドパãƒŧãƒ†ã‚ŖãƒŧãƒĒã‚Ŋãƒŧ゚", - "time_based_memories": "時間ãĢã‚ˆã‚‹ãƒĄãƒĸãƒĒãƒŧ", + "time_based_memories": "過åŽģぎ思いå‡ē", "timeline": "ã‚ŋã‚¤ãƒ ãƒŠã‚¤ãƒŗ", "timezone": "ã‚ŋイムゞãƒŧãƒŗ", "to_archive": "ã‚ĸãƒŧã‚Ģイブ", @@ -1828,7 +1835,6 @@ "to_parent": "上äŊãŽéšŽåą¤ã¸", "to_trash": "ã‚´ãƒŸįŽą", "toggle_settings": "č¨­åŽšã‚’ãƒˆã‚°ãƒĢ", - "toggle_theme": "ダãƒŧクテãƒŧマを切りæ›ŋえ", "total": "合荈", "total_usage": "įˇäŊŋį”¨é‡", "trash": "ã‚´ãƒŸįŽą", @@ -1850,6 +1856,7 @@ "unable_to_setup_pin_code": "PINã‚ŗãƒŧドをã‚ģットã‚ĸップできぞせんでした", "unarchive": "ã‚ĸãƒŧã‚Ģã‚¤ãƒ–ã‚’č§Ŗé™¤", "unarchived_count": "{count, plural, other {#枚ã‚ĸãƒŧã‚Ģã‚¤ãƒ–ã‚’č§Ŗé™¤ã—ãžã—ãŸ}}", + "undo": "元ãĢæˆģす", "unfavorite": "お気ãĢå…Ĩã‚Šč§Ŗé™¤", "unhide_person": "äēēį‰ŠãŽéžčĄ¨į¤ēã‚’č§Ŗé™¤", "unknown": "不明", @@ -1866,10 +1873,9 @@ "unsaved_change": "æœĒäŋå­˜ãŽå¤‰æ›´", "unselect_all": "全ãĻé¸æŠžč§Ŗé™¤", "unselect_all_duplicates": "全ãĻãŽé‡č¤‡ãŽé¸æŠžã‚’č§Ŗé™¤", + "unselect_all_in": "{group}ぎすずãĻãŽé¸æŠžã‚’č§Ŗé™¤", "unstack": "゚ã‚ŋãƒƒã‚¯ã‚’č§Ŗé™¤", "unstacked_assets_count": "{count, plural, one {#個ぎã‚ĸã‚ģット} other {#個ぎã‚ĸã‚ģット}}を゚ã‚ŋãƒƒã‚¯ã‹ã‚‰č§Ŗé™¤ã—ãžã—ãŸ", - "untracked_files": "æœĒčŋŊčˇĄãƒ•ã‚Ąã‚¤ãƒĢ", - "untracked_files_decription": "ã“ã‚Œã‚‰ãŽãƒ•ã‚Ąã‚¤ãƒĢはã‚ĸプãƒĒã‚ąãƒŧã‚ˇãƒ§ãƒŗãĢã‚ˆãŖãĻčŋŊčˇĄã•ã‚ŒãĻいぞせん。これらはį§ģå‹•ãŽå¤ąæ•—ã€ã‚ĸップロãƒŧドぎ中断、ぞたはバグãĢより取り掋されたもぎである可čƒŊ性がありぞす", "up_next": "æŦĄã¸", "updated_at": "更新", "updated_password": "パ゚ワãƒŧドを更新しぞした", @@ -1886,6 +1892,7 @@ "upload_success": "ã‚ĸップロãƒŧド成功、新しくã‚ĸップロãƒŧドされたã‚ĸã‚ģットをčĻ‹ã‚‹ãĢはペãƒŧジを更新しãĻください。", "upload_to_immich": "ImmichãĢã‚ĸップロãƒŧド ({count})", "uploading": "ã‚ĸップロãƒŧド中", + "url": "URL", "usage": "äŊŋį”¨åŽšé‡", "use_biometric": "į”ŸäŊ“čĒč¨ŧã‚’ã”åˆŠį”¨ãã ã•ã„", "use_current_connection": "įžåœ¨ãŽæŽĨįƒ…å ąã‚’äŊŋᔍ", @@ -1896,6 +1903,7 @@ "user_liked": "{user} が{type, select, photo {ã“ãŽå†™įœŸã‚’} video {こぎ動į”ģを} asset {こぎã‚ĸã‚ģットを} other {}}いいねしぞした", "user_pin_code_settings": "PINã‚ŗãƒŧド", "user_pin_code_settings_description": "PINã‚ŗãƒŧãƒ‰ã‚’įŽĄį†", + "user_privacy": "ãƒĻãƒŧã‚ļãƒŧãƒ—ãƒŠã‚¤ãƒã‚ˇãƒŧ", "user_purchase_settings": "čŗŧå…Ĩ", "user_purchase_settings_description": "čŗŧå…Ĩã‚’įŽĄį†", "user_role_set": "{user} を{role}ãĢč¨­åŽšã—ãžã—ãŸ", @@ -1911,11 +1919,6 @@ "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_3": "をįĸēčĒã—ãĻください。docker-composeや.envãƒ•ã‚Ąã‚¤ãƒĢが最新ぎįŠļ態ãĢ更新済ãŋã‹ã€į‰šãĢWatchTowerãĒおぎツãƒŧãƒĢをäŊŋãŖãĻDockerã‚¤ãƒĄãƒŧジをč‡Ē動ã‚ĸップデãƒŧトされãĻる斚はįĸēčĒã—ãĻください。", - "version_announcement_overlay_title": "ã‚ĩãƒŧバãƒŧãŽæœ€æ–°į‰ˆãŒå…Ŧ開中🎉", "version_history": "バãƒŧã‚¸ãƒ§ãƒŗåąĨæ­´", "version_history_item": "{date}ãĢ{version}ã‚’ã‚¤ãƒŗã‚šãƒˆãƒŧãƒĢ", "video": "動į”ģ", @@ -1935,6 +1938,7 @@ "view_previous_asset": "前ぎã‚ĸã‚ģットをčĻ‹ã‚‹", "view_qr_code": "QRã‚ŗãƒŧドをčĻ‹ã‚‹", "view_stack": "ビãƒĨãƒŧ゚ã‚ŋック", + "view_user": "ãƒĻãƒŧã‚ļãƒŧをčĻ‹ã‚‹", "viewer_remove_from_stack": "゚ã‚ŋックから外す", "viewer_stack_use_as_main_asset": "ãƒĄã‚¤ãƒŗãŽį”ģ像としãĻäŊŋį”¨ã™ã‚‹", "viewer_unstack": "゚ã‚ŋãƒƒã‚¯ã‚’č§Ŗé™¤", diff --git a/i18n/ka.json b/i18n/ka.json index e0421d6225..13b0e1d065 100644 --- a/i18n/ka.json +++ b/i18n/ka.json @@ -42,8 +42,6 @@ "backup_keep_last_amount": "áƒŦინა დამპების áƒ¨áƒ”áƒĄáƒáƒœáƒáƒ áƒŠáƒŖáƒœáƒ”áƒ‘áƒ”áƒšáƒ˜ რაოდენობა", "backup_settings": "მონაáƒĒემთა ბაზის დამპის მორგება", "backup_settings_description": "მონაáƒĒემთა ბაზის პარამეáƒĸრების ამრთვა. შენიშვნა: ამ დავალებების მონიáƒĸორინგი არ ხდება და თáƒĨვენ არ მოგივათ შეáƒĸყობინება, áƒ—áƒŖ იქ ჩავარდება.", - "check_all": "შეამოáƒŦმე ყველა", - "cleanup": "áƒ’áƒáƒĄáƒŖáƒ¤áƒ—áƒáƒ•áƒ”áƒ‘áƒ", "cleared_jobs": "დავალებები {job}-ისათვის გაáƒŦმენდილია", "config_set_by_file": "მიმდინარე áƒ™áƒáƒœáƒ¤áƒ˜áƒ’áƒŖáƒ áƒáƒĒია ფაილის მიერ არიქ áƒ“áƒáƒ§áƒ”áƒœáƒ”áƒ‘áƒŖáƒšáƒ˜", "confirm_delete_library": "ნამდვილად გინდა {library} ბიბლიოთეკის áƒŦაშლა?", diff --git a/i18n/kk.json b/i18n/kk.json index e3a1f51c3e..5dabe59a13 100644 --- a/i18n/kk.json +++ b/i18n/kk.json @@ -1,4 +1,6 @@ { + "about": "ĐĸŅƒŅ€Đ°ĐģŅ‹", + "account": "ĐĸŅ–Ņ€ĐēĐĩĐģĐŗŅ–", "add_photos": "ŅŅƒŅ€Đĩ҂҂ĐĩŅ€Đ´Ņ– Ō›ĐžŅŅƒ", "add_to": "Ō›ĐžŅŅƒâ€Ļ", "add_to_album": "аĐģŅŒĐąĐžĐŧŌ“Đ° Ō›ĐžŅŅƒ", @@ -9,7 +11,6 @@ "added_to_archive": "ĐŅ€Ņ…Đ¸Đ˛ĐēĐĩ ĐļŅ–ĐąĐĩҀҖĐģĐŗĐĩĐŊ", "added_to_favorites": "Ņ‚Đ°ŌŖĐ´Đ°ŅƒĐģŅ‹ĐģĐ°Ņ€Ō“Đ° Ō›ĐžŅŅ‹ĐģŌ“Đ°ĐŊ", "admin": { - "check_all": "Đ‘Ķ™Ņ€Ņ–ĐŊ Ņ‚ĐĩĐēҁĐĩҀҖĐŋ аĐģ҃", "create_job": "Đ–ŌąĐŧҋҁ҂ҋ ĐąĐ°ŅŅ‚Đ°Ņƒ" }, "zoom_image": "ŅŅƒŅ€Đĩ҂҂Җ Ō¯ĐģĐēĐĩĐšŅ‚Ņƒ" diff --git a/i18n/ko.json b/i18n/ko.json index 38a88ecd3c..01b29e6776 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -26,7 +26,6 @@ "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": "ëŗ´ę´€í•¨ė— ėļ”ę°€ë˜ė—ˆėŠĩ니다.", @@ -45,8 +44,6 @@ "backup_keep_last_amount": "ëŗ´ę´€í•  ė´ė „ ë¤í”„ė˜ 눘", "backup_settings": "ë°ė´í„°ë˛ ė´ėŠ¤ 덤프 네렕", "backup_settings_description": "ë°ė´í„°ë˛ ė´ėŠ¤ 덤프 ė„¤ė •ė„ 관ëĻŦ합니다. 및溠: ė´ ėž‘ė—…ė€ ė§„í–‰ 및 ė‹¤íŒ¨ ė—Ŧëļ€ëĨŧ í™•ė¸í•  눘 ė—†ėŠĩ니다.", - "check_all": "ëĒ¨ë‘ í™•ė¸", - "cleanup": "ė •ëĻŦ", "cleared_jobs": "ėž‘ė—… ė¤‘ë‹¨: {job}", "config_set_by_file": "현ėžŦ ęĩŦė„ąė€ 네렕 파ėŧė„ í†ĩ해 ė§€ė •ë˜ė–´ ėžˆėŠĩ니다.", "confirm_delete_library": "{library} ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", @@ -62,14 +59,12 @@ "disable_login": "ëĄœęˇ¸ė¸ ëš„í™œė„ąí™”", "duplicate_detection_job_description": "揰溄 학ėŠĩė„ í†ĩ해 뜠ė‚Ŧ한 ė´ë¯¸ė§€ëĨŧ ę°ė§€í•Šë‹ˆë‹¤. ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė´ í™œė„ąí™”ë˜ė–´ ėžˆė–´ė•ŧ 합니다.", "exclusion_pattern_description": "ė œė™¸ ęˇœėš™ė„ ė‚ŦėšŠí•˜ė—Ŧ íŠšė • 파ėŧęŗŧ 폴더ëĨŧ ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē”ė—ė„œ ė œė™¸í•  눘 ėžˆėŠĩ니다. ę°€ė ¸ė˜¤ę¸° ė›í•˜ė§€ ė•ŠëŠ” 파ėŧ(RAW 파ėŧ 등)ė´ í´ë”ė— ėĄ´ėžŦ하는 ę˛Ŋ뚰 ėœ ėšŠí•Šë‹ˆë‹¤.", - "external_library_created_at": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ ({date}뗐 ėƒė„ąë¨)", "external_library_management": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ 관ëĻŦ", "face_detection": "ė–ŧęĩ´ 氐맀", "face_detection_description": "揰溄 학ėŠĩė„ í†ĩ해 항ëĒŠė—ė„œ ė–ŧęĩ´ė„ ę°ė§€í•Šë‹ˆë‹¤. ë™ė˜ėƒė˜ ę˛Ŋ뚰 ė„Ŧ네ėŧ만 ëļ„ė„ė— ė‚ŦėšŠëŠë‹ˆë‹¤. \"ėƒˆëĄœęŗ ėš¨\"ė€ ëĒ¨ë“  항ëĒŠė„ (ėžŦ)래ëĻŦ하며, \"ė´ˆę¸°í™”\"는 현ėžŦ ëĒ¨ë“  ė–ŧęĩ´ ë°ė´í„°ëĨŧ ėļ”ę°€ëĄœ ė‚­ė œí•Šë‹ˆë‹¤. \"누ëŊ됨\"ė€ 땄링 래ëĻŦë˜ė§€ ė•Šė€ 항ëĒŠė„ ëŒ€ę¸°ė—´ė— ėļ”ę°€í•Šë‹ˆë‹¤. ė–ŧęĩ´ 氐맀氀 ė™„ëŖŒë˜ëŠ´ ę°ė§€ëœ ė–ŧęĩ´ë“¤ė€ ė–ŧęĩ´ ė¸ė‹ ë‹¨ęŗ„ëĄœ ë„˜ė–´ę°€ëŠ°, ę¸°ėĄ´ ė¸ëŦŧė´ë‚˜ ėƒˆëĄœėš´ ė¸ëŦŧ로 ęˇ¸ëŖší™”ëŠë‹ˆë‹¤.", "facial_recognition_job_description": "ę°ė§€ëœ ė–ŧęĩ´ė„ ė¸ëŦŧëŗ„ëĄœ ęˇ¸ëŖší™”í•Šë‹ˆë‹¤. ė´ ėž‘ė—…ė€ ė–ŧęĩ´ 氐맀 ėž‘ė—…ė´ ė™„ëŖŒëœ 후 ė§„í–‰ëŠë‹ˆë‹¤. \"ė´ˆę¸°í™”\"는 ëĒ¨ë“  ė–ŧęĩ´ė˜ ęˇ¸ëŖší™”ëĨŧ ë‹¤ė‹œ ė§„í–‰í•Šë‹ˆë‹¤. \"누ëŊ\"ė€ ęˇ¸ëŖší™”ë˜ė§€ ė•Šė€ ė–ŧęĩ´ė„ ëŒ€ę¸°ė—´ė— ėļ”ę°€í•Šë‹ˆë‹¤.", "failed_job_command": "{job} ėž‘ė—…ė—ė„œ {command} ė‹¤íŒ¨", "force_delete_user_warning": "ę˛Ŋęŗ : ė´ ėž‘ė—…ė€ 해당 ė‚ŦėšŠėžė™€ ꡸ ė‚ŦėšŠėžę°€ ė†Œėœ í•œ ëĒ¨ë“  항ëĒŠė„ ėĻ‰ė‹œ ė‚­ė œí•Šë‹ˆë‹¤. ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėœŧ늰, ė‚­ė œëœ 파ėŧė€ ëŗĩęĩŦ할 눘 ė—†ėŠĩ니다.", - "forcing_refresh_library_files": "ëĒ¨ë“  ëŧė´ë¸ŒëŸŦëĻŦ 파ėŧ ę°•ė œ ėƒˆëĄœęŗ ėš¨ 뤑...", "image_format": "í˜•ė‹", "image_format_description": "WebP는 JPEGëŗ´ë‹¤ 파ėŧ íŦ기가 ėž‘ė§€ë§Œ, ė¸ėŊ”ë”Šė— 더 ë§Žė€ ė‹œę°„ė´ ė†Œėš”ëŠë‹ˆë‹¤.", "image_fullsize_description": "ëŠ”íƒ€ë°ė´í„°ę°€ ė œęą°ëœ 렄랴 íŦ기 ė´ë¯¸ė§€ëĄœ, 확대 ëŗ´ę¸° ė‹œ ė‚ŦėšŠëŠë‹ˆë‹¤.", @@ -198,7 +193,7 @@ "oauth_enable_description": "OAuth ëĄœęˇ¸ė¸", "oauth_mobile_redirect_uri": "ëĒ¨ë°”ėŧ ëĻŦë‹¤ė´ë ‰íŠ¸ URI", "oauth_mobile_redirect_uri_override": "ëĒ¨ë°”ėŧ ëĻŦë‹¤ė´ë ‰íŠ¸ URI ėžŦė •ė˜", - "oauth_mobile_redirect_uri_override_description": "OAuth ęŗĩę¸‰ėžę°€ '{callback}'ęŗŧ ę°™ė€ ëĒ¨ë°”ėŧ URIëĨŧ 렜ęŗĩí•˜ė§€ ė•ŠëŠ” ę˛Ŋ뚰 í™œė„ąí™”í•˜ė„¸ėš”.", + "oauth_mobile_redirect_uri_override_description": "OAuth ęŗĩę¸‰ėžę°€ ''{callback}''ęŗŧ ę°™ė€ ëĒ¨ë°”ėŧ URIëĨŧ 렜ęŗĩí•˜ė§€ ė•ŠëŠ” ę˛Ŋ뚰 í™œė„ąí™”í•˜ė„¸ėš”.", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth ëĄœęˇ¸ė¸ 네렕 관ëĻŦ", "oauth_settings_more_details": "ė´ 기ëŠĨ뗐 대한 ėžė„¸í•œ ë‚´ėšŠė€ ëŦ¸ė„œëĨŧ ė°¸ėĄ°í•˜ė„¸ėš”.", @@ -210,8 +205,6 @@ "oauth_storage_quota_default_description": "ėž…ë Ĩí•˜ė§€ ė•Šė€ ę˛Ŋ뚰 ė‚ŦėšŠí•  GiB ë‹¨ėœ„ė˜ ę¸°ëŗ¸ 할당량 (ëŦ´ė œí•œ í• ë‹šëŸ‰ė˜ ę˛Ŋ뚰 0 ėž…ë Ĩ)", "oauth_timeout": "ėš”ė˛­ íƒ€ėž„ė•„ė›ƒ", "oauth_timeout_description": "ėš”ė˛­ íƒ€ėž„ė•„ė›ƒ (밀ëĻŦ봈 ë‹¨ėœ„)", - "offline_paths": "누ëŊ된 파ėŧ", - "offline_paths_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦė˜ 항ëĒŠė´ ė•„ë‹Œ 파ėŧė„ ėˆ˜ë™ėœŧ로 ė‚­ė œí•œ ę˛Ŋ뚰 ë°œėƒí•  눘 ėžˆėŠĩ니다.", "password_enable_description": "ė´ëŠ”ėŧęŗŧ 비밀번호로 ëĄœęˇ¸ė¸", "password_settings": "비밀번호 ëĄœęˇ¸ė¸", "password_settings_description": "비밀번호 ëĄœęˇ¸ė¸ 네렕 관ëĻŦ", @@ -221,9 +214,6 @@ "refreshing_all_libraries": "ëĒ¨ë“  ëŧė´ë¸ŒëŸŦëĻŦ ë‹¤ė‹œ 늤ėē” ė¤‘...", "registration": "관ëĻŦėž ęŗ„ė • ėƒė„ą", "registration_description": "ë‹šė‹ ė€ ė˛Ģ ë˛ˆė§¸ ė‚ŦėšŠėžė´ëŠ° 관ëĻŦėž ęļŒí•œė´ ėžë™ėœŧ로 ëļ€ė—Ŧ됩니다. 관ëĻŦ ėž‘ė—…ęŗŧ ė‚ŦėšŠėž ėƒė„ąė´ 가ëŠĨ합니다.", - "repair_all": "ëĒ¨ë‘ 눘ëĻŦ", - "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": "ë§ˆė§€ë§‰ėœŧ로 ė €ėžĨ된 네렕 ëŗĩ뛐", @@ -252,7 +242,6 @@ "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": "ė—…ëĄœë“œëœ 항ëĒŠė˜ 폴더 ęĩŦėĄ° 및 파ėŧ ė´ëĻ„ 관ëĻŦ", @@ -264,7 +253,6 @@ "template_email_invite_album": "ė•¨ë˛” ė´ˆëŒ€ėžĨ 템플ëĻŋ", "template_email_preview": "미ëĻŦëŗ´ę¸°", "template_email_settings": "ė´ëŠ”ėŧ 템플ëĻŋ", - "template_email_settings_description": "ė‚ŦėšŠėž ė •ė˜ ė´ëŠ”ėŧ 템플ëĻŋ 관ëĻŦ", "template_email_update_album": "ė•¨ë˛” ė—…ë°ė´íŠ¸ ė•ˆë‚´ 템플ëĻŋ", "template_email_welcome": "í™˜ė˜ 메ėŧ 템플ëĻŋ", "template_settings": "ė•ŒëĻŧ 템플ëĻŋ", @@ -273,7 +261,6 @@ "theme_custom_css_settings_description": "Immich뗐 ė ėšŠí•  ė‚ŦėšŠėž ė •ė˜ CSS(Cascading Style Sheets) 네렕", "theme_settings": "테마 네렕", "theme_settings_description": "Immich ė›š ė¸í„°íŽ˜ė´ėŠ¤ ė‚ŦėšŠėž ė •ė˜", - "these_files_matched_by_checksum": "랴íŦė„Ŧė´ 동ėŧ한 파ėŧ ëĒŠëĄėž…ë‹ˆë‹¤.", "thumbnail_generation_job": "ė„Ŧ네ėŧ ėƒė„ą", "thumbnail_generation_job_description": "각 항ëĒŠė— 대한 큰 ė„Ŧ네ėŧ, ėž‘ė€ ė„Ŧ네ėŧ, 흐ëϰ ė„Ŧ네ėŧ 및 ė¸ëŦŧ ė„Ŧ네ėŧ ėƒė„ą", "transcoding_acceleration_api": "ę°€ė† API", @@ -304,7 +291,6 @@ "transcoding_hardware_acceleration_description": "ė‹¤í—˜ė ė¸ 기ëŠĨėž…ë‹ˆë‹¤. ė†ë„ę°€ í–Ĩėƒë˜ė§€ë§Œ 동ėŧ ëš„íŠ¸ë ˆė´íŠ¸ė—ė„œ í’ˆė§ˆė´ ėƒëŒ€ė ėœŧ로 ë‚Žė„ 눘 ėžˆėŠĩ니다.", "transcoding_hardware_decoding": "í•˜ë“œė›¨ė–´ 디ėŊ”딊", "transcoding_hardware_decoding_setting_description": "ė¸ėŊ”딊 ę°€ė†ė„ ėœ„í•´ ė—”ë“œ íˆŦ ė—”ë“œ ę°€ė†ė„ ė‚ŦėšŠí•Šë‹ˆë‹¤. ëĒ¨ë“  ë™ė˜ėƒė—ė„œ ėž‘ë™í•˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다.", - "transcoding_hevc_codec": "HEVC ėŊ”덱", "transcoding_max_b_frames": "ėĩœëŒ€ B í”„ë ˆėž„", "transcoding_max_b_frames_description": "ę°’ė´ 높ėœŧ늴 ė••ėļ• íš¨ėœ¨ė´ í–Ĩėƒë˜ė§€ë§Œ ė¸ėŊ”딊 ė†ë„ę°€ ė €í•˜ëŠë‹ˆë‹¤. ė˜¤ëž˜ëœ ę¸°ę¸°ė˜ í•˜ë“œė›¨ė–´ ę°€ė†ęŗŧ í˜¸í™˜ë˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다. 0ė„ ėž…ë Ĩ한 ę˛Ŋ뚰 B í”„ë ˆėž„ė„ ëš„í™œė„ąí™”í•˜ëŠ°, -1ė„ ėž…ë Ĩ한 ę˛Ŋ뚰 ėžë™ėœŧ로 ė„¤ė •í•Šë‹ˆë‹¤.", "transcoding_max_bitrate": "ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸", @@ -342,8 +328,6 @@ "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": "ė‚­ė œ 뜠똈 기간", @@ -402,10 +386,6 @@ "album_remove_user": "ė‚ŦėšŠėžëĨŧ ė œęą°í•˜ė‹œę˛ ėŠĩ니까?", "album_remove_user_confirmation": "{user}ë‹˜ė„ ė•¨ë˛”ė—ė„œ ė œęą°í•˜ė‹œę˛ ėŠĩ니까?", "album_share_no_users": "ė´ë¯¸ ëĒ¨ë“  ė‚ŦėšŠėžė™€ ė•¨ë˛”ė„ ęŗĩ뜠 ė¤‘ė´ęą°ë‚˜ 다ëĨ¸ ė‚ŦėšŠėžę°€ ė—†ëŠ” 것 같ėŠĩ니다.", - "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} ė•¨ë˛”ė—ė„œ ë‚˜ė˜´", @@ -539,6 +519,7 @@ "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": "ë‚¨ė€ 항ëĒŠ", @@ -576,21 +557,17 @@ "bulk_keep_duplicates_confirmation": "뤑ëŗĩ된 항ëĒŠ {count, plural, one {#氜ëĨŧ} other {#氜ëĨŧ}} 그대로 ėœ ė§€í•˜ė‹œę˛ ėŠĩ니까? ė´ ėž‘ė—…ė€ ė–´ë–¤ 항ëĒŠë„ ė‚­ė œí•˜ė§€ ė•Šęŗ , ëĒ¨ë“  뤑ëŗĩ ęˇ¸ëŖšė„ í™•ė¸í•œ 것ėœŧ로 래ëĻŦ합니다.", "bulk_trash_duplicates_confirmation": "뤑ëŗĩ된 항ëĒŠ {count, plural, one {#氜ëĨŧ} other {#氜ëĨŧ}} ėŧ괄 íœ´ė§€í†ĩėœŧ로 ė´ë™í•˜ė‹œę˛ ėŠĩ니까? ė´ ėž‘ė—…ė€ 각 ęˇ¸ëŖšė—ė„œ 가ėžĨ 큰 항ëĒŠë§Œ ë‚¨ę¸°ęŗ  ë‚˜ë¨¸ė§€ 뤑ëŗĩ 항ëĒŠė„ íœ´ė§€í†ĩėœŧ로 ė´ë™í•Šë‹ˆë‹¤.", "buy": "Immich ęĩŦ매", - "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": "뤑ëŗĩ 항ëĒŠ ({count})", - "cache_settings_image_cache_size": "ė´ë¯¸ė§€ ėēė‹œ íŦ기 ({count} 항ëĒŠ)", "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": "Immich ëĒ¨ë°”ėŧ ė•ąė˜ ėēė‹ą ë™ėž‘ ė œė–´", - "cache_settings_thumbnail_size": "ė„Ŧ네ėŧ ėēė‹œ íŦ기 ({count} 항ëĒŠ)", "cache_settings_tile_subtitle": "로ėģŦ ėŠ¤í† ëĻŦė§€ ë™ėž‘ ė œė–´", "cache_settings_tile_title": "로ėģŦ ėŠ¤í† ëĻŦė§€", "cache_settings_title": "ėēė‹œ 네렕", @@ -621,7 +598,6 @@ "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가 ė—°ę˛°ëœ ėƒíƒœė—ė„œë§Œ ė‹¤í–‰í•˜ė„¸ėš”. ė´ ėž‘ė—…ė€ ëLJ ëļ„ ė •ë„ ė†Œėš”ë  눘 ėžˆėŠĩ니다.", @@ -661,10 +637,11 @@ "confirm_keep_this_delete_others": "ė´ 항ëĒŠė„ ė œė™¸í•œ ėŠ¤íƒė˜ ëĒ¨ë“  항ëĒŠė´ ė‚­ė œëŠë‹ˆë‹¤. ęŗ„ė†í•˜ė‹œę˛ ėŠĩ니까?", "confirm_new_pin_code": "냈 PIN ėŊ”드 í™•ė¸", "confirm_password": "비밀번호 í™•ė¸", + "confirm_tag_face": "ė´ ė–ŧęĩ´ė„ {name}로 íƒœęˇ¸í•˜ė‹œę˛ ėŠĩ니까?", + "connected_to": "ė—°ę˛°ë¨:", "contain": "맞ėļ¤", "context": "ë‚´ėšŠ", "continue": "ęŗ„ė†", - "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": "ę¸°ę¸°ė—ė„œ ė‚­ė œ", @@ -758,6 +735,7 @@ "direction": "ë°Ší–Ĩ", "disabled": "ëš„í™œė„ąí™”ë¨", "disallow_edits": "ëˇ°ė–´ëĄœ 네렕", + "discord": "ë””ėŠ¤ėŊ”드", "discover": "íƒėƒ‰", "dismiss_all_errors": "ëĒ¨ë“  똤ëĨ˜ ëŦ´ė‹œ", "dismiss_error": "똤ëĨ˜ ëŦ´ė‹œ", @@ -774,7 +752,6 @@ "download_enqueue": "ëŒ€ę¸°ė—´ė— ë‹¤ėš´ëĄœë“œ", "download_error": "ë‹¤ėš´ëĄœë“œ 똤ëĨ˜", "download_failed": "ë‹¤ėš´ëĄœë“œ ė‹¤íŒ¨", - "download_filename": "파ėŧ: {filename}", "download_finished": "ë‹¤ėš´ëĄœë“œę°€ ė™„ëŖŒë˜ė—ˆėŠĩ니다.", "download_include_embedded_motion_videos": "내ėžĨ된 ë™ė˜ėƒ", "download_include_embedded_motion_videos_description": "ëĒ¨ė…˜ íŦí† ė— 내ėžĨ된 ë™ė˜ėƒė„ ę°œëŗ„ 파ėŧ로 íŦ함", @@ -799,6 +776,7 @@ "edit_date": "ë‚ ė§œ ëŗ€ę˛Ŋ", "edit_date_and_time": "ë‚ ė§œ 및 ė‹œę°„ ëŗ€ę˛Ŋ", "edit_description": "네ëĒ… íŽ¸ė§‘", + "edit_description_prompt": "냈 네ëĒ…ė„ ėž…ë Ĩí•˜ė„¸ėš”:", "edit_exclusion_pattern": "ė œė™¸ ęˇœėš™ ėˆ˜ė •", "edit_faces": "ė–ŧęĩ´ ėˆ˜ė •", "edit_import_path": "氀렏ė˜Ŧ ę˛Ŋ로 ėˆ˜ė •", @@ -848,7 +826,6 @@ "cant_get_number_of_comments": "댓글 눘ëĨŧ ëļˆëŸŦė˜Ŧ 눘 ė—†ėŒ", "cant_search_people": "ė¸ëŦŧė„ ę˛€ėƒ‰í•  눘 ė—†ėŒ", "cant_search_places": "ėžĨė†ŒëĨŧ ę˛€ėƒ‰í•  눘 ė—†ėŒ", - "cleared_jobs": "{job} ėž‘ė—… ė¤‘ë‹¨ë¨", "error_adding_assets_to_album": "ė•¨ë˛”ė— 항ëĒŠė„ ėļ”ę°€í•˜ë˜ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다.", "error_adding_users_to_album": "ė•¨ë˛”ė— ė‚ŦėšŠėžëĨŧ ėļ”ę°€í•˜ë˜ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다.", "error_deleting_shared_user": "ęŗĩėœ ëœ ė‚ŦėšŠėžëĨŧ ė œęą°í•˜ë˜ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다.", @@ -857,7 +834,6 @@ "error_removing_assets_from_album": "ė•¨ë˛”ė—ė„œ 항ëĒŠė„ ė œęą°í•˜ë˜ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다. ėŊ˜ė†”ė—ė„œ 넏ëļ€ ė •ëŗ´ëĨŧ í™•ė¸í•˜ė„¸ėš”.", "error_selecting_all_assets": "ëĒ¨ë“  항ëĒŠė„ ė„ íƒí•˜ë˜ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다.", "exclusion_pattern_already_exists": "ė´ ė œė™¸ ęˇœėš™ė€ ė´ë¯¸ ėĄ´ėžŦ합니다.", - "failed_job_command": "{job} ėž‘ė—… {command} ė‹¤íŒ¨", "failed_to_create_album": "ė•¨ë˛”ė„ ėƒė„ąí•˜ė§€ ëĒģ했ėŠĩ니다.", "failed_to_create_shared_link": "ęŗĩ뜠 링íŦëĨŧ ėƒė„ąí•˜ė§€ ëĒģ했ėŠĩ니다.", "failed_to_edit_shared_link": "ęŗĩ뜠 링íŦëĨŧ ėˆ˜ė •í•˜ė§€ ëĒģ했ėŠĩ니다.", @@ -876,7 +852,6 @@ "paths_validation_failed": "ę˛Ŋ로 {paths, plural, one {#氜} 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": "ëŒ“ę¸€ė„ ėļ”가할 눘 ė—†ėŠĩ니다.", @@ -895,7 +870,6 @@ "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ëĨŧ í†ĩ해 ė ‘ė† ė¤‘ė¸ė§€ í™•ė¸í•˜ė„¸ėš”.", "unable_to_create_admin_account": "관ëĻŦėž ęŗ„ė •ė„ ėƒė„ąí•  눘 ė—†ėŠĩ니다.", "unable_to_create_api_key": "API 키ëĨŧ ėƒė„ąí•  눘 ė—†ėŠĩ니다.", @@ -919,14 +893,9 @@ "unable_to_hide_person": "ė¸ëŦŧė„ 눍描 눘 ė—†ėŠĩ니다.", "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_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": "항ëĒŠė„ 냈 ė¸ëŦŧ뗐 할당할 눘 ė—†ėŠĩ니다.", @@ -934,11 +903,9 @@ "unable_to_remove_album_users": "ė•¨ë˛”ė—ė„œ ė‚ŦėšŠėžëĨŧ ė œęą°í•  눘 ė—†ėŠĩ니다.", "unable_to_remove_api_key": "API 키ëĨŧ ė‚­ė œí•  눘 ė—†ėŠĩ니다.", "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": "뤑ëŗĩ된 항ëĒŠė„ 래ëĻŦ할 눘 ė—†ėŠĩ니다.", @@ -974,7 +941,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": "ėƒí›„ {months}ę°œė›”", "exif_bottom_sheet_person_age_year_months": "ėƒí›„ 1년 {months}ę°œė›”", "exif_bottom_sheet_person_age_years": "{years}넏", @@ -1064,10 +1030,13 @@ "home_page_favorite_err_local": "ę¸°ę¸°ė˜ 항ëĒŠė€ ėĻę˛¨ė°žę¸°ė— ėļ”가할 눘 ė—†ėŠĩ니다. 건너뜁니다.", "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": "한 ë˛ˆė— ėĩœëŒ€ 30ę°œė˜ 항ëĒŠë§Œ ė—…ëĄœë“œí•  눘 ėžˆėŠĩ니다.", "host": "í˜¸ėŠ¤íŠ¸", "hour": "ė‹œę°„", + "id": "ID", "ignore_icloud_photos": "iCloud ė‚Ŧė§„ ė œė™¸", "ignore_icloud_photos_description": "iCloud뗐 ė €ėžĨ된 ė‚Ŧė§„ė´ Immich뗐 ė—…ëĄœë“œë˜ė§€ ė•ŠėŠĩ니다.", "image": "ė´ë¯¸ė§€", @@ -1140,6 +1109,7 @@ "list": "ëĒŠëĄ", "loading": "로드 뤑", "loading_search_results_failed": "ę˛€ėƒ‰ 결ęŗŧ 로드 ė‹¤íŒ¨", + "local_network": "로ėģŦ ë„¤íŠ¸ė›ŒíŦ", "local_network_sheet_info": "ė§€ė •í•œ Wi-Fi뗐 ė—°ę˛°ëœ ę˛Ŋ뚰 ė•ąė€ 해당 URLė„ í†ĩ해 ė„œë˛„ė— ė—°ę˛°í•Šë‹ˆë‹¤.", "location_permission": "ėœ„ėš˜ ęļŒí•œ", "location_permission_content": "ėžë™ ė „í™˜ 기ëŠĨė„ ė‚ŦėšŠí•˜ë ¤ëŠ´ Immich가 현ėžŦ Wi-Fi ë„¤íŠ¸ė›ŒíŦ ė´ëĻ„ė„ í™•ė¸í•˜ę¸° ėœ„í•œ 'ė •í™•í•œ ėœ„ėš˜' ęļŒí•œė´ í•„ėš”í•Šë‹ˆë‹¤.", @@ -1158,6 +1128,8 @@ "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": "ėœ íš¨í•˜ė§€ ė•Šė€ ė´ëŠ”ėŧ", @@ -1244,6 +1216,7 @@ "monthly_title_text_date_format": "yyyy년 Mė›”", "more": "ë”ëŗ´ę¸°", "move": "ė´ë™", + "move_off_locked_folder": "ėž ę¸´ í´ë”ė—ė„œ í•´ė œ", "move_to_locked_folder": "ėž ę¸´ 폴더로 ė´ë™", "move_to_locked_folder_confirmation": "ė´ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė´ ëĒ¨ë“  ė•¨ë˛”ė—ė„œ ė œęą°ë˜ëŠ°, ėž ę¸´ í´ë”ė—ė„œë§Œ ëŗŧ 눘 ėžˆėŠĩ니다.", "moved_to_archive": "ëŗ´ę´€í•¨ėœŧ로 항ëĒŠ {count, plural, one {#氜} other {#氜}} ė´ë™ë¨", @@ -1301,17 +1274,15 @@ "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": "ė´ ėžĨėš˜ė—ė„œ", "onboarding": "ė˜¨ëŗ´ë”Š", "onboarding_privacy_description": "ė´ ė„ íƒė  기ëŠĨė€ 뙏ëļ€ ė„œëš„ėŠ¤ëĨŧ ė‚ŦėšŠí•˜ëŠ°, 관ëĻŦėž ė„¤ė •ė—ė„œ ė–¸ė œë“  ëš„í™œė„ąí™”í•  눘 ėžˆėŠĩ니다.", "onboarding_theme_description": "ėƒ‰ėƒ 테마ëĨŧ ė„ íƒí•˜ė„¸ėš”. ë‚˜ė¤‘ė— ė„¤ė •ė—ė„œ ëŗ€ę˛Ŋ할 눘 ėžˆėŠĩ니다.", - "onboarding_welcome_description": "ëLJ 氀맀 ėŧë°˜ė ė¸ ė„¤ė •ė„ ė§„í–‰í•˜ę˛ ėŠĩ니다.", "onboarding_welcome_user": "{user}님, í™˜ė˜í•Šë‹ˆë‹¤", "online": "똍ëŧė¸", "only_favorites": "ėĻę˛¨ė°žę¸°ë§Œ", @@ -1488,6 +1459,7 @@ "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": "ėļ”ė–ĩė—ė„œ ė‚Ŧė§„ ė œęą°", @@ -1585,6 +1557,7 @@ "search_settings": "네렕 ę˛€ėƒ‰", "search_state": "맀뗭 ę˛€ėƒ‰...", "search_suggestion_list_smart_search_hint_1": "ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė´ ę¸°ëŗ¸ė ėœŧ로 í™œė„ąí™”ë˜ė–´ ėžˆėŠĩ니다. ëŠ”íƒ€ë°ė´í„°ëĄœ ę˛€ėƒ‰í•˜ë ¤ëŠ´ ë‹¤ėŒė„ ė‚ŦėšŠí•˜ė„¸ėš”. ", + "search_suggestion_list_smart_search_hint_2": "m:ę˛€ėƒ‰ė–´", "search_tags": "태그로 ę˛€ėƒ‰...", "search_timezone": "ė‹œę°„ëŒ€ ę˛€ėƒ‰...", "search_type": "ę˛€ėƒ‰ ėĸ…ëĨ˜", @@ -1633,7 +1606,6 @@ "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": "ėĻ‰ė‹œ", @@ -1696,6 +1668,7 @@ "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": "ęŗĩ뜠 링íŦ", @@ -1819,7 +1792,6 @@ "to_parent": "ėƒėœ„ 항ëĒŠėœŧ로", "to_trash": "ė‚­ė œ", "toggle_settings": "네렕 ëŗ€ę˛Ŋ", - "toggle_theme": "다íŦ ëĒ¨ë“œ ė‚ŦėšŠ", "total": "렄랴", "total_usage": "ė´ ė‚ŦėšŠëŸ‰", "trash": "íœ´ė§€í†ĩ", @@ -1859,9 +1831,8 @@ "unselect_all_duplicates": "ëĒ¨ë‘ ė„ íƒ í•´ė œ", "unstack": "ėŠ¤íƒ í•´ė œ", "unstacked_assets_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ė˜ ėŠ¤íƒė„ í•´ė œí–ˆėŠĩ니다.", - "untracked_files": "ėļ”ė ë˜ė§€ ė•ŠëŠ” 파ėŧ", - "untracked_files_decription": "ė• í”ŒëĻŦėŧ€ė´ė…˜ė—ė„œ ėļ”ė ë˜ė§€ ė•ŠëŠ” 파ėŧ ëĒŠëĄėž…ë‹ˆë‹¤. ė´ë™ ė‹¤íŒ¨, ė—…ëĄœë“œ ė¤‘ë‹¨ 또는 버그로 ė¸í•´ ë°œėƒí•  눘 ėžˆėŠĩ니다.", "up_next": "ë‹¤ėŒ", + "updated_at": "ė—…ë°ė´íŠ¸ë¨", "updated_password": "비밀번호가 ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다.", "upload": "ė—…ëĄœë“œ", "upload_concurrency": "ė—…ëĄœë“œ ë™ė‹œė„ą", @@ -1876,11 +1847,13 @@ "upload_success": "ė—…ëĄœë“œę°€ ė™„ëŖŒë˜ė—ˆėŠĩ니다. ė—…ëĄœë“œëœ 항ëĒŠė„ ëŗ´ë ¤ëŠ´ íŽ˜ė´ė§€ëĨŧ ėƒˆëĄœęŗ ėš¨í•˜ė„¸ėš”.", "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 ėŊ”드", @@ -1900,11 +1873,6 @@ "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_title": "냈 ė„œë˛„ ë˛„ė „ ė‚ŦėšŠ 가ëŠĨ 🎉", "version_history": "ë˛„ė „ 기록", "version_history_item": "{date} ë˛„ė „ {version} ė„¤ėš˜", "video": "ë™ė˜ėƒ", diff --git a/i18n/lt.json b/i18n/lt.json index 0e11e41676..d5e6ff20ed 100644 --- a/i18n/lt.json +++ b/i18n/lt.json @@ -4,6 +4,7 @@ "account_settings": "Paskyros nustatymai", "acknowledge": "Patvirtinti", "action": "Veiksmas", + "action_common_update": "Naujinti", "actions": "Veiksmai", "active": "Vykdoma", "activity": "Veikla", @@ -20,15 +21,18 @@ "add_partner": "Pridėti partnerį", "add_path": "Pridėti kelią", "add_photos": "Pridėti nuotraukÅŗ", + "add_tag": "Pridėti Åžymą", "add_to": "Pridėti įâ€Ļ", "add_to_album": "Pridėti į albumą", + "add_to_album_bottom_sheet_added": "Pridėta į {album}", + "add_to_album_bottom_sheet_already_exists": "Jau yra albume {album}", "add_to_shared_album": "Pridėti į bendrinamą albumą", "add_url": "Pridėti URL", "added_to_archive": "Pridėta į archyvą", "added_to_favorites": "Pridėta prie mėgstamiausiÅŗ", "added_to_favorites_count": "{count, plural, one {# pridėtas} few {# pridėti} other {# pridėta}} prie mėgstamiausiÅŗ", "admin": { - "asset_offline_description": "Å is iÅĄorinės bibliotekos elementas nebepasiekiamas diske ir buvo perkeltas į ÅĄiukÅĄliadėŞę. Jei failas buvo perkeltas toje pačioje bibliotekoje, laiko skalėje rasite naują atitinkamą elementą. Jei norite ÅĄÄ¯ elementą atkurti, įsitikinkite, kad Immich gali pasiekti failą Åžemiau nurodytu adresu, ir suvykdykite bibliotekos skanavimą.", + "asset_offline_description": "Å is iÅĄorinės bibliotekos elementas nebepasiekiamas diske ir buvo perkeltas į ÅĄiukÅĄliadėŞę. Jei failas buvo perkeltas toje pačioje bibliotekoje, laiko skalėje rasite naują atitinkamą elementą. Jei norite ÅĄÄ¯ elementą atkurti, įsitikinkite, kad Immich gali pasiekti failą Åžemiau nurodytu adresu, ir suvykdykite bibliotekos skenavimą.", "authentication_settings": "Autentifikavimo nustatymai", "authentication_settings_description": "Tvarkyti slaptaÅžodÅžiÅŗ, OAuth ir kitus autentifikavimo nustatymus", "authentication_settings_disable_all": "Ar tikrai norite iÅĄjungti visus prisijungimo bÅĢdus? Prisijungimas bus visiÅĄkai iÅĄjungtas.", @@ -39,8 +43,6 @@ "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,21 +53,22 @@ "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", + "cron_expression_description": "Nustatyti skenavimo intervalą naudojant cron formatą. Norėdami gauti daugiau informacijos ÅžiÅĢrėkite Crontab Guru", "cron_expression_presets": "IÅĄankstiniai Cron nustatymai", "disable_login": "IÅĄjungti prisijungimą", - "duplicate_detection_job_description": "Vykdykite maÅĄininį mokymąsi panaÅĄiÅŗ vaizdÅŗ aptikimui. Priklauso nuo iÅĄmaniosios paieÅĄkos", + "duplicate_detection_job_description": "Vykdyti maÅĄininį mokymąsi panaÅĄiÅŗ vaizdÅŗ aptikimui. Priklauso nuo iÅĄmaniosios paieÅĄkos", "exclusion_pattern_description": "IÅĄimčiÅŗ ÅĄablonai leidÅžia nepaisyti failÅŗ ir aplankÅŗ skenuojant jÅĢsÅŗ biblioteką. Tai yra naudinga, jei turite aplankÅŗ su failais, kuriÅŗ nenorite importuoti, pavyzdÅžiui, RAW failai.", - "external_library_created_at": "IÅĄorinė biblioteka (sukurta {date})", "external_library_management": "IÅĄoriniÅŗ bibliotekÅŗ tvarkymas", "face_detection": "VeidÅŗ aptikimas", "face_detection_description": "VeidÅŗ aptikimas bibliotekos elementuose naudojant maÅĄininį mokymąsi. Vaizdo įraÅĄÅŗ atveju naudojama tik miniatiÅĢra. \"Atnaujinti\" iÅĄ naujo nuskaito visus bibliotekos elementus. \"Atstatyti\" ne tik atnaujina, bet ir iÅĄvalo visus esamus veidÅŗ duomenis. \"TrÅĢkstami\" nuskaito tik dar nenuskaitytus bibliotekos elementus. VeidÅŗ aptikimo darbui pasibaigus, aptikti veidai patenka į veidÅŗ atpaÅžinimo darbÅŗ eilę, kur jie priskiriami jau esamiems ar naujai atpaÅžintiems Åžmonėms.", "facial_recognition_job_description": "AptiktÅŗ veidÅŗ atpaÅžinimas ir priskyrimas Åžmonėms. Å is darbas vykdomas pasibaigus \"veidÅŗ aptikimo\" darbui. \"Atstatyti\" (per)grupuoja visus aptiktus veidus. \"TrÅĢkstami\" apdoroja jokiam Åžmogui dar nepriskirtus aptiktus veidus.", "failed_job_command": "Darbo {job} komanda {command} nepavyko", "force_delete_user_warning": "ÄŽSPĖJIMAS: Å is veiksmas iÅĄ karto paÅĄalins naudotoją ir visą jo informaciją. Å is Åžingsnis nesugrÄ…Åžinamas ir failÅŗ nebus galima atkurti.", - "forcing_refresh_library_files": "Priverstinai atnaujinami visi failai bibliotekoje", "image_format": "Formatas", - "image_format_description": "WebP sukuria maÅžesnius failus nei JPEG, bet lėčiau juos apdoroja.", + "image_format_description": "WebP sukuria maÅžesnius failus nei JPEG, tačiau lėčiau juos apdoroja.", + "image_fullsize_enabled": "ÄŽgalinti pilno dydÅžio nuotraukÅŗ generavimą", + "image_fullsize_quality_description": "Pilno dydÅžio nuotraukÅŗ kokybė 1-100. Didesnė yra geresnė, tačiau sukuria didesniu failus.", + "image_fullsize_title": "Pilno dydÅžio nuotraukÅŗ Nustatymai", "image_prefer_embedded_preview": "Pageidautinai rodyti įterptą perÅžiÅĢrą", "image_prefer_wide_gamut": "Teikti pirmenybę plačiai gamai", "image_preview_description": "Vidutinio dydÅžio vaizdas su iÅĄvalytais metaduomenimis, naudojamas kai ÅžiÅĢrimas vienas objektas arba maÅĄininiam mokymuisi", @@ -76,7 +79,7 @@ "image_resolution_description": "Didesnės rezoliucijos gali iÅĄsaugoti daugiau detaliÅŗ, bet ilgiau uÅžtrunka uÅžkoduoti, failai yra didesni ir programos reagavimo laikas gali sumaŞėti.", "image_settings": "Nuotraukos nustatymai", "image_settings_description": "Keisti sugeneruotÅŗ nuotraukÅŗ kokybę ir rezoliuciją", - "image_thumbnail_description": "MaÅža miniatiÅĢra su iÅĄvalytais metaduomenimis, naudojama kai ÅžiÅĢrima nuotraukÅŗ grupės, kaip pagrindinėje laiko juostoje", + "image_thumbnail_description": "MaÅža miniatiÅĢra su iÅĄvalytais metaduomenimis, naudojama kai ÅžiÅĢrimos nuotraukÅŗ grupės, kaip ir pagrindinėje laiko juostoje", "image_thumbnail_quality_description": "MiniatiÅĢros kokybė nuo 1-100. AukÅĄtesnės reikÅĄmės yra geriau, bet pagaminami didesni failai ir gali bÅĢti sulėtintas programos reagavimo greitis.", "image_thumbnail_title": "MiniatiÅĢros nustatymai", "job_concurrency": "{job} lygiagretumas", @@ -88,12 +91,12 @@ "library_created": "Sukurta biblioteka: {library}", "library_deleted": "Biblioteka iÅĄtrinta", "library_import_path_description": "Nurodykite aplanką, kurį norite importuoti. Å iame aplanke, įskaitant poaplankius, bus nuskaityti vaizdai ir vaizdo įraÅĄai.", - "library_scanning": "Periodinis skanavimas", + "library_scanning": "Periodinis skenavimas", "library_scanning_description": "KonfigÅĢruoti periodinį bibliotekos skanavimą", - "library_scanning_enable_description": "ÄŽgalinti periodinį bibliotekos skanavimą", + "library_scanning_enable_description": "ÄŽgalinti periodinį bibliotekos skenavimą", "library_settings": "IÅĄorinė biblioteka", "library_settings_description": "Tvarkyti iÅĄorinės bibliotekos parametrus", - "library_tasks_description": "Atlikit bibliotekos uÅžduotis", + "library_tasks_description": "Skenuoti iÅĄorines bibliotekas, ieÅĄkant naujÅŗ arba pakeistÅŗ iÅĄtekliÅŗ", "library_watching_enable_description": "Stebėti iÅĄorines bibliotekas dėl failÅŗ pakeitimÅŗ", "library_watching_settings": "BibliotekÅŗ stebėjimas (EKSPERIMENTINIS)", "library_watching_settings_description": "AutomatiÅĄkai stebėti dėl pakeistÅŗ failÅŗ", @@ -103,6 +106,7 @@ "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": "Jei iÅĄjungta, visiÅĄkai identiÅĄki elementai vis tiek bus deduplikuoti.", "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Åŗ.", @@ -130,7 +134,7 @@ "map_enable_description": "ÄŽgalinti Åžemėlapio funkcijas", "map_gps_settings": "ÅŊemėlapio ir GPS nustatymai", "map_gps_settings_description": "Tvarkyti Åžemėlapio ir GPS (atvirkÅĄtinio geokodavimo) nustatymus", - "map_implications": "ÅŊemėlapio funkcija naudojasi iÅĄoriniu plyteliÅŗ servisu (tiles.immich.cloud)", + "map_implications": "ÅŊemėlapio funkcija naudojasi iÅĄorine plyteliÅŗ paslauga (tiles.immich.cloud)", "map_light_style": "Å viesioji tema", "map_manage_reverse_geocoding_settings": "Tvarkyti atvirkÅĄtinio geokodavimo nustatymus", "map_reverse_geocoding": "AtvirkÅĄtinis geokodavimas", @@ -176,40 +180,52 @@ "oauth_enable_description": "Prisijungti su OAuth", "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_mobile_redirect_uri_override_description": "ÄŽjunkite, kai OAuth teikėjas nepalaiko mobiliojo URI, tokio kaip ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "Tvarkyti OAuth prisijungimo nustatymus", "oauth_settings_more_details": "Detaliau apie ÅĄią funkciją galite paskaityti dokumentacijoje.", - "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.", + "oauth_storage_quota_default": "Numatyta atminties kvota (GiB)", + "oauth_timeout": "UÅžklausa virÅĄijo laiko limitą", + "oauth_timeout_description": "Laiko limitas uÅžklausoms milisekundėmis", "password_enable_description": "Prisijungti su el. paÅĄtu ir slaptaÅžodÅžiu", "password_settings": "Prisijungimas slaptaÅžodÅžiu", "password_settings_description": "Tvarkyti prisijungimo slaptaÅžodÅžiu nustatymus", "paths_validated_successfully": "Visi keliai patvirtinti sėkmingai", + "quota_size_gib": "Kvotos dydis (GiB)", "refreshing_all_libraries": "Perkraunamos visos bibliotekos", "registration": "Administratoriaus registracija", "registration_description": "Kadangi esate pirmasis ÅĄio sistemos naudotojas, jums bus priskirta administratoriaus rolė, ir bÅĢsite atsakingas uÅž administracines uÅžduotis ir papildomÅŗ naudotojÅŗ kÅĢrimą.", - "repair_all": "Pataisyti visus", "require_password_change_on_login": "Reikalauti, kad naudotojas pasikeistÅŗ slaptaÅžodį po pirmojo prisijungimo", "reset_settings_to_default": "Atstatyti nustatymus į numatytuosius", "reset_settings_to_recent_saved": "NustatymÅŗ atstatymas į neseniai iÅĄsaugotus nustatymus", + "scanning_library": "Biblioteka skenuojama", + "search_jobs": "IeÅĄkoma darbÅŗâ€Ļ", "send_welcome_email": "SiÅŗsti sveikinimo el. laiÅĄką", "server_external_domain_settings": "IÅĄorinis domenas", "server_external_domain_settings_description": "Bendrinimo nuorodÅŗ domenas, įskaitant http(s)://", + "server_public_users": "VieÅĄi naudotojai", + "server_public_users_description": "Pridedant naudotoją į bendrinamus albumus, rodomas visÅŗ naudotojÅŗ sąraÅĄas (vardas ir el. paÅĄtas). Jei iÅĄjungta, naudotojÅŗ sąraÅĄas bus prieinamas tik administratoriÅŗ paskyroms.", "server_settings": "Serverio nustatymai", "server_settings_description": "Tvarkyti serverio nustatymus", "server_welcome_message": "Sveikinimo praneÅĄimas", "server_welcome_message_description": "ÅŊinutė, rodoma prisijungimo puslapyje.", "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_date_time_description": "Elemento sukÅĢrimo laiko Åžymė yra naudojama laiko informacijai", + "storage_template_date_time_sample": "Pavyzdinis laikas {date}", "system_settings": "Sistemos nustatymai", "tag_cleanup_job": "ÅŊymÅŗ iÅĄvalymas", + "template_email_preview": "PerÅžiÅĢra", + "template_email_settings": "El. paÅĄto Å ablonai", + "template_settings": "PraneÅĄimÅŗ ÅĄablonai", + "template_settings_description": "Tvarkyti pasirinktinius praneÅĄimÅŗ ÅĄablonus", "theme_custom_css_settings": "Individualizuotas CSS", "theme_settings": "Temos nustatymai", "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_nvenc": "NVENC (reikalinga NVIDIA GPU)", + "transcoding_acceleration_qsv": "Quick Sync (reikalingas 7-os arba vėlesnės generacijos Intel procesorius)", "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_containers": "Priimami konteineriai", "transcoding_advanced_options_description": "Parinktys, kuriÅŗ daugelis naudotojÅŗ keisti neturėtÅŗ", @@ -219,7 +235,6 @@ "transcoding_constant_quality_mode": "Pastovios kokybės reÅžimas", "transcoding_hardware_acceleration": "Techninės įrangos spartinimas", "transcoding_hardware_decoding": "Aparatinis dekodavimas", - "transcoding_hevc_codec": "HEVC kodekas", "transcoding_max_bitrate": "Maksimalus bitÅŗ srautas", "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_video_codec": "Video kodekas", @@ -227,8 +242,6 @@ "trash_number_of_days": "DienÅŗ skaičius", "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_management": "NaudotojÅŗ valdymas", "user_password_has_been_reset": "Naudotojo slaptaÅžodis buvo iÅĄ naujo nustatytas:", @@ -326,7 +339,6 @@ "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_your_password": "Pakeisti slaptaÅžodį", "changed_visibility_successfully": "Matomumas pakeistas sėkmingai", - "check_all": "ÅŊymėti viską", "check_logs": "Tikrinti Åžurnalus", "city": "Miestas", "clear": "IÅĄvalyti", @@ -471,7 +483,6 @@ "unable_to_change_password": "Negalima pakeisti slaptaÅžodÅžio", "unable_to_complete_oauth_login": "Nepavyko prisijungti su OAuth", "unable_to_connect": "Nepavyko prisijungti", - "unable_to_connect_to_server": "Nepavyko prisijungti prie serverio", "unable_to_copy_to_clipboard": "Negalima kopijuoti į iÅĄkarpinę, įsitikinkite, kad prie puslapio prieinate per https", "unable_to_create_admin_account": "Nepavyko sukurti administratoriaus paskyros", "unable_to_create_api_key": "Nepavyko sukurti naujo API rakto", @@ -489,7 +500,6 @@ "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_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", @@ -497,7 +507,6 @@ "unable_to_refresh_user": "Nepavyksta atnaujinti naudotojo", "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", @@ -657,7 +666,6 @@ "notifications_setting_description": "Tvarkyti praneÅĄimus", "official_immich_resources": "OficialÅĢs Immich iÅĄtekliai", "offline": "Neprisijungęs", - "offline_paths": "Nepasiekiami adresai", "oldest_first": "Seniausias pirmas", "onboarding_welcome_user": "Sveiki atvykę, {user}", "online": "Prisijungęs", @@ -748,6 +756,8 @@ "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_assets_title": "PaÅĄalinti elementus?", + "remove_deleted_assets": "PaÅĄalinti IÅĄtrintus Elemenuts", "remove_from_album": "PaÅĄalinti iÅĄ albumo", "remove_from_favorites": "PaÅĄalinti iÅĄ mėgstamiausiÅŗ", "remove_from_shared_link": "PaÅĄalinti iÅĄ bendrinimo nuorodos", @@ -764,47 +774,82 @@ "repair_no_results_message": "Nesekami ir trÅĢkstami failai bus rodomi čia", "replace_with_upload": "Pakeisti naujai įkeltu failu", "require_password": "Reikalauti slaptaÅžodÅžio", + "rescan": "Perskenuoti", "reset": "Atstatyti", + "reset_password": "Atstayti slaptaÅžodį", + "reset_pin_code": "Atsatyti PIN kodą", + "reset_to_default": "Atkurti numatytuosius", "resolve_duplicates": "Sutvarkyti dublikatus", "resolved_all_duplicates": "Sutvarkyti visi dublikatai", "restore": "Atkurti", "restore_all": "Atkurti visus", "restore_user": "Atkurti naudotoją", + "restored_asset": "Atkurti elementą", "review_duplicates": "PerÅžiÅĢrėti dublikatus", "save": "IÅĄsaugoti", + "save_to_gallery": "IÅĄsaugoti galerijoje", "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": "ÄŽvyko klaida", "scan_all_libraries": "Skenuoti visas bibliotekas", "scan_library": "Skenuoti", "scan_settings": "Skenavimo nustatymai", + "scanning_for_album": "Skenuojama albumÅŗ...", "search": "IeÅĄkoti", + "search_albums": "IeÅĄkoti albumÅŗ", "search_by_context": "IeÅĄkoti pagal kontekstą", + "search_by_description": "IeÅĄkoti pagal apraÅĄymą", "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": "IeÅĄkoti pagal kameros gamintoją...", + "search_camera_model": "IeÅĄkoti kameros modelį...", + "search_city": "IeÅĄkoti miesto...", "search_country": "IeÅĄkoti ÅĄalies...", + "search_filter_date": "Data", + "search_filter_display_option_not_in_album": "Ne albume", + "search_filter_display_options": "Rodymo Nustatymai", + "search_filter_filename": "IeÅĄkoti pagal failo pavadinimą", + "search_filter_location": "Vietovė", + "search_filter_location_title": "Pasirinkti vietovę", + "search_filter_media_type": "Medijos timas", + "search_filter_media_type_title": "Pasirinkti medijos tipą", + "search_no_more_result": "Nėra daugiau rezultatÅŗ", "search_no_people_named": "Nėra ÅžmoniÅŗ vardu „{name}“", + "search_page_screenshots": "Ekrano nuotraukos", + "search_page_selfies": "Asmenukės", + "search_page_things": "Dalykai", + "search_page_view_all_button": "PerÅžiÅĢrėti visus", "search_people": "IeÅĄkoti ÅžmoniÅŗ", "search_places": "IeÅĄkoti vietÅŗ", + "search_rating": "IeÅĄkoti pagal įvertinimą...", "search_settings": "IeÅĄkoti nustatymÅŗ", "search_tags": "IeÅĄkoti ÅžymÅŗ...", + "search_timezone": "IeÅĄkoti laiko zonos...", "search_type": "PaieÅĄkos tipas", "search_your_photos": "IeÅĄkoti nuotraukÅŗ", "select_all_duplicates": "Pasirinkti visus dublikatus", + "select_all_in": "PaÅžymėti visus esančius {group}", "select_avatar_color": "Pasirinkti avataro spalvą", "select_face": "Pasirinkti veidą", "select_featured_photo": "Pasirinkti rodomą nuotrauką", + "select_from_computer": "Pasirinkti iÅĄ kompiuterio", "select_keep_all": "Visus paÅžymėti \"Palikti\"", "select_library_owner": "Pasirinkti bibliotekos savininką", + "select_new_face": "Pasirinkti naują veidą", + "select_photos": "Pasirinkti nuotraukas", "select_trash_all": "Visus paÅžymėti \"IÅĄmesti\"", "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_info_box_app_version": "Programėlės versija", + "server_info_box_server_url": "Serverio URL", "server_offline": "Serveris nepasiekiamas", "server_online": "Serveris pasiekiamas", + "server_privacy": "Serverio Privatumas", "server_stats": "Serverio statistika", "server_version": "Serverio versija", "set": "Nustatyti", @@ -812,24 +857,52 @@ "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ą", + "set_stack_primary_asset": "Nustatyti kaip pagrindinį elementą", + "setting_image_viewer_original_title": "UÅžkrauti originalią nuotrauką", + "setting_image_viewer_preview_title": "UÅžkrauti perÅžiÅĢros nuotrauką", + "setting_image_viewer_title": "Nuotraukos", + "setting_languages_apply": "Pritaikyti", + "setting_notifications_notify_never": "niekada", + "setting_notifications_single_progress_subtitle": "Detali įkėlimo progreso informacija kiekvienam elementui", "settings": "Nustatymai", + "settings_require_restart": "PraÅĄome perkrauti Immich, siekiant pritaikyti ÅĄÄ¯ nustatymą", + "settings_saved": "Nustatymai iÅĄsaugoti", + "setup_pin_code": "Nustatyti PIN kodą", "share": "Dalintis", + "share_add_photos": "ÄŽtraukti nuotraukÅŗ", + "share_dialog_preparing": "RuoÅĄiama...", + "share_link": "Bendrinti nuorodą", "shared": "Bendrinami", + "shared_by_user": "Bendrina {user}", + "shared_by_you": "Bendrinama jÅĢsÅŗ", + "shared_from_partner": "Nuotraukos iÅĄ {partner}", + "shared_link_clipboard_copied_massage": "Nukopijuota į iÅĄkarpinę", "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": "Bendrinama su manimi", "shared_with_partner": "Pasidalinta su {partner}", "sharing": "Dalijimasis", "sharing_enter_password": "Norėdami perÅžiÅĢrėti ÅĄÄ¯ puslapį, įveskite slaptaÅžodį.", + "sharing_page_album": "Bendrinami albumai", + "sharing_page_empty_list": "TUŠČIAS SĄRAÅ AS", "sharing_sidebar_description": "Rodyti bendrinimo rodinio nuorodą ÅĄoninėje juostoje", + "sharing_silver_appbar_create_shared_album": "Naujas bendrinamas albumas", + "sharing_silver_appbar_share_partner": "Bendrinti su partneriu", "show_album_options": "Rodyti albumo parinktis", + "show_albums": "Rodyti albumus", + "show_all_people": "Rodyti visus asmenis", + "show_and_hide_people": "Rodyti ir paslėpti Åžmones", "show_file_location": "Rodyti rinkmenos vietą", "show_gallery": "Rodyti galeriją", + "show_hidden_people": "Rodyti paslėptus asmenis", "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": "Rodyti klaviatÅĢros trumpinius", "show_metadata": "Rodyti metaduomenis", "show_or_hide_info": "Rodyti arba slėpti informaciją", "show_password": "Rodyti slaptaÅžodį", + "show_progress_bar": "Rodyti progreso juostą", "show_search_options": "Rodyti paieÅĄkos parinktis", "show_slideshow_transition": "Rodyti perėjimą tarp skaidriÅŗ", "show_supporter_badge": "Rėmėjo Åženklelis", @@ -840,11 +913,16 @@ "sign_up": "UÅžsiregistruoti", "size": "Dydis", "skip_to_content": "Pereiti prie turinio", + "skip_to_folders": "Praleisti iki aplankÅŗ", + "skip_to_tags": "Praleisti iki ÅžymiÅŗ", "slideshow": "SkaidriÅŗ perÅžiÅĢra", "slideshow_settings": "SkaidriÅŗ perÅžiÅĢros nustatymai", + "sort_albums_by": "Rikiuoti albumus pagal...", "sort_created": "SukÅĢrimo data", + "sort_items": "ElementÅŗ skaičiÅŗ", "sort_modified": "Keitimo data", "sort_oldest": "Seniausia nuotrauka", + "sort_people_by_similarity": "Rikiuoti Åžmonės pagal panaÅĄumą", "sort_recent": "Naujausia nuotrauka", "sort_title": "Pavadinimas", "source": "Å altinis", @@ -856,12 +934,17 @@ "start": "Pradėti", "start_date": "PradÅžios data", "status": "Statusas", + "stop_casting": "Nutraukti transliavimą", "storage": "Saugykla", "storage_usage": "Naudojama {used} iÅĄ {available}", "submit": "Pateikti", + "suggestions": "PasiÅĢlymai", "sunrise_on_the_beach": "Saulėtekis paplÅĢdimyje", + "support": "Pagalba", "support_and_feedback": "Palaikymas ir atsiliepimai", "sync": "Sinchronizuoti", + "sync_albums": "Sinchronizuoti albumus", + "sync_upload_album_setting_subtitle": "Sukurti ir įkelti jÅĢsÅŗ nuotraukas ir vaizdo įraÅĄus į pasirinktus Immich albumus", "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", @@ -871,33 +954,47 @@ "tags": "ÅŊymos", "template": "Å ablonas", "theme": "Tema", + "theme_selection": "Temos pasirinkimas", + "theme_setting_primary_color_title": "Pagrindinė spalva", + "theme_setting_system_primary_color_title": "Naudoti sistemos spalvą", + "theme_setting_system_theme_switch": "Automatinė (Naudoti sistemos nustatymus)", "time_based_memories": "Atsiminimai pagal laiką", "timeline": "Laiko skalė", "timezone": "Laiko juosta", "to_archive": "Archyvuoti", "to_change_password": "Pakeisti slaptaÅžodį", "to_favorite": "ÄŽtraukti prie mėgstamiausiÅŗ", + "to_login": "Prisijungti", "to_trash": "IÅĄmesti", "trash": "Å iukÅĄliadėŞė", "trash_all": "Perkelti visus į ÅĄiukÅĄliadėŞę", "trash_count": "Perkelti {count, number} į ÅĄiukÅĄliadėŞę", + "trash_emptied": "IÅĄvalytos ÅĄiukÅĄlės", "trash_no_results_message": "ÄŽ ÅĄiukÅĄliadėŞę perkeltos nuotraukos ir vaizdo įraÅĄai bus rodomi čia.", + "trash_page_delete_all": "IÅĄtrinti Visus", + "trash_page_empty_trash_dialog_content": "Ar norite iÅĄtrinti iÅĄmestus elementus? Å ie elementai bus visam laikui paÅĄalinti iÅĄ Immich", + "trash_page_no_assets": "Nėra iÅĄmestÅŗ elementÅŗ", + "trash_page_restore_all": "Atkurti Visus", "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": "Nebeslėpti Åžmogaus", + "unknown_country": "NeÅžinoma Å alis", "unknown_year": "NeÅžinomi metai", + "unlimited": "Neribota", "unlink_oauth": "Atsieti OAuth", "unlinked_oauth_account": "Atsieta OAuth paskyra", + "unnamed_album": "Neįvardytas Albumas", "unnamed_album_delete_confirmation": "Ar tikrai norite iÅĄtrinti ÅĄÄ¯ albumą?", + "unnamed_share": "Neįvardytas Bendrinimas", "unsaved_change": "NeiÅĄsaugoti pakeitimai", "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": "Seknatis", "updated_at": "Atnaujintas", "updated_password": "SlaptaÅžodis atnaujintas", "upload": "ÄŽkelti", @@ -913,24 +1010,32 @@ "upload_to_immich": "ÄŽkelti į Immich ({count})", "uploading": "ÄŽkeliama", "usage": "Naudojymas", + "use_biometric": "Naudoti biometriją", "use_current_connection": "naudoti dabartinį ryÅĄÄ¯", "user": "Naudotojas", "user_has_been_deleted": "Å is naudotojas buvo iÅĄtrintas.", "user_id": "Naudotojo ID", + "user_liked": "{user} patinka {type, select, photo {ÅĄi nuotrauka} video {ÅĄis vaizdo įraÅĄas} asset {ÅĄis elementas} other {tai}}", "user_pin_code_settings": "PIN kodas", "user_pin_code_settings_description": "Tvarkykite savo PIN kodą", + "user_privacy": "Vartotojo Privatumas", + "user_purchase_settings": "ÄŽsigyti", + "user_role_set": "Nustatyti {user}, kaip {role}", "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": "PraÅĄome įvesti galiojantį URL", "variables": "Kintamieji", "version": "Versija", "version_announcement_closing": "Tavo draugas, Alex", + "version_announcement_message": "Sveiki! Nauja „Immich“ versija yra pasiekiama. PraÅĄome skirti ÅĄiek tiek laiko perskaityti leidimo pastabas, kad įsitikintumėte, jog jÅĢsÅŗ nustatymai yra atnaujinti. Tai padės iÅĄvengti netinkamo sukonfigÅĢravimo, ypač jei naudojate „WatchTower“ ar kitą mechanizmą, kuris automatiÅĄkai atnaujina jÅĢsÅŗ „Immich“ serverį.", "version_history": "VersijÅŗ istorija", "version_history_item": "Versija {version} įdiegta {date}", "video": "Vaizdo įraÅĄas", + "video_hover_setting": "Paleisti vaizdo įraÅĄo miniatiÅĢrą uÅžvedus pele", "video_hover_setting_description": "Atkurti vaizdo įraÅĄo miniatiÅĢrą, kai pelė uÅžvedama ant elemento. Net ir iÅĄjungus, atkÅĢrimą galima pradėti uÅžvedus pelės Åžymeklį ant atkÅĢrimo piktogramos.", "videos": "Video", "videos_count": "{count, plural, one {# vaizdo įraÅĄas} few {# vaizdo įraÅĄai} other {# vaizdo įraÅĄÅŗ}}", @@ -940,16 +1045,19 @@ "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_qr_code": "ÅŊiÅĢrėti QR kodą", "view_stack": "PerÅžiÅĢrėti grupę", "waiting": "Laukiama", "warning": "ÄŽspėjimas", "week": "Savaitė", + "welcome": "Sveiki atvykę", "welcome_to_immich": "Sveiki atvykę į Immich", - "wifi_name": "WiFi Name", + "wifi_name": "Wi-Fi Pavadinimas", + "wrong_pin_code": "Neteisingas PIN kodas", "year": "Metai", "years_ago": "PrieÅĄ {years, plural, one {# metus} other {# metÅŗ}}", "yes": "Taip", "you_dont_have_any_shared_links": "Bendrinimo nuorodÅŗ neturite", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "JÅĢsÅŗ Wi-Fi pavadinimas", "zoom_image": "Priartinti vaizdą" } diff --git a/i18n/lv.json b/i18n/lv.json index 6b54ee3196..3fcf228612 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -30,9 +30,10 @@ "add_url": "Pievienot URL", "added_to_archive": "Pievienots arhÄĢvam", "added_to_favorites": "Pievienots izlasei", - "added_to_favorites_count": "Pievienots {count, number} izlasei", + "added_to_favorites_count": "{count, number} pievienoti izlasei", "admin": { "add_exclusion_pattern_description": "Pievienojiet izlaiÅĄanas shēmas. AizstājējzÄĢmju izmantoÅĄa *, **, un ? tiek atbalstÄĢta. Lai ignorētu visus failus jebkurā direktorijā ar nosaukumu “RAW”, izmantojiet “**/RAW/**”. Lai ignorētu visus failus, kas beidzas ar “. tif”, izmantojiet “**/*. tif”. Lai ignorētu absolÅĢto ceÄŧu, izmantojiet “/path/to/ignore/**”.", + "admin_user": "Administrators", "asset_offline_description": "Å is ārējās bibliotēkas resurss vairs nav atrodams diskā un ir pārvietots uz atkritumu grozu. Ja fails tika pārvietots bibliotēkas ietvaros, pārbaudiet, vai jÅĢsu hronoloÄŖijā ir jauns atbilstoÅĄais resurss. Lai atjaunotu ÅĄo resursu, pārliecinieties, vai Immich var piekÄŧÅĢt tālāk norādÄĢtajam faila ceÄŧam un skenēt bibliotēku.", "authentication_settings": "Autentifikācijas iestatÄĢjumi", "authentication_settings_description": "ParoÄŧu, OAuth un citu autentifikācijas iestatÄĢjumu pārvaldÄĢba", @@ -40,7 +41,6 @@ "authentication_settings_reenable": "Lai atkārtoti iespējotu, izmantojiet Servera Komandu.", "background_task_job": "Fona Uzdevumi", "backup_settings_description": "Datubāzes dublÄ“ÅĄanas iestatÄĢjumu pārvaldÄĢba", - "check_all": "PārbaudÄĢt Visu", "cleared_jobs": "NotÄĢrÄĢti uzdevumi priekÅĄ: {job}", "config_set_by_file": "Konfigurāciju paÅĄlaik iestata konfigurācijas fails", "confirm_delete_library": "Vai tieÅĄÄm vēlaties dzēst {library} bibliotēku?", @@ -50,23 +50,34 @@ "create_job": "Izveidot uzdevumu", "cron_expression": "Cron izteiksme", "disable_login": "Atspējot pieteikÅĄanos", - "duplicate_detection_job_description": "Palaidiet maÅĄÄĢnmācÄĢÅĄanos uz lÄĢdzekÄŧiem, lai noteiktu lÄĢdzÄĢgus attēlus. PaÄŧaujas uz Viedo MeklÄ“ÅĄanu", - "external_library_created_at": "Ārēja bibliotēka (izveidota {date})", + "duplicate_detection_job_description": "Palaidiet maÅĄÄĢnmācÄĢÅĄanos uz failiem, lai noteiktu lÄĢdzÄĢgus attēlus. PaÄŧaujas uz viedo meklÄ“ÅĄanu", "external_library_management": "Ārējo bibliotēku pārvaldÄĢba", "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_fullsize_enabled_description": "Äĸenerēt pilna izmēra attēlu formātiem, kas nav piemēroti izmantoÅĄanai tÄĢmeklÄĢ. Ja ir iespējota opcija \"PriekÅĄroka iegultajam priekÅĄskatÄĢjumam\", tiks izmantoti iegultie priekÅĄskatÄĢjumi bez konvertÄ“ÅĄanas. Neietekmē tÄĢmeklim draudzÄĢgus formātus, piemēram, JPEG.", + "image_fullsize_quality_description": "Pilnizmēra attēlu kvalitāte no 1-100. Augstāka vērtÄĢba dos labāku kvalitāti, taču faili bÅĢs lielāka izmēra.", + "image_fullsize_title": "Pilnizmēra attēlu iestatÄĢjumi", + "image_prefer_embedded_preview": "PriekÅĄroka iegultajam priekÅĄskatÄĢjumam", + "image_prefer_embedded_preview_setting_description": "Izmantot RAW fotoattēlos iestrādātos priekÅĄskatÄĢjumus, ja tādi ir pieejami, kā ievades datus attēlu apstrādei. Tādējādi daÅžiem attēliem var iegÅĢt precÄĢzākas krāsas, taču priekÅĄskatÄĢjuma kvalitāte ir atkarÄĢga no fotokameras un attēlam var bÅĢt vairāk saspieÅĄanas artefaktu.", "image_quality": "Kvalitāte", "image_resolution": "IzÅĄÄˇirtspēja", "image_settings": "Attēla IestatÄĢjumi", "image_settings_description": "Äĸenerēto attēlu kvalitātes un izÅĄÄˇirtspējas pārvaldÄĢba", "image_thumbnail_title": "SÄĢktēlu iestatÄĢjumi", + "job_concurrency": "{job} vienlaicÄĢgi", "job_created": "Uzdevums izveidots", "job_settings": "Uzdevumu iestatÄĢjumi", "job_settings_description": "Uzdevumu izpildes vienlaicÄĢguma pārvaldÄĢba", "job_status": "Uzdevumu statuss", "library_deleted": "Bibliotēka dzēsta", + "library_scanning": "Periodiska skenÄ“ÅĄana", + "library_scanning_description": "Konfigurē periodisku bibliotēku skenÄ“ÅĄanu", + "library_scanning_enable_description": "Iespējot periodisku bibliotēku skenÄ“ÅĄanu", + "library_settings": "Ārējā bibliotēka", "library_settings_description": "Ārējo bibliotēku iestatÄĢjumu pārvaldÄĢba", + "library_watching_settings": "Bibliotēku uzraudzÄĢÅĄana (EKSPERIMENTĀLA)", + "library_watching_settings_description": "Automātiski uzraudzÄĢt, vai ir mainÄĢti faili", "machine_learning_clip_model": "CLIP modelis", "machine_learning_duplicate_detection": "Dublikātu noteikÅĄana", "machine_learning_facial_recognition": "Seju atpazÄĢÅĄana", @@ -96,6 +107,7 @@ "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_test_email": "NosÅĢtÄĢt testa e-pastu", + "notification_email_test_email_sent": "Uz {email} ir nosÅĢtÄĢts testa e-pasts. LÅĢdzu, pārbaudi savu iesÅĢtni.", "notification_settings": "Paziņojumu iestatÄĢjumi", "notification_settings_description": "Paziņojumu iestatÄĢjumu, tostarp e-pasta, pārvaldÄĢba", "oauth_button_text": "Pogas teksts", @@ -109,7 +121,6 @@ "person_cleanup_job": "Personu tÄĢrÄĢÅĄana", "quota_size_gib": "Kvotas izmērs (GiB)", "registration": "Administratora reÄŖistrācija", - "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â€Ļ", @@ -124,7 +135,6 @@ "storage_template_settings": "Krātuves veidne", "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": "Cascading Style Sheets Äŧauj pielāgot Immich izskatu.", @@ -174,10 +184,6 @@ "album_leave": "Pamest albumu?", "album_name": "Albuma nosaukums", "album_remove_user": "Noņemt lietotāju?", - "album_thumbnail_card_item": "1 vienums", - "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_user_left": "Pameta {album}", "album_user_removed": "Noņēma {user}", @@ -251,13 +257,13 @@ "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: {duration}", + "backup_controller_page_background_delay": "Aizkavēt jaunu failu 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", "backup_controller_page_background_turn_off": "Izslēgt fona pakalpojumu", "backup_controller_page_background_turn_on": "Ieslēgt fona pakalpojumu", - "backup_controller_page_background_wifi": "Tikai WiFi tÄĢklā", + "backup_controller_page_background_wifi": "Tikai Wi-Fi tÄĢklā", "backup_controller_page_backup": "DublÄ“ÅĄana", "backup_controller_page_backup_selected": "AtlasÄĢts: ", "backup_controller_page_backup_sub": "Dublētie Fotoattēli un videoklipi", @@ -266,6 +272,7 @@ "backup_controller_page_excluded": "Izņemot: ", "backup_controller_page_failed": "Neizdevās ({count})", "backup_controller_page_filename": "Faila nosaukums: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Dublējuma Informācija", "backup_controller_page_none_selected": "Neviens nav atlasÄĢts", "backup_controller_page_remainder": "Atlikums", @@ -287,32 +294,35 @@ "backup_manual_success": "VeiksmÄĢgi", "backup_manual_title": "AugÅĄupielādes statuss", "backup_options_page_title": "DublÄ“ÅĄanas iestatÄĢjumi", + "biometric_auth_enabled": "Ieslēgta biometriskā autentifikācija", + "biometric_locked_out": "Biometriskā autentifikācija tev ir bloķēta", + "biometric_no_options": "Nav pieejamas biometriskās autentifikācijas iespējas", + "biometric_not_available": "Biometriskā autentifikācija ÅĄajā ierÄĢcē nav pieejama", "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ÄĢ.", "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 ({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 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": "{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ÅĄ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": "Fotokamera", "cancel": "Atcelt", "cannot_merge_people": "Nevar apvienot cilvēkus", "change_date": "MainÄĢt datumu", + "change_description": "MainÄĢt aprakstu", + "change_display_order": "MainÄĢt attēloÅĄanas secÄĢbu", "change_expiration_time": "IzmainÄĢt derÄĢguma termiņu", "change_location": "MainÄĢt atraÅĄanās vietu", "change_name": "MainÄĢt nosaukumu", @@ -329,6 +339,7 @@ "clear": "NotÄĢrÄĢt", "clear_all": "NotÄĢrÄĢt visu", "clear_value": "NotÄĢrÄĢt vērtÄĢbu", + "client_cert_title": "SSL klienta sertifikāts", "clockwise": "PulksteņrādÄĢtāja virzienā", "close": "Aizvērt", "collapse": "SakÄŧaut", @@ -341,9 +352,10 @@ "confirm": "Apstiprināt", "confirm_new_pin_code": "Apstiprināt jauno PIN kodu", "confirm_password": "Apstiprināt paroli", + "confirm_tag_face": "Vai vēlies atzÄĢmēt ÅĄo seju kā {name}?", + "confirm_tag_face_unnamed": "Vai vēlies atzÄĢmēt ÅĄo seju?", "context": "Konteksts", "continue": "Turpināt", - "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", @@ -364,6 +376,7 @@ "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_at": "Izveidots", "curated_object_page_title": "Lietas", "current_pin_code": "EsoÅĄais PIN kods", "daily_title_text_date_year": "E, MMM dd, gggg", @@ -398,21 +411,32 @@ "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", + "direction": "SecÄĢba", + "discord": "Discord", "display_order": "AttēloÅĄanas secÄĢba", "documentation": "Dokumentācija", "done": "Gatavs", "download": "Lejupielādēt", - "download_filename": "fails: {filename}", + "download_canceled": "Lejupielāde atcelta", + "download_complete": "Lejupielāde pabeigta", + "download_error": "Lejupielādes kÄŧÅĢda", + "download_failed": "Lejupielāde neizdevās", + "download_notfound": "Lejupielāde nav atrasta", + "download_paused": "Lejupielāde nopauzēta", "download_settings": "Lejupielāde", "download_settings_description": "Ar failu lejupielādi saistÄĢto iestatÄĢjumu pārvaldÄĢba", + "download_started": "Lejupielāde sākta", + "download_sucess": "Lejupielāde izdevās", "downloading": "Lejupielādē", "downloading_asset_filename": "Lejupielādē failu {filename}", "duplicates": "Dublikāti", + "duration": "Ilgums", "edit": "Labot", "edit_album": "Labot albumu", "edit_date": "Labot datumu", "edit_date_and_time": "Labot datumu un laiku", + "edit_description": "Labot aprakstu", + "edit_description_prompt": "LÅĢdzu, izvēlies jaunu aprakstu:", "edit_faces": "Labot sejas", "edit_import_path": "Labot importa ceÄŧu", "edit_import_paths": "Labot importa ceÄŧus", @@ -428,26 +452,36 @@ "editor": "Redaktors", "editor_close_without_save_prompt": "Izmaiņas netiks saglabātas", "editor_close_without_save_title": "Aizvērt redaktoru?", + "editor_crop_tool_h2_rotation": "Rotācija", "email": "E-pasts", "email_notifications": "E-pasta paziņojumi", + "empty_folder": "Å ÄĢ mape ir tukÅĄa", "empty_trash": "IztukÅĄot atkritni", - "enter_wifi_name": "Enter WiFi name", + "enable_biometric_auth_description": "Lai iespējotu biometrisko autentifikāciju, Ievadiet savu PIN kodu", + "end_date": "Beigu datums", + "enter_wifi_name": "Ievadi Wi-Fi nosaukumu", + "enter_your_pin_code": "Ievadi savu PIN kodu", + "enter_your_pin_code_subtitle": "Ievadi savu PIN kodu, lai piekÄŧÅĢtu slēgtajai mapei", + "error": "KÄŧÅĢda", "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_change_description": "Neizdevās nomainÄĢt aprakstu", "unable_to_create_user": "Neizdevās izveidot lietotāju", "unable_to_delete_user": "Neizdevās dzēst lietotāju", "unable_to_hide_person": "Neizdevās paslēpt personu", - "unable_to_save_date_of_birth": "Neizdevās saglabāt dzimÅĄanas datumu" + "unable_to_save_date_of_birth": "Neizdevās saglabāt dzimÅĄanas datumu", + "unable_to_scan_libraries": "Bibliotēku skenÄ“ÅĄana neizdevās", + "unable_to_scan_library": "Bibliotēkas skenÄ“ÅĄana neizdevās" }, + "exif": "Exif", "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": "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}", @@ -459,11 +493,17 @@ "expire_after": "DerÄĢguma termiÅ†ÅĄ beidzas pēc", "expired": "DerÄĢguma termiÅ†ÅĄ beidzās", "explore": "IzpētÄĢt", + "export": "Eksportēt", + "extension": "PaplaÅĄinājums", "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_to_authenticate": "Neizdevās autentificēties", + "failed_to_load_assets": "Neizdevās ielādēt failus", "favorite": "Izlase", "favorites": "Izlase", - "favorites_page_no_favorites": "Nav atrasti iecienÄĢtākie aktÄĢvi", + "favorites_page_no_favorites": "Nav atrasti iecienÄĢtākie faili", "features_setting_description": "Lietotnes funkciju pārvaldÄĢba", + "filename": "Faila nosaukums", + "filetype": "Faila tips", "folders": "Mapes", "haptic_feedback_switch": "IestatÄĢt haptisku reakciju", "haptic_feedback_title": "Haptiska Reakcija", @@ -479,14 +519,18 @@ "home_page_building_timeline": "Tiek izveidota laika skala", "home_page_delete_err_partner": "Nevarēja dzēst partnera aktÄĢvus, notiek izlaiÅĄana", "home_page_delete_remote_err_local": "Lokālie aktÄĢvi dzÄ“ÅĄanai attālinātajā izvēlē, tiek izlaists", - "home_page_favorite_err_local": "Vēl nevar pievienot izlaisei vietējos aktÄĢvus, notiek izlaiÅĄana", - "home_page_favorite_err_partner": "Pagaidām nevar ievietot izlasē partnera aktÄĢvus, notiek izlaiÅĄana", + "home_page_favorite_err_local": "Vēl nevar pievienot izlasei vietējos failus, izlaiÅž", + "home_page_favorite_err_partner": "Pagaidām nevar ievietot izlasē partnera failus, izlaiÅž", "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_locked_error_local": "Nevar pārvietot vietējos failus uz slēgto mapi, izlaiÅž", + "home_page_locked_error_partner": "Nevar pārvietot partneru failus uz slēgto mapi, izlaiÅž", "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", + "hour": "Stunda", + "id": "ID", "image": "Attēls", "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_download_success": "Lejupielāde izdevās", "image_viewer_page_state_provider_share_error": "KopÄĢgoÅĄanas KÄŧÅĢda", "immich_logo": "Immich logo", "import_from_json": "Importēt no JSON", @@ -530,6 +574,7 @@ "location_picker_latitude_hint": "Ievadiet savu ÄŖeogrāfisko platumu ÅĄeit", "location_picker_longitude_error": "Ievadiet korektu ÄŖeogrāfisko garumu", "location_picker_longitude_hint": "Ievadiet savu ÄŖeogrāfisko garumu ÅĄeit", + "locked_folder": "Slēgtā mape", "log_out": "IzrakstÄĢties", "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.", @@ -585,14 +630,14 @@ "map_settings_dialog_title": "Kartes IestatÄĢjumi", "map_settings_include_show_archived": "IekÄŧaut Arhivētos", "map_settings_include_show_partners": "IekÄŧaut Partnerus", - "map_settings_only_show_favorites": "RādÄĢt tikai Izlasi", + "map_settings_only_show_favorites": "RādÄĢt tikai izlasi", "map_settings_theme_settings": "Kartes Dizains", "map_zoom_to_see_photos": "Attāliniet, lai redzētu fotoattēlus", "matches": "AtbilstÄĢbas", "media_type": "Multivides veids", "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_check_back_tomorrow": "Atgriezies rÄĢt, lai skatÄĢtu vairāk atmiņu", "memories_start_over": "Sākt no jauna", "memories_swipe_to_close": "Pavelciet uz augÅĄu, lai aizvērtu", "memory": "Atmiņa", @@ -609,6 +654,9 @@ "month": "Mēnesis", "monthly_title_text_date_format": "MMMM g", "more": "Vairāk", + "move": "Pārvietot", + "move_off_locked_folder": "Izņemt no slēgtās mapes", + "move_to_locked_folder": "Pārvietot uz slēgto mapi", "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", @@ -624,7 +672,7 @@ "new_pin_code": "Jaunais PIN kods", "new_user_created": "Izveidots jauns lietotājs", "new_version_available": "PIEEJAMA JAUNA VERSIJA", - "next": "NākoÅĄais", + "next": "Nākamais", "next_memory": "Nākamā atmiņa", "no": "Nē", "no_albums_message": "Izveido albumu, lai organizētu savas fotogrāfijas un video", @@ -639,6 +687,7 @@ "no_results_description": "IzmēĪiniet sinonÄĢmu vai vispārÄĢgāku atslēgvārdu", "not_in_any_album": "Nav nevienā albumā", "notes": "PiezÄĢmes", + "nothing_here_yet": "Å eit vēl nekā nav", "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.", "notification_permission_list_tile_enable_button": "Iespējot Paziņojumus", @@ -646,6 +695,7 @@ "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", @@ -675,6 +725,8 @@ "password": "Parole", "password_does_not_match": "Parole nesakrÄĢt", "path": "CeÄŧÅĄ", + "pause": "Pauzēt", + "paused": "Nopauzēts", "people": "Cilvēki", "permission_onboarding_back": "AtpakaÄŧ", "permission_onboarding_continue_anyway": "Tomēr turpināt", @@ -687,18 +739,24 @@ "person": "Persona", "photos": "Fotoattēli", "photos_from_previous_years": "Fotogrāfijas no iepriekÅĄÄ“jiem gadiem", + "pin_verification": "PIN koda pārbaude", + "place": "AtraÅĄanās vieta", "places": "Vietas", + "please_auth_to_access": "Lai piekÄŧÅĢtu, lÅĢdzu, autentificējieties", "port": "Ports", "preferences_settings_title": "IestatÄĢjumi", "preview": "PriekÅĄskatÄĢjums", + "previous": "IepriekÅĄÄ“jais", "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_out_of_date_major": "Mobilā lietotne ir novecojusi. LÅĢdzu, atjaunini to uz jaunāko pamatversiju.", + "profile_drawer_client_out_of_date_minor": "Mobilā lietotne ir novecojusi. LÅĢdzu, atjaunini to uz jaunāko papildversiju.", "profile_drawer_client_server_up_to_date": "Klients un serveris ir atjaunināti", - "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_drawer_server_out_of_date_major": "Serveris ir novecojis. LÅĢdzu, atjaunini to uz jaunāko pamatversiju.", + "profile_drawer_server_out_of_date_minor": "Serveris ir novecojis. LÅĢdzu, atjaunini to uz jaunāko papildversiju.", + "purchase_account_info": "AtbalstÄĢtājs", + "purchase_button_buy": "Pirkt", "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", @@ -708,7 +766,7 @@ "purchase_license_subtitle": "Nopērc Immich licenci, lai atbalstÄĢtu turpmāku pakalpojuma attÄĢstÄĢbu", "purchase_lifetime_description": "Pirkums uz mÅĢÅžu", "purchase_option_title": "IEGĀDES IESPĒJAS", - "purchase_panel_title": "AtbalstÄĢt projektu", + "purchase_panel_title": "Atbalsti projektu", "purchase_remove_product_key": "Noņemt produkta atslēgu", "purchase_remove_server_product_key": "Noņemt servera produkta atslēgu", "purchase_server_description_1": "Visam serverim", @@ -721,14 +779,17 @@ "remove": "Noņemt", "remove_from_album": "Noņemt no albuma", "remove_from_favorites": "Noņemt no izlases", + "remove_from_locked_folder": "Izņemt no slēgtās mapes", "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", + "removed_from_favorites_count": "{count, plural, other {Izņēma #}} no izlases", "rename": "Pārsaukt", "repair": "Remonts", "replace_with_upload": "Aizstāt ar augÅĄupielādi", "require_user_to_change_password_on_first_login": "PieprasÄĢt lietotājam mainÄĢt paroli pēc pirmās pieteikÅĄanās", + "rescan": "Pārskenēt atkārtoti", "resolve_duplicates": "Atrisināt dublÄ“ÅĄanās gadÄĢjumus", "resolved_all_duplicates": "Visi dublikāti ir atrisināti", "restore": "Atjaunot", @@ -746,6 +807,10 @@ "saved_settings": "IestatÄĢjumi saglabāti", "say_something": "Teikt kaut ko", "scaffold_body_error_occurred": "Radās kÄŧÅĢda", + "scan_all_libraries": "Skenēt visas bibliotēkas", + "scan_library": "Skenēt", + "scan_settings": "SkenÄ“ÅĄanas iestatÄĢjumi", + "scanning_for_album": "Skenē albumu...", "search": "Meklēt", "search_albums": "Meklēt albumus", "search_by_filename_example": "piemēram, IMG_1234.JPG vai PNG", @@ -765,7 +830,7 @@ "search_page_your_map": "JÅĢsu Karte", "search_people": "Meklēt cilvēkus", "search_result_page_new_search_hint": "Jauns Meklējums", - "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_1": "Viedā meklÄ“ÅĄana pēc noklusējuma ir iespējota, lai meklētu metadatos, izmanto sintaksi ", "search_suggestion_list_smart_search_hint_2": "m:jÅĢsu-meklÄ“ÅĄanas-frāze", "search_your_photos": "Meklēt JÅĢsu fotoattēlus", "second": "Sekunde", @@ -786,7 +851,6 @@ "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_title": "Valodas", "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", @@ -859,8 +923,10 @@ "show_gallery": "RādÄĢt galeriju", "show_hidden_people": "RādÄĢt paslēptos cilvēkus", "show_metadata": "RādÄĢt metadatus", + "show_progress_bar": "RādÄĢt progresa joslu", "show_supporter_badge": "AtbalstÄĢtāja nozÄĢmÄĢte", "show_supporter_badge_description": "RādÄĢt atbalstÄĢtāja nozÄĢmÄĢti", + "shuffle": "Jaukta", "size": "Izmērs", "slideshow": "SlÄĢdrāde", "slideshow_settings": "SlÄĢdrādes iestatÄĢjumi", @@ -902,6 +968,7 @@ "timezone": "Laika zona", "to_archive": "Arhivēt", "to_change_password": "MainÄĢt paroli", + "to_favorite": "Pievienot izlasei", "toggle_settings": "Pārslēgt iestatÄĢjumus", "total": "Kopā", "total_usage": "Kopējais lietojums", @@ -927,6 +994,7 @@ "unnamed_album": "Albums bez nosaukuma", "unsaved_change": "Nesaglabāta izmaiņa", "unstack": "At-Stekot", + "updated_at": "Atjaunināts", "updated_password": "Parole ir atjaunināta", "upload": "AugÅĄupielādēt", "upload_dialog_info": "Vai vēlaties veikt izvēlētā(-o) aktÄĢva(-u) dublējumu uz servera?", @@ -935,7 +1003,9 @@ "upload_status_errors": "KÄŧÅĢdas", "upload_status_uploaded": "AugÅĄupielādēts", "upload_to_immich": "AugÅĄupielādēt Immich ({count})", + "url": "URL", "usage": "Lietojums", + "use_biometric": "Izmantot biometrisko autentifikāciju", "user": "Lietotājs", "user_has_been_deleted": "Å is lietotājs ir dzēsts.", "user_id": "Lietotāja ID", @@ -950,11 +1020,6 @@ "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_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", @@ -967,10 +1032,13 @@ "viewer_unstack": "At-Stekot", "waiting": "Gaida", "week": "NedēÄŧa", - "wifi_name": "WiFi Name", + "welcome": "Laipni lÅĢgti", + "welcome_to_immich": "Laipni lÅĢgti Immich", + "wifi_name": "Wi-Fi nosaukums", + "wrong_pin_code": "Nepareizs PIN kods", "year": "Gads", "years_ago": "Pirms {years, plural, one {# gada} other {# gadiem}}", "yes": "Jā", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "Tava Wi-Fi nosaukums", "zoom_image": "Pietuvināt attēlu" } diff --git a/i18n/mk.json b/i18n/mk.json index e70108cd63..31d223cbd6 100644 --- a/i18n/mk.json +++ b/i18n/mk.json @@ -40,7 +40,6 @@ "backup_keep_last_amount": "КоĐģĐ¸Ņ‡Đ¸ĐŊа ĐŊа ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋии Са Ņ‡ŅƒĐ˛Đ°ŅšĐĩ", "backup_settings": "ĐŸĐžŅŅ‚Đ°Đ˛Đēи Са Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋии", "backup_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ŅƒĐ˛Đ°Ņ˜ ŅĐž ĐŋĐžŅŅ‚Đ°Đ˛Đēи Са Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋии ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° ĐŊа ĐŋĐžĐ´Đ°Ņ‚ĐžŅ†Đ¸", - "check_all": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸ ҁҐ", "cleared_jobs": "Đ˜ŅŅ‡Đ¸ŅŅ‚ĐĩĐŊи ĐˇĐ°Đ´Đ°Ņ‡Đ¸ Са: {job}", "config_set_by_file": "КоĐŊĐŗĐ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸Ņ˜Đ°Ņ‚Đ° Đĩ ĐŧĐžĐŧĐĩĐŊŅ‚Đ°ĐģĐŊĐž ĐŋĐžŅŅ‚Đ°Đ˛ĐĩĐŊа Од ĐēĐžĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸ŅĐēа Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", "confirm_delete_library": "ДаĐģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи Đ´ĐĩĐēа ŅĐ°ĐēĐ°Ņ‚Đĩ да Ņ˜Đ° Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ° {library}?", @@ -72,16 +71,13 @@ "oauth_auto_register": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚ŅĐēа Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ˜Đ°", "oauth_button_text": "ĐĸĐĩĐēҁ҂ ĐŊа ĐēĐžĐŋ҇Đĩ", "oauth_settings": "OAuth", - "offline_paths": "ĐžŅ„ĐģĐ°Ņ˜ĐŊ ĐŋĐ°Ņ‚ĐĩĐēи", "password_settings": "ĐĐ°Ņ˜Đ°Đ˛Đ° ŅĐž ĐģОСиĐŊĐēа", - "repair_all": "ПоĐŋŅ€Đ°Đ˛Đ¸ ĐŗĐ¸ ŅĐ¸Ņ‚Đĩ", "sidecar_job": "Sidecar ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ‚ĐžŅ†Đ¸", "storage_template_settings": "ШайĐģĐžĐŊ Са ҁĐēĐģĐ°Đ´Đ¸Ņ€Đ°ŅšĐĩ", "system_settings": "ĐĄĐ¸ŅŅ‚ĐĩĐŧҁĐēи ĐŋĐžŅŅ‚Đ°Đ˛Đēи", "thumbnail_generation_job": "ГĐĩĐŊĐĩŅ€Đ¸Ņ€Đ°Ņ˜ ҁĐģиĐēĐ¸Ņ‡Đēи", "transcoding_acceleration_vaapi": "VAAPI", - "transcoding_threads": "ĐĐ¸ŅˆĐēи", - "untracked_files": "НĐĩҁĐģĐĩĐ´ĐĩĐŊи Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēи" + "transcoding_threads": "ĐĐ¸ŅˆĐēи" }, "admin_email": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸ŅĐēа Е-ĐŋĐžŅˆŅ‚Đ°", "admin_password": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸ŅĐēа ĐģОСиĐŊĐēа", diff --git a/i18n/ml.json b/i18n/ml.json index 0967ef424b..8d25cb5604 100644 --- a/i18n/ml.json +++ b/i18n/ml.json @@ -1 +1,12 @@ -{} +{ + "about": "ā´ĩā´ŋⴎⴝⴤāĩā´¤āĩ†ā´•āĩā´•āĩā´ąā´ŋⴚāĩā´šāĩ", + "account": "ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ", + "account_settings": "ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ ā´¸āĩ†ā´ąāĩā´ąā´ŋā´‚ā´—āĩā´¸āĩ", + "acknowledge": "ā´…ā´‚ā´—āĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "add": "ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_a_description": "ā´’ā´°āĩ ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_a_location": "ā´’ā´°āĩ ā´¸āĩā´Ĩⴞⴂ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_a_name": "ā´’ā´°āĩ ā´Ēāĩ‡ā´°āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_a_title": "ā´’ā´°āĩ ā´ļāĩ€āĩŧⴎⴕⴂ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_endpoint": "ā´Žāĩģā´Ąāĩā´Ēāĩ‹ā´¯ā´ŋā´¨āĩā´ąāĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•" +} diff --git a/i18n/mn.json b/i18n/mn.json index 23fe574c6a..8a18a1d5e5 100644 --- a/i18n/mn.json +++ b/i18n/mn.json @@ -4,6 +4,7 @@ "account_settings": "Đ‘Ō¯Ņ€Ņ‚ĐŗŅĐģиКĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", "acknowledge": "ОйĐģĐŗĐžĐģОО", "action": "ŌŽĐšĐģĐ´ŅĐģ", + "action_common_update": "ШиĐŊŅŅ‡ĐģŅŅ…", "actions": "ŌŽĐšĐģĐ´ĐģŌ¯Ō¯Đ´", "active": "Đ˜Đ´ŅĐ˛Ņ…Ņ‚ŅĐš", "activity": "ŌŽĐšĐģĐ´ĐģиКĐŊ ĐąŌ¯Ņ€Ņ‚ĐŗŅĐģ", @@ -13,12 +14,16 @@ "add_a_location": "Đ‘Đ°ĐšŅ€ŅˆĐ¸Đģ ĐŊŅĐŧŅŅ…", "add_a_name": "ĐŅŅ€ ĶŠĐŗĶŠŅ…", "add_a_title": "Đ“Đ°Ņ€Ņ‡Đ¸Đŗ ĐžŅ€ŅƒŅƒĐģĐ°Ņ…", + "add_endpoint": "Endpoint ĐŊŅĐŧŅŅ…", "add_location": "Đ‘Đ°ĐšŅ€ŅˆĐ¸Đģ ĐžŅ€ŅƒŅƒĐģĐ°Ņ…", "add_more_users": "Ķ¨ĶŠŅ€ Ņ…ŅŅ€ŅĐŗĐģŅĐŗŅ‡Đ¸Đ´ ĐŊŅĐŧŅŅ…", "add_partner": "ĐĨаĐŧŅ‚Ņ€Đ°ĐŗŅ‡ ĐŊŅĐŧŅŅ…", "add_photos": "Đ—ŅƒŅ€Đ°Đŗ ĐŊŅĐŧŅŅ…", "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} ĐŊŅĐŧŅĐŗĐ´ĐģŅŅ", @@ -26,7 +31,15 @@ "authentication_settings": "ĐĸаĐŊиĐŊ ĐŊŅĐ˛Ņ‚Ņ€ŅĐģŅ‚ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", "authentication_settings_description": "ĐŅƒŅƒŅ† Ō¯ĐŗĐ¸ĐšĐŊ ŅƒĐ´Đ¸Ņ€Đ´ĐģĐ°ĐŗĐ°, OAuth йОĐģĐžĐŊ ĐąŅƒŅĐ°Đ´ Ņ‚Đ°ĐŊиĐŊ ĐŊŅĐ˛Ņ‚Ņ€ŅĐģŅ‚Đ¸ĐšĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", "authentication_settings_disable_all": "Đ‘Ō¯Ņ… ĐŊŅĐ˛Ņ‚Ņ€ŅŅ… Đ°Ņ€ĐŗŅƒŅƒĐ´Ņ‹Đŗ Đ¸Đ´ŅĐ˛Ņ…Đ¸ĐŗŌ¯Đš йОĐģĐŗĐžŅ…Đ´ĐžĐž Đ¸Ņ‚ĐŗŅĐģŅ‚ŅĐš йаКĐŊа ҃҃? ĐŅĐ˛Ņ‚Ņ€ŅŅ… Ō¯ĐšĐģĐ´ŅĐģ ĐąŌ¯Ņ€ŅĐŊ Đ¸Đ´ŅĐ˛Ņ…Đ¸ĐŗŌ¯Đš йОĐģĐŊĐž.", - "check_all": "Đ‘Ō¯ĐŗĐ´Đ¸ĐšĐŗ ŅĐžĐŊĐŗĐžŅ…", + "background_task_job": "ĐŅ€Ņ‹ĐŊ аĐļиĐģ", + "backup_database": "Ķ¨ĐŗĶŠĐŗĐ´ĐģиКĐŊ ŅĐ°ĐŊĐŗĐ¸ĐšĐŊ даĐŧĐŋ Ō¯Ō¯ŅĐŗŅŅ…", + "backup_database_enable_description": "Ķ¨ĐŗĶŠĐŗĐ´ĐģиКĐŊ ŅĐ°ĐŊĐŗĐ¸ĐšĐŊ даĐŧĐŋ Đ¸Đ´ŅĐ˛Ņ…Đ¸ĐļŌ¯Ō¯ĐģŅŅ…", + "backup_keep_last_amount": "͍ĐŧĐŊĶŠŅ… Ņ…ŅĐ´ŅĐŊ даĐŧĐŋŅ‹Đŗ Ņ…Đ°Đ´ĐŗĐ°ĐģĐ°Ņ… Đ˛Ņ", + "config_set_by_file": "ĐĸĐžŅ…Đ¸Ņ€ĐŗĐžĐžĐŗ ĐžĐ´ĐžĐžĐŗĐžĐžŅ€ Ņ„Đ°ĐšĐģĐ°Đ°Ņ Đ°Đ˛Ņ‡ йаКĐŊа", + "confirm_delete_library": "Đĸа {library} ĐŗŅŅŅĐŊ ŅĐ°ĐŊĐŗ ŅƒŅŅ‚ĐŗĐ°Ņ…Đ´Đ°Đ° Đ¸Ņ‚ĐŗŅĐģŅ‚ŅĐš йаКĐŊа ҃҃?", + "confirm_delete_library_assets": "Đĸа ŅĐŊŅ ŅĐ°ĐŊĐŗ ŅƒŅŅ‚ĐŗĐ°Ņ…Đ´Đ°Đ° Đ¸Ņ‚ĐŗŅĐģŅ‚ŅĐš йаКĐŊа ҃҃? Đ­ĐŊŅ Ō¯ĐšĐģĐ´ĐģŅŅŅ€ Ņ‚Đ°ĐŊŅ‹ {count, plural, one {# contained asset} other {all # contained assets}} ҁĐĩŅ€Đ˛ĐĩŅ€ŅŅŅ ŅƒŅŅ‚Đ°Ņ… ĐąĶŠĐŗĶŠĶŠĐ´ ĐąŅƒŅ†Đ°Đ°Ņ… йОĐģĐžĐŧĐļĐŗŌ¯Đš. Đ“ŅŅ…Đ´ŅŅ Ņ„Đ°ĐšĐģŅƒŅƒĐ´ Đ´Đ¸ŅĐē Đ´ŅŅŅ€ŅŅ Ō¯ĐģĐ´ŅĐŊŅ.", + "confirm_email_below": "Đ‘Đ°Ņ‚Đ°ĐģĐŗĐ°Đ°Đļ҃҃ĐģĐ°Ņ…Ņ‹ĐŊ Ņ‚ŅƒĐģĐ´ Ņ‚Đ° \"{email}\" ĐŗŅĐļ ĐąĐ¸Ņ‡ĐŊŅ Ō¯Ō¯", + "confirm_reprocess_all_faces": "Đ‘Ō¯Ņ… Ņ†Đ°Ņ€Đ°ĐšĐŗ Đ´Đ°Ņ…Đ¸ĐŊ ĐŋŅ€ĐžŅ†Đĩҁҁ Ņ…Đ¸ĐšŅ… Ō¯Ō¯? ĐĸŅĐŗĐ˛ŅĐģ ĐąŌ¯Ņ… ĐŊŅŅ€Ņ Đ°Ņ€Đ¸ĐģĐ°Ņ… йОĐģĐŊĐž.", "face_detection": "ĐŌ¯Ō¯Ņ€ иĐģŅ€Ō¯Ō¯ĐģŅŅ…", "image_quality": "ЧаĐŊĐ°Ņ€", "job_settings": "АĐļĐģŅ‹ĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", diff --git a/i18n/mr.json b/i18n/mr.json index ec05d6c702..6c2ab7406c 100644 --- a/i18n/mr.json +++ b/i18n/mr.json @@ -40,7 +40,6 @@ "backup_keep_last_amount": "ā¤ĒāĨ‚⤰āĨā¤ĩāĨ€ā¤šāĨā¤¯ā¤ž ⤕ā¤ŋ⤤āĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤ĒāĨ€ ⤠āĨ‡ā¤ĩā¤žā¤¯ā¤šāĨā¤¯ā¤ž", "backup_settings": "ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤ĒāĨ€ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤ž", "backup_settings_description": "ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ā¤¸ā¤‚ā¤šā¤¯ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤ĒāĨ€ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤍", - "check_all": "⤏⤰āĨā¤ĩ ⤤ā¤Ēā¤žā¤¸ā¤ž", "cleared_jobs": "{job}: ⤚āĨā¤¯ā¤ž ā¤•ā¤žā¤°āĨā¤¯ā¤ĩā¤žā¤šāĨā¤¯ā¤ž ā¤•ā¤žā¤ĸ⤞āĨā¤¯ā¤ž", "config_set_by_file": "ā¤¸ā¤‚ā¤°ā¤šā¤¨ā¤ž ⤏⤧āĨā¤¯ā¤ž ā¤¸ā¤‚ā¤°ā¤šā¤¨ā¤ž ā¤–ā¤¤ā¤žā¤ĩ⤪āĨ€ā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ ⤍ā¤ŋā¤ļāĨā¤šā¤ŋ⤤ ⤕āĨ‡ā¤˛āĨ€ ā¤†ā¤šāĨ‡", "confirm_delete_library": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ⤍⤕āĨā¤•āĨ€ ā¤šāĨ‡ {library} ⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯ ā¤šā¤Ÿā¤ĩā¤žā¤¯ā¤šāĨ‡ ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", @@ -55,7 +54,6 @@ "disable_login": "ā¤ĒāĨā¤°ā¤ĩāĨ‡ā¤ļā¤žā¤§ā¤ŋ⤕⤰ ā¤ĩ⤰āĨā¤œāĨā¤¯ ā¤•ā¤°ā¤ž", "duplicate_detection_job_description": "ā¤¸ā¤žā¤°ā¤–āĨā¤¯ā¤ž ā¤›ā¤žā¤¯ā¤žā¤šā¤ŋ⤤āĨā¤°ā¤žā¤‚ā¤šā¤ž ā¤ļāĨ‹ā¤§ ⤘āĨ‡ā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤¯ā¤žā¤‚ā¤¤āĨā¤°ā¤ŋ⤕āĨ€ ā¤ĒāĨā¤°ā¤ļā¤ŋ⤕āĨā¤ˇā¤Ŗ ā¤ĻāĨā¤¯ā¤ž. ā¤šāĨ€ ā¤•ā¤žā¤°āĨā¤¯ā¤•āĨā¤ˇā¤Žā¤¤ā¤ž ⤚⤤āĨā¤° ā¤ļāĨ‹ā¤§ā¤ĒāĨā¤°ā¤Ŗā¤žā¤˛āĨ€ā¤ĩ⤰ ⤅ā¤ĩ⤞⤂ā¤ŦāĨ‚⤍ ā¤†ā¤šāĨ‡", "exclusion_pattern_description": "⤆ā¤Ē⤞āĨ‡ ⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯ ā¤šā¤žā¤ŗā¤¤ā¤žā¤¨ā¤ž ⤅ā¤Ēā¤ĩā¤žā¤Ļ ā¤¨ā¤ŽāĨā¤¨āĨ‡ ⤆ā¤Ē⤞āĨā¤¯ā¤žā¤˛ā¤ž ā¤–ā¤¤ā¤žā¤ĩ⤪āĨā¤¯ā¤ž ⤆⤪ā¤ŋ ⤰āĨā¤¨ā¤ŋ⤰āĨā¤ĻāĨ‡ā¤ļā¤ŋ⤕āĨ‡ā¤˛ā¤ž ā¤ĻāĨā¤°āĨā¤˛ā¤•āĨā¤ˇāĨ€ā¤¤ ⤕⤰āĨ‚ ā¤ĻāĨ‡ā¤¤ā¤žā¤¤. ⤆ā¤Ē⤞āĨā¤¯ā¤žā¤•ā¤ĄāĨ‡ ā¤•ā¤šāĨā¤šāĨā¤¯ā¤ž ā¤–ā¤¤ā¤žā¤ĩ⤪āĨā¤¯ā¤ž ā¤¸ā¤žā¤°ā¤–āĨā¤¯ā¤ž ā¤†ā¤¯ā¤žā¤¤ ⤕⤰āĨ‚ ā¤‡ā¤šāĨā¤›ā¤ŋ⤤ ⤍⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ⤅⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ (RAW) ā¤–ā¤¤ā¤žā¤ĩ⤪āĨā¤¯ā¤ž ⤅⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ⤍ā¤ŋ⤰āĨā¤ĻāĨ‡ā¤ļā¤ŋā¤•ā¤ž ⤅⤏⤞āĨā¤¯ā¤žā¤¸ ā¤šāĨ‡ ⤉ā¤Ē⤝āĨā¤•āĨā¤¤ ā¤†ā¤šāĨ‡.", - "external_library_created_at": "ā¤Ŧā¤žā¤šāĨā¤¯ ⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯ ({date} ⤰āĨ‹ā¤œāĨ€ ā¤Ŧ⤍ā¤ĩ⤞āĨ‡ ⤗āĨ‡ā¤˛āĨ‡)", "external_library_management": "ā¤Ŧā¤žā¤šāĨā¤¯ ⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤍", "face_detection": "ā¤ŽāĨā¤– ⤏⤂ā¤ļāĨ‹ā¤§ā¤¨" } diff --git a/i18n/ms.json b/i18n/ms.json index d5af40720d..cfe935102e 100644 --- a/i18n/ms.json +++ b/i18n/ms.json @@ -4,6 +4,7 @@ "account_settings": "Tetapan Akaun", "acknowledge": "Akui", "action": "Tindakan", + "action_common_update": "Kemaskini", "actions": "Tindakan", "active": "Aktif", "activity": "Aktiviti", @@ -22,6 +23,7 @@ "add_photos": "Tambah gambar", "add_to": "Tambah keâ€Ļ", "add_to_album": "Tambah ke album", + "add_to_album_bottom_sheet_added": "Dimasukkan 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", @@ -41,7 +43,6 @@ "backup_keep_last_amount": "Jumlah sandaran sebelumnya yang hendak disimpan", "backup_settings": "Tetapan Sandaran", "backup_settings_description": "Urus tetapan sandaran pangkalan data", - "check_all": "Tanda Semua", "cleared_jobs": "Kerja telah dibersihkan untuk: {job}", "config_set_by_file": "Konfigurasi kini ditetapkan oleh fail konfigurasi", "confirm_delete_library": "Adakah anda pasti mahu memadamkan {library}?", @@ -49,6 +50,7 @@ "confirm_email_below": "Untuk mengesahkan, sila taip \"{email}\" dibawah", "confirm_reprocess_all_faces": "Adakah anda pasti mahu memproses semula semua wajah? Ini juga akan membersihkan orang bernama.", "confirm_user_password_reset": "Adakah anda pasti mahu menetapkan semula kata laluan {user}?", + "confirm_user_pin_code_reset": "Adakah anda pasti untuk mengubah kod PIN {user}'s ?", "create_job": "Cipta tugas", "cron_expression": "Ungkapan cron", "cron_expression_description": "Tetapkan selang imbasan menggunakan format cron. Untuk maklumat lanjut, sila rujuk ke sebagai contoh Crontab Guru", @@ -56,14 +58,12 @@ "disable_login": "Lumpuhkan fungsi log masuk", "duplicate_detection_job_description": "Jalankan pembelajaran mesin pada aset untuk mengesan imej yang serupa. Bergantung pada Carian Pintar", "exclusion_pattern_description": "Corak pengecualian membolehkan anda mengabaikan fail dan folder semasa mengimbas pustaka anda. Ini berguna jika anda mempunyai folder yang mengandungi fail yang anda tidak mahu import, seperti fail RAW.", - "external_library_created_at": "Pustaka luaran (dicipta pada {date})", "external_library_management": "Pengurusan Perpustakaan Luar", "face_detection": "Pengesanan wajah", "face_detection_description": "Kesan wajah dalam aset menggunakan pembelajaran mesin. Untuk video, hanya lakaran kecil dipertimbangkan. \"Segar Semula\" memproses semula semua aset. \"Tetapkan Semula\" juga mengosongkan semua data wajah semasa. \"Hilang\" baris gilir aset yang belum diproses lagi. Wajah yang dikesan akan beratur untuk Pengecaman Wajah selepas Pengesanan Wajah selesai, menghimpunkannya kepada orang sedia ada atau baharu.", "facial_recognition_job_description": "Kumpulan wajah yang dikesan ke dalam orang. Langkah ini dijalankan selepas Pengesanan Wajah selesai. \"Tetapkan semula\" mengelompokkan semula semua wajah. \"Hilang\" jalankan proses pada wajah yang tidak mempunyai orang yang ditetapkan.", "failed_job_command": "Perintah {command} gagal untuk kerja: {job}", "force_delete_user_warning": "AMARAN: Ini akan mengalih keluar pengguna dan semua aset dengan serta-merta. Ia tidak boleh dibuat asal dan fail tidak boleh dipulihkan.", - "forcing_refresh_library_files": "Memaksa muat semula semua fail perpustakaan", "image_format": "Format", "image_format_description": "WebP menghasilkan fail yang lebih kecil daripada JPEG, tetapi lebih perlahan untuk mengekod.", "image_fullsize_description": "Imej bersaiz penuh dengan metadata yang dilucutkan, digunakan apabila dizum masuk", @@ -189,7 +189,7 @@ "oauth_enable_description": "Log masuk dengan OAuth", "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_mobile_redirect_uri_override_description": "Aktifkan apabila pembekal OAuth tidak membenarkan URI mudah alih, seperti ''{callback}''", "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.", @@ -199,8 +199,6 @@ "oauth_storage_quota_claim_description": "Tetapkan kuota storan pengguna secara automatik kepada nilai tuntutan ini.", "oauth_storage_quota_default": "Kuota storan lalai (GiB)", "oauth_storage_quota_default_description": "Kuota dalam GiB untuk digunakan apabila tiada tuntutan disediakan (Masukkan 0 untuk kuota tanpa had).", - "offline_paths": "Laluan Luar Talian", - "offline_paths_description": "Keputusan ini mungkin disebabkan oleh pemadaman manual fail yang bukan sebahagian daripada pustaka luaran.", "password_enable_description": "Log masuk dengan e-mel dan kata laluan", "password_settings": "Kata Laluan Log Masuk", "password_settings_description": "Urus tetapan-tetapan kata laluan log masuk", @@ -210,9 +208,6 @@ "refreshing_all_libraries": "Menyegarkan semua perpustakaan", "registration": "Pendaftaran Pentadbir", "registration_description": "Memandangkan anda adalah pengguna pertama pada sistem, anda akan ditugaskan sebagai Admin dan bertanggungjawab untuk tugas pentadbiran, serta pengguna tambahan yang akan anda tambah.", - "repair_all": "Baiki Semua", - "repair_matched_items": "Memadankan {count, plural, one {# item} other {# items}}", - "repaired_items": "Dibaiki {count, plural, one {# item} other {# items}}", "require_password_change_on_login": "Perlukan pengguna menukar kata laluan ketika log masuk pertama", "reset_settings_to_default": "Tetapkan semula tetapan kepada lalai", "reset_settings_to_recent_saved": "Tetapkan semula tetapan kepada tetapan yang disimpan baru-baru ini", @@ -241,7 +236,6 @@ "storage_template_migration_info": "Perubahan templat hanya akan digunakan pada aset baharu. Untuk menggunakan templat secara retroaktif pada aset-aset yang dimuat naik sebelum ini, jalankan {job}.", "storage_template_migration_job": "Kerja Migrasi Templat Storan", "storage_template_more_details": "Untuk butiran lanjut tentang ciri ini, rujuk kepada Templat Storan dan implikasi", - "storage_template_onboarding_description": "Apabila didayakan, ciri ini akan menyusun fail secara automatik berdasarkan templat yang ditentukan pengguna. Disebabkan isu kestabilan, ciri ini telah dimatikan secara umum. Untuk mendapatkan maklumat lanjut, sila lihat dokumentasi.", "storage_template_path_length": "Anggaran kepanjangan laluan: {length, number}/{limit, number}", "storage_template_settings": "Templat Storan", "storage_template_settings_description": "Urus struktur folder dan nama fail aset dimuat naik", @@ -253,7 +247,6 @@ "template_email_invite_album": "Templat Jemputan Album", "template_email_preview": "Previu", "template_email_settings": "Templat E-mel", - "template_email_settings_description": "Templat urus pemberitahuan dengan e-mel tersuai", "template_email_update_album": "Templat Kemas kini Album", "template_email_welcome": "Templat e-mel alu-aluan", "template_settings": "Templat Pemberitahuan", @@ -262,7 +255,6 @@ "theme_custom_css_settings_description": "Lembaran Gaya Lata membolehkan reka bentuk Immich disuaikan.", "theme_settings": "Tetapan Tema", "theme_settings_description": "Urus penyesuaian antara muka web Immich", - "these_files_matched_by_checksum": "Fail ini dipadankan dengan semakan mereka", "thumbnail_generation_job": "Jana Imej Kenit", "thumbnail_generation_job_description": "Janakan imej kenit yang besar, kecil, dan kabur untuk setiap aset, serta imej kenit untuk setiap orang", "transcoding_acceleration_api": "API Pecutan", @@ -293,7 +285,6 @@ "transcoding_hardware_acceleration_description": "Eksperimen; lebih pantas, tetapi akan mempunyai kualiti yang lebih rendah pada kadar bit yang sama", "transcoding_hardware_decoding": "Penyahkodan perkakasan", "transcoding_hardware_decoding_setting_description": "Mendayakan pecutan hujung ke hujung dan bukannya hanya mempercepatkan pengekodan. Mungkin tidak berfungsi pada semua video.", - "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "Bingkai-B maksimum", "transcoding_max_b_frames_description": "Nilai yang lebih tinggi meningkatkan kecekapan mampatan, tetapi memperlahankan pengekodan. Mungkin tidak serasi dengan pecutan perkakasan pada peranti lama. 0 melumpuhkan bingkai B, manakala -1 menetapkan nilai ini secara automatik.", "transcoding_max_bitrate": "Kadar bit maksimum", diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index b5dd76ce56..3478262019 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -1,7 +1,7 @@ { "about": "Om", "account": "Konto", - "account_settings": "Konto Innstillinger", + "account_settings": "Kontoinnstillinger", "acknowledge": "Bekreft", "action": "Handling", "action_common_update": "Oppdater", @@ -22,11 +22,11 @@ "add_partner": "Legg til partner", "add_path": "Legg til sti", "add_photos": "Legg til bilder", + "add_tag": "Legg til tag", "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": "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", @@ -34,6 +34,7 @@ "added_to_favorites_count": "Lagt til {count, number} i favoritter", "admin": { "add_exclusion_pattern_description": "Legg til ekskluderingsmønstre. Globbing med *, ** og ? støttes. For ÃĨ ignorere alle filer i en hvilken som helst mappe som heter \"Raw\", bruk \"**/Raw/**\". For ÃĨ ignorere alle filer som slutter pÃĨ \".tif\", bruk \"**/*.tif\". For ÃĨ ignorere en absolutt filplassering, bruk \"/filsti/til/ignorer/**\".", + "admin_user": "Administrasjonsbruker", "asset_offline_description": "Denne eksterne bibliotekressursen finnes ikke lenger pÃĨ disk og har blitt flyttet til papirkurven. Hvis filen ble flyttet innad i biblioteket, sjekk tidslinjen din for den tilsvarende ressursen. For ÃĨ gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengelig for Immich og skan biblioteket.", "authentication_settings": "Godkjenningsinnstillinger", "authentication_settings_description": "Administrer passord, OAuth, og andre innstillinger for autentisering", @@ -44,9 +45,7 @@ "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", + "backup_settings_description": "HÃĨndter innstillinger for database-dump.", "cleared_jobs": "Ryddet opp jobber for: {job}", "config_set_by_file": "Konfigurasjonen er for øyeblikket satt av en konfigurasjonsfil", "confirm_delete_library": "Er du sikker pÃĨ at du vil slette biblioteket {library}?", @@ -62,14 +61,12 @@ "disable_login": "Deaktiver innlogging", "duplicate_detection_job_description": "Kjør maskinlÃĻring pÃĨ filer for ÃĨ oppdage lignende bilder. Krever bruk av Smart Search", "exclusion_pattern_description": "Ekskluderingsmønstre lar deg ignorere filer og mapper nÃĨr du skanner biblioteket ditt. Dette er nyttig hvis du har mapper som inneholder filer du ikke vil importere, for eksempel RAW-filer.", - "external_library_created_at": "Ekstern bibliotek (opprettet {date})", "external_library_management": "Administrasjon av eksterne biblioteker", "face_detection": "Ansiktsgjenkjennelse", "face_detection_description": "Finn ansikter i bilder ved hjelp av maskinlÃĻring. For videoer brukes bare miniatyrbildet. \"Alle\" gÃĨr gjennom alle bilder (igjen). \"Tilbakestill\" fjerner all gjeldende ansiktsdata. \"Manglende\" legger til filer som ikke har blitt behandlet enda i køen. Oppdagede ansikter vil blir sendt til ansiktsgjenkjenning, og koblet til eksisterende eller nye personer.", "facial_recognition_job_description": "Kobler oppdagede ansikt til personer. Dette utføres etter at ansiktssøk er fullført. \"Tilbakestill\" (om-)grupperer alle ansikt pÃĨ nytt. \"Missing\" stiller opp ansikt som ikke har blitt tilordnet en person ennÃĨ.", "failed_job_command": "Kommandoen {command} feilet for jobb: {job}", "force_delete_user_warning": "ADVARSEL: Dette vil umiddelbart fjerne brukeren og alle data. Dette kan ikke angres, og filene kan ikke gjenopprettes.", - "forcing_refresh_library_files": "Tvinger oppdatering av alle bibliotekfiler", "image_format": "Format", "image_format_description": "WebP gir mindre filer enn JPEG, men er tregere ÃĨ lage.", "image_fullsize_description": "Bilde i full størrelse med strippet metadata, brukt nÃĨr du zoomer inn", @@ -174,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Merk: For ÃĨ bruke lagringsetiketten pÃĨ tidligere opplastede filer, kjør", "note_cannot_be_changed_later": "MERK: Dette kan ikke endres senere!", "notification_email_from_address": "Fra adresse", - "notification_email_from_address_description": "Avsenderens e-postadresse, for eksempel: \"Immich Photo Server \"", + "notification_email_from_address_description": "Avsenderens e-postadresse, for eksempel: \"Immich Photo Server \". Bruk en e-postadresse du har tillatelse til ÃĨ sende epost fra.", "notification_email_host_description": "Verten til e-posts serveren (f.eks. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorer sertifikatfeil", "notification_email_ignore_certificate_errors_description": "Ignorer valideringsfeil for TLS-sertifikat (ikke anbefalt)", @@ -198,7 +195,7 @@ "oauth_enable_description": "Logg inn med OAuth", "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_mobile_redirect_uri_override_description": "Aktiver nÃĨr OAuth-leverandøren ikke tillater en mobil URI, som ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "Administrer innstillinger for OAuth-innlogging", "oauth_settings_more_details": "For mer informasjon om denne funksjonen, se dokumentasjonen.", @@ -207,11 +204,9 @@ "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_storage_quota_default_description": "Kvote i GiB som skal brukes nÃĨr ingen krav er oppgitt.", "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", "password_settings": "Passordinnlogging", "password_settings_description": "Administrer innstillinger for passordinnlogging", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "Oppdaterer alle biblioteker", "registration": "Administrator registrering", "registration_description": "Siden du er den første brukeren pÃĨ systemet, vil du bli utnevnt til administrator og ha ansvar for administrative oppgaver. Du vil ogsÃĨ opprette eventuelle nye brukere.", - "repair_all": "Reparer alle", - "repair_matched_items": "Samsvarte med {count, plural, one {# element} other {# elementer}}", - "repaired_items": "Reparerte {count, plural, one {# item} other {# items}}", "require_password_change_on_login": "Krev at brukeren endrer passord ved første pÃĨlogging", "reset_settings_to_default": "Tilbakestill innstillinger til standard", "reset_settings_to_recent_saved": "Tilbakestill innstillingene til de nylig lagrede innstillingene", @@ -252,7 +244,7 @@ "storage_template_migration_info": "Lagringsmalen vil endre filtypen til smÃĨ bokstaver. Malendringer vil kun gjelde nye ressurser. For ÃĨ anvende malen pÃĨ tidligere opplastede ressurser, kjør {job}.", "storage_template_migration_job": "Migreringsjobb for lagringsmal", "storage_template_more_details": "For mer informasjon om denne funksjonen, se lagringsmalen og dens konsekvenser", - "storage_template_onboarding_description": "NÃĨr aktivert, vil denne funksjonen automatisk organisere filer basert pÃĨ en brukerdefinert mal. PÃĨ grunn av stabilitetsproblemer er funksjonen deaktivert som standard. For mer informasjon, se documentation.", + "storage_template_onboarding_description_v2": "NÃĨr aktivert vil denne funksjonen automatisk organisere filer basert pÃĨ en brukerdefinert mal. For mer informasjon, se denne linken dokumentasjon.", "storage_template_path_length": "Omtrentlig stilengdebegrensning: {length, number}/{limit, number}", "storage_template_settings": "Lagringsmal", "storage_template_settings_description": "Administrer mappestrukturen og filnavnet til opplastede fil", @@ -264,16 +256,14 @@ "template_email_invite_album": "Inviter Album Mal", "template_email_preview": "ForhÃĨndsvis", "template_email_settings": "Epost mal", - "template_email_settings_description": "Administrer tilpasset mal for varslings maler", "template_email_update_album": "Oppdater Album Mal", "template_email_welcome": "Mal for velkomst epost", "template_settings": "Varslings Mal", - "template_settings_description": "Administrer tilpassede maler for varsling.", + "template_settings_description": "Administrer tilpassede maler for varsling", "theme_custom_css_settings": "Egendefinert CSS", "theme_custom_css_settings_description": "Cascading Style Sheets gjør det mulig ÃĨ tilpasse designet av Immich.", "theme_settings": "Tema innstillinger", "theme_settings_description": "Administrer tilpasning av Immich webgrensesnitt", - "these_files_matched_by_checksum": "Disse filene er matchet ved hjelp av deres checksum", "thumbnail_generation_job": "Generer miniatyrbilder", "thumbnail_generation_job_description": "Generer store, smÃĨ og uskarpe miniatyrbilder for hver fil, samt miniatyrbilder for hver person", "transcoding_acceleration_api": "Akselerasjons-API", @@ -301,10 +291,9 @@ "transcoding_encoding_options": "Kodek Alternativer", "transcoding_encoding_options_description": "Sett kodeks, oppløsning, kvalitet og andre valg for koding av videoer", "transcoding_hardware_acceleration": "Maskinvareakselerasjon", - "transcoding_hardware_acceleration_description": "Eksperimentell; mye raskere, men vil ha lavere kvalitet ved samme bithastighet", + "transcoding_hardware_acceleration_description": "Eksperimentell: raskere transkoding, men kan ha lavere kvalitet ved samme bithastighet", "transcoding_hardware_decoding": "Maskinvaredekoding", "transcoding_hardware_decoding_setting_description": "Gjelder bare for NVENC,QSV og RKMPP. Aktiverer ende-til-ende akselerasjon i stedet for bare akselerering av koding. Vil ikke fungere med alle videoer.", - "transcoding_hevc_codec": "HEVC-codec", "transcoding_max_b_frames": "Maksimalt antall B-frames", "transcoding_max_b_frames_description": "Høyere verdier forbedrer komprimeringseffektiviteten, men senker ned kodingen. Kan vÃĻre inkompatibelt med maskinvareakselerasjon pÃĨ eldre enheter. 0 deaktiverer B-rammer, mens -1 setter verdien automatisk.", "transcoding_max_bitrate": "Maksimal bithastighet", @@ -342,8 +331,6 @@ "trash_number_of_days_description": "Antall dager ÃĨ beholde filer i papirkurven før de fjernes permanent", "trash_settings": "Innstillinger for papirkurv", "trash_settings_description": "Administrer papirkurv-innstillinger", - "untracked_files": "Usporede filer", - "untracked_files_description": "Disse filene er ikke sporet av applikasjonen. De kan vÃĻre resultatet av mislykkede flyttinger, avbrutte opplastninger eller etterlatt pÃĨ grunn av en feil", "user_cleanup_job": "Bruker opprydning", "user_delete_delay": "{user}s konto og elementer vil legges i kø for permanent sletting om {delay, plural, one {# dag} other {# dager}}.", "user_delete_delay_settings": "Sletteforsinkelse", @@ -402,10 +389,6 @@ "album_remove_user": "Fjerne bruker?", "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": "{count} objekter", - "album_thumbnail_card_shared": " ¡ Delt", - "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}", @@ -421,6 +404,9 @@ "album_with_link_access": "La hvem som helst med lenken se bilder og folk i dette albumet.", "albums": "Albumer", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albumer}}", + "albums_default_sort_order": "Standard sorteringsrekkefølge for albumer", + "albums_default_sort_order_description": "Standard sorteringsrekkefølge for bilder nÃĨr man lager et nytt album.", + "albums_feature_description": "Samlinger av bilder som kan deles med andre brukere.", "all": "Alle", "all_albums": "Alle album", "all_people": "Alle personer", @@ -479,9 +465,12 @@ "assets_added_count": "Lagt til {count, plural, one {# element} other {# elementer}}", "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_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} kan ikke legges til i albumet", "assets_count": "{count, plural, one {# fil} other {# filer}}", "assets_deleted_permanently": "{count} objekt(er) slettet permanent", "assets_deleted_permanently_from_server": "{count} objekt(er) slettet permanent fra Immich-serveren", + "assets_downloaded_failed": "{count, plural, one {Nedlasting av # fil - {error} fil feilet} other {Nedlastede # filer - {error} filer feilet}}", + "assets_downloaded_successfully": "{count, plural, one {Downloaded # file successfully} other {Downloaded # files successfully}}", "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}}", @@ -496,6 +485,7 @@ "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", "automatic_endpoint_switching_title": "Automatisk URL bytte", + "autoplay_slideshow": "Autoavspilling av lysbildefremvisning", "back": "Tilbake", "back_close_deselect": "Tilbake, lukk eller fjern merking", "background_location_permission": "Bakgrunnstillatelse for plassering", @@ -520,6 +510,7 @@ "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", @@ -576,21 +567,17 @@ "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 ({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 ({count})", - "cache_settings_image_cache_size": "Størrelse pÃĨ bildebuffer ({count} objekter)", "cache_settings_statistics_album": "Bibliotekminiatyrbilder", - "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 ({count} objekter)", "cache_settings_tile_subtitle": "Kontroller lokal lagring", "cache_settings_tile_title": "Lokal lagring", "cache_settings_title": "Bufringsinnstillinger", @@ -603,6 +590,8 @@ "cannot_merge_people": "Kan ikke slÃĨ sammen personer", "cannot_undo_this_action": "Du kan ikke gjøre om denne handlingen!", "cannot_update_the_description": "Kan ikke oppdatere beskrivelsen", + "cast": "Strøm", + "cast_description": "Konfigurer tilgjengelige cast-destinasjoner", "change_date": "Endre dato", "change_description": "Endre beskrivelsen", "change_display_order": "Endre visningsrekkefølge", @@ -620,7 +609,6 @@ "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. Denne sjekken kan ta noen minutter.", @@ -632,6 +620,7 @@ "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", @@ -659,10 +648,13 @@ "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", + "confirm_tag_face": "Vil du merke dette ansiktet som {name}?", + "confirm_tag_face_unnamed": "Vil du merke dette ansiktet?", + "connected_device": "Tilkoblet enhet", + "connected_to": "Koblet til", "contain": "Inneholder", "context": "Kontekst", "continue": "Fortsett", - "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", @@ -711,6 +703,7 @@ "daily_title_text_date": "E MMM. dd", "daily_title_text_date_year": "E MMM. dddd, yyyy", "dark": "Mørk", + "darkTheme": "Aktiver mørkt utsende", "date_after": "Dato etter", "date_and_time": "Dato og tid", "date_before": "Dato før", @@ -756,7 +749,9 @@ "direction": "Retning", "disabled": "Deaktivert", "disallow_edits": "Forby redigering", + "discord": "Discord", "discover": "Oppdag", + "discovered_devices": "Oppdagede enheter", "dismiss_all_errors": "Avvis alle feil", "dismiss_error": "Avvis feil", "display_options": "Visningsalternativer", @@ -772,7 +767,6 @@ "download_enqueue": "Nedlasting satt i kø", "download_error": "Nedlasting feilet", "download_failed": "Nedlasting feilet", - "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", @@ -835,6 +829,7 @@ "error_delete_face": "Feil ved sletting av ansikt fra aktivia", "error_loading_image": "Feil ved lasting av bilde", "error_saving_image": "Feil: {error}", + "error_tag_face_bounding_box": "Feil ved merking av ansikt - klarte ikke ÃĨ fÃĨ koordinatene pÃĨ omrisset", "error_title": "Feil - Noe gikk galt", "errors": { "cannot_navigate_next_asset": "Kan ikke navigere til neste fil", @@ -847,7 +842,6 @@ "cant_get_number_of_comments": "Kan ikke hente antall kommentarer", "cant_search_people": "Kan ikke søke etter mennesker", "cant_search_places": "Kan ikke søke etter plasser", - "cleared_jobs": "Fjernet jobber for: {job}", "error_adding_assets_to_album": "Feil med ÃĨ legge til bilder til album", "error_adding_users_to_album": "Feil, kan ikke legge til brukere til album", "error_deleting_shared_user": "Feil med ÃĨ slette delt bruker", @@ -856,7 +850,6 @@ "error_removing_assets_from_album": "Feil med ÃĨ fjerne bilder fra album, sjekk konsoll for mer detaljer", "error_selecting_all_assets": "Feil med ÃĨ velge alle bilder", "exclusion_pattern_already_exists": "Dette eksklusjonsmønsteret eksisterer allerede.", - "failed_job_command": "Kommandoen {command} mislyktes for jobben: {job}", "failed_to_create_album": "Feil med ÃĨ lage album", "failed_to_create_shared_link": "Feil med ÃĨ lage delt lenke", "failed_to_edit_shared_link": "Feilet med ÃĨ redigere delt lenke", @@ -875,7 +868,6 @@ "paths_validation_failed": "{paths, plural, one {# sti} other {# sti}} mislyktes validering", "profile_picture_transparent_pixels": "Profil bilde kan ikke ha gjennomsiktige piksler. Vennligst zoom inn og/eller flytt bilde.", "quota_higher_than_disk_size": "Du har satt en kvote høyere enn diskstørrelsen", - "repair_unable_to_check_items": "Kan ikke sjekke {count, select, one {element} other {elementer}}", "unable_to_add_album_users": "Kan ikke legge til brukere i albumet", "unable_to_add_assets_to_shared_link": "Kan ikke legge til bilder til delt lenke", "unable_to_add_comment": "Kan ikke legge til kommentar", @@ -894,7 +886,6 @@ "unable_to_change_visibility": "Kan ikke endre synlighet for {count, plural, one {# person} other {# people}}", "unable_to_complete_oauth_login": "Kunne ikke fullføre OAuth innlogging", "unable_to_connect": "Kan ikke koble til", - "unable_to_connect_to_server": "Kan ikke koble til server", "unable_to_copy_to_clipboard": "Kan ikke kopiere til utklippstavlen, sørg for at du fÃĨr tilgang til siden via HTTPS", "unable_to_create_admin_account": "Kan ikke opprette administrator bruker", "unable_to_create_api_key": "Kan ikke opprette en ny API-nøkkel", @@ -918,14 +909,9 @@ "unable_to_hide_person": "Kan ikke skjule person", "unable_to_link_motion_video": "Kan ikke lenke bevegelig video", "unable_to_link_oauth_account": "Kan ikke lenke til OAuth-konto", - "unable_to_load_album": "Kan ikke laste inn album", - "unable_to_load_asset_activity": "Kan ikke laste inn aktivitet for filen", - "unable_to_load_items": "Kan ikke laste inn elementer", - "unable_to_load_liked_status": "Kan ikke laste inn likt status", "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", @@ -933,11 +919,9 @@ "unable_to_remove_album_users": "Kan ikke fjerne brukere fra album", "unable_to_remove_api_key": "Kan ikke fjerne API-nøkkel", "unable_to_remove_assets_from_shared_link": "Kunne ikke fjerne bilder fra delt lenke", - "unable_to_remove_deleted_assets": "Kan ikke fjerne offlinefiler", "unable_to_remove_library": "Kan ikke fjerne bibliotek", "unable_to_remove_partner": "Kan ikke fjerne partner", "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", @@ -973,7 +957,6 @@ "exif_bottom_sheet_location": "PLASSERING", "exif_bottom_sheet_people": "MENNESKER", "exif_bottom_sheet_person_add_person": "Legg til navn", - "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}", @@ -1011,6 +994,7 @@ "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", @@ -1020,6 +1004,8 @@ "folders": "Mapper", "folders_feature_description": "Utforsker mappe visning for bilder og videoer pÃĨ fil systemet", "forward": "Fremover", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Denne funksjonen laster eksterne ressurser fra Google for ÃĨ fungere.", "general": "Generelt", "get_help": "FÃĨ Hjelp", "get_wifiname_error": "Kunne ikke hente Wi-Fi-navnet. Sørg for at du har gitt de nødvendige tillatelsene og er koblet til et Wi-Fi-nettverk", @@ -1086,6 +1072,7 @@ "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", @@ -1096,6 +1083,7 @@ "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}}", @@ -1106,6 +1094,12 @@ "invalid_date_format": "Ugyldig datoformat", "invite_people": "Inviter Personer", "invite_to_album": "Inviter til album", + "ios_debug_info_fetch_ran_at": "Fetch kjørte {dateTime}", + "ios_debug_info_last_sync_at": "Siste synkronisering {dateTime}", + "ios_debug_info_no_processes_queued": "Ingen bakgrunnsprosesser i kø", + "ios_debug_info_no_sync_yet": "Ingen bakgrunnssynkroniseringsjobb har kjørt ennÃĨ", + "ios_debug_info_processes_queued": "{count, plural, one {{count} bakgrunnsprosess lagt i kø} other {{count} bakgrunnsprosesser lagt i kø}}", + "ios_debug_info_processing_ran_at": "Behandlingen ble kjørt {dateTime}", "items_count": "{count, plural, one {# gjenstand} other {# gjenstander}}", "jobs": "Oppgaver", "keep": "Behold", @@ -1114,6 +1108,9 @@ "kept_this_deleted_others": "Behold denne filen og slett {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Tastatursnarveier", "language": "SprÃĨk", + "language_no_results_subtitle": "Prøv ÃĨ endre søkeord", + "language_no_results_title": "Ingen sprÃĨk funnet", + "language_search_hint": "Søker etter sprÃĨk...", "language_setting_description": "Velg ditt foretrukket sprÃĨk", "last_seen": "Sist sett", "latest_version": "Siste versjon", @@ -1139,6 +1136,7 @@ "list": "Liste", "loading": "Laster", "loading_search_results_failed": "Klarte ikke ÃĨ laste inn søkeresultater", + "local_asset_cast_failed": "Kan ikke caste et bilde som ikke er lastet opp til serveren", "local_network": "Lokalt nettverk", "local_network_sheet_info": "Appen vil koble til serveren via denne URL-en nÃĨr du bruker det angitte Wi-Fi-nettverket", "location_permission": "Stedstillatelse", @@ -1152,6 +1150,7 @@ "locked_folder": "LÃĨst mappe", "log_out": "Logg ut", "log_out_all_devices": "Logg ut fra alle enheter", + "logged_in_as": "Logget inn som {user}", "logged_out_all_devices": "Logg ut av alle enheter", "logged_out_device": "Logg ut enhet", "login": "Logg inn", @@ -1243,6 +1242,7 @@ "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", @@ -1278,6 +1278,7 @@ "no_archived_assets_message": "Arkiver bilder og videoer for ÃĨ skjule dem fra visningen av bildene dine", "no_assets_message": "KLIKK FOR Å LASTE OPP DITT FØRSTE BILDE", "no_assets_to_show": "Ingen objekter ÃĨ vise", + "no_cast_devices_found": "Ingen caste-enheter oppdaget", "no_duplicates_found": "Ingen duplikater ble funnet.", "no_exif_info_available": "Ingen EXIF-informasjon tilgjengelig", "no_explore_results_message": "Last opp flere bilder for ÃĨ utforske samlingen din.", @@ -1303,16 +1304,18 @@ "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", - "onboarding_privacy_description": "Følgene (valgfrie) funksjoner er avhengige av eksterne tjeneste, og kan bli deaktivert nÃĨr som helst under administrator instillinger.", + "onboarding_locale_description": "Velg ditt foretrukne sprÃĨk. Du kan endre dette senere under innstillinger.", + "onboarding_privacy_description": "Følgene (valgfrie) funksjoner er avhengige av eksterne tjenester, og kan bli deaktivert nÃĨr som helst under innstillinger.", + "onboarding_server_welcome_description": "La oss sette opp din instans med noen standard innstillinger.", "onboarding_theme_description": "Velg et fargetema for din bruker. Du kan endre denne senere under dine instillinger.", - "onboarding_welcome_description": "La oss sette opp denne installasjonen med noen vanlige instillinger.", + "onboarding_user_welcome_description": "La oss fÃĨ deg startet!", "onboarding_welcome_user": "Velkommen, {user}", "online": "Tilkoblet", "only_favorites": "Bare favoritter", @@ -1323,11 +1326,13 @@ "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", + "owned": "Dine", "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", @@ -1367,6 +1372,8 @@ "permanently_delete_assets_prompt": "Er du sikker pÃĨ at du vil permanent slette {count, plural, one {this asset?} other {these # assets?}} Dette vil ogsÃĨ slette {count, plural, one {it from its} other {them from their}} album.", "permanently_deleted_asset": "Filen har blitt permanent slettet", "permanently_deleted_assets_count": "Permanent slett {count, plural, one {# asset} other {# assets}}", + "permission": "Tillatelse", + "permission_empty": "Dine tillatelser burde ikke vÃĻre tomme", "permission_onboarding_back": "Tilbake", "permission_onboarding_continue_anyway": "Fortsett uansett", "permission_onboarding_get_started": "Kom i gang", @@ -1375,6 +1382,7 @@ "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", "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.", @@ -1395,13 +1403,17 @@ "play_motion_photo": "Spill av bevegelsesbilde", "play_or_pause_video": "Spill av eller pause video", "please_auth_to_access": "Vennligst autentiser for ÃĨ fortsette", + "port": "Port", "preferences_settings_subtitle": "Administrer appens preferanser", "preferences_settings_title": "Innstillinger", "preset": "ForhÃĨndsinstilling", "preview": "ForhÃĨndsvis", "previous": "Forrige", "previous_memory": "Forrige minne", - "previous_or_next_photo": "Forrige eller neste bilde", + "previous_or_next_day": "Dag forover/bakover", + "previous_or_next_month": "MÃĨned forover/bakover", + "previous_or_next_photo": "Bilde forover/bakover", + "previous_or_next_year": "År forover/bakover", "primary": "PrimÃĻr", "privacy": "Privat", "profile": "Profil", @@ -1409,6 +1421,7 @@ "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}", @@ -1445,6 +1458,7 @@ "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", @@ -1483,10 +1497,11 @@ "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_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", + "remove_tag": "Fjern tag", "remove_url": "Fjern URL", "remove_user": "Fjern bruker", "removed_api_key": "Fjernet API-nøkkel: {name}", @@ -1519,6 +1534,7 @@ "retry_upload": "Prøv opplasting pÃĨ nytt", "review_duplicates": "GjennomgÃĨ duplikater", "role": "Rolle", + "role_editor": "Redigerer", "role_viewer": "Visning", "save": "Lagre", "save_to_gallery": "Lagre til galleriet", @@ -1592,6 +1608,7 @@ "select_album_cover": "Velg albumomslag", "select_all": "Velg alle", "select_all_duplicates": "Velg alle duplikater", + "select_all_in": "Velg alt i {group}", "select_avatar_color": "Velg avatarfarge", "select_face": "Velg ansikt", "select_featured_photo": "Velg fremhevet bilde", @@ -1612,6 +1629,7 @@ "server_info_box_server_url": "Server-adresse", "server_offline": "Server frakoblet", "server_online": "Server tilkoblet", + "server_privacy": "Server personvern", "server_stats": "Server Statistikk", "server_version": "Server Versjon", "set": "Sett", @@ -1621,6 +1639,7 @@ "set_date_of_birth": "Sett fødselsdato", "set_profile_picture": "Sett profilbilde", "set_slideshow_to_fullscreen": "Sett lysbildefremvisning til fullskjerm", + "set_stack_primary_asset": "Velg som primÃĻrbilde", "setting_image_viewer_help": "Detaljvisningen laster først miniatyrbildet, deretter forhÃĨndsvisningsbildet (hvis aktivert), og til slutt originalen (hvis aktivert).", "setting_image_viewer_original_subtitle": "Aktiver for ÃĨ laste originalbildet i full oppløsning (stort!). Deaktiver for ÃĨ spare databruk (bÃĨde nettverksbruk og bufferdata pÃĨ enheten).", "setting_image_viewer_original_title": "Last originalbildet", @@ -1629,7 +1648,6 @@ "setting_image_viewer_title": "Bilder", "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: {duration}", "setting_notifications_notify_hours": "{count} timer", "setting_notifications_notify_immediately": "umiddelbart", @@ -1641,6 +1659,7 @@ "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", @@ -1691,6 +1710,7 @@ "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", @@ -1753,8 +1773,11 @@ "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_casting": "Stopp casting", "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.", @@ -1812,7 +1835,7 @@ "to_parent": "GÃĨ til overodnet", "to_trash": "Papirkurv", "toggle_settings": "Bytt innstillinger", - "toggle_theme": "Bytt tema", + "total": "Total", "total_usage": "Totalt brukt", "trash": "Papirkurv", "trash_all": "Slett alt", @@ -1828,10 +1851,12 @@ "trash_page_select_assets_btn": "Velg objekter", "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 #}}", + "undo": "Angre", "unfavorite": "Fjern favoritt", "unhide_person": "Vis person", "unknown": "Ukjent", @@ -1848,10 +1873,9 @@ "unsaved_change": "Ulagrede endringer", "unselect_all": "Fjern alle valg", "unselect_all_duplicates": "Fjern markeringen av alle duplikater", + "unselect_all_in": "Fjern alle i {group}", "unstack": "avstable", "unstacked_assets_count": "Ikke stablet {count, plural, one {# asset} other {# assets}}", - "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", @@ -1868,6 +1892,7 @@ "upload_success": "Opplasting vellykket, oppdater siden for ÃĨ se nye opplastninger.", "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", @@ -1878,6 +1903,7 @@ "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_privacy": "Personverninnstillinger", "user_purchase_settings": "Kjøpe", "user_purchase_settings_description": "Administrer dine kjøp", "user_role_set": "Sett {user} som {role}", @@ -1893,13 +1919,9 @@ "version": "Versjon", "version_announcement_closing": "Din venn, Alex", "version_announcement_message": "Hei! En ny versjon av Immich er tilgjengelig. Vennligst ta deg tid til ÃĨ lese utgivelsesnotatene for ÃĨ sikre at oppsettet ditt er oppdatert for ÃĨ forhindre feilkonfigurasjoner, spesielt hvis du bruker WatchTower eller en annen mekanisme som hÃĨndterer oppdatering av Immich-forekomsten din automatisk.", - "version_announcement_overlay_release_notes": "endringsloggen", - "version_announcement_overlay_text_1": "Hei, det er en ny versjon av", - "version_announcement_overlay_text_2": "vennligst ta deg tid til ÃĨ besøke ", - "version_announcement_overlay_text_3": " og verifiser at docker-compose og .env-oppsettet ditt er oppdatert for ÃĨ forhindre en eventuell feilkonfigurasjon, spesielt hvis du benytter WatchTower eller en annen tjeneste som hÃĨndterer oppdatering av server-applikasjonen automatisk.", - "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", @@ -1916,6 +1938,7 @@ "view_previous_asset": "Vis forrige fil", "view_qr_code": "Vis QR-kode", "view_stack": "Vis Stabbel", + "view_user": "Vis bruker", "viewer_remove_from_stack": "Fjern fra stabling", "viewer_stack_use_as_main_asset": "Bruk som hovedobjekt", "viewer_unstack": "avstable", diff --git a/i18n/nl.json b/i18n/nl.json index 80aa9ea76b..07699e0b31 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -1,6 +1,7 @@ { "about": "Over", - "account_settings": "Account Instellingen", + "account": "Account", + "account_settings": "Accountinstellingen", "acknowledge": "Begrepen", "action": "Actie", "action_common_update": "Bijwerken", @@ -21,11 +22,11 @@ "add_partner": "Partner toevoegen", "add_path": "Pad toevoegen", "add_photos": "Foto's toevoegen", + "add_tag": "Tag toevoegen", "add_to": "Toevoegen aanâ€Ļ", "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", @@ -33,51 +34,48 @@ "added_to_favorites_count": "{count, number} toegevoegd aan favorieten", "admin": { "add_exclusion_pattern_description": "Uitsluitingspatronen toevoegen. Globbing met *, ** en ? wordt ondersteund. Om alle bestanden in een map met de naam \"Raw\" te negeren, gebruik \"**/Raw/**\". Om alle bestanden die eindigen op \".tif\" te negeren, gebruik \"**/*.tif\". Om een absoluut pad te negeren, gebruik \"/path/to/ignore/**\".", + "admin_user": "Beheerder gebruiker", "asset_offline_description": "Deze asset uit een externe bibliotheek is niet meer beschikbaar op de schijf en is naar de prullenbak verplaatst. Als het bestand binnen de bibliotheek is verplaatst, controleer dan je tijdlijn voor de nieuwe bijbehorende asset. Om dit bestand te herstellen, zorg ervoor dat het onderstaande bestandspad toegankelijk is voor Immich en scan de bibliotheek opnieuw.", "authentication_settings": "Authenticatie-instellingen", "authentication_settings_description": "Wachtwoord, OAuth, en andere authenticatie-instellingen beheren", "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": "Maak database backup", - "backup_database_enable_description": "Database dumps activeren", + "backup_database": "Maak database back-up", + "backup_database_enable_description": "Database back-ups 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", + "backup_settings": "Database back-up instellingen", + "backup_settings_description": "Beheer database back-up instellingen.", "cleared_jobs": "Taken gewist voor: {job}", "config_set_by_file": "Instellingen worden momenteel beheerd door een configuratiebestand", "confirm_delete_library": "Weet je zeker dat je de bibliotheek {library} wilt verwijderen?", "confirm_delete_library_assets": "Weet je zeker dat je deze bibliotheek wilt verwijderen? Hiermee {count, plural, one {wordt # asset} other {worden alle # assets}} uit Immich verwijderd en dit kan niet ongedaan worden gemaakt. Bestanden blijven op de schijf staan.", "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_password_reset": "Weet je 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", + "cron_expression_description": "Stel het scaninterval in met het cron-formaat. Voor meer informatie kun je bijvoorbeeld kijken naar Crontab Guru", "cron_expression_presets": "Cron-expressie presets", "disable_login": "Inloggen uitschakelen", "duplicate_detection_job_description": "Machine learning uitvoeren op assets om vergelijkbare assets te vinden. Dit is gebaseerd op Slim Zoeken", "exclusion_pattern_description": "Met uitsluitingspatronen kun je bestanden en mappen negeren bij het scannen van je bibliotheek. Dit is handig als je mappen hebt met bestanden die je niet wilt importeren, zoals RAW bestanden.", - "external_library_created_at": "Externe bibliotheek (gemaakt op {date})", "external_library_management": "Externe bibliotheek beheren", "face_detection": "Gezichtsdetectie", - "face_detection_description": "Detecteer gezichten in assets met behulp van machine learning. Voor video's wordt alleen de thumbnail gebruikt. \"Resetten\" verwerkt alle assets (opnieuw). \"Reset\" verwijdert daarnaast alle huidige gezichtgegevens. \"Missend\" plaatst assets in de wachtrij die nog niet zijn verwerkt. Gedetecteerde gezichten worden in de wachtrij geplaatst voor gezichtsherkenning nadat gezichtsdetectie is voltooid, waarbij ze worden gegroepeerd in bestaande of nieuwe mensen.", + "face_detection_description": "Detecteer gezichten in assets met behulp van machine learning. Voor video's wordt alleen de thumbnail gebruikt. \"Vernieuwen\" verwerkt alle assets (opnieuw). \"Reset\" verwijdert daarnaast alle huidige gezichtgegevens. \"Missend\" plaatst assets in de wachtrij die nog niet zijn verwerkt. Gedetecteerde gezichten worden in de wachtrij geplaatst voor gezichtsherkenning nadat gezichtsdetectie is voltooid, waarbij ze worden gegroepeerd in bestaande of nieuwe mensen.", "facial_recognition_job_description": "Groepeer gedetecteerde gezichten tot mensen. Deze stap wordt uitgevoerd nadat gezichtsdetectie is voltooid. \"Resetten\" (her-)clustert alle gezichten. \"Missend\" plaatst gezichten in de wachtrij waaraan geen persoon is toegewezen.", "failed_job_command": "Commando {command} mislukt voor taak: {job}", "force_delete_user_warning": "WAARSCHUWING: Hiermee worden de gebruiker en alle assets onmiddellijk verwijderd. Dit kan niet ongedaan worden gemaakt en de bestanden kunnen niet worden hersteld.", - "forcing_refresh_library_files": "Geforceerd vernieuwen van alle bibliotheekbestanden", "image_format": "Formaat", "image_format_description": "WebP produceert kleinere bestanden dan JPEG, maar is langzamer om te verwerken.", "image_fullsize_description": "Afbeelding op ware grootte met gestripte metadata, gebruikt bij inzoomen", - "image_fullsize_enabled": "Genereer afbeeldingen op ware grootte inschakelen", - "image_fullsize_enabled_description": "Genereer afbeelding op volledig formaat voor niet-webvriendelijke formaten. Als “Ingebed voorvertoning verkiezen” is ingeschakeld, worden ingesloten voorvertoningen direct gebruikt zonder conversie. Heeft geen invloed op webvriendelijke formaten zoals JPEG.", + "image_fullsize_enabled": "Genereren van afbeeldingen op ware grootte inschakelen", + "image_fullsize_enabled_description": "Genereer afbeelding op volledig formaat voor niet-webvriendelijke formaten. Als “Verkies ingesloten voorbeeldafbeelding” is ingeschakeld, worden ingesloten voorvertoningen direct gebruikt zonder conversie. Heeft geen invloed op webvriendelijke formaten zoals JPEG.", "image_fullsize_quality_description": "Beeldkwaliteit op ware grootte van 1-100. Hoger is beter, maar genereert grotere bestanden.", "image_fullsize_title": "Instellingen afbeelding op ware grootte", - "image_prefer_embedded_preview": "Ingebedde voorbeeldafbeelding gebruiken", - "image_prefer_embedded_preview_setting_description": "Ingebedde voorbeeldafbeelding van RAW bestanden gebruiken als invoer voor beeldverwerking wanneer beschikbaar. Dit kan preciezere kleuren produceren voor sommige afbeeldingen, maar de kwaliteit van het voorbeeld is afhankelijk van de camera en de afbeelding kan mogelijk meer compressie-artefacten bevatten.", + "image_prefer_embedded_preview": "Verkies ingesloten voorbeeldafbeelding", + "image_prefer_embedded_preview_setting_description": "Gebruik ingesloten voorbeeldafbeelding van RAW-bestanden als invoer voor beeldverwerking wanneer beschikbaar. Dit kan preciezere kleuren produceren voor sommige afbeeldingen, maar de kwaliteit van het voorbeeld is afhankelijk van de camera en de afbeelding kan mogelijk meer compressie-artefacten bevatten.", "image_prefer_wide_gamut": "Voorkeur geven aan wide gamut", "image_prefer_wide_gamut_setting_description": "Display P3 gebruiken voor voorbeeldafbeeldingen. Dit behoudt de levendigheid van afbeeldingen met brede kleurruimtes beter, maar afbeeldingen kunnen er anders uitzien op oude apparaten met een oude browserversie. sRGB-afbeeldingen blijven sRGB gebruiken om kleurverschuivingen te vermijden.", "image_preview_description": "Middelgrote afbeelding met verwijderde metadata, gebruikt bij het bekijken van een enkele asset en voor machine learning", @@ -86,11 +84,11 @@ "image_quality": "Kwaliteit", "image_resolution": "Resolutie", "image_resolution_description": "Hogere resoluties behouden meer details, maar verhogen de coderingstijd, bestandsgrootte en kunnen de app vertragen.", - "image_settings": "Afbeeldings instellingen", + "image_settings": "Afbeeldingsinstellingen", "image_settings_description": "Beheer de kwaliteit en resolutie van gegenereerde afbeeldingen", "image_thumbnail_description": "Kleine thumbnail zonder metadata, gebruikt voor het bekijken van groepen met foto's zoals de tijdlijn", "image_thumbnail_quality_description": "Thumbnail kwaliteit van 1-100. Hoger is beter, maar produceert grotere bestanden en kan de app vertragen.", - "image_thumbnail_title": "Thumbnail instellingen", + "image_thumbnail_title": "Thumbnailinstellingen", "job_concurrency": "{job} gelijktijdigheid", "job_created": "Taak aangemaakt", "job_not_concurrency_safe": "Deze taak kan niet gelijktijdig worden uitgevoerd.", @@ -158,8 +156,8 @@ "map_settings": "Kaart", "map_settings_description": "Beheer kaartinstellingen", "map_style_description": "URL naar een style.json kaartthema", - "memory_cleanup_job": "Geheugen opschonen", - "memory_generate_job": "Geheugen genereren", + "memory_cleanup_job": "Herinneringen opschonen", + "memory_generate_job": "Herinneringen genereren", "metadata_extraction_job": "Metadata ophalen", "metadata_extraction_job_description": "Metadata ophalen van iedere asset, zoals GPS, gezichten en resolutie", "metadata_faces_import_setting": "Gezichten importeren inschakelen", @@ -173,10 +171,10 @@ "note_apply_storage_label_previous_assets": "Opmerking: om het opslaglabel toe te passen op eerder geÃŧploade assets, voer de volgende taak uit", "note_cannot_be_changed_later": "LET OP: Dit kan later niet meer worden gewijzigd!", "notification_email_from_address": "Adres afzender", - "notification_email_from_address_description": "E-mailadres van de afzender, bijvoorbeeld: \"Immich Foto Server \"", + "notification_email_from_address_description": "E-mailadres van de afzender, bijvoorbeeld: \"Immich Foto Server \". Zorg ervoor dat je een adres gebruikt waar je e-mails van mag verzenden.", "notification_email_host_description": "Host van de e-mailserver (bijv. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Negeer certificaatfouten", - "notification_email_ignore_certificate_errors_description": "Negeer TLS certificaat validatiefouten (niet aanbevolen)", + "notification_email_ignore_certificate_errors_description": "Negeer validatiefouten van TLS-certificaat (niet aanbevolen)", "notification_email_password_description": "Wachtwoord om te gebruiken bij authenticatie met de e-mailserver", "notification_email_port_description": "Poort van de e-mailserver (bijv. 25, 465 of 587)", "notification_email_sent_test_email_button": "Verstuur testmail en opslaan", @@ -184,7 +182,7 @@ "notification_email_test_email": "Verstuur testmail", "notification_email_test_email_failed": "Verzenden van testmail is mislukt, controleer je instellingen", "notification_email_test_email_sent": "Een testmail is verzonden naar {email}. Controleer je inbox.", - "notification_email_username_description": "Gebruikers om te gebruiken bij authenticatie met de e-mailserver", + "notification_email_username_description": "Gebruikersnaam voor authenticatie met de e-mailserver", "notification_enable_email_notifications": "E-mailmeldingen inschakelen", "notification_settings": "Meldingsinstellingen", "notification_settings_description": "Beheer meldingsinstellingen, inclusief e-mail", @@ -192,12 +190,12 @@ "oauth_auto_launch_description": "Automatisch inloggen met OAuth bij het navigeren naar de inlogpagina", "oauth_auto_register": "Automatisch registreren", "oauth_auto_register_description": "Nieuwe gebruikers automatisch registreren na inloggen met OAuth", - "oauth_button_text": "Button tekst", + "oauth_button_text": "Knoptekst", "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_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_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_settings": "OAuth", "oauth_settings_description": "Beheer OAuth inloginstellingen", "oauth_settings_more_details": "Raadpleeg de documentatie voor meer informatie over deze functie.", @@ -206,11 +204,9 @@ "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_storage_quota_default_description": "Limiet in GiB die moet worden gebruikt als er geen claim is opgegeven.", "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", "password_settings": "Inloggen met wachtwoord", "password_settings_description": "Beheer instellingen voor inloggen met wachtwoord", @@ -218,11 +214,8 @@ "person_cleanup_job": "Persoon opschoning", "quota_size_gib": "Opslaglimiet (GiB)", "refreshing_all_libraries": "Alle bibliotheken aan het vernieuwen", - "registration": "Beheerder registratie", + "registration": "Beheerdersregistratie", "registration_description": "Omdat je de eerste gebruiker in het systeem bent, word je toegewezen als beheerder en ben je verantwoordelijk voor administratieve taken. Extra gebruikers kunnen door jou worden aangemaakt.", - "repair_all": "Repareer alle", - "repair_matched_items": "Overeenkomend {count, plural, one {# item} other {# items}}", - "repaired_items": "Gerepareerd {count, plural, one {# item} other {# items}}", "require_password_change_on_login": "Vereisen dat de gebruiker het wachtwoord wijzigt bij de eerste keer inloggen", "reset_settings_to_default": "Instellingen teruggezet naar standaard", "reset_settings_to_recent_saved": "Instellingen zijn gereset naar de recent opgeslagen instellingen", @@ -248,10 +241,10 @@ "storage_template_hash_verification_enabled_description": "Zet hashverificatie aan, schakel dit niet uit tenzij je zeker bent van de implicaties", "storage_template_migration": "Opslagtemplate migratie", "storage_template_migration_description": "Pas de huidige {template} toe op eerder geÃŧploade assets", - "storage_template_migration_info": "Wijzigingen in het opslag template worden alleen toegepast op nieuwe assets. Om de template met terugwerkende kracht toe te passen op eerder geÃŧploade assets, voer je de {job} uit.", + "storage_template_migration_info": "Wijzigingen in de opslagtemplate worden alleen toegepast op nieuwe assets. Om de template met terugwerkende kracht toe te passen op eerder geÃŧploade assets, voer je de {job} uit.", "storage_template_migration_job": "Opslagtemplate migratietaak", "storage_template_more_details": "Voor meer details over deze functie, bekijk de Opslagstemplate en de implicaties daarvan", - "storage_template_onboarding_description": "Wanneer ingeschakeld, zal deze functie bestanden automatisch organiseren gebaseerd op een gebruiker-definieerd template. Gezien de stabiliteitsproblemen is de functie standaard uitgeschakeld. Voor meer informatie, bekijk de documentatie.", + "storage_template_onboarding_description_v2": "Wanneer ingeschakeld, zal deze functie bestanden automatisch organiseren gebaseerd op een template gedefinieerd door de gebruiker. Voor meer informatie, bekijk de documentatie.", "storage_template_path_length": "Geschatte padlengte: {length, number}/{limit, number}", "storage_template_settings": "Opslagtemplate", "storage_template_settings_description": "Beheer de mapstructuur en bestandsnaam van geÃŧploade bestanden", @@ -262,17 +255,15 @@ "template_email_if_empty": "Wanneer het sjabloon leeg is, wordt de standaard mail gebruikt.", "template_email_invite_album": "Uitgenodigd in album sjabloon", "template_email_preview": "Voorbeeld", - "template_email_settings": "Email", - "template_email_settings_description": "Beheer aangepaste email melding sjablonen", + "template_email_settings": "Emailsjablonen", "template_email_update_album": "Update in album sjabloon", - "template_email_welcome": "Welkom email sjabloon", + "template_email_welcome": "Welkomstmail sjabloon", "template_settings": "Melding sjablonen", "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", + "theme_settings": "Thema-instellingen", "theme_settings_description": "Beheer het uiterlijk van de Immich webinterface", - "these_files_matched_by_checksum": "Deze bestanden worden gematcht op basis van hun checksums", "thumbnail_generation_job": "Thumbnail genereren", "thumbnail_generation_job_description": "Genereer grote, kleine en vervaagde thumbnails voor iedere asset, en genereer thumbnails voor iedere persoon", "transcoding_acceleration_api": "Acceleration API", @@ -300,10 +291,9 @@ "transcoding_encoding_options": "Coderings Opties", "transcoding_encoding_options_description": "Stel codecs, resolutie, kwaliteit en andere opties in voor de gecodeerde video's", "transcoding_hardware_acceleration": "Hardware acceleratie", - "transcoding_hardware_acceleration_description": "Experimenteel; veel sneller, maar zal een lagere kwaliteit hebben bij dezelfde bitrate", + "transcoding_hardware_acceleration_description": "Experimenteel; snellere transcodering maar kan kwaliteit verminderen bij dezelfde bitrate", "transcoding_hardware_decoding": "Hardware decodering", "transcoding_hardware_decoding_setting_description": "Maakt end-to-end versnelling mogelijk in plaats van alleen de codering te versnellen. Werkt mogelijk niet op alle video's.", - "transcoding_hevc_codec": "HEVC codec", "transcoding_max_b_frames": "Maximum B-Frames", "transcoding_max_b_frames_description": "Hogere waarden verbeteren de compressie efficiÃĢntie, maar vertragen de codering. Is mogelijk niet compatibel met hardwareversnelling op oudere apparaten. 0 schakelt B-frames uit, terwijl -1 deze waarde automatisch instelt.", "transcoding_max_bitrate": "Maximum bitrate", @@ -341,8 +331,6 @@ "trash_number_of_days_description": "Aantal dagen dat de assets in de prullenbak worden bewaard voordat ze definitief worden verwijderd", "trash_settings": "Prullenbak instellingen", "trash_settings_description": "Beheer prullenbak instellingen", - "untracked_files": "Niet bijgehouden bestanden", - "untracked_files_description": "Deze bestanden worden niet bijgehouden door de applicatie. Dit kan het resultaat zijn van een mislukte verplaatsing, onderbroken upload of een bug", "user_cleanup_job": "Gebruiker opschoning", "user_delete_delay": "Het account en de assets van {user} worden over {delay, plural, one {# dag} other {# dagen}} permanent verwijderd.", "user_delete_delay_settings": "Verwijder vertraging", @@ -371,7 +359,7 @@ "advanced": "Geavanceerd", "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_log_level_title": "Logniveau: {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", @@ -401,8 +389,6 @@ "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_shared": " ¡ Gedeeld", - "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 +402,11 @@ "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}}", + "albums_default_sort_order": "Standaard sorteervolgorde album", + "albums_default_sort_order_description": "InitiÃĢle sorteervolgorde bij het maken van nieuwe albums.", + "albums_feature_description": "Collectie van assets die je kan delen met andere gebruikers.", "all": "Alle", "all_albums": "Alle albums", "all_people": "Alle mensen", @@ -427,10 +417,10 @@ "allow_public_user_to_upload": "Sta openbare gebruiker toe om te uploaden", "alt_text_qr_code": "QR-codeafbeelding", "anti_clockwise": "Linksom", - "api_key": "API key", + "api_key": "API-sleutel", "api_key_description": "Deze waarde wordt slechts ÊÊn keer getoond. Zorg ervoor dat je deze kopieert voordat je het venster sluit.", - "api_key_empty": "De naam van uw API key mag niet leeg zijn", - "api_keys": "API keys", + "api_key_empty": "De naam van uw API-sleutel mag niet leeg zijn", + "api_keys": "API-sleutels", "app_bar_signout_dialog_content": "Weet je zeker dat je wilt uitloggen?", "app_bar_signout_dialog_ok": "Ja", "app_bar_signout_dialog_title": "Log uit", @@ -451,6 +441,7 @@ "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", @@ -458,6 +449,7 @@ "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", @@ -469,11 +461,16 @@ "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_cannot_be_added_to_album_count": "{count, plural, one {# asset} other {# assets}} konden niet aan album toegevoegd worden", + "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} asset(s) permanent verwijderd", "assets_deleted_permanently_from_server": "{count} asset(s) permanent verwijderd van de Immich server", + "assets_downloaded_failed": "{count, plural, one {# bestand gedownload - {error} bestand mislukt} other {# bestanden gedownload - {error} bestanden mislukt}}", + "assets_downloaded_successfully": "{count, plural, one {# bestand succesvol gedownload} other {# bestanden succesvol gedownload}}", "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", @@ -488,6 +485,7 @@ "authorized_devices": "Geautoriseerde apparaten", "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", + "autoplay_slideshow": "Diavoorstelling automatisch afspelen", "back": "Terug", "back_close_deselect": "Terug, sluiten of deselecteren", "background_location_permission": "Achtergrond locatie toestemming", @@ -512,13 +510,14 @@ "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 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", + "backup_controller_page_background_is_off": "Automatische achtergrondback-up staat uit", + "backup_controller_page_background_is_on": "Automatische achtergrondback-up staat aan", "backup_controller_page_background_turn_off": "Achtergrondservice uitzetten", "backup_controller_page_background_turn_on": "Achtergrondservice aanzetten", "backup_controller_page_background_wifi": "Alleen op WiFi", @@ -530,6 +529,7 @@ "backup_controller_page_excluded": "Uitgezonderd: ", "backup_controller_page_failed": "Mislukt ({count})", "backup_controller_page_filename": "Bestandsnaam: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Back-up informatie", "backup_controller_page_none_selected": "Geen geselecteerd", "backup_controller_page_remainder": "Resterend", @@ -561,28 +561,27 @@ "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": "Bouwen", "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 ({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 ({count})", - "cache_settings_image_cache_size": "Grootte afbeeldingscache ({count} assets)", "cache_settings_statistics_album": "Bibliotheekthumbnails", - "cache_settings_statistics_assets": "{count} bestanden ({size})", "cache_settings_statistics_full": "Volledige afbeeldingen", "cache_settings_statistics_shared": "Gedeeld-albumthumbnails", + "cache_settings_statistics_thumbnail": "Minaturen", "cache_settings_statistics_title": "Cachegebruik", "cache_settings_subtitle": "Beheer het cachegedrag van de Immich app", - "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", @@ -592,11 +591,12 @@ "cannot_undo_this_action": "Je kunt deze actie niet ongedaan maken!", "cannot_update_the_description": "Kan de beschrijving niet bijwerken", "cast": "Cast", + "cast_description": "Configureer beschikbare cast bestemmingen", "change_date": "Wijzig datum", "change_description": "Wijzig beschrijving", "change_display_order": "Weergavevolgorde wijzigen", "change_expiration_time": "Verlooptijd wijzigen", - "change_location": "Locatie wijzigen", + "change_location": "Wijzig locatie", "change_name": "Naam wijzigen", "change_name_successfully": "Naam succesvol gewijzigd", "change_password": "Wijzig wachtwoord", @@ -609,7 +609,6 @@ "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.", @@ -621,7 +620,7 @@ "clear_all_recent_searches": "Wis alle recente zoekopdrachten", "clear_message": "Bericht wissen", "clear_value": "Waarde wissen", - "client_cert_dialog_msg_confirm": "Ok", + "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Voer wachtwoord in", "client_cert_import": "Importeren", "client_cert_import_success_msg": "Clientcertificaat is geïmporteerd", @@ -634,7 +633,7 @@ "collapse": "Inklappen", "collapse_all": "Alles inklappen", "color": "Kleur", - "color_theme": "Kleurthema", + "color_theme": "Kleurenthema", "comment_deleted": "Opmerking verwijderd", "comment_options": "Opties voor opmerkingen", "comments_and_likes": "Opmerkingen & likes", @@ -649,10 +648,13 @@ "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", + "confirm_tag_face": "Wil je dit gezicht taggen als {name}?", + "confirm_tag_face_unnamed": "Wil je dit gezicht taggen?", + "connected_device": "Verbonden apparaat", "connected_to": "Verbonden met", "contain": "Bevat", + "context": "Context", "continue": "Doorgaan", - "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", @@ -672,10 +674,11 @@ "copy_to_clipboard": "KopiÃĢren naar klembord", "country": "Land", "cover": "Bedekken", + "covers": "Covers", "create": "Aanmaken", "create_album": "Album aanmaken", "create_album_page_untitled": "Naamloos", - "create_library": "Maak bibliotheek", + "create_library": "Bibliotheek maken", "create_link": "Link maken", "create_link_to_share": "Gedeelde link maken", "create_link_to_share_description": "Laat iedereen met de link de geselecteerde foto(s) zien", @@ -700,6 +703,7 @@ "daily_title_text_date": "E dd MMM", "daily_title_text_date_year": "E dd MMM yyyy", "dark": "Donker", + "darkTheme": "Donker thema in-/uitschakelen", "date_after": "Datum na", "date_and_time": "Datum en tijd", "date_before": "Datum voor", @@ -716,7 +720,7 @@ "default_locale_description": "Formatteer datums en getallen op basis van de landinstellingen van je browser", "delete": "Verwijderen", "delete_album": "Album verwijderen", - "delete_api_key_prompt": "Weet je zeker dat je deze API key wilt verwijderen?", + "delete_api_key_prompt": "Weet je zeker dat je deze API-sleutel wilt verwijderen?", "delete_dialog_alert": "Deze items zullen permanent verwijderd worden van Immich en je apparaat", "delete_dialog_alert_local": "Deze items worden permanent verwijderd van je apparaat, maar blijven beschikbaar op de Immich server", "delete_dialog_alert_local_non_backed_up": "Van sommige items is geen back-up gemaakt in Immich en zullen permanent van je apparaat worden verwijderd", @@ -741,10 +745,13 @@ "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", + "discovered_devices": "Gevonden apparaten", "dismiss_all_errors": "Negeer alle fouten", "dismiss_error": "Negeer fout", "display_options": "Weergaveopties", @@ -760,7 +767,6 @@ "download_enqueue": "Download in wachtrij", "download_error": "Fout bij downloaden", "download_failed": "Download mislukt", - "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", @@ -784,7 +790,7 @@ "edit_avatar": "Avatar bewerken", "edit_date": "Datum bewerken", "edit_date_and_time": "Datum en tijd bewerken", - "edit_description": "Bewerk beschrijving", + "edit_description": "Beschrijving bewerken", "edit_description_prompt": "Selecteer een nieuwe beschrijving:", "edit_exclusion_pattern": "Uitsluitingspatroon bewerken", "edit_faces": "Gezichten bewerken", @@ -823,6 +829,7 @@ "error_delete_face": "Fout bij verwijderen gezicht uit asset", "error_loading_image": "Fout bij laden afbeelding", "error_saving_image": "Fout: {error}", + "error_tag_face_bounding_box": "Fout bij taggen van gezicht - kan coÃļrdinaten van omvattend kader niet ophalen", "error_title": "Fout - Er is iets misgegaan", "errors": { "cannot_navigate_next_asset": "Kan niet naar de volgende asset navigeren", @@ -835,16 +842,14 @@ "cant_get_number_of_comments": "Kan het aantal opmerkingen niet ophalen", "cant_search_people": "Kan mensen niet zoeken", "cant_search_places": "Kan plaatsen niet zoeken", - "cleared_jobs": "Taken gewist voor: {job}", "error_adding_assets_to_album": "Fout bij toevoegen van assets aan album", - "error_adding_users_to_album": "Fout bij toevoegen gebruikers aan album", - "error_deleting_shared_user": "Fout bij verwijderen gedeelde gebruiker", + "error_adding_users_to_album": "Fout bij toevoegen van gebruikers aan album", + "error_deleting_shared_user": "Fout bij verwijderen van gedeelde gebruiker", "error_downloading": "Fout bij downloaden {filename}", "error_hiding_buy_button": "Fout bij het verbergen van de koop knop", "error_removing_assets_from_album": "Fout bij verwijderen van assets uit album, controleer de console voor meer details", "error_selecting_all_assets": "Fout bij selecteren van alle assets", "exclusion_pattern_already_exists": "Dit uitsluitingspatroon bestaat al.", - "failed_job_command": "Opdracht {command} is mislukt voor taak: {job}", "failed_to_create_album": "Fout bij maken van album", "failed_to_create_shared_link": "Fout bij maken van gedeelde link", "failed_to_edit_shared_link": "Fout bij bewerken van gedeelde link", @@ -854,16 +859,15 @@ "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_remove_product_key": "Fout 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", + "failed_to_update_notification_status": "Kon notificatiestatus 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", "profile_picture_transparent_pixels": "Profielfoto's kunnen geen transparante pixels bevatten. Zoom in en/of verplaats de afbeelding.", "quota_higher_than_disk_size": "Je hebt een opslaglimiet ingesteld die hoger is dan de schijfgrootte", - "repair_unable_to_check_items": "Kan {count, select, one {item} other {items}} niet controleren", "unable_to_add_album_users": "Kan gebruikers niet aan album toevoegen", "unable_to_add_assets_to_shared_link": "Kan assets niet aan gedeelde link toevoegen", "unable_to_add_comment": "Kan geen opmerking toevoegen", @@ -882,10 +886,9 @@ "unable_to_change_visibility": "Kan de zichtbaarheid van {count, plural, one {# persoon} other {# mensen}} niet wijzigen", "unable_to_complete_oauth_login": "Kan inloggen met OAuth niet voltooie", "unable_to_connect": "Kan niet verbinden", - "unable_to_connect_to_server": "Kan geen verbinding maken met server", "unable_to_copy_to_clipboard": "Kan niet naar klembord kopiÃĢren, zorg ervoor dat je de pagina via https opent", "unable_to_create_admin_account": "Kan beheerdersaccount niet aanmaken", - "unable_to_create_api_key": "Kan geen nieuwe API key aanmaken", + "unable_to_create_api_key": "Kan geen nieuwe API-sleutel aanmaken", "unable_to_create_library": "Kan bibliotheek niet aanmaken", "unable_to_create_user": "Kan geen gebruiker aanmaken", "unable_to_delete_album": "Kan album niet verwijderen", @@ -901,31 +904,24 @@ "unable_to_empty_trash": "Kan prullenbak niet legen", "unable_to_enter_fullscreen": "Kan volledig scherm niet openen", "unable_to_exit_fullscreen": "Kan volledig scherm niet afsluiten", - "unable_to_get_comments_number": "Niet mogelijk om het aantal opmerkingen op te halen", + "unable_to_get_comments_number": "Kan aantal opmerkingen niet ophalen", "unable_to_get_shared_link": "Kan gedeelde link niet ophalen", "unable_to_hide_person": "Kan persoon niet verbergen", - "unable_to_link_motion_video": "Kan bewegende video niet verbinden", - "unable_to_link_oauth_account": "Kan OAuth account niet koppelen", - "unable_to_load_album": "Kan album niet laden", - "unable_to_load_asset_activity": "Kan asset activiteit niet laden", - "unable_to_load_items": "Kan items niet laden", - "unable_to_load_liked_status": "Kan de status 'geliket' niet laden", + "unable_to_link_motion_video": "Kan bewegende video niet koppelen", + "unable_to_link_oauth_account": "Kan OAuth-account niet koppelen", "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", "unable_to_refresh_user": "Kan gebruiker niet vernieuwen", "unable_to_remove_album_users": "Kan gebruiker niet van album verwijderen", - "unable_to_remove_api_key": "Kan API key niet verwijderen", + "unable_to_remove_api_key": "Kan API-sleutel niet verwijderen", "unable_to_remove_assets_from_shared_link": "Kan assets niet verwijderen uit gedeelde link", - "unable_to_remove_deleted_assets": "Kan offline bestanden niet verwijderen", "unable_to_remove_library": "Kan bibliotheek niet verwijderen", "unable_to_remove_partner": "Kan partner niet verwijderen", "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", @@ -933,7 +929,7 @@ "unable_to_restore_trash": "Kan niet herstellen uit prullenbak", "unable_to_restore_user": "Kan gebruiker niet herstellen", "unable_to_save_album": "Kan album niet opslaan", - "unable_to_save_api_key": "Kan API key niet opslaan", + "unable_to_save_api_key": "Kan API-sleutel niet opslaan", "unable_to_save_date_of_birth": "Kan geboortedatum niet opslaan", "unable_to_save_name": "Kan naam niet opslaan", "unable_to_save_profile": "Kan profiel niet opslaan", @@ -945,7 +941,7 @@ "unable_to_submit_job": "Kan taak niet uitvoeren", "unable_to_trash_asset": "Kan asset niet naar prullenbak verplaatsen", "unable_to_unlink_account": "Kan account niet ontkoppelen", - "unable_to_unlink_motion_video": "Kan bewegende video niet los maken", + "unable_to_unlink_motion_video": "Kan bewegende video niet ontkoppelen", "unable_to_update_album_cover": "Kan album cover niet bijwerken", "unable_to_update_album_info": "Kan albumgegevens niet bijwerken", "unable_to_update_library": "Kan bibliotheek niet bijwerken", @@ -955,11 +951,12 @@ "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 {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}", @@ -997,6 +994,7 @@ "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", @@ -1006,6 +1004,8 @@ "folders": "Mappen", "folders_feature_description": "Bladeren door de mapweergave van de foto's en video's op het bestandssysteem", "forward": "Vooruit", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Deze functie gebruikt externe bronnen van Google om te kunnen werken.", "general": "Algemeen", "get_help": "Krijg hulp", "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", @@ -1017,9 +1017,9 @@ "group_albums_by": "Groepeer albums op...", "group_country": "Groepeer op land", "group_no": "Niet groeperen", - "group_owner": "Groeperen op eigenaar", + "group_owner": "Groepeer op eigenaar", "group_places_by": "Groepeer plaatsen op...", - "group_year": "Groeperen op jaar", + "group_year": "Groepeer op jaar", "haptic_feedback_switch": "Aanraaktrillingen inschakelen", "haptic_feedback_title": "Aanraaktrillingen", "has_quota": "Heeft limiet", @@ -1052,34 +1052,38 @@ "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", - "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} genomen op {date}", - "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} genomen met {person1} op {date}", - "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} genomen met {person1} en {person2} op {date}", - "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} genomen met {person1}, {person2}, en {person3} op {date}", - "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video} other {Image}} genomen met {person1}, {person2}, en {additionalCount, number} anderen op {date}", - "image_alt_text_date_place": "{isVideo, select, true {Video} other {Image}} genomen in {city}, {country} op {date}", - "image_alt_text_date_place_1_person": "{isVideo, select, true {Video} other {Image}} genomen in {city}, {country} met {person1} op {date}", - "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} genomen in {city}, {country} met {person1} en {person2} op {date}", - "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} genomen in {city}, {country} met {person1}, {person2}, en {person3} op {date}", - "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} genomen in {city}, {country} met {person1}, {person2}, en {additionalCount, number} anderen op {date}", + "image_alt_text_date": "{isVideo, select, true {Video} other {Afbeelding}} genomen op {date}", + "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Afbeelding}} genomen met {person1} op {date}", + "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Afbeelding}} genomen met {person1} en {person2} op {date}", + "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Afbeelding}} genomen met {person1}, {person2}, en {person3} op {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video} other {Afbeelding}} genomen met {person1}, {person2}, en {additionalCount, number} anderen op {date}", + "image_alt_text_date_place": "{isVideo, select, true {Video} other {Afbeelding}} genomen in {city}, {country} op {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Video} other {Afbeelding}} genomen in {city}, {country} met {person1} op {date}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Afbeelding}} genomen in {city}, {country} met {person1} en {person2} op {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Afbeelding}} genomen in {city}, {country} met {person1}, {person2}, en {person3} op {date}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Afbeelding}} genomen in {city}, {country} met {person1}, {person2}, en {additionalCount, number} anderen op {date}", "image_saved_successfully": "Afbeelding opgeslagen", "image_viewer_page_state_provider_download_started": "Download gestart", "image_viewer_page_state_provider_download_success": "Download succesvol", - "image_viewer_page_state_provider_share_error": "Deel Error", - "immich_logo": "Immich logo", + "image_viewer_page_state_provider_share_error": "Error bij delen", + "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}}", @@ -1090,6 +1094,13 @@ "invalid_date_format": "Ongeldig datumformaat", "invite_people": "Mensen uitnodigen", "invite_to_album": "Uitnodigen voor album", + "ios_debug_info_fetch_ran_at": "Ophalen gelukt op {dateTime}", + "ios_debug_info_last_sync_at": "Laatst gesynchroniseerd {dateTime}", + "ios_debug_info_no_processes_queued": "Geen achtergrondprocessen in de wachtrij", + "ios_debug_info_no_sync_yet": "Er is nog geen achtergrondsynchronisatie uitgevoerd", + "ios_debug_info_processes_queued": "{count, plural, one {{count} achtergrondproces in de wachtrij} other {{count} achtergrondprocessen in de wachtrij}}", + "ios_debug_info_processing_ran_at": "Verwerking uitgevoerd op {dateTime}", + "items_count": "{count, plural, one {# item} other {# items}}", "jobs": "Taken", "keep": "Behouden", "keep_all": "Behoud alle", @@ -1097,11 +1108,15 @@ "kept_this_deleted_others": "Deze asset behouden en {count, plural, one {# andere asset} other {# andere assets}} verwijderd", "keyboard_shortcuts": "Sneltoetsen", "language": "Taal", + "language_no_results_subtitle": "Probeer je zoekterm aan te passen", + "language_no_results_title": "Geen talen gevonden", + "language_search_hint": "Zoek talen...", "language_setting_description": "Selecteer je voorkeurstaal", "last_seen": "Laatst gezien", "latest_version": "Nieuwste versie", "latitude": "Breedtegraad", "leave": "Verlaten", + "lens_model": "Lensmodel", "let_others_respond": "Laat anderen reageren", "level": "Niveau", "library": "Bibliotheek", @@ -1114,16 +1129,17 @@ "library_page_sort_title": "Albumtitel", "light": "Licht", "like_deleted": "Like verwijderd", - "link_motion_video": "verbind bewegende video", - "link_options": "Opties voor link", + "link_motion_video": "Koppel bewegende video", + "link_options": "Opties voor koppeling", "link_to_oauth": "Koppel OAuth", "linked_oauth_account": "Gekoppeld OAuth account", "list": "Lijst", "loading": "Laden", "loading_search_results_failed": "Laden van zoekresultaten mislukt", + "local_asset_cast_failed": "Kan geen asset casten die nog niet geÃŧpload is naar de server", "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", - "location_permission": "Locatie toestemming", + "location_permission": "Locatietoestemming", "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", @@ -1134,13 +1150,14 @@ "locked_folder": "Vergrendelde map", "log_out": "Uitloggen", "log_out_all_devices": "Uitloggen op alle apparaten", + "logged_in_as": "Ingelogd als {user}", "logged_out_all_devices": "Uitgelogd op alle apparaten", "logged_out_device": "Uitgelogd van apparaat", "login": "Inloggen", "login_disabled": "Aanmelding uitgeschakeld", "login_form_api_exception": "API fout. Controleer de server URL en probeer opnieuw.", "login_form_back_button_text": "Terug", - "login_form_email_hint": "jouwemail@email.nl", + "login_form_email_hint": "jouwemail@email.com", "login_form_endpoint_hint": "http://jouw-server-ip:poort", "login_form_endpoint_url": "Server-URL", "login_form_err_http": "Voer http:// of https:// in", @@ -1165,16 +1182,16 @@ "look": "Uiterlijk", "loop_videos": "Video's herhalen", "loop_videos_description": "Inschakelen om video's automatisch te herhalen in de detailweergave.", - "main_branch_warning": "U gebruikt een ontwikkelingsversie. Wij raden u ten zeerste aan een releaseversie te gebruiken!", + "main_branch_warning": "Je gebruikt een ontwikkelingsversie. We raden je ten zeerste aan een releaseversie te gebruiken!", "main_menu": "Hoofdmenu", "make": "Merk", "manage_shared_links": "Beheer gedeelde links", "manage_sharing_with_partners": "Beheer delen met partners", "manage_the_app_settings": "Beheer de appinstellingen", "manage_your_account": "Beheer je account", - "manage_your_api_keys": "Beheer je API keys", + "manage_your_api_keys": "Beheer je API-sleutels", "manage_your_devices": "Beheer je ingelogde apparaten", - "manage_your_oauth_connection": "Beheer je OAuth koppeling", + "manage_your_oauth_connection": "Beheer je OAuth-koppeling", "map": "Kaart", "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} foto's", @@ -1184,21 +1201,21 @@ "map_location_service_disabled_content": "Locatie service moet ingeschakeld zijn om assets van je huidige locatie weer te geven. Wil je het nu inschakelen?", "map_location_service_disabled_title": "Locatie service uitgeschakeld", "map_marker_for_images": "Kaartmarkering voor afbeeldingen gemaakt in {city}, {country}", - "map_marker_with_image": "Kaart marker met afbeelding", + "map_marker_with_image": "Kaartmarkering met afbeelding", "map_no_assets_in_bounds": "Geen foto's in dit gebied", - "map_no_location_permission_content": "Locatie toestemming is nodig om assets van je huidige locatie weer te geven. Wil je het nu toestaan?", - "map_no_location_permission_title": "Locatie toestemming geweigerd", + "map_no_location_permission_content": "Locatietoestemming is nodig om assets van je huidige locatie weer te geven. Wil je dit nu toestaan?", + "map_no_location_permission_title": "Locatietoestemming geweigerd", "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 {days} dagen", "map_settings_date_range_option_year": "Afgelopen jaar", "map_settings_date_range_option_years": "Afgelopen {years} jaar", - "map_settings_dialog_title": "Kaart Instellingen", + "map_settings_dialog_title": "Kaartinstellingen", "map_settings_include_show_archived": "Toon gearchiveerde", - "map_settings_include_show_partners": "Inclusief partners", + "map_settings_include_show_partners": "Toon partners", "map_settings_only_show_favorites": "Toon enkel favorieten", - "map_settings_theme_settings": "Kaart thema", + "map_settings_theme_settings": "Kaartthema", "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", @@ -1213,6 +1230,7 @@ "memories_swipe_to_close": "Swipe omhoog om te sluiten", "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", @@ -1222,7 +1240,9 @@ "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", @@ -1238,10 +1258,10 @@ "name": "Naam", "name_or_nickname": "Naam of gebruikersnaam", "networking_settings": "Netwerk", - "networking_subtitle": "Beheer de instellingen voor de server URL", + "networking_subtitle": "Beheer de instellingen voor de server-URL", "never": "Nooit", "new_album": "Nieuw album", - "new_api_key": "Nieuwe API key", + "new_api_key": "Nieuwe API-sleutel", "new_password": "Nieuw wachtwoord", "new_person": "Nieuw persoon", "new_pin_code": "Nieuwe PIN code", @@ -1258,6 +1278,7 @@ "no_archived_assets_message": "Archiveer foto's en video's om ze te verbergen in je Foto's overzicht", "no_assets_message": "KLIK HIER OM JE EERSTE FOTO TE UPLOADEN", "no_assets_to_show": "Geen foto's om te laten zien", + "no_cast_devices_found": "Geen cast-apparaten gevonden", "no_duplicates_found": "Er zijn geen duplicaten gevonden.", "no_exif_info_available": "Geen exif info beschikbaar", "no_explore_results_message": "Upload meer foto's om je verzameling te verkennen.", @@ -1265,7 +1286,7 @@ "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_notifications": "Geen meldingen", "no_people_found": "Geen mensen gevonden", "no_places": "Geen plaatsen", "no_results": "Geen resultaten", @@ -1283,15 +1304,20 @@ "notification_toggle_setting_description": "E-mailmeldingen inschakelen", "notifications": "Meldingen", "notifications_setting_description": "Beheer meldingen", + "oauth": "OAuth", "official_immich_resources": "OfficiÃĢle Immich bronnen", - "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.", + "offline": "Offline", + "ok": "Ok", "oldest_first": "Oudste eerst", "on_this_device": "Op dit apparaat", - "onboarding_privacy_description": "De volgende (optionele) functies zijn afhankelijk van externe services en kunnen op elk moment worden uitgeschakeld in de beheerdersinstellingen.", + "onboarding": "Onboarding", + "onboarding_locale_description": "Selecteer je voorkeurstaal. Je dan dit later wijzigen in je instellingen.", + "onboarding_privacy_description": "De volgende (optionele) functies zijn afhankelijk van externe services en kunnen op elk moment worden uitgeschakeld in de instellingen.", + "onboarding_server_welcome_description": "Laten we je instantie instellen met een aantal veelgebruikte instellingen.", "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_user_welcome_description": "Laten we beginnen!", "onboarding_welcome_user": "Welkom, {user}", + "online": "Online", "only_favorites": "Alleen favorieten", "open": "Openen", "open_in_map_view": "Openen in kaartweergave", @@ -1306,6 +1332,7 @@ "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", @@ -1318,6 +1345,7 @@ "partner_page_shared_to_title": "Gedeeld met", "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", @@ -1344,6 +1372,8 @@ "permanently_delete_assets_prompt": "Weet je zeker dat je deze {count, plural, one {asset} other {# assets}} permanent wilt verwijderen? Hiermee {count, plural, one {wordt} other {worden}} deze ook uit de bijbehorende album(s) verwijderd.", "permanently_deleted_asset": "Asset permanent verwijderd", "permanently_deleted_assets_count": "{count, plural, one {# asset} other {# assets}} permanent verwijderd", + "permission": "Rechten", + "permission_empty": "Je rechten mogen niet leeg zijn", "permission_onboarding_back": "Terug", "permission_onboarding_continue_anyway": "Toch doorgaan", "permission_onboarding_get_started": "Aan de slag", @@ -1380,19 +1410,25 @@ "preview": "Voorbeeld", "previous": "Vorige", "previous_memory": "Vorige herinnering", - "previous_or_next_photo": "Vorige of volgende foto", + "previous_or_next_day": "Dag vooruit/achteruit", + "previous_or_next_month": "Maand vooruit/achteruit", + "previous_or_next_photo": "Foto vooruit/achteruit", + "previous_or_next_year": "Jaar vooruit/achteruit", "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}", "purchase_activated_title": "Je licentiesleutel is succesvol geactiveerd", @@ -1405,7 +1441,7 @@ "purchase_button_select": "Selecteren", "purchase_failed_activation": "Activeren mislukt! Controleer je e-mail voor de juiste licentiesleutel!", "purchase_individual_description_1": "Voor een gebruiker", - "purchase_individual_description_2": "Supporter badge", + "purchase_individual_description_2": "Supporterstatus", "purchase_individual_title": "Gebruiker", "purchase_input_suggestion": "Heb je een licentiesleutel? Voer deze hieronder in", "purchase_license_subtitle": "Koop Immich om de verdere ontwikkeling van de service te ondersteunen", @@ -1414,24 +1450,27 @@ "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?", "purchase_remove_server_product_key": "Verwijder server licentiesleutel", "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_description_2": "Supporterstatus", + "purchase_server_title": "Server", "purchase_settings_server_activated": "De licentiesleutel van de server wordt beheerd door de beheerder", - "rating": "Ster waardering", + "rating": "Sterwaardering", "rating_clear": "Waardering verwijderen", "rating_count": "{count, plural, one {# ster} other {# sterren}}", "rating_description": "De EXIF-waardering weergeven in het infopaneel", - "reaction_options": "Reactie opties", + "reaction_options": "Reactie-opties", "read_changelog": "Lees wijzigingen", "reassign": "Opnieuw toewijzen", "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", @@ -1462,9 +1501,10 @@ "remove_from_shared_link": "Verwijderen uit gedeelde link", "remove_memory": "Herinnering verwijderen", "remove_photo_from_memory": "Foto uit deze herinnering verwijderen", + "remove_tag": "Verwijder tag", "remove_url": "Verwijder URL", - "remove_user": "Gebruiker verwijderen", - "removed_api_key": "API sleutel verwijderd: {name}", + "remove_user": "Verwijder gebruiker", + "removed_api_key": "API-sleutel verwijderd: {name}", "removed_from_archive": "Verwijderd uit archief", "removed_from_favorites": "Verwijderd uit favorieten", "removed_from_favorites_count": "{count, plural, other {# verwijderd}} uit favorieten", @@ -1475,15 +1515,17 @@ "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", "reset": "Resetten", "reset_password": "Wachtwoord resetten", "reset_people_visibility": "Zichtbaarheid mensen resetten", + "reset_pin_code": "Reset PIN code", "reset_to_default": "Resetten naar standaard", "resolve_duplicates": "Duplicaten oplossen", - "resolved_all_duplicates": "Alle duplicaten verwerkt", + "resolved_all_duplicates": "Alle duplicaten opgelost", "restore": "Herstellen", "restore_all": "Herstel alle", "restore_user": "Gebruiker herstellen", @@ -1496,7 +1538,7 @@ "role_viewer": "Bekijker", "save": "Opslaan", "save_to_gallery": "Opslaan in galerij", - "saved_api_key": "API sleutel opgeslagen", + "saved_api_key": "API-sleutel opgeslagen", "saved_profile": "Profiel opgeslagen", "saved_settings": "Instellingen opgeslagen", "say_something": "Zeg iets", @@ -1540,7 +1582,9 @@ "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", @@ -1551,7 +1595,7 @@ "search_result_page_new_search_hint": "Nieuwe zoekopdracht", "search_settings": "Zoek instellingen", "search_state": "Zoek staat...", - "search_suggestion_list_smart_search_hint_1": "Slim zoeken is standaard ingeschakeld, om naar metadata te zoeken gebruik", + "search_suggestion_list_smart_search_hint_1": "Slim zoeken is standaard ingeschakeld, om naar metadata te zoeken gebruik ", "search_suggestion_list_smart_search_hint_2": "m:je-zoekterm", "search_tags": "Tags zoeken...", "search_timezone": "Zoek tijdzone...", @@ -1564,6 +1608,7 @@ "select_album_cover": "Selecteer album cover", "select_all": "Alles selecteren", "select_all_duplicates": "Selecteer alle duplicaten", + "select_all_in": "Selecteer alles in {group}", "select_avatar_color": "Selecteer avatarkleur", "select_face": "Selecteer gezicht", "select_featured_photo": "Selecteer uitgelichte foto", @@ -1579,12 +1624,14 @@ "selected_count": "{count, plural, other {# geselecteerd}}", "send_message": "Bericht versturen", "send_welcome_email": "Stuur welkomstmail", - "server_endpoint": "Server url", + "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_privacy": "Serverprivacy", "server_stats": "Serverstatistieken", - "server_version": "Server versie", + "server_version": "Serverversie", "set": "Instellen", "set_as_album_cover": "Stel in als album cover", "set_as_featured_photo": "Instellen als uitgelichte foto", @@ -1592,6 +1639,7 @@ "set_date_of_birth": "Geboortedatum instellen", "set_profile_picture": "Profielfoto instellen", "set_slideshow_to_fullscreen": "Diavoorstelling op volledig scherm", + "set_stack_primary_asset": "Instellen als primaire asset", "setting_image_viewer_help": "De gedetailleerde weergave laadt eerst de kleine thumbnail, vervolgens het middelgrote voorbeeld (indien ingeschakeld) en ten slotte het origineel (indien ingeschakeld).", "setting_image_viewer_original_subtitle": "Schakel in om de originele afbeelding met volledige resolutie (groot!) te laden. Schakel uit om datagebruik te verminderen (zowel netwerk als apparaatcache).", "setting_image_viewer_original_title": "Originele afbeelding laden", @@ -1600,7 +1648,6 @@ "setting_image_viewer_title": "Afbeeldingen", "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: {duration}", "setting_notifications_notify_hours": "{count} uur", "setting_notifications_notify_immediately": "meteen", @@ -1623,7 +1670,7 @@ "share_add_photos": "Foto's toevoegen", "share_assets_selected": "{count} geselecteerd", "share_dialog_preparing": "Voorbereiden...", - "share_link": "Deel link", + "share_link": "Link delen", "shared": "Gedeeld", "shared_album_activities_input_disable": "Reactie is uitgeschakeld", "shared_album_activity_remove_content": "Wil je deze activiteit verwijderen?", @@ -1652,7 +1699,7 @@ "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_error_server_url_fetch": "Kan de server-url niet ophalen", "shared_link_expires_day": "Verloopt over {count} dag", "shared_link_expires_days": "Verloopt over {count} dagen", "shared_link_expires_hour": "Verloopt over {count} uur", @@ -1663,6 +1710,7 @@ "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", @@ -1684,7 +1732,7 @@ "show_all_people": "Toon alle mensen", "show_and_hide_people": "Toon & verberg mensen", "show_file_location": "Toon bestandslocatie", - "show_gallery": "Toon gallerij", + "show_gallery": "Toon galerij", "show_hidden_people": "Verbogen mensen weergeven", "show_in_timeline": "Toon in tijdlijn", "show_in_timeline_setting_description": "Toon foto's en video's van deze gebruiker in je tijdlijn", @@ -1697,7 +1745,8 @@ "show_search_options": "Zoekopties weergeven", "show_shared_links": "Toon gedeelde links", "show_slideshow_transition": "Diavoorstellingsovergang tonen", - "show_supporter_badge_description": "Toon een supporterbadge", + "show_supporter_badge": "Supportersbadge", + "show_supporter_badge_description": "Toon een supportersbadge", "shuffle": "Willekeurig", "sidebar": "Zijbalk", "sidebar_display_description": "Toon een link naar deze pagina in de zijbalk", @@ -1723,8 +1772,12 @@ "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_casting": "Stop met casten", "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.", @@ -1740,9 +1793,11 @@ "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", @@ -1750,6 +1805,8 @@ "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", @@ -1778,7 +1835,6 @@ "to_parent": "Ga naar hoofdmap", "to_trash": "Prullenbak", "toggle_settings": "Zichtbaarheid instellingen wisselen", - "toggle_theme": "Donker thema toepassen", "total": "Totaal", "total_usage": "Totaal gebruik", "trash": "Prullenbak", @@ -1795,17 +1851,19 @@ "trash_page_select_assets_btn": "Selecteer assets", "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}}", + "undo": "Ongedaan maken", "unfavorite": "Verwijderen uit favorieten", "unhide_person": "Persoon zichtbaar maken", "unknown": "Onbekend", "unknown_country": "Onbekend Land", "unknown_year": "Onbekend jaar", "unlimited": "Onbeperkt", - "unlink_motion_video": "Maak bewegende video los", + "unlink_motion_video": "Ontkoppel bewegende video", "unlink_oauth": "Ontkoppel OAuth", "unlinked_oauth_account": "OAuth account ontkoppeld", "unmute_memories": "Dempen van herrinneringen opheffen", @@ -1815,10 +1873,9 @@ "unsaved_change": "Niet-opgeslagen wijziging", "unselect_all": "Alles deselecteren", "unselect_all_duplicates": "Deselecteer alle duplicaten", + "unselect_all_in": "Deselecteer alles in {group}", "unstack": "Ontstapelen", "unstacked_assets_count": "{count, plural, one {# asset} other {# assets}} ontstapeld", - "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", @@ -1835,6 +1892,7 @@ "upload_success": "Uploaden gelukt, vernieuw de pagina om de nieuwe assets te zien.", "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", @@ -1843,7 +1901,9 @@ "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": "PIN Code", "user_pin_code_settings_description": "Beheer je PIN code", + "user_privacy": "Gebruikersprivacy", "user_purchase_settings": "Kopen", "user_purchase_settings_description": "Beheer je aankoop", "user_role_set": "{user} instellen als {role}", @@ -1859,15 +1919,11 @@ "version": "Versie", "version_announcement_closing": "Je vriend, Alex", "version_announcement_message": "Hallo! Er is een nieuwe versie van Immich beschikbaar. Neem even de tijd om de release notes te lezen en zorg ervoor dat je setup up-to-date is om misconfiguraties te voorkomen, vooral als je WatchTower of een andere update-mechanisme gebruikt.", - "version_announcement_overlay_release_notes": "releaseopmerkingen", - "version_announcement_overlay_text_1": "Hoi, er is een nieuwe versie beschikbaar van", - "version_announcement_overlay_text_2": "neem je tijd en bezoek de ", - "version_announcement_overlay_text_3": " en controleer of je docker-compose en .env up-to-date zijn, om misconfiguraties te voorkomen, in het bijzonder als je gebruik maakt van WatchTower of een ander mechanisme dat je serverapplicatie automatisch bijwerkt.", - "version_announcement_overlay_title": "Nieuwe serverversie beschikbaar 🎉", "version_history": "Versiegeschiedenis", "version_history_item": "{version} geïnstalleerd op {date}", - "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.", + "video": "Video", + "video_hover_setting": "Speel videothumbnail af bij hoveren", + "video_hover_setting_description": "Speel videothumbnail 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", "videos_count": "{count, plural, one {# video} other {# video's}}", "view": "Bekijken", @@ -1882,12 +1938,14 @@ "view_previous_asset": "Bekijk vorige asset", "view_qr_code": "QR-code bekijken", "view_stack": "Bekijk stapel", + "view_user": "Bekijk gebruiker", "viewer_remove_from_stack": "Verwijder van Stapel", "viewer_stack_use_as_main_asset": "Gebruik als Hoofd Asset", "viewer_unstack": "Ontstapel", "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", diff --git a/i18n/nn.json b/i18n/nn.json index a26311cf81..7fb5fdef02 100644 --- a/i18n/nn.json +++ b/i18n/nn.json @@ -44,8 +44,6 @@ "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}?", @@ -60,14 +58,12 @@ "disable_login": "Deaktiver innlogging", "duplicate_detection_job_description": "Kjør maskinlÃĻring pÃĨ filer for ÃĨ oppdage liknande bilete. Krev bruk av Smart Search", "exclusion_pattern_description": "Utelatingsmønster let deg utelate filer og mapper nÃĨr du skannar biblioteket ditt. Det er nyttig om du har mapper som inneheld filer du ikkje ynskjer ÃĨ importere, til dømes RAW-filer.", - "external_library_created_at": "Eksterne bibliotek (oppretta {date})", "external_library_management": "Handsaming av eksterne bibliotek", "face_detection": "Ansiktssøk", "face_detection_description": "Finn ansikt i bilete ved hjelp av maskinlÃĻring. For videoar vert berre miniatyrbilete bruka. \"Alle\" søkjer (opp att) gjennom alle bilete. \"Tilbakestill\" fjernar all gjeldande ansiktsdata. \"Manglande\" legg filer som ikkje vert behandla til i køa for ansiktssøk. Oppdaga ansikt vert lagt i køa for ansiktsattkjenning, og kopla til eksisterande eller nye personar.", "facial_recognition_job_description": "Koplar attkjende ansikt til personar. Det skjer fyrst nÃĨr anskiktssøkjet er ferdig. \"Tilbakestill\" fjernar alle koplingar til personar, og tilbakestiller ansiktsgrupper. \"Manglande\" legg ansikt som ikkje er oppkopla til i køa.", "failed_job_command": "Kommandoen {command} feila for jobb: {job}", "force_delete_user_warning": "ÅTVARING: Handlinga fjernar brukaren og all data. Du kan ikkje angre, og filane kan ikkje gjenopprettast.", - "forcing_refresh_library_files": "Tvingar lasting av alle filer i bibliotek", "image_format": "Format", "image_format_description": "WebP gjev mindre filstorleik enn JPEG, men er treigare ÃĨ lage.", "image_prefer_embedded_preview": "Bruk helst innebygd førehandsvisning", @@ -154,9 +150,6 @@ "refreshing_all_libraries": "Laster alle bibliotek opp att", "registration": "Administrator registrering", "registration_description": "Sidan du er den første brukaren pÃĨ systemet, vil du bli utnevnt til administrator og ha ansvar for administrative oppgÃĨver. Du vil Ã˛g opprette eventuelle nye brukarar.", - "repair_all": "Reparer alle", - "repair_matched_items": "Samsvarte med {count, plural, one {# element} other {# element}}", - "repaired_items": "Reparerte {count, plural, one {# element} other {# element}}", "require_password_change_on_login": "Krev at brukaren endrar passord ved første pÃĨlogging", "reset_settings_to_default": "Tilbakestill innstillingar til standard", "reset_settings_to_recent_saved": "Tilbakestill innstillingane til de nyleg lagra innstillingane", @@ -241,7 +234,6 @@ "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", diff --git a/i18n/pl.json b/i18n/pl.json index 5da38eac60..e84c6f53e6 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -22,11 +22,11 @@ "add_partner": "Dodaj partnera", "add_path": "Dodaj ścieÅŧkę", "add_photos": "Dodaj zdjęcia", + "add_tag": "Dodaj tag", "add_to": "Dodaj doâ€Ļ", "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_album_bottom_sheet_already_exists": "JuÅŧ jest w {album}", "add_to_shared_album": "Dodaj do udostępnionego albumu", "add_url": "Dodaj URL", "added_to_archive": "Dodano do archiwum", @@ -34,6 +34,7 @@ "added_to_favorites_count": "Dodano {count, number} do ulubionych", "admin": { "add_exclusion_pattern_description": "Dodaj wzorce wykluczające. Wspierane są specjalne sekwencje (glob) *, ** oraz ?. Aby ignorować całą zawartość wszystkich folderÃŗw nazwanych \"Raw\", uÅŧyj \"**/Raw/**\". Aby ignorować wszystkie pliki kończące się na \".tif\", uÅŧyj \"**/*.tif\". Aby ignorować ścieÅŧkę absolutną, uÅŧyj \"/ścieÅŧka/do/ignorowania/**\".", + "admin_user": "Administrator", "asset_offline_description": "Ten zewnętrzny zasÃŗb biblioteki nie jest juÅŧ dostępny na dysku i został przeniesiony do kosza. Jeśli plik został przeniesiony w obrębie biblioteki, sprawdÅē swoją oś czasu pod kątem nowego odpowiadającego zasobu. Aby przywrÃŗcić ten zasÃŗb, upewnij się, Åŧe ścieÅŧka pliku poniÅŧej jest dostępna dla Immich i przeskanuj bibliotekę.", "authentication_settings": "Ustawienia Uwierzytelnienia", "authentication_settings_description": "Zarządzaj hasłem, OAuth i innymi ustawienia uwierzytelnienia", @@ -44,9 +45,7 @@ "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", + "backup_settings_description": "Zarządzanie ustawieniami zrzutu bazy danych.", "cleared_jobs": "Usunięto zadania dla: {job}", "config_set_by_file": "Konfiguracja pochodzi z pliku konfiguracyjnego", "confirm_delete_library": "Czy na pewno chcesz usunąć bibliotekę {library}?", @@ -62,14 +61,12 @@ "disable_login": "Wyłącz logowanie", "duplicate_detection_job_description": "Włącz uczenie maszynowe na zasobie aby wykrywać podobne obrazy. Ta funkcja opiera się na inteligentnym wyszukiwaniu", "exclusion_pattern_description": "Wzory wykluczające pozwalają na ignorowanie plikÃŗw i folderÃŗw podczas skanowania Twojej biblioteki. Są one przydatne na przykład gdy nie chcesz importować zdjęć w formacie RAW.", - "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. \"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ć.", - "forcing_refresh_library_files": "Wymuś skanowanie wszystkich pliki w bibliotece", "image_format": "Format", "image_format_description": "UÅŧycie formatu WebP skutkuje utworzeniem plikÃŗw o rozmiarze mniejszym niÅŧ w przypadku JPEG ale jego kodowanie trwa dłuÅŧej.", "image_fullsize_description": "Pełnowymiarowy obraz z usuniętymi metadanymi, uÅŧywany przy powiększeniu", @@ -174,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Uwaga: aby zastosować etykietę magazynu do wcześniej przesłanych zasobÃŗw, uruchom", "note_cannot_be_changed_later": "UWAÅģAJ: Nie moÅŧna tego pÃŗÅēniej zmienić!", "notification_email_from_address": "Z adresu", - "notification_email_from_address_description": "Adres e-mail nadawcy, na przykład: „Immich Photo Server ”", + "notification_email_from_address_description": "Adres e-mail nadawcy, na przykład: „Immich Photo Server ”. Upewnij się, Åŧe uÅŧywasz adresu z ktÃŗrego masz prawo wysyłać wiadomości.", "notification_email_host_description": "Host serwera e-mail (np. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignoruj niepoprawny certyfikat", "notification_email_ignore_certificate_errors_description": "Ignoruj błąd walidacji certyfikatu TLS (nie zalecane)", @@ -198,7 +195,7 @@ "oauth_enable_description": "Loguj się za pomocą OAuth", "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_mobile_redirect_uri_override_description": "Włącz, gdy dostawca OAuth nie pozwala na mobilne identyfikatory URI typu ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "Zarządzaj ustawieniami logowania OAuth", "oauth_settings_more_details": "Więcej informacji o tej funkcji znajdziesz w dokumentacji.", @@ -207,11 +204,9 @@ "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_storage_quota_default_description": "Limit w GiB do wykorzystania, gdy nie podano Åŧadnej wartości.", "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", "password_settings": "Logowanie Hasłem", "password_settings_description": "Zarządzaj ustawieniami logowania hasłem", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "Wszystkie biblioteki zostaną odświeÅŧone", "registration": "Rejestracja Administratora", "registration_description": "Jesteś pierwszym uÅŧytkownikiem aplikacji, więc twoje konto jest administratorem. MoÅŧesz zarządzać platformą, w tym dodawać nowych uÅŧytkownikÃŗw.", - "repair_all": "Napraw Wszystko", - "repair_matched_items": "Powiązano {count, plural, one {# element} few {# elementy} other {# elementÃŗw}}", - "repaired_items": "Naprawiono {count, plural, one {# element} few {# elementy} other {# elementÃŗw}}", "require_password_change_on_login": "Wymagaj zmiany hasła po pierwszym zalogowaniu", "reset_settings_to_default": "PrzywrÃŗÄ‡ ustawienia fabryczne", "reset_settings_to_recent_saved": "PrzywrÃŗÄ‡ ustawienia do ostatnio zapisanych", @@ -252,7 +244,7 @@ "storage_template_migration_info": "Szablon Magazynu przekonwertuje wszystkie rozszerzenia na pisane małą literą. Zmiany w szablonie zostaną zastosowane tylko do nowych zasobÃŗw. Aby wstecznie zastosować szablon do wcześniej przesłanych zasobÃŗw, uruchom zadanie {job}.", "storage_template_migration_job": "Zadanie migracji szablonu przechowywania", "storage_template_more_details": "Aby uzyskać więcej szczegÃŗÅ‚Ãŗw na temat tej funkcji, odwiedÅē Szablon Przechowywania oraz jego implikacje", - "storage_template_onboarding_description": "Po włączeniu tej funkcji pliki będą organizowane automatycznie na podstawie szablonu zdefiniowanego przez uÅŧytkownika. Obecnie domyślnie wyłączona przez problemy ze stabilnością. Więcej informacji znajdziesz w dokumentacji.", + "storage_template_onboarding_description_v2": "Po włączeniu ta funkcja automatycznie organizuje pliki w oparciu o zdefiniowany przez uÅŧytkownika szablon. Więcej informacji moÅŧna znaleÅēć w dokumentacji.", "storage_template_path_length": "PrzybliÅŧony limit długości ścieÅŧki: {length, number}/{limit, number}", "storage_template_settings": "Szablon Magazynu", "storage_template_settings_description": "Zarządzaj strukturą folderÃŗw i nazwą pliku przesyłanego zasobu", @@ -264,16 +256,14 @@ "template_email_invite_album": "Szablon zaproszenia do albumu", "template_email_preview": "Podgląd", "template_email_settings": "Szablony e-mail", - "template_email_settings_description": "Zarządzaj niestandardowymi e-mail powiadomieniami", "template_email_update_album": "Szablon aktualizacji albumu", "template_email_welcome": "Szablon powitalnego e-mail", "template_settings": "Szablony Powiadomień", - "template_settings_description": "Zarządzaj niestandardowymi szablonami powiadomień e-mail.", + "template_settings_description": "Zarządzaj niestandardowymi szablonami powiadomień e-mail", "theme_custom_css_settings": "Własny CSS", "theme_custom_css_settings_description": "Własny CSS pozwala na zmianę wyglądu aplikacji Immich.", "theme_settings": "Ustawienia Motywu", "theme_settings_description": "Zarządzaj wyglądem aplikacji Immich w przeglądarce", - "these_files_matched_by_checksum": "Pliki te są powiązane na podstawie ich sum kontrolnych", "thumbnail_generation_job": "StwÃŗrz Miniaturki", "thumbnail_generation_job_description": "Generuj duÅŧe, małe i rozmyte miniatury dla kaÅŧdego zasobu, a takÅŧe miniatury dla kaÅŧdej osoby", "transcoding_acceleration_api": "API akceleracji", @@ -301,10 +291,9 @@ "transcoding_encoding_options": "Opcje kodowania", "transcoding_encoding_options_description": "Ustawia kodeki, rozdzielczość, jakość oraz inne opcje dla kodowanych filmÃŗw", "transcoding_hardware_acceleration": "Przyspieszenie Sprzętowe", - "transcoding_hardware_acceleration_description": "Eksperymentalny; znacznie szybszy, ale będzie miał niÅŧszą jakość przy tej samej szybkości transmisji", + "transcoding_hardware_acceleration_description": "Eksperymentalny: szybsze transkodowanie, ale będzie miał niÅŧszą jakość przy tej samej szybkości transmisji", "transcoding_hardware_decoding": "Dekodowanie sprzętowe", "transcoding_hardware_decoding_setting_description": "UmoÅŧliwia całkowite przyspieszenie sprzętowe zamiast tylko przyspieszania kodowania. MoÅŧe nie działać we wszystkich filmach.", - "transcoding_hevc_codec": "Kodek HEVC", "transcoding_max_b_frames": "Maksymalne klatki B (B-Frames)", "transcoding_max_b_frames_description": "WyÅŧsze wartości poprawiają wydajność kompresji, ale spowalniają kodowanie. MoÅŧe nie być kompatybilny z akceleracją sprzętową na starszych urządzeniach. 0 wyłącza klatki B (B-frames), natomiast -1 ustawia tę wartość automatycznie.", "transcoding_max_bitrate": "Maksymalna szybkość transmisji", @@ -342,8 +331,6 @@ "trash_number_of_days_description": "Liczba dni przechowywania zasobÃŗw w koszu przed ich trwałym usunięciem", "trash_settings": "Ustawienia Kosza", "trash_settings_description": "Zarządzaj ustawieniami kosza", - "untracked_files": "Nieśledzone pliki", - "untracked_files_description": "Pliki te nie są śledzone przez aplikację. Mogą być wynikiem nieudanych przeniesień, przerwanego przesyłania lub pozostawienia z powodu błędu", "user_cleanup_job": "Porządkowanie uÅŧytkownika", "user_delete_delay": "Konto {user} oraz jego zasoby zostaną zaplanowane do trwałego usunięcia za {delay, plural, one {# dzień} few {# dni} many {# dni} other {# dni}}.", "user_delete_delay_settings": "Usuń opÃŗÅēnienie", @@ -402,10 +389,6 @@ "album_remove_user": "Usunąć uÅŧytkownika?", "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": "{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}", @@ -421,6 +404,9 @@ "album_with_link_access": "PozwÃŗl kaÅŧdemu z dostępem do linku zobaczyć zdjęcia i osoby w tym albumie.", "albums": "Albumy", "albums_count": "{count, plural, one {{count, number} Album} few {{count, number} Albumy} other {{count, number} AlbumÃŗw}}", + "albums_default_sort_order": "Domyślna kolejność sortowania w albumach", + "albums_default_sort_order_description": "Początkowa kolejność sortowania zasobÃŗw przy tworzeniu nowych albumÃŗw.", + "albums_feature_description": "Kolekcje zasobÃŗw, ktÃŗre moÅŧna udostępniać innym uÅŧytkownikom.", "all": "Wszystkie", "all_albums": "Wszystkie albumy", "all_people": "Wszystkie osoby", @@ -479,23 +465,27 @@ "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_cannot_be_added_to_album_count": "{count, plural, one {sztuka Elementu} other {szt. ElementÃŗw}} nie moÅŧe być dodana do albumu", "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_deleted_permanently": "{count} zostało trwale usuniętych", + "assets_deleted_permanently_from_server": "{count} zostało trwale usuniętych z serwera Immich", + "assets_downloaded_failed": "{count, plural, one {Pomyślnie pobrano # plik - {error} plik nie powiÃŗdł się} few {Pomyślnie pobrano # pliki - {error} pliki nie powiodły się} other {Pomyślnie pobrano # plikÃŗw - {error} pliki nie powiodły się}}", + "assets_downloaded_successfully": "{count, plural, one {Pomyślnie pobrano # plik} few {Pomyślnie pobrano # pliki} other {Pomyślnie pobrano # plikÃŗw}}", "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_removed_permanently_from_device": "{count} 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} 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_restored_successfully": "{count} pomyślnie przywrÃŗcono", + "assets_trashed": "{count} szt. 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_trashed_from_server": "{count} szt. 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", "automatic_endpoint_switching_title": "Automatyczne przełączanie adresÃŗw URL", + "autoplay_slideshow": "Automatyczne odtwarzanie pokazu slajdÃŗw", "back": "Wstecz", "back_close_deselect": "WrÃŗÄ‡, zamknij lub odznacz", "background_location_permission": "Uprawnienia do lokalizacji w tle", @@ -520,6 +510,7 @@ "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", @@ -538,6 +529,7 @@ "backup_controller_page_excluded": "Wykluczone: ", "backup_controller_page_failed": "Nieudane ({count})", "backup_controller_page_filename": "Nazwa pliku: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informacje o kopii zapasowej", "backup_controller_page_none_selected": "Brak wybranych", "backup_controller_page_remainder": "Reszta", @@ -575,21 +567,17 @@ "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 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 ({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": "{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 ({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", @@ -602,7 +590,8 @@ "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", + "cast": "OdtwÃŗrz na telewizorze", + "cast_description": "Skonfiguruj dostępne cele do przesyłania", "change_date": "Zmień datę", "change_description": "Zmiana opisu", "change_display_order": "Zmień kolejność wyświetlania", @@ -620,7 +609,6 @@ "change_pin_code": "Zmień kod PIN", "change_your_password": "Zmień swoje hasło", "changed_visibility_successfully": "Pomyślnie zmieniono widoczność", - "check_all": "Zaznacz wszystko", "check_corrupt_asset_backup": "SprawdÅē, czy kopie zapasowe zasobÃŗw nie są uszkodzone", "check_corrupt_asset_backup_button": "Wykonaj sprawdzenie", "check_corrupt_asset_backup_description": "Uruchom sprawdzenie tylko przez Wi-Fi i po utworzeniu kopii zapasowej wszystkich zasobÃŗw. Procedura moÅŧe potrwać kilka minut.", @@ -632,6 +620,7 @@ "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", @@ -659,11 +648,13 @@ "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", + "confirm_tag_face": "Chcesz dodać do tej twarzy etykietę {name}?", + "confirm_tag_face_unnamed": "Chcesz dodać do tej twarzy etykietę?", + "connected_device": "Podłączone urządzenie", "connected_to": "Połączony z", "contain": "Zawiera", "context": "Kontekst", "continue": "Kontynuuj", - "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", @@ -712,6 +703,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Ciemny", + "darkTheme": "Włącz ciemny motyw", "date_after": "Data po", "date_and_time": "Data i godzina", "date_before": "Data przed", @@ -757,7 +749,9 @@ "direction": "Kierunek", "disabled": "Wyłączone", "disallow_edits": "Nie pozwalaj edytować", + "discord": "Discord", "discover": "Odkryj", + "discovered_devices": "Wykryte urządzenia", "dismiss_all_errors": "Odrzuć wszystkie błędy", "dismiss_error": "Odrzuć błąd", "display_options": "Opcje wyświetlania", @@ -773,7 +767,6 @@ "download_enqueue": "Pobieranie w kolejce", "download_error": "Błąd pobierania", "download_failed": "Pobieranie nieudane", - "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", @@ -836,6 +829,7 @@ "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}", + "error_tag_face_bounding_box": "Błąd przy dodawaniu etykiety dla tej twarzy - nie moÅŧe uzyskać wspÃŗÅ‚rzędnych granicznych", "error_title": "Błąd - Coś poszło nie tak", "errors": { "cannot_navigate_next_asset": "Nie moÅŧna przejść do następnego zasobu", @@ -848,7 +842,6 @@ "cant_get_number_of_comments": "Nie moÅŧna uzyskać liczby komentarzy", "cant_search_people": "Nie moÅŧna znaleÅēć osÃŗb", "cant_search_places": "Nie moÅŧna znaleÅēć miejsc", - "cleared_jobs": "Wyczyszczono zadania dla:{job}", "error_adding_assets_to_album": "Błąd dodania zasobÃŗw do albumu", "error_adding_users_to_album": "Błąd dodania uÅŧytkownikÃŗw do albumu", "error_deleting_shared_user": "Błąd podczas usuwania udostępnionego uÅŧytkownika", @@ -857,7 +850,6 @@ "error_removing_assets_from_album": "Błąd usuwania zasobÃŗw z albumu, sprawdÅē konsolę w celu uzyskania więcej szczegÃŗÅ‚Ãŗw", "error_selecting_all_assets": "Błąd przy wybieraniu wszystkich zasobÃŗw", "exclusion_pattern_already_exists": "Ten wzÃŗr wykluczający juÅŧ istnieje.", - "failed_job_command": "Polecenie {command} nie powiodło się dla zadania: {job}", "failed_to_create_album": "Nie udało się utworzyć albumu", "failed_to_create_shared_link": "Nie udało się utworzyć udostępnionego linku", "failed_to_edit_shared_link": "Nie udało się edytować udostępnionego linku", @@ -865,18 +857,17 @@ "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_load_notifications": "Nie udało się załadować powiadomień", + "failed_to_load_people": "Nie udało się pobrać 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", + "failed_to_update_notification_status": "Nie udało się zaktualizować stanu 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}}", "profile_picture_transparent_pixels": "Zdjęcia profilowe nie mogą mieć przezroczystych pikseli. Powiększ i/lub przesuń obraz.", - "quota_higher_than_disk_size": "Limit jest większy niÅŧ dostępna przestrzeń", - "repair_unable_to_check_items": "Nie moÅŧna sprawdzić {count, select, one {rzecz} other {rzeczy}}", + "quota_higher_than_disk_size": "Ustawiony przez ciebie limit większy niÅŧ rozmiar dysku", "unable_to_add_album_users": "Nie moÅŧna dodać uÅŧytkownikÃŗw do albumu", "unable_to_add_assets_to_shared_link": "Nie moÅŧna dodać zasobÃŗw do udostępnionego linku", "unable_to_add_comment": "Nie moÅŧna dodać komentarza", @@ -884,7 +875,7 @@ "unable_to_add_import_path": "Nie moÅŧna dodać ścieÅŧki importu", "unable_to_add_partners": "Nie moÅŧna dodać partnerÃŗw", "unable_to_add_remove_archive": "Nie moÅŧna {archived, select, true {usunąć zasobu z} other {dodać zasobu do}} archiwum", - "unable_to_add_remove_favorites": "Nie moÅŧna {favorite, select, true {dodać zasobu do} other {usunąć zasobu z }} ubulionych", + "unable_to_add_remove_favorites": "Nie moÅŧna {favorite, select, true {dodać zasobu do} other {usunąć zasobu z }} ulubionych", "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", @@ -895,8 +886,7 @@ "unable_to_change_visibility": "Nie moÅŧna zmienić widoczności dla {count, plural, one {# osoby} other {# osÃŗb}}", "unable_to_complete_oauth_login": "Nie moÅŧna ukończyć logowania przy uÅŧyciu OAuth", "unable_to_connect": "Nie moÅŧna się połączyć", - "unable_to_connect_to_server": "Nie moÅŧna się połączyć z serwerem", - "unable_to_copy_to_clipboard": "Kopiowanie do schowka nie powiodło się. Upewnij się, Åŧe jesteś na stronie https://", + "unable_to_copy_to_clipboard": "Nie moÅŧna skopiować do schowka, upewnij się, Åŧe łączysz się ze stroną przez https", "unable_to_create_admin_account": "Nie moÅŧna utworzyć konta administratora", "unable_to_create_api_key": "Nie moÅŧna stworzyć Klucza API", "unable_to_create_library": "Nie moÅŧna stworzyć biblioteki", @@ -919,14 +909,9 @@ "unable_to_hide_person": "Ukrycie osoby nie powiodło się", "unable_to_link_motion_video": "Nie moÅŧna podłączyć ruchome wideo", "unable_to_link_oauth_account": "Nie moÅŧna powiązać konta OAuth", - "unable_to_load_album": "Ładowanie albumu nie powiodło się", - "unable_to_load_asset_activity": "Ładowanie aktywności nie powiodło się", - "unable_to_load_items": "Ładowanie elementÃŗw nie powiodło się", - "unable_to_load_liked_status": "Ładowanie stanu polubienia nie powiodło się", "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", @@ -934,11 +919,9 @@ "unable_to_remove_album_users": "Usunięcie uÅŧytkownikÃŗw z albumu nie powiodło się", "unable_to_remove_api_key": "Usunięcie Klucza API nie powiodło się", "unable_to_remove_assets_from_shared_link": "Nie moÅŧna usunąć zasobÃŗw z udostępnionego linku", - "unable_to_remove_deleted_assets": "Usunięcie niedostępnych plikÃŗw nie powiodło się", "unable_to_remove_library": "Usunięcie biblioteki nie powiodło się", "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": "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ę", @@ -949,23 +932,23 @@ "unable_to_save_api_key": "Zapisywanie Klucza API nie powiodło się", "unable_to_save_date_of_birth": "Nie moÅŧna zapisać daty urodzenia", "unable_to_save_name": "Zapisywanie nazwy nie powiodło się", - "unable_to_save_profile": "Zapisywanie profilu nie powiodło się", - "unable_to_save_settings": "Zapisywanie ustawień nie powiodło się", + "unable_to_save_profile": "Nie moÅŧna zapisać profilu", + "unable_to_save_settings": "Nie moÅŧna zapisać ustawień", "unable_to_scan_libraries": "Nie moÅŧna przeskanować bibliotek", "unable_to_scan_library": "Nie moÅŧna przeskanować biblioteki", "unable_to_set_feature_photo": "Nie moÅŧna ustawić zdjęcia gÅ‚Ãŗwnego", - "unable_to_set_profile_picture": "Zmiana zdjęcia profilowego nie powiodła się", + "unable_to_set_profile_picture": "Nie moÅŧna zmienić zdjęcia profilowego", "unable_to_submit_job": "Nie moÅŧna przesłać zadania", - "unable_to_trash_asset": "Przeniesienie zasobu do kosza nie powiodło się", - "unable_to_unlink_account": "Odłączenie konta nie powiodło się", + "unable_to_trash_asset": "Nie moÅŧna przenieść zasobu do kosza", + "unable_to_unlink_account": "Nie moÅŧna odłączyć konta", "unable_to_unlink_motion_video": "Nie moÅŧna odłączyć ruchomego wideo", "unable_to_update_album_cover": "Nie moÅŧna zaktualizować okładki albumu", "unable_to_update_album_info": "Nie moÅŧna zaktualizować informacji o albumie", "unable_to_update_library": "Nie moÅŧna zaktualizować biblioteki", - "unable_to_update_location": "Zmiana lokalizacji nie powiodła się", - "unable_to_update_settings": "Zmiana ustawień nie powiodła się", - "unable_to_update_timeline_display_status": "Zmiana ustawienia wyświetlania na osi czasu nie powiodła się", - "unable_to_update_user": "Zmiana uÅŧytkownika nie powiodła się", + "unable_to_update_location": "Nie moÅŧna zaktualizować lokalizacji", + "unable_to_update_settings": "Nie moÅŧna zmienić ustawień", + "unable_to_update_timeline_display_status": "Nie moÅŧna zaktualizować stanu wyświetlania na osi czasu", + "unable_to_update_user": "Nie moÅŧna zmienić uÅŧytkownika", "unable_to_upload_file": "Nie moÅŧna przesłać pliku" }, "exif": "Metadane EXIF", @@ -974,14 +957,13 @@ "exif_bottom_sheet_location": "LOKALIZACJA", "exif_bottom_sheet_people": "LUDZIE", "exif_bottom_sheet_person_add_person": "Dodaj nazwę", - "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}}", + "exif_bottom_sheet_person_age_months": "Wiek {months} miesięcy", + "exif_bottom_sheet_person_age_year_months": "Wiek 1 rok, {months} miesięcy", + "exif_bottom_sheet_person_age_years": "Wiek: {years} lat", "exit_slideshow": "Zamknij Pokaz SlajdÃŗw", "expand_all": "Rozwiń wszystko", "experimental_settings_new_asset_list_subtitle": "Praca w toku", - "experimental_settings_new_asset_list_title": "Włącz eksperymentalną układ zdjęć", + "experimental_settings_new_asset_list_title": "Włącz eksperymentalny układ zdjęć", "experimental_settings_subtitle": "UÅŧywaj na własne ryzyko!", "experimental_settings_title": "Eksperymentalny", "expire_after": "Wygasa po", @@ -1017,10 +999,13 @@ "filter_places": "Filtruj miejsca", "find_them_fast": "Wyszukuj szybciej przypisując nazwę", "fix_incorrect_match": "Napraw nieprawidłowe dopasowanie", + "folder": "Folder", "folder_not_found": "Nie znaleziono folderu", "folders": "Foldery", "folders_feature_description": "Przeglądanie zdjęć i filmÃŗw w widoku folderÃŗw", "forward": "Do przodu", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Ta funkcja pobiera zewnętrzne zasoby z Google, aby działać.", "general": "OgÃŗlne", "get_help": "Pomoc", "get_wifiname_error": "Nie moÅŧna uzyskać nazwy Wi-Fi. Upewnij się, Åŧe udzieliłeś niezbędnych uprawnień i jesteś połączony z siecią Wi-Fi", @@ -1067,7 +1052,9 @@ "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", + "id": "ID", "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", @@ -1107,6 +1094,12 @@ "invalid_date_format": "Nieprawidłowy format daty", "invite_people": "Zaproś Osoby", "invite_to_album": "Zaproś do albumu", + "ios_debug_info_fetch_ran_at": "Pobieranie przebiegło {dateTime}", + "ios_debug_info_last_sync_at": "Ostatnia synchronizacja {dateTime}", + "ios_debug_info_no_processes_queued": "Brak procesÃŗw w tle w kolejce", + "ios_debug_info_no_sync_yet": "Nie uruchomiono jeszcze Åŧadnego zadania synchronizacji w tle", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proces w tle w kolejce} few {{count} procesy w tle w kolejce} other {{count} procesÃŗw w tle w kolejce}}", + "ios_debug_info_processing_ran_at": "Przetwarzanie przebiegło {dateTime}", "items_count": "{count, plural, one {# element} other {# elementy}}", "jobs": "Zadania", "keep": "Zachowaj", @@ -1115,6 +1108,9 @@ "kept_this_deleted_others": "Zachowano ten zasÃŗb i usunięto {count, plural, one {#zasÃŗb} other {#zasoby}}", "keyboard_shortcuts": "SkrÃŗty klawiaturowe", "language": "Język", + "language_no_results_subtitle": "SprÃŗbuj dostosować wyszukiwaną frazę", + "language_no_results_title": "Nie znaleziono Åŧadnych językÃŗw", + "language_search_hint": "Szukaj językÃŗw...", "language_setting_description": "Wybierz swÃŗj preferowany język", "last_seen": "Ostatnio widziane", "latest_version": "Najnowsza Wersja", @@ -1140,6 +1136,7 @@ "list": "Lista", "loading": "Ładowanie", "loading_search_results_failed": "Ładowanie wynikÃŗw wyszukiwania nie powiodło się", + "local_asset_cast_failed": "Nie moÅŧna strumieniować zasobu, ktÃŗry nie został przesłany na serwer", "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ę", @@ -1153,6 +1150,7 @@ "locked_folder": "Folder zablokowany", "log_out": "Wyloguj", "log_out_all_devices": "Wyloguj ze Wszystkich Urządzeń", + "logged_in_as": "Zalogowano jako {user}", "logged_out_all_devices": "Wylogowano ze wszystkich urządzeń", "logged_out_device": "Wylogowany z urządzenia", "login": "Logowanie", @@ -1184,7 +1182,7 @@ "look": "Wygląd", "loop_videos": "Powtarzaj filmy", "loop_videos_description": "Włącz automatyczne zapętlanie wideo w przeglądarce szczegÃŗÅ‚Ãŗw.", - "main_branch_warning": "UÅŧywasz wersji deweloperskiej. Rekomendujemy instalację stabilnej wersji aplikacji!", + "main_branch_warning": "UÅŧywasz wersji deweloperskiej. Zdecydowanie zalecamy korzystanie z wydanej wersji aplikacji!", "main_menu": "Menu gÅ‚Ãŗwne", "make": "Marka", "manage_shared_links": "Zarządzaj udostępnionymi linkami", @@ -1196,7 +1194,7 @@ "manage_your_oauth_connection": "Zarządzaj swoim połączeniem OAuth", "map": "Mapa", "map_assets_in_bound": "{count} zdjęcie", - "map_assets_in_bounds": "{count, plural, few {# zdjęcia} other {# zdjęć}}", + "map_assets_in_bounds": "{count} 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", @@ -1206,13 +1204,13 @@ "map_marker_with_image": "Znacznik na mapie ze zdjęciem", "map_no_assets_in_bounds": "Brak zdjęć w tym obszarze", "map_no_location_permission_content": "Aby wyświetlić zasoby z Twojej bieÅŧącej lokalizacji, potrzebne jest pozwolenie na lokalizację. Czy chcesz teraz na to pozwolić?", - "map_no_location_permission_title": "Odmowa pozwolenia na lokalizację", + "map_no_location_permission_title": "Odmowa dostępu do lokalizacji", "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": "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_date_range_option_years": "Ostatnie {years} lat", "map_settings_dialog_title": "Ustawienia mapy", "map_settings_include_show_archived": "Uwzględnij zarchiwizowane", "map_settings_include_show_partners": "Uwzględnij partnerÃŗw", @@ -1232,6 +1230,7 @@ "memories_swipe_to_close": "Przesuń w gÃŗrę, aby zamknąć", "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", @@ -1241,7 +1240,9 @@ "minimize": "Zminimalizuj", "minute": "Minuta", "missing": "Brakujące", + "model": "Model", "month": "Miesiąc", + "monthly_title_text_date_format": "MMMM y", "more": "Więcej", "move": "Przenieś", "move_off_locked_folder": "Przenieś z folderu zablokowanego", @@ -1277,6 +1278,7 @@ "no_archived_assets_message": "Archiwizuj zdjęcia i filmy, aby ukryć je ze strony Zdjęcia", "no_assets_message": "KLIKNIJ, ABY WYSŁAĆ PIERWSZE ZDJĘCIE", "no_assets_to_show": "Brak zasobÃŗw do pokazania", + "no_cast_devices_found": "Nie znaleziono urządzeń do przesyłania strumieniowego", "no_duplicates_found": "Nie znaleziono duplikatÃŗw.", "no_exif_info_available": "Nie znaleziono informacji exif", "no_explore_results_message": "Prześlij więcej zdjęć, aby przeglądać swÃŗj zbiÃŗr.", @@ -1302,15 +1304,18 @@ "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_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.", + "offline": "Offline", + "ok": "Ok", "oldest_first": "Od najstarszych", "on_this_device": "Na tym urządzeniu", "onboarding": "WdroÅŧenie", - "onboarding_privacy_description": "Śledzenie (opcjonalne) funkcja opiera się na zewnętrznych usługach i moÅŧe zostać wyłączona w dowolnym momencie w ustawieniach administracyjnych.", + "onboarding_locale_description": "Wybierz preferowany język. MoÅŧna to pÃŗÅēniej zmienić w ustawieniach.", + "onboarding_privacy_description": "Śledzenie (opcjonalne) funkcja opiera się na zewnętrznych usługach i moÅŧe zostać wyłączona w dowolnym momencie w ustawieniach.", + "onboarding_server_welcome_description": "Skonfigurujmy twoją instancję z kilkoma typowymi ustawieniami.", "onboarding_theme_description": "Wybierz motyw kolorystyczny dla twojej instancji. MoÅŧesz go pÃŗÅēniej zmienić w ustawieniach.", - "onboarding_welcome_description": "PrzejdÅēmy do konfiguracji twojej instancji, ustawiając kilka powszechnych opcji.", + "onboarding_user_welcome_description": "Zaczynamy!", "onboarding_welcome_user": "Witaj, {user}", "online": "Połączony", "only_favorites": "Tylko ulubione", @@ -1327,6 +1332,7 @@ "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", @@ -1366,6 +1372,8 @@ "permanently_delete_assets_prompt": "Czy na pewno chcesz trwale usunąć {count, plural, one {ten zasÃŗb?} other {te # zasoby?}} Spowoduje to rÃŗwnieÅŧ usunięcie {count, plural, one {go z jego} other {ich z ich}} album(Ãŗw).", "permanently_deleted_asset": "Pomyślnie trwale usunięto zasÃŗb", "permanently_deleted_assets_count": "Trwale usunięto {count, plural, one {# zasÃŗb} other {# zasobÃŗw}}", + "permission": "Pozwolenie", + "permission_empty": "Twoje pozwolenie nie powinno być puste", "permission_onboarding_back": "Cofnij", "permission_onboarding_continue_anyway": "Kontynuuj mimo to", "permission_onboarding_get_started": "Rozpocznij", @@ -1395,13 +1403,17 @@ "play_motion_photo": "OdtwÃŗrz Ruchome Zdjęcie", "play_or_pause_video": "OdtwÃŗrz lub wstrzymaj wideo", "please_auth_to_access": "Uwierzytelnij się, aby uzyskać dostęp", + "port": "Port", "preferences_settings_subtitle": "Zarządzaj preferencjami aplikacji", "preferences_settings_title": "Ustawienia", "preset": "Ustawienie", "preview": "Podgląd", "previous": "Poprzedni", "previous_memory": "Poprzednie wspomnienie", - "previous_or_next_photo": "Poprzednie lub następne zdjęcie", + "previous_or_next_day": "Dzień następny/poprzedni", + "previous_or_next_month": "Miesiąc następny/poprzedni", + "previous_or_next_photo": "Zdjęcie następne/poprzednie", + "previous_or_next_year": "Rok następny/poprzedni", "primary": "GÅ‚Ãŗwny", "privacy": "Prywatność", "profile": "Profil", @@ -1409,6 +1421,7 @@ "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}", @@ -1484,10 +1497,11 @@ "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_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", + "remove_tag": "Usuń tag", "remove_url": "Usuń URL", "remove_user": "Usuń uÅŧytkownika", "removed_api_key": "Usunięto Klucz API: {name}", @@ -1505,6 +1519,7 @@ "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", @@ -1593,6 +1608,7 @@ "select_album_cover": "Wybierz okładkę albumu", "select_all": "Zaznacz wszystko", "select_all_duplicates": "Wybierz wszystkie duplikaty", + "select_all_in": "Wybierz wszystkie w {group}", "select_avatar_color": "Wybierz kolor awatara", "select_face": "Wybierz twarz", "select_featured_photo": "Zmień gÅ‚Ãŗwne zdjęcie", @@ -1613,6 +1629,7 @@ "server_info_box_server_url": "Adres URL", "server_offline": "Serwer Offline", "server_online": "Serwer Online", + "server_privacy": "Ochrona prywatności serwera", "server_stats": "Statystyki serwera", "server_version": "Wersja serwera", "set": "Ustaw", @@ -1622,6 +1639,7 @@ "set_date_of_birth": "Ustaw datę urodzenia", "set_profile_picture": "Ustaw zdjęcie profilowe", "set_slideshow_to_fullscreen": "Ustaw Pokaz slajdÃŗw na pełny ekran", + "set_stack_primary_asset": "Ustaw jako gÅ‚Ãŗwny zasÃŗb", "setting_image_viewer_help": "Przeglądarka szczegÃŗÅ‚Ãŗw najpierw ładuje małą miniaturę, następnie ładuje podgląd średniej wielkości (jeśli jest włączony), a na koniec ładuje oryginał (jeśli jest włączony).", "setting_image_viewer_original_subtitle": "Włącz ładowanie oryginalnego obrazu w pełnej rozdzielczości (duÅŧego!). Wyłącz, aby zmniejszyć zuÅŧycie danych (zarÃŗwno w sieci, jak i w pamięci podręcznej urządzenia).", "setting_image_viewer_original_title": "Załaduj oryginalny obraz", @@ -1630,13 +1648,12 @@ "setting_image_viewer_title": "Zdjęcia", "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: {duration}", - "setting_notifications_notify_hours": "{count, plural, one {# godzina} few {# godziny} other {# godzin}}", + "setting_notifications_notify_hours": "{count} godzin", "setting_notifications_notify_immediately": "natychmiast", - "setting_notifications_notify_minutes": "{count, plural, one {# minuta} few {# minuty} other {# minut}}", + "setting_notifications_notify_minutes": "{count} minut", "setting_notifications_notify_never": "nigdy", - "setting_notifications_notify_seconds": "{count, plural, one {# sekunda} few {# sekundy} other {# sekund}}", + "setting_notifications_notify_seconds": "{count} 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ń", @@ -1679,25 +1696,26 @@ "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_expire_after_option_year": "{count} roku", "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 {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_hours": "Wygasa za {count} godzin(y)", "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_minutes": "Wygasa za {count} minut(y)", "shared_link_expires_never": "Wygasa ∞", "shared_link_expires_second": "Wygasa za {count} sekundę", - "shared_link_expires_seconds": "Wygasa za {count, plural, one {# sekundę} few {# sekundy} other {# sekund}}", + "shared_link_expires_seconds": "Wygasa za {count} sekund(y)", "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", "shared_links_description": "Udostępnij zdjęcia oraz filmy przez link", - "shared_photos_and_videos_count": "{assetCount, plural, other {# udostępnione zdjęcia i filmy.}}", + "shared_photos_and_videos_count": "{assetCount, plural, one {# udostępnione zdjęcie lub film.} other {# udostępnione zdjęcia i filmy.}}", "shared_with_me": "Udostępniono mi", "shared_with_partner": "Dzielisz się z {partner}", "sharing": "Udostępnianie", @@ -1755,8 +1773,11 @@ "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": "WojewÃŗdztwo", + "status": "Status", + "stop_casting": "Zatrzymaj strumieniowanie", "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ęć.", @@ -1814,7 +1835,6 @@ "to_parent": "IdÅē do rodzica", "to_trash": "Kosz", "toggle_settings": "Przełącz ustawienia", - "toggle_theme": "Przełącz ciemny motyw", "total": "Całkowity", "total_usage": "Całkowite wykorzystanie", "trash": "Kosz", @@ -1825,7 +1845,7 @@ "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 {days, plural, one {# dniu} other {# dniach}}", + "trash_page_info": "Elementy przeniesione do kosza zostaną trwale usunięte po {days} dniach", "trash_page_no_assets": "Brak usuniętych zasobÃŗw", "trash_page_restore_all": "PrzywrÃŗcić wszystkie", "trash_page_select_assets_btn": "Wybierz zasoby", @@ -1836,6 +1856,7 @@ "unable_to_setup_pin_code": "Nie moÅŧna ustawić kodu PIN", "unarchive": "Cofnij archiwizację", "unarchived_count": "{count, plural, one {# cofnięta archiwizacja} few {# cofnięte archiwizacje} other {# cofniętych archiwizacji}}", + "undo": "Cofnij", "unfavorite": "Usuń z ulubionych", "unhide_person": "PrzywrÃŗÄ‡ osobę", "unknown": "Nieznany", @@ -1852,10 +1873,9 @@ "unsaved_change": "Niezapisana zmiana", "unselect_all": "Odznacz wszystko", "unselect_all_duplicates": "Odznacz wszystkie duplikaty", + "unselect_all_in": "Odznacz wszystkie w {group}", "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", @@ -1872,6 +1892,7 @@ "upload_success": "Przesyłanie powiodło się, odświeÅŧ stronę, aby zobaczyć nowo przesłane zasoby.", "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", @@ -1882,6 +1903,7 @@ "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_privacy": "Ochrona prywatności uÅŧytkownika", "user_purchase_settings": "Zakup", "user_purchase_settings_description": "Zarządzaj swoim zakupem", "user_role_set": "Ustaw {user} jako {role}", @@ -1897,11 +1919,6 @@ "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 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 ", - "version_announcement_overlay_text_3": " i upewnij się, Åŧe twoja konfiguracja docker-compose i .env jest aktualna, aby zapobiec błędnym konfiguracjom, zwłaszcza jeśli uÅŧywasz WatchTower lub dowolnego mechanizmu, ktÃŗry obsługuje automatyczną aktualizację aplikacji serwera.", - "version_announcement_overlay_title": "Nowa wersja serwera dostępna 🎉", "version_history": "Historia wersji", "version_history_item": "Zainstalowano {version} w {date}", "video": "Wideo", @@ -1921,6 +1938,7 @@ "view_previous_asset": "Wyświetl poprzedni zasÃŗb", "view_qr_code": "PokaÅŧ kod QR", "view_stack": "Zobacz UłoÅŧenie", + "view_user": "Wyświetl uÅŧytkownika", "viewer_remove_from_stack": "Usuń ze stosu", "viewer_stack_use_as_main_asset": "UÅŧyj jako gÅ‚Ãŗwnego zasobu", "viewer_unstack": "RozÅ‚ÃŗÅŧ Stos", diff --git a/i18n/pt.json b/i18n/pt.json index c22c64ab4b..bb88cafbfa 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -22,11 +22,11 @@ "add_partner": "Adicionar parceiro", "add_path": "Adicionar caminho", "add_photos": "Adicionar fotos", + "add_tag": "Adicionar Marcador", "add_to": "Adicionar aâ€Ļ", "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", @@ -34,6 +34,7 @@ "added_to_favorites_count": "{count, plural, one {{count, number} adicionado aos favoritos} other {{count, number} adicionados aos favoritos}}", "admin": { "add_exclusion_pattern_description": "Adicione padrÃĩes de exclusÃŖo. Utilizar *, ** ou ? sÃŖo suportados. Para ignorar todos os ficheiros em qualquer diretÃŗrio chamado \"Raw\", use \"**/Raw/**'. Para ignorar todos os ficheiros que finalizam em \".tif\", use \"**/*.tif\". Para ignorar um caminho absoluto, use \"/caminho/para/ignorar/**\".", + "admin_user": "Utilizador Administrador", "asset_offline_description": "Este ficheiro proveniente de uma biblioteca externa deixou de estar disponível no disco e foi movido para a reciclagem. Se o ficheiro foi movido no interior da biblioteca, procure na linha de tempo pelo novo ficheiro correspondente. Para restaurar este ficheiro, certifique-se que o caminho do ficheiro abaixo pode ser acedido pelo Immich e analise a biblioteca.", "authentication_settings": "DefiniçÃĩes de AutenticaÃ§ÃŖo", "authentication_settings_description": "Gerir palavras-passe, OAuth, e outras definiçÃĩes de autenticaÃ§ÃŖo", @@ -44,9 +45,7 @@ "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", + "backup_settings_description": "Gerir definiçÃĩes de cÃŗpia da base de dados.", "cleared_jobs": "Eliminadas as tarefas de: {job}", "config_set_by_file": "A configuraÃ§ÃŖo estÃĄ atualmente definida por um ficheiro de configuraÃ§ÃŖo", "confirm_delete_library": "Tem a certeza de que deseja eliminar a biblioteca {library} ?", @@ -62,14 +61,12 @@ "disable_login": "Desativar inicio de sessÃŖo", "duplicate_detection_job_description": "Executa a aprendizagem de mÃĄquina em ficheiros para detetar imagens semelhantes. Depende da Pesquisa Inteligente", "exclusion_pattern_description": "Os padrÃĩes de exclusÃŖo permitem ignorar ficheiros e pastas ao analisar a sua biblioteca. Isto Ê Ãētil se tiver pastas que contenham ficheiros que nÃŖo deseja importar, como ficheiros RAW.", - "external_library_created_at": "Biblioteca externa (criada em {date})", "external_library_management": "GestÃŖo de bibliotecas externas", "face_detection": "DeteÃ§ÃŖo de Rostos", "face_detection_description": "Deteta rostos em ficheiros utilizando aprendizagem automÃĄtica. Para vídeos, apenas a miniatura Ê considerada. \"Atualizar\" (re)processa todos os ficheiros, enquanto \"Redefinir\" elimina todos os dados de rostos. \"Em falta\" coloca em fila ficheiros que ainda nÃŖo foram processados. Os rostos detetados serÃŖo colocados em fila para Reconhecimento Facial apÃŗs a conclusÃŖo da DeteÃ§ÃŖo de Rostos, agrupando-os em pessoas novas ou jÃĄ existentes.", "facial_recognition_job_description": "Agrupa rostos detetadas em pessoas. Esta etapa Ê executada apÃŗs a conclusÃŖo da DeteÃ§ÃŖo de Rostos. \"Redefinir\" (re)agrupa todos os rostos. \"Em falta\" coloca em fila rostos que ainda nÃŖo tÃĒm uma pessoa atribuída.", - "failed_job_command": "Comando {command} falhou para a tarefa: {job}", - "force_delete_user_warning": "AVISO: Isto removerÃĄ imediatamente o utilizador e todos os ficheiros. Isso nÃŖo pode ser revertido e os ficheiros nÃŖo poderÃŖo ser recuperados.", - "forcing_refresh_library_files": "A forçar a atualizaÃ§ÃŖo de todos os ficheiros da biblioteca", + "failed_job_command": "Ocorreu um erro no comando {command} para a tarefa: {job}", + "force_delete_user_warning": "AVISO: Isto removerÃĄ imediatamente o utilizador e todos os ficheiros. Esta aÃ§ÃŖo nÃŖo pode ser anulada e os ficheiros nÃŖo poderÃŖo ser recuperados.", "image_format": "Formato", "image_format_description": "WebP produz ficheiros mais pequenos do que JPEG, mas Ê mais lento para codificar.", "image_fullsize_description": "Imagem de tamanho inteiro sem meta dados, utilizada quando esta for ampliada", @@ -122,7 +119,7 @@ "machine_learning_duplicate_detection_enabled_description": "Se desativado, ficheiros exatamente idÃĒnticos serÃŖo desduplicados na mesma.", "machine_learning_duplicate_detection_setting_description": "Utilizar embeddings CLIP para encontrar itens possivelmente duplicados", "machine_learning_enabled": "Ativar a aprendizagem de mÃĄquina", - "machine_learning_enabled_description": "Se desativado, todos as funcionalidades de ML serÃŖo desativados, independentemente das definiçÃĩes abaixo.", + "machine_learning_enabled_description": "Se desativado, todas as funcionalidades de ML serÃŖo desativadas, independentemente das definiçÃĩes abaixo.", "machine_learning_facial_recognition": "Reconhecimento Facial", "machine_learning_facial_recognition_description": "Detetar, reconhecer e agrupar rostos em imagens", "machine_learning_facial_recognition_model": "Modelo de reconhecimento facial", @@ -174,7 +171,7 @@ "note_apply_storage_label_previous_assets": "ObservaÃ§ÃŖo: Para aplicar o RÃŗtulo de Armazenamento a ficheiros carregados anteriormente, execute o", "note_cannot_be_changed_later": "NOTA: Isto nÃŖo pode ser alterado posteriormente!", "notification_email_from_address": "A partir do endereço", - "notification_email_from_address_description": "Endereço de e-mail do remetente, por exemplo: \"Servidor de Fotos Immich \"", + "notification_email_from_address_description": "Endereço de e-mail do remetente, por exemplo: \"Servidor de Fotos Immich \". Certifique-se de que utiliza um endereço atravÊs do qual pode enviar e-mails.", "notification_email_host_description": "Host do servidor de e-mail (por exemplo, smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorar erros de certificado", "notification_email_ignore_certificate_errors_description": "Ignorar erros de validaÃ§ÃŖo de certificado TLS (nÃŖo recomendado)", @@ -183,7 +180,7 @@ "notification_email_sent_test_email_button": "Enviar e-mail de teste e gravar", "notification_email_setting_description": "DefiniçÃĩes para envio de notificaçÃĩes por e-mail", "notification_email_test_email": "Enviar e-mail de teste", - "notification_email_test_email_failed": "Falha ao enviar e-mail de teste, verifique os valores", + "notification_email_test_email_failed": "Ocorreu um erro ao enviar e-mail de teste, verifique os valores", "notification_email_test_email_sent": "Um email de teste foi enviado para {email}. Por favor, verifique a sua caixa de entrada.", "notification_email_username_description": "Nome de utilizador a ser usado ao autenticar com o servidor de e-mail", "notification_enable_email_notifications": "Ativar notificaçÃĩes por e-mail", @@ -198,7 +195,7 @@ "oauth_enable_description": "Iniciar sessÃŖo com o OAuth", "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_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth nÃŖo permite um URI mÃŗvel, como ''{callback}''", "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.", @@ -207,11 +204,9 @@ "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_storage_quota_default_description": "Quota em GiB a ser usada quando nenhuma reivindicaÃ§ÃŖo for fornecida.", "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", "password_settings": "Palavra-passe de acesso", "password_settings_description": "Gerir definiçÃĩes de inicio de sessÃŖo e palavra-passe", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "A atualizar todas as bibliotecas", "registration": "Registo de Administrador", "registration_description": "Como Ê o primeiro utilizador no sistema, serÃĄ marcado como administrador, e serÃĄ responsÃĄvel pelas tarefas administrativas, sendo que utilizadores adicionais serÃŖo criados por si.", - "repair_all": "Reparar tudo", - "repair_matched_items": "{count, plural, one {Encontrado # item} other {Encontrados # itens}}", - "repaired_items": "{count, plural, one {Reparado # item} other {Reparados # itens}}", "require_password_change_on_login": "Exigir que o utilizador altere a palavra-passe no primeiro início de sessÃŖo", "reset_settings_to_default": "Redefinir as definiçÃĩes para o padrÃŖo", "reset_settings_to_recent_saved": "Redefinir as definiçÃĩes para as guardadas mais recentemente", @@ -252,7 +244,7 @@ "storage_template_migration_info": "O modelo de armazenamento irÃĄ converter todas as extensÃĩes para letra minÃēscula. As mudanças do modelo apenas se aplicarÃŖo a novos ficheiros. Para aplicar o modelo retroativamente para os ficheiros carregados anteriormente, execute o {job}.", "storage_template_migration_job": "Tarefa de MigraÃ§ÃŖo do Modelo de Armazenamento", "storage_template_more_details": "Para mais informaçÃĩes sobre esta funcionalidade, dirija-se a Modelo de Armazenamento e às suas implicaçÃĩes", - "storage_template_onboarding_description": "Quando ativada, esta funcionalidade irÃĄ organizar os ficheiros automaticamente baseando-se num modelo definido pelo utilizador. Devido a problemas de estabilidade esta funcionalidade estÃĄ desativada por padrÃŖo. Para mais informaçÃĩes, por favor leia a documentaÃ§ÃŖo.", + "storage_template_onboarding_description_v2": "Quando ativada, estÃĄ funÃ§ÃŖo irÃĄ automaticamente organizar ficheiros com base num modelo definido pelo utilizador. Para mais informaçÃĩes, consulte a documentaÃ§ÃŖo.", "storage_template_path_length": "Limite aproximado do tamanho do caminho: {length, number}{limit, number}", "storage_template_settings": "Modelo de Armazenamento", "storage_template_settings_description": "Gerir a estrutura de pastas e o nome do ficheiro carregado", @@ -264,7 +256,6 @@ "template_email_invite_album": "Modelo do e-mail de convite para ÃĄlbum", "template_email_preview": "PrÊ-visualizar", "template_email_settings": "Modelos de e-mail", - "template_email_settings_description": "Gerir modelos personalizados de e-mail de notificaÃ§ÃŖo", "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", @@ -273,7 +264,6 @@ "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", "theme_settings_description": "Gerir a personalizaÃ§ÃŖo da interface web do Immich", - "these_files_matched_by_checksum": "Estes ficheiros sÃŖo correspondidos pelas suas somas de verificaÃ§ÃŖo", "thumbnail_generation_job": "Gerar miniaturas", "thumbnail_generation_job_description": "Gera miniaturas grandes, pequenas e desfocadas para cada ficheiro, bem como miniaturas para cada pessoa", "transcoding_acceleration_api": "API de aceleraÃ§ÃŖo", @@ -301,10 +291,9 @@ "transcoding_encoding_options": "DefiniçÃĩes de codificaÃ§ÃŖo de vídeo", "transcoding_encoding_options_description": "Definir codecs, resoluÃ§ÃŖo, qualidade e outras opçÃĩes para videos codificados", "transcoding_hardware_acceleration": "AceleraÃ§ÃŖo de hardware", - "transcoding_hardware_acceleration_description": "Experimental; muito mais rÃĄpido, mas terÃĄ qualidade inferior com a mesma taxa de bits", + "transcoding_hardware_acceleration_description": "Experimental; transcodificaÃ§ÃŖo mais rÃĄpida, mas poderÃĄ ter qualidade inferior com a mesma taxa de bits", "transcoding_hardware_decoding": "DecodificaÃ§ÃŖo de hardware", "transcoding_hardware_decoding_setting_description": "Permite a aceleraÃ§ÃŖo ponta a ponta em vez de apenas acelerar a codificaÃ§ÃŖo. Pode nÃŖo funcionar em todos os formatos de arquivo.", - "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "MÃĄximo de quadros B", "transcoding_max_b_frames_description": "Valores mais altos melhoram a eficiÃĒncia da compressÃŖo, mas tornam a codificaÃ§ÃŖo mais lenta. Pode nÃŖo ser compatível com aceleraÃ§ÃŖo de hardware em dispositivos mais antigos. 0 desativa os quadros B, enquanto -1 define esse valor automaticamente.", "transcoding_max_bitrate": "Taxa de bits mÃĄxima", @@ -342,8 +331,6 @@ "trash_number_of_days_description": "NÃēmero de dias para manter os ficheiros na reciclagem antes de os eliminar permanentemente", "trash_settings": "DefiniçÃĩes da Reciclagem", "trash_settings_description": "Gerir definiçÃĩes da reciclagem", - "untracked_files": "Ficheiros nÃŖo monitorizados", - "untracked_files_description": "Estes ficheiros nÃŖo sÃŖo monitorizados pela aplicaÃ§ÃŖo. Podem ser o resultado de transferÃĒncias mal-sucedidas, carregamentos interrompidos ou deixados para trÃĄs devido a um problema", "user_cleanup_job": "Limpeza de utilizadores", "user_delete_delay": "A conta e os ficheiros de {user} serÃŖo agendados para eliminaÃ§ÃŖo permanente dentro de {delay, plural, one {# dia} other {# dias}}.", "user_delete_delay_settings": "Atraso de eliminaÃ§ÃŖo", @@ -402,25 +389,24 @@ "album_remove_user": "Remover utilizador?", "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": "{count} ficheiros", - "album_thumbnail_card_shared": " ¡ Compartilhado", - "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}", "album_user_removed": "Utilizador {user} removido", "album_viewer_appbar_delete_confirm": "Tem certeza 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_delete": "Ocorreu um erro ao eliminar ÃĄlbum", + "album_viewer_appbar_share_err_leave": "Ocorreu um erro ao sair do ÃĄlbum", "album_viewer_appbar_share_err_remove": "Houveram problemas ao remover arquivos do ÃĄlbum", - "album_viewer_appbar_share_err_title": "Falha ao alterar título do ÃĄlbum", + "album_viewer_appbar_share_err_title": "Ocorreu um erro ao alterar o título do ÃĄlbum", "album_viewer_appbar_share_leave": "Deixar ÃĄlbum", "album_viewer_appbar_share_to": "Compartilhar com", "album_viewer_page_share_add_users": "Adicionar usuÃĄrios", "album_with_link_access": "Permite o acesso a fotos e pessoas deste ÃĄlbum por qualquer pessoa com o link.", "albums": "Álbuns", "albums_count": "{count, plural, one {{count, number} Álbum} other {{count, number} Álbuns}}", + "albums_default_sort_order": "Ordem padrÃŖo de organizaÃ§ÃŖo do ÃĄlbum", + "albums_default_sort_order_description": "Ordem inicial dos ficheiros ao criar novos ÃĄlbuns.", + "albums_feature_description": "ColeçÃĩes de ficheiros que podem ser partilhados com outros utilizadores.", "all": "Todos", "all_albums": "Todos os ÃĄlbuns", "all_people": "Todas as pessoas", @@ -479,9 +465,12 @@ "assets_added_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}}", "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_cannot_be_added_to_album_count": "NÃŖo foi possível adicionar {count, plural, one {ficheiro} other {ficheiros}} ao ÃĄlbum", "assets_count": "{count, plural, one {# ficheiro} other {# ficheiros}}", "assets_deleted_permanently": "{count} ficheiro(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} ficheiro(s) eliminado(s) permanentemente do servidor Immich", + "assets_downloaded_failed": "{count, plural, one {# ficheiro transferido - {error} ficheiro falhou} other {# ficheiros transferidos - {error} ficheiros falharam}}", + "assets_downloaded_successfully": "{count, plural, one {# ficheiro transferido com sucesso} other {# ficheiros transferidos com sucesso}}", "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}}", @@ -496,6 +485,7 @@ "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", + "autoplay_slideshow": "ApresentaÃ§ÃŖo automÃĄtica de diapositivos", "back": "Voltar", "back_close_deselect": "Voltar, fechar ou desmarcar", "background_location_permission": "PermissÃŖo de localizaÃ§ÃŖo em segundo plano", @@ -507,22 +497,23 @@ "backup_album_selection_page_selection_info": "InformaçÃĩes da SeleÃ§ÃŖo", "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 ligaÃ§ÃŖo ao servidor. A tentar de novoâ€Ļ", + "backup_background_service_backup_failed_message": "Ocorreu um erro ao efetuar cÃŗpia de segurança dos ficheiros. A tentar de novoâ€Ļ", + "backup_background_service_connection_failed_message": "Ocorreu um erro 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": "Ocorreu um erro ao enviar {filename}", "backup_controller_page_albums": "Backup Álbuns", - "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_content": "Para utilizar a cÃŗpia de segurança em segundo plano, ative a atualizaÃ§ÃŖo da aplicaÃ§ÃŖo em segundo plano em DefiniçÃĩ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_configure_error": "Ocorreu um erro ao configurar o serviço em segundo plano", "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", @@ -530,6 +521,7 @@ "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": "CÃŗpia de segurança", "backup_controller_page_backup_selected": "Selecionado: ", "backup_controller_page_backup_sub": "Fotos e vídeos salvos em backup", "backup_controller_page_created": "Criado em: {date}", @@ -537,6 +529,7 @@ "backup_controller_page_excluded": "Eliminado: ", "backup_controller_page_failed": "Falhou ({count})", "backup_controller_page_filename": "Nome do ficheiro: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "InformaçÃĩes do backup", "backup_controller_page_none_selected": "Nenhum selecionado", "backup_controller_page_remainder": "Restante", @@ -574,21 +567,17 @@ "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 ({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": "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": "{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 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", @@ -602,6 +591,7 @@ "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", + "cast_description": "Configurar destinos de reproduÃ§ÃŖo remota disponíveis", "change_date": "Alterar data", "change_description": "Alterar descriÃ§ÃŖo", "change_display_order": "Mudar ordem de exibiÃ§ÃŖo", @@ -611,15 +601,14 @@ "change_name_successfully": "Nome alterado com sucesso", "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_confirm_password": "Confirmar palavra-passe", "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_password_form_new_password": "Nova palavra-passe", + "change_password_form_password_mismatch": "As palavras-passe nÃŖo condizem", + "change_password_form_reenter_new_password": "Confirme a nova palavra-passe", "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", "check_corrupt_asset_backup": "Verificar por 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.", @@ -631,10 +620,11 @@ "clear_all_recent_searches": "Limpar todas as pesquisas recentes", "clear_message": "Limpar mensagem", "clear_value": "Limpar valor", - "client_cert_enter_password": "Digite a senha", + "client_cert_dialog_msg_confirm": "OK", + "client_cert_enter_password": "Escrever palavra-passe", "client_cert_import": "Importar", "client_cert_import_success_msg": "Certificado do cliente foi importado", - "client_cert_invalid_msg": "Certificado invÃĄlido ou senha incorreta", + "client_cert_invalid_msg": "Certificado invÃĄlido ou palavra-passe incorreta", "client_cert_remove_msg": "Certificado do cliente foi removido", "client_cert_subtitle": "Somente hÃĄ suporte ao formato PKCS12 (.p12, .pfx). Importar/Remover certificados estÃĄ disponivel somente durante o login", "client_cert_title": "Certificado de Cliente SSL", @@ -658,11 +648,13 @@ "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", + "confirm_tag_face": "Deseja marcar este rosto como {name}?", + "confirm_tag_face_unnamed": "Deseja marcar este rosto?", + "connected_device": "Dispositivo ligado", "connected_to": "Ligado a", "contain": "Ajustar", "context": "Contexto", "continue": "Continuar", - "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", @@ -711,6 +703,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", + "darkTheme": "Alternar tema escuro", "date_after": "Data apÃŗs", "date_and_time": "Data e Hora", "date_before": "Data antes", @@ -756,7 +749,9 @@ "direction": "DireÃ§ÃŖo", "disabled": "Desativado", "disallow_edits": "NÃŖo permitir ediçÃĩes", + "discord": "Discord", "discover": "Descobrir", + "discovered_devices": "Dispositivos descobertos", "dismiss_all_errors": "Dispensar todos os erros", "dismiss_error": "Dispensar erro", "display_options": "OpçÃĩes de exibiÃ§ÃŖo", @@ -771,8 +766,7 @@ "download_complete": "Sucesso", "download_enqueue": "Na fila", "download_error": "Erro ao baixar", - "download_failed": "Falha", - "download_filename": "ficheiro: {filename}", + "download_failed": "Ocorreu um erro ao descarregar", "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", @@ -812,6 +806,7 @@ "edit_title": "Editar Título", "edit_user": "Editar utilizador", "edited": "Editado", + "editor": "Editar", "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", @@ -830,10 +825,11 @@ "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_change_sort_album": "Ocorreu um erro 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}", + "error_tag_face_bounding_box": "Erro ao marcar o rosto - nÃŖo foi possível localizar o rosto", "error_title": "Erro - Algo correu mal", "errors": { "cannot_navigate_next_asset": "NÃŖo foi possível navegar para o prÃŗximo ficheiro", @@ -846,7 +842,6 @@ "cant_get_number_of_comments": "NÃŖo foi possível obter o nÃēmero de comentÃĄrios", "cant_search_people": "NÃŖo foi possível pesquisar pessoas", "cant_search_places": "NÃŖo foi possível pesquisar locais", - "cleared_jobs": "Tarefas eliminadas para: {job}", "error_adding_assets_to_album": "Erro ao adicionar ficheiros ao ÃĄlbum", "error_adding_users_to_album": "Erro ao adicionar utilizador ao ÃĄlbum", "error_deleting_shared_user": "Erro ao apagar o utilizador partilhado", @@ -855,26 +850,24 @@ "error_removing_assets_from_album": "Erro ao eliminar ficheiros do ÃĄlbum, verifique a consola para mais detalhes", "error_selecting_all_assets": "Erro ao selecionar todos os ficheiros", "exclusion_pattern_already_exists": "Este padrÃŖo de exclusÃŖo jÃĄ existe.", - "failed_job_command": "Comando {command} falhou para a tarefa: {job}", - "failed_to_create_album": "NÃŖo foi possível criar ÃĄlbum", - "failed_to_create_shared_link": "NÃŖo foi possível criar o link partilhado", - "failed_to_edit_shared_link": "NÃŖo foi possível editar o link partilhado", - "failed_to_get_people": "NÃŖo foi possível obter pessoas", + "failed_to_create_album": "Ocorreu um erro ao criar ÃĄlbum", + "failed_to_create_shared_link": "Ocorreu um erro ao criar o link partilhado", + "failed_to_edit_shared_link": "Ocorreu um erro ao editar o link partilhado", + "failed_to_get_people": "Ocorreu um erro ao obter pessoas", "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_asset": "Ocorreu um erro na leitura do ficheiro", + "failed_to_load_assets": "Ocorreu um erro na leitura dos 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_load_people": "Ocorreu um erro ao carregar pessoas", + "failed_to_remove_product_key": "Ocorreu um erro ao remover chave de produto", + "failed_to_stack_assets": "Ocorreu um erro ao empilhar os ficheiros", + "failed_to_unstack_assets": "Ocorreu um erro ao 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}}", + "paths_validation_failed": "Ocorreu um erro na validaÃ§ÃŖo de {paths, plural, one {# caminho} other {# caminhos}}", "profile_picture_transparent_pixels": "Imagem de perfil nÃŖo pode ter pixeis transparentes. Por favor amplie e/ou mova a imagem.", "quota_higher_than_disk_size": "Definiu uma quota maior do que o tamanho do disco", - "repair_unable_to_check_items": "NÃŖo foi possível verificar {count, select, one {um item} other {alguns itens}}", "unable_to_add_album_users": "NÃŖo foi possível adicionar utilizadores ao ÃĄlbum", "unable_to_add_assets_to_shared_link": "NÃŖo foi possível adicionar os ficheiros ao link partilhado", "unable_to_add_comment": "NÃŖo foi possível adicionar o comentÃĄrio", @@ -893,7 +886,6 @@ "unable_to_change_visibility": "NÃŖo Ê possível alterar a visibilidade de {count, plural, one {# pessoa} other {# pessoas}}", "unable_to_complete_oauth_login": "NÃŖo foi possível completar o início de sessÃŖo com OAuth", "unable_to_connect": "NÃŖo Ê possível ligar", - "unable_to_connect_to_server": "NÃŖo foi possível ligar ao servidor", "unable_to_copy_to_clipboard": "NÃŖo foi possível copiar para a ÃĄrea de transferÃĒncia, certifique-se de que estÃĄ a aceder à pagina atravÊs de https", "unable_to_create_admin_account": "NÃŖo foi possível criar conta de administrador", "unable_to_create_api_key": "NÃŖo foi possível criar uma nova Chave de API", @@ -913,18 +905,13 @@ "unable_to_enter_fullscreen": "NÃŖo foi possível entrar em modo de ecrÃŖ inteiro", "unable_to_exit_fullscreen": "NÃŖo foi possível sair do modo de ecrÃŖ inteiro", "unable_to_get_comments_number": "NÃŖo foi possível obter nÃēmero de comentÃĄrios", - "unable_to_get_shared_link": "NÃŖo foi possível obter link partilhado", + "unable_to_get_shared_link": "Ocorreu um erro ao obter link partilhado", "unable_to_hide_person": "NÃŖo foi possível esconder a pessoa", "unable_to_link_motion_video": "NÃŖo foi possível relacionar o video animado", "unable_to_link_oauth_account": "NÃŖo foi possível associar a conta OAuth", - "unable_to_load_album": "NÃŖo foi possível carregar o ÃĄlbum", - "unable_to_load_asset_activity": "NÃŖo foi possível carregar a atividade do ficheiro", - "unable_to_load_items": "NÃŖo foi possível carregar os itens", - "unable_to_load_liked_status": "NÃŖo foi possível carregar o estado de gostos", "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", @@ -932,11 +919,9 @@ "unable_to_remove_album_users": "NÃŖo foi possível remover utilizador do ÃĄlbum", "unable_to_remove_api_key": "NÃŖo foi possível remover a Chave de API", "unable_to_remove_assets_from_shared_link": "NÃŖo foi possível remover os ficheiros do link partilhado", - "unable_to_remove_deleted_assets": "NÃŖo foi possível remover ficheiros indisponíveis", "unable_to_remove_library": "NÃŖo foi possível remover a biblioteca", "unable_to_remove_partner": "NÃŖo foi possível remover parceiro", "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", @@ -966,12 +951,12 @@ "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": "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}", @@ -980,6 +965,7 @@ "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 apÃŗs", "expired": "Expirou", "expires_date": "Expira a {date}", @@ -993,9 +979,9 @@ "external_network": "Rede externa", "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": "Ocorreu um erro", "failed_to_authenticate": "NÃŖo foi possível autenticar", - "failed_to_load_assets": "Falha ao carregar ficheiros", + "failed_to_load_assets": "Ocorreu um erro ao carregar ficheiros", "failed_to_load_folder": "Ocorreu um erro ao carregar a pasta", "favorite": "Favorito", "favorite_or_unfavorite_photo": "Marcar ou desmarcar a foto como favorita", @@ -1018,6 +1004,8 @@ "folders": "Pastas", "folders_feature_description": "Navegar na vista de pastas por fotos e vídeos no sistema de ficheiros", "forward": "Para a frente", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Esta funcionalidade requer o carregamento de recursos externos da Google para poder funcionar.", "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", @@ -1048,7 +1036,7 @@ "hide_password": "Ocultar palavra-passe", "hide_person": "Ocultar pessoa", "hide_unnamed_people": "Ocultar pessoas sem nome", - "home_page_add_to_album_conflicts": "Adicionado {added} arquivos ao ÃĄlbum {album}. {failed} arquivos jÃĄ estÃŖo no ÃĄlbum.", + "home_page_add_to_album_conflicts": "Foram adicionados {added} ficheiros ao ÃĄlbum {album}. {failed} ficheiros jÃĄ estÃŖo no ÃĄlbum.", "home_page_add_to_album_err_local": "Ainda nÃŖo Ê possível adicionar recursos locais aos ÃĄlbuns, ignorando", "home_page_add_to_album_success": "Adicionado {added} arquivos ao ÃĄlbum {album}.", "home_page_album_err_partner": "Ainda nÃŖo Ê possível adicionar arquivos do parceiro a um ÃĄlbum, ignorando", @@ -1064,7 +1052,9 @@ "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": "Servidor", "hour": "Hora", + "id": "ID", "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", "image": "Imagem", @@ -1104,6 +1094,12 @@ "invalid_date_format": "Formato de data invÃĄlido", "invite_people": "Convidar Pessoas", "invite_to_album": "Convidar para o ÃĄlbum", + "ios_debug_info_fetch_ran_at": "Busca ocorreu a {dateTime}", + "ios_debug_info_last_sync_at": "Última sincronizaÃ§ÃŖo a {dateTime}", + "ios_debug_info_no_processes_queued": "NÃŖo existem processos de segundo plano em fila", + "ios_debug_info_no_sync_yet": "Ainda nÃŖo foi executada nenhuma tarefa de sincronizaÃ§ÃŖo em segundo plano", + "ios_debug_info_processes_queued": "{count, plural, one {{count} processo de segundo plano em fila} other {{count} processos de segundo plano em fila}}", + "ios_debug_info_processing_ran_at": "Processamento executado em {dateTime}", "items_count": "{count, plural, one {item #} other {itens #}}", "jobs": "Tarefas", "keep": "Manter", @@ -1112,9 +1108,13 @@ "kept_this_deleted_others": "Foi mantido ficheiro e {count, plural, one {eliminado # outro} other {eliminados # outros}}", "keyboard_shortcuts": "Atalhos do teclado", "language": "Idioma", + "language_no_results_subtitle": "Tente ajustar o seu termo de pesquisa", + "language_no_results_title": "Nenhuma língua encontrada", + "language_search_hint": "Procurar línguas...", "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", @@ -1135,7 +1135,8 @@ "linked_oauth_account": "Conta OAuth Associada", "list": "Lista", "loading": "A Carregar", - "loading_search_results_failed": "NÃŖo foi possível carregar os resultados da pesquisa", + "loading_search_results_failed": "Ocorreu um erro ao carregar os resultados da pesquisa", + "local_asset_cast_failed": "NÃŖo Ê possível transmitir um ficheiro que nÃŖo tenha sido enviado antes para o servidor", "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", @@ -1146,9 +1147,10 @@ "location_picker_longitude_error": "Digite uma longitude vÃĄlida", "location_picker_longitude_hint": "Digite a longitude", "lock": "Trancar", - "locked_folder": "Pasta trancada", + "locked_folder": "Pasta Trancada", "log_out": "Sair", "log_out_all_devices": "Terminar a sessÃŖo de todos os dispositivos", + "logged_in_as": "Utilizador atual: {user}", "logged_out_all_devices": "SessÃŖo terminada em todos os dispositivos", "logged_out_device": "SessÃŖo terminada no dispositivo", "login": "Iniciar sessÃŖo", @@ -1163,19 +1165,20 @@ "login_form_err_invalid_url": "URL invÃĄlida", "login_form_err_leading_whitespace": "Espaço em branco no início", "login_form_err_trailing_whitespace": "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, email e senha", + "login_form_failed_get_oauth_server_config": "Ocorreu um erro ao iniciar sessÃŖo com o OAuth, verifique o URL do servidor", + "login_form_failed_get_oauth_server_disable": "A funÃ§ÃŖo OAuth nÃŖo estÃĄ disponível neste servidor", + "login_form_failed_login": "Ocorreu um erro ao iniciar sessÃŖo, verifique o URL do servidor, o e-mail e a palavra-passe", "login_form_handshake_exception": "Erro ao conectar com o servidor. Ative o suporte para certificados auto-assinados nas configuraçÃĩes se estiver utilizando um certificado auto-assinado.", - "login_form_password_hint": "senha", + "login_form_password_hint": "Palavra-passe", "login_form_save_login": "Lembrar login", "login_form_server_empty": "Digite a URL de servidor.", "login_form_server_error": "NÃŖo foi possível conectar ao servidor.", "login_has_been_disabled": "Início de sessÃŖo foi desativado.", - "login_password_changed_error": "Erro ao atualizar a sua senha", - "login_password_changed_success": "Senha atualizada com sucesso", + "login_password_changed_error": "Ocorreu um erro ao atualizar a sua palavra-passe", + "login_password_changed_success": "Palavra-passe 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.", @@ -1227,6 +1230,7 @@ "memories_swipe_to_close": "Deslize para cima para fechar", "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", @@ -1238,6 +1242,7 @@ "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", @@ -1273,6 +1278,7 @@ "no_archived_assets_message": "Arquive fotos e vídeos para os ocultar da sua visualizaÃ§ÃŖo de fotos", "no_assets_message": "FAÇA CLIQUE PARA CARREGAR A SUA PRIMEIRA FOTO", "no_assets_to_show": "NÃŖo hÃĄ arquivos para exibir", + "no_cast_devices_found": "Nenhum dispositivo de transmissÃŖo encontrado", "no_duplicates_found": "Nenhum item duplicado foi encontrado.", "no_exif_info_available": "Sem informaçÃĩes exif disponíveis", "no_explore_results_message": "Carregue mais fotos para explorar a sua coleÃ§ÃŖo.", @@ -1298,16 +1304,20 @@ "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_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.", + "offline": "Offline", + "ok": "Ok", "oldest_first": "Mais antigo primeiro", "on_this_device": "Neste dispositivo", "onboarding": "IntegraÃ§ÃŖo", - "onboarding_privacy_description": "As seguintes funcionalidades opcionais dependem de serviços externos e podem ser desativados a qualquer momento nas definiçÃĩes de administraÃ§ÃŖo.", + "onboarding_locale_description": "Selecione a sua língua preferida. Pode alterÃĄ-la mais tarde nas definiçÃĩes.", + "onboarding_privacy_description": "As seguintes funcionalidades opcionais dependem de serviços externos e podem ser desativados a qualquer momento nas definiçÃĩes.", + "onboarding_server_welcome_description": "Vamos configurar a sua instÃĸncia com algumas definiçÃĩes comuns.", "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_user_welcome_description": "Vamos começar!", "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", @@ -1316,6 +1326,7 @@ "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", @@ -1329,14 +1340,14 @@ "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_partner_add_failed": "Ocorreu um erro ao adicionar parceiro", "partner_page_select_partner": "Selecionar parceiro", "partner_page_shared_to_title": "Compartilhar com", "partner_page_stop_sharing_content": "{partner} irÃĄ deixar de ter acesso às suas fotos.", "partner_sharing": "Partilha com Parceiro", "partners": "Parceiros", "password": "Palavra-passe", - "password_does_not_match": "As palavras-passe nÃŖo condizem", + "password_does_not_match": "A palavra-passe nÃŖo condiz", "password_required": "A palavra-passe Ê obrigatÃŗria", "password_reset_success": "Palavra-passe redefinida com sucesso", "past_durations": { @@ -1361,6 +1372,8 @@ "permanently_delete_assets_prompt": "Tem a certeza de que deseja eliminar permanentemente {count, plural, one {este ficheiro?} other {estes # ficheiros?}} Esta aÃ§ÃŖo tambÊm removerÃĄ {count, plural, one {isto do ÃĄlbum} other {isto dos ÃĄlbuns}}.", "permanently_deleted_asset": "Ficheiro eliminado permanentemente", "permanently_deleted_assets_count": "{count, plural, one {# Ficheiro eliminado} other {# Ficheiros eliminados}} permanentemente", + "permission": "PermissÃŖo", + "permission_empty": "A sua permissÃŖo nÃŖo pode estar vazia", "permission_onboarding_back": "Voltar", "permission_onboarding_continue_anyway": "Continuar mesmo assim", "permission_onboarding_get_started": "Começar", @@ -1389,7 +1402,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", + "please_auth_to_access": "Por favor autentique-se para aceder", "port": "Porta", "preferences_settings_subtitle": "Gerenciar preferÃĒncias do aplicativo", "preferences_settings_title": "PreferÃĒncias", @@ -1397,7 +1410,10 @@ "preview": "PrÊ-visualizar", "previous": "Anterior", "previous_memory": "MemÃŗria anterior", - "previous_or_next_photo": "Foto anterior ou prÃŗxima", + "previous_or_next_day": "Dia seguinte/anterior", + "previous_or_next_month": "MÃĒs seguinte/anterior", + "previous_or_next_photo": "Foto seguinte/anterior", + "previous_or_next_year": "Ano seguinte/anterior", "primary": "PrimÃĄrio", "privacy": "Privacidade", "profile": "Perfil", @@ -1405,6 +1421,7 @@ "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}", @@ -1484,6 +1501,7 @@ "remove_from_shared_link": "Remover do link partilhado", "remove_memory": "Remover memÃŗria", "remove_photo_from_memory": "Remover foto desta memÃŗria", + "remove_tag": "Remover marcador", "remove_url": "Remover URL", "remove_user": "Remover utilizador", "removed_api_key": "Foi removida a Chave de API: {name}", @@ -1516,6 +1534,7 @@ "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", @@ -1589,6 +1608,7 @@ "select_album_cover": "Escolher capa do ÃĄlbum", "select_all": "Selecionar todos", "select_all_duplicates": "Selecionar todos os itens duplicados", + "select_all_in": "Selecionar tudo em {group}", "select_avatar_color": "Selecionar cor do avatar", "select_face": "Selecionar rosto", "select_featured_photo": "Selecionar foto principal", @@ -1599,7 +1619,7 @@ "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", + "select_user_for_sharing_page_err_album": "Ocorreu um erro ao criar o ÃĄlbum", "selected": "Selecionados", "selected_count": "{count, plural, other {# selecionados}}", "send_message": "Enviar mensagem", @@ -1609,6 +1629,7 @@ "server_info_box_server_url": "URL do servidor", "server_offline": "Servidor Offline", "server_online": "Servidor Online", + "server_privacy": "Privacidade do Servidor", "server_stats": "Estado do servidor", "server_version": "VersÃŖo do servidor", "set": "Definir", @@ -1618,6 +1639,7 @@ "set_date_of_birth": "Definir data de nascimento", "set_profile_picture": "Definir foto de perfil", "set_slideshow_to_fullscreen": "ApresentaÃ§ÃŖo em ecrÃŖ inteiro", + "set_stack_primary_asset": "Definir como ficheiro principal", "setting_image_viewer_help": "O visualizador carrega primeiro a miniatura pequena, depois carrega a visualizaÃ§ÃŖo de tamanho mÊdio (se ativado) e, finalmente, carrega o original (se ativado).", "setting_image_viewer_original_subtitle": "Ative para carregar a imagem original em resoluÃ§ÃŖo total (grande!). Desative para reduzir o uso de dados (na rede e no cache do dispositivo).", "setting_image_viewer_original_title": "Carregar imagem original", @@ -1626,7 +1648,6 @@ "setting_image_viewer_title": "Imagens", "setting_languages_apply": "Aplicar", "setting_languages_subtitle": "Alterar o idioma do aplicativo", - "setting_languages_title": "Idioma", "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", @@ -1676,7 +1697,7 @@ "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_password_hint": "Escreva a palavra-passe do link partilhado", "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 {count} dia", @@ -1689,6 +1710,7 @@ "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", @@ -1750,10 +1772,12 @@ "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", "status": "Estado", + "stop_casting": "Parar transmissÃŖo", "stop_motion_photo": "Parar foto em movimento", "stop_photo_sharing": "Deixar de partilhar as suas fotos?", "stop_photo_sharing_description": "{partner} deixarÃĄ de ter acesso às suas fotos.", @@ -1811,7 +1835,7 @@ "to_parent": "Subir um nível", "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", @@ -1832,6 +1856,7 @@ "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 #}}", + "undo": "Anular", "unfavorite": "Remover favorito", "unhide_person": "Exibir pessoa", "unknown": "Desconhecido", @@ -1848,10 +1873,9 @@ "unsaved_change": "AlteraÃ§ÃŖo nÃŖo guardada", "unselect_all": "Limpar seleÃ§ÃŖo", "unselect_all_duplicates": "Remover seleÃ§ÃŖo de todos os itens duplicados", + "unselect_all_in": "Remover seleÃ§ÃŖo de {group}", "unstack": "Desempilhar", "unstacked_assets_count": "Desempilhados {count, plural, one {# ficheiro} other {# ficheiros}}", - "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", @@ -1868,6 +1892,7 @@ "upload_success": "Carregamento realizado com sucesso, atualize a pÃĄgina para ver os novos ficheiros carregados.", "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", @@ -1878,6 +1903,7 @@ "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_privacy": "Privacidade de Utilizador", "user_purchase_settings": "Comprar", "user_purchase_settings_description": "Gerir a sua compra", "user_role_set": "Definir {user} como {role}", @@ -1893,11 +1919,6 @@ "version": "VersÃŖo", "version_announcement_closing": "O seu amigo, Alex", "version_announcement_message": "OlÃĄ! EstÃĄ disponível uma nova versÃŖo do Immich. Por favor leia as notas de lançamento para garantir que as suas configuraçÃĩes estÃŖo atualizadas e para evitar quaisquer erros, especialmente se usar o WatchTower ou qualquer mecanismo que lide com a atualizaÃ§ÃŖo automÃĄtica do Immich.", - "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 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}", "video": "Vídeo", @@ -1917,6 +1938,7 @@ "view_previous_asset": "Ver ficheiro anterior", "view_qr_code": "Ver cÃŗdigo QR", "view_stack": "Ver pilha", + "view_user": "Ver utilizador", "viewer_remove_from_stack": "Remover da pilha", "viewer_stack_use_as_main_asset": "Usar como foto principal", "viewer_unstack": "Desempilhar", diff --git a/i18n/pt_BR.json b/i18n/pt_BR.json index 79f6cdbbc8..705180cafd 100644 --- a/i18n/pt_BR.json +++ b/i18n/pt_BR.json @@ -22,6 +22,7 @@ "add_partner": "Adicionar parceiro", "add_path": "Adicionar caminho", "add_photos": "Adicionar fotos", + "add_tag": "Adicionar Marcador", "add_to": "Adicionar aâ€Ļ", "add_to_album": "Adicionar ao ÃĄlbum", "add_to_album_bottom_sheet_added": "Adicionado ao {album}", @@ -33,6 +34,7 @@ "added_to_favorites_count": "{count, plural, one {{count, number} adicionado aos favoritos} other {{count, number} adicionados aos favoritos}}", "admin": { "add_exclusion_pattern_description": "Adicione padrÃĩes de exclusÃŖo. Utilizar *, ** ou ? sÃŖo suportados. Para ignorar todos os arquivos em qualquer diretÃŗrio chamado \"Raw\", use \"**/Raw/**'. Para ignorar todos os arquivos que terminam em \".tif\", use \"**/*.tif\". Para ignorar um caminho absoluto, use \"/caminho/para/ignorar/**\".", + "admin_user": "UsuÃĄrio Administrador", "asset_offline_description": "Este arquivo nÃŖo foi encontrado na biblioteca externa, entÃŖo foi enviado para a lixeira. Se o arquivo foi movido para outra pasta dentro da biblioteca, verifique sua linha do tempo para encontrar o arquivo novamente. Para restaurar este arquivo, certifique-se de que o caminho descrito abaixo pode ser acessado pelo Immich e entÃŖo escaneie a biblioteca.", "authentication_settings": "ConfiguraçÃĩes de AutenticaÃ§ÃŖo", "authentication_settings_description": "Gerenciar senhas, OAuth, e outras configuraçÃĩes de autenticaÃ§ÃŖo", @@ -43,9 +45,7 @@ "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. Nota: essas tarefas nÃŖo sÃŖo monitoradas e vocÃĒ nÃŖo serÃĄ notificado em caso de falhas.", - "check_all": "Selecionar Tudo", - "cleanup": "Limpeza", + "backup_settings_description": "Gerenciar configuraçÃĩes de backup do banco de dados.", "cleared_jobs": "Tarefas removidas de: {job}", "config_set_by_file": "A configuraÃ§ÃŖo estÃĄ atualmente definida por um arquivo de configuraÃ§ÃŖo", "confirm_delete_library": "VocÃĒ tem certeza que deseja excluir a biblioteca {library} ?", @@ -53,6 +53,7 @@ "confirm_email_below": "Para confirmar, digite \"{email}\" abaixo", "confirm_reprocess_all_faces": "Tem certeza de que deseja reprocessar todos os rostos? Isso tambÊm limparÃĄ as pessoas nomeadas.", "confirm_user_password_reset": "Tem certeza de que deseja redefinir a senha de {user}?", + "confirm_user_pin_code_reset": "Tem certeza de que deseja redefinir o cÃŗdigo PIN do usuÃĄrio {user}?", "create_job": "Criar tarefa", "cron_expression": "ExpressÃŖo CRON", "cron_expression_description": "Defina o intervalo de anÃĄlise no formato Cron. Para mais informaçÃĩes, por favor veja o Crontab Guru", @@ -60,14 +61,12 @@ "disable_login": "Desabilitar login", "duplicate_detection_job_description": "Execute o aprendizado de mÃĄquina em arquivos para detectar imagens semelhantes. Depende da Pesquisa Inteligente", "exclusion_pattern_description": "Os padrÃĩes de exclusÃŖo permitem ignorar arquivos e pastas ao escanear sua biblioteca. Isso Ê Ãētil se vocÃĒ tiver pastas que contenham arquivos que nÃŖo deseja importar, como arquivos RAW.", - "external_library_created_at": "Biblioteca externa (criada em {date})", "external_library_management": "Gerenciamento de bibliotecas externas", "face_detection": "DetecÃ§ÃŖo de rostos", "face_detection_description": "Detectar rostos nos arquivos usando aprendizado de mÃĄquina. Para vídeos, apenas a miniatura Ê considerada. ‘Atualizar’ (re)processa todos os arquivos. ‘Resetar’ tambÊm limpa todos os dados de rosto atuais. ‘Faltando’ coloca em fila os arquivos que ainda nÃŖo foram processados. Rostos detectados serÃŖo colocados em fila para o Reconhecimento Facial apÃŗs a conclusÃŖo da DetecÃ§ÃŖo de Rostos, agrupando-os em pessoas existentes ou novas.", "facial_recognition_job_description": "Agrupar rostos detectados em pessoas. Esta etapa Ê executada apÃŗs a conclusÃŖo da DetecÃ§ÃŖo de Rostos. ‘Resetar’ (re)agruparÃĄ todos os rostos. ‘Faltando’ coloca em fila os rostos que nÃŖo tÃĒm uma pessoa atribuída.", "failed_job_command": "O comando {command} falhou para a tarefa: {job}", "force_delete_user_warning": "AVISO: Isso removerÃĄ imediatamente o usuÃĄrio e todos os arquivos. Isso nÃŖo pode ser desfeito e os arquivos nÃŖo podem ser recuperados.", - "forcing_refresh_library_files": "Forçando a atualizaÃ§ÃŖo de todos os arquivos da biblioteca", "image_format": "Formato", "image_format_description": "WebP produz arquivos menores que JPEG, mas Ê mais lento para codificar.", "image_fullsize_description": "Imagem em tamanho real sem os metadados exibida quando der zoom", @@ -172,8 +171,8 @@ "note_apply_storage_label_previous_assets": "ObservaÃ§ÃŖo: Para aplicar o rÃŗtulo de armazenamento a arquivos carregados anteriormente, execute o", "note_cannot_be_changed_later": "NOTA: Isto nÃŖo pode ser alterado posteriormente!", "notification_email_from_address": "E-mail de origem", - "notification_email_from_address_description": "Endereço de e-mail do remetente, por exemplo: \"Immich Photo Server \"", - "notification_email_host_description": "Host do servidor de e-mail (por exemplo, smtp.immich.app)", + "notification_email_from_address_description": "Endereço de e-mail do remetente, por exemplo: \"Immich Photo Server \". Tenha certeza de ter permissÃŖo para enviar e-mails a partir do endereço selecionado.", + "notification_email_host_description": "Servidor de e-mail (por exemplo, smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorar erros de certificado", "notification_email_ignore_certificate_errors_description": "Ignorar erros de validaÃ§ÃŖo de certificado TLS (nÃŖo recomendado)", "notification_email_password_description": "Senha a ser usada ao autenticar no servidor de e-mail", @@ -196,20 +195,18 @@ "oauth_enable_description": "Faça login com OAuth", "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_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth nÃŖo suportar uma URI de aplicativo, por exemplo ''{callback}''", "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_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": "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_storage_quota_default_description": "Cota em GiB a ser usada quando nenhuma outra reivindicaÃ§ÃŖo for fornecida.", "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", "password_settings": "Senha de acesso", "password_settings_description": "Gerenciar configuraçÃĩes de login e senha", @@ -219,9 +216,6 @@ "refreshing_all_libraries": "Atualizando todas as bibliotecas", "registration": "Registro de Administrador", "registration_description": "Como vocÃĒ ÃŠ o primeiro usuÃĄrio no sistema, serÃĄ designado como o Administrador e serÃĄ responsÃĄvel pelas tarefas administrativas. VocÃĒ tambÊm poderÃĄ criar usuÃĄrios adicionais.", - "repair_all": "Reparar tudo", - "repair_matched_items": "{count, plural, one {# item encontrado} other {# itens encontrados}}", - "repaired_items": "{count, plural, one {# item reparado} other {# itens reparados}}", "require_password_change_on_login": "Exigir que o usuÃĄrio altere a senha no primeiro login", "reset_settings_to_default": "Redefinir as configuraçÃĩes para o padrÃŖo", "reset_settings_to_recent_saved": "Redefinir as configuraçÃĩes para as configuraçÃĩes salvas recentemente", @@ -250,7 +244,7 @@ "storage_template_migration_info": "O modelo altera todas extensÃĩes para minÃēsculo. As mudanças no modelo serÃŖo aplicadas apenas em novos arquivos. Para aplicar retroativamente o modelo aos arquivos carregados anteriormente, execute o {job}.", "storage_template_migration_job": "Tarefa de MigraÃ§ÃŖo de Modelo de Armazenamento", "storage_template_more_details": "Para mais detalhes sobre este recurso, consulte o Modelo de Armazenamento e suas implicaçÃĩes", - "storage_template_onboarding_description": "Quando ativado, este recurso organizarÃĄ automaticamente os arquivos com base em um modelo definido pelo usuÃĄrio. Devido a problemas de estabilidade, o recurso estÃĄ desativado por padrÃŖo. Para mais informaçÃĩes, consulte a documentaÃ§ÃŖo.", + "storage_template_onboarding_description_v2": "Ao ser ativado, este recurso irÃĄ organizar automaticamente os arquivos com base em um modelo definido pelo usuÃĄrio. Para mais informaçÃĩes, consulte a documentaÃ§ÃŖo.", "storage_template_path_length": "Limite aproximado de comprimento do caminho: {length, number}/{limit, number}", "storage_template_settings": "Modelo de Armazenamento", "storage_template_settings_description": "Gerencie a estrutura de pasta e o nome do arquivo carregado", @@ -262,16 +256,14 @@ "template_email_invite_album": "Modelo do e-mail de convite para ÃĄlbum", "template_email_preview": "PrÊ visualizaÃ§ÃŖo", "template_email_settings": "Modelos de e-mail", - "template_email_settings_description": "Gerenciar modelos personalizados de e-mail de notificaÃ§ÃŖo", "template_email_update_album": "Modelo do e-mail de atualizaÃ§ÃŖo do ÃĄlbum", "template_email_welcome": "Modelo do e-mail de boas vindas", "template_settings": "Modelos de notificaÃ§ÃŖo", - "template_settings_description": "Gerenciar modelos personalizados para notificaçÃĩes.", + "template_settings_description": "Gerenciar modelos personalizados para notificaçÃĩes", "theme_custom_css_settings": "CSS customizado", "theme_custom_css_settings_description": "Folhas de estilo em cascata permitem que o design do Immich seja personalizado.", "theme_settings": "ConfiguraçÃĩes de tema", "theme_settings_description": "Gerencie a personalizaÃ§ÃŖo da interface web do Immich", - "these_files_matched_by_checksum": "Esses arquivos sÃŖo correspondidos por seus checksum", "thumbnail_generation_job": "Gerar Miniaturas", "thumbnail_generation_job_description": "Gere miniaturas grandes, pequenas e desfocadas para cada arquivo, bem como miniaturas para cada pessoa", "transcoding_acceleration_api": "API de aceleraÃ§ÃŖo", @@ -299,10 +291,9 @@ "transcoding_encoding_options": "OpçÃĩes de codificaÃ§ÃŖo de vídeo", "transcoding_encoding_options_description": "Defina codecs, resoluÃ§ÃŖo, qualidade e outras opçÃĩes para vídeos codificados", "transcoding_hardware_acceleration": "AceleraÃ§ÃŖo de hardware", - "transcoding_hardware_acceleration_description": "Experimental; muito mais rÃĄpido, mas terÃĄ qualidade inferior com a mesma taxa de bits", + "transcoding_hardware_acceleration_description": "Experimental: mais rÃĄpido, mas talvez produza uma qualidade inferior com a mesma taxa de bits", "transcoding_hardware_decoding": "DecodificaÃ§ÃŖo de hardware", "transcoding_hardware_decoding_setting_description": "Habilita a aceleraÃ§ÃŖo de ponta a ponta, em vez de apenas acelerar a codificaÃ§ÃŖo. Pode nÃŖo funcionar em todos os vídeos.", - "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "MÃĄximo de quadros B", "transcoding_max_b_frames_description": "Valores mais altos melhoram a eficiÃĒncia da compactaÃ§ÃŖo, mas retardam a codificaÃ§ÃŖo. Pode nÃŖo ser compatível com aceleraÃ§ÃŖo de hardware em dispositivos mais antigos. 0 desativa os quadros B, enquanto -1 define esse valor automaticamente.", "transcoding_max_bitrate": "Taxa de bits mÃĄxima", @@ -340,14 +331,13 @@ "trash_number_of_days_description": "NÃēmero de dias para manter os arquivos na lixeira antes de deletar permanentemente", "trash_settings": "ConfiguraçÃĩes da Lixeira", "trash_settings_description": "Gerenciar configuraçÃĩes da lixeira", - "untracked_files": "Arquivos nÃŖo rastreados", - "untracked_files_description": "Esses arquivos nÃŖo sÃŖo rastreados pelo aplicativo. Eles podem ser o resultado de movimentos malsucedidos, carregamentos interrompidos ou deixados para trÃĄs devido a um erro", "user_cleanup_job": "Limpeza de usuÃĄrios", "user_delete_delay": "A conta e os arquivos de {user} serÃŖo programados para exclusÃŖo permanente em {delay, plural, one {# dia} other {# dias}}.", - "user_delete_delay_settings": "Excluir atraso", + "user_delete_delay_settings": "Remover atraso", "user_delete_delay_settings_description": "NÃēmero de dias apÃŗs a remoÃ§ÃŖo para excluir permanentemente a conta e os arquivos de um usuÃĄrio. A tarefa de exclusÃŖo de usuÃĄrio Ê executada à meia-noite para verificar usuÃĄrios que estÃŖo prontos para exclusÃŖo. As alteraçÃĩes nesta configuraÃ§ÃŖo serÃŖo avaliadas na prÃŗxima execuÃ§ÃŖo.", "user_delete_immediately": "A conta e os arquivos de {user} serÃŖo programados para exclusÃŖo permanente imediata.", "user_delete_immediately_checkbox": "Adicionar o usuÃĄrio e seus ativos na fila para serem deletados imediatamente", + "user_details": "Detalhes do UsuÃĄrio", "user_management": "Gerenciamento de usuÃĄrios", "user_password_has_been_reset": "A senha do usuÃĄrio foi redefinida:", "user_password_reset_description": "Forneça a senha temporÃĄria ao usuÃĄrio e informe que ele precisarÃĄ alterar a senha no prÃŗximo login.", @@ -399,9 +389,6 @@ "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_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}", @@ -417,6 +404,9 @@ "album_with_link_access": "Permitir que qualquer pessoa com o link veja as fotos e as pessoas neste ÃĄlbum.", "albums": "Álbuns", "albums_count": "{count, plural, one {{count, number} Álbum} other {{count, number} Álbuns}}", + "albums_default_sort_order": "Ordem padrÃŖo do ÃĄlbum", + "albums_default_sort_order_description": "Ordem padrÃŖo dos arquivos ao criar novos ÃĄlbuns.", + "albums_feature_description": "ColeçÃĩes de arquivos que podem ser compartilhados com outros usuÃĄrios.", "all": "Todos", "all_albums": "Todos os ÃĄlbuns", "all_people": "Todas as pessoas", @@ -447,7 +437,7 @@ "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_action_share_err_offline": "NÃŖo foi possível obter os arquivos indisponíveis, 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", @@ -459,6 +449,7 @@ "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_layout_sub_title": "Layout", "asset_list_settings_subtitle": "ConfiguraçÃĩes de layout da grade de fotos", "asset_list_settings_title": "Grade de Fotos", "asset_offline": "Arquivo indisponível", @@ -474,9 +465,12 @@ "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_cannot_be_added_to_album_count": "NÃŖo foi possível adicionar {count, plural, one {o arquivo} other {os arquivos}} ao ÃĄ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_downloaded_failed": "{count, plural, one {# arquivo transferido - {error} arquivo falhou} other {# arquivos transferidos - {error} arquivos falharam}}", + "assets_downloaded_successfully": "{count, plural, one {# arquivo transferido com sucesso} other {# arquivos transferidos com sucesso}}", "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}}", @@ -491,6 +485,7 @@ "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", + "autoplay_slideshow": "ApresentaÃ§ÃŖo de slides automÃĄtica", "back": "Voltar", "back_close_deselect": "Voltar, fechar ou desmarcar", "background_location_permission": "PermissÃŖo de localizaÃ§ÃŖo em segundo plano", @@ -515,6 +510,7 @@ "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_ok": "OK", "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", @@ -525,6 +521,7 @@ "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 Wi-Fi", + "backup_controller_page_backup": "Backup", "backup_controller_page_backup_selected": "Selecionado: ", "backup_controller_page_backup_sub": "Backup de fotos e vídeos", "backup_controller_page_created": "Criado em: {date}", @@ -532,6 +529,7 @@ "backup_controller_page_excluded": "Excluído: ", "backup_controller_page_failed": "Falhou ({count})", "backup_controller_page_filename": "Nome do arquivo: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "InformaçÃĩes de backup", "backup_controller_page_none_selected": "Nenhum selecionado", "backup_controller_page_remainder": "Restante", @@ -555,6 +553,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 por biometria ativada", + "biometric_locked_out": "Sua autenticaÃ§ÃŖo por biometria estÃĄ bloqueada", + "biometric_no_options": "NÃŖo hÃĄ opçÃĩes de biometria disponíveis", + "biometric_not_available": "A autenticaÃ§ÃŖo por biometria nÃŖo estÃĄ disponível neste dispositivo", "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.", "blurred_background": "Fundo desfocado", @@ -565,21 +567,17 @@ "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", @@ -592,7 +590,10 @@ "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", + "cast": "Transmitir", + "cast_description": "Configure destinos de transmissÃŖo disponíveis", "change_date": "Alterar data", + "change_description": "Alterar descriÃ§ÃŖo", "change_display_order": "Alterar ordem de exibiÃ§ÃŖo", "change_expiration_time": "Alterar o prazo de validade", "change_location": "Alterar localizaÃ§ÃŖo", @@ -605,9 +606,9 @@ "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 sua senha", "changed_visibility_successfully": "Visibilidade alterada com sucesso", - "check_all": "Verificar tudo", "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.", @@ -619,6 +620,7 @@ "clear_all_recent_searches": "Limpar todas as buscas 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 importado", @@ -641,14 +643,18 @@ "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_face": "Tem certeza que deseja remover a rosto 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 do grupo serÃŖo excluídos, exceto este arquivo. Tem certeza de que deseja continuar?", + "confirm_new_pin_code": "Confirmar novo cÃŗdigo PIN", "confirm_password": "Confirme a senha", + "confirm_tag_face": "Deseja marcar este rosto como {name}?", + "confirm_tag_face_unnamed": "Deseja marcar este rosto?", + "connected_device": "Dispositivo conectado", + "connected_to": "Conectado a", "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", @@ -686,15 +692,18 @@ "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", + "created_at": "Criado em", "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 Customizada", "custom_locale_description": "Formatar datas e nÃēmeros baseados na linguagem e regiÃŖo", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", + "darkTheme": "trocar para tema escuro", "date_after": "Data apÃŗs", "date_and_time": "Data e Hora", "date_before": "Data antes", @@ -719,7 +728,7 @@ "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_face": "Remover rosto", "delete_key": "Excluir chave", "delete_library": "Excluir biblioteca", "delete_link": "Excluir link", @@ -728,7 +737,7 @@ "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": "Excluir 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", @@ -740,7 +749,9 @@ "direction": "DireÃ§ÃŖo", "disabled": "Desativado", "disallow_edits": "NÃŖo permitir ediçÃĩes", + "discord": "Discord", "discover": "Descobrir", + "discovered_devices": "Dispositivos encontrados", "dismiss_all_errors": "Dispensar todos os erros", "dismiss_error": "Dispensar erro", "display_options": "OpçÃĩes de exibiÃ§ÃŖo", @@ -756,7 +767,6 @@ "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", @@ -780,6 +790,8 @@ "edit_avatar": "Editar foto 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", @@ -800,19 +812,24 @@ "editor_crop_tool_h2_aspect_ratios": "ProporçÃĩes", "editor_crop_tool_h2_rotation": "RotaÃ§ÃŖo", "email": "E-mail", + "email_notifications": "NotificaçÃĩes por 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", + "enable_biometric_auth_description": "Insira seu cÃŗdigo PIN para ativar a autenticaÃ§ÃŖo por biometria", "enabled": "Habilitado", "end_date": "Data final", "enqueued": "Na fila", "enter_wifi_name": "Digite o nome do Wi-Fi", + "enter_your_pin_code": "Insira seu cÃŗdigo PIN", + "enter_your_pin_code_subtitle": "Insira seu cÃŗdigo PIN para acessar a pasta com senha", "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_tag_face_bounding_box": "Erro ao marcar o rosto - nÃŖo foi possível localizar o rosto", "error_title": "Erro - Algo deu errado", "errors": { "cannot_navigate_next_asset": "NÃŖo foi possível navegar para o prÃŗximo arquivo", @@ -825,7 +842,6 @@ "cant_get_number_of_comments": "NÃŖo foi possível obter o nÃēmero de comentÃĄrios", "cant_search_people": "NÃŖo foi possível procurar pessoas", "cant_search_places": "NÃŖo foi possível procurar locais", - "cleared_jobs": "Tarefas eliminadas para: {job}", "error_adding_assets_to_album": "Erro ao adicionar arquivos para o ÃĄlbum", "error_adding_users_to_album": "Erro ao adicionar usuÃĄrios para o ÃĄlbum", "error_deleting_shared_user": "Erro ao deletar o usuÃĄrio compartilhado", @@ -834,7 +850,6 @@ "error_removing_assets_from_album": "Erro ao remover arquivos do ÃĄlbum, verifique o console para mais detalhes", "error_selecting_all_assets": "Erro ao selecionar todos os arquivos", "exclusion_pattern_already_exists": "Este padrÃŖo de exclusÃŖo jÃĄ existe.", - "failed_job_command": "Comando {command} falhou para a tarefa: {job}", "failed_to_create_album": "Falha ao criar o ÃĄlbum", "failed_to_create_shared_link": "Falha ao criar o link compartilhado", "failed_to_edit_shared_link": "Falha ao editar o link compartilhado", @@ -853,7 +868,6 @@ "paths_validation_failed": "A validaÃ§ÃŖo de {paths, plural, one {# caminho falhou} other {# caminhos falharam}}", "profile_picture_transparent_pixels": "As imagens de perfil nÃŖo podem ter pixels transparentes. Aumente o zoom e/ou mova a imagem.", "quota_higher_than_disk_size": "VocÃĒ definiu uma cota maior do que o tamanho do disco", - "repair_unable_to_check_items": "NÃŖo foi possível verificar {count, select, one {um item} other {alguns itens}}", "unable_to_add_album_users": "NÃŖo foi possível adicionar usuÃĄrios ao ÃĄlbum", "unable_to_add_assets_to_shared_link": "NÃŖo Ê possível adicionar arquivos ao link compartilhado", "unable_to_add_comment": "NÃŖo foi possível adicionar o comentÃĄrio", @@ -865,13 +879,13 @@ "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 usuÃĄrio 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 alterar o favorito para o arquivo", "unable_to_change_location": "NÃŖo foi possível alterar a localizaÃ§ÃŖo", "unable_to_change_password": "NÃŖo foi possível alterar a senha", "unable_to_change_visibility": "NÃŖo foi possível alterar a visibilidade de {count, plural, one {# pessoa} other {# pessoas}}", "unable_to_complete_oauth_login": "NÃŖo foi possível concluir o login OAuth", "unable_to_connect": "NÃŖo foi possível conectar", - "unable_to_connect_to_server": "NÃŖo foi possível se conectar ao servidor", "unable_to_copy_to_clipboard": "NÃŖo Ê possível copiar para a ÃĄrea de transferÃĒncia, certifique-se que estÃĄ acessando a pagina atravÊs de https", "unable_to_create_admin_account": "NÃŖo foi possível criar uma conta de administrador", "unable_to_create_api_key": "NÃŖo foi possível criar uma nova Chave de API", @@ -895,10 +909,6 @@ "unable_to_hide_person": "NÃŖo foi possível esconder a pessoa", "unable_to_link_motion_video": "NÃŖo foi possível relacionar ao video animado", "unable_to_link_oauth_account": "NÃŖo foi possível associar a conta OAuth", - "unable_to_load_album": "NÃŖo foi possível carregar o ÃĄlbum", - "unable_to_load_asset_activity": "NÃŖo foi possível carregar as atividades do arquivo", - "unable_to_load_items": "NÃŖo foi possível carregar os items", - "unable_to_load_liked_status": "NÃŖo foi possível carregar os status de gostei", "unable_to_log_out_all_devices": "NÃŖo foi possível sair de todos os dispositivos", "unable_to_log_out_device": "NÃŖo foi possível sair do dispositivo", "unable_to_login_with_oauth": "NÃŖo foi possível fazer login com OAuth", @@ -909,12 +919,11 @@ "unable_to_remove_album_users": "NÃŖo foi possível remover usuÃĄrios do ÃĄlbum", "unable_to_remove_api_key": "NÃŖo foi possível a Chave de API", "unable_to_remove_assets_from_shared_link": "NÃŖo foi possível remover arquivos do link compartilhado", - "unable_to_remove_deleted_assets": "NÃŖo foi possível remover arquivos offline", "unable_to_remove_library": "NÃŖo foi possível remover a biblioteca", "unable_to_remove_partner": "NÃŖo foi possível remover parceiro", "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 resetar a senha", + "unable_to_reset_pin_code": "NÃŖo foi possível redefinir o cÃŗdigo PIN", "unable_to_resolve_duplicate": "NÃŖo foi possível resolver a duplicidade", "unable_to_restore_assets": "NÃŖo foi possível restaurar", "unable_to_restore_trash": "NÃŖo foi possível restaurar itens da lixeira", @@ -942,12 +951,12 @@ "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}", @@ -956,6 +965,7 @@ "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!", + "experimental_settings_title": "Experimental", "expire_after": "Expira depois", "expired": "Expirou", "expires_date": "Expira em {date}", @@ -970,6 +980,7 @@ "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_authenticate": "NÃŖo foi possível autenticar", "failed_to_load_assets": "Falha ao carregar arquivos", "failed_to_load_folder": "Falha ao carregar a pasta", "favorite": "Favorito", @@ -993,6 +1004,8 @@ "folders": "Pastas", "folders_feature_description": "Navegar pelas pastas das fotos e vídeos no sistema de arquivos", "forward": "Para frente", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Esta funcionalidade carrega recursos externos do Google para funcionar.", "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", @@ -1035,9 +1048,13 @@ "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_locked_error_local": "NÃŖo Ê possível mover arquivos locais para a pasta com senha, ignorando", + "home_page_locked_error_partner": "NÃŖo Ê possível mover arquivos do parceiro para a pasta com senha, Ignorando", "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", + "host": "Servidor", "hour": "Hora", + "id": "ID", "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", @@ -1077,6 +1094,12 @@ "invalid_date_format": "Formato de data invÃĄlido", "invite_people": "Convidar Pessoas", "invite_to_album": "Convidar para o ÃĄlbum", + "ios_debug_info_fetch_ran_at": "Coleta ocorreu {dateTime}", + "ios_debug_info_last_sync_at": "Ultima sincronizaÃ§ÃŖo {dateTime}", + "ios_debug_info_no_processes_queued": "Nenhum processo de segundo plano enfileirado", + "ios_debug_info_no_sync_yet": "Nenhum serviço de sicronizaÃ§ÃŖo em segundo plano foi executado ainda", + "ios_debug_info_processes_queued": "{count, plural, one {{count} processo em segundo plano enfileirado} other {{count} processos em segundo plano enfileirados}}", + "ios_debug_info_processing_ran_at": "processamento executado em {dateTime}", "items_count": "{count, plural, one {# item} other {# itens}}", "jobs": "Tarefas", "keep": "Manter", @@ -1085,9 +1108,13 @@ "kept_this_deleted_others": "Este foi mantido e {count, plural, one {# arquivo foi excluído} other {# arquivos foram excluídos}}", "keyboard_shortcuts": "Atalhos do teclado", "language": "Idioma", + "language_no_results_subtitle": "tente refinar seu termo de pesquisa", + "language_no_results_title": "nenhum idioma encontrado", + "language_search_hint": "Procure idiomas...", "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", @@ -1109,6 +1136,7 @@ "list": "Lista", "loading": "Carregando", "loading_search_results_failed": "Falha ao carregar os resultados da pesquisa", + "local_asset_cast_failed": "NÃŖo Ê possível transmitir um arquivo que nÃŖo foi enviado ao servidor", "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", @@ -1118,14 +1146,18 @@ "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": "Sair de todos dispositivos", + "logged_in_as": "UsuÃĄrio atual: {user}", "logged_out_all_devices": "Saiu de todos os dispositivos", "logged_out_device": "Dispositivo desconectado", "login": "Iniciar sessÃŖo", "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_email_hint": "seu@email.com", "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://", @@ -1137,6 +1169,7 @@ "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_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_password_hint": "senha", "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.", @@ -1145,10 +1178,11 @@ "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.", - "main_branch_warning": "VocÃĒ estÃĄ utilizando a versÃŖo de desenvolvimento. É altamente recomendado que utilize a versÃŖo estÃĄvel!", + "main_branch_warning": "VocÃĒ estÃĄ utilizando uma versÃŖo de desenvolvimento. É fortemente recomendado que utilize uma versÃŖo estÃĄvel!", "main_menu": "Menu Principal", "make": "Marca", "manage_shared_links": "Gerir links partilhados", @@ -1196,6 +1230,7 @@ "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", @@ -1207,7 +1242,12 @@ "missing": "Faltando", "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 com senha", + "move_to_locked_folder": "Mover para a pasta com senha", + "move_to_locked_folder_confirmation": "Estas fotos e vídeos serÃŖo removidos de todos os ÃĄlbuns e somente poderÃŖo ser visualizados de dentro da pasta com senha", "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", @@ -1224,6 +1264,8 @@ "new_api_key": "Nova Chave de API", "new_password": "Nova senha", "new_person": "Nova Pessoa", + "new_pin_code": "Novo cÃŗdigo PIN", + "new_pin_code_subtitle": "Esta Ê a primeira vez que estÃĄ acessando a pasta com senha. Crie um cÃŗdigo PIN para acessar esta pÃĄgina de forma segura", "new_user_created": "Novo usuÃĄrio criado", "new_version_available": "NOVA VERSÃO DISPONÍVEL", "newest_first": "Mais recente primeiro", @@ -1236,11 +1278,13 @@ "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_cast_devices_found": "Nenhum dispositivo encontrado", "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_locked_photos_message": "Fotos e vídeos na pasta com senha sÃŖo ocultos e nÃŖo serÃŖo exibidos enquanto explora ou pesquisa na biblioteca.", "no_name": "Sem Nome", "no_notifications": "Nenhuma notificaÃ§ÃŖo", "no_people_found": "Nenhuma pessoa encontrada", @@ -1252,6 +1296,7 @@ "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", + "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": "Conceda permissÃŖo para ativar notificaçÃĩes.", "notification_permission_list_tile_enable_button": "Ativar notificaçÃĩes", @@ -1259,16 +1304,20 @@ "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_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.", + "offline": "Desconectado", + "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_locale_description": "Selecione sua linguagem preferida. VocÃĒ pode alterar isso mais tarde em suas configuraçÃĩes.", + "onboarding_privacy_description": "Os seguintes recursos opcionais dependem de serviços externos e podem ser desabilitados a qualquer momento nas configuraçÃĩes.", + "onboarding_server_welcome_description": "Vamos colocar sua instÃĸncia no ar com algumas configuraçÃĩes usuais.", "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_user_welcome_description": "Vamos começar!", "onboarding_welcome_user": "Bem-vindo, {user}", + "online": "Conectado", "only_favorites": "Somente favoritos", "open": "Abrir", "open_in_map_view": "Mostrar no mapa", @@ -1277,6 +1326,7 @@ "options": "OpçÃĩes", "or": "ou", "organize_your_library": "Organize sua biblioteca", + "original": "original", "other": "Outro", "other_devices": "Outros dispositivos", "other_variables": "Outras variÃĄveis", @@ -1322,6 +1372,8 @@ "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": "PermissÃŖo", + "permission_empty": "Sua permissÃŖo nÃŖo deveria ser vazia", "permission_onboarding_back": "Voltar", "permission_onboarding_continue_anyway": "Continuar assim mesmo", "permission_onboarding_get_started": "Começar", @@ -1339,6 +1391,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 redefinido com sucesso", + "pin_code_setup_successfully": "CÃŗdigo PIN criado 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}}", @@ -1346,6 +1402,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 acessar", "port": "Porta", "preferences_settings_subtitle": "Gerenciar as preferÃĒncias do aplicativo", "preferences_settings_title": "PreferÃĒncias", @@ -1353,12 +1410,18 @@ "preview": "PrÊ-visualizar", "previous": "Anterior", "previous_memory": "MemÃŗria anterior", - "previous_or_next_photo": "Foto anterior ou prÃŗxima", + "previous_or_next_day": "Dia seguinte/anterior", + "previous_or_next_month": "MÃĒs seguinte/anterior", + "previous_or_next_photo": "Foto seguinte/anterior", + "previous_or_next_year": "Ano seguinte/anterior", "primary": "PrimÃĄrio", "privacy": "Privacidade", + "profile": "Perfil", + "profile_drawer_app_logs": "Logs", "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_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 do perfil de {user}", @@ -1430,12 +1493,15 @@ "remove_assets_shared_link_confirmation": "Tem certeza de que deseja remover {count, plural, one {# arquivo} other {# arquivos}} desse link compartilhado?", "remove_assets_title": "Remover arquivos?", "remove_custom_date_range": "Remover intervalo de datas personalizado", - "remove_deleted_assets": "Remover arquivos offline", + "remove_deleted_assets": "Remover arquivos excluídos", "remove_from_album": "Remover do ÃĄlbum", "remove_from_favorites": "Remover dos favoritos", + "remove_from_locked_folder": "Remover da pasta com senha", + "remove_from_locked_folder_confirmation": "Tem a certeza de que deseja mover estes arquivos para fora da pasta com senha? Eles ficarÃŖo visíveis na biblioteca principal.", "remove_from_shared_link": "Remover do link compartilhado", "remove_memory": "Remover memÃŗria", "remove_photo_from_memory": "Remover foto desta memÃŗria", + "remove_tag": "Remover marcador", "remove_url": "Remover URL", "remove_user": "Remover usuÃĄrio", "removed_api_key": "Removido a Chave de API: {name}", @@ -1456,6 +1522,7 @@ "reset": "Resetar", "reset_password": "Resetar senha", "reset_people_visibility": "Resetar pessoas ocultas", + "reset_pin_code": "Redefinir cÃŗdigo PIN", "reset_to_default": "Redefinir para a configuraÃ§ÃŖo padrÃŖo", "resolve_duplicates": "Resolver duplicatas", "resolved_all_duplicates": "Todas duplicidades resolvidas", @@ -1467,6 +1534,7 @@ "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", @@ -1516,6 +1584,7 @@ "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_selfies": "Selfies", "search_page_things": "Coisas", "search_page_view_all_button": "Ver tudo", "search_page_your_activity": "Sua atividade", @@ -1539,6 +1608,7 @@ "select_album_cover": "Escolher capa do ÃĄlbum", "select_all": "Selecionar todos", "select_all_duplicates": "Selecionar todas as duplicatas", + "select_all_in": "Selecionar tudo em {group}", "select_avatar_color": "Selecionar cor do avatar", "select_face": "Selecionar rosto", "select_featured_photo": "Selecionar foto principal", @@ -1559,6 +1629,7 @@ "server_info_box_server_url": "URL do servidor", "server_offline": "Servidor Indisponível", "server_online": "Servidor Disponível", + "server_privacy": "Privacidade do servidor", "server_stats": "Status do servidor", "server_version": "VersÃŖo do servidor", "set": "Definir", @@ -1568,6 +1639,7 @@ "set_date_of_birth": "Definir data de nascimento", "set_profile_picture": "Definir foto de perfil", "set_slideshow_to_fullscreen": "ApresentaÃ§ÃŖo em tela cheia", + "set_stack_primary_asset": "Selecionar como arquivo principal", "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", @@ -1576,7 +1648,6 @@ "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", @@ -1594,10 +1665,12 @@ "settings": "ConfiguraçÃĩes", "settings_require_restart": "Reinicie o Immich para aplicar esta configuraÃ§ÃŖo", "settings_saved": "ConfiguraçÃĩes salvas", + "setup_pin_code": "Criar um cÃŗdigo PIN", "share": "Compartilhar", "share_add_photos": "Adicionar fotos", "share_assets_selected": "{count} selecionado", "share_dialog_preparing": "Preparando...", + "share_link": "Compartilhar Link", "shared": "Compartilhado", "shared_album_activities_input_disable": "ComentÃĄrios desativados", "shared_album_activity_remove_content": "Deseja excluir esta atividade?", @@ -1637,6 +1710,7 @@ "shared_link_expires_second": "Expira em {count} segundo", "shared_link_expires_seconds": "Expira em {count} segundos", "shared_link_individual_shared": "Compartilhado Individualmente", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gerenciar links compartilhados", "shared_link_options": "OpçÃĩes de link compartilhado", "shared_links": "Links compartilhados", @@ -1698,15 +1772,19 @@ "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", + "stacktrace": "Stacktrace", "start": "Início", "start_date": "Data inicial", "state": "Estado", + "status": "Status", + "stop_casting": "Parar transmissÃŖo", "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.", "stop_sharing_photos_with_user": "Parar de compartilhar suas fotos com este usuÃĄrio", "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", @@ -1757,7 +1835,7 @@ "to_parent": "Voltar para nível acima", "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", @@ -1774,8 +1852,11 @@ "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", + "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 criar o cÃŗdigo PIN", "unarchive": "Desarquivar", "unarchived_count": "{count, plural, one {# Desarquivado} other {# Desarquivados}}", + "undo": "Desfazer", "unfavorite": "Remover favorito", "unhide_person": "Exibir pessoa", "unknown": "Desconhecido", @@ -1792,11 +1873,11 @@ "unsaved_change": "AlteraÃ§ÃŖo nÃŖo salva", "unselect_all": "Desselecionar todos", "unselect_all_duplicates": "Desselecionar todas as duplicatas", + "unselect_all_in": "Remover seleÃ§ÃŖo de {group}", "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_at": "Atualizado em", "updated_password": "Senha atualizada", "upload": "Carregar", "upload_concurrency": "Envios simultÃĸneos", @@ -1811,12 +1892,18 @@ "upload_success": "Carregado com sucesso, atualize a pÃĄgina para ver os novos arquivos.", "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", + "url": "URL", "usage": "Uso", + "use_biometric": "Usar biometria", "use_current_connection": "usar conexÃŖo atual", "use_custom_date_range": "Usar intervalo de datas personalizado", "user": "UsuÃĄrio", + "user_has_been_deleted": "Este usuÃĄrio foi excluído.", "user_id": "ID do usuÃĄrio", "user_liked": "{user} curtiu {type, select, photo {esta foto} video {este vídeo} asset {este arquivo} other {isto}}", + "user_pin_code_settings": "CÃŗdigo PIN", + "user_pin_code_settings_description": "Gerenciar o seu cÃŗdigo PIN", + "user_privacy": "Privacidade do usuÃĄrio", "user_purchase_settings": "Comprar", "user_purchase_settings_description": "Gerenciar sua compra", "user_role_set": "Definir {user} como {role}", @@ -1832,11 +1919,6 @@ "version": "VersÃŖo", "version_announcement_closing": "De seu amigo, Alex", "version_announcement_message": "OlÃĄ! Uma nova versÃŖo do Immich estÃĄ disponível. Para evitar configuraçÃĩes incorretas, leia com calma a pÃĄgina de notas da versÃŖo e verifique se Ê necessÃĄrio alterar alguma configuraÃ§ÃŖo, principalmente se vocÃĒ usa o WatchTower ou qualquer outro mecanismo que faça atualizaçÃĩes automÃĄticas do Immich.", - "version_announcement_overlay_release_notes": "notas de lançamento", - "version_announcement_overlay_text_1": "Oi amigo, hÃĄ um novo lançamento de", - "version_announcement_overlay_text_2": "reserve um tempo para visitar o ", - "version_announcement_overlay_text_3": " e verifique se a configuraÃ§ÃŖo do docker-compose e do .env estÃĄ atualizada para evitar configuraçÃĩes incorretas, especialmente se vocÃĒ usar o WatchTower ou qualquer mecanismo que lide com a atualizaÃ§ÃŖo automÃĄtica do aplicativo do servidor.", - "version_announcement_overlay_title": "Nova versÃŖo do servidor disponível 🎉", "version_history": "HistÃŗrico de versÃĩes", "version_history_item": "VersÃŖo {version} instalada em {date}", "video": "Vídeo", @@ -1856,6 +1938,7 @@ "view_previous_asset": "Ver arquivo anterior", "view_qr_code": "Ver QR Code", "view_stack": "Ver grupo", + "view_user": "Visualizar usuÃĄrio", "viewer_remove_from_stack": "Remover do grupo", "viewer_stack_use_as_main_asset": "Usar como foto principal", "viewer_unstack": "Desagrupar", @@ -1866,6 +1949,7 @@ "welcome": "Bem-vindo(a)", "welcome_to_immich": "Bem-vindo(a) ao Immich", "wifi_name": "Nome do Wi-Fi", + "wrong_pin_code": "CÃŗdigo PIN incorreto", "year": "Ano", "years_ago": "{years, plural, one {# ano} other {# anos}} atrÃĄs", "yes": "Sim", diff --git a/i18n/ro.json b/i18n/ro.json index 9f1bc03cb2..0b2ef61a36 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -11,12 +11,12 @@ "activity_changed": "Activitatea este {enabled, select, true {activată} other {dezactivată}}", "add": "Adaugă", "add_a_description": "Adaugă o descriere", - "add_a_location": "Adaugă locație", + "add_a_location": "Adaugă o locație", "add_a_name": "Adaugă un nume", "add_a_title": "Adaugă un titlu", "add_exclusion_pattern": "Adăugă un model de excludere", "add_import_path": "Adaugă o cale de import", - "add_location": "Adaugă o locație", + "add_location": "Adaugă locație", "add_more_users": "Adaugă mai mulți utilizatori", "add_partner": "Adaugă partener", "add_path": "Adaugă o cale", @@ -43,8 +43,6 @@ "backup_keep_last_amount": "Cantitatea de copii de rezervă anterioare de păstrat", "backup_settings": "Setări Copii de Rezervă", "backup_settings_description": "Gestionați setările de salvare a bazei de date", - "check_all": "Bifează Toate", - "cleanup": "Curăță", "cleared_jobs": "Activități eliminate pentru: {job}", "config_set_by_file": "Configurația este setată ÃŽn prezent de un fișier de configurare", "confirm_delete_library": "Sigur doriți să ștergeți biblioteca {library}?", @@ -59,14 +57,12 @@ "disable_login": "Dezactivați autentificarea", "duplicate_detection_job_description": "Rulați ÃŽnvățarea automată pe materiale pentru a detecta imagini similare. Se bazează pe Căutare Inteligentă", "exclusion_pattern_description": "Modelele de excludere vă permit să ignorați fișierele și folderele atunci cÃĸnd vă scanați biblioteca. Acest lucru este util dacă aveți foldere care conțin fișiere pe care nu doriți să le importați, cum ar fi fișierele RAW.", - "external_library_created_at": "Bibliotecă externă (creată pe {date})", "external_library_management": "Managementul Bibliotecii Externe", "face_detection": "Detecție facială", "face_detection_description": "Detectează fețele din fișiere folosind ÃŽnvățare automată. Pentru videoclipuri, este luată ÃŽn considerare doar miniatura. „ReÃŽnprospătează” (re)procesează toate fișierele. „Resetează” adaugă ÃŽn coadă fișierele care nu au fost ÃŽncă procesate. Fețele detectate vor fi puse ÃŽn coadă pentru recunoașterea facială după finalizarea detectării feței, grupÃĸndu-le ÃŽn persoane existente sau noi.", "facial_recognition_job_description": "Grupați fețele detectate ÃŽn persoane. Acest pas rulează după ce Detectarea Feței este finalizată. „Resetează” (re)grupează toate fețele. „Lipsă” adaugă ÃŽn coadă fețe care nu au o persoană desemnată.", "failed_job_command": "Comanda {command} a eșuat pentru jobul: {job}", "force_delete_user_warning": "AVERTISMENT: Acest lucru va elimina imediat utilizatorul și toate activele sale. Acest lucru nu poate fi anulat și fișierele nu pot fi recuperate.", - "forcing_refresh_library_files": "Forțarea reÃŽmprospătării tuturor fișierelor din bibliotecă", "image_format": "Formatează", "image_format_description": "WebP produce fișiere mai mici decÃĸt JPEG, dar este mai lent de codat.", "image_fullsize_description": "Imaginea ÃŽntreagă fără metadata, mărită", @@ -194,7 +190,7 @@ "oauth_enable_description": "Autentifică-te cu OAuth", "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_mobile_redirect_uri_override_description": "Activați atunci cÃĸnd furnizorul OAuth nu permite un URI mobil, precum ''{callback}''", "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.", @@ -204,8 +200,6 @@ "oauth_storage_quota_claim_description": "Setează automat spațiul de stocare al utilizatorului la valoarea acestei cereri.", "oauth_storage_quota_default": "Cota implicită a spațiului de stocare (GiB)", "oauth_storage_quota_default_description": "Spațiul ÃŽn GiB ce urmează a fi utilizat atunci cÃĸnd nu este furnizată nicio solicitare (introduceți 0 pentru spațiu nelimitat).", - "offline_paths": "Căi Offline", - "offline_paths_description": "Acestea pot fi rezultate ÃŽn urma ștergerii manuale a fișierelor ce nu fac parte dintr-o bibliotecĮŽ externĮŽ.", "password_enable_description": "Autentificare cu email și parolĮŽ", "password_settings": "Autentificare cu ParolĮŽ", "password_settings_description": "GestioneazĮŽ setĮŽrile de autentificare cu parola", @@ -215,9 +209,6 @@ "refreshing_all_libraries": "Bibliotecile sunt ÃŽn curs de reÃŽmprospĮŽtare", "registration": "Înregistrare Administratori", "registration_description": "Deoarece sunteți primul utilizator de pe sistem, veți fi desemnat ca administrator și sunteți responsabil pentru sarcinile administrative, iar utilizatorii suplimentari vor fi creați de dumneavoastră.", - "repair_all": "ReparĮŽ Toate", - "repair_matched_items": "S-au potrivit {count, plural, one {# element} other {# elemente}}", - "repaired_items": "S-au reparat {count, plural, one {# element} other {# elemente}}", "require_password_change_on_login": "ObligĮŽ utilizatorul sĮŽ ÃŽČ™i schimbe parola la prima autentificare", "reset_settings_to_default": "ReseteazĮŽ setĮŽrile la valorile implicite", "reset_settings_to_recent_saved": "ReseteazĮŽ setĮŽrile la valorile salvate recent", @@ -246,7 +237,6 @@ "storage_template_migration_info": "Șablonul de stocare va converti extensiile in litere mici. Modificările șablonului se vor aplica doar materialelor noi. Pentru a aplica retroactiv șablonul la materialele ÃŽncărcate anterior, rulați {job}.", "storage_template_migration_job": "Sarcină Migrare Șablon Stocare", "storage_template_more_details": "Pentru mai multe detalii despre aceasta caracteristică, accesați Șablon stocare si implicațiile", - "storage_template_onboarding_description": "Atunci cÃĸnd este activată, această caracteristică va organiza automat fișierele pe baza unui șablon definit de utilizator. Din cauza unor probleme de stabilitate, această caracteristică este dezactivată implicit. Pentru mai multe informații, te rog să consulți documentația.", "storage_template_path_length": "Limita de lungime pentru calea aproximativă: {length, number}/{limit, number}", "storage_template_settings": "Șablon Stocare", "storage_template_settings_description": "Gestionează structura folderelor și numele fișierelor pentru elementele ÃŽncărcate", @@ -258,7 +248,6 @@ "template_email_invite_album": "Șablon de Album de Invitație", "template_email_preview": "Previzualizare", "template_email_settings": "Șabloane de E-mail", - "template_email_settings_description": "Gestionați șabloanele personalizate de notificare prin e-mail", "template_email_update_album": "Actualizați Șablonul de Album", "template_email_welcome": "Șablon de e-mail de bun venit", "template_settings": "Șabloane de Notificare", @@ -267,7 +256,6 @@ "theme_custom_css_settings_description": "Foile de stil ÃŽn cascadă (CSS) permit personalizarea designului Immich.", "theme_settings": "Setări Temă", "theme_settings_description": "Gestionează personalizarea interfeței web Immich", - "these_files_matched_by_checksum": "Aceste fișiere sunt comparate folosind sumele de control", "thumbnail_generation_job": "Generare Miniaturi", "thumbnail_generation_job_description": "Generează miniaturi mari, mici și estompate pentru fiecare resursă, precum și miniaturi pentru fiecare persoană", "transcoding_acceleration_api": "API de accelerare", @@ -298,7 +286,6 @@ "transcoding_hardware_acceleration_description": "Experimental; mult mai rapid, dar va avea o calitate mai scăzută la același bitrate", "transcoding_hardware_decoding": "Decodare hardware", "transcoding_hardware_decoding_setting_description": "Se aplică doar pentru NVENC, QSV și RKMPP. Activează accelerarea completă ÃŽn loc de doar accelerarea codificării. S-ar putea să nu funcționeze pentru toate videoclipurile.", - "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "Număr maxim de cadre B", "transcoding_max_b_frames_description": "Valorile mai mari ÃŽmbunătățesc eficiența compresiei, dar ÃŽncetinesc codarea. Este posibil să nu fie compatibile cu accelerarea hardware pe dispozitivele mai vechi. 0 dezactivează cadrele B, ÃŽn timp ce -1 setează această valoare automat.", "transcoding_max_bitrate": "Rata de biți maximă", @@ -336,8 +323,6 @@ "trash_number_of_days_description": "NumĮŽr de zile pentru pĮŽstrarea fișierelor ÃŽn coșul de gunoi pÃĸnĮŽ la ștergerea permanentĮŽ", "trash_settings": "SetĮŽri Coș de Gunoi", "trash_settings_description": "GestioneazĮŽ setĮŽrile coșului de gunoi", - "untracked_files": "Fișiere Neurmărite", - "untracked_files_description": "Aceste fișiere nu sunt urmărite de aplicație. Ele pot fi rezultatul unor mutări eșuate, ÃŽncărcări ÃŽntrerupte sau pot rămÃĸne ÃŽn urmă din cauza unei erori", "user_cleanup_job": "Curățare utilizator", "user_delete_delay": "Contul și resursele utilizatorului {user} vor fi programate pentru ștergere permanentă ÃŽn {delay, plural, one {# zi} other {# zile}}.", "user_delete_delay_settings": "ÎntÃĸrziere la ștergere", @@ -393,10 +378,6 @@ "album_remove_user": "Eliminare utilizator?", "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": "{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}", @@ -541,21 +522,17 @@ "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 ({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 ({count})", - "cache_settings_image_cache_size": "Mărime cache imagine ({count} resurse)", "cache_settings_statistics_album": "Miniaturi pentru librării", - "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 ({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", @@ -581,7 +558,6 @@ "change_password_form_reenter_new_password": "Reintrodu noua parolă", "change_your_password": "Schimbă-ți parola", "changed_visibility_successfully": "Schimbare vizibilitate cu succes", - "check_all": "Selectați Tot", "check_logs": "Verificați Jurnale", "choose_matching_people_to_merge": "Alegeți persoanele care se potrivesc pentru a le fuziona", "city": "Oraș", @@ -610,7 +586,6 @@ "confirm_password": "Confirmați parola", "contain": "Încadrează", "continue": "Continuați", - "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", @@ -761,7 +736,6 @@ "cant_get_number_of_comments": "Nu pot obține numărul de comentarii", "cant_search_people": "Nu pot căuta oameni", "cant_search_places": "Nu se pot căuta locații", - "cleared_jobs": "Sarcini terminate pentru: {job}", "error_adding_assets_to_album": "Eroare la adăugarea resurselor la album", "error_adding_users_to_album": "Eroare la adăugarea utilizatorilor la album", "error_deleting_shared_user": "Eroare la ștergerea utilizatorului partajat", @@ -770,7 +744,6 @@ "error_removing_assets_from_album": "Eroare la eliminarea resurselor din album, verificați consola pentru mai multe detalii", "error_selecting_all_assets": "Eroare la selectarea tuturor resurselor", "exclusion_pattern_already_exists": "Acest model de excludere există deja.", - "failed_job_command": "Comanda {command} a eșuat pentru sarcina: {job}", "failed_to_create_album": "A eșuat crearea albumului", "failed_to_create_shared_link": "A eșuat crearea legăturii partajate", "failed_to_edit_shared_link": "A eșuat editarea legăturii partajate", @@ -787,7 +760,6 @@ "paths_validation_failed": "{paths, plural, one {# cale} other {# căi}} nu a trecut validarea", "profile_picture_transparent_pixels": "Pozele de profil nu pot avea pixeli transparenți. Te rugăm să mărești imaginea și/sau să o muți.", "quota_higher_than_disk_size": "Ați stabilit o valoare a spațiului de stocare mai mare decÃĸt dimensiunea discului", - "repair_unable_to_check_items": "Imposibil de verificat {count, select, one {element} other {elemente}}", "unable_to_add_album_users": "Imposibil de adăugat utilizatori ÃŽn album", "unable_to_add_assets_to_shared_link": "Imposibil de adăugat resurse la link-ul partajat", "unable_to_add_comment": "Imposibil de adăugat comentariu", @@ -805,7 +777,6 @@ "unable_to_change_visibility": "Nu se poate schimba vizibilitatea pentru {count, plural, one {# persoană} other {# persoane}}", "unable_to_complete_oauth_login": "Nu s-a realizat logarea prin OAuth", "unable_to_connect": "Nu se poate conecta", - "unable_to_connect_to_server": "Nu se poate conecta la server", "unable_to_copy_to_clipboard": "Nu poate fi copiat, asigură-te că accesezi pagina prin https", "unable_to_create_admin_account": "Nu se poate crea contul de administrator", "unable_to_create_api_key": "Nu se poate crea o nouă cheie API", @@ -829,10 +800,6 @@ "unable_to_hide_person": "Nu se poate ascunde persoana", "unable_to_link_motion_video": "Imposibil de conectat videoclipul ÃŽn mișcare", "unable_to_link_oauth_account": "Nu se poate conecta contul OAuth", - "unable_to_load_album": "Nu se poate ÃŽncărca albumul", - "unable_to_load_asset_activity": "Nu se poate ÃŽncărca activitatea cu materiale", - "unable_to_load_items": "Nu se pot ÃŽncărca articole", - "unable_to_load_liked_status": "Nu se poate ÃŽncărca starea de apreciat", "unable_to_log_out_all_devices": "Nu se pot deconecta toate dispozitivele", "unable_to_log_out_device": "Nu se poate deconecta dispozitivul", "unable_to_login_with_oauth": "Nu se poate autentifica cu OAuth", @@ -843,11 +810,9 @@ "unable_to_remove_album_users": "Nu se pot șterge userii din album", "unable_to_remove_api_key": "Nu se poate șterge cheia API", "unable_to_remove_assets_from_shared_link": "Nu se pot elimina resursele din linkul partajat", - "unable_to_remove_deleted_assets": "Nu se pot șterge fișierele offline", "unable_to_remove_library": "Nu se poate șterge biblioteca", "unable_to_remove_partner": "Imposibil de eliminat partenerul", "unable_to_remove_reaction": "Nu se poate elimina reacția", - "unable_to_repair_items": "Imposibil de reparat elementele", "unable_to_reset_password": "Imposibil de resetat parola", "unable_to_resolve_duplicate": "Nu se poate rezolva duplicatul", "unable_to_restore_assets": "Nu se pot restaura resursele", @@ -985,6 +950,7 @@ }, "invite_people": "Invitați Persoane", "invite_to_album": "Invitați ÃŽn album", + "ios_debug_info_processing_ran_at": "Procesarea a rulat {dateTime}", "items_count": "{count, plural, one {# element} other{# elemente}}", "jobs": "Sarcini", "keep": "Păstrați", @@ -1152,14 +1118,11 @@ "notifications": "Notificări", "notifications_setting_description": "Gestionați notificările", "official_immich_resources": "Resurse Oficiale Immich", - "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", "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}", "only_favorites": "Doar favorite", "open": "Deschide", @@ -1240,7 +1203,7 @@ "preview": "Previzualizare", "previous": "Anterior", "previous_memory": "Memoria anterioară", - "previous_or_next_photo": "Poza anterioară sau următoare", + "previous_or_next_photo": "Fotografie ÃŽnainte/ÃŽnapoi", "primary": "Primar", "privacy": "Confidențialitate", "profile_drawer_app_logs": "Log-uri", @@ -1598,7 +1561,6 @@ "to_parent": "Du-te la părinte", "to_trash": "Coș de gunoi", "toggle_settings": "Activați setările", - "toggle_theme": "Activați tema ÃŽntunecată", "total_usage": "Utilizare totală", "trash": "Coș de gunoi", "trash_all": "Ștergeți Tot", @@ -1634,8 +1596,6 @@ "unselect_all_duplicates": "Deselectați toate duplicatele", "unstack": "Dezasamblați", "unstacked_assets_count": "Nestivuit {count, plural, one {# resursă} other {# resurse}}", - "untracked_files": "Fișiere neurmărite", - "untracked_files_decription": "Aceste fișiere nu sunt urmărite de aplicație. Acestea pot fi rezultatele unor mișcări eșuate, ÃŽncărcări ÃŽntrerupte sau rămase din cauza unei erori", "up_next": "Mai departe", "updated_password": "Parolă actualizată", "upload": "Încărcați", @@ -1668,11 +1628,6 @@ "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_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 fd846b088d..5810a31053 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -6,7 +6,7 @@ "action": "ДĐĩĐšŅŅ‚Đ˛Đ¸Đĩ", "action_common_update": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ", "actions": "ДĐĩĐšŅŅ‚Đ˛Đ¸Ņ", - "active": "В ĐžĐąŅ€Đ°ĐąĐžŅ‚ĐēĐĩ", + "active": "Đ’Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ", "activity": "АĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚ŅŒ", "activity_changed": "АĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚ŅŒ {enabled, select, true {вĐēĐģŅŽŅ‡ĐĩĐŊа} other {ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊа}}", "add": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ", @@ -22,18 +22,19 @@ "add_partner": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ°", "add_path": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŋŅƒŅ‚ŅŒ", "add_photos": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ Ņ„ĐžŅ‚Đž", + "add_tag": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ Ņ‚ĐĩĐŗ", "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} в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", "admin": { - "add_exclusion_pattern_description": "Đ”ĐžĐąĐ°Đ˛ŅŒŅ‚Đĩ ŅˆĐ°ĐąĐģĐžĐŊŅ‹ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊиК. ĐŸĐžĐ´ŅŅ‚Đ°ĐŊОвĐēа ҁ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩĐŧ *, ** и ? ĐŋОддĐĩŅ€ĐļиваĐĩŅ‚ŅŅ. Đ§Ņ‚ĐžĐąŅ‹ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Ņ„Đ°ĐšĐģŅ‹ в ĐģŅŽĐąĐžĐŧ ĐēĐ°Ņ‚Đ°ĐģĐžĐŗĐĩ ҁ иĐŧĐĩĐŊĐĩĐŧ ÂĢRawÂģ, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ ÂĢ**/Raw/**Âģ. Đ§Ņ‚ĐžĐąŅ‹ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Ņ„Đ°ĐšĐģŅ‹, СаĐēаĐŊŅ‡Đ¸Đ˛Đ°ŅŽŅ‰Đ¸ĐĩŅŅ ĐŊа ÂĢ.tifÂģ, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ ÂĢ**/*.tifÂģ. Đ§Ņ‚ĐžĐąŅ‹ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ°ĐąŅĐžĐģŅŽŅ‚ĐŊŅ‹Đš ĐŋŅƒŅ‚ŅŒ, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ ÂĢ/path/to/ignore/**Âģ.", + "add_exclusion_pattern_description": "Đ”ĐžĐąĐ°Đ˛ŅŒŅ‚Đĩ ŅˆĐ°ĐąĐģĐžĐŊŅ‹ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊиК. ПоддĐĩŅ€ĐļĐ¸Đ˛Đ°ŅŽŅ‚ŅŅ ŅĐ¸ĐŧвОĐģŅ‹ ĐŋĐžĐ´ŅŅ‚Đ°ĐŊОвĐēи *, ** и ?. Đ§Ņ‚ĐžĐąŅ‹ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Ņ„Đ°ĐšĐģŅ‹ в ĐģŅŽĐąĐžĐŧ ĐēĐ°Ņ‚Đ°ĐģĐžĐŗĐĩ ҁ иĐŧĐĩĐŊĐĩĐŧ \"Raw\", ҃ĐēаĐļĐ¸Ņ‚Đĩ \"**/Raw/**\". Đ§Ņ‚ĐžĐąŅ‹ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Ņ„Đ°ĐšĐģŅ‹, СаĐēаĐŊŅ‡Đ¸Đ˛Đ°ŅŽŅ‰Đ¸ĐĩŅŅ ĐŊа \".tif\", Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ \"**/*.tif\". Đ§Ņ‚ĐžĐąŅ‹ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋŅƒŅ‚ŅŒ ҆ĐĩĐģиĐēĐžĐŧ, ҃ĐēаĐļĐ¸Ņ‚Đĩ \"/path/to/ignore/**\".", + "admin_user": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€", "asset_offline_description": "Đ­Ņ‚ĐžŅ‚ Ņ„Đ°ĐšĐģ вĐŊĐĩ҈ĐŊĐĩĐš йийĐģĐ¸ĐžŅ‚ĐĩĐēи ĐŊĐĩ ĐąŅ‹Đģ ĐŊаКдĐĩĐŊ ĐŊа Đ´Đ¸ŅĐēĐĩ и ĐąŅ‹Đģ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃. Đ•ŅĐģи Ņ„Đ°ĐšĐģ ĐąŅ‹Đģ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ вĐŊŅƒŅ‚Ņ€Đ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐēи, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊŅƒŅŽ ҈ĐēаĐģ҃, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŊĐ°ĐšŅ‚Đ¸ ĐŊĐžĐ˛Ņ‹Đš ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đš Ņ€ĐĩŅŅƒŅ€Ņ. Đ§Ņ‚ĐžĐąŅ‹ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Ņ„Đ°ĐšĐģ, ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž ĐŋŅƒŅ‚ŅŒ ĐŊиĐļĐĩ Đ´ĐžŅŅ‚ŅƒĐŋĐĩĐŊ Đ´ĐģŅ Immich и Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚Đĩ ҁĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēи.", "authentication_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", "authentication_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋĐ°Ņ€ĐžĐģŅĐŧи, OAuth и Đ´Ņ€ŅƒĐŗĐ¸Đŧи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", @@ -41,17 +42,15 @@ "authentication_settings_reenable": "Đ§Ņ‚ĐžĐąŅ‹ ҁĐŊОва вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ КоĐŧаĐŊĐ´Ņƒ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", "background_task_job": "ФОĐŊĐžĐ˛Ņ‹Đĩ ĐˇĐ°Đ´Đ°Ņ‡Đ¸", "backup_database": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅƒŅŽ ĐēĐžĐŋĐ¸ŅŽ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", - "backup_database_enable_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ даĐŧĐŋŅ‹ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", + "backup_database_enable_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅĐžĐˇĐ´Đ°ĐŊиĐĩ даĐŧĐŋОв ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", "backup_keep_last_amount": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž Ņ…Ņ€Đ°ĐŊиĐŧҋ҅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊҋ҅ ĐēĐžĐŋиК ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", "backup_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", - "backup_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž ŅĐžĐˇĐ´Đ°ĐŊĐ¸Ņ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊҋ҅ ĐēĐžĐŋиК ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅. ĐŸŅ€Đ¸ĐŧĐĩŅ‡Đ°ĐŊиĐĩ: Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊиĐĩ ĐŊĐĩ ĐēĐžĐŊŅ‚Ņ€ĐžĐģĐ¸Ņ€ŅƒĐĩŅ‚ŅŅ, Đ˛Ņ‹ ĐŊĐĩ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚Đĩ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ в ҁĐģŅƒŅ‡Đ°Đĩ ŅĐąĐžŅ.", - "check_all": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ", - "cleanup": "ĐžŅ‡Đ¸ŅŅ‚Đēа", + "backup_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅĐžĐˇĐ´Đ°ĐŊĐ¸Ņ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊҋ҅ ĐēĐžĐŋиК ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅.", "cleared_jobs": "ĐžŅ‡Đ¸Ņ‰ĐĩĐŊŅ‹ ĐˇĐ°Đ´Đ°Ņ‡Đ¸ Đ´ĐģŅ: {job}", "config_set_by_file": "ĐĐ°ŅŅ‚Ņ€ĐžĐĩĐŊĐž ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ Ņ„Đ°ĐšĐģа ĐēĐžĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸Đ¸", - "confirm_delete_library": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃ \"{library}\"?", + "confirm_delete_library": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃ {library}?", "confirm_delete_library_assets": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚Ņƒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃? Đ­Ņ‚Đž ĐąĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊĐž ŅƒĐ´Đ°ĐģĐ¸Ņ‚ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}} иС Immich. ФаКĐģŅ‹ ĐžŅŅ‚Đ°ĐŊŅƒŅ‚ŅŅ ĐŊа Đ´Đ¸ŅĐēĐĩ.", - "confirm_email_below": "Đ§Ņ‚ĐžĐąŅ‹ ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚ŅŒ, ввĐĩĐ´Đ¸Ņ‚Đĩ \"{email}\" ĐŊиĐļĐĩ", + "confirm_email_below": "ВвĐĩĐ´Đ¸Ņ‚Đĩ \"{email}\" Đ´ĐģŅ ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊĐ¸Ņ", "confirm_reprocess_all_faces": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐģĐ¸Ņ†Đ°? Đ‘ŅƒĐ´ŅƒŅ‚ Ņ‚Đ°ĐēĐļĐĩ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иĐŧĐĩĐŊа ŅĐž Đ˛ŅĐĩŅ… ĐģĐ¸Ņ†.", "confirm_user_password_reset": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}?", "confirm_user_pin_code_reset": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ PIN-ĐēОд ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}?", @@ -61,15 +60,13 @@ "cron_expression_presets": "Đ Đ°ŅĐŋĐ¸ŅĐ°ĐŊиĐĩ (ĐŋŅ€ĐĩĐ´ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ‹Đĩ Đ˛Đ°Ņ€Đ¸Đ°ĐŊ҂ҋ)", "disable_login": "ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Đ˛Ņ…ĐžĐ´", "duplicate_detection_job_description": "ЗаĐŋ҃ҁĐēаĐĩŅ‚ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐĩĐŊиĐĩ ĐŋĐžŅ…ĐžĐļĐ¸Ņ… Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК ĐŋŅ€Đ¸ ĐŋĐžĐŧĐžŅ‰Đ¸ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐˇŅ€ĐĩĐŊĐ¸Ņ (ĐˇĐ°Đ˛Đ¸ŅĐ¸Ņ‚ ĐžŅ‚ ҃ĐŧĐŊĐžĐŗĐž ĐŋĐžĐ¸ŅĐēа)", - "exclusion_pattern_description": "ШайĐģĐžĐŊŅ‹ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ ĐŋОСвОĐģŅŅŽŅ‚ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Ņ„Đ°ĐšĐģŅ‹ и ĐŋаĐŋĐēи ĐŋŅ€Đ¸ ҁĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°ĐŊии Đ˛Đ°ŅˆĐĩĐš йийĐģĐ¸ĐžŅ‚ĐĩĐēи. Đ­Ņ‚Đž ĐŋĐžĐģĐĩСĐŊĐž, ĐĩҁĐģи ҃ Đ˛Đ°Ņ ĐĩŅŅ‚ŅŒ ĐŋаĐŋĐēи, ŅĐžĐ´ĐĩŅ€ĐļĐ°Ņ‰Đ¸Đĩ Ņ„Đ°ĐšĐģŅ‹, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ Đ˛Ņ‹ ĐŊĐĩ Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ иĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ, ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€, RAW-Ņ„Đ°ĐšĐģŅ‹.", - "external_library_created_at": "ВĐŊĐĩ҈ĐŊŅŅ йийĐģĐ¸ĐžŅ‚ĐĩĐēа (ŅĐžĐˇĐ´Đ°ĐŊа {date})", + "exclusion_pattern_description": "ШайĐģĐžĐŊŅ‹ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊиК ĐŋОСвОĐģŅŅŽŅ‚ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŊĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ Ņ„Đ°ĐšĐģŅ‹ и ĐŋаĐŋĐēи ĐŋŅ€Đ¸ ҁĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°ĐŊии йийĐģĐ¸ĐžŅ‚ĐĩĐēи. Đ­Ņ‚Đž ĐŋĐžĐģĐĩСĐŊĐž, ĐĩҁĐģи в ĐŋаĐŋĐēĐĩ ĐĩŅŅ‚ŅŒ Ņ„Đ°ĐšĐģŅ‹, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ĐŊĐĩ ĐŊ҃ĐļĐŊĐž иĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ. НаĐŋŅ€Đ¸ĐŧĐĩŅ€ RAW-Ņ„Đ°ĐšĐģŅ‹.", "external_library_management": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ вĐŊĐĩ҈ĐŊиĐŧи йийĐģĐ¸ĐžŅ‚ĐĩĐēаĐŧи", "face_detection": "ОбĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩ ĐģĐ¸Ņ†", - "face_detection_description": "ОбĐŊĐ°Ņ€ŅƒĐļиваĐĩŅ‚ ĐģĐ¸Ņ†Đ° ĐŊа ĐŧĐĩдиа ҁ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩĐŧ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ. ДĐģŅ видĐĩĐž аĐŊаĐģĐ¸ĐˇĐ¸Ņ€ŅƒĐĩŅ‚ŅŅ Ņ‚ĐžĐģҌĐēĐž ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ°. \"ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ\" ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž ĐžĐąŅ€Đ°ĐąĐ°Ņ‚Ņ‹Đ˛Đ°ĐĩŅ‚ Đ˛ŅĐĩ ĐŧĐĩдиа. \"ĐĄĐąŅ€ĐžŅ\" Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊĐž ŅƒĐ´Đ°ĐģŅĐĩŅ‚ Đ˛ŅĐĩ иĐŧĐĩŅŽŅ‰Đ¸ĐĩŅŅ даĐŊĐŊŅ‹Đĩ Đž ĐģĐ¸Ņ†Đ°Ņ…. \"ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đĩ\" ŅŅ‚Đ°Đ˛Đ¸Ņ‚ в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ ĐŧĐĩдиа, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ Đĩ҉ґ ĐŊĐĩ ĐąŅ‹Đģи ĐžĐąŅ€Đ°ĐąĐžŅ‚Đ°ĐŊŅ‹. ОбĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐŊŅ‹Đĩ ĐģĐ¸Ņ†Đ° ĐąŅƒĐ´ŅƒŅ‚ ĐŋĐĩŅ€ĐĩдаĐŊŅ‹ в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ Đ´ĐģŅ Ņ€Đ°ŅĐŋОСĐŊаваĐŊĐ¸Ņ ĐŋĐžŅĐģĐĩ СавĐĩŅ€ŅˆĐĩĐŊĐ¸Ņ ĐŋŅ€ĐžŅ†ĐĩŅŅĐ° Đ¸Ņ… ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐ¸Ņ, ĐŋŅ€Đ¸Đ˛ŅĐˇŅ‹Đ˛Đ°ŅŅŅŒ Đē ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đŧ иĐģи ĐŊĐžĐ˛Ņ‹Đŧ ĐģŅŽĐ´ŅĐŧ.", - "facial_recognition_job_description": "Đ“Ņ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đēа ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐŊҋ҅ ĐģĐ¸Ņ† ĐŋĐž ĐģŅŽĐ´ŅĐŧ. Đ­Ņ‚ĐžŅ‚ ŅˆĐ°Đŗ Đ˛Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ ĐŋĐžŅĐģĐĩ СавĐĩŅ€ŅˆĐĩĐŊĐ¸Ņ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐ¸Ņ ĐģĐ¸Ņ†. \"ĐĄĐąŅ€ĐžŅ\" (ĐŋĐĩŅ€Đĩ)ĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ŅƒĐĩŅ‚ Đ˛ŅĐĩ ĐģĐ¸Ņ†Đ°. \"ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đĩ\" ŅŅ‚Đ°Đ˛Đ¸Ņ‚ в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ ĐģĐ¸Ņ†Đ°, ĐŊĐĩ ĐŋŅ€Đ¸Đ˛ŅĐˇĐ°ĐŊĐŊŅ‹Đĩ Đē ҇ĐĩĐģОвĐĩĐē҃.", + "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": "ПоĐģĐŊĐžŅ€Đ°ĐˇĐŧĐĩŅ€ĐŊĐžĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ĐąĐĩС ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ŅŅ ĐŋŅ€Đ¸ ŅƒĐ˛ĐĩĐģĐ¸Ņ‡ĐĩĐŊии", @@ -81,8 +78,8 @@ "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_quality_description": "ĐšĐ°Ņ‡ĐĩŅŅ‚Đ˛Đž ĐŋŅ€ĐĩĐ´Đ˛Đ°Ņ€Đ¸Ņ‚ĐĩĐģҌĐŊĐžĐŗĐž ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° ĐžŅ‚ 1 Đ´Đž 100. ЧĐĩĐŧ Đ˛Ņ‹ŅˆĐĩ, Ņ‚ĐĩĐŧ ĐģŅƒŅ‡ŅˆĐĩ, ĐŊĐž ĐŋŅ€Đ¸ ŅŅ‚ĐžĐŧ ŅĐžĐˇĐ´Đ°ŅŽŅ‚ŅŅ Ņ„Đ°ĐšĐģŅ‹ йОĐģҌ҈ĐĩĐŗĐž Ņ€Đ°ĐˇĐŧĐĩŅ€Đ° и ĐŧĐžĐļĐĩŅ‚ ҁĐŊĐ¸ĐˇĐ¸Ņ‚ŅŒŅŅ ҁĐēĐžŅ€ĐžŅŅ‚ŅŒ ĐžŅ‚ĐēĐģиĐēа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ. ĐŖŅŅ‚Đ°ĐŊОвĐēа ĐŊиСĐēĐžĐŗĐž СĐŊĐ°Ņ‡ĐĩĐŊĐ¸Ņ ĐŧĐžĐļĐĩŅ‚ ĐŋОвĐģĐ¸ŅŅ‚ŅŒ ĐŊа ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đž ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ.", + "image_preview_description": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ҁҀĐĩĐ´ĐŊĐĩĐŗĐž Ņ€Đ°ĐˇĐŧĐĩŅ€Đ° ĐąĐĩС ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩĐŧĐžĐĩ ĐŋŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ ĐžŅ‚Đ´ĐĩĐģҌĐŊҋ҅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ и Đ´ĐģŅ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ", + "image_preview_quality_description": "ĐšĐ°Ņ‡ĐĩŅŅ‚Đ˛Đž ĐŋŅ€ĐĩĐ´Đ˛Đ°Ņ€Đ¸Ņ‚ĐĩĐģҌĐŊĐžĐŗĐž ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° ĐžŅ‚ 1 Đ´Đž 100. ЧĐĩĐŧ Đ˛Ņ‹ŅˆĐĩ, Ņ‚ĐĩĐŧ ĐģŅƒŅ‡ŅˆĐĩ, ĐŊĐž ŅĐžĐˇĐ´Đ°ŅŽŅ‚ŅŅ Ņ„Đ°ĐšĐģŅ‹ йОĐģҌ҈ĐĩĐŗĐž Ņ€Đ°ĐˇĐŧĐĩŅ€Đ°, и ĐŧĐžĐļĐĩŅ‚ ҁĐŊĐ¸ĐˇĐ¸Ņ‚ŅŒŅŅ ҁĐēĐžŅ€ĐžŅŅ‚ŅŒ ĐžŅ‚ĐēĐģиĐēа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ. ĐŖŅŅ‚Đ°ĐŊОвĐēа ĐŊиСĐēĐžĐŗĐž СĐŊĐ°Ņ‡ĐĩĐŊĐ¸Ņ ĐŧĐžĐļĐĩŅ‚ ĐŋОвĐģĐ¸ŅŅ‚ŅŒ ĐŊа ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đž ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ.", "image_preview_title": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŋŅ€ĐĩĐ´Đ˛Đ°Ņ€Đ¸Ņ‚ĐĩĐģҌĐŊĐžĐŗĐž ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ°", "image_quality": "ĐšĐ°Ņ‡ĐĩŅŅ‚Đ˛Đž", "image_resolution": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ", @@ -100,16 +97,16 @@ "job_status": "ĐĄĐžŅŅ‚ĐžŅĐŊиĐĩ Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ ĐˇĐ°Đ´Đ°Ņ‡", "jobs_delayed": "{jobCount, plural, one {# ĐžŅ‚ĐģĐžĐļĐĩĐŊа} other {# ĐžŅ‚ĐģĐžĐļĐĩĐŊĐž}}", "jobs_failed": "{jobCount, plural, other {# ĐŊĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ}}", - "library_created": "ХОСдаĐŊĐŊĐ°Ņ йийĐģĐ¸ĐžŅ‚ĐĩĐēа: {library}", + "library_created": "ХОСдаĐŊа ĐŊĐžĐ˛Đ°Ņ йийĐģĐ¸ĐžŅ‚ĐĩĐēа: {library}", "library_deleted": "БибĐģĐ¸ĐžŅ‚ĐĩĐēа ŅƒĐ´Đ°ĐģĐĩĐŊа", - "library_import_path_description": "ĐŖĐēаĐļĐ¸Ņ‚Đĩ ĐŋаĐŋĐē҃ Đ´ĐģŅ иĐŧĐŋĐžŅ€Ņ‚Đ°. Đ­Ņ‚Đ° ĐŋаĐŋĐēа, вĐēĐģŅŽŅ‡Đ°Ņ вĐģĐžĐļĐĩĐŊĐŊŅ‹Đĩ ĐŋаĐŋĐēи, ĐąŅƒĐ´ĐĩŅ‚ ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐĩĐŊа ĐŊа ĐŊаĐģĐ¸Ņ‡Đ¸Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК и видĐĩĐž.", + "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_enable_description": "ĐžŅ‚ŅĐģĐĩĐļĐ¸Đ˛Đ°Ņ‚ŅŒ иСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ Ņ„Đ°ĐšĐģОв вО вĐŊĐĩ҈ĐŊĐ¸Ņ… йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ…", "library_watching_settings": "ĐĄĐģĐĩĐļĐĩĐŊиĐĩ Са йийĐģĐ¸ĐžŅ‚ĐĩĐēОК (ЭКСПЕРИМЕНĐĸАЛĐŦНОЕ)", "library_watching_settings_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ҁĐģĐĩĐ´Đ¸Ņ‚ŅŒ Са иСĐŧĐĩĐŊĐĩĐŊĐ¸ŅĐŧи Ņ„Đ°ĐšĐģОв", "logging_enable_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ вĐĩĐ´ĐĩĐŊиĐĩ ĐļŅƒŅ€ĐŊаĐģа", @@ -132,7 +129,7 @@ "machine_learning_max_detection_distance": "МаĐēŅĐ¸ĐŧаĐģҌĐŊĐžĐĩ Ņ€Đ°ĐˇĐģĐ¸Ņ‡Đ¸Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК", "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_recognized_faces": "МиĐŊиĐŧ҃Đŧ Ņ€Đ°ŅĐŋОСĐŊаĐŊĐŊҋ҅ ĐģĐ¸Ņ†", @@ -143,22 +140,22 @@ "machine_learning_smart_search_description": "ĐĄĐĩĐŧаĐŊŅ‚Đ¸Ņ‡ĐĩҁĐēиК ĐŋĐžĐ¸ŅĐē Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК ҁ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩĐŧ вĐģĐžĐļĐĩĐŊиК CLIP", "machine_learning_smart_search_enabled": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ иĐŊŅ‚ĐĩĐģĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ‹Đš ĐŋĐžĐ¸ŅĐē", "machine_learning_smart_search_enabled_description": "Đ•ŅĐģи ŅŅ‚ĐžŅ‚ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊ, Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒŅŅ Đ´ĐģŅ иĐŊŅ‚ĐĩĐģĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊĐžĐŗĐž ĐŋĐžĐ¸ŅĐēа.", - "machine_learning_url_description": "URL-Đ°Đ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ. Đ•ŅĐģи ҃ĐēаСаĐŊŅ‹ ĐŊĐĩҁĐēĐžĐģҌĐēĐž, СаĐŋŅ€ĐžŅŅ‹ ĐąŅƒĐ´ŅƒŅ‚ ĐŋĐžŅĐģаĐŊŅ‹ ĐŊа ĐēаĐļĐ´Ņ‹Đš, ҁ ĐŋĐĩŅ€Đ˛ĐžĐŗĐž Đ´Đž ĐŋĐžŅĐģĐĩĐ´ĐŊĐĩĐŗĐž, ĐŋĐž ĐžŅ‡ĐĩŅ€Đĩди, ĐŋĐžĐēа ĐŊĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊ ҃ҁĐŋĐĩ҈ĐŊŅ‹Đš ĐžŅ‚Đ˛ĐĩŅ‚.", + "machine_learning_url_description": "URL-Đ°Đ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ. Đ•ŅĐģи ҃ĐēаСаĐŊĐž ĐŊĐĩҁĐēĐžĐģҌĐēĐž, СаĐŋŅ€ĐžŅŅ‹ ĐąŅƒĐ´ŅƒŅ‚ ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒŅŅ ĐŋĐž ĐžŅ‡ĐĩŅ€Đĩди ĐŊа ĐēаĐļĐ´Ņ‹Đš, ĐŋĐžĐēа ĐžŅ‚ ОдĐŊĐžĐŗĐž иС ĐŊĐ¸Ņ… ĐŊĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊ ҃ҁĐŋĐĩ҈ĐŊŅ‹Đš ĐžŅ‚Đ˛ĐĩŅ‚. ĐĄĐĩŅ€Đ˛ĐĩҀҋ, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ĐŊĐĩ ĐžŅ‚Đ˛ĐĩŅ‡Đ°ŅŽŅ‚, ĐąŅƒĐ´ŅƒŅ‚ Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊĐž Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒŅŅ Đ´Đž Ņ‚ĐĩŅ… ĐŋĐžŅ€, ĐŋĐžĐēа ĐŊĐĩ ŅŅ‚Đ°ĐŊŅƒŅ‚ ҁĐŊОва Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹.", "manage_concurrency": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋĐ°Ņ€Đ°ĐģĐģĐĩĐģҌĐŊĐžŅŅ‚ŅŒŅŽ СадаĐŊиК", "manage_log_settings": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐļŅƒŅ€ĐŊаĐģа", "map_dark_style": "ĐĸŅ‘ĐŧĐŊŅ‹Đš ŅŅ‚Đ¸ĐģҌ", "map_enable_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ ĐēĐ°Ņ€Ņ‚Ņ‹", "map_gps_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐēĐ°Ņ€Ņ‚Ņ‹ и GPS", - "map_gps_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐēĐ°Ņ€Ņ‚Ņ‹ и GPS (ĐžĐąŅ€Đ°Ņ‚ĐŊŅ‹Đš ĐŗĐĩĐžĐēОдиĐŊĐŗ)", - "map_implications": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ĐˇĐ°Đ˛Đ¸ŅĐ¸Ņ‚ ĐžŅ‚ вĐŊĐĩ҈ĐŊĐĩĐŗĐž ҁĐĩŅ€Đ˛Đ¸ŅĐ° ĐŋĐģĐ¸Ņ‚ĐžĐē (tiles.immich.cloud)", + "map_gps_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐēĐ°Ņ€Ņ‚Ņ‹ и GPS (ĐžĐąŅ€Đ°Ņ‚ĐŊĐžĐŗĐž ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ)", + "map_implications": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŊа ĐēĐ°Ņ€Ņ‚Đĩ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ вĐŊĐĩ҈ĐŊиК ҁĐĩŅ€Đ˛Đ¸Ņ ĐŋĐģĐ¸Ņ‚ĐžĐē (tiles.immich.cloud)", "map_light_style": "ХвĐĩŅ‚ĐģŅ‹Đš ŅŅ‚Đ¸ĐģҌ", - "map_manage_reverse_geocoding_settings": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐžĐąŅ€Đ°Ņ‚ĐŊĐžĐŗĐž ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", - "map_reverse_geocoding": "ĐžĐąŅ€Đ°Ņ‚ĐŊĐžĐĩ ГĐĩĐžĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", + "map_manage_reverse_geocoding_settings": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐžĐąŅ€Đ°Ņ‚ĐŊĐžĐŗĐž ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", + "map_reverse_geocoding": "ĐžĐąŅ€Đ°Ņ‚ĐŊĐžĐĩ ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", "map_reverse_geocoding_enable_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐžĐąŅ€Đ°Ņ‚ĐŊĐžĐĩ ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", "map_reverse_geocoding_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐžĐąŅ€Đ°Ņ‚ĐŊĐžĐŗĐž ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", "map_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐēĐ°Ņ€Ņ‚Ņ‹", "map_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐēĐ°Ņ€Ņ‚Ņ‹", - "map_style_description": "URL-Đ°Đ´Ņ€Đĩҁ Ņ‚ĐĩĐŧŅ‹ ĐēĐ°Ņ€Ņ‚Ņ‹ style.json", + "map_style_description": "URL-Đ°Đ´Ņ€Đĩҁ Đē JSON Ņ„Đ°ĐšĐģ҃ Ņ‚ĐĩĐŧŅ‹ ĐēĐ°Ņ€Ņ‚Ņ‹", "memory_cleanup_job": "ĐžŅ‡Đ¸ŅŅ‚Đēа Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊиК", "memory_generate_job": "ХОСдаĐŊиĐĩ Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊиК", "metadata_extraction_job": "ИСвĐģĐĩ҇ĐĩĐŊиĐĩ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅", @@ -168,24 +165,24 @@ "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 Photo Server \"", + "notification_email_from_address_description": "ĐĐ´Ņ€Đĩҁ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Ņ‹ ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ĐĩĐģŅ, ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€: \"Immich Photo Server \".", "notification_email_host_description": "ДоĐŧĐĩĐŊĐŊĐžĐĩ иĐŧŅ ĐŋĐžŅ‡Ņ‚ĐžĐ˛ĐžĐŗĐž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° (ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€, smtp.immich.app)", "notification_email_ignore_certificate_errors": "Đ˜ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐžŅˆĐ¸ĐąĐēи ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ°", "notification_email_ignore_certificate_errors_description": "Đ˜ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐžŅˆĐ¸ĐąĐēи ĐŋŅ€ĐžĐ˛ĐĩŅ€Đēи ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ° TLS (ĐŊĐĩ Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒĐĩŅ‚ŅŅ)", - "notification_email_password_description": "ĐŸĐ°Ņ€ĐžĐģҌ, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩĐŧŅ‹Đš ĐŋŅ€Đ¸ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Ņ‹", + "notification_email_password_description": "ĐŸĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Ņ‹", "notification_email_port_description": "ĐŸĐžŅ€Ņ‚ ĐŋĐžŅ‡Ņ‚ĐžĐ˛ĐžĐŗĐž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° (ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€, 25, 465 иĐģи 587)", "notification_email_sent_test_email_button": "ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐžŅ‡ĐŊĐžĐĩ ĐŋĐ¸ŅŅŒĐŧĐž и ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ", "notification_email_setting_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐžŅ‚ĐŋŅ€Đ°Đ˛Đēи ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ", "notification_email_test_email": "ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐžŅ‡ĐŊĐžĐĩ ĐŋĐ¸ŅŅŒĐŧĐž", "notification_email_test_email_failed": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐžŅ‡ĐŊĐžĐĩ ĐŋĐ¸ŅŅŒĐŧĐž, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ Đ˛Đ°ŅˆĐ¸ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ", "notification_email_test_email_sent": "ĐŸŅ€ĐžĐ˛ĐĩŅ€ĐžŅ‡ĐŊĐžĐĩ ĐŋĐ¸ŅŅŒĐŧĐž ĐąŅ‹ĐģĐž ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐž ĐŊа Đ°Đ´Ņ€Đĩҁ {email}. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ ŅĐ˛ĐžĐš ĐŋĐžŅ‡Ņ‚ĐžĐ˛Ņ‹Đš ŅŅ‰Đ¸Đē.", - "notification_email_username_description": "ИĐŧŅ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ, ĐēĐžŅ‚ĐžŅ€ĐžĐĩ ĐąŅƒĐ´ĐĩŅ‚ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒŅŅ ĐŋŅ€Đ¸ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Ņ‹", + "notification_email_username_description": "ИĐŧŅ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ Đ´ĐģŅ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Ņ‹", "notification_enable_email_notifications": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ", "notification_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", "notification_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК, вĐēĐģŅŽŅ‡Đ°Ņ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊŅƒŅŽ ĐŋĐžŅ‡Ņ‚Ņƒ", @@ -198,32 +195,27 @@ "oauth_enable_description": "Đ’Ņ…ĐžĐ´ ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ OAuth", "oauth_mobile_redirect_uri": "URI Ņ€ĐĩĐ´Đ¸Ņ€ĐĩĐēŅ‚Đ° Đ´ĐģŅ ĐŧОйиĐģҌĐŊҋ҅", "oauth_mobile_redirect_uri_override": "ПĐĩŅ€ĐĩĐŊаĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ URI Đ´ĐģŅ ĐŧОйиĐģҌĐŊҋ҅ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛", - "oauth_mobile_redirect_uri_override_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ, ĐĩҁĐģи ĐŋĐžŅŅ‚Đ°Đ˛Ņ‰Đ¸Đē OAuth ĐŊĐĩ Ņ€Đ°ĐˇŅ€ĐĩŅˆĐ°ĐĩŅ‚ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ ĐŧОйиĐģҌĐŊĐžĐŗĐž URI, ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€, '{callback}'", + "oauth_mobile_redirect_uri_override_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ, ĐĩҁĐģи ĐŋĐžŅŅ‚Đ°Đ˛Ņ‰Đ¸Đē OAuth ĐŊĐĩ Ņ€Đ°ĐˇŅ€ĐĩŅˆĐ°ĐĩŅ‚ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ ĐŧОйиĐģҌĐŊĐžĐŗĐž URI, ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€, ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Đ˛Ņ…ĐžĐ´Đ° ҇ĐĩŅ€ĐĩС OAuth", "oauth_settings_more_details": "ДĐģŅ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐ¸Ņ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đš Ой ŅŅ‚ĐžĐš Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚ĐĩҁҌ Đē Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Đ¸.", - "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_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, ĐēĐžŅ‚ĐžŅ€Đ°Ņ ĐąŅƒĐ´ĐĩŅ‚ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒŅŅ, ĐĩҁĐģи ŅƒŅ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊиĐĩ ĐŊĐĩ СадаĐŊĐž.", "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": "ĐžŅ‡Đ¸ŅŅ‚Đēа ĐŋĐĩŅ€ŅĐžĐŊŅ‹", "quota_size_gib": "РаСĐŧĐĩŅ€ ĐēĐ˛ĐžŅ‚Ņ‹ (ГБ)", "refreshing_all_libraries": "ОбĐŊОвĐģĐĩĐŊиĐĩ Đ˛ŅĐĩŅ… йийĐģĐ¸ĐžŅ‚ĐĩĐē", - "registration": "Đ ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", - "registration_description": "ĐŸĐžŅĐēĐžĐģҌĐē҃ Đ˛Ņ‹ ŅĐ˛ĐģŅĐĩŅ‚ĐĩҁҌ ĐŋĐĩŅ€Đ˛Ņ‹Đŧ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐŧ в ŅĐ¸ŅŅ‚ĐĩĐŧĐĩ, ваĐŧ ĐąŅƒĐ´ĐĩŅ‚ ĐŋŅ€Đ¸ŅĐ˛ĐžĐĩĐŊа Ņ€ĐžĐģҌ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°, и Đ˛Ņ‹ ĐąŅƒĐ´ĐĩŅ‚Đĩ ĐžŅ‚Đ˛ĐĩŅ‡Đ°Ņ‚ŅŒ Са адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚Đ¸Đ˛ĐŊŅ‹Đĩ ĐˇĐ°Đ´Đ°Ņ‡Đ¸. ДоĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊҋ҅ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš ĐąŅƒĐ´ĐĩŅ‚Đĩ ŅĐžĐˇĐ´Đ°Đ˛Đ°Ņ‚ŅŒ Đ˛Ņ‹.", - "repair_all": "ĐŸĐžŅ‡Đ¸ĐŊĐ¸Ņ‚ŅŒ Đ˛ŅŅ‘", - "repair_matched_items": "ĐĄĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛ŅƒĐĩŅ‚ {count, plural, one {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Ņƒ} few {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°Đŧ} many {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°Đŧ} other {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°Đŧ}}", - "repaired_items": "{count, plural, one {Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ # ŅĐģĐĩĐŧĐĩĐŊŅ‚} many {Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž # ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛} other {Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž # ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°}}", + "registration": "Đ ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", + "registration_description": "ПĐĩŅ€Đ˛Ņ‹Đš ĐˇĐ°Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊŅ‹Đš ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ ĐąŅƒĐ´ĐĩŅ‚ ĐŊаСĐŊĐ°Ņ‡ĐĩĐŊ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ. В даĐģҌĐŊĐĩĐšŅˆĐĩĐŧ ŅŅ‚ĐžĐš ŅƒŅ‡ĐĩŅ‚ĐŊОК СаĐŋĐ¸ŅĐ¸ ĐąŅƒĐ´ĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐž ŅĐžĐˇĐ´Đ°ĐŊиĐĩ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊҋ҅ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš и ҃ĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ.", "require_password_change_on_login": "ĐĸŅ€ĐĩĐąĐžĐ˛Đ°Ņ‚ŅŒ ҁĐŧĐĩĐŊ҃ ĐŋĐ°Ņ€ĐžĐģŅ ĐŋŅ€Đ¸ ĐŋĐĩŅ€Đ˛ĐžĐŧ Đ˛Ņ…ĐžĐ´Đĩ", "reset_settings_to_default": "ĐĄĐąŅ€ĐžŅ ĐŊĐ°ŅŅ‚Ņ€ĐžĐĩĐē Đ´Đž СĐŊĐ°Ņ‡ĐĩĐŊиК ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ", "reset_settings_to_recent_saved": "ĐĄĐąŅ€ĐžŅŅŒŅ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи Đē ĐŋĐžŅĐģĐĩĐ´ĐŊиĐŧ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊĐŊŅ‹Đŧ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧ", @@ -242,21 +234,21 @@ "sidecar_job_description": "ОбĐŊĐ°Ņ€ŅƒĐļиваĐĩŅ‚ и ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€ŅƒĐĩŅ‚ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊŅ‹Đĩ иС sidecar-Ņ„Đ°ĐšĐģОв", "slideshow_duration_description": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž ҁĐĩĐē҃ĐŊĐ´ Đ´ĐģŅ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ĐēаĐļĐ´ĐžĐŗĐž Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "smart_search_job_description": "Đ Đ°ŅĐŋОСĐŊаĐĩŅ‚ ŅĐžĐ´ĐĩŅ€ĐļиĐŧĐžĐĩ ĐŧĐĩĐ´Đ¸Đ°Ņ„Đ°ĐšĐģОв Đ´ĐģŅ ҃ĐŧĐŊĐžĐŗĐž ĐŋĐžĐ¸ŅĐēа", - "storage_template_date_time_description": "Đ’Ņ€ĐĩĐŧŅ ŅĐžĐˇĐ´Đ°ĐŊĐ¸Ņ Ņ„Đ°ĐšĐģа Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊĐž ĐēаĐē иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Đž Đ˛Ņ€ĐĩĐŧĐĩĐŊи ҁҊĐĩĐŧĐēи", - "storage_template_date_time_sample": "Đ’Ņ€ĐĩĐŧŅ Đ˛Ņ‹ĐąĐžŅ€Đēи {date}", - "storage_template_enable_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_hash_verification_enabled_description": "ВĐēĐģŅŽŅ‡Đ°ĐĩŅ‚ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃ Ņ…ĐĩŅˆĐ°, ĐŊĐĩ ĐžŅ‚ĐēĐģŅŽŅ‡Đ°ĐšŅ‚Đĩ ĐĩŅ‘, ĐĩҁĐģи ĐŊĐĩ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹ в ĐŋĐžŅĐģĐĩĐ´ŅŅ‚Đ˛Đ¸ŅŅ…", "storage_template_migration": "ĐŸŅ€Đ¸ĐŧĐĩĐŊĐĩĐŊиĐĩ ŅˆĐ°ĐąĐģĐžĐŊа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", - "storage_template_migration_description": "ĐŸŅ€Đ¸ĐŧĐĩĐŊŅĐĩŅ‚ Ņ‚ĐĩĐēŅƒŅ‰Đ¸Đš {template} Đē Ņ€Đ°ĐŊĐĩĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đŧ Ņ€ĐĩŅŅƒŅ€ŅĐ°Đŧ", + "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_migration_job": "ЗадаĐŊиĐĩ ĐŋĐž ĐŋŅ€Đ¸ĐŧĐĩĐŊĐĩĐŊĐ¸ŅŽ ŅˆĐ°ĐąĐģĐžĐŊа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", + "storage_template_more_details": "ДĐģŅ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐ¸Ņ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸ Ой ŅŅ‚ĐžĐš Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚ĐĩҁҌ Đē Ņ€Đ°ĐˇĐ´ĐĩĐģаĐŧ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Đ¸ ШайĐģĐžĐŊ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° и ĐĄŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Đ° Ņ…Ņ€Đ°ĐŊĐĩĐŊĐ¸Ņ Ņ„Đ°ĐšĐģОв", + "storage_template_onboarding_description_v2": "Đ•ŅĐģи ŅŅ‚Đ° Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ вĐēĐģŅŽŅ‡ĐĩĐŊа, ĐžĐŊа Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇŅƒĐĩŅ‚ Ņ„Đ°ĐšĐģŅ‹ ĐŊа ĐžŅĐŊОвĐĩ СадаĐŊĐŊĐžĐŗĐž ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐŧ ŅˆĐ°ĐąĐģĐžĐŊа. ДĐģŅ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐ¸Ņ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸ ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚ĐĩҁҌ Đē Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Đ¸.", + "storage_template_path_length": "ĐŸŅ€Đ¸ĐŧĐĩŅ€ĐŊŅ‹Đš ĐŋŅ€ĐĩĐ´ĐĩĐģ Đ´ĐģиĐŊŅ‹ ĐŋŅƒŅ‚Đ¸: {length, number}/{limit, number}", "storage_template_settings": "ШайĐģĐžĐŊ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", - "storage_template_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€ĐžĐš ĐŋаĐŋĐžĐē и иĐŧĐĩĐŊĐĩĐŧ ĐˇĐ°ĐŗŅ€ŅƒĐļаĐĩĐŧĐžĐŗĐž Ņ„Đ°ĐšĐģа", - "storage_template_user_label": "{label} - ŅŅ‚Đž ĐŧĐĩŅ‚Đēа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", + "storage_template_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€ĐžĐš ĐŋаĐŋĐžĐē и иĐŧĐĩĐŊаĐŧи ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊҋ҅ Ņ„Đ°ĐšĐģОв", + "storage_template_user_label": "{label} — ĐŧĐĩŅ‚Đēа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "system_settings": "ĐĄĐ¸ŅŅ‚ĐĩĐŧĐŊŅ‹Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "tag_cleanup_job": "ĐžŅ‡Đ¸ŅŅ‚Đēа Ņ‚ĐĩĐŗĐ°", "template_email_available_tags": "В ŅŅ‚ĐžĐŧ ŅˆĐ°ĐąĐģĐžĐŊĐĩ Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹ ҁĐģĐĩĐ´ŅƒŅŽŅ‰Đ¸Đĩ ĐŋĐĩŅ€ĐĩĐŧĐĩĐŊĐŊŅ‹Đĩ: {tags}", @@ -264,16 +256,14 @@ "template_email_invite_album": "ШайĐģĐžĐŊ ĐŋŅ€Đ¸ĐŗĐģĐ°ŅˆĐĩĐŊĐ¸Ņ в аĐģŅŒĐąĐžĐŧ", "template_email_preview": "ĐŸŅ€ĐĩĐ´Đ˛Đ°Ņ€Đ¸Ņ‚ĐĩĐģҌĐŊŅ‹Đš ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€", "template_email_settings": "ШайĐģĐžĐŊŅ‹ ŅĐģ. ĐŋĐ¸ŅĐĩĐŧ", - "template_email_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšŅ‚Đĩ ŅˆĐ°ĐąĐģĐžĐŊŅ‹ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК ĐŋĐž ŅĐģ. ĐŋĐžŅ‡Ņ‚Đĩ", "template_email_update_album": "ШайĐģĐžĐŊ иСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ аĐģŅŒĐąĐžĐŧа", "template_email_welcome": "ШайĐģĐžĐŊ ĐŋŅ€Đ¸Đ˛ĐĩŅ‚ŅŅ‚Đ˛Đ¸Ņ", "template_settings": "ШайĐģĐžĐŊŅ‹ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", - "template_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšŅ‚Đĩ ŅˆĐ°ĐąĐģĐžĐŊŅ‹ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК.", + "template_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšŅ‚Đĩ ŅˆĐ°ĐąĐģĐžĐŊŅ‹ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", "theme_custom_css_settings": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēиĐĩ CSS", "theme_custom_css_settings_description": "ĐšĐ°ŅĐēадĐŊŅ‹Đĩ Ņ‚Đ°ĐąĐģĐ¸Ņ†Ņ‹ ŅŅ‚Đ¸ĐģĐĩĐš ĐŋОСвОĐģŅŅŽŅ‚ ĐŊĐ°ŅŅ‚Ņ€Đ°Đ¸Đ˛Đ°Ņ‚ŅŒ диСаКĐŊ Immich.", "theme_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Ņ‚ĐĩĐŧŅ‹", "theme_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēОК вĐĩĐą-иĐŊŅ‚ĐĩҀ҄ĐĩĐšŅĐ° Immich", - "these_files_matched_by_checksum": "Đ­Ņ‚Đ¸ Ņ„Đ°ĐšĐģŅ‹ ŅĐžĐŋĐžŅŅ‚Đ°Đ˛ĐģŅŅŽŅ‚ŅŅ ĐŋĐž Đ¸Ņ… ĐēĐžĐŊŅ‚Ņ€ĐžĐģҌĐŊŅ‹Đŧ ҁ҃ĐŧĐŧаĐŧ", "thumbnail_generation_job": "ХОСдаĐŊиĐĩ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€", "thumbnail_generation_job_description": "ХОСдаĐĩŅ‚ йОĐģŅŒŅˆĐ¸Đĩ, ĐŧаĐģĐĩĐŊҌĐēиĐĩ и Ņ€Đ°ĐˇĐŧҋ҂ҋĐĩ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ Đ´ĐģŅ ĐēаĐļĐ´ĐžĐŗĐž Ņ„Đ°ĐšĐģа и ҇ĐĩĐģОвĐĩĐēа", "transcoding_acceleration_api": "API ҃ҁĐēĐžŅ€Đ¸Ņ‚ĐĩĐģŅ", @@ -301,10 +291,9 @@ "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": "КодĐĩĐē HEVC", "transcoding_max_b_frames": "МаĐēŅĐ¸ĐŧаĐģҌĐŊĐž ĐŋŅ€ĐžĐŧĐĩĐļŅƒŅ‚ĐžŅ‡ĐŊҋ҅ ĐēĐ°Đ´Ņ€ĐžĐ˛", "transcoding_max_b_frames_description": "БоĐģĐĩĐĩ Đ˛Ņ‹ŅĐžĐēиĐĩ СĐŊĐ°Ņ‡ĐĩĐŊĐ¸Ņ ĐŋĐžĐ˛Ņ‹ŅˆĐ°ŅŽŅ‚ ŅŅ„Ņ„ĐĩĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚ŅŒ ҁĐļĐ°Ņ‚Đ¸Ņ, ĐŊĐž СаĐŧĐĩĐ´ĐģŅŅŽŅ‚ ĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ. МоĐļĐĩŅ‚ ĐąŅ‹Ņ‚ŅŒ ĐŊĐĩŅĐžĐ˛ĐŧĐĩŅŅ‚Đ¸ĐŧĐž ҁ аĐŋĐŋĐ°Ņ€Đ°Ņ‚ĐŊŅ‹Đŧ ҃ҁĐēĐžŅ€ĐĩĐŊиĐĩĐŧ ĐŊа ŅŅ‚Đ°Ņ€Ņ‹Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ…. 0 ĐžŅ‚ĐēĐģŅŽŅ‡Đ°ĐĩŅ‚ B-ĐēĐ°Đ´Ņ€Ņ‹, а -1 ŅƒŅŅ‚Đ°ĐŊавĐģиваĐĩŅ‚ ŅŅ‚Đž СĐŊĐ°Ņ‡ĐĩĐŊиĐĩ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи.", "transcoding_max_bitrate": "МаĐēŅĐ¸ĐŧаĐģҌĐŊŅ‹Đš ĐąĐ¸Ņ‚Ņ€ĐĩĐšŅ‚", @@ -342,8 +331,6 @@ "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 {# Đ´ĐĩĐŊҌ} few {# Đ´ĐŊŅ} many {# Đ´ĐŊĐĩĐš} other {# Đ´ĐŊŅ}}.", "user_delete_delay_settings": "ĐžŅ‚ĐģĐžĐļĐĩĐŊĐŊĐžĐĩ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩ", @@ -355,12 +342,12 @@ "user_password_has_been_reset": "ĐŸĐ°Ņ€ĐžĐģҌ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ ĐąŅ‹Đģ ŅĐąŅ€ĐžŅˆĐĩĐŊ:", "user_password_reset_description": "ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ŅŒŅ‚Đĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊŅ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅŽ и ŅĐžĐžĐąŅ‰Đ¸Ņ‚Đĩ ĐĩĐŧ҃, Ņ‡Ņ‚Đž ĐŋŅ€Đ¸ ҁĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐŧ Đ˛Ņ…ĐžĐ´Đĩ в ŅĐ¸ŅŅ‚ĐĩĐŧ҃ ĐŋĐ°Ņ€ĐžĐģҌ ĐŊ҃ĐļĐŊĐž ĐąŅƒĐ´ĐĩŅ‚ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ.", "user_restore_description": "АĐēĐēĐ°ŅƒĐŊŅ‚ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user} ĐąŅƒĐ´ĐĩŅ‚ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ.", - "user_restore_scheduled_removal": "Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиĐĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ – СаĐŋĐģаĐŊĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊĐžĐĩ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩ {date, date, long}", + "user_restore_scheduled_removal": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ (ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģҌĐŊĐžĐĩ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩ СаĐŋĐģаĐŊĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž ĐŊа {date, date, long})", "user_settings": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "user_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš", "user_successfully_removed": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ {email} ĐąŅ‹Đģ ҃ҁĐŋĐĩ҈ĐŊĐž ŅƒĐ´Đ°ĐģĐĩĐŊ.", "version_check_enabled_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃ ĐŊаĐģĐ¸Ņ‡Đ¸Ņ ĐŊĐžĐ˛Ņ‹Ņ… вĐĩŅ€ŅĐ¸Đš", - "version_check_implications": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đēи вĐĩŅ€ŅĐ¸Đ¸ ĐˇĐ°Đ˛Đ¸ŅĐ¸Ņ‚ ĐžŅ‚ ĐŋĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž вСаиĐŧОдĐĩĐšŅŅ‚Đ˛Đ¸Ņ ҁ github.com", + "version_check_implications": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đēи вĐĩŅ€ŅĐ¸Đ¸ ĐŋĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐĩҁĐēи ĐžĐąŅ€Đ°Ņ‰Đ°ĐĩŅ‚ŅŅ Đē ŅĐ°ĐšŅ‚Ņƒ github.com", "version_check_settings": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đēа вĐĩŅ€ŅĐ¸Đ¸", "version_check_settings_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ/ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ Đž ĐŊОвОК вĐĩŅ€ŅĐ¸Đ¸", "video_conversion_job": "ПĐĩŅ€ĐĩĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ видĐĩĐž", @@ -384,9 +371,9 @@ "advanced_settings_tile_subtitle": "Đ Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ‹Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "advanced_settings_troubleshooting_subtitle": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ€Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ‹Đĩ вОСĐŧĐžĐļĐŊĐžŅŅ‚Đ¸ Đ´ĐģŅ Ņ€Đĩ҈ĐĩĐŊĐ¸Ņ ĐŋŅ€ĐžĐąĐģĐĩĐŧ", "advanced_settings_troubleshooting_title": "Đ Đĩ҈ĐĩĐŊиĐĩ ĐŋŅ€ĐžĐąĐģĐĩĐŧ", - "age_months": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ {months, plural, one {# ĐŧĐĩŅŅŅ†} few {# ĐŧĐĩŅŅŅ†Đ°} many {# ĐŧĐĩŅŅŅ†Đĩв} other {# ĐŧĐĩŅŅŅ†Đ°}}", - "age_year_months": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ 1 ĐŗĐžĐ´, {months, plural, one {# ĐŧĐĩŅŅŅ†} few {# ĐŧĐĩŅŅŅ†Đ°} many {# ĐŧĐĩŅŅŅ†Đĩв} other {# ĐŧĐĩŅŅŅ†Đ°}}", - "age_years": "{years, plural, other {Đ’ĐžĐˇŅ€Đ°ŅŅ‚ #}}", + "age_months": "{months, plural, one {# ĐŧĐĩŅŅŅ†} many {# ĐŧĐĩŅŅŅ†Đĩв} other {# ĐŧĐĩŅŅŅ†Đ°}}", + "age_year_months": "1 ĐŗĐžĐ´ {months, plural, one {# ĐŧĐĩŅŅŅ†} many {# ĐŧĐĩŅŅŅ†Đĩв} other {# ĐŧĐĩŅŅŅ†Đ°}}", + "age_years": "{years, plural, one {# ĐŗĐžĐ´} many {# ĐģĐĩŅ‚} other {# ĐŗĐžĐ´Đ°}}", "album_added": "АĐģŅŒĐąĐžĐŧ дОйавĐģĐĩĐŊ", "album_added_notification_setting_description": "ПоĐģŅƒŅ‡Đ°Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ, ĐēĐžĐŗĐ´Đ° Đ˛Ņ‹ дОйавĐģĐĩĐŊŅ‹ Đē ĐžĐąŅ‰ĐĩĐŧ҃ аĐģŅŒĐąĐžĐŧ҃", "album_cover_updated": "ОбĐģĐžĐļĐēа аĐģŅŒĐąĐžĐŧа ОйĐŊОвĐģĐĩĐŊа", @@ -402,10 +389,6 @@ "album_remove_user": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ?", "album_remove_user_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}?", "album_share_no_users": "ĐŸĐžŅ…ĐžĐļĐĩ, Đ˛Ņ‹ ĐŋОдĐĩĐģиĐģĐ¸ŅŅŒ ŅŅ‚Đ¸Đŧ аĐģŅŒĐąĐžĐŧĐžĐŧ ŅĐž Đ˛ŅĐĩĐŧи ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧи иĐģи ҃ Đ˛Đ°Ņ ĐŊĐĩŅ‚ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš, ҁ ĐēĐžŅ‚ĐžŅ€Ņ‹Đŧи ĐŧĐžĐļĐŊĐž ĐŋОдĐĩĐģĐ¸Ņ‚ŅŒŅŅ.", - "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}", @@ -416,11 +399,14 @@ "album_viewer_appbar_share_err_remove": "ВозĐŊиĐēĐģи ĐŋŅ€ĐžĐąĐģĐĩĐŧŅ‹ ҁ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩĐŧ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ иС аĐģŅŒĐąĐžĐŧа", "album_viewer_appbar_share_err_title": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩиĐŧĐĩĐŊĐžĐ˛Đ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", "album_viewer_appbar_share_leave": "ПоĐēиĐŊŅƒŅ‚ŅŒ аĐģŅŒĐąĐžĐŧ", - "album_viewer_appbar_share_to": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ", + "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} аĐģŅŒĐąĐžĐŧа} many {{count, number} аĐģŅŒĐąĐžĐŧОв} other {{count, number} аĐģŅŒĐąĐžĐŧа}}", + "albums_default_sort_order": "ĐŸĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đēи в аĐģŅŒĐąĐžĐŧĐ°Ņ… ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ", + "albums_default_sort_order_description": "ПĐĩŅ€Đ˛ĐžĐŊĐ°Ņ‡Đ°ĐģҌĐŊŅ‹Đš ĐŋĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đēи, ŅƒŅŅ‚Đ°ĐŊавĐģиваĐĩĐŧŅ‹Đš в ĐŊĐžĐ˛Ņ‹Ņ… аĐģŅŒĐąĐžĐŧĐ°Ņ….", + "albums_feature_description": "КоĐģĐģĐĩĐēŅ†Đ¸Đ¸ Ņ„ĐžŅ‚Đž и видĐĩĐž, ĐēĐžŅ‚ĐžŅ€Ņ‹Đŧи ĐŧĐžĐļĐŊĐž Đ´ĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁ Đ´Ņ€ŅƒĐŗĐ¸Đŧи ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧи.", "all": "Đ’ŅĐĩ", "all_albums": "Đ’ŅĐĩ аĐģŅŒĐąĐžĐŧŅ‹", "all_people": "Đ’ŅĐĩ ĐģŅŽĐ´Đ¸", @@ -431,9 +417,9 @@ "allow_public_user_to_upload": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đŧ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧ ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ Ņ„Đ°ĐšĐģŅ‹", "alt_text_qr_code": "QR-ĐēОд", "anti_clockwise": "ĐŸŅ€ĐžŅ‚Đ¸Đ˛ Ņ‡Đ°ŅĐžĐ˛ĐžĐš", - "api_key": "API КĐģŅŽŅ‡", + "api_key": "API ĐēĐģŅŽŅ‡", "api_key_description": "Đ­Ņ‚Đž СĐŊĐ°Ņ‡ĐĩĐŊиĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋĐžĐēаСаĐŊĐž Ņ‚ĐžĐģҌĐēĐž ОдиĐŊ Ņ€Đ°Đˇ. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž ҁĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°Đģи ĐĩĐŗĐž ĐŋĐĩŅ€ĐĩĐ´ СаĐēŅ€Ņ‹Ņ‚Đ¸ĐĩĐŧ ĐžĐēĐŊа.", - "api_key_empty": "Đ’Đ°Ņˆ API ĐēĐģŅŽŅ‡ ĐŊĐĩ Đ´ĐžĐģĐļĐĩĐŊ ĐąŅ‹Ņ‚ŅŒ ĐŋŅƒŅŅ‚Ņ‹Đŧ", + "api_key_empty": "ИĐŧŅ API ĐēĐģŅŽŅ‡Đ° ĐŊĐĩ Đ´ĐžĐģĐļĐŊĐž ĐąŅ‹Ņ‚ŅŒ ĐŋŅƒŅŅ‚Ņ‹Đŧ", "api_keys": "КĐģŅŽŅ‡Đ¸ API", "app_bar_signout_dialog_content": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ Đ˛Ņ‹ĐšŅ‚Đ¸?", "app_bar_signout_dialog_ok": "Да", @@ -450,8 +436,8 @@ "archived_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŊĐĩҁґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŊĐĩҁĐĩĐŊĐž} 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": "ОĐŋĐ¸ŅĐ°ĐŊиĐĩ ОйĐŊОвĐģĐĩĐŊĐž", @@ -479,9 +465,12 @@ "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_cannot_be_added_to_album_count": "{count, plural, one {ĐžĐąŅŠĐĩĐēŅ‚ ĐŊĐĩ ĐŧĐžĐļĐĩŅ‚ ĐąŅ‹Ņ‚ŅŒ дОйавĐģĐĩĐŊ} other {ĐžĐąŅŠĐĩĐē҂ҋ ĐŊĐĩ ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ дОйавĐģĐĩĐŊŅ‹}} в аĐģŅŒĐąĐžĐŧ", "assets_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", "assets_deleted_permanently": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ŅƒĐ´Đ°ĐģĐĩĐŊĐž ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", "assets_deleted_permanently_from_server": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Immich", + "assets_downloaded_failed": "{count, plural, one {ĐĄĐēĐ°Ņ‡Đ°ĐŊ # Ņ„Đ°ĐšĐģ} many {ĐĄĐēĐ°Ņ‡Đ°ĐŊĐž # Ņ„Đ°ĐšĐģОв} other {ĐĄĐēĐ°Ņ‡Đ°ĐŊĐž # Ņ„Đ°ĐšĐģа}}, {error} - ŅĐąĐžĐš", + "assets_downloaded_successfully": "ĐŖŅĐŋĐĩ҈ĐŊĐž {count, plural, one {ҁĐēĐ°Ņ‡Đ°ĐŊ # Ņ„Đ°ĐšĐģ} many {ҁĐēĐ°Ņ‡Đ°ĐŊĐž # Ņ„Đ°ĐšĐģОв} other {ҁĐēĐ°Ņ‡Đ°ĐŊĐž # Ņ„Đ°ĐšĐģа}}", "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 {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}}", @@ -492,10 +481,11 @@ "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": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐŊŅ‹Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", + "assets_were_part_of_album_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}} ҃ĐļĐĩ в аĐģŅŒĐąĐžĐŧĐĩ", + "authorized_devices": "ĐĐ˛Ņ‚ĐžŅ€Đ¸ĐˇĐžĐ˛Đ°ĐŊĐŊŅ‹Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "automatic_endpoint_switching_subtitle": "ПодĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ ĐģĐžĐēаĐģҌĐŊĐž ĐŋĐž Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊОК ҁĐĩŅ‚Đ¸ и Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊŅ‹Đĩ Đ°Đ´Ņ€ĐĩŅĐ° в иĐŊĐžĐŧ ҁĐģŅƒŅ‡Đ°Đĩ", "automatic_endpoint_switching_title": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐ°Ņ ҁĐŧĐĩĐŊа URL", + "autoplay_slideshow": "ĐĐ˛Ņ‚ĐžĐ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ ҁĐģĐ°ĐšĐ´ŅˆĐžŅƒ", "back": "Назад", "back_close_deselect": "Назад, СаĐēŅ€Ņ‹Ņ‚ŅŒ иĐģи ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąĐžŅ€", "background_location_permission": "Đ”ĐžŅŅ‚ŅƒĐŋ Đē ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸ŅŽ в Ņ„ĐžĐŊĐĩ", @@ -539,6 +529,7 @@ "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": "ĐžŅŅ‚Đ°ĐģĐžŅŅŒ", @@ -547,7 +538,7 @@ "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_storage_format": "{used} иС {total}", "backup_controller_page_to_backup": "АĐģŅŒĐąĐžĐŧŅ‹ Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", "backup_controller_page_total_sub": "Đ’ŅĐĩ ҃ĐŊиĐēаĐģҌĐŊŅ‹Đĩ Ņ„ĐžŅ‚Đž и видĐĩĐž иС Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊҋ҅ аĐģŅŒĐąĐžĐŧОв", "backup_controller_page_turn_off": "Đ’Ņ‹ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ", @@ -567,30 +558,26 @@ "biometric_no_options": "БиоĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐĩҁĐēĐ°Ņ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊа", "biometric_not_available": "БиоĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐĩҁĐēĐ°Ņ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊа ĐŊа ŅŅ‚ĐžĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ", "birthdate_saved": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ ҃ҁĐŋĐĩ҈ĐŊĐž ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊа", - "birthdate_set_description": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ŅŅ Đ´ĐģŅ Ņ€Đ°ŅŅ‡ĐĩŅ‚Đ° Đ˛ĐžĐˇŅ€Đ°ŅŅ‚Đ° ŅŅ‚ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа ĐŊа ĐŧĐžĐŧĐĩĐŊŅ‚ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸.", + "birthdate_set_description": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ŅŅ Đ´ĐģŅ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐĩĐŊĐ¸Ņ Đ˛ĐžĐˇŅ€Đ°ŅŅ‚Đ° ҇ĐĩĐģОвĐĩĐēа ĐŊа ĐŧĐžĐŧĐĩĐŊŅ‚ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸.", "blurred_background": "РаСĐŧŅ‹Ņ‚Ņ‹Đš Ņ„ĐžĐŊ", - "bugs_and_feature_requests": "ĐžŅˆĐ¸ĐąĐēи и СаĐŋŅ€ĐžŅŅ‹", + "bugs_and_feature_requests": "ĐžŅˆĐ¸ĐąĐēи и ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐ¸Ņ", "build": "ĐĄĐąĐžŅ€Đēа", "build_image": "ВĐĩŅ€ŅĐ¸Ņ ŅĐąĐžŅ€Đēи", "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": "МиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ† йийĐģĐ¸ĐžŅ‚ĐĩĐēи ({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": "Đ”ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐĩŅŅ ĐžĐąŅŠĐĩĐē҂ҋ ({count})", - "cache_settings_image_cache_size": "РаСĐŧĐĩŅ€ ĐēŅŅˆĐ° Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК ({count} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛)", "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": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐēŅŅˆĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩĐŧ ĐŧОйиĐģҌĐŊĐžĐŗĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ", - "cache_settings_thumbnail_size": "РаСĐŧĐĩŅ€ ĐēŅŅˆĐ° ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€ ({count} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛)", "cache_settings_tile_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐģĐžĐēаĐģҌĐŊŅ‹Đŧ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰ĐĩĐŧ", "cache_settings_tile_title": "ЛоĐēаĐģҌĐŊĐžĐĩ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ", "cache_settings_title": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐēŅŅˆĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", @@ -604,6 +591,7 @@ "cannot_undo_this_action": "Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩĐģŅŒĐˇŅ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ!", "cannot_update_the_description": "НĐĩвОСĐŧĐžĐļĐŊĐž ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "cast": "ĐĸŅ€Đ°ĐŊҁĐģĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", + "cast_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа Đ´ĐžŅŅ‚ŅƒĐŋĐŊҋ҅ ҆ĐĩĐģĐĩĐš Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Đ¸Đ¸", "change_date": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", "change_description": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "change_display_order": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐžŅ€ŅĐ´ĐžĐē ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", @@ -618,10 +606,9 @@ "change_password_form_new_password": "ĐĐžĐ˛Ņ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ", "change_password_form_password_mismatch": "ĐŸĐ°Ņ€ĐžĐģи ĐŊĐĩ ŅĐžĐ˛ĐŋĐ°Đ´Đ°ŅŽŅ‚", "change_password_form_reenter_new_password": "ĐŸĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž ввĐĩĐ´Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Ņ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ", - "change_pin_code": "ИСĐŧĐĩĐŊĐĩĐŊиĐĩ PIN-ĐēОда", + "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 и ĐŋĐžŅĐģĐĩ ŅĐžĐˇĐ´Đ°ĐŊĐ¸Ņ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊОК ĐēĐžĐŋии Đ˛ŅĐĩŅ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛. ОĐŋĐĩŅ€Đ°Ņ†Đ¸Ņ ĐŧĐžĐļĐĩŅ‚ СаĐŊŅŅ‚ŅŒ ĐŊĐĩҁĐēĐžĐģҌĐēĐž ĐŧиĐŊŅƒŅ‚.", @@ -633,6 +620,7 @@ "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": "КĐģиĐĩĐŊ҂ҁĐēиК ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ иĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊ", @@ -654,24 +642,26 @@ "common_server_error": "ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ Đē ҁĐĩŅ‚Đ¸ и ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž Đ˛Đ°Ņˆ ҁĐĩŅ€Đ˛ĐĩŅ€ Đ´ĐžŅŅ‚ŅƒĐŋĐĩĐŊ, а вĐĩŅ€ŅĐ¸Đ¸ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ и ҁĐĩŅ€Đ˛ĐĩŅ€Đ° — ŅĐžĐ˛ĐŧĐĩŅŅ‚Đ¸ĐŧŅ‹.", "completed": "ЗавĐĩŅ€ŅˆĐĩĐŊĐž", "confirm": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚ŅŒ", - "confirm_admin_password": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", - "confirm_delete_face": "Đ’Ņ‹ Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐģĐ¸Ņ†Đž ҇ĐĩĐģОвĐĩĐēа {name} иС ĐžĐąŅŠĐĩĐēŅ‚Đ°?", + "confirm_admin_password": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", + "confirm_delete_face": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐģĐ¸Ņ†Đž ҇ĐĩĐģОвĐĩĐēа {name} иС ŅŅ‚ĐžĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚Đ°?", "confirm_delete_shared_link": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚Ņƒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅƒŅŽ ҁҁҋĐģĐē҃?", "confirm_keep_this_delete_others": "Đ’ŅĐĩ ĐžŅŅ‚Đ°ĐģҌĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ в ĐŗŅ€ŅƒĐŋĐŋĐĩ ĐąŅƒĐ´ŅƒŅ‚ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹, ĐēŅ€ĐžĐŧĐĩ ŅŅ‚ĐžĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚Đ°. Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ?", "confirm_new_pin_code": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Ņ‹Đš PIN-ĐēОд", "confirm_password": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ", + "confirm_tag_face": "Đ’Ņ‹ Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‚ĐŧĐĩŅ‚Đ¸Ņ‚ŅŒ ŅŅ‚ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа ĐēаĐē {name}?", + "confirm_tag_face_unnamed": "ĐĨĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‚ĐŧĐĩŅ‚Đ¸Ņ‚ŅŒ ŅŅ‚ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа?", + "connected_device": "ПодĐēĐģŅŽŅ‡ĐĩĐŊĐŊĐžĐĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž", "connected_to": "ПодĐēĐģŅŽŅ‡ĐĩĐŊĐž Đē", "contain": "ВĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ", "context": "КоĐŊŅ‚ĐĩĐēҁ҂", "continue": "ĐŸŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ", - "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": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁҁҋĐģĐēОК", - "control_bottom_app_bar_share_to": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ", + "control_bottom_app_bar_share_to": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁ...", "control_bottom_app_bar_trash_from_immich": "В ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", "copied_image_to_clipboard": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ҁĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧĐĩĐŊа.", "copied_to_clipboard": "ĐĄĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧĐĩĐŊа!", @@ -710,10 +700,14 @@ "current_server_address": "ĐĸĐĩĐēŅƒŅ‰Đ¸Đš Đ°Đ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "custom_locale": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēиК Ņ€ĐĩĐŗĐ¸ĐžĐŊ", "custom_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ Đ´Đ°Ņ‚ и Ņ‡Đ¸ŅĐĩĐģ в ĐˇĐ°Đ˛Đ¸ŅĐ¸ĐŧĐžŅŅ‚Đ¸ ĐžŅ‚ ŅĐˇŅ‹Đēа и Ņ€ĐĩĐŗĐ¸ĐžĐŊа", - "dark": "ĐĸŅ‘ĐŧĐŊŅ‹Đš", + "daily_title_text_date": "E, MMM dd", + "daily_title_text_date_year": "E, MMM dd, yyyy", + "dark": "ĐĸŅ‘ĐŧĐŊĐ°Ņ", + "darkTheme": "ПĐĩŅ€ĐĩĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ Ņ‚ĐĩĐŧĐŊОК Ņ‚ĐĩĐŧŅ‹", "date_after": "Đ”Đ°Ņ‚Đ° ĐŋĐžŅĐģĐĩ", "date_and_time": "Đ”Đ°Ņ‚Đ° и Đ’Ņ€ĐĩĐŧŅ", "date_before": "Đ”Đ°Ņ‚Đ° Đ´Đž", + "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ ҃ҁĐŋĐĩ҈ĐŊĐž ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊа", "date_range": "ДиаĐŋаСОĐŊ Đ´Đ°Ņ‚", "day": "ДĐĩĐŊҌ", @@ -726,7 +720,7 @@ "default_locale_description": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Ņ„ĐžŅ€ĐŧĐ°Ņ‚ Đ´Đ°Ņ‚Ņ‹ и Đ˛Ņ€ĐĩĐŧĐĩĐŊи в ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛Đ¸Đ¸ ҁ ŅĐˇŅ‹ĐēĐžĐ˛Ņ‹Đŧ ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐžĐŧ Đ˛Đ°ŅˆĐĩĐŗĐž ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°", "delete": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ", "delete_album": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", - "delete_api_key_prompt": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚ĐžŅ‚ ĐēĐģŅŽŅ‡ API?", + "delete_api_key_prompt": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚ĐžŅ‚ API ĐēĐģŅŽŅ‡?", "delete_dialog_alert": "Đ­Ņ‚Đ¸ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ ĐąŅƒĐ´ŅƒŅ‚ ĐąĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊĐž ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, а Ņ‚Đ°ĐēĐļĐĩ ҁ Đ˛Đ°ŅˆĐĩĐŗĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "delete_dialog_alert_local": "Đ­Ņ‚Đ¸ ĐžĐąŅŠĐĩĐē҂ҋ ĐąŅƒĐ´ŅƒŅ‚ ĐąĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊĐž ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҁ Đ˛Đ°ŅˆĐĩĐŗĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°, ĐŊĐž ĐŋĐž-ĐŋŅ€ĐĩĐļĐŊĐĩĐŧ҃ ĐąŅƒĐ´ŅƒŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ Immich", "delete_dialog_alert_local_non_backed_up": "Đ ĐĩСĐĩŅ€Đ˛ĐŊŅ‹Đĩ ĐēĐžĐŋии ĐŊĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŊĐĩ ĐąŅ‹Đģи ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊŅ‹ в Immich и ĐąŅƒĐ´ŅƒŅ‚ ĐąĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊĐž ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҁ Đ˛Đ°ŅˆĐĩĐŗĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", @@ -755,14 +749,15 @@ "direction": "НаĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ", "disabled": "ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐž", "disallow_edits": "ЗаĐŋŅ€ĐĩŅ‚Đ¸Ņ‚ŅŒ Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", - "discord": "ĐžĐąŅ‰ĐĩĐŊиĐĩ в Discord", + "discord": "ĐžĐąŅŅƒĐļĐ´ĐĩĐŊиĐĩ в Discord", "discover": "ОбĐŊĐ°Ņ€ŅƒĐļĐ¸Ņ‚ŅŒ", + "discovered_devices": "ОбĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐŊŅ‹Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "dismiss_all_errors": "ĐĄĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐžŅˆĐ¸ĐąĐēи", "dismiss_error": "ĐĄĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ ĐžŅˆĐ¸ĐąĐē҃", "display_options": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "display_order": "ĐŸĐžŅ€ŅĐ´ĐžĐē ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "display_original_photos": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģҌĐŊҋ҅ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš", - "display_original_photos_setting_description": "ĐŸŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°Ņ‚ŅŒ Đ¸ŅŅ…ĐžĐ´ĐŊŅƒŅŽ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅŽ ĐŋŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ Ņ€ĐĩŅŅƒŅ€ŅĐ° вĐŧĐĩŅŅ‚Đž ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹, ĐĩҁĐģи Đ¸ŅŅ…ĐžĐ´ĐŊŅ‹Đš Ņ€ĐĩŅŅƒŅ€Ņ ĐŋОддĐĩŅ€ĐļиваĐĩŅ‚ŅŅ. Đ­Ņ‚Đž ĐŧĐžĐļĐĩŅ‚ ҁĐŊĐ¸ĐˇĐ¸Ņ‚ŅŒ ҁĐēĐžŅ€ĐžŅŅ‚Đ¸ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš.", + "display_original_photos_setting_description": "ĐžŅ‚ĐēŅ€Ņ‹Đ˛Đ°Ņ‚ŅŒ ĐŋŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ вĐŧĐĩŅŅ‚Đž ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹, ĐĩҁĐģи Đ¸ŅŅ…ĐžĐ´ĐŊŅ‹Đš Ņ„ĐžŅ€ĐŧĐ°Ņ‚ ĐŋОддĐĩŅ€ĐļиваĐĩŅ‚ŅŅ ĐąŅ€Đ°ŅƒĐˇĐĩŅ€ĐžĐŧ. ВозĐŧĐžĐļĐŊĐž ҁĐŊиĐļĐĩĐŊиĐĩ ҁĐēĐžŅ€ĐžŅŅ‚Đ¸ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš.", "do_not_show_again": "НĐĩ ĐŋĐžĐēĐ°ĐˇŅ‹Đ˛Đ°Ņ‚ŅŒ ŅŅ‚Đž ŅĐžĐžĐąŅ‰ĐĩĐŊиĐĩ в даĐģҌĐŊĐĩĐšŅˆĐĩĐŧ", "documentation": "ДоĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ", "done": "Đ“ĐžŅ‚ĐžĐ˛Đž", @@ -772,7 +767,6 @@ "download_enqueue": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа в ĐžŅ‡ĐĩŅ€Đĩди", "download_error": "ĐžŅˆĐ¸ĐąĐēа ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи", "download_failed": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐŊĐĩ ŅƒĐ´Đ°ĐģĐ°ŅŅŒ", - "download_filename": "Ņ„Đ°ĐšĐģ: {filename}", "download_finished": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐžĐēĐžĐŊ҇ĐĩĐŊа", "download_include_embedded_motion_videos": "Đ’ŅŅ‚Ņ€ĐžĐĩĐŊĐŊŅ‹Đĩ видĐĩĐž", "download_include_embedded_motion_videos_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ видĐĩĐž, Đ˛ŅŅ‚Ņ€ĐžĐĩĐŊĐŊŅ‹Đĩ в ĐļĐ¸Đ˛Ņ‹Đĩ Ņ„ĐžŅ‚Đž, в видĐĩ ĐžŅ‚Đ´ĐĩĐģҌĐŊĐžĐŗĐž Ņ„Đ°ĐšĐģа", @@ -793,7 +787,7 @@ "duration": "ĐŸŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ĐĩĐģҌĐŊĐžŅŅ‚ŅŒ", "edit": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", "edit_album": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", - "edit_avatar": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ°Đ˛Đ°Ņ‚Đ°Ņ€", + "edit_avatar": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ°Đ˛Đ°Ņ‚Đ°Ņ€", "edit_date": "Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", "edit_date_and_time": "Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ и Đ˛Ņ€ĐĩĐŧŅ", "edit_description": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", @@ -835,6 +829,7 @@ "error_delete_face": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅƒĐ´Đ°ĐģĐĩĐŊии ĐģĐ¸Ņ†Đ° иС ĐžĐąŅŠĐĩĐēŅ‚Đ°", "error_loading_image": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "error_saving_image": "ĐžŅˆĐ¸ĐąĐēа: {error}", + "error_tag_face_bounding_box": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ дОйавĐģĐĩĐŊии ĐžŅ‚ĐŧĐĩŅ‚Đēи - ĐŊĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Ņ‹ Ņ€Đ°ĐŧĐēи ĐģĐ¸Ņ†Đ°", "error_title": "ĐžŅˆĐ¸ĐąĐēа - Đ§Ņ‚Đž-Ņ‚Đž ĐŋĐžŅˆĐģĐž ĐŊĐĩ Ņ‚Đ°Đē", "errors": { "cannot_navigate_next_asset": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩĐšŅ‚Đ¸ Đē ҁĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐŧ҃ ĐžĐąŅŠĐĩĐēŅ‚Ņƒ", @@ -847,7 +842,6 @@ "cant_get_number_of_comments": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐēĐžĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž ĐēĐžĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đĩв", "cant_search_people": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ ĐŋĐžĐ¸ŅĐē ĐģŅŽĐ´ĐĩĐš", "cant_search_places": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ ĐŋĐžĐ¸ŅĐē ĐŧĐĩҁ҂", - "cleared_jobs": "ĐžŅ‡Đ¸Ņ‰ĐĩĐŊĐŊŅ‹Đĩ СадаĐŊĐ¸Ņ Đ´ĐģŅ: {job}", "error_adding_assets_to_album": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ дОйавĐģĐĩĐŊии Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛ в аĐģŅŒĐąĐžĐŧ", "error_adding_users_to_album": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ дОйавĐģĐĩĐŊии ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš в аĐģŅŒĐąĐžĐŧ", "error_deleting_shared_user": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅƒĐ´Đ°ĐģĐĩĐŊии ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ ҁ ĐžĐąŅ‰Đ¸Đŧ Đ´ĐžŅŅ‚ŅƒĐŋĐžĐŧ", @@ -856,7 +850,6 @@ "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": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅƒŅŽ ҁҁҋĐģĐē҃", @@ -872,107 +865,98 @@ "failed_to_update_notification_status": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅŅ‚Đ°Ņ‚ŅƒŅ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ", "import_path_already_exists": "Đ­Ņ‚ĐžŅ‚ ĐŋŅƒŅ‚ŅŒ иĐŧĐŋĐžŅ€Ņ‚Đ° ҃ĐļĐĩ ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒĐĩŅ‚.", "incorrect_email_or_password": "НĐĩвĐĩŅ€ĐŊŅ‹Đš Đ°Đ´Ņ€Đĩҁ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Ņ‹ иĐģи ĐŋĐ°Ņ€ĐžĐģҌ", - "paths_validation_failed": "{paths, plural, one {# ĐŋŅƒŅ‚ŅŒ} other {# ĐŋŅƒŅ‚ĐĩĐš}} ĐŊĐĩ ĐŋŅ€ĐžŅˆĐģи ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃", - "profile_picture_transparent_pixels": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ĐŋŅ€ĐžŅ„Đ¸ĐģŅ ĐŊĐĩ ĐŧĐžĐŗŅƒŅ‚ ŅĐžĐ´ĐĩŅ€ĐļĐ°Ņ‚ŅŒ ĐŋŅ€ĐžĐˇŅ€Đ°Ņ‡ĐŊҋ҅ ĐŋиĐēҁĐĩĐģĐĩĐš. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ŅƒĐ˛ĐĩĐģĐ¸Ņ‡ŅŒŅ‚Đĩ и/иĐģи ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ.", + "paths_validation_failed": "{paths, plural, one {# ĐŋŅƒŅ‚ŅŒ ĐŊĐĩ ĐŋŅ€ĐžŅˆŅ‘Đģ} many {# ĐŋŅƒŅ‚ĐĩĐš ĐŊĐĩ ĐŋŅ€ĐžŅˆĐģи} 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_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 {ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ€ĐĩŅŅƒŅ€Ņ иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž}}", + "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_archive_unarchive": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ {archived, select, true {Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ} other {Ņ€Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ}}", "unable_to_change_album_user_role": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Ņ€ĐžĐģҌ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ в аĐģŅŒĐąĐžĐŧĐĩ", - "unable_to_change_date": "НĐĩвОСĐŧĐžĐļĐŊĐž иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", + "unable_to_change_date": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", "unable_to_change_description": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", - "unable_to_change_favorite": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅŅ‚Đ°Ņ‚ŅƒŅ \"Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ\" Đ´ĐģŅ Ņ€ĐĩŅŅƒŅ€ŅĐ°", - "unable_to_change_location": "НĐĩвОСĐŧĐžĐļĐŊĐž иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", - "unable_to_change_password": "НĐĩвОСĐŧĐžĐļĐŊĐž иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", + "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", - "unable_to_create_admin_account": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ ŅƒŅ‡ĐĩŅ‚ĐŊŅƒŅŽ СаĐŋĐ¸ŅŅŒ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", - "unable_to_create_api_key": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐŊĐžĐ˛Ņ‹Đš ĐēĐģŅŽŅ‡ API", + "unable_to_connect": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒŅŅ", + "unable_to_copy_to_clipboard": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ҁĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧĐĩĐŊа, ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž Đ˛Ņ‹ ĐŋĐžĐģŅƒŅ‡Đ°ĐĩŅ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đē ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đĩ ĐŋĐž ĐŋŅ€ĐžŅ‚ĐžĐēĐžĐģ҃ https", + "unable_to_create_admin_account": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ ŅƒŅ‡ĐĩŅ‚ĐŊŅƒŅŽ СаĐŋĐ¸ŅŅŒ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", + "unable_to_create_api_key": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐŊĐžĐ˛Ņ‹Đš API ĐēĐģŅŽŅ‡", "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_album": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", + "unable_to_delete_asset": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐēŅ‚", + "unable_to_delete_assets": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅƒĐ´Đ°ĐģĐĩĐŊии ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", + "unable_to_delete_exclusion_pattern": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅˆĐ°ĐąĐģĐžĐŊ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ", + "unable_to_delete_import_path": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋŅƒŅ‚ŅŒ иĐŧĐŋĐžŅ€Ņ‚Đ°", "unable_to_delete_shared_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_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": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅĐ˛ŅĐˇĐ°Ņ‚ŅŒ двиĐļŅƒŅ‰ĐĩĐĩŅŅ видĐĩĐž", - "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_move_to_locked_folder": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃", - "unable_to_play_video": "НĐĩвОСĐŧĐžĐļĐŊĐž Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩŅŅ‚Đ¸ видĐĩĐž", + "unable_to_link_motion_video": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐ˛ŅĐˇĐ°Ņ‚ŅŒ двиĐļŅƒŅ‰ĐĩĐĩŅŅ видĐĩĐž", + "unable_to_link_oauth_account": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋŅ€Đ¸Đ˛ŅĐˇĐ°Ņ‚ŅŒ ŅƒŅ‡ĐĩŅ‚ĐŊŅƒŅŽ СаĐŋĐ¸ŅŅŒ OAuth", + "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_refresh_user": "НĐĩвОСĐŧĐžĐļĐŊĐž ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", + "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_deleted_assets": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ°Đ˛Ņ‚ĐžĐŊĐžĐŧĐŊŅ‹Đĩ Ņ„Đ°ĐšĐģŅ‹", - "unable_to_remove_library": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", - "unable_to_remove_partner": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", - "unable_to_remove_reaction": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ€ĐĩаĐēŅ†Đ¸ŅŽ", - "unable_to_repair_items": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ", + "unable_to_remove_api_key": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ API ĐēĐģŅŽŅ‡", + "unable_to_remove_assets_from_shared_link": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ иС ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊОК ҁҁҋĐģĐēи", + "unable_to_remove_library": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", + "unable_to_remove_partner": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ°", + "unable_to_remove_reaction": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ€ĐĩаĐēŅ†Đ¸ŅŽ", "unable_to_reset_password": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", - "unable_to_reset_pin_code": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅĐąŅ€ĐžŅĐĩ PIN-ĐēОда", - "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_trash": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅĐžĐ´ĐĩŅ€ĐļиĐŧĐžĐĩ ĐēĐžŅ€ĐˇĐ¸ĐŊŅ‹", "unable_to_restore_user": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", - "unable_to_save_album": "НĐĩвОСĐŧĐžĐļĐŊĐž ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", - "unable_to_save_api_key": "НĐĩвОСĐŧĐžĐļĐŊĐž ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ API-ĐēĐģŅŽŅ‡", - "unable_to_save_date_of_birth": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ", - "unable_to_save_name": "НĐĩвОСĐŧĐžĐļĐŊĐž ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ иĐŧŅ", + "unable_to_save_album": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", + "unable_to_save_api_key": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ API ĐēĐģŅŽŅ‡", + "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_scan_libraries": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋŅ€ĐžŅĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ йийĐģĐ¸ĐžŅ‚ĐĩĐēи", + "unable_to_scan_library": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋŅ€ĐžŅĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", "unable_to_set_feature_photo": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅŽ ĐŊа ОйĐģĐžĐļĐē҃", - "unable_to_set_profile_picture": "НĐĩвОСĐŧĐžĐļĐŊĐž ŅƒŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ĐŋŅ€ĐžŅ„Đ¸ĐģŅ", - "unable_to_submit_job": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ СадаĐŊиĐĩ", + "unable_to_set_profile_picture": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Ņ„ĐžŅ‚Đž ĐŋŅ€ĐžŅ„Đ¸ĐģŅ", + "unable_to_submit_job": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ СадаĐŊиĐĩ", "unable_to_trash_asset": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐēŅ‚ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", - "unable_to_unlink_account": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐžŅ‚ŅĐžĐĩдиĐŊĐ¸Ņ‚ŅŒ ŅƒŅ‡ĐĩŅ‚ĐŊŅƒŅŽ СаĐŋĐ¸ŅŅŒ", - "unable_to_unlink_motion_video": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ĐžŅ‚ŅĐžĐĩдиĐŊĐ¸Ņ‚ŅŒ двиĐļŅƒŅ‰ĐĩĐĩŅŅ видĐĩĐž", + "unable_to_unlink_account": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐžŅ‚ŅĐžĐĩдиĐŊĐ¸Ņ‚ŅŒ ŅƒŅ‡Ņ‘Ņ‚ĐŊŅƒŅŽ СаĐŋĐ¸ŅŅŒ", + "unable_to_unlink_motion_video": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐžŅ‚ŅĐžĐĩдиĐŊĐ¸Ņ‚ŅŒ двиĐļŅƒŅ‰ĐĩĐĩŅŅ видĐĩĐž", "unable_to_update_album_cover": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ОйĐģĐžĐļĐē҃ аĐģŅŒĐąĐžĐŧа", - "unable_to_update_album_info": "НĐĩвОСĐŧĐžĐļĐŊĐž ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸ŅŽ Ой аĐģŅŒĐąĐžĐŧĐĩ", + "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_timeline_display_status": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅŅ‚Đ°Ņ‚ŅƒŅ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ĐŊа ҈ĐēаĐģĐĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊи", "unable_to_update_user": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", - "unable_to_upload_file": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ Ņ„Đ°ĐšĐģ" + "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": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ {months} ĐŧĐĩŅŅŅ†Đĩв", "exif_bottom_sheet_person_age_year_months": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ 1 ĐŗĐžĐ´, {months} ĐŧĐĩŅŅŅ†Đĩв", "exif_bottom_sheet_person_age_years": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ {years}", @@ -1020,10 +1004,12 @@ "folders": "ПаĐŋĐēи", "folders_feature_description": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ ĐŋаĐŋĐžĐē ҁ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧи и видĐĩĐž в Ņ„Đ°ĐšĐģОвОК ŅĐ¸ŅŅ‚ĐĩĐŧĐĩ", "forward": "ВĐŋĐĩŅ€Ņ‘Đ´", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Đ­Ņ‚ĐžŅ‚ Ņ„ŅƒĐŊĐēŅ†Đ¸ĐžĐŊаĐģ ҂ҀĐĩĐąŅƒĐĩŅ‚ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи вĐŊĐĩ҈ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐ˛ Google.", "general": "ĐžĐąŅ‰Đ¸Đĩ", "get_help": "ПоĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐŋĐžĐŧĐžŅ‰ŅŒ", "get_wifiname_error": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ иĐŧŅ Wi-Fi ҁĐĩŅ‚Đ¸. ĐŖĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž Đ˛Ņ‹ ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊŅ‹ Đē ҁĐĩŅ‚Đ¸ и ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛Đ¸Đģи ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧŅ‹Đĩ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐ¸Ņ", - "getting_started": "ĐŸŅ€Đ¸ŅŅ‚ŅƒĐŋĐ°Ņ Đē Ņ€Đ°ĐąĐžŅ‚Đĩ", + "getting_started": "ĐĄŅ‚Đ°Ņ€Ņ‚", "go_back": "Назад", "go_to_folder": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ в ĐŋаĐŋĐē҃", "go_to_search": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ Đē ĐŋĐžĐ¸ŅĐē҃", @@ -1068,6 +1054,7 @@ "home_page_upload_err_limit": "Đ’Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ ĐŧаĐēŅĐ¸Đŧ҃Đŧ 30 Ņ„Đ°ĐšĐģОв Са Ņ€Đ°Đˇ, ĐŋŅ€ĐžĐŋ҃ҁĐē", "host": "ĐĨĐžŅŅ‚", "hour": "Đ§Đ°Ņ", + "id": "ID", "ignore_icloud_photos": "ĐŸŅ€ĐžĐŋ҃ҁĐēĐ°Ņ‚ŅŒ Ņ„Đ°ĐšĐģŅ‹ иС iCloud", "ignore_icloud_photos_description": "НĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ Ņ„Đ°ĐšĐģŅ‹ в Immich, ĐĩҁĐģи ĐžĐŊи Ņ…Ņ€Đ°ĐŊŅŅ‚ŅŅ в iCloud", "image": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", @@ -1099,7 +1086,7 @@ "info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ", "interval": { "day_at_onepm": "КаĐļĐ´Ņ‹Đš Đ´ĐĩĐŊҌ в 13:00", - "hours": "КаĐļĐ´Ņ‹Đš {hours, plural, one {Ņ‡Đ°Ņ} few {# Ņ‡Đ°ŅĐ°} many {# Ņ‡Đ°ŅĐžĐ˛} other {# Ņ‡Đ°ŅĐ°}}", + "hours": "{hours, plural, one {КаĐļĐ´Ņ‹Đš Ņ‡Đ°Ņ} many {КаĐļĐ´Ņ‹Đĩ # Ņ‡Đ°ŅĐžĐ˛} other {КаĐļĐ´Ņ‹Đĩ # Ņ‡Đ°ŅĐ°}}", "night_at_midnight": "КаĐļĐ´ŅƒŅŽ ĐŊĐžŅ‡ŅŒ в ĐŋĐžĐģĐŊĐžŅ‡ŅŒ", "night_at_twoam": "КаĐļĐ´ŅƒŅŽ ĐŊĐžŅ‡ŅŒ в 2:00" }, @@ -1107,6 +1094,12 @@ "invalid_date_format": "НĐĩвĐĩŅ€ĐŊŅ‹Đš Ņ„ĐžŅ€ĐŧĐ°Ņ‚ Đ´Đ°Ņ‚Ņ‹", "invite_people": "ĐŸŅ€Đ¸ĐŗĐģĐ°ŅĐ¸Ņ‚ŅŒ", "invite_to_album": "ĐŸŅ€Đ¸ĐŗĐģĐ°ŅĐ¸Ņ‚ŅŒ в аĐģŅŒĐąĐžĐŧ", + "ios_debug_info_fetch_ran_at": "Đ’Ņ‹ĐąĐžŅ€Đēа СаĐŋŅƒŅ‰ĐĩĐŊа {dateTime}", + "ios_debug_info_last_sync_at": "ĐŸĐžŅĐģĐĩĐ´ĐŊŅŅ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ {dateTime}", + "ios_debug_info_no_processes_queued": "НĐĩŅ‚ Ņ„ĐžĐŊĐžĐ˛Ņ‹Ņ… ĐŋŅ€ĐžŅ†ĐĩŅŅĐžĐ˛ в ĐžŅ‡ĐĩŅ€Đĩди", + "ios_debug_info_no_sync_yet": "ФОĐŊĐžĐ˛Đ°Ņ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ Đĩ҉Đĩ ĐŊĐĩ Đ˛Ņ‹ĐŋĐžĐģĐŊŅĐģĐ°ŅŅŒ", + "ios_debug_info_processes_queued": "{count, plural, one {{count} Ņ„ĐžĐŊĐžĐ˛Ņ‹Đš ĐŋŅ€ĐžŅ†Đĩҁҁ} many {{count} Ņ„ĐžĐŊĐžĐ˛Ņ‹Ņ… ĐŋŅ€ĐžŅ†ĐĩŅŅĐžĐ˛} other {{count} Ņ„ĐžĐŊĐžĐ˛Ņ‹Ņ… ĐŋŅ€ĐžŅ†ĐĩŅŅĐ°}} в ĐžŅ‡ĐĩŅ€Đĩди", + "ios_debug_info_processing_ran_at": "ĐžĐąŅ€Đ°ĐąĐžŅ‚Đēа СаĐŋŅƒŅ‰ĐĩĐŊа {dateTime}", "items_count": "{count, plural, one {# ŅĐģĐĩĐŧĐĩĐŊŅ‚} many {# ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛} other {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°}}", "jobs": "Đ—Đ°Đ´Đ°Ņ‡Đ¸", "keep": "ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ", @@ -1115,9 +1108,12 @@ "kept_this_deleted_others": "ĐĄĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊ ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚ и {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}}", "keyboard_shortcuts": "ĐĄĐžŅ‡ĐĩŅ‚Đ°ĐŊĐ¸Ņ ĐēĐģĐ°Đ˛Đ¸Ņˆ", "language": "Đ¯ĐˇŅ‹Đē", + "language_no_results_subtitle": "ПоĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ ҁĐēĐžŅ€Ņ€ĐĩĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋĐžĐ¸ŅĐēĐžĐ˛Ņ‹Đš СаĐŋŅ€ĐžŅ", + "language_no_results_title": "Đ¯ĐˇŅ‹ĐēОв ĐŊĐĩ ĐŊаКдĐĩĐŊĐž", + "language_search_hint": "ĐŸĐžĐ¸ŅĐē ŅĐˇŅ‹ĐēОв...", "language_setting_description": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐĩĐŧŅ‹Đš ваĐŧи ŅĐˇŅ‹Đē", - "last_seen": "ĐŸĐžŅĐģĐĩĐ´ĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ ĐžŅŅƒŅ‰ĐĩŅŅ‚Đ˛ĐģŅĐģŅŅ", - "latest_version": "ĐŸĐžŅĐģĐĩĐ´ĐŊŅŅ ВĐĩŅ€ŅĐ¸Ņ", + "last_seen": "ĐŸĐžŅĐģĐĩĐ´ĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ", + "latest_version": "ĐŸĐžŅĐģĐĩĐ´ĐŊŅŅ вĐĩŅ€ŅĐ¸Ņ", "latitude": "Đ¨Đ¸Ņ€ĐžŅ‚Đ°", "leave": "ПоĐēиĐŊŅƒŅ‚ŅŒ", "lens_model": "МодĐĩĐģҌ ĐžĐąŅŠĐĩĐēŅ‚Đ¸Đ˛Đ°", @@ -1140,6 +1136,7 @@ "list": "ĐĄĐŋĐ¸ŅĐžĐē", "loading": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа", "loading_search_results_failed": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐ˛ ĐŋĐžĐ¸ŅĐēа ĐŊĐĩ ŅƒĐ´Đ°ĐģĐ°ŅŅŒ", + "local_asset_cast_failed": "НĐĩвОСĐŧĐžĐļĐŊĐž Ņ‚Ņ€Đ°ĐŊҁĐģĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐēŅ‚, ĐēĐžŅ‚ĐžŅ€Ņ‹Đš Đĩ҉ґ ĐŊĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€", "local_network": "ЛоĐēаĐģҌĐŊĐ°Ņ ҁĐĩŅ‚ŅŒ", "local_network_sheet_info": "ĐŸŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋОдĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ ĐŋĐž ŅŅ‚ĐžĐŧ҃ Đ°Đ´Ņ€Đĩҁ҃, ĐēĐžĐŗĐ´Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐž Đē Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊОК Wi-Fi ҁĐĩŅ‚Đ¸", "location_permission": "Đ”ĐžŅŅ‚ŅƒĐŋ Đē ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸ŅŽ", @@ -1152,13 +1149,16 @@ "lock": "ЗабĐģĐžĐēĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", "locked_folder": "Đ›Đ¸Ņ‡ĐŊĐ°Ņ ĐŋаĐŋĐēа", "log_out": "Đ’Ņ‹ĐšŅ‚Đ¸", - "log_out_all_devices": "Đ’Ņ‹ĐšĐ´Đ¸Ņ‚Đĩ иС ŅĐ¸ŅŅ‚ĐĩĐŧŅ‹ ŅĐž Đ˛ŅĐĩŅ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛", - "logged_out_all_devices": "Đ’Ņ‹ĐšŅ‚Đ¸ ĐŊа Đ˛ŅĐĩŅ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ…", - "logged_out_device": "Đ’Ņ‹ĐšŅ‚Đ¸ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ", + "log_out_all_devices": "ЗавĐĩŅ€ŅˆĐ¸Ņ‚ŅŒ ҁĐĩаĐŊҁҋ ĐŊа Đ˛ŅĐĩŅ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ…", + "logged_in_as": "ĐĐ˛Ņ‚ĐžŅ€Đ¸ĐˇĐžĐ˛Đ°ĐŊ ĐēаĐē {user}", + "logged_out_all_devices": "ЗавĐĩŅ€ŅˆĐĩĐŊŅ‹ Đ˛ŅĐĩ ҁĐĩаĐŊҁҋ, ĐēŅ€ĐžĐŧĐĩ Ņ‚ĐĩĐēŅƒŅ‰ĐĩĐŗĐž", + "logged_out_device": "ĐĄĐĩаĐŊҁ ĐŊа Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊĐžĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ СавĐĩŅ€ŅˆŅ‘ĐŊ", "login": "Đ’ĐžĐšŅ‚Đ¸", "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": "НĐĩĐēĐžŅ€Ņ€ĐĩĐēŅ‚ĐŊŅ‹Đš Đ°Đ´Ņ€Đĩҁ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Ņ‹", @@ -1168,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-Đ°Đ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", @@ -1176,21 +1176,21 @@ "login_has_been_disabled": "Đ’Ņ…ĐžĐ´ в ŅĐ¸ŅŅ‚ĐĩĐŧ҃ ĐąŅ‹Đģ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊ.", "login_password_changed_error": "ĐŸŅ€ĐžĐ¸ĐˇĐžŅˆĐģа ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ОйĐŊОвĐģĐĩĐŊии ĐŋĐ°Ņ€ĐžĐģŅ", "login_password_changed_success": "ĐŸĐ°Ņ€ĐžĐģҌ ҃ҁĐŋĐĩ҈ĐŊĐž ОйĐŊОвĐģĐĩĐŊ", - "logout_all_device_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ Đ˛Ņ‹ĐšŅ‚Đ¸ ĐŊа Đ˛ŅĐĩŅ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ…?", - "logout_this_device_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ Đ˛Ņ‹ĐšŅ‚Đ¸ ĐŊа Ņ‚ĐĩĐēŅƒŅ‰ĐĩĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ?", + "logout_all_device_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ СавĐĩŅ€ŅˆĐ¸Ņ‚ŅŒ Đ˛ŅĐĩ ҁĐĩаĐŊҁҋ, ĐēŅ€ĐžĐŧĐĩ Ņ‚ĐĩĐēŅƒŅ‰ĐĩĐŗĐž?", + "logout_this_device_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ СавĐĩŅ€ŅˆĐ¸Ņ‚ŅŒ ҁĐĩаĐŊҁ ĐŊа ŅŅ‚ĐžĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ?", "longitude": "ДоĐģĐŗĐžŅ‚Đ°", "look": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€", - "loop_videos": "ĐĻиĐēĐģĐ¸Ņ‡ĐĩҁĐēĐžĐĩ Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ", - "loop_videos_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ†Đ¸ĐēĐģĐ¸Ņ‡ĐĩҁĐēĐžĐĩ Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ видĐĩĐž.", - "main_branch_warning": "Đ’Ņ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚Đĩ вĐĩŅ€ŅĐ¸ŅŽ Đ´ĐģŅ Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Đēи; ĐŧŅ‹ ĐŊĐ°ŅŅ‚ĐžŅŅ‚ĐĩĐģҌĐŊĐž Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒĐĩĐŧ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Ņ€ĐĩĐģиСĐŊŅƒŅŽ вĐĩŅ€ŅĐ¸ŅŽ!", + "loop_videos": "ĐĻиĐēĐģĐ¸Ņ‡ĐĩҁĐēĐžĐĩ Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ видĐĩĐž", + "loop_videos_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēиК ĐŋĐžĐ˛Ņ‚ĐžŅ€ видĐĩĐž ĐŋŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ.", + "main_branch_warning": "Đ’Ņ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚Đĩ вĐĩŅ€ŅĐ¸ŅŽ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ Đ´ĐģŅ Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Đēи. ĐĐ°ŅŅ‚ĐžŅŅ‚ĐĩĐģҌĐŊĐž Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒĐĩŅ‚ŅŅ ĐŋĐĩŅ€ĐĩĐšŅ‚Đ¸ ĐŊа Ņ€ĐĩĐģиСĐŊŅƒŅŽ вĐĩŅ€ŅĐ¸ŅŽ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ!", "main_menu": "ГĐģавĐŊĐžĐĩ ĐŧĐĩĐŊŅŽ", "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_api_keys": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ API ĐēĐģŅŽŅ‡Đ°Đŧи Đ´ĐģŅ вСаиĐŧОдĐĩĐšŅŅ‚Đ˛Đ¸Ņ ҁ Đ´Ņ€ŅƒĐŗĐ¸Đŧи ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐŧаĐŧи", + "manage_your_devices": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Đŧи, ĐŊа ĐēĐžŅ‚ĐžŅ€Ņ‹Ņ… Đ˛Ņ‹ Đ˛Ņ…ĐžĐ´Đ¸Đģи в ŅĐ˛ĐžĐš аĐēĐēĐ°ŅƒĐŊŅ‚", "manage_your_oauth_connection": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŋОдĐēĐģŅŽŅ‡Ņ‘ĐŊĐŊĐžĐŗĐž OAuth", "map": "ĐšĐ°Ņ€Ņ‚Đ°", "map_assets_in_bound": "{count} Ņ„ĐžŅ‚Đž", @@ -1242,6 +1242,7 @@ "missing": "ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đĩ", "model": "МодĐĩĐģҌ", "month": "МĐĩŅŅŅ†", + "monthly_title_text_date_format": "MMMM y", "more": "БоĐģҌ҈Đĩ", "move": "ПĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ", "move_off_locked_folder": "ПĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ иС ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēи", @@ -1260,7 +1261,7 @@ "networking_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ", "never": "ĐŊиĐēĐžĐŗĐ´Đ°", "new_album": "ĐĐžĐ˛Ņ‹Đš аĐģŅŒĐąĐžĐŧ", - "new_api_key": "ĐĐžĐ˛Ņ‹Đš API-ĐēĐģŅŽŅ‡", + "new_api_key": "ĐĐžĐ˛Ņ‹Đš API ĐēĐģŅŽŅ‡", "new_password": "ĐĐžĐ˛Ņ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ", "new_person": "ĐĐžĐ˛Ņ‹Đš ҇ĐĩĐģОвĐĩĐē", "new_pin_code": "ĐĐžĐ˛Ņ‹Đš PIN-ĐēОд", @@ -1275,13 +1276,14 @@ "no_albums_with_name_yet": "ĐŸĐžŅ…ĐžĐļĐĩ, ҃ Đ˛Đ°Ņ ĐŋĐžĐēа ĐŊĐĩŅ‚ аĐģŅŒĐąĐžĐŧОв ҁ Ņ‚Đ°ĐēиĐŧ ĐŊаСваĐŊиĐĩĐŧ.", "no_albums_yet": "ĐŸĐžŅ…ĐžĐļĐĩ, ҃ Đ˛Đ°Ņ ĐŋĐžĐēа ĐŊĐĩŅ‚ аĐģŅŒĐąĐžĐŧОв.", "no_archived_assets_message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ŅƒĐšŅ‚Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ и видĐĩĐž, Ņ‡Ņ‚ĐžĐąŅ‹ ҁĐēŅ€Ņ‹Ņ‚ŅŒ Đ¸Ņ… ĐŋŅ€Đ¸ ĐžĐąŅ‰ĐĩĐŧ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ", - "no_assets_message": "ЩЁЛКНИĐĸЕ, ЧĐĸОБĐĢ Đ—ĐĐ“Đ ĐŖĐ—Đ˜ĐĸĐŦ ВАШЕ ПЕРВОЕ ФОĐĸО", + "no_assets_message": "НАЖМИĐĸЕ Đ”Đ›Đ¯ Đ—ĐĐ“Đ ĐŖĐ—ĐšĐ˜ ВАШЕГО ПЕРВОГО ФОĐĸО", "no_assets_to_show": "МĐĩдиа ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‚", + "no_cast_devices_found": "НĐĩ ĐŊаКдĐĩĐŊĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ Đ´ĐģŅ Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Đ¸Đ¸", "no_duplicates_found": "Đ”ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛ ĐŊĐĩ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐž.", "no_exif_info_available": "НĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸ exif", "no_explore_results_message": "Đ—Đ°ĐŗŅ€ŅƒĐļĐ°ĐšŅ‚Đĩ йОĐģҌ҈Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŊĐ°ŅĐģаĐļĐ´Đ°Ņ‚ŅŒŅŅ Đ˛Đ°ŅˆĐĩĐš ĐēĐžĐģĐģĐĩĐēŅ†Đ¸ĐĩĐš.", "no_favorites_message": "ДобавĐģŅĐšŅ‚Đĩ в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐąŅ‹ŅŅ‚Ņ€Đž ĐŊĐ°ĐšŅ‚Đ¸ ŅĐ˛ĐžĐ¸ ĐģŅƒŅ‡ŅˆĐ¸Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ и видĐĩĐž", - "no_libraries_message": "ĐĄĐžĐˇĐ´Đ°ĐšŅ‚Đĩ вĐŊĐĩ҈ĐŊŅŽŅŽ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃ Đ´ĐģŅ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° Đ˛Đ°ŅˆĐ¸Ņ… Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš и видĐĩĐž", + "no_libraries_message": "ĐĄĐžĐˇĐ´Đ°ĐšŅ‚Đĩ вĐŊĐĩ҈ĐŊŅŽŅŽ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃ Đ´ĐģŅ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° в Immich ŅŅ‚ĐžŅ€ĐžĐŊĐŊĐ¸Ņ… Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš и видĐĩĐž", "no_locked_photos_message": "Đ¤ĐžŅ‚Đž и видĐĩĐž, ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐŊŅ‹Đĩ в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃, ҁĐēҀҋ҂ҋ и ĐŊĐĩ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°ŅŽŅ‚ŅŅ ĐŋŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēи.", "no_name": "НĐĩŅ‚ иĐŧĐĩĐŊи", "no_notifications": "НĐĩŅ‚ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", @@ -1292,7 +1294,7 @@ "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": "ĐŸŅ€Đ¸ĐŧĐĩŅ‡Đ°ĐŊиĐĩ", "nothing_here_yet": "ЗдĐĩҁҌ ĐŋĐžĐēа ĐŊĐ¸Ņ‡ĐĩĐŗĐž ĐŊĐĩŅ‚", "notification_permission_dialog_content": "Đ§Ņ‚ĐžĐąŅ‹ вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ, ĐŋĐĩŅ€ĐĩĐšĐ´Đ¸Ņ‚Đĩ в ÂĢĐĐ°ŅŅ‚Ņ€ĐžĐšĐēиÂģ и Đ˛Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ÂĢĐ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒÂģ.", @@ -1302,17 +1304,18 @@ "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": "На ŅŅ‚ĐžĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ", "onboarding": "ĐĐ°Ņ‡Đ°ĐģĐž Ņ€Đ°ĐąĐžŅ‚Ņ‹", - "onboarding_privacy_description": "ĐĄĐģĐĩĐ´ŅƒŅŽŅ‰Đ¸Đĩ (ĐŊĐĩĐžĐąŅĐˇĐ°Ņ‚ĐĩĐģҌĐŊŅ‹Đĩ) Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ ĐˇĐ°Đ˛Đ¸ŅŅŅ‚ ĐžŅ‚ вĐŊĐĩ҈ĐŊĐ¸Ņ… ҁĐĩŅ€Đ˛Đ¸ŅĐžĐ˛ и ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊŅ‹ в ĐģŅŽĐąĐžĐĩ Đ˛Ņ€ĐĩĐŧŅ в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ… адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ.", - "onboarding_theme_description": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ Ņ†Đ˛ĐĩŅ‚ĐžĐ˛ŅƒŅŽ Ņ‚ĐĩĐŧ҃. Đ’Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐĩĐĩ ĐŋОСĐļĐĩ в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ….", - "onboarding_welcome_description": "Đ”Đ°Đ˛Đ°ĐšŅ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Đŧ Đ˛Đ°Ņˆ ŅĐēСĐĩĐŧĐŋĐģŅŅ€ ҁ ĐŊĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Đŧи ĐžĐąŅ‰Đ¸Đŧи ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€Đ°Đŧи.", + "onboarding_locale_description": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ŅĐˇŅ‹Đē ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ. ĐŸŅ€Đ¸ ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐžŅŅ‚Đ¸ ĐĩĐŗĐž ĐŋĐžŅ‚ĐžĐŧ ĐŧĐžĐļĐŊĐž ĐąŅƒĐ´ĐĩŅ‚ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ….", + "onboarding_privacy_description": "ĐĄĐģĐĩĐ´ŅƒŅŽŅ‰Đ¸Đĩ ĐŊĐĩĐžĐąŅĐˇĐ°Ņ‚ĐĩĐģҌĐŊŅ‹Đĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ ĐˇĐ°Đ˛Đ¸ŅŅŅ‚ ĐžŅ‚ вĐŊĐĩ҈ĐŊĐ¸Ņ… ҁĐĩŅ€Đ˛Đ¸ŅĐžĐ˛ и в ĐģŅŽĐąĐžĐĩ Đ˛Ņ€ĐĩĐŧŅ ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊŅ‹ в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ….", + "onboarding_server_welcome_description": "Đ”Đ°Đ˛Đ°ĐšŅ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Đŧ Đ˛Đ°Ņˆ ŅĐēСĐĩĐŧĐŋĐģŅŅ€ ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ ĐŊĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Ņ… ĐžĐąŅ‰Đ¸Ņ… ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€ĐžĐ˛.", + "onboarding_theme_description": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ Ņ‚ĐĩĐŧ҃. Đ’Ņ‹ Ņ‚Đ°ĐēĐļĐĩ ҁĐŧĐžĐļĐĩŅ‚Đĩ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐĩŅ‘ ĐŋОСĐļĐĩ в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ….", + "onboarding_user_welcome_description": "Đ”Đ°Đ˛Đ°ĐšŅ‚Đĩ ĐŊĐ°Ņ‡ĐŊĐĩĐŧ!", "onboarding_welcome_user": "Đ”ĐžĐąŅ€Đž ĐŋĐžĐļаĐģĐžĐ˛Đ°Ņ‚ŅŒ, {user}", "online": "Đ”ĐžŅŅ‚ŅƒĐŋĐĩĐŊ", "only_favorites": "ĐĸĐžĐģҌĐēĐž Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", @@ -1348,9 +1351,9 @@ "password_required": "ĐĸŅ€ĐĩĐąŅƒĐĩŅ‚ŅŅ ĐŋĐ°Ņ€ĐžĐģҌ", "password_reset_success": "ĐĄĐąŅ€ĐžŅ ĐŋĐ°Ņ€ĐžĐģŅ ҃ҁĐŋĐĩ҈ĐŊĐž Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊ", "past_durations": { - "days": "За {days, plural, one {Đ´ĐĩĐŊҌ} few {# Đ´ĐŊŅ} many {# Đ´ĐŊĐĩĐš} other {# Đ´ĐŊŅ}}", - "hours": "За {hours, plural, one {Ņ‡Đ°Ņ} few {# Ņ‡Đ°ŅĐ°} many {# Ņ‡Đ°ŅĐžĐ˛} other {# Ņ‡Đ°ŅĐ°}}", - "years": "За {years, plural, one {ĐŗĐžĐ´} few {# ĐŗĐžĐ´Đ°} many {# ĐģĐĩŅ‚} other {# ĐŗĐžĐ´Đ°}}" + "days": "За {days, plural, one {# Đ´ĐĩĐŊҌ} many {# Đ´ĐŊĐĩĐš} other {# Đ´ĐŊŅ}}", + "hours": "За {hours, plural, one {# Ņ‡Đ°Ņ} many {# Ņ‡Đ°ŅĐžĐ˛} other {# Ņ‡Đ°ŅĐ°}}", + "years": "За {years, plural, one {# ĐŗĐžĐ´} many {# ĐģĐĩŅ‚} other {# ĐŗĐžĐ´Đ°}}" }, "path": "ĐŸŅƒŅ‚ŅŒ", "pattern": "ШайĐģĐžĐŊ", @@ -1363,12 +1366,14 @@ "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 {ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚} many {ŅŅ‚Đ¸ # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {ŅŅ‚Đ¸ # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}? Đ­Ņ‚Đž Ņ‚Đ°ĐēĐļĐĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ {count, plural, one {ĐĩĐŗĐž} other {Đ¸Ņ…}} иС Đ˛ŅĐĩŅ… аĐģŅŒĐąĐžĐŧОв.", "permanently_deleted_asset": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", "permanently_deleted_assets_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}} ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", + "permission": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐ¸Ņ", + "permission_empty": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐ¸Ņ ĐŊĐĩ ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ ĐŋŅƒŅŅ‚Ņ‹Đŧи", "permission_onboarding_back": "Назад", "permission_onboarding_continue_anyway": "Đ’ŅĐĩ Ņ€Đ°Đ˛ĐŊĐž ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ", "permission_onboarding_get_started": "Đ”Đ°Đ˛Đ°ĐšŅ‚Đĩ ĐŊĐ°Ņ‡ĐŊŅ‘Đŧ", @@ -1382,7 +1387,7 @@ "person_hidden": "{name}{hidden, select, true { (ҁĐēҀҋ҂)} other {}}", "photo_shared_all_users": "ĐŸĐžŅ…ĐžĐļĐĩ, Ņ‡Ņ‚Đž Đ˛Ņ‹ ĐŋОдĐĩĐģиĐģĐ¸ŅŅŒ ŅĐ˛ĐžĐ¸Đŧи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧи ŅĐž Đ˛ŅĐĩĐŧи ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧи иĐģи ҃ Đ˛Đ°Ņ ĐŊĐĩŅ‚ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš, ҁ ĐēĐžŅ‚ĐžŅ€Ņ‹Đŧи ĐŧĐžĐļĐŊĐž ĐŋОдĐĩĐģĐ¸Ņ‚ŅŒŅŅ.", "photos": "Đ¤ĐžŅ‚Đž", - "photos_and_videos": "Đ¤ĐžŅ‚Đž и ВидĐĩĐž", + "photos_and_videos": "Đ¤ĐžŅ‚Đž и видĐĩĐž", "photos_count": "{count, plural, one {{count, number} Ņ„ĐžŅ‚Đž} other {{count, number} Ņ„ĐžŅ‚Đž}}", "photos_from_previous_years": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ ĐŋŅ€ĐžŅˆĐģҋ҅ ĐģĐĩŅ‚ в ŅŅ‚ĐžŅ‚ Đ´ĐĩĐŊҌ", "pick_a_location": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", @@ -1401,11 +1406,14 @@ "port": "ĐŸĐžŅ€Ņ‚", "preferences_settings_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа вĐŊĐĩ҈ĐŊĐĩĐŗĐž вида", "preferences_settings_title": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ", - "preset": "ĐŸŅ€ĐĩĐ´ŅƒŅŅ‚Đ°ĐŊОвĐēа", + "preset": "ĐŸŅ€ĐĩĐ´ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ‹Đĩ Đ˛Đ°Ņ€Đ¸Đ°ĐŊ҂ҋ", "preview": "ĐŸŅ€ĐĩĐ´Đ˛Đ°Ņ€Đ¸Ņ‚ĐĩĐģҌĐŊŅ‹Đš ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€", "previous": "ĐŸŅ€ĐĩĐ´Ņ‹Đ´ŅƒŅ‰ĐĩĐĩ", "previous_memory": "ĐŸŅ€ĐĩĐ´Ņ‹Đ´ŅƒŅ‰ĐĩĐĩ Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊиĐĩ", - "previous_or_next_photo": "ĐŸŅ€ĐĩĐ´Ņ‹Đ´ŅƒŅ‰Đ°Ņ иĐģи ҁĐģĐĩĐ´ŅƒŅŽŅ‰Đ°Ņ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ", + "previous_or_next_day": "ДĐĩĐŊҌ вĐŋĐĩŅ€Ņ‘Đ´/ĐŊаСад", + "previous_or_next_month": "МĐĩŅŅŅ† вĐŋĐĩŅ€Ņ‘Đ´/ĐŊаСад", + "previous_or_next_photo": "ĐŸŅ€ĐĩĐ´Ņ‹Đ´ŅƒŅ‰ĐĩĐĩ/ҁĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐĩ Ņ„ĐžŅ‚Đž", + "previous_or_next_year": "Год вĐŋĐĩŅ€Ņ‘Đ´/ĐŊаСад", "primary": "ГĐģавĐŊĐžĐĩ", "privacy": "КоĐŊŅ„Đ¸Đ´ĐĩĐŊŅ†Đ¸Đ°ĐģҌĐŊĐžŅŅ‚ŅŒ", "profile": "ĐŸŅ€ĐžŅ„Đ¸ĐģҌ", @@ -1413,10 +1421,11 @@ "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}", - "profile_picture_set": "ĐŖŅŅ‚Đ°ĐŊОвĐģĐĩĐŊа ĐēĐ°Ņ€Ņ‚Đ¸ĐŊĐēа ĐŋŅ€ĐžŅ„Đ¸ĐģŅ.", + "profile_picture_set": "Đ¤ĐžŅ‚Đž ĐŋŅ€ĐžŅ„Đ¸ĐģŅ ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž.", "public_album": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đš аĐģŅŒĐąĐžĐŧ", "public_share": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đš Đ´ĐžŅŅ‚ŅƒĐŋ", "purchase_account_info": "ПоддĐĩŅ€ĐļĐēа", @@ -1439,7 +1448,7 @@ "purchase_lifetime_description": "ЕдиĐŊĐžĐ˛Ņ€ĐĩĐŧĐĩĐŊĐŊĐ°Ņ ĐŋĐžĐē҃ĐŋĐēа", "purchase_option_title": "Đ’Đ°Ņ€Đ¸Đ°ĐŊ҂ҋ ĐŋĐžĐē҃ĐŋĐēи", "purchase_panel_info_1": "ХОСдаĐŊиĐĩ Immich ĐžŅ‚ĐŊиĐŧаĐĩŅ‚ ĐŧĐŊĐžĐŗĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊи и ŅƒŅĐ¸ĐģиК, и ҃ ĐŊĐ°Ņ ĐĩŅŅ‚ŅŒ ŅˆŅ‚Đ°Ņ‚ĐŊŅ‹Đĩ Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Ņ‡Đ¸Đēи, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ Ņ€Đ°ĐąĐžŅ‚Đ°ŅŽŅ‚ ĐŊад Ņ‚ĐĩĐŧ, Ņ‡Ņ‚ĐžĐąŅ‹ ŅĐ´ĐĩĐģĐ°Ņ‚ŅŒ ĐĩĐŗĐž ĐŊĐ°ŅŅ‚ĐžĐģҌĐēĐž Ņ…ĐžŅ€ĐžŅˆĐ¸Đŧ, ĐŊĐ°ŅĐēĐžĐģҌĐēĐž ŅŅ‚Đž вОСĐŧĐžĐļĐŊĐž. ĐĐ°ŅˆĐ° ĐŧĐ¸ŅŅĐ¸Ņ СаĐēĐģŅŽŅ‡Đ°ĐĩŅ‚ŅŅ в Ņ‚ĐžĐŧ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐŧĐŊĐžĐĩ ОйĐĩҁĐŋĐĩ҇ĐĩĐŊиĐĩ ҁ ĐžŅ‚ĐēҀҋ҂ҋĐŧ Đ¸ŅŅ…ĐžĐ´ĐŊŅ‹Đŧ ĐēОдОĐŧ и ŅŅ‚Đ¸Ņ‡ĐĩҁĐēиĐĩ ĐŧĐĩŅ‚ĐžĐ´Ņ‹ вĐĩĐ´ĐĩĐŊĐ¸Ņ йиСĐŊĐĩŅĐ° ŅŅ‚Đ°Đģи ŅƒŅŅ‚ĐžĐšŅ‡Đ¸Đ˛Ņ‹Đŧ Đ¸ŅŅ‚ĐžŅ‡ĐŊиĐēĐžĐŧ Đ´ĐžŅ…ĐžĐ´Đ° Đ´ĐģŅ Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Ņ‡Đ¸ĐēОв и Ņ‡Ņ‚ĐžĐąŅ‹ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ ŅĐēĐžŅĐ¸ŅŅ‚ĐĩĐŧ҃, ŅƒĐ˛Đ°ĐļĐ°ŅŽŅ‰ŅƒŅŽ ĐēĐžĐŊŅ„Đ¸Đ´ĐĩĐŊŅ†Đ¸Đ°ĐģҌĐŊĐžŅŅ‚ŅŒ, ҁ Ņ€ĐĩаĐģҌĐŊŅ‹Đŧи аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛Đ°Đŧи ŅĐēҁĐŋĐģŅƒĐ°Ņ‚Đ°Ņ‚ĐžŅ€ŅĐēиĐŧ ОйĐģĐ°Ņ‡ĐŊŅ‹Đŧ ҁĐĩŅ€Đ˛Đ¸ŅĐ°Đŧ.", - "purchase_panel_info_2": "ĐŸĐžŅĐēĐžĐģҌĐē҃ ĐŧŅ‹ ĐžĐąŅĐˇĐ°ĐģĐ¸ŅŅŒ ĐŊĐĩ дОйавĐģŅŅ‚ŅŒ ĐŋĐģĐ°Ņ‚ĐŊŅ‹Đĩ Đ´ĐžŅŅ‚ŅƒĐŋŅ‹, ŅŅ‚Đ° ĐŋĐžĐē҃ĐŋĐēа ĐŊĐĩ ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ ваĐŧ ĐŊиĐēаĐēĐ¸Ņ… Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊҋ҅ Ņ„ŅƒĐŊĐēŅ†Đ¸Đš в Immich. ĐœŅ‹ Ņ€Đ°ŅŅŅ‡Đ¸Ņ‚Ņ‹Đ˛Đ°ĐĩĐŧ ĐŊа Ņ‚Đ°ĐēĐ¸Ņ… ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš, ĐēаĐē Đ˛Ņ‹, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŋОддĐĩŅ€ĐļĐ¸Đ˛Đ°Ņ‚ŅŒ Ņ‚ĐĩĐēŅƒŅ‰ŅƒŅŽ Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Đē҃ Immich.", + "purchase_panel_info_2": "ĐŸĐžŅĐēĐžĐģҌĐē҃ ĐŧŅ‹ ĐžĐąŅĐˇĐ°ĐģĐ¸ŅŅŒ ĐŊĐĩ дОйавĐģŅŅ‚ŅŒ ĐŋĐģĐ°Ņ‚ĐŊŅ‹Đĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸, ŅŅ‚Đ° ĐŋĐžĐē҃ĐŋĐēа ĐŊĐĩ Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊҋ҅ вОСĐŧĐžĐļĐŊĐžŅŅ‚ĐĩĐš в Immich. ĐœŅ‹ Ņ€Đ°ŅŅŅ‡Đ¸Ņ‚Ņ‹Đ˛Đ°ĐĩĐŧ ĐŊа Ņ‚Đ°ĐēĐ¸Ņ… ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš ĐēаĐē Đ˛Ņ‹, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŋОддĐĩŅ€ĐļĐ¸Đ˛Đ°Ņ‚ŅŒ даĐģҌĐŊĐĩĐšŅˆŅƒŅŽ Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Đē҃ Immich.", "purchase_panel_title": "ПоддĐĩŅ€ĐļĐ¸Ņ‚Đĩ ĐŋŅ€ĐžĐĩĐēŅ‚", "purchase_per_server": "На ҁĐĩŅ€Đ˛ĐĩŅ€", "purchase_per_user": "На ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", @@ -1492,9 +1501,10 @@ "remove_from_shared_link": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊОК ҁҁҋĐģĐēи", "remove_memory": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊиĐĩ", "remove_photo_from_memory": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ„ĐžŅ‚Đž иС Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊĐ¸Ņ", + "remove_tag": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ‚ĐĩĐŗ", "remove_url": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ URL", "remove_user": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", - "removed_api_key": "ĐŖĐ´Đ°ĐģĐĩĐŊ ĐēĐģŅŽŅ‡ API: {name}", + "removed_api_key": "ĐŖĐ´Đ°ĐģŅ‘ĐŊ API ĐēĐģŅŽŅ‡ {name}", "removed_from_archive": "ĐŖĐ´Đ°ĐģĐĩĐŊ иС Đ°Ņ€Ņ…Đ¸Đ˛Đ°", "removed_from_favorites": "ĐŖĐ´Đ°ĐģĐĩĐŊĐž иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", "removed_from_favorites_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}} иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", @@ -1528,7 +1538,7 @@ "role_viewer": "Đ—Ņ€Đ¸Ņ‚ĐĩĐģҌ", "save": "ĐĄĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ", "save_to_gallery": "ĐĄĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ в ĐŗĐ°ĐģĐĩŅ€ĐĩŅŽ", - "saved_api_key": "ĐĄĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊĐŊŅ‹Đĩ ĐēĐģŅŽŅ‡Đ¸ API", + "saved_api_key": "API ĐēĐģŅŽŅ‡ иСĐŧĐĩĐŊŅ‘ĐŊ", "saved_profile": "ĐŸŅ€ĐžŅ„Đ¸ĐģҌ ŅĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊ", "saved_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊŅ‹", "say_something": "ĐĄĐēаĐļĐ¸Ņ‚Đĩ Ņ‡Ņ‚Đž-ĐŊĐ¸ĐąŅƒĐ´ŅŒ", @@ -1598,14 +1608,15 @@ "select_album_cover": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ОйĐģĐžĐļĐē҃ аĐģŅŒĐąĐžĐŧа", "select_all": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅĐĩ", "select_all_duplicates": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", - "select_avatar_color": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Ņ†Đ˛ĐĩŅ‚ Đ°Đ˛Đ°Ņ‚Đ°Ņ€Đ°", + "select_all_in": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅĐĩ в {group}", + "select_avatar_color": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ Ņ†Đ˛ĐĩŅ‚ Đ°Đ˛Đ°Ņ‚Đ°Ņ€Đ°", "select_face": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ĐģĐ¸Ņ†Đž", "select_featured_photo": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ Ņ„ĐžŅ‚Đž", "select_from_computer": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ҁ ĐēĐžĐŧĐŋŅŒŅŽŅ‚ĐĩŅ€Đ°", "select_keep_all": "ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ Đ˛ŅŅ‘ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊĐžĐĩ", - "select_library_owner": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ вĐģадĐĩĐģŅŒŅ†Đ° йийĐģĐ¸ĐžŅ‚ĐĩĐēи", + "select_library_owner": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ вĐģадĐĩĐģŅŒŅ†Đ° йийĐģĐ¸ĐžŅ‚ĐĩĐēи", "select_new_face": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ´Ņ€ŅƒĐŗĐžĐĩ ĐģĐ¸Ņ†Đž", - "select_person_to_tag": "Đ’Ņ‹Đ´ĐĩĐģĐ¸Ņ‚Đĩ ҇ĐĩĐģОвĐĩĐēа, ĐēĐžŅ‚ĐžŅ€ĐžĐŗĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‚ĐŧĐĩŅ‚Đ¸Ņ‚ŅŒ", + "select_person_to_tag": "Đ’Ņ‹Đ´ĐĩĐģĐ¸Ņ‚Đĩ ĐģĐ¸Ņ†Đž ҇ĐĩĐģОвĐĩĐēа, ĐēĐžŅ‚ĐžŅ€ĐžĐŗĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‚ĐŧĐĩŅ‚Đ¸Ņ‚ŅŒ", "select_photos": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸", "select_trash_all": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ˛ŅŅ‘ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊĐžĐĩ", "select_user_for_sharing_page_err_album": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", @@ -1618,15 +1629,17 @@ "server_info_box_server_url": "URL ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_offline": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐŊĐĩ в ҁĐĩŅ‚Đ¸", "server_online": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ в ҁĐĩŅ‚Đ¸", + "server_privacy": "КоĐŊŅ„Đ¸Đ´ĐĩĐŊŅ†Đ¸Đ°ĐģҌĐŊĐžŅŅ‚ŅŒ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_stats": "ĐĄŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đēа ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", - "server_version": "ВĐĩŅ€ŅĐ¸Ņ ĐĄĐĩŅ€Đ˛ĐĩŅ€Đ°", + "server_version": "ВĐĩŅ€ŅĐ¸Ņ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "set": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ", "set_as_album_cover": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ в ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đĩ ОйĐģĐžĐļĐēи аĐģŅŒĐąĐžĐŧа", "set_as_featured_photo": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐēаĐē ĐžŅĐŊОвĐŊĐžĐĩ Ņ„ĐžŅ‚Đž", "set_as_profile_picture": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐēаĐē Ņ„ĐžŅ‚Đž ĐŋŅ€ĐžŅ„Đ¸ĐģŅ", "set_date_of_birth": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ", - "set_profile_picture": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ĐŋŅ€ĐžŅ„Đ¸ĐģŅ", - "set_slideshow_to_fullscreen": "ПĐĩŅ€ĐĩвĐĩĐ´Đ¸Ņ‚Đĩ ҁĐģаКд-ŅˆĐžŅƒ в ĐŋĐžĐģĐŊĐžŅĐēŅ€Đ°ĐŊĐŊŅ‹Đš Ņ€ĐĩĐļиĐŧ", + "set_profile_picture": "ĐŖŅŅ‚Đ°ĐŊОвĐēа Ņ„ĐžŅ‚Đž ĐŋŅ€ĐžŅ„Đ¸ĐģŅ", + "set_slideshow_to_fullscreen": "ПĐĩŅ€ĐĩĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ҁĐģаКд-ŅˆĐžŅƒ в ĐŋĐžĐģĐŊĐžŅĐēŅ€Đ°ĐŊĐŊŅ‹Đš Ņ€ĐĩĐļиĐŧ", + "set_stack_primary_asset": "НазĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ ĐžŅĐŊОвĐŊŅ‹Đŧ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐŧ", "setting_image_viewer_help": "ĐŸŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ҁĐŋĐĩŅ€Đ˛Đ° ĐˇĐ°ĐŗŅ€ŅƒĐļаĐĩŅ‚ŅŅ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ°, ĐˇĐ°Ņ‚ĐĩĐŧ ҃ĐŧĐĩĐŊҌ҈ĐĩĐŊĐŊĐžĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ҁҀĐĩĐ´ĐŊĐĩĐŗĐž ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đ° (ĐĩҁĐģи вĐēĐģŅŽŅ‡ĐĩĐŊĐž), а ĐˇĐ°Ņ‚ĐĩĐŧ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ (ĐĩҁĐģи вĐēĐģŅŽŅ‡ĐĩĐŊĐž).", "setting_image_viewer_original_subtitle": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Đ´ĐģŅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи Đ¸ŅŅ…ĐžĐ´ĐŊĐžĐŗĐž Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ в ĐŋĐžĐģĐŊĐžĐŧ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊии (йОĐģŅŒŅˆĐžĐĩ!). ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Đ´ĐģŅ ҃ĐŧĐĩĐŊҌ҈ĐĩĐŊĐ¸Ņ ĐžĐąŅŠŅ‘Đŧа даĐŊĐŊҋ҅ (ĐēаĐē ҁĐĩŅ‚Đ¸, Ņ‚Đ°Đē и ĐēŅŅˆĐ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°).", "setting_image_viewer_original_title": "Đ—Đ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ Đ¸ŅŅ…ĐžĐ´ĐŊĐžĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ", @@ -1635,7 +1648,6 @@ "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": "ĐŊĐĩĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐž", @@ -1698,6 +1710,7 @@ "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": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đĩ ҁҁҋĐģĐēи", @@ -1738,7 +1751,7 @@ "sidebar": "БоĐēĐžĐ˛Đ°Ņ ĐŋаĐŊĐĩĐģҌ", "sidebar_display_description": "ПоĐēĐ°ĐˇŅ‹Đ˛Đ°Ņ‚ŅŒ ҁҁҋĐģĐē҃ ĐŊа ĐŋŅ€ĐĩĐ´ŅŅ‚Đ°Đ˛ĐģĐĩĐŊиĐĩ в йОĐēОвОК ĐŋаĐŊĐĩĐģи", "sign_out": "Đ’Ņ‹Ņ…ĐžĐ´", - "sign_up": "Đ’ĐžĐšŅ‚Đ¸", + "sign_up": "Đ—Đ°Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒŅŅ", "size": "РаСĐŧĐĩŅ€", "skip_to_content": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ Đē ŅĐžĐ´ĐĩŅ€ĐļаĐŊĐ¸ŅŽ", "skip_to_folders": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ Đē ĐŋаĐŋĐēаĐŧ", @@ -1764,14 +1777,15 @@ "start_date": "Đ”Đ°Ņ‚Đ° ĐŊĐ°Ņ‡Đ°Đģа", "state": "Đ ĐĩĐŗĐ¸ĐžĐŊ", "status": "ĐĄĐžŅŅ‚ĐžŅĐŊиĐĩ", + "stop_casting": "ĐžŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Đ¸ŅŽ", "stop_motion_photo": "ПоĐēĐ°Đ´Ņ€ĐžĐ˛Đ°Ņ аĐŊиĐŧĐ°Ņ†Đ¸Ņ", "stop_photo_sharing": "ЗаĐēŅ€Ņ‹Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ° Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚Đž?", "stop_photo_sharing_description": "{partner} йОĐģҌ҈Đĩ ĐŊĐĩ ҁĐŧĐžĐļĐĩŅ‚ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧ.", "stop_sharing_photos_with_user": "ĐŸŅ€ĐĩĐēŅ€Đ°Ņ‚Đ¸Ņ‚ŅŒ Đ´ĐĩĐģĐ¸Ņ‚ŅŒŅŅ ŅĐ˛ĐžĐ¸Đŧи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧи ҁ ŅŅ‚Đ¸Đŧ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐŧ", "storage": "ĐĨŅ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ", - "storage_label": "ĐĸĐĩĐŗ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", + "storage_label": "МĐĩŅ‚Đēа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", "storage_quota": "ĐšĐ˛ĐžŅ‚Đ° Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", - "storage_usage": "{used} иС {available} Đ´ĐžŅŅ‚ŅƒĐŋĐŊҋ҅", + "storage_usage": "{used} иС {available}", "submit": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚ŅŒ", "suggestions": "ĐŸŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐ¸Ņ", "sunrise_on_the_beach": "Đ’ĐžŅŅ…ĐžĐ´ ŅĐžĐģĐŊŅ†Đ° ĐŊа ĐŋĐģŅĐļĐĩ", @@ -1821,9 +1835,8 @@ "to_parent": "ВĐĩŅ€ĐŊŅƒŅ‚ŅŒŅŅ ĐŊаСад", "to_trash": "ĐšĐžŅ€ĐˇĐ¸ĐŊа", "toggle_settings": "ПĐĩŅ€ĐĩĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐĩĐē", - "toggle_theme": "ПĐĩŅ€ĐĩĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ Ņ‚ĐĩĐŧŅ‹", "total": "Đ’ŅĐĩĐŗĐž", - "total_usage": "ĐžĐąŅ‰ĐĩĐĩ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ", + "total_usage": "ĐžĐąŅ‰Đ°Ņ ŅŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đēа", "trash": "ĐšĐžŅ€ĐˇĐ¸ĐŊа", "trash_all": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ˛ŅŅ‘", "trash_count": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, number}", @@ -1837,12 +1850,13 @@ "trash_page_restore_all": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ", "trash_page_select_assets_btn": "Đ’Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ", "trash_page_title": "ĐšĐžŅ€ĐˇĐ¸ĐŊа ({count})", - "trashed_items_will_be_permanently_deleted_after": "Đ­ĐģĐĩĐŧĐĩĐŊ҂ҋ в ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҇ĐĩŅ€ĐĩС {days, plural, one {# Đ´ĐĩĐŊҌ} other {# Đ´ĐŊĐĩĐš}}.", + "trashed_items_will_be_permanently_deleted_after": "ĐžĐąŅŠĐĩĐē҂ҋ в ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҇ĐĩŅ€ĐĩС {days, plural, one {# Đ´ĐĩĐŊҌ} many {# Đ´ĐŊĐĩĐš} other {# Đ´ĐŊŅ}}.", "type": "ĐĸиĐŋ", "unable_to_change_pin_code": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ иСĐŧĐĩĐŊĐĩĐŊии PIN-ĐēОда", "unable_to_setup_pin_code": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅĐžĐˇĐ´Đ°ĐŊии PIN-ĐēОда", "unarchive": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ", "unarchived_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ Đ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‰Ņ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ Đ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‰ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° Đ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‰ĐĩĐŊĐž}} иС Đ°Ņ€Ņ…Đ¸Đ˛Đ°", + "undo": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ", "unfavorite": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", "unhide_person": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ҇ĐĩĐģОвĐĩĐēа", "unknown": "НĐĩиСвĐĩҁ҂ĐŊĐž", @@ -1856,16 +1870,15 @@ "unnamed_album": "АĐģŅŒĐąĐžĐŧ ĐąĐĩС ĐŊаСваĐŊĐ¸Ņ", "unnamed_album_delete_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚ĐžŅ‚ аĐģŅŒĐąĐžĐŧ?", "unnamed_share": "ĐžĐąŅ‰Đ¸Đš Đ´ĐžŅŅ‚ŅƒĐŋ ĐąĐĩС ĐŊаСваĐŊĐ¸Ņ", - "unsaved_change": "НĐĩ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊĐŊĐžĐĩ иСĐŧĐĩĐŊĐĩĐŊиĐĩ", + "unsaved_change": "НĐĩŅĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊĐŊĐžĐĩ иСĐŧĐĩĐŊĐĩĐŊиĐĩ", "unselect_all": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ‹Đ´ĐĩĐģĐĩĐŊиĐĩ", "unselect_all_duplicates": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąĐžŅ€ Đ˛ŅĐĩŅ… Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛", + "unselect_all_in": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ‹Đ´ĐĩĐģĐĩĐŊиĐĩ в {group}", "unstack": "Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", "unstacked_assets_count": "{count, plural, one {Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊ # ĐžĐąŅŠĐĩĐēŅ‚} many {Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", - "untracked_files": "НЕОĐĸСЛЕЖИВАЕМĐĢЕ ФАЙЛĐĢ", - "untracked_files_decription": "ĐŸŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐŊĐĩ ĐžŅ‚ŅĐģĐĩĐļиваĐĩŅ‚ ŅŅ‚Đ¸ Ņ„Đ°ĐšĐģŅ‹. ОĐŊи ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐŧ ĐŊĐĩŅƒĐ´Đ°Ņ‡ĐŊҋ҅ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊиК, ĐŋŅ€ĐĩŅ€Đ˛Đ°ĐŊĐŊҋ҅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐžĐē иĐģи ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊŅ‹ иС-Са ĐžŅˆĐ¸ĐąĐēи", "up_next": "ĐĄĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐĩ", "updated_at": "ОбĐŊОвĐģŅ‘ĐŊ", - "updated_password": "ĐŸĐ°Ņ€ĐžĐģҌ ОйĐŊОвĐģŅ‘ĐŊ", + "updated_password": "ĐŸĐ°Ņ€ĐžĐģҌ иСĐŧĐĩĐŊŅ‘ĐŊ", "upload": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ", "upload_concurrency": "ĐŸĐ°Ņ€Đ°ĐģĐģĐĩĐģҌĐŊĐžŅŅ‚ŅŒ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи", "upload_dialog_info": "ĐĨĐžŅ‚Đ¸Ņ‚Đĩ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€?", @@ -1879,20 +1892,22 @@ "upload_success": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐŋŅ€ĐžŅˆĐģа ҃ҁĐŋĐĩ҈ĐŊĐž. ОбĐŊĐžĐ˛Đ¸Ņ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ, Ņ‡Ņ‚ĐžĐąŅ‹ ŅƒĐ˛Đ¸Đ´ĐĩŅ‚ŅŒ ĐŊĐžĐ˛Ņ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ.", "upload_to_immich": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа в Immich ({count})", "uploading": "Đ—Đ°ĐŗŅ€ŅƒĐļаĐĩŅ‚ŅŅ", + "url": "URL", "usage": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ", "use_biometric": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ йиОĐŧĐĩŅ‚Ņ€Đ¸ŅŽ", "use_current_connection": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Ņ‚ĐĩĐēŅƒŅ‰ĐĩĐĩ ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ", "use_custom_date_range": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēиК диаĐŋаСОĐŊ Đ´Đ°Ņ‚", "user": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ", - "user_has_been_deleted": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ ĐąŅ‹Đģ ŅƒĐ´Đ°ĐģŅ‘ĐŊ.", + "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_pin_code_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа PIN-ĐēОда Đ´ĐģŅ Đ´ĐžŅŅ‚ŅƒĐŋа Đē ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēĐĩ", + "user_privacy": "КоĐŊŅ„Đ¸Đ´ĐĩĐŊŅ†Đ¸Đ°ĐģҌĐŊĐžŅŅ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "user_purchase_settings": "ПоĐē҃ĐŋĐēа", "user_purchase_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋĐžĐē҃ĐŋĐēОК", "user_role_set": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ {user} в ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đĩ {role}", - "user_usage_detail": "ĐŸĐžĐ´Ņ€ĐžĐąĐŊĐ°Ņ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Ой Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊии ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐŧ", + "user_usage_detail": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ ĐŋĐž ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧ", "user_usage_stats": "ĐĄŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đēа Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊĐ¸Ņ аĐēĐēĐ°ŅƒĐŊŅ‚Đ°", "user_usage_stats_description": "ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ ŅŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đē҃ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊĐ¸Ņ аĐēĐēĐ°ŅƒĐŊŅ‚Đ°", "username": "ИĐŧŅ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", @@ -1904,16 +1919,11 @@ "version": "ВĐĩŅ€ŅĐ¸Ņ", "version_announcement_closing": "ĐĸвОК Đ´Ņ€ŅƒĐŗ АĐģĐĩĐēҁ", "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_title": "Đ”ĐžŅŅ‚ŅƒĐŋĐŊа ĐŊĐžĐ˛Đ°Ņ вĐĩŅ€ŅĐ¸Ņ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° 🎉", "version_history": "Đ˜ŅŅ‚ĐžŅ€Đ¸Ņ вĐĩŅ€ŅĐ¸Đš", "version_history_item": "ВĐĩŅ€ŅĐ¸Ņ {version} ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊа {date}", "video": "ВидĐĩĐž", - "video_hover_setting": "Đ’ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ видĐĩĐž ĐŋŅ€Đ¸ ĐŊавĐĩĐ´ĐĩĐŊии ĐēŅƒŅ€ŅĐžŅ€Đ° ĐŧŅ‹ŅˆĐ¸", - "video_hover_setting_description": "Đ’ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸Ņ‚ŅŒ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ видĐĩĐž ĐŋŅ€Đ¸ ĐŊавĐĩĐ´ĐĩĐŊии ĐēŅƒŅ€ŅĐžŅ€Đ° ĐŧŅ‹ŅˆĐ¸ ĐŊа ĐžĐąŅŠĐĩĐēŅ‚. ДаĐļĐĩ ĐĩҁĐģи ŅŅ‚ĐžŅ‚ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊ, Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ ĐŧĐžĐļĐŊĐž СаĐŋŅƒŅŅ‚Đ¸Ņ‚ŅŒ, ĐŊавĐĩĐ´Ņ ĐēŅƒŅ€ŅĐžŅ€ ĐŊа СĐŊĐ°Ņ‡ĐžĐē Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊĐ¸Ņ.", + "video_hover_setting": "Đ’ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ видĐĩĐž ĐŋŅ€Đ¸ ĐŊавĐĩĐ´ĐĩĐŊии ĐēŅƒŅ€ŅĐžŅ€Đ° ĐŧŅ‹ŅˆĐ¸", + "video_hover_setting_description": "Đ’ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸Ņ‚ŅŒ видĐĩĐž ĐŋŅ€Đ¸ ĐŊавĐĩĐ´ĐĩĐŊии ĐēŅƒŅ€ŅĐžŅ€Đ° ĐŧŅ‹ŅˆĐ¸ ĐŊа ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņƒ. ДаĐļĐĩ ĐĩҁĐģи ŅŅ‚Đ° Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ Đ˛Ņ‹ĐēĐģŅŽŅ‡ĐĩĐŊа, Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ ĐŧĐžĐļĐŊĐž СаĐŋŅƒŅŅ‚Đ¸Ņ‚ŅŒ, ĐŊавĐĩĐ´Ņ ĐēŅƒŅ€ŅĐžŅ€ ĐŊа СĐŊĐ°Ņ‡ĐžĐē Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊĐ¸Ņ.", "videos": "ВидĐĩĐž", "videos_count": "{count, plural, one {# видĐĩĐž} other {# видĐĩĐž}}", "view": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€", @@ -1928,6 +1938,7 @@ "view_previous_asset": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŋŅ€ĐĩĐ´Ņ‹Đ´ŅƒŅ‰Đ¸Đš ĐžĐąŅŠĐĩĐēŅ‚", "view_qr_code": "ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ QR ĐēОд", "view_stack": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ҁ҂ĐĩĐē", + "view_user": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "viewer_remove_from_stack": "ĐŖĐąŅ€Đ°Ņ‚ŅŒ иС ĐŗŅ€ŅƒĐŋĐŋŅ‹", "viewer_stack_use_as_main_asset": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ в ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đĩ ĐžŅĐŊОвĐŊĐžĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚Đ°", "viewer_unstack": "Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", @@ -1940,7 +1951,7 @@ "wifi_name": "ИĐŧŅ ҁĐĩŅ‚Đ¸", "wrong_pin_code": "НĐĩвĐĩŅ€ĐŊŅ‹Đš PIN-ĐēОд", "year": "Год", - "years_ago": "{years, plural, one {# ĐŗĐžĐ´} few {# ĐŗĐžĐ´Đ°} many {# ĐģĐĩŅ‚} other {# ĐģĐĩŅ‚}} ĐŊаСад", + "years_ago": "{years, plural, one {# ĐŗĐžĐ´} few {# ĐŗĐžĐ´Đ°} many {# ĐģĐĩŅ‚} other {# ĐŗĐžĐ´Đ°}} ĐŊаСад", "yes": "Да", "you_dont_have_any_shared_links": "ĐŖ Đ˛Đ°Ņ ĐŊĐĩŅ‚ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊҋ҅ ҁҁҋĐģĐžĐē", "your_wifi_name": "ИĐŧŅ Đ˛Đ°ŅˆĐĩĐš Wi-Fi ҁĐĩŅ‚Đ¸", diff --git a/i18n/sk.json b/i18n/sk.json index 4253b7eeab..cd64c52532 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -21,6 +21,7 @@ "add_partner": "PridaÅĨ partnera", "add_path": "PridaÅĨ cestu", "add_photos": "PridaÅĨ fotografie", + "add_tag": "PridaÅĨ ÅĄtítok", "add_to": "PridaÅĨ doâ€Ļ", "add_to_album": "PridaÅĨ do albumu", "add_to_album_bottom_sheet_added": "PridanÊ do {album}", @@ -38,13 +39,11 @@ "authentication_settings_disable_all": "Naozaj chcete zakÃĄzaÅĨ vÅĄetky spôsoby prihlÃĄsenia? PrihlÃĄsenie bude Ãēplne zakÃĄzanÊ.", "authentication_settings_reenable": "Pre opätovnÊ povolenie pouÅžite ServerovÃŊ príkaz.", "background_task_job": "Úlohy na pozadí", - "backup_database": "ZÃĄlohovaÅĨ databÃĄzu", - "backup_database_enable_description": "PovoliÅĨ zÃĄlohovanie databÃĄzy", - "backup_keep_last_amount": "MnoÅžtvo predoÅĄlÃŊch zÃĄloh, ktorÊ sa majÃē zachovaÅĨ", - "backup_settings": "Nastavenia zÃĄlohovania", - "backup_settings_description": "SpravovaÅĨ nastavenia zÃĄloh", - "check_all": "SkontrolovaÅĨ vÅĄetko", - "cleanup": "Vyčistenie", + "backup_database": "VytvoriÅĨ vÃŊpis databÃĄzy", + "backup_database_enable_description": "PovoliÅĨ vÃŊpisy z databÃĄzy", + "backup_keep_last_amount": "MnoÅžstvo predchÃĄdzajÃēcich vÃŊpisov, ktorÊ sa majÃē zachovaÅĨ", + "backup_settings": "Nastavenia vÃŊpisu databÃĄzy", + "backup_settings_description": "SprÃĄva nastavení vÃŊpisu databÃĄzy.", "cleared_jobs": "HotovÊ Ãēlohy pre: {job}", "config_set_by_file": "KonfigurÃĄcia je v sÃēčasnosti nastavenÃĄ konfiguračnÃŊm sÃēborom", "confirm_delete_library": "Naozaj chcete vymazaÅĨ kniÅžnicu {library}?", @@ -52,6 +51,7 @@ "confirm_email_below": "Pre potvrdenie zadajte \"{email}\" niÅžÅĄie", "confirm_reprocess_all_faces": "Naozaj chcete spracovaÅĨ vÅĄetky tvÃĄre znova? Tento proces vymaÅže pomenovanÃŊch Äžudí.", "confirm_user_password_reset": "Naozaj chcete resetovaÅĨ heslo pre {user}?", + "confirm_user_pin_code_reset": "Ste si istí, Åže chcete opätovne nastaviÅĨ PIN kÃŗd pouŞívateÄža {user}?", "create_job": "VytvoriÅĨ Ãēlohu", "cron_expression": "VÃŊraz cron", "cron_expression_description": "Nastavte interval skenovania pomocou formÃĄtu cron. Pre viac informÃĄcií navÅĄtívte Crontab Guru", @@ -59,18 +59,21 @@ "disable_login": "ZakÃĄzaÅĨ prihlÃĄsenie", "duplicate_detection_job_description": "SpustiÅĨ strojovÊ učenie na poloÅžkÃĄch pre detekciu podobnÃŊch obrÃĄzkov. Spolieha sa na inteligentnÊ vyhÄžadÃĄvanie", "exclusion_pattern_description": "Vylučovacie vzory VÃĄm umoŞňujÃē ignorovaÅĨ sÃēbory a priečinky pri skenovaní VaÅĄej kniÅžnice. Toto je uÅžitočnÊ, ak mÃĄte priečinky obsahujÃēce sÃēbory, ktorÊ nechcete importovaÅĨ, napríklad RAW sÃēbory.", - "external_library_created_at": "ExternÃĄ kniÅžnica (vytvorenÃĄ {date})", "external_library_management": "SprÃĄva Externej KniÅžnice", "face_detection": "Detekcia tvÃĄrí", - "face_detection_description": "Detekujte tvÃĄre v poloÅžkÃĄch pomocou strojovÊho učenia. Pri videÃĄch sa berie do Ãēvahy iba miniatÃēra. „ObnoviÅĨ“ znovu spracuje vÅĄetky poloÅžky. „ResetovaÅĨ“ navyÅĄe vymaÅže vÅĄetky aktuÃĄlne Ãēdaje o tvÃĄrach. „ChÃŊbajÃēce“ zaradí poloÅžky, ktorÊ eÅĄte neboli spracovanÊ. DetekovanÊ tvÃĄre budÃē zaradenÊ na rozpoznÃĄvanie tvÃĄrí po dokončení detekcie tvÃĄrí, pričom sa zoskupia do existujÃēcich alebo novÃŊch osôb.", - "facial_recognition_job_description": "ZoskupovaÅĨ detekovanÊ tvÃĄre do osôb. Tento krok sa vykonÃĄ po dokončení detekcie tvÃĄrí. „ResetovaÅĨ“ (znovu) zoskupí vÅĄetky tvÃĄre. „ChÃŊbajÃēce“ zaradí tvÃĄre, ktorÊ nemajÃē pridelenÃē osobu.", + "face_detection_description": "Rozpoznajte tvÃĄre v poloÅžkÃĄch pomocou strojovÊho učenia. V prípade videí sa berie do Ãēvahy len nÃĄhÄžad. „ObnoviÅĨ“ (znovu) spracuje vÅĄetky poloÅžky. „ObnoviÅĨ“ dodatočne vymaÅže vÅĄetky aktuÃĄlne Ãēdaje o tvÃĄrach. „ChÃŊbajÃēce“ zaradí do poradia poloÅžky aktív, ktorÊ eÅĄte neboli spracovanÊ. ZistenÊ tvÃĄre sa po dokončení rozpoznÃĄvania tvÃĄrí zaradia do poradia na rozpoznÃĄvanie tvÃĄrí, pričom sa zoskupia do existujÃēcich alebo novÃŊch osôb.", + "facial_recognition_job_description": "ZoskupovaÅĨ rozpoznanÊ tvÃĄre do osôb. Tento krok sa vykonÃĄ po dokončení rozpoznÃĄvania tvÃĄrí. „ObnoviÅĨ“ (znovu) zoskupí vÅĄetky tvÃĄre. „ChÃŊbajÃēce“ zaradí tvÃĄre, ktorÊ nemajÃē pridelenÃē osobu.", "failed_job_command": "Príkaz {command} zlyhal pre Ãēlohu: {job}", "force_delete_user_warning": "VAROVANIE: Toto okamÅžite odstrÃĄni pouŞívateÄža a vÅĄetky poloÅžky. Tento krok nie je moÅžnÊ vrÃĄtiÅĨ späÅĨ a sÃēbory nebude moÅžnÊ obnoviÅĨ.", - "forcing_refresh_library_files": "VynÃētenie obnovy vÅĄetkÃŊch sÃēborov kniÅžnice", "image_format": "FormÃĄt", "image_format_description": "WebP vytvÃĄra menÅĄie sÃēbory ako JPEG, ale kÃŗdovanie je pomalÅĄie.", + "image_fullsize_description": "ObrÃĄzok v plnom rozlÃ­ÅĄení s odstrÃĄnenÃŊmi metadÃĄtami, pouŞívanÊ pri priblíŞení", + "image_fullsize_enabled": "Povolenie generovania obrÃĄzkov v plnej veÄžkosti", + "image_fullsize_enabled_description": "GenerovaÅĨ obrÃĄzok v plnej veÄžkosti pre formÃĄty, ktorÊ nie sÃē vhodnÊ pre web. Ak je zapnutÃĄ moÅžnosÅĨ „UprednostniÅĨ vloÅženÃŊ nÃĄhÄžad“, vloÅženÊ nÃĄhÄžady sa pouÅžijÃē priamo bez konverzie. NemÃĄ vplyv na formÃĄty vhodnÊ pre web, ako je JPEG.", + "image_fullsize_quality_description": "Kvalita obrÃĄzku v plnej veÄžkosti od 1 do 100. VyÅĄÅĄia je lepÅĄia, ale vytvÃĄra vÃ¤ÄÅĄie sÃēbory.", + "image_fullsize_title": "Nastavenia obrÃĄzku v plnej veÄžkosti", "image_prefer_embedded_preview": "UprednostňovaÅĨ vstavanÃŊ nÃĄhÄžad", - "image_prefer_embedded_preview_setting_description": "PouÅžiÅĨ vloÅženÊ nÃĄhÄžady vo fotografiÃĄch RAW ako vstup pre spracovanie obrazu, ak sÃē k dispozícii. To môŞe vytvoriÅĨ presnejÅĄie farby pre niektorÊ obrÃĄzky, ale kvalita nÃĄhÄžadu zÃĄvisí od fotoaparÃĄtu a obrÃĄzok môŞe maÅĨ viac kompresnÃŊch artefaktov.", + "image_prefer_embedded_preview_setting_description": "PouÅžiÅĨ vloÅženÊ nÃĄhÄžady vo fotografiÃĄch RAW ako vstup pre spracovanie obrazu a ak sÃē k dispozícii. To môŞe vytvoriÅĨ presnejÅĄie farby pre niektorÊ obrÃĄzky, ale kvalita nÃĄhÄžadu zÃĄvisí od fotoaparÃĄtu a obrÃĄzok môŞe maÅĨ viac kompresnÃŊch artefaktov.", "image_prefer_wide_gamut": "UprednostňovaÅĨ ÅĄirokÃŊ farebnÃŊ rozsah", "image_prefer_wide_gamut_setting_description": "PouÅžiÅĨ Display P3 pre miniatÃēry. Toto lepÅĄie zachovÃĄva ÅživosÅĨ obrÃĄzkov so ÅĄirokÃŊm farebnÃŊm rozsahom. ObrÃĄzky sa môŞu zobraziÅĨ odliÅĄne na starÅĄÃ­ch zariadeniach so starou verziou prehliadača. sRGB obrÃĄzky zostÃĄvajÃē sRGB, aby sa zabrÃĄnilo farebnÃŊm posunom.", "image_preview_description": "Stredne veÄžkÃŊ obrÃĄzok s odstrÃĄnenÃŊmi metadÃĄtami, pouŞívanÃŊ pri prezeraní jednej poloÅžky a na strojovÊ učenie", @@ -166,7 +169,7 @@ "note_apply_storage_label_previous_assets": "PoznÃĄmka: Ak chcete pouÅžiÅĨ Å títkovanie ÃēloÅžiska na predtÃŊm nahranÊ aktíva, spustite príkaz", "note_cannot_be_changed_later": "POZNÁMKA: Toto nie je moÅžnÊ neskôr zmeniÅĨ!", "notification_email_from_address": "Z adresy", - "notification_email_from_address_description": "E-mailovÃĄ adresa odosielateÄža, príklad: \"Immich Photo Server \"", + "notification_email_from_address_description": "E-mailovÃĄ adresa odosielateÄža, napríklad: \"Immich Foto Server \". Uistite sa, Åže pouŞívate adresu, z ktorej mÃĄte povolenÊ odosielaÅĨ e-maily.", "notification_email_host_description": "Adresa emailovÊho serveru (príklad: smtp.immich.app)", "notification_email_ignore_certificate_errors": "IgnorovaÅĨ chyby certifikÃĄtu", "notification_email_ignore_certificate_errors_description": "IgnorovaÅĨ chyby pri overení TLS certifikÃĄtu (neodporÃēča sa)", @@ -189,7 +192,7 @@ "oauth_enable_description": "PrihlÃĄsiÅĨ sa pomocou OAuth", "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_mobile_redirect_uri_override_description": "PovoÄžte, keď poskytovateÄž protokolu OAuth nepovoÄžuje identifikÃĄtor URI pre mobilnÊ zariadenia, napríklad ''{callback}''", "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.", @@ -198,9 +201,7 @@ "oauth_storage_quota_claim": "DeklarÃĄcia kvÃŗty ÃēloÅžiska", "oauth_storage_quota_claim_description": "Automaticky nastaviÅĨ kvÃŗtu ÃēloÅžiska pouŞívateÄža na hodnotu tejto deklarÃĄcie.", "oauth_storage_quota_default": "PredvolenÃŊ limit ÃēloÅžiska (GiB)", - "oauth_storage_quota_default_description": "KvÃŗta v GiB, ktorÃĄ sa mÃĄ pouÅžiÅĨ, keď nie je poskytnutÃŊ Åžiadna deklarÃĄcia (zadajte 0 pre neobmedzenÃē kvÃŗtu).", - "offline_paths": "Offline cesty", - "offline_paths_description": "Tieto vÃŊsledky môŞu byÅĨ spôsobenÊ ručnÃŊm odstrÃĄnením sÃēborov, ktorÊ nie sÃē sÃēčasÅĨou externej kniÅžnice.", + "oauth_storage_quota_default_description": "KvÃŗta v GiB, ktorÃĄ sa pouÅžije, ak nie je poskytnutÃĄ Åžiadna poÅžiadavka.", "password_enable_description": "PrihlÃĄsiÅĨ sa pomocou emailu a hesla", "password_settings": "PrihlÃĄsenie cez heslo", "password_settings_description": "SpravovaÅĨ nastavenia prihlÃĄsenia cez heslo", @@ -210,9 +211,6 @@ "refreshing_all_libraries": "ObnovujÃē sa vÅĄetky kniÅžnice", "registration": "RegistrÃĄcia administrÃĄtora", "registration_description": "KeďŞe ste prvÃŊm pouŞívateÄžom v systÊme, budÃē vÃĄm pridelenÊ sprÃĄvcovskÊ prÃĄva na vykonÃĄvanie vÅĄetkÃŊch Ãēloh a vrÃĄtane tvorby novÃŊch pouŞívateÄžov.", - "repair_all": "OpraviÅĨ VÅĄetko", - "repair_matched_items": "Zhody {count, plural, one {# item} other {# items}}", - "repaired_items": "OpravenÃŊch {count, plural, one {# item} other {# items}}", "require_password_change_on_login": "VyÅžadovaÅĨ od pouŞívateÄža zmenu hesla pri prvom prihlÃĄsení", "reset_settings_to_default": "ObnoviÅĨ pôvodnÊ nastavenia", "reset_settings_to_recent_saved": "ObnoviÅĨ naposledy uloÅženÊ nastavenia", @@ -241,7 +239,6 @@ "storage_template_migration_info": "Å ablÃŗna ÃēloÅžiska skonvertuje vÅĄetky prípony na malÊ písmenÃĄ. Zmeny ÅĄablÃŗn sa budÃē vzÅĨahovaÅĨ iba na novÊ diela. Ak chcete ÅĄablÃŗnu spätne pouÅžiÅĨ na predtÃŊm nahranÊ mÊdiÃĄ, spustite {job}.", "storage_template_migration_job": "Úloha migrÃĄcie ÅĄablÃŗny ÃēloÅžiska", "storage_template_more_details": "ĎalÅĄie podrobnosti o tejto funkcii nÃĄjdete v Å ablÃŗna ÃēloÅžiska a jej dôsledky", - "storage_template_onboarding_description": "Keď je tÃĄto funkcia povolenÃĄ, automaticky usporiada sÃēbory na zÃĄklade ÅĄablÃŗny definovanej pouŞívateÄžom. Kvôli problÊmom so stabilitou bola funkcia predvolene vypnutÃĄ. Viac informÃĄcií nÃĄjdete v dokumentÃĄcii.", "storage_template_path_length": "PribliÅžnÃŊ limit dÄēÅžky cesty: {length, number}/{limit, number}", "storage_template_settings": "Å ablÃŗna ÃēloÅžiska", "storage_template_settings_description": "Spravujte ÅĄtruktÃēru priečinkov a nÃĄzov sÃēboru odovzdanÊho mÊdia", @@ -253,16 +250,14 @@ "template_email_invite_album": "Å ablÃŗna PozvÃĄnky do albumu", "template_email_preview": "UkÃĄÅžka", "template_email_settings": "EmailovÊ ÅĄablÃŗny", - "template_email_settings_description": "Spravovanie vlastnÃŊch ÅĄablÃŗn pre emailovÊ upozornenia", "template_email_update_album": "UpraviÅĨ ÅĄablÃŗnu albumu", "template_email_welcome": "Å ablÃŗna uvítajÃēceho emailu", "template_settings": "Å ablÃŗna upozornení", - "template_settings_description": "Spravovanie vlastnÃŊch ÅĄablÃŗn upozornení.", + "template_settings_description": "Spravovanie vlastnÃŊch ÅĄablÃŗn upozornení", "theme_custom_css_settings": "VlastnÊ CSS", "theme_custom_css_settings_description": "CSS ÅĄtÃŊly umoŞňujÃē prispôsobiÅĨ dizajn Immich.", "theme_settings": "Motívy", "theme_settings_description": "SpravovaÅĨ prispôsobenie webovÊho rozhrania Immich", - "these_files_matched_by_checksum": "Tieto sÃēbory zodpovedajÃē kontrolnÃŊm sÃēčtom", "thumbnail_generation_job": "GenerovaÅĨ MiniatÃēry", "thumbnail_generation_job_description": "Generuje veÄžkÊ, malÊ a rozostrení miniatÃēry pre kaÅždÃē poloÅžku, ako aj miniatÃēry pre kaÅždÃē osobu", "transcoding_acceleration_api": "API pre akcelerÃĄciu", @@ -290,10 +285,9 @@ "transcoding_encoding_options": "MoÅžnosti kÃŗdovania", "transcoding_encoding_options_description": "Nastavte kodeky, rozlÃ­ÅĄenie, kvalitu a ďalÅĄie moÅžnosti pre kÃŗdovanÊ videÃĄ", "transcoding_hardware_acceleration": "HardvÊrovÃĄ akcelerÃĄcia", - "transcoding_hardware_acceleration_description": "ExperimentÃĄlne; oveÄža rÃŊchlejÅĄie, ale bude maÅĨ niÅžÅĄiu kvalitu pri rovnakej bitovej rÃŊchlosti", + "transcoding_hardware_acceleration_description": "ExperimentÃĄlne: rÃŊchlejÅĄie prekÃŗdovanie, ale môŞe sa zníŞiÅĨ kvalita pri rovnakom dÃĄtovom toku", "transcoding_hardware_decoding": "HardvÊrovÊ dekÃŗdovanie", "transcoding_hardware_decoding_setting_description": "UmoŞňuje end-to-end zrÃŊchlenie namiesto iba zrÃŊchlenia kÃŗdovania. Nemusí fungovaÅĨ na vÅĄetkÃŊch videÃĄch.", - "transcoding_hevc_codec": "Kodek HEVC", "transcoding_max_b_frames": "MaximÃĄlny počet B-snímkov", "transcoding_max_b_frames_description": "VyÅĄÅĄie hodnoty zvyÅĄujÃē ÃēčinnosÅĨ kompresie, ale spomaÄžujÃē kÃŗdovanie. Nemusí byÅĨ kompatibilnÃŊ s hardvÊrovou akcelerÃĄciou na starÅĄÃ­ch zariadeniach. Hodnota 0 zakÃĄÅže B-snímky, zatiaÄž čo -1 nastaví tÃēto hodnotu automaticky.", "transcoding_max_bitrate": "MaximÃĄlna bitovÃĄ rÃŊchlosÅĨ", @@ -331,14 +325,13 @@ "trash_number_of_days_description": "Počet dní, počas ktorÃŊch sa mÃĄ majetok ponechaÅĨ v koÅĄi pred jeho trvalÃŊm odstrÃĄnením", "trash_settings": "KÃ´ÅĄ", "trash_settings_description": "SpravovaÅĨ nastavenia koÅĄa", - "untracked_files": "NesledovanÊ sÃēbory", - "untracked_files_description": "Tieto sÃēbory aplikÃĄcia nesleduje. MôŞu byÅĨ vÃŊsledkom neÃēspeÅĄnÃŊch presunov, preruÅĄenÊho odosielania alebo môŞu zostaÅĨ v dôsledku chyby", "user_cleanup_job": "Premazanie pouŞívateÄžov", "user_delete_delay": "Konto {user} a jeho mÊdiÃĄ budÃē podÄža plÃĄnu natrvalo vymazanÊ za {delay, plural, one {# day} other {# days}}.", "user_delete_delay_settings": "OdstrÃĄniÅĨ oneskorenie", "user_delete_delay_settings_description": "Počet dní po odstrÃĄnení na trvalÊ vymazanie Ãēčtu a aktív pouŞívateÄža. Úloha odstraňovania pouŞívateÄžov sa spÃēÅĄÅĨa o polnoci, aby sa skontrolovali pouŞívatelia, ktorí sÃē pripravení na odstrÃĄnenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalÅĄom spustení.", "user_delete_immediately": "Konto a mÊdiÃĄ {user} budÃē zaradenÊ do frontu na trvalÊ vymazanie okamÅžite.", "user_delete_immediately_checkbox": "PouŞívateÄž a mÊdiÃĄ budÃē zaradení do frontu na okamÅžitÊ vymazanie", + "user_details": "Podrobnosti o pouŞívateÄžovi", "user_management": "SprÃĄva pouŞívateÄžov", "user_password_has_been_reset": "Heslo pouŞívateÄža bolo resetovanÊ:", "user_password_reset_description": "Poskytnite pouŞívateÄžovi dočasnÊ heslo a informujte ho, Åže si ho bude musieÅĨ zmeniÅĨ pri ďalÅĄom prihlÃĄsení.", @@ -384,10 +377,6 @@ "album_remove_user": "OdstrÃĄniÅĨ pouŞívateÄža?", "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": "{count} poloÅžiek", - "album_thumbnail_card_shared": "ZdieÄžanÊ", - "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}", @@ -395,7 +384,7 @@ "album_viewer_appbar_delete_confirm": "Ste si istÃŊ Åže chcete vymazaÅĨ tento album z vÃĄÅĄho Ãēčtu?", "album_viewer_appbar_share_err_delete": "Nepodarilo sa odstrÃĄniÅĨ album", "album_viewer_appbar_share_err_leave": "Nepodarilo sa ukončiÅĨ album", - "album_viewer_appbar_share_err_remove": "Pri odstraňovaní sÃēborov z albumu sa vyskytli problÊmy.", + "album_viewer_appbar_share_err_remove": "Pri odstraňovaní sÃēborov z albumu sa vyskytli problÊmy", "album_viewer_appbar_share_err_title": "Nepodarilo sa zmeniÅĨ nÃĄzov albumu", "album_viewer_appbar_share_leave": "OpustiÅĨ album", "album_viewer_appbar_share_to": "ZdieÄžaÅĨ s", @@ -428,6 +417,7 @@ "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": "ArchivovanÊ", "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ÅĨ?", @@ -464,6 +454,7 @@ "assets_removed_count": "{count, plural, one {OdstrÃĄnenÃĄ # poloÅžka} few {OdstrÃĄnenÊ # poloÅžky} other {OdstrÃĄnenÃŊch # poloÅžiek}}", "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": "{count} medií ÃēspeÅĄne obnovenÃŊch", "assets_trashed_count": "{count, plural, one {OdstrÃĄnenÃĄ # poloÅžka} few {OdstrÃĄnenÊ # poloÅžky} other {OdstrÃĄnenÃŊch # poloÅžiek}}", "assets_were_part_of_album_count": "{count, plural, one {PoloÅžka bola} other {PoloÅžky boli}} sÃēčasÅĨou albumu", "authorized_devices": "AutorizovanÊ zariadenia", @@ -476,19 +467,20 @@ "backup_album_selection_page_selection_info": "InformÃĄcie o vÃŊbere", "backup_album_selection_page_total_assets": "CelkovÃŊ počet jedinečnÃŊch sÃēborov", "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_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 {filename}", - "backup_background_service_default_notification": "Kontrola novÃŊch mÊdií {}", + "backup_background_service_default_notification": "Kontrola novÃŊch zdrojovâ€Ļ", "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_in_progress_notification": "VytvÃĄram kÃŗpiu vaÅĄich mÊdiíâ€Ļ", "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_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_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. \n\nKeďŞ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í", @@ -507,6 +499,7 @@ "backup_controller_page_excluded": "VylÃēčenÊ: ", "backup_controller_page_failed": "Nepodarilo sa ({count})", "backup_controller_page_filename": "NÃĄzov sÃēboru: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "InformÃĄcie o zÃĄlohovaní", "backup_controller_page_none_selected": "ÅŊiadne vybranÊ", "backup_controller_page_remainder": "ZostÃĄva", @@ -533,41 +526,40 @@ "birthdate_set_description": "DÃĄtum narodenia sa pouŞíva na vÃŊpočet veku tejto osoby v čase fotografie.", "blurred_background": "RozmazanÊ pozadie", "bugs_and_feature_requests": "Chyby a poÅžiadavky na funkcie", - "build": "BudovaÅĨ", - "build_image": "VytvoriÅĨ obrÃĄzok", + "build": "Zostava", + "build_image": "Obraz zostavy", "bulk_delete_duplicates_confirmation": "Naozaj chcete hromadne odstrÃĄniÅĨ {count, plural, one {# duplikÃĄtnu poloÅžku} few {# duplikÃĄte poloÅžky} other {# duplikÃĄtnych poloÅžiek}}? TÃŊmto sa zachovÃĄ najvÃ¤ÄÅĄia poloÅžka z kaÅždej skupiny a vÅĄetky ostatnÊ duplikÃĄty sa natrvalo odstrÃĄnia. TÃēto akciu nie je moÅžnÊ vrÃĄtiÅĨ späÅĨ!", "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 {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_clear_button": "VYČISTIŤ", "cache_settings_duplicated_assets_subtitle": "Fotky a videÃĄ ktorÊ sÃē na čiernej listine zvolenÊ aplikÃĄciou", "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": "{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 {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", "camera": "FotoaparÃĄt", - "camera_brand": "VÃŊrobca fotoaparÃĄtu", + "camera_brand": "Značka fotoaparÃĄtu", "camera_model": "Model fotoaparÃĄtu", "cancel": "ZruÅĄiÅĨ", "cancel_search": "ZruÅĄiÅĨ vyhÄžadÃĄvanie", + "canceled": "ZruÅĄenÊ", "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_description": "ZmeniÅĨ popis", + "change_display_order": "ZmeniÅĨ poradie zobrazenia", "change_expiration_time": "ZmeniÅĨ čas vyprÅĄania", - "change_location": "UpraviÅĨ lokÃĄciu", + "change_location": "ZmeniÅĨ polohu", "change_name": "UpraviÅĨ meno", "change_name_successfully": "Meno bolo zmenenÊ", "change_password": "ZmeniÅĨ Heslo", @@ -577,17 +569,21 @@ "change_password_form_new_password": "NovÊ heslo", "change_password_form_password_mismatch": "HeslÃĄ sa nezhodujÃē", "change_password_form_reenter_new_password": "Znova zadajte novÊ heslo", + "change_pin_code": "ZmeniÅĨ PIN kÃŗd", "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_button": "VykonaÅĨ kontrolu", "check_logs": "SkontrolovaÅĨ logy", "choose_matching_people_to_merge": "Vyberte rovnakÃŊch Äžudí na zlÃēčenie", "city": "Mesto", - "clear": "VYMAZAŤ", - "clear_all": "VymazaÅĨ vÅĄetko", - "clear_all_recent_searches": "VymazaÅĨ nedÃĄvne vyhÄžadÃĄvania", + "clear": "VyčistiÅĨ", + "clear_all": "VyčistiÅĨ vÅĄetko", + "clear_all_recent_searches": "VyčistiÅĨ nedÃĄvne vyhÄžadÃĄvania", "clear_message": "VymazaÅĨ sprÃĄvu", "clear_value": "VymazaÅĨ hodnotu", + "client_cert_dialog_msg_confirm": "OK", + "client_cert_enter_password": "ZadaÅĨ heslo", + "client_cert_import": "ImportovaÅĨ", "clockwise": "V smere hodinovÃŊch ručičiek", "close": "ZatvoriÅĨ", "collapse": "ZbaliÅĨ", @@ -600,21 +596,26 @@ "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": "DokončenÊ", "confirm": "PotvrdiÅĨ", "confirm_admin_password": "PotvrdiÅĨ AdministrÃĄtorskÊ Heslo", "confirm_delete_face": "Naozaj chcete z poloÅžky odstrÃĄniÅĨ tvÃĄr osoby {name}?", "confirm_delete_shared_link": "Ste si istÃŊ, Åže chcete odstrÃĄniÅĨ tento zdieÄžanÃŊ odkaz?", "confirm_keep_this_delete_others": "VÅĄetky ostatnÊ poloÅžky v zÃĄsobníku budÃē odstrÃĄnenÊ okrem tejto poloÅžky. Naozaj chcete pokračovaÅĨ?", + "confirm_new_pin_code": "PotvrdiÅĨ novÃŊ PIN kÃŗd", "confirm_password": "PotvrdiÅĨ heslo", + "confirm_tag_face": "Chcete označiÅĨ tÃēto tvÃĄr ako {name}?", + "confirm_tag_face_unnamed": "Chcete označiÅĨ tÃēto tvÃĄr?", + "connected_device": "PripojenÊ zariadenie", "contain": "ObsiahnÃēÅĨ", "context": "Kontext", "continue": "PokračovaÅĨ", - "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_immich": "VymazaÅĨ z aplikÃĄcie Immich", "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": "ZdieÄžaÅĨ odkaz", "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!", @@ -635,6 +636,7 @@ "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": "VYTVORIŤ NOVÉ", "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", @@ -644,8 +646,12 @@ "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Ê", + "created_at": "VytvorenÊ", + "crop": "OrezaÅĨ", "curated_object_page_title": "Veci", "current_device": "SÃēčasnÊ zariadenie", + "current_pin_code": "AktuÃĄlny PIN kÃŗd", + "current_server_address": "AktuÃĄlna adresa servera", "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", @@ -668,7 +674,7 @@ "delete": "VymazaÅĨ", "delete_album": "OdstrÃĄniÅĨ album", "delete_api_key_prompt": "Naozaj chcete odstrÃĄniÅĨ tento API kÄžÃēč?", - "delete_dialog_alert": "Tieto poloÅžky budÃē natrvalo odstrÃĄnenÊ z Immich a z vÃĄÅĄho zariadenia.", + "delete_dialog_alert": "Tieto poloÅžky budÃē natrvalo odstrÃĄnenÊ z aplikÃĄcie Immich a z vÃĄÅĄho zariadenia", "delete_dialog_alert_local": "Tieto poloÅžky budÃē permanentne vymazanÊ z vaÅĄeho zariadenia, ale budÃē stÃĄle k dispozícií na serveri Immich", "delete_dialog_alert_local_non_backed_up": "NiektorÊ poloÅžky nie sÃē zÃĄlohovanÊ na Immichi a budÃē permanentne vymazanÊ z vÃĄÅĄho zariadenia", "delete_dialog_alert_remote": "Tieto poloÅžky budÃē permanentne vymazanÊ zo serveru Immich", @@ -696,10 +702,12 @@ "direction": "Smer", "disabled": "VypnutÊ", "disallow_edits": "ZakÃĄzaÅĨ editovanie", + "discord": "Discord", "discover": "ObjaviÅĨ", + "discovered_devices": "ObjavenÊ zariadenia", "dismiss_all_errors": "ZamietnuÅĨ vÅĄetky chyby", "dismiss_error": "ZamietnuÅĨ chybu", - "display_options": "ZobraziÅĨ moÅžnosti", + "display_options": "MoÅžnosti zobrazenia", "display_order": "Poradie zobrazenia", "display_original_photos": "ZobraziÅĨ pôvodnÊ fotky", "display_original_photos_setting_description": "UprednostniÅĨ zobrazenie pôvodnej fotky, pri prezeraní poloÅžky, namiesto miniatÃēry, ak je pôvodnÃĄ poloÅžka kompatibilnÃĄ s webom. To môŞe maÅĨ za nÃĄsledok niÅžÅĄiu rÃŊchlosÅĨ zobrazenia fotografií.", @@ -707,12 +715,21 @@ "documentation": "DokumentÃĄcia", "done": "Hotovo", "download": "StiahnuÅĨ", + "download_canceled": "Stiahnutie zruÅĄenÊ", + "download_complete": "Stiahnutie dokončenÊ", + "download_error": "Chyba sÅĨahovania", + "download_failed": "Stiahnutie sa nepodarilo", + "download_finished": "Stiahnutie dokončenÊ", "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_paused": "Stiahnutie pozastavenÊ", "download_settings": "StiahnuÅĨ", "download_settings_description": "SpravovaÅĨ nastavenia sÃēvisiace so sÅĨahovaním poloÅžiek", + "download_started": "SÅĨahovanie spustenÊ", + "download_sucess_android": "MÊdiÃĄ boli stiahnutÊ do DCIM/Immich", "downloading": "SÅĨahuje sa", "downloading_asset_filename": "SÅĨahuje sa poloÅžka {filename}", + "downloading_media": "SÅĨahovanie mÊdií", "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Ê", @@ -722,6 +739,8 @@ "edit_avatar": "UpraviÅĨ avatar", "edit_date": "UpraviÅĨ dÃĄtum", "edit_date_and_time": "UpraviÅĨ dÃĄtum a čas", + "edit_description": "UpraviÅĨ popis", + "edit_description_prompt": "Vyberte prosím novÃŊ popis:", "edit_exclusion_pattern": "UpraviÅĨ vzor vylÃēčenia", "edit_faces": "UpraviÅĨ tvÃĄre", "edit_import_path": "UpraviÅĨ cestu importu", @@ -736,20 +755,26 @@ "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", + "email_notifications": "E-mailovÊ oznÃĄmenia", + "empty_folder": "Tento priečinok je prÃĄzdny", "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", - "enter_wifi_name": "Enter WiFi name", + "enter_wifi_name": "Zadajte nÃĄzov Wi-Fi", + "enter_your_pin_code": "Zadajte svoj PIN kÃŗd", + "enter_your_pin_code_subtitle": "Zadaním kÃŗdu PIN získate prístup k zamknutÊmu priečinku", "error": "Chyba", "error_delete_face": "Chyba pri odstraňovaní tvÃĄre z poloÅžky", "error_loading_image": "Nepodarilo sa načítaÅĨ obrÃĄzok", + "error_saving_image": "Chyba: {error}", "error_title": "Chyba - niečo sa pokazilo", "errors": { "cannot_navigate_next_asset": "NedokÃĄÅžem prejsÅĨ na ďaÄžÅĄiu poloÅžku", @@ -762,7 +787,6 @@ "cant_get_number_of_comments": "NedokÃĄÅžem získaÅĨ počet komentÃĄrov", "cant_search_people": "NedokÃĄÅžem hÄžadaÅĨ osoby", "cant_search_places": "NedokÃĄÅžem hÄžadaÅĨ miesta", - "cleared_jobs": "VyčistenÊ prÃĄce pre: {job}", "error_adding_assets_to_album": "Nepodarilo sa pridaÅĨ poloÅžky do albumu", "error_adding_users_to_album": "Nepodarilo sa pridaÅĨ uŞívateÄžov do albumu", "error_deleting_shared_user": "Nepodarilo sa odstrÃĄniÅĨ zdieÄžanÊho pouŞívateÄža", @@ -771,7 +795,6 @@ "error_removing_assets_from_album": "Nepodarilo sa odstrÃĄniÅĨ poloÅžku z albumu, podrobnejÅĄie informÃĄcie nÃĄjdete v konzole", "error_selecting_all_assets": "Nepodarilo sa vybraÅĨ poloÅžky", "exclusion_pattern_already_exists": "Tento vzor vylÃēčenia uÅž existuje.", - "failed_job_command": "Príkaz {command} zlyhal pre prÃĄcu: {job}", "failed_to_create_album": "Nepodarilo sa vytvoriÅĨ album", "failed_to_create_shared_link": "Nepodarilo sa vytvoriÅĨ zdieÄžanÃŊ odkaz", "failed_to_edit_shared_link": "Nepodarilo sa editovaÅĨ zdieÄžanÃŊ odkaz", @@ -788,7 +811,6 @@ "paths_validation_failed": "{paths, plural, one {# cesta zlyhala} few {# cesty zlyhali} other {# ciest zlyhalo}} pri validÃĄcii", "profile_picture_transparent_pixels": "ProfilovÊ obrÃĄzky nemôŞu maÅĨ priehÄžadnÊ pixely. Prosím priblíŞte a/alebo posuňte obrÃĄzok.", "quota_higher_than_disk_size": "Nastavili ste kvÃŗtu vyÅĄÅĄiu ako je veÄžkosÅĨ disku", - "repair_unable_to_check_items": "Nepodarilo sa skontrolovaÅĨ {count, select, one {poloÅžku} other {poloÅžky}}", "unable_to_add_album_users": "Nie je moÅžnÊ pridaÅĨ pouŞívateÄžov do albumu", "unable_to_add_assets_to_shared_link": "Nie je moÅžnÊ pridaÅĨ poloÅžky k zdieÄžanÊmu odkazu", "unable_to_add_comment": "Nie je moÅžnÊ pridaÅĨ komentÃĄr", @@ -800,13 +822,13 @@ "unable_to_archive_unarchive": "Nepodarilo sa {archived, select, true {archivovaÅĨ} other {odarchivovaÅĨ}}", "unable_to_change_album_user_role": "Nie je moÅžnÊ zmeniÅĨ rolu pouŞívateÄža pre album", "unable_to_change_date": "Nie je moÅžnÊ zmeniÅĨ dÃĄtum", + "unable_to_change_description": "Nie je moÅžnÊ zmeniÅĨ popis", "unable_to_change_favorite": "Nie je moÅžnÊ zmeniÅĨ obÄžÃēbenÊ pre poloÅžku", "unable_to_change_location": "Nie je moÅžnÊ zmeniÅĨ polohu", "unable_to_change_password": "Nie je moÅžnÊ zmeniÅĨ heslo", "unable_to_change_visibility": "Nie je moÅžnÊ zmeniÅĨ viditeÄžnosÅĨ pre {count, plural, one {# osobu} other {# Äžudí}}", "unable_to_complete_oauth_login": "NemoÅžno dokončiÅĨ prihlÃĄsenie cez OAuth", "unable_to_connect": "Nie je moÅžnÊ sa pripojiÅĨ", - "unable_to_connect_to_server": "Nie je moÅžnÊ sa pripojiÅĨ k serveru", "unable_to_copy_to_clipboard": "Nie je moÅžnÊ kopírovaÅĨ do schrÃĄnky, overte si, Åže strÃĄnku navÅĄtevujete cez https", "unable_to_create_admin_account": "Nie je moÅžnÊ vytvoriÅĨ admin Ãēčet", "unable_to_create_api_key": "Nie je moÅžnÊ vytvoriÅĨ novÃŊ API KlÃēč", @@ -818,7 +840,7 @@ "unable_to_delete_exclusion_pattern": "Nie je moÅžnÊ vymazaÅĨ vylučovací vzor", "unable_to_delete_import_path": "Nie je moÅžnÊ odstrÃĄniÅĨ cestu importu", "unable_to_delete_shared_link": "Nie je moÅžnÊ vymazaÅĨ zdieÄžanÃŊ odkaz", - "unable_to_delete_user": "Nie je moÅžnÊ vymazaÅĨ uÅživateÄža", + "unable_to_delete_user": "Nie je moÅžnÊ vymazaÅĨ pouŞívateÄža", "unable_to_download_files": "Nie je moÅžnÊ stiahnuÅĨ sÃēbory", "unable_to_edit_exclusion_pattern": "Nie je moÅžnÊ upravit vzorec vylÃēčenia", "unable_to_edit_import_path": "Nie je moÅžnÊ upraviÅĨ cestu importu", @@ -830,10 +852,6 @@ "unable_to_hide_person": "Nie je moÅžnÊ skryÅĨ osobu", "unable_to_link_motion_video": "Nie je moÅžnÊ prepojiÅĨ pohyblivÊ video", "unable_to_link_oauth_account": "Nie je moÅžnÊ prepojiÅĨ Ãēčet OAuth", - "unable_to_load_album": "Nie je moÅžnÊ načítaÅĨ album", - "unable_to_load_asset_activity": "Nie je moÅžnÊ načítaÅĨ aktivitu poloÅžky", - "unable_to_load_items": "Nie je moÅžnÊ načítaÅĨ poloÅžky", - "unable_to_load_liked_status": "Nie je moÅžnÊ načítaÅĨ stav obÄžÃēbenosti", "unable_to_log_out_all_devices": "Nie je moÅžnÊ odhlÃĄsiÅĨ vÅĄetky zariadenia", "unable_to_log_out_device": "Nie je moÅžnÊ odhlÃĄsiÅĨ zariadenie", "unable_to_login_with_oauth": "Nie je moÅžnÊ prihlÃĄsiÅĨ sa cez OAuth", @@ -844,12 +862,11 @@ "unable_to_remove_album_users": "Nie je moÅžnÊ odstrÃĄniÅĨ pouŞívateÄžov z albumu", "unable_to_remove_api_key": "Nie je moÅžnÊ odstrÃĄniÅĨ kÄžÃēč API", "unable_to_remove_assets_from_shared_link": "Nie je moÅžnÊ odstrÃĄniÅĨ poloÅžky zo zdieÄžanÊho odkazu", - "unable_to_remove_deleted_assets": "Nie je moÅžnÊ odstrÃĄniÅĨ offline sÃēbory", "unable_to_remove_library": "Nie je moÅžnÊ odstrÃĄniÅĨ kniÅžnicu", "unable_to_remove_partner": "Nie je moÅžnÊ odstrÃĄniÅĨ partnera", "unable_to_remove_reaction": "Nie je moÅžnÊ odstrÃĄniÅĨ reakciu", - "unable_to_repair_items": "Nie je moÅžnÊ opraviÅĨ poloÅžky", "unable_to_reset_password": "Nie je moÅžnÊ resetovaÅĨ heslo", + "unable_to_reset_pin_code": "Nie je moÅžnÊ obnoviÅĨ PIN kÃŗd", "unable_to_resolve_duplicate": "Nie je moÅžnÊ vyrieÅĄiÅĨ duplikÃĄt", "unable_to_restore_assets": "Nie je moÅžnÊ obnoviÅĨ poloÅžky", "unable_to_restore_trash": "Nie je moÅžnÊ obnoviÅĨ kÃ´ÅĄ", @@ -871,17 +888,20 @@ "unable_to_update_album_cover": "Nie je moÅžnÊ aktualizovaÅĨ obal albumu", "unable_to_update_album_info": "Nie je moÅžnÊ aktualizovaÅĨ informÃĄcie o albume", "unable_to_update_library": "Nie je moÅžnÊ aktualizovaÅĨ kniÅžnicu", - "unable_to_update_location": "Nie je moÅžnÊ aktualizovaÅĨ lokalitu", + "unable_to_update_location": "Nie je moÅžnÊ aktualizovaÅĨ polohu", "unable_to_update_settings": "Nie je moÅžnÊ aktualizovaÅĨ nastavenia", "unable_to_update_timeline_display_status": "Nie je moÅžnÊ aktualizovaÅĨ stav zobrazenia časovej osi", "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_location": "POLOHA", "exif_bottom_sheet_people": "ÄŊUDIA", "exif_bottom_sheet_person_add_person": "PridaÅĨ meno", + "exif_bottom_sheet_person_age_year_months": "Vek 1 rok, {months} mesiacov", + "exif_bottom_sheet_person_age_years": "Vek {years}", "exit_slideshow": "OpustiÅĨ Slideshow", "expand_all": "RozbaliÅĨ vÅĄetko", "experimental_settings_new_asset_list_subtitle": "PrebiehajÃēca prÃĄca", @@ -898,8 +918,10 @@ "extension": "RozÅĄÃ­renie", "external": "ExternÃŊ", "external_libraries": "ExternÊ kniÅžnice", - "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": "ExternÃĄ sieÅĨ", + "external_network_sheet_info": "Ak nie ste v preferovanej sieti Wi-Fi, aplikÃĄcia sa pripojí k serveru prostredníctvom prvej z niÅžÅĄie uvedenÃŊch adries URL, na ktorÃē sa dostane, počnÃēc zhora nadol", "face_unassigned": "NepriradenÃĄ", + "failed": "NeÃēspeÅĄnÊ", "failed_to_load_assets": "Nepodarilo sa načítaÅĨ poloÅžky", "favorite": "ObÄžÃēbenÊ", "favorite_or_unfavorite_photo": "OznačiÅĨ fotku ako obÄžÃēbenÃē alebo neobÄžÃēbenÃē", @@ -912,9 +934,12 @@ "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í", + "filter_places": "FiltrovaÅĨ miesta", "find_them_fast": "NÃĄjdite ich rÃŊchlejÅĄie podÄža mena", "fix_incorrect_match": "OpraviÅĨ nesprÃĄvnu zhodu", + "folder": "Priečinok", "folders": "Priečinky", "folders_feature_description": "Prehliadanie zobrazenia priečinka s fotografiami a videami na sÃēborovom systÊme", "forward": "Dopredu", @@ -924,6 +949,7 @@ "go_back": "VrÃĄtiÅĨ sa späÅĨ", "go_to_folder": "PrejsÅĨ do priečinka", "go_to_search": "PrejsÅĨ na vyhÄžadÃĄvanie", + "grant_permission": "UdeliÅĨ povolenie", "group_albums_by": "ZoskupiÅĨ albumy podÄža...", "group_country": "Zoskupenie podÄža krajiny", "group_no": "NezoskupovaÅĨ", @@ -945,16 +971,17 @@ "home_page_add_to_album_success": "PridanÊ {added} poloÅžky do albumu {album}.", "home_page_album_err_partner": "Na teraz nemôŞete pridaÅĨ partnerove mÊdiÃĄ do albumov", "home_page_archive_err_local": "ZatiaÄž nemoÅžno archivovaÅĨ lokÃĄlne mÊdiÃĄ, preskakuje sa", - "home_page_archive_err_partner": "Na teraz nemôŞete premiestniÅĨ partnerove mÊdiÃĄ do archívu", + "home_page_archive_err_partner": "NemoÅžno archivovaÅĨ partnerskÊ poloÅžky, preskakuje sa", "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_err_partner": "Nie je moÅžnÊ vymazaÅĨ poloÅžky partnera, preskakuje sa", "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.", + "home_page_first_time_notice": "Ak aplikÃĄciu pouŞívate prvÃŊkrÃĄt, uistite sa, Åže ste si vybrali zÃĄloÅžnÃŊ album, aby sa na časovej osi mohli zobrazovaÅĨ fotografie a videÃĄ", "home_page_share_err_local": "NemoÅžno zdieÄžaÅĨ lokÃĄlne mÊdiÃĄ pomocou odkazu", - "home_page_upload_err_limit": "Naraz môŞete nahraÅĨ len 30 mÊdií, preskakujem...", + "home_page_upload_err_limit": "Naraz môŞete nahraÅĨ len 30 mÊdií, preskakuje sa", "host": "HostiteÄž", "hour": "Hodina", + "id": "ID", "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}", @@ -965,7 +992,7 @@ "image_alt_text_date_place_1_person": "{isVideo, select, true {Video} other {ObrÃĄzok}} zo dňa {date} v {city}, {country} s {person1}", "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_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} nasnímanÃŊ v {city}, {country} s {person1}, {person2} a {additionalCount, number} inÃŊmi dňa {date}", "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", @@ -987,6 +1014,7 @@ "night_at_midnight": "KaÅždÃŊ deň o polnoci", "night_at_twoam": "KaÅždÃē noc o 2:00" }, + "invalid_date": "NeplatnÃŊ dÃĄtum", "invite_people": "PozvaÅĨ Äžudí", "invite_to_album": "PozvaÅĨ do albumu", "items_count": "{count, plural, one {# poloÅžka} few {# poloÅžky} other {# poloÅžiek}}", @@ -1004,6 +1032,7 @@ "leave": "OpustiÅĨ", "lens_model": "Model objektívu", "let_others_respond": "Nechajte ostatnÃŊch reagovaÅĨ", + "level": "Úroveň", "library": "KniÅžnica", "library_options": "MoÅžnosti kniÅžnice", "library_page_device_albums": "Albumy v zariadení", @@ -1021,12 +1050,15 @@ "list": "Zoznam", "loading": "Načítavanie", "loading_search_results_failed": "Načítanie vÃŊsledkov hÄžadania sa nepodarilo", - "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", - "location_picker_latitude_hint": "Zadajte sem vaÅĄu zemepisnÃē dÄēÅžku", + "local_network": "Miestna sieÅĨ", + "location_permission_content": "Na pouŞívanie funkcie automatickÊho prepínania potrebuje aplikÃĄcia Immich presnÊ povolenie na určenie polohy, aby mohla prečítaÅĨ nÃĄzov aktuÃĄlnej Wi-Fi siete", + "location_picker_choose_on_map": "ZvoÄžte na mape", + "location_picker_latitude_error": "Zadajte platnÃē zemepisnÃē ÅĄÃ­rku", + "location_picker_latitude_hint": "Zadajte sem vaÅĄu zemepisnÃē ÅĄÃ­rku", "location_picker_longitude_error": "Zadajte platnÃē zemepisnÃē dÄēÅžku", - "location_picker_longitude_hint": "Zadajte platnÃē zemepisnÃē ÅĄÃ­rku", + "location_picker_longitude_hint": "Zadajte platnÃē zemepisnÃē dÄēÅžku", + "lock": "ZamknÃēÅĨ", + "locked_folder": "ZamknutÃŊ priečinok", "log_out": "OdhlÃĄsiÅĨ sa", "log_out_all_devices": "OdhlÃĄsiÅĨ vÅĄetky zariadenia", "logged_out_all_devices": "VÅĄetky zariadenia odhlÃĄsenÊ", @@ -1045,11 +1077,11 @@ "login_form_err_trailing_whitespace": "KoncovÊ medzera", "login_form_failed_get_oauth_server_config": "Chyba prihlÃĄsenia pomocou OAuth, skontrolujte adresu URL servera", "login_form_failed_get_oauth_server_disable": "Funkcia OAuth nie je na tomto serveri dostupnÃĄ", - "login_form_failed_login": "Chyba prihlÃĄsenia, skontrolujte url adresu servera, e-mail a heslo.", + "login_form_failed_login": "Chyba prihlÃĄsenia, skontrolujte url adresu servera, e-mail a heslo", "login_form_handshake_exception": "Nastala chyba handshake. Zapnite podoporu samo-podpísanÃŊch certifikÃĄtov v nastaveniach ak pouŞívate samo-podpísanÊ certifikÃĄty.", "login_form_password_hint": "heslo", "login_form_save_login": "ZostaÅĨ prihlÃĄsenÃŊ", - "login_form_server_empty": "Zadajte URL servera", + "login_form_server_empty": "Zadajte URL adresu servera.", "login_form_server_error": "NemoÅžno pripojiÅĨ na server.", "login_has_been_disabled": "PrihlÃĄsenie bolo vypnutÊ.", "login_password_changed_error": "Nastala chyba pri aktualizovaní hesla", @@ -1060,7 +1092,8 @@ "look": "Zobrazenie", "loop_videos": "OpakovaÅĨ videÃĄ", "loop_videos_description": "Povolí prehrÃĄvanie videí v slučke v detailnom zobrazení.", - "main_branch_warning": "PouŞívate vÃŊvojÃĄrsku verziu; silno odporÃēčame pouŞívaÅĨ vydanÊ verzie!", + "main_branch_warning": "PouŞívate vÃŊvojÃĄrsku verziu; dôrazne odporÃēčame pouŞívaÅĨ vydanÊ verzie!", + "main_menu": "HlavnÃĄ ponuka", "make": "VÃŊrobca", "manage_shared_links": "SpravovaÅĨ zdieÄžanÊ odkazy", "manage_sharing_with_partners": "SpravovaÅĨ zdieÄžanie s partnermi", @@ -1072,16 +1105,16 @@ "map": "Mapa", "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_cannot_get_user_location": "Nie je moÅžnÊ získaÅĨ polohu pouŞívateÄža", "map_location_dialog_yes": "Áno", "map_location_picker_page_use_location": "PouÅžiÅĨ tÃēto polohu", - "map_location_service_disabled_content": "SluÅžba polohovania musí byÅĨ zapnutÃĄ aby mÊdiÃĄ zobrazovali vaÅĄu aktuÃĄlnu polohu. Chcete ju zapnÃēÅĨ?", - "map_location_service_disabled_title": "SluÅžba polohovania vypnutÃĄ", + "map_location_service_disabled_content": "SluÅžba určovania polohy musí byÅĨ povolenÃĄ, aby sa zobrazovali poloÅžky z vaÅĄej aktuÃĄlnej polohy. Chcete ju teraz zapnÃēÅĨ?", + "map_location_service_disabled_title": "SluÅžba určovania polohy vypnutÃĄ", "map_marker_for_images": "Značka na mape pre obrÃĄzky odfotenÊ v {city}, {country}", "map_marker_with_image": "MapovÃĄ značka pre obrÃĄzok", "map_no_assets_in_bounds": "Nič tu nie je", - "map_no_location_permission_content": "Povolenie polohy je potrebnÊ aby sa zobrazovali mÊdiÃĄ z vaÅĄej aktuÃĄlnej polohy. Chcete udeliÅĨ toto povolenie?", - "map_no_location_permission_title": "Udelenie polohy zamietnutÊ", + "map_no_location_permission_content": "Na zobrazenie poloÅžiek z vaÅĄej aktuÃĄlnej polohy je potrebnÊ povolenie na polohu. Chcete to teraz povoliÅĨ?", + "map_no_location_permission_title": "Povolenie polohy zamietnutÊ", "map_settings": "Nastavenia mÃĄp", "map_settings_dark_mode": "TmavÃŊ reÅžim", "map_settings_date_range_option_day": "PoslednÃŊch 24 hodín", @@ -1104,6 +1137,7 @@ "memories_swipe_to_close": "Zatvoríte posunom nahor", "memory": "PamäÅĨ", "memory_lane_title": "PÃĄs spomienok {title}", + "menu": "Ponuka", "merge": "ZlÃēčiÅĨ", "merge_people": "ZlÃēčiÅĨ Äžudí", "merge_people_limit": "ZlÃēčiÅĨ môŞete naraz najviac 5 tvÃĄrí", @@ -1113,12 +1147,17 @@ "minimize": "MinimalizovaÅĨ", "minute": "MinÃēta", "missing": "ChÃŊbajÃēce", + "model": "Model", "month": "Mesiac", "monthly_title_text_date_format": "LLLL y", "more": "Viac", + "move": "PresunÃēÅĨ", + "move_off_locked_folder": "PresunÃēÅĨ zo zamknutÊho priečinka", + "move_to_locked_folder": "PresunÃēÅĨ do zamknutÊho priečinka", + "move_to_locked_folder_confirmation": "Tieto fotografie a videÃĄ budÃē odstrÃĄnenÊ zo vÅĄetkÃŊch albumov a bude ich moÅžnÊ zobraziÅĨ len v zamknutom priečinku", "moved_to_trash": "PresunutÊ do koÅĄa", "multiselect_grid_edit_date_time_err_read_only": "NemoÅžno upraviÅĨ dÃĄtum poloÅžky len na čítanie, preskakujem", - "multiselect_grid_edit_gps_err_read_only": "NemoÅžno upraviÅĨ polohu poloÅžky len na čítanie, preskakujem", + "multiselect_grid_edit_gps_err_read_only": "Nie je moÅžnÊ upraviÅĨ polohu poloÅžky (poloÅžiek) len na čítanie, preskakuje sa", "mute_memories": "Vyblednutie spomienok", "my_albums": "Moje albumy", "name": "Meno", @@ -1128,6 +1167,8 @@ "new_api_key": "NovÃŊ API kÄžÃēč", "new_password": "NovÊ heslo", "new_person": "NovÃĄ osoba", + "new_pin_code": "NovÃŊ PIN kÃŗd", + "new_pin_code_subtitle": "Toto je vÃĄÅĄ prvÃŊ prístup k zamknutÊmu priečinku. Vytvorte si PIN kÃŗd na bezpečnÃŊ prístup k tejto strÃĄnke", "new_user_created": "NovÃŊ pouŞívateÄž vytvorenÃŊ", "new_version_available": "JE DOSTUPNÁ NOVÁ VERZIA", "newest_first": "NajnovÅĄie prvÊ", @@ -1145,6 +1186,7 @@ "no_explore_results_message": "Nahrajte viac fotiek na objavovanie vaÅĄej zbierky.", "no_favorites_message": "Pridajte si obÄžÃēbenÊ, aby ste rÃŊchlo naÅĄli svoje najlepÅĄie obrÃĄzky a videÃĄ", "no_libraries_message": "Vytvorí externÃē kniÅžnicu na prezeranie fotiek a videí", + "no_locked_photos_message": "Fotografie a videÃĄ v zamknutom priečinku sÃē skrytÊ a nezobrazujÃē sa pri prehÄžadÃĄvaní alebo vyhÄžadÃĄvaní v kniÅžnici.", "no_name": "Bez mena", "no_places": "Bez miesta", "no_results": "ÅŊiadne vÃŊsledky", @@ -1160,17 +1202,20 @@ "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_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.", + "offline": "Offline", "ok": "OK", "oldest_first": "NajstarÅĄie prvÊ", + "on_this_device": "Na tomto zariadení", "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_privacy_description": "NasledujÃēce (voliteÄžnÊ) funkcie zÃĄvisia na externÃŊch sluÅžbÃĄch a kedykoÄžvek ich môŞete vypnÃēÅĨ 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_user_welcome_description": "Začnime!", "onboarding_welcome_user": "Vitaj, {user}", + "online": "Online", "only_favorites": "Len obÄžÃēbenÊ", + "open": "OtvoriÅĨ", "open_in_map_view": "OtvoriÅĨ v mape", "open_in_openstreetmap": "OtvoriÅĨ v OpenStreetMap", "open_the_search_filters": "OtvoriÅĨ vyhÄžadÃĄvacie filtre", @@ -1183,9 +1228,10 @@ "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ÃĄ", + "partner_can_access_location": "Poloha, kde boli vaÅĄe fotografie nasnímanÊ", "partner_list_user_photos": "Fotky pouŞívateÄža {user}", "partner_list_view_all": "ZobraziÅĨ vÅĄetky", "partner_page_empty_message": "VaÅĄe fotky zatiaÄž nie sÃē zdieÄžanÊ so Åžiadnym partnerom.", @@ -1222,6 +1268,7 @@ "permanently_delete_assets_prompt": "Naozaj si prajete navÅždy zmazaÅĨ {count, plural, one {tÃēto poloÅžku?} other {tÃŊchto # poloÅžiek?}} VymaÅžÃē sa aj {count, plural, one {zo svojho albumu} other {zo svojich albumov}}.", "permanently_deleted_asset": "NavÅždy odstrÃĄnenÃĄ poloÅžka", "permanently_deleted_assets_count": "NavÅždy {count, plural, one {odstrÃĄnenÃĄ # poloÅžka} other {odstrÃĄnenÊ # poloÅžky}}", + "permission": "Povolenie", "permission_onboarding_back": "SpäÅĨ", "permission_onboarding_continue_anyway": "PokračovaÅĨ aj tak", "permission_onboarding_get_started": "ZačaÅĨ", @@ -1238,7 +1285,7 @@ "photos_and_videos": "Fotografie & Videa", "photos_count": "{count, plural, one {{count, number} Fotka} other {{count, number} Fotiek}}", "photos_from_previous_years": "Fotky z minulÃŊch rokov", - "pick_a_location": "Vyberte miesto", + "pick_a_location": "Vyberte polohu", "place": "Miesto", "places": "Miesta", "places_count": "{count, plural, one {{count, number} miesto} few {{count, number} miesta} other {{count, number} miest}}", @@ -1246,18 +1293,21 @@ "play_memories": "PrehraÅĨ spomienky", "play_motion_photo": "PrehraÅĨ pohyblivÃē fotku", "play_or_pause_video": "Pustí alebo pozastaví video", + "port": "Port", "preferences_settings_title": "Preferencie", "preset": "Prednastavenie", "preview": "NÃĄhÄžad", "previous": "PredoÅĄlÊ", "previous_memory": "PredoÅĄlÃĄ spomienka", - "previous_or_next_photo": "PredoÅĄlÃĄ alebo ďalÅĄia fotka", + "previous_or_next_photo": "Fotka ďalÅĄia/predoÅĄlÃĄ", "primary": "PrimÃĄrne", "privacy": "SÃēkromie", + "profile": "Profil", "profile_drawer_app_logs": "Logy", "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}", @@ -1284,7 +1334,7 @@ "purchase_lifetime_description": "DoÅživotnÃĄ platnosÅĨ", "purchase_option_title": "MOÅŊNOSTI NÁKUPU", "purchase_panel_info_1": "VÃŊvoj Immich zaberÃĄ veÄža času a Ãēsilia, a mÃĄme zamestnanÃŊch fulltime inÅžinierov, aby ho spravili ako sa najlepÅĄie dÃĄ. NaÅĄa misia je, aby sa open-source softvÊr a etickÊ biznis praktiky stali udrÅžateÄžnÃŊm zdrojom príjmu pre vÃŊvojÃĄrov a vytvorili ekosystÊm reÅĄpektujÃēci sÃēkromie so skutočnÃŊmi nÃĄhradami voči zneuŞívajÃēcim cloudovÃŊm sluÅžbÃĄm.", - "purchase_panel_info_2": "KeďŞe sme zaviazaní nezavÃĄdzaÅĨ paywally, nezískate tÃŊmto nÃĄkupom Åžiadne prídavnÊ funkcie. Spoliehame sa na pouŞívateÄžov ako vy na podporu neustÃĄleho vÃŊvoja Immich.", + "purchase_panel_info_2": "KeďŞe sme zaviazaní nezavÃĄdzaÅĨ platenÊ verzie, nezískate tÃŊmto nÃĄkupom Åžiadne prídavnÊ funkcie. Spoliehame sa na pouŞívateÄžov, ako ste vy, Åže podporia neustÃĄly vÃŊvoj aplikÃĄcie Immich.", "purchase_panel_title": "PodporiÅĨ projekt", "purchase_per_server": "Za server", "purchase_per_user": "Za pouŞívateÄža", @@ -1294,6 +1344,7 @@ "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", @@ -1309,6 +1360,8 @@ "recent-albums": "PoslednÊ albumy", "recent_searches": "PoslednÊ vyhÄžadÃĄvania", "recently_added_page_title": "NedÃĄvno pridanÊ", + "recently_taken": "NedÃĄvno nasnímanÊ", + "recently_taken_page_title": "NedÃĄvno zhotovenÊ", "refresh": "ObnoviÅĨ", "refresh_encoded_videos": "ObnoviÅĨ enkÃŗdovanÊ videÃĄ", "refresh_faces": "ObnoviÅĨ tvÃĄre", @@ -1328,9 +1381,12 @@ "remove_deleted_assets": "OdstrÃĄniÅĨ vymazanÊ poloÅžky", "remove_from_album": "OdstrÃĄniÅĨ z albumu", "remove_from_favorites": "OdstrÃĄniÅĨ z obÄžÃēbenÃŊch", + "remove_from_locked_folder": "OdstrÃĄniÅĨ zo zamknutÊho priečinka", + "remove_from_locked_folder_confirmation": "Ste si istí, Åže chcete tieto fotografie a videÃĄ presunÃēÅĨ zo zamknutÊho priečinka? BudÃē viditeÄžnÊ vo vaÅĄej kniÅžnici.", "remove_from_shared_link": "OdstrÃĄniÅĨ zo zdieÄžanÊho odkazu", "remove_memory": "OdstrÃĄniÅĨ spomienku", "remove_photo_from_memory": "OdstrÃĄniÅĨ fotografiu z tejto spomienky", + "remove_tag": "OdstrÃĄniÅĨ ÅĄtítok", "remove_url": "OdstrÃĄniÅĨ URL", "remove_user": "OdstrÃĄniÅĨ pouŞívateÄža", "removed_api_key": "OdstrÃĄniÅĨ API kÄžÃēč: {name}", @@ -1351,6 +1407,7 @@ "reset": "ResetovaÅĨ", "reset_password": "ObnoviÅĨ heslo", "reset_people_visibility": "ResetovaÅĨ viditeÄžnosÅĨ Äžudí", + "reset_pin_code": "ObnoviÅĨ PIN kÃŗd", "reset_to_default": "ResetovaÅĨ na predvolenÊ", "resolve_duplicates": "VyrieÅĄiÅĨ duplicity", "resolved_all_duplicates": "VyrieÅĄenÊ vÅĄetky duplicity", @@ -1362,6 +1419,7 @@ "retry_upload": "ZopakovaÅĨ nahrÃĄvanie", "review_duplicates": "PrezrieÅĨ duplikÃĄty", "role": "Rola", + "role_editor": "Editor", "role_viewer": "DivÃĄk", "save": "UloÅžiÅĨ", "saved_api_key": "UloÅženÃŊ API KÄžÃēč", @@ -1385,17 +1443,30 @@ "search_city": "HÄžadaÅĨ mesto...", "search_country": "HÄžadaÅĨ krajinu...", "search_filter_apply": "PouÅžiÅĨ filter", + "search_filter_camera_title": "Vyberte typ kamery", + "search_filter_date": "DÃĄtum", + "search_filter_date_title": "Vyberte rozsah dÃĄtumov", "search_filter_display_option_not_in_album": "Mimo albumu", + "search_filter_display_options": "MoÅžnosti zobrazenia", + "search_filter_location": "Poloha", + "search_filter_location_title": "Vyberte polohu", + "search_filter_media_type": "Typ mÊdia", + "search_filter_media_type_title": "Vyberte typ mÊdia", + "search_filter_people_title": "Vyberte Äžudí", "search_for": "VyhÄžadaÅĨ", "search_for_existing_person": "HÄžadaÅĨ existujÃēcu osobu", + "search_no_more_result": "ÅŊiadne ďalÅĄie vÃŊsledky", "search_no_people": "ÅŊiadne osoby", "search_no_people_named": "ÅŊiadne osoby menom \"{name}\"", + "search_no_result": "NenaÅĄli sa Åžiadne vÃŊsledky, skÃēste pouÅžiÅĨ inÃŊ vyhÄžadÃĄvací vÃŊraz alebo kombinÃĄciu", "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": "VyhÄžadajte svoje fotografie a videÃĄ", + "search_page_selfies": "AutoportrÊty", "search_page_things": "Veci", "search_page_view_all_button": "ZobraziÅĨ vÅĄetky", "search_page_your_activity": "VaÅĄa aktivita", @@ -1406,7 +1477,7 @@ "search_result_page_new_search_hint": "NovÊ vyhÄžadÃĄvanie", "search_settings": "HÄžadaÅĨ v nastaveniach", "search_state": "HÄžadaÅĨ ÅĄtÃĄty...", - "search_suggestion_list_smart_search_hint_1": "InteligentnÊ vyhÄžadÃĄvanie je predvolene povolenÊ, na vyhÄžadÃĄvanie metadÃĄt pouÅžite syntax", + "search_suggestion_list_smart_search_hint_1": "InteligentnÊ vyhÄžadÃĄvanie je predvolene povolenÊ, na vyhÄžadÃĄvanie metadÃĄt pouÅžite syntax ", "search_suggestion_list_smart_search_hint_2": "m:vÃĄÅĄ-hÄžadanÃŊ-vÃŊraz", "search_tags": "HÄžadaÅĨ ÅĄtítky...", "search_timezone": "HÄžadaÅĨ časovÃē zÃŗnu...", @@ -1426,6 +1497,7 @@ "select_keep_all": "VybraÅĨ ponechaÅĨ vÅĄetky", "select_library_owner": "VybraÅĨ vlastníka kniÅžnice", "select_new_face": "VybraÅĨ novÃē tvÃĄr", + "select_person_to_tag": "Vyberte osobu, ktorÃē chcete označiÅĨ", "select_photos": "VybraÅĨ fotky", "select_trash_all": "VybraÅĨ zahodiÅĨ vÅĄetky", "select_user_for_sharing_page_err_album": "Nepodarilo sa vytvoriÅĨ album", @@ -1453,7 +1525,6 @@ "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_title": "Jazyky", "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", @@ -1473,18 +1544,20 @@ "share_add_photos": "PridaÅĨ fotografie", "share_assets_selected": "{count} označenÃŊch", "share_dialog_preparing": "Pripravujem...", + "share_link": "ZdieÄžaÅĨ odkaz", "shared": "ZdieÄžanÊ", "shared_album_activities_input_disable": "KomentÃĄr je zakÃĄzanÃŊ", "shared_album_activity_remove_content": "Chcete vymazaÅĨ tÃēto aktivitu?", "shared_album_activity_remove_title": "VymazaÅĨ aktivitu", "shared_album_section_people_action_error": "Vyskytla sa chyba pri odchÃĄdzaní / odstraňovaní pouŞívateÄža z albumu", - "shared_album_section_people_action_leave": "OdísÅĨ z albumu", + "shared_album_section_people_action_leave": "OdstrÃĄniÅĨ pouŞívateÄža z albumu", "shared_album_section_people_action_remove_user": "OdstrÃĄniÅĨ pouŞívateÄža z albumu", "shared_album_section_people_title": "ÄŊUDIA", "shared_by": "ZdieÄža", "shared_by_user": "ZdieÄža {user}", "shared_by_you": "ZdieÄžanÊ vami", "shared_from_partner": "Fotky od {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} nahranÃŊch", "shared_link_app_bar_title": "ZdieÄžanÊ odkazy", "shared_link_clipboard_copied_massage": "SkopírovanÊ do schrÃĄnky", "shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}", @@ -1511,6 +1584,7 @@ "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", @@ -1530,7 +1604,7 @@ "show_albums": "ZobraziÅĨ albumy", "show_all_people": "ZobraziÅĨ vÅĄetkÃŊch Äžudí", "show_and_hide_people": "ZobraziÅĨ a skryÅĨ Äžudí", - "show_file_location": "Zobrazí umiestnenie sÃēboru", + "show_file_location": "ZobraziÅĨ umiestnenie sÃēboru", "show_gallery": "ZobraziÅĨ galÊriu", "show_hidden_people": "ZobraziÅĨ skrytÃŊch Äžudí", "show_in_timeline": "ZobraziÅĨ na časovej osi", @@ -1582,6 +1656,7 @@ "stop_sharing_photos_with_user": "ZastaviÅĨ zdieÄžanie tÃŊchto fotiek s tÃŊmto pouŞívateÄžom", "storage": "Ukladací priestor", "storage_label": "Å títok ÃēloÅžiska", + "storage_quota": "ÚloÅžnÃŊ limit", "storage_usage": "VyuÅžitÃŊch {used} z {available}", "submit": "OdoslaÅĨ", "suggestions": "NÃĄvrhy", @@ -1591,6 +1666,7 @@ "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": "SynchronizovaÅĨ albumy", "tag": "Značka", "tag_assets": "PridaÅĨ značku", "tag_created": "VytvorenÃĄ značka: {tag}", @@ -1610,7 +1686,7 @@ "theme_setting_image_viewer_quality_title": "Kvalita prehliadača obrÃĄzkov", "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.", + "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", "theme_setting_three_stage_loading_title": "Povolenie trojstupňovÊho načítavania", "they_will_be_merged_together": "ZlÃēčia sa dokopy", "third_party_resources": "Zdroje tretích strÃĄn", @@ -1624,7 +1700,6 @@ "to_parent": "PrejsÅĨ k nadradenÊmu", "to_trash": "KÃ´ÅĄ", "toggle_settings": "PrepnÃēÅĨ nastavenie", - "toggle_theme": "PrepnÃēÅĨ tmavÃē tÊmu", "total": "Celkom", "total_usage": "CelkovÊ vyuÅžitie", "trash": "KÃ´ÅĄ", @@ -1643,6 +1718,7 @@ "type": "Typ", "unarchive": "OdarchivovaÅĨ", "unarchived_count": "{count, plural, other {OdarchivovanÃŊch #}}", + "undo": "SpäÅĨ", "unfavorite": "OdznačiÅĨ ako obÄžÃēbenÊ", "unhide_person": "OdkryÅĨ osobu", "unknown": "NeznÃĄme", @@ -1661,9 +1737,8 @@ "unselect_all_duplicates": "ZruÅĄiÅĨ vÃŊber vÅĄetkÃŊch duplicít", "unstack": "OdskupiÅĨ", "unstacked_assets_count": "{count, plural, one {RozloÅženÃĄ # poloÅžka} few {RozloÅženÊ # poloÅžky} other {RozloÅženÃŊch # poloÅžiek}}", - "untracked_files": "NesledovanÊ sÃēbory", - "untracked_files_decription": "Tieto sÃēbory nie sÃē sledovanÊ aplikÃĄciou. Dôvodom môŞe byÅĨ zlyhanÃŊ presun, preruÅĄenÊ nahrÃĄvanie, alebo vÃŊsledkom bugu", "up_next": "To je vÅĄetko", + "updated_at": "AktualizovanÊ", "updated_password": "Heslo zmenenÊ", "upload": "NahraÅĨ", "upload_concurrency": "SÃēbeÅžnosÅĨ nahrÃĄvania", @@ -1676,12 +1751,17 @@ "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": "NahraÅĨ na Immich ({count})", + "uploading": "NahrÃĄvanie", "url": "Odkaz URL", "usage": "PouÅžitie", - "use_custom_date_range": "PouÅžite radÅĄej vlastnÃŊ rozsah dÃĄtumov", + "use_custom_date_range": "PouÅžiÅĨ radÅĄej vlastnÃŊ rozsah dÃĄtumov", "user": "PouŞívateÄž", + "user_has_been_deleted": "Tento pouŞívateÄž bol vymazanÃŊ.", "user_id": "PouŞívateÄžskÊ ID", "user_liked": "PouŞívateÄžovi {user} sa pÃĄÄi {type, select, photo {tÃĄto fotka} video {toto video} asset {tÃĄto poloÅžka} other {toto}}", + "user_pin_code_settings": "PIN kÃŗd", + "user_pin_code_settings_description": "Spravujte svoj PIN kÃŗd", "user_purchase_settings": "NÃĄkup", "user_purchase_settings_description": "SprÃĄva vÃĄÅĄho nÃĄkupu", "user_role_set": "Nastav {user} ako {role}", @@ -1696,13 +1776,9 @@ "version": "Verzia", "version_announcement_closing": "Tvoj kamarÃĄt, Alex", "version_announcement_message": "Ahoj! NovÃĄ verzia Immich je dostupnÃĄ. Prosím prečítajte si poznÃĄmky k vydaniu, aby ste sa uistili, Åže inÅĄtalÃĄcia bude aktuÃĄlna bez problÊmov, najmä ak pouŞívate WatchTower alebo akÃŊkoÄžvek spôsob automatickej aktualizÃĄcie Immich servera.", - "version_announcement_overlay_release_notes": "poznÃĄmky k vydaniu", - "version_announcement_overlay_text_1": "Ahoj, je tu novÃĄ verzia", - "version_announcement_overlay_text_2": "nÃĄjdite si čas na nÃĄvÅĄtevu ", - "version_announcement_overlay_text_3": " a uistite sa, Åže vaÅĄa konfigurÃĄcia docker-compose a .env je aktuÃĄlna, aby ste prediÅĄli nesprÃĄvnej konfigurÃĄcii, najmä ak pouŞívate WatchTower alebo akÃŊkoÄžvek mechanizmus, ktorÃŊ podporuje automatickÊ aktualizÃĄcie serverovÃŊch aplikÃĄcií.", - "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ÃĄ", @@ -1717,7 +1793,9 @@ "view_name": "ZobraziÅĨ", "view_next_asset": "ZobraziÅĨ nasledujÃēci sÃēbor", "view_previous_asset": "ZobraziÅĨ predchÃĄdzajÃēci sÃēbor", + "view_qr_code": "ZobraziÅĨ QR kÃŗd", "view_stack": "ZobraziÅĨ zoskupenie", + "view_user": "ZobraziÅĨ pouŞívateÄža", "viewer_remove_from_stack": "OdstrÃĄniÅĨ zo zoskupenia", "viewer_stack_use_as_main_asset": "PouÅžiÅĨ ako hlavnÃē fotku", "viewer_unstack": "OdskupiÅĨ", @@ -1727,11 +1805,12 @@ "week": "TÃŊÅždeň", "welcome": "Vitajte", "welcome_to_immich": "Vitajte v Immich", - "wifi_name": "WiFi Name", + "wifi_name": "NÃĄzov Wi-Fi", + "wrong_pin_code": "NesprÃĄvny PIN kÃŗd", "year": "Rok", "years_ago": "pred {years, plural, one {# rokom} other {# rokmi}}", "yes": "Áno", "you_dont_have_any_shared_links": "NemÃĄte Åžiadne zdielanÊ linky", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "VÃĄÅĄ nÃĄzov siete Wi-Fi", "zoom_image": "PriblíŞiÅĨ obrÃĄzok" } diff --git a/i18n/sl.json b/i18n/sl.json index db1e7aead9..5132d09fc8 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -22,11 +22,11 @@ "add_partner": "Dodaj partnerja", "add_path": "Dodaj pot", "add_photos": "Dodaj fotografije", + "add_tag": "Dodaj oznako", "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 {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", @@ -34,6 +34,7 @@ "added_to_favorites_count": "{count, number} dodanih med priljubljene", "admin": { "add_exclusion_pattern_description": "Dodajte vzorec izključitev. Globiranje z uporabo *, ** in ? je podprto. Če Åželite prezreti vse datoteke v katerem koli imeniku z imenom \"Raw\", uporabite \"**/Raw/**\". Če Åželite prezreti vse datoteke, ki se končajo na \".tif\", uporabite \"**/*.tif\". Če Åželite prezreti absolutno pot, uporabite \"/pot/za/ignoriranje/**\".", + "admin_user": "SkrbniÅĄki uporabnik", "asset_offline_description": "Sredstva zunanje knjiÅžnice ni več mogoče najti na disku in je bilo premaknjeno v koÅĄ. Če je bila datoteka premaknjena znotraj knjiÅžnice, preverite svojo časovnico za novo ustrezno sredstvo. Če Åželite obnoviti to sredstvo, zagotovite, da ima Immich dostop do spodnje poti datoteke, in skenirajte knjiÅžnico.", "authentication_settings": "Nastavitve preverjanja pristnosti", "authentication_settings_description": "Upravljanje gesel, OAuth in drugih nastavitev preverjanja pristnosti", @@ -44,9 +45,7 @@ "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", + "backup_settings_description": "Upravljanje nastavitev izpisa podatkovne baze.", "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}?", @@ -62,14 +61,12 @@ "disable_login": "Onemogoči prijavo", "duplicate_detection_job_description": "ZaÅženite strojno učenje na sredstvih, da zaznate podobne slike. ZanaÅĄa se na Pametno Iskanje", "exclusion_pattern_description": "Vzorci izključitev vam omogočajo, da prezrete datoteke in mape pri skeniranju knjiÅžnice. To je uporabno, če imate mape z datotekami, ki jih ne Åželite uvoziti, na primer datoteke RAW.", - "external_library_created_at": "Zunanja knjiÅžnica (ustvarjena dne {date})", "external_library_management": "Upravljanje zunanje knjiÅžnice", "face_detection": "Zaznavanje obrazov", "face_detection_description": "Zaznajte obraze v sredstvih s pomočjo strojnega učenja. Pri videoposnetkih se upoÅĄteva samo sličica. \"Vse\" (ponovno) obdela vsa sredstva. \"Manjkajoče\" postavi v čakalno vrsto sredstva, ki ÅĄe niso bila obdelana. Zaznani obrazi bodo postavljeni v čakalno vrsto za prepoznavanje obrazov, ko bo zaznavanje obrazov končano, in jih bodo zdruÅžili v obstoječe ali nove osebe.", "facial_recognition_job_description": "ZdruÅži zaznane obraze v osebe. Ta korak se izvede po končanem zaznavanju obrazov. \"Vse\" (ponovno) zdruÅžuje vse obraze. \"Manjkajoče\", doda v čakalno vrsto obraze, ki nimajo dodeljene osebe.", "failed_job_command": "Za opravilo {job} ukaz {command} ni uspel", "force_delete_user_warning": "OPOZORILO: S tem boste takoj odstranili uporabnika in vsa sredstva. Tega ni mogoče razveljaviti in datotek ni mogoče obnoviti.", - "forcing_refresh_library_files": "Vsiljena osveÅžitev vseh datotek knjiÅžnice", "image_format": "Format", "image_format_description": "WebP ustvari manjÅĄe datoteke kot JPEG, vendar je počasnejÅĄi za kodiranje.", "image_fullsize_description": "Slika v polni velikosti brez metapodatkov, uporabljena pri povečavi", @@ -174,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Opomba: Če Åželite oznako za shranjevanje uporabiti za predhodno naloÅžena sredstva, zaÅženite", "note_cannot_be_changed_later": "OPOMBA: Tega pozneje ni mogoče spremeniti!", "notification_email_from_address": "Iz naslova", - "notification_email_from_address_description": "E-poÅĄtni naslov poÅĄiljatelja, na primer: \"Immich Photo Server \"", + "notification_email_from_address_description": "E-poÅĄtni naslov poÅĄiljatelja, na primer: \"Immich Photo Server \". Uporabite naslov, s katerega lahko poÅĄiljate e-poÅĄto.", "notification_email_host_description": "Gostitelj e-poÅĄtnega streÅžnika (npr. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Prezri napake potrdil", "notification_email_ignore_certificate_errors_description": "Prezri napake pri preverjanju potrdila TLS (ni priporočljivo)", @@ -198,7 +195,7 @@ "oauth_enable_description": "Prijava z OAuth", "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_mobile_redirect_uri_override_description": "Omogoči, ko ponudnik OAuth ne dovoli mobilnega URI-ja, kot je ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "Upravljanje nastavitev prijave OAuth", "oauth_settings_more_details": "Za več podrobnosti o tej funkciji glejte dokumentacijo.", @@ -207,11 +204,9 @@ "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_storage_quota_default_description": "Kvota v GiB, ki se uporabi, kadar ni podanega zahtevka.", "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", "password_settings": "Prijava z geslom", "password_settings_description": "Upravljajte nastavitve prijave z geslom", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "OsveÅževanje vseh knjiÅžnic", "registration": "Administratorska registracija", "registration_description": "Ker ste prvi uporabnik v sistemu, boste dodeljeni kot skrbnik in ste odgovorni za skrbniÅĄka opravila, dodatne uporabnike pa boste ustvarili sami.", - "repair_all": "Popravi vse", - "repair_matched_items": "Ujemanje {count, plural, one {# predmet} two {# predmeta} few {# predmeti} other {# predmetov}}", - "repaired_items": "Popravljeno {count, plural, one {# predmet} two {# predmeta} few {# predmeti} other {# predmetov}}", "require_password_change_on_login": "Od uporabnika zahtevajte spremembo gesla ob prvi prijavi", "reset_settings_to_default": "Ponastavi nastavitve na privzete", "reset_settings_to_recent_saved": "Ponastavite nastavitve na nedavno shranjene nastavitve", @@ -252,7 +244,7 @@ "storage_template_migration_info": "Spremembe predloge bodo veljale samo za nova sredstva. Če Åželite retroaktivno uporabiti predlogo za predhodno naloÅžena sredstva, zaÅženite {job}.", "storage_template_migration_job": "Opravilo selitve predloge za shranjevanje", "storage_template_more_details": "Za več podrobnosti o tej funkciji si oglejte Predlogo za shranjevanje in njene posledice", - "storage_template_onboarding_description": "Ko je omogočena, bo ta funkcija samodejno organizirala datoteke na podlagi uporabniÅĄko določene predloge. Zaradi teÅžav s stabilnostjo je bila funkcija privzeto izklopljena. Za več informacij si oglejte dokumentacijo.", + "storage_template_onboarding_description_v2": "Ko je omogočena, bo ta funkcija samodejno organizirala datoteke na podlagi uporabniÅĄko določene predloge. Za več informacij glejte dokumentacijo.", "storage_template_path_length": "PribliÅžna omejitev dolÅžine poti: {length, number}/{limit, number}", "storage_template_settings": "Predloga za shranjevanje", "storage_template_settings_description": "Upravljajte strukturo map in ime datoteke sredstva za nalaganje", @@ -264,16 +256,14 @@ "template_email_invite_album": "Predloga povabila v album", "template_email_preview": "Predogled", "template_email_settings": "E-poÅĄtne predloge", - "template_email_settings_description": "Upravljajte predloge e-poÅĄtnih obvestil po meri", "template_email_update_album": "Predloga posodobitve albuma", "template_email_welcome": "Predloga pozdravnega e-poÅĄtnega sporočila", "template_settings": "Predloge obvestil", - "template_settings_description": "Upravljajte predloge po meri za obvestila.", + "template_settings_description": "Upravljanje predlog po meri za obvestila", "theme_custom_css_settings": "CSS po meri", "theme_custom_css_settings_description": "Kaskadni slogovni listi (CSS) omogočajo prilagajanje oblikovanja Immicha.", "theme_settings": "Nastavitve teme", "theme_settings_description": "Upravljanje prilagajanja spletnega vmesnika Immich", - "these_files_matched_by_checksum": "Te datoteke se ujemajo z njihovimi kontrolnimi vsotami", "thumbnail_generation_job": "Ustvarite sličice", "thumbnail_generation_job_description": "Ustvari velike, majhne in zamegljene sličice za vsako sredstvo ter sličice za vsako osebo", "transcoding_acceleration_api": "API za pospeÅĄevanje", @@ -301,10 +291,9 @@ "transcoding_encoding_options": "MoÅžnosti kodiranja", "transcoding_encoding_options_description": "Nastavite kodeke, ločljivost, kakovost in druge moÅžnosti za kodirane videoposnetke", "transcoding_hardware_acceleration": "Strojno pospeÅĄevanje", - "transcoding_hardware_acceleration_description": "Eksperimentalno; veliko hitreje, vendar bo imel slabÅĄo kakovost pri isti bitni hitrosti", + "transcoding_hardware_acceleration_description": "Eksperimentalno: hitrejÅĄe prekodiranje, vendar se lahko kakovost pri enaki bitni hitrosti zmanjÅĄa", "transcoding_hardware_decoding": "Strojno dekodiranje", "transcoding_hardware_decoding_setting_description": "Omogoča pospeÅĄevanje od konca do konca namesto samo pospeÅĄevanja kodiranja. Morda ne bo delovalo na vseh videoposnetkih.", - "transcoding_hevc_codec": "Kodek HEVC", "transcoding_max_b_frames": "Največji B-okvirji", "transcoding_max_b_frames_description": "ViÅĄje vrednosti izboljÅĄajo učinkovitost stiskanja, vendar upočasnijo kodiranje. Morda ni zdruÅžljivo s strojnim pospeÅĄevanjem na starejÅĄih napravah. 0 onemogoči okvirje B, medtem ko -1 samodejno nastavi to vrednost.", "transcoding_max_bitrate": "Največja bitna hitrost", @@ -321,7 +310,7 @@ "transcoding_reference_frames": "Referenčni okvirji", "transcoding_reference_frames_description": "Å tevilo okvirjev, na katere se sklicujete pri stiskanju danega okvira. ViÅĄje vrednosti izboljÅĄajo učinkovitost stiskanja, vendar upočasnijo kodiranje. 0 samodejno nastavi to vrednost.", "transcoding_required_description": "Samo videoposnetki, ki niso v sprejemljivi obliki", - "transcoding_settings": "Nastavitve video transkodiranja", + "transcoding_settings": "Nastavitve video prekodiranja", "transcoding_settings_description": "Upravljajte katere videoposnetke Åželite prekodirati in kako jih obdelati", "transcoding_target_resolution": "Ciljna ločljivost", "transcoding_target_resolution_description": "ViÅĄje ločljivosti lahko ohranijo več podrobnosti, vendar kodiranje traja dlje, imajo večje velikosti datotek in lahko zmanjÅĄajo odzivnost aplikacije.", @@ -332,7 +321,7 @@ "transcoding_tone_mapping": "Tonska preslikava", "transcoding_tone_mapping_description": "PoskuÅĄa ohraniti videz videoposnetkov HDR pri pretvorbi v SDR. Vsak algoritem naredi različne kompromise glede barve, podrobnosti in svetlosti. Hable ohrani podrobnosti, Mobius ohrani barvo, Reinhard pa svetlost.", "transcoding_transcode_policy": "Politika prekodiranja", - "transcoding_transcode_policy_description": "Pravilnik o tem, kdaj je treba videoposnetek prekodirati. Videoposnetki HDR bodo vedno prekodirani (razen če je transkodiranje onemogočeno).", + "transcoding_transcode_policy_description": "Pravilnik o tem, kdaj je treba videoposnetek prekodirati. Videoposnetki HDR bodo vedno prekodirani (razen če je prekodiranje onemogočeno).", "transcoding_two_pass_encoding": "Dvohodno kodiranje", "transcoding_two_pass_encoding_setting_description": "Prekodirajte v dveh prehodih za ustvarjanje bolje kodiranih videoposnetkov. Ko je omogočena največja bitna hitrost (ki je potrebna za delovanje s H.264 in HEVC), ta način uporablja obseg bitne hitrosti, ki temelji na največji bitni hitrosti, in ignorira CRF. Za VP9 je mogoče uporabiti CRF, če je največja bitna hitrost onemogočena.", "transcoding_video_codec": "Video kodek", @@ -342,8 +331,6 @@ "trash_number_of_days_description": "Å tevilo dni za shranjevanje sredstev v smetnjaku, preden jih trajno odstranite", "trash_settings": "Nastavitve smetnjaka", "trash_settings_description": "Upravljanje nastavitev smetnjaka", - "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 {# dan} other {# dni}}.", "user_delete_delay_settings": "Zamakni izbris", @@ -386,7 +373,7 @@ "advanced_settings_troubleshooting_title": "Odpravljanje teÅžav", "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 #}}", + "age_years": "Starost {years, plural, one {# leto} two {# leti} few {# leta} other {# let}}", "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", @@ -402,10 +389,6 @@ "album_remove_user": "Odstrani uporabnika?", "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": "{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}", @@ -421,6 +404,9 @@ "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} two {{count, number} albuma} few {{count, number} albumi} other {{count, number} albumov}}", + "albums_default_sort_order": "Privzeti vrstni red razvrÅĄÄanja albumov", + "albums_default_sort_order_description": "Začetni vrstni red razvrÅĄÄanja sredstev pri ustvarjanju novih albumov.", + "albums_feature_description": "Zbirke sredstev, ki jih je mogoče deliti z drugimi uporabniki.", "all": "Vse", "all_albums": "Vsi albumi", "all_people": "Vsi ljudje", @@ -479,9 +465,12 @@ "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_cannot_be_added_to_album_count": "{count, plural, one {Sredstvo} two {Sredstvi} few {Sredstva} other {Sredstev}} ni mogoče dodati v 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_downloaded_failed": "{count, plural, one {PreneÅĄena # datoteka - {error} datoteka ni uspela} two {PreneÅĄeni # datoteki - {error} datoteki nista uspeli} few {PreneÅĄene # datoteke - {error} datoteke niso uspele} other {PreneÅĄenih # datotek - {error} datoteke niso uspele}}", + "assets_downloaded_successfully": "{count, plural, one {UspeÅĄno preneÅĄena # datoteka} two {UspeÅĄno preneÅĄeni # datoteki} few {UspeÅĄno preneÅĄene # datoteke} other {UspeÅĄno preneÅĄenih # datotek}}", "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}}", @@ -496,6 +485,7 @@ "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", + "autoplay_slideshow": "Samodejno predvajanje diaprojekcije", "back": "Nazaj", "back_close_deselect": "Nazaj, zaprite ali prekličite izbiro", "background_location_permission": "Dovoljenje za iskanje lokacije v ozadju", @@ -539,6 +529,7 @@ "backup_controller_page_excluded": "Izključeno: ", "backup_controller_page_failed": "NeuspeÅĄno ({count})", "backup_controller_page_filename": "Ime datoteke: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informacija o varnostnem kopiranju", "backup_controller_page_none_selected": "Noben izbran", "backup_controller_page_remainder": "Ostanek", @@ -576,21 +567,17 @@ "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 ({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 ({count})", - "cache_settings_image_cache_size": "Velikost predpomnilnika slik ({count} sredstev)", "cache_settings_statistics_album": "Sličice knjiÅžnice", - "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 ({count} sredstev)", "cache_settings_tile_subtitle": "Nadzoruj vedenje lokalnega shranjevanja", "cache_settings_tile_title": "Lokalna shramba", "cache_settings_title": "Nastavitve predpomnjenja", @@ -604,13 +591,14 @@ "cannot_undo_this_action": "Tega dejanja ne morete razveljaviti!", "cannot_update_the_description": "Opisa ni mogoče posodobiti", "cast": "Pretakaj", + "cast_description": "Konfigurirajte razpoloÅžljive cilje za predvajanje", "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", "change_name": "Spremeni ime", - "change_name_successfully": "Sprememba imena uspeÅĄna", + "change_name_successfully": "Ime uspeÅĄno spremenjeno", "change_password": "Zamenjaj geslo", "change_password_description": "To je bodisi prvič, da se vpisujete v sistem ali pa je bila podana zahteva za spremembo vaÅĄega gesla. Spodaj vnesite novo geslo.", "change_password_form_confirm_password": "Potrdi geslo", @@ -621,7 +609,6 @@ "change_pin_code": "Spremeni PIN kodo", "change_your_password": "Spremenite geslo", "changed_visibility_successfully": "UspeÅĄno spremenjena vidnost", - "check_all": "Označite vse", "check_corrupt_asset_backup": "Preverite poÅĄkodovane varnostne kopije sredstev", "check_corrupt_asset_backup_button": "Izvedi preverjanje", "check_corrupt_asset_backup_description": "To preverjanje zaÅženite samo prek omreÅžja Wi-Fi in potem, ko so vsa sredstva varnostno kopirana. Postopek lahko traja nekaj minut.", @@ -661,11 +648,13 @@ "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", + "confirm_tag_face": "Ali Åželite označiti ta obraz kot {name}?", + "confirm_tag_face_unnamed": "Ali Åželite označiti ta obraz?", + "connected_device": "Povezana naprava", "connected_to": "Povezan s", "contain": "Vsebuje", "context": "Kontekst", "continue": "Nadaljuj", - "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", @@ -711,10 +700,14 @@ "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", + "darkTheme": "Preklopi na temno temo", "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", @@ -756,7 +749,9 @@ "direction": "Usmeritev", "disabled": "Onemogočeno", "disallow_edits": "Onemogoči urejanje", + "discord": "Discord", "discover": "Odkrij", + "discovered_devices": "Odkrite naprave", "dismiss_all_errors": "Opusti vse napake", "dismiss_error": "Opusti napako", "display_options": "MoÅžnosti prikaza", @@ -772,7 +767,6 @@ "download_enqueue": "Prenos v čakalni vrsti", "download_error": "Napaka pri prenosu", "download_failed": "Prenos ni uspel", - "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", @@ -835,6 +829,7 @@ "error_delete_face": "Napaka pri brisanju obraza iz sredstva", "error_loading_image": "Napaka pri nalaganju slike", "error_saving_image": "Napaka: {error}", + "error_tag_face_bounding_box": "Napaka pri označevanju obraza - ni mogoče pridobiti koordinat omejevalnega okvirja", "error_title": "Napaka - nekaj je ÅĄlo narobe", "errors": { "cannot_navigate_next_asset": "Ni mogoče krmariti do naslednjega sredstva", @@ -847,7 +842,6 @@ "cant_get_number_of_comments": "Ni mogoče pridobiti ÅĄtevila komentarjev", "cant_search_people": "Ni mogoče iskati ljudi", "cant_search_places": "Ne morem iskati mest", - "cleared_jobs": "PočiÅĄÄena opravila za: {job}", "error_adding_assets_to_album": "Napaka pri dodajanju sredstev v album", "error_adding_users_to_album": "Napaka pri dodajanju uporabnikov v album", "error_deleting_shared_user": "Napaka pri brisanju uporabnika v skupni rabi", @@ -856,7 +850,6 @@ "error_removing_assets_from_album": "Napaka pri odstranjevanju sredstev iz albuma, preverite konzolo za več podrobnosti", "error_selecting_all_assets": "Napaka pri izbiri vseh sredstev", "exclusion_pattern_already_exists": "Ta vzorec izključitve Åže obstaja.", - "failed_job_command": "Ukaz {command} ni uspel za opravilo: {job}", "failed_to_create_album": "Albuma ni bilo mogoče ustvariti", "failed_to_create_shared_link": "Povezave v skupni rabi ni bilo mogoče ustvariti", "failed_to_edit_shared_link": "Povezave v skupni rabi ni bilo mogoče urediti", @@ -875,7 +868,6 @@ "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}}", "unable_to_add_album_users": "Uporabnikov ni mogoče dodati v album", "unable_to_add_assets_to_shared_link": "Povezavi v skupni rabi ni mogoče dodati sredstev", "unable_to_add_comment": "Ni mogoče dodati komentarja", @@ -894,7 +886,6 @@ "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", "unable_to_copy_to_clipboard": "Ni mogoče kopirati v odloÅžiÅĄÄe, preverite, ali dostopate do strani prek https", "unable_to_create_admin_account": "Ni mogoče ustvariti skrbniÅĄkega računa", "unable_to_create_api_key": "Ni mogoče ustvariti novega API ključa", @@ -918,14 +909,9 @@ "unable_to_hide_person": "Osebe ni mogoče skriti", "unable_to_link_motion_video": "Ni mogoče povezati videa gibanja", "unable_to_link_oauth_account": "Računa OAuth ni mogoče povezati", - "unable_to_load_album": "Albuma ni mogoče naloÅžiti", - "unable_to_load_asset_activity": "Dejavnosti sredstva ni mogoče naloÅžiti", - "unable_to_load_items": "Elementov ni mogoče naloÅžiti", - "unable_to_load_liked_status": "Ni mogoče naloÅžiti statusa vÅĄečka", "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", @@ -933,11 +919,9 @@ "unable_to_remove_album_users": "Uporabnikov ni mogoče odstraniti iz albuma", "unable_to_remove_api_key": "Ključa API ni mogoče odstraniti", "unable_to_remove_assets_from_shared_link": "Ni mogoče odstraniti sredstev iz skupne povezave", - "unable_to_remove_deleted_assets": "Datotek brez povezave ni mogoče odstraniti", "unable_to_remove_library": "KnjiÅžnice ni mogoče odstraniti", "unable_to_remove_partner": "Partnerja ni mogoče odstraniti", "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", @@ -967,13 +951,13 @@ "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 {age}", - "exif_bottom_sheet_person_age_months": "Staros {months} mesecev", + "exif_bottom_sheet_person_age_months": "Starost {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", @@ -1010,6 +994,7 @@ "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", @@ -1019,6 +1004,8 @@ "folders": "Mape", "folders_feature_description": "Brskanje po pogledu mape za fotografije in videoposnetke v datotečnem sistemu", "forward": "Naprej", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Ta funkcija za delovanje nalaga zunanje vire iz Googla.", "general": "SploÅĄno", "get_help": "PoiÅĄÄite pomoč", "get_wifiname_error": "Imena Wi-Fi ni bilo mogoče dobiti. Prepričajte se, da ste podelili potrebna dovoljenja in ste povezani v omreÅžje Wi-Fi", @@ -1067,6 +1054,7 @@ "home_page_upload_err_limit": "Hkrati lahko naloÅžite največ 30 sredstev, preskakujem", "host": "Gostitelj", "hour": "Ura", + "id": "ID", "ignore_icloud_photos": "Ignoriraj fotografije iCloud", "ignore_icloud_photos_description": "Fotografije, shranjene v iCloud, ne bodo naloÅžene na streÅžnik Immich", "image": "Slika", @@ -1095,6 +1083,7 @@ "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} two {uri} few {ure} other {{hours, number} ur}}", @@ -1105,6 +1094,12 @@ "invalid_date_format": "Neveljavna oblika datuma", "invite_people": "Povabi ljudi", "invite_to_album": "Povabi v album", + "ios_debug_info_fetch_ran_at": "Pridobivanje se je izvedlo {dateTime}", + "ios_debug_info_last_sync_at": "Zadnja sinhronizacija {dateTime}", + "ios_debug_info_no_processes_queued": "Ni čakalnih vrst v ozadju", + "ios_debug_info_no_sync_yet": "Ni bilo ÅĄe izvedeno nobeno opravilo sinhronizacije v ozadju", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proces v ozadju v čakalni vrsti} two {{count} procesa v ozadju v čakalni vrsti} few {{count} procesi v ozadju v čakalni vrsti} other {{count} procesov v ozadju v čakalni vrsti}}", + "ios_debug_info_processing_ran_at": "Obdelava je potekala {dateTime}", "items_count": "{count, plural, one {# predmet} two {# predmeta} few {# predmeti} other {# predmetov}}", "jobs": "Opravila", "keep": "ObdrÅži", @@ -1113,6 +1108,9 @@ "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_no_results_subtitle": "Poskusite prilagoditi iskalni izraz", + "language_no_results_title": "Ni najdenih jezikov", + "language_search_hint": "Iskanje jezikov...", "language_setting_description": "Izberite Åželeni jezik", "last_seen": "Nazadnje viden", "latest_version": "NajnovejÅĄa različica", @@ -1138,6 +1136,7 @@ "list": "Seznam", "loading": "Nalaganje", "loading_search_results_failed": "Nalaganje rezultatov iskanja ni uspelo", + "local_asset_cast_failed": "Sredstva, ki niso naloÅžena na streÅžnik, ni mogoče predvajati", "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", @@ -1151,6 +1150,7 @@ "locked_folder": "Zaklenjena mapa", "log_out": "Odjava", "log_out_all_devices": "Odjava vseh naprav", + "logged_in_as": "Prijavljen kot {user}", "logged_out_all_devices": "Odjavljene so vse naprave", "logged_out_device": "Odjavljena naprava", "login": "Prijava", @@ -1240,7 +1240,9 @@ "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", @@ -1276,12 +1278,13 @@ "no_archived_assets_message": "Arhivirajte fotografije in videoposnetke, da jih skrijete v pogledu fotografij", "no_assets_message": "KLIKNITE ZA NALOÅŊITEV SVOJE PRVE FOTOGRAFIJE", "no_assets_to_show": "Ni sredstev za prikaz", + "no_cast_devices_found": "Naprav za predvajanje ni bilo mogoče najti", "no_duplicates_found": "Najden ni bil noben dvojnik.", "no_exif_info_available": "Podatki o exif niso na voljo", "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_locked_photos_message": "Fotografije in videoposnetki v zaklenjeni mapi so skriti in se ne bodo prikazali med brskanjem ali iskanjem po knjiÅžnici.", "no_name": "Brez imena", "no_notifications": "Ni obvestil", "no_people_found": "Ni najdenih ustreznih oseb", @@ -1301,17 +1304,18 @@ "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", - "offline_paths_description": "Ti rezultati so morda posledica ročnega brisanja datotek, ki niso del zunanje knjiÅžnice.", "ok": "V redu", "oldest_first": "Najprej najstarejÅĄi", "on_this_device": "Na tej napravi", "onboarding": "Vkrcanje", - "onboarding_privacy_description": "Naslednje (neobvezne) funkcije so odvisne od zunanjih storitev in jih je mogoče kadar koli onemogočiti v skrbniÅĄkih nastavitvah.", + "onboarding_locale_description": "Izberite Åželeni jezik. To lahko pozneje spremenite v nastavitvah.", + "onboarding_privacy_description": "Naslednje (neobvezne) funkcije so odvisne od zunanjih storitev in jih je mogoče kadar koli onemogočiti v nastavitvah.", + "onboarding_server_welcome_description": "Nastavimo vaÅĄ primerek z nekaj pogostimi nastavitvami.", "onboarding_theme_description": "Izberite barvno temo za svoj primer. To lahko pozneje spremenite v nastavitvah.", - "onboarding_welcome_description": "Nastavimo vaÅĄ primerek z nekaj običajnimi nastavitvami.", + "onboarding_user_welcome_description": "Pa začnimo!", "onboarding_welcome_user": "Pozdravljen/a, {user}", "online": "Povezano", "only_favorites": "Samo priljubljene", @@ -1328,6 +1332,7 @@ "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", @@ -1348,7 +1353,7 @@ "past_durations": { "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}}" + "years": "{years, plural, one {Preteklo leto} two {Pretekli # leti} few {Pretekla # leta} other {Preteklih # let}}" }, "path": "Pot", "pattern": "Vzorec", @@ -1367,6 +1372,8 @@ "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}}", + "permission": "Dovoljenje", + "permission_empty": "VaÅĄe dovoljenje ne sme biti prazno", "permission_onboarding_back": "Sredstev partnerja ni mogoče izbrisati, preskakujem", "permission_onboarding_continue_anyway": "Vseeno nadaljuj", "permission_onboarding_get_started": "Začnimo", @@ -1403,7 +1410,10 @@ "preview": "Predogled", "previous": "PrejÅĄnj-a/-i", "previous_memory": "PrejÅĄnji spomin", - "previous_or_next_photo": "PrejÅĄnja ali naslednja fotografija", + "previous_or_next_day": "Dan naprej/nazaj", + "previous_or_next_month": "Mesec naprej/nazaj", + "previous_or_next_photo": "Fotografija naprej/nazaj", + "previous_or_next_year": "Leto naprej/nazaj", "primary": "Primarni", "privacy": "Zasebnost", "profile": "Profil", @@ -1411,6 +1421,7 @@ "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}", @@ -1486,10 +1497,11 @@ "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_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", + "remove_tag": "Odstrani oznako", "remove_url": "Odstrani URL", "remove_user": "Odstrani uporabnika", "removed_api_key": "Odstranjen ključ API-ja: {name}", @@ -1521,7 +1533,7 @@ "resume": "Nadaljuj", "retry_upload": "Poskusite znova naloÅžiti", "review_duplicates": "Pregled dvojnikov", - "role": "Dovoljenje", + "role": "Vloga", "role_editor": "Urejevalec", "role_viewer": "Gledalec", "save": "Shrani", @@ -1596,6 +1608,7 @@ "select_album_cover": "Izberi naslovnico albuma", "select_all": "Izberi vse", "select_all_duplicates": "Izberi vse dvojnike", + "select_all_in": "Izberi vse v {group}", "select_avatar_color": "Izberi barvo avatarja", "select_face": "Izberi obraz", "select_featured_photo": "Izberi predstavljeno fotografijo", @@ -1616,6 +1629,7 @@ "server_info_box_server_url": "URL streÅžnika", "server_offline": "StreÅžnik nima povezave", "server_online": "StreÅžnik povezan", + "server_privacy": "Zasebnost streÅžnika", "server_stats": "Statistika streÅžnika", "server_version": "Različica streÅžnika", "set": "Nastavi", @@ -1625,6 +1639,7 @@ "set_date_of_birth": "Nastavi datum rojstva", "set_profile_picture": "Nastavi profilno sliko", "set_slideshow_to_fullscreen": "Nastavi diaprojekcijo na celozaslonski način", + "set_stack_primary_asset": "Nastavi kot glavno sredstvo", "setting_image_viewer_help": "Pregledovalnik podrobnosti najprej naloÅži majhno sličico, nato naloÅži predogled srednje velikosti (če je omogočen), na koncu naloÅži izvirnik (če je omogočen).", "setting_image_viewer_original_subtitle": "Omogoči nalaganje originalne slike polne ločljivosti (velike!). Onemogočite, da zmanjÅĄate porabo podatkov (tako v omreÅžju kot v predpomnilniku naprave).", "setting_image_viewer_original_title": "NaloÅži originalno sliko", @@ -1633,7 +1648,6 @@ "setting_image_viewer_title": "Slike", "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: {duration}", "setting_notifications_notify_hours": "{count} ur", "setting_notifications_notify_immediately": "takoj", @@ -1696,6 +1710,7 @@ "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", @@ -1761,6 +1776,8 @@ "start": "Začetek", "start_date": "Datum začetka", "state": "DeÅžela", + "status": "Status", + "stop_casting": "Ustavi predvajanje", "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.", @@ -1818,7 +1835,6 @@ "to_parent": "Pojdi na prvotno", "to_trash": "Smetnjak", "toggle_settings": "Preklopi na nastavitve", - "toggle_theme": "Preklopi na temno temo", "total": "Skupno", "total_usage": "Skupna poraba", "trash": "Smetnjak", @@ -1840,6 +1856,7 @@ "unable_to_setup_pin_code": "PIN kode ni mogoče nastaviti", "unarchive": "Odstrani iz arhiva", "unarchived_count": "{count, plural, other {nearhiviranih #}}", + "undo": "Razveljavi", "unfavorite": "Odznači priljubljeno", "unhide_person": "PrikaÅži osebo", "unknown": "Neznano", @@ -1856,10 +1873,9 @@ "unsaved_change": "Neshranjena sprememba", "unselect_all": "Odznači vse", "unselect_all_duplicates": "Odznači vse dvojnike", + "unselect_all_in": "Prekliči izbor vseh v {group}", "unstack": "Razklad", "unstacked_assets_count": "RazloÅži {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", - "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", @@ -1876,6 +1892,7 @@ "upload_success": "Nalaganje je uspelo, osveÅžite stran, da vidite nova sredstva za nalaganje.", "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", @@ -1886,6 +1903,7 @@ "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_privacy": "Zasebnost uporabnika", "user_purchase_settings": "Nakup", "user_purchase_settings_description": "Upravljajte svoj nakup", "user_role_set": "Nastavi {user} kot {role}", @@ -1901,13 +1919,9 @@ "version": "Različica", "version_announcement_closing": "Tvoj prijatelj, Alex", "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_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", @@ -1924,6 +1938,7 @@ "view_previous_asset": "Ogled prejÅĄnjega sredstva", "view_qr_code": "Oglej si kodo QR", "view_stack": "Ogled sklada", + "view_user": "Poglej uporabnika", "viewer_remove_from_stack": "Odstrani iz sklada", "viewer_stack_use_as_main_asset": "Uporabi kot glavno sredstvo", "viewer_unstack": "Razkladi", diff --git a/i18n/sr_Cyrl.json b/i18n/sr_Cyrl.json index 872418b548..b518bb39af 100644 --- a/i18n/sr_Cyrl.json +++ b/i18n/sr_Cyrl.json @@ -44,8 +44,6 @@ "backup_keep_last_amount": "КоĐģĐ¸Ņ‡Đ¸ĐŊа ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐ¸Ņ… даĐŧĐŋОва ĐēĐžŅ˜Đĩ ҂ҀĐĩйа ĐˇĐ°Đ´Ņ€ĐļĐ°Ņ‚Đ¸", "backup_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° даĐŧĐŋа йаСĐĩ ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", "backup_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа даĐŧĐŋа йаСĐĩ ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа. НаĐŋĐžĐŧĐĩĐŊа: Ови ĐŋĐžŅĐģОви ҁĐĩ ĐŊĐĩ ĐŋŅ€Đ°Ņ‚Đĩ и ĐŊĐĩ҆ˁĐĩŅ‚Đĩ ĐąĐ¸Ņ‚Đ¸ ОйавĐĩŅˆŅ‚ĐĩĐŊи Đž ĐŊĐĩ҃ҁĐŋĐĩŅ…Ņƒ.", - "check_all": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸ ŅĐ˛Đĩ", - "cleanup": "Đ§Đ¸ŅˆŅ†ĖĐĩҚĐĩ", "cleared_jobs": "ĐžŅ‡Đ¸ŅˆŅ†ĖĐĩĐŊи ĐŋĐžŅĐģОви Са: {job}", "config_set_by_file": "КоĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸Ņ˜Ņƒ ҂ҀĐĩĐŊŅƒŅ‚ĐŊĐž ĐŋĐžŅŅ‚Đ°Đ˛Ņ™Đ° ĐēĐžĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸ĐžĐŊи Ņ„Đ°Ņ˜Đģ", "confirm_delete_library": "Да Đģи ŅŅ‚Đ˛Đ°Ņ€ĐŊĐž ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃ {library} ?", @@ -61,14 +59,12 @@ "disable_login": "ОĐŊĐĩĐŧĐžĐŗŅƒŅ†ĖĐ¸ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ", "duplicate_detection_job_description": "ПоĐēŅ€ĐĩĐŊĐ¸Ņ‚Đĩ ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐž ŅƒŅ‡ĐĩҚĐĩ ĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ¸Đŧа да ĐąĐ¸ŅŅ‚Đĩ ĐžŅ‚ĐēŅ€Đ¸Đģи ҁĐģĐ¸Ņ‡ĐŊĐĩ ҁĐģиĐēĐĩ. ĐžŅĐģĐ°ŅšĐ° ҁĐĩ ĐŊа ĐŋаĐŧĐĩŅ‚ĐŊ҃ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ", "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": "ĐŖĐŸĐžĐ—ĐžĐ Đ•ĐĐˆĐ•: Ово ҆ˁĐĩ ОдĐŧĐ°Ņ… ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа и ŅĐ˛Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. Ово ҁĐĩ ĐŊĐĩ ĐŧĐžĐļĐĩ ĐžĐŋĐžĐˇĐ˛Đ°Ņ‚Đ¸ и Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҁĐĩ ĐŊĐĩ ĐŧĐžĐŗŅƒ ĐžĐŋĐžŅ€Đ°Đ˛Đ¸Ņ‚Đ¸.", - "forcing_refresh_library_files": "ĐŸŅ€Đ¸ĐŊŅƒĐ´ĐŊĐž ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ ŅĐ˛Đ¸Ņ… Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", "image_format": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚", "image_format_description": "WĐĩбП ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ ĐŧĐ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ Од ЈПЕГ, аĐģи ҁĐĩ ҁĐŋĐžŅ€Đ¸Ņ˜Đĩ ĐēĐžĐ´Đ¸Ņ€Đ°.", "image_fullsize_description": "ĐĄĐģиĐēа ҃ Đŋ҃ĐŊĐžŅ˜ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊи ŅĐ° ĐžĐŗĐžŅ™ĐĩĐŊиĐŧ ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ†Đ¸Đŧа, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ҁĐĩ Đēада ҘĐĩ ŅƒĐ˛ĐĩŅ†ĖĐ°ĐŊа", @@ -209,8 +205,6 @@ "oauth_storage_quota_default_description": "ĐšĐ˛ĐžŅ‚Đ° ҃ ГиБ ĐēĐžŅ˜Đ° ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ Đēада ĐŊĐĩĐŧа ĐŋĐžŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°ŅšĐ° (҃ĐŊĐĩŅĐ¸Ņ‚Đĩ 0 Са ĐŊĐĩĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊ҃ ĐēĐ˛ĐžŅ‚Ņƒ).", "oauth_timeout": "Đ’Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ ĐˇĐ°Ņ…Ņ‚Đĩва", "oauth_timeout_description": "Đ’Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ Са ĐˇĐ°Ņ…Ņ‚ĐĩвĐĩ ҃ ĐŧиĐģĐ¸ŅĐĩĐē҃ĐŊдаĐŧа", - "offline_paths": "ВаĐŊĐŧŅ€ĐĩĐļĐŊĐĩ ĐŋŅƒŅ‚Đ°ŅšĐĩ", - "offline_paths_description": "Ови Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸ ĐŧĐžĐŗŅƒ ĐąĐ¸Ņ‚Đ¸ ĐŋĐžŅĐģĐĩĐ´Đ¸Ņ†Đ° Ņ€ŅƒŅ‡ĐŊĐžĐŗ ĐąŅ€Đ¸ŅĐ°ŅšĐ° Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐēĐžŅ˜Đĩ ĐŊĐ¸ŅŅƒ Đ´ĐĩĐž ҁĐŋĐžŅ™ĐŊĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ.", "password_enable_description": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ҁĐĩ ĐŋĐžĐŧĐžŅ†ĖŅƒ Đĩ-ĐŋĐžŅˆŅ‚Đĩ и ĐģОСиĐŊĐēĐĩ", "password_settings": "ЛозиĐŊĐēа Са ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ", "password_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа Са ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ ĐģОСиĐŊĐēĐžĐŧ", @@ -220,9 +214,6 @@ "refreshing_all_libraries": "ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ ŅĐ˛Đ¸Ņ… йийĐģĐ¸ĐžŅ‚ĐĩĐēа", "registration": "Đ ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ˜Đ° адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", "registration_description": "ĐŸĐžŅˆŅ‚Đž ҁ҂Đĩ ĐŋŅ€Đ˛Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐē ĐŊа ŅĐ¸ŅŅ‚ĐĩĐŧ҃, ĐąĐ¸Ņ†ĖĐĩŅ‚Đĩ дОдĐĩŅ™ĐĩĐŊи ĐēаО АдĐŧиĐŊ и ĐžĐ´ĐŗĐžĐ˛ĐžŅ€ĐŊи ҁ҂Đĩ Са адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚Đ¸Đ˛ĐŊĐĩ ĐˇĐ°Đ´Đ°Ņ‚ĐēĐĩ, а Đ´ĐžĐ´Đ°Ņ‚ĐŊĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ ҆ˁĐĩŅ‚Đĩ ĐēŅ€ĐĩĐ¸Ņ€Đ°Ņ‚Đ¸ ви.", - "repair_all": "ПоĐŋŅ€Đ°Đ˛Đ¸ ŅĐ˛Đĩ", - "repair_matched_items": "ПоĐēĐģаĐŋа ҁĐĩ ŅĐ° {count, plural, one {1 ŅŅ‚Đ°Đ˛ĐēĐžĐŧ} few {# ŅŅ‚Đ°Đ˛ĐēĐĩ} other {# ŅŅ‚Đ°Đ˛Đēи}}", - "repaired_items": "{count, plural, one {ПоĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊа 1 ŅŅ‚Đ°Đ˛Đēа} few {ПоĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊĐĩ # ŅŅ‚Đ°Đ˛ĐēĐĩ} other {ПоĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊĐĩ # ŅŅ‚Đ°Đ˛Đēи}}", "require_password_change_on_login": "Đ—Đ°Ņ…Ņ‚ĐĩĐ˛Đ°Ņ‚Đ¸ Од ĐēĐžŅ€Đ¸ŅĐŊиĐēа да ĐŋŅ€ĐžĐŧĐĩĐŊи ĐģОСиĐŊĐē҃ ĐŋŅ€Đ¸ ĐŋŅ€Đ˛ĐžĐŧ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšŅƒ", "reset_settings_to_default": "Đ ĐĩҁĐĩŅ‚ŅƒŅ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ĐŊа ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸", "reset_settings_to_recent_saved": "Đ ĐĩҁĐĩŅ‚ŅƒŅ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ĐŊа ĐŊĐĩдавĐŊĐž ŅĐ°Ņ‡ŅƒĐ˛Đ°ĐŊа ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", @@ -251,7 +242,6 @@ "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": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€ĐžĐŧ Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐžŅ€Đ¸Ņ˜ŅƒĐŧа и иĐŧĐĩĐŊĐžĐŧ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° Са ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ", @@ -263,16 +253,14 @@ "template_email_invite_album": "ШайĐģĐžĐŊ Са ĐŋОСив ҃ аĐģĐąŅƒĐŧ", "template_email_preview": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´", "template_email_settings": "ШайĐģĐžĐŊи Đĩ-ĐŋĐžŅˆŅ‚Đĩ", - "template_email_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊиĐŧ ŅˆĐ°ĐąĐģĐžĐŊиĐŧа ОйавĐĩŅˆŅ‚ĐĩŅšĐ° ĐŋŅƒŅ‚ĐĩĐŧ Đĩ-ĐŋĐžŅˆŅ‚Đĩ", "template_email_update_album": "АĐļŅƒŅ€Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ŅˆĐ°ĐąĐģĐžĐŊ аĐģĐąŅƒĐŧа", "template_email_welcome": "ШайĐģĐžĐŊ Đĩ-ĐŋĐžŅˆŅ‚Đĩ Đ´ĐžĐąŅ€ĐžĐ´ĐžŅˆĐģĐ¸Ņ†Đĩ", "template_settings": "ШайĐģĐžĐŊи ОйавĐĩŅˆŅ‚ĐĩŅšĐ°", - "template_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊиĐŧ ŅˆĐ°ĐąĐģĐžĐŊиĐŧа Са ОйавĐĩŅˆŅ‚ĐĩŅšĐ°.", + "template_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊиĐŧ ŅˆĐ°ĐąĐģĐžĐŊиĐŧа Са ОйавĐĩŅˆŅ‚ĐĩŅšĐ°", "theme_custom_css_settings": "ĐŸŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊи ĐĻĐĄĐĄ", "theme_custom_css_settings_description": "ĐšĐ°ŅĐēадĐŊи ĐģĐ¸ŅŅ‚ĐžĐ˛Đ¸ ŅŅ‚Đ¸ĐģОва (ĐĻĐĄĐĄ) ĐžĐŧĐžĐŗŅƒŅ†ĖĐ°Đ˛Đ°Ņ˜Ņƒ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’Đ°Đ˛Đ°ŅšĐĩ Đ´Đ¸ĐˇĐ°Ņ˜ĐŊа Immich-a.", "theme_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ Ņ‚ĐĩĐŧа", "theme_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’Đ°Đ˛Đ°ŅšĐĩĐŧ Immich wĐĩĐą иĐŊŅ‚ĐĩҀ҄ĐĩŅ˜ŅĐ°", - "these_files_matched_by_checksum": "ОвиĐŧ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēаĐŧа ҁĐĩ ĐŋĐžĐ´ŅƒĐ´Đ°Ņ€Đ°Ņ˜Ņƒ ŅšĐ¸Ņ…ĐžĐ˛Đ¸ ĐēĐžĐŊŅ‚Ņ€ĐžĐģĐŊи-ĐˇĐąĐ¸Ņ€ĐžĐ˛Đ¸", "thumbnail_generation_job": "ГĐĩĐŊĐĩŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ", "thumbnail_generation_job_description": "ГĐĩĐŊĐĩŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ вĐĩĐģиĐēĐĩ, ĐŧаĐģĐĩ и СаĐŧŅƒŅ†ĖĐĩĐŊĐĩ ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ Са ŅĐ˛Đ°ĐēĐž ҁҀĐĩĐ´ŅŅ‚Đ˛Đž, ĐēаО и ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ Са ŅĐ˛Đ°Đē҃ ĐžŅĐžĐąŅƒ", "transcoding_acceleration_api": "АПИ Са ŅƒĐąŅ€ĐˇĐ°ŅšĐĩ", @@ -303,7 +291,6 @@ "transcoding_hardware_acceleration_description": "ЕĐēĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģĐŊĐž; ĐŧĐŊĐžĐŗĐž ĐąŅ€ĐļĐĩ, аĐģи ҆ˁĐĩ иĐŧĐ°Ņ‚Đ¸ ĐŊиĐļи ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ ĐŋŅ€Đ¸ Đ¸ŅŅ‚ĐžŅ˜ ĐąŅ€ĐˇĐ¸ĐŊи ĐŋŅ€ĐĩĐŊĐžŅĐ°", "transcoding_hardware_decoding": "ĐĨĐ°Ņ€Đ´Đ˛ĐĩҀҁĐēĐž Đ´ĐĩĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ", "transcoding_hardware_decoding_setting_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ°Đ˛Đ° ŅƒĐąŅ€ĐˇĐ°ŅšĐĩ Од ĐēŅ€Đ°Ņ˜Đ° Đ´Đž ĐēŅ€Đ°Ņ˜Đ° ҃ĐŧĐĩŅŅ‚Đž да ŅĐ°ĐŧĐž ŅƒĐąŅ€ĐˇĐ°Đ˛Đ° ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. МоĐļда ĐŊĐĩ҆ˁĐĩ Ņ€Đ°Đ´Đ¸Ņ‚Đ¸ ĐŊа ŅĐ˛Đ¸Đŧ видĐĩĐž ҁĐŊиĐŧŅ†Đ¸Đŧа.", - "transcoding_hevc_codec": "HEVC ĐēОдĐĩĐē", "transcoding_max_b_frames": "МаĐēŅĐ¸ĐŧаĐģĐŊи Б-ĐēĐ°Đ´Ņ€Đ¸", "transcoding_max_b_frames_description": "Đ’Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ĐŋĐžĐąĐžŅ™ŅˆĐ°Đ˛Đ°Ņ˜Ņƒ ĐĩŅ„Đ¸ĐēĐ°ŅĐŊĐžŅŅ‚ ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸Ņ˜Đĩ, аĐģи ҃ҁĐŋĐžŅ€Đ°Đ˛Đ°Ņ˜Ņƒ ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. МоĐļда ĐŊĐ¸Ņ˜Đĩ ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐž ŅĐ° Ņ…Đ°Ņ€Đ´Đ˛ĐĩҀҁĐēиĐŧ ŅƒĐąŅ€ĐˇĐ°ŅšĐĩĐŧ ĐŊа ŅŅ‚Đ°Ņ€Đ¸Ņ˜Đ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа. 0 oneĐŧĐžĐŗŅƒŅ†ĖĐ°Đ˛Đ° Б-ĐēĐ°Đ´Ņ€Đĩ, Đ´ĐžĐē -1 Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŋĐžŅŅ‚Đ°Đ˛Ņ™Đ° ĐžĐ˛Ņƒ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚.", "transcoding_max_bitrate": "МаĐēŅĐ¸ĐŧаĐģĐŊи ĐąĐ¸Ņ‚Ņ€Đ°Ņ‚Đĩ", @@ -341,8 +328,6 @@ "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": "Đ˜ĐˇĐąŅ€Đ¸ŅˆĐ¸ ŅƒĐˇ ĐēĐ°ŅˆŅšĐĩҚĐĩ", @@ -401,10 +386,6 @@ "album_remove_user": "ĐŖĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа?", "album_remove_user_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đĩ {user}?", "album_share_no_users": "Đ˜ĐˇĐŗĐģĐĩда да ҁ҂Đĩ ĐŋОдĐĩĐģиĐģи ĐžĐ˛Đ°Ņ˜ аĐģĐąŅƒĐŧ ŅĐ° ŅĐ˛Đ¸Đŧ ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸Đŧа иĐģи да ĐŊĐĩĐŧĐ°Ņ‚Đĩ ĐŊĐ¸Ņ˜ĐĩĐ´ĐŊĐžĐŗ ĐēĐžŅ€Đ¸ŅĐŊиĐēа ŅĐ° ĐēĐžŅ˜Đ¸Đŧ ĐąĐ¸ŅŅ‚Đĩ Đ´ĐĩĐģиĐģи.", - "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}", @@ -572,21 +553,17 @@ "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": "ОĐŋŅ†Đ¸Ņ˜Đĩ Са ĐēĐĩŅˆĐ¸Ņ€Đ°ŅšĐĩ", @@ -615,7 +592,6 @@ "change_pin_code": "ĐŸŅ€ĐžĐŧĐĩĐŊа ПИН ĐēОда", "change_your_password": "ĐŸŅ€ĐžĐŧĐĩĐŊи ŅĐ˛ĐžŅ˜Ņƒ ŅˆĐ¸Ņ„Ņ€Ņƒ", "changed_visibility_successfully": "Đ’Đ¸Đ´Ņ™Đ¸Đ˛ĐžŅŅ‚ ҘĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋŅ€ĐžĐŧĐĩҚĐĩĐŊа", - "check_all": "Đ¨Ņ‚Đ¸ĐēĐģĐ¸Ņ€Đ°Ņ‚Đ¸ ŅĐ˛Đĩ", "check_corrupt_asset_backup": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ да Đģи ĐŋĐžŅŅ‚ĐžŅ˜Đĩ ĐžŅˆŅ‚Đĩ҆ˁĐĩĐŊĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ иĐŧОвиĐŊĐĩ", "check_corrupt_asset_backup_button": "Đ˜ĐˇĐ˛Ņ€ŅˆĐ¸Ņ‚Đĩ ĐŋŅ€ĐžĐ˛ĐĩŅ€Ņƒ", "check_corrupt_asset_backup_description": "ПоĐēŅ€ĐĩĐŊĐ¸Ņ‚Đĩ ĐžĐ˛Ņƒ ĐŋŅ€ĐžĐ˛ĐĩŅ€Ņƒ ŅĐ°ĐŧĐž ĐŋŅ€ĐĩĐēĐž Wi-Fi ĐŧŅ€ĐĩĐļĐĩ и ĐŊаĐēĐžĐŊ ŅˆŅ‚Đž ҁĐĩ ĐŊаĐŋŅ€Đ°Đ˛Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ° ŅĐ˛Đ¸Ņ… ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа. ĐŸĐžŅŅ‚ŅƒĐŋаĐē ĐŧĐžĐļĐĩ ĐŋĐžŅ‚Ņ€Đ°Ņ˜Đ°Ņ‚Đ¸ ĐŊĐĩĐēĐžĐģиĐēĐž ĐŧиĐŊŅƒŅ‚Đ°.", @@ -658,7 +634,6 @@ "contain": "ĐžĐąŅƒŅ…Đ˛Đ°Ņ‚Đ¸", "context": "КоĐŊŅ‚ĐĩĐēҁ҂", "continue": "ĐĐ°ŅŅ‚Đ°Đ˛Đ¸", - "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": "ĐžĐąŅ€Đ¸ŅˆĐ¸ ŅĐ° ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°", @@ -769,7 +744,6 @@ "download_enqueue": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ҘĐĩ ŅŅ‚Đ°Đ˛Ņ™ĐĩĐŊĐž ҃ Ņ€ĐĩĐ´", "download_error": "ДоwĐŊĐģОад Đ•Ņ€Ņ€ĐžŅ€", "download_failed": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", - "download_filename": "Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа: {filename}", "download_finished": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ĐˇĐ°Đ˛Ņ€ŅˆĐĩĐŊĐž", "download_include_embedded_motion_videos": "ĐŖĐŗŅ€Đ°Ņ’ĐĩĐŊи видĐĩĐž ҁĐŊиĐŧŅ†Đ¸", "download_include_embedded_motion_videos_description": "ĐŖĐēŅ™ŅƒŅ‡Đ¸Ņ‚Đĩ видĐĩĐž СаĐŋĐ¸ŅĐĩ ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊĐĩ ҃ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ ҃ ĐŋĐžĐēŅ€ĐĩŅ‚Ņƒ ĐēаО ĐˇĐ°ŅĐĩĐąĐŊ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃", @@ -839,7 +813,6 @@ "cant_get_number_of_comments": "НĐĩ ĐŧĐžĐŗŅƒ Đ´ĐžĐąĐ¸Ņ‚Đ¸ ĐąŅ€ĐžŅ˜ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ°", "cant_search_people": "НĐĩ ĐŧĐžĐŗŅƒ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°Ņ‚Đ¸ ĐžŅĐžĐąĐĩ", "cant_search_places": "НĐĩ ĐŧĐžĐŗŅƒ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°Ņ‚Đ¸ ĐŧĐĩŅŅ‚Đ°", - "cleared_jobs": "ĐžŅ‡Đ¸ŅˆŅ†ĖĐĩĐŊи ĐŋĐžŅĐģОви Са: {job}", "error_adding_assets_to_album": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ´ĐžĐ´Đ°Đ˛Đ°ŅšŅƒ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ҃ аĐģĐąŅƒĐŧ", "error_adding_users_to_album": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ´ĐžĐ´Đ°Đ˛Đ°ŅšŅƒ ĐēĐžŅ€Đ¸ŅĐŊиĐēа ҃ аĐģĐąŅƒĐŧ", "error_deleting_shared_user": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐąŅ€Đ¸ŅĐ°ŅšŅƒ Đ´ĐĩŅ™ĐĩĐŊĐžĐŗ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", @@ -848,7 +821,6 @@ "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": "ĐŸŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Đ´ĐĩŅ™ĐĩĐŊĐžĐŗ linkа ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", "failed_to_edit_shared_link": "ĐŖŅ€ĐĩŅ’Đ¸Đ˛Đ°ŅšĐĩ Đ´ĐĩŅ™ĐĩĐŊĐžĐŗ linkа ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", @@ -867,7 +839,6 @@ "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": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€", @@ -885,7 +856,6 @@ "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)", @@ -909,10 +879,6 @@ "unable_to_hide_person": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅĐ°ĐēŅ€Đ¸Ņ‚Đ¸ ĐžŅĐžĐąŅƒ", "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-а", @@ -923,11 +889,9 @@ "unable_to_remove_album_users": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ иС аĐģĐąŅƒĐŧа", "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": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Ņ€Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚Đ¸ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚", @@ -957,12 +921,12 @@ "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": "Адд 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}", @@ -1286,15 +1250,12 @@ "notifications_setting_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ОйавĐĩŅˆŅ‚ĐĩŅšĐ¸Đŧа", "official_immich_resources": "ЗваĐŊĐ¸Ņ‡ĐŊи Immich Ņ€ĐĩŅŅƒŅ€ŅĐ¸", "offline": "ĐžĐ´ŅŅƒŅ‚Đ°ĐŊ (ĐžŅ„Ņ„ĐģиĐŊĐĩ)", - "offline_paths": "НĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊĐĩ (ĐžŅ„Ņ„ĐģиĐŊĐĩ) ĐŋŅƒŅ‚Đ°ŅšĐĩ", - "offline_paths_description": "Ови Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸ ĐŧĐžĐŗŅƒ ĐąĐ¸Ņ‚Đ¸ ĐŋĐžŅĐģĐĩĐ´Đ¸Ņ†Đ° Ņ€ŅƒŅ‡ĐŊĐžĐŗ ĐąŅ€Đ¸ŅĐ°ŅšĐ° Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐēĐžŅ˜Đĩ ĐŊĐ¸ŅŅƒ Đ´ĐĩĐž ҁĐŋĐžŅ™ĐŊĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ.", "ok": "ОĐē", "oldest_first": "ĐĐ°Ņ˜ŅŅ‚Đ°Ņ€Đ¸Ņ˜Đĩ ĐŋŅ€Đ˛Đž", "on_this_device": "На ОвОĐŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Ņƒ", "onboarding": "ĐŸŅ€Đ¸ŅŅ‚ŅƒĐŋĐ°ŅšĐĩ (ОĐŊĐąĐžĐ°Ņ€Đ´Đ¸ĐŊĐŗ)", "onboarding_privacy_description": "ĐĄĐģĐĩĐ´Đĩ҆ˁĐĩ (ĐžĐŋŅ†Đ¸one) Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ ҁĐĩ ĐžŅĐģĐ°ŅšĐ°Ņ˜Ņƒ ĐŊа ҁĐŋĐžŅ™ĐŊĐĩ ҃ҁĐģŅƒĐŗĐĩ и ĐŧĐžĐŗŅƒ ҁĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đ¸ ҃ йиĐģĐž ĐēĐžĐŧ ҂ҀĐĩĐŊŅƒŅ‚Đē҃ ҃ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ˜Đĩ.", "onboarding_theme_description": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ Ņ‚ĐĩĐŧ҃ ĐąĐžŅ˜Đ° Са ŅĐ˛ĐžŅ˜ ĐŊаĐģĐžĐŗ. Ово ĐŧĐžĐļĐĩŅ‚Đĩ ĐēĐ°ŅĐŊĐ¸Ņ˜Đĩ да ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ ҃ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа.", - "onboarding_welcome_description": "ĐĨĐ°Ņ˜Đ´Đĩ да ĐŋОдĐĩŅĐ¸ĐŧĐž Đ˛Đ°ŅˆŅƒ иĐŊŅŅ‚Đ°ĐŊŅ†Ņƒ ŅĐ° ĐŊĐĩĐēиĐŧ ŅƒĐžĐąĐ¸Ņ‡Đ°Ņ˜ĐĩĐŊиĐŧ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа.", "onboarding_welcome_user": "Đ”ĐžĐąŅ€ĐžĐ´ĐžŅˆĐģи, {user}", "online": "Đ”ĐžŅŅ‚ŅƒĐŋаĐŊ (ОĐŊĐģиĐŊĐĩ)", "only_favorites": "ХаĐŧĐž Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚Đ¸", @@ -1614,7 +1575,6 @@ "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": "ОдĐŧĐ°Ņ…", @@ -1675,6 +1635,7 @@ "shared_link_expires_second": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ Са {count} ҁĐĩĐē҃ĐŊĐ´Ņƒ", "shared_link_expires_seconds": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ Са {count} ҁĐĩĐē҃ĐŊди", "shared_link_individual_shared": "ĐŸĐžŅ˜ĐĩдиĐŊĐ°Ņ‡ĐŊĐž Đ´ĐĩŅ™ĐĩĐŊĐž", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ Đ´ĐĩŅ™ĐĩĐŊиĐŧ linkОвиĐŧа", "shared_link_options": "ОĐŋŅ†Đ¸Ņ˜Đĩ Đ´ĐĩŅ™ĐĩĐŊĐĩ вĐĩСĐĩ", "shared_links": "ДĐĩŅ™ĐĩĐŊĐĩ вĐĩСĐĩ", @@ -1798,7 +1759,6 @@ "to_parent": "Đ’Ņ€Đ°Ņ‚Đ¸ ҁĐĩ ĐŊаСад", "to_trash": "ĐĄĐŧĐĩ҆ˁĐĩ", "toggle_settings": "NameŅŅ‚Đ¸ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", - "toggle_theme": "NameŅŅ‚Đ¸ Ņ‚Đ°ĐŧĐŊ҃ Ņ‚ĐĩĐŧ҃", "total": "ĐŖĐē҃ĐŋĐŊĐž", "total_usage": "ĐŖĐē҃ĐŋĐŊа ҃ĐŋĐžŅ‚Ņ€Đĩйа", "trash": "ĐžŅ‚Đŋад", @@ -1838,8 +1798,6 @@ "unselect_all_duplicates": "ПоĐŊĐ¸ŅˆŅ‚Đ¸ Đ¸ĐˇĐąĐžŅ€ ŅĐ˛Đ¸Ņ… Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đ°", "unstack": "Đ Đ°ĐˇĐŗĐžĐŧиĐģĐ°Ņ˜ (ĐŖĐŊ-ŅŅ‚Đ°Ņ†Đē)", "unstacked_assets_count": "НĐĩҁĐģĐžĐļĐĩĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}}", - "untracked_files": "НĐĩĐŋŅ€Đ°Ņ†ĖĐĩĐŊĐĩ Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", - "untracked_files_decription": "АĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ĐŊĐĩ ĐŋŅ€Đ°Ņ‚Đ¸ ОвĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. ОĐŊĐĩ ĐŧĐžĐŗŅƒ ĐŊĐ°ŅŅ‚Đ°Ņ‚Đ¸ ĐˇĐąĐžĐŗ ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐ¸Ņ… ĐŋŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩŅšĐ°, ĐˇĐąĐžĐŗ ĐŋŅ€ĐĩĐēиĐŊŅƒŅ‚Đ¸Ņ… ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐ° иĐģи ĐēаО ĐŋŅ€ĐĩĐžŅŅ‚Đ°Ņ‚Đ°Đē ĐˇĐąĐžĐŗ ĐŗŅ€Đĩ҈ĐēĐĩ", "up_next": "ĐĄĐģĐĩĐ´Đĩ҆ˁĐĩ", "updated_at": "АĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊĐž", "updated_password": "АĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊа ĐģОСиĐŊĐēа", @@ -1856,6 +1814,7 @@ "upload_success": "ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ ҘĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž, ĐžŅĐ˛ĐĩĐļĐ¸Ņ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ да ĐąĐ¸ŅŅ‚Đĩ видĐĩĐģи ĐŊОва ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° Са ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ (҃ĐŋĐģОад).", "upload_to_immich": "ĐžŅ‚ĐŋŅ€ĐĩĐŧи ҃ Immich ({count})", "uploading": "ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ", + "url": "URL", "usage": "ĐŖĐŋĐžŅ‚Ņ€Đĩйа", "use_current_connection": "ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ҂ҀĐĩĐŊŅƒŅ‚ĐŊ҃ вĐĩĐˇŅƒ", "use_custom_date_range": "ĐŖĐŧĐĩŅŅ‚Đž Ņ‚ĐžĐŗĐ° ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊи ĐŋĐĩŅ€Đ¸ĐžĐ´", @@ -1879,11 +1838,6 @@ "version": "ВĐĩŅ€ĐˇĐ¸Ņ˜Đ°", "version_announcement_closing": "ĐĸĐ˛ĐžŅ˜ ĐŋŅ€Đ¸Ņ˜Đ°Ņ‚ĐĩŅ™, АĐģĐĩĐēҁ", "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} {date}", "video": "ВидĐĩĐž СаĐŋĐ¸Ņ", diff --git a/i18n/sr_Latn.json b/i18n/sr_Latn.json index b63002ef79..aa92e6da49 100644 --- a/i18n/sr_Latn.json +++ b/i18n/sr_Latn.json @@ -43,8 +43,6 @@ "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}", "config_set_by_file": "Konfiguraciju trenutno postavlja konfiguracioni fajl", "confirm_delete_library": "Da li stvarno Åželite da izbriÅĄete biblioteku {library} ?", @@ -60,14 +58,12 @@ "disable_login": "Onemogucˁi prijavu", "duplicate_detection_job_description": "Pokrenite maÅĄinsko učenje na sredstvima da biste otkrili slične slike. Oslanja se na pametnu pretragu", "exclusion_pattern_description": "Obrasci izuzimanja vam omogucˁavaju da ignoriÅĄete datoteke i fascikle kada skenirate biblioteku. Ovo je korisno ako imate fascikle koje sadrÅže datoteke koje ne Åželite da uvezete, kao ÅĄto su RAW datoteke.", - "external_library_created_at": "Eksterna biblioteka (napravljena {date})", "external_library_management": "Upravljanje eksternim bibliotekama", "face_detection": "Detekcija lica", "face_detection_description": "Otkrijte lica u datotekama pomocˁu maÅĄinskog učenja. Za video snimke se uzima u obzir samo sličica. „OsveÅži“ (ponovno) obrađuje sve datoteke. „Resetovanje“ dodatno briÅĄe sve trenutne podatke o licu. „Nedostaju“ datoteke u redu koje joÅĄ nisu obrađene. Otkrivena lica cˁe biti stavljena u red za prepoznavanje lica nakon ÅĄto se prepoznavanje lica zavrÅĄi, grupiÅĄucˁi ih u postojecˁe ili nove osobe.", "facial_recognition_job_description": "Grupa je detektovala lica i dodala ih postojecˁim osobama. Ovaj korak se pokrecˁe nakon ÅĄto je prepoznavanje lica zavrÅĄeno. „Resetuj“ (ponovno) grupiÅĄe sva lica. „Nedostaju“ lica u redovima kojima nije dodeljena osoba.", "failed_job_command": "Komanda {command} nije uspela za posao: {job}", "force_delete_user_warning": "UPOZORENJE: Ovo cˁe odmah ukloniti korisnika i sve datoteke. Ovo se ne moÅže opozvati i datoteke se ne mogu oporaviti.", - "forcing_refresh_library_files": "Prinudno osveÅžavanje svih datoteka biblioteke", "image_format": "Format", "image_format_description": "WebP proizvodi manje datoteke od JPEG, ali se sporije kodira.", "image_fullsize_description": "Slika u punoj veličini sa ogoljenim metapodacima, koristi se kada je uvecˁana", @@ -196,7 +192,7 @@ "oauth_enable_description": "Prijavite se pomocˁu OAuth-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_mobile_redirect_uri_override_description": "Omogucˁi kada OAuth dobavljač (provider) ne dozvoljava mobilni URI, kao ÅĄto je ''{callback}''", "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.", @@ -208,8 +204,6 @@ "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", "password_settings": "Lozinka za prijavu", "password_settings_description": "Upravljajte podeÅĄavanjima za prijavu lozinkom", @@ -219,9 +213,6 @@ "refreshing_all_libraries": "OsveÅžavanje svih biblioteka", "registration": "Registracija administratora", "registration_description": "PoÅĄto ste prvi korisnik na sistemu, bicˁete dodeljeni kao Admin i odgovorni ste za administrativne zadatke, a dodatne korisnike cˁete kreirati vi.", - "repair_all": "Popravi sve", - "repair_matched_items": "Poklapa se sa {count, plural, one {1 stavkom} few {# stavke} other {# stavki}}", - "repaired_items": "{count, plural, one {Popravljena 1 stavka} few {Popravljene # stavke} other {Popravljene # stavki}}", "require_password_change_on_login": "Zahtevati od korisnika da promeni lozinku pri prvom prijavljivanju", "reset_settings_to_default": "Resetujte podeÅĄavanja na podrazumevane vrednosti", "reset_settings_to_recent_saved": "Resetujte podeÅĄavanja na nedavno sačuvana podeÅĄavanja", @@ -250,7 +241,6 @@ "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.", "storage_template_path_length": "PribliÅžno ograničenje duÅžine putanje: {length, number}/{limit, number}", "storage_template_settings": "Å ablon za skladiÅĄtenje", "storage_template_settings_description": "Upravljajte strukturom direktorijuma i imenom datoteke sredstva za otpremanje", @@ -262,16 +252,14 @@ "template_email_invite_album": "Å ablon za poziv u album", "template_email_preview": "Pregled", "template_email_settings": "Å abloni e-poÅĄte", - "template_email_settings_description": "Upravljajte prilagođenim ÅĄablonima obaveÅĄtenja putem e-poÅĄte", "template_email_update_album": "AÅžurirajte ÅĄablon albuma", "template_email_welcome": "Å ablon e-poÅĄte dobrodoÅĄlice", "template_settings": "Å abloni obaveÅĄtenja", - "template_settings_description": "Upravljajte prilagođenim ÅĄablonima za obaveÅĄtenja.", + "template_settings_description": "Upravljajte prilagođenim ÅĄablonima za obaveÅĄtenja", "theme_custom_css_settings": "Prilagođeni CSS", "theme_custom_css_settings_description": "Kaskadni listovi stilova (CSS) omogucˁavaju prilagođavanje dizajna Immich-a.", "theme_settings": "PodeÅĄavanje tema", "theme_settings_description": "Upravljajte prilagođavanjem Immich web interfejsa", - "these_files_matched_by_checksum": "Ovim datotekama se podudaraju njihovi kontrolni-zbirovi", "thumbnail_generation_job": "GeneriÅĄite sličice", "thumbnail_generation_job_description": "GeneriÅĄite velike, male i zamucˁene sličice za svako sredstvo, kao i sličice za svaku osobu", "transcoding_acceleration_api": "API za ubrzanje", @@ -302,7 +290,6 @@ "transcoding_hardware_acceleration_description": "Ekperimentalno; mnogo brÅže, ali cˁe imati niÅži kvalitet pri istoj brzini prenosa", "transcoding_hardware_decoding": "Hardversko dekodiranje", "transcoding_hardware_decoding_setting_description": "Omogucˁava ubrzanje od kraja do kraja umesto da samo ubrzava kodiranje. MoÅžda necˁe raditi na svim video snimcima.", - "transcoding_hevc_codec": "HEVC kodek", "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", @@ -340,8 +327,6 @@ "trash_number_of_days_description": "Broj dana za drÅžanje datoteka u otpadu pre nego ÅĄto ih trajno uklonite", "trash_settings": "PodeÅĄavanja smecˁa", "trash_settings_description": "Upravljajte podeÅĄavanjima smecˁa", - "untracked_files": "Nepracˁene datoteke", - "untracked_files_description": "Aplikacija ne prati ove datoteke. One mogu nastati zbog neuspeÅĄnih premeÅĄtenja, zbog prekinutih otpremanja ili kao preostatak zbog greÅĄke", "user_cleanup_job": "ČiÅĄcˁenje korisnika", "user_delete_delay": "Nalog i datoteke {user} bicˁe zakazani za trajno brisanje za {delay, plural, one {# dan} other {# dana}}.", "user_delete_delay_settings": "IzbriÅĄi uz kaÅĄnjenje", @@ -400,10 +385,6 @@ "album_remove_user": "Ukloniti korisnika?", "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": "{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}", @@ -567,20 +548,16 @@ "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 ({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_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": "{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 ({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", @@ -609,7 +586,6 @@ "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": "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.", @@ -649,7 +625,6 @@ "contain": "Obuhvati", "context": "Kontekst", "continue": "Nastavi", - "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": "ObriÅĄi iz Immich-a", "control_bottom_app_bar_delete_from_local": "ObriÅĄi sa uređaja", @@ -757,7 +732,6 @@ "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", @@ -827,7 +801,6 @@ "cant_get_number_of_comments": "Ne mogu dobiti broj komentara", "cant_search_people": "Ne mogu pretraÅživati osobe", "cant_search_places": "Ne mogu pretraÅživati mesta", - "cleared_jobs": "OčiÅĄcˁeni poslovi za: {job}", "error_adding_assets_to_album": "GreÅĄka pri dodavanju datoteka u album", "error_adding_users_to_album": "GreÅĄka pri dodavanju korisnika u album", "error_deleting_shared_user": "GreÅĄka pri brisanju deljenog korisnika", @@ -836,7 +809,6 @@ "error_removing_assets_from_album": "GreÅĄka pri uklanjanju datoteke iz albuma, proverite konzolu za viÅĄe detalja", "error_selecting_all_assets": "GreÅĄka pri izboru svih datoteka", "exclusion_pattern_already_exists": "Ovaj obrazac isključenja vecˁ postoji.", - "failed_job_command": "Komanda {command} nije uspela za zadatak: {job}", "failed_to_create_album": "Nije moguće kreirati album", "failed_to_create_shared_link": "Pravljenje deljenog linka nije uspelo", "failed_to_edit_shared_link": "Uređivanje deljenog linka nije uspelo", @@ -855,7 +827,6 @@ "paths_validation_failed": "{paths, plural, one {# putanja nije proÅĄla} few {# putanje nisu proÅĄle} other {# putanja nisu proÅĄle}} proveru valjanosti", "profile_picture_transparent_pixels": "Slike profila ne mogu imati prozirne piksele. Molimo uvecˁajte i/ili pomerite sliku.", "quota_higher_than_disk_size": "Postavili ste kvotu vecˁu od veličine diska", - "repair_unable_to_check_items": "Nije mogucˁe proveriti {count, select, one {stavku} other {stavke}}", "unable_to_add_album_users": "Nije mogucˁe dodati korisnike u album", "unable_to_add_assets_to_shared_link": "Nije mogucˁe dodati elemente deljenoj vezi", "unable_to_add_comment": "Nije mogucˁe dodati komentar", @@ -873,7 +844,6 @@ "unable_to_change_visibility": "Nije mogucˁe promeniti vidljivost za {count, plural, one {# osobu} other {# osobe}}", "unable_to_complete_oauth_login": "Nije mogucˁe dovrÅĄiti OAuth prijavu", "unable_to_connect": "Nije mogucˁe povezati se", - "unable_to_connect_to_server": "Nemogucˁe je povezati se sa serverom", "unable_to_copy_to_clipboard": "Nije mogucˁe kopirati u međuspremnik (clipboard), proverite da li pristupate stranici preko https-a", "unable_to_create_admin_account": "Nije mogucˁe napraviti administratorski nalog", "unable_to_create_api_key": "Nije mogucˁe napraviti novi API ključ (key)", @@ -897,10 +867,6 @@ "unable_to_hide_person": "Nije moguće sakriti osobu", "unable_to_link_motion_video": "Nije moguće povezati video sa slikom", "unable_to_link_oauth_account": "Nije mogucˁe povezati OAuth nalog", - "unable_to_load_album": "Nije mogucˁe učitati album", - "unable_to_load_asset_activity": "Nije mogucˁe učitati aktivnost sredstava", - "unable_to_load_items": "Nije mogucˁe učitati stavke", - "unable_to_load_liked_status": "Nije mogucˁe učitati status sviđanja", "unable_to_log_out_all_devices": "Nije mogucˁe odjaviti sve uređaje", "unable_to_log_out_device": "Nije mogucˁe odjaviti uređaj", "unable_to_login_with_oauth": "Nije mogucˁe prijaviti se pomocˁu OAuth-a", @@ -911,11 +877,9 @@ "unable_to_remove_album_users": "Nije moguće ukloniti korisnike iz albuma", "unable_to_remove_api_key": "Nije moguće ukloniti API ključ (key)", "unable_to_remove_assets_from_shared_link": "Nije mogucˁe ukloniti elemente sa deljenog linka", - "unable_to_remove_deleted_assets": "Nije moguće ukloniti vanmreÅžne datoteke", "unable_to_remove_library": "Nije moguće ukloniti biblioteku", "unable_to_remove_partner": "Nije moguće ukloniti partnera", "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", @@ -949,7 +913,6 @@ "exif_bottom_sheet_description": "Dodaj opis...", "exif_bottom_sheet_details": "DETALJI", "exif_bottom_sheet_location": "LOKACIJA", - "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}", @@ -1041,6 +1004,7 @@ "home_page_upload_err_limit": "MoÅžete otpremiti najviÅĄe 30 elemenata istovremeno, preskačucˁi", "host": "Domacˁin (Host)", "hour": "Sat", + "id": "ID", "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", @@ -1267,14 +1231,11 @@ "notifications_setting_description": "Upravljajte obaveÅĄtenjima", "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.", "oldest_first": "Najstarije prvo", "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.", - "onboarding_welcome_description": "Hajde da podesimo vaÅĄu instancu sa nekim uobičajenim podeÅĄavanjima.", "onboarding_welcome_user": "DobrodoÅĄli, {user}", "online": "Dostupan (Online)", "only_favorites": "Samo favoriti", @@ -1587,7 +1548,6 @@ "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", @@ -1767,7 +1727,6 @@ "to_parent": "Vrati se nazad", "to_trash": "Smecˁe", "toggle_settings": "Namesti podeÅĄavanja", - "toggle_theme": "Namesti tamnu temu", "total": "Ukupno", "total_usage": "Ukupna upotreba", "trash": "Otpad", @@ -1807,8 +1766,6 @@ "unselect_all_duplicates": "PoniÅĄti izbor svih duplikata", "unstack": "Razgomilaj (Un-stack)", "unstacked_assets_count": "NesloÅženo {count, plural, one {# datoteka} other {# datoteke}}", - "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", @@ -1848,11 +1805,6 @@ "version": "Verzija", "version_announcement_closing": "Tvoj prijatelj, Aleks", "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_title": "Nova verzija servera je dostupna 🎉", "version_history": "Istorija verzija", "version_history_item": "Instalirano {version} {date}", "video": "Video zapis", diff --git a/i18n/sv.json b/i18n/sv.json index 8aaa76a5ec..a4c08f8c22 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -14,7 +14,7 @@ "add_a_location": "Lägg till en plats", "add_a_name": "Lägg till ett namn", "add_a_title": "Lägg till en titel", - "add_endpoint": "Lägg till endpoint", + "add_endpoint": "Lägg till ändpunkt", "add_exclusion_pattern": "Lägg till uteslutningsmÃļnster", "add_import_path": "Lägg till importsÃļkväg", "add_location": "Lägg till plats", @@ -26,7 +26,6 @@ "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", @@ -44,9 +43,7 @@ "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", + "backup_settings_description": "Hantera inställningar fÃļr databasdumpning.", "cleared_jobs": "Rensade jobben fÃļr:{job}", "config_set_by_file": "Konfigurationen är satt av en konfigurationsfil", "confirm_delete_library": "Är du säker pÃĨ att du vill radera {library} album?", @@ -62,14 +59,12 @@ "disable_login": "Inaktivera inloggning", "duplicate_detection_job_description": "KÃļr maskininlärning pÃĨ objekt fÃļr att upptäcka liknande bilder. Bygger pÃĨ Smart Search", "exclusion_pattern_description": "ExkluderingsmÃļnster tillÃĨter dig att ignorera filer och mappar när skanning gÃļrs av ditt album. Detta är användbart om du har mappar som innehÃĨller filer som du inte vill importera, t.ex. RAW-filer.", - "external_library_created_at": "Externt bibliotek (skapat {date})", "external_library_management": "Hantera externa bibliotek", "face_detection": "Ansiktsdetektering", "face_detection_description": "Identifiera ansikten i foton med hjälp av maskininlärning. FÃļr videor används endast miniatyrbilden. \"Ladda om\" gÃļr om sÃļkningen fÃļr alla objekt. \"Återställ\" rensar all gällande ansikts-data. \"Saknade\" letar i de objekt som ännu inte sÃļkts igenom. Alla identifierade ansikten läggs sedan i jobbkÃļn fÃļr ansiktsigenkänning där de mappas till nya eller befintliga personer.", "facial_recognition_job_description": "Gruppera upptäckta ansikten till personer. Det här steget kÃļrs efter att ansiktsdetektering är klar. \"Återställ\" (ÃĨter-)grupperar alla ansikten. \"Saknade\" kÃļar ansikten som inte har en person tilldelad.", "failed_job_command": "Kommando {command} misslyckades fÃļr jobb: {job}", "force_delete_user_warning": "VARNING: Detta tar omedelbart bort användaren och alla mediafiler. Detta kan inte ÃĨngras och filerna kan inte ÃĨterställas.", - "forcing_refresh_library_files": "Tvingar uppdatering av alla biblioteksfiler", "image_format": "Format", "image_format_description": "WebP producerar mindre filer än JPEG, men kodas lÃĨngsammare.", "image_fullsize_description": "Fullstor bild med borttagen metadata, används vid inzoomning", @@ -194,10 +189,11 @@ "oauth_auto_register": "Autoregistrera", "oauth_auto_register_description": "Registrera nya användare automatiskt efter inloggning med OAuth", "oauth_button_text": "Knapptext", + "oauth_client_secret_description": "Krävs om PKCE (Proof Key for Code Exchange) inte stÃļds av OAuth-leverantÃļren", "oauth_enable_description": "Logga in med OAuth", "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_mobile_redirect_uri_override_description": "Aktivera om OAuth-leverantÃļren inte tillÃĨter mobila URI:er, sÃĨ som ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "Hantera OAuth-logininställningar", "oauth_settings_more_details": "FÃļr ytterligare detaljer om denna funktion, se dokumentationen.", @@ -207,9 +203,8 @@ "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": "Begäran tog fÃļr lÃĨng tid", "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", "password_settings": "LÃļsenordsinloggning", "password_settings_description": "Hantera inställningar fÃļr lÃļsenords-inloggning", @@ -219,9 +214,6 @@ "refreshing_all_libraries": "Samtliga bibliotek uppdateras", "registration": "AdministratÃļrsregistrering", "registration_description": "Du utses till administratÃļr eftersom du är systemets fÃļrsta användare. Du ansvarar fÃļr administration och kan skapa ytterligare användare.", - "repair_all": "Reparera alla", - "repair_matched_items": "Matchade {count, plural, one {# fÃļremÃĨl} other {# fÃļremÃĨl}}", - "repaired_items": "Reparerade {count, plural, one {# item} other {# items}}", "require_password_change_on_login": "Kräv av användaren att byta lÃļsenord vid fÃļrsta inloggning", "reset_settings_to_default": "Återställ inställningar till standard", "reset_settings_to_recent_saved": "Återställ inställningar till de senaste sparade", @@ -250,7 +242,6 @@ "storage_template_migration_info": "Lagringsmallen kommer konvertera alla filändelser till gemena bokstäver. Ändringar gäller endast fÃļr nya resurser, fÃļr att retoaktivt tillämpa mallen pÃĨ befintliga resurser kÃļr {job}.", "storage_template_migration_job": "Lagringsmall migreringsjobb", "storage_template_more_details": "FÃļr mer information om den här funktionen se Lagringsmall och dess konsekvenser", - "storage_template_onboarding_description": "Vid aktivering organiserar denna funktion automatiskt filer baserat pÃĨ en användardefinierad mall. PÃĨ grund av stabilitetsproblem är denna funktion avstängd som standard, fÃļr mer information se dokumentation.", "storage_template_path_length": "Uppskattad längdbegränsning pÃĨ sÃļkväg: {length, number}/{limit, number}", "storage_template_settings": "Lagringsmall", "storage_template_settings_description": "Hantera mappstruktur och filnamn fÃļr uppladdade resurser", @@ -262,16 +253,14 @@ "template_email_invite_album": "Inbjudan Album Mall", "template_email_preview": "FÃļrhandsgranskning", "template_email_settings": "E-post mall", - "template_email_settings_description": "Hantera skräddarsydda e-postaviseringsmallar", "template_email_update_album": "Uppdatera Album Mall", "template_email_welcome": "Välkommen e-post mall", "template_settings": "Notifikations Mall", - "template_settings_description": "Hantera anpassade mallar fÃļr notifikationer.", + "template_settings_description": "Hantera anpassade mallar fÃļr notifikationer", "theme_custom_css_settings": "Anpassad CSS", "theme_custom_css_settings_description": "Cascading Style Sheets mÃļjliggÃļr designanpassningar av Immich.", "theme_settings": "Temainställningar", "theme_settings_description": "Hantera anpassningar av webbgränssnittet fÃļr Immich", - "these_files_matched_by_checksum": "Dessa filer matchas av deras kontrollsummor", "thumbnail_generation_job": "Generera Miniatyrer", "thumbnail_generation_job_description": "Generera stora, smÃĨ och suddiga miniatyrer fÃļr varje objekt, samt fÃļr varje person", "transcoding_acceleration_api": "Accelerations-API", @@ -299,10 +288,9 @@ "transcoding_encoding_options": "Kodningsval", "transcoding_encoding_options_description": "Välj codec, upplÃļsning, kvalitet och andra val fÃļr kodade videor", "transcoding_hardware_acceleration": "HÃĨrdvaruacceleration", - "transcoding_hardware_acceleration_description": "Experimentell; betydligt snabbare men med lägre kvalitet vid samma bittakt", + "transcoding_hardware_acceleration_description": "Experimentell: snabbare transkodning men kan minska kvaliteten vid samma bithastighet", "transcoding_hardware_decoding": "HÃĨrdvaruavkodning", "transcoding_hardware_decoding_setting_description": "Tillämpas enbart pÃĨ NVENC, QSV och RKMPP. Aktiverar end-to-end accelerering i stället fÃļr endast kodningsacceleration. Fungerar inte med alla videor.", - "transcoding_hevc_codec": "HEVC-codec", "transcoding_max_b_frames": "Max B-ramar", "transcoding_max_b_frames_description": "HÃļgre värden fÃļrbättrar kompressionseffektiviteten, men saktar ner kodningen. Kan vara inkompatibel med hÃĨrdvaruacceleration pÃĨ äldre enheter. 0 avaktiverar B-frames, medan -1 anger detta värde automatiskt.", "transcoding_max_bitrate": "Max bithastighet", @@ -340,8 +328,6 @@ "trash_number_of_days_description": "Antal dagar fÃļr att fÃļrvara tillgÃĨngarna i papperskorgen innan de permanent tas bort", "trash_settings": "Papperskorginställningar", "trash_settings_description": "Hantera papperskorginställningar", - "untracked_files": "OspÃĨrade filer", - "untracked_files_description": "Dessa filer spÃĨras inte av applikationen. De kan vara resultatet av misslyckade rÃļrelser, avbrutna uppladdningar eller kvarlämnade pÃĨ grund av en bugg", "user_cleanup_job": "Användarrensning", "user_delete_delay": "{user}s konto och tillgÃĨngar kommer att schemaläggas fÃļr permanent radering om {delay, plural, one {# day} other {# days}}.", "user_delete_delay_settings": "BorttagningsfÃļrdrÃļjning", @@ -364,8 +350,12 @@ "video_conversion_job": "Omkoda videor", "video_conversion_job_description": "Koda om videor fÃļr bredare kompatibilitet med webbläsare och enheter" }, + "admin_email": "E-postadress fÃļr administratÃļr", "admin_password": "Admin LÃļsenord", + "administration": "Administration", "advanced": "Avancerat", + "advanced_settings_enable_alternate_media_filter_subtitle": "Använd det här alternativet fÃļr att filtrera media under synkronisering baserat pÃĨ alternativa kriterier. Prova detta endast om du har problem med att appen inte hittar alla album.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTELLT] Använd alternativ enhetsalbum-synkroniseringsfilter", "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", @@ -373,6 +363,7 @@ "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_subtitle": "Radera eller ÃĨterställ automatiskt en resurs pÃĨ den här enheten när den ÃĨtgärden utfÃļrs pÃĨ webben", "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", @@ -395,10 +386,6 @@ "album_remove_user": "Ta bort användare?", "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": "{count} objekt", - "album_thumbnail_card_shared": " ¡ Delad", - "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}", @@ -456,6 +443,7 @@ "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", @@ -494,17 +482,17 @@ "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 ({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_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", "backup_album_selection_page_selection_info": "Info om valda objekt", "backup_album_selection_page_total_assets": "Antal unika objekt", "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_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 {filename}", - "backup_background_service_default_notification": "SÃļker efter nya objekt...", + "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_in_progress_notification": "Säkerhetskopierar dina foton och videorâ€Ļ", "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.", @@ -512,6 +500,7 @@ "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", @@ -568,21 +557,17 @@ "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 ({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 ({count})", - "cache_settings_image_cache_size": "Cacheminnets storlek ({count} bilder och videor)", "cache_settings_statistics_album": "Miniatyrbilder fÃļr bibliotek", - "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 ({count} bilder och videor)", + "cache_settings_subtitle": "Hantera cachebeteendet fÃļr Immich-appen", "cache_settings_tile_subtitle": "Kontrollera beteende fÃļr lokal lagring", "cache_settings_tile_title": "Lokal Lagring", "cache_settings_title": "Cache Inställningar", @@ -595,12 +580,14 @@ "cannot_merge_people": "Kan inte slÃĨ samman personer", "cannot_undo_this_action": "Du kan inte ÃĨngra den här ÃĨtgärden!", "cannot_update_the_description": "Det gÃĨr inte att uppdatera beskrivningen", + "cast": "Casta", "change_date": "Ändra datum", + "change_description": "Ändra beskrivning", "change_display_order": "Ändra visningsordning", "change_expiration_time": "Ändra utgÃĨngstid", "change_location": "Ändra plats", "change_name": "Byt namn", - "change_name_successfully": "Bytt namn framgÃĨngsrikt", + "change_name_successfully": "Bytte namn framgÃĨngsrikt", "change_password": "Ändra LÃļsenord", "change_password_description": "Detta är antingen fÃļrsta gÃĨngen du loggar in i systemet eller sÃĨ har en begäran gjorts om att ändra ditt lÃļsenord. Vänligen ange det nya lÃļsenordet nedan.", "change_password_form_confirm_password": "Bekräfta lÃļsenord", @@ -608,9 +595,9 @@ "change_password_form_new_password": "Nytt lÃļsenord", "change_password_form_password_mismatch": "LÃļsenorden matchar inte", "change_password_form_reenter_new_password": "Ange Nytt LÃļsenord Igen", + "change_pin_code": "Ändra PIN-kod", "change_your_password": "Ändra ditt lÃļsenord", "changed_visibility_successfully": "Synligheten har ändrats", - "check_all": "Markera alla", "check_corrupt_asset_backup": "Kontrollera om det finns korrupta säkerhetskopior av objekt", "check_corrupt_asset_backup_button": "Kontrollera", "check_corrupt_asset_backup_description": "KÃļr kontrollen endast Ãļver Wi-Fi och när alla objekt har säkerhetskopierats. Det kan ta nÃĨgra minuter.", @@ -622,12 +609,13 @@ "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", "client_cert_invalid_msg": "Felaktig certifikatfil eller fel lÃļsenord", "client_cert_remove_msg": "Klientcertifikatet är borttaget", - "client_cert_subtitle": "StÃļdjer endast formatet PKCS12 (.p12, .pfx). Import/borttagning av certifikat är tillgängligt endast fÃļre inloggning", + "client_cert_subtitle": "StÃļdjer endast formatet PKCS12 (.p12, .pfx). Import/borttagning av certifikat är tillgängligt endast fÃļre inloggning", "client_cert_title": "SSL-Klientcertifikat", "clockwise": "Medsols", "close": "Stäng", @@ -647,11 +635,14 @@ "confirm_delete_face": "Är du säker pÃĨ att du vill ta bort {name}'s ansikte frÃĨn objektet?", "confirm_delete_shared_link": "Är du säker pÃĨ att du vill ta bort den här delade länken?", "confirm_keep_this_delete_others": "Alla tillgÃĨngar fÃļrutom den här tas bort frÃĨn stacken. Är du säker pÃĨ att du vill fortsätta?", + "confirm_new_pin_code": "Bekräfta ny PIN-kod", "confirm_password": "Bekräfta lÃļsenord", + "confirm_tag_face": "Vill du tagga det här ansiktet som {name}?", + "confirm_tag_face_unnamed": "Vill du tagga detta ansikte?", + "connected_to": "Ansluten till", "contain": "Anpassa", "context": "Sammanhang", "continue": "Fortsätt", - "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", @@ -689,15 +680,18 @@ "create_tag_description": "Skapa en ny tagg. FÃļr kapslade taggar anger du hela sÃļkvägen fÃļr taggen inklusive snedstreck.", "create_user": "Skapa användare", "created": "Skapad", + "created_at": "Skapad", "crop": "Beskär", "curated_object_page_title": "Objekt", "current_device": "Aktuell enhet", + "current_pin_code": "Nuvarande PIN-kod", "current_server_address": "Aktuell server-adress", "custom_locale": "Anpassad plats", "custom_locale_description": "Formatera datum och siffror baserat pÃĨ sprÃĨket och regionen", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "MÃļrk", + "darkTheme": "Växla till mÃļrkt tema", "date_after": "Datum efter", "date_and_time": "Datum och Tid", "date_before": "Datum fÃļre", @@ -717,7 +711,7 @@ "delete_api_key_prompt": "Är du säker pÃĨ att du vill ta bort denna API-nyckel?", "delete_dialog_alert": "Dessa objekt kommer att raderas permanent frÃĨn Immich och din enhet", "delete_dialog_alert_local": "Dessa objekt kommer att tas bort frÃĨn din enhet men fortsatt vara tillgängliga pÃĨ Immich-servern", - "delete_dialog_alert_local_non_backed_up": "NÃĨgra objekt har inte säkerhetskopierats till Immich och kommer att tas bort permanent frÃĨn din enhet.", + "delete_dialog_alert_local_non_backed_up": "NÃĨgra objekt har inte säkerhetskopierats till Immich och kommer att tas bort permanent frÃĨn din enhet", "delete_dialog_alert_remote": "Dessa objekt kommer att tas bort permanent frÃĨn Immich-servern", "delete_dialog_ok_force": "Ta Bort ÄndÃĨ", "delete_dialog_title": "Radera permanent", @@ -743,6 +737,7 @@ "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", @@ -759,7 +754,6 @@ "download_enqueue": "Nedladdning kÃļad", "download_error": "Fel vid nedladdning", "download_failed": "Nedladdning misslyckades", - "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", @@ -783,6 +777,8 @@ "edit_avatar": "Redigera avatar", "edit_date": "Redigera datum", "edit_date_and_time": "Redigera datum och tid", + "edit_description": "Redigera beskrivning", + "edit_description_prompt": "Vänligen välj en ny beskrivning:", "edit_exclusion_pattern": "Redigera uteslutningsmÃļnster", "edit_faces": "Redigera ansikten", "edit_import_path": "Redigera importsÃļkvägar", @@ -801,11 +797,14 @@ "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", + "email_notifications": "E-postaviseringar", "empty_folder": "Mappen är tom", "empty_trash": "TÃļm papperskorg", "empty_trash_confirmation": "Är du säker pÃĨ att du vill tÃļmma papperskorgen? Detta tar bort alla objekt i papperskorgen permanent frÃĨn Immich.\nDu kan inte ÃĨngra den här ÃĨtgärden!", "enable": "Aktivera", + "enable_biometric_auth_description": "Skriv in din pinkod fÃļr att aktivera biometrisk autentisering", "enabled": "Aktiverad", "end_date": "Slutdatum", "enqueued": "KÃļad", @@ -817,6 +816,7 @@ "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}", + "error_tag_face_bounding_box": "Fel vid taggning av ansikte – kan inte hämta koordinater fÃļr begränsningsruta", "error_title": "Fel – nÃĨgot gick fel", "errors": { "cannot_navigate_next_asset": "Det gÃĨr inte att navigera till nästa objekt", @@ -829,7 +829,6 @@ "cant_get_number_of_comments": "Kan inte fÃĨ antal kommentarer", "cant_search_people": "Kan inte sÃļka efter personer", "cant_search_places": "Kan inte sÃļka platser", - "cleared_jobs": "Raderade jobb fÃļr: {job}", "error_adding_assets_to_album": "Det gick inte att lägga till objekt i albumet", "error_adding_users_to_album": "Det gick inte att lägga till användare till albumet", "error_deleting_shared_user": "Det gick inte att ta bort delad användare", @@ -838,7 +837,6 @@ "error_removing_assets_from_album": "Det gick inte att ta bort objekt frÃĨn albumet, kontrollera konsolen fÃļr mer information", "error_selecting_all_assets": "Fel vid val av alla objekt", "exclusion_pattern_already_exists": "Detta uteslutningsmÃļnster finns redan.", - "failed_job_command": "Kommandot {command} misslyckades fÃļr jobbet: {job}", "failed_to_create_album": "Det gick inte att skapa album", "failed_to_create_shared_link": "Det gick inte att skapa delad länk", "failed_to_edit_shared_link": "Det gick inte att redigera delad länk", @@ -851,12 +849,12 @@ "failed_to_remove_product_key": "Det gick inte att ta bort produktnyckeln", "failed_to_stack_assets": "Det gick inte att stapla objekt", "failed_to_unstack_assets": "Det gick inte att avstapla objekt", + "failed_to_update_notification_status": "Misslyckades med att uppdatera aviseringens status", "import_path_already_exists": "Denna importsÃļkväg finns redan.", "incorrect_email_or_password": "Felaktig e-postadress eller lÃļsenord", "paths_validation_failed": "{paths, plural, one {# path} other {# paths}} misslyckades valideringen", "profile_picture_transparent_pixels": "Profilbilder kan inte ha genomskinliga pixlar. Zooma in och/eller flytta bilden.", "quota_higher_than_disk_size": "Du har angett en kvot som är hÃļgre än diskstorleken", - "repair_unable_to_check_items": "Det gÃĨr inte att kontrollera {count, select, one {item} other {items}}", "unable_to_add_album_users": "Kunde inte lägga till använder i album", "unable_to_add_assets_to_shared_link": "Det gÃĨr inte att lägga till objekt till delad länk", "unable_to_add_comment": "Kunde inte lägga till kommentar", @@ -875,7 +873,6 @@ "unable_to_change_visibility": "Det gick inte att ändra synligheten fÃļr {count, plural, one {# person} other {# people}}", "unable_to_complete_oauth_login": "Det gick inte att slutfÃļra OAuth-inloggning", "unable_to_connect": "Det gÃĨr inte att ansluta", - "unable_to_connect_to_server": "Det gÃĨr inte att ansluta till servern", "unable_to_copy_to_clipboard": "Kan inte kopiera till urklipp, se till att du kommer ÃĨt sidan via https", "unable_to_create_admin_account": "Det gick inte att skapa ett administratÃļrskonto", "unable_to_create_api_key": "Det gick inte att skapa en ny API-nyckel", @@ -899,14 +896,9 @@ "unable_to_hide_person": "Det gÃĨr inte att dÃļlja personen", "unable_to_link_motion_video": "Det gÃĨr inte att länka rÃļrlig video", "unable_to_link_oauth_account": "Det gick inte att länka OAuth-kontot", - "unable_to_load_album": "Det gick inte att ladda albumet", - "unable_to_load_asset_activity": "Det gÃĨr inte att läsa in tillgÃĨngsaktivitet", - "unable_to_load_items": "Kunde inte ladda objekt", - "unable_to_load_liked_status": "kunde inte ladda gillade status", "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", @@ -914,11 +906,9 @@ "unable_to_remove_album_users": "Kunde inte ta bort personen frÃĨn albumet", "unable_to_remove_api_key": "Det gick inte att ta bort API Keyet", "unable_to_remove_assets_from_shared_link": "Kunde inte ta bort objekt frÃĨn delade länkar", - "unable_to_remove_deleted_assets": "Kunde inte ta bort offline filer", "unable_to_remove_library": "Kunde inte ta bort bibliotek", "unable_to_remove_partner": "Kunde inte ta bort partner", "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", @@ -948,12 +938,12 @@ "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 {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}", @@ -991,6 +981,7 @@ "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", @@ -1000,6 +991,8 @@ "folders": "Mappar", "folders_feature_description": "Bläddra i mappvyn fÃļr foton och videoklipp i filsystemet", "forward": "FramÃĨt", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Denna funktion läser in externa resurser frÃĨn Google fÃļr att fungera.", "general": "Allmänt", "get_help": "FÃĨ hjälp", "get_wifiname_error": "Kunde inte hämta Wi-Fi-namn. Säkerställ att du tillÃĨtit nÃļdvändiga rättigheter och är ansluten till ett Wi-Fi-nätverk", @@ -1041,11 +1034,14 @@ "home_page_delete_remote_err_local": "Lokala objekt i urvalet fÃļr att ta bort frÃĨn servern, hoppar Ãļver", "home_page_favorite_err_local": "Kan inte favorisera lokala objekt ännu, hoppar Ãļver", "home_page_favorite_err_partner": "Kan inte favorisera partner-objekt ännu, hoppar Ãļver", - "home_page_first_time_notice": "Om det här är fÃļrsta gÃĨngen du använder appen, välj ett eller flera backup-album sÃĨ att tidslinjen kan fyllas med foton och videor frÃĨn albumen.", + "home_page_first_time_notice": "Om det här är fÃļrsta gÃĨngen du använder appen, välj ett eller flera backup-album sÃĨ att tidslinjen kan fyllas med foton och videor frÃĨn albumen", + "home_page_locked_error_local": "Kan inte flytta lokala resurser till lÃĨst mapp, hoppar Ãļver", + "home_page_locked_error_partner": "Kan inte flytta partnerresurser till lÃĨst mapp, hoppar Ãļver", "home_page_share_err_local": "Kan inte dela lokalt objekt via länk, hoppar Ãļver", "home_page_upload_err_limit": "Kan bara ladda upp max 30 objekt ÃĨt gÃĨngen, hoppar Ãļver", "host": "Värd", "hour": "Timme", + "id": "ID", "ignore_icloud_photos": "Ignorera iCloud-foton", "ignore_icloud_photos_description": "Foton lagrade i iCloud kommer inte laddas upp till Immich-servern", "image": "Bild", @@ -1063,6 +1059,7 @@ "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-logga", "immich_web_interface": "Immich Web gränssnitt", "import_from_json": "Importera frÃĨn JSON", "import_path": "ImportsÃļkväg", @@ -1084,6 +1081,8 @@ "invalid_date_format": "Felaktigt datumformat", "invite_people": "Bjud in personer", "invite_to_album": "Bjuder in till album", + "ios_debug_info_last_sync_at": "Senaste synkning {dateTime}", + "ios_debug_info_no_processes_queued": "Inga bakgrundsprocesser kÃļade", "items_count": "{count, plural, one {# objekt} other {# objekt}}", "jobs": "Jobb", "keep": "BehÃĨll", @@ -1126,6 +1125,8 @@ "location_picker_latitude_hint": "Ange din latitud här", "location_picker_longitude_error": "Ange en giltig longitud", "location_picker_longitude_hint": "Ange din longitud här", + "lock": "LÃĨs", + "locked_folder": "LÃĨst Mapp", "log_out": "Logga ut", "log_out_all_devices": "Logga ut alla enheter", "logged_out_all_devices": "Loggat ut frÃĨn alla enheter", @@ -1136,6 +1137,7 @@ "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": "Slutpunkt-URL fÃļr server", "login_form_err_http": "Var god ange http:// eller https://", "login_form_err_invalid_email": "Ogiltig email", "login_form_err_invalid_url": "Ogiltig webbadress", @@ -1143,12 +1145,12 @@ "login_form_err_trailing_whitespace": "Mellanrum efter", "login_form_failed_get_oauth_server_config": "Kunde inte logga in med OAuth. Kontrollera serverns webbadress", "login_form_failed_get_oauth_server_disable": "OAuth är inte tillgänglig pÃĨ den här servern", - "login_form_failed_login": "Kunde inte logga in. Kontrollera serverns webbadress, email och lÃļsenord.", + "login_form_failed_login": "Kunde inte logga in. Kontrollera serverns webbadress, email och lÃļsenord", "login_form_handshake_exception": "Ett Undantag vid Handskakning med servern har skett. Aktivera stÃļd fÃļr självsignerade certifikat i inställningar om du använder ett självsignerat certifikat.", "login_form_password_hint": "lÃļsenord", "login_form_save_login": "HÃĨll mig inloggad", "login_form_server_empty": "Ange en server-URL.", - "login_form_server_error": "Kunde inte ansluta till servern", + "login_form_server_error": "Kunde inte ansluta till servern.", "login_has_been_disabled": "Inloggning har blivit inaktiverat.", "login_password_changed_error": "Ett fel uppstod vid uppdatering av ditt lÃļsenord", "login_password_changed_success": "Uppdatering av lÃļsenord lyckades", @@ -1193,6 +1195,9 @@ "map_settings_only_show_favorites": "Visa Endast Favoriter", "map_settings_theme_settings": "Kart-tema", "map_zoom_to_see_photos": "Zooma ut fÃļr att se foton", + "mark_all_as_read": "Markera alla som lästa", + "mark_as_read": "Markera som läst", + "marked_all_as_read": "Markerade alla som lästa", "matches": "Matchar", "media_type": "Mediatyp", "memories": "Minnen", @@ -1215,7 +1220,14 @@ "missing": "Saknade", "model": "Modell", "month": "MÃĨnad", + "monthly_title_text_date_format": "MMMM y", "more": "Mer", + "move": "Flytta", + "move_off_locked_folder": "Flytta frÃĨn lÃĨst mapp", + "move_to_locked_folder": "Flytta till lÃĨst mapp", + "move_to_locked_folder_confirmation": "Dessa foton och videor kommer tas bort frÃĨn alla album och gÃĨr endast se i lÃĨsta mappen", + "moved_to_archive": "Flyttade {count, plural, one {# resurs} other {# assets}} till arkivet", + "moved_to_library": "\"Flyttade {count, plural, one {# asset} other {# assets}} till biblioteket.\"", "moved_to_trash": "Flyttad till papperskorgen", "multiselect_grid_edit_date_time_err_read_only": "Kan inte ändra datum pÃĨ skrivskyddade objekt, hoppar Ãļver", "multiselect_grid_edit_gps_err_read_only": "Kan inte ändra plats pÃĨ skrivskyddade objekt, hoppar Ãļver", @@ -1230,6 +1242,8 @@ "new_api_key": "Ny API-nyckel", "new_password": "Nytt lÃļsenord", "new_person": "Ny person", + "new_pin_code": "Ny PIN-kod", + "new_pin_code_subtitle": "Det här är fÃļrsta gÃĨngen du Ãļppnar den lÃĨsta mappen. Skapa en PIN-kod fÃļr att säkert fÃĨ ÃĨtkomst till den här sidan", "new_user_created": "Ny användare skapad", "new_version_available": "NY VERSION TILLGÄNGLIG", "newest_first": "Nyast fÃļrst", @@ -1247,7 +1261,10 @@ "no_explore_results_message": "Ladda upp fler bilder fÃļr att utforska din samling.", "no_favorites_message": "Lägg till favoriter fÃļr att snabbt hitta dina bästa bilder och videor", "no_libraries_message": "Skapa ett externt bibliotek fÃļr att se dina bilder och videor", + "no_locked_photos_message": "Foton och videor i den lÃĨsta mappen är dolda och visas inte när du bläddrar eller sÃļker i ditt bibliotek.", "no_name": "Inget namn", + "no_notifications": "Inga aviseringar", + "no_people_found": "Inga matchande personer hittade", "no_places": "Inga platser", "no_results": "Inga resultat", "no_results_description": "PrÃļva en synonym eller ett annat mer allmänt sÃļkord", @@ -1256,38 +1273,40 @@ "not_selected": "Ej vald", "note_apply_storage_label_to_previously_uploaded assets": "Obs: Om du vill använda lagringsetiketten pÃĨ tidigare uppladdade tillgÃĨngar kÃļr du", "notes": "Notera", - "notification_permission_dialog_content": "FÃļr att aktivera notiser, gÃĨ till Inställningar och välj tillÃĨt", + "nothing_here_yet": "Inget här ännu", + "notification_permission_dialog_content": "FÃļr att aktivera notiser, gÃĨ till Inställningar och välj tillÃĨt.", "notification_permission_list_tile_content": "TillÃĨt rättighet fÃļr att slÃĨ pÃĨ notiser.", "notification_permission_list_tile_enable_button": "Aktivera Notiser", "notification_permission_list_tile_title": "Notisrättighet", "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", - "offline_paths_description": "Dessa resultat kan bero pÃĨ att filer som ej ingÃĨr i ett externt bibliotek har tagits bort manuellt.", - "ok": "OK", + "ok": "Ok", "oldest_first": "Äldst fÃļrst", "on_this_device": "PÃĨ enheten", "onboarding": "Introduktion", - "onboarding_privacy_description": "FÃļljande (valfria) funktioner är beroende av externa tjänster och kan när som helst avaktiveras i administratÃļrsinställningarna.", + "onboarding_privacy_description": "FÃļljande (valfria) funktioner är beroende av externa tjänster och kan när som helst avaktiveras i inställningarna.", "onboarding_theme_description": "Välj en färg fÃļr din instans. Du kan ändra detta senare via inställningarna.", - "onboarding_welcome_description": "Vi bÃļrjar med att ställa in din instans med nÃĨgra vanliga inställningar.", "onboarding_welcome_user": "Välkommen, {user}", "online": "Ansluten", "only_favorites": "Endast favoriter", + "open": "Öppen", "open_in_map_view": "Öppna kartvy", "open_in_openstreetmap": "Öppna med OpenStreetMap", "open_the_search_filters": "Öppna sÃļkfilter", "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", @@ -1300,6 +1319,7 @@ "partner_page_shared_to_title": "Delad till", "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", @@ -1311,6 +1331,7 @@ }, "path": "SÃļkväg", "pattern": "MÃļnster", + "pause": "Pause", "pause_memories": "Pausa minnen", "paused": "Pausad", "pending": "Väntande", @@ -1325,6 +1346,8 @@ "permanently_delete_assets_prompt": "Är du säker pÃĨ att du permanent vill ta bort {count, plural, one {denna fil?} other{these # filer?}} Detta kommer ocksÃĨ ta bort {count, plural, one {dem frÃĨn } other{them from their}} album.", "permanently_deleted_asset": "Permanent raderad tillgÃĨng", "permanently_deleted_assets_count": "Permanent borttagning av {count, plural, one {# asset} other {# assets}}", + "permission": "BehÃļrigheter", + "permission_empty": "Dina behÃļrighet kan inte vara blankt", "permission_onboarding_back": "BakÃĨt", "permission_onboarding_continue_anyway": "Fortsätt ändÃĨ", "permission_onboarding_get_started": "Kom igÃĨng", @@ -1333,6 +1356,7 @@ "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.", @@ -1341,6 +1365,10 @@ "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Foton}}", "photos_from_previous_years": "Foton frÃĨn tidigare ÃĨr", "pick_a_location": "Välj en plats", + "pin_code_changed_successfully": "Lyckades ändra PIN-koden", + "pin_code_reset_successfully": "Lyckades ÃĨterställa PIN-kod", + "pin_code_setup_successfully": "Lyckades skapa PIN-kod", + "pin_verification": "Verifiering av PIN-kod", "place": "Plats", "places": "Platser", "places_count": "{count, plural, one {{count, number} Plats} other {{count, number} Platser}}", @@ -1348,25 +1376,33 @@ "play_memories": "Spela upp minnen", "play_motion_photo": "Spela upp rÃļrligt foto", "play_or_pause_video": "Spela upp eller pausa video", + "please_auth_to_access": "Vänligen autentisera fÃļr att fÃĨ ÃĨtkomst", + "port": "Port", "preferences_settings_subtitle": "Hantera appens inställningar", "preferences_settings_title": "Inställningar", "preset": "FÃļrinställt värde", "preview": "FÃļrhandsvisning", "previous": "FÃļregÃĨende", "previous_memory": "FÃļregÃĨende minne", + "previous_or_next_day": "FÃļregÃĨende eller nästa dag", + "previous_or_next_month": "FÃļregÃĨende eller nästa mÃĨnad", "previous_or_next_photo": "FÃļregÃĨende eller nästa foto", + "previous_or_next_year": "FÃļregÃĨende eller nästa ÃĨr", "primary": "Primär", "privacy": "Sekretess", + "profile": "Profil", "profile_drawer_app_logs": "Loggar", "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}", "purchase_activated_title": "Aktiveringan av din nyckel lyckades", @@ -1388,6 +1424,7 @@ "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?", @@ -1395,6 +1432,7 @@ "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", @@ -1411,6 +1449,8 @@ "recent_searches": "Senaste sÃļkningar", "recently_added": "Nyligen tillagda", "recently_added_page_title": "Nyligen tillagda", + "recently_taken": "Nyligen tagna", + "recently_taken_page_title": "Nyligen Tagna", "refresh": "Ladda om", "refresh_encoded_videos": "Ladda om kodade videor", "refresh_faces": "Ladda om ansikten", @@ -1430,6 +1470,8 @@ "remove_deleted_assets": "Ta bort borttagna tillgÃĨngar", "remove_from_album": "Ta bort frÃĨn album", "remove_from_favorites": "Ta bort frÃĨn favoriter", + "remove_from_locked_folder": "Ta bort frÃĨn lÃĨst mapp", + "remove_from_locked_folder_confirmation": "Är du säker pÃĨ att du vill flytta dessa foton och videor frÃĨn den lÃĨsta mappen? De kommer att vara synliga i ditt bibliotek.", "remove_from_shared_link": "Ta bort frÃĨn delad länk", "remove_memory": "Ta bort minne", "remove_photo_from_memory": "Ta bort fotot frÃĨn detta minnet", @@ -1453,6 +1495,7 @@ "reset": "Återställ", "reset_password": "Nollställ lÃļsenord", "reset_people_visibility": "Återställ personers synlighet", + "reset_pin_code": "Återställ PIN-kod", "reset_to_default": "Återställ till standard", "resolve_duplicates": "LÃļs dubletter", "resolved_all_duplicates": "LÃļs alla dubletter", @@ -1514,6 +1557,7 @@ "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", @@ -1524,7 +1568,7 @@ "search_result_page_new_search_hint": "Ny sÃļkning", "search_settings": "SÃļk inställningar", "search_state": "SÃļk stat...", - "search_suggestion_list_smart_search_hint_1": "SmartsÃļk är aktiverat som standard, fÃļr att sÃļka efter metadata, använd syntaxen", + "search_suggestion_list_smart_search_hint_1": "SmartsÃļk är aktiverat som standard, fÃļr att sÃļka efter metadata, använd syntaxen ", "search_suggestion_list_smart_search_hint_2": "m:ditt-sÃļkord", "search_tags": "SÃļk taggar...", "search_timezone": "SÃļk tidszon...", @@ -1544,6 +1588,7 @@ "select_keep_all": "Spara alla", "select_library_owner": "Välj biblioteksägare", "select_new_face": "Välj nytt ansikte", + "select_person_to_tag": "Välj en person att tagga", "select_photos": "Välj foton", "select_trash_all": "Släng alla", "select_user_for_sharing_page_err_album": "Kunde inte skapa nytt album", @@ -1573,7 +1618,6 @@ "setting_image_viewer_title": "Bilder", "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: {duration}", "setting_notifications_notify_hours": "{count} timmar", "setting_notifications_notify_immediately": "omedelbart", @@ -1591,10 +1635,12 @@ "settings": "Inställningar", "settings_require_restart": "Starta om Immich fÃļr att tillämpa den här inställningen", "settings_saved": "Inställningar sparade", + "setup_pin_code": "Konfigurera pinkod", "share": "Dela", "share_add_photos": "Lägg till foton", "share_assets_selected": "{count} valda", "share_dialog_preparing": "FÃļrbereder...", + "share_link": "Dela Länk", "shared": "Delad", "shared_album_activities_input_disable": "Kommentar är inaktiverad", "shared_album_activity_remove_content": "Vill du ta bort den här aktiviteten?", @@ -1634,6 +1680,7 @@ "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", @@ -1699,16 +1746,20 @@ "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.", "stop_sharing_photos_with_user": "Sluta dela dina bilder med denna användaren", "storage": "Lagring", "storage_label": "FÃļrvaringsetikett", + "storage_quota": "Lagrinkskvot", "storage_usage": "{used} av {available} används", "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", @@ -1753,7 +1804,7 @@ "to_parent": "GÃĨ till fÃļrälder", "to_trash": "Papperskorg", "toggle_settings": "Växla inställningar", - "toggle_theme": "Växla tema", + "total": "Totalt", "total_usage": "Total användning", "trash": "Papperskorg", "trash_all": "Kasta alla", @@ -1770,6 +1821,8 @@ "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", + "unable_to_change_pin_code": "Kunde inte ändra pinkod", + "unable_to_setup_pin_code": "Kunde inte konfigurera pinkod", "unarchive": "Ångra arkivering", "unarchived_count": "{count, plural, one {# borttagen frÃĨn arkiv} other {# borttagna frÃĨn arkiv}}", "unfavorite": "Avfavorisera", @@ -1790,9 +1843,8 @@ "unselect_all_duplicates": "Avmarkera alla dubletter", "unstack": "Stapla Av", "unstacked_assets_count": "Avstaplade {count, plural, one {# asset} other {# assets}}", - "untracked_files": "OspÃĨrade filer", - "untracked_files_decription": "Dessa filer spÃĨras inte av applikationen. Det kan bero pÃĨ misslyckad flytt, avbruten uppladdning eller att de lämnats kvar pÃĨ grund av en bugg", "up_next": "Nästa", + "updated_at": "Uppdaterat", "updated_password": "LÃļsenordet har uppdaterats", "upload": "Ladda upp", "upload_concurrency": "Uppladdning samtidighet", @@ -1807,12 +1859,17 @@ "upload_success": "Uppladdning lyckades, ladda om sidan fÃļr att se nya objekt.", "upload_to_immich": "Ladda upp till Immich ({count})", "uploading": "Laddar upp", + "url": "URL", "usage": "Användning", + "use_biometric": "Använd biometri", "use_current_connection": "Använd aktuell anslutning", "use_custom_date_range": "Använd anpassat datumintervall istället", "user": "Användare", + "user_has_been_deleted": "Den här användaren har raderats.", "user_id": "Användar-ID", "user_liked": "{user} gillade {type, select, photo {detta fotot} video {denna filmen} asset {detta objekt} other {detta}}", + "user_pin_code_settings": "Pinkod", + "user_pin_code_settings_description": "Hantera pinkod", "user_purchase_settings": "KÃļp", "user_purchase_settings_description": "Hantera dina kÃļp", "user_role_set": "Sätt {user} som {role}", @@ -1825,15 +1882,12 @@ "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", - "version_announcement_overlay_text_1": "Hej vännen, det finns en ny version av", - "version_announcement_overlay_text_2": ". Ta gärna din tid att besÃļka ", - "version_announcement_overlay_text_3": " fÃļr att se till att din docker-compose och .env-fil är uppdaterad fÃļr att undvika felkonfiguration, speciellt om du använder WatchTower eller liknande mekanism som automatiskt uppdaterar din container", - "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", @@ -1848,7 +1902,9 @@ "view_name": "Visa", "view_next_asset": "Visa nästa objekt", "view_previous_asset": "Visa fÃļregÃĨende objekt", + "view_qr_code": "Visa QR-kod", "view_stack": "Visa Stapel", + "view_user": "Visa Användare", "viewer_remove_from_stack": "Ta bort frÃĨn Stapeln", "viewer_stack_use_as_main_asset": "Använd som Huvudobjekt", "viewer_unstack": "Stapla Av", @@ -1858,11 +1914,12 @@ "week": "Vecka", "welcome": "Välkommen", "welcome_to_immich": "Välkommen till Immich", - "wifi_name": "WiFi-namn", + "wifi_name": "Wi-Fi-namn", + "wrong_pin_code": "Fel pinkod", "year": "År", "years_ago": "{years, plural, one {# ÃĨr} other {# ÃĨr}} sedan", "yes": "Ja", "you_dont_have_any_shared_links": "Du har inga delade länkar", - "your_wifi_name": "Ditt WiFi-namn", + "your_wifi_name": "Ditt Wi-Fi-namn", "zoom_image": "Zooma bild" } diff --git a/i18n/ta.json b/i18n/ta.json index 679d35ec46..ce0768982d 100644 --- a/i18n/ta.json +++ b/i18n/ta.json @@ -40,7 +40,6 @@ "backup_keep_last_amount": "āŽĩā¯ˆāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽ¨ā¯āŽ¤ā¯ˆāŽ¯ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ•āŽŗāŽŋāŽŠā¯ āŽ…āŽŗāŽĩ❁", "backup_settings": "āŽ•āŽžāŽĒā¯āŽĒ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "backup_settings_description": "āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", - "check_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯", "cleared_jobs": "āŽŽā¯āŽŸāŽŋāŽ¤ā¯āŽ¤ āŽĩā¯‡āŽ˛ā¯ˆāŽ•āŽŗā¯: {job}", "config_set_by_file": "config āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ āŽ’āŽ°ā¯ config āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", "confirm_delete_library": "{library} āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ¨āŽŋāŽšā¯āŽšāŽ¯āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", @@ -55,14 +54,12 @@ "disable_login": "āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❈ āŽŽā¯āŽŸāŽ•ā¯āŽ•ā¯", "duplicate_detection_job_description": "āŽ’āŽ¤ā¯āŽ¤ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽŖā¯āŽŸāŽąāŽŋāŽ¯, āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ‡āŽ¯āŽ¨ā¯āŽ¤āŽŋāŽ°āŽ•ā¯ āŽ•āŽąā¯āŽąāŽ˛ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯. āŽ¸ā¯āŽŽāŽžāŽ°ā¯āŽŸā¯ āŽ¤ā¯‡āŽŸāŽ˛ā¯ˆ āŽ¨āŽŽā¯āŽĒāŽŋāŽ¯ā¯āŽŗā¯āŽŗāŽ¤ā¯", "exclusion_pattern_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ¸ā¯āŽ•ā¯‡āŽŠā¯ āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽŽā¯ āŽĒā¯‹āŽ¤ā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋāŽ•ā¯āŽ• āŽĩāŽŋāŽ˛āŽ•ā¯āŽ•ā¯ āŽĩāŽŸāŽŋāŽĩāŽ™ā¯āŽ•āŽŗā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ˆ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•ā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ. RAW āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽĒā¯‹āŽŠā¯āŽą āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ āŽšā¯†āŽ¯ā¯āŽ¯ āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒāŽžāŽ¤ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ•āŽŗā¯ āŽ‰āŽ™ā¯āŽ•āŽŗāŽŋāŽŸāŽŽā¯ āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤āŽžāŽ˛ā¯ āŽ‡āŽ¤ā¯ āŽĒāŽ¯āŽŠā¯āŽŗā¯āŽŗāŽ¤āŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯.", - "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_prefer_embedded_preview": "āŽ‰āŽŸā¯āŽĒā¯ŠāŽ¤āŽŋāŽ¨ā¯āŽ¤ āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯āŽŠā¯āŽŠāŽŋāŽŸā¯", @@ -193,8 +190,6 @@ "oauth_storage_quota_claim_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ•ā¯‹āŽ°āŽ˛āŽŋāŽŠā¯ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒāŽŋāŽąā¯āŽ•ā¯ āŽĒāŽ¯āŽŠāŽ°āŽŋāŽŠā¯ āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯āŽŸā¯ˆ āŽ¤āŽžāŽŠāŽžāŽ• āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", "oauth_storage_quota_default": "āŽ‡āŽ¯āŽ˛ā¯āŽĒā¯āŽ¨āŽŋāŽ˛ā¯ˆ āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯ (GiB)", "oauth_storage_quota_default_description": "GiB āŽ‡āŽ˛ā¯ āŽ‰āŽŗā¯āŽŗ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯ āŽŽāŽ¨ā¯āŽ¤ āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ•ā¯‹āŽ°āŽ˛ā¯āŽŽā¯ āŽĩāŽ´āŽ™ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽ¤āŽĒā¯‹āŽ¤ā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŽā¯ (āŽĩāŽ°āŽŽā¯āŽĒāŽąā¯āŽą āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯āŽŸāŽŋāŽąā¯āŽ•ā¯ 0 āŽ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯).", - "offline_paths": "āŽ†āŽƒāŽĒā¯āŽ˛ā¯ˆāŽŠā¯ āŽĒāŽžāŽ¤ā¯ˆāŽ•āŽŗā¯", - "offline_paths_description": "āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽĒāŽ•ā¯āŽ¤āŽŋāŽ¯āŽžāŽ• āŽ‡āŽ˛ā¯āŽ˛āŽžāŽ¤ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ•ā¯ˆāŽŽā¯āŽąā¯ˆāŽ¯āŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽŋāŽ¯āŽ¤āŽŠā¯ āŽ•āŽžāŽ°āŽŖāŽŽāŽžāŽ• āŽ‡āŽ¨ā¯āŽ¤ āŽŽā¯āŽŸāŽŋāŽĩā¯āŽ•āŽŗā¯ āŽ‡āŽ°ā¯āŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯.", "password_enable_description": "āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ¯āŽĩā¯āŽŽā¯", "password_settings": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁", "password_settings_description": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -204,9 +199,6 @@ "refreshing_all_libraries": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¨ā¯‚āŽ˛āŽ•āŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯", "registration": "āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ• āŽĒāŽ¤āŽŋāŽĩ❁", "registration_description": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŖāŽŋāŽŠāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽŽā¯āŽ¤āŽ˛ā¯ āŽĒāŽ¯āŽŠāŽ°āŽžāŽ• āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽ¤āŽžāŽ˛ā¯, āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽŋāŽ¯āŽžāŽ• āŽ¨āŽŋāŽ¯āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽĩā¯€āŽ°ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽĒā¯ āŽĒāŽŖāŽŋāŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯āŽĒā¯ āŽĒā¯ŠāŽąā¯āŽĒā¯āŽĒāŽžāŽĩā¯€āŽ°ā¯āŽ•āŽŗā¯, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ‰āŽ™ā¯āŽ•āŽŗāŽžāŽ˛ā¯ āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽĩāŽžāŽ°ā¯āŽ•āŽŗā¯.", - "repair_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒāŽ´ā¯āŽ¤ā¯āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", - "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": "āŽ…āŽŖā¯āŽŽā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -235,7 +227,6 @@ "storage_template_migration_info": "āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯ āŽŽāŽžāŽąā¯āŽąāŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤ā¯āŽŽā¯. āŽŽā¯āŽŠā¯āŽĒ❁ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽŋāŽ¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯āŽŸā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤, {job} āŽ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", "storage_template_migration_job": "āŽ¸ā¯āŽŸā¯‹āŽ°ā¯‡āŽœā¯ āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯ āŽ‡āŽŸāŽŽā¯āŽĒā¯†āŽ¯āŽ°ā¯āŽĩ❁ āŽĩā¯‡āŽ˛ā¯ˆ", "storage_template_more_details": "āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯āŽšāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽąā¯āŽąāŽŋāŽ¯ āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯, Storage Template āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ…āŽ¤āŽŠā¯ āŽ¤āŽžāŽ•ā¯āŽ•āŽ™ā¯āŽ•āŽŗā¯ āŽāŽĒā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", - "storage_template_onboarding_description": "āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽžāŽ˛ā¯, āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯āŽšāŽŽā¯ āŽĒāŽ¯āŽŠāŽ°ā¯ āŽĩāŽ°ā¯ˆāŽ¯āŽąā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯āŽŸāŽŋāŽŠā¯ āŽ…āŽŸāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤āŽžāŽŠāŽžāŽ• āŽ’āŽ´ā¯āŽ™ā¯āŽ•āŽŽā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯. āŽ¨āŽŋāŽ˛ā¯ˆāŽ¤ā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽšāŽŋāŽ•ā¯āŽ•āŽ˛ā¯āŽ•āŽŗā¯ āŽ•āŽžāŽ°āŽŖāŽŽāŽžāŽ•, āŽ…āŽŽā¯āŽšāŽŽā¯ āŽ‡āŽ¯āŽ˛ā¯āŽĒāŽžāŽ•āŽĩ❇ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯. āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ¤āŽ•āŽĩāŽ˛ā¯āŽ•ā¯āŽ•ā¯, āŽ†āŽĩāŽŖāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", "storage_template_path_length": "āŽ¤ā¯‹āŽ°āŽžāŽ¯āŽŽāŽžāŽŠ āŽĒāŽžāŽ¤ā¯ˆ āŽ¨ā¯€āŽŗ āŽĩāŽ°āŽŽā¯āŽĒ❁: {length, number}/{limit, number}", "storage_template_settings": "āŽ¸ā¯āŽŸā¯‹āŽ°ā¯‡āŽœā¯ āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯", "storage_template_settings_description": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❁ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĒā¯†āŽ¯āŽ°ā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -247,7 +238,6 @@ "template_email_invite_album": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯āŽĩ❈ āŽ…āŽ´ā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "template_email_preview": "āŽŽā¯āŽŠā¯āŽŠā¯‹āŽŸā¯āŽŸāŽŽā¯", "template_email_settings": "āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯āŽ•ā¯āŽ•āŽŗā¯", - "template_email_settings_description": "āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒ❁ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "template_email_update_album": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯āŽĩ❈āŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "template_email_welcome": "āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯āŽĩ❈ āŽĩāŽ°āŽĩā¯‡āŽąā¯āŽ•āŽŋāŽąā¯‹āŽŽā¯", "template_settings": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒ❁ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯āŽ•ā¯āŽ•āŽŗā¯", @@ -256,7 +246,6 @@ "theme_custom_css_settings_description": "CSS āŽ…āŽŽā¯āŽšāŽŽā¯ Immich āŽĩāŽŸāŽŋāŽĩāŽŽā¯ˆāŽĒā¯āŽĒ❈ āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠāŽžāŽ•ā¯āŽ• āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯.", "theme_settings": "āŽ¤ā¯€āŽŽā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "theme_settings_description": "āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽĩāŽ˛ā¯ˆ āŽ‡āŽŸā¯ˆāŽŽā¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠāŽžāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", - "these_files_matched_by_checksum": "āŽ‡āŽ¨ā¯āŽ¤āŽ•ā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽ…āŽĩāŽąā¯āŽąāŽŋāŽŠā¯ āŽšā¯†āŽ•ā¯āŽšāŽŽā¯āŽ•āŽŗā¯āŽŸāŽŠā¯ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤ā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ", "thumbnail_generation_job": "āŽšāŽŋāŽąā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "thumbnail_generation_job_description": "āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯ āŽĒā¯†āŽ°āŽŋāŽ¯, āŽšāŽŋāŽąāŽŋāŽ¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽŽāŽ™ā¯āŽ•āŽ˛āŽžāŽŠ āŽšāŽŋāŽąā¯ āŽ‰āŽ°ā¯āŽĩāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯, āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽ¨āŽĒāŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯ āŽšāŽŋāŽąā¯ āŽ‰āŽ°ā¯āŽĩāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ™ā¯āŽ•āŽŗā¯", "transcoding_acceleration_api": "āŽŽā¯āŽŸā¯āŽ•ā¯āŽ•āŽŽā¯ āŽĒāŽ¨āŽŋāŽ‡", @@ -285,7 +274,6 @@ "transcoding_hardware_acceleration_description": "āŽšā¯‹āŽ¤āŽŠā¯ˆ; āŽŽāŽŋāŽ• āŽĩā¯‡āŽ•āŽŽāŽžāŽ•, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ…āŽ¤ā¯‡ āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯āŽŸāŽŋāŽ˛ā¯ āŽ•ā¯āŽąā¯ˆāŽ¨ā¯āŽ¤ āŽ¤āŽ•ā¯āŽ¤āŽŋ āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯", "transcoding_hardware_decoding": "āŽĩāŽŠā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽŸāŽŋāŽ•ā¯‹āŽŸāŽŋāŽ™ā¯", "transcoding_hardware_decoding_setting_description": "āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽĩāŽŋāŽ°ā¯ˆāŽĩ❁āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽĒāŽ¤āŽŋāŽ˛āŽžāŽ• āŽ‡āŽąā¯āŽ¤āŽŋ āŽŽā¯āŽ¤āŽ˛ā¯ āŽ‡āŽąā¯āŽ¤āŽŋ āŽŽā¯āŽŸā¯āŽ•ā¯āŽ•āŽŽā¯ āŽ†āŽ•āŽŋāŽ¯āŽĩāŽąā¯āŽąā¯ˆ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽŽāŽ˛ā¯āŽ˛āŽž āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗāŽŋāŽ˛ā¯āŽŽā¯ āŽĩā¯‡āŽ˛ā¯ˆ āŽšā¯†āŽ¯ā¯āŽ¯āŽ•ā¯āŽ•ā¯‚āŽŸāŽžāŽ¤ā¯.", - "transcoding_hevc_codec": "HEVC āŽ•ā¯‹āŽŸā¯†āŽ•ā¯", "transcoding_max_b_frames": "āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽĒāŽŋ-āŽĒāŽŋāŽ°ā¯‡āŽŽā¯āŽ•āŽŗā¯", "transcoding_max_b_frames_description": "āŽ…āŽ¤āŽŋāŽ• āŽŽāŽ¤āŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽšā¯āŽ°ā¯āŽ•ā¯āŽ• āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽŋāŽąāŽŠā¯ˆ āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯†āŽ¤ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ. āŽĒāŽ´ā¯ˆāŽ¯ āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽĩāŽŠā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽŽā¯āŽŸā¯āŽ•ā¯āŽ•āŽŽā¯ āŽ‰āŽŸāŽŠā¯ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤āŽžāŽ¤ā¯. 0 āŽĒāŽŋ -āŽĒāŽŋāŽ°ā¯‡āŽŽā¯āŽ•āŽŗā¯ˆ āŽŽā¯āŽŸāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯, āŽ…āŽ¤ā¯‡ āŽ¨ā¯‡āŽ°āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ -1 āŽ‡āŽ¨ā¯āŽ¤ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒ❈ āŽ¤āŽžāŽŠāŽžāŽ• āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯.", "transcoding_max_bitrate": "āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯", @@ -319,8 +307,6 @@ "trash_number_of_days_description": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ…āŽ•āŽąā¯āŽąā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽŽā¯āŽŠā¯ āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¤ā¯āŽ¤ā¯ŠāŽŸā¯āŽŸāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽĩā¯ˆāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽ¨āŽžāŽŸā¯āŽ•āŽŗā¯ āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ", "trash_settings": "āŽ•ā¯āŽĒā¯āŽĒ❈ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "trash_settings_description": "āŽ•ā¯āŽĒā¯āŽĒ❈ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", - "untracked_files": "āŽ•āŽŸā¯āŽŸā¯āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸāŽžāŽ¤ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯", - "untracked_files_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸāŽžāŽ˛ā¯ āŽ•āŽŖā¯āŽ•āŽžāŽŖāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ. āŽ…āŽĩ❈ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯ā¯āŽąā¯āŽą āŽ¨āŽ•āŽ°ā¯āŽĩā¯āŽ•āŽŗā¯, āŽ•ā¯āŽąā¯āŽ•ā¯āŽ•āŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ’āŽ°ā¯ āŽĒāŽŋāŽ´ā¯ˆāŽ¯āŽŋāŽŠā¯ āŽ•āŽžāŽ°āŽŖāŽŽāŽžāŽ• āŽĩāŽŋāŽŸā¯āŽŸā¯āŽšā¯āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽŽā¯ āŽŽā¯āŽŸāŽŋāŽĩā¯āŽ•āŽŗāŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯", "user_cleanup_job": "āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ¤ā¯‚āŽ¯ā¯āŽŽā¯ˆāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ¤āŽ˛ā¯", "user_delete_delay": " {user} āŽ‡āŽŠā¯ āŽ•āŽŖāŽ•ā¯āŽ•ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ {āŽ¤āŽžāŽŽāŽ¤āŽŽā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ¨āŽžāŽŗā¯} āŽŽāŽąā¯āŽą {# āŽ¨āŽžāŽŸā¯āŽ•āŽŗā¯}} āŽ‡āŽ˛ā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ° āŽ¨ā¯€āŽ•ā¯āŽ• āŽ¤āŽŋāŽŸā¯āŽŸāŽŽāŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", "user_delete_delay_settings": "āŽ¤āŽžāŽŽāŽ¤āŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", @@ -446,7 +432,6 @@ "change_password_description": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŖāŽŋāŽŠāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽ•ā¯ˆāŽ¯ā¯ŠāŽĒā¯āŽĒāŽŽāŽŋāŽŸā¯āŽĩāŽ¤ā¯ āŽ‡āŽ¤ā¯āŽĩ❇ āŽŽā¯āŽ¤āŽ˛ā¯ āŽŽā¯āŽąā¯ˆ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąā¯āŽĩāŽ¤āŽąā¯āŽ•āŽžāŽŠ āŽ•ā¯‹āŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯. āŽ•ā¯€āŽ´ā¯‡ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯.", "change_your_password": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "changed_visibility_successfully": "āŽ¤ā¯†āŽ°āŽŋāŽĩā¯āŽ¨āŽŋāŽ˛ā¯ˆ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽŽāŽžāŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", - "check_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "check_logs": "āŽĒāŽ¤āŽŋāŽĩā¯āŽ•āŽŗā¯ˆ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "choose_matching_people_to_merge": "āŽ’āŽŠā¯āŽąāŽŋāŽŖā¯ˆāŽ•ā¯āŽ• āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽ¨āŽĒāŽ°ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ•", "city": "āŽ¨āŽ•āŽ°āŽŽā¯", @@ -595,7 +580,6 @@ "cant_get_number_of_comments": "āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆāŽ¯ā¯ˆāŽĒā¯ āŽĒā¯†āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", "cant_search_people": "āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸ āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", "cant_search_places": "āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸ āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", - "cleared_jobs": "āŽ‡āŽ¤āŽąā¯āŽ•āŽžāŽŠ āŽ…āŽ´āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩā¯‡āŽ˛ā¯ˆāŽ•āŽŗā¯: {job}", "error_adding_assets_to_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽĒā¯āŽĒāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ", "error_adding_users_to_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽĒā¯āŽĒāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ", "error_deleting_shared_user": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ", @@ -604,7 +588,6 @@ "error_removing_assets_from_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ…āŽ•āŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ•āŽŠā¯āŽšā¯‹āŽ˛ā¯ˆ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "error_selecting_all_assets": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽĒā¯āŽĒāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ", "exclusion_pattern_already_exists": "āŽ‡āŽ¨ā¯āŽ¤ āŽĩāŽŋāŽ˛āŽ•ā¯āŽ•ā¯ āŽŽā¯āŽąā¯ˆ āŽāŽąā¯āŽ•āŽŠāŽĩ❇ āŽ‰āŽŗā¯āŽŗāŽ¤ā¯.", - "failed_job_command": "āŽ•āŽŸā¯āŽŸāŽŗā¯ˆ {āŽ•āŽŸā¯āŽŸāŽŗā¯ˆ the āŽĩā¯‡āŽ˛ā¯ˆāŽ•ā¯āŽ•ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯ā¯āŽąā¯āŽąāŽ¤ā¯: {command}", "failed_to_create_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯", "failed_to_create_shared_link": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯", "failed_to_edit_shared_link": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯", @@ -621,7 +604,6 @@ "paths_validation_failed": "{āŽĒāŽžāŽ¤ā¯ˆāŽ•āŽŗā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽĒāŽžāŽ¤ā¯ˆ} āŽŽāŽąā¯āŽą {# āŽĒāŽžāŽ¤ā¯ˆāŽ•āŽŗā¯}} āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯ā¯āŽąā¯āŽą āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽĒā¯āŽĒ❁", "profile_picture_transparent_pixels": "āŽšā¯āŽ¯āŽĩāŽŋāŽĩāŽ°āŽĒā¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽžāŽŠ āŽĒāŽŸāŽĒā¯āŽĒā¯āŽŗā¯āŽŗāŽŋāŽ•āŽŗā¯ āŽ‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯. āŽ¤āŽ¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ¤ā¯ āŽĒā¯†āŽ°āŽŋāŽ¤āŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯/āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯.", "quota_higher_than_disk_size": "āŽĩāŽŸā¯āŽŸā¯ āŽ…āŽŗāŽĩ❈ āŽĩāŽŋāŽŸ āŽ…āŽ¤āŽŋāŽ•āŽŽāŽžāŽ• āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯āŽŸā¯ˆ āŽ…āŽŽā¯ˆāŽ¤ā¯āŽ¤ā¯āŽŗā¯āŽŗā¯€āŽ°ā¯āŽ•āŽŗā¯", - "repair_unable_to_check_items": "āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯, āŽ’āŽŠā¯āŽąā¯ {item} āŽŽāŽąā¯āŽą {items}}", "unable_to_add_album_users": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_add_assets_to_shared_link": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•ā¯āŽ•ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_add_comment": "āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", @@ -639,7 +621,6 @@ "unable_to_change_visibility": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ¨āŽĒāŽ°ā¯} āŽĒāŽŋāŽą {# āŽŽāŽ•ā¯āŽ•āŽŗā¯}} āŽ•ā¯āŽ•āŽžāŽŠ āŽ¤ā¯†āŽ°āŽŋāŽĩā¯āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯ā¯ˆ āŽŽāŽžāŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_complete_oauth_login": "OAuth āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❈ āŽŽā¯āŽŸāŽŋāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_connect": "āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", - "unable_to_connect_to_server": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤ā¯āŽŸāŽŠā¯ āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_copy_to_clipboard": "āŽ‡āŽŸā¯ˆāŽ¨āŽŋāŽ˛ā¯ˆāŽĒā¯āŽĒāŽ˛āŽ•ā¯ˆāŽ•ā¯āŽ•ā¯ āŽ¨āŽ•āŽ˛ā¯†āŽŸā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯, āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ HTTPS āŽŽā¯‚āŽ˛āŽŽā¯ āŽĒāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ…āŽŖā¯āŽ•ā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗā¯ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋāŽ•ā¯ āŽ•ā¯ŠāŽŗā¯āŽŗā¯āŽ™ā¯āŽ•āŽŗā¯", "unable_to_create_admin_account": "āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ• āŽ•āŽŖāŽ•ā¯āŽ•ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_create_api_key": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ¨āŽŋāŽ‡ āŽĩāŽŋāŽšā¯ˆāŽ¯ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", @@ -663,10 +644,6 @@ "unable_to_hide_person": "āŽ¨āŽĒāŽ°ā¯ˆ āŽŽāŽąā¯ˆāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "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 āŽ‰āŽŸāŽŠā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ¯ āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", @@ -677,11 +654,9 @@ "unable_to_remove_album_users": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ˆ āŽ…āŽ•āŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_remove_api_key": "āŽĒāŽ¨āŽŋāŽ‡ āŽĩāŽŋāŽšā¯ˆāŽ¯ā¯ˆ āŽ…āŽ•āŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "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_resolve_duplicate": "āŽ¨āŽ•āŽ˛ā¯ āŽ¤ā¯€āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_restore_assets": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", @@ -898,14 +873,11 @@ "oauth": "Oauth", "official_immich_resources": "āŽ‰āŽ¤ā¯āŽ¤āŽŋāŽ¯ā¯‹āŽ•āŽĒā¯‚āŽ°ā¯āŽĩ āŽ‡āŽŽā¯āŽŽāŽž āŽĩāŽŗāŽ™ā¯āŽ•āŽŗā¯", "offline": "āŽ‡āŽŖā¯ˆāŽ¯āŽŽāŽŋāŽ˛ā¯āŽ˛āŽžāŽŽāŽ˛ā¯", - "offline_paths": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒāŽŋāŽ˛ā¯āŽ˛āŽžāŽ¤ āŽĒāŽžāŽ¤ā¯ˆāŽ•āŽŗā¯", - "offline_paths_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽŽā¯āŽŸāŽŋāŽĩā¯āŽ•āŽŗā¯ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽĒāŽ•ā¯āŽ¤āŽŋāŽ¯āŽžāŽ• āŽ‡āŽ˛ā¯āŽ˛āŽžāŽ¤ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ•ā¯ˆāŽ¯ā¯‡āŽŸā¯ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽĩāŽ¤āŽŠā¯ āŽ•āŽžāŽ°āŽŖāŽŽāŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯.", "ok": "āŽšāŽ°āŽŋ", "oldest_first": "āŽŽā¯āŽ¤āŽ˛āŽŋāŽ˛ā¯ āŽĒāŽ´āŽŽā¯ˆāŽ¯āŽžāŽŠāŽ¤ā¯", "onboarding": "āŽ†āŽŠā¯ āŽĒā¯‹āŽ°ā¯āŽŸāŽŋāŽ™ā¯", "onboarding_privacy_description": "āŽĒāŽŋāŽŠā¯āŽĩāŽ°ā¯āŽŽā¯ (āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒāŽŋāŽŠāŽžāŽ˛ā¯) āŽ¨āŽąā¯āŽĒā¯ŠāŽ°ā¯āŽ¤ā¯āŽ¤āŽ™ā¯āŽ•āŽŗā¯ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽšā¯‡āŽĩā¯ˆāŽ•āŽŗā¯ˆ āŽ¨āŽŽā¯āŽĒāŽŋāŽ¯ā¯āŽŗā¯āŽŗāŽŠ, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ• āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽŽāŽ¨ā¯āŽ¤ āŽ¨ā¯‡āŽ°āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯āŽŽā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽ˛āŽžāŽŽā¯.", "onboarding_theme_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ‰āŽ¤āŽžāŽ°āŽŖāŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•ā¯ āŽĩāŽŖā¯āŽŖ āŽ•āŽ°ā¯āŽĒā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ•. āŽ‡āŽ¤ā¯ˆ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽĒāŽŋāŽŠā¯āŽŠāŽ°ā¯ āŽŽāŽžāŽąā¯āŽąāŽ˛āŽžāŽŽā¯.", - "onboarding_welcome_description": "āŽšāŽŋāŽ˛ āŽĒā¯ŠāŽ¤ā¯āŽĩāŽžāŽŠ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽŸāŽŠā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽ•āŽ´ā¯āŽĩ❈ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯‹āŽŽā¯.", "onboarding_welcome_user": "āŽĩāŽ°āŽĩā¯‡āŽąā¯āŽ•āŽŋāŽąā¯‹āŽŽā¯, {user}", "online": "āŽ†āŽŠā¯āŽ˛ā¯ˆāŽŠāŽŋāŽ˛ā¯", "only_favorites": "āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽĩ❈ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡", @@ -1238,7 +1210,6 @@ "to_parent": "āŽĒā¯†āŽąā¯āŽąā¯‹āŽ°āŽŋāŽŸāŽŽā¯ āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽ™ā¯āŽ•āŽŗā¯", "to_trash": "āŽ•ā¯āŽĒā¯āŽĒ❈", "toggle_settings": "āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", - "toggle_theme": "āŽ‡āŽ°ā¯āŽŖā¯āŽŸ āŽ•āŽ°ā¯āŽĒā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "total": "āŽŽā¯ŠāŽ¤ā¯āŽ¤āŽŽā¯", "total_usage": "āŽŽā¯ŠāŽ¤ā¯āŽ¤ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯", "trash": "āŽ•ā¯āŽĒā¯āŽĒ❈", @@ -1266,8 +1237,6 @@ "unselect_all_duplicates": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "unstack": "āŽ…āŽŠā¯-āŽšā¯āŽŸāŽžāŽ•ā¯", "unstacked_assets_count": "āŽ…āŽŠā¯-āŽšā¯āŽŸāŽžāŽ•ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", - "untracked_files": "āŽ•āŽŸā¯āŽŸā¯āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸāŽžāŽ¤ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯", - "untracked_files_decription": "āŽ‡āŽ¨ā¯āŽ¤ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸāŽžāŽ˛ā¯ āŽ•āŽŖā¯āŽ•āŽžāŽŖāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ. āŽ…āŽĩ❈ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯ā¯āŽąā¯āŽą āŽ¨āŽ•āŽ°ā¯āŽĩā¯āŽ•āŽŗā¯, āŽ•ā¯āŽąā¯āŽ•ā¯āŽ•āŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ’āŽ°ā¯ āŽĒāŽŋāŽ´ā¯ˆāŽ¯āŽŋāŽŠā¯ āŽ•āŽžāŽ°āŽŖāŽŽāŽžāŽ• āŽĩāŽŋāŽŸā¯āŽŸā¯āŽšā¯āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽŽā¯ āŽŽā¯āŽŸāŽŋāŽĩā¯āŽ•āŽŗāŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯", "up_next": "āŽ…āŽŸā¯āŽ¤ā¯āŽ¤ā¯", "updated_password": "āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯", "upload": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽŽā¯", diff --git a/i18n/te.json b/i18n/te.json index 52b503cc6f..8bd57bc3bc 100644 --- a/i18n/te.json +++ b/i18n/te.json @@ -4,6 +4,7 @@ "account_settings": "ā°–ā°žā°¤ā°ž ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛āą", "acknowledge": "ā°—āąā°°āąā°¤ā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "action": "ā°šā°°āąā°¯", + "action_common_update": "ā°¨ā°ĩāą€ā°•ā°°ā°ŋā°‚ā°šāą", "actions": "ā°šā°°āąā°¯ā°˛āą", "active": "ā°šāąā°°āąā°•āąā°—ā°ž", "activity": "ā°•ā°žā°°āąā°¯ā°žā°šā°°ā°Ŗ", @@ -40,8 +41,6 @@ "backup_keep_last_amount": "ā°‰ā°‚ā°šāąā°•āą‹ā°ĩā°žā°˛āąā°¸ā°ŋā°¨ ā°Žāąā°¨āąā°ĒⰟā°ŋ ā°Ŧāąā°¯ā°žā°•ā°Ēāąâ€Œā°˛ ā°ŽāąŠā°¤āąā°¤ā°‚", "backup_settings": "ā°Ŧāąā°¯ā°žā°•ā°Ēāą ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛āą", "backup_settings_description": "ā°Ąāą‡ā°Ÿā°žā°Ŧāą‡ā°¸āą ā°Ŧāąā°¯ā°žā°•ā°Ēāą ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛ā°¨āą ā°¨ā°ŋā°°āąā°ĩā°šā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", - "check_all": "ā°…ā°¨āąā°¨āą€ ⰤⰍā°ŋā°–āą€ ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ", - "cleanup": "ā°ļāąā°­āąā°°ā°Ēā°°ā°šā°Ąā°‚", "cleared_jobs": "ā°Ļāą€ā°¨ā°ŋ ā°•āą‹ā°¸ā°‚ ā°‰ā°Ļāąā°¯āą‹ā°—ā°žā°˛āą ā°•āąā°˛ā°ŋā°¯ā°°āą ā°šāą‡ā°¯ā°Ŧā°Ąāąā°Ąā°žā°¯ā°ŋ: {job}", "config_set_by_file": "ā°•ā°žā°¨āąā°Ģā°ŋā°—ā°°āą‡ā°ˇā°¨āą ā°Ēāąā°°ā°¸āąā°¤āąā°¤ā°‚ ā°•ā°žā°¨āąā°Ģā°ŋā°—ā°°āą‡ā°ˇā°¨āą ā°Ģāąˆā°˛āą ā°Ļāąā°ĩā°žā°°ā°ž ā°¸āą†ā°Ÿāą ā°šāą‡ā°¯ā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", "confirm_delete_library": "ā°Žāą€ā°°āą ā°–ā°šāąā°šā°ŋā°¤ā°‚ā°—ā°ž {library} ā°˛āąˆā°Ŧāąā°°ā°°āą€ā°¨ā°ŋ ā°¤āąŠā°˛ā°—ā°ŋā°‚ā°šā°žā°˛ā°¨āąā°•āąā°‚ā°Ÿāąā°¨āąā°¨ā°žā°°ā°ž?", @@ -56,14 +55,12 @@ "disable_login": "ā°˛ā°žā°—ā°ŋā°¨āąâ€Œā°¨āą ā°¨ā°ŋā°˛ā°ŋā°Ēā°ŋā°ĩāą‡ā°¯ā°‚ā°Ąā°ŋ", "duplicate_detection_job_description": "ā°¸ā°žā°°āą‚ā°Ēāąā°¯ Ⱊā°ŋā°¤āąā°°ā°žā°˛ā°¨āą ā°—āąā°°āąā°¤ā°ŋā°‚ā°šā°Ąā°žā°¨ā°ŋā°•ā°ŋ ā°†ā°¸āąā°¤āąā°˛ā°Ēāąˆ ā°¯ā°‚ā°¤āąā°° ā°…ā°­āąā°¯ā°žā°¸ā°žā°¨āąā°¨ā°ŋ ā°…ā°Žā°˛āą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ. ā°¸āąā°Žā°žā°°āąā°Ÿāą ā°ļāą‹ā°§ā°¨ā°Ēāąˆ ā°†ā°§ā°žā°°ā°Ēā°Ąāąā°¤āąā°‚ā°Ļā°ŋ", "exclusion_pattern_description": "ā°Žā°ŋā°¨ā°šā°žā°¯ā°ŋā°‚ā°Ēāą ā°¨ā°Žāą‚ā°¨ā°žā°˛āą ā°Žāą€ ā°˛āąˆā°Ŧāąā°°ā°°āą€ā°¨ā°ŋ ā°¸āąā°•ā°žā°¨āą ā°šāą‡ā°¸āąā°¤āąā°¨āąā°¨ā°Ēāąā°Ēāąā°Ąāą ā°Ģāąˆā°˛āąâ€Œā°˛āą ā°Žā°°ā°ŋā°¯āą ā°Ģāą‹ā°˛āąā°Ąā°°āąâ€Œā°˛ā°¨āą ā°ĩā°ŋā°¸āąā°Žā°°ā°ŋā°‚ā°šā°Ąā°žā°¨ā°ŋā°•ā°ŋ ā°Žā°ŋā°Žāąā°Žā°˛āąā°¨ā°ŋ ā°…ā°¨āąā°Žā°¤ā°ŋā°¸āąā°¤ā°žā°¯ā°ŋ. ā°Žāą€ā°°āą ā°Ļā°ŋā°—āąā°Žā°¤ā°ŋ ā°šāą‡ā°¯ā°•āą‚ā°Ąā°Ļā°¨āąā°•āąā°¨āą‡ RAW ā°Ģāąˆā°˛āąâ€Œā°˛āą ā°ĩā°‚ā°Ÿā°ŋ ā°Ģāą‹ā°˛āąā°Ąā°°āąâ€Œā°˛ā°¨āą ā°•ā°˛ā°ŋā°—ā°ŋ ā°‰ā°¨āąā°¨ā°Ÿāąā°˛ā°¯ā°ŋā°¤āą‡ ā°‡ā°Ļā°ŋ ā°‰ā°Ēā°¯āą‹ā°—ā°•ā°°ā°‚ā°—ā°ž ā°‰ā°‚ā°Ÿāąā°‚ā°Ļā°ŋ.", - "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_prefer_embedded_preview": "ā°ĒāąŠā°‚ā°Ļāąā°Ēā°°ā°ŋⰚā°ŋā°¨ ā°Ēā°°ā°ŋā°Ļāąƒā°ļāąā°¯ā°žā°¨ā°ŋā°•ā°ŋ ā°Ēāąā°°ā°žā°§ā°žā°¨āąā°¯ā°¤ ā°‡ā°ĩāąā°ĩā°‚ā°Ąā°ŋ", @@ -186,7 +183,7 @@ "oauth_enable_description": "OAuth ā°¤āą‹ ā°˛ā°žā°—ā°ŋā°¨āą ā°…ā°ĩāąā°ĩā°‚ā°Ąā°ŋ", "oauth_mobile_redirect_uri": "ā°ŽāąŠā°Ŧāąˆā°˛āą ā°Ļā°žā°°ā°ŋā°Žā°žā°°āąā°Ēāą URI", "oauth_mobile_redirect_uri_override": "ā°ŽāąŠā°Ŧāąˆā°˛āą ā°Ļā°žā°°ā°ŋā°Žā°žā°°āąā°Ēāą URI ā°“ā°ĩā°°āąâ€Œā°°āąˆā°Ąāą", - "oauth_mobile_redirect_uri_override_description": "OAuth ā°Ēāąā°°āąŠā°ĩāąˆā°Ąā°°āą '{callback}'ā°ĩā°‚ā°Ÿā°ŋ ā°ŽāąŠā°Ŧāąˆā°˛āą URIā°¨ā°ŋ ā°…ā°¨āąā°Žā°¤ā°ŋā°‚ā°šā°¨ā°Ēāąā°Ēāąā°Ąāą ā°Ēāąā°°ā°žā°°ā°‚ā°­ā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", + "oauth_mobile_redirect_uri_override_description": "OAuth ā°Ēāąā°°āąŠā°ĩāąˆā°Ąā°°āą ''{callback}''ā°ĩā°‚ā°Ÿā°ŋ ā°ŽāąŠā°Ŧāąˆā°˛āą URIā°¨ā°ŋ ā°…ā°¨āąā°Žā°¤ā°ŋā°‚ā°šā°¨ā°Ēāąā°Ēāąā°Ąāą ā°Ēāąā°°ā°žā°°ā°‚ā°­ā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth ā°˛ā°žā°—ā°ŋā°¨āą ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛ā°¨āą ā°¨ā°ŋā°°āąā°ĩā°šā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "oauth_settings_more_details": "Ⰸ ā°Ģāą€ā°šā°°āą ā°—āąā°°ā°ŋā°‚ā°šā°ŋ ā°Žā°°ā°ŋā°¨āąā°¨ā°ŋ ā°ĩā°ŋā°ĩā°°ā°žā°˛ ā°•āą‹ā°¸ā°‚, ā°Ąā°žā°•āąā°¸āą ā°šāą‚ā°Ąā°‚ā°Ąā°ŋ.", @@ -196,8 +193,6 @@ "oauth_storage_quota_claim_description": "Ⰸ ā°•āąā°˛āą†ā°¯ā°ŋā°Žāą ā°ĩā°ŋā°˛āąā°ĩā°•āą ā°¯āą‚ā°œā°°āą ā°¨ā°ŋā°˛āąā°ĩ ā°•āą‹ā°Ÿā°žā°¨āą ā°†ā°Ÿāą‹ā°Žāą‡ā°Ÿā°ŋā°•āąâ€Œā°—ā°ž ā°¸āą†ā°Ÿāą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ.", "oauth_storage_quota_default": "ā°Ąā°ŋā°Ģā°žā°˛āąā°Ÿāą ā°¨ā°ŋā°˛āąā°ĩ ā°•āą‹ā°Ÿā°ž (GiB)", "oauth_storage_quota_default_description": "ā°•āąā°˛āą†ā°¯ā°ŋā°Žāą ā°…ā°‚ā°Ļā°ŋā°‚ā°šā°¨ā°Ēāąā°Ēāąā°Ąāą GiBā°˛āą‹ā°¨ā°ŋ ā°•āą‹ā°Ÿā°ž ā°‰ā°Ēā°¯āą‹ā°—ā°ŋā°‚ā°šā°Ŧā°Ąāąā°¤āąā°‚ā°Ļā°ŋ (ā°…ā°Ēā°°ā°ŋā°Žā°ŋā°¤ ā°•āą‹ā°Ÿā°ž ā°•āą‹ā°¸ā°‚ 0 ā°¨ā°Žāą‹ā°Ļāą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ).", - "offline_paths": "ā°†ā°Ģāąâ€Œā°˛āąˆā°¨āą ā°Žā°žā°°āąā°—ā°žā°˛āą", - "offline_paths_description": "ā°Ŧā°žā°šāąā°¯ ā°˛āąˆā°Ŧāąā°°ā°°āą€ā°˛āą‹ ā°­ā°žā°—ā°‚ ā°•ā°žā°¨ā°ŋ ā°Ģāąˆā°ŗāąā°ŗā°¨āą ā°Žā°žā°¨āąā°¯āąā°ĩā°˛āąâ€Œā°—ā°ž ā°¤āąŠā°˛ā°—ā°ŋā°‚ā°šā°Ąā°‚ ā°ĩā°˛āąā°˛ Ⰸ ā°Ģā°˛ā°ŋā°¤ā°žā°˛āą ā°ĩā°šāąā°šā°ŋ ā°‰ā°‚ā°Ąā°ĩā°šāąā°šāą.", "password_enable_description": "ā°‡ā°Žāą†ā°¯ā°ŋā°˛āą ā°Žā°°ā°ŋā°¯āą ā°Ēā°žā°¸āąâ€Œā°ĩā°°āąā°Ąāąâ€Œā°¤āą‹ ā°˛ā°žā°—ā°ŋā°¨āą ā°…ā°ĩāąā°ĩā°‚ā°Ąā°ŋ", "password_settings": "ā°Ēā°žā°¸āąâ€Œā°ĩā°°āąā°Ąāą ā°˛ā°žā°—ā°ŋā°¨āą", "password_settings_description": "ā°Ēā°žā°¸āąâ€Œā°ĩā°°āąā°Ąāą ā°˛ā°žā°—ā°ŋā°¨āą ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛ā°¨āą ā°¨ā°ŋā°°āąā°ĩā°šā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", @@ -207,9 +202,6 @@ "refreshing_all_libraries": "ā°…ā°¨āąā°¨ā°ŋ ā°˛āąˆā°Ŧāąā°°ā°°āą€ā°˛ā°¨āą ā°°ā°ŋā°Ģāąā°°āą†ā°ˇāą ā°šāą‡ā°¸āąā°¤āą‹ā°‚ā°Ļā°ŋ", "registration": "ā°¨ā°ŋā°°āąā°ĩā°žā°šā°•āąā°˛ ā°¨ā°Žāą‹ā°Ļāą", "registration_description": "ā°Žāą€ā°°āą ā°¸ā°ŋā°¸āąā°Ÿā°Žāąâ€Œā°˛āą‹ ā°ŽāąŠā°ĻⰟā°ŋ ā°ĩā°ŋā°¨ā°ŋā°¯āą‹ā°—ā°Ļā°žā°°āą ā°•ā°žā°Ŧā°Ÿāąā°Ÿā°ŋ, ā°Žāą€ā°°āą ā°¨ā°ŋā°°āąā°ĩā°žā°šā°•āąā°Ąā°ŋā°—ā°ž ā°¨ā°ŋā°¯ā°Žā°ŋā°‚ā°šā°Ŧā°Ąā°¤ā°žā°°āą ā°Žā°°ā°ŋā°¯āą ā°¨ā°ŋā°°āąā°ĩā°žā°šā°• ā°Ēā°¨āąā°˛ā°•āą ā°Ŧā°žā°§āąā°¯ā°¤ ā°ĩā°šā°ŋā°¸āąā°¤ā°žā°°āą ā°Žā°°ā°ŋā°¯āą ā°…ā°Ļā°¨ā°Ēāą ā°ĩā°ŋā°¨ā°ŋā°¯āą‹ā°—ā°Ļā°žā°°āąā°˛ā°¨āą ā°Žāą€ā°°āą‡ ā°¸āąƒā°ˇāąā°Ÿā°ŋā°¸āąā°¤ā°žā°°āą.", - "repair_all": "ā°…ā°¨āąā°¨āą€ ā°Ŧā°žā°—āą ā°šāą‡ā°¯ā°ŋ", - "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": "ā°‡ā°Ÿāą€ā°ĩā°˛ ā°¸āą‡ā°ĩāą ā°šāą‡ā°¸ā°ŋā°¨ ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛ā°•āą ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛ā°¨āą ā°°āą€ā°¸āą†ā°Ÿāą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ", @@ -238,7 +230,6 @@ "storage_template_migration_info": "ā°¨ā°ŋā°˛āąā°ĩ ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāą ā°…ā°¨āąā°¨ā°ŋ ā°ĒāąŠā°Ąā°ŋā°—ā°ŋā°‚ā°Ēāąā°˛ā°¨āą Ⱊā°ŋā°¨āąā°¨ ā°…ā°•āąā°ˇā°°ā°žā°˛ā°•āą ā°Žā°žā°°āąā°¸āąā°¤āąā°‚ā°Ļā°ŋ. ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāą ā°Žā°žā°°āąā°Ēāąā°˛āą ā°•āąŠā°¤āąā°¤ ā°†ā°¸āąā°¤āąā°˛ā°•āą ā°Žā°žā°¤āąā°°ā°Žāą‡ ā°ĩā°°āąā°¤ā°ŋā°¸āąā°¤ā°žā°¯ā°ŋ. ā°—ā°¤ā°‚ā°˛āą‹ ā°…ā°Ēāąâ€Œā°˛āą‹ā°Ąāą ā°šāą‡ā°¸ā°ŋā°¨ ā°†ā°¸āąā°¤āąā°˛ā°•āą ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāąâ€Œā°¨āą ā°Žāąā°¨āąā°Ēāą ā°ĩā°°āąā°¤ā°ŋā°‚ā°Ēā°œāą‡ā°¯ā°Ąā°žā°¨ā°ŋā°•ā°ŋ, {job}ā°¨āą ā°…ā°Žā°˛āą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ.", "storage_template_migration_job": "ā°¨ā°ŋā°˛āąā°ĩ ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāą ā°Žāąˆā°—āąā°°āą‡ā°ˇā°¨āą ā°œā°žā°Ŧāą", "storage_template_more_details": "Ⰸ ā°Ģāą€ā°šā°°āą ā°—āąā°°ā°ŋā°‚ā°šā°ŋ ā°Žā°°ā°ŋā°¨āąā°¨ā°ŋ ā°ĩā°ŋā°ĩā°°ā°žā°˛ ā°•āą‹ā°¸ā°‚, storage template ā°Žā°°ā°ŋā°¯āą ā°Ļā°žā°¨ā°ŋ implications ā°šāą‚ā°Ąā°‚ā°Ąā°ŋ", - "storage_template_onboarding_description": "ā°Ēāąā°°ā°žā°°ā°‚ā°­ā°ŋā°‚ā°šā°Ŧā°Ąā°ŋā°¨ā°Ēāąā°Ēāąā°Ąāą, Ⰸ ā°˛ā°•āąā°ˇā°Ŗā°‚ ā°ĩā°ŋā°¨ā°ŋā°¯āą‹ā°—ā°Ļā°žā°°āą ā°¨ā°ŋā°°āąā°ĩⰚā°ŋā°‚ā°šā°ŋā°¨ ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāą ā°†ā°§ā°žā°°ā°‚ā°—ā°ž ā°Ģāąˆā°ŗāąā°ŗā°¨āą ā°¸āąā°ĩā°¯ā°‚ā°šā°žā°˛ā°•ā°‚ā°—ā°ž ā°¨ā°ŋā°°āąā°ĩā°šā°ŋā°¸āąā°¤āąā°‚ā°Ļā°ŋ. ā°¸āąā°Ĩā°ŋā°°ā°¤āąā°ĩ ā°¸ā°Žā°¸āąā°¯ā°˛ ā°•ā°žā°°ā°Ŗā°‚ā°—ā°ž Ⰸ ā°˛ā°•āąā°ˇā°Ŗā°‚ ā°Ąā°ŋā°Ģā°žā°˛āąā°Ÿāąâ€Œā°—ā°ž ā°†ā°Ēā°ŋā°ĩāą‡ā°¯ā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ. ā°Žā°°ā°ŋā°¨āąā°¨ā°ŋ ā°ĩā°ŋā°ĩā°°ā°žā°˛ ā°•āą‹ā°¸ā°‚, ā°Ļā°¯ā°šāą‡ā°¸ā°ŋ documentation ā°šāą‚ā°Ąā°‚ā°Ąā°ŋ.", "storage_template_path_length": "ā°¸āąā°Žā°žā°°āą ā°Ēā°žā°¤āą ā°ĒāąŠā°Ąā°ĩāą ā°Ēā°°ā°ŋā°Žā°ŋā°¤ā°ŋ: {length, number}/{limit, number}", "storage_template_settings": "ā°¨ā°ŋā°˛āąā°ĩ ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāą", "storage_template_settings_description": "ā°…ā°Ēāąâ€Œā°˛āą‹ā°Ąāą ā°†ā°¸āąā°¤ā°ŋ ā°¯āąŠā°•āąā°• ā°Ģāą‹ā°˛āąā°Ąā°°āą ā°¨ā°ŋā°°āąā°Žā°žā°Ŗā°‚ ā°Žā°°ā°ŋā°¯āą ā°Ģāąˆā°˛āą ā°Ēāą‡ā°°āąā°¨āą ā°¨ā°ŋā°°āąā°ĩā°šā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", @@ -250,7 +241,6 @@ "template_email_invite_album": "ā°†ā°˛āąā°Ŧā°Žāą ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāąâ€Œā°¨āą ā°†ā°šāąā°ĩā°žā°¨ā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "template_email_preview": "ā°Ēāąā°°ā°ŋā°ĩāąā°¯āą‚", "template_email_settings": "ā°‡ā°Žāą†ā°¯ā°ŋā°˛āą ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāąâ€Œā°˛āą", - "template_email_settings_description": "ā°…ā°¨āąā°•āą‚ā°˛ ā°‡ā°Žāą†ā°¯ā°ŋā°˛āą ā°¨āą‹ā°Ÿā°ŋā°Ģā°ŋā°•āą‡ā°ˇā°¨āą ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāąâ€Œā°˛ā°¨āą ā°¨ā°ŋā°°āąā°ĩā°šā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "template_email_update_album": "ā°†ā°˛āąā°Ŧā°Žāą ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāąâ€Œā°¨āą ā°¨ā°ĩāą€ā°•ā°°ā°ŋā°‚ā°šāą", "template_email_welcome": "ā°¸āąā°ĩā°žā°—ā°¤ ā°‡ā°Žāą†ā°¯ā°ŋā°˛āą ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāą", "template_settings": "ā°¨āą‹ā°Ÿā°ŋā°Ģā°ŋā°•āą‡ā°ˇā°¨āą ā°Ÿāą†ā°‚ā°Ēāąā°˛āą‡ā°Ÿāąâ€Œā°˛āą", @@ -259,7 +249,6 @@ "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": "ā°¤āąā°ĩā°°ā°Ŗā°‚ API", @@ -290,7 +279,6 @@ "transcoding_hardware_acceleration_description": "ā°Ēāąā°°ā°¯āą‹ā°—ā°žā°¤āąā°Žā°•ā°‚; ā°šā°žā°˛ā°ž ā°ĩāą‡ā°—ā°‚ā°—ā°ž ā°‰ā°‚ā°Ÿāąā°‚ā°Ļā°ŋ, ā°•ā°žā°¨āą€ ā°…ā°Ļāą‡ ā°Ŧā°ŋā°Ÿāąā°°āą‡ā°Ÿāą ā°ĩā°Ļāąā°Ļ ā°¤ā°•āąā°•āąā°ĩ ā°¨ā°žā°Ŗāąā°¯ā°¤ā°¨āą ā°•ā°˛ā°ŋā°—ā°ŋ ā°‰ā°‚ā°Ÿāąā°‚ā°Ļā°ŋ", "transcoding_hardware_decoding": "ā°šā°žā°°āąā°Ąāąâ€Œā°ĩāą‡ā°°āą ā°Ąāą€ā°•āą‹ā°Ąā°ŋā°‚ā°—āą", "transcoding_hardware_decoding_setting_description": "ā°•āą‡ā°ĩⰞⰂ ā°¯ā°žā°•āąā°¸ā°ŋā°˛ā°°āą‡ā°Ÿā°ŋā°‚ā°—āą ā°Žā°¨āąâ€Œā°•āą‹ā°Ąā°ŋā°‚ā°—āąâ€Œā°•āą ā°Ŧā°Ļāąā°˛āąā°—ā°ž ā°Žā°‚ā°Ąāą-ā°Ÿāą-ā°Žā°‚ā°Ąāą ā°¯ā°žā°•āąā°¸ā°ŋā°˛ā°°āą‡ā°ˇā°¨āąâ€Œā°¨āą ā°Ēāąā°°ā°žā°°ā°‚ā°­ā°ŋā°¸āąā°¤āąā°‚ā°Ļā°ŋ. ā°…ā°¨āąā°¨ā°ŋ ā°ĩāą€ā°Ąā°ŋā°¯āą‹ā°˛ā°˛āą‹ ā°Ēā°¨ā°ŋ ā°šāą‡ā°¯ā°•ā°Ēāą‹ā°ĩā°šāąā°šāą.", - "transcoding_hevc_codec": "HEVC ā°•āą‹ā°Ąāą†ā°•āą", "transcoding_max_b_frames": "ā°—ā°°ā°ŋā°ˇāąā°Ÿ B-ā°Ģāąā°°āą‡ā°Žāąâ€Œā°˛āą", "transcoding_max_b_frames_description": "ā°…ā°§ā°ŋā°• ā°ĩā°ŋā°˛āąā°ĩā°˛āą ā°•āąā°Ļā°ŋā°‚ā°Ēāą ā°¸ā°žā°Žā°°āąā°Ĩāąā°¯ā°žā°¨āąā°¨ā°ŋ ā°Žāą†ā°°āąā°—āąā°Ēā°°āąā°¸āąā°¤ā°žā°¯ā°ŋ, ā°•ā°žā°¨āą€ ā°Žā°¨āąâ€Œā°•āą‹ā°Ąā°ŋā°‚ā°—āąâ€Œā°¨āą ā°¨āą†ā°Žāąā°Žā°Ļā°ŋā°¸āąā°¤ā°žā°¯ā°ŋ. ā°Ēā°žā°¤ ā°Ēā°°ā°ŋā°•ā°°ā°žā°˛āąā°˛āą‹ ā°šā°žā°°āąā°Ąāąâ€Œā°ĩāą‡ā°°āą ā°¤āąā°ĩā°°ā°Ŗā°‚ā°¤āą‹ ā°…ā°¨āąā°•āą‚ā°˛ā°‚ā°—ā°ž ā°‰ā°‚ā°Ąā°•ā°Ēāą‹ā°ĩā°šāąā°šāą. 0 B-ā°Ģāąā°°āą‡ā°Žāąâ€Œā°˛ā°¨āą ā°¨ā°ŋā°˛ā°ŋā°Ēā°ŋā°ĩāą‡ā°¸āąā°¤āąā°‚ā°Ļā°ŋ, ā°…ā°¯ā°ŋā°¤āą‡ -1 Ⰸ ā°ĩā°ŋā°˛āąā°ĩā°¨āą ā°¸āąā°ĩā°¯ā°‚ā°šā°žā°˛ā°•ā°‚ā°—ā°ž ā°¸āą†ā°Ÿāą ā°šāą‡ā°¸āąā°¤āąā°‚ā°Ļā°ŋ.", "transcoding_max_bitrate": "ā°—ā°°ā°ŋā°ˇāąā°Ÿ ā°Ŧā°ŋā°Ÿāąā°°āą‡ā°Ÿāą", @@ -328,8 +316,6 @@ "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 {# day} other {# days}} ā°˛āą‹ ā°ļā°žā°ļāąā°ĩā°¤ ā°¤āąŠā°˛ā°—ā°ŋā°‚ā°Ēāąā°•āą ā°ˇāą†ā°Ąāąā°¯āą‚ā°˛āą ā°šāą‡ā°¯ā°Ŧā°Ąā°¤ā°žā°¯ā°ŋ.", "user_delete_delay_settings": "ā°†ā°˛ā°¸āąā°¯ā°žā°¨āąā°¨ā°ŋ ā°¤āąŠā°˛ā°—ā°ŋā°‚ā°šāą", @@ -456,7 +442,6 @@ "change_password_description": "ā°‡ā°Ļāą‡ ā°Žāą€ā°°āą ā°ĩāąā°¯ā°ĩā°¸āąā°Ĩā°˛āą‹ā°•ā°ŋ ā°ŽāąŠā°Ÿāąā°Ÿā°ŽāąŠā°ĻⰟā°ŋā°¸ā°žā°°ā°ŋ ā°¸āąˆā°¨āąā°‡ā°¨āą ā°šāą‡ā°¯ā°Ąā°‚, ā°˛āą‡ā°Ļā°ž ā°Žāą€ ā°Ēā°žā°¸āąā°ĩā°°āąā°Ąāą ā°Žā°žā°°āąā°šāą‡ ā°…ā°­āąā°¯ā°°āąā°§ā°¨ ā°šāą‡ā°¯ā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ. ā°Ļā°¯ā°šāą‡ā°¸ā°ŋ ā°•āąŠā°¤āąā°¤ ā°Ēā°žā°¸āąā°ĩā°°āąā°Ąāą ā°•ā°ŋā°‚ā°Ļ ā°‡ā°ĩāąā°ĩā°‚ā°Ąā°ŋ.", "change_your_password": "ā°Žāą€ ā°Ēā°žā°¸āąā°ĩā°°āąā°Ąāąâ€Œā°¨āą ā°Žā°žā°°āąā°šā°‚ā°Ąā°ŋ", "changed_visibility_successfully": "ā°Ļāąƒā°ļāąā°¯ā°Žā°žā°¨ā°¤ ā°ĩā°ŋⰜⰝā°ĩā°‚ā°¤ā°‚ā°—ā°ž ā°Žā°žā°°āąā°šā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", - "check_all": "ā°…ā°¨āąā°¨ā°ŋā°‚ā°Ÿā°ŋā°¨ā°ŋ ⰤⰍā°ŋā°–āą€ ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ", "check_logs": "ā°˛ā°žā°—āąâ€Œā°˛ā°¨āą ⰤⰍā°ŋā°–āą€ ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ", "choose_matching_people_to_merge": "ā°ĩā°ŋā°˛āą€ā°¨ā°‚ ā°šāą‡ā°¯ā°Ąā°žā°¨ā°ŋā°•ā°ŋ Ⰿⰰā°ŋā°Ēāą‹ā°˛ā°ŋā°¨ ā°ĩāąā°¯ā°•āąā°¤āąā°˛ā°¨āą ā°Žā°‚ā°šāąā°•āą‹ā°‚ā°Ąā°ŋ", "city": "ⰍⰗⰰⰂ", @@ -612,7 +597,6 @@ "cant_get_number_of_comments": "ā°ĩāąā°¯ā°žā°–āąā°¯ā°˛ ā°¸ā°‚ā°–āąā°¯ā°¨āą ā°ĒāąŠā°‚ā°Ļā°˛āą‡ā°•ā°Ēāą‹ā°¤āąā°¨āąā°¨ā°žā°¨āą", "cant_search_people": "ā°ĩāąā°¯ā°•āąā°¤āąā°˛ā°¨āą ā°ļāą‹ā°§ā°ŋā°‚ā°šā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°Ļāą", "cant_search_places": "ā°¸āąā°Ĩā°˛ā°žā°˛ā°¨āą ā°ļāą‹ā°§ā°ŋā°‚ā°šā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", - "cleared_jobs": "{job} ā°•āą‹ā°¸ā°‚ ā°‰ā°Ļāąā°¯āą‹ā°—ā°žā°˛ā°¨āą ā°¤āąŠā°˛ā°—ā°ŋā°‚ā°šā°žā°°āą", "error_adding_assets_to_album": "ā°†ā°˛āąā°Ŧā°Žāąâ€Œā°•āą ā°†ā°¸āąā°¤āąā°˛ā°¨āą ā°œāą‹ā°Ąā°ŋā°‚ā°šā°Ąā°‚ā°˛āą‹ ā°˛āą‹ā°Ēā°‚ ā°ā°°āąā°Ēā°Ąā°ŋā°‚ā°Ļā°ŋ", "error_adding_users_to_album": "ā°†ā°˛āąā°Ŧā°Žāąâ€Œā°•āą ā°ĩā°ŋā°¨ā°ŋā°¯āą‹ā°—ā°Ļā°žā°°āąā°˛ā°¨āą ā°œāą‹ā°Ąā°ŋā°‚ā°šā°Ąā°‚ā°˛āą‹ ā°˛āą‹ā°Ēā°‚ ā°ā°°āąā°Ēā°Ąā°ŋā°‚ā°Ļā°ŋ", "error_deleting_shared_user": "ā°ˇāą‡ā°°āąā°Ąāą ā°¯āą‚ā°œā°°āąâ€Œā°¨āą ā°¤āąŠā°˛ā°—ā°ŋā°‚ā°šā°Ąā°‚ā°˛āą‹ ā°Žā°°āąā°°ā°°āą ā°ā°°āąā°Ēā°Ąā°ŋā°‚ā°Ļā°ŋ", @@ -621,7 +605,6 @@ "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": "ā°ˇāą‡ā°°āą ā°šāą‡ā°¯ā°žā°˛āąā°¸ā°ŋā°¨ ā°˛ā°ŋā°‚ā°•āąâ€Œā°¨āą ā°¸ā°ĩā°°ā°ŋā°‚ā°šā°Ąā°‚ā°˛āą‹ ā°ĩā°ŋā°Ģā°˛ā°Žāąˆā°‚ā°Ļā°ŋ", @@ -638,7 +621,6 @@ "paths_validation_failed": "{paths, plural, one {# path} other {# paths}} ā°§āąā°°āąā°ĩāą€ā°•ā°°ā°Ŗ ā°ĩā°ŋā°Ģā°˛ā°Žāąˆā°‚ā°Ļā°ŋ", "profile_picture_transparent_pixels": "ā°Ēāąā°°āąŠā°Ģāąˆā°˛āą Ⱊā°ŋā°¤āąā°°ā°žā°˛ā°˛āą‹ ā°Ēā°žā°°ā°Ļā°°āąā°ļā°• ā°Ēā°ŋā°•āąā°¸āą†ā°˛āąâ€Œā°˛āą ā°‰ā°‚ā°Ąā°•āą‚ā°Ąā°Ļāą. ā°Ļā°¯ā°šāą‡ā°¸ā°ŋ Ⱊā°ŋā°¤āąā°°ā°žā°¨āąā°¨ā°ŋ ā°œāą‚ā°Žāą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ ā°Žā°°ā°ŋā°¯āą/ā°˛āą‡ā°Ļā°ž ⰤⰰⰞā°ŋā°‚ā°šā°‚ā°Ąā°ŋ.", "quota_higher_than_disk_size": "ā°Žāą€ā°°āą ā°Ąā°ŋā°¸āąā°•āą ā°Ēā°°ā°ŋā°Žā°žā°Ŗā°‚ ā°•ā°‚ā°Ÿāą‡ ā°Žā°•āąā°•āąā°ĩ ā°•āą‹ā°Ÿā°žā°¨āą ā°¸āą†ā°Ÿāą ā°šāą‡ā°¸ā°žā°°āą", - "repair_unable_to_check_items": "{count, select, one {item} other {items}} ā°¨ā°ŋ ā°šāą†ā°•āą ā°šāą‡ā°¯ā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", "unable_to_add_album_users": "ā°†ā°˛āąā°Ŧā°Žāąâ€Œā°•āą ā°ĩā°ŋā°¨ā°ŋā°¯āą‹ā°—ā°Ļā°žā°°āąā°˛ā°¨āą ā°œāą‹ā°Ąā°ŋā°‚ā°šā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", "unable_to_add_assets_to_shared_link": "ā°ˇāą‡ā°°āą ā°šāą‡ā°¸ā°ŋā°¨ ā°˛ā°ŋā°‚ā°•āąâ€Œā°•āą ā°†ā°¸āąā°¤āąā°˛ā°¨āą ā°œāą‹ā°Ąā°ŋā°‚ā°šā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", "unable_to_add_comment": "ā°ĩāąā°¯ā°žā°–āąā°¯ā°¨āą ā°œāą‹ā°Ąā°ŋā°‚ā°šā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", @@ -656,7 +638,6 @@ "unable_to_change_visibility": "{count, plural, one {# person} other {# people}} ā°•ā°ŋ ā°Ļāąƒā°ļāąā°¯ā°Žā°žā°¨ā°¤ā°¨āą ā°Žā°žā°°āąā°šā°˛āą‡ā°•ā°Ēāą‹ā°¯ā°žā°Žāą", "unable_to_complete_oauth_login": "OAuth ā°˛ā°žā°—ā°ŋā°¨āąâ€Œā°¨āą ā°Ēāą‚ā°°āąā°¤ā°ŋ ā°šāą‡ā°¯ā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", "unable_to_connect": "ā°•ā°¨āą†ā°•āąā°Ÿāą ā°šāą‡ā°¯ā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", - "unable_to_connect_to_server": "ā°¸ā°°āąā°ĩā°°āąâ€Œā°•āą ā°•ā°¨āą†ā°•āąā°Ÿāą ā°šāą‡ā°¯ā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", "unable_to_copy_to_clipboard": "ā°•āąā°˛ā°ŋā°Ēāąâ€Œā°Ŧāą‹ā°°āąā°Ąāąâ€Œā°•āą ā°•ā°žā°Ēāą€ ā°šāą‡ā°¯ā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°Ļāą, ā°Žāą€ā°°āą https ā°Ļāąā°ĩā°žā°°ā°ž ā°Ēāą‡ā°œāą€ā°¨ā°ŋ ā°¯ā°žā°•āąā°¸āą†ā°¸āą ā°šāą‡ā°¸āąā°¤āąā°¨āąā°¨ā°žā°°ā°¨ā°ŋ ā°¨ā°ŋā°°āąā°§ā°žā°°ā°ŋā°‚ā°šāąā°•āą‹ā°‚ā°Ąā°ŋ", "unable_to_create_admin_account": "ā°¨ā°ŋā°°āąā°ĩā°žā°šā°• ā°–ā°žā°¤ā°žā°¨āą ā°¸āąƒā°ˇāąā°Ÿā°ŋā°‚ā°šā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", "unable_to_create_api_key": "ā°•āąŠā°¤āąā°¤ API ā°•āą€ā°¨ā°ŋ ā°¸āąƒā°ˇāąā°Ÿā°ŋā°‚ā°šā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", @@ -680,10 +661,6 @@ "unable_to_hide_person": "ā°ĩāąā°¯ā°•āąā°¤ā°ŋā°¨ā°ŋ ā°Ļā°žā°šā°˛āą‡ā°•ā°Ēāą‹ā°¯ā°žā°°āą", "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 ā°¤āą‹ ā°˛ā°žā°—ā°ŋā°¨āą ā°…ā°ĩāąā°ĩā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", @@ -694,11 +671,9 @@ "unable_to_remove_album_users": "ā°†ā°˛āąā°Ŧā°Žāą ā°¨āąā°‚ā°Ąā°ŋ ā°ĩā°ŋā°¨ā°ŋā°¯āą‹ā°—ā°Ļā°žā°°āąā°˛ā°¨āą ā°¤āą€ā°¸ā°ŋā°ĩāą‡ā°¯ā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", "unable_to_remove_api_key": "API ā°•āą€ā°¨ā°ŋ ā°¤āą€ā°¸ā°ŋā°ĩāą‡ā°¯ā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", "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_resolve_duplicate": "ⰍⰕā°ŋā°˛āą€ā°¨ā°ŋ ā°Ēā°°ā°ŋā°ˇāąā°•ā°°ā°ŋā°‚ā°šā°˛āą‡ā°•ā°Ēāą‹ā°¯ā°ŋā°‚ā°Ļā°ŋ", "unable_to_restore_assets": "ā°†ā°¸āąā°¤āąā°˛ā°¨āą ā°Ēāąā°¨ā°°āąā°Ļāąā°§ā°°ā°ŋā°‚ā°šā°Ąā°‚ ā°¸ā°žā°§āąā°¯ā°‚ ā°•ā°žā°˛āą‡ā°Ļāą", @@ -921,14 +896,11 @@ "notifications_setting_description": "ā°¨āą‹ā°Ÿā°ŋā°Ģā°ŋā°•āą‡ā°ˇā°¨āąâ€Œā°˛ā°¨āą ā°¨ā°ŋā°°āąā°ĩā°šā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "official_immich_resources": "ā°…ā°§ā°ŋā°•ā°žā°°ā°ŋā°• ā°‡ā°Žāąā°Žā°ŋā°šāą ā°ĩā°¨ā°°āąā°˛āą", "offline": "ā°†ā°Ģāąâ€Œā°˛āąˆā°¨āą", - "offline_paths": "ā°†ā°Ģāąâ€Œā°˛āąˆā°¨āą ā°Ēā°žā°¤āąâ€Œā°˛āą", - "offline_paths_description": "ā°Ŧā°žā°šāąā°¯ ā°˛āąˆā°Ŧāąā°°ā°°āą€ā°˛āą‹ ā°­ā°žā°—ā°‚ ā°•ā°žā°¨ā°ŋ ā°Ģāąˆā°ŗāąā°ŗā°¨āą ā°Žā°žā°¨āąā°¯āąā°ĩā°˛āąâ€Œā°—ā°ž ā°¤āąŠā°˛ā°—ā°ŋā°‚ā°šā°Ąā°‚ ā°ĩā°˛āąā°˛ Ⰸ ā°Ģā°˛ā°ŋā°¤ā°žā°˛āą ā°ĩā°šāąā°šā°ŋ ā°‰ā°‚ā°Ąā°ĩā°šāąā°šāą.", "ok": "ā°¸ā°°āą‡", "oldest_first": "ā°Žāąā°‚ā°Ļāąā°—ā°ž ā°Ēā°žā°¤ā°Ļā°ŋ", "onboarding": "ā°†ā°¨āąâ€Œā°Ŧāą‹ā°°āąā°Ąā°ŋā°‚ā°—āą", "onboarding_privacy_description": "ā°•ā°ŋā°‚ā°Ļā°ŋ (ā°ā°šāąā°›ā°ŋā°•ā°‚) ā°˛ā°•āąā°ˇā°Ŗā°žā°˛āą ā°Ŧā°žā°šāąā°¯ ā°¸āą‡ā°ĩā°˛ā°Ēāąˆ ā°†ā°§ā°žā°°ā°Ēā°Ąā°ŋ ā°‰ā°‚ā°Ÿā°žā°¯ā°ŋ ā°Žā°°ā°ŋā°¯āą ā°Ēā°°ā°ŋā°Ēā°žā°˛ā°¨ ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛ā°˛āą‹ ā°Žā°Ēāąā°Ēāąā°Ąāąˆā°¨ā°ž ā°¨ā°ŋā°˛ā°ŋā°Ēā°ŋā°ĩāą‡ā°¯ā°Ŧā°Ąā°¤ā°žā°¯ā°ŋ.", "onboarding_theme_description": "ā°Žāą€ ā°‡ā°¨āąā°¸āąā°Ÿā°žā°¨āąā°¸āąâ€Œ ā°•āą‹ā°¸ā°‚ ā°°ā°‚ā°—āą ā°Ĩāą€ā°Žāąâ€Œā°¨āą ā°Žā°‚ā°šāąā°•āą‹ā°‚ā°Ąā°ŋ. ā°Žāą€ā°°āą ā°Ļāą€ā°¨āąā°¨ā°ŋ ā°¤ā°°āąā°ĩā°žā°¤ ā°Žāą€ ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛ā°˛āą‹ ā°Žā°žā°°āąā°šā°ĩā°šāąā°šāą.", - "onboarding_welcome_description": "ā°Žāą€ ā°‡ā°¨āąā°¸āąā°Ÿā°žā°¨āąā°¸āąâ€Œā°¨āą ā°•āąŠā°¨āąā°¨ā°ŋ ā°¸ā°žā°§ā°žā°°ā°Ŗ ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛ā°¤āą‹ ā°¸āą†ā°Ÿā°Ēāą ā°šāą‡ā°¸āąā°•āąā°‚ā°Ļā°žā°‚.", "onboarding_welcome_user": "ā°¸āąā°ĩā°žā°—ā°¤ā°‚, {user }", "online": "ā°†ā°¨āąâ€Œā°˛āąˆā°¨āą", "only_favorites": "ā°‡ā°ˇāąā°Ÿā°Žāąˆā°¨ā°ĩā°ŋ ā°Žā°žā°¤āąā°°ā°Žāą‡", @@ -1161,8 +1133,6 @@ "unselect_all": "ā°Žā°‚ā°šāąā°•āąā°¨āąā°¨ā°ĩā°¨āąā°¨āą€ ā°¤āąŠā°˛ā°—ā°ŋā°‚ā°šāą", "unselect_all_duplicates": "ā°…ā°¨āąā°¨ā°ŋ ⰍⰕā°ŋā°˛āą€ā°˛ ā°Žā°‚ā°Ēā°ŋā°•ā°¨āą ā°¤āą€ā°¸ā°ŋā°ĩāą‡ā°¯ā°‚ā°Ąā°ŋ", "unstack": "ā°…ā°¨āą-ā°¸āąā°Ÿā°žā°•āą", - "untracked_files": "ā°…ā°¨āąâ€Œā°Ÿāąā°°ā°žā°•āą ā°šāą‡ā°¯ā°Ŧā°Ąā°ŋā°¨ ā°Ģāąˆā°˛āąâ€Œā°˛āą", - "untracked_files_decription": "Ⰸ ā°Ģāąˆā°˛āąâ€Œā°˛āą ā°…ā°Ēāąā°˛ā°ŋā°•āą‡ā°ˇā°¨āą ā°Ļāąā°ĩā°žā°°ā°ž ā°Ÿāąā°°ā°žā°•āą ā°šāą‡ā°¯ā°Ŧā°Ąā°ĩāą. ā°…ā°ĩā°ŋ ā°ĩā°ŋā°Ģā°˛ā°Žāąˆā°¨ ā°•ā°Ļā°˛ā°ŋā°•ā°˛āą, ā°…ā°‚ā°¤ā°°ā°žā°¯ā°‚ ā°•ā°˛ā°ŋā°—ā°ŋā°‚ā°šā°ŋā°¨ ā°…ā°Ēāąâ€Œā°˛āą‹ā°Ąāąâ€Œā°˛āą ā°˛āą‡ā°Ļā°ž ā°Ŧā°—āą ā°•ā°žā°°ā°Ŗā°‚ā°—ā°ž ā°Žā°ŋā°—ā°ŋā°˛ā°ŋā°Ēāą‹ā°¯ā°ŋā°¨ ā°Ģā°˛ā°ŋā°¤ā°žā°˛āą ā°•ā°žā°ĩā°šāąā°šāą", "up_next": "ā°¤ā°Ļāąā°Ēā°°ā°ŋ", "updated_password": "ā°¨ā°ĩāą€ā°•ā°°ā°ŋā°‚ā°šā°Ŧā°Ąā°ŋā°¨ ā°Ēā°žā°¸āąâ€Œā°ĩā°°āąā°Ąāą", "upload": "ā°…ā°Ēāąâ€Œā°˛āą‹ā°Ąāą", diff --git a/i18n/th.json b/i18n/th.json index 36b5b97b5c..13d9514d63 100644 --- a/i18n/th.json +++ b/i18n/th.json @@ -14,6 +14,7 @@ "add_a_location": "āš€ā¸žā¸´āšˆā¸Ąā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡", "add_a_name": "āš€ā¸žā¸´āšˆā¸Ąā¸Šā¸ˇāšˆā¸­", "add_a_title": "āš€ā¸žā¸´āšˆā¸Ąā¸Ģā¸ąā¸§ā¸‚āš‰ā¸­", + "add_endpoint": "āš€ā¸žā¸´āšˆā¸Ąā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸›ā¸Ĩ⏞ā¸ĸ⏗⏞⏇", "add_exclusion_pattern": "āš€ā¸žā¸´āšˆā¸Ąā¸‚āš‰ā¸­ā¸ĸā¸āš€ā¸§āš‰ā¸™", "add_import_path": "āš€ā¸žā¸´āšˆā¸Ąāš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸™ā¸ŗāš€ā¸‚āš‰ā¸˛", "add_location": "āš€ā¸žā¸´āšˆā¸Ąā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡", @@ -39,12 +40,10 @@ "authentication_settings_reenable": "āš€ā¸žā¸ˇāšˆā¸­āš€ā¸›ā¸´ā¸”āšƒā¸Ģā¸Ąāšˆ āšƒā¸Ģāš‰āšƒā¸Šāš‰ā¸„ā¸ŗā¸Ēā¸ąāšˆā¸‡āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ", "background_task_job": "ā¸‡ā¸˛ā¸™āš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡", "backup_database": "ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸ā¸˛ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", - "backup_database_enable_description": "āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸ā¸˛ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", + "backup_database_enable_description": "āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸ā¸˛ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "backup_keep_last_amount": "ā¸ˆā¸ŗā¸™ā¸§ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸āšˆā¸­ā¸™ā¸Ģā¸™āš‰ā¸˛ā¸—ā¸ĩāšˆā¸•āš‰ā¸­ā¸‡āš€ā¸āš‡ā¸šāš„ā¸§āš‰", "backup_settings": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", - "backup_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸ā¸˛ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", - "check_all": "ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", - "cleanup": "ā¸—ā¸ŗā¸„ā¸§ā¸˛ā¸Ąā¸Ē⏰⏭⏞⏔", + "backup_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸ā¸˛ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ ⏇⏞⏙ā¸Ē⏺⏪⏭⏇⏙ā¸ĩāš‰ā¸ˆā¸°āš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāšā¸Ĩā¸°ā¸„ā¸¸ā¸“ā¸ˆā¸°āš„ā¸Ąāšˆāš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸Ąā¸ˇāšˆā¸­ā¸Ąā¸ąā¸™ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡āš„ā¸Ąāšˆā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "cleared_jobs": "āš€ā¸„ā¸Ĩā¸ĩā¸ĸā¸ŖāšŒā¸‡ā¸˛ā¸™ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸š: {job}", "config_set_by_file": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸„ā¸­ā¸™ā¸Ÿā¸´ā¸ā¸ā¸ŗā¸Ĩā¸ąā¸‡ā¸–ā¸šā¸ā¸ā¸ŗā¸Ģā¸™ā¸”āš‚ā¸”ā¸ĸāš„ā¸Ÿā¸ĨāšŒā¸„ā¸­ā¸™ā¸Ÿā¸´ā¸", "confirm_delete_library": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸­ā¸ĸ⏞⏁ā¸Ĩā¸šā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸ž {library} ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ?", @@ -52,6 +51,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", @@ -59,18 +59,17 @@ "disable_login": "⏛⏴⏔⏁⏞⏪ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™", "duplicate_detection_job_description": "āšƒā¸Šāš‰ machine learning ā¸ā¸ąā¸šā¸Ēā¸ĩāšˆā¸­āš€ā¸žā¸ˇāšˆā¸­ā¸•ā¸Ŗā¸§ā¸ˆā¸ˆā¸ąā¸šā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸—ā¸ĩāšˆā¸„ā¸Ĩāš‰ā¸˛ā¸ĸā¸ā¸ąā¸™ āš‚ā¸”ā¸ĸāšƒā¸Šāš‰ā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģā¸˛ā¸­ā¸ąā¸ˆā¸‰ā¸Ŗā¸´ā¸ĸ⏰", "exclusion_pattern_description": "ā¸‚āš‰ā¸­ā¸ĸā¸āš€ā¸§āš‰ā¸™ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩā¸°āš€ā¸§āš‰ā¸™āš„ā¸Ÿā¸ĨāšŒāšā¸Ĩā¸°āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸‚ā¸“ā¸°ā¸Ēāšā¸ā¸™ā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸žā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ ā¸Ąā¸ĩā¸›ā¸Ŗā¸°āš‚ā¸ĸā¸Šā¸™āšŒāš€ā¸Ąā¸ˇāšˆā¸­āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸Ąā¸ĩāš„ā¸Ÿā¸ĨāšŒā¸—ā¸ĩāšˆāš„ā¸Ąāšˆā¸­ā¸ĸā¸˛ā¸ā¸™ā¸ŗāš€ā¸‚āš‰ā¸˛ āš€ā¸Šāšˆā¸™āš„ā¸Ÿā¸ĨāšŒ RAW", - "external_library_created_at": "⏄ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸žā¸ ā¸˛ā¸ĸ⏙⏭⏁ (ā¸–ā¸šā¸ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āš€ā¸Ąā¸ˇāšˆā¸­ {date})", "external_library_management": "ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸žā¸ ā¸˛ā¸ĸ⏙⏭⏁", "face_detection": "ā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸ˆā¸ąā¸šāšƒā¸šā¸Ģā¸™āš‰ā¸˛", "face_detection_description": "ā¸•ā¸Ŗā¸§ā¸ˆā¸ˆā¸ąā¸šāšƒā¸šā¸Ģā¸™āš‰ā¸˛āšƒā¸™ā¸Ēā¸ĩāšˆā¸­āš‚ā¸”ā¸ĸāšƒā¸Šāš‰ machine learning ⏧⏴⏔ā¸ĩāš‚ā¸­ā¸ˆā¸°āšƒā¸Šāš‰ā¸ ā¸˛ā¸žā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸ˆā¸˛ā¸ā¸§ā¸´ā¸”ā¸ĩāš‚ā¸­āš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™ \"ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”\" ā¸ˆā¸°ā¸›ā¸Ŗā¸°ā¸Ąā¸§ā¸Ĩ⏜ā¸Ĩā¸Ēā¸ĩāšˆā¸­ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸” \"⏂⏞⏔ā¸Ģ⏞ā¸ĸ\" ā¸ˆā¸°ā¸›ā¸Ŗā¸°ā¸Ąā¸§ā¸Ĩ⏜ā¸Ĩā¸Ēā¸ĩāšˆā¸­ā¸—ā¸ĩāšˆā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆāš„ā¸”āš‰ā¸›ā¸Ŗā¸°ā¸Ąā¸§ā¸Ĩ⏜ā¸Ĩ āšƒā¸šā¸Ģā¸™āš‰ā¸˛ā¸—ā¸ĩāšˆā¸–ā¸šā¸ā¸•ā¸Ŗā¸§ā¸ˆā¸ˆā¸ąā¸šāšā¸Ĩāš‰ā¸§ā¸ˆā¸°ā¸–ā¸šā¸āš€ā¸‚āš‰ā¸˛ā¸„ā¸´ā¸§ā¸›ā¸Ŗā¸°ā¸Ąā¸§ā¸Ĩ⏜ā¸Ĩā¸ā¸˛ā¸Ŗā¸ˆā¸”ā¸ˆā¸ŗāšƒā¸šā¸Ģā¸™āš‰ā¸˛ āš€ā¸žā¸´āšˆā¸Ąāš€ā¸‚āš‰ā¸˛āš„ā¸›āšƒā¸™ā¸ā¸Ĩā¸¸āšˆā¸Ąā¸—ā¸ĩāšˆā¸Ąā¸ĩ⏭ā¸ĸā¸šāšˆāšā¸Ĩāš‰ā¸§ā¸Ģā¸Ŗā¸ˇā¸­ā¸„ā¸™āšƒā¸Ģā¸Ąāšˆ", "facial_recognition_job_description": "ā¸™ā¸ŗāšƒā¸šā¸Ģā¸™āš‰ā¸˛ā¸—ā¸ĩāšˆā¸•ā¸Ŗā¸§ā¸ˆā¸ˆā¸ąā¸šāš„ā¸”āš‰āš„ā¸›ā¸ˆā¸ąā¸šā¸ā¸Ĩā¸¸āšˆā¸Ąā¸•ā¸˛ā¸Ąā¸œā¸šāš‰ā¸„ā¸™ ā¸‚ā¸ąāš‰ā¸™ā¸•ā¸­ā¸™ā¸™ā¸ĩāš‰ā¸—ā¸ŗā¸‡ā¸˛ā¸™ā¸Ģā¸Ĩā¸ąā¸‡ā¸ˆā¸˛ā¸ā¸•ā¸Ŗā¸§ā¸ˆā¸ˆā¸ąā¸šāšƒā¸šā¸Ģā¸™āš‰ā¸˛ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ \"ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”\" ⏈⏰⏈⏺⏁ā¸Ĩā¸¸āšˆā¸Ąāšƒā¸šā¸Ģā¸™āš‰ā¸˛ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”āšƒā¸Ģā¸Ąāšˆ \"⏂⏞⏔ā¸Ģ⏞ā¸ĸ\" ā¸ˆā¸°ā¸ˆā¸ąā¸”ā¸„ā¸´ā¸§āšƒā¸šā¸Ģā¸™āš‰ā¸˛ā¸—ā¸ĩāšˆā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆāš„ā¸”āš‰ā¸Ŗā¸°ā¸šā¸¸ā¸„ā¸™", "failed_job_command": "⏄⏺ā¸Ēā¸ąāšˆā¸‡ {command} ⏂⏭⏇⏇⏞⏙ {job} ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧", "force_delete_user_warning": "ā¸„āšā¸˛āš€ā¸•ā¸ˇā¸­ā¸™: ā¸‚ā¸ąāš‰ā¸™ā¸•ā¸­ā¸™ā¸™ā¸ĩāš‰ā¸ˆā¸°ā¸Ĩā¸šā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āšā¸Ĩ⏰ā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”ā¸—ā¸ąā¸™ā¸—ā¸ĩ āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ĸāš‰ā¸­ā¸™ā¸ā¸Ĩā¸ąā¸šā¸Ąā¸˛āš„ā¸”āš‰āšā¸Ĩā¸°ā¸ā¸šāš‰ā¸„ā¸ˇā¸™āš„ā¸Ÿā¸ĨāšŒāš„ā¸Ąāšˆāš„ā¸”āš‰", - "forcing_refresh_library_files": "ā¸šā¸ąā¸‡ā¸„ā¸ąā¸šā¸Ŗā¸ĩāš€ā¸Ÿā¸Ŗā¸Šāš„ā¸Ÿā¸ĨāšŒā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "image_format": "Format", "image_format_description": "WebP ā¸ˆā¸°āšƒā¸Ģāš‰āš„ā¸Ÿā¸ĨāšŒā¸—ā¸ĩāšˆāš€ā¸Ĩāš‡ā¸ā¸ā¸§āšˆā¸˛ JPEG āšā¸•āšˆāšƒā¸Šāš‰āš€ā¸§ā¸Ĩā¸˛āšā¸›ā¸Ĩā¸‡āš„ā¸Ÿā¸ĨāšŒā¸™ā¸˛ā¸™ā¸ā¸§āšˆā¸˛", "image_fullsize_description": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸‚ā¸™ā¸˛ā¸”āš€ā¸•āš‡ā¸Ąā¸—ā¸ĩāšˆā¸–ā¸šā¸ā¸–ā¸­ā¸”ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ metadata ⏭⏭⏁ āšƒā¸Šāš‰āšƒā¸™ā¸‚ā¸“ā¸°ā¸—ā¸ŗā¸ā¸˛ā¸Ŗā¸‚ā¸ĸ⏞ā¸ĸā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸”ā¸š", "image_fullsize_enabled": "āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸ā¸˛ā¸Ŗā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸‚ā¸™ā¸˛ā¸”āš€ā¸•āš‡ā¸Ą", + "image_fullsize_enabled_description": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žāš€ā¸•āš‡ā¸Ąā¸‚ā¸™ā¸˛ā¸”ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸Ŗā¸šā¸›āšā¸šā¸šā¸—ā¸ĩāšˆāš„ā¸Ąāšˆāš€ā¸‚āš‰ā¸˛ā¸ā¸ąā¸™ā¸ā¸ąā¸šāš€ā¸§āš‡ā¸š āš€ā¸Ąā¸ˇāšˆā¸­āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ \"ā¸–āš‰ā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸”ā¸šā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡\" ā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡āšā¸šā¸šā¸ā¸ąā¸‡ā¸ˆā¸°ā¸–ā¸šā¸āšƒā¸Šāš‰āš‚ā¸”ā¸ĸā¸•ā¸Ŗā¸‡āš‚ā¸”ā¸ĸ⏕⏪⏇ āšā¸Ĩā¸°āš„ā¸Ąāšˆā¸Ēāšˆā¸‡ā¸œā¸Ĩā¸•āšˆā¸­ā¸Ŗā¸šā¸›āšā¸šā¸šāš„ā¸Ąāšˆāš€ā¸‚āš‰ā¸˛ā¸ā¸ąā¸™ā¸ā¸ąā¸šāš€ā¸§āš‡ā¸š āš€ā¸Šāšˆā¸™ JPEG", "image_fullsize_quality_description": "ā¸„ā¸¸ā¸“ā¸ ā¸˛ā¸žā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸‚ā¸™ā¸˛ā¸”āš€ā¸•āš‡ā¸Ąā¸ˆā¸˛ā¸ 1-100 ā¸„āšˆā¸˛ā¸ĸā¸´āšˆā¸‡ā¸Ēā¸šā¸‡ā¸„ā¸¸ā¸“ā¸ ā¸˛ā¸žā¸ĸā¸´āšˆā¸‡ā¸Ēā¸šā¸‡ āšā¸•āšˆāšā¸Ĩā¸ā¸Ąā¸˛ā¸”āš‰ā¸§ā¸ĸā¸‚ā¸™ā¸˛ā¸”āš„ā¸Ÿā¸ĨāšŒā¸—ā¸ĩāšˆāšƒā¸Ģā¸āšˆā¸‚ā¸ļāš‰ā¸™", "image_fullsize_title": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸‚ā¸™ā¸˛ā¸”āš€ā¸•āš‡ā¸Ą", "image_prefer_embedded_preview": "āšƒā¸Šāš‰ā¸žā¸Ŗā¸ĩā¸§ā¸´ā¸§āšā¸šā¸šā¸ā¸ąā¸‡ā¸•ā¸ąā¸§", @@ -190,6 +189,7 @@ "oauth_auto_register": "ā¸Ĩā¸‡ā¸—ā¸°āš€ā¸šā¸ĩā¸ĸā¸™ā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´", "oauth_auto_register_description": "ā¸Ĩā¸‡ā¸—ā¸°āš€ā¸šā¸ĩā¸ĸā¸™ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āšƒā¸Ģā¸Ąāšˆā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´āš€ā¸Ąā¸ˇāšˆā¸­ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸œāšˆā¸˛ā¸™ OAuth", "oauth_button_text": "ā¸‚āš‰ā¸­ā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸¸āšˆā¸Ąā¸ā¸”", + "oauth_client_secret_description": "ā¸ˆā¸ŗāš€ā¸›āš‡ā¸™ā¸•āš‰ā¸­ā¸‡ā¸Ŗā¸°ā¸šā¸¸ā¸Ģā¸˛ā¸ā¸œā¸šāš‰āšƒā¸Ģāš‰ā¸šā¸Ŗā¸´ā¸ā¸˛ā¸Ŗ OAuth āš„ā¸Ąāšˆā¸Ŗā¸­ā¸‡ā¸Ŗā¸ąā¸š PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸œāšˆā¸˛ā¸™ OAuth", "oauth_mobile_redirect_uri": "URI āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸šā¸™āš‚ā¸—ā¸Ŗā¸¨ā¸ąā¸žā¸—āšŒ", "oauth_mobile_redirect_uri_override": "āšā¸—ā¸™ā¸—ā¸ĩāšˆ URI āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸šā¸™āš‚ā¸—ā¸Ŗā¸¨ā¸ąā¸žā¸—āšŒ", @@ -203,8 +203,8 @@ "oauth_storage_quota_claim_description": "ā¸•ā¸ąāš‰ā¸‡āš‚ā¸„ā¸§ā¸•āš‰ā¸˛ā¸žā¸ˇāš‰ā¸™ā¸—ā¸ĩāšˆā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸‚ā¸­ā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸•ā¸˛ā¸Ąā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒā¸—ā¸ĩāšˆāšƒā¸Šāš‰ā¸­āš‰ā¸˛ā¸‡ā¸–ā¸ļā¸‡āš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´", "oauth_storage_quota_default": "āš‚ā¸„ā¸§ā¸•āš‰ā¸˛ā¸žā¸ˇāš‰ā¸™ā¸—ā¸ĩāšˆāš€ā¸āš‡ā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™ (GiB)", "oauth_storage_quota_default_description": "āš‚ā¸„ā¸§ā¸•āš‰ā¸˛āšƒā¸™ā¸Ģā¸™āšˆā¸§ā¸ĸ GiB ⏗ā¸ĩāšˆā¸ˆā¸°āšƒā¸Šāš‰āš€ā¸Ąā¸ˇāšˆā¸­āš„ā¸Ąāšˆā¸Ąā¸ĩā¸ā¸˛ā¸Ŗā¸­āš‰ā¸˛ā¸‡ā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒ (ā¸›āš‰ā¸­ā¸™ 0 ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šāš‚ā¸„ā¸§ā¸•āš‰ā¸˛āš„ā¸Ąāšˆā¸ˆā¸ŗā¸ā¸ąā¸”)", - "offline_paths": "ā¸žā¸˛ā¸˜ā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒ", - "offline_paths_description": "⏜ā¸Ĩā¸Ĩā¸ąā¸žā¸˜āšŒāš€ā¸Ģā¸Ĩāšˆā¸˛ā¸™ā¸ĩāš‰ā¸­ā¸˛ā¸ˆāš€ā¸ā¸´ā¸”ā¸ˆā¸˛ā¸ā¸Ĩā¸šāš„ā¸Ÿā¸ĨāšŒā¸—ā¸ĩāšˆāš„ā¸Ąāšˆāš„ā¸”āš‰āš€ā¸›āš‡ā¸™ā¸Ēāšˆā¸§ā¸™ā¸Ģ⏙ā¸ļāšˆā¸‡ā¸‚ā¸­ā¸‡āš„ā¸Ĩ⏚⏪⏞⏪ā¸ĩāšˆā¸ ā¸˛ā¸ĸ⏙⏭⏁.", + "oauth_timeout": "ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩā¸˛ā¸ā¸˛ā¸Ŗā¸Ŗāš‰ā¸­ā¸‡ā¸‚ā¸­", + "oauth_timeout_description": "⏪⏰ā¸ĸā¸°āš€ā¸§ā¸Ĩ⏞ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩ⏞ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸Ŗāš‰ā¸­ā¸‡ā¸‚ā¸­ (ā¸Ģā¸™āšˆā¸§ā¸ĸāš€ā¸›āš‡ā¸™ā¸Ąā¸´ā¸Ĩā¸Ĩ⏴⏧⏴⏙⏞⏗ā¸ĩ)", "password_enable_description": "ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸ā¸ąā¸šā¸­ā¸ĩāš€ā¸Ąā¸Ĩāšā¸Ĩ⏰⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", "password_settings": "ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸œāšˆā¸˛ā¸™ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", "password_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸‚ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸œāšˆā¸˛ā¸™ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", @@ -214,9 +214,6 @@ "refreshing_all_libraries": "⏪ā¸ĩāš€ā¸Ÿā¸Ŗā¸Šā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸žā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "registration": "ā¸Ĩā¸‡ā¸—ā¸°āš€ā¸šā¸ĩā¸ĸā¸™ā¸œā¸šāš‰ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗ", "registration_description": "āš€ā¸™ā¸ˇāšˆā¸­ā¸‡ā¸ˆā¸˛ā¸ā¸„ā¸¸ā¸“āš€ā¸›āš‡ā¸™ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āšā¸Ŗā¸ā¸‚ā¸­ā¸‡ā¸Ŗā¸°ā¸šā¸š ā¸„ā¸¸ā¸“ā¸ˆā¸°ā¸–ā¸šā¸āšā¸•āšˆā¸‡ā¸•ā¸ąāš‰ā¸‡āš€ā¸›āš‡ā¸™ā¸œā¸šāš‰ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗāšā¸Ĩā¸°ā¸Ŗā¸ąā¸šā¸œā¸´ā¸”ā¸Šā¸­ā¸šā¸‡ā¸˛ā¸™ā¸šā¸Ŗā¸´ā¸Ģ⏞⏪ ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ąā¸ˆā¸°ā¸–ā¸šā¸ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āš‚ā¸”ā¸ĸ⏄⏏⏓", - "repair_all": "ā¸‹āšˆā¸­ā¸Ąāšā¸‹ā¸Ąā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", - "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": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛āš€ā¸›āš‡ā¸™ā¸„āšˆā¸˛ā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", @@ -245,7 +242,6 @@ "storage_template_migration_info": "āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•ā¸‚ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸ˆā¸°āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸•ā¸ąā¸§ā¸­ā¸ąā¸ā¸Šā¸Ŗāš€ā¸›āš‡ā¸™ā¸•ā¸ąā¸§ā¸žā¸´ā¸Ąā¸žāšŒāš€ā¸Ĩāš‡ā¸ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸” ā¸ā¸˛ā¸Ŗāš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āšā¸›ā¸Ĩā¸‡āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•ā¸ˆā¸°ā¸Ąā¸ĩ⏜ā¸Ĩā¸ā¸ąā¸šāšā¸­ā¸Ēāš€ā¸‹āš‡ā¸•āšƒā¸Ģā¸Ąāšˆāš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™ ā¸Ģā¸˛ā¸ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸™ā¸ŗāš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•āš„ā¸›āšƒā¸Šāš‰ā¸ā¸ąā¸š Asset ⏗ā¸ĩāšˆā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩā¸”ā¸āšˆā¸­ā¸™ā¸Ģā¸™āš‰ā¸˛ā¸™ā¸ĩāš‰ āšƒā¸Ģāš‰ā¸Ŗā¸ąā¸™ {job}.", "storage_template_migration_job": "āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩ⏕⏁⏞⏪ Migration ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "storage_template_more_details": "ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸Ŗā¸˛ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸā¸”āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ąāš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸šā¸Ÿā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒā¸™ā¸ĩāš‰ āš‚ā¸›ā¸Ŗā¸”ā¸”ā¸šā¸—ā¸ĩāšˆ Storage Template āšā¸Ĩ⏰ ⏜ā¸Ĩā¸ā¸Ŗā¸°ā¸—ā¸š", - "storage_template_onboarding_description": "āš€ā¸Ąā¸ˇāšˆā¸­āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ ⏟ā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒā¸™ā¸ĩāš‰ā¸ˆā¸°ā¸ˆā¸ąā¸”ā¸Ŗā¸°āš€ā¸šā¸ĩā¸ĸā¸šāš„ā¸Ÿā¸ĨāšŒāš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´ā¸•ā¸˛ā¸Ąāš€ā¸—ā¸Ąāš€ā¸žā¸Ĩ⏕⏗ā¸ĩāšˆā¸œā¸šāš‰āšƒā¸Šāš‰ā¸ā¸ŗā¸Ģ⏙⏔ āš€ā¸™ā¸ˇāšˆā¸­ā¸‡ā¸ˆā¸˛ā¸ā¸›ā¸ąā¸ā¸Ģā¸˛ā¸”āš‰ā¸˛ā¸™ā¸„ā¸§ā¸˛ā¸Ąāš€ā¸Ē⏖ā¸ĩā¸ĸ⏪ ⏟ā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒā¸™ā¸ĩāš‰ā¸ˆā¸ļā¸‡ā¸–ā¸šā¸ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āš€ā¸›āš‡ā¸™ā¸„āšˆā¸˛āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™ ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ą āš‚ā¸›ā¸Ŗā¸”ā¸”ā¸šā¸—ā¸ĩāšˆ āš€ā¸­ā¸ā¸Ēā¸˛ā¸Ŗā¸›ā¸Ŗā¸°ā¸ā¸­ā¸š", "storage_template_path_length": "⏂ā¸ĩā¸”ā¸ˆā¸ŗā¸ā¸ąā¸”ā¸‚ā¸­ā¸‡ā¸„ā¸§ā¸˛ā¸Ąā¸ĸā¸˛ā¸§ā¸žā¸˛ā¸˜āš‚ā¸”ā¸ĸā¸›ā¸Ŗā¸°ā¸Ąā¸˛ā¸“: {length, number}/{limit, number}", "storage_template_settings": "āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "storage_template_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗāš‚ā¸„ā¸Ŗā¸‡ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒāšā¸Ĩā¸°ā¸Šā¸ˇāšˆā¸­āš„ā¸Ÿā¸ĨāšŒā¸—ā¸ĩāšˆā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩ⏔", @@ -257,7 +253,6 @@ "template_email_invite_album": "āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•āš€ā¸Šā¸´ā¸āš€ā¸‚āš‰ā¸˛ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "template_email_preview": "ā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡", "template_email_settings": "⏭ā¸ĩāš€ā¸Ąā¸Ĩāš€ā¸—āš‡ā¸Ąāš€ā¸žā¸Ĩ⏕", - "template_email_settings_description": "ā¸›ā¸Ŗā¸ąā¸šāšā¸•āšˆā¸‡ā¸Ŗā¸šā¸›āšā¸šā¸šā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™ā¸­ā¸ĩāš€ā¸Ąā¸Ĩ", "template_email_update_album": "ā¸­ā¸ąā¸›āš€ā¸”ā¸•āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "template_email_welcome": "āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩ⏕ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸­ā¸ĩāš€ā¸Ąā¸Ĩā¸•āš‰ā¸­ā¸™ā¸Ŗā¸ąā¸š", "template_settings": "āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•ā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™", @@ -266,7 +261,6 @@ "theme_custom_css_settings_description": "Cascading Style Sheets ā¸Šāšˆā¸§ā¸ĸāšƒā¸Ģāš‰ā¸›ā¸Ŗā¸ąā¸šāšā¸•āšˆā¸‡āš€ā¸„āš‰ā¸˛āš‚ā¸„ā¸Ŗā¸‡ Immich āš„ā¸”āš‰", "theme_settings": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸˜ā¸ĩā¸Ą", "theme_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸›ā¸Ŗā¸ąā¸šāšā¸•āšˆā¸‡ā¸Ģā¸™āš‰ā¸˛āš€ā¸§āš‡ā¸š Immich", - "these_files_matched_by_checksum": "āš„ā¸Ÿā¸ĨāšŒāš€ā¸Ģā¸Ĩāšˆā¸˛ā¸™ā¸ĩāš‰āš€ā¸Ģā¸Ąā¸ˇā¸­ā¸™ā¸ā¸ąā¸™ā¸ˆā¸˛ā¸ checksums", "thumbnail_generation_job": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸ ā¸˛ā¸žā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡", "thumbnail_generation_job_description": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸ ā¸˛ā¸žā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸‚ā¸™ā¸˛ā¸”āšƒā¸Ģā¸āšˆ ā¸‚ā¸™ā¸˛ā¸”āš€ā¸Ĩāš‡ā¸āšā¸Ĩā¸°āšā¸šā¸šāš€ā¸šā¸Ĩ⏭ ā¸Ēāšā¸˛ā¸Ģā¸Ŗā¸ąā¸šāšā¸•āšˆā¸Ĩ⏰ā¸Ēā¸ˇāšˆā¸­āšā¸Ĩā¸°ā¸šā¸¸ā¸„ā¸„ā¸Ĩ", "transcoding_acceleration_api": "API āš€ā¸Ŗāšˆā¸‡ā¸„ā¸§ā¸˛ā¸Ąāš€ā¸Ŗāš‡ā¸§āšā¸›ā¸Ĩ⏇ā¸Ēā¸ˇāšˆā¸­", @@ -294,10 +288,9 @@ "transcoding_encoding_options": "ā¸•ā¸ąā¸§āš€ā¸Ĩā¸ˇā¸­ā¸ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ē", "transcoding_encoding_options_description": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ codecs, ā¸„ā¸§ā¸˛ā¸Ąā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸ⏔, ā¸„ā¸¸ā¸“ā¸ ā¸˛ā¸ž (āšā¸Ĩā¸°ā¸•ā¸ąā¸§āš€ā¸Ĩā¸ˇā¸­ā¸ā¸­ā¸ˇāšˆā¸™āš† ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸§ā¸´ā¸”ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ē (encoded videos)", "transcoding_hardware_acceleration": "ā¸ā¸˛ā¸Ŗāš€ā¸Ŗāšˆā¸‡ā¸„ā¸§ā¸˛ā¸Ąāš€ā¸Ŗāš‡ā¸§ā¸”āš‰ā¸§ā¸ĸā¸Žā¸˛ā¸ŖāšŒā¸”āšā¸§ā¸ŖāšŒ", - "transcoding_hardware_acceleration_description": "⏁⏞⏪⏗⏔ā¸Ĩ⏭⏇ āš€ā¸Ŗāš‡ā¸§ā¸ā¸§āšˆā¸˛ā¸Ąā¸˛ā¸ āšā¸•āšˆā¸ˆā¸°ā¸Ąā¸ĩā¸„ā¸¸ā¸“ā¸ ā¸˛ā¸žā¸•āšˆā¸ŗā¸ā¸§āšˆā¸˛ā¸—ā¸ĩāšˆā¸šā¸´ā¸•āš€ā¸Ŗā¸•āš€ā¸—āšˆā¸˛ā¸ā¸ąā¸™", + "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-frame āšƒā¸™ā¸‚ā¸“ā¸°ā¸—ā¸ĩāšˆā¸„āšˆā¸˛ -1 ā¸ˆā¸°ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸„āšˆā¸˛ā¸™ā¸ĩāš‰āš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´", "transcoding_max_bitrate": "bitrate ā¸Ēā¸šā¸‡ā¸Ē⏏⏔", @@ -335,14 +328,13 @@ "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_immediately": "ā¸šā¸ąā¸ā¸Šā¸ĩāšā¸Ĩ⏰ā¸Ēā¸ˇāšˆā¸­ā¸‚ā¸­ā¸‡ {user} ⏈⏰⏭ā¸ĸā¸šāšˆāšƒā¸™ā¸„ā¸´ā¸§ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸Ĩā¸šā¸–ā¸˛ā¸§ā¸Ŗ āš‚ā¸”ā¸ĸā¸—ā¸ąā¸™ā¸—ā¸ĩ", "user_delete_immediately_checkbox": "ā¸„ā¸´ā¸§ā¸œā¸šāš‰āšƒā¸Šāš‰āšā¸Ĩ⏰ā¸Ēā¸ˇāšˆā¸­ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸Ĩā¸šā¸—ā¸ąā¸™ā¸—ā¸ĩ", + "user_details": "⏪⏞ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸā¸”ā¸œā¸šāš‰āšƒā¸Šāš‰", "user_management": "ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸œā¸šāš‰āšƒā¸Šāš‰", "user_password_has_been_reset": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸‚ā¸­ā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸–ā¸šā¸ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛āšƒā¸Ģā¸Ąāšˆāšā¸Ĩāš‰ā¸§:", "user_password_reset_description": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸‚ā¸­ā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸ˆā¸°ā¸–ā¸šā¸ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛āšƒā¸Ģā¸Ąāšˆāšā¸Ĩ⏰ā¸Ēāšˆā¸‡āš„ā¸›ā¸ĸā¸ąā¸‡ā¸­ā¸ĩāš€ā¸Ąā¸Ĩ⏗ā¸ĩāšˆā¸Ĩā¸‡ā¸—ā¸°āš€ā¸šā¸ĩā¸ĸ⏙", @@ -362,11 +354,17 @@ "admin_password": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸œā¸šāš‰ā¸”ā¸šāšā¸Ĩ⏪⏰⏚⏚", "administration": "ā¸ā¸˛ā¸Ŗā¸”ā¸šāšā¸Ĩ⏪⏰⏚⏚", "advanced": "ā¸‚ā¸ąāš‰ā¸™ā¸Ēā¸šā¸‡", + "advanced_settings_enable_alternate_media_filter_subtitle": "āšƒā¸Šāš‰ā¸•ā¸ąā¸§āš€ā¸Ĩ⏎⏭⏁⏙ā¸ĩāš‰āš€ā¸žā¸ˇāšˆā¸­ā¸ā¸Ŗā¸­ā¸‡ā¸Ēā¸ˇāšˆā¸­ā¸Ŗā¸°ā¸Ģā¸§āšˆā¸˛ā¸‡ā¸ā¸˛ā¸Ŗā¸‹ā¸´ā¸‡ā¸āšŒāš‚ā¸”ā¸ĸā¸­ā¸´ā¸‡ā¸•ā¸˛ā¸Ąāš€ā¸‡ā¸ˇāšˆā¸­ā¸™āš„ā¸‚ā¸—ā¸˛ā¸‡āš€ā¸Ĩ⏎⏭⏁ āšā¸™ā¸°ā¸™ā¸ŗāšƒā¸Ģāš‰āšƒā¸Šāš‰āš€ā¸‰ā¸žā¸˛ā¸°āš€ā¸Ąā¸ˇāšˆā¸­āšā¸­ā¸›ā¸Ąā¸ĩā¸›ā¸ąā¸ā¸Ģā¸˛āšƒā¸™ā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸ˆā¸ąā¸šā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", + "advanced_settings_enable_alternate_media_filter_title": "[⏄⏏⏓ā¸Ēā¸Ąā¸šā¸ąā¸•ā¸´ā¸—ā¸”ā¸Ĩ⏭⏇] āšƒā¸Šāš‰ā¸•ā¸ąā¸§ā¸ā¸Ŗā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸‹ā¸´ā¸‡ā¸āšŒā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸‚ā¸­ā¸‡ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒāšā¸šā¸šā¸—ā¸˛ā¸‡āš€ā¸Ĩ⏎⏭⏁", "advanced_settings_log_level_title": "ā¸Ŗā¸°ā¸”ā¸ąā¸šā¸ā¸˛ā¸Ŗ Log: {level}", "advanced_settings_prefer_remote_subtitle": "ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸šā¸˛ā¸‡āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡āš‚ā¸Ģā¸Ĩā¸”ā¸Ŗā¸šā¸›ā¸ĸāšˆā¸­ā¸Šāš‰ā¸˛ā¸Ąā¸˛ā¸ āš€ā¸›ā¸´ā¸”ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸™ā¸ĩāš‰āš€ā¸žā¸ˇāšˆā¸­āš‚ā¸Ģā¸Ĩā¸”ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸ˆā¸˛ā¸ā¸Ŗā¸ĩāš‚ā¸Ąā¸—āšā¸—ā¸™", "advanced_settings_prefer_remote_title": "āšƒā¸Ģāš‰ā¸„ā¸§ā¸˛ā¸Ąā¸Ēā¸ŗā¸„ā¸ąā¸ā¸ā¸ąā¸šā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸Ŗā¸ĩāš‚ā¸Ąā¸—", + "advanced_settings_proxy_headers_subtitle": "⏁⏺ā¸Ģ⏙⏔ proxy headers ⏗ā¸ĩāšˆ Immich ⏄⏧⏪ā¸Ēāšˆā¸‡ā¸žā¸Ŗāš‰ā¸­ā¸Ąā¸ā¸ąā¸šāšā¸•āšˆā¸Ĩā¸°ā¸„ā¸ŗā¸‚ā¸­āš€ā¸„ā¸Ŗā¸ˇā¸­ā¸‚āšˆā¸˛ā¸ĸ", + "advanced_settings_proxy_headers_title": "ā¸žāš‡ā¸­ā¸ā¸‹ā¸ĩāšˆ āš€ā¸Žā¸”āš€ā¸”ā¸­ā¸ŖāšŒ", "advanced_settings_self_signed_ssl_subtitle": "ā¸‚āš‰ā¸˛ā¸Ąā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ SSL ā¸ˆā¸ŗāš€ā¸›āš‡ā¸™ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šāšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡āšā¸šā¸š self-signed", "advanced_settings_self_signed_ssl_title": "ā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•āšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ SSL āšā¸šā¸š self-signed ", + "advanced_settings_sync_remote_deletions_subtitle": "⏚ā¸Ģā¸Ŗā¸ˇā¸­ā¸ā¸šāš‰ā¸„ā¸ˇā¸™āš„ā¸Ÿā¸ĨāšŒā¸šā¸™ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸™ā¸ĩāš‰āš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´āš€ā¸Ąā¸ˇāšˆā¸­ā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸”ā¸ąā¸‡ā¸ā¸Ĩāšˆā¸˛ā¸§ā¸œāšˆā¸˛ā¸™āš€ā¸§āš‡ā¸š", + "advanced_settings_sync_remote_deletions_title": "ā¸‹ā¸´ā¸‡ā¸āšŒā¸ā¸˛ā¸Ŗā¸Ĩ⏚⏈⏞⏁⏪⏰ā¸ĸā¸°āš„ā¸ā¸Ĩ [⏄⏏⏓ā¸Ēā¸Ąā¸šā¸ąā¸•ā¸´ā¸—ā¸”ā¸Ĩ⏭⏇]", "advanced_settings_tile_subtitle": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸‚ā¸ąāš‰ā¸™ā¸Ēā¸šā¸‡", "advanced_settings_troubleshooting_subtitle": "āš€ā¸›ā¸´ā¸”ā¸Ÿā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒāš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ąāš€ā¸žā¸ˇāšˆā¸­āšā¸āš‰āš„ā¸‚ā¸›ā¸ąā¸ā¸Ģ⏞", "advanced_settings_troubleshooting_title": "āšā¸āš‰āš„ā¸‚ā¸›ā¸ąā¸ā¸Ģ⏞", @@ -388,10 +386,6 @@ "album_remove_user": "ā¸Ĩā¸šā¸œā¸šāš‰āšƒā¸Šāš‰ ?", "album_remove_user_confirmation": "ā¸„ā¸¸ā¸“ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸—ā¸ĩāšˆā¸ˆā¸°ā¸Ĩā¸šā¸œā¸šāš‰āšƒā¸Šāš‰ {user} ?", "album_share_no_users": "ā¸”ā¸šāš€ā¸Ģā¸Ąā¸ˇā¸­ā¸™ā¸§āšˆā¸˛ā¸„ā¸¸ā¸“āš„ā¸”āš‰āšā¸Šā¸ŖāšŒā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸™ā¸ĩāš‰ā¸ā¸ąā¸šā¸œā¸šāš‰āšƒā¸Šāš‰ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”āšā¸Ĩāš‰ā¸§", - "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}", @@ -432,6 +426,8 @@ "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": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩā¸šā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšā¸šā¸šā¸­āšˆā¸˛ā¸™ā¸­ā¸ĸāšˆā¸˛ā¸‡āš€ā¸”ā¸ĩā¸ĸā¸§āš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", @@ -441,6 +437,7 @@ "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": "ā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´", @@ -540,21 +537,17 @@ "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": "ā¸Ŗā¸šā¸›ā¸ĸāšˆā¸­ā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸ž ({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": "ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪⏗ā¸ĩāšˆā¸‹āš‰ā¸ŗā¸ā¸ąā¸™ ({count})", - "cache_settings_image_cache_size": "ā¸‚ā¸™ā¸˛ā¸”āšā¸„ā¸Šā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž ({count} ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪)", "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": "ā¸„ā¸§ā¸šā¸„ā¸¸ā¸Ąā¸žā¸¤ā¸•ā¸´ā¸ā¸Ŗā¸Ŗā¸Ąā¸ā¸˛ā¸Ŗāšā¸„ā¸Šā¸‚ā¸­ā¸‡āšā¸­ā¸›ā¸žā¸Ĩā¸´āš€ā¸„ā¸Šā¸ąā¸™ Immich", - "cache_settings_thumbnail_size": "ā¸‚ā¸™ā¸˛ā¸”āšā¸„ā¸Šā¸Ŗā¸šā¸›ā¸ĸāšˆā¸­ ({count} ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪)", "cache_settings_tile_subtitle": "ā¸„ā¸§ā¸šā¸„ā¸¸ā¸Ąā¸žā¸¤ā¸•ā¸´ā¸ā¸Ŗā¸Ŗā¸Ąā¸‚ā¸­ā¸‡ā¸—ā¸ĩāšˆā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šāšƒā¸™ā¸•ā¸ąā¸§āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡", "cache_settings_tile_title": "⏗ā¸ĩāšˆā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šāšƒā¸™ā¸•ā¸ąā¸§āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡", "cache_settings_title": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛āšā¸„ā¸Š", @@ -581,7 +574,6 @@ "change_pin_code": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", "change_your_password": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", "changed_visibility_successfully": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸ā¸˛ā¸Ŗā¸Ąā¸­ā¸‡āš€ā¸Ģāš‡ā¸™āš€ā¸Ŗā¸ĩā¸ĸā¸šā¸Ŗāš‰ā¸­ā¸ĸāšā¸Ĩāš‰ā¸§", - "check_all": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "check_logs": "ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šā¸šā¸ąā¸™ā¸—ā¸ļ⏁", "choose_matching_people_to_merge": "āš€ā¸Ĩ⏎⏭⏁⏄⏙⏗ā¸ĩāšˆā¸•ā¸Ŗā¸‡ā¸ā¸ąā¸™āš€ā¸žā¸ˇāšˆā¸­ā¸Ŗā¸§ā¸Ąāš€ā¸‚āš‰ā¸˛ā¸”āš‰ā¸§ā¸ĸā¸ā¸ąā¸™", "city": "āš€ā¸Ąā¸ˇā¸­ā¸‡", @@ -613,7 +605,6 @@ "contain": "ā¸Ąā¸ĩ⏭ā¸ĸā¸šāšˆ", "context": "ā¸šā¸Ŗā¸´ā¸šā¸—", "continue": "ā¸•āšˆā¸­āš„ā¸›", - "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": "ā¸Ĩā¸šā¸ˆā¸˛ā¸āš€ā¸Ŗā¸ˇāšˆā¸­ā¸‡", @@ -769,7 +760,6 @@ "cant_get_number_of_comments": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸Ŗā¸ĩā¸ĸā¸ā¸”ā¸šā¸ˆā¸ŗā¸™ā¸§ā¸™ā¸„ā¸§ā¸˛ā¸Ąā¸„ā¸´ā¸”āš€ā¸Ģāš‡ā¸™āš„ā¸”āš‰", "cant_search_people": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸„āš‰ā¸™ā¸Ģā¸˛ā¸šā¸¸ā¸„ā¸„ā¸Ĩā¸„ā¸™āš„ā¸”āš‰", "cant_search_places": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸„āš‰ā¸™ā¸Ģ⏞ā¸Ē⏖⏞⏙⏗ā¸ĩāšˆāš„ā¸”āš‰", - "cleared_jobs": "ā¸Ĩāš‰ā¸˛ā¸‡ā¸‡ā¸˛ā¸™: {job} ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "error_adding_assets_to_album": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩā¸˛ā¸”āšƒā¸™ā¸ā¸˛ā¸Ŗāš€ā¸žā¸´āšˆā¸Ąā¸Ēā¸ˇāšˆā¸­āš„ā¸›ā¸ĸā¸ąā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "error_adding_users_to_album": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩā¸˛ā¸”āšƒā¸™ā¸ā¸˛ā¸Ŗāš€ā¸žā¸´āšˆā¸Ąā¸œā¸šāš‰āšƒā¸Šāš‰āš„ā¸›ā¸ĸā¸ąā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "error_deleting_shared_user": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩā¸˛ā¸”āšƒā¸™ā¸ā¸˛ā¸Ŗā¸Ĩā¸šā¸œā¸šāš‰āšƒā¸Šāš‰ā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒ", @@ -778,7 +768,6 @@ "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": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āšā¸āš‰āš„ā¸‚ā¸Ĩā¸´ā¸‡ā¸āšŒā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒāš„ā¸”āš‰", @@ -795,7 +784,6 @@ "paths_validation_failed": "ā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ē⏭⏚ {paths, plural, one {# path} other {# paths}} ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧", "profile_picture_transparent_pixels": "ā¸Ŗā¸šā¸›āš‚ā¸›ā¸Ŗāš„ā¸Ÿā¸ĨāšŒāš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ąā¸ĩā¸žā¸´ā¸āš€ā¸‹ā¸Ĩāš‚ā¸›ā¸Ŗāšˆā¸‡āšƒā¸Ēāš„ā¸”āš‰ āš‚ā¸›ā¸Ŗā¸”ā¸‹ā¸šā¸Ąāš€ā¸‚āš‰ā¸˛āšā¸Ĩ⏰/ā¸Ģ⏪⏎⏭ā¸ĸāš‰ā¸˛ā¸ĸā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", "quota_higher_than_disk_size": "ā¸„ā¸¸ā¸“ā¸•ā¸ąāš‰ā¸‡āš‚ā¸„ā¸§ā¸•ā¸˛āš„ā¸§āš‰ā¸Ēā¸šā¸‡ā¸ā¸§āšˆā¸˛ā¸‚ā¸™ā¸˛ā¸”ā¸”ā¸´ā¸Ēā¸āšŒ", - "repair_unable_to_check_items": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸•ā¸Ŗā¸§ā¸ˆā¸Ē⏭⏚ {count, select, one {item} other {items}} āš„ā¸”āš‰", "unable_to_add_album_users": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸žā¸´āšˆā¸Ąā¸œā¸šāš‰āšƒā¸Šāš‰āš„ā¸›ā¸ĸā¸ąā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāš„ā¸”āš‰", "unable_to_add_assets_to_shared_link": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸žā¸´āšˆā¸Ąā¸Ĩā¸‡āšƒā¸™ā¸Ĩā¸´ā¸‡ā¸āšŒā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒāš„ā¸”āš‰", "unable_to_add_comment": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸žā¸´āšˆā¸Ąā¸„ā¸§ā¸˛ā¸Ąāš€ā¸Ģāš‡ā¸™āš„ā¸”āš‰", @@ -813,7 +801,6 @@ "unable_to_change_visibility": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸ā¸˛ā¸Ŗā¸Ąā¸­ā¸‡āš€ā¸Ģāš‡ā¸™ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸š {count, plural, one {# person} other {# people}}", "unable_to_complete_oauth_login": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸—ā¸ŗā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸š OAuth āšƒā¸Ģāš‰āš€ā¸Ēā¸Ŗāš‡ā¸ˆā¸Ēā¸Ąā¸šā¸šā¸Ŗā¸“āšŒāš„ā¸”āš‰", "unable_to_connect": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­āš„ā¸”āš‰", - "unable_to_connect_to_server": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ā¸ā¸ąā¸š Server āš„ā¸”āš‰", "unable_to_copy_to_clipboard": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸„ā¸ąā¸”ā¸Ĩā¸­ā¸āš„ā¸›ā¸ĸā¸ąā¸‡ā¸„ā¸Ĩā¸´ā¸›ā¸šā¸­ā¸ŖāšŒā¸”āš„ā¸”āš‰ ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāšƒā¸Ģāš‰āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸„ā¸¸ā¸“āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļ⏇ā¸Ģā¸™āš‰ā¸˛ā¸œāšˆā¸˛ā¸™ā¸—ā¸˛ā¸‡ https", "unable_to_create_admin_account": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸šā¸ąā¸ā¸Šā¸ĩā¸œā¸šāš‰ā¸”ā¸šāšā¸Ĩā¸Ŗā¸°ā¸šā¸šāš„ā¸”āš‰", "unable_to_create_api_key": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ API ⏄ā¸ĩā¸ĸāšŒ āš„ā¸”āš‰", @@ -837,10 +824,6 @@ "unable_to_hide_person": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸‹āšˆā¸­ā¸™ā¸šā¸¸ā¸„ā¸„ā¸Ĩāš„ā¸”āš‰", "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": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš‚ā¸Ģā¸Ĩ⏔ā¸Ē⏖⏞⏙⏰ like āš„ā¸”āš‰", "unable_to_log_out_all_devices": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸‚ā¸­ā¸‡ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”āš„ā¸”āš‰", "unable_to_log_out_device": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸Ŗā¸°ā¸šā¸šāš„ā¸”āš‰", "unable_to_login_with_oauth": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸šā¸ā¸ąā¸š OAuth āš„ā¸”āš‰", @@ -851,11 +834,9 @@ "unable_to_remove_album_users": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩā¸šā¸œā¸šāš‰āšƒā¸Šāš‰ā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāš„ā¸”āš‰", "unable_to_remove_api_key": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩ⏚ API Key āš„ā¸”āš‰", "unable_to_remove_assets_from_shared_link": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩ⏚⏭⏭⏁⏈⏞⏁ā¸Ĩā¸´ā¸‡ā¸āšŒā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒāš„ā¸”āš‰", - "unable_to_remove_deleted_assets": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩā¸šāš„ā¸Ÿā¸ĨāšŒā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒāš„ā¸”āš‰", "unable_to_remove_library": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩā¸šā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸žāš„ā¸”āš‰", "unable_to_remove_partner": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩā¸šā¸„ā¸šāšˆā¸Ģā¸šāš„ā¸”āš‰", "unable_to_remove_reaction": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩ⏚ reaction āš„ā¸”āš‰", - "unable_to_repair_items": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸‹āšˆā¸­ā¸Ąāšā¸‹ā¸Ąā¸Ŗā¸˛ā¸ĸā¸ā¸˛ā¸Ŗāš„ā¸”āš‰", "unable_to_reset_password": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸•ā¸ąāš‰ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āšƒā¸Ģā¸Ąāšˆāš„ā¸”āš‰", "unable_to_resolve_duplicate": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āšā¸āš‰āš„ā¸‚ā¸‚ā¸­ā¸‡ā¸‹āš‰ā¸ŗāš„ā¸”āš‰", "unable_to_restore_assets": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸Ŗā¸ĩā¸ĸ⏁⏄⏎⏙ā¸Ēā¸ˇāšˆā¸­āš„ā¸”āš‰", @@ -1171,14 +1152,11 @@ "notifications_setting_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™", "official_immich_resources": "āšā¸Ģā¸Ĩāšˆā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ Immich ⏭ā¸ĸāšˆā¸˛ā¸‡āš€ā¸›āš‡ā¸™ā¸—ā¸˛ā¸‡ā¸ā¸˛ā¸Ŗ", "offline": "ā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒ", - "offline_paths": "āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸—ā¸ĩāšˆā¸•ā¸ąāš‰ā¸‡ā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒ", - "offline_paths_description": "⏜ā¸Ĩā¸Ĩā¸ąā¸žā¸˜āšŒāš€ā¸Ģā¸Ĩāšˆā¸˛ā¸™ā¸ĩāš‰ā¸­ā¸˛ā¸ˆāš€ā¸ā¸´ā¸”ā¸ˆā¸˛ā¸ā¸ā¸˛ā¸Ŗā¸Ĩā¸šāš„ā¸Ÿā¸ĨāšŒā¸—ā¸ĩāšˆāš„ā¸Ąāšˆāš„ā¸”āš‰āš€ā¸›āš‡ā¸™ā¸Ēāšˆā¸§ā¸™ā¸Ģ⏙ā¸ļāšˆā¸‡ā¸‚ā¸­ā¸‡āš„ā¸Ĩ⏚⏪⏞⏪ā¸ĩ⏠⏞ā¸ĸā¸™ā¸­ā¸ā¸”āš‰ā¸§ā¸ĸā¸•ā¸™āš€ā¸­ā¸‡", "ok": "⏕⏁ā¸Ĩ⏇", "oldest_first": "āš€ā¸Ŗā¸ĩā¸ĸā¸‡āš€ā¸āšˆā¸˛ā¸Ēā¸¸ā¸”ā¸āšˆā¸­ā¸™", "onboarding": "ā¸ā¸˛ā¸Ŗāš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", "onboarding_privacy_description": "⏄⏏⏓ā¸Ĩā¸ąā¸ā¸Šā¸“ā¸° (āš„ā¸Ąāšˆā¸ˆā¸ŗāš€ā¸›āš‡ā¸™) ā¸•āšˆā¸­āš„ā¸›ā¸™ā¸ĩāš‰ā¸•āš‰ā¸­ā¸‡ā¸­ā¸˛ā¸¨ā¸ąā¸ĸ⏚⏪⏴⏁⏞⏪⏠⏞ā¸ĸ⏙⏭⏁ āšā¸Ĩ⏰ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āš„ā¸”āš‰ā¸•ā¸Ĩā¸­ā¸”āš€ā¸§ā¸Ĩā¸˛āšƒā¸™ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸”ā¸šāšā¸Ĩ⏪⏰⏚⏚", "onboarding_theme_description": "āš€ā¸Ĩ⏎⏭⏁⏘ā¸ĩā¸Ąā¸Ēā¸ĩ ⏄⏏⏓ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āšā¸›ā¸Ĩā¸‡āš„ā¸”āš‰āšƒā¸™ā¸ ā¸˛ā¸ĸā¸Ģā¸Ĩā¸ąā¸‡āšƒā¸™ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", - "onboarding_welcome_description": "ā¸Ąā¸˛ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ Immich ⏂⏭⏇⏄⏏⏓ ā¸”āš‰ā¸§ā¸ĸā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸—ā¸ąāšˆā¸§āš„ā¸›ā¸ā¸ąā¸™", "onboarding_welcome_user": "ā¸ĸ⏴⏙⏔ā¸ĩā¸•āš‰ā¸­ā¸™ā¸Ŗā¸ąā¸šā¸„ā¸¸ā¸“ {user}", "online": "ā¸­ā¸­ā¸™āš„ā¸Ĩā¸™āšŒ", "only_favorites": "⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”āš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™", @@ -1470,7 +1448,6 @@ "setting_image_viewer_preview_title": "āš‚ā¸Ģā¸Ĩā¸”ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡", "setting_image_viewer_title": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", "setting_languages_apply": "ā¸šā¸ąā¸™ā¸—ā¸ļ⏁", - "setting_languages_title": "ā¸ ā¸˛ā¸Šā¸˛", "setting_notifications_notify_failures_grace_period": "āšā¸ˆāš‰ā¸‡ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšƒā¸™āš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧: {duration}", "setting_notifications_notify_hours": "{count} ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", "setting_notifications_notify_immediately": "āš‚ā¸”ā¸ĸā¸—ā¸ąā¸™ā¸—ā¸ĩ", @@ -1636,7 +1613,6 @@ "to_parent": "āš„ā¸›ā¸ĸā¸ąā¸‡ā¸šā¸™ā¸Ē⏏⏔", "to_trash": "ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", "toggle_settings": "ā¸Ēā¸Ĩā¸ąā¸šā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛", - "toggle_theme": "ā¸Ēā¸Ĩā¸ąā¸šā¸˜ā¸ĩā¸Ą", "total": "ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "total_usage": "ā¸ā¸˛ā¸Ŗāšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸Ŗā¸§ā¸Ą", "trash": "ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", @@ -1696,11 +1672,6 @@ "variables": "ā¸•ā¸ąā¸§āšā¸›ā¸Ŗ", "version": "ā¸Ŗā¸¸āšˆā¸™", "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_3": "āšā¸Ĩā¸°ā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ā¸§āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸•ā¸´ā¸”ā¸•ā¸ąāš‰ā¸‡ docker-compose āšā¸Ĩ⏰ .env āš€ā¸›āš‡ā¸™ā¸›ā¸ąā¸ˆā¸ˆā¸¸ā¸šā¸ąā¸™āš€ā¸žā¸ˇāšˆā¸­āš„ā¸Ąāšˆāšƒā¸Ģāš‰āš€ā¸ā¸´ā¸”ā¸ā¸˛ā¸Ŗā¸•ā¸´ā¸”ā¸•ā¸ąāš‰ā¸‡ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔ āš‚ā¸”ā¸ĸāš€ā¸‰ā¸žā¸˛ā¸°ā¸œā¸šāš‰āšƒā¸Šāš‰ WatchTower ā¸Ģā¸Ŗā¸ˇā¸­ā¸Ŗā¸°ā¸šā¸šā¸­ā¸ąā¸žāš€ā¸”ā¸•āšā¸­ā¸›ā¸žā¸Ĩā¸´āš€ā¸„ā¸Šā¸ąāšˆā¸™āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´", - "version_announcement_overlay_title": "ā¸Ąā¸ĩāš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™āšƒā¸Ģā¸Ąāšˆā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šāš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ 🎉", "version_history": "ā¸ā¸˛ā¸Ŗāš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āšā¸›ā¸Ĩ⏇", "version_history_item": "ā¸•ā¸´ā¸”ā¸•ā¸ąāš‰ā¸‡ {version} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", "video": "⏧⏴⏔ā¸ĩāš‚ā¸­", diff --git a/i18n/tr.json b/i18n/tr.json index 96032cb9a8..ddb99ca019 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -22,6 +22,7 @@ "add_partner": "Partner ekle", "add_path": "Yol ekle", "add_photos": "Fotoğraf ekle", + "add_tag": "Etiket ekle", "add_to": "Şuraya ekleâ€Ļ", "add_to_album": "AlbÃŧme ekle", "add_to_album_bottom_sheet_added": "{album} albÃŧmÃŧne eklendi", @@ -39,13 +40,11 @@ "authentication_settings_disable_all": "TÃŧm giriş yÃļntemlerini devre dÄąÅŸÄą bÄąrakmak istediğinize emin misiniz? Giriş yapma fonksiyonu tamamen devre dÄąÅŸÄą bÄąrakÄąlacak.", "authentication_settings_reenable": "Yeniden aktif etmek için Sunucu Komutu'nu kullanÄąn.", "background_task_job": "Arka Plan GÃļrevleri", - "backup_database": "Yedek VeritabanÄą", - "backup_database_enable_description": "VeritabanÄą Yedeklerinş Etkinleştir", - "backup_keep_last_amount": "Önceki yedeklemeden saklanacak miktar", - "backup_settings": "Yedekleme AyarlarÄą", + "backup_database": "VeritabanÄą yığınÄą oluştur", + "backup_database_enable_description": "VeritabanÄą yığınlarÄąnÄą etkinleştir", + "backup_keep_last_amount": "TutulmasÄą gereken geçmiş yığınÄą miktarÄą", + "backup_settings": "VeritabanÄą yığınÄą ayarlarÄą", "backup_settings_description": "VeritabanÄą Yedekleme AyarlarÄąnÄą YÃļnet", - "check_all": "Hepsini Kontrol Et", - "cleanup": "Temizle", "cleared_jobs": "{job} için işler temizlendi", "config_set_by_file": "Ayarlar şuanda config dosyasÄą tarafÄąndan ayarlanmÄąÅŸtÄąr", "confirm_delete_library": "{library} kÃŧtÃŧphanesini silmek istediğinize emin misiniz?", @@ -61,18 +60,19 @@ "disable_login": "Girişi devre dÄąÅŸÄą bÄąrak", "duplicate_detection_job_description": "Benzer fotoğraflarÄą bulmak için makine Ãļğrenmesini çalÄąÅŸtÄąr. Bu işlem AkÄąllÄą Arama'ya bağlÄądÄąr", "exclusion_pattern_description": "KÃŧtÃŧphaneyi tararken dosya ve klasÃļrleri gÃļrmezden gelmek için dÄąÅŸlama desenlerini kullanabilirsiniz. RAW dosyalarÄą gibi bazÄą dosya ve klasÃļrleri içe aktarmak istemediğinizde bu seçeneği kullanabilirsiniz.", - "external_library_created_at": "DÄąÅŸ kÃŧtÃŧphane ({date} tarihinde oluşturuldu.)", "external_library_management": "DÄąÅŸ KÃŧtÃŧphane YÃļnetimi", "face_detection": "YÃŧz tarama", "face_detection_description": "Makine Ãļğrenimi kullanarak varlÄąklardaki yÃŧzleri tespit et. Videolar için sadece kÃŧçÃŧk resim (thumbnail) dikkate alÄąnÄąr. 'Yenile' tÃŧm varlÄąklarÄą yeniden işler. 'SÄąfÄąrla', mevcut tÃŧm yÃŧz verilerini temizleyerek işlemi yeniden başlatÄąr. 'Eksik' henÃŧz işlenmemiş varlÄąklarÄą sÄąraya alÄąr. Tespit edilen yÃŧzler, YÃŧz TanÄąma işlemi tamamlandÄąktan sonra mevcut ya da yeni kişilere gruplanmak Ãŧzere YÃŧz TanÄąma için sÄąraya alÄąnacaktÄąr.", "facial_recognition_job_description": "AlgÄąlanan yÃŧzleri kişilere grupla. Bu adÄąm, YÃŧz Tespit işlemi tamamlandÄąktan sonra çalÄąÅŸÄąr. \"SÄąfÄąrla\", tÃŧm yÃŧzleri yeniden gruplandÄąrÄąr. \"Eksik\" ise henÃŧz bir kişiye atanmamÄąÅŸ yÃŧzleri sÄąraya alÄąr.", "failed_job_command": "{job} gÃļrevi için {command} komutu başarÄąsÄąz", "force_delete_user_warning": "UYARI: Bu işlem kullanÄącÄąyÄą ve tÃŧm varlÄąklarÄą anÄąnda kaldÄąracaktÄąr. Bu geri alÄąnamaz ve dosyalar geri getirilemez.", - "forcing_refresh_library_files": "TÃŧm kÃŧtÃŧphane dosyalarÄąnÄąn zorunlu olarak yenilenmesi sağlanÄąyor", "image_format": "Biçim", "image_format_description": "WebP, JPEG'e gÃļre daha kÃŧçÃŧk dosya boyutu sunar fakat işlemesi daha uzun sÃŧrer.", + "image_fullsize_enabled": "Tam boyutlu gÃļrÃŧntÃŧ Ãŧretimini etkinleştir", + "image_fullsize_quality_description": "1-100 arasÄąnda tam boyutlu gÃļrÃŧntÃŧ kalitesi. Daha yÃŧksek kalitelidir, ancak daha bÃŧyÃŧk dosyalar Ãŧretir.", + "image_fullsize_title": "Tam boyutlu gÃļrÃŧntÃŧ ayarlarÄą", "image_prefer_embedded_preview": "GÃļmÃŧlÃŧ Ãļnizlemeyi tercih et", - "image_prefer_embedded_preview_setting_description": "RAW fotoğraflarÄą için mÃŧmkÃŧn olduğunda gÃļmÃŧlÃŧ Ãļnizlemeyi kullan. Bu, bazÄą fotoğraflarda daha gerçekçi renkler Ãŧretebilir, fakat Ãļnizlemenin kalitesi kullanÄąlan kameraya bağlÄądÄąr ve fotoğrafta normalden daha fazla gÃļrÃŧntÃŧ bozukluklarÄąna sebep olabilir.", + "image_prefer_embedded_preview_setting_description": "RAtoğraflarÄą için mÃŧmkÃŧn olduğunda gÃļmÃŧlÃŧ Ãļnizlemeyi kullan. Bu, bazÄą fotoğraflarda daha gerçekçi renkler n kameraya bağlÄądÄąr ve fotoğrafta normalden daha fazla gÃļrÃŧntÃŧ bozukluklarÄąna sebep olabilir.", "image_prefer_wide_gamut": "Geniş renk aralığınÄą tercih et", "image_prefer_wide_gamut_setting_description": "Önizleme gÃļrseli için P3 renk paletini tercih et. Bu, geniş renk paletli fotoğraflarda renk canlÄąlığınÄą daha iyi korur, fakat fotoğraflar eski tarayÄącÄąlarda ve eski cihazlarda daha farklÄą gÃļrÃŧnebilir. sRGB fotoğraflar renk paletini korumak için sRGB olarak tutulur.", "image_preview_description": "Orta boyutlu gÃļrÃŧntÃŧ, meta verisi Ã§ÄąkarÄąlmÄąÅŸ, tekil bir varlÄąk gÃļrÃŧntÃŧlenirken ve makine Ãļğrenimi için kullanÄąlÄąr", @@ -191,7 +191,7 @@ "oauth_enable_description": "OAuth ile giriş yap", "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_mobile_redirect_uri_override_description": "Mobil URI'ye izin vermeyen OAuth sağlayÄącÄąsÄą olduğunda etkinleştir, Ãļrneğin ''{callback}''", "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.", @@ -201,8 +201,8 @@ "oauth_storage_quota_claim_description": "KullanÄącÄąya depolama kotasÄą koymak için kullanÄąlacak değer (en: OAuth claim).", "oauth_storage_quota_default": "VarsayÄąlan depolama kotasÄą (GiB)", "oauth_storage_quota_default_description": "Değer (en: OAuth claim) mevcut değilse konulacak kota. GiB cinsinden, sÄąnÄąrsÄąz kota için 0 kullanÄąn.", - "offline_paths": "ÇevrimdÄąÅŸÄą yollar", - "offline_paths_description": "Bu sonuçlar dÄąÅŸ kÃŧtÃŧphaneye bağlÄą olmayan dosyalarÄąn elle silinmesinden kaynaklÄą olabilir.", + "oauth_timeout": "İstek zaman aÅŸÄąmÄą", + "oauth_timeout_description": "Milisaniye cinsinden istek zaman aÅŸÄąmÄą", "password_enable_description": "Email ve şifre ile giriş yap", "password_settings": "Şifre giriş", "password_settings_description": "Şifre giriş ayarlarÄąnÄą yÃļnet", @@ -212,9 +212,6 @@ "refreshing_all_libraries": "TÃŧm kÃŧtÃŧphaneler yenileniyor", "registration": "YÃļnetici kaydÄą", "registration_description": "Sistemdeki ilk kullanÄącÄą olduğunuz için hesabÄąnÄąz YÃļnetici olarak ayarlandÄą. Yeni oluşturulan Ãŧyeliklerin, ve yÃļnetici gÃļrevlerinin sorumlusu olarak atandÄąnÄąz.", - "repair_all": "TÃŧmÃŧnÃŧ onar", - "repair_matched_items": "{count, plural, one {# Ãļğe eşleşti} other {# Ãļğeler eşleşti}}", - "repaired_items": "{count, plural, one {# item} other {# items}} tamir edildi", "require_password_change_on_login": "KullanÄącÄąnÄąn ilk girişinde şifre değiştirmesini zorunlu kÄąl", "reset_settings_to_default": "AyarlarÄą varsayÄąlana sÄąfÄąrla", "reset_settings_to_recent_saved": "AyarlarÄą kaydedilmiş Ãļnceki ayarlara dÃļndÃŧr", @@ -243,7 +240,6 @@ "storage_template_migration_info": "Şablon ayarlarÄąndaki değişiklikler sadece yeni varlÄąklara uygulanacak. Şablon ayarlarÄąnÄą daha Ãļnce yÃŧklenmiş olan varlÄąklara uygulamak için {job} çalÄąÅŸtÄąrÄąn.", "storage_template_migration_job": "Depolama Adreslerini Değiştirme GÃļrevi", "storage_template_more_details": "Bu Ãļzellik hakkÄąnda daha fazla bilgi için, Depolama Şablonu ve onun etkileri kÄąsmÄąna bakÄąn", - "storage_template_onboarding_description": "Bu Ãļzellik aÃ§Äąldığında, dosyalarÄą kullanÄącÄą için belirlenen depolama adresi taslağına gÃļre otomatik olarak dÃŧzenler. Bu Ãļzellik bazen sorun Ã§Äąkarabildiğini için kapalÄą gelmektedir. Daha fazla bilgi için dokÃŧmantasyona bakabilirsiniz.", "storage_template_path_length": "Tahmini dosya adresi uzunluğu: {length, number}/{limit, number}", "storage_template_settings": "Depolama Şablonu", "storage_template_settings_description": "YÃŧklenen dosyanÄąn ismini ve klasÃļr yapÄąsÄąnÄą dÃŧzenle", @@ -255,7 +251,6 @@ "template_email_invite_album": "AlbÃŧm Daveti Şablonu", "template_email_preview": "Ön izleme", "template_email_settings": "Eposta TaslaklarÄą", - "template_email_settings_description": "Özel e-posta bildirim şablonlarÄąnÄą yÃļnet", "template_email_update_album": "AlbÃŧm Şablonunu GÃŧncelle", "template_email_welcome": "Hoş geldiniz e-posta şablonu", "template_settings": "Bildirim ŞablonlarÄą", @@ -264,7 +259,6 @@ "theme_custom_css_settings_description": "CSS (Cascading Style Sheets) kullanÄąlarak Immich'in tasarÄąmÄą değiştirilebilir.", "theme_settings": "Tema ayarlarÄą", "theme_settings_description": "Immich web arayÃŧzÃŧnÃŧn Ãļzelleştirilmesi ayarlarÄąnÄą yÃļnet", - "these_files_matched_by_checksum": "Bu dosyalarÄąn imzalarÄą uyuşuyor", "thumbnail_generation_job": "Önizlemeleri oluştur", "thumbnail_generation_job_description": "Her kişi ve obje için bÃŧyÃŧk, kÃŧçÃŧk ve bulanÄąk thumbnail (kÃŧçÃŧk resim) oluştur", "transcoding_acceleration_api": "HÄązlandÄąrma API", @@ -295,7 +289,6 @@ "transcoding_hardware_acceleration_description": "Deneysel; daha hÄązlÄą, fakat aynÄą bitrate ayarlarÄąnda daha dÃŧşÃŧk kaliteye sahip", "transcoding_hardware_decoding": "DonanÄąm çÃļzÃŧcÃŧ", "transcoding_hardware_decoding_setting_description": "Uçtan uca hÄązlandÄąrmayÄą, sadece kodlamayÄą hÄązlandÄąrmanÄąn yerine etkinleştirir. TÃŧm videolarda çalÄąÅŸmayabilir.", - "transcoding_hevc_codec": "HEVC kodek", "transcoding_max_b_frames": "Maksimum B-kareler", "transcoding_max_b_frames_description": "Daha yÃŧksek değerler sÄąkÄąÅŸtÄąrma verimliliğini artÄąrÄąr, ancak kodlamayÄą yavaşlatÄąr. Eski cihazlarda donanÄąm hÄązlandÄąrma ile uyumlu olmayabilir. 0, B-çerçevelerini devre dÄąÅŸÄą bÄąrakÄąr, -1 ise bu değeri otomatik olarak ayarlar.", "transcoding_max_bitrate": "Maksimum bitrate", @@ -333,14 +326,13 @@ "trash_number_of_days_description": "VarlÄąklarÄąn kalÄącÄą olarak silinmeden Ãļnce çÃļpte kaç gÃŧn tutulacağı", "trash_settings": "ÇÃļp ayarlarÄą", "trash_settings_description": "ÇÃļp ayarlarÄąnÄą yÃļnet", - "untracked_files": "İzlenmeyen dosyalar", - "untracked_files_description": "Bu dosyalar uygulama tarafÄąndan izlenmiyor. YarÄąda kesilen yÃŧklemeler veya uygulama hatasÄą bunlara sebep olmuş olabilir", "user_cleanup_job": "KullanÄącÄą temizleme", "user_delete_delay": "{user} hesabÄą ve varlÄąklarÄą {delay, plural, one {# day} other {# days}} gÃŧn içinde kalÄącÄą olarak silinmek için planlandÄą.", "user_delete_delay_settings": "Silme gecikmesi", "user_delete_delay_settings_description": "Bir kullanÄącÄąnÄąn hesabÄąnÄą ve varlÄąklarÄąnÄą kalÄącÄą olarak silmek için kaldÄąrÄąldÄąktan sonra gereken gÃŧn sayÄąsÄą. KullanÄącÄą silme işi, silinmeye hazÄąr kullanÄącÄąlarÄą kontrol etmek için gece yarÄąsÄą çalÄąÅŸÄąr. Bu ayardaki değişiklikler bir sonraki yÃŧrÃŧtmede değerlendirilecektir.", "user_delete_immediately": "{user}'in hesabÄą ve varlÄąklarÄą hemen kalÄącÄą olarak silinmek Ãŧzere sÄąraya alÄąnacak.", "user_delete_immediately_checkbox": "KullanÄącÄą ve varlÄąklarÄą hemen silinmek Ãŧzere sÄąraya al", + "user_details": "KullanÄącÄą AyrÄąntÄąlarÄą", "user_management": "KullanÄącÄą YÃļnetimi", "user_password_has_been_reset": "KullanÄącÄąnÄąn şifresi sÄąfÄąrlandÄą:", "user_password_reset_description": "LÃŧtfen kullanÄącÄąya geçici şifreyi sağlayÄąn ve bir sonraki oturum aÃ§ÄąÅŸÄąnda şifreyi değiştirmesi gerektiğini bildirin.", @@ -360,6 +352,7 @@ "admin_password": "YÃļnetici Şifresi", "administration": "YÃļnetim", "advanced": "Gelişmiş", + "advanced_settings_enable_alternate_media_filter_title": "[DENEYSEL] Alternatif cihaz albÃŧm eşleme sÃŧzgeci kullanÄąn", "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", @@ -367,6 +360,7 @@ "advanced_settings_proxy_headers_title": "Proxy Header'lar", "advanced_settings_self_signed_ssl_subtitle": "Sunucu uç noktasÄą için SSL sertifika doğrulamasÄąnÄą atlar. Kendinden imzalÄą sertifikalar için gereklidir.", "advanced_settings_self_signed_ssl_title": "Kendi kendine imzalanmÄąÅŸ SSL sertifikalarÄąna izin ver", + "advanced_settings_sync_remote_deletions_title": "Uzaktan silinmeleri eşle [DENEYSEL]", "advanced_settings_tile_subtitle": "Gelişmiş kullanÄącÄą ayarlarÄą", "advanced_settings_troubleshooting_subtitle": "Sorun giderme için ek Ãļzellikleri etkinleştirin", "advanced_settings_troubleshooting_title": "Sorun Giderme", @@ -388,10 +382,6 @@ "album_remove_user": "KullanÄącÄąyÄą kaldÄąr?", "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": "{count} Ãļğe", - "album_thumbnail_card_shared": " ¡ 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", @@ -482,6 +472,7 @@ "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", "automatic_endpoint_switching_title": "Otomatik URL değiştirme", + "autoplay_slideshow": "Otomatik slayt gÃļsterisi", "back": "Geri", "back_close_deselect": "Geri, kapat veya seçimi kaldÄąr", "background_location_permission": "Arka plan konum izni", @@ -518,13 +509,14 @@ "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çilen: ", "backup_controller_page_backup_sub": "Yedeklenen Ãļğeler", "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 ({count})", "backup_controller_page_filename": "Dosya adÄą: {filename} [{size}]", + "backup_controller_page_id": "KNu: {id}", "backup_controller_page_info": "Yedekleme bilgileri", "backup_controller_page_none_selected": "Hiçbiri seçilmedi", "backup_controller_page_remainder": "Kalan", @@ -548,6 +540,10 @@ "backup_options_page_title": "Yedekleme seçenekleri", "backup_setting_subtitle": "Arka planda ve Ãļn planda yÃŧkleme ayarlarÄąnÄą dÃŧzenle", "backward": "Geriye doğru", + "biometric_auth_enabled": "Biyometrik kimlik doğrulama etkin", + "biometric_locked_out": "Biyometrik kimlik doğrulamasÄą kilitli", + "biometric_no_options": "Biyometrik seçenek yok", + "biometric_not_available": "Bu cihazda biyometrik kimlik doğrulama mevcut değil", "birthdate_saved": "Doğum gÃŧnÃŧ başarÄąlÄą bir şekilde kaydedildi", "birthdate_set_description": "Doğum gÃŧnÃŧ, fotoğraftaki insanÄąn fotoğraf çekildiği zamandaki yaÅŸÄąnÄąn hesaplanmasÄą için kullanÄąlÄąr.", "blurred_background": "BulanÄąk arkaplan", @@ -558,21 +554,17 @@ "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 ({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 ({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": "{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 ({count} Ãļğe)", "cache_settings_tile_subtitle": "Yerel depolama davranÄąÅŸÄąnÄą kontrol et", "cache_settings_tile_title": "Yerel Depolama", "cache_settings_title": "Önbellek AyarlarÄą", @@ -581,15 +573,17 @@ "camera_model": "Kamera modeli", "cancel": "İptal", "cancel_search": "AramayÄą iptal et", + "canceled": "İptal edildi", "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", "change_date": "Tarihi değiştir", + "change_description": "AÃ§ÄąklamayÄą değiştir", "change_display_order": "GÃļrÃŧntÃŧleme sÄąrasÄąnÄą değiştir", "change_expiration_time": "Son kullanma sÃŧresini değiştir", "change_location": "Konumu değiştir", "change_name": "İsim değiştir", - "change_name_successfully": "İsim başarÄąyla değiştirildi", + "change_name_successfully": "AdÄą başarÄąyla değiştirildi", "change_password": "Şifre Değiştir", "change_password_description": "Bu ya sistemdeki ilk oturum aÃ§ÄąÅŸÄąnÄąz ya da şifre değişikliği için bir talepte bulunuldu. LÃŧtfen yeni şifreyi aşağıya yazÄąnÄąz.", "change_password_form_confirm_password": "Parola OnayÄą", @@ -600,7 +594,6 @@ "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ç", "check_corrupt_asset_backup": "Bozuk yedek dosyalarÄąnÄą kontrol et", "check_corrupt_asset_backup_button": "Kontrol et", "check_corrupt_asset_backup_description": "Bu kontrolÃŧ yalnÄązca Wi-Fi Ãŧzerinden ve tÃŧm dosyalar yedeklendikten sonra çalÄąÅŸtÄąrÄąn. İşlem birkaç dakika sÃŧrebilir.", @@ -632,6 +625,7 @@ "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": "TamamlandÄą", "confirm": "Onayla", "confirm_admin_password": "YÃļnetici Şifresini Onayla", "confirm_delete_face": "VarlÄąktan {name} yÃŧzÃŧnÃŧ silmek istediğinizden emin misiniz?", @@ -639,15 +633,18 @@ "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", + "confirm_tag_face": "Bu yÃŧzÃŧ {name} olarak etiketlemek ister misiniz?", + "confirm_tag_face_unnamed": "Bu yÃŧzÃŧ etiketlemek ister misin?", + "connected_device": "Cihaz bağlandÄą", "contain": "İçermek", "context": "Bağlam", "continue": "Devam et", - "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": "İlişimi paylaş", "control_bottom_app_bar_share_to": "Paylaş:", "control_bottom_app_bar_trash_from_immich": "ÇÃļp Kutusuna At", "copied_image_to_clipboard": "Resim, panoya kopyalandÄą.", @@ -679,6 +676,7 @@ "create_tag_description": "Yeni bir etiket oluşturun. İç içe geçmiş etiketler için, etiketi tam yolu ve eğik çizgileri de dahil ederek giriniz.", "create_user": "KullanÄącÄą oluştur", "created": "Oluşturuldu", + "created_at": "Oluşturuldu", "crop": "Kes", "curated_object_page_title": "Nesneler", "current_device": "Mevcut cihaz", @@ -734,6 +732,7 @@ "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", @@ -750,7 +749,6 @@ "download_enqueue": "İndirme sÄąraya alÄąndÄą", "download_error": "İndirme HatasÄą", "download_failed": "İndirme başarÄąsÄąz oldu", - "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", @@ -774,6 +772,8 @@ "edit_avatar": "AvatarÄą DÃŧzenle", "edit_date": "Tarihi DÃŧzenle", "edit_date_and_time": "Tarih ve zamanÄą dÃŧzenleyin", + "edit_description": "AÃ§ÄąklamayÄą dÃŧzenle", + "edit_description_prompt": "LÃŧtfen yeni bir aÃ§Äąklama seçin:", "edit_exclusion_pattern": "Hariç tutma desenini dÃŧzenle", "edit_faces": "YÃŧzleri DÃŧzenleyin", "edit_import_path": "İçe aktarma yolunu dÃŧzenleyin", @@ -794,12 +794,17 @@ "editor_crop_tool_h2_aspect_ratios": "En boy oranlarÄą", "editor_crop_tool_h2_rotation": "Rotasyon", "email": "E-posta", + "email_notifications": "E-posta bildirimleri", + "empty_folder": "Bu klasÃļr boş", "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", + "enable_biometric_auth_description": "Biyometrik kimlik doğrulamasÄąnÄą etkinleştirmek için PIN kodu girin", "enabled": "Etkinleştirildi", "end_date": "Bitiş tarihi", "enter_wifi_name": "Wi-Fi adÄąnÄą girin", + "enter_your_pin_code": "Pin kodu girin", + "enter_your_pin_code_subtitle": "Kilitli klasÃļre erişmek için PIN kodunuzu 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Äą", @@ -817,7 +822,6 @@ "cant_get_number_of_comments": "YorumlarÄąn sayÄąsÄą alÄąnamadÄą", "cant_search_people": "Kişiler aranamÄąyor", "cant_search_places": "Mekanlar aranamÄąyor", - "cleared_jobs": "İşler temizlendi: {job}", "error_adding_assets_to_album": "AlbÃŧme varlÄąk ekleme hatasÄą", "error_adding_users_to_album": "AlbÃŧme kullanÄącÄą ekleme hatasÄą", "error_deleting_shared_user": "PaylaÅŸÄąlan kullanÄącÄą silme hatasÄą", @@ -826,7 +830,6 @@ "error_removing_assets_from_album": "Varlığı albÃŧmden silme hatasÄą, daha fazla detay için konsolu kontrol et", "error_selecting_all_assets": "BÃŧtÃŧn varlÄąklarÄą seçme hatasÄą", "exclusion_pattern_already_exists": "Bu dÄąÅŸlama modeli halihazÄąrda mevcut.", - "failed_job_command": "{command} komutu iş: {job} için tamamlanamadÄą", "failed_to_create_album": "AlbÃŧm oluşturulamadÄą", "failed_to_create_shared_link": "PaylaÅŸÄąlan bağlantÄą oluşturulamadÄą", "failed_to_edit_shared_link": "PaylaÅŸÄąlan bağlantÄą dÃŧzenlenemedi", @@ -834,16 +837,17 @@ "failed_to_keep_this_delete_others": "Bu Ãļğenin tutulmasÄą ve diğer Ãļğenin silinmesi başarÄąsÄąz oldu", "failed_to_load_asset": "VarlÄąk yÃŧklenemedi", "failed_to_load_assets": "VarlÄąklar yÃŧklenemedi", + "failed_to_load_notifications": "Bildirim yÃŧklenemedi", "failed_to_load_people": "Kişiler yÃŧklenemedi", "failed_to_remove_product_key": "ÜrÃŧn anahtarÄą kaldÄąrÄąlamadÄą", "failed_to_stack_assets": "VarlÄąklar yığınlanamadÄą", "failed_to_unstack_assets": "VarlÄąklarÄąn yığınÄą kaldÄąrÄąlamadÄą", + "failed_to_update_notification_status": "Bildirim durumu gÃŧncellenemedi", "import_path_already_exists": "Bu içe aktarma yolu halihazÄąrda mevcut.", "incorrect_email_or_password": "YanlÄąÅŸ e-posta veya şifre", "paths_validation_failed": "{paths, plural, one {# Yol} other {# Yollar}} doğrulanamadÄą", "profile_picture_transparent_pixels": "Profil resimleri şeffaf piksele sahip olamaz. LÃŧtfen resme yakÄąnlaştÄąrÄąn ve/veya resmi hareket ettirin.", "quota_higher_than_disk_size": "Disk boyutundan daha yÃŧksek bir kota belirlediniz", - "repair_unable_to_check_items": "{count, select, one {Öğe} other {Öğeler}} kontrol edilemedi", "unable_to_add_album_users": "KullanÄącÄąlar albÃŧme eklenemiyor", "unable_to_add_assets_to_shared_link": "VarlÄąklar paylaÅŸÄąlan bağlantÄąya eklenemiyor", "unable_to_add_comment": "Yorum eklenemiyor", @@ -861,7 +865,6 @@ "unable_to_change_visibility": "{count, plural, one {# kişi} other {# kişi}} için gÃļrÃŧnÃŧrlÃŧk değiştirilemedi", "unable_to_complete_oauth_login": "OAuth giriş işlemi tamamlanamadÄą", "unable_to_connect": "BağlanÄąlamÄąyor", - "unable_to_connect_to_server": "Sunucuya bağlanÄąlamÄąyor", "unable_to_copy_to_clipboard": "Panoya kopyalanamÄąyor, sayfaya https Ãŧzerinden eriştiğinizden emin olun", "unable_to_create_admin_account": "YÃļnetici hesabÄą oluşturulamÄąyor", "unable_to_create_api_key": "Yeni API anahtarÄą oluşturulamÄąyor", @@ -885,10 +888,6 @@ "unable_to_hide_person": "Kişi gizlenemiyor", "unable_to_link_motion_video": "Hareket videosu bağlanamÄąyor", "unable_to_link_oauth_account": "OAuth hesabÄą bağlanamÄąyor", - "unable_to_load_album": "AlbÃŧm yÃŧklenemiyor", - "unable_to_load_asset_activity": "VarlÄąk aktivitesi yÃŧklenemiyor", - "unable_to_load_items": "Öğeler yÃŧklenemiyor", - "unable_to_load_liked_status": "Beğenilen durum yÃŧklenemiyor", "unable_to_log_out_all_devices": "TÃŧm cihazlardan Ã§ÄąkÄąÅŸ yapÄąlamÄąyor", "unable_to_log_out_device": "Cihazdan Ã§ÄąkÄąÅŸ yapÄąlamÄąyor", "unable_to_login_with_oauth": "OAuth ile giriş yapÄąlamÄąyor", @@ -899,11 +898,9 @@ "unable_to_remove_album_users": "AlbÃŧm kullanÄącÄąlarÄą kaldÄąrÄąlamÄąyor", "unable_to_remove_api_key": "API anahtarÄą kaldÄąrÄąlamÄąyor", "unable_to_remove_assets_from_shared_link": "VarlÄąklar paylaÅŸÄąlan bağlantÄądan kaldÄąrÄąlamÄąyor", - "unable_to_remove_deleted_assets": "Silinmiş varlÄąklar kaldÄąrÄąlamÄąyor", "unable_to_remove_library": "KÃŧtÃŧphane kaldÄąrÄąlamadÄą", "unable_to_remove_partner": "Ortak kaldÄąrÄąlamÄąyor", "unable_to_remove_reaction": "Reaksiyon kaldÄąrÄąlamÄąyor", - "unable_to_repair_items": "Ögeler onarÄąlamadÄą", "unable_to_reset_password": "Şifre sÄąfÄąrlanamÄąyor", "unable_to_resolve_duplicate": "Çiftler çÃļzÃŧmlenemiyor", "unable_to_restore_assets": "VarlÄąklar geri yÃŧklenemiyor", @@ -1240,15 +1237,12 @@ "notifications_setting_description": "Bildirimleri yÃļnetin", "official_immich_resources": "Resmi Immich KaynaklarÄą", "offline": "Çevrim dÄąÅŸÄą", - "offline_paths": "Çevrim dÄąÅŸÄą yollar", - "offline_paths_description": "Bu sonuçlar, harici bir kÃŧtÃŧphaneye ait olmayan dosyalarÄąn elle silinmesinden kaynaklanÄąyor olabilir.", "ok": "Tamam", "oldest_first": "Eski olan Ãļnce", "on_this_device": "Bu cihazda", "onboarding": "Uyum SÃŧreci", "onboarding_privacy_description": "Şu (isteğe bağlÄą) Ãļzellikler harici hizmetlere dayanÄąr ve yÃļnetim ayarlarÄąndan herhangi bir zamanda devre dÄąÅŸÄą bÄąrakÄąlabilir.", "onboarding_theme_description": "İnstance’ınÄąz için bir renk temasÄą seçin. Bunu daha sonra ayarlarÄąnÄązdan değiştirebilirsiniz.", - "onboarding_welcome_description": "Şimdi, instance’ınÄązÄą bazÄą yaygÄąn ayarlarla kurmaya başlayalÄąm.", "onboarding_welcome_user": "Hoş geldin, {user}", "online": "Çevrimiçi", "only_favorites": "Sadece favoriler", @@ -1554,7 +1548,6 @@ "setting_image_viewer_title": "Resimler", "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: {duration}", "setting_notifications_notify_hours": "{count} saat", "setting_notifications_notify_immediately": "hemen", @@ -1735,7 +1728,6 @@ "to_parent": "Üst Ãļğeye git", "to_trash": "ÇÃļpe taÅŸÄą", "toggle_settings": "AyarlarÄą değiştir", - "toggle_theme": "Tema değiştir", "total": "Toplam", "total_usage": "Toplam kullanÄąm", "trash": "ÇÃļp", @@ -1775,8 +1767,6 @@ "unselect_all_duplicates": "TÃŧm çiftlerin seçimini kaldÄąr", "unstack": "YığınÄą kaldÄąr", "unstacked_assets_count": "{count, plural, one {# dosya} other {# dosya}} yığınÄą kaldÄąrÄąldÄą", - "untracked_files": "İzlenmeyen dosyalar", - "untracked_files_decription": "Bu dosyalar uygulama tarafÄąndan izlenmiyor. BaşarÄąsÄąz taÅŸÄąma işlemlerinin, kesintiye uğrayan yÃŧklemelerin sonuçlarÄą olabilir veya bir hata nedeniyle geride kalmÄąÅŸ olabilirler", "up_next": "SÄąradaki", "updated_password": "Şifreyi gÃŧncelle", "upload": "YÃŧkle", @@ -1813,11 +1803,6 @@ "version": "Versiyon", "version_announcement_closing": "ArkadaÅŸÄąnÄąz, Alex", "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_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_hover_setting": "Üzerinde durulduğunda video Ãļnizlemesi oynat", diff --git a/i18n/uk.json b/i18n/uk.json index 1862e82d8e..7b843ceff9 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -22,11 +22,11 @@ "add_partner": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", "add_path": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ҈ĐģŅŅ…", "add_photos": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ СĐŊŅ–ĐŧĐēи", + "add_tag": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Ņ‚ĐĩĐŗ", "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": "ДодаĐŊĐž Đ´Đž Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ", @@ -34,6 +34,7 @@ "added_to_favorites_count": "ДодаĐŊĐž {count, number} Đ´Đž ĐžĐąŅ€Đ°ĐŊĐžĐŗĐž", "admin": { "add_exclusion_pattern_description": "Đ”ĐžĐ´Đ°ĐšŅ‚Đĩ ŅˆĐ°ĐąĐģĐžĐŊи виĐēĐģŅŽŅ‡ĐĩĐŊҌ. ĐŸŅ–Đ´ŅŅ‚Đ°ĐŊОвĐēа С виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅĐŧ *, ** Ņ‚Đ° ? ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ”Ņ‚ŅŒŅŅ. ДĐģŅ Ņ–ĐŗĐŊĐžŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛ŅŅ–Ņ… Ņ„Đ°ĐšĐģŅ–Đ˛ ҃ ĐąŅƒĐ´ŅŒ-ŅĐēĐžĐŧ҃ ĐēĐ°Ņ‚Đ°ĐģĐžĐˇŅ– С Ņ–Đŧ'ŅĐŧ ÂĢRawÂģ, виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ \"**/Raw/**\". ДĐģŅ Ņ–ĐŗĐŊĐžŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛ŅŅ–Ņ… Ņ„Đ°ĐšĐģŅ–Đ˛, Ņ‰Đž СаĐēŅ–ĐŊŅ‡ŅƒŅŽŅ‚ŅŒŅŅ ĐŊа \".tif\", виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ \"**/*.tif\". ДĐģŅ Ņ–ĐŗĐŊĐžŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ°ĐąŅĐžĐģŅŽŅ‚ĐŊĐžĐŗĐž ҈ĐģŅŅ…Ņƒ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ \"/path/to/ignore/**\".", + "admin_user": "АдĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€", "asset_offline_description": "ĐĻĐĩĐš СОвĐŊŅ–ŅˆĐŊŅ–Đš ĐąŅ–ĐąĐģŅ–ĐžŅ‚Đĩ҇ĐŊиК аĐēŅ‚Đ¸Đ˛ ĐąŅ–ĐģҌ҈Đĩ ĐŊĐĩ СĐŊаКдĐĩĐŊĐž ĐŊа Đ´Đ¸ŅĐē҃ Ņ– ĐąŅƒĐ˛ ĐŋĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊиК Đ´Đž ҁĐŧŅ–Ņ‚ĐŊиĐēа. Đ¯ĐēŅ‰Đž Ņ„Đ°ĐšĐģ ĐąŅƒĐ˛ ĐŋĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊиК ҃ ĐŧĐĩĐļĐ°Ņ… ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи, ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Ņ‚Đĩ ŅĐ˛Ņ–Đš Ņ‚Đ°ĐšĐŧĐģаКĐŊ ĐŊа ĐŊĐ°ŅĐ˛ĐŊŅ–ŅŅ‚ŅŒ ĐŊĐžĐ˛ĐžĐŗĐž Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´ĐŊĐžĐŗĐž аĐēŅ‚Đ¸Đ˛Ņƒ. ЊОй Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ҆ĐĩĐš аĐēŅ‚Đ¸Đ˛, ĐŋĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ, Ņ‰Đž ҈ĐģŅŅ… Ņ„Đ°ĐšĐģ҃ ĐŊиĐļ҇Đĩ Đ´ĐžŅŅ‚ŅƒĐŋĐŊиК Đ´ĐģŅ Immich, Ņ– ĐŋŅ€ĐžŅĐēаĐŊŅƒĐšŅ‚Đĩ ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē҃.", "authentication_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–Ņ—", "authentication_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģŅ–ĐŊĐŊŅ ĐŋĐ°Ņ€ĐžĐģŅĐŧи, OAuth Ņ‚Đ° Ņ–ĐŊŅˆĐ¸Đŧи ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–Ņ—", @@ -44,9 +45,7 @@ "backup_database_enable_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ даĐŧĐŋи йаСи даĐŊĐ¸Ņ…", "backup_keep_last_amount": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐŊŅ–Ņ… даĐŧĐŋŅ–Đ˛, ŅĐēŅ– СйĐĩŅ€Ņ–ĐŗĐ°Ņ‚Đ¸", "backup_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ даĐŧĐŋа йаСи даĐŊĐ¸Ņ…", - "backup_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи даĐŧĐŋа йаСи даĐŊĐ¸Ņ…. ĐŸŅ€Đ¸ĐŧŅ–Ņ‚Đēа: ҆Җ СавдаĐŊĐŊŅ ĐŊĐĩ ĐēĐžĐŊŅ‚Ņ€ĐžĐģŅŽŅŽŅ‚ŅŒŅŅ, Ņ– ви ĐŊĐĩ ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ”Ņ‚Đĩ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ ĐŋŅ€Đž ĐŋĐžĐŧиĐģĐēи.", - "check_all": "ПĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đ¸Ņ‚Đ¸ Đ˛ŅĐĩ", - "cleanup": "ĐžŅ‡Đ¸Ņ‰ĐĩĐŊĐŊŅ", + "backup_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи даĐŧĐŋа йаСи даĐŊĐ¸Ņ….", "cleared_jobs": "ĐžŅ‡Đ¸Ņ‰ĐĩĐŊŅ– СавдаĐŊĐŊŅ Đ´ĐģŅ: {job}", "config_set_by_file": "НаĐģĐ°ŅˆŅ‚ĐžĐ˛Đ°ĐŊĐž Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ ĐēĐžĐŊŅ„Ņ–Đŗ-Ņ„Đ°ĐšĐģ҃", "confirm_delete_library": "Ви Đ´Ņ–ĐšŅĐŊĐž йаĐļĐ°Ņ”Ņ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē҃ \"{library}\"?", @@ -62,14 +61,12 @@ "disable_login": "ВиĐŧĐēĐŊŅƒŅ‚Đ¸ Đ˛Ņ…Ņ–Đ´", "duplicate_detection_job_description": "ЗаĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐĩ ĐŊĐ°Đ˛Ņ‡Đ°ĐŊĐŊŅ ĐŊа аĐēŅ‚Đ¸Đ˛Đ°Ņ… Đ´ĐģŅ Đ˛Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ ŅŅ…ĐžĐļĐ¸Ņ… ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊҌ. ЗаĐģĐĩĐļĐ¸Ņ‚ŅŒ Đ˛Ņ–Đ´ Ņ–ĐŊŅ‚ĐĩĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊĐžĐŗĐž ĐŋĐžŅˆŅƒĐē҃", "exclusion_pattern_description": "ШайĐģĐžĐŊи виĐēĐģŅŽŅ‡ĐĩĐŊҌ дОСвОĐģŅŅŽŅ‚ŅŒ Ņ–ĐŗĐŊĐžŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ Ņ„Đ°ĐšĐģи Ņ‚Đ° ĐŋаĐŋĐēи ĐŋŅ–Đ´ Ņ‡Đ°Ņ ҁĐēаĐŊŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Đ°ŅˆĐžŅ— ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи. ĐĻĐĩ ĐēĐžŅ€Đ¸ŅĐŊĐž, ŅĐēŅ‰Đž ҃ Đ˛Đ°Ņ Ņ” ĐŋаĐŋĐēи, ŅĐēŅ– ĐŧŅ–ŅŅ‚ŅŅ‚ŅŒ Ņ„Đ°ĐšĐģи, ŅĐēŅ– ви ĐŊĐĩ Ņ…ĐžŅ‡ĐĩŅ‚Đĩ Ņ–ĐŧĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸, ĐŊаĐŋŅ€Đ¸ĐēĐģад, RAW-Ņ„Đ°ĐšĐģи.", - "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": "ПовĐŊĐžŅ€ĐžĐˇĐŧŅ–Ņ€ĐŊĐĩ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ С видаĐģĐĩĐŊиĐŧи ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊиĐŧи, ŅĐēŅ– виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅŽŅ‚ŅŒŅŅ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐˇĐąŅ–ĐģҌ҈ĐĩĐŊĐŊŅ", @@ -174,7 +171,7 @@ "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_from_address_description": "ĐĐ´Ņ€ĐĩŅĐ° ĐĩĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊĐžŅ— ĐŋĐžŅˆŅ‚Đ¸ Đ˛Ņ–Đ´ĐŋŅ€Đ°Đ˛ĐŊиĐēа, ĐŊаĐŋŅ€Đ¸ĐēĐģад: \"Immich Photo Server \". ПĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ, Ņ‰Đž виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚Đĩ Đ°Đ´Ņ€Đĩҁ҃, С ŅĐēĐžŅ— ваĐŧ дОСвОĐģĐĩĐŊĐž ĐŊĐ°Đ´ŅĐ¸ĐģĐ°Ņ‚Đ¸ ĐģĐ¸ŅŅ‚Đ¸.", "notification_email_host_description": "ĐĨĐžŅŅ‚ ĐŋĐžŅˆŅ‚ĐžĐ˛ĐžĐŗĐž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° (ĐŊаĐŋŅ€Đ¸ĐēĐģад, smtp.immich.app)", "notification_email_ignore_certificate_errors": "Đ†ĐŗĐŊĐžŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋĐžĐŧиĐģĐēи ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚Đ°", "notification_email_ignore_certificate_errors_description": "Đ†ĐŗĐŊĐžŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋĐžĐŧиĐģĐēи ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đēи ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚Ņ–Đ˛ TLS (ĐŊĐĩ Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒŅ”Ņ‚ŅŒŅŅ)", @@ -198,7 +195,7 @@ "oauth_enable_description": "ĐŖĐ˛Ņ–ĐšŅ‚Đ¸ Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ OAuth", "oauth_mobile_redirect_uri": "URI ĐŧĐžĐąŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŊаĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐŊŅ", "oauth_mobile_redirect_uri_override": "ПĐĩŅ€ĐĩвиСĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ URI ĐŧĐžĐąŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŊаĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐŊŅ", - "oauth_mobile_redirect_uri_override_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸, ŅĐēŅ‰Đž OAuth-ĐŋŅ€ĐžĐ˛Đ°ĐšĐ´ĐĩŅ€ ĐŊĐĩ ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ” ĐŧĐžĐąŅ–ĐģҌĐŊиК URI, ŅĐē '{callback}'", + "oauth_mobile_redirect_uri_override_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸, ŅĐēŅ‰Đž OAuth-ĐŋŅ€ĐžĐ˛Đ°ĐšĐ´ĐĩŅ€ ĐŊĐĩ ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ” ĐŧĐžĐąŅ–ĐģҌĐŊиК URI, ŅĐē ''{callback}''", "oauth_settings": "OAuth", "oauth_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи Đ˛Ņ…ĐžĐ´Ņƒ ҇ĐĩŅ€ĐĩС OAuth", "oauth_settings_more_details": "ДĐģŅ ĐžŅ‚Ņ€Đ¸ĐŧаĐŊĐŊŅ Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛ĐžŅ— Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ— ĐŋŅ€Đž Ņ†ŅŽ Ņ„ŅƒĐŊĐēŅ†Ņ–ŅŽ, СвĐĩŅ€ĐŊŅ–Ņ‚ŅŒŅŅ Đ´Đž Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Ņ–Ņ—.", @@ -207,11 +204,9 @@ "oauth_storage_quota_claim": "Đ—Đ°ŅĐ˛Đēа ĐŊа ĐēĐ˛ĐžŅ‚Ņƒ ĐŊа СйĐĩŅ€Ņ–ĐŗĐ°ĐŊĐŊŅ", "oauth_storage_quota_claim_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Đ˛ŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐēĐ˛ĐžŅ‚Ņƒ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ° ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° ĐŊа СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ҆ҖҔҗ виĐŧĐžĐŗĐ¸.", "oauth_storage_quota_default": "ĐšĐ˛ĐžŅ‚Đ° Са СаĐŧĐžĐ˛Ņ‡ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ (GiB)", - "oauth_storage_quota_default_description": "ĐšĐ˛ĐžŅ‚Đ° в GiB, Ņ‰Đž виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ, ĐēĐžĐģи ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊĐĩ ĐŊадаĐŊĐž (ввĐĩĐ´Ņ–Ņ‚ŅŒ 0 Đ´ĐģŅ ĐŊĐĩОйĐŧĐĩĐļĐĩĐŊĐžŅ— ĐēĐ˛ĐžŅ‚Đ¸).", + "oauth_storage_quota_default_description": "ĐšĐ˛ĐžŅ‚Đ° в GiB, Ņ‰Đž виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ, ĐēĐžĐģи ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊĐĩ ĐŊадаĐŊĐž.", "oauth_timeout": "ĐĸаКĐŧ-Đ°ŅƒŅ‚ Đ´ĐģŅ СаĐŋĐ¸Ņ‚Ņ–Đ˛", "oauth_timeout_description": "МаĐēŅĐ¸ĐŧаĐģҌĐŊиК Ņ‡Đ°Ņ ĐžŅ‡Ņ–ĐēŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´Ņ– в ĐŧŅ–ĐģҖҁĐĩĐē҃ĐŊĐ´Đ°Ņ…", - "offline_paths": "НĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊŅ– ШĐģŅŅ…Đ¸", - "offline_paths_description": "ĐĻŅ– Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Đ¸ ĐŧĐžĐļŅƒŅ‚ŅŒ ĐąŅƒŅ‚Đ¸ ĐŋОв'ŅĐˇĐ°ĐŊŅ– С Ņ€ŅƒŅ‡ĐŊиĐŧ видаĐģĐĩĐŊĐŊŅĐŧ Ņ„Đ°ĐšĐģŅ–Đ˛, ŅĐēŅ– ĐŊĐĩ Đ˛Ņ…ĐžĐ´ŅŅ‚ŅŒ Đ´Đž СОвĐŊŅ–ŅˆĐŊŅŒĐžŅ— ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи.", "password_enable_description": "ĐŖĐ˛Ņ–ĐšŅ‚Đ¸ Са ĐĩĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊĐžŅŽ ĐŋĐžŅˆŅ‚ĐžŅŽ Ņ‚Đ° ĐŋĐ°Ņ€ĐžĐģĐĩĐŧ", "password_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Ņ…ĐžĐ´Ņƒ С ĐŋĐ°Ņ€ĐžĐģĐĩĐŧ", "password_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи Đ˛Ņ…ĐžĐ´Ņƒ Са ĐŋĐ°Ņ€ĐžĐģĐĩĐŧ", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "ОĐŊОвĐģĐĩĐŊĐŊŅ Đ˛ŅŅ–Ņ… ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē", "registration": "Đ ĐĩŅ”ŅŅ‚Ņ€Đ°Ņ†Ņ–Ņ адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", "registration_description": "ĐžŅĐēŅ–ĐģҌĐēи ви ĐŋĐĩŅ€ŅˆĐ¸Đš ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡ в ŅĐ¸ŅŅ‚ĐĩĐŧŅ–, ви ĐąŅƒĐ´ĐĩŅ‚Đĩ ĐŋŅ€Đ¸ĐˇĐŊĐ°Ņ‡ĐĩĐŊŅ– АдĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ Ņ– Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´Đ°Ņ‚Đ¸ĐŧĐĩŅ‚Đĩ Са адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚Đ¸Đ˛ĐŊŅ– СавдаĐŊĐŊŅ, а Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛Ņ– ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ– ĐąŅƒĐ´ŅƒŅ‚ŅŒ ŅŅ‚Đ˛ĐžŅ€ĐĩĐŊŅ– ваĐŧи.", - "repair_all": "Đ’Ņ–Đ´Ņ€ĐĩĐŧĐžĐŊŅ‚ŅƒĐšŅ‚Đĩ Đ˛ŅĐĩ", - "repair_matched_items": "Đ’Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´Đ°Ņ” {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} few {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸} many {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛}}", - "repaired_items": "Đ’Ņ–Đ´ĐŊОвĐģĐĩĐŊĐž {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} few {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸} many {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛}}", "require_password_change_on_login": "ВиĐŧĐ°ĐŗĐ°Ņ‚Đ¸ СĐŧŅ–ĐŊи ĐŋĐ°Ņ€ĐžĐģŅ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° ĐŋŅ€Đ¸ ĐŋĐĩŅ€ŅˆĐžĐŧ҃ Đ˛Ņ…ĐžĐ´Ņ–", "reset_settings_to_default": "ĐĄĐēиĐŊŅƒŅ‚Đ¸ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Đ´Đž ĐˇĐ°Đ˛ĐžĐ´ŅŅŒĐēĐ¸Ņ… СĐŊĐ°Ņ‡ĐĩĐŊҌ", "reset_settings_to_recent_saved": "ĐĄĐēиĐŊŅƒŅ‚Đ¸ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Đ´Đž ĐŊĐĩдавĐŊĐž СйĐĩŅ€ĐĩĐļĐĩĐŊĐ¸Ņ… ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊҌ", @@ -252,7 +244,6 @@ "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": "КĐĩŅ€ŅƒĐšŅ‚Đĩ ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€ĐžŅŽ Ņ‚ĐĩĐē Ņ‚Đ° Ņ–ĐŧĐĩĐŊĐĩĐŧ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐžĐŗĐž Ņ„Đ°ĐšĐģ҃", @@ -264,16 +255,14 @@ "template_email_invite_album": "ШайĐģĐžĐŊ СаĐŋŅ€ĐžŅˆĐĩĐŊĐŊŅ Đ´Đž аĐģŅŒĐąĐžĐŧ҃", "template_email_preview": "ĐŸŅ€ĐĩĐ˛â€™ŅŽ", "template_email_settings": "ШайĐģĐžĐŊи ĐĩĐģ. ĐģĐ¸ŅŅ‚Ņ–Đ˛", - "template_email_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ ŅˆĐ°ĐąĐģĐžĐŊаĐŧи ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊҌ ĐĩĐģ. ĐŋĐžŅˆŅ‚Đ¸", "template_email_update_album": "ОĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ŅˆĐ°ĐąĐģĐžĐŊ аĐģŅŒĐąĐžĐŧ҃", "template_email_welcome": "ШайĐģĐžĐŊ Đ˛Ņ–Ņ‚Đ°ĐģҌĐŊĐžĐŗĐž ĐĩĐģ. ĐģĐ¸ŅŅ‚Đ°", "template_settings": "ШайĐģĐžĐŊи ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊҌ", - "template_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ ŅˆĐ°ĐąĐģĐžĐŊаĐŧи Đ´ĐģŅ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊҌ.", + "template_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ ŅˆĐ°ĐąĐģĐžĐŊаĐŧи Đ´ĐģŅ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊҌ", "theme_custom_css_settings": "ВĐģĐ°ŅĐŊиК CSS", "theme_custom_css_settings_description": "ĐšĐ°ŅĐēадĐŊŅ– Ņ‚Đ°ĐąĐģĐ¸Ņ†Ņ– ŅŅ‚Đ¸ĐģŅ–Đ˛ дОСвОĐģŅŅŽŅ‚ŅŒ ĐŊĐ°ŅŅ‚Ņ€ĐžŅŽĐ˛Đ°Ņ‚Đ¸ диСаКĐŊ Immich.", "theme_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Ņ‚ĐĩĐŧи", "theme_settings_description": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋĐĩŅ€ŅĐžĐŊаĐģŅ–ĐˇĐ°Ņ†Ņ–Ņ— вĐĩĐą-Ņ–ĐŊŅ‚ĐĩҀ҄ĐĩĐšŅŅƒ Immich", - "these_files_matched_by_checksum": "ĐĻŅ– Ņ„Đ°ĐšĐģи Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´Đ°ŅŽŅ‚ŅŒ ŅĐ˛ĐžŅ—Đŧ ĐēĐžĐŊŅ‚Ņ€ĐžĐģҌĐŊиĐŧ ҁ҃ĐŧаĐŧ", "thumbnail_generation_job": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€", "thumbnail_generation_job_description": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ вĐĩĐģиĐēŅ–, ĐŧаĐģŅ– Ņ‚Đ° Ņ€ĐžĐˇĐŧĐ¸Ņ‚Ņ– ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸ Đ´ĐģŅ ĐēĐžĐļĐŊĐžĐŗĐž Ņ€ĐĩŅŅƒŅ€ŅŅƒ, а Ņ‚Đ°ĐēĐžĐļ ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸ Đ´ĐģŅ ĐēĐžĐļĐŊĐžŅ— ĐžŅĐžĐąĐ¸", "transcoding_acceleration_api": "API ĐŋŅ€Đ¸ŅĐēĐžŅ€ĐĩĐŊĐŊŅ", @@ -301,10 +290,9 @@ "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": "КодĐĩĐē HEVC", "transcoding_max_b_frames": "МаĐēŅĐ¸ĐŧаĐģҌĐŊа ĐēŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ĐŋŅ€ĐžĐŧŅ–ĐļĐŊĐ¸Ņ… ĐēĐ°Đ´Ņ€Ņ–Đ˛", "transcoding_max_b_frames_description": "Đ’Đ¸Ņ‰Ņ– СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ĐŋĐžĐēŅ€Đ°Ņ‰ŅƒŅŽŅ‚ŅŒ ĐĩŅ„ĐĩĐēŅ‚Đ¸Đ˛ĐŊŅ–ŅŅ‚ŅŒ ŅŅ‚Đ¸ŅĐŊĐĩĐŊĐŊŅ, аĐģĐĩ ĐˇĐąŅ–ĐģŅŒŅˆŅƒŅŽŅ‚ŅŒ Ņ‡Đ°Ņ ĐēĐžĐ´ŅƒĐ˛Đ°ĐŊĐŊŅ. МоĐļŅƒŅ‚ŅŒ ĐąŅƒŅ‚Đ¸ ĐŊĐĩҁ҃ĐŧҖҁĐŊŅ– С аĐŋĐ°Ņ€Đ°Ņ‚ĐŊиĐŧ ĐŋŅ€Đ¸ŅĐēĐžŅ€ĐĩĐŊĐŊŅĐŧ ĐŊа ŅŅ‚Đ°Ņ€Đ¸Ņ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŅ…. ЗĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ 0 виĐŧиĐēĐ°Ņ” B-҄ҀĐĩĐšĐŧи, а -1 Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŊаĐģĐ°ŅˆŅ‚ĐžĐ˛ŅƒŅ” ҆Đĩ СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ.", "transcoding_max_bitrate": "МаĐēŅĐ¸ĐŧаĐģҌĐŊиК ĐąŅ–Ņ‚Ņ€ĐĩĐšŅ‚", @@ -342,8 +330,6 @@ "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 {# Đ´ĐĩĐŊҌ} few {# Đ´ĐŊŅ–} many {# Đ´ĐŊŅ–Đ˛} other {# Đ´ĐŊŅ–Đ˛}}.", "user_delete_delay_settings": "Đ’Ņ–Đ´ĐēĐģадĐĩĐŊĐĩ видаĐģĐĩĐŊĐŊŅ", @@ -402,10 +388,6 @@ "album_remove_user": "ВидаĐģĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°?", "album_remove_user_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ {user}?", "album_share_no_users": "ĐĄŅ…ĐžĐļĐĩ, ви ĐŋĐžĐ´Ņ–ĐģиĐģĐ¸ŅŅ Ņ†Đ¸Đŧ аĐģŅŒĐąĐžĐŧĐžĐŧ С ŅƒŅŅ–Đŧа ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°Đŧи айО ҃ Đ˛Đ°Ņ ĐŊĐĩĐŧĐ°Ņ” ĐļОдĐŊĐžĐŗĐž ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°, С ŅĐēиĐŧ ĐŧĐžĐļĐŊа ĐąŅƒĐģĐž Đą ĐŋĐžĐ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ.", - "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}", @@ -421,6 +403,9 @@ "album_with_link_access": "ĐŸĐžĐ´Ņ–ĐģŅ–Ņ‚ŅŒŅŅ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅĐŧ ĐŊа аĐģŅŒĐąĐžĐŧ, Ņ‰ĐžĐą Đ˛Đ°ŅˆŅ– Đ´Ņ€ŅƒĐˇŅ– ĐŧĐžĐŗĐģи ĐšĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸.", "albums": "АĐģŅŒĐąĐžĐŧи", "albums_count": "{count, plural, one {1 аĐģŅŒĐąĐžĐŧ} few {{count, number} аĐģŅŒĐąĐžĐŧи} many {{count, number} аĐģŅŒĐąĐžĐŧŅ–Đ˛} other {{count, number} аĐģŅŒĐąĐžĐŧŅ–Đ˛}}", + "albums_default_sort_order": "ĐŸĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚ŅƒĐ˛Đ°ĐŊĐŊŅ аĐģŅŒĐąĐžĐŧŅ–Đ˛ Са СаĐŧĐžĐ˛Ņ‡ŅƒĐ˛Đ°ĐŊŅĐŧ", + "albums_default_sort_order_description": "ĐŸĐžŅ‡Đ°Ņ‚ĐēОвиК ĐŋĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ŅŅ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ ĐŊĐžĐ˛Đ¸Ņ… аĐģŅŒĐąĐžĐŧŅ–Đ˛.", + "albums_feature_description": "КоĐģĐĩĐē҆Җҗ Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛, ŅĐēŅ– ĐŧĐžĐļĐŊа ҁĐŋŅ–ĐģҌĐŊĐž виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ С Ņ–ĐŊŅˆĐ¸Đŧи ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°Đŧи.", "all": "ĐŖŅŅ–", "all_albums": "ĐŖŅŅ– аĐģŅŒĐąĐžĐŧи", "all_people": "ĐŖŅŅ– ĐģŅŽĐ´Đ¸", @@ -479,9 +464,12 @@ "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 {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛}} Đ´Đž {hasName, select, true {{name}} other {ĐŊĐžĐ˛ĐžĐŗĐž аĐģŅŒĐąĐžĐŧ҃}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Đ ĐĩŅŅƒŅ€Ņ} other {Đ ĐĩŅŅƒŅ€ŅĐ¸}} ĐŊĐĩ ĐŧĐžĐļĐŊа Đ´ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đž аĐģŅŒĐąĐžĐŧ҃", "assets_count": "{count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", "assets_deleted_permanently": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊĐž", "assets_deleted_permanently_from_server": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) видаĐģĐĩĐŊĐž ĐŊаСавĐļди С ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Immich", + "assets_downloaded_failed": "{count, plural, one {ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž # Ņ„Đ°ĐšĐģ — {error} Ņ„Đ°ĐšĐģ ĐŊĐĩ вдаĐģĐžŅŅ} other {ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž # Ņ„Đ°ĐšĐģŅ–Đ˛ — {error} Ņ„Đ°ĐšĐģŅ–Đ˛ ĐŊĐĩ вдаĐģĐžŅŅ}}", + "assets_downloaded_successfully": "{count, plural, one {ĐŖŅĐŋŅ–ŅˆĐŊĐž СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž # Ņ„Đ°ĐšĐģ} other {ĐŖŅĐŋŅ–ŅˆĐŊĐž СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž # Ņ„Đ°ĐšĐģŅ–Đ˛}}", "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 {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", @@ -496,6 +484,7 @@ "authorized_devices": "ĐĐ˛Ņ‚ĐžŅ€Đ¸ĐˇĐžĐ˛Đ°ĐŊŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—", "automatic_endpoint_switching_subtitle": "ĐŸŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ŅŅ ĐģĐžĐēаĐģҌĐŊĐž ҇ĐĩŅ€ĐĩС СаСĐŊĐ°Ņ‡ĐĩĐŊ҃ Wi-Fi ĐŧĐĩŅ€ĐĩĐļ҃, ĐēĐžĐģи ҆Đĩ ĐŧĐžĐļĐģивО, Ņ– виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊŅ– С'Ņ”Đ´ĐŊаĐŊĐŊŅ в Ņ–ĐŊŅˆĐ¸Ņ… виĐŋадĐēĐ°Ņ…", "automatic_endpoint_switching_title": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ ĐŋĐĩŅ€ĐĩĐŧиĐēаĐŊĐŊŅ URL", + "autoplay_slideshow": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ Đ˛Ņ–Đ´Ņ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ ҁĐģĐ°ĐšĐ´ŅˆĐžŅƒ", "back": "Назад", "back_close_deselect": "ПовĐĩŅ€ĐŊŅƒŅ‚Đ¸ŅŅ, СаĐēŅ€Đ¸Ņ‚Đ¸ айО ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸ Đ˛Đ¸ĐąŅ–Ņ€", "background_location_permission": "Đ”ĐžĐˇĐ˛Ņ–Đģ Đ´Đž ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ ҃ Ņ„ĐžĐŊŅ–", @@ -539,6 +528,7 @@ "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": "ЗаĐģĐ¸ŅˆĐžĐē", @@ -562,6 +552,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": "РОСĐŧĐ¸Ņ‚Đ¸Đš Ņ„ĐžĐŊ", @@ -572,21 +566,17 @@ "bulk_keep_duplicates_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ СаĐģĐ¸ŅˆĐ¸Ņ‚Đ¸ {count, plural, one {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ} few {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}? ĐĻĐĩ дОСвОĐģĐ¸Ņ‚ŅŒ Đ˛Đ¸Ņ€Ņ–ŅˆĐ¸Ņ‚Đ¸ Đ˛ŅŅ– ĐŗŅ€ŅƒĐŋи Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Ņ–Đ˛ ĐąĐĩС видаĐģĐĩĐŊĐŊŅ Ņ‡ĐžĐŗĐž-ĐŊĐĩĐąŅƒĐ´ŅŒ.", "bulk_trash_duplicates_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ виĐēиĐŊŅƒŅ‚Đ¸ в ҁĐŧŅ–Ņ‚ĐŊиĐē {count, plural, one {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ} few {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}} ĐŧĐ°ŅĐžĐ˛Đž? ĐĻĐĩ СаĐģĐ¸ŅˆĐ¸Ņ‚ŅŒ ĐŊĐ°ĐšĐąŅ–ĐģŅŒŅˆĐ¸Đš Ņ€ĐĩŅŅƒŅ€Ņ ҃ ĐēĐžĐļĐŊŅ–Đš ĐŗŅ€ŅƒĐŋŅ– Ņ– виĐēиĐŊĐĩ в ҁĐŧŅ–Ņ‚ĐŊиĐē Đ˛ŅŅ– Ņ–ĐŊŅˆŅ– Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸.", "buy": "ĐŸŅ€Đ¸Đ´ĐąĐ°ĐšŅ‚Đĩ Immich", - "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": "Đ”ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ({count})", - "cache_settings_image_cache_size": "РОСĐŧŅ–Ņ€ ĐēĐĩŅˆĐžĐ˛Đ°ĐŊĐ¸Ņ… ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊҌ ({count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸)", "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": "КоĐŊŅ‚Ņ€ĐžĐģŅŽŅ” ĐēĐĩŅˆŅƒĐ˛Đ°ĐŊĐŊŅ ҃ ĐŧĐžĐąŅ–ĐģҌĐŊĐžĐŧ҃ ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐē҃", - "cache_settings_thumbnail_size": "РОСĐŧŅ–Ņ€ ĐēĐĩŅˆĐžĐ˛Đ°ĐŊĐ¸Ņ… ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€ ({count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸)", "cache_settings_tile_subtitle": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋОвĐĩĐ´Ņ–ĐŊĐēĐžŅŽ ĐģĐžĐēаĐģҌĐŊĐžĐŗĐž ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ°", "cache_settings_tile_title": "ЛоĐēаĐģҌĐŊĐĩ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đĩ", "cache_settings_title": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐēĐĩŅˆŅƒĐ˛Đ°ĐŊĐŊŅ", @@ -599,7 +589,10 @@ "cannot_merge_people": "НĐĩĐŧĐžĐļĐģивО Ой'Ņ”Đ´ĐŊĐ°Ņ‚Đ¸ ĐģŅŽĐ´ĐĩĐš", "cannot_undo_this_action": "Ви ĐŊĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸ Ņ†ŅŽ Đ´Ņ–ŅŽ!", "cannot_update_the_description": "НĐĩĐŧĐžĐļĐģивО ĐžĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐžĐŋĐ¸Ņ", + "cast": "ĐĸŅ€Đ°ĐŊҁĐģŅŽĐ˛Đ°Ņ‚Đ¸", + "cast_description": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ– ĐŧŅ–ŅŅ†Ņ Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Ņ–Ņ—", "change_date": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ Đ´Đ°Ņ‚Ņƒ", + "change_description": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐžĐŋĐ¸Ņ", "change_display_order": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŋĐžŅ€ŅĐ´ĐžĐē Đ˛Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", "change_expiration_time": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ Ņ‚ĐĩŅ€ĐŧŅ–ĐŊ Đ´Ņ–Ņ—", "change_location": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", @@ -615,7 +608,6 @@ "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 Ņ‚Đ° ĐŋҖҁĐģŅ Ņ‚ĐžĐŗĐž, ŅĐē Đ˛ŅŅ– аĐēŅ‚Đ¸Đ˛Đ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ СаваĐŊŅ‚Đ°ĐļĐĩĐŊŅ– ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€. ĐŸŅ€ĐžŅ†Đĩҁ ĐŧĐžĐļĐĩ СаКĐŊŅŅ‚Đ¸ ĐēŅ–ĐģҌĐēа Ņ…Đ˛Đ¸ĐģиĐŊ.", @@ -627,6 +619,7 @@ "clear_all_recent_searches": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ĐžŅŅ‚Đ°ĐŊĐŊŅ– ĐŋĐžŅˆŅƒĐēĐžĐ˛Ņ– СаĐŋĐ¸Ņ‚Đ¸", "clear_message": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐŋĐžĐ˛Ņ–Đ´ĐžĐŧĐģĐĩĐŊĐŊŅ", "clear_value": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ", + "client_cert_dialog_msg_confirm": "ОĐē", "client_cert_enter_password": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", "client_cert_import": "ІĐŧĐŋĐžŅ€Ņ‚", "client_cert_import_success_msg": "КĐģŅ–Ņ”ĐŊŅ‚ŅŅŒĐēиК ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚ Ņ–ĐŧĐŋĐžŅ€Ņ‚ĐžĐ˛Đ°ĐŊĐž", @@ -654,10 +647,13 @@ "confirm_keep_this_delete_others": "ĐŖŅŅ– Ņ–ĐŊŅˆŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸ в ҁ҂ĐĩĐē҃ ĐąŅƒĐ´Đĩ видаĐģĐĩĐŊĐž, ĐžĐēҀҖĐŧ Ņ†ŅŒĐžĐŗĐž Ņ€ĐĩŅŅƒŅ€ŅŅƒ. Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐŋŅ€ĐžĐ´ĐžĐ˛ĐļĐ¸Ņ‚Đ¸?", "confirm_new_pin_code": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ŅŒŅ‚Đĩ ĐŊОвиК PIN-ĐēОд", "confirm_password": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ", + "confirm_tag_face": "БаĐļĐ°Ņ”Ņ‚Đĩ ĐŋОСĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸ ҆Đĩ ОйĐģĐ¸Ņ‡Ņ‡Ņ ŅĐē {name}?", + "confirm_tag_face_unnamed": "БаĐļĐ°Ņ”Ņ‚Đĩ ĐŋОСĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸ ҆Đĩ ОйĐģĐ¸Ņ‡Ņ‡Ņ?", + "connected_device": "ĐŸŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊиК ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš", + "connected_to": "ĐŸŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊĐž Đ´Đž", "contain": "ĐœŅ–ŅŅ‚Đ¸Ņ‚Đ¸", "context": "КоĐŊŅ‚ĐĩĐēҁ҂", "continue": "ĐŸŅ€ĐžĐ´ĐžĐ˛ĐļŅƒĐšŅ‚Đĩ", - "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,6 +691,7 @@ "create_tag_description": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ĐŊОвиК Ņ‚ĐĩĐŗ. ДĐģŅ вĐēĐģадĐĩĐŊĐ¸Ņ… Ņ‚ĐĩĐŗŅ–Đ˛ вĐēаĐļŅ–Ņ‚ŅŒ ĐŋОвĐŊиК ҈ĐģŅŅ… Ņ‚ĐĩĐŗĐ°, вĐēĐģŅŽŅ‡Đ°ŅŽŅ‡Đ¸ ҁĐģĐĩŅˆŅ–.", "create_user": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "created": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐž", + "created_at": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐž", "crop": "ĐšĐ°Đ´Ņ€ŅƒĐ˛Đ°Ņ‚Đ¸", "curated_object_page_title": "Đ Đĩ҇Җ", "current_device": "ĐŸĐžŅ‚ĐžŅ‡ĐŊиК ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš", @@ -702,10 +699,14 @@ "current_server_address": "ĐŸĐžŅ‚ĐžŅ‡ĐŊа Đ°Đ´Ņ€ĐĩŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "custom_locale": "ĐšĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ†ŅŒĐēиК Ņ€ĐĩĐŗŅ–ĐžĐŊ", "custom_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚Đ¸ Ņ‚Đ° Ņ‡Đ¸ŅĐģа С ŅƒŅ€Đ°Ņ…ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ ĐŧОви Ņ‚Đ° Ņ€ĐĩĐŗŅ–ĐžĐŊ҃", + "daily_title_text_date": "Е, МММ Đ´Đ´", + "daily_title_text_date_year": "Е, МММ Đ´Đ´, ҀҀҀҀ", "dark": "ĐĸĐĩĐŧĐŊиК", + "darkTheme": "ПĐĩŅ€ĐĩĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ‚ĐĩĐŧĐŊ҃ Ņ‚ĐĩĐŧ҃", "date_after": "Đ”Đ°Ņ‚Đ° ĐŋҖҁĐģŅ", "date_and_time": "Đ”Đ°Ņ‚Đ° Ņ– Ņ‡Đ°Ņ", "date_before": "Đ”Đ°Ņ‚Đ° Đ´Đž", + "date_format": "Е, ЛЛЛ Đ´, Ņ€ â€ĸ Đŗ:ĐŧĐŧ дĐŋ", "date_of_birth_saved": "Đ”Đ°Ņ‚Đ° ĐŊĐ°Ņ€ĐžĐ´ĐļĐĩĐŊĐŊŅ ҃ҁĐŋŅ–ŅˆĐŊĐž СйĐĩŅ€ĐĩĐļĐĩĐŊа", "date_range": "ĐŸŅ€ĐžĐŧŅ–ĐļĐžĐē Ņ‡Đ°ŅŅƒ", "day": "ДĐĩĐŊҌ", @@ -747,7 +748,9 @@ "direction": "НаĐŋŅ€ŅĐŧ", "disabled": "ВиĐŧĐēĐŊĐĩĐŊĐž", "disallow_edits": "Đ—Đ°ĐąĐžŅ€ĐžĐŊĐ¸Ņ‚Đ¸ Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°ĐŊĐŊŅ", + "discord": "Discord", "discover": "Đ’Đ¸ŅĐ˛Đ¸Ņ‚Đ¸", + "discovered_devices": "Đ’Đ¸ŅĐ˛ĐģĐĩĐŊŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—", "dismiss_all_errors": "ĐŸŅ€ĐžĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ĐŋĐžĐŧиĐģĐēи", "dismiss_error": "ĐŸŅ€ĐžĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ ĐŋĐžĐŧиĐģĐē҃", "display_options": "ĐŸĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€Đ¸ Đ˛Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", @@ -763,7 +766,6 @@ "download_enqueue": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŋĐžŅŅ‚Đ°Đ˛ĐģĐĩĐŊĐž в ҇ĐĩŅ€ĐŗŅƒ", "download_error": "ПоĐŧиĐģĐēа СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", "download_failed": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŊĐĩ вдаĐģĐžŅŅ", - "download_filename": "Ņ„Đ°ĐšĐģ: {filename}", "download_finished": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ СаĐēŅ–ĐŊ҇ĐĩĐŊĐž", "download_include_embedded_motion_videos": "Đ’ĐąŅƒĐ´ĐžĐ˛Đ°ĐŊŅ– Đ˛Ņ–Đ´ĐĩĐž", "download_include_embedded_motion_videos_description": "ВĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ Đ˛Ņ–Đ´ĐĩĐž, Đ˛ĐąŅƒĐ´ĐžĐ˛Đ°ĐŊŅ– в Ņ€ŅƒŅ…ĐžĐŧŅ– Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ—, ŅĐē ĐžĐēŅ€ĐĩĐŧиК Ņ„Đ°ĐšĐģ", @@ -787,6 +789,8 @@ "edit_avatar": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ Đ°Đ˛Đ°Ņ‚Đ°Ņ€", "edit_date": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚Ņƒ", "edit_date_and_time": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚Ņƒ Ņ‚Đ° Ņ‡Đ°Ņ", + "edit_description": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ĐžĐŋĐ¸Ņ", + "edit_description_prompt": "Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, вийĐĩŅ€Ņ–Ņ‚ŅŒ ĐŊОвиК ĐžĐŋĐ¸Ņ:", "edit_exclusion_pattern": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ŅˆĐ°ĐąĐģĐžĐŊ виĐēĐģŅŽŅ‡ĐĩĐŊҌ", "edit_faces": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°ĐŊĐŊŅ ОйĐģĐ¸Ņ‡", "edit_import_path": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ҈ĐģŅŅ… Ņ–ĐŧĐŋĐžŅ€Ņ‚Ņƒ", @@ -807,19 +811,24 @@ "editor_crop_tool_h2_aspect_ratios": "ĐŸŅ€ĐžĐŋĐžŅ€Ņ†Ņ–Ņ— ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", "editor_crop_tool_h2_rotation": "ĐžŅ€Ņ–Ņ”ĐŊŅ‚Đ°Ņ†Ņ–Ņ", "email": "ЕĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊа ĐŋĐžŅˆŅ‚Đ°", + "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}", + "error_tag_face_bounding_box": "ПоĐŧиĐģĐēа ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐŋОСĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ОйĐģĐ¸Ņ‡Ņ‡Ņ – ĐŊĐĩ вдаĐģĐžŅŅ ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ¸ Ņ€Đ°ĐŧĐēи", "error_title": "ПоĐŧиĐģĐēа: Ņ‰ĐžŅŅŒ ĐŋŅ–ŅˆĐģĐž ĐŊĐĩ Ņ‚Đ°Đē", "errors": { "cannot_navigate_next_asset": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ ĐŋĐĩŅ€ĐĩĐšŅ‚Đ¸ Đ´Đž ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊĐžĐŗĐž Ņ€ĐĩŅŅƒŅ€ŅŅƒ", @@ -832,7 +841,6 @@ "cant_get_number_of_comments": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ ĐēŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Ņ–Đ˛", "cant_search_people": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ виĐēĐžĐŊĐ°Ņ‚Đ¸ ĐŋĐžŅˆŅƒĐē ĐģŅŽĐ´ĐĩĐš", "cant_search_places": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ виĐēĐžĐŊĐ°Ņ‚Đ¸ ĐŋĐžŅˆŅƒĐē ĐŧŅ–ŅŅ†ŅŒ", - "cleared_jobs": "ĐžŅ‡Đ¸Ņ‰ĐĩĐŊĐž СавдаĐŊĐŊŅ Đ´ĐģŅ: {job}", "error_adding_assets_to_album": "ПоĐŧиĐģĐēа дОдаваĐŊĐŊŅ Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛ Đ´Đž аĐģŅŒĐąĐžĐŧ҃", "error_adding_users_to_album": "ПоĐŧиĐģĐēа дОдаваĐŊĐŊŅ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛ Đ´Đž аĐģŅŒĐąĐžĐŧ҃", "error_deleting_shared_user": "ПоĐŧиĐģĐēа ĐŋŅ–Đ´ Ņ‡Đ°Ņ видаĐģĐĩĐŊĐŊŅ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° ĐˇŅ– ĐˇĐ°ĐŗĐ°ĐģҌĐŊиĐŧ Đ´ĐžŅŅ‚ŅƒĐŋĐžĐŧ", @@ -841,7 +849,6 @@ "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": "НĐĩ вдаĐģĐžŅŅ Đ˛Ņ–Đ´Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ҁĐŋŅ–ĐģҌĐŊĐĩ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", @@ -860,7 +867,6 @@ "paths_validation_failed": "{paths, plural, one {# ҈ĐģŅŅ…} few {# ҈ĐģŅŅ…Đ¸} many {# ҈ĐģŅŅ…Ņ–Đ˛} 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": "НĐĩĐŧĐžĐļĐģивО Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€", @@ -872,13 +878,13 @@ "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 {# ĐžŅŅ–Đą} other {# ĐģŅŽĐ´ĐĩĐš}}", "unable_to_complete_oauth_login": "НĐĩĐŧĐžĐļĐģивО СавĐĩŅ€ŅˆĐ¸Ņ‚Đ¸ Đ˛Ņ…Ņ–Đ´ ҇ĐĩŅ€ĐĩС OAuth", "unable_to_connect": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ¸Ņ‚Đ¸ŅŅ", - "unable_to_connect_to_server": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ¸Ņ‚Đ¸ŅŅ Đ´Đž ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "unable_to_copy_to_clipboard": "НĐĩĐŧĐžĐļĐģивО ҁĐēĐžĐŋŅ–ŅŽĐ˛Đ°Ņ‚Đ¸ в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧŅ–ĐŊ҃. ПĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ, Ņ‰Đž ви ĐˇĐ°Ņ…ĐžĐ´Đ¸Ņ‚Đĩ ĐŊа ŅŅ‚ĐžŅ€Ņ–ĐŊĐē҃ ҇ĐĩŅ€ĐĩС HTTPS", "unable_to_create_admin_account": "НĐĩĐŧĐžĐļĐģивО ŅŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ОйĐģŅ–ĐēОвиК СаĐŋĐ¸Ņ адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", "unable_to_create_api_key": "НĐĩĐŧĐžĐļĐģивО ŅŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ĐŊОвиК ĐēĐģŅŽŅ‡ API", @@ -902,10 +908,6 @@ "unable_to_hide_person": "НĐĩĐŧĐžĐļĐģивО ĐŋŅ€Đ¸Ņ…ĐžĐ˛Đ°Ņ‚Đ¸ ĐģŅŽĐ´Đ¸ĐŊ҃", "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", @@ -916,11 +918,9 @@ "unable_to_remove_album_users": "НĐĩĐŧĐžĐļĐģивО видаĐģĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛ С аĐģŅŒĐąĐžĐŧ҃", "unable_to_remove_api_key": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ видаĐģĐ¸Ņ‚Đ¸ ĐēĐģŅŽŅ‡ API", "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": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ Đ˛Đ¸Ņ€Ņ–ŅˆĐ¸Ņ‚Đ¸ Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚", @@ -950,12 +950,12 @@ "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": "Đ’Ņ–Đē {months} ĐŧŅ–ŅŅŅ†Ņ–Đ˛", "exif_bottom_sheet_person_age_year_months": "Đ’Ņ–Đē 1 ҀҖĐē, {months} ĐŧŅ–ŅŅŅ†Ņ–Đ˛", "exif_bottom_sheet_person_age_years": "Đ’Ņ–Đē {years}", @@ -979,6 +979,7 @@ "external_network_sheet_info": "КоĐģи ви ĐŊĐĩ ĐŋŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊŅ– Đ´Đž ĐŋĐĩŅ€ĐĩваĐļĐŊĐžŅ— ĐŧĐĩŅ€ĐĩĐļŅ– Wi-Fi, Đ´ĐžĐ´Đ°Ņ‚ĐžĐē ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ĐŧĐĩŅ‚ŅŒŅŅ Đ´Đž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҇ĐĩŅ€ĐĩС ĐŋĐĩŅ€ŅˆŅƒ С ĐŊавĐĩĐ´ĐĩĐŊĐ¸Ņ… ĐŊиĐļ҇Đĩ URL-Đ°Đ´Ņ€Đĩҁ, ŅĐē҃ Đ˛Ņ–ĐŊ СĐŧĐžĐļĐĩ Đ´ĐžŅŅĐŗŅ‚Đ¸, ĐŋĐžŅ‡Đ¸ĐŊĐ°ŅŽŅ‡Đ¸ СвĐĩŅ€Ņ…Ņƒ вĐŊиС", "face_unassigned": "НĐĩ ĐŋŅ€Đ¸ĐˇĐŊĐ°Ņ‡ĐĩĐŊĐž", "failed": "НĐĩ вдаĐģĐžŅŅ", + "failed_to_authenticate": "ПоĐŧиĐģĐēа Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–Ņ—", "failed_to_load_assets": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€ŅĐ¸", "failed_to_load_folder": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ ĐŋаĐŋĐē҃", "favorite": "До ҃ĐģŅŽĐąĐģĐĩĐŊĐ¸Ņ…", @@ -1002,6 +1003,8 @@ "folders": "ПаĐŋĐēи", "folders_feature_description": "ПĐĩŅ€ĐĩĐŗĐģŅĐ´ ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņƒ ĐŋаĐŋĐžĐē Đ´ĐģŅ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš Ņ– Đ˛Ņ–Đ´ĐĩĐž ҃ Ņ„Đ°ĐšĐģĐžĐ˛Ņ–Đš ŅĐ¸ŅŅ‚ĐĩĐŧŅ–", "forward": "ПĐĩŅ€ĐĩҁĐģĐ°Ņ‚Đ¸", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "ĐĻŅ Ņ„ŅƒĐŊĐēŅ†Ņ–Ņ СаваĐŊŅ‚Đ°ĐļŅƒŅ” СОвĐŊŅ–ŅˆĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸ С Google Đ´ĐģŅ ŅĐ˛ĐžŅ”Ņ— Ņ€ĐžĐąĐžŅ‚Đ¸.", "general": "Đ—Đ°ĐŗĐ°ĐģҌĐŊŅ–", "get_help": "ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ Đ´ĐžĐŋĐžĐŧĐžĐŗŅƒ", "get_wifiname_error": "НĐĩ вдаĐģĐžŅŅ ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ ĐŊĐ°ĐˇĐ˛Ņƒ Wi-Fi. ПĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ, Ņ‰Đž ви ĐŊадаĐģи ĐŊĐĩĐžĐąŅ…Ņ–Đ´ĐŊŅ– дОСвОĐģи Ņ‚Đ° ĐŋŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊŅ– Đ´Đž Wi-Fi ĐŧĐĩŅ€ĐĩĐļŅ–", @@ -1044,10 +1047,13 @@ "home_page_favorite_err_local": "ПоĐēи Ņ‰Đž ĐŊĐĩ ĐŧĐžĐļĐŊа Đ´ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đž ҃ĐģŅŽĐąĐģĐĩĐŊĐ¸Ņ… ĐģĐžĐēаĐģҌĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "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": "МоĐļĐŊа ваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ ĐŊĐĩ ĐąŅ–ĐģҌ҈Đĩ 30 ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ вОдĐŊĐžŅ‡Đ°Ņ, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "host": "ĐĨĐžŅŅ‚", "hour": "ГодиĐŊа", + "id": "ID", "ignore_icloud_photos": "ĐŸŅ€ĐžĐŋ҃ҁĐēĐ°Ņ‚Đ¸ Ņ„Đ°ĐšĐģи С iCloud", "ignore_icloud_photos_description": "НĐĩ СаваĐŊŅ‚Đ°ĐļŅƒĐ˛Đ°Ņ‚Đ¸ Ņ„Đ°ĐšĐģи в Immich, ŅĐēŅ‰Đž вОĐŊи СйĐĩŅ€Ņ–ĐŗĐ°ŅŽŅ‚ŅŒŅŅ в iCloud", "image": "Đ—ĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", @@ -1087,6 +1093,12 @@ "invalid_date_format": "НĐĩĐ´Ņ–ĐšŅĐŊиК Ņ„ĐžŅ€ĐŧĐ°Ņ‚ Đ´Đ°Ņ‚Đ¸", "invite_people": "ЗаĐŋŅ€ĐžŅĐ¸Ņ‚Đ¸", "invite_to_album": "ЗаĐŋŅ€ĐžŅĐ¸Ņ‚Đ¸ в аĐģŅŒĐąĐžĐŧ", + "ios_debug_info_fetch_ran_at": "ĐžŅ‚Ņ€Đ¸ĐŧаĐŊĐž даĐŊŅ– {dateTime}", + "ios_debug_info_last_sync_at": "ĐžŅŅ‚Đ°ĐŊĐŊŅ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ {dateTime}", + "ios_debug_info_no_processes_queued": "ФОĐŊĐžĐ˛Ņ– ĐŋŅ€ĐžŅ†ĐĩŅĐ¸ Đ˛Ņ–Đ´ŅŅƒŅ‚ĐŊŅ– в ҇ĐĩŅ€ĐˇŅ–", + "ios_debug_info_no_sync_yet": "ФОĐŊОвĐĩ СавдаĐŊĐŊŅ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ— ҉Đĩ ĐŊĐĩ СаĐŋ҃ҁĐēаĐģĐžŅŅ", + "ios_debug_info_processes_queued": "{count, plural, one {{count} Ņ„ĐžĐŊОвиК ĐŋŅ€ĐžŅ†Đĩҁ ҃ ҇ĐĩŅ€ĐˇŅ–} other {{count} Ņ„ĐžĐŊĐžĐ˛Đ¸Ņ… ĐŋŅ€ĐžŅ†ĐĩŅŅ–Đ˛ ҃ ҇ĐĩŅ€ĐˇŅ–}}", + "ios_debug_info_processing_ran_at": "ĐžĐąŅ€ĐžĐąĐē҃ виĐēĐžĐŊаĐŊĐž {dateTime}", "items_count": "{count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} few {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸} many {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°}}", "jobs": "ЗавдаĐŊĐŊŅ", "keep": "ЗаĐģĐ¸ŅˆĐ¸Ņ‚Đ¸", @@ -1095,6 +1107,9 @@ "kept_this_deleted_others": "ЗбĐĩŅ€ĐĩĐļĐĩĐŊĐž ҆ĐĩĐš Ņ€ĐĩŅŅƒŅ€Ņ Ņ– видаĐģĐĩĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} many {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛} other {# Ņ€ĐĩŅŅƒŅ€ŅŅƒ}}", "keyboard_shortcuts": "ĐĄĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐŊŅ ĐēĐģĐ°Đ˛Ņ–Ņˆ", "language": "Мова", + "language_no_results_subtitle": "ĐĄĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ СĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŋĐžŅˆŅƒĐēОвиК СаĐŋĐ¸Ņ‚", + "language_no_results_title": "Мови ĐŊĐĩ СĐŊаКдĐĩĐŊĐž", + "language_search_hint": "ĐŸĐžŅˆŅƒĐē ĐŧОв...", "language_setting_description": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ ĐŧĐžĐ˛Ņƒ, ŅĐēŅ–Đš ви ĐŊĐ°Đ´Đ°Ņ”Ņ‚Đĩ ĐŋĐĩŅ€ĐĩĐ˛Đ°ĐŗŅƒ", "last_seen": "Đ’ĐžŅŅ‚Đ°ĐŊĐŊŅ” ĐąĐ°Ņ‡Đ¸Đģи", "latest_version": "ĐžŅŅ‚Đ°ĐŊĐŊŅ вĐĩŅ€ŅŅ–Ņ", @@ -1120,6 +1135,7 @@ "list": "ПĐĩŅ€ĐĩĐģŅ–Đē", "loading": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", "loading_search_results_failed": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Đ¸ ĐŋĐžŅˆŅƒĐē҃", + "local_asset_cast_failed": "НĐĩĐŧĐžĐļĐģивО Ņ‚Ņ€Đ°ĐŊҁĐģŅŽĐ˛Đ°Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€Ņ, ŅĐēиК ĐŊĐĩ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€", "local_network": "ЛоĐēаĐģҌĐŊа ĐŧĐĩŅ€ĐĩĐļа", "local_network_sheet_info": "Đ”ĐžĐ´Đ°Ņ‚ĐžĐē ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ĐŧĐĩŅ‚ŅŒŅŅ Đ´Đž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҇ĐĩŅ€ĐĩС ҆ĐĩĐš URL, ĐēĐžĐģи виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ вĐēаСаĐŊа Wi-Fi ĐŧĐĩŅ€ĐĩĐļа", "location_permission": "Đ”ĐžĐˇĐ˛Ņ–Đģ Đ´Đž ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", @@ -1129,14 +1145,19 @@ "location_picker_latitude_hint": "ВĐēаĐļŅ–Ņ‚ŅŒ ŅˆĐ¸Ņ€ĐžŅ‚Ņƒ", "location_picker_longitude_error": "ВĐēаĐļŅ–Ņ‚ŅŒ Đ´Ņ–ĐšŅĐŊ҃ Đ´ĐžĐ˛ĐŗĐžŅ‚Ņƒ", "location_picker_longitude_hint": "ВĐēаĐļŅ–Ņ‚ŅŒ Đ´ĐžĐ˛ĐŗĐžŅ‚Ņƒ", + "lock": "ЗабĐģĐžĐēŅƒĐ˛Đ°Ņ‚Đ¸", + "locked_folder": "ĐžŅĐžĐąĐ¸ŅŅ‚Đ° ĐŋаĐŋĐēа", "log_out": "Đ’Đ¸ĐšŅ‚Đ¸", "log_out_all_devices": "Đ’Đ¸ĐšŅ‚Đ¸ С ŅƒŅŅ–Ņ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—Đ˛", + "logged_in_as": "Đ’Ņ…Ņ–Đ´ виĐēĐžĐŊаĐŊĐž ŅĐē {user}", "logged_out_all_devices": "Đ’Đ¸ĐšŅˆĐģи С ŅƒŅŅ–Ņ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—Đ˛", "logged_out_device": "Đ’Đ¸Ņ…Ņ–Đ´ С ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", "login": "Đ’Ņ…Ņ–Đ´", "login_disabled": "ĐĐ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Ņ–Ņ ĐąŅƒĐģа Đ˛Ņ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊа", "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": "ĐĨийĐŊиК Ņ–ĐŧĐĩĐšĐģ", @@ -1160,7 +1181,7 @@ "look": "Đ”Đ¸Đ˛Đ¸Ņ‚Đ¸ŅŅ", "loop_videos": "ĐĻиĐēĐģҖ҇ĐŊŅ– Đ˛Ņ–Đ´ĐĩĐž", "loop_videos_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ†Đ¸ĐēĐģҖ҇ĐŊĐĩ Đ˛Ņ–Đ´Ņ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ Đ˛Ņ–Đ´ĐĩĐž.", - "main_branch_warning": "Ви виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚Đĩ вĐĩŅ€ŅŅ–ŅŽ Đ´ĐģŅ Ņ€ĐžĐˇŅ€ĐžĐąĐŊиĐēŅ–Đ˛; Đŧи ĐŊĐ°ŅŅ‚Ņ–ĐšĐŊĐž Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒŅ”ĐŧĐž виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ Ņ€ĐĩĐģŅ–ĐˇĐŊ҃ вĐĩŅ€ŅŅ–ŅŽ!", + "main_branch_warning": "Ви виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚Đĩ вĐĩŅ€ŅŅ–ŅŽ Đ´ĐģŅ Ņ€ĐžĐˇŅ€ĐžĐąĐŊиĐēŅ–Đ˛; ĐŊĐ°ŅŅ‚Ņ–ĐšĐŊĐž Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒŅ”ĐŧĐž виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ Ņ€ĐĩĐģŅ–ĐˇĐŊ҃ вĐĩŅ€ŅŅ–ŅŽ!", "main_menu": "ГоĐģОвĐŊĐĩ ĐŧĐĩĐŊŅŽ", "make": "Đ’Đ¸Ņ€ĐžĐąĐŊиĐē", "manage_shared_links": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ҁĐŋŅ–ĐģҌĐŊиĐŧи ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅĐŧи", @@ -1220,7 +1241,12 @@ "missing": "Đ’Ņ–Đ´ŅŅƒŅ‚ĐŊŅ–", "model": "МодĐĩĐģҌ", "month": "ĐœŅ–ŅŅŅ†ŅŒ", + "monthly_title_text_date_format": "ММММ Ņ€", "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": "ПĐĩŅ€ĐĩĐŊĐĩҁĐĩĐŊĐž Đ´Đž ҁĐŧŅ–Ņ‚ĐŊиĐēа", @@ -1238,6 +1264,7 @@ "new_password": "Новий ĐŋĐ°Ņ€ĐžĐģҌ", "new_person": "Нова ĐģŅŽĐ´Đ¸ĐŊа", "new_pin_code": "Новий PIN-ĐēОд", + "new_pin_code_subtitle": "Ви вĐŋĐĩŅ€ŅˆĐĩ ĐžŅ‚Ņ€Đ¸ĐŧŅƒŅ”Ņ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž ĐžŅĐžĐąĐ¸ŅŅ‚ĐžŅ— ĐŋаĐŋĐēи. ĐĄŅ‚Đ˛ĐžŅ€Ņ–Ņ‚ŅŒ PIN-ĐēОд Đ´ĐģŅ ĐąĐĩСĐŋĐĩ҇ĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋ҃ Đ´Đž ҆ҖҔҗ ŅŅ‚ĐžŅ€Ņ–ĐŊĐēи", "new_user_created": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐž ĐŊĐžĐ˛ĐžĐŗĐž ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "new_version_available": "ДОСĐĸĐŖĐŸĐĐ НОВА Đ’Đ•Đ ĐĄĐ†Đ¯", "newest_first": "ĐĄĐŋĐžŅ‡Đ°Ņ‚Đē҃ ĐŊĐžĐ˛Ņ–", @@ -1250,11 +1277,13 @@ "no_archived_assets_message": "Đ—Đ°Đ°Ņ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ— Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž, Ņ‰ĐžĐą ĐŋŅ€Đ¸Ņ…ĐžĐ˛Đ°Ņ‚Đ¸ Ņ—Ņ… ҃ Đ˛Đ°ŅˆĐžĐŧ҃ ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņ– Ņ„ĐžŅ‚Đž", "no_assets_message": "НАĐĸИСНІĐĸĐŦ, ЩОБ ЗАВАНĐĸАЖИĐĸИ ВАШЕ ПЕРШЕ ФОĐĸО", "no_assets_to_show": "ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ Đ˛Ņ–Đ´ŅŅƒŅ‚ĐŊŅ–", + "no_cast_devices_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": "Đ›ŅŽĐ´ĐĩĐš, Ņ‰Đž Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´Đ°ŅŽŅ‚ŅŒ СаĐŋĐ¸Ņ‚Ņƒ, ĐŊĐĩ СĐŊаКдĐĩĐŊĐž", @@ -1266,6 +1295,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": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ ĐĄĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ", @@ -1273,17 +1303,18 @@ "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": "На Ņ†ŅŒĐžĐŧ҃ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—", "onboarding": "ВвĐĩĐ´ĐĩĐŊĐŊŅ", - "onboarding_privacy_description": "ĐĐ°ŅŅ‚ŅƒĐŋĐŊŅ– (ĐŊĐĩОйОв'ŅĐˇĐēĐžĐ˛Ņ–) Ņ„ŅƒĐŊĐē҆Җҗ СаĐģĐĩĐļĐ°Ņ‚ŅŒ Đ˛Ņ–Đ´ СОвĐŊŅ–ŅˆĐŊŅ–Ņ… ҁĐĩŅ€Đ˛Ņ–ŅŅ–Đ˛ Ņ– ĐŧĐžĐļŅƒŅ‚ŅŒ ĐąŅƒŅ‚Đ¸ виĐŧĐēĐŊĐĩĐŊŅ– в ĐąŅƒĐ´ŅŒ-ŅĐēиК Ņ‡Đ°Ņ ҃ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ… адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ†Ņ–Ņ—.", + "onboarding_locale_description": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ йаĐļаĐŊ҃ ĐŧĐžĐ˛Ņƒ. Ви СĐŧĐžĐļĐĩŅ‚Đĩ СĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ҆Đĩ ĐŋŅ–ĐˇĐŊŅ–ŅˆĐĩ в ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ….", + "onboarding_privacy_description": "ĐĐ°ŅŅ‚ŅƒĐŋĐŊŅ– (ĐŊĐĩĐžĐąĐžĐ˛â€™ŅĐˇĐēĐžĐ˛Ņ–) Ņ„ŅƒĐŊĐē҆Җҗ СаĐģĐĩĐļĐ°Ņ‚ŅŒ Đ˛Ņ–Đ´ СОвĐŊŅ–ŅˆĐŊŅ–Ņ… ҁĐĩŅ€Đ˛Ņ–ŅŅ–Đ˛ Ņ– ĐŧĐžĐļŅƒŅ‚ŅŒ ĐąŅƒŅ‚Đ¸ виĐŧĐēĐŊĐĩĐŊŅ– ĐąŅƒĐ´ŅŒ-ĐēĐžĐģи в ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ….", + "onboarding_server_welcome_description": "Đ”Đ°Đ˛Đ°ĐšŅ‚Đĩ ĐŊаĐģĐ°ŅˆŅ‚ŅƒŅ”ĐŧĐž Đ˛Đ°ŅˆŅƒ Ņ–ĐŊŅŅ‚Đ°ĐŊŅ†Ņ–ŅŽ С Đ´ĐĩŅĐēиĐŧи ĐŋĐžŅˆĐ¸Ņ€ĐĩĐŊиĐŧи ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€Đ°Đŧи.", "onboarding_theme_description": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ ĐēĐžĐģŅ–Ņ€ĐŊ҃ Ņ‚ĐĩĐŧ҃ Đ´ĐģŅ ŅĐ˛ĐžĐŗĐž ĐĩĐēСĐĩĐŧĐŋĐģŅŅ€Đ°. Ви ĐŧĐžĐļĐĩŅ‚Đĩ СĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ Ņ—Ņ— ĐŋŅ–ĐˇĐŊŅ–ŅˆĐĩ в ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ….", - "onboarding_welcome_description": "Đ”Đ°Đ˛Đ°ĐšŅ‚Đĩ ĐŊаĐģĐ°ŅˆŅ‚ŅƒŅ”ĐŧĐž Đ˛Đ°Ņˆ ĐĩĐēСĐĩĐŧĐŋĐģŅŅ€ Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ Đ´ĐĩŅĐēĐ¸Ņ… ĐˇĐ°ĐŗĐ°ĐģҌĐŊĐ¸Ņ… ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€Ņ–Đ˛.", + "onboarding_user_welcome_description": "ĐŸĐžŅ‡ĐŊĐĩĐŧĐž!", "onboarding_welcome_user": "Đ›Đ°ŅĐēавО ĐŋŅ€ĐžŅĐ¸ĐŧĐž, {user}", "online": "Đ”ĐžŅŅ‚ŅƒĐŋĐŊиК", "only_favorites": "Đ›Đ¸ŅˆĐĩ ĐžĐąŅ€Đ°ĐŊŅ–", @@ -1340,6 +1371,8 @@ "permanently_delete_assets_prompt": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐŊаСавĐļди видаĐģĐ¸Ņ‚Đ¸ {count, plural, one {҆ĐĩĐš Ņ€ĐĩŅŅƒŅ€Ņ?} other {҆Җ # Ņ€ĐĩŅŅƒŅ€ŅĐ¸?}} ĐĻĐĩ Ņ‚Đ°ĐēĐžĐļ видаĐģĐ¸Ņ‚ŅŒ {count, plural, one {ĐšĐžĐŗĐž С ĐšĐžĐŗĐž} other {Ņ—Ņ… С Ņ—Ņ…ĐŊŅ–Ņ…}} аĐģŅŒĐąĐžĐŧ҃(Ņ–Đ˛).", "permanently_deleted_asset": "ВидаĐģĐ¸Ņ‚Đ¸ ĐŊаСавĐļди", "permanently_deleted_assets_count": "ВидаĐģĐĩĐŊĐž ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} many {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", + "permission": "ДозвоĐģи", + "permission_empty": "ДозвоĐģи ĐŊĐĩ ĐŋОвиĐŊŅ– ĐąŅƒŅ‚Đ¸ ĐŋĐžŅ€ĐžĐļĐŊŅ–Đŧи", "permission_onboarding_back": "Назад", "permission_onboarding_continue_anyway": "Đ’ŅĐĩ ОдĐŊĐž ĐŋŅ€ĐžĐ´ĐžĐ˛ĐļĐ¸Ņ‚Đ¸", "permission_onboarding_get_started": "РОСĐŋĐžŅ‡Đ°Ņ‚Đ¸", @@ -1360,6 +1393,7 @@ "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} ĐœŅ–ŅŅ†Ņ}}", @@ -1367,6 +1401,7 @@ "play_memories": "Đ’Ņ–Đ´Ņ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ҁĐŋĐžĐŗĐ°Đ´Đ¸", "play_motion_photo": "Đ’Ņ–Đ´Ņ‚Đ˛ĐžŅ€ŅŽĐ˛Đ°Ņ‚Đ¸ Ņ€ŅƒŅ…ĐžĐŧŅ– Ņ„ĐžŅ‚Đž", "play_or_pause_video": "Đ’Ņ–Đ´Ņ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ айО ĐŋŅ€Đ¸ĐˇŅƒĐŋиĐŊĐĩĐŊĐŊŅ Đ˛Ņ–Đ´ĐĩĐž", + "please_auth_to_access": "Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐŋŅ€ĐžĐšĐ´Ņ–Ņ‚ŅŒ Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–ŅŽ", "port": "ĐŸĐžŅ€Ņ‚", "preferences_settings_subtitle": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи Đ´ĐžĐ´Đ°Ņ‚Đē҃", "preferences_settings_title": "ĐŸĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€Đ¸", @@ -1374,13 +1409,18 @@ "preview": "ĐŸŅ€Đĩв'ŅŽ", "previous": "ПоĐŋĐĩŅ€ĐĩĐ´ĐŊŅ”", "previous_memory": "ПоĐŋĐĩŅ€ĐĩĐ´ĐŊŅ–Đš ҁĐŋĐžĐŗĐ°Đ´", - "previous_or_next_photo": "ПоĐŋĐĩŅ€ĐĩĐ´ĐŊŅ айО ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊа Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ", + "previous_or_next_day": "ДĐĩĐŊҌ вĐŋĐĩŅ€ĐĩĐ´/ĐŊаСад", + "previous_or_next_month": "ĐœŅ–ŅŅŅ†ŅŒ вĐŋĐĩŅ€ĐĩĐ´/ĐŊаСад", + "previous_or_next_photo": "Đ¤ĐžŅ‚Đž вĐŋĐĩŅ€ĐĩĐ´/ĐŊаСад", + "previous_or_next_year": "Đ Ņ–Đē вĐŋĐĩŅ€ĐĩĐ´/ĐŊаСад", "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}", @@ -1407,7 +1447,7 @@ "purchase_lifetime_description": "НазавĐļди", "purchase_option_title": "ВАРІАНĐĸИ ĐšĐŖĐŸĐ†Đ’Đ›Đ†", "purchase_panel_info_1": "Đ ĐžĐˇŅ€ĐžĐąĐēа Immich виĐŧĐ°ĐŗĐ°Ņ” ĐąĐ°ĐŗĐ°Ņ‚Đž Ņ‡Đ°ŅŅƒ Ņ‚Đ° ĐˇŅƒŅĐ¸ĐģҌ. Ми ĐŧĐ°Ņ”ĐŧĐž ŅˆŅ‚Đ°Ņ‚ĐŊĐ¸Ņ… Ņ–ĐŊĐļĐĩĐŊĐĩŅ€Ņ–Đ˛, ŅĐēŅ– ĐŋŅ€Đ°Ņ†ŅŽŅŽŅ‚ŅŒ ĐŊад Ņ‚Đ¸Đŧ, Ņ‰ĐžĐą ĐˇŅ€ĐžĐąĐ¸Ņ‚Đ¸ ĐšĐžĐŗĐž ŅĐēĐžĐŧĐžĐŗĐ° ĐēŅ€Đ°Ņ‰Đ¸Đŧ. ĐĐ°ŅˆĐ° ĐŧŅ–ŅŅ–Ņ — ĐˇŅ€ĐžĐąĐ¸Ņ‚Đ¸ ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐŊĐĩ СайĐĩСĐŋĐĩ҇ĐĩĐŊĐŊŅ С Đ˛Ņ–Đ´ĐēŅ€Đ¸Ņ‚Đ¸Đŧ ĐēОдОĐŧ Ņ‚Đ° ĐĩŅ‚Đ¸Ņ‡ĐŊŅ– ĐąŅ–ĐˇĐŊĐĩҁ-ĐŋŅ€Đ°ĐēŅ‚Đ¸Đēи ŅŅ‚Ņ–ĐšĐēиĐŧ Đ´ĐļĐĩŅ€ĐĩĐģĐžĐŧ Đ´ĐžŅ…ĐžĐ´Ņƒ Đ´ĐģŅ Ņ€ĐžĐˇŅ€ĐžĐąĐŊиĐēŅ–Đ˛ Ņ– ŅŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ĐĩĐēĐžŅĐ¸ŅŅ‚ĐĩĐŧ҃, Ņ‰Đž ĐŋОваĐļĐ°Ņ” ĐŋŅ€Đ¸Đ˛Đ°Ņ‚ĐŊŅ–ŅŅ‚ŅŒ, С Ņ€ĐĩаĐģҌĐŊиĐŧи аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛Đ°Đŧи ĐĩĐēҁĐŋĐģŅƒĐ°Ņ‚Đ°Ņ‚ĐžŅ€ŅŅŒĐēиĐŧ Ņ…ĐŧĐ°Ņ€ĐŊиĐŧ ҁĐĩŅ€Đ˛Ņ–ŅĐ°Đŧ.", - "purchase_panel_info_2": "ĐžŅĐēŅ–ĐģҌĐēи Đŧи СОйОв'ŅĐˇĐ°ĐģĐ¸ŅŅ ĐŊĐĩ Đ´ĐžĐ´Đ°Đ˛Đ°Ņ‚Đ¸ ĐŋĐģĐ°Ņ‚ĐŊĐ¸Ņ… ĐąĐģĐžĐēŅƒĐ˛Đ°ĐŊҌ, Ņ†Ņ ĐŋĐžĐē҃ĐŋĐēа ĐŊĐĩ ĐŊĐ°Đ´Đ°ŅŅ‚ŅŒ ваĐŧ Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛Đ¸Ņ… Ņ„ŅƒĐŊĐēŅ†Ņ–Đš ҃ Immich. Ми ĐŋĐžĐēĐģĐ°Đ´Đ°Ņ”ĐŧĐžŅŅ ĐŊа ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛, Ņ‚Đ°ĐēĐ¸Ņ… ŅĐē ви, Ņ‰ĐžĐą ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋĐžŅŅ‚Ņ–ĐšĐŊиК Ņ€ĐžĐˇĐ˛Đ¸Ņ‚ĐžĐē Immich.", + "purchase_panel_info_2": "ĐžŅĐēŅ–ĐģҌĐēи Đŧи ĐˇĐžĐąĐžĐ˛â€™ŅĐˇŅƒŅ”ĐŧĐžŅŅ ĐŊĐĩ Đ´ĐžĐ´Đ°Đ˛Đ°Ņ‚Đ¸ ĐŋĐģĐ°Ņ‚ĐŊŅ– ОйĐŧĐĩĐļĐĩĐŊĐŊŅ, Ņ†Ņ ĐŋĐžĐē҃ĐŋĐēа ĐŊĐĩ ĐŊĐ°Đ´Đ°ŅŅ‚ŅŒ ваĐŧ Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛Đ¸Ņ… Ņ„ŅƒĐŊĐēŅ†Ņ–Đš в Immich. Ми ĐŋĐžĐēĐģĐ°Đ´Đ°Ņ”ĐŧĐžŅŅ ĐŊа Ņ‚Đ°ĐēĐ¸Ņ… ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛, ŅĐē ви, Ņ‰ĐžĐą ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋОдаĐģŅŒŅˆĐ¸Đš Ņ€ĐžĐˇĐ˛Đ¸Ņ‚ĐžĐē Immich.", "purchase_panel_title": "ĐŸŅ–Đ´Ņ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ ĐŋŅ€ĐžŅ”ĐēŅ‚", "purchase_per_server": "На ҁĐĩŅ€Đ˛ĐĩŅ€", "purchase_per_user": "На ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", @@ -1455,9 +1495,12 @@ "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": "ВидаĐģĐ¸Ņ‚Đ¸ Ņ„ĐžŅ‚Đž С Ņ†ŅŒĐžĐŗĐž ҁĐŋĐžĐŗĐ°Đ´Ņƒ", + "remove_tag": "ВидаĐģĐ¸Ņ‚Đ¸ Ņ‚ĐĩĐŗ", "remove_url": "ВидаĐģĐ¸Ņ‚Đ¸ URL", "remove_user": "ВидаĐģĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "removed_api_key": "ВидаĐģĐĩĐŊĐž ĐēĐģŅŽŅ‡ API: {name}", @@ -1564,6 +1607,7 @@ "select_album_cover": "ĐžĐąŅ€Đ°Ņ‚Đ¸ ОйĐēĐģадиĐŊĐē҃ аĐģŅŒĐąĐžĐŧ҃", "select_all": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ Đ˛ŅĐĩ", "select_all_duplicates": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ Đ˛ŅŅ– Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸", + "select_all_in": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ Đ˛ŅĐĩ в {group}", "select_avatar_color": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ ĐēĐžĐģŅ–Ņ€ Đ°Đ˛Đ°Ņ‚Đ°Ņ€Đ°", "select_face": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ ОйĐģĐ¸Ņ‡Ņ‡Ņ", "select_featured_photo": "ĐžĐąŅ€Đ°Ņ‚Đ¸ ĐžĐąŅ€Đ°ĐŊĐĩ Ņ„ĐžŅ‚Đž", @@ -1584,6 +1628,7 @@ "server_info_box_server_url": "URL ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_offline": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐžŅ„ĐģаКĐŊ", "server_online": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐžĐŊĐģаКĐŊ", + "server_privacy": "КоĐŊŅ„Ņ–Đ´ĐĩĐŊŅ†Ņ–ĐšĐŊŅ–ŅŅ‚ŅŒ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_stats": "ĐĄŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đēа ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_version": "ВĐĩŅ€ŅŅ–Ņ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "set": "Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Ņ–Ņ‚ŅŒ", @@ -1593,6 +1638,7 @@ "set_date_of_birth": "Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ Đ´Đ°Ņ‚Ņƒ ĐŊĐ°Ņ€ĐžĐ´ĐļĐĩĐŊĐŊŅ", "set_profile_picture": "Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ ĐŋŅ€ĐžŅ„Ņ–ĐģŅŽ", "set_slideshow_to_fullscreen": "Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ҁĐģаКд-ŅˆĐžŅƒ ĐŊа вĐĩҁҌ ĐĩĐēŅ€Đ°ĐŊ", + "set_stack_primary_asset": "Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ŅĐē ĐžŅĐŊОвĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ", "setting_image_viewer_help": "ПовĐŊĐžĐĩĐēŅ€Đ°ĐŊĐŊиК ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Đ°Ņ‡ ҁĐŋĐžŅ‡Đ°Ņ‚Đē҃ СаваĐŊŅ‚Đ°ĐļŅƒŅ” ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ Đ´ĐģŅ ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐŊŅŒĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņƒ в ĐŊĐ¸ĐˇŅŒĐēŅ–Đš Ņ€ĐžĐˇĐ´Ņ–ĐģҌĐŊŅ–Đš ĐˇĐ´Đ°Ņ‚ĐŊĐžŅŅ‚Ņ–, ĐŋĐžŅ‚Ņ–Đŧ СаваĐŊŅ‚Đ°ĐļŅƒŅ” ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ в СĐŧĐĩĐŊ҈ĐĩĐŊŅ–Đš Ņ€ĐžĐˇĐ´Ņ–ĐģҌĐŊŅ–Đš ĐˇĐ´Đ°Ņ‚ĐŊĐžŅŅ‚Ņ– Đ˛Ņ–Đ´ĐŊĐžŅĐŊĐž ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģ҃ (ŅĐēŅ‰Đž вĐēĐģŅŽŅ‡ĐĩĐŊĐž) Ņ– ĐˇŅ€ĐĩŅˆŅ‚ĐžŅŽ СаваĐŊŅ‚Đ°ĐļŅƒŅ” ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģ (ŅĐēŅ‰Đž вĐēĐģŅŽŅ‡ĐĩĐŊĐž).", "setting_image_viewer_original_subtitle": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ Đ´ĐģŅ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģҌĐŊĐžĐŗĐž ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ С ĐŋОвĐŊĐžŅŽ Ņ€ĐžĐˇĐ´Ņ–ĐģҌĐŊĐžŅŽ ĐˇĐ´Đ°Ņ‚ĐŊŅ–ŅŅ‚ŅŽ (вĐĩĐģиĐēĐĩ!). ВиĐŧĐēĐŊŅƒŅ‚Đ¸, Ņ‰ĐžĐą СĐŧĐĩĐŊŅˆĐ¸Ņ‚Đ¸ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ даĐŊĐ¸Ņ… (ŅĐē ҇ĐĩŅ€ĐĩС ĐŧĐĩŅ€ĐĩĐļ҃, Ņ‚Đ°Đē Ņ– ĐŊа ĐēĐĩŅˆŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ).", "setting_image_viewer_original_title": "ЗаваĐŊŅ‚Đ°ĐļŅƒĐ˛Đ°Ņ‚Đ¸ ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģҌĐŊĐĩ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", @@ -1601,7 +1647,6 @@ "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": "ĐŊĐĩĐŗĐ°ĐšĐŊĐž", @@ -1624,6 +1669,7 @@ "share_add_photos": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ СĐŊŅ–ĐŧĐēи", "share_assets_selected": "{count} ĐžĐąŅ€Đ°ĐŊĐž", "share_dialog_preparing": "ĐŸŅ–Đ´ĐŗĐžŅ‚ĐžĐ˛Đēа...", + "share_link": "ĐŸĐžĐ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅĐŧ", "shared": "ĐĄĐŋŅ–ĐģҌĐŊŅ–", "shared_album_activities_input_disable": "КоĐŧĐĩĐŊŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ виĐŧĐēĐŊĐĩĐŊĐž", "shared_album_activity_remove_content": "Ви йаĐļĐ°Ņ”Ņ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ ҆Đĩ ĐŋĐžĐ˛Ņ–Đ´ĐžĐŧĐģĐĩĐŊĐŊŅ?", @@ -1663,6 +1709,7 @@ "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": "ĐĄĐŋŅ–ĐģҌĐŊŅ– ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", @@ -1729,12 +1776,14 @@ "start_date": "Đ”Đ°Ņ‚Đ° ĐŋĐžŅ‡Đ°Ņ‚Đē҃", "state": "Đ ĐĩĐŗŅ–ĐžĐŊ", "status": "ĐĄŅ‚Đ°ĐŊ", + "stop_casting": "Đ—ŅƒĐŋиĐŊĐ¸Ņ‚Đ¸ Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Ņ–ŅŽ", "stop_motion_photo": "Đ¤ĐžŅ‚Đž \"ĐĄŅ‚ĐžĐŋ-ĐŧĐžŅƒŅˆĐĩĐŊ\"", "stop_photo_sharing": "ĐŸŅ€Đ¸ĐŋиĐŊĐ¸Ņ‚Đ¸ ĐŊадаĐŊĐŊŅ Đ˛Đ°ŅˆĐ¸Ņ… СĐŊŅ–ĐŧĐēŅ–Đ˛?", "stop_photo_sharing_description": "{partner} ĐąŅ–ĐģҌ҈Đĩ ĐŊĐĩ ĐŧĐ°Ņ‚Đ¸ĐŧĐĩ Đ´ĐžŅŅ‚ŅƒĐŋ҃ Đ´Đž Đ˛Đ°ŅˆĐ¸Ņ… Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš.", "stop_sharing_photos_with_user": "ĐŸŅ€Đ¸ĐŋиĐŊĐ¸Ņ‚Đ¸ Đ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ ŅĐ˛ĐžŅ—Đŧи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–ŅĐŧи С Ņ†Đ¸Đŧ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡ĐĩĐŧ", "storage": "ĐĄŅ…ĐžĐ˛Đ¸Ņ‰Đĩ", "storage_label": "ĐœŅ–Ņ‚Đēа Đ´ĐģŅ СйĐĩŅ€Ņ–ĐŗĐ°ĐŊĐŊŅ", + "storage_quota": "ĐžĐąŅŅĐŗ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ°", "storage_usage": "{used} С {available} Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐ¸Ņ…", "submit": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đ¸", "suggestions": "ĐŸŅ€ĐžĐŋĐžĐˇĐ¸Ņ†Ņ–Ņ—", @@ -1785,7 +1834,6 @@ "to_parent": "ПовĐĩŅ€ĐŊŅƒŅ‚Đ¸ŅŅŒ ĐŊаСад", "to_trash": "ĐĄĐŧŅ–Ņ‚ĐŊиĐē", "toggle_settings": "ПĐĩŅ€ĐĩĐŧиĐēаĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊҌ", - "toggle_theme": "ПĐĩŅ€ĐĩĐŧиĐēаĐŊĐŊŅ Ņ‚ĐĩĐŧи", "total": "ĐŖŅŅŒĐžĐŗĐž", "total_usage": "Đ—Đ°ĐŗĐ°ĐģҌĐŊĐĩ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ", "trash": "ĐĄĐŧŅ–Ņ‚ĐŊиĐē", @@ -1807,6 +1855,7 @@ "unable_to_setup_pin_code": "НĐĩĐŧĐžĐļĐģивО ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚Đ¸ PIN-ĐēОд", "unarchive": "Đ ĐžĐˇĐ°Ņ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸", "unarchived_count": "{count, plural, other {ПовĐĩŅ€ĐŊŅƒŅ‚Đž С Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ #}}", + "undo": "ĐĄĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸", "unfavorite": "ВидаĐģĐ¸Ņ‚Đ¸ С ҃ĐģŅŽĐąĐģĐĩĐŊĐ¸Ņ…", "unhide_person": "РОСĐēŅ€Đ¸Ņ‚Đ¸ ĐžŅĐžĐąŅƒ", "unknown": "НĐĩĐ˛Ņ–Đ´ĐžĐŧĐž", @@ -1823,11 +1872,11 @@ "unsaved_change": "НĐĩСйĐĩŅ€ĐĩĐļĐĩĐŊа СĐŧŅ–ĐŊа", "unselect_all": "ЗĐŊŅŅ‚Đ¸ Đ˛ŅĐĩ", "unselect_all_duplicates": "ĐĄĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸ Đ˛Đ¸ĐąŅ–Ņ€ ŅƒŅŅ–Ņ… Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Ņ–Đ˛", + "unselect_all_in": "ЗĐŊŅŅ‚Đ¸ Đ˛Đ¸ĐąŅ–Ņ€ ҃ Đ˛ŅŅŒĐžĐŧ҃ {group}", "unstack": "Đ ĐžĐˇŅ–ĐąŅ€Đ°Ņ‚Đ¸ ҁ҂ĐĩĐē", "unstacked_assets_count": "Đ ĐžĐˇĐŗĐžŅ€ĐŊŅƒŅ‚Đ¸ {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} many {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", - "untracked_files": "ФаКĐģи, Ņ‰Đž ĐŊĐĩ Đ˛Ņ–Đ´ŅŅ‚ĐĩĐļŅƒŅŽŅ‚ŅŒŅŅ", - "untracked_files_decription": "ĐĻŅ– Ņ„Đ°ĐšĐģи ĐŊĐĩ Đ˛Ņ–Đ´ŅŅ‚ĐĩĐļŅƒŅŽŅ‚ŅŒŅŅ ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐžŅŽ. ВоĐŊи ĐŧĐžĐļŅƒŅ‚ŅŒ ĐąŅƒŅ‚Đ¸ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐŧ ĐŊĐĩвдаĐģĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐŊŅ, ĐŋĐĩŅ€ĐĩŅ€Đ˛Đ°ĐŊĐžĐŗĐž СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ айО СаĐģĐ¸ŅˆĐ¸Ņ‚Đ¸ŅŅ ҇ĐĩŅ€ĐĩС ĐŋĐžĐŧиĐģĐē҃ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧи", "up_next": "ĐĐ°ŅŅ‚ŅƒĐŋĐŊĐĩ", + "updated_at": "ОĐŊОвĐģĐĩĐŊĐž", "updated_password": "ĐŸĐ°Ņ€ĐžĐģҌ ĐžĐŊОвĐģĐĩĐŊĐž", "upload": "ЗаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸", "upload_concurrency": "ĐŸĐ°Ņ€Đ°ĐģĐĩĐģҌĐŊŅ–ŅŅ‚ŅŒ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", @@ -1842,14 +1891,18 @@ "upload_success": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ҃ҁĐŋŅ–ŅˆĐŊĐĩ. ОĐŊĐžĐ˛Ņ–Ņ‚ŅŒ ŅŅ‚ĐžŅ€Ņ–ĐŊĐē҃, Ņ‰ĐžĐą ĐŋĐžĐąĐ°Ņ‡Đ¸Ņ‚Đ¸ ĐŊĐžĐ˛Ņ– СаваĐŊŅ‚Đ°ĐļĐĩĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸.", "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_privacy": "КоĐŊŅ„Ņ–Đ´ĐĩĐŊŅ†Ņ–ĐšĐŊŅ–ŅŅ‚ŅŒ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "user_purchase_settings": "ĐŸŅ€Đ¸Đ´ĐąĐ°Ņ‚Đ¸", "user_purchase_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ Đ˛Đ°ŅˆĐžŅŽ ĐŋĐžĐē҃ĐŋĐēĐžŅŽ", "user_role_set": "ĐŸŅ€Đ¸ĐˇĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸ {user} ĐŊа Ņ€ĐžĐģҌ {role}", @@ -1865,11 +1918,6 @@ "version": "ВĐĩŅ€ŅŅ–Ņ", "version_announcement_closing": "ĐĸĐ˛Ņ–Đš Đ´Ņ€ŅƒĐŗ, АĐģĐĩĐēҁ", "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_3": " Ņ– ĐŋĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ, Ņ‰Đž Đ˛Đ°ŅˆŅ– ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ docker-compose Ņ‚Đ° .env ĐžĐŊОвĐģĐĩĐŊŅ–, айи СаĐŋĐžĐąŅ–ĐŗŅ‚Đ¸ ĐąŅƒĐ´ŅŒ-ŅĐēŅ–Đš ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊŅ–Đš ĐēĐžĐŊŅ„Ņ–ĐŗŅƒŅ€Đ°Ņ†Ņ–Ņ—, ĐžŅĐžĐąĐģивО, ŅĐēŅ‰Đž ви виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚Đĩ WatchTower айО Ņ–ĐŊŅˆĐ¸Đš ĐŧĐĩŅ…Đ°ĐŊŅ–ĐˇĐŧ, Đ´ĐģŅ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐ¸Ņ… ĐžĐŊОвĐģĐĩĐŊҌ Đ˛Đ°ŅˆĐžŅ— ҁĐĩŅ€Đ˛ĐĩŅ€ĐŊĐžŅ— Ņ‡Đ°ŅŅ‚Đ¸ĐŊи.", - "version_announcement_overlay_title": "Đ”ĐžŅŅ‚ŅƒĐŋĐŊа ĐŊОва вĐĩŅ€ŅŅ–Ņ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° 🎉", "version_history": "Đ†ŅŅ‚ĐžŅ€Ņ–Ņ вĐĩŅ€ŅŅ–Đš", "version_history_item": "Đ’ŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž {version} {date}", "video": "Đ’Ņ–Đ´ĐĩĐž", @@ -1889,6 +1937,7 @@ "view_previous_asset": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐŊŅ–Đš Ņ€ĐĩŅŅƒŅ€Ņ", "view_qr_code": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ QR-ĐēОд", "view_stack": "ПĐĩŅ€ĐĩĐŗĐģŅĐ´ ҁ҂ĐĩĐē҃", + "view_user": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "viewer_remove_from_stack": "ВидаĐģĐ¸Ņ‚Đ¸ ĐˇŅ– ҁ҂ĐĩĐē҃", "viewer_stack_use_as_main_asset": "ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ŅĐē ĐžŅĐŊОвĐŊиК ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "viewer_unstack": "Đ ĐžĐˇŅ–ĐąŅ€Đ°Ņ‚Đ¸ ҁ҂ĐĩĐē", @@ -1899,6 +1948,7 @@ "welcome": "Đ›Đ°ŅĐēавО ĐŋŅ€ĐžŅĐ¸ĐŧĐž", "welcome_to_immich": "Đ›Đ°ŅĐēавО ĐŋŅ€ĐžŅĐ¸ĐŧĐž Đ´Đž Immich", "wifi_name": "Назва Wi-Fi", + "wrong_pin_code": "НĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊиК PIN-ĐēОд", "year": "Đ Ņ–Đē", "years_ago": "{years, plural, one {# ҀҖĐē} few {# Ņ€ĐžĐēи} many {# Ņ€ĐžĐēŅ–Đ˛} other {# Ņ€ĐžĐēŅ–Đ˛}} Ņ‚ĐžĐŧ҃", "yes": "ĐĸаĐē", diff --git a/i18n/ur.json b/i18n/ur.json index d34cfa759d..d4a51b7ad1 100644 --- a/i18n/ur.json +++ b/i18n/ur.json @@ -34,11 +34,22 @@ "admin": { "add_exclusion_pattern_description": "Ø§ØŽØąØ§ØŦ ÚŠÛ’ Ų†Ų…ŲˆŲ†Û’ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē۔ *، **، Ø§ŲˆØą ? ڊا Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąØĒے ÛŲˆØĻے Globbing ڊا Ø§ØŗØĒØšŲ…Ø§Ų„ ڊیا ØŦا ØŗÚŠØĒا ہے۔ \"RAW\" Ų†Ø§Ų…ÛŒ ÚŠØŗÛŒ بڞی ڈاØĻØąÛŒÚŠŲšØąÛŒ Ų…ÛŒÚē ØĒŲ…Ø§Ų… ŲØ§ØĻŲ„ŲˆÚē ÚŠŲˆ Ų†Ø¸Øą Ø§Ų†Ø¯Ø§Ø˛ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ \"**/Raw/**\" Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē۔ \".tif\" ØŗÛ’ ØŽØĒŲ… ÛŲˆŲ†Û’ ŲˆØ§Ų„ÛŒ ØĒŲ…Ø§Ų… ŲØ§ØĻŲ„ŲˆÚē ÚŠŲˆ Ų†Ø¸Øą Ø§Ų†Ø¯Ø§Ø˛ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ \"**/*.tif\" Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē۔ ÚŠØŗÛŒ Ų…ØˇŲ„Ų‚ ØąØ§ØŗØĒے ÚŠŲˆ Ų†Ø¸Øą Ø§Ų†Ø¯Ø§Ø˛ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ \"/path/to/ignore/**\" ڊا Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē۔", "asset_offline_description": "Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ڊا یہ Ø¨ÛŒØąŲˆŲ†ÛŒ اØĢاØĢہ اب ÚˆØŗÚŠ ŲžØą Ų†ÛÛŒÚē Ų…Ų„Ø§ Ø§ŲˆØą Ø§ØŗÛ’ ÚŠŲˆÚ‘Û’ Ø¯Ø§Ų† Ų…ÛŒÚē ÚˆØ§Ų„ دیا گیا ہے۔ Ø§Ú¯Øą ŲØ§ØĻŲ„ Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ÚŠÛ’ Ø§Ų†Ø¯Øą Ų…Ų†ØĒŲ‚Ų„ ÚŠÛŒ Ú¯ØĻی ØĒڞی، ØĒ؈ Ų†ØĻے Ų…ØĒØšŲ„Ų‚Û اØĢاØĢے ÚŠÛ’ Ų„ÛŒÛ’ Ø§ŲžŲ†ÛŒ ŲšØ§ØĻŲ… Ų„Ø§ØĻŲ† چیڊ ÚŠØąÛŒÚē۔ Ø§Øŗ اØĢاØĢے ÚŠŲˆ Ø¨Ø­Ø§Ų„ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ Ø¨ØąØ§Û ÚŠØąŲ… ÛŒŲ‚ÛŒŲ†ÛŒ Ø¨Ų†Ø§ØĻیÚē ڊہ Ų†ÛŒÚ†Û’ دیے Ú¯ØĻے ŲØ§ØĻŲ„ ÚŠÛ’ ØąØ§ØŗØĒے ØĒÚŠ Immich ÚŠŲˆ ØąØŗØ§ØĻی حاØĩŲ„ ہے Ø§ŲˆØą Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ÚŠŲˆ Ø§ØŗÚŠÛŒŲ† ÚŠØąÛŒÚē۔", + "authentication_settings": "ØĒ؈ØĢÛŒŲ‚ ÚŠÛŒ ØĒØąØĒیباØĒ", + "authentication_settings_description": "ŲžØ§Øŗ ŲˆØąÚˆØŒ OAuth، Ø§ŲˆØą ØĒØĩØ¯ÛŒŲ‚ ÚŠÛŒ Ø¯ÛŒÚ¯Øą ØĒØąØĒیباØĒ ڊا Ų†Ø¸Ų… ÚŠØąÛŒÚē", "authentication_settings_disable_all": "ڊیا ØĸŲž ŲˆØ§Ų‚ØšÛŒ Ų„Ø§Ú¯ Ø§Ų† ÚŠÛ’ ØĒŲ…Ø§Ų… ØˇØąÛŒŲ‚ŲˆÚē ÚŠŲˆ ØēÛŒØą ŲØšØ§Ų„ ÚŠØąŲ†Ø§ چاہØĒے ہیÚ稟 Ų„Ø§Ú¯ Ø§Ų† Ų…ÚŠŲ…Ų„ ØˇŲˆØą ŲžØą ØēÛŒØą ŲØšØ§Ų„ ÛŲˆ ØŦاØĻے گا۔", - "check_all": "ØŗØ¨ چیڊ ÚŠØąÛŒÚē", - "cleanup": "ØĩØ§Ų ÚŠØąŲˆ", + "authentication_settings_reenable": "Ø¯ŲˆØ¨Ø§ØąÛ ŲØšØ§Ų„ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ ØŗØąŲˆØą ÚŠŲ…Ø§Ų†Úˆ Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē", + "background_task_job": "ŲžØŗ Ų…Ų†Ø¸Øą ÚŠÛ’ ÚŠØ§Ų…", + "backup_database": "ÚˆÛŒŲšØ§ Ø¨ÛŒØŗ ڊا ÚˆŲ…Ųž Ø¨Ų†Ø§ØĻیÚē", + "backup_database_enable_description": "ÚˆÛŒŲšØ§ Ø¨ÛŒØŗ ÚˆŲ…Ųž ÚŠŲˆ ŲØšØ§Ų„ ÚŠØąÛŒÚē", + "backup_keep_last_amount": "ØąÚŠÚžŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ ŲžÚ†ÚžŲ„Û’ ÚˆŲ…Ųž ÚŠÛŒ Ų…Ų‚Ø¯Ø§Øą", + "backup_settings": "ÚˆÛŒŲšØ§ Ø¨ÛŒØŗ ÚˆŲ…Ųž ÚŠÛŒ ØĒØąØĒیباØĒ", + "backup_settings_description": "ÚˆÛŒŲšØ§ Ø¨ÛŒØŗ ÚˆŲ…Ųž ÚŠÛŒ ØĒØąØĒیباØĒ ڊا Ų†Ø¸Ų… ÚŠØąÛŒÚē۔ Ų†ŲˆŲš: Ø§Ų† Ų…Ų„Ø§Ø˛Ų…ØĒ؈Úē ÚŠÛŒ Ų†Ú¯ØąØ§Ų†ÛŒ Ų†ÛÛŒÚē ÚŠÛŒ ØŦاØĒی ہے Ø§ŲˆØą ØĸŲž ÚŠŲˆ Ų†Ø§ÚŠØ§Ų…ÛŒ ÚŠÛŒ Ø§ØˇŲ„Ø§Øš Ų†ÛÛŒÚē Ø¯ÛŒ ØŦاØĻے گی", + "cleared_jobs": "Ų…Ų„Ø§Ø˛Ų…ØĒیÚē Ø§Øŗ ÚŠÛ’ Ų„ÛŒÛ’ ØĩØ§Ų ÚŠÛŒ Ú¯ØĻیÚē: {job}", + "config_set_by_file": "Config ŲÛŒ Ø§Ų„Ø­Ø§Ų„ ایڊ config ŲØ§ØĻŲ„ ÚŠÛ’ Ø°ØąÛŒØšÛ ØĒØąØĒیب دی Ú¯ØĻی ہے", "confirm_delete_library": "ڊیا ØĸŲž ŲˆØ§Ų‚ØšÛŒ {library} Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ÚŠŲˆ Ø­Ø°Ų ÚŠØąŲ†Ø§ چاہØĒے ہیÚ稟", + "confirm_delete_library_assets": "ڊیا ØĸŲž ŲˆØ§Ų‚ØšÛŒ Ø§Øŗ Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ÚŠŲˆ Ø­Ø°Ų ÚŠØąŲ†Ø§ چاہØĒے ہیÚ稟 یہ Immich ØŗÛ’ {count, plural, one {# contained asset} Ø¯ÛŒÚ¯Øą {all # contained assets}} ÚŠŲˆ Ø­Ø°Ų ÚŠØą دے گا Ø§ŲˆØą Ø§ØŗÛ’ ÚŠØ§Ų„ØšØ¯Ų… Ų†ÛÛŒÚē ڊیا ØŦا ØŗÚŠØĒا۔ ŲØ§ØĻŲ„ÛŒÚē ÚˆØŗÚŠ ŲžØą Ų…ŲˆØŦŲˆØ¯ ØąÛÛŒÚē گی۔", "confirm_email_below": "ØĒØĩØ¯ÛŒŲ‚ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ Ų†ÛŒÚ†Û’ ای Ų…ÛŒŲ„ ŲšØ§ØĻŲž ÚŠØąÛŒÚē {email}", + "confirm_reprocess_all_faces": "ڊیا ØĸŲž ŲˆØ§Ų‚ØšÛŒ ØĒŲ…Ø§Ų… Ú†ÛØąŲˆÚē ÚŠŲˆ Ø¯ŲˆØ¨Ø§ØąÛ ŲžØąŲˆØŗÛŒØŗ ÚŠØąŲ†Ø§ چاہØĒے ہیÚ稟 Ø§Øŗ ØŗÛ’ Ų†Ø§Ų… ŲˆØ§Ų„Û’ Ø§ŲØąØ§Ø¯ بڞی ØĩØ§Ų ÛŲˆ ØŦاØĻیÚē گے۔", "image_preview_title": "ŲžÛŒØ´ Ų†Ø¸Ø§ØąÛ", "image_quality": "Ų…ØšÛŒØ§Øą", "image_settings": "ØĒØĩŲˆÛŒØą ÚŠÛŒ ØĒØąØĒیباØĒ" diff --git a/i18n/vi.json b/i18n/vi.json index d952bc9197..5890ef87db 100644 --- a/i18n/vi.json +++ b/i18n/vi.json @@ -33,18 +33,17 @@ "added_to_favorites_count": "ÄÃŖ thÃĒm {count, number} vào MáģĨc yÃĒu thích", "admin": { "add_exclusion_pattern_description": "ThÃĒm quy táē¯c loáēĄi tráģĢ. Háģ— tráģŖ sáģ­ dáģĨng kÃŊ táģą *, **, và ?. Đáģƒ báģ qua táēĨt cáēŖ cÃĄc táē­p tin báēĨt káģŗ trong thư máģĨc tÃĒn \"Raw\", hÃŖy dÚng \"**/Raw/**\". Đáģƒ báģ qua cÃĄc táē­p tin cÃŗ đuôi \".tif\", hÃŖy dÚng \"**/*.tif\". Đáģƒ báģ qua máģ™t đưáģng dáēĢn cáģ‘ Ä‘áģ‹nh, hÃŖy dÚng \"/path/to/ignore/**\".", - "asset_offline_description": "áēĸnh thư viáģ‡n ngoài này không cÃ˛n trÃĒn áģ• Ä‘ÄŠa và Ä‘ÃŖ báģ‹ chuyáģƒn vào thÚng rÃĄc. Náēŋu áēŖnh Ä‘ÃŖ báģ‹ di chuyáģƒn trong thư viáģ‡n, kiáģƒm tra dÃ˛ng tháģi gian cáģ§a báēĄn đáģƒ tÃŦm áēŖnh máģ›i tÆ°ÆĄng áģŠng. Đáģƒ khôi pháģĨc, hÃŖy đáēŖm báēŖo Immich cÃŗ tháģƒ truy cáē­p đưáģng dáēĢn áēŖnh bÃĒn dưáģ›i và quÊt láēĄi thư viáģ‡n.", + "asset_offline_description": "áēĸnh thư viáģ‡n bÃĒn ngoài này không cÃ˛n trÃĒn áģ• Ä‘ÄŠa và Ä‘ÃŖ báģ‹ chuyáģƒn vào thÚng rÃĄc. Náēŋu áēŖnh Ä‘ÃŖ báģ‹ di chuyáģƒn trong thư viáģ‡n, kiáģƒm tra dÃ˛ng tháģi gian cáģ§a báēĄn đáģƒ tÃŦm áēŖnh máģ›i tÆ°ÆĄng áģŠng. Đáģƒ khôi pháģĨc, hÃŖy đáēŖm báēŖo Immich cÃŗ tháģƒ truy cáē­p đưáģng dáēĢn áēŖnh bÃĒn dưáģ›i và quÊt láēĄi thư viáģ‡n.", "authentication_settings": "Đăng nháē­p", "authentication_settings_description": "QuáēŖn lÃŊ máē­t kháēŠu, OAuth và cÃĄc cài đáēˇt xÃĄc tháģąc khÃĄc", "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": "TáēĄo báēŖn sao lưu cÆĄ sáģŸ dáģ¯ liáģ‡u", + "backup_database": "TáēĄo báēŖn sao lưu CSDL", "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", - "check_all": "Cháģn táēĨt cáēŖ", + "backup_keep_last_amount": "Sáģ‘ lưáģŖng cÃĄc báēŖn sao lưu CSDL trưáģ›c Ä‘Ãŗ đưáģŖc giáģ¯ láēĄi", + "backup_settings": "Cài đáēˇt sao lưu cÆĄ sáģŸ dáģ¯ liáģ‡u", + "backup_settings_description": "QuáēŖn lÃŊ cài đáēˇt sao lưu CSDL.", "cleared_jobs": "ÄÃŖ xoÃĄ cÃĄc tÃĄc váģĨ: {job}", "config_set_by_file": "CáēĨu hÃŦnh hiáģ‡n táēĄi đang đưáģŖc đáēˇt báģŸi máģ™t táē­p tin cáēĨu hÃŦnh", "confirm_delete_library": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n xÃŗa thư viáģ‡n {library} không?", @@ -52,21 +51,20 @@ "confirm_email_below": "Đáģƒ xÃĄc nháē­n, nháē­p \"{email}\" bÃĒn dưáģ›i", "confirm_reprocess_all_faces": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n xáģ­ lÃŊ láēĄi táēĨt cáēŖ cÃĄc khuôn máēˇt? Thao tÃĄc này sáēŊ xoÃĄ tÃĒn ngưáģi Ä‘ÃŖ đưáģŖc gÃĄn.", "confirm_user_password_reset": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n đáēˇt láēĄi máē­t kháēŠu cáģ§a {user}?", + "confirm_user_pin_code_reset": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n đáēˇt láēĄi mÃŖ PIN cáģ§a {user} không?", "create_job": "TáēĄo tÃĄc váģĨ", "cron_expression": "Biáģƒu tháģŠc Cron", - "cron_expression_description": "Thiáēŋt láē­p khoáēŖng tháģi gian đáģƒ quÊt báēąng biáģƒu tháģŠc cron. Tham kháēŖo Crontab Guru đáģƒ biáēŋt thÃĒm thông tin.", + "cron_expression_description": "Thiáēŋt láē­p khoáēŖng tháģi gian đáģƒ quÊt báēąng biáģƒu tháģŠc cron. Tham kháēŖo Crontab Guru đáģƒ biáēŋt thÃĒm thông tin", "cron_expression_presets": "MáēĢu biáģƒu tháģŠc Cron", "disable_login": "Vô hiáģ‡u hoÃĄ đăng nháē­p", - "duplicate_detection_job_description": "Sáģ­ dáģĨng Háģc mÃĄy đáģƒ phÃĄt hiáģ‡n cÃĄc hÃŦnh áēŖnh giáģ‘ng nhau. Dáģąa vào TÃŦm kiáēŋm Thông Minh", + "duplicate_detection_job_description": "CháēĄy háģc mÃĄy đáģƒ phÃĄt hiáģ‡n cÃĄc hÃŦnh áēŖnh giáģ‘ng nhau. Dáģąa vào TÃŦm kiáēŋm Thông Minh", "exclusion_pattern_description": "Quy táē¯c loáēĄi tráģĢ cho báēĄn báģ qua cÃĄc táē­p tin và thư máģĨc khi quÊt thư viáģ‡n cáģ§a báēĄn. Điáģu này háģ¯u ích náēŋu báēĄn cÃŗ cÃĄc thư máģĨc cháģŠa táē­p tin báēĄn không muáģ‘n nháē­p, cháēŗng háēĄn như cÃĄc táē­p tin RAW.", - "external_library_created_at": "Thư viáģ‡n bÃĒn ngoài (đưáģŖc táēĄo vào {date})", "external_library_management": "QuáēŖn lÃŊ thư viáģ‡n bÃĒn ngoài", "face_detection": "PhÃĄt hiáģ‡n khuôn máēˇt", - "face_detection_description": "Sáģ­ dáģĨng Háģc mÃĄy đáģƒ nháē­n dáēĄng khuôn máēˇt trong áēŖnh. Đáģ‘i váģ›i video, sáēŊ sáģ­ dáģĨng hÃŦnh áēŖnh thu nháģ. \"Làm Máģ›i\" sáēŊ xáģ­ lÃŊ láēĄi táēĨt cáēŖ cÃĄc hÃŦnh. \"Xáģ­ LÃŊ LáēĄi\" sáēŊ xoÃĄ háēŋt táēĨt cáēŖ dáģ¯ liáģ‡u khuôn máēˇt và nháē­n dáēĄng láēĄi. \"CÃ˛n Thiáēŋu\" sáēŊ xáģ­ lÃŊ cÃĄc áēŖnh cÃ˛n thiáēŋu. CÃĄc khuôn máēˇt đưáģŖc phÃĄt hiáģ‡n sáēŊ đưáģŖc xáģ­ lÃŊ báģŸi tÃĄc váģĨ Nháē­n DáēĄng Khuôn Máēˇt đáģƒ nhÃŗm chÃēng vào nháģ¯ng ngưáģi Ä‘ÃŖ cÃŗ hoáēˇc ngưáģi máģ›i.", + "face_detection_description": "Sáģ­ dáģĨng háģc mÃĄy đáģƒ nháē­n dáēĄng khuôn máēˇt trong áēŖnh. Đáģ‘i váģ›i video, sáēŊ sáģ­ dáģĨng hÃŦnh áēŖnh thu nháģ. \"Làm Máģ›i\" sáēŊ xáģ­ lÃŊ láēĄi táēĨt cáēŖ cÃĄc hÃŦnh. \"Xáģ­ LÃŊ LáēĄi\" sáēŊ xoÃĄ háēŋt táēĨt cáēŖ dáģ¯ liáģ‡u khuôn máēˇt và nháē­n dáēĄng láēĄi. \"CÃ˛n Thiáēŋu\" sáēŊ xáģ­ lÃŊ cÃĄc áēŖnh cÃ˛n thiáēŋu. CÃĄc khuôn máēˇt đưáģŖc phÃĄt hiáģ‡n sáēŊ đưáģŖc xáģ­ lÃŊ báģŸi tÃĄc váģĨ Nháē­n DáēĄng Khuôn Máēˇt đáģƒ nhÃŗm chÃēng vào nháģ¯ng ngưáģi Ä‘ÃŖ cÃŗ hoáēˇc ngưáģi máģ›i.", "facial_recognition_job_description": "NhÃŗm cÃĄc khuôn máēˇt Ä‘ÃŖ phÃĄt hiáģ‡n thành ngưáģi. Bưáģ›c này đưáģŖc tháģąc hiáģ‡n sau khi tÃĄc váģĨ PhÃĄt hiáģ‡n Khuôn máēˇt hoàn táēĨt. \"Xáģ­ LÃŊ LáēĄi\" sáēŊ nhÃŗm láēĄi táēĨt cáēŖ cÃĄc khuôn máēˇt. \"CÃ˛n Thiáēŋu\" sáēŊ xáģ­ lÃŊ cÃĄc khuôn máēˇt chưa cÃŗ gÃĄn váģ›i ngưáģi nào.", "failed_job_command": "Láģ‡nh {command} không tháģąc hiáģ‡n đưáģŖc tÃĄc váģĨ: {job}", "force_delete_user_warning": "CáēĸNH BÁO: Thao tÃĄc này sáēŊ ngay láē­p táģŠc xoÃĄ ngưáģi dÚng và táēĨt cáēŖ áēŖnh. Hành đáģ™ng này không tháģƒ hoàn tÃĄc và cÃĄc táē­p tin không tháģƒ khôi pháģĨc.", - "forcing_refresh_library_files": "Làm máģ›i toàn báģ™ thư viáģ‡n áēŖnh", "image_format": "Đáģ‹nh dáēĄng", "image_format_description": "Đáģ‹nh dáēĄng WebP dung lưáģŖng nháģ hÆĄn JPEG, nhưng mÃŖ hÃŗa cháē­m hÆĄn.", "image_fullsize_description": "áēĸnh kích thưáģ›c đáēŠy đáģ§ váģ›i thông tin metadata báģ‹ loáēĄi báģ, đưáģŖc dÚng khi phÃŗng to", @@ -75,11 +73,11 @@ "image_fullsize_quality_description": "CháēĨt lưáģŖng áēŖnh kích thưáģ›c đáē§y đáģ§ táģĢ 1-100. Sáģ‘ láģ›n hÆĄn là áēŖnh cháēĨt lưáģŖng táģ‘t hÆĄn, nhưng không sinh ra file cÃŗ kích thưáģ›c láģ›n hÆĄn.", "image_fullsize_title": "Cài đáēˇt áēŖnh kích thưáģ›c đáē§y đáģ§", "image_prefer_embedded_preview": "Ưu tiÃĒn áēŖnh xem trưáģ›c đi kèm", - "image_prefer_embedded_preview_setting_description": "áģ¨ng dáģĨng sáēŊ sáģ­ dáģĨng áēŖnh xem trưáģ›c trong áēŖnh RAW khi cÃŗ sáēĩn đáģƒ xáģ­ lÃŊ hÃŦnh áēŖnh. Điáģu này cÃŗ tháģƒ giÃēp tÃĄi táēĄo màu sáē¯c chính xÃĄc hÆĄn cho máģ™t sáģ‘ hÃŦnh áēŖnh, nhưng cháēĨt lưáģŖng cáģ§a áēŖnh xem trưáģ›c pháģĨ thuáģ™c vào mÃĄy áēŖnh và cÃŗ tháģƒ báģ‹ nÊn.", + "image_prefer_embedded_preview_setting_description": "DÚng áēŖnh xem trưáģ›c trong áēŖnh RAW khi cÃŗ sáēĩn đáģƒ xáģ­ lÃŊ hÃŦnh áēŖnh. Điáģu này cÃŗ tháģƒ giÃēp tÃĄi táēĄo màu sáē¯c chính xÃĄc hÆĄn cho máģ™t sáģ‘ áēŖnh, nhưng cháēĨt lưáģŖng cáģ§a áēŖnh xem trưáģ›c pháģĨ thuáģ™c vào mÃĄy áēŖnh và cÃŗ tháģƒ báģ‹ nhiáģ…u do nÊn.", "image_prefer_wide_gamut": "Ưu tiÃĒn gam màu máģŸ ráģ™ng", "image_prefer_wide_gamut_setting_description": "Hiáģƒn tháģ‹ hÃŦnh thu nháģ áģŸ gam màu Display P3. Điáģu này giÃēp giáģ¯ màu sáē¯c ráģąc ráģĄ cáģ§a nháģ¯ng hÃŦnh áēŖnh cÃŗ gam màu ráģ™ng, nhưng hÃŦnh áēŖnh cÃŗ tháģƒ trông khÃĄc trÃĒn cÃĄc thiáēŋt báģ‹ cÅŠ và trÃŦnh duyáģ‡t cÅŠ. HÃŦnh áēŖnh sRGB đưáģŖc giáģ¯ nguyÃĒn đáģƒ trÃĄnh thay đáģ•i màu sáē¯c.", - "image_preview_description": "HÃŦnh áēŖnh kích thưáģ›c trung bÃŦnh Ä‘ÃŖ loáēĄi báģ metadata, đưáģŖc sáģ­ dáģĨng khi xem máģ™t hÃŦnh duy nháēĨt và cho Háģc mÃĄy", - "image_preview_quality_description": "CháēĨt lưáģŖng xem trưáģ›c táģĢ 1-100. Càng cao càng táģ‘t, nhưng sáēŊ táēĄo ra cÃĄc táē­p tin láģ›n hÆĄn cÃŗ tháģƒ làm giáēŖm kháēŖ năng pháēŖn háģ“i cáģ§a áģŠng dáģĨng. Sáģ­ dáģĨng giÃĄ tráģ‹ tháēĨp cÃŗ tháģƒ áēŖnh hưáģŸng đáēŋn cháēĨt lưáģŖng tÃĄc váģĨ Háģc mÃĄy.", + "image_preview_description": "HÃŦnh áēŖnh kích thưáģ›c trung bÃŦnh Ä‘ÃŖ loáēĄi báģ metadata, đưáģŖc sáģ­ dáģĨng khi xem máģ™t hÃŦnh duy nháēĨt và cho háģc mÃĄy", + "image_preview_quality_description": "CháēĨt lưáģŖng xem trưáģ›c táģĢ 1-100. Càng cao càng táģ‘t, nhưng sáēŊ táēĄo ra cÃĄc táē­p tin láģ›n hÆĄn cÃŗ tháģƒ làm giáēŖm kháēŖ năng pháēŖn háģ“i cáģ§a áģŠng dáģĨng. Sáģ­ dáģĨng giÃĄ tráģ‹ tháēĨp cÃŗ tháģƒ áēŖnh hưáģŸng đáēŋn cháēĨt lưáģŖng tÃĄc váģĨ háģc mÃĄy.", "image_preview_title": "Cài đáēˇt Xem trưáģ›c", "image_quality": "CháēĨt lưáģŖng", "image_resolution": "Đáģ™ phÃĸn giáēŖi", @@ -89,11 +87,11 @@ "image_thumbnail_description": "HÃŦnh thu nháģ kích thưáģ›c nháģ Ä‘ÃŖ loáēĄi báģ metadata, dÚng khi xem nhiáģu áēŖnh cÚng lÃēc, ví dáģĨ như xem DÃ˛ng Tháģi gian chính", "image_thumbnail_quality_description": "CháēĨt lưáģŖng hÃŦnh thu nháģ táģĢ 1-100. Càng cao càng táģ‘t, nhưng sáēŊ táēĄo ra cÃĄc táē­p tin láģ›n hÆĄn cÃŗ tháģƒ làm giáēŖm kháēŖ năng pháēŖn háģ“i cáģ§a áģŠng dáģĨng.", "image_thumbnail_title": "Cài đáēˇt hÃŦnh thu nháģ", - "job_concurrency": "{job} tháģąc hiáģ‡n đáģ“ng tháģi", + "job_concurrency": "{job} cháēĄy đáģ“ng tháģi", "job_created": "TÃĄc váģĨ Ä‘ÃŖ đưáģŖc táēĄo", - "job_not_concurrency_safe": "TÃĄc váģĨ này không an toàn đáģƒ tháģąc hiáģ‡n đáģ“ng tháģi.", + "job_not_concurrency_safe": "TÃĄc váģĨ này không an toàn đáģƒ cháēĄy đáģ“ng tháģi.", "job_settings": "TÃĄc váģĨ", - "job_settings_description": "QuáēŖn lÃŊ máģŠc đáģ™ tháģąc hiáģ‡n đáģ“ng tháģi cáģ§a tÃĄc váģĨ", + "job_settings_description": "QuáēŖn lÃŊ sáģ‘ lưáģŖng tÃĄc váģĨ cháēĄy đáģ“ng tháģi", "job_status": "TráēĄng thÃĄi tÃĄc váģĨ", "jobs_delayed": "{jobCount, plural, other {# tÃĄc váģĨ báģ‹ hoÃŖn láēĄi}}", "jobs_failed": "{jobCount, plural, other {# tÃĄc váģĨ báģ‹ tháēĨt báēĄi}}", @@ -118,8 +116,8 @@ "machine_learning_duplicate_detection_enabled": "Báē­t phÃĄt hiáģ‡n áēŖnh trÚng láēˇp", "machine_learning_duplicate_detection_enabled_description": "Náēŋu báģ‹ táē¯t, cÃĄc áēŖnh trÚng láēˇp giáģ‘ng háģ‡t nhau váēĢn sáēŊ báģ‹ loáēĄi báģ.", "machine_learning_duplicate_detection_setting_description": "Sáģ­ dáģĨng vector nhÃēng CLIP đáģƒ tÃŦm kiáēŋm áēŖnh trÚng láēˇp", - "machine_learning_enabled": "Báē­t Háģc mÃĄy", - "machine_learning_enabled_description": "Náēŋu báģ‹ táē¯t, táēĨt cáēŖ cÃĄc tính năng và cài đáēˇt Háģc mÃĄy sáēŊ báģ‹ loáēĄi báģ.", + "machine_learning_enabled": "Báē­t háģc mÃĄy", + "machine_learning_enabled_description": "Náēŋu báģ‹ táē¯t, táēĨt cáēŖ cÃĄc tính năng và cài đáēˇt háģc mÃĄy sáēŊ báģ‹ loáēĄi báģ.", "machine_learning_facial_recognition": "Nháē­n dáēĄng Khuôn máēˇt", "machine_learning_facial_recognition_description": "PhÃĄt hiáģ‡n, nháē­n dáēĄng và nhÃŗm cÃĄc khuôn máēˇt trong áēŖnh", "machine_learning_facial_recognition_model": "Mô hÃŦnh nháē­n dáēĄng khuôn máēˇt", @@ -135,12 +133,12 @@ "machine_learning_min_recognized_faces": "Sáģ‘ khuôn máēˇt táģ‘i thiáģƒu đáģƒ nháē­n dáēĄng", "machine_learning_min_recognized_faces_description": "Sáģ‘ khuôn máēˇt táģ‘i thiáģƒu cáē§n nháē­n dáēĄng đáģƒ táēĄo thành máģ™t ngưáģi. Tăng sáģ‘ lưáģŖng này sáēŊ làm cho Nháē­n dáēĄng khuôn máēˇt chính xÃĄc hÆĄn, nhưng sáēŊ tăng kháēŖ năng máģ™t khuôn máēˇt không đưáģŖc gÃĄn cho ngưáģi phÚ háģŖp.", "machine_learning_settings": "Cài đáēˇt Háģc mÃĄy", - "machine_learning_settings_description": "QuáēŖn lÃŊ cÃĄc tính năng và cài đáēˇt Háģc mÃĄy", + "machine_learning_settings_description": "QuáēŖn lÃŊ cÃĄc tính năng và cài đáēˇt háģc mÃĄy", "machine_learning_smart_search": "TÃŦm kiáēŋm Thông minh", "machine_learning_smart_search_description": "TÃŦm kiáēŋm hÃŦnh áēŖnh theo ngáģ¯ cáēŖnh váģ›i CLIP", "machine_learning_smart_search_enabled": "Báē­t TÃŦm kiáēŋm Thông minh", "machine_learning_smart_search_enabled_description": "Náēŋu táē¯t, hÃŦnh áēŖnh sáēŊ không đưáģŖc mÃŖ hoÃĄ đáģƒ tÃŦm kiáēŋm thông minh.", - "machine_learning_url_description": "Đáģ‹a cháģ‰ mÃĄy cháģ§ Háģc mÃĄy. Náēŋu cÃŗ nhiáģu hÆĄn máģ™t đáģ‹a cháģ‰ Ä‘Æ°áģŖc cung cáēĨp, máģ—i mÃĄy cháģ§ sáēŊ đưáģŖc kiáģƒm tra máģ™t láē§n cho đáēŋn khi cÃŗ máģ™t mÃĄy cháģ§ tráēŖ láģi thành công, theo tháģŠ táģą táģĢ mÃĄy cháģ§ Ä‘áē§u tiÃĒn đáēŋn mÃĄy cháģ§ cuáģ‘i cÚng. MÃĄy cháģ§ không pháēŖn háģ“i sáēŊ táēĄm tháģi đưáģŖc báģ qua cho đáēŋn khi mÃĄy cháģ§ online tráģŸ láēĄi.", + "machine_learning_url_description": "Đáģ‹a cháģ‰ mÃĄy cháģ§ háģc mÃĄy. Náēŋu cÃŗ nhiáģu hÆĄn máģ™t đáģ‹a cháģ‰ Ä‘Æ°áģŖc cung cáēĨp, máģ—i mÃĄy cháģ§ sáēŊ đưáģŖc kiáģƒm tra máģ™t láē§n cho đáēŋn khi cÃŗ máģ™t mÃĄy cháģ§ tráēŖ láģi thành công, theo tháģŠ táģą táģĢ mÃĄy cháģ§ Ä‘áē§u tiÃĒn đáēŋn mÃĄy cháģ§ cuáģ‘i cÚng. MÃĄy cháģ§ không pháēŖn háģ“i sáēŊ táēĄm tháģi đưáģŖc báģ qua cho đáēŋn khi mÃĄy cháģ§ online tráģŸ láēĄi.", "manage_concurrency": "QuáēŖn lÃŊ TÃĄc váģĨ", "manage_log_settings": "QuáēŖn lÃŊ cài đáēˇt nháē­t kÃŊ", "map_dark_style": "Giao diáģ‡n táģ‘i", @@ -155,7 +153,9 @@ "map_reverse_geocoding_settings": "Cài đáēˇt MÃŖ hoÃĄ Đáģ‹a lÃŊ NgưáģŖc (Reverse Geocoding)", "map_settings": "BáēŖn đáģ“", "map_settings_description": "QuáēŖn lÃŊ cài đáēˇt báēŖn đáģ“", - "map_style_description": "Đưáģng dáēĢn URL đáēŋn táē­p tin tuáģŗ biáēŋn báēŖn đáģ“ style.json", + "map_style_description": "Đưáģng dáēĢn URL đáēŋn táē­p tin tÚy biáēŋn báēŖn đáģ“ style.json", + "memory_cleanup_job": "Dáģn dáēšp káģˇ niáģ‡m", + "memory_generate_job": "TáēĄo káģˇ niáģ‡m", "metadata_extraction_job": "Trích xuáēĨt Metadata", "metadata_extraction_job_description": "Trích xuáēĨt Metadata táģĢ máģ—i áēŖnh, cháēŗng háēĄn như GPS, khuôn máēˇt và đáģ™ phÃĸn giáēŖi", "metadata_faces_import_setting": "Báē­t tính năng nháē­p khuôn máēˇt", @@ -189,10 +189,11 @@ "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_secret_description": "Báē¯t buáģ™c náēŋu nhà cung cáēĨp OAuth không háģ— tráģŖ PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "Đăng nháē­p váģ›i 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_mobile_redirect_uri_override_description": "Báē­t khi nhà cung cáēĨp OAuth không cho phÊp URI di đáģ™ng, như ''{callback}''", "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.", @@ -202,8 +203,8 @@ "oauth_storage_quota_claim_description": "Táģą Ä‘áģ™ng đáēˇt háēĄn máģŠc lưu tráģ¯ cáģ§a ngưáģi dÚng theo giÃĄ tráģ‹ cáģ§a claim này.", "oauth_storage_quota_default": "HáēĄn máģŠc lưu tráģ¯ máēˇc đáģ‹nh (GiB)", "oauth_storage_quota_default_description": "HáēĄn máģŠc (GiB) sáēŊ đưáģŖc sáģ­ dáģĨng khi không cÃŗ yÃĒu cáē§u nào đưáģŖc cung cáēĨp (Nháē­p 0 đáģƒ háēĄn máģŠc không giáģ›i háēĄn).", - "offline_paths": "CÃĄc đưáģng dáēĢn ngoáēĄi tuyáēŋn", - "offline_paths_description": "Nháģ¯ng đưáģng dáēĢn này cÃŗ tháģƒ là do viáģ‡c xÃŗa tháģ§ công cÃĄc táē­p tin không thuáģ™c thư viáģ‡n bÃĒn ngoài.", + "oauth_timeout": "Tháģi gian cháģ pháēŖn háģ“i", + "oauth_timeout_description": "Đáēˇt tháģi gian cháģ pháēŖn háģ“i báēąng mili giÃĸy", "password_enable_description": "Đăng nháē­p váģ›i email và máē­t kháēŠu", "password_settings": "Máē­t kháēŠu đăng nháē­p", "password_settings_description": "QuáēŖn lÃŊ cài đáēˇt máē­t kháēŠu đăng nháē­p", @@ -213,9 +214,6 @@ "refreshing_all_libraries": "Làm máģ›i táēĨt cáēŖ cÃĄc thư viáģ‡n", "registration": "Đăng kÃŊ QuáēŖn tráģ‹ viÃĒn", "registration_description": "VÃŦ báēĄn là ngưáģi dÚng đáē§u tiÃĒn, báēĄn sáēŊ tráģŸ thành QuáēŖn tráģ‹ viÃĒn và cháģ‹u trÃĄch nhiáģ‡m cho viáģ‡c quáēŖn lÃŊ háģ‡ tháģ‘ng. Ngoài ra, báēĄn cÃŗ tháģƒ thÃĒm cÃĄc ngưáģi dÚng khÃĄc.", - "repair_all": "Sáģ­a cháģ¯a táēĨt cáēŖ", - "repair_matched_items": "ÄÃŖ tÃŦm tháēĨy {count, plural, one {# máģĨc} other {# máģĨc}} trÚng kháģ›p", - "repaired_items": "ÄÃŖ sáģ­a cháģ¯a {count, plural, one{# máģĨc} other {# máģĨc}}", "require_password_change_on_login": "YÃĒu cáē§u ngưáģi dÚng thay đáģ•i máē­t kháēŠu trong láē§n đăng nháē­p đáē§u tiÃĒn", "reset_settings_to_default": "Đáēˇt láēĄi cài đáēˇt váģ máēˇc đáģ‹nh", "reset_settings_to_recent_saved": "Đáēˇt láēĄi cài đáēˇt váģ cài đáēˇt trưáģ›c Ä‘Ãŗ", @@ -224,6 +222,7 @@ "send_welcome_email": "Gáģ­i email chào máģĢng", "server_external_domain_settings": "TÃĒn miáģn công khai", "server_external_domain_settings_description": "TÃĒn miáģn dành cho cÃĄc liÃĒn káēŋt chia sáēģ công khai, bao gáģ“m http(s)://", + "server_public_users": "Ngưáģi dÚng công khai", "server_public_users_description": "TáēĨt cáēŖ ngưáģi dÚng (tÃĒn và email) đưáģŖc liáģ‡t kÃĒ khi thÃĒm máģ™t ngưáģi dÚng vào máģ™t album đưáģŖc chia sáēģ. Khi táē¯t láģąa cháģn này, danh sÃĄch ngưáģi dÚng cháģ‰ cÃŗ tháģƒ Ä‘Æ°áģŖc tháēĨy báģŸi ngưáģi dÚng quáēŖn tráģ‹.", "server_settings": "MÃĄy cháģ§", "server_settings_description": "QuáēŖn lÃŊ cài đáēˇt mÃĄy cháģ§", @@ -232,38 +231,36 @@ "sidecar_job": "SiÃĒu dáģ¯ liáģ‡u đi kèm", "sidecar_job_description": "TÃŦm hoáēˇc đáģ“ng báģ™ cÃĄc táē­p tin siÃĒu dáģ¯ liáģ‡u đi kèm táģĢ háģ‡ tháģ‘ng", "slideshow_duration_description": "Sáģ‘ giÃĸy đáģƒ hiáģƒn tháģ‹ cho táģĢng áēŖnh", - "smart_search_job_description": "CháēĄy machine learning trÃĒn toàn báģ™ áēŖnh đáģƒ háģ— tráģŖ tÃŦm kiáēŋm thông minh", - "storage_template_date_time_description": "DáēĨu tháģi gian táēĄo áēŖnh đưáģŖc sáģ­ dáģĨng cho thông tin ngày giáģ", + "smart_search_job_description": "CháēĄy háģc mÃĄy trÃĒn toàn báģ™ áēŖnh đáģƒ háģ— tráģŖ tÃŦm kiáēŋm thông minh", + "storage_template_date_time_description": "Máģ‘c tháģi gian táēĄo áēŖnh đưáģŖc dÚng làm thông tin ngày giáģ", "storage_template_date_time_sample": "Tháģi gian máēĢu {date}", "storage_template_enable_description": "Báē­t công cáģĨ máēĢu lưu tráģ¯", "storage_template_hash_verification_enabled": "Báē­t xÃĄc minh băm", "storage_template_hash_verification_enabled_description": "Báē­t xÃĄc minh băm, không táē¯t tính năng này tráģĢ khi báēĄn cháē¯c cháē¯n váģ cÃĄc ráģ§i ro cÃŗ tháģƒ xáēŖy ra", "storage_template_migration": "Di chuyáģƒn máēĢu lưu tráģ¯", "storage_template_migration_description": "Áp dáģĨng {template} hiáģ‡n táēĄi cho cÃĄc áēŖnh Ä‘ÃŖ đưáģŖc táēŖi lÃĒn trưáģ›c đÃĸy", - "storage_template_migration_info": "BáēŖn máēĢu lưu tráģ¯ sáēŊ chuyáģƒn đáģ•i táēĨt cáēŖ cáēŖ Ä‘áģ‹nh dang file thành cháģ¯ nháģ. CÃĄc thay đáģ•i báēŖn máēĢu cháģ‰ ÃĄp dáģĨng cho cÃĄc áēŖnh máģ›i. Đáģƒ ÃĄp dáģĨng láēĄi máēĢu cho cÃĄc áēŖnh Ä‘ÃŖ đưáģŖc táēŖi lÃĒn trưáģ›c đÃĸy, hÃŖy cháēĄy {job}.", + "storage_template_migration_info": "MáēĢu lưu tráģ¯ sáēŊ chuyáģƒn táēĨt cáēŖ Ä‘áģ‹nh dáēĄng táē­p tin thành cháģ¯ thưáģng. Nháģ¯ng thay đáģ•i cáģ§a máēĢu này cháģ‰ ÃĄp dáģĨng cho cÃĄc áēŖnh máģ›i táēŖi lÃĒn. Đáģƒ ÃĄp dáģĨng máēĢu này cho cÃĄc áēŖnh Ä‘ÃŖ táēŖi lÃĒn trưáģ›c đÃĸy, hÃŖy cháēĄy {job}.", "storage_template_migration_job": "TÃĄc váģĨ di chuyáģƒn máēĢu lưu tráģ¯", "storage_template_more_details": "Cáē§n thÃĒm thông tin chi tiáēŋt váģ tính năng này, vui lÃ˛ng tham kháēŖo MáēĢu lưu tráģ¯ và cÃĄc háģ‡ quáēŖ cáģ§a nÃŗ", - "storage_template_onboarding_description": "Khi đưáģŖc báē­t, tính năng này sáēŊ táģą Ä‘áģ™ng sáē¯p xáēŋp cÃĄc táē­p tin dáģąa trÃĒn máēĢu do ngưáģi dÚng đáģ‹nh nghÄŠa. Do cÃĄc váēĨn đáģ váģ Ä‘áģ™ áģ•n đáģ‹nh nÃĒn tính năng này Ä‘ÃŖ báģ‹ táē¯t theo máēˇc đáģ‹nh. Đáģƒ biáēŋt thÃĒm thông tin, vui lÃ˛ng xem tài liáģ‡u.", "storage_template_path_length": "Giáģ›i háēĄn đáģ™ dài đưáģng dáēĢn xáēĨp xáģ‰: {length, number}/{limit, number}", "storage_template_settings": "MáēĢu lưu tráģ¯", "storage_template_settings_description": "QuáēŖn lÃŊ cáēĨu trÃēc thư máģĨc và tÃĒn táē­p tin cáģ§a áēŖnh táēŖi lÃĒn", "storage_template_user_label": "CáģĨm táģĢ {label} là NhÃŖn lưu tráģ¯ cáģ§a ngưáģi dÚng", "system_settings": "Cài đáēˇt háģ‡ tháģ‘ng", "tag_cleanup_job": "Dáģn dáēšp tháēģ", - "template_email_available_tags": "BáēĄn cÃŗ tháģƒ dÚng cÃĄc biáēŋn sau trong đáģ‹nh dáēĄng máēĢu: {tags}", - "template_email_if_empty": "Náēŋu đáģ‹nh dáēĄng máēĢu tráģ‘ng, đáģ‹a cháģ‰ email máēˇc đáģ‹nh sáēŊ đưáģŖc sáģ­ dáģĨng.", + "template_email_available_tags": "BáēĄn cÃŗ tháģƒ sáģ­ dáģĨng cÃĄc biáēŋn sau trong máēĢu cáģ§a báēĄn: {tags}", + "template_email_if_empty": "Náēŋu máēĢu đáģƒ tráģ‘ng, email máēˇc đáģ‹nh sáēŊ đưáģŖc sáģ­ dáģĨng.", + "template_email_invite_album": "MáēĢu máģi tham gia album", "template_email_preview": "Xem trưáģ›c", - "template_email_settings": "MáēĢu đáģ‹nh dáēĄng email", - "template_email_settings_description": "QuáēŖn lÃŊ cÃĄc máēĢu đáģ‹nh dáēĄng email thông bÃĄo tÚy biáēŋn", - "template_email_update_album": "Cáē­p nháē­t MáēĢu Album", - "template_email_welcome": "Đáģ‹nh dáēĄng email chào máģĢng", - "template_settings": "CÃĄc đáģ‹nh dáēĄng thông bÃĄo", - "template_settings_description": "QuáēŖn lÃŊ cÃĄc đáģ‹nh dáēĄng thông bÃĄo tÚy biáēŋn.", + "template_email_settings": "MáēĢu Email", + "template_email_update_album": "MáēĢu khi album cÃŗ náģ™i dung máģ›i", + "template_email_welcome": "MáēĢu email chào máģĢng", + "template_settings": "MáēĢu thông bÃĄo", + "template_settings_description": "QuáēŖn lÃŊ cÃĄc máēĢu thông bÃĄo tÚy cháģ‰nh", "theme_custom_css_settings": "CSS tÚy cháģ‰nh", "theme_custom_css_settings_description": "Cascading Style Sheets cho phÊp tÚy cháģ‰nh thiáēŋt káēŋ cáģ§a Immich.", "theme_settings": "Cháģ§ Ä‘áģ", "theme_settings_description": "QuáēŖn lÃŊ tÚy cháģ‰nh giao diáģ‡n web cáģ§a Immich", - "these_files_matched_by_checksum": "CÃĄc táē­p tin này kháģ›p váģ›i cÃĄc giÃĄ tráģ‹ băm cáģ§a chÃēng", "thumbnail_generation_job": "TáēĄo hÃŦnh thu nháģ", "thumbnail_generation_job_description": "TáēĄo hÃŦnh thu nháģ láģ›n, nháģ và máģ cho máģ—i áēŖnh, cÅŠng như hÃŦnh thu nháģ cho máģ—i ngưáģi", "transcoding_acceleration_api": "API Tăng táģ‘c", @@ -272,14 +269,14 @@ "transcoding_acceleration_qsv": "Quick Sync (yÃĒu cáē§u CPU Intel tháēŋ háģ‡ 7 hoáēˇc máģ›i hÆĄn)", "transcoding_acceleration_rkmpp": "RKMPP (cháģ‰ trÃĒn cÃĄc SOC cáģ§a Rockchip)", "transcoding_acceleration_vaapi": "VAAPI", - "transcoding_accepted_audio_codecs": "CÃĄc codec Ãĸm thanh đưáģŖc cháēĨp nháē­n", - "transcoding_accepted_audio_codecs_description": "Cháģn cÃĄc codec Ãĸm thanh không cáē§n pháēŖi chuyáģƒn mÃŖ. Cháģ‰ Ä‘Æ°áģŖc sáģ­ dáģĨng cho máģ™t sáģ‘ quy táē¯c chuyáģƒn mÃŖ nháēĨt đáģ‹nh.", + "transcoding_accepted_audio_codecs": "CÃĄc chuáēŠn nÊn Ãĸm thanh đưáģŖc cháēĨp nháē­n", + "transcoding_accepted_audio_codecs_description": "Cháģn cÃĄc chuáēŠn nÊn Ãĸm thanh không cáē§n pháēŖi chuyáģƒn mÃŖ. Cháģ‰ Ä‘Æ°áģŖc sáģ­ dáģĨng cho máģ™t sáģ‘ quy táē¯c chuyáģƒn mÃŖ nháēĨt đáģ‹nh.", "transcoding_accepted_containers": "CÃĄc đáģ‹nh dáēĄng video đưáģŖc cháēĨp nháē­n", "transcoding_accepted_containers_description": "Cháģn cÃĄc đáģ‹nh dáēĄng táē­p tin không cáē§n chuyáģƒn đáģ•i sang MP4. Cháģ‰ Ä‘Æ°áģŖc sáģ­ dáģĨng cho máģ™t sáģ‘ quy táē¯c chuyáģƒn mÃŖ nháēĨt đáģ‹nh.", - "transcoding_accepted_video_codecs": "CÃĄc codec video đưáģŖc cháēĨp nháē­n", - "transcoding_accepted_video_codecs_description": "Cháģn cÃĄc codec video không cáē§n pháēŖi chuyáģƒn mÃŖ. Cháģ‰ Ä‘Æ°áģŖc sáģ­ dáģĨng cho máģ™t sáģ‘ quy táē¯c chuyáģƒn mÃŖ nháēĨt đáģ‹nh.", + "transcoding_accepted_video_codecs": "CÃĄc chuáēŠn nÊn video đưáģŖc cháēĨp nháē­n", + "transcoding_accepted_video_codecs_description": "Cháģn cÃĄc chuáēŠn nÊn video không cáē§n pháēŖi chuyáģƒn mÃŖ. Cháģ‰ Ä‘Æ°áģŖc sáģ­ dáģĨng cho máģ™t sáģ‘ quy táē¯c chuyáģƒn mÃŖ nháēĨt đáģ‹nh.", "transcoding_advanced_options_description": "CÃĄc tÚy cháģn mà háē§u háēŋt ngưáģi dÚng không cáē§n pháēŖi thay đáģ•i", - "transcoding_audio_codec": "Codec Ãĸm thanh", + "transcoding_audio_codec": "ChuáēŠn nÊn Ãĸm thanh", "transcoding_audio_codec_description": "Opus là tÚy cháģn cháēĨt lưáģŖng cao nháēĨt, nhưng cÃŗ tính tÆ°ÆĄng thích tháēĨp hÆĄn váģ›i cÃĄc thiáēŋt báģ‹ hoáēˇc pháē§n máģm cÅŠ.", "transcoding_bitrate_description": "Video cÃŗ bitrate cao hÆĄn hoáēˇc không áģŸ Ä‘áģ‹nh dáēĄng đưáģŖc cháēĨp nháē­n", "transcoding_codecs_learn_more": "Đáģƒ tÃŦm hiáģƒu thÃĒm váģ thuáē­t ngáģ¯ Ä‘Æ°áģŖc sáģ­ dáģĨng áģŸ Ä‘Ãĸy, hÃŖy tham kháēŖo tài liáģ‡u FFmpeg cho codec H.264, codec HEVC và codec VP9.", @@ -289,12 +286,11 @@ "transcoding_constant_rate_factor_description": "MáģŠc cháēĨt lưáģŖng video. CÃĄc giÃĄ tráģ‹ Ä‘iáģƒn hÃŦnh là 23 cho H.264, 28 cho HEVC, 31 cho VP9 và 35 cho AV1. GiÃĄ tráģ‹ tháēĨp hÆĄn thÃŦ táģ‘t hÆĄn, nhưng táēĄo ra cÃĄc táē­p tin láģ›n hÆĄn.", "transcoding_disabled_description": "Không chuyáģƒn mÃŖ báēĨt káģŗ video nào, cÃŗ tháģƒ gÃĸy láģ—i phÃĄt láēĄi trÃĒn máģ™t sáģ‘ thiáēŋt báģ‹", "transcoding_encoding_options": "CÃĄc tÚy cháģn mÃŖ hÃŗa", - "transcoding_encoding_options_description": "Thiáēŋt láē­p codec, đáģ™ phÃĸn giáēŖi, cháēĨt lưáģŖng và cÃĄc tÚy cháģn khÃĄc cho video", + "transcoding_encoding_options_description": "Thiáēŋt láē­p chuáēŠn nÊn video, đáģ™ phÃĸn giáēŖi, cháēĨt lưáģŖng và cÃĄc tÚy cháģn khÃĄc cho video", "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_acceleration_description": "Tháģ­ nghiáģ‡m: nhanh hÆĄn nhiáģu nhưng cÃŗ tháģƒ làm giáēŖm cháēĨt lưáģŖng áģŸ cÚng máģŠc 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 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.", "transcoding_max_bitrate": "Bitrate táģ‘i đa", @@ -302,16 +298,17 @@ "transcoding_max_keyframe_interval": "KhoáēŖng cÃĄch táģ‘i đa giáģ¯a cÃĄc khung hÃŦnh chính", "transcoding_max_keyframe_interval_description": "Thiáēŋt láē­p khoáēŖng tháģi gian táģ‘i đa giáģ¯a cÃĄc khung hÃŦnh chính. GiÃĄ tráģ‹ tháēĨp hÆĄn làm giáēŖm hiáģ‡u suáēĨt nÊn, nhưng cáēŖi thiáģ‡n tháģi gian tÃŦm kiáēŋm và cÃŗ tháģƒ cáēŖi thiáģ‡n cháēĨt lưáģŖng trong cÃĄc cáēŖnh cÃŗ chuyáģƒn đáģ™ng nhanh. GiÃĄ tráģ‹ 0 đáģƒ táģą Ä‘áģ™ng thiáēŋt láē­p giÃĄ tráģ‹ này.", "transcoding_optimal_description": "Video cÃŗ đáģ™ phÃĸn giáēŖi cao hÆĄn máģĨc tiÃĒu hoáēˇc không áģŸ Ä‘áģ‹nh dáēĄng đưáģŖc cháēĨp nháē­n", - "transcoding_policy": "Chính sÃĄch chuyáģƒn mÃŖ video", - "transcoding_preferred_hardware_device": "Thiáēŋt báģ‹ pháē§n cáģŠng ưa thích", + "transcoding_policy": "Quy táē¯c chuyáģƒn mÃŖ video", + "transcoding_policy_description": "Cháģn khi nào video sáēŊ chuyáģƒn mÃŖ", + "transcoding_preferred_hardware_device": "Thiáēŋt báģ‹ pháē§n cáģŠng ưa tiÃĒn", "transcoding_preferred_hardware_device_description": "Cháģ‰ ÃĄp dáģĨng cho VAAPI và QSV. Thiáēŋt láē­p nÃēt dri đưáģŖc sáģ­ dáģĨng cho chuyáģƒn mÃŖ pháē§n cáģŠng.", - "transcoding_preset_preset": "Preset (-preset)", - "transcoding_preset_preset_description": "Táģ‘c đáģ™ nÊn. CÃĄc preset cháē­m hÆĄn táēĄo ra cÃĄc táē­p tin nháģ hÆĄn và cáēŖi thiáģ‡n cháēĨt lưáģŖng khi máģĨc tiÃĒu là máģ™t bitrate cáģĨ tháģƒ. VP9 cháģ‰ háģ— tráģŖ cÃĄc preset táģĢ 'ultrafast' đáēŋn 'faster'.", + "transcoding_preset_preset": "MáēĢu cÃŗ sáēĩn (-preset)", + "transcoding_preset_preset_description": "Táģ‘c đáģ™ nÊn. CÃĄc máēĢu cÃŗ sáēĩn cháē­m hÆĄn táēĄo ra cÃĄc táē­p tin nháģ hÆĄn và cáēŖi thiáģ‡n cháēĨt lưáģŖng khi máģĨc tiÃĒu là máģ™t bitrate cáģĨ tháģƒ. VP9 cháģ‰ háģ— tráģŖ cÃĄc máēĢu cÃŗ sáēĩn táģĢ 'ultrafast' đáēŋn 'faster'.", "transcoding_reference_frames": "Khung hÃŦnh tham chiáēŋu", "transcoding_reference_frames_description": "Sáģ‘ lưáģŖng khung hÃŦnh tham chiáēŋu khi nÊn máģ™t khung hÃŦnh nháēĨt đáģ‹nh. GiÃĄ tráģ‹ cao hÆĄn cáēŖi thiáģ‡n hiáģ‡u suáēĨt nÊn nhưng làm cháē­m quÃĄ trÃŦnh mÃŖ hÃŗa. GiÃĄ tráģ‹ 0 đáģƒ táģą Ä‘áģ™ng thiáēŋt láē­p giÃĄ tráģ‹ này.", "transcoding_required_description": "Cháģ‰ video không áģŸ Ä‘áģ‹nh dáēĄng đưáģŖc cháēĨp nháē­n", "transcoding_settings": "Chuyáģƒn mÃŖ video", - "transcoding_settings_description": "QuáēŖn lÃŊ thông tin đáģ™ phÃĸn giáēŖi và mÃŖ hÃŗa cáģ§a cÃĄc video", + "transcoding_settings_description": "QuáēŖn lÃŊ video chuyáģƒn mÃŖ và cÃĄch xáģ­ lÃŊ", "transcoding_target_resolution": "Đáģ™ phÃĸn giáēŖi máģĨc tiÃĒu", "transcoding_target_resolution_description": "Đáģ™ phÃĸn giáēŖi cao hÆĄn cÃŗ tháģƒ giáģ¯ láēĄi nhiáģu chi tiáēŋt hÆĄn nhưng máēĨt nhiáģu tháģi gian hÆĄn đáģƒ mÃŖ hÃŗa, cÃŗ kích thưáģ›c táē­p tin láģ›n hÆĄn và cÃŗ tháģƒ làm giáēŖm kháēŖ năng pháēŖn háģ“i cáģ§a áģŠng dáģĨng.", "transcoding_temporal_aq": "LưáģŖng táģ­ hÃŗa thích áģŠng (Temporal AQ)", @@ -324,21 +321,20 @@ "transcoding_transcode_policy_description": "Quy táē¯c khi nào video nÃĒn đưáģŖc chuyáģƒn mÃŖ. CÃĄc video HDR luôn đưáģŖc chuyáģƒn mÃŖ (ngoáēĄi tráģĢ khi tính năng chuyáģƒn mÃŖ báģ‹ táē¯t).", "transcoding_two_pass_encoding": "MÃŖ hÃŗa hai láē§n", "transcoding_two_pass_encoding_setting_description": "Chuyáģƒn mÃŖ hai láē§n đáģƒ táēĄo ra video đưáģŖc mÃŖ hÃŗa táģ‘t hÆĄn. Khi bitrate táģ‘i đa đưáģŖc báē­t (báē¯t buáģ™c đáģƒ hoáēĄt đáģ™ng váģ›i H.264 và HEVC), cháēŋ đáģ™ này sáģ­ dáģĨng máģ™t pháēĄm vi bitrate dáģąa trÃĒn bitrate táģ‘i đa và báģ qua CRF. Đáģ‘i váģ›i VP9, CRF cÃŗ tháģƒ Ä‘Æ°áģŖc sáģ­ dáģĨng náēŋu bitrate táģ‘i đa báģ‹ táē¯t.", - "transcoding_video_codec": "Codec Video", + "transcoding_video_codec": "ChuáēŠn nÊn video", "transcoding_video_codec_description": "VP9 cÃŗ hiáģ‡u suáēĨt cao và tÆ°ÆĄng thích táģ‘t váģ›i web, nhưng tháģi gian chuyáģƒn mÃŖ lÃĸu hÆĄn. HEVC cÃŗ hiáģ‡u suáēĨt tÆ°ÆĄng táģą, nhưng tÆ°ÆĄng thích web tháēĨp hÆĄn. H.264 tÆ°ÆĄng thích ráģ™ng rÃŖi và chuyáģƒn mÃŖ nhanh, nhưng táēĄo ra cÃĄc táē­p tin cÃŗ kích thưáģ›c láģ›n. AV1 là codec hiáģ‡u quáēŖ nháēĨt nhưng không đưáģŖc háģ— tráģŖ trÃĒn cÃĄc thiáēŋt báģ‹ cÅŠ.", "trash_enabled_description": "Báē­t tính năng ThÚng rÃĄc", "trash_number_of_days": "Sáģ‘ ngày", "trash_number_of_days_description": "Sáģ‘ ngày giáģ¯ cÃĄc áēŖnh trong thÚng rÃĄc trưáģ›c khi xÃŗa chÃēng vÄŠnh viáģ…n", "trash_settings": "ThÚng rÃĄc", "trash_settings_description": "QuáēŖn lÃŊ cài đáēˇt thÚng rÃĄc", - "untracked_files": "CÃĄc táē­p tin không đưáģŖc theo dÃĩi", - "untracked_files_description": "Nháģ¯ng táē­p tin này không đưáģŖc áģŠng dáģĨng theo dÃĩi. ChÃēng cÃŗ tháģƒ là káēŋt quáēŖ cáģ§a cÃĄc thao tÃĄc di chuyáģƒn tháēĨt báēĄi, táēŖi lÃĒn báģ‹ giÃĄn đoáēĄn, hoáēˇc báģ‹ báģ láēĄi do láģ—i", "user_cleanup_job": "Dáģn dáēšp ngưáģi dÚng", "user_delete_delay": "Tài khoáēŖn và cÃĄc áēŖnh cáģ§a {user} sáēŊ đưáģŖc lÃĒn láģ‹ch xÃŗa vÄŠnh viáģ…n sau {delay, plural, one {# ngày} other {# ngày}}.", "user_delete_delay_settings": "Tháģi gian xÃŗa", "user_delete_delay_settings_description": "Sáģ‘ ngày cháģ xÃŗa đáģƒ xÃŗa vÄŠnh viáģ…n tài khoáēŖn và cÃĄc áēŖnh cáģ§a ngưáģi dÚng. TÃĄc váģĨ xÃŗa ngưáģi dÚng cháēĄy vào giáģ¯a đÃĒm đáģƒ kiáģƒm tra cÃĄc ngưáģi dÚng sáēĩn sàng báģ‹ xÃŗa. Thay đáģ•i cài đáēˇt này sáēŊ đưáģŖc Ä‘ÃĄnh giÃĄ vào láē§n tháģąc hiáģ‡n tiáēŋp theo.", - "user_delete_immediately": "Tài khoáēŖn và cÃĄc áēŖnh cáģ§a {user} sáēŊ đưáģŖc xáēŋp hàng đáģƒ xÃŗa vÄŠnh viáģ…n ngay láē­p táģŠc.", + "user_delete_immediately": "Tài khoáēŖn và cÃĄc áēŖnh cáģ§a {user} sáēŊ đưáģŖc đưa vào hàng đáģŖi đáģƒ xÃŗa vÄŠnh viáģ…n ngay láē­p táģŠc.", "user_delete_immediately_checkbox": "Xáēŋp hàng ngưáģi dÚng và cÃĄc áēŖnh đáģƒ xÃŗa ngay láē­p táģŠc", + "user_details": "Chi tiáēŋt Ngưáģi dÚng", "user_management": "QuáēŖn lÃŊ ngưáģi dÚng", "user_password_has_been_reset": "Máē­t kháēŠu cáģ§a ngưáģi dÚng Ä‘ÃŖ đưáģŖc đáēˇt láēĄi:", "user_password_reset_description": "Vui lÃ˛ng cung cáēĨp máē­t kháēŠu táēĄm tháģi cho ngưáģi dÚng và thông bÃĄo ráēąng háģ cáē§n thay đáģ•i máē­t kháēŠu khi đăng nháē­p láē§n tiáēŋp theo.", @@ -358,15 +354,19 @@ "admin_password": "Máē­t kháēŠu QuáēŖn tráģ‹ viÃĒn", "administration": "QuáēŖn tráģ‹", "advanced": "NÃĸng cao", + "advanced_settings_enable_alternate_media_filter_subtitle": "DÚng tÚy cháģn này đáģƒ láģc phÆ°ÆĄng tiáģ‡n khi đáģ“ng báģ™ theo tiÃĒu chí khÃĄc. Cháģ‰ tháģ­ khi áģŠng dáģĨng không nháē­n diáģ‡n đưáģŖc táēĨt cáēŖ cÃĄc album.", + "advanced_settings_enable_alternate_media_filter_title": "[THáģŦ NGHIáģ†M] DÚng báģ™ láģc đáģ“ng báģ™ album thay tháēŋ", "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.", - "advanced_settings_proxy_headers_title": "CÃĄc header cáģ§a proxy", + "advanced_settings_proxy_headers_subtitle": "XÃĄc đáģ‹nh cÃĄc tiÃĒu đáģ proxy Immich sáēŊ gáģ­i kèm máģ—i yÃĒu cáē§u máēĄng", + "advanced_settings_proxy_headers_title": "TiÃĒu đáģ proxy", "advanced_settings_self_signed_ssl_subtitle": "Báģ qua xÃĄc minh cháģŠng cháģ‰ SSL cho mÃĄy cháģ§ cuáģ‘i. YÃĒu cáē§u cho cháģŠng cháģ‰ táģą kÃŊ.", "advanced_settings_self_signed_ssl_title": "Cho phÊp cháģŠng cháģ‰ SSL táģą kÃŊ", + "advanced_settings_sync_remote_deletions_subtitle": "Táģą Ä‘áģ™ng xÃŗa hoáēˇc khôi pháģĨc dáģ¯ liáģ‡u trÃĒn thiáēŋt báģ‹ này khi báēĄn thao tÃĄc trÃĒn web", + "advanced_settings_sync_remote_deletions_title": "Đáģ“ng báģ™ viáģ‡c xÃŗa táģĢ thiáēŋt báģ‹ khÃĄc [THáģŦ NGHIáģ†M]", "advanced_settings_tile_subtitle": "Cài đáēˇt cho ngưáģi dÚng nÃĸng cao", - "advanced_settings_troubleshooting_subtitle": "Báē­t cÃĄc tính năng báģ• sung đáģƒ xáģ­ lÃŊ sáģą cáģ‘.", + "advanced_settings_troubleshooting_subtitle": "Báē­t cÃĄc tính năng báģ• sung đáģƒ xáģ­ lÃŊ sáģą cáģ‘", "advanced_settings_troubleshooting_title": "Xáģ­ lÃŊ sáģą cáģ‘", "age_months": "{months, plural, one {# thÃĄng} other {# thÃĄng}} tuáģ•i", "age_year_months": "1 tuáģ•i, {months, plural, one {# thÃĄng} other {# thÃĄng}}", @@ -386,17 +386,13 @@ "album_remove_user": "XÃŗa ngưáģi dÚng?", "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": "{count} máģĨc", - "album_thumbnail_card_shared": " ¡ Chia sáēģ", - "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}", "album_user_removed": "ÄÃŖ xÃŗa {user}", "album_viewer_appbar_delete_confirm": "BáēĄn cÃŗ muáģ‘n xÃŗa album này kháģi tài khoáēŖn cáģ§a mÃŦnh không?", "album_viewer_appbar_share_err_delete": "XoÃĄ album tháēĨt báēĄi", - "album_viewer_appbar_share_err_leave": "Ráģi kháģi album tháēĨt báēĄáģ‹", + "album_viewer_appbar_share_err_leave": "Ráģi kháģi album tháēĨt báēĄi", "album_viewer_appbar_share_err_remove": "CÃŗ váēĨn đáģ khi xoÃĄ áēŖnh kháģi album", "album_viewer_appbar_share_err_title": "Thay đáģ•i tiÃĒu đáģ album tháēĨt báēĄi", "album_viewer_appbar_share_leave": "Ráģi kháģi album", @@ -405,6 +401,9 @@ "album_with_link_access": "Cho phÊp báēĨt káģŗ ai cÃŗ liÃĒn káēŋt xem áēŖnh và ngưáģi trong album này.", "albums": "Album", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", + "albums_default_sort_order": "TháģŠ táģą sáē¯p xáēŋp album máēˇc đáģ‹nh", + "albums_default_sort_order_description": "TháģŠ táģą sáē¯p xáēŋp ban đáē§u cho cÃĄc áēŖnh khi táēĄo album máģ›i.", + "albums_feature_description": "CÃĄc báģ™ sưu táē­p áēŖnh cÃŗ tháģƒ Ä‘Æ°áģŖc chia sáēģ váģ›i nháģ¯ng ngưáģi dÚng khÃĄc.", "all": "TáēĨt cáēŖ", "all_albums": "TáēĨt cáēŖ album", "all_people": "TáēĨt cáēŖ máģi ngưáģi", @@ -413,6 +412,7 @@ "allow_edits": "Cho phÊp cháģ‰nh sáģ­a", "allow_public_user_to_download": "Cho phÊp ngưáģi dÚng công khai táēŖi xuáģ‘ng", "allow_public_user_to_upload": "Cho phÊp ngưáģi dÚng công khai táēŖi lÃĒn", + "alt_text_qr_code": "áēĸnh mÃŖ QR", "anti_clockwise": "Xoay trÃĄi", "api_key": "KhÃŗa API", "api_key_description": "GiÃĄ tráģ‹ này cháģ‰ Ä‘Æ°áģŖc hiáģƒn tháģ‹ máģ™t láē§n. Vui lÃ˛ng sao chÊp nÃŗ trưáģ›c khi Ä‘Ãŗng cáģ­a sáģ•.", @@ -436,15 +436,15 @@ "asset_action_delete_err_read_only": "Không tháģƒ xoÃĄ áēŖnh cháģ‰ cÃŗ quyáģn đáģc, báģ qua", "asset_action_share_err_offline": "Không tháģƒ táēŖi áēŖnh ngoáēĄi tuyáēŋn, báģ qua", "asset_added_to_album": "ÄÃŖ thÃĒm vào album", - "asset_adding_to_album": "Đang thÃĒm vào album...", + "asset_adding_to_album": "Đang thÃĒm vào albumâ€Ļ", "asset_description_updated": "Mô táēŖ áēŖnh Ä‘ÃŖ đưáģŖc cáē­p nháē­t", "asset_filename_is_offline": "áēĸnh {filename} đang ngoáēĄi tuyáēŋn", "asset_has_unassigned_faces": "áēĸnh chưa đưáģŖc gÃĄn khuôn máēˇt", - "asset_hashing": "Đang băm...", + "asset_hashing": "Đang bămâ€Ļ", "asset_list_group_by_sub_title": "NhÃŗm theo", "asset_list_layout_settings_dynamic_layout_title": "Báģ‘ cáģĨc đáģ™ng", "asset_list_layout_settings_group_automatically": "Táģą Ä‘áģ™ng", - "asset_list_layout_settings_group_by": " NhÃŗm áēŖnh theo", + "asset_list_layout_settings_group_by": "NhÃŗm áēŖnh theo", "asset_list_layout_settings_group_by_month_day": "ThÃĄng + ngày", "asset_list_layout_sub_title": "Báģ‘ cáģĨc", "asset_list_settings_subtitle": "Cài đáēˇt báģ‘ cáģĨc lưáģ›i áēŖnh", @@ -455,7 +455,7 @@ "asset_skipped": "ÄÃŖ báģ qua", "asset_skipped_in_trash": "Trong thÚng rÃĄc", "asset_uploaded": "ÄÃŖ táēŖi lÃĒn", - "asset_uploading": "Đang táēŖi lÃĒn...", + "asset_uploading": "Đang táēŖi lÃĒnâ€Ļ", "asset_viewer_settings_subtitle": "QuáēŖn lÃŊ hiáģƒn tháģ‹ thư viáģ‡n", "asset_viewer_settings_title": "TrÃŦnh xem áēŖnh", "assets": "CÃĄc táē­p tin", @@ -465,6 +465,8 @@ "assets_count": "{count, plural, one {# máģĨc} other {# máģĨc}}", "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_downloaded_failed": "{count, plural, one {ÄÃŖ táēŖi xuáģ‘ng # táē­p tin - {error} táē­p tin tháēĨt báēĄi} other {ÄÃŖ táēŖi xuáģ‘ng # táē­p tin - {error} táē­p tin tháēĨt báēĄi}}", + "assets_downloaded_successfully": "{count, plural, one {ÄÃŖ táēŖi xuáģ‘ng # táē­p tin thành công} other {ÄÃŖ táēŖi xuáģ‘ng # táē­p tin thành công}}", "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}}", @@ -479,6 +481,7 @@ "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", "automatic_endpoint_switching_title": "Táģą Ä‘áģ™ng chuyáģƒn đáģ•i đáģ‹a cháģ‰ mÃĄy cháģ§", + "autoplay_slideshow": "Táģą Ä‘áģ™ng phÃĄt trÃŦnh chiáēŋu", "back": "Quay láēĄi", "back_close_deselect": "Quay láēĄi, Ä‘Ãŗng, hoáēˇc báģ cháģn", "background_location_permission": "Quyáģn truy cáē­p váģ‹ trí áģŸ náģn", @@ -490,17 +493,17 @@ "backup_album_selection_page_selection_info": "Thông tin cÃĄc máģĨc Ä‘ÃŖ cháģn", "backup_album_selection_page_total_assets": "Táģ•ng sáģ‘ áēŖnh không trÚng láēˇp", "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_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 {filename}", - "backup_background_service_default_notification": "Đang kiáģƒm tra áēŖnh máģ›i...", + "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 {filename}", + "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 {filename} tháēĨt báēĄi", "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_content": "Báē­t làm máģ›i áģŠng dáģĨng trong náģn táēĄi 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ÃĄ", - "backup_controller_page_background_app_refresh_enable_button_text": "Chuyáģƒn táģ›i cài đáēˇt", + "backup_controller_page_background_app_refresh_enable_button_text": "Đi táģ›i cài đáēˇt", "backup_controller_page_background_battery_info_link": "HÃŖy cháģ‰ cho tôi cÃĄch làm", "backup_controller_page_background_battery_info_message": "Đáģƒ cÃŗ tráēŖi nghiáģ‡m sao lưu náģn táģ‘t nháēĨt, vui lÃ˛ng vô hiáģ‡u hÃŗa báēĨt káģŗ táģ‘i ưu hÃŗa pin nào đang háēĄn cháēŋ hoáēĄt đáģ™ng náģn cáģ§a Immich.\n\nVÃŦ điáģu này pháģĨ thuáģ™c vào thiáēŋt báģ‹, vui lÃ˛ng tham kháēŖo thông tin cáē§n thiáēŋt cáģ§a nhà sáēŖn xuáēĨt thiáēŋt báģ‹ cáģ§a báēĄn.", "backup_controller_page_background_battery_info_ok": "Đáģ“ng ÃŊ", @@ -522,6 +525,7 @@ "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_id": "ID: {id}", "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", @@ -542,9 +546,13 @@ "backup_manual_in_progress": "Đang táēŖi lÃĒn. Vui lÃ˛ng tháģ­ láēĄi sau", "backup_manual_success": "Thành công", "backup_manual_title": "TráēĄng thÃĄi táēŖi lÃĒn", - "backup_options_page_title": "Tuáģŗ cháģ‰nh sao lưu", - "backup_setting_subtitle": "QuáēŖn lÃŊ cÃĄch táēŖi lÃĒn khi app đang đưáģŖc sáģ­ dáģĨng, và khi app đưáģŖc đưa vào náģn", + "backup_options_page_title": "CÃĄc tÚy cháģn sao lưu", + "backup_setting_subtitle": "QuáēŖn lÃŊ cài đáēˇt táēŖi lÃĒn áģŸ cháēŋ đáģ™ náģn và khi đang máģŸ", "backward": "LÚi láēĄi", + "biometric_auth_enabled": "ÄÃŖ báē­t xÃĄc tháģąc sinh tráē¯c háģc", + "biometric_locked_out": "BáēĄn Ä‘ÃŖ báģ‹ khÃŗa xÃĄc tháģąc báēąng sinh tráē¯c háģc", + "biometric_no_options": "Không cÃŗ tÚy cháģn báēąng sinh tráē¯c háģc", + "biometric_not_available": "XÃĄc tháģąc sinh tráē¯c háģc không kháēŖ dáģĨng trÃĒn thiáēŋt báģ‹ này", "birthdate_saved": "Ngày sinh Ä‘ÃŖ đưáģŖc lưu thành công", "birthdate_set_description": "Ngày sinh đưáģŖc sáģ­ dáģĨng đáģƒ tính tuáģ•i cáģ§a ngưáģi này táēĄi tháģi điáģƒm cháģĨp áēŖnh.", "blurred_background": "Náģn máģ", @@ -555,21 +563,17 @@ "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áģ ({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 ({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": "{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áģ ({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", @@ -582,7 +586,10 @@ "cannot_merge_people": "Không tháģƒ háģŖp nháēĨt ngưáģi", "cannot_undo_this_action": "BáēĄn không tháģƒ hoàn tÃĄc hành đáģ™ng này!", "cannot_update_the_description": "Không tháģƒ cáē­p nháē­t mô táēŖ", + "cast": "Chiáēŋu", + "cast_description": "CáēĨu hÃŦnh cÃĄc thiáēŋt báģ‹ chiáēŋu kháēŖ dáģĨng", "change_date": "Thay đáģ•i ngày", + "change_description": "Thay đáģ•i mô táēŖ", "change_display_order": "Thay đáģ•i tháģŠ táģą hiáģƒu tháģ‹", "change_expiration_time": "Thay đáģ•i tháģi gian háēŋt háēĄn", "change_location": "Thay đáģ•i váģ‹ trí", @@ -595,12 +602,12 @@ "change_password_form_new_password": "Máē­t kháēŠu máģ›i", "change_password_form_password_mismatch": "Máē­t kháēŠu không giáģ‘ng nhau", "change_password_form_reenter_new_password": "Nháē­p láēĄi máē­t kháēŠu máģ›i", + "change_pin_code": "Thay đáģ•i mÃŖ PIN", "change_your_password": "Thay đáģ•i máē­t kháēŠu cáģ§a báēĄn", "changed_visibility_successfully": "ÄÃŖ thay đáģ•i tráēĄng thÃĄi hiáģƒn tháģ‹ thành công", - "check_all": "Cháģn táēĨt cáēŖ", "check_corrupt_asset_backup": "Kiáģƒm tra táē­p tin báģ‹ háģng", "check_corrupt_asset_backup_button": "Tiáēŋn hành kiáģƒm tra", - "check_corrupt_asset_backup_description": "CháēĄy láģ‡nh này qua máēĄng WiFi và khi toàn báģ™ táē­p tin Ä‘ÃŖ đưáģŖc táēŖi lÃĒn trÃĒn mÃĄy cháģ§. Láģ‡nh này cÃŗ tháģƒ cháēĄy trong máģ™t vài phÃēt", + "check_corrupt_asset_backup_description": "Cháģ‰ cháēĄy kiáģƒm tra này khi cÃŗ Wi-Fi và sau khi Ä‘ÃŖ sao lưu toàn báģ™ dáģ¯ liáģ‡u. QuÃĄ trÃŦnh cÃŗ tháģƒ máēĨt vài phÃēt.", "check_logs": "Kiáģƒm tra nháē­t kÃŊ", "choose_matching_people_to_merge": "Cháģn nháģ¯ng ngưáģi trÚng kháģ›p đáģƒ háģŖp nháēĨt", "city": "Thành pháģ‘", @@ -628,24 +635,29 @@ "comments_and_likes": "BÃŦnh luáē­n & lưáģŖt thích", "comments_are_disabled": "BÃŦnh luáē­n Ä‘ÃŖ báģ‹ táē¯t", "common_create_new_album": "TáēĄo album máģ›i", - "common_server_error": "Vui lÃ˛ng kiáģƒm tra káēŋt náģ‘i máēĄng cáģ§a báēĄn, đáēŖm báēŖo mÃĄy cháģ§ cÃŗ tháģƒ truy cáē­p đưáģŖc và cÃĄc phiÃĒn báēŖn áģŠng dáģĨng/mÃĄy cháģ§ pháēŖi tÆ°ÆĄng thích váģ›i nhau", + "common_server_error": "Vui lÃ˛ng kiáģƒm tra káēŋt náģ‘i máēĄng cáģ§a báēĄn, đáēŖm báēŖo mÃĄy cháģ§ cÃŗ tháģƒ truy cáē­p đưáģŖc và phiÃĒn báēŖn áģŠng dáģĨng/mÃĄy cháģ§ tÆ°ÆĄng thích váģ›i nhau.", "completed": "Hoàn táēĨt", "confirm": "XÃĄc nháē­n", "confirm_admin_password": "XÃĄc nháē­n máē­t kháēŠu quáēŖn tráģ‹ viÃĒn", + "confirm_delete_face": "BáēĄn cÃŗ cháē¯c muáģ‘n xÃŗa khuôn máēˇt {name} kháģi áēŖnh này không?", "confirm_delete_shared_link": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n xÃŗa liÃĒn káēŋt chia sáēģ này không?", - "confirm_keep_this_delete_others": "CÃĄc hÃŦnh cÃ˛n láēĄi trong stack này sáēŊ báģ‹ xoÃĄ ngoáēĄi tráģĢ hÃŦnh này. BáēĄn cÃŗ cháē¯c cháē¯n tiáēŋp táģĨc không?", + "confirm_keep_this_delete_others": "CÃĄc áēŖnh cÃ˛n láēĄi trong nhÃŗm sáēŊ báģ‹ xoÃĄ ngoáēĄi tráģĢ áēŖnh này. BáēĄn cÃŗ cháē¯c cháē¯n tiáēŋp táģĨc không?", + "confirm_new_pin_code": "XÃĄc nháē­n mÃŖ PIN máģ›i", "confirm_password": "XÃĄc nháē­n máē­t kháēŠu", + "confirm_tag_face": "BáēĄn cÃŗ muáģ‘n gáē¯n tháēģ gÆ°ÆĄng máēˇt này là {name} không?", + "confirm_tag_face_unnamed": "BáēĄn cÃŗ muáģ‘n gáē¯n tháēģ gÆ°ÆĄng máēˇt này không?", + "connected_to": "ÄÃŖ káēŋt náģ‘i táģ›i", "contain": "CháģŠa", "context": "Ngáģ¯ cáēŖnh", "continue": "Tiáēŋp táģĨc", - "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_delete_from_local": "XÃŗa kháģi thiáēŋt báģ‹", "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": "LiÃĒn káēŋt chia sáēģ", "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", + "control_bottom_app_bar_trash_from_immich": "Di chuyáģƒn đáēŋn ThÚng rÃĄc", "copied_image_to_clipboard": "ÄÃŖ sao chÊp hÃŦnh áēŖnh vào clipboard.", "copied_to_clipboard": "ÄÃŖ sao chÊp vào clipboard!", "copy_error": "Sao chÊp láģ—i", @@ -675,20 +687,30 @@ "create_tag_description": "TáēĄo tháēģ máģ›i. Váģ›i cÃĄc tháēģ láģ“ng nhau, vui lÃ˛ng nháē­p đưáģng dáēĢn đáē§y đáģ§ cáģ§a tháēģ bao gáģ“m dáēĨu gáēĄch chÊo.", "create_user": "TáēĄo ngưáģi dÚng", "created": "ÄÃŖ táēĄo", + "created_at": "ÄÃŖ táēĄo", "crop": "Cáē¯t", "curated_object_page_title": "Sáģą váē­t", "current_device": "Thiáēŋt báģ‹ hiáģ‡n táēĄi", + "current_pin_code": "MÃŖ PIN hiáģ‡n táēĄi", "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, dd MMM", + "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Táģ‘i", + "darkTheme": "Chuyáģƒn đáģ•i cháģ§ Ä‘áģ táģ‘i", "date_after": "Ngày sau", "date_and_time": "Ngày và giáģ", "date_before": "Ngày trưáģ›c", + "date_format": "E, d LLL, 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", "deduplicate_all": "XÃŗa táēĨt cáēŖ máģĨc trÚng láēˇp", + "deduplication_criteria_1": "Kích thưáģ›c hÃŦnh áēŖnh theo byte", + "deduplication_criteria_2": "Sáģ‘ lưáģŖng dáģ¯ liáģ‡u EXIF", + "deduplication_info": "Thông tin loáēĄi báģ dáģ¯ liáģ‡u trÚng láēˇp", + "deduplication_info_description": "Đáģƒ táģą Ä‘áģ™ng cháģn trưáģ›c và loáēĄi báģ cÃĄc máģĨc trÚng láēˇp hàng loáēĄt, chÃēng tôi sáēŊ xem xÊt dáģąa trÃĒn:", "default_locale": "Ngôn ngáģ¯ và khu váģąc máēˇc đáģ‹nh", "default_locale_description": "Đáģ‹nh dáēĄng ngày và sáģ‘ dáģąa trÃĒn ngôn ngáģ¯ trÃŦnh duyáģ‡t cáģ§a báēĄn", "delete": "XÃŗa", @@ -696,17 +718,18 @@ "delete_api_key_prompt": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n xÃŗa khÃŗa API này không?", "delete_dialog_alert": "Nháģ¯ng máģĨc này sáēŊ báģ‹ xÃŗa vÄŠnh viáģ…n kháģi Immich và thiáēŋt báģ‹ cáģ§a báēĄn", "delete_dialog_alert_local": "Nháģ¯ng máģĨc này sáēŊ báģ‹ xÃŗa vÄŠnh viáģ…n kháģi thiáēŋt báģ‹ cáģ§a báēĄn nhưng váēĢn cÃ˛n lưu tráģ¯ trÃĒn mÃĄy cháģ§ Immich", - "delete_dialog_alert_local_non_backed_up": "Máģ™t sáģ‘ máģĨc chưa đưáģŖc sao lưu lÃĒn Immich và sáēŊ báģ‹ xÃŗa vÄŠnh viáģ…n kháģi thiáēŋt báģ‹ cáģ§a báēĄn.", + "delete_dialog_alert_local_non_backed_up": "Máģ™t sáģ‘ máģĨc chưa đưáģŖc sao lưu lÃĒn Immich và sáēŊ báģ‹ xÃŗa vÄŠnh viáģ…n kháģi thiáēŋt báģ‹ cáģ§a báēĄn", "delete_dialog_alert_remote": "Nháģ¯ng máģĨc này sáēŊ báģ‹ xÃŗa vÄŠnh viáģ…n kháģi mÃĄy cháģ§ Immich", "delete_dialog_ok_force": "XÃŗa VÄŠnh Viáģ…n", "delete_dialog_title": "XoÃĄ vÄŠnh viáģ…n", "delete_duplicates_confirmation": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n xÃŗa vÄŠnh viáģ…n cÃĄc báēŖn trÚng láēˇp này không?", + "delete_face": "XÃŗa khuôn máēˇt", "delete_key": "XÃŗa khÃŗa", "delete_library": "XÃŗa Thư viáģ‡n", "delete_link": "XÃŗa liÃĒn káēŋt", "delete_local_dialog_ok_backed_up_only": "XoÃĄ áēŖnh Ä‘ÃŖ sao lưu", "delete_local_dialog_ok_force": "VáēĢn xoÃĄ", - "delete_others": "XoÃĄ cÃĄc hÃŦnh cÃ˛n láēĄi", + "delete_others": "XoÃĄ áēŖnh cÃ˛n láēĄi", "delete_shared_link": "XÃŗa liÃĒn káēŋt chia sáēģ", "delete_shared_link_dialog_title": "XoÃĄ liÃĒn káēŋt Ä‘ÃŖ chia sáēģ", "delete_tag": "XÃŗa tháēģ", @@ -721,7 +744,8 @@ "direction": "Hưáģ›ng", "disabled": "Táē¯t", "disallow_edits": "Không cho phÊp cháģ‰nh sáģ­a", - "discover": "TÃŦm", + "discord": "Discord", + "discover": "KhÃĄm phÃĄ", "dismiss_all_errors": "Báģ qua táēĨt cáēŖ láģ—i", "dismiss_error": "Báģ qua láģ—i", "display_options": "TÚy cháģn hiáģƒn tháģ‹", @@ -734,10 +758,9 @@ "download": "TáēŖi xuáģ‘ng", "download_canceled": "ÄÃŖ háģ§y táēŖi xuáģ‘ng", "download_complete": "TáēŖi xuáģ‘ng hoàn táēĨt", - "download_enqueue": "Đang cháģ táēŖi xuáģ‘ng", + "download_enqueue": "TáēŖi xuáģ‘ng đang cháģ", "download_error": "Láģ—i táēŖi xuáģ‘ng", "download_failed": "TáēŖi xuáģ‘ng tháēĨt báēĄi", - "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", @@ -761,6 +784,8 @@ "edit_avatar": "Cháģ‰nh sáģ­a áēŖnh đáēĄi diáģ‡n", "edit_date": "Cháģ‰nh sáģ­a ngày", "edit_date_and_time": "Cháģ‰nh sáģ­a ngày và giáģ", + "edit_description": "Cháģ‰nh sáģ­a mô táēŖ", + "edit_description_prompt": "Vui lÃ˛ng cháģn máģ™t mô táēŖ máģ›i:", "edit_exclusion_pattern": "Cháģ‰nh sáģ­a quy táē¯c loáēĄi tráģĢ", "edit_faces": "Cháģ‰nh sáģ­a khuôn máēˇt", "edit_import_path": "Cháģ‰nh sáģ­a đưáģng dáēĢn nháē­p", @@ -780,18 +805,25 @@ "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", + "email_notifications": "Thông bÃĄo qua 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!", "enable": "Báē­t", + "enable_biometric_auth_description": "Nháē­p mÃŖ PIN cáģ§a báēĄn đáģƒ báē­t xÃĄc tháģąc sinh tráē¯c háģc", "enabled": "ÄÃŖ báē­t", "end_date": "Ngày káēŋt thÃēc", "enqueued": "ÄÃŖ xáēŋp hàng", - "enter_wifi_name": "Nháē­p tÃĒn WiFi", + "enter_wifi_name": "Nháē­p tÃĒn Wi-Fi", + "enter_your_pin_code": "Nháē­p mÃŖ PIN cáģ§a báēĄn", + "enter_your_pin_code_subtitle": "Nháē­p mÃŖ PIN cáģ§a báēĄn đáģƒ truy cáē­p Thư máģĨc báēŖo máē­t", "error": "Láģ—i", - "error_change_sort_album": "Thay đáģ•i tháģŠ táģą hiáģƒu tháģ‹ tháēĨt báēĄi", + "error_change_sort_album": "Thay đáģ•i tháģŠ táģą sáē¯p xáēŋp album tháēĨt báēĄi", + "error_delete_face": "Láģ—i khi xÃŗa khuôn máēˇt kháģi áēŖnh", "error_loading_image": "Láģ—i táēŖi áēŖnh", "error_saving_image": "Láģ—i: {error}", + "error_tag_face_bounding_box": "Láģ—i gáē¯n tháēģ khuôn máēˇt: - không tháģƒ láēĨy đưáģŖc táģa đáģ™ khung bao", "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", @@ -804,7 +836,6 @@ "cant_get_number_of_comments": "Không tháģƒ táēŖi sáģ‘ lưáģŖng bÃŦnh luáē­n", "cant_search_people": "Không tháģƒ tÃŦm kiáēŋm ngưáģi", "cant_search_places": "Không tháģƒ tÃŦm kiáēŋm đáģ‹a điáģƒm", - "cleared_jobs": "ÄÃŖ xoÃĄ cÃĄc tÃĄc váģĨ: {job}", "error_adding_assets_to_album": "Láģ—i khi thÃĒm áēŖnh vào album", "error_adding_users_to_album": "Láģ—i khi thÃĒm ngưáģi dÚng vào album", "error_deleting_shared_user": "Láģ—i khi xÃŗa ngưáģi dÚng chia sáēģ", @@ -813,24 +844,24 @@ "error_removing_assets_from_album": "Láģ—i khi xÃŗa áēŖnh kháģi album, kiáģƒm tra báēŖng điáģu khiáģƒn đáģƒ biáēŋt thÃĒm chi tiáēŋt", "error_selecting_all_assets": "Láģ—i khi cháģn táēĨt cáēŖ áēŖnh", "exclusion_pattern_already_exists": "Quy táē¯c loáēĄi tráģĢ này Ä‘ÃŖ táģ“n táēĄi.", - "failed_job_command": "Láģ‡nh {command} không thành công cho tÃĄc váģĨ: {job}", - "failed_to_create_album": "Không tháģƒ táēĄo album", + "failed_to_create_album": "TáēĄo album tháēĨt báēĄi", "failed_to_create_shared_link": "Không tháģƒ táēĄo liÃĒn káēŋt chia sáēģ", "failed_to_edit_shared_link": "Không tháģƒ cháģ‰nh sáģ­a liÃĒn káēŋt chia sáēģ", "failed_to_get_people": "Không tháģƒ táēŖi ngưáģi", "failed_to_keep_this_delete_others": "CÃŗ láģ—i trong quÃĄ trÃŦnh xoÃĄ cÃĄc hÃŦnh", "failed_to_load_asset": "Không tháģƒ táēŖi áēŖnh", "failed_to_load_assets": "Không tháģƒ táēŖi cÃĄc áēŖnh", + "failed_to_load_notifications": "Không tháģƒ táēŖi cÃĄc thông bÃĄo", "failed_to_load_people": "Không tháģƒ táēŖi ngưáģi", "failed_to_remove_product_key": "Không tháģƒ xÃŗa khÃŗa sáēŖn pháēŠm", "failed_to_stack_assets": "Không tháģƒ nhÃŗm cÃĄc áēŖnh", "failed_to_unstack_assets": "Không tháģƒ huáģˇ xáēŋp nhÃŗm cÃĄc áēŖnh", + "failed_to_update_notification_status": "Cáē­p nháē­t tráēĄng thÃĄi thông bÃĄo tháēĨt báēĄi", "import_path_already_exists": "Đưáģng dáēĢn nháē­p này Ä‘ÃŖ táģ“n táēĄi.", "incorrect_email_or_password": "Email hoáēˇc máē­t kháēŠu không chính xÃĄc", "paths_validation_failed": "{paths, plural, one {# đưáģng dáēĢn} other {# đưáģng dáēĢn}} không háģŖp láģ‡", "profile_picture_transparent_pixels": "áēĸnh đáēĄi diáģ‡n không tháģƒ cÃŗ điáģƒm áēŖnh trong suáģ‘t. Vui lÃ˛ng phÃŗng to và/hoáēˇc di chuyáģƒn hÃŦnh áēŖnh.", "quota_higher_than_disk_size": "BáēĄn Ä‘ÃŖ đáēˇt háēĄn máģŠc cao hÆĄn kích thưáģ›c áģ• Ä‘ÄŠa", - "repair_unable_to_check_items": "Không tháģƒ kiáģƒm tra {count, select, one {máģĨc} other {máģĨc}}", "unable_to_add_album_users": "Không tháģƒ thÃĒm ngưáģi dÚng vào album", "unable_to_add_assets_to_shared_link": "Không tháģƒ thÃĒm áēŖnh vào liÃĒn káēŋt chia sáēģ", "unable_to_add_comment": "Không tháģƒ thÃĒm bÃŦnh luáē­n", @@ -842,13 +873,13 @@ "unable_to_archive_unarchive": "Không tháģƒ {archived, select, true {lưu tráģ¯} other {huáģˇ lưu tráģ¯}}", "unable_to_change_album_user_role": "Không tháģƒ thay đáģ•i vai trÃ˛ cáģ§a ngưáģi dÚng album", "unable_to_change_date": "Không tháģƒ thay đáģ•i ngày", + "unable_to_change_description": "Không tháģƒ thay đáģ•i mô táēŖ", "unable_to_change_favorite": "Không tháģƒ thay đáģ•i yÃĒu thích cho áēŖnh", "unable_to_change_location": "Không tháģƒ thay đáģ•i váģ‹ trí", "unable_to_change_password": "Không tháģƒ thay đáģ•i máē­t kháēŠu", "unable_to_change_visibility": "Không tháģƒ thay đáģ•i tráēĄng thÃĄi hiáģƒn tháģ‹ cho {count, plural, one {# ngưáģi} other {# ngưáģi}}", "unable_to_complete_oauth_login": "Không tháģƒ hoàn táēĨt đăng nháē­p OAuth", "unable_to_connect": "Không tháģƒ káēŋt náģ‘i", - "unable_to_connect_to_server": "Không tháģƒ káēŋt náģ‘i đáēŋn mÃĄy cháģ§", "unable_to_copy_to_clipboard": "Không tháģƒ sao chÊp vào clipboard, hÃŖy đáēŖm báēŖo báēĄn đang truy cáē­p trang qua https", "unable_to_create_admin_account": "Không tháģƒ táēĄo tài khoáēŖn quáēŖn tráģ‹ viÃĒn", "unable_to_create_api_key": "Không tháģƒ táēĄo khÃŗa API máģ›i", @@ -872,10 +903,6 @@ "unable_to_hide_person": "Không tháģƒ áēŠn ngưáģi", "unable_to_link_motion_video": "Không tháģƒ liÃĒn káēŋt video chuyáģƒn đáģ™ng", "unable_to_link_oauth_account": "Không tháģƒ liÃĒn káēŋt tài khoáēŖn OAuth", - "unable_to_load_album": "Không tháģƒ táēŖi album", - "unable_to_load_asset_activity": "Không tháģƒ táēŖi hoáēĄt đáģ™ng cáģ§a áēŖnh", - "unable_to_load_items": "Không tháģƒ táēŖi cÃĄc máģĨc", - "unable_to_load_liked_status": "Không tháģƒ táēŖi tráēĄng thÃĄi thích", "unable_to_log_out_all_devices": "Không tháģƒ Ä‘Äƒng xuáēĨt kháģi táēĨt cáēŖ cÃĄc thiáēŋt báģ‹", "unable_to_log_out_device": "Không tháģƒ Ä‘Äƒng xuáēĨt kháģi thiáēŋt báģ‹", "unable_to_login_with_oauth": "Không tháģƒ Ä‘Äƒng nháē­p váģ›i OAuth", @@ -886,12 +913,11 @@ "unable_to_remove_album_users": "Không tháģƒ xÃŗa ngưáģi dÚng kháģi album", "unable_to_remove_api_key": "Không tháģƒ xÃŗa khÃŗa API", "unable_to_remove_assets_from_shared_link": "Không tháģƒ xÃŗa cÃĄc máģĨc Ä‘ÃŖ cháģn kháģi liÃĒn káēŋt chia sáēģ", - "unable_to_remove_deleted_assets": "Không tháģƒ xÃŗa táē­p tin ngoáēĄi tuyáēŋn", "unable_to_remove_library": "Không tháģƒ xÃŗa thư viáģ‡n", "unable_to_remove_partner": "Không tháģƒ xÃŗa ngưáģi thÃĸn", "unable_to_remove_reaction": "Không tháģƒ xÃŗa pháēŖn áģŠng", - "unable_to_repair_items": "Không tháģƒ sáģ­a cháģ¯a cÃĄc máģĨc", "unable_to_reset_password": "Không tháģƒ Ä‘áēˇt láēĄi máē­t kháēŠu", + "unable_to_reset_pin_code": "Không tháģƒ Ä‘áēˇt láēĄi mÃŖ PIN", "unable_to_resolve_duplicate": "Không tháģƒ xáģ­ lÃŊ trÚng láēˇp", "unable_to_restore_assets": "Không tháģƒ khôi pháģĨc áēŖnh", "unable_to_restore_trash": "Không tháģƒ khôi pháģĨc thÚng rÃĄc", @@ -919,11 +945,15 @@ "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_months": "{months} thÃĄng tuáģ•i", + "exif_bottom_sheet_person_age_year_months": "1 tuáģ•i, {months} thÃĄng", + "exif_bottom_sheet_person_age_years": "{years} tuáģ•i", "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", @@ -941,9 +971,10 @@ "external": "BÃĒn ngoài", "external_libraries": "Thư viáģ‡n bÃĒn ngoài", "external_network": "MáēĄng bÃĒn ngoài", - "external_network_sheet_info": "Khi không áģŸ trÃĒn máēĄng WiFi ưa thích, áģŠng dáģĨng sáēŊ káēŋt náģ‘i váģ›i mÃĄy cháģ§ thông qua URL đáē§u tiÃĒn bÃĒn dưáģ›i mà nÃŗ cÃŗ tháģƒ truy cáē­p, báē¯t đáē§u táģĢ trÃĒn xuáģ‘ng dưáģ›i", + "external_network_sheet_info": "Khi không áģŸ trÃĒn máēĄng Wi-Fi ưu tiÃĒn, áģŠng dáģĨng sáēŊ káēŋt náģ‘i váģ›i mÃĄy cháģ§ thông qua URL đáē§u tiÃĒn bÃĒn dưáģ›i mà nÃŗ cÃŗ tháģƒ truy cáē­p, báē¯t đáē§u táģĢ trÃĒn xuáģ‘ng dưáģ›i", "face_unassigned": "Chưa đưáģŖc gÃĄn", "failed": "TháēĨt báēĄi", + "failed_to_authenticate": "XÃĄc tháģąc tháēĨt báēĄi", "failed_to_load_assets": "Không táēŖi đưáģŖc áēŖnh", "failed_to_load_folder": "Không táēŖi đưáģŖc thư máģĨc", "favorite": "YÃĒu thích", @@ -959,6 +990,7 @@ "filetype": "LoáēĄi táē­p tin", "filter": "Báģ™ láģc", "filter_people": "Láģc ngưáģi", + "filter_places": "Láģc đáģ‹a điáģƒm", "find_them_fast": "TÃŦm nhanh báēąng tÃĒn váģ›i tÃŦm kiáēŋm", "fix_incorrect_match": "Sáģ­a láģ—i trÚng kháģ›p không chính xÃĄc", "folder": "Thư máģĨc", @@ -966,16 +998,21 @@ "folders": "Thư máģĨc", "folders_feature_description": "Duyáģ‡t áēŖnh và video theo thư máģĨc trÃĒn háģ‡ tháģ‘ng táē­p tin", "forward": "Tiáēŋn váģ phía trưáģ›c", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Tính năng này táēŖi cÃĄc tài nguyÃĒn bÃĒn ngoài táģĢ Google đáģƒ hoáēĄt đáģ™ng.", "general": "Chung", "get_help": "Nháē­n tráģŖ giÃēp", "get_wifiname_error": "Không tháģƒ láēĨy tÃĒn Wi-Fi. HÃŖy đáēŖm báēŖo báēĄn Ä‘ÃŖ cáēĨp cÃĄc quyáģn cáē§n thiáēŋt và đưáģŖc káēŋt náģ‘i váģ›i máēĄng Wi-Fi", "getting_started": "Báē¯t đáē§u", "go_back": "Quay láēĄi", - "go_to_search": "Đi đáēŋn tÃŦm kiáēŋm", + "go_to_folder": "Đi táģ›i thư máģĨc", + "go_to_search": "Đi táģ›i tÃŦm kiáēŋm", "grant_permission": "CáēĨp quyáģn", "group_albums_by": "NhÃŗm album theo...", + "group_country": "NhÃŗm theo quáģ‘c gia", "group_no": "Không nhÃŗm", "group_owner": "NhÃŗm theo cháģ§ sáģŸ háģ¯u", + "group_places_by": "NhÃŗm đáģ‹a điáģƒm theoâ€Ļ", "group_year": "NhÃŗm theo năm", "haptic_feedback_switch": "Báē­t pháēŖn háģ“i haptic", "haptic_feedback_title": "PháēŖn háģ“i Hapic", @@ -984,8 +1021,8 @@ "header_settings_field_validator_msg": "Trưáģng này không đưáģŖc đáģƒ tráģ‘ng", "header_settings_header_name_input": "TÃĒn Header", "header_settings_header_value_input": "GiÃĄ tráģ‹ Header", - "headers_settings_tile_subtitle": "XÃĄc đáģ‹nh proxy header áģŠng dáģĨng sáēŊ gáģ­i kèm theo máģ—i yÃĒu cáē§u máēĄng.", - "headers_settings_tile_title": "Tuáģŗ cháģ‰nh cÃĄc header cáģ§a proxy", + "headers_settings_tile_subtitle": "Thiáēŋt láē­p tiÃĒu đáģ proxy gáģ­i kèm máģ—i yÃĒu cáē§u máēĄng", + "headers_settings_tile_title": "TiÃĒu đáģ proxy tÚy cháģ‰nh", "hi_user": "Chào {name} ({email})", "hide_all_people": "áē¨n táēĨt cáēŖ máģi ngưáģi", "hide_gallery": "áē¨n thư viáģ‡n", @@ -993,21 +1030,25 @@ "hide_password": "áē¨n máē­t kháēŠu", "hide_person": "áē¨n ngưáģi", "hide_unnamed_people": "áē¨n nháģ¯ng ngưáģi không tÃĒn", - "home_page_add_to_album_conflicts": "ÄÃŖ thÃĒm {added} máģĨc vào album {album}. {failed}\nmáģĨc Ä‘ÃŖ cÃŗ sáēĩn trong album. ", + "home_page_add_to_album_conflicts": "ÄÃŖ thÃĒm {added} máģĨc vào album {album}. {failed} máģĨc Ä‘ÃŖ cÃŗ sáēĩn trong album.", "home_page_add_to_album_err_local": "Không tháģƒ thÃĒm áēŖnh cáģĨc báģ™ vào album, báģ qua", - "home_page_add_to_album_success": "ÄÃŖ thÃĒm {added} máģĨc vào album {album}", + "home_page_add_to_album_success": "ÄÃŖ thÃĒm {added} máģĨc vào album {album}.", "home_page_album_err_partner": "Không tháģƒ thÃĒm áēŖnh cáģ§a nguáģi thÃĸn vào album, báģ qua", "home_page_archive_err_local": "Không tháģƒ lưu tráģ¯ áēŖnh cáģĨc báģ™, báģ qua", "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": "áēĸnh cáģĨc báģ™ trong láģąa cháģn xÃŗa táģĢ xa, báģ qua", "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", + "home_page_first_time_notice": "Náēŋu đÃĸy là láē§n đáē§u báēĄn dÚng áģŠng dáģĨng, hÃŖy cháģn máģ™t album sao lưu đáģƒ dÃ˛ng tháģi gian cÃŗ tháģƒ hiáģƒn tháģ‹ áēŖnh và video cáģ§a báēĄn", + "home_page_locked_error_local": "Không tháģƒ di chuyáģƒn áēŖnh cáģĨc báģ™ Ä‘áēŋn thư máģĨc báēŖo máē­t, báģ qua", + "home_page_locked_error_partner": "Không tháģƒ di chuyáģƒn áēŖnh ngưáģi thÃĸn đáēŋn thư máģĨc báēŖo máē­t, báģ qua", "home_page_share_err_local": "Không tháģƒ chia sáēģ áēŖnh cáģĨc báģ™ qua liÃĒn káēŋt, báģ qua", "home_page_upload_err_limit": "Cháģ‰ cÃŗ tháģƒ táēŖi lÃĒn táģ‘i đa 30 áēŖnh cÚng máģ™t lÃēc, báģ qua", "host": "MÃĄy cháģ§", "hour": "Giáģ", + "id": "ID", "ignore_icloud_photos": "Báģ qua áēŖnh iCloud", "ignore_icloud_photos_description": "áēĸnh đưáģŖc lưu tráģ¯ trÃĒn iCloud sáēŊ không đưáģŖc táēŖi lÃĒn mÃĄy cháģ§ Immich", "image": "HÃŦnh áēŖnh", @@ -1035,6 +1076,7 @@ "include_shared_albums": "Bao gáģ“m cÃĄc album chia sáēģ", "include_shared_partner_assets": "Bao gáģ“m cÃĄc áēŖnh ngưáģi thÃĸn chia sáēģ", "individual_share": "Chia sáēģ cÃĄ nhÃĸn", + "individual_shares": "Chia sáēģ cÃĄ nhÃĸn", "info": "Thông tin", "interval": { "day_at_onepm": "Máģ—i ngày vào lÃēc 1 giáģ chiáģu", @@ -1046,18 +1088,29 @@ "invalid_date_format": "Đáģ‹nh dáēĄng ngày không háģŖp láģ‡", "invite_people": "Máģi máģi ngưáģi", "invite_to_album": "Máģi vào album", + "ios_debug_info_fetch_ran_at": "LáēĨy dáģ¯ liáģ‡u Ä‘ÃŖ cháēĄy vào {dateTime}", + "ios_debug_info_last_sync_at": "Đáģ“ng báģ™ láē§n cuáģ‘i vào {dateTime}", + "ios_debug_info_no_processes_queued": "Không cÃŗ tiáēŋn trÃŦnh náģn trong hàng đáģŖi", + "ios_debug_info_no_sync_yet": "Chưa cÃŗ tÃĄc váģĨ đáģ“ng báģ™ náģn cháēĄy", + "ios_debug_info_processes_queued": "{count, plural, one {{count} tiáēŋn trÃŦnh náģn Ä‘ÃŖ đưáģŖc đưa vào hàng đáģŖi} other {{count} tiáēŋn trÃŦnh náģn Ä‘ÃŖ đưáģŖc đưa vào hàng đáģŖi}}", + "ios_debug_info_processing_ran_at": "QuÃĄ trÃŦnh xáģ­ lÃŊ Ä‘ÃŖ cháēĄy vào {dateTime}", "items_count": "{count, plural, one {# máģĨc} other {# máģĨc}}", "jobs": "TÃĄc váģĨ", "keep": "Giáģ¯", "keep_all": "Giáģ¯ táēĨt cáēŖ", - "keep_this_delete_others": "Giáģ¯ táēĨm này và xoÃĄ táēĨt cáēŖ cÃ˛n láēĄi", + "keep_this_delete_others": "Giáģ¯ áēŖnh này, xÃŗa cÃĄc áēŖnh khÃĄc", + "kept_this_deleted_others": "ÄÃŖ giáģ¯ láēĄi máģĨc này và xÃŗa {count, plural, one {# máģĨc} other {# máģĨc}}", "keyboard_shortcuts": "Phím táē¯t", "language": "Ngôn ngáģ¯", + "language_no_results_subtitle": "Tháģ­ Ä‘iáģu cháģ‰nh táģĢ khÃŗa tÃŦm kiáēŋm", + "language_no_results_title": "Không tÃŦm tháēĨy ngôn ngáģ¯", + "language_search_hint": "TÃŦm kiáēŋm ngôn ngáģ¯...", "language_setting_description": "Cháģn ngôn ngáģ¯ Æ°a thích cáģ§a báēĄn", "last_seen": "Láē§n cuáģ‘i nhÃŦn tháēĨy", "latest_version": "PhiÃĒn báēŖn máģ›i nháēĨt", "latitude": "VÄŠ đáģ™", "leave": "Ráģi kháģi", + "lens_model": "Kiáģƒu tháēĨu kính", "let_others_respond": "Cho phÊp ngưáģi khÃĄc pháēŖn háģ“i", "level": "CáēĨp đáģ™", "library": "Thư viáģ‡n", @@ -1080,12 +1133,14 @@ "local_network": "MáēĄng náģ™i báģ™", "local_network_sheet_info": "áģ¨ng dáģĨng sáēŊ káēŋt náģ‘i váģ›i mÃĄy cháģ§ qua URL này khi sáģ­ dáģĨng máēĄng Wi-Fi đưáģŖc cháģ‰ Ä‘áģ‹nh", "location_permission": "Quyáģn truy cáē­p váģ‹ trí", - "location_permission_content": "Đáģƒ sáģ­ dáģĨng tính năng táģą Ä‘áģ™ng chuyáģƒn đáģ•i, Immich cáē§n cÃŗ quyáģn váģ‹ trí chính xÃĄc đáģƒ cÃŗ tháģƒ Ä‘áģc tÃĒn máēĄng WiFi hiáģ‡n táēĄi", + "location_permission_content": "Đáģƒ sáģ­ dáģĨng tính năng táģą Ä‘áģ™ng chuyáģƒn đáģ•i, Immich cáē§n cÃŗ quyáģn váģ‹ trí chính xÃĄc đáģƒ cÃŗ tháģƒ Ä‘áģc tÃĒn cáģ§a máēĄng Wi-Fi hiáģ‡n táēĄi", "location_picker_choose_on_map": "Cháģn trÃĒn báēŖn đáģ“", "location_picker_latitude_error": "Nháē­p vÄŠ đáģ™ háģŖp láģ‡", "location_picker_latitude_hint": "Nháē­p vÄŠ đáģ™ cáģ§a báēĄn", "location_picker_longitude_error": "Nháē­p kinh đáģ™ háģŖp láģ‡", "location_picker_longitude_hint": "Nháē­p kinh đáģ™ cáģ§a báēĄn", + "lock": "KhÃŗa", + "locked_folder": "Thư máģĨc báēŖo máē­t", "log_out": "Đăng xuáēĨt", "log_out_all_devices": "Đăng xuáēĨt táēĨt cáēŖ cÃĄc thiáēŋt báģ‹", "logged_out_all_devices": "TáēĨt cáēŖ cÃĄc thiáēŋt báģ‹ Ä‘ÃŖ đăng xuáēĨt", @@ -1103,12 +1158,12 @@ "login_form_err_leading_whitespace": "KhoáēŖng tráē¯ng trưáģ›c", "login_form_err_trailing_whitespace": "KhoáēŖng tráē¯ng sau", "login_form_failed_get_oauth_server_config": "Không tháģƒ Ä‘Äƒng nháē­p báēąng OAuth, vui lÃ˛ng kiáģƒm tra đáģ‹a cháģ‰ mÃĄy cháģ§", - "login_form_failed_get_oauth_server_disable": "MÃĄy cháģ§ không đưáģŖc cáēĨu hÃŦnh đáģƒ háģ— tráģŖ OAuth.", + "login_form_failed_get_oauth_server_disable": "Tính năng OAuth không kháēŖ dáģĨng trÃĒn mÃĄy cháģ§ này", "login_form_failed_login": "Đăng nháē­p láģ—i, vui lÃ˛ng kiáģƒm tra đáģ‹a cháģ‰ mÃĄy cháģ§, email và máē­t kháēŠu", "login_form_handshake_exception": "ÄÃŖ xáēŖy ra láģ—i Handshake Exception váģ›i mÃĄy cháģ§. Náēŋu báēĄn đang sáģ­ dáģĨng cháģŠng cháģ‰ táģą kÃŊ, hÃŖy báē­t háģ— tráģŖ cháģŠng cháģ‰ táģą kÃŊ trong cài đáēˇt.", "login_form_password_hint": "máē­t kháēŠu", "login_form_save_login": "Lưu tráēĄng thÃĄi đăng nháē­p", - "login_form_server_empty": "Nháē­p đáģ‹a cháģ‰ mÃĄy cháģ§", + "login_form_server_empty": "Nháē­p đáģ‹a cháģ‰ mÃĄy cháģ§.", "login_form_server_error": "Không tháģƒ káēŋt náģ‘i táģ›i mÃĄy cháģ§.", "login_has_been_disabled": "Đăng nháē­p Ä‘ÃŖ báģ‹ vô hiáģ‡u hÃŗa.", "login_password_changed_error": "Thay đáģ•i máē­t kháēŠu không thành công", @@ -1120,6 +1175,7 @@ "loop_videos": "Láēˇp video", "loop_videos_description": "Báē­t đáģƒ video táģą Ä‘áģ™ng láēˇp láēĄi trong trÃŦnh xem chi tiáēŋt.", "main_branch_warning": "BáēĄn đang dÚng phiÃĒn báēŖn đang phÃĄt triáģƒn; chÃēng tôi khuyÃĒn báēĄn nÃĒn dÚng phiÃĒn báēŖn phÃĄt hành!", + "main_menu": "Menu chính", "make": "ThÆ°ÆĄng hiáģ‡u", "manage_shared_links": "QuáēŖn lÃŊ liÃĒn káēŋt chia sáēģ", "manage_sharing_with_partners": "QuáēŖn lÃŊ chia sáēģ váģ›i ngưáģi thÃĸn", @@ -1153,16 +1209,20 @@ "map_settings_only_show_favorites": "Cháģ‰ hiáģƒn tháģ‹ máģĨc yÃĒu thích", "map_settings_theme_settings": "Giao diáģ‡n báēŖn đáģ“", "map_zoom_to_see_photos": "Thu nháģ Ä‘áģƒ xem áēŖnh", + "mark_all_as_read": "ÄÃĄnh dáēĨu Ä‘ÃŖ đáģc táēĨt cáēŖ", + "mark_as_read": "ÄÃĄnh dáēĨu Ä‘ÃŖ đáģc", + "marked_all_as_read": "ÄÃŖ Ä‘ÃĄnh dáēĨu táēĨt cáēŖ Ä‘ÃŖ đáģc", "matches": "Kháģ›p", "media_type": "LoáēĄi phÆ°ÆĄng tiáģ‡n", "memories": "Káģˇ niáģ‡m", "memories_all_caught_up": "BáēĄn Ä‘ÃŖ xem háēŋt ráģ“i", - "memories_check_back_tomorrow": "Háēšn gáēˇp láēĄi báēĄn vào ngày mai váģ›i nháģ¯ng káģˇ niáģ‡m máģ›i!", + "memories_check_back_tomorrow": "Quay láēĄi ngày mai đáģƒ xem thÃĒm nháģ¯ng káģˇ niáģ‡m", "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", "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", @@ -1174,10 +1234,18 @@ "missing": "Thiáēŋu", "model": "DÃ˛ng", "month": "ThÃĄng", + "monthly_title_text_date_format": "MMMM y", "more": "ThÃĒm", + "move": "Di chuyáģƒn", + "move_off_locked_folder": "Di chuyáģƒn ra kháģi thư máģĨc báēŖo máē­t", + "move_to_locked_folder": "Di chuyáģƒn đáēŋn thư máģĨc báēŖo máē­t", + "move_to_locked_folder_confirmation": "áēĸnh và video này sáēŊ báģ‹ xÃŗa kháģi cÃĄc album, cháģ‰ cÃŗ tháģƒ xem đưáģŖc trong thư máģĨc báēŖo máē­t", + "moved_to_archive": "ÄÃŖ di chuyáģƒn {count, plural, one {# áēŖnh} other {# áēŖnh}} đáēŋn lưu tráģ¯", + "moved_to_library": "ÄÃŖ di chuyáģƒn {count, plural, one {# áēŖnh} other {# áēŖnh}} đáēŋn thư viáģ‡n", "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", "multiselect_grid_edit_gps_err_read_only": "Không tháģƒ cháģ‰nh sáģ­a váģ‹ trí cáģ§a áēŖnh cháģ‰ cÃŗ quyáģn đáģc, báģ qua", + "mute_memories": "Táē¯t tiáēŋng Káģˇ niáģ‡m", "my_albums": "Album cáģ§a tôi", "name": "TÃĒn", "name_or_nickname": "TÃĒn hoáēˇc biáģ‡t danh", @@ -1188,6 +1256,8 @@ "new_api_key": "KhÃŗa API máģ›i", "new_password": "Máē­t kháēŠu máģ›i", "new_person": "Ngưáģi máģ›i", + "new_pin_code": "MÃŖ PIN máģ›i", + "new_pin_code_subtitle": "ĐÃĸy là láē§n đáē§u báēĄn vào thư máģĨc báēŖo máē­t. HÃŖy táēĄo mÃŖ PIN đáģƒ truy cáē­p an toàn", "new_user_created": "Ngưáģi dÚng máģ›i Ä‘ÃŖ đưáģŖc táēĄo", "new_version_available": "CÓ PHIÊN BáēĸN MáģšI", "newest_first": "Máģ›i nháēĨt trưáģ›c", @@ -1205,7 +1275,10 @@ "no_explore_results_message": "TáēŖi thÃĒm áēŖnh lÃĒn đáģƒ khÃĄm phÃĄ báģ™ sưu táē­p cáģ§a báēĄn.", "no_favorites_message": "ThÃĒm áēŖnh yÃĒu thích đáģƒ nhanh chÃŗng tÃŦm tháēĨy nháģ¯ng báģŠc áēŖnh và video đáēšp nháēĨt cáģ§a báēĄn", "no_libraries_message": "TáēĄo máģ™t thư viáģ‡n bÃĒn ngoài đáģƒ xem áēŖnh và video cáģ§a báēĄn", + "no_locked_photos_message": "áēĸnh và video trong thư máģĨc báēŖo máē­t sáēŊ đưáģŖc áēŠn đi và không hiáģƒn tháģ‹ khi báēĄn duyáģ‡t hay tÃŦm kiáēŋm trong thư viáģ‡n.", "no_name": "Không cÃŗ tÃĒn", + "no_notifications": "Không cÃŗ thông bÃĄo", + "no_people_found": "Không cÃŗ ngưáģi nào kháģ›p váģ›i tÃŦm kiáēŋm", "no_places": "Không cÃŗ đáģ‹a điáģƒm", "no_results": "Không cÃŗ káēŋt quáēŖ", "no_results_description": "Tháģ­ máģ™t táģĢ Ä‘áģ“ng nghÄŠa hoáēˇc táģĢ khÃŗa táģ•ng quÃĄt hÆĄn", @@ -1214,27 +1287,30 @@ "not_selected": "Không đưáģŖc cháģn", "note_apply_storage_label_to_previously_uploaded assets": "Lưu ÃŊ: Đáģƒ ÃĄp dáģĨng NhÃŖn lưu tráģ¯ cho cÃĄc áēŖnh Ä‘ÃŖ táēŖi lÃĒn trưáģ›c Ä‘Ãŗ, hÃŖy cháēĄy", "notes": "Lưu ÃŊ", - "notification_permission_dialog_content": "Đáģƒ báē­t thông bÃĄo, chuyáģƒn táģ›i Cài đáēˇt và cháģn cho phÊp", - "notification_permission_list_tile_content": "CáēĨp quyáģn đáģƒ báē­t thông bÃĄo", + "nothing_here_yet": "Chưa cÃŗ náģ™i dung nào", + "notification_permission_dialog_content": "Đáģƒ báē­t thông bÃĄo, chuyáģƒn táģ›i Cài đáēˇt và cháģn cho phÊp.", + "notification_permission_list_tile_content": "CáēĨp quyáģn đáģƒ báē­t thông bÃĄo.", "notification_permission_list_tile_enable_button": "Báē­t thông bÃĄo", "notification_permission_list_tile_title": "Quyáģn thông bÃĄo", "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", - "offline_paths_description": "Nháģ¯ng káēŋt quáēŖ này cÃŗ tháģƒ do viáģ‡c xÃŗa tháģ§ công cÃĄc táē­p tin không pháēŖi là máģ™t pháē§n cáģ§a thư viáģ‡n bÃĒn ngoài.", "ok": "Đáģ“ng ÃŊ", "oldest_first": "CÅŠ nháēĨt trưáģ›c", "on_this_device": "TrÃĒn mÃĄy này", "onboarding": "Hưáģ›ng dáēĢn sáģ­ dáģĨng", - "onboarding_privacy_description": "CÃĄc tính năng (tÚy cháģn) sau đÃĸy pháģĨ thuáģ™c vào cÃĄc dáģ‹ch váģĨ bÃĒn ngoài và cÃŗ tháģƒ báģ‹ táē¯t báēĨt káģŗ lÃēc nào trong cài đáēˇt quáēŖn tráģ‹.", + "onboarding_locale_description": "Cháģn ngôn ngáģ¯ Æ°a thích cáģ§a báēĄn. BáēĄn cÃŗ tháģƒ thay đáģ•i láēĄi sau trong cài đáēˇt.", + "onboarding_privacy_description": "CÃĄc tính năng (tÚy cháģn) sau đÃĸy pháģĨ thuáģ™c vào cÃĄc dáģ‹ch váģĨ bÃĒn ngoài và cÃŗ tháģƒ báģ‹ táē¯t báēĨt káģŗ lÃēc nào trong cài đáēˇt.", + "onboarding_server_welcome_description": "HÃŖy thiáēŋt láē­p phiÃĒn báēŖn cáģ§a báēĄn váģ›i máģ™t sáģ‘ cài đáēˇt pháģ• biáēŋn.", "onboarding_theme_description": "Cháģn cháģ§ Ä‘áģ màu sáē¯c cho tài khoáēŖn riÃĒng cáģ§a báēĄn. BáēĄn cÃŗ tháģƒ thay đáģ•i điáģu này sau trong cài đáēˇt cáģ§a báēĄn.", - "onboarding_welcome_description": "HÃŖy thiáēŋt láē­p tài khoáēŖn riÃĒng cáģ§a báēĄn váģ›i máģ™t sáģ‘ cài đáēˇt cÆĄ báēŖn.", + "onboarding_user_welcome_description": "Báē¯t đáē§u thôi nào!", "onboarding_welcome_user": "Chào máģĢng, {user}", "online": "Tráģąc tuyáēŋn", "only_favorites": "Cháģ‰ yÃĒu thích", + "open": "MáģŸ", "open_in_map_view": "MáģŸ trong báēŖn đáģ“", "open_in_openstreetmap": "MáģŸ trong OpenStreetMap", "open_the_search_filters": "MáģŸ báģ™ láģc tÃŦm kiáēŋm", @@ -1287,15 +1363,18 @@ "permanently_delete_assets_prompt": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n xÃŗa vÄŠnh viáģ…n {count, plural, one {máģĨc này?} other {# máģĨc này?}} Điáģu này cÅŠng sáēŊ xÃŗa {count, plural, one {nÃŗ kháģi} other {chÃēng kháģi}} cÃĄc album.", "permanently_deleted_asset": "áēĸnh Ä‘ÃŖ báģ‹ xÃŗa vÄŠnh viáģ…n", "permanently_deleted_assets_count": "ÄÃŖ xÃŗa vÄŠnh viáģ…n {count, plural, one {# máģĨc} other {# máģĨc}}", + "permission": "Quyáģn", + "permission_empty": "Quyáģn cáģ§a báēĄn không đưáģŖc đáģƒ tráģ‘ng", "permission_onboarding_back": "Quay láēĄi", "permission_onboarding_continue_anyway": "VáēĢn tiáēŋp táģĨc", "permission_onboarding_get_started": "Báē¯t đáē§u", - "permission_onboarding_go_to_settings": "Chuyáģƒn táģ›i cài đáēˇt", + "permission_onboarding_go_to_settings": "Đi táģ›i cài đáēˇt", "permission_onboarding_permission_denied": "Không cÃŗ quyáģn truy cáē­p vào áēŖnh cáģ§a báēĄn. Đáģƒ sáģ­ dáģĨng Immich, hÃŖy cáēĨp quyáģn truy cáē­p vào thư viáģ‡n áēŖnh trong Cài đáēˇt.", - "permission_onboarding_permission_granted": "CáēĨp quyáģn hoàn táēĨt!", + "permission_onboarding_permission_granted": "CáēĨp quyáģn hoàn táēĨt! BáēĄn cÃŗ tháģƒ báē¯t đáē§u.", "permission_onboarding_permission_limited": "Quyáģn truy cáē­p vào áēŖnh cáģ§a báēĄn báģ‹ háēĄn cháēŋ. Đáģƒ Immich sao lưu và quáēŖn lÃŊ toàn báģ™ thư viáģ‡n áēŖnh cáģ§a báēĄn, hÃŖy cáēĨp quyáģn truy cáē­p toàn báģ™ áēŖnh trong Cài đáēˇt.", - "permission_onboarding_request": "Immich cáē§n quyáģn đáģƒ xem áēŖnh và video cáģ§a báēĄn", + "permission_onboarding_request": "Immich cáē§n quyáģn đáģƒ xem áēŖnh và video cáģ§a báēĄn.", "person": "Máģi ngưáģi", + "person_birthdate": "Sinh vào {date}", "person_hidden": "{name}{hidden, select, true { (Ä‘ÃŖ áēŠn)} other {}}", "photo_shared_all_users": "CÃŗ váēģ như báēĄn Ä‘ÃŖ chia sáēģ áēŖnh cáģ§a mÃŦnh váģ›i táēĨt cáēŖ ngưáģi dÚng hoáēˇc báēĄn không cÃŗ ngưáģi dÚng nào đáģƒ chia sáēģ.", "photos": "áēĸnh", @@ -1303,26 +1382,37 @@ "photos_count": "{count, plural, one {{count, number} áēĸnh} other {{count, number} áēĸnh}}", "photos_from_previous_years": "áēĸnh táģĢ cÃĄc năm trưáģ›c", "pick_a_location": "Cháģn máģ™t váģ‹ trí", + "pin_code_changed_successfully": "Thay đáģ•i mÃŖ PIN thành công", + "pin_code_reset_successfully": "Đáēˇt láēĄi mÃŖ PIN thành công", + "pin_code_setup_successfully": "Thiáēŋt láē­p mÃŖ PIN thành công", + "pin_verification": "XÃĄc minh mÃŖ PIN", "place": "Đáģ‹a điáģƒm", "places": "Đáģ‹a điáģƒm", + "places_count": "{count, plural, one {{count, number} Đáģ‹a điáģƒm} other {{count, number} Đáģ‹a điáģƒm}}", "play": "PhÃĄt", "play_memories": "PhÃĄt káģˇ niáģ‡m", "play_motion_photo": "PhÃĄt áēŖnh chuyáģƒn đáģ™ng", "play_or_pause_video": "PhÃĄt hoáēˇc táēĄm dáģĢng video", + "please_auth_to_access": "Vui lÃ˛ng xÃĄc tháģąc đáģƒ truy cáē­p", "port": "Cáģ•ng", - "preferences_settings_subtitle": "QuáēŖn lÃŊ tuáģŗ cháģn cáģ§a app", - "preferences_settings_title": "Tuáģŗ cháģ‰nh", + "preferences_settings_subtitle": "QuáēŖn lÃŊ tÚy cháģn cáģ§a áģŠng dáģĨng", + "preferences_settings_title": "TÚy cháģn", "preset": "MáēĢu cÃŗ sáēĩn", "preview": "Xem trưáģ›c", "previous": "Trưáģ›c", "previous_memory": "Káģˇ niáģ‡m trưáģ›c", - "previous_or_next_photo": "áēĸnh trưáģ›c hoáēˇc sau", + "previous_or_next_day": "Ngày trưáģ›c/sau", + "previous_or_next_month": "ThÃĄng trưáģ›c/sau", + "previous_or_next_photo": "áēĸnh trưáģ›c/sau", + "previous_or_next_year": "Năm trưáģ›c/sau", "primary": "Chính", "privacy": "BáēŖo máē­t", + "profile": "Háģ“ sÆĄ", "profile_drawer_app_logs": "Nháē­t kÃŊ", "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}", @@ -1362,7 +1452,7 @@ "purchase_server_title": "MÃĄy cháģ§", "purchase_settings_server_activated": "KhÃŗa sáēŖn pháēŠm mÃĄy cháģ§ Ä‘Æ°áģŖc quáēŖn lÃŊ báģŸi quáēŖn tráģ‹ viÃĒn", "rating": "Xáēŋp háēĄng sao", - "rating_clear": "XÃŗa Ä‘ÃĄnh giÃĄ", + "rating_clear": "XÃŗa xáēŋp háēĄng", "rating_count": "{count, plural, one {# sao} other {# sao}}", "rating_description": "Hiáģƒn tháģ‹ xáēŋp háēĄng EXIF trong báēŖng thông tin", "reaction_options": "TÚy cháģn pháēŖn áģŠng", @@ -1372,9 +1462,12 @@ "reassigned_assets_to_new_person": "ÄÃŖ gÃĄn láēĄi {count, plural, one {# áēŖnh} other {# áēŖnh}} cho máģ™t ngưáģi máģ›i", "reassing_hint": "GÃĄn cÃĄc áēŖnh Ä‘ÃŖ cháģn cho máģ™t ngưáģi hiáģ‡n cÃŗ", "recent": "Gáē§n đÃĸy", + "recent-albums": "Album gáē§n đÃĸy", "recent_searches": "TÃŦm kiáēŋm gáē§n đÃĸy", "recently_added": "ThÃĒm gáē§n đÃĸy", "recently_added_page_title": "Máģ›i thÃĒm gáē§n đÃĸy", + "recently_taken": "CháģĨp gáē§n đÃĸy", + "recently_taken_page_title": "CháģĨp Gáē§n đÃĸy", "refresh": "Làm máģ›i", "refresh_encoded_videos": "Làm máģ›i video Ä‘ÃŖ mÃŖ hÃŗa", "refresh_faces": "Làm máģ›i khuôn máēˇt", @@ -1394,12 +1487,19 @@ "remove_deleted_assets": "LoáēĄi báģ táē­p tin ngoáēĄi tuyáēŋn", "remove_from_album": "XÃŗa kháģi album", "remove_from_favorites": "XÃŗa kháģi MáģĨc yÃĒu thích", + "remove_from_locked_folder": "XÃŗa kháģi thư máģĨc báēŖo máē­t", + "remove_from_locked_folder_confirmation": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n di chuyáģƒn áēŖnh và video này kháģi thư máģĨc báēŖo máē­t không? ChÃēng sáēŊ hiáģ‡n trong thư viáģ‡n cáģ§a báēĄn.", "remove_from_shared_link": "XÃŗa kháģi liÃĒn káēŋt chia sáēģ", + "remove_memory": "XÃŗa káģˇ niáģ‡m", + "remove_photo_from_memory": "XÃŗa áēŖnh kháģi káģˇ niáģ‡m này", + "remove_url": "XÃŗa URL", "remove_user": "XÃŗa ngưáģi dÚng", "removed_api_key": "KhÃŗa API Ä‘ÃŖ xÃŗa: {name}", "removed_from_archive": "ÄÃŖ xoÃĄ kháģi Kho lưu tráģ¯", "removed_from_favorites": "ÄÃŖ xoÃĄ kháģi MáģĨc yÃĒu thích", "removed_from_favorites_count": "{count, plural, other {ÄÃŖ xoÃĄ #}} kháģi MáģĨc yÃĒu thích", + "removed_memory": "ÄÃŖ xÃŗa káģˇ niáģ‡m", + "removed_photo_from_memory": "ÄÃŖ xÃŗa áēŖnh kháģi káģˇ niáģ‡m", "removed_tagged_assets": "ÄÃŖ xÃŗa tháēģ kháģi {count, plural, one {# máģĨc} other {# máģĨc}}", "rename": "Đáģ•i tÃĒn", "repair": "Sáģ­a cháģ¯a", @@ -1408,9 +1508,11 @@ "repository": "Kho lưu tráģ¯", "require_password": "YÃĒu cáē§u máē­t kháēŠu", "require_user_to_change_password_on_first_login": "YÃĒu cáē§u ngưáģi dÚng thay đáģ•i máē­t kháēŠu áģŸ láē§n đáē§u đăng nháē­p", + "rescan": "QuÊt láēĄi", "reset": "Đáēˇt láēĄi", "reset_password": "Đáēˇt láēĄi máē­t kháēŠu", "reset_people_visibility": "Đáēˇt láēĄi tráēĄng thÃĄi hiáģƒn tháģ‹ cáģ§a máģi ngưáģi", + "reset_pin_code": "Đáēˇt láēĄi mÃŖ PIN", "reset_to_default": "Đáēˇt láēĄi váģ máēˇc đáģ‹nh", "resolve_duplicates": "Xáģ­ lÃŊ cÃĄc báēŖn trÚng láēˇp", "resolved_all_duplicates": "ÄÃŖ xáģ­ lÃŊ táēĨt cáēŖ cÃĄc báēŖn trÚng láēˇp", @@ -1459,6 +1561,7 @@ "search_filter_media_type": "LoáēĄi phÆ°ÆĄng tiáģ‡n", "search_filter_media_type_title": "Cháģn loáēĄi phÆ°ÆĄng tiáģ‡n", "search_filter_people_title": "Cháģn ngưáģi", + "search_for": "TÃŦm kiáēŋm", "search_for_existing_person": "TÃŦm kiáēŋm ngưáģi hiáģ‡n cÃŗ", "search_no_more_result": "Không cÃ˛n káēŋt quáēŖ", "search_no_people": "Không cÃŗ ngưáģi", @@ -1478,8 +1581,9 @@ "search_page_your_map": "BáēŖn đáģ“ cáģ§a báēĄn", "search_people": "TÃŦm kiáēŋm ngưáģi", "search_places": "TÃŦm kiáēŋm đáģ‹a điáģƒm", + "search_rating": "TÃŦm kiáēŋm theo xáēŋp háēĄngâ€Ļ", "search_result_page_new_search_hint": "TÃŦm kiáēŋm máģ›i", - "search_settings": "Cài đáēˇt tÃŦm kiáēŋm", + "search_settings": "TÃŦm kiáēŋm cài đáēˇt", "search_state": "TÃŦm kiáēŋm táģ‰nh...", "search_suggestion_list_smart_search_hint_1": "TÃŦm kiáēŋm thông minh đưáģŖc báē­t máēˇc đáģ‹nh, đáģƒ tÃŦm kiáēŋm siÃĒu dáģ¯ liáģ‡u hÃŖy sáģ­ dáģĨng cÃē phÃĄp ", "search_suggestion_list_smart_search_hint_2": "m:cáģĨm-táģĢ-tÃŦm-kiáēŋm-cáģ§a-báēĄn", @@ -1490,6 +1594,7 @@ "searching_locales": "Đang tÃŦm kiáēŋm khu váģąc...", "second": "GiÃĸy", "see_all_people": "Xem táēĨt cáēŖ máģi ngưáģi", + "select": "Cháģn", "select_album_cover": "Cháģn áēŖnh bÃŦa album", "select_all": "Cháģn táēĨt cáēŖ", "select_all_duplicates": "Cháģn táēĨt cáēŖ cÃĄc báēŖn trÚng láēˇp", @@ -1500,6 +1605,7 @@ "select_keep_all": "Cháģn giáģ¯ táēĨt cáēŖ", "select_library_owner": "Cháģn cháģ§ sáģŸ háģ¯u thư viáģ‡n", "select_new_face": "Cháģn khuôn máēˇt máģ›i", + "select_person_to_tag": "Cháģn ngưáģi đáģƒ gáē¯n tháēģ", "select_photos": "Cháģn áēŖnh", "select_trash_all": "Cháģn xoÃĄ táēĨt cáēŖ", "select_user_for_sharing_page_err_album": "TáēĄo album tháēĨt báēĄi", @@ -1512,10 +1618,12 @@ "server_info_box_server_url": "Đáģ‹a cháģ‰ mÃĄy cháģ§", "server_offline": "MÃĄy cháģ§ ngoáēĄi tuyáēŋn", "server_online": "MÃĄy cháģ§ tráģąc tuyáēŋn", + "server_privacy": "Quyáģn riÃĒng tư mÃĄy cháģ§", "server_stats": "Tháģ‘ng kÃĒ mÃĄy cháģ§", "server_version": "PhiÃĒn báēŖn mÃĄy cháģ§", "set": "Đáēˇt", "set_as_album_cover": "Đáēˇt làm áēŖnh bÃŦa album", + "set_as_featured_photo": "Đáēˇt làm áēŖnh náģ•i báē­t", "set_as_profile_picture": "Đáēˇt làm áēŖnh đáēĄi diáģ‡n", "set_date_of_birth": "Đáēˇt ngày sinh", "set_profile_picture": "Đáēˇt áēŖnh đáēĄi diáģ‡n", @@ -1527,9 +1635,8 @@ "setting_image_viewer_preview_title": "TáēŖi áēŖnh xem trưáģ›c", "setting_image_viewer_title": "HÃŦnh áēŖnh", "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: {duration}", + "setting_languages_subtitle": "Thay đáģ•i ngôn ngáģ¯ cáģ§a áģŠng dáģĨng", + "setting_notifications_notify_failures_grace_period": "Thông bÃĄo láģ—i sao lưu náģn: {duration}", "setting_notifications_notify_hours": "{count} giáģ", "setting_notifications_notify_immediately": "ngay láē­p táģŠc", "setting_notifications_notify_minutes": "{count} phÃēt", @@ -1537,7 +1644,7 @@ "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", + "setting_notifications_subtitle": "TÚy cháģ‰nh thông bÃĄo cáģ§a báēĄn", "setting_notifications_total_progress_subtitle": "Toàn báģ™ quÃĄ trÃŦnh táēŖi lÃĒn (hoàn thành/táģ•ng sáģ‘ áēŖnh)", "setting_notifications_total_progress_title": "Hiáģ‡n tháģ‹ toàn báģ™ sao lưu náģn đang tháģąc hiáģ‡n", "setting_video_viewer_looping_title": "Láēˇp láēĄi", @@ -1546,11 +1653,13 @@ "settings": "Cài đáēˇt", "settings_require_restart": "Vui lÃ˛ng kháģŸi đáģ™ng láēĄi Immich đáģƒ ÃĄp dáģĨng cài đáēˇt này", "settings_saved": "ÄÃŖ lưu cài đáēˇt", + "setup_pin_code": "Thiáēŋt láē­p mÃŖ PIN", "share": "Chia sáēģ", "share_add_photos": "ThÃĒm áēŖnh", "share_assets_selected": "{count} Ä‘ÃŖ cháģn", "share_dialog_preparing": "Đang xáģ­ lÃŊ...", - "shared": "ÄÃŖ đưáģŖc chia sáēģ", + "share_link": "LiÃĒn káēŋt chia sáēģ", + "shared": "ĐưáģŖc chia sáēģ", "shared_album_activities_input_disable": "Nháē­n xÊt hiáģ‡n Ä‘ÃŖ táē¯t", "shared_album_activity_remove_content": "BáēĄn cÃŗ muáģ‘n xoÃĄ hoáēĄt đáģ™ng này?", "shared_album_activity_remove_title": "XoÃĄ hoáēĄt đáģ™ng", @@ -1585,7 +1694,7 @@ "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_never": "Háēŋt háēĄn ∞", "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ư", @@ -1593,8 +1702,9 @@ "shared_link_manage_links": "QuáēŖn lÃŊ liÃĒn káēŋt đưáģŖc chia sáēģ", "shared_link_options": "TÚy cháģn liÃĒn káēŋt chia sáēģ", "shared_links": "LiÃĒn káēŋt chia sáēģ", + "shared_links_description": "Chia sáēģ áēŖnh và video báēąng liÃĒn káēŋt", "shared_photos_and_videos_count": "{assetCount, plural, other {# áēŖnh & video Ä‘ÃŖ chia sáēģ.}}", - "shared_with_me": "Chia sáēŊ váģ›i tôi", + "shared_with_me": "Chia sáēģ váģ›i tôi", "shared_with_partner": "ĐưáģŖc chia sáēģ váģ›i {partner}", "sharing": "Chia sáēģ", "sharing_enter_password": "Vui lÃ˛ng nháē­p máē­t kháēŠu đáģƒ xem trang này.", @@ -1621,6 +1731,7 @@ "show_person_options": "Hiáģƒn tháģ‹ tÚy cháģn ngưáģi", "show_progress_bar": "Hiáģƒn tháģ‹ thanh tiáēŋn trÃŦnh", "show_search_options": "Hiáģƒn tháģ‹ tÚy cháģn tÃŦm kiáēŋm", + "show_shared_links": "Hiáģƒn tháģ‹ cÃĄc liÃĒn káēŋt đưáģŖc chia sáēģ", "show_slideshow_transition": "Hiáģƒn tháģ‹ hiáģ‡u áģŠng chuyáģƒn tiáēŋp", "show_supporter_badge": "Huy hiáģ‡u ngưáģi áģ§ng háģ™", "show_supporter_badge_description": "Hiáģƒn tháģ‹ huy hiáģ‡u ngưáģi áģ§ng háģ™", @@ -1640,12 +1751,13 @@ "sort_items": "Sáģ‘ lưáģŖng máģĨc", "sort_modified": "Ngày sáģ­a đáģ•i", "sort_oldest": "áēĸnh cÅŠ nháēĨt", + "sort_people_by_similarity": "Sáē¯p xáēŋp ngưáģi theo đáģ™ tÆ°ÆĄng đáģ“ng", "sort_recent": "áēĸnh gáē§n đÃĸy nháēĨt", "sort_title": "TiÃĒu đáģ", "source": "Nguáģ“n", "stack": "NhÃŗm áēŖnh", "stack_duplicates": "NhÃŗm máģĨc trÚng láēˇp", - "stack_select_one_photo": "Cháģn máģ™t áēŖnh chính cho nhÃŗm áēŖnh", + "stack_select_one_photo": "Cháģn áēŖnh chính cho nhÃŗm áēŖnh", "stack_selected_photos": "NhÃŗm cÃĄc áēŖnh Ä‘ÃŖ cháģn", "stacked_assets_count": "ÄÃŖ nhÃŗm {count, plural, one {# máģĨc} other {# máģĨc}}", "stacktrace": "Thông tin chi tiáēŋt láģ—i", @@ -1659,6 +1771,7 @@ "stop_sharing_photos_with_user": "DáģĢng chia sáēģ áēŖnh cáģ§a báēĄn váģ›i ngưáģi dÚng này", "storage": "Báģ™ nháģ›", "storage_label": "NhÃŖn lưu tráģ¯", + "storage_quota": "Giáģ›i háēĄn Dung lưáģŖng", "storage_usage": "ÄÃŖ sáģ­ dáģĨng {used} cáģ§a {available}", "submit": "Gáģ­i", "suggestions": "GáģŖi ÃŊ", @@ -1676,6 +1789,7 @@ "tag_created": "ÄÃŖ táēĄo tháēģ: {tag}", "tag_feature_description": "Duyáģ‡t áēŖnh và video đưáģŖc nhÃŗm theo cháģ§ Ä‘áģ tháēģ háģŖp lÃŊ", "tag_not_found_question": "Không tÃŦm tháēĨy tháēģ? TáēĄo máģ™t tháēģ máģ›i", + "tag_people": "Gáē¯n tháēģ Máģi ngưáģi", "tag_updated": "ÄÃŖ cáē­p nháē­t tháēģ: {tag}", "tagged_assets": "ÄÃŖ gáē¯n tháēģ {count, plural, one {# máģĨc} other {# máģĨc}}", "tags": "Tháēģ", @@ -1683,9 +1797,9 @@ "theme": "Cháģ§ Ä‘áģ", "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_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 ({count})", - "theme_setting_colorful_interface_subtitle": "Áp dáģĨng màu cháģ§ Ä‘áēĄo cho náģn áģŠng dáģĨng", + "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", "theme_setting_image_viewer_quality_title": "CháēĨt lưáģŖng trÃŦnh xem áēŖnh", @@ -1694,20 +1808,21 @@ "theme_setting_system_primary_color_title": "DÚng màu háģ‡ tháģ‘ng", "theme_setting_system_theme_switch": "Táģą Ä‘áģ™ng (Theo cài đáēˇt háģ‡ tháģ‘ng)", "theme_setting_theme_subtitle": "Cháģn cài đáēˇt giao diáģ‡n áģŠng dáģĨng", - "theme_setting_three_stage_loading_subtitle": "TáēŖi ba giai doáēĄn cÃŗ tháģƒ tăng hiáģ‡u năng táēŖi áēŖnh nhưng sáēŊ táģ‘n dáģ¯ liáģ‡u máēĄng Ä‘ÃĄng káģƒ.", + "theme_setting_three_stage_loading_subtitle": "TáēŖi ba giai đoáēĄn cÃŗ tháģƒ tăng táģ‘c đáģ™ táēŖi áēŖnh nhưng sáēŊ táģ‘n dáģ¯ liáģ‡u máēĄng Ä‘ÃĄng káģƒ", "theme_setting_three_stage_loading_title": "Báē­t táēŖi ba giai đoáēĄn", "they_will_be_merged_together": "ChÃēng sáēŊ đưáģŖc háģŖp nháēĨt váģ›i nhau", "third_party_resources": "Tài nguyÃĒn bÃĒn tháģŠ ba", "time_based_memories": "Káģˇ niáģ‡m dáģąa trÃĒn tháģi gian", + "timeline": "DÃ˛ng tháģi gian", "timezone": "MÃēi giáģ", "to_archive": "Lưu tráģ¯", "to_change_password": "Đáģ•i máē­t kháēŠu", "to_favorite": "YÃĒu thích", "to_login": "Đăng nháē­p", - "to_parent": "Đáēŋn thư máģĨc cha", + "to_parent": "Đi táģ›i thư máģĨc cha", "to_trash": "XÃŗa", "toggle_settings": "Chuyáģƒn đáģ•i cài đáēˇt", - "toggle_theme": "Chuyáģƒn đáģ•i cháģ§ Ä‘áģ táģ‘i", + "total": "Táģ•ng cáģ™ng", "total_usage": "Táģ•ng dung lưáģŖng Ä‘ÃŖ sáģ­ dáģĨng", "trash": "ThÚng rÃĄc", "trash_all": "XÃŗa háēŋt", @@ -1724,16 +1839,21 @@ "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", + "unable_to_change_pin_code": "Thay đáģ•i mÃŖ PIN tháēĨt báēĄi", + "unable_to_setup_pin_code": "Thiáēŋt láē­p mÃŖ PIN tháēĨt báēĄi", "unarchive": "Huáģˇ lưu tráģ¯", "unarchived_count": "{count, plural, other {ÄÃŖ huáģˇ lưu tráģ¯ # máģĨc}}", + "undo": "Hoàn tÃĄc", "unfavorite": "Báģ yÃĒu thích", "unhide_person": "Hiáģ‡n ngưáģi", "unknown": "Không xÃĄc đáģ‹nh", + "unknown_country": "Quáģ‘c gia không xÃĄc đáģ‹nh", "unknown_year": "Năm không xÃĄc đáģ‹nh", "unlimited": "Không giáģ›i háēĄn", "unlink_motion_video": "Háģ§y liÃĒn káēŋt video chuyáģƒn đáģ™ng", "unlink_oauth": "Huáģˇ liÃĒn káēŋt OAuth", "unlinked_oauth_account": "ÄÃŖ huáģˇ liÃĒn káēŋt tài khoáēŖn OAuth", + "unmute_memories": "Báē­t tiáēŋng Káģˇ niáģ‡m", "unnamed_album": "Album chưa đáēˇt tÃĒn", "unnamed_album_delete_confirmation": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n xÃŗa album này không?", "unnamed_share": "Chia sáēģ chưa đáēˇt tÃĒn", @@ -1742,9 +1862,8 @@ "unselect_all_duplicates": "Báģ cháģn táēĨt cáēŖ cÃĄc báēŖn trÚng láēˇp", "unstack": "Huáģˇ xáēŋp nhÃŗm", "unstacked_assets_count": "ÄÃŖ huáģˇ xáēŋp nhÃŗm {count, plural, one {# máģĨc} other {# máģĨc}}", - "untracked_files": "CÃĄc táē­p tin không đưáģŖc theo dÃĩi", - "untracked_files_decription": "CÃĄc táē­p tin này không đưáģŖc áģŠng dáģĨng theo dÃĩi. ChÃēng cÃŗ tháģƒ là káēŋt quáēŖ cáģ§a quÃĄ trÃŦnh di chuyáģƒn tháēĨt báēĄi, táēŖi lÃĒn báģ‹ giÃĄn đoáēĄn hoáēˇc báģ‹ báģ láēĄi do láģ—i", "up_next": "Tiáēŋp theo", + "updated_at": "ÄÃŖ cáē­p nháē­t", "updated_password": "ÄÃŖ cáē­p nháē­t máē­t kháēŠu", "upload": "TáēŖi lÃĒn", "upload_concurrency": "TáēŖi lÃĒn đáģ“ng tháģi", @@ -1759,16 +1878,24 @@ "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 ({count})", "uploading": "Đang táēŖi lÃĒn", + "url": "URL", "usage": "Sáģ­ dáģĨng", + "use_biometric": "DÚng sinh tráē¯c háģc", "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", + "use_custom_date_range": "Sáģ­ dáģĨng khoáēŖng tháģi gian tÚy cháģ‰nh", "user": "Ngưáģi dÚng", + "user_has_been_deleted": "Ngưáģi dÚng này Ä‘ÃŖ báģ‹ xÃŗa.", "user_id": "ID ngưáģi dÚng", - "user_liked": "{user} Ä‘ÃŖ thích {type, select, photo {áēŖnh này} video {video này} asset {táē­p tin này} other {nÃŗ}}", + "user_liked": "{user} Ä‘ÃŖ thích {type, select, photo {áēŖnh này} video {video này} asset {máģĨc này} other {nÃŗ}}", + "user_pin_code_settings": "MÃŖ PIN", + "user_pin_code_settings_description": "QuáēŖn lÃŊ mÃŖ PIN cáģ§a báēĄn", + "user_privacy": "Quyáģn riÃĒng tư ngưáģi dÚng", "user_purchase_settings": "Mua", "user_purchase_settings_description": "QuáēŖn lÃŊ máģĨc mua cáģ§a báēĄn", "user_role_set": "Đáēˇt {user} làm {role}", "user_usage_detail": "Chi tiáēŋt sáģ­ dáģĨng cáģ§a ngưáģi dÚng", + "user_usage_stats": "Tháģ‘ng kÃĒ sáģ­ dáģĨng cáģ§a tài khoáēŖn", + "user_usage_stats_description": "Xem tháģ‘ng kÃĒ sáģ­ dáģĨng cáģ§a tài khoáēŖn", "username": "TÃĒn ngưáģi dÚng", "users": "Ngưáģi dÚng", "utilities": "Tiáģ‡n ích", @@ -1778,13 +1905,9 @@ "version": "PhiÃĒn báēŖn", "version_announcement_closing": "BáēĄn cáģ§a báēĄn, Alex", "version_announcement_message": "Chào báēĄn! Máģ™t phiÃĒn báēŖn máģ›i cáģ§a Immich Ä‘ÃŖ phÃĄt hành. Vui lÃ˛ng dành tháģi gian đáģƒ xem danh sÃĄch thay đáģ•i đáģƒ Ä‘áēŖm báēŖo cáēĨu hÃŦnh cáģ§a báēĄn đưáģŖc cáē­p nháē­t đáģƒ trÃĄnh láģ—i cáēĨu hÃŦnh sai, đáēˇc biáģ‡t náēŋu báēĄn sáģ­ dáģĨng WatchTower hoáēˇc báēĨt káģŗ cÆĄ cháēŋ táģą Ä‘áģ™ng cáē­p nháē­t Immich cáģ§a báēĄn.", - "version_announcement_overlay_release_notes": "ghi chÃē phÃĄt hành", - "version_announcement_overlay_text_1": "Chào báēĄn, cÃŗ máģ™t báēŖn phÃĄt hành máģ›i cáģ§a", - "version_announcement_overlay_text_2": "vui lÃ˛ng dành tháģi gian cáģ§a báēĄn đáģƒ Ä‘áēŋn thăm", - "version_announcement_overlay_text_3": "và đáēŖm báēŖo cài đáēˇt docker-compose và táģ‡p .env cáģ§a báēĄn Ä‘ÃŖ cáē­p nháē­t đáģƒ trÃĄnh báēĨt káģŗ cáēĨu hÃŦnh sai sÃŗt, đáēˇc biáģ‡t náēŋu báēĄn dÚng WatchTower hoáēˇc báēĨt káģŗ cÆĄ cháēŋ nào xáģ­ lÃŊ viáģ‡c cáē­p nháē­t áģŠng dáģĨng mÃĄy cháģ§ cáģ§a báēĄn táģą Ä‘áģ™ng.", - "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", @@ -1794,10 +1917,14 @@ "view_all": "Xem táēĨt cáēŖ", "view_all_users": "Xem táēĨt cáēŖ ngưáģi dÚng", "view_in_timeline": "Xem trong dÃ˛ng tháģi gian", + "view_link": "Xem liÃĒn káēŋt", "view_links": "Xem cÃĄc liÃĒn káēŋt", + "view_name": "Giao diáģ‡n", "view_next_asset": "Xem áēŖnh tiáēŋp theo", "view_previous_asset": "Xem áēŖnh trưáģ›c Ä‘Ãŗ", + "view_qr_code": "Xem mÃŖ QR", "view_stack": "Xem nhÃŗm áēŖnh", + "view_user": "Xem Ngưáģi dÚng", "viewer_remove_from_stack": "XoÃĄ kháģi nhÃŗm", "viewer_stack_use_as_main_asset": "Đáēˇt làm láģąa cháģn hàng đáē§u", "viewer_unstack": "Huáģˇ xáēŋp nhÃŗm", @@ -1807,11 +1934,12 @@ "week": "Tuáē§n", "welcome": "Chào máģĢng", "welcome_to_immich": "Chào máģĢng đáēŋn váģ›i Immich", - "wifi_name": "TÃĒn WiFi", + "wifi_name": "TÃĒn Wi-Fi", + "wrong_pin_code": "MÃŖ PIN không đÃēng", "year": "Năm", "years_ago": "{years, plural, one {# năm} other {# năm}} trưáģ›c", - "yes": "CÃŗ", + "yes": "Đáģ“ng ÃŊ", "you_dont_have_any_shared_links": "BáēĄn không cÃŗ liÃĒn káēŋt chia sáēģ nào", - "your_wifi_name": "TÃĒn WiFi cáģ§a báēĄn", + "your_wifi_name": "TÃĒn Wi-Fi cáģ§a báēĄn", "zoom_image": "Thu phÃŗng áēŖnh" } diff --git a/i18n/zh_Hant.json b/i18n/zh_Hant.json index cde9717e22..e5a8a65e25 100644 --- a/i18n/zh_Hant.json +++ b/i18n/zh_Hant.json @@ -22,6 +22,7 @@ "add_partner": "新åĸžčĻĒæœ‹åĨŊ友", "add_path": "新åĸžčˇ¯åž‘", "add_photos": "加å…Ĩᅧቇ", + "add_tag": "加å…Ĩæ¨™įą¤", "add_to": "加å…Ĩ到â€Ļ", "add_to_album": "加å…Ĩåˆ°į›¸į°ŋ", "add_to_album_bottom_sheet_added": "新åĸžåˆ° {album}", @@ -33,6 +34,7 @@ "added_to_favorites_count": "將 {count, number} å€‹é …į›ŽåŠ å…Ĩæ”ļ藏", "admin": { "add_exclusion_pattern_description": "新åĸžæŽ’除æĸäģļ。支援äŊŋį”¨ã€Œ*」、「 **」、「?」䞆扞尋įŦĻ合čĻå‰‡įš„å­—ä¸˛ã€‚åĻ‚æžœčρ圍äģģäŊ•名į‚ē「Rawã€įš„į›ŽéŒ„å…§æŽ’é™¤æ‰€æœ‰įŦĻ合æĸäģļįš„æĒ”æĄˆīŧŒčĢ‹äŊŋį”¨ã€Œ**/Raw/**」。åĻ‚æžœčĻæŽ’é™¤æ‰€æœ‰ã€Œ.tif」įĩå°žįš„æĒ”æĄˆīŧŒčĢ‹äŊŋį”¨ã€Œ**/*.tif」。åĻ‚æžœčĻæŽ’é™¤æŸå€‹įĩ•å°čˇ¯åž‘īŧŒčĢ‹äŊŋį”¨ã€Œ/path/to/ignore/**」。", + "admin_user": "įŽĄį†å“Ą", "asset_offline_description": "᪁įĸŸä¸Šæ‰žä¸åˆ°æ­¤å¤–éƒ¨į›¸į°ŋæĒ”æĄˆīŧŒä¸”厞į§ģč‡ŗåžƒåœžæĄļ。åĻ‚æžœæĒ”æĄˆåœ¨į›¸į°ŋ內čĸĢį§ģ動īŧŒčĢ‹æĒĸæŸĨ時間čģ¸ä¸­æ˜¯åĻæœ‰æ–°įš„į›¸æ‡‰įš„æĒ”æĄˆã€‚č‹ĨčĻé‚„åŽŸé€™äģŊæĒ”æĄˆīŧŒčĢ‹įĸēäŋ Immich 可äģĨå¯Ģå…Ĩ下列æĒ”æĄˆčˇ¯åž‘īŧŒä¸ĻčŽ€å–æŽƒæį›¸į°ŋ內厚。", "authentication_settings": "éŠ—č­‰č¨­åŽš", "authentication_settings_description": "įŽĄį†å¯†įĸŧ、OAuth 與å…ļäģ–éŠ—č­‰č¨­åŽš", @@ -43,9 +45,7 @@ "backup_database_enable_description": "é–‹å•Ÿčŗ‡æ–™åēĢ備äģŊ", "backup_keep_last_amount": "äŋį•™å…ˆå‰å‚™äģŊįš„æ•¸é‡", "backup_settings": "čŗ‡æ–™åēĢ備äģŊč¨­åŽš", - "backup_settings_description": "įŽĄį†čŗ‡æ–™åēĢ備äģŊč¨­åŽšã€‚ *č¨ģīŧšé€™é …äģģå‹™ä¸æœƒæœ‰į´€éŒ„īŧŒå¤ąæ•—æ™‚į„Ąæŗ•æ”ļ到通įŸĨ。", - "check_all": "全選", - "cleanup": "æ¸…į†", + "backup_settings_description": "įŽĄį†čŗ‡æ–™åēĢ備äģŊč¨­åŽšã€‚", "cleared_jobs": "厞åˆĒ除「{job}」äģģ務", "config_set_by_file": "åˇ˛é€éŽč¨­åŽšæĒ”æ›´æ–°č¨­åޚ", "confirm_delete_library": "įĸē厚čρåˆĒ除 {library} ᛏį°ŋ嗎īŧŸ", @@ -54,24 +54,22 @@ "confirm_reprocess_all_faces": "įĸē厚čĻé‡æ–°č™•į†æ‰€æœ‰č‡‰å­”å—ŽīŧŸé€™æœƒæ¸…除厞å‘Ŋåįš„äēēį‰Šã€‚", "confirm_user_password_reset": "您įĸē厚čĻé‡č¨­ {user} įš„å¯†įĸŧ嗎īŧŸ", "confirm_user_pin_code_reset": "įĸē厚čĻé‡įŊŽ {user} įš„ PIN įĸŧ嗎īŧŸ", - "create_job": "åģēįĢ‹äŊœæĨ­", + "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": "äŊŋį”¨æŠŸå™¨å­¸įŋ’åĩæ¸Ŧé …į›Žä¸­įš„č‡‰å­”(åŊąį‰‡åĒ會åĩæ¸Ŧį¸Žåœ–ä¸­įš„č‡‰å­”)ã€‚é¸æ“‡ã€Œé‡æ–°č™•į†ã€æœƒé‡æ–°č™•į†æ‰€æœ‰å°šæœĒ處ᐆäģĨåŠåˇ˛įļ“č™•į†įš„é …į›Žã€‚é¸æ“‡ã€Œé‡č¨­ã€æœƒæ¸…é™¤į›Žå‰æ‰€æœ‰įš„č‡‰å­”čŗ‡æ–™ã€‚é¸æ“‡ã€ŒæŽ’å…ĨæœĒč™•į†ã€æœƒæŠŠå°šæœĒč™•į†įš„é …į›ŽæŽ’å…Ĩ處ᐆåēåˆ—ä¸­ã€‚č‡‰å­”åĩæ¸Ŧ厌成垌īŧŒæœƒæŠŠåĩæ¸Ŧåˆ°įš„č‡‰å­”æŽ’å…Ĩ臉部辨識åēåˆ—中īŧŒå°‡å…ļ分įĩ„åˆ°įžæœ‰įš„æˆ–æ–°įš„äēēį‰Šä¸­ã€‚", - "facial_recognition_job_description": "將åĩæ¸Ŧåˆ°įš„č‡‰å­”äžį…§äēēį‰Šåˆ†įĩ„。此æ­ĨéŠŸæœƒåœ¨č‡‰å­”åĩæ¸ŦåŽŒæˆåžŒåŸˇčĄŒã€‚é¸æ“‡ã€Œé‡č¨­ã€æœƒé‡æ–°åˆ†įĩ„æ‰€æœ‰č‡‰å­”ã€‚é¸æ“‡ã€Œéēå¤ąįš„ã€æœƒæŠŠå°šæœĒ指厚äēēį‰Šįš„č‡‰å­”æŽ’å…ĨäŊ‡åˆ—。", + "facial_recognition_job_description": "將åĩæ¸Ŧåˆ°įš„č‡‰å­”äžį…§äēēį‰Šåˆ†éĄžã€‚æ­¤æ­ĨéŠŸæœƒåœ¨č‡‰å­”åĩæ¸ŦåŽŒæˆåžŒåŸˇčĄŒã€‚é¸æ“‡ã€Œé‡č¨­ã€æœƒé‡æ–°åˆ†įĩ„æ‰€æœ‰č‡‰å­”ã€‚é¸æ“‡ã€ŒæŽ’å…ĨæœĒč™•į†ã€æœƒæŠŠå°šæœĒ指厚äēēį‰Šįš„č‡‰å­”æŽ’å…Ĩ處ᐆåēåˆ—中。", "failed_job_command": "{job} äģģå‹™įš„ {command} 指äģ¤åŸˇčĄŒå¤ąæ•—", "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įš„å…¨å°ēå¯¸åœ–į‰‡å“čŗĒ。čļŠéĢ˜įš„æ•¸å­—äģŖčĄ¨č‘—į”ĸå‡ēįš„å“čŗĒčļŠé̘īŧŒæĒ”æĄˆæ›´å¤§ã€‚", @@ -92,13 +90,13 @@ "image_thumbnail_quality_description": "į¸Žåœ–å“čŗĒį‚ē 1 īŊž 100。數å€ŧčļŠå¤§å“čŗĒčļŠé̘īŧŒäŊ†æœƒį”ĸį”Ÿčŧƒå¤§įš„æĒ”æĄˆīŧŒä¸”可čƒŊ降äŊŽæ‡‰į”¨į¨‹åŧįš„回應速åēĻ。", "image_thumbnail_title": "į¸Žåœ–č¨­åŽš", "job_concurrency": "{job}ä¸Ļ行", - "job_created": "厞åģēįĢ‹äŊœæĨ­", + "job_created": "厞åģēįĢ‹äģģ務", "job_not_concurrency_safe": "這個äģģ務ä¸Ļ行ä¸Ļ不厉全。", - "job_settings": "äŊœæĨ­č¨­åޚ", - "job_settings_description": "įŽĄį†äŊœæĨ­ä¸Ļ行", - "job_status": "äŊœæĨ­į‹€æ…‹", - "jobs_delayed": "厞åģļ垌 {jobCount, plural, other {# 項äŊœæĨ­}}", - "jobs_failed": "{jobCount, plural, other {# 項}}äŊœæĨ­å¤ąæ•—", + "job_settings": "äģģå‹™č¨­åŽš", + "job_settings_description": "ä¸Ļ行äģģå‹™įŽĄį†", + "job_status": "äģģå‹™į‹€æ…‹", + "jobs_delayed": "厞åģļ垌 {jobCount, plural, other {# 項äģģ務}}", + "jobs_failed": "{jobCount, plural, other {# 項äģģå‹™å¤ąæ•—}}", "library_created": "厞åģēįĢ‹åœ–åēĢīŧš{library}", "library_deleted": "ᛏį°ŋ厞åˆĒ除", "library_import_path_description": "選取čρčŧ‰å…Ĩįš„čŗ‡æ–™å¤žã€‚äģĨæŽƒæčŗ‡æ–™å¤žīŧˆåĢå­čŗ‡æ–™å¤žīŧ‰å…§įš„åŊąåƒå’ŒåŊąį‰‡ã€‚", @@ -115,7 +113,7 @@ "logging_level_description": "å•Ÿį”¨æ™‚įš„č¨˜éŒ„åą¤į´šã€‚", "logging_settings": "記錄æĒ”", "machine_learning_clip_model": "CLIP æ¨Ąåž‹", - "machine_learning_clip_model_description": "é€™čŖĄæœ‰äģŊ CLIP æ¨Ąåž‹åå–Žã€‚č¨ģīŧšæ›´æ›æ¨Ąåž‹åžŒé ˆå°æ‰€æœ‰åœ–į‰‡é‡æ–°åŸˇčĄŒã€Œæ™ē慧搜尋」äŊœæĨ­ã€‚", + "machine_learning_clip_model_description": "é€™čŖĄæœ‰äģŊ CLIP æ¨Ąåž‹åå–Žã€‚*č¨ģīŧšæ›´æ›æ¨Ąåž‹åžŒé ˆå°æ‰€æœ‰åœ–į‰‡é‡æ–°åŸˇčĄŒã€Œæ™ē慧搜尋」äģģ務。", "machine_learning_duplicate_detection": "é‡č¤‡é …į›Žåĩæ¸Ŧ", "machine_learning_duplicate_detection_enabled": "å•Ÿį”¨é‡č¤‡é …į›Žåĩæ¸Ŧ", "machine_learning_duplicate_detection_enabled_description": "é—œé–‰čŠ˛åŠŸčƒŊ會åŋŊį•Ĩæœ‰é‡č¤‡įš„é …į›Žã€‚", @@ -158,8 +156,8 @@ "map_settings": "地圖", "map_settings_description": "įŽĄį†åœ°åœ–č¨­åŽš", "map_style_description": "地圖ä¸ģ題īŧˆstyle.jsonīŧ‰įš„įļ˛å€", - "memory_cleanup_job": "記æ†ļéĢ”æ¸…į†", - "memory_generate_job": "記æ†ļéĢ”į”Ÿæˆ", + "memory_cleanup_job": "回æ†ļæ¸…į†", + "memory_generate_job": "回æ†ļį”ĸį”Ÿ", "metadata_extraction_job": "æ“ˇå–čŠŽé‡‹čŗ‡æ–™", "metadata_extraction_job_description": "æ“ˇå–æ‰€æœ‰æĒ”æĄˆįš„ GPSã€č‡‰å­”ã€č§ŖæžåēĻį­‰åŽŸå§‹čŠŗį´°čŗ‡æ–™", "metadata_faces_import_setting": "å•Ÿį”¨č‡‰å­”åŒ¯å…Ĩ", @@ -173,7 +171,7 @@ "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_from_address_description": "寄äģļ者é›ģ子éƒĩäģļ地址īŧŒäž‹īŧšImmich Photo Server 。čĢ‹įĸēäŋå¯„äģļ者äŋĄįŽąå…č¨ąį™ŧ送到æ”ļäģļ者é›ģ子éƒĩäģļįš„åœ°å€ã€‚", "notification_email_host_description": "é›ģ子éƒĩäģļäŧ翜å™¨ä¸ģ抟īŧˆäž‹īŧšsmtp.immich.appīŧ‰", "notification_email_ignore_certificate_errors": "åŋŊį•Ĩæ†‘č­‰éŒ¯čǤ", "notification_email_ignore_certificate_errors_description": "åŋŊį•Ĩ TLS æ†‘č­‰éŠ—č­‰éŒ¯čǤīŧˆä¸åģēč­°īŧ‰", @@ -197,7 +195,7 @@ "oauth_enable_description": "ᔍ OAuth į™ģå…Ĩ", "oauth_mobile_redirect_uri": "į§ģ動įĢ¯é‡åŽšå‘ URI", "oauth_mobile_redirect_uri_override": "į§ģ動įĢ¯é‡åŽšå‘ URI čφ蓋", - "oauth_mobile_redirect_uri_override_description": "į•ļ OAuth æäž›č€…ä¸å…č¨ąäŊŋį”¨čĄŒå‹• URIīŧˆåĻ‚ã€Œ'{callback}'」īŧ‰æ™‚å•Ÿį”¨", + "oauth_mobile_redirect_uri_override_description": "į•ļ OAuth æäž›č€…ä¸å…č¨ąäŊŋį”¨čĄŒå‹• URIīŧˆåĻ‚ã€Œ''{callback}''」īŧ‰æ™‚å•Ÿį”¨", "oauth_settings": "OAuth", "oauth_settings_description": "įŽĄį† OAuth į™ģå…Ĩč¨­åŽš", "oauth_settings_more_details": "æŦ˛įž­č§Ŗæ­¤åŠŸčƒŊīŧŒčĢ‹åƒé–ąčĒĒæ˜Žæ›¸ã€‚", @@ -206,11 +204,9 @@ "oauth_storage_quota_claim": "å„˛å­˜é…éĄåŽŖå‘Š", "oauth_storage_quota_claim_description": "č‡Ē動將äŊŋį”¨č€…įš„å„˛å­˜é…éĄåŽšį‚ēæ­¤åŽŖå‘Šäš‹å€ŧ。", "oauth_storage_quota_default": "é č¨­å„˛å­˜é…éĄīŧˆGiBīŧ‰", - "oauth_storage_quota_default_description": "æœĒåŽŖå‘Šæ™‚æ‰€äŊŋį”¨įš„é…éĄīŧˆå–ŽäŊīŧšGiBīŧ‰īŧˆčŧ¸å…Ĩ 0 襨į¤ē不限åˆļ配額īŧ‰ã€‚", + "oauth_storage_quota_default_description": "æœĒåŽŖå‘Šæ™‚æ‰€äŊŋį”¨įš„é…éĄīŧˆå–ŽäŊīŧšGiBīŧ‰ã€‚", "oauth_timeout": "čĢ‹æą‚é€žæ™‚", "oauth_timeout_description": "čĢ‹æą‚įš„é€žæ™‚æ™‚é–“īŧˆæ¯Ģį§’īŧ‰", - "offline_paths": "å¤ąæ•ˆčˇ¯åž‘", - "offline_paths_description": "這äē›å¯čƒŊ是手動åˆĒ除非外部圖åēĢįš„æĒ”æĄˆæ™‚æ‰€éēį•™įš„ã€‚", "password_enable_description": "ᔍé›ģ子éƒĩäģļ和密įĸŧį™ģå…Ĩ", "password_settings": "密įĸŧį™ģå…Ĩ", "password_settings_description": "įŽĄį†å¯†įĸŧį™ģå…Ĩč¨­åŽš", @@ -220,17 +216,14 @@ "refreshing_all_libraries": "æ­Ŗåœ¨é‡æ–°æ•´į†æ‰€æœ‰åœ–åēĢ", "registration": "įŽĄį†č€…č¨ģ冊", "registration_description": "į”ąæ–ŧ您是æœŦįŗģįĩąįš„éĻ–äŊäŊŋᔍ者īŧŒå› æ­¤å°‡æ‚¨æŒ‡æ´žį‚ē負č˛ŦįŽĄį†æœŦįŗģįĩąįš„įŽĄį†č€…īŧŒå…ļäģ–äŊŋį”¨č€…é ˆį”ąæ‚¨å”åŠŠåģēįĢ‹å¸ŗč™Ÿã€‚", - "repair_all": "å…¨éƒ¨įŗžæ­Ŗ", - "repair_matched_items": "有 {count, plural, other {# å€‹é …į›Žį›¸įŦĻ}}", - "repaired_items": "åˇ˛įŗžæ­Ŗ {count, plural, other {# å€‹é …į›Ž}}", "require_password_change_on_login": "čĻæą‚äŊŋį”¨č€…åœ¨éĻ–æŦĄį™ģå…Ĩ時更攚密įĸŧ", "reset_settings_to_default": "å°‡č¨­åŽšé‡č¨­å›žé č¨­", "reset_settings_to_recent_saved": "åˇ˛č¨­å›žæœ€åžŒå„˛å­˜įš„č¨­åŽš", "scanning_library": "掃描圖åēĢ", - "search_jobs": "搜尋äŊœæĨ­â€Ļ", + "search_jobs": "搜尋äģģ務â€Ļ", "send_welcome_email": "å‚ŗé€æ­ĄčŋŽé›ģ子éƒĩäģļ", "server_external_domain_settings": "外部įļ˛åŸŸ", - "server_external_domain_settings_description": "å…Ŧ開įļ˛å€,īŧŒåŒ…åĢ http(s)://", + "server_external_domain_settings_description": "å…Ŧ開įļ˛å€īŧŒåŒ…åĢ http(s)://", "server_public_users": "č¨ĒåŽĸäŊŋᔍ者", "server_public_users_description": "將äŊŋį”¨č€…æ–°åĸžč‡ŗå…ąį”¨į›¸į°ŋ時īŧŒæœƒåˆ—å‡ē所有äŊŋᔍ者īŧˆå§“名、emailīŧ‰ã€‚關閉時īŧŒäŊŋį”¨č€…åˆ—čĄ¨åƒ…å°įŽĄį†č€…į”Ÿæ•ˆã€‚", "server_settings": "äŧ翜å™¨", @@ -251,7 +244,7 @@ "storage_template_migration_info": "é€į”¨å„˛å­˜į¯„äž‹å°‡å°‡æŠŠæ‰€æœ‰æĒ”æĄˆå‰¯æĒ”åæ”šįˆ˛å°å¯Ģã€‚æ¨Ąæŋæ›´æ–°åƒ…éŠį”¨æ–ŧæ–°é …į›Žã€‚č‹ĨčρåĨ—į”¨éŽåŽģį¯„äž‹čĢ‹å…ˆä¸Šå‚ŗé …į›ŽīŧŒčĢ‹åŸˇčĄŒ {job}。", "storage_template_migration_job": "å­˜å„˛æ¨Ąæŋ遡į§ģäģģ務", "storage_template_more_details": "æŦ˛äē†č§Ŗæ›´å¤šæœ‰é—œæ­¤åŠŸčƒŊįš„čŠŗį´°äŋĄæ¯īŧŒčĢ‹åƒé–ą å­˜å„˛æ¨Ąæŋ 及å…ļ åŊąéŸŋ", - "storage_template_onboarding_description": "å•Ÿį”¨æ­¤åŠŸčƒŊ垌īŧŒå°‡æ šæ“šį”¨æˆļč‡ĒåŽšįžŠįš„æ¨Ąæŋč‡Ē動įĩ„į𔿖‡äģļã€‚į”ąæ–ŧįŠŠåŽšæ€§å•éĄŒīŧŒæ­¤åŠŸčƒŊ厞éģ˜čĒé—œé–‰ã€‚æŦ˛äē†č§Ŗæ›´å¤šäŋĄæ¯īŧŒčĢ‹åƒé–ą 文æĒ”。", + "storage_template_onboarding_description_v2": "å•Ÿį”¨æ­¤åŠŸčƒŊ垌īŧŒįŗģįĩąå°‡æ šæ“šäŊŋᔍ者č‡Ēč¨‚įš„į¯„æœŦč‡Ēå‹•æ•´į†æĒ”æĄˆã€‚æœ‰é—œæ›´å¤šäŋĄæ¯īŧŒčĢ‹åƒé–ąæ–‡æĒ” 。", "storage_template_path_length": "å¤§č‡´čˇ¯åž‘é•ˇåēĻ限åˆļīŧš{length, number}/{limit, number}", "storage_template_settings": "å­˜å„˛æ¨Ąæŋ", "storage_template_settings_description": "įŽĄį†ä¸Šå‚ŗæĒ”æĄˆįš„čŗ‡æ–™å¤žįĩæ§‹å’ŒæĒ”名", @@ -263,16 +256,14 @@ "template_email_invite_album": "邀čĢ‹é …į›Žį¯„æœŦ", "template_email_preview": "預čĻŊ", "template_email_settings": "EmailᝄæœŦ", - "template_email_settings_description": "įŽĄį†č‡ĒåŽšįžŠEmail通įŸĨæ¨Ąæŋ", "template_email_update_album": "更新向æœŦᝄæœŦ", "template_email_welcome": "æ­ĄčŋŽEmailᝄæœŦ", "template_settings": "通įŸĨᝄæœŦ", - "template_settings_description": "įŽĄį†é€šįŸĨč‡Ē荂ᝄæœŦ。", + "template_settings_description": "įŽĄį†é€šįŸĨįš„č‡ĒåŽšįžŠį¯„æœŦ", "theme_custom_css_settings": "č‡Ē訂 CSS", "theme_custom_css_settings_description": "可äģĨį”¨åą¤į–Šæ¨ŖåŧčĄ¨īŧˆCSSīŧ‰äž†č‡Ē訂 Immich įš„č¨­č¨ˆã€‚", "theme_settings": "ä¸ģ題", "theme_settings_description": "č‡Ē訂 Immich įš„įļ˛é į•Œéĸ", - "these_files_matched_by_checksum": "這äē›æĒ”æĄˆįš„æ ¸å°å’ŒīŧˆChecksumīŧ‰æ˜¯į›¸įŦĻįš„", "thumbnail_generation_job": "į”ĸį”Ÿį¸Žåœ–", "thumbnail_generation_job_description": "į‚ē每個æĒ”æĄˆį”ĸį”Ÿå¤§ã€å°åŠæ¨ĄįŗŠį¸Žåœ–īŧŒäšŸį‚ē每äŊäēēį‰Šį”ĸį”Ÿį¸Žåœ–", "transcoding_acceleration_api": "加速 API", @@ -300,10 +291,9 @@ "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": "HEVC ᎍ觪įĸŧ器", "transcoding_max_b_frames": "最大 B 嚀數", "transcoding_max_b_frames_description": "čŧƒéĢ˜įš„æ•¸å€ŧå¯æå‡åŖ“į¸Žæ•ˆįŽ‡īŧŒäŊ†æœƒé™äŊŽįˇ¨įĸŧ速åēĻ。可čƒŊ與čŧƒčˆŠč¨­å‚™įš„įĄŦéĢ”åŠ é€Ÿä¸į›¸åŽšã€‚č¨­åŽšį‚ē 0 æ™‚æœƒåœį”¨ B-framesīŧŒč€Œ -1 則會č‡Ēå‹•č¨­åŽšæ­¤æ•¸å€ŧ。", "transcoding_max_bitrate": "最大äŊå…ƒé€Ÿįއ", @@ -341,14 +331,13 @@ "trash_number_of_days_description": "永䚅åˆĒé™¤å‰é …į›Žå°‡äŋį•™åœ¨åžƒåœžæĄļ中數夊", "trash_settings": "垃圞æĄļ", "trash_settings_description": "įŽĄį†åžƒåœžæĄļč¨­åŽš", - "untracked_files": "æœĒčĸĢčŋŊčš¤įš„æĒ”æĄˆ", - "untracked_files_description": "這äē›æĒ”æĄˆä¸æœƒčĸĢčŋŊčš¤ã€‚åŽƒå€‘å¯čƒŊ是į§ģå‹•å¤ąæ•—ã€ä¸Šå‚ŗå¤ąæ•—ã€æŧæ´žč€Œé€ æˆįš„。", "user_cleanup_job": "æ¸…į†äŊŋᔍ者", "user_delete_delay": "{user} įš„å¸ŗč™Ÿå’Œé …į›Žæœƒåœ¨ {delay, plural, other {# 夊}} 垌永䚅åˆĒ除。", "user_delete_delay_settings": "åģļ垌åˆĒ除", "user_delete_delay_settings_description": "夊數垌將永䚅åˆĒé™¤å¸ŗč™Ÿčˆ‡é …į›Žã€‚åˆĒ除äģģ務會在 00:00 垌æĒĸæŸĨ可äģĨåˆĒé™¤įš„äŊŋį”¨č€…ã€‚čŽŠæ›´č¨­åŽšåžŒæœƒåœ¨ä¸‹æŦĄåŸˇčĄŒæĒĸæŸĨ。", "user_delete_immediately": "{user} įš„å¸ŗč™Ÿå’Œé …į›Žå°‡ įĢ‹åŗ 永䚅åˆĒ除。", "user_delete_immediately_checkbox": "將äŊŋį”¨č€…å’Œé …į›ŽįĢ‹åŗåˆĒ除", + "user_details": "ᔍæˆļčŠŗį´°äŋĄæ¯", "user_management": "äŊŋį”¨č€…įŽĄį†", "user_password_has_been_reset": "äŊŋᔍ者坆įĸŧåˇ˛é‡č¨­īŧš", "user_password_reset_description": "čĢ‹æäž›äŊŋį”¨č€…č‡¨æ™‚å¯†įĸŧīŧŒä¸Ļ告įŸĨ下æŦĄį™ģå…Ĩ時需čĻæ›´æ”šå¯†įĸŧ。", @@ -378,7 +367,7 @@ "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_sync_remote_deletions_title": "同æ­Ĩ遠į̝åˆĒ除 [å¯Ļ銗性]", "advanced_settings_tile_subtitle": "é€˛éšŽį”¨æˆļč¨­åŽš", "advanced_settings_troubleshooting_subtitle": "啓ᔍᔍæ–ŧæ•…éšœæŽ’é™¤įš„éĄå¤–åŠŸčƒŊ", "advanced_settings_troubleshooting_title": "故障排除", @@ -400,10 +389,6 @@ "album_remove_user": "į§ģ除äŊŋᔍ者īŧŸ", "album_remove_user_confirmation": "įĸē厚čρį§ģ除 {user} 嗎īŧŸ", "album_share_no_users": "įœ‹äž†æ‚¨čˆ‡æ‰€æœ‰äŊŋį”¨č€…å…ąäēĢäē†é€™æœŦᛏį°ŋīŧŒæˆ–æ˛’æœ‰å…ļäģ–äŊŋį”¨č€…å¯äž›åˆ†äēĢ。", - "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}", @@ -419,6 +404,9 @@ "album_with_link_access": "įŸĨ道逪įĩįš„äŊŋᔍ者éƒŊ可äģĨæŸĨįœ‹é€™æœŦᛏį°ŋä¸­įš„į›¸į‰‡å’ŒäŊŋį”¨č€…ã€‚", "albums": "ᛏį°ŋ", "albums_count": "{count, plural, one {{count, number} æœŦᛏį°ŋ} other {{count, number} æœŦᛏį°ŋ}}", + "albums_default_sort_order": "預荭ᛏį°ŋ排åē", + "albums_default_sort_order_description": "åģēįĢ‹æ–°į›¸į°ŋ時čĻåˆå§‹åŒ–é …į›ŽæŽ’åēã€‚", + "albums_feature_description": "一įŗģ列可äģĨ分äēĢįĩĻå…ļäģ–ᔍæˆļįš„é …į›Žã€‚", "all": "全部", "all_albums": "æ‰€æœ‰į›¸į°ŋ", "all_people": "所有äēēį‰Š", @@ -477,6 +465,7 @@ "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_cannot_be_added_to_album_count": "{count. plural, one {個} other {個}} é …į›ŽæœĒčƒŊčĸĢæˇģåŠ č‡ŗį›¸į°ŋ", "assets_count": "{count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", "assets_deleted_permanently": "{count} å€‹é …į›Žåˇ˛čĸĢæ°¸äš…åˆĒ除", "assets_deleted_permanently_from_server": "åˇ˛åžžäŧ翜å™¨ä¸­æ°¸äš…į§ģ除 {count} å€‹é …į›Ž", @@ -496,7 +485,7 @@ "automatic_endpoint_switching_title": "č‡Ēå‹•åˆ‡æ›é€Ŗįĩ", "back": "čŋ”回", "back_close_deselect": "čŋ”回、關閉及取æļˆé¸å–", - "background_location_permission": "čƒŒæ™¯åŽšäŊæŦŠé™", + "background_location_permission": "čƒŒæ™¯å­˜å–äŊįŊŽæŦŠé™", "background_location_permission_content": "é–‹å•ŸčƒŒæ™¯åŸˇčĄŒæ™‚č‡Ē動切換įļ˛čˇ¯īŧŒčĢ‹å……č¨ą Immich ä¸€åž‹å……č¨ąäŊŋį”¨į˛žįĸēäŊįŊŽæŦŠé™īŧŒäģĨįĸēčĒ Wi-Fi įļ˛čˇ¯åį¨ą", "backup_album_selection_page_albums_device": "čŖįŊŽä¸Šįš„ᛏį°ŋīŧˆ{count}īŧ‰", "backup_album_selection_page_albums_tap": "éģžæ“Šé¸å–īŧŒé€ŖįēŒéģžæ“Šå…ŠæŦĄå–æļˆ", @@ -537,6 +526,7 @@ "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": "削餘", @@ -558,8 +548,12 @@ "backup_manual_success": "成功", "backup_manual_title": "ä¸Šå‚ŗį‹€æ…‹", "backup_options_page_title": "備äģŊ選項", - "backup_setting_subtitle": "įŽĄį†åžŒå°čˆ‡å‰å°ä¸Šå‚ŗč¨­åŽš", + "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 +564,17 @@ "bulk_keep_duplicates_confirmation": "您įĸē厚čρäŋį•™ {count, plural, one {# å€‹é‡č¤‡æĒ”æĄˆ} other {# å€‹é‡č¤‡æĒ”æĄˆ}} 嗎īŧŸé€™å°‡č§Ŗæąēæ‰€æœ‰é‡č¤‡įĩ„č€Œä¸åˆĒ除äģģäŊ•內厚。", "bulk_trash_duplicates_confirmation": "įĸē厚čρ䏀æŦĄä¸ŸæŽ‰ {count, plural, other {# å€‹é‡č¤‡įš„æĒ”æĄˆ}}嗎īŧŸé€™æ¨Ŗæ¯įĩ„é‡č¤‡įš„æĒ”æĄˆä¸­īŧŒæœ€å¤§įš„æœƒį•™ä¸‹äž†īŧŒå…ļåŽƒįš„æœƒčĸĢä¸Ÿé€˛åžƒåœžæĄļ。", "buy": "čŗŧįŊŽ Immich", - "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": "é‡č¤‡é …į›Žīŧˆ{count}īŧ‰", - "cache_settings_image_cache_size": "åœ–į‰‡åŋĢ取大小īŧˆ{count} 項īŧ‰", "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": "控åˆļ Immich app įš„įˇŠå­˜čĄŒį‚ē", - "cache_settings_thumbnail_size": "į¸Žåœ–åŋĢ取大小({count} 項)", "cache_settings_tile_subtitle": "č¨­åŽšæœŦåœ°å­˜å„˛čĄŒį‚ē", "cache_settings_tile_title": "æœŦåœ°å­˜å„˛", "cache_settings_title": "įˇŠå­˜č¨­åŽš", @@ -597,12 +587,14 @@ "cannot_merge_people": "į„Ąæŗ•åˆäŊĩäēēį‰Š", "cannot_undo_this_action": "æ­¤æ­ĨéŠŸį„Ąæŗ•å–æļˆå–”īŧ", "cannot_update_the_description": "į„Ąæŗ•æ›´æ–°æčŋ°", + "cast": "投åŊą", "change_date": "更攚æ—Ĩ期", - "change_display_order": "æ›´æ›éĄ¯į¤ē順åē", + "change_description": "更攚描čŋ°", + "change_display_order": "čŽŠæ›´éĄ¯į¤ē順åē", "change_expiration_time": "æ›´æ”šå¤ąæ•ˆæœŸé™", "change_location": "更攚äŊįŊŽ", "change_name": "攚名", - "change_name_successfully": "攚名成功", + "change_name_successfully": "åˇ˛æˆåŠŸčŽŠæ›´åå­—", "change_password": "更攚密įĸŧ", "change_password_description": "這是您įŦŦ一æŦĄį™ģå…ĨįŗģįĩąīŧŒæˆ–您čĸĢčĻæą‚æ›´æ”šå¯†įĸŧ。čĢ‹åœ¨ä¸‹éĸčŧ¸å…Ĩ新密įĸŧ。", "change_password_form_confirm_password": "įĸēčĒå¯†įĸŧ", @@ -613,7 +605,6 @@ "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 æ™‚åŸˇčĄŒæ­¤æĒĸæŸĨã€‚æ­¤į¨‹åēå¯čƒŊ需čĻåšžåˆ†é˜ã€‚", @@ -653,10 +644,13 @@ "confirm_keep_this_delete_others": "æ‰€æœ‰įš„å…ļäģ–å †į–Šé …į›Žå°‡čĸĢåˆĒ除。įĸē厚įšŧįēŒå—ŽīŧŸ", "confirm_new_pin_code": "įĸēčĒæ–° PIN įĸŧ", "confirm_password": "įĸēčĒå¯†įĸŧ", + "confirm_tag_face": "æ‚¨æƒŗčĻå°‡æ­¤č‡‰å­”æ¨™č¨˜į‚ē {name} 嗎īŧŸ", + "confirm_tag_face_unnamed": "æ‚¨æƒŗæ¨™č¨˜é€™åŧĩč‡‰å—ŽīŧŸ", + "connected_device": "厞逪įĩčŖįŊŽ", + "connected_to": "厞逪æŽĨ到", "contain": "包åĢ", "context": "情åĸƒ", "continue": "įšŧįēŒ", - "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": "åžžį§ģå‹•čŖįŊŽä¸­åˆĒ除", @@ -694,6 +688,7 @@ "create_tag_description": "åģēįĢ‹æ–°įš„æ¨™įą¤ã€‚č‹ĨčρåģēįĢ‹ä¸åŒįž¤įĩ„åˆ†éĄžæ¨™įą¤īŧŒčĢ‹čŧ¸å…ĨåŽŒæ•´įš„æ¨™įą¤čˇ¯åž‘(包æ‹Ŧæ­Ŗæ–œįˇš / )。", "create_user": "åģēįĢ‹äŊŋᔍ者", "created": "åģēįĢ‹æ–ŧ", + "created_at": "åģēįĢ‹æ–ŧ", "crop": "誁å‰Ē", "curated_object_page_title": "äē‹į‰Š", "current_device": "æ­¤čŖįŊŽ", @@ -701,10 +696,14 @@ "current_server_address": "į›Žå‰įš„äŧ翜å™¨äŊå€", "custom_locale": "č‡Ēč¨‚å€åŸŸ", "custom_locale_description": "䞝čĒžč¨€å’Œå€åŸŸč¨­åŽšæ—Ĩ期和數字æ ŧåŧ", + "daily_title_text_date": "E, MMM dd", + "daily_title_text_date_year": "YYYYåš´M月Dæ—Ĩ (E)", "dark": "æˇąč‰˛", + "darkTheme": "åˆ‡æ›įˆ˛æˇąč‰˛ä¸ģ題", "date_after": "æ—Ĩ期䚋垌", "date_and_time": "æ—ĨæœŸčˆ‡æ™‚é–“", "date_before": "æ—Ĩ期䚋前", + "date_format": "yåš´M月dæ—Ĩ (E) h:mm a", "date_of_birth_saved": "å‡ēį”Ÿæ—ĨæœŸå„˛å­˜æˆåŠŸ", "date_range": "æ—ĨæœŸį¯„åœ", "day": "æ—Ĩ", @@ -738,7 +737,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": "更新描čŋ°æ™‚å‡ē錯īŧŒčĢ‹æĒĸæŸĨæ—ĨčnjäģĨį˛å–æ›´å¤ščŠŗį´°čŗ‡č¨Š", @@ -746,8 +745,9 @@ "direction": "斚向", "disabled": "åœį”¨", "disallow_edits": "ä¸å…č¨ąįˇ¨čŧ¯", - "discord": "Discord į¤žįž¤", + "discord": "Discord", "discover": "æŽĸį´ĸ", + "discovered_devices": "厞æŽĸį´ĸå¤ščŖįŊŽ", "dismiss_all_errors": "åŋŊį•Ĩ所有錯čǤ", "dismiss_error": "åŋŊį•Ĩ錯čǤ", "display_options": "éĄ¯į¤ē選項", @@ -763,7 +763,6 @@ "download_enqueue": "厞加å…Ĩ下čŧ‰éšŠåˆ—", "download_error": "下čŧ‰å‡ē錯", "download_failed": "下čŧ‰å¤ąæ•—", - "download_filename": "文äģļ: {filename}", "download_finished": "下čŧ‰åŽŒæˆ", "download_include_embedded_motion_videos": "åĩŒå…ĨåŊąį‰‡", "download_include_embedded_motion_videos_description": "把åĩŒå…Ĩå‹•æ…‹į…§į‰‡įš„åŊąį‰‡äŊœį‚ēå–Žį¨įš„æĒ”æĄˆåŒ…åĢ在內", @@ -787,6 +786,8 @@ "edit_avatar": "ᎍčŧ¯åŊĸ蹥", "edit_date": "ᎍčŧ¯æ—Ĩ期", "edit_date_and_time": "ᎍčŧ¯æ—ĨæœŸčˆ‡æ™‚é–“", + "edit_description": "ᎍčŧ¯æčŋ°", + "edit_description_prompt": "čĢ‹é¸æ“‡æ–°įš„æčŋ°īŧš", "edit_exclusion_pattern": "ᎍčŧ¯æŽ’é™¤æ¨Ąåŧ", "edit_faces": "ᎍčŧ¯č‡‰å­”", "edit_import_path": "ᎍčŧ¯åŒ¯å…Ĩčˇ¯åž‘", @@ -812,15 +813,19 @@ "empty_trash": "清įŠē垃圞æĄļ", "empty_trash_confirmation": "įĸē厚čĻæ¸…įŠē垃圞æĄļ嗎īŧŸé€™æœƒæ°¸äš…åˆĒ除 Immich 垃圞æĄļä¸­æ‰€æœ‰įš„æĒ”æĄˆã€‚\næ­¤æ­ĨéŠŸį„Ąæŗ•å–æļˆå–”īŧ", "enable": "å•Ÿį”¨", + "enable_biometric_auth_description": "čŧ¸å…Ĩæ‚¨įš„ PIN įĸŧäģĨå•Ÿį”¨į”Ÿį‰Ščž¨č­˜éŠ—č­‰", "enabled": "åˇąå•Ÿį”¨", "end_date": "įĩæŸæ—Ĩ期", - "enqueued": "排å…ĨäŊ‡åˆ—中", + "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}", + "error_tag_face_bounding_box": "æ¨™č¨˜č‡‰éƒ¨éŒ¯čǤ - į„Ąæŗ•å–åž—é‚Šį•ŒæĄ†åæ¨™", "error_title": "錯čǤ - å‡ēå•éĄŒäē†", "errors": { "cannot_navigate_next_asset": "į„Ąæŗ•į€čĻŊ下一個æĒ”æĄˆ", @@ -833,7 +838,6 @@ "cant_get_number_of_comments": "į„Ąæŗ•å–åž—į•™č¨€æ•¸é‡", "cant_search_people": "æœĒ搜尋到äēēį‰Š", "cant_search_places": "į„Ąæŗ•æœå°‹åœ°éģž", - "cleared_jobs": "åˇ˛æ¸…é™¤įš„äŊœæĨ­īŧš{job}", "error_adding_assets_to_album": "將æĒ”æĄˆåŠ å…Ĩᛏį°ŋ時å‡ē錯", "error_adding_users_to_album": "將äŊŋį”¨č€…åŠ å…Ĩᛏį°ŋ時å‡ē錯", "error_deleting_shared_user": "åˆĒé™¤å…ąäēĢäŊŋį”¨č€…æ™‚å‡ē錯", @@ -842,7 +846,6 @@ "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": "ᎍčŧ¯å…ąäēĢ逪įĩå¤ąæ•—", @@ -861,7 +864,6 @@ "paths_validation_failed": "{paths, plural, one {# å€‹čˇ¯åž‘} other {# å€‹čˇ¯åž‘}} éŠ—č­‰å¤ąæ•—", "profile_picture_transparent_pixels": "個äēē頭像不čƒŊæœ‰é€æ˜Žåƒį´ ã€‚čĢ‹æ”žå¤§ä¸Ļ/或į§ģ動圖像。", "quota_higher_than_disk_size": "æ‚¨åŽšįš„é…éĄé̘æ–ŧ᪁įĸŸåŽšé‡", - "repair_unable_to_check_items": "į„Ąæŗ•æĒĸæŸĨ {count, select, other { å€‹é …į›Ž}}", "unable_to_add_album_users": "į„Ąæŗ•å°‡äŊŋį”¨č€…åŠ å…Ĩᛏį°ŋ", "unable_to_add_assets_to_shared_link": "į„Ąæŗ•åŠ å…Ĩé …į›Žåˆ°å…ąäēĢ逪įĩ", "unable_to_add_comment": "į„Ąæŗ•æ–°åĸžį•™č¨€", @@ -873,13 +875,13 @@ "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 {# äŊäēēåŖĢ} other {# äŊäēēåŖĢ}} įš„å¯čĻ‹æ€§", "unable_to_complete_oauth_login": "į„Ąæŗ•åŽŒæˆ OAuth į™ģå…Ĩ", "unable_to_connect": "į„Ąæŗ•é€ŖæŽĨ", - "unable_to_connect_to_server": "į„Ąæŗ•é€ŖæŽĨ到äŧ翜å™¨", "unable_to_copy_to_clipboard": "į„Ąæŗ•č¤‡čŖŊ到å‰Ēč˛ŧæŋīŧŒčĢ‹įĸēäŋæ‚¨äģĨ https å­˜å–čŠ˛é éĸ", "unable_to_create_admin_account": "į„Ąæŗ•åģēįĢ‹įŽĄį†č€…å¸ŗč™Ÿ", "unable_to_create_api_key": "į„Ąæŗ•åģēįĢ‹æ–°įš„ API 金鑰", @@ -903,10 +905,6 @@ "unable_to_hide_person": "į„Ąæŗ•éšąč—äēēį‰Š", "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 į™ģå…Ĩ", @@ -917,11 +915,9 @@ "unable_to_remove_album_users": "į„Ąæŗ•åžžį›¸į°ŋ中į§ģ除äŊŋᔍ者", "unable_to_remove_api_key": "į„Ąæŗ•į§ģ除 API 金鑰", "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": "į„Ąæŗ•č§Ŗæąē重複項", @@ -938,7 +934,7 @@ "unable_to_scan_library": "į„Ąæŗ•æŽƒæåœ–åēĢ", "unable_to_set_feature_photo": "į„Ąæŗ•č¨­įŊŽį‰šč‰˛į…§į‰‡", "unable_to_set_profile_picture": "į„Ąæŗ•č¨­įŊŽå€‹äēē頭像", - "unable_to_submit_job": "į„Ąæŗ•æäē¤äŊœæĨ­", + "unable_to_submit_job": "į„Ąæŗ•æäē¤äģģ務", "unable_to_trash_asset": "į„Ąæŗ•å°‡æĒ”æĄˆä¸Ÿé€˛åžƒåœžæĄļ", "unable_to_unlink_account": "į„Ąæŗ•å°å¸ŗč™Ÿå–æļˆé€ŖæŽĨ", "unable_to_unlink_motion_video": "į„Ąæŗ•å–æļˆéˆįĩå‹•æ…‹åŊąį‰‡", @@ -957,7 +953,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": "åš´éŊĄ {months} 月", "exif_bottom_sheet_person_age_year_months": "1 æ­˛ {months} 個月", "exif_bottom_sheet_person_age_years": "{years} æ­˛", @@ -978,9 +973,10 @@ "external": "外部", "external_libraries": "外部圖åēĢ", "external_network": "外部įļ˛čˇ¯", - "external_network_sheet_info": "č‹Ĩį„Ąæŗ•äŊŋį”¨ååĨŊįš„ Wi-FiīŧŒå°‡äžåˆ—čĄ¨åžžä¸Šåˆ°ä¸‹é¸æ“‡å¯é€Ŗįˇšįš„äŧ翜å™¨įļ˛å€", + "external_network_sheet_info": "č‹ĨæœĒ逪æŽĨ偏åĨŊįš„ Wi-FiīŧŒå°‡äžåˆ—čĄ¨åžžä¸Šåˆ°ä¸‹é¸æ“‡å¯é€Ŗįˇšįš„äŧ翜å™¨įļ˛å€", "face_unassigned": "æœĒ指厚", "failed": "å¤ąæ•—", + "failed_to_authenticate": "čēĢäģŊéŠ—č­‰å¤ąæ•—", "failed_to_load_assets": "į„Ąæŗ•åŠ čŧ‰æĒ”æĄˆ", "failed_to_load_folder": "į„Ąæŗ•čŧ‰å…Ĩčŗ‡æ–™å¤ž", "favorite": "æ”ļ藏", @@ -994,7 +990,7 @@ "file_name_or_extension": "æĒ”名或副æĒ”名", "filename": "æĒ”æĄˆåį¨ą", "filetype": "æĒ”æĄˆéĄžåž‹", - "filter": "į¯Šé¸", + "filter": "æŋžéĄ", "filter_people": "į¯Šé¸äēēį‰Š", "filter_places": "į¯Šé¸åœ°éģž", "find_them_fast": "æœå°‹åį¨ąīŧŒåŋĢ速扞äēē", @@ -1004,6 +1000,7 @@ "folders": "čŗ‡æ–™å¤ž", "folders_feature_description": "äģĨčŗ‡æ–™å¤žį€čĻŊæĒ”æĄˆįŗģįĩąä¸­įš„į…§į‰‡å’ŒåŊąį‰‡", "forward": "順åē", + "gcast_enabled": "Google Cast", "general": "一čˆŦ", "get_help": "įˇšä¸Šæą‚åŠŠ", "get_wifiname_error": "į„Ąæŗ•å–åž— Wi-Fi åį¨ąã€‚čĢ‹įĸēčĒæ‚¨åˇ˛æŽˆäēˆåŋ…čĻįš„æŦŠé™īŧŒä¸Ļ厞逪æŽĨ臺 Wi-Fi įļ˛čˇ¯", @@ -1011,7 +1008,7 @@ "go_back": "čŋ”回", "go_to_folder": "čŊ‰č‡ŗčŗ‡æ–™å¤ž", "go_to_search": "前垀搜尋", - "grant_permission": "į˛åž—æŦŠé™", + "grant_permission": "授ä爿ŦŠé™", "group_albums_by": "åˆ†éĄžįž¤įĩ„įš„æ–šåŧ...", "group_country": "按國åŽļ分įĩ„", "group_no": "į„Ąåˆ†įĩ„", @@ -1034,9 +1031,9 @@ "hide_password": "éšąč—å¯†įĸŧ", "hide_person": "隱藏äēēį‰Š", "hide_unnamed_people": "隱藏æœĒå‘Ŋ名äēēį‰Š", - "home_page_add_to_album_conflicts": "åˇ˛åœ¨į›¸į°ŋ {album} 中新åĸž {added} 項。å…ļ中 {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": "į„Ąæŗ•å°å­˜čĻĒæœ‹åĨŊå‹įš„é …į›ŽīŧŒį•Ĩ過", @@ -1045,11 +1042,14 @@ "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": "時", + "id": "ID", "ignore_icloud_photos": "åŋŊį•ĨiCloudᅧቇ", "ignore_icloud_photos_description": "å­˜å„˛åœ¨iCloudä¸­įš„į…§į‰‡ä¸æœƒä¸Šå‚ŗč‡ŗImmichäŧ翜å™¨", "image": "åœ–į‰‡", @@ -1090,7 +1090,7 @@ "invite_people": "邀čĢ‹äēēå“Ą", "invite_to_album": "邀č̋臺ᛏį°ŋ", "items_count": "{count, plural, other {# å€‹é …į›Ž}}", - "jobs": "äŊœæĨ­", + "jobs": "äģģ務", "keep": "äŋį•™", "keep_all": "全部äŋį•™", "keep_this_delete_others": "äŋį•™é€™å€‹īŧŒåˆĒ除å…ļäģ–", @@ -1122,14 +1122,17 @@ "list": "åˆ—čĄ¨", "loading": "čŧ‰å…Ĩ中", "loading_search_results_failed": "čŧ‰å…Ĩ搜尋įĩæžœå¤ąæ•—", - "local_network_sheet_info": "į•ļäŊŋį”¨æŒ‡åŽšįš„ Wi-Fi įļ˛čˇ¯æ™‚īŧŒæ‡‰į”¨į¨‹åŧå°‡é€éŽæ­¤é€Ŗįĩé€Ŗįˇšč‡ŗäŧ翜å™¨", - "location_permission": "厚äŊæŦŠé™", - "location_permission_content": "į‚ēäē†äŊŋᔍč‡Ē動切換功čƒŊīŧŒImmich 需čĻį˛žįĸēįš„åŽšäŊæŦŠé™īŧŒäģĨäžŋčŽ€å–į›Žå‰æ‰€é€ŖæŽĨįš„ Wi-Fi įļ˛čˇ¯åį¨ą", + "local_network": "æœŦ地įļ˛čˇ¯", + "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": "čĢ‹åœ¨æ­¤č™•čŧ¸å…Ĩæ‚¨įš„įˇ¯åēĻå€ŧ", "location_picker_longitude_error": "čŧ¸å…Ĩæœ‰æ•ˆįš„įļ“åēĻå€ŧ", "location_picker_longitude_hint": "čĢ‹åœ¨æ­¤č™•čŧ¸å…Ĩæ‚¨įš„įļ“åēĻå€ŧ", + "lock": "鎖厚", + "locked_folder": "éŽ–åŽšįš„čŗ‡æ–™å¤ž", "log_out": "į™ģå‡ē", "log_out_all_devices": "į™ģå‡ēæ‰€æœ‰čŖįŊŽ", "logged_out_all_devices": "厞į™ģå‡ēæ‰€æœ‰čŖįŊŽ", @@ -1138,6 +1141,7 @@ "login_disabled": "厞įρᔍį™ģå…Ĩ", "login_form_api_exception": "API ᕰ叏īŧŒčĢ‹æĒĸæŸĨäŧ翜å™¨åœ°å€ä¸Ļ重čŠĻ。", "login_form_back_button_text": "垌退", + "login_form_email_hint": "é›ģ子éƒĩäģļ地址", "login_form_endpoint_hint": "http://æ‚¨įš„äŧ翜å™¨åœ°å€:įĢ¯åŖ", "login_form_endpoint_url": "äŧ翜å™¨éˆæŽĨ地址", "login_form_err_http": "čĢ‹æŗ¨æ˜Ž http:// 或 https://", @@ -1183,8 +1187,8 @@ "map_marker_for_images": "在 {city}、{country} æ‹æ”åœ–åƒįš„åœ°åœ–æ¨™č¨˜", "map_marker_with_image": "å¸ļæœ‰åœ–åƒįš„åœ°åœ–æ¨™č¨˜", "map_no_assets_in_bounds": "æ­¤å€åŸŸä¸­æ˛’æœ‰į›¸é—œé …į›Ž", - "map_no_location_permission_content": "需čρäŊįŊŽæŦŠé™æ‰čƒŊéĄ¯į¤ē與į•ļ前äŊįŊŽį›¸é—œįš„é …į›Žã€‚čĻįžåœ¨å°ąæŽˆäēˆäŊįŊŽæŦŠé™å—ŽīŧŸ", - "map_no_location_permission_title": "äŊįŊŽæŦŠé™čĸĢæ‹’įĩ•", + "map_no_location_permission_content": "需čρäŊįŊŽæŦŠé™æ‰čƒŊéĄ¯į¤ē與į•ļ前äŊįŊŽã€‚čĻįžåœ¨å°ąæŽˆäēˆäŊįŊŽæŦŠé™å—ŽīŧŸ", + "map_no_location_permission_title": "æ˛’æœ‰äŊįŊŽæŦŠé™", "map_settings": "åœ°åœ–č¨­åŽš", "map_settings_dark_mode": "æˇąč‰˛æ¨Ąåŧ", "map_settings_date_range_option_day": "過åŽģ24小時", @@ -1219,10 +1223,15 @@ "merged_people_count": "合äŊĩäē† {count, plural, one {# äŊäēēåŖĢ} other {# äŊäēēåŖĢ}}", "minimize": "最小化", "minute": "分", - "missing": "éēå¤ąįš„", + "missing": "排å…ĨæœĒ處ᐆ", "model": "åž‹č™Ÿ", "month": "月", + "monthly_title_text_date_format": "y MMMM", "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": "åˇ˛ä¸Ÿé€˛åžƒåœžæĄļ", @@ -1240,6 +1249,7 @@ "new_password": "新密įĸŧ", "new_person": "æ–°įš„äēēį‰Š", "new_pin_code": "新 PIN įĸŧ", + "new_pin_code_subtitle": "這是您įŦŦ一æŦĄå­˜å–éŽ–åŽšįš„čŗ‡æ–™å¤žã€‚åģēįĢ‹ PIN įĸŧäģĨ厉全存取此頁éĸ", "new_user_created": "厞åģēįĢ‹æ–°äŊŋᔍ者", "new_version_available": "æ–°į‰ˆæœŦ厞į™ŧ布", "newest_first": "最新å„Ē先", @@ -1252,11 +1262,13 @@ "no_archived_assets_message": "å°‡į…§į‰‡å’ŒåŊąį‰‡å°å­˜īŧŒå°ąä¸æœƒéĄ¯į¤ēåœ¨ã€Œį…§į‰‡ã€ä¸­", "no_assets_message": "æŒ‰é€™čŖĄä¸Šå‚ŗæ‚¨įš„įŦŦ一åŧĩᅧቇ", "no_assets_to_show": "į„Ąé …į›Žåą•į¤ē", + "no_cast_devices_found": "æ˛’æœ‰æ‰žåˆ° Google Cast čŖįŊŽ", "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": "扞不到įŦĻåˆįš„äēēį‰Š", @@ -1268,24 +1280,24 @@ "not_selected": "æœĒ選擇", "note_apply_storage_label_to_previously_uploaded assets": "*č¨ģīŧšåŸˇčĄŒåĨ—į”¨å„˛å­˜æ¨™įą¤å‰å…ˆä¸Šå‚ŗé …į›Ž", "notes": "提į¤ē", - "notification_permission_dialog_content": "čĻå•“į”¨é€šįŸĨīŧŒčĢ‹å‰åž€ã€Œč¨­åŽšã€īŧŒä¸Ļé¸æ“‡ã€Œå…č¨ąã€ã€‚", - "notification_permission_list_tile_content": "授äēˆé€šįŸĨæŦŠé™ã€‚", - "notification_permission_list_tile_enable_button": "å•“į”¨é€šįŸĨ", + "nothing_here_yet": "æšĢį„Ąč¨Šæ¯", + "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 čŗ‡æē", "offline": "é›ĸ᎚", - "offline_paths": "å¤ąæ•ˆčˇ¯åž‘", - "offline_paths_description": "這äē›å¯čƒŊ是手動åˆĒ除非外部圖åēĢįš„æĒ”æĄˆæ™‚æ‰€éēį•™įš„ã€‚", "ok": "įĸē厚", "oldest_first": "į”ąčˆŠč‡ŗæ–°", "on_this_device": "åœ¨æ­¤čŖįŊŽ", "onboarding": "å…Ĩ門指南", + "onboarding_locale_description": "選擇äŊ æƒŗčĻéĄ¯į¤ēįš„čĒžįŗģã€‚č¨­åŽšåŽŒæˆäš‹åžŒį”Ÿæ•ˆã€‚", "onboarding_privacy_description": "äģĨ下īŧˆå¯é¸īŧ‰åŠŸčƒŊäžčŗ´å¤–éƒ¨æœå‹™īŧŒå¯éš¨æ™‚åœ¨įŽĄį†č¨­åŽšä¸­åœį”¨ã€‚", "onboarding_theme_description": "åšĢå¯Ļ例選色åŊŠä¸ģéĄŒã€‚äš‹åžŒäšŸå¯äģĨåœ¨č¨­åŽšä¸­æ›´æ”šã€‚", - "onboarding_welcome_description": "åœ¨å•Ÿį”¨å¯Ļ䞋前先做一äē›åŸēæœŦįš„č¨­åŽšã€‚", "onboarding_welcome_user": "æ­ĄčŋŽīŧŒ{user}", "online": "圍᎚", "only_favorites": "åƒ…éĄ¯į¤ēåˇąæ”ļ藏", @@ -1342,14 +1354,16 @@ "permanently_delete_assets_prompt": "įĸē厚čĻæ°¸äš…åˆĒ除 {count, plural, other {這 # 個æĒ”æĄˆīŧŸ}}é€™æ¨Ŗ{count, plural, one {厃} other {厃們}}䚟會垞č‡Ēåˇąæ‰€åœ¨įš„į›¸į°ŋ中æļˆå¤ąã€‚", "permanently_deleted_asset": "永䚅åˆĒé™¤įš„æĒ”æĄˆ", "permanently_deleted_assets_count": "永䚅åˆĒé™¤įš„ {count, plural, one {# 個æĒ”æĄˆ} other {# 個æĒ”æĄˆ}}", + "permission": "æŦŠé™", + "permission_empty": "äŊ æ˛’有æŦŠé™", "permission_onboarding_back": "čŋ”回", - "permission_onboarding_continue_anyway": "äģį„ļįšŧįēŒ", + "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 備äģŊå’ŒįŽĄį†æ‚¨įš„æ•´å€‹åœ–åēĢæ”ļ藏īŧŒčĢ‹åœ¨ã€Œč¨­åŽšã€ä¸­æŽˆäēˆį…§į‰‡å’ŒįŸ­į‰‡æŦŠé™ã€‚", - "permission_onboarding_request": "Immich 需čρæŦŠé™æ‰čƒŊæŸĨįœ‹æ‚¨įš„į…§į‰‡å’ŒįŸ­į‰‡ã€‚", + "permission_onboarding_permission_denied": "åĻ‚čρįšŧįēŒīŧŒčĢ‹å…č¨ą Immich å­˜å–į›¸į‰‡å’ŒåŊąį‰‡æŦŠé™ã€‚", + "permission_onboarding_permission_granted": "åˇ˛å…č¨ąīŧä¸€åˆ‡å°ąįˇ’。", + "permission_onboarding_permission_limited": "åĻ‚čρįšŧįēŒīŧŒčĢ‹å…č¨ą Immich 備äģŊå’ŒįŽĄį†æ‚¨įš„į›¸į°ŋæ”ļ藏īŧŒåœ¨č¨­åŽšä¸­æŽˆäēˆį›¸į‰‡å’ŒåŊąį‰‡æŦŠé™ã€‚", + "permission_onboarding_request": "Immich 需čρæŦŠé™æ‰čƒŊæŸĨįœ‹æ‚¨įš„į›¸į‰‡å’ŒįŸ­į‰‡ã€‚", "person": "äēēį‰Š", "person_birthdate": "į”Ÿæ–ŧ {date}", "person_hidden": "{name}{hidden, select, true {īŧˆéšąč—īŧ‰} other {}}", @@ -1362,6 +1376,7 @@ "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} 個地éģž}}", @@ -1369,6 +1384,7 @@ "play_memories": "播攞回æ†ļ", "play_motion_photo": "æ’­æ”žå‹•æ…‹į…§į‰‡", "play_or_pause_video": "播攞或æšĢ停åŊąį‰‡", + "please_auth_to_access": "čĢ‹é€˛čĄŒčēĢäģŊéŠ—č­‰æ‰čƒŊ存取", "port": "åŸ åŖ", "preferences_settings_subtitle": "įŽĄį†æ‡‰į”¨į¨‹åŧååĨŊč¨­åŽš", "preferences_settings_title": "偏åĨŊč¨­åŽš", @@ -1379,10 +1395,12 @@ "previous_or_next_photo": "上、下一åŧĩᅧቇ", "primary": "éĻ–čρ", "privacy": "éšąį§", + "profile": "叺æˆļč¨­åŽš", "profile_drawer_app_logs": "æ—Ĩčnj", "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} įš„å€‹äēēčŗ‡æ–™åœ–į‰‡", @@ -1408,7 +1426,7 @@ "purchase_license_subtitle": "čŗŧįŊŽ Immich 䞆支援čģŸé̔開į™ŧ", "purchase_lifetime_description": "įĩ‚čēĢčŗŧįŊŽ", "purchase_option_title": "čŗŧįŊŽé¸é …", - "purchase_panel_info_1": "開į™ŧ Immich 可不是äģļåŽšæ˜“įš„äē‹īŧŒčŠąä熿ˆ‘們不少功å¤Ģ。åĨŊåœ¨æœ‰ä¸€įž¤å…¨čˇåˇĨፋå¸Ģåœ¨čƒŒåžŒéģ˜éģ˜åŠĒ力īŧŒį‚ēįš„å°ąæ˜¯æŠŠåŽƒåšåˆ°æœ€åĨŊã€‚æˆ‘å€‘įš„į›Žæ¨™åžˆį°Ąå–Žīŧščޓ開æēčģŸéĢ”å’Œæ­Ŗį•ļįš„å•†æĨ­æ¨ĄåŧčƒŊ成į‚ē開į™ŧč€…įš„é•ˇæœŸéŖ¯įĸ—īŧŒåŒæ™‚打造å‡ē重čĻ–éšąį§įš„į”Ÿæ…‹įŗģįĩąīŧŒčޓ大åŽļ有個不čĸĢå‰å‰Šįš„é›˛įĢ¯æœå‹™æ–°é¸æ“‡ã€‚", + "purchase_panel_info_1": "開į™ŧ Immich 可不是äģļåŽšæ˜“įš„äē‹īŧŒčŠąä熿ˆ‘們不少功å¤Ģ。åĨŊåœ¨æœ‰ä¸€įž¤å…¨čˇåˇĨፋå¸Ģåœ¨čƒŒåžŒéģ˜éģ˜åŠĒ力īŧŒį‚ēįš„å°ąæ˜¯æŠŠåŽƒåšåˆ°æœ€åĨŊã€‚æˆ‘å€‘įš„į›Žæ¨™åžˆį°Ąå–ŽīŧščŽ“é–‹æ”žåŽŸå§‹įĸŧčģŸéĢ”å’Œæ­Ŗį•ļįš„å•†æĨ­æ¨ĄåŧčƒŊ成į‚ē開į™ŧč€…įš„é•ˇæœŸéŖ¯įĸ—īŧŒåŒæ™‚打造å‡ē重čĻ–éšąį§įš„į”Ÿæ…‹įŗģįĩąīŧŒčޓ大åŽļ有個不čĸĢ限åˆļįš„é›˛įĢ¯æœå‹™æ–°é¸æ“‡ã€‚", "purchase_panel_info_2": "我們æ‰ŋčĢžä¸č¨­äģ˜č˛ģቆīŧŒæ‰€äģĨčŗŧįŊŽ Immich ä¸Ļä¸æœƒčŽ“æ‚¨į˛åž—éĄå¤–įš„åŠŸčƒŊã€‚æˆ‘å€‘æ˜¯äžčŗ´äŊŋį”¨č€…å€‘įš„æ”¯æ´äž†é–‹į™ŧ Immich įš„ã€‚", "purchase_panel_title": "æ”¯æ´é€™é …å°ˆæĄˆ", "purchase_per_server": "每č‡ēäŧ翜å™¨", @@ -1457,9 +1475,12 @@ "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": "å°‡åœ–į‰‡åžžæ­¤č¨˜æ†ļ中į§ģ除", + "remove_tag": "į§ģé™¤æ¨™įą¤", "remove_url": "į§ģ除 URL", "remove_user": "į§ģ除ᔍæˆļ", "removed_api_key": "厞į§ģ除 API 金鑰īŧš{name}", @@ -1471,7 +1492,7 @@ "removed_tagged_assets": "厞į§ģ除 {count, plural, other {# 個æĒ”æĄˆ}}įš„æ¨™č¨˜", "rename": "攚名", "repair": "įŗžæ­Ŗ", - "repair_no_results_message": "æœĒčĸĢčŋŊčš¤åŠéēå¤ąįš„æĒ”æĄˆæœƒéĄ¯į¤ēåœ¨é€™čŖĄ", + "repair_no_results_message": "æœĒčĸĢčŋŊčš¤åŠæœĒč™•į†įš„æĒ”æĄˆæœƒéĄ¯į¤ēåœ¨é€™čŖĄ", "replace_with_upload": "į”¨ä¸Šå‚ŗįš„æĒ”æĄˆå–äģŖ", "repository": "å„˛å­˜åēĢ", "require_password": "需čρ坆įĸŧ", @@ -1541,7 +1562,7 @@ "search_page_no_objects": "æ‰žä¸åˆ°į‰Šäģļčŗ‡č¨Š", "search_page_no_places": "扞不到地éģžčŗ‡č¨Š", "search_page_screenshots": "åąåš•æˆĒ圖", - "search_page_search_photos_videos": "æœå°‹æ‚¨įš„į…§į‰‡å’ŒåŊąį‰‡", + "search_page_search_photos_videos": "æœå°‹æ‚¨įš„į…§į‰‡čˆ‡åŊąį‰‡", "search_page_selfies": "č‡Ē拍", "search_page_things": "äē‹į‰Š", "search_page_view_all_button": "æŸĨįœ‹å…¨éƒ¨", @@ -1553,7 +1574,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": "搜尋時區â€Ļ", @@ -1602,8 +1623,7 @@ "setting_image_viewer_preview_title": "čŧ‰å…Ĩ預čĻŊ圖", "setting_image_viewer_title": "åœ–į‰‡", "setting_languages_apply": "åĨ—ᔍ", - "setting_languages_subtitle": "čŽŠæ›´æ‡‰į”¨į¨‹åŧčĒžč¨€", - "setting_languages_title": "čĒžč¨€", + "setting_languages_subtitle": "čŽŠæ›´æ‡‰į”¨į¨‹åŧįš„čĒžč¨€", "setting_notifications_notify_failures_grace_period": "čƒŒæ™¯å‚™äģŊå¤ąæ•—é€šįŸĨīŧš{duration}", "setting_notifications_notify_hours": "{count} 小時", "setting_notifications_notify_immediately": "įĢ‹åŗ", @@ -1616,7 +1636,7 @@ "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": "čĢ‹é‡å•“ Immich äģĨäŊŋč¨­åŽšį”Ÿæ•ˆ", @@ -1626,6 +1646,7 @@ "share_add_photos": "新åĸžé …į›Ž", "share_assets_selected": "{count} åˇ˛é¸æ“‡", "share_dialog_preparing": "æ­Ŗåœ¨æē–å‚™...", + "share_link": "分äēĢ逪įĩ", "shared": "å…ąäēĢ", "shared_album_activities_input_disable": "厞įĻį”¨čŠ•čĢ–", "shared_album_activity_remove_content": "您įĸē厚čρåˆĒ除此æ´ģ動嗎īŧŸ", @@ -1665,6 +1686,7 @@ "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": "å…ąäēĢ逪įĩ", @@ -1788,7 +1810,6 @@ "to_parent": "åˆ°ä¸Šä¸€į´š", "to_trash": "垃圞æĄļ", "toggle_settings": "åˆ‡æ›č¨­åŽš", - "toggle_theme": "åˆ‡æ›æˇąč‰˛ä¸ģ題", "total": "įĩąč¨ˆ", "total_usage": "į¸Ŋį”¨é‡", "trash": "垃圞æĄļ", @@ -1810,6 +1831,7 @@ "unable_to_setup_pin_code": "į„Ąæŗ•č¨­åŽš PIN įĸŧ", "unarchive": "取æļˆå°å­˜", "unarchived_count": "{count, plural, other {åˇ˛å–æļˆå°å­˜ # å€‹é …į›Ž}}", + "undo": "還原", "unfavorite": "取æļˆæ”ļ藏", "unhide_person": "取æļˆéšąč—äēēį‰Š", "unknown": "æœĒįŸĨ", @@ -1828,9 +1850,8 @@ "unselect_all_duplicates": "取æļˆé¸å–æ‰€æœ‰įš„é‡č¤‡é …į›Ž", "unstack": "取æļˆå †å ", "unstacked_assets_count": "åˇ˛č§Ŗé™¤å †į–Š {count, plural, other {# 個æĒ”æĄˆ}}", - "untracked_files": "æœĒčĸĢčŋŊčš¤įš„æĒ”æĄˆ", - "untracked_files_decription": "這äē›æĒ”æĄˆä¸æœƒčĸĢčŋŊčš¤ã€‚åŽƒå€‘å¯čƒŊ是į§ģå‹•å¤ąčĒ¤ã€ä¸Šå‚ŗä¸­æ–ˇæˆ–é‡åˆ°æŧæ´žč€Œéēį•™įš„į”ĸį‰Š", "up_next": "下一個", + "updated_at": "更新æ–ŧ", "updated_password": "åˇ˛æ›´æ–°å¯†įĸŧ", "upload": "ä¸Šå‚ŗ", "upload_concurrency": "ä¸Šå‚ŗä¸Ļ行", @@ -1847,6 +1868,7 @@ "uploading": "ä¸Šå‚ŗä¸­", "url": "įļ˛å€", "usage": "į”¨é‡", + "use_biometric": "äŊŋį”¨į”Ÿį‰Ščž¨č­˜", "use_current_connection": "äŊŋį”¨į›Žå‰įš„é€Ŗįˇš", "use_custom_date_range": "æ”šį”¨č‡Ē訂æ—ĨæœŸį¯„åœ", "user": "äŊŋᔍ者", @@ -1862,7 +1884,7 @@ "user_usage_stats": "å¸ŗč™ŸäŊŋį”¨é‡įĩąč¨ˆ", "user_usage_stats_description": "æŸĨįœ‹å¸ŗč™ŸäŊŋį”¨é‡", "username": "äŊŋį”¨č€…åį¨ą", - "users": "äŊŋᔍ者", + "users": "admin", "utilities": "åˇĨå…ˇ", "validate": "驗證", "validate_endpoint_error": "čĢ‹čŧ¸å…Ĩæœ‰æ•ˆįš„é€Ŗįĩ", @@ -1870,11 +1892,6 @@ "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_title": "服務įĢ¯æœ‰æ–°į‰ˆæœŦå•Ļ 🎉", "version_history": "į‰ˆæœŦį´€éŒ„", "version_history_item": "{date} åŽ‰čŖäē† {version}", "video": "åŊąį‰‡", @@ -1894,6 +1911,7 @@ "view_previous_asset": "æŸĨįœ‹ä¸Šä¸€é …", "view_qr_code": "æŸĨįœ‹ QR code", "view_stack": "æŸĨįœ‹å †į–Š", + "view_user": "éĄ¯į¤ēäŊŋᔍ者", "viewer_remove_from_stack": "åžžå †į–Šä¸­į§ģ除", "viewer_stack_use_as_main_asset": "äŊœį‚ēä¸ģé …į›ŽäŊŋᔍ", "viewer_unstack": "取æļˆå †į–Š", @@ -1904,6 +1922,7 @@ "welcome": "æ­ĄčŋŽ", "welcome_to_immich": "æ­ĄčŋŽäŊŋᔍ Immich", "wifi_name": "Wi-Fi åį¨ą", + "wrong_pin_code": "PIN įĸŧ錯čǤ", "year": "åš´", "years_ago": "{years, plural, other {# åš´}}前", "yes": "是", diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index 1ba5b98e95..17c6954ab7 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -22,11 +22,11 @@ "add_partner": "æˇģ加同äŧ´", "add_path": "æˇģåŠ čˇ¯åž„", "add_photos": "æˇģåŠ į…§į‰‡", + "add_tag": "æˇģåŠ æ ‡į­ž", "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": "æˇģ加到åŊ’æĄŖ", @@ -34,6 +34,7 @@ "added_to_favorites_count": "æˇģ加{count, number}éĄšåˆ°æ”ļ藏", "admin": { "add_exclusion_pattern_description": "æˇģåŠ æŽ’é™¤č§„åˆ™ã€‚æ”¯æŒäŊŋᔍ *、** 和 ? 通配įŦĻ。比åĻ‚čρåŋŊį•ĨäģģäŊ•名ä¸ē “Raw” įš„æ–‡äģļå¤šä¸­įš„æ‰€æœ‰æ–‡äģļīŧŒč¯ˇäŊŋᔍ “**/Raw/**”īŧ›čρåŋŊį•Ĩ所有äģĨ “.tif” įģ“å°žįš„æ–‡äģļīŧŒč¯ˇäŊŋᔍ “**/*.tif”īŧ›čρåŋŊį•Ĩįģå¯ščˇ¯åž„īŧŒč¯ˇäŊŋᔍ “/path/to/ignore/**”。", + "admin_user": "įŽĄį†å‘˜į”¨æˆˇ", "asset_offline_description": "įŖį›˜ä¸Šåˇ˛æ‰žä¸åˆ°æ­¤å¤–éƒ¨åē“éĄšį›ŽīŧŒåˇ˛å°†å…ļį§ģč‡ŗå›žæ”ļįĢ™ã€‚åĻ‚æžœæ–‡äģļ厞圍åē“中į§ģ动īŧŒč¯ˇæŖ€æŸĨæ—ļ间įēŋ中是åĻ有寚åē”éĄšį›Žã€‚čρæĸå¤æ­¤éĄšį›ŽīŧŒč¯ˇįĄŽäŋ Immich 可äģĨčŽŋ问äģĨ下文äģļčˇ¯åž„åšļæ‰§čĄŒâ€œæ‰Ģ描åē“”äģģåŠĄã€‚", "authentication_settings": "čŽ¤č¯čŽžįŊŽ", "authentication_settings_description": "įŽĄį†å¯†į ã€OAuth 和å…ļåŽƒčŽ¤č¯čŽžįŊŽ", @@ -44,9 +45,7 @@ "backup_database_enable_description": "å¯į”¨æ•°æŽåē“å¯ŧå‡ē备äģŊ", "backup_keep_last_amount": "čρäŋį•™įš„åŽ†å˛å¯ŧå‡ē数量", "backup_settings": "数捎åē“å¯ŧå‡ē莞įŊŽ", - "backup_settings_description": "įŽĄį†æ•°æŽåē“备äģŊ莞įŊŽã€‚æŗ¨æ„īŧščŋ™äē›äģģåŠĄä¸äŧščĸĢį›‘æŽ§īŧŒå¤ąč´Ĩ也不äŧšé€šįŸĨ您。", - "check_all": "æŖ€æŸĨ全部", - "cleanup": "æ¸…į†", + "backup_settings_description": "įŽĄį†æ•°æŽåē“备äģŊ莞įŊŽã€‚", "cleared_jobs": "åˇ˛æ¸…į†äģģåŠĄīŧš{job}", "config_set_by_file": "åŊ“前配įŊŽåˇ˛é€ščŋ‡é…įŊŽæ–‡äģļ莞įŊŽ", "confirm_delete_library": "įĄŽåŽščĻåˆ é™¤å›žåē““{library}”吗īŧŸ", @@ -62,14 +61,12 @@ "disable_login": "įρᔍį™ģåŊ•", "duplicate_detection_job_description": "å¯šį…§į‰‡čŋ›čĄŒæœē器å­Ļäš å¤„į†æĨæŖ€æĩ‹į›¸äŧŧéĄšį›ŽīŧŒäžčĩ–äēŽæ™ēčƒŊ搜į´ĸ", "exclusion_pattern_description": "æŽ’é™¤č§„åˆ™å…čŽ¸åœ¨æ‰Ģ描回å瓿—ļåŋŊį•Ĩ文äģļ和文äģļ多。åĻ‚æžœæœ‰åŒ…åĢä¸æƒŗå¯ŧå…Ĩįš„æ–‡äģļįš„æ–‡äģļ多īŧŒäž‹åĻ‚ RAW 文äģļīŧŒæŽ’é™¤č§„åˆ™å°†éžå¸¸æœ‰į”¨ã€‚", - "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": "åŽģé™¤å…ƒæ•°æŽįš„å…¨å°ē寸回像īŧŒæ”žå¤§æ—ļäŊŋᔍ", @@ -174,7 +171,7 @@ "note_apply_storage_label_previous_assets": "提į¤ēīŧščĻå°†å­˜å‚¨æ ‡į­žåē”ᔍäēŽäš‹å‰ä¸Šäŧ įš„éĄšį›ŽīŧŒéœ€čρčŋčĄŒ", "note_cannot_be_changed_later": "æŗ¨æ„īŧšæ­¤éĄšä¸€æ—ĻčŽžåŽšīŧŒäģĨåŽæ— æŗ•æ›´æ”šīŧ", "notification_email_from_address": "发äģļäēē地址", - "notification_email_from_address_description": "发äģļäēēé‚ŽįŽąīŧŒäž‹åĻ‚īŧšâ€œåŧ ä¸‰<12345@qq.com>”", + "notification_email_from_address_description": "发äģļäēēé‚ŽįŽąīŧŒäž‹åĻ‚īŧšâ€œåŧ ä¸‰<12345@qq.com>â€ã€‚č¯ˇįĄŽäŋäŊŋį”¨å…čŽ¸æ‚¨å‘é€į”ĩ子邎äģļįš„åœ°å€ã€‚", "notification_email_host_description": "æœåŠĄå™¨åœ°å€īŧˆäž‹åĻ‚īŧšsmtp.qq.comīŧ‰", "notification_email_ignore_certificate_errors": "åŋŊį•Ĩ蝁äšĻ错蝝", "notification_email_ignore_certificate_errors_description": "åŋŊį•Ĩ TLS 蝁äšĻéĒŒč¯é”™č¯¯īŧˆä¸åģē莎īŧ‰", @@ -198,7 +195,7 @@ "oauth_enable_description": "äŊŋᔍ OAuth į™ģåŊ•", "oauth_mobile_redirect_uri": "į§ģ动įĢ¯é‡åŽšå‘ URI", "oauth_mobile_redirect_uri_override": "į§ģ动įĢ¯é‡åŽšå‘ URI čφᛖ", - "oauth_mobile_redirect_uri_override_description": "åŊ“ OAuth æäž›å•†ä¸å…čŽ¸äŊŋᔍį§ģ动 URI æ—ļ吝ᔍīŧŒåĻ‚â€œ'{callback}'”", + "oauth_mobile_redirect_uri_override_description": "åŊ“ OAuth æäž›å•†ä¸å…čŽ¸äŊŋᔍį§ģ动 URI æ—ļ吝ᔍīŧŒåĻ‚â€œ''{callback}''”", "oauth_settings": "OAuth", "oauth_settings_description": "įŽĄį† OAuth į™ģåŊ•莞įŊŽ", "oauth_settings_more_details": "å…ŗäēŽæ­¤åŠŸčƒŊįš„æ›´å¤šč¯Ļįģ†äŋĄæ¯īŧŒč¯ˇæŸĨįœ‹į›¸å…ŗæ–‡æĄŖã€‚", @@ -207,11 +204,9 @@ "oauth_storage_quota_claim": "存储配éĸåŖ°æ˜Ž", "oauth_storage_quota_claim_description": "č‡ĒåŠ¨å°†į”¨æˆˇįš„å­˜å‚¨é…éĸčŽžįŊŽä¸ēæ­¤éĄšįš„å€ŧ。", "oauth_storage_quota_default": "éģ˜čŽ¤å­˜å‚¨é…éĸīŧˆGBīŧ‰", - "oauth_storage_quota_default_description": "æœĒæäž›åŖ°æ˜Žæ—ļäŊŋį”¨įš„é…éĸīŧˆGBīŧ‰īŧˆ0襨į¤ē无限åˆļīŧ‰ã€‚", + "oauth_storage_quota_default_description": "æœĒæäž›åŖ°æ˜Žæ—ļäŊŋį”¨įš„é…éĸīŧˆGBīŧ‰ã€‚", "oauth_timeout": "č¯ˇæą‚čļ…æ—ļ", "oauth_timeout_description": "č¯ˇæą‚čļ…æ—ļīŧˆæ¯Ģį§’īŧ‰", - "offline_paths": "įĻģįēŋ文äģļ", - "offline_paths_description": "čŋ™å¯čƒŊæ˜¯į”ąäēŽæ‰‹åŠ¨åˆ é™¤äē†ä¸åąžäēŽå¤–部回åē“įš„æ–‡äģļ。", "password_enable_description": "äŊŋį”¨é‚ŽįŽąå’Œå¯†į į™ģåŊ•", "password_settings": "坆᠁į™ģåŊ•", "password_settings_description": "įŽĄį†å¯†į į™ģåŊ•莞įŊŽ", @@ -221,9 +216,6 @@ "refreshing_all_libraries": "åˆˇæ–°æ‰€æœ‰å›žåē“", "registration": "æŗ¨å†ŒįŽĄį†å‘˜", "registration_description": "į”ąäēŽæ‚¨æ˜¯įŗģįģŸä¸Šįš„įŦŦ一ä¸Ēį”¨æˆˇīŧŒæ‚¨å°†čĸĢæŒ‡åޚä¸ēįŽĄį†å‘˜åšļč´Ÿč´ŖįŽĄį†äģģåŠĄīŧŒį”ąæ‚¨æĨ创åģēæ–°įš„į”¨æˆˇã€‚", - "repair_all": "äŋŽå¤æ‰€æœ‰", - "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": "æĸ复到最čŋ‘äŋå­˜įš„莞įŊŽ", @@ -252,7 +244,7 @@ "storage_template_migration_info": "å­˜å‚¨æ¨Ąæŋäŧšå°†æ‰€æœ‰æ‰Šåą•名čŊŦæĸä¸ēå°å†™ã€‚æ¨ĄæŋäŋŽæ”šåĒäŧšäŊœį”¨äēŽæ–°įš„éĄšį›ŽīŧŒåĻ‚éœ€åē”į”¨æ­¤æ¨Ąæŋ到䚋前上äŧ įš„éĄšį›ŽīŧŒč¯ˇčŋčĄŒ{job}。", "storage_template_migration_job": "å­˜å‚¨æ¨ĄæŋčŊŦæĸäģģåŠĄ", "storage_template_more_details": "å…ŗäēŽæœŦ功čƒŊįš„æ›´å¤šįģ†čŠ‚īŧŒč¯ˇå‚č§å­˜å‚¨æ¨Ąæŋ及å…ļåŽžįŽ°æ–šåŧ", - "storage_template_onboarding_description": "å¯į”¨åŽīŧŒæœŦ功čƒŊå°†æ šæŽį”¨æˆˇåŽšäš‰įš„æ¨Ąæŋč‡ĒåŠ¨æ•´į†æ–‡äģļ。å‡ēäēŽį¨ŗåŽšæ€§č€ƒč™‘īŧŒæœŦ功čƒŊéģ˜čŽ¤æ˜¯įĻį”¨įš„ã€‚æ›´å¤šč¯Ļįģ†äŋĄæ¯č¯ˇå‚见 æ–‡æĄŖã€‚", + "storage_template_onboarding_description_v2": "å¯į”¨åŽīŧŒč¯Ĩ功čƒŊå°†æ šæŽį”¨æˆˇåŽšäš‰įš„æ¨Ąæŋč‡ĒåŠ¨æ•´į†æ–‡äģļã€‚æœ‰å…ŗč¯Ļįģ†äŋĄæ¯īŧŒč¯ˇå‚阅 æ–‡æĄŖã€‚", "storage_template_path_length": "čˇ¯åž„įš„å­—įŦĻé•ŋåēĻ及限åˆļīŧš{length, number}/{limit, number}", "storage_template_settings": "å­˜å‚¨æ¨Ąæŋ", "storage_template_settings_description": "įŽĄį†ä¸Šäŧ éĄšį›Žæ–‡äģļ多į쓿ž„和文äģļ名", @@ -264,16 +256,14 @@ "template_email_invite_album": "į›¸å†Œé‚€č¯ˇæ¨Ąæŋ", "template_email_preview": "éĸ„č§ˆ", "template_email_settings": "邮äģᅪĄæŋ", - "template_email_settings_description": "įŽĄį†č‡Ē厚䚉邎äģļ通įŸĨæ¨Ąæŋ", "template_email_update_album": "į›¸å†Œæ›´æ–°æ¨Ąæŋ", "template_email_welcome": "æŦĸčŋŽé‚ŽäģᅪĄæŋ", "template_settings": "通įŸĨæ¨Ąæŋ", - "template_settings_description": "įŽĄį†č‡Ē厚䚉通įŸĨæ¨Ąæŋ。", + "template_settings_description": "įŽĄį†č‡Ē厚䚉通įŸĨæ¨Ąæŋ", "theme_custom_css_settings": "č‡Ē厚䚉 CSS", "theme_custom_css_settings_description": "可äģĨ通čŋ‡ CSS č‡Ē厚䚉 Immich å¤–č§‚ã€‚", "theme_settings": "ä¸ģéĸ˜čŽžįŊŽ", "theme_settings_description": "č‡Ē厚䚉 Immich Web į•Œéĸ", - "these_files_matched_by_checksum": "čŋ™ä盿–‡äģļä¸Žæ ĄéĒŒåŒšé…", "thumbnail_generation_job": "į”ŸæˆįŧŠį•Ĩ回", "thumbnail_generation_job_description": "ä¸ē每ä¸ĒéĄšį›Žį”Ÿæˆä¸åŒå°ēå¯¸įš„įŧŠį•Ĩ回īŧŒåšļä¸ē每ä¸Ēäēēį‰Šį”ŸæˆįŧŠį•Ĩ回", "transcoding_acceleration_api": "加速器 API", @@ -301,10 +291,9 @@ "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": "HEVC įŧ–觪᠁噍", "transcoding_max_b_frames": "最大B帧数", "transcoding_max_b_frames_description": "螃éĢ˜įš„å€ŧ可äģĨ提éĢ˜åŽ‹įŧŠæ•ˆįއīŧŒäŊ†äŧšå‡æ…ĸįŧ–į é€ŸåēĻ。可čƒŊä¸Žæ—§čŽžå¤‡ä¸Šįš„įĄŦäģļ加速不å…ŧ厚。0襨į¤ē将įρᔍB帧īŧŒ-1襨į¤ē将č‡ĒåŠ¨čŽžįŊŽæ­¤å‚数。", "transcoding_max_bitrate": "最éĢ˜į įŽ‡", @@ -342,8 +331,6 @@ "trash_number_of_days_description": "čĸĢæ°¸äš…删除䚋前īŧŒéĄšį›Žåœ¨å›žæ”ļį̙䏭äŋį•™įš„夊数", "trash_settings": "回æ”ļįĢ™čŽžįŊŽ", "trash_settings_description": "įŽĄį†å›žæ”ļįĢ™čŽžįŊŽ", - "untracked_files": "æœĒčĸĢæ‰Ģæįš„æ–‡äģļ", - "untracked_files_description": "čŋ™ä盿–‡äģᅵĒčĸĢįŗģį쟿‰Ģ描。 čŋ™å¯čƒŊ是į§ģåŠ¨å¤ąč´Ĩ、上äŧ ä¸­æ–­æˆ–å› bug而čŊ下", "user_cleanup_job": "æ¸…į†į”¨æˆˇ", "user_delete_delay": "{user}įš„č´ĻæˆˇåŠéĄšį›Žå°†åœ¨{delay, plural, one {#夊} other {#夊}}后č‡Ē动永䚅删除。", "user_delete_delay_settings": "åģᅵŸåˆ é™¤", @@ -402,10 +389,6 @@ "album_remove_user": "į§ģé™¤į”¨æˆˇīŧŸ", "album_remove_user_confirmation": "įĄŽåŽščρį§ģ除“{user}”吗īŧŸ", "album_share_no_users": "įœ‹čĩˇæĨæ‚¨åˇ˛ä¸Žæ‰€æœ‰į”¨æˆˇå…ąäēĢä熿­¤į›¸å†ŒīŧŒæˆ–č€…æ‚¨æ šæœŦæ˛Ąæœ‰äģģäŊ•į”¨æˆˇå¯å…ąäēĢ。", - "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}”", @@ -421,6 +404,9 @@ "album_with_link_access": "æ‹Ĩ有此铞æŽĨįš„äģģäŊ•äēē均可æŸĨįœ‹æœŦį›¸å†Œä¸­įš„į…§į‰‡å’Œäēēį‰Šã€‚", "albums": "į›¸å†Œ", "albums_count": "{count, plural, one {{count, number} ä¸Ēį›¸å†Œ} other {{count, number} ä¸Ēį›¸å†Œ}}", + "albums_default_sort_order": "éģ˜čŽ¤į›¸å†ŒæŽ’åēæ–šåŧ", + "albums_default_sort_order_description": "创åģēæ–°į›¸å†Œæ—ļįš„éĄšį›Žåˆå§‹æŽ’åēæ–šåŧã€‚", + "albums_feature_description": "可与å…ļäģ–į”¨æˆˇå…ąäēĢįš„éĄšį›Žæ”ļč—ã€‚", "all": "全部", "all_albums": "æ‰€æœ‰į›¸å†Œ", "all_people": "全部äēēį‰Š", @@ -433,7 +419,7 @@ "anti_clockwise": "逆æ—ļ针", "api_key": "API 密é’Ĩ", "api_key_description": "č¯Ĩåē”ᔍ坆é’ĨåĒäŧšæ˜žį¤ē一æŦĄã€‚č¯ˇįĄŽäŋåœ¨å…ŗé—­įĒ—åŖå‰å¤åˆļ下æĨ。", - "api_key_empty": "API Key įš„åį§°ä¸å¯äģĨä¸ēįŠē", + "api_key_empty": "API 密é’Ĩįš„åį§°ä¸å¯äģĨä¸ēįŠē", "api_keys": "API 密é’Ĩ", "app_bar_signout_dialog_content": "æ‚¨įĄŽåŽščρ退å‡ē吗īŧŸ", "app_bar_signout_dialog_ok": "是", @@ -479,9 +465,12 @@ "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_cannot_be_added_to_album_count": "æ— æŗ•æˇģ加 {count, plural, one {ä¸ĒéĄšį›Ž} other {ä¸ĒéĄšį›Ž}} åˆ°į›¸å†Œä¸­", "assets_count": "{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}", "assets_deleted_permanently": "{count} ä¸ĒéĄšį›Žåˇ˛čĸĢæ°¸äš…删除", "assets_deleted_permanently_from_server": "åˇ˛æ°¸äš…į§ģ除 {count} ä¸ĒéĄšį›Ž", + "assets_downloaded_failed": "{count, plural, one {厞䏋čŊŊ#ä¸Ē文äģļ - {error} 文äģļå¤ąč´Ĩ} other {厞䏋čŊŊ#ä¸Ē文äģļ - {error} ä¸Ē文äģļå¤ąč´Ĩ}}", + "assets_downloaded_successfully": "{count, plural, one {åˇ˛æˆåŠŸä¸‹čŊŊäē† # ä¸Ē文äģļ} other {åˇ˛æˆåŠŸä¸‹čŊŊäē† # ä¸Ē文äģļ}}", "assets_moved_to_trash_count": "厞į§ģ动{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}到回æ”ļįĢ™", "assets_permanently_deleted_count": "åˇ˛æ°¸äš…åˆ é™¤{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}", "assets_removed_count": "厞į§ģ除{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}", @@ -492,10 +481,11 @@ "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 {éĄšį›Ž}}厞įģåœ¨į›¸å†Œä¸­", + "assets_were_part_of_album_count": "{count, plural, one {ä¸ĒéĄšį›Ž} other {ä¸ĒéĄšį›Ž}}厞įģåœ¨į›¸å†Œä¸­", "authorized_devices": "åˇ˛æŽˆæƒčŽžå¤‡", "automatic_endpoint_switching_subtitle": "åŊ“čŋžæŽĨåˆ°æŒ‡åŽšįš„ Wi-Fi æ—ļäŊŋᔍæœŦ地čŋžæŽĨīŧŒåœ¨å…ļåŽƒįŽ¯åĸƒä¸‹äŊŋᔍæ›ŋäģŖčŋžæŽĨ", "automatic_endpoint_switching_title": "č‡Ē动切æĸ URL", + "autoplay_slideshow": "č‡Ē动播攞åšģၝቇ", "back": "čŋ”回", "back_close_deselect": "čŋ”å›žã€å…ŗé—­æˆ–åé€‰", "background_location_permission": "后台厚äŊæƒé™", @@ -577,21 +567,17 @@ "bulk_keep_duplicates_confirmation": "æ‚¨įĄŽåŽščρäŋį•™{count, plural, one {#ä¸Ēé‡å¤éĄšį›Ž} other {#ä¸Ēé‡å¤éĄšį›Ž}}吗īŧŸčŋ™å°†æ¸…įŠēæ‰€æœ‰é‡å¤čŽ°åŊ•īŧŒäŊ†ä¸äŧšåˆ é™¤äģģäŊ•内厚。", "bulk_trash_duplicates_confirmation": "æ‚¨įĄŽåŽščĻæ‰šé‡åˆ é™¤{count, plural, one {#ä¸Ēé‡å¤éĄšį›Ž} other {#ä¸Ēé‡å¤éĄšį›Ž}}吗īŧŸčŋ™å°†äŋį•™æ¯įģ„ä¸­æœ€å¤§įš„éĄšį›Žåšļ删除所有å…ļåŽƒé‡å¤éĄšį›Žã€‚", "buy": "č´­äš° Immich", - "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": "é‡å¤éĄšį›Žīŧˆ{count}īŧ‰", - "cache_settings_image_cache_size": "回像įŧ“存大小īŧˆ{count} 饚īŧ‰", "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": "控åˆļ Immich app įš„įŧ“å­˜čĄŒä¸ē", - "cache_settings_thumbnail_size": "įŧŠį•Ĩ回įŧ“存大小īŧˆ{count} 饚īŧ‰", "cache_settings_tile_subtitle": "莞įŊŽæœŦåœ°å­˜å‚¨čĄŒä¸ē", "cache_settings_tile_title": "æœŦ地存储", "cache_settings_title": "įŧ“å­˜čŽžįŊŽ", @@ -605,6 +591,7 @@ "cannot_undo_this_action": "æŗ¨æ„īŧšč¯Ĩ操äŊœæ— æŗ•čĸĢæ’¤æļˆīŧ", "cannot_update_the_description": "æ— æŗ•æ›´æ–°æčŋ°", "cast": "æŠ•åą", + "cast_description": "配įŊŽå¯į”¨įš„æŠ•åąčŽžå¤‡", "change_date": "更攚æ—Ĩ期", "change_description": "äŋŽæ”šæčŋ°", "change_display_order": "更攚昞į¤ēéĄēåē", @@ -622,7 +609,6 @@ "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 åšļåŽŒæˆæ‰€æœ‰éĄšį›Žå¤‡äģŊåŽæ‰§čĄŒæ­¤æŖ€æŸĨ。č¯Ĩčŋ‡į¨‹å¯čƒŊ需čĻå‡ åˆ†é’Ÿã€‚", @@ -657,16 +643,18 @@ "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": "įĄŽčŽ¤å¯†į ", + "confirm_tag_face": "æ‚¨æƒŗå°†čŋ™åŧ č„¸æ ‡čްä¸ē{name}吗īŧŸ", + "confirm_tag_face_unnamed": "äŊ æƒŗæ ‡čްčŋ™åŧ č„¸å—īŧŸ", + "connected_device": "厞čŋžæŽĨčŽžå¤‡", "connected_to": "厞čŋžæŽĨ到", "contain": "包åĢ", "context": "äģĨ文搜回", "continue": "įģ§įģ­", - "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": "äģŽį§ģåŠ¨čŽžå¤‡ä¸­åˆ é™¤", @@ -712,10 +700,14 @@ "current_server_address": "åŊ“å‰æœåŠĄå™¨åœ°å€", "custom_locale": "č‡Ē厚䚉地åŒē", "custom_locale_description": "æ—Ĩ期和数字昞į¤ēæ ŧåŧčˇŸéšč¯­č¨€å’Œåœ°åŒē", + "daily_title_text_date": "MMM dd (E)", + "daily_title_text_date_year": "YYYYåš´M月Dæ—Ĩ (E)", "dark": "æˇąč‰˛", + "darkTheme": "æš—č‰˛ä¸ģéĸ˜", "date_after": "åŧ€å§‹æ—Ĩ期", "date_and_time": "æ—Ĩ期与æ—ļ间", "date_before": "į쓿Ÿæ—Ĩ期", + "date_format": "yåš´M月dæ—Ĩ (E) h:mm a", "date_of_birth_saved": "å‡ēį”Ÿæ—Ĩ期äŋå­˜æˆåŠŸ", "date_range": "æ—ĨæœŸčŒƒå›´", "day": "æ—Ĩ", @@ -728,7 +720,7 @@ "default_locale_description": "æ šæŽæ‚¨įš„æĩč§ˆå™¨åœ°åŒē莞įŊŽæ—Ĩ期和数字昞į¤ēæ ŧåŧ", "delete": "删除", "delete_album": "åˆ é™¤į›¸å†Œ", - "delete_api_key_prompt": "įĄŽåŽšåˆ é™¤æ­¤ API key 吗īŧŸ", + "delete_api_key_prompt": "įĄŽåŽšåˆ é™¤æ­¤ API 密é’Ĩ吗īŧŸ", "delete_dialog_alert": "čŋ™äē›éĄšį›Žå°†äģŽ Immich å’Œæ‚¨įš„čŽžå¤‡ä¸­æ°¸äš…åˆ é™¤", "delete_dialog_alert_local": "čŋ™äē›éĄšį›Žå°†äģŽæ‚¨įš„į§ģåŠ¨čŽžå¤‡ä¸­æ°¸äš…åˆ é™¤īŧŒäŊ†äģį„ļ可äģĨäģŽ Immich æœåŠĄå™¨ä¸­å†æŦĄčŽˇå–", "delete_dialog_alert_local_non_backed_up": "éƒ¨åˆ†éĄšį›Žčŋ˜æœĒ备äģŊ臺 Immich æœåŠĄå™¨īŧŒå°†äģŽæ‚¨įš„į§ģåŠ¨čŽžå¤‡ä¸­æ°¸äš…åˆ é™¤", @@ -759,6 +751,7 @@ "disallow_edits": "ä¸å…čŽ¸įŧ–čž‘", "discord": "Discord į¤žåŒē", "discover": "å‘įŽ°", + "discovered_devices": "åˇ˛å‘įŽ°įš„čŽžå¤‡", "dismiss_all_errors": "åŋŊį•Ĩæ‰€æœ‰é”™č¯¯", "dismiss_error": "åŋŊį•Ĩ错蝝", "display_options": "昞į¤ē选项", @@ -774,7 +767,6 @@ "download_enqueue": "厞加å…Ĩ下čŊŊ队列", "download_error": "下čŊŊå‡ē错", "download_failed": "下čŊŊå¤ąč´Ĩ", - "download_filename": "文äģļīŧš{filename}", "download_finished": "下čŊŊ厌成", "download_include_embedded_motion_videos": "内åĩŒč§†éĸ‘", "download_include_embedded_motion_videos_description": "将厞å†ĩį…§į‰‡ä¸­įš„å†…åĩŒč§†éĸ‘äŊœä¸ē单į‹Ŧ文äģļįēŗå…Ĩ", @@ -804,7 +796,7 @@ "edit_faces": "įŧ–čž‘äēē脸", "edit_import_path": "įŧ–čž‘å¯ŧå…Ĩčˇ¯åž„", "edit_import_paths": "įŧ–čž‘å¯ŧå…Ĩčˇ¯åž„", - "edit_key": "įŧ–čž‘ API Key", + "edit_key": "įŧ–čž‘ API 密é’Ĩ", "edit_link": "įŧ–螑铞æŽĨ", "edit_location": "įŧ–čž‘äŊįŊŽ", "edit_location_dialog_title": "äŊįŊŽ", @@ -837,6 +829,7 @@ "error_delete_face": "删除äēēč„¸å¤ąč´Ĩ", "error_loading_image": "加čŊŊå›žį‰‡æ—ļå‡ē错", "error_saving_image": "错蝝īŧš{error}", + "error_tag_face_bounding_box": "æ ‡čŽ°äēē脸å‡ē错 - æ— æŗ•čŽˇå–äēēč„¸æĄ†åæ ‡", "error_title": "错蝝 - åĨŊ像å‡ēäē†é—Žéĸ˜", "errors": { "cannot_navigate_next_asset": "æ— æŗ•å¯ŧčˆĒ到下一ä¸ĒéĄšį›Ž", @@ -849,7 +842,6 @@ "cant_get_number_of_comments": "æ— æŗ•čŽˇå–č¯„čŽē数量", "cant_search_people": "æ— æŗ•æŖ€į´ĸäēēį‰Š", "cant_search_places": "æ— æŗ•æŖ€į´ĸåœ°į‚š", - "cleared_jobs": "åˇ˛åˆ é™¤äģģåŠĄīŧš{job}", "error_adding_assets_to_album": "æˇģåŠ éĄšį›Žåˆ°į›¸å†Œæ—ļå‡ē错", "error_adding_users_to_album": "æˇģåŠ į”¨æˆˇåˆ°į›¸å†Œæ—ļå‡ē错", "error_deleting_shared_user": "åˆ é™¤å…ąäēĢį”¨æˆˇæ—ļå‡ē错", @@ -858,7 +850,6 @@ "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": "įŧ–čž‘å…ąäēĢ链æŽĨå¤ąč´Ĩ", @@ -877,7 +868,6 @@ "paths_validation_failed": "{paths, plural, one {#æĄčˇ¯åž„} 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": "æ— æŗ•æˇģåŠ č¯„čŽē", @@ -896,10 +886,9 @@ "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čŽŋ问æœŦéĄĩ", "unable_to_create_admin_account": "æ— æŗ•åˆ›åģēįŽĄį†å‘˜č´Ļæˆˇ", - "unable_to_create_api_key": "æ— æŗ•åˆ›åģēæ–°įš„ API Key", + "unable_to_create_api_key": "æ— æŗ•åˆ›åģēæ–°įš„ API 密é’Ĩ", "unable_to_create_library": "æ— æŗ•åˆ›åģē回åē“", "unable_to_create_user": "æ— æŗ•åˆ›åģēį”¨æˆˇ", "unable_to_delete_album": "æ— æŗ•åˆ é™¤į›¸å†Œ", @@ -920,26 +909,19 @@ "unable_to_hide_person": "æ— æŗ•éšč—äēēį‰Š", "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_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 Key", + "unable_to_remove_api_key": "æ— æŗ•į§ģ除 API 密é’Ĩ", "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": "æ— æŗ•č§Ŗå†ŗé‡å¤éĄš", @@ -947,7 +929,7 @@ "unable_to_restore_trash": "æ— æŗ•æĸ复回æ”ļįĢ™", "unable_to_restore_user": "æ— æŗ•æĸå¤į”¨æˆˇ", "unable_to_save_album": "æ— æŗ•äŋå­˜į›¸å†Œ", - "unable_to_save_api_key": "æ— æŗ•äŋå­˜ API Key", + "unable_to_save_api_key": "æ— æŗ•äŋå­˜ API 密é’Ĩ", "unable_to_save_date_of_birth": "æ— æŗ•äŋå­˜å‡ēį”Ÿæ—Ĩ期", "unable_to_save_name": "æ— æŗ•äŋå­˜åį§°", "unable_to_save_profile": "æ— æŗ•äŋå­˜é…įŊŽæ–‡äģļ", @@ -975,7 +957,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": "{months} 月鞄", "exif_bottom_sheet_person_age_year_months": "1垁 {months} ä¸Ē月", "exif_bottom_sheet_person_age_years": "{years} 垁", @@ -1013,7 +994,7 @@ "file_name_or_extension": "文äģļ名", "filename": "文äģļ名", "filetype": "文äģļįąģ型", - "filter": "᭛选", + "filter": "æģ¤é•œ", "filter_people": "čŋ‡æģ¤äēēį‰Š", "filter_places": "į­›é€‰åœ°į‚š", "find_them_fast": "æŒ‰åį§°åŋĢ速搜į´ĸ", @@ -1023,6 +1004,8 @@ "folders": "文äģļ多", "folders_feature_description": "在文äģļå¤šč§†å›žä¸­æĩč§ˆæ–‡äģļįŗģįģŸä¸Šįš„į…§į‰‡å’Œč§†éĸ‘", "forward": "向前", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "č¯Ĩ功čƒŊ需čρ加čŊŊæĨč‡Ē Google įš„å¤–éƒ¨čĩ„æēã€‚", "general": "é€šį”¨", "get_help": "čŽˇå–å¸ŽåŠŠ", "get_wifiname_error": "æ— æŗ•čŽˇå– Wi-Fi åį§°ã€‚įĄŽäŋåˇ˛æŽˆäēˆåŋ…čĻįš„æƒé™īŧŒåšļ厞čŋžæŽĨ到 Wi-Fi įŊ‘įģœ", @@ -1071,6 +1054,7 @@ "home_page_upload_err_limit": "一æŦĄæœ€å¤šåĒčƒŊ上äŧ  30 ä¸ĒéĄšį›ŽīŧŒčˇŗčŋ‡", "host": "æœåŠĄå™¨", "hour": "æ—ļ", + "id": "ID", "ignore_icloud_photos": "åŋŊį•Ĩ iCloud ᅧቇ", "ignore_icloud_photos_description": "存储在 iCloud ä¸­įš„į…§į‰‡ä¸äŧšä¸Šäŧ č‡ŗ Immich æœåŠĄå™¨", "image": "å›žį‰‡", @@ -1088,6 +1072,7 @@ "image_viewer_page_state_provider_download_started": "下čŊŊ启动", "image_viewer_page_state_provider_download_success": "下čŊŊ成功", "image_viewer_page_state_provider_share_error": "å…ąäēĢå‡ē错", + "immich_logo": "Immich 标åŋ—", "immich_web_interface": "Immich Web į•Œéĸ", "import_from_json": "äģŽ JSON å¯ŧå…Ĩ", "import_path": "å¯ŧå…Ĩčˇ¯åž„", @@ -1109,6 +1094,12 @@ "invalid_date_format": "æ— æ•ˆįš„æ—Ĩ期æ ŧåŧ", "invite_people": "邀蝎äēē员", "invite_to_album": "é‚€č¯ˇåŠ å…Ĩį›¸å†Œ", + "ios_debug_info_fetch_ran_at": "čŋčĄŒæ‹‰å– {dateTime}", + "ios_debug_info_last_sync_at": "上æŦĄåŒæ­Ĩ {dateTime}", + "ios_debug_info_no_processes_queued": "æ˛Ąæœ‰åŽå°čŋ›į¨‹åœ¨é˜Ÿåˆ—中", + "ios_debug_info_no_sync_yet": "尚æœĒ后台同æ­ĨäģģåŠĄåœ¨čŋčĄŒ", + "ios_debug_info_processes_queued": "{count, plural, one {{count} ä¸Ē后台čŋ›į¨‹åœ¨é˜Ÿåˆ—中} other {{count} ä¸Ē后台čŋ›į¨‹åœ¨é˜Ÿåˆ—中}}", + "ios_debug_info_processing_ran_at": "čŋčĄŒå¤„ᐆ {dateTime}", "items_count": "{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}", "jobs": "äģģåŠĄ", "keep": "äŋį•™", @@ -1117,6 +1108,9 @@ "kept_this_deleted_others": "äŋį•™č¯ĨéĄšį›Žåšļ删除 {count, plural, one {# ä¸ĒéĄšį›Ž} other {# ä¸ĒéĄšį›Ž}}", "keyboard_shortcuts": "é”Žį›˜åŋĢæˇé”Ž", "language": "蝭荀", + "language_no_results_subtitle": "å°č¯•č°ƒæ•´æ‚¨įš„æœį´ĸč¯", + "language_no_results_title": "æœĒ扞到寚åē”蝭荀", + "language_search_hint": "搜į´ĸ蝭荀...", "language_setting_description": "é€‰æ‹Šæ‚¨įš„č¯­č¨€ååĨŊ", "last_seen": "最后上įēŋäēŽ", "latest_version": "æœ€æ–°į‰ˆæœŦ", @@ -1142,6 +1136,7 @@ "list": "åˆ—čĄ¨", "loading": "加čŊŊ中", "loading_search_results_failed": "加čŊŊ搜į´ĸį쓿žœå¤ąč´Ĩ", + "local_asset_cast_failed": "æ— æŗ•æŠ•æ”žæœĒ上äŧ č‡ŗæœåŠĄå™¨įš„éĄšį›Ž", "local_network": "æœŦ地įŊ‘įģœ", "local_network_sheet_info": "åŊ“äŊŋį”¨æŒ‡åŽšįš„ Wi-Fi įŊ‘į윿—ļīŧŒåē”ᔍፋåēå°†é€ščŋ‡æ­¤ URL čŽŋé—ŽæœåŠĄå™¨", "location_permission": "厚äŊæƒé™", @@ -1155,12 +1150,14 @@ "locked_folder": "锁厚文äģļ多", "log_out": "æŗ¨é”€", "log_out_all_devices": "æŗ¨é”€æ‰€æœ‰čŽžå¤‡", + "logged_in_as": "äģĨ {user} čēĢäģŊį™ģåŊ•", "logged_out_all_devices": "äģŽæ‰€æœ‰čŽžå¤‡æŗ¨é”€", "logged_out_device": "äģŽčŽžå¤‡æŗ¨é”€", "login": "į™ģåŊ•", "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://", @@ -1245,6 +1242,7 @@ "missing": "įŧēå¤ą", "model": "åž‹åˇ", "month": "月", + "monthly_title_text_date_format": "y MMMM", "more": "更多", "move": "į§ģ动", "move_off_locked_folder": "į§ģå‡ē锁厚文äģļ多", @@ -1263,7 +1261,7 @@ "networking_subtitle": "įŽĄį†æœåŠĄå™¨æŽĨåŖčŽžįŊŽ", "never": "永不čŋ‡æœŸ", "new_album": "æ–°į›¸å†Œ", - "new_api_key": "新åĸž API Key", + "new_api_key": "新åĸž API 密é’Ĩ", "new_password": "æ–°å¯†į ", "new_person": "新äēēį‰Š", "new_pin_code": "æ–°įš„PIN᠁", @@ -1280,12 +1278,13 @@ "no_archived_assets_message": "åŊ’æĄŖį…§į‰‡å’Œč§†éĸ‘äģĨäžŋåœ¨į…§į‰‡č§†å›žä¸­éšč—åŽƒäģŦ", "no_assets_message": "į‚šå‡ģ上äŧ æ‚¨įš„įŦŦ一åŧ į…§į‰‡", "no_assets_to_show": "æ— éĄšį›Žåą•į¤ē", + "no_cast_devices_found": "æœĒæ‰žåˆ°æŠ•æ”žčŽžå¤‡", "no_duplicates_found": "æœĒå‘įŽ°é‡å¤éĄšã€‚", "no_exif_info_available": "æ˛Ąæœ‰å¯į”¨įš„ EXIF äŋĄæ¯", "no_explore_results_message": "上äŧ æ›´å¤šį…§į‰‡æĨæŽĸį´ĸ。", "no_favorites_message": "æˇģ加到æ”ļč—å¤šīŧŒåŋĢ速æŸĨ扞最äŊŗå›žį‰‡å’Œč§†éĸ‘", "no_libraries_message": "创åģē外部回å瓿ĨæŸĨįœ‹æ‚¨įš„į…§į‰‡å’Œč§†éĸ‘", - "no_locked_photos_message": "锁厚文äģļå¤šä¸­įš„į…§į‰‡å’Œč§†éĸ‘å°†čĸĢ隐藏īŧŒä¸äŧšåœ¨æ‚¨æĩč§ˆæ‚¨įš„回å瓿—ļå‡ēįŽ°ã€‚", + "no_locked_photos_message": "锁厚文äģļå¤šä¸­įš„į…§į‰‡å’Œč§†éĸ‘å°†čĸĢ隐藏īŧŒä¸äŧšåœ¨æ‚¨æĩč§ˆã€æœį´ĸ回å瓿—ļå‡ēįŽ°ã€‚", "no_name": "æœĒå‘Ŋ名", "no_notifications": "æ˛Ąæœ‰é€šįŸĨ", "no_people_found": "æœĒæ‰žåˆ°åŒšé…įš„äēēį‰Š", @@ -1305,17 +1304,18 @@ "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": "åœ¨æ­¤čŽžå¤‡", "onboarding": "ᛛ大åŧ€å¯", - "onboarding_privacy_description": "äģĨ下īŧˆå¯é€‰īŧ‰åŠŸčƒŊ䞝čĩ–å¤–éƒ¨æœåŠĄīŧŒå¯éšæ—ļåœ¨įŽĄį†čŽžįŊŽä¸­įĻį”¨ã€‚", + "onboarding_locale_description": "选拊您偏åĨŊįš„č¯­č¨€ã€‚æ‚¨å¯äģĨåœ¨čŽžįŊŽä¸­éšæ—ļæ›´æ”šæ­¤é€‰éĄšã€‚", + "onboarding_privacy_description": "äģĨ下īŧˆå¯é€‰īŧ‰åŠŸčƒŊ䞝čĩ–å¤–éƒ¨æœåŠĄīŧŒå¯éšæ—ļåœ¨čŽžįŊŽä¸­įĻį”¨ã€‚", + "onboarding_server_welcome_description": "čŽŠæˆ‘äģŦä¸ēæ‚¨čŽžįŊŽįŗģįģŸåšļ配įŊŽä¸€äē›é€šį”¨čŽžįŊŽã€‚", "onboarding_theme_description": "é€‰æ‹ŠæœåŠĄįš„éĸœč‰˛ä¸ģéĸ˜ã€‚į¨åŽå¯äģĨåœ¨čŽžįŊŽä¸­čŋ›čĄŒäŋŽæ”šã€‚", - "onboarding_welcome_description": "我äģŦåœ¨å¯į”¨æœåŠĄå‰å…ˆåšä¸€äē›é€šį”¨čŽžįŊŽã€‚", + "onboarding_user_welcome_description": "我äģŦå‡ē发吧īŧ", "onboarding_welcome_user": "æŦĸčŋŽīŧŒ{user}", "online": "在įēŋ", "only_favorites": "äģ…æ˜žį¤ē厞æ”ļ藏", @@ -1372,6 +1372,8 @@ "permanently_delete_assets_prompt": "įĄŽåŽščĻæ°¸äš…åˆ é™¤ {count, plural, one {æ­¤éĄšį›ŽīŧŸ} other {čŋ™#ä¸ĒéĄšį›ŽīŧŸ}} č¯Ĩ操äŊœäŧšåŒæ—ļ将 {count, plural, one {厃} other {厃äģŦ}} äģŽå…￉€åœ¨į›¸å†Œä¸­į§ģ除。", "permanently_deleted_asset": "æ°¸äš…åˆ é™¤įš„éĄšį›Ž", "permanently_deleted_assets_count": "{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}åˇ˛åˆ é™¤", + "permission": "权限", + "permission_empty": "æ‚¨įš„æƒé™ä¸čƒŊä¸ēįŠē", "permission_onboarding_back": "čŋ”回", "permission_onboarding_continue_anyway": "äģį„ļįģ§įģ­", "permission_onboarding_get_started": "åŧ€å§‹äŊŋᔍ", @@ -1408,7 +1410,10 @@ "preview": "éĸ„č§ˆ", "previous": "上一ä¸Ē", "previous_memory": "上一ä¸Ē", - "previous_or_next_photo": "上一åŧ æˆ–下一åŧ į…§į‰‡", + "previous_or_next_day": "前一夊/后一夊", + "previous_or_next_month": "下ä¸Ē月/上ä¸Ē月", + "previous_or_next_photo": "下一åŧ /上一åŧ ", + "previous_or_next_year": "明嚴/åŽģåš´", "primary": "éĻ–čρ", "privacy": "隐ᧁ", "profile": "č¯Ļ情", @@ -1416,6 +1421,7 @@ "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}įš„ä¸Ēäēēčĩ„æ–™å›žį‰‡", @@ -1491,13 +1497,14 @@ "remove_from_album": "äģŽį›¸å†Œä¸­į§ģ除", "remove_from_favorites": "į§ģå‡ēæ”ļ藏", "remove_from_locked_folder": "äģŽé”åŽšæ–‡äģļ多中į§ģ除", - "remove_from_locked_folder_confirmation": "æ‚¨įĄŽåŽščρ将čŋ™äē›į…§į‰‡å’Œč§†éĸ‘į§ģå‡ē锁厚文äģļ多吗īŧŸį§ģå‡ē后厃äģŦ将äŧšåœ¨æ‚¨įš„回åē“ä¸­å¯č§", + "remove_from_locked_folder_confirmation": "æ‚¨įĄŽåŽščρ将čŋ™äē›į…§į‰‡å’Œč§†éĸ‘į§ģå‡ē锁厚文äģļ多吗īŧŸį§ģå‡ē后厃äģŦ将äŧšåœ¨æ‚¨įš„回åē“ä¸­å¯č§ã€‚", "remove_from_shared_link": "äģŽå…ąäēĢ链æŽĨ中į§ģ除", "remove_memory": "į§ģå‡ē回åŋ†åŒē", "remove_photo_from_memory": "äģŽåŊ“前回åŋ†åŒēį§ģ除ᅧቇ", + "remove_tag": "į§ģé™¤æ ‡į­ž", "remove_url": "į§ģ除 URL", "remove_user": "į§ģé™¤į”¨æˆˇ", - "removed_api_key": "厞į§ģ除 API Keyīŧš{name}", + "removed_api_key": "厞į§ģ除 API 密é’Ĩīŧš{name}", "removed_from_archive": "äģŽåŊ’æĄŖä¸­į§ģ除", "removed_from_favorites": "äģŽæ”ļ藏中į§ģ除", "removed_from_favorites_count": "äģŽæ”ļ藏中į§ģ除{count, plural, other {#饚}}", @@ -1531,7 +1538,7 @@ "role_viewer": "äģ…æŸĨįœ‹", "save": "äŋå­˜", "save_to_gallery": "äŋå­˜åˆ°å›žåē“", - "saved_api_key": "厞äŋå­˜įš„ API Key", + "saved_api_key": "厞äŋå­˜įš„ API 密é’Ĩ", "saved_profile": "厞äŋå­˜čĩ„æ–™", "saved_settings": "厞äŋå­˜čŽžįŊŽ", "say_something": "č¯´į‚šäģ€äšˆ", @@ -1601,6 +1608,7 @@ "select_album_cover": "é€‰æ‹Šį›¸å†Œå°éĸ", "select_all": "全选", "select_all_duplicates": "é€‰æ‹Šæ‰€æœ‰é‡å¤éĄš", + "select_all_in": "选拊 {group} ä¸­įš„æ‰€æœ‰å†…åŽš", "select_avatar_color": "选拊头像éĸœč‰˛", "select_face": "选拊äēē脸", "select_featured_photo": "选拊ä¸Ē性头像", @@ -1621,6 +1629,7 @@ "server_info_box_server_url": "æœåŠĄå™¨åœ°å€", "server_offline": "æœåŠĄå™¨įĻģįēŋ", "server_online": "æœåŠĄå™¨åœ¨įēŋ", + "server_privacy": "æœåŠĄå™¨éšį§", "server_stats": "æœåŠĄå™¨įŠļ态", "server_version": "æœåŠĄå™¨į‰ˆæœŦ", "set": "莞įŊŽ", @@ -1630,6 +1639,7 @@ "set_date_of_birth": "莞įŊŽå‡ēį”Ÿæ—Ĩ期", "set_profile_picture": "莞įŊŽä¸Ēäēēčĩ„æ–™å›žį‰‡", "set_slideshow_to_fullscreen": "å…¨åąæ”žæ˜ åšģၝቇ", + "set_stack_primary_asset": "莞ä¸ēä¸ģčĻéĄšį›Ž", "setting_image_viewer_help": "č¯Ļįģ†äŋĄæ¯æŸĨįœ‹å™¨éĻ–å…ˆåŠ čŊŊ小įŧŠį•Ĩ回īŧŒį„ļ后加čŊŊä¸­į­‰å¤§å°įš„éĸ„č§ˆå›žīŧˆč‹Ĩ吝ᔍīŧ‰īŧŒæœ€åŽåŠ čŊŊ原始回像。", "setting_image_viewer_original_subtitle": "吝ᔍäģĨ加čŊŊ原回īŧŒįρᔍäģĨ减少数捎äŊŋį”¨é‡īŧˆįŊ‘įģœå’ŒčŽžå¤‡įŧ“å­˜īŧ‰ã€‚", "setting_image_viewer_original_title": "加čŊŊ原回", @@ -1638,7 +1648,6 @@ "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": "įĢ‹åŗ", @@ -1701,6 +1710,7 @@ "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": "å…ąäēĢ链æŽĨ", @@ -1767,6 +1777,7 @@ "start_date": "åŧ€å§‹æ—Ĩ期", "state": "ᜁäģŊ", "status": "įŠļ态", + "stop_casting": "停æ­ĸ投攞", "stop_motion_photo": "厚æ ŧᅧቇ", "stop_photo_sharing": "停æ­ĸå…ąäēĢᅧቇīŧŸ", "stop_photo_sharing_description": "“{partner}”将不čƒŊčŽŋé—Žæ‚¨įš„į…§į‰‡ã€‚", @@ -1824,7 +1835,6 @@ "to_parent": "čŋ”回上一įē§", "to_trash": "攞å…Ĩ回æ”ļįĢ™", "toggle_settings": "切æĸ莞įŊŽ", - "toggle_theme": "切æĸæˇąč‰˛ä¸ģéĸ˜", "total": "æ€ģ莥", "total_usage": "æ€ģį”¨é‡", "trash": "回æ”ļįĢ™", @@ -1846,6 +1856,7 @@ "unable_to_setup_pin_code": "æ— æŗ•čŽžįŊŽPIN᠁", "unarchive": "取æļˆåŊ’æĄŖ", "unarchived_count": "{count, plural, other {取æļˆåŊ’æĄŖ # 饚}}", + "undo": "撤销", "unfavorite": "取æļˆæ”ļ藏", "unhide_person": "昞į¤ēäēēį‰Š", "unknown": "æœĒįŸĨ", @@ -1862,10 +1873,9 @@ "unsaved_change": "äŋŽæ”šæœĒäŋå­˜", "unselect_all": "取æļˆå…¨é€‰", "unselect_all_duplicates": "取æļˆé€‰æ‹Šæ‰€æœ‰é‡å¤éĄš", + "unselect_all_in": "取æļˆé€‰æ‹Š {group} ä¸­įš„æ‰€æœ‰å†…åŽš", "unstack": "取æļˆå †å ", "unstacked_assets_count": "{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}åˇ˛å–æļˆå †å ", - "untracked_files": "æœĒ莟č¸Ēįš„æ–‡äģļ", - "untracked_files_decription": "åē”ᔍፋåēä¸äŧščˇŸč¸Ēčŋ™ä盿–‡äģļ。厃äģŦ可čƒŊæ˜¯į”ąäēŽį§ģåŠ¨å¤ąč´Ĩ、上äŧ ä¸­æ–­æˆ–å› é”™č¯¯č€Œé—į•™", "up_next": "下一ä¸Ē", "updated_at": "åˇ˛æ›´æ–°", "updated_password": "æ›´æ–°å¯†į ", @@ -1882,6 +1892,7 @@ "upload_success": "上äŧ æˆåŠŸīŧŒåˆˇæ–°éĄĩéĸæŸĨįœ‹æ–°ä¸Šäŧ įš„éĄšį›Žã€‚", "upload_to_immich": "上äŧ č‡ŗ Immichīŧˆ{count}īŧ‰", "uploading": "æ­Ŗåœ¨ä¸Šäŧ ", + "url": "URL", "usage": "į”¨é‡", "use_biometric": "äŊŋį”¨į”Ÿį‰Šč¯†åˆĢ", "use_current_connection": "äŊŋᔍåŊ“前čŋžæŽĨ", @@ -1892,6 +1903,7 @@ "user_liked": "“{user}â€į‚ščĩžäē†{type, select, photo {č¯Ĩᅧቇ} video {č¯Ĩ视éĸ‘} asset {č¯ĨéĄšį›Ž} other {厃}}", "user_pin_code_settings": "PIN᠁", "user_pin_code_settings_description": "įŽĄį†äŊ įš„PIN᠁", + "user_privacy": "į”¨æˆˇéšį§", "user_purchase_settings": "č´­äš°", "user_purchase_settings_description": "įŽĄį†č´­äš°čŽĸ单", "user_role_set": "莞įŊŽâ€œ{user}”ä¸ē“{role}”", @@ -1907,11 +1919,6 @@ "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_title": "æœåŠĄįĢ¯æœ‰æ–°į‰ˆæœŦå•Ļ 🎉", "version_history": "į‰ˆæœŦæ›´æ–°åŽ†å˛čŽ°åŊ•", "version_history_item": "在 {date} åŽ‰čŖ… {version} į‰ˆæœŦ", "video": "视éĸ‘", @@ -1931,6 +1938,7 @@ "view_previous_asset": "æŸĨįœ‹ä¸Šä¸€éĄš", "view_qr_code": "æŸĨįœ‹äēŒįģ´į ", "view_stack": "æŸĨįœ‹å †å éĄšį›Ž", + "view_user": "æŸĨįœ‹į”¨æˆˇ", "viewer_remove_from_stack": "äģŽå †å ä¸­į§ģ除", "viewer_stack_use_as_main_asset": "äŊœä¸ēä¸ģéĄšį›ŽäŊŋᔍ", "viewer_unstack": "取æļˆå †å ", diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index 1f4d7aa635..372982af67 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -1,6 +1,6 @@ ARG DEVICE=cpu -FROM python:3.11-bookworm@sha256:d2621a9f74d31a8a60af19f97b09cc3ac54382c8680b6544018713a12ef6c048 AS builder-cpu +FROM python:3.11-bookworm@sha256:ce3b954c9285a7a145cba620bae03db836ab890b6b9e0d05a3ca522ea00dfbc9 AS builder-cpu FROM builder-cpu AS builder-openvino @@ -59,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:4faec156e35a5f345d57804d8858c6ba1cf6352ce5f4bffc11b7fdebdef46a38 /uv /uvx /bin/ +COPY --from=ghcr.io/astral-sh/uv:latest@sha256:9653efd4380d5a0e5511e337dcfc3b8ba5bc4e6ea7fa3be7716598261d5503fa /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 \ @@ -68,11 +68,11 @@ RUN if [ "$DEVICE" = "rocm" ]; then \ uv pip install /opt/onnxruntime_rocm-*.whl; \ fi -FROM python:3.11-slim-bookworm@sha256:7a3ed1226224bcc1fe5443262363d42f48cf832a540c1836ba8ccbeaadf8637c AS prod-cpu +FROM python:3.11-slim-bookworm@sha256:9e1912aab0a30bbd9488eb79063f68f42a68ab0946cbe98fecf197fe5b085506 AS prod-cpu ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 -FROM python:3.11-slim-bookworm@sha256:7a3ed1226224bcc1fe5443262363d42f48cf832a540c1836ba8ccbeaadf8637c AS prod-openvino +FROM python:3.11-slim-bookworm@sha256:9e1912aab0a30bbd9488eb79063f68f42a68ab0946cbe98fecf197fe5b085506 AS prod-openvino RUN apt-get update && \ apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \ diff --git a/machine-learning/immich_ml/main.py b/machine-learning/immich_ml/main.py index 6ad5b8b545..e884af0e69 100644 --- a/machine-learning/immich_ml/main.py +++ b/machine-learning/immich_ml/main.py @@ -244,7 +244,6 @@ async def load(model: InferenceModel) -> InferenceModel: async def idle_shutdown_task() -> None: while True: - log.debug("Checking for inactivity...") if ( last_called is not None and not active_requests diff --git a/machine-learning/uv.lock b/machine-learning/uv.lock index 115ea259f3..7da2fd3920 100644 --- a/machine-learning/uv.lock +++ b/machine-learning/uv.lock @@ -517,16 +517,16 @@ wheels = [ [[package]] name = "fastapi" -version = "0.115.12" +version = "0.115.14" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { 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, upload-time = "2025-03-23T22:55:43.822Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/53/8c38a874844a8b0fa10dd8adf3836ac154082cf88d3f22b544e9ceea0a15/fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739", size = 296263, upload-time = "2025-06-26T15:29:08.21Z" } wheels = [ - { 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" }, + { url = "https://files.pythonhosted.org/packages/53/50/b1222562c6d270fea83e9c9075b8e8600b8479150a18e4516a6138b980d1/fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca", size = 95514, upload-time = "2025-06-26T15:29:06.49Z" }, ] [[package]] @@ -900,7 +900,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "0.32.4" +version = "0.33.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -912,9 +912,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/c8/4f7d270285c46324fd66f62159eb16739aa5696f422dba57678a8c6b78e9/huggingface_hub-0.32.4.tar.gz", hash = "sha256:f61d45cd338736f59fb0e97550b74c24ee771bcc92c05ae0766b9116abe720be", size = 424494, upload-time = "2025-06-03T09:59:46.105Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/42/8a95c5632080ae312c0498744b2b852195e10b05a20b1be11c5141092f4c/huggingface_hub-0.33.2.tar.gz", hash = "sha256:84221defaec8fa09c090390cd68c78b88e3c4c2b7befba68d3dc5aacbc3c2c5f", size = 426637, upload-time = "2025-07-02T06:26:05.156Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/8b/222140f3cfb6f17b0dd8c4b9a0b36bd4ebefe9fb0098ba35d6960abcda0f/huggingface_hub-0.32.4-py3-none-any.whl", hash = "sha256:37abf8826b38d971f60d3625229221c36e53fe58060286db9baf619cfbf39767", size = 512101, upload-time = "2025-06-03T09:59:44.099Z" }, + { url = "https://files.pythonhosted.org/packages/44/f4/5f3f22e762ad1965f01122b42dae5bf0e009286e2dba601ce1d0dba72424/huggingface_hub-0.33.2-py3-none-any.whl", hash = "sha256:3749498bfa91e8cde2ddc2c1db92c79981f40e66434c20133b39e5928ac9bcc5", size = 515373, upload-time = "2025-07-02T06:26:03.072Z" }, ] [[package]] @@ -1044,7 +1044,7 @@ requires-dist = [ { name = "onnxruntime", marker = "extra == 'armnn'", specifier = ">=1.15.0,<2" }, { name = "onnxruntime", marker = "extra == 'cpu'", specifier = ">=1.15.0,<2" }, { name = "onnxruntime", marker = "extra == 'rknn'", specifier = ">=1.15.0,<2" }, - { name = "onnxruntime-gpu", marker = "extra == 'cuda'", specifier = ">=1.17.0,<2", index = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/" }, + { name = "onnxruntime-gpu", marker = "extra == 'cuda'", specifier = ">=1.17.0,<2", index = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple" }, { name = "onnxruntime-openvino", marker = "extra == 'openvino'", specifier = ">=1.17.1,<1.19.0" }, { name = "opencv-python-headless", specifier = ">=4.7.0.72,<5.0" }, { name = "orjson", specifier = ">=3.9.5" }, @@ -1225,7 +1225,7 @@ wheels = [ [[package]] name = "locust" -version = "2.37.9" +version = "2.37.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "configargparse" }, @@ -1245,14 +1245,14 @@ dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/05/2bfdf19756c6a12f6f9513f75340ecf0595d83cab4d9fc91162225908e3d/locust-2.37.9.tar.gz", hash = "sha256:e43673b594ec5ecde4f9ba6e0d5c66c00d7c0ae93591951abe83e8d186c67175", size = 2252507, upload-time = "2025-06-05T09:26:58.186Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/19/66cdab585f7d4385be615d3792402fc75a1bed7519e5283adbe7133dbc78/locust-2.37.11.tar.gz", hash = "sha256:89c79bc599aa57160bd41dd3876e35d8b9dee5abded78e35008d01fd8f1640ed", size = 2252602, upload-time = "2025-06-23T08:22:23.922Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/1c/0ece4176231c578e819d970ec08d124492833e50aafd171c582bcc414446/locust-2.37.9-py3-none-any.whl", hash = "sha256:e17da439f3a252d1fb6d4c34daf00d7e8b87e99d833a32e8a79f4f8ebb07767d", size = 2269084, upload-time = "2025-06-05T09:26:56.257Z" }, + { url = "https://files.pythonhosted.org/packages/3a/2d/e5ae05911521bf84113be349d51b16d54589e986837d2d518f63434ea3ec/locust-2.37.11-py3-none-any.whl", hash = "sha256:b826f95fbfd5d9a32df6ab1b74672b88e65bbc33ec99fdc10af98079952ad517", size = 2269179, upload-time = "2025-06-23T08:22:21.067Z" }, ] [[package]] name = "locust-cloud" -version = "1.23.1" +version = "1.24.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "configargparse" }, @@ -1262,9 +1262,9 @@ dependencies = [ { name = "python-socketio", extra = ["client"] }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/7c/d9cbbd051490aeedfbd6ddda8ad48f77dd848ee490f6ebd166d20db5911e/locust_cloud-1.23.1.tar.gz", hash = "sha256:a09161752b8c9a9205e97cef5223ee3ad967bc2d91c52d61952aaa3da6802a55", size = 450937, upload-time = "2025-06-05T06:07:53.773Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/77/bda24167a2b763ba5d3cad1f3fa2a938f5273e51a61bffdbc8dc2e3ba24d/locust_cloud-1.24.2.tar.gz", hash = "sha256:a2656537ff367e6d4d4673477ba9e81ed73a8423a71573cd2512248740eded77", size = 451122, upload-time = "2025-06-23T11:08:00.558Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/01/5af43edee540e38ba0ee0a2e3beb72c50073e0f646bb543a8b34650315e3/locust_cloud-1.23.1-py3-none-any.whl", hash = "sha256:11677895c6ed6d0beef1b425a6f04f10ea2cfcaeaefbf00a97fb3c9134296e54", size = 408323, upload-time = "2025-06-05T06:07:51.947Z" }, + { url = "https://files.pythonhosted.org/packages/f4/38/8cda8aa1c6dfe5c5abbf69a9b10c03585c37eff64ca92733a291806052ac/locust_cloud-1.24.2-py3-none-any.whl", hash = "sha256:64a5e6f2bf0a1a012d9805291d44fb57e57535c2b5c0fa5bc87ba0d7cce9ef9c", size = 408594, upload-time = "2025-06-23T11:07:59.092Z" }, ] [[package]] @@ -1415,7 +1415,7 @@ wheels = [ [[package]] name = "mypy" -version = "1.16.0" +version = "1.16.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, @@ -1423,33 +1423,33 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/38/13c2f1abae94d5ea0354e146b95a1be9b2137a0d506728e0da037c4276f6/mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab", size = 3323139, upload-time = "2025-05-29T13:46:12.532Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747, upload-time = "2025-06-16T16:51:35.145Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/5e/a0485f0608a3d67029d3d73cec209278b025e3493a3acfda3ef3a88540fd/mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c", size = 10967416, upload-time = "2025-05-29T13:34:17.783Z" }, - { url = "https://files.pythonhosted.org/packages/4b/53/5837c221f74c0d53a4bfc3003296f8179c3a2a7f336d7de7bbafbe96b688/mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571", size = 10087654, upload-time = "2025-05-29T13:32:37.878Z" }, - { url = "https://files.pythonhosted.org/packages/29/59/5fd2400352c3093bed4c09017fe671d26bc5bb7e6ef2d4bf85f2a2488104/mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491", size = 11875192, upload-time = "2025-05-29T13:34:54.281Z" }, - { url = "https://files.pythonhosted.org/packages/ad/3e/4bfec74663a64c2012f3e278dbc29ffe82b121bc551758590d1b6449ec0c/mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777", size = 12612939, upload-time = "2025-05-29T13:33:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/88/1f/fecbe3dcba4bf2ca34c26ca016383a9676711907f8db4da8354925cbb08f/mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b", size = 12874719, upload-time = "2025-05-29T13:21:52.09Z" }, - { url = "https://files.pythonhosted.org/packages/f3/51/c2d280601cd816c43dfa512a759270d5a5ef638d7ac9bea9134c8305a12f/mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93", size = 9487053, upload-time = "2025-05-29T13:33:29.797Z" }, - { url = "https://files.pythonhosted.org/packages/24/c4/ff2f79db7075c274fe85b5fff8797d29c6b61b8854c39e3b7feb556aa377/mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab", size = 10884498, upload-time = "2025-05-29T13:18:54.066Z" }, - { url = "https://files.pythonhosted.org/packages/02/07/12198e83006235f10f6a7808917376b5d6240a2fd5dce740fe5d2ebf3247/mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2", size = 10011755, upload-time = "2025-05-29T13:34:00.851Z" }, - { url = "https://files.pythonhosted.org/packages/f1/9b/5fd5801a72b5d6fb6ec0105ea1d0e01ab2d4971893076e558d4b6d6b5f80/mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff", size = 11800138, upload-time = "2025-05-29T13:32:55.082Z" }, - { url = "https://files.pythonhosted.org/packages/2e/81/a117441ea5dfc3746431e51d78a4aca569c677aa225bca2cc05a7c239b61/mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666", size = 12533156, upload-time = "2025-05-29T13:19:12.963Z" }, - { url = "https://files.pythonhosted.org/packages/3f/38/88ec57c6c86014d3f06251e00f397b5a7daa6888884d0abf187e4f5f587f/mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c", size = 12742426, upload-time = "2025-05-29T13:20:22.72Z" }, - { url = "https://files.pythonhosted.org/packages/bd/53/7e9d528433d56e6f6f77ccf24af6ce570986c2d98a5839e4c2009ef47283/mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b", size = 9478319, upload-time = "2025-05-29T13:21:17.582Z" }, - { url = "https://files.pythonhosted.org/packages/70/cf/158e5055e60ca2be23aec54a3010f89dcffd788732634b344fc9cb1e85a0/mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13", size = 11062927, upload-time = "2025-05-29T13:35:52.328Z" }, - { url = "https://files.pythonhosted.org/packages/94/34/cfff7a56be1609f5d10ef386342ce3494158e4d506516890142007e6472c/mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090", size = 10083082, upload-time = "2025-05-29T13:35:33.378Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7f/7242062ec6288c33d8ad89574df87c3903d394870e5e6ba1699317a65075/mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1", size = 11828306, upload-time = "2025-05-29T13:21:02.164Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5f/b392f7b4f659f5b619ce5994c5c43caab3d80df2296ae54fa888b3d17f5a/mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8", size = 12702764, upload-time = "2025-05-29T13:20:42.826Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c0/7646ef3a00fa39ac9bc0938626d9ff29d19d733011be929cfea59d82d136/mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730", size = 12896233, upload-time = "2025-05-29T13:18:37.446Z" }, - { url = "https://files.pythonhosted.org/packages/6d/38/52f4b808b3fef7f0ef840ee8ff6ce5b5d77381e65425758d515cdd4f5bb5/mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec", size = 9565547, upload-time = "2025-05-29T13:20:02.836Z" }, - { url = "https://files.pythonhosted.org/packages/97/9c/ca03bdbefbaa03b264b9318a98950a9c683e06472226b55472f96ebbc53d/mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b", size = 11059753, upload-time = "2025-05-29T13:18:18.167Z" }, - { url = "https://files.pythonhosted.org/packages/36/92/79a969b8302cfe316027c88f7dc6fee70129490a370b3f6eb11d777749d0/mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0", size = 10073338, upload-time = "2025-05-29T13:19:48.079Z" }, - { url = "https://files.pythonhosted.org/packages/14/9b/a943f09319167da0552d5cd722104096a9c99270719b1afeea60d11610aa/mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b", size = 11827764, upload-time = "2025-05-29T13:46:04.47Z" }, - { url = "https://files.pythonhosted.org/packages/ec/64/ff75e71c65a0cb6ee737287c7913ea155845a556c64144c65b811afdb9c7/mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d", size = 12701356, upload-time = "2025-05-29T13:35:13.553Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ad/0e93c18987a1182c350f7a5fab70550852f9fabe30ecb63bfbe51b602074/mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52", size = 12900745, upload-time = "2025-05-29T13:17:24.409Z" }, - { url = "https://files.pythonhosted.org/packages/28/5d/036c278d7a013e97e33f08c047fe5583ab4f1fc47c9a49f985f1cdd2a2d7/mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb", size = 9572200, upload-time = "2025-05-29T13:33:44.92Z" }, - { url = "https://files.pythonhosted.org/packages/99/a3/6ed10530dec8e0fdc890d81361260c9ef1f5e5c217ad8c9b21ecb2b8366b/mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031", size = 2265773, upload-time = "2025-05-29T13:35:18.762Z" }, + { url = "https://files.pythonhosted.org/packages/8e/12/2bf23a80fcef5edb75de9a1e295d778e0f46ea89eb8b115818b663eff42b/mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a", size = 10958644, upload-time = "2025-06-16T16:51:11.649Z" }, + { url = "https://files.pythonhosted.org/packages/08/50/bfe47b3b278eacf348291742fd5e6613bbc4b3434b72ce9361896417cfe5/mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72", size = 10087033, upload-time = "2025-06-16T16:35:30.089Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/40307c12fe25675a0776aaa2cdd2879cf30d99eec91b898de00228dc3ab5/mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea", size = 11875645, upload-time = "2025-06-16T16:35:48.49Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d8/85bdb59e4a98b7a31495bd8f1a4445d8ffc86cde4ab1f8c11d247c11aedc/mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574", size = 12616986, upload-time = "2025-06-16T16:48:39.526Z" }, + { url = "https://files.pythonhosted.org/packages/0e/d0/bb25731158fa8f8ee9e068d3e94fcceb4971fedf1424248496292512afe9/mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d", size = 12878632, upload-time = "2025-06-16T16:36:08.195Z" }, + { url = "https://files.pythonhosted.org/packages/2d/11/822a9beb7a2b825c0cb06132ca0a5183f8327a5e23ef89717c9474ba0bc6/mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6", size = 9484391, upload-time = "2025-06-16T16:37:56.151Z" }, + { url = "https://files.pythonhosted.org/packages/9a/61/ec1245aa1c325cb7a6c0f8570a2eee3bfc40fa90d19b1267f8e50b5c8645/mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc", size = 10890557, upload-time = "2025-06-16T16:37:21.421Z" }, + { url = "https://files.pythonhosted.org/packages/6b/bb/6eccc0ba0aa0c7a87df24e73f0ad34170514abd8162eb0c75fd7128171fb/mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782", size = 10012921, upload-time = "2025-06-16T16:51:28.659Z" }, + { url = "https://files.pythonhosted.org/packages/5f/80/b337a12e2006715f99f529e732c5f6a8c143bb58c92bb142d5ab380963a5/mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507", size = 11802887, upload-time = "2025-06-16T16:50:53.627Z" }, + { url = "https://files.pythonhosted.org/packages/d9/59/f7af072d09793d581a745a25737c7c0a945760036b16aeb620f658a017af/mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca", size = 12531658, upload-time = "2025-06-16T16:33:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/82/c4/607672f2d6c0254b94a646cfc45ad589dd71b04aa1f3d642b840f7cce06c/mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4", size = 12732486, upload-time = "2025-06-16T16:37:03.301Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5e/136555ec1d80df877a707cebf9081bd3a9f397dedc1ab9750518d87489ec/mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6", size = 9479482, upload-time = "2025-06-16T16:47:37.48Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d6/39482e5fcc724c15bf6280ff5806548c7185e0c090712a3736ed4d07e8b7/mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d", size = 11066493, upload-time = "2025-06-16T16:47:01.683Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e5/26c347890efc6b757f4d5bb83f4a0cf5958b8cf49c938ac99b8b72b420a6/mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9", size = 10081687, upload-time = "2025-06-16T16:48:19.367Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/b5cb264c97b86914487d6a24bd8688c0172e37ec0f43e93b9691cae9468b/mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79", size = 11839723, upload-time = "2025-06-16T16:49:20.912Z" }, + { url = "https://files.pythonhosted.org/packages/15/f8/491997a9b8a554204f834ed4816bda813aefda31cf873bb099deee3c9a99/mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15", size = 12722980, upload-time = "2025-06-16T16:37:40.929Z" }, + { url = "https://files.pythonhosted.org/packages/df/f0/2bd41e174b5fd93bc9de9a28e4fb673113633b8a7f3a607fa4a73595e468/mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd", size = 12903328, upload-time = "2025-06-16T16:34:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/61/81/5572108a7bec2c46b8aff7e9b524f371fe6ab5efb534d38d6b37b5490da8/mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b", size = 9562321, upload-time = "2025-06-16T16:48:58.823Z" }, + { url = "https://files.pythonhosted.org/packages/28/e3/96964af4a75a949e67df4b95318fe2b7427ac8189bbc3ef28f92a1c5bc56/mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438", size = 11063480, upload-time = "2025-06-16T16:47:56.205Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4d/cd1a42b8e5be278fab7010fb289d9307a63e07153f0ae1510a3d7b703193/mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536", size = 10090538, upload-time = "2025-06-16T16:46:43.92Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4f/c3c6b4b66374b5f68bab07c8cabd63a049ff69796b844bc759a0ca99bb2a/mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f", size = 11836839, upload-time = "2025-06-16T16:36:28.039Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7e/81ca3b074021ad9775e5cb97ebe0089c0f13684b066a750b7dc208438403/mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359", size = 12715634, upload-time = "2025-06-16T16:50:34.441Z" }, + { url = "https://files.pythonhosted.org/packages/e9/95/bdd40c8be346fa4c70edb4081d727a54d0a05382d84966869738cfa8a497/mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be", size = 12895584, upload-time = "2025-06-16T16:34:54.857Z" }, + { url = "https://files.pythonhosted.org/packages/5a/fd/d486a0827a1c597b3b48b1bdef47228a6e9ee8102ab8c28f944cb83b65dc/mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee", size = 9573886, upload-time = "2025-06-16T16:36:43.589Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d3/53e684e78e07c1a2bf7105715e5edd09ce951fc3f47cf9ed095ec1b7a037/mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37", size = 2265923, upload-time = "2025-06-16T16:48:02.366Z" }, ] [[package]] @@ -1568,7 +1568,7 @@ wheels = [ [[package]] name = "onnxruntime-gpu" version = "1.19.2" -source = { registry = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/" } +source = { registry = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple" } dependencies = [ { name = "coloredlogs" }, { name = "flatbuffers" }, @@ -1834,7 +1834,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.5" +version = "2.11.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1842,9 +1842,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -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" } +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } wheels = [ - { 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" }, + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, ] [[package]] @@ -1936,16 +1936,16 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.9.1" +version = "2.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -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" } +sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583, upload-time = "2025-06-24T13:26:46.841Z" } wheels = [ - { 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" }, + { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" }, ] [[package]] @@ -1977,7 +1977,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.4.0" +version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1988,9 +1988,9 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/aa/405082ce2749be5398045152251ac69c0f3578c7077efc53431303af97ce/pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6", size = 1515232, upload-time = "2025-06-02T17:36:30.03Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", size = 363797, upload-time = "2025-06-02T17:36:27.859Z" }, + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, ] [[package]] @@ -2007,15 +2007,16 @@ wheels = [ [[package]] name = "pytest-cov" -version = "6.1.1" +version = "6.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, { name = "pytest" }, ] -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" } +sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } wheels = [ - { 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" }, + { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, ] [[package]] @@ -2303,27 +2304,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.11.13" +version = "0.12.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ed/da/9c6f995903b4d9474b39da91d2d626659af3ff1eeb43e9ae7c119349dba6/ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514", size = 4282054, upload-time = "2025-06-05T21:00:15.721Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/3d/d9a195676f25d00dbfcf3cf95fdd4c685c497fcfa7e862a44ac5e4e96480/ruff-0.12.2.tar.gz", hash = "sha256:d7b4f55cd6f325cb7621244f19c873c565a08aff5a4ba9c69aa7355f3f7afd3e", size = 4432239, upload-time = "2025-07-03T16:40:19.566Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/ce/a11d381192966e0b4290842cc8d4fac7dc9214ddf627c11c1afff87da29b/ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46", size = 10292516, upload-time = "2025-06-05T20:59:32.944Z" }, - { url = "https://files.pythonhosted.org/packages/78/db/87c3b59b0d4e753e40b6a3b4a2642dfd1dcaefbff121ddc64d6c8b47ba00/ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48", size = 11106083, upload-time = "2025-06-05T20:59:37.03Z" }, - { url = "https://files.pythonhosted.org/packages/77/79/d8cec175856ff810a19825d09ce700265f905c643c69f45d2b737e4a470a/ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b", size = 10436024, upload-time = "2025-06-05T20:59:39.741Z" }, - { url = "https://files.pythonhosted.org/packages/8b/5b/f6d94f2980fa1ee854b41568368a2e1252681b9238ab2895e133d303538f/ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a", size = 10646324, upload-time = "2025-06-05T20:59:42.185Z" }, - { url = "https://files.pythonhosted.org/packages/6c/9c/b4c2acf24ea4426016d511dfdc787f4ce1ceb835f3c5fbdbcb32b1c63bda/ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc", size = 10174416, upload-time = "2025-06-05T20:59:44.319Z" }, - { url = "https://files.pythonhosted.org/packages/f3/10/e2e62f77c65ede8cd032c2ca39c41f48feabedb6e282bfd6073d81bb671d/ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629", size = 11724197, upload-time = "2025-06-05T20:59:46.935Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f0/466fe8469b85c561e081d798c45f8a1d21e0b4a5ef795a1d7f1a9a9ec182/ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933", size = 12511615, upload-time = "2025-06-05T20:59:49.534Z" }, - { url = "https://files.pythonhosted.org/packages/17/0e/cefe778b46dbd0cbcb03a839946c8f80a06f7968eb298aa4d1a4293f3448/ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165", size = 12117080, upload-time = "2025-06-05T20:59:51.654Z" }, - { url = "https://files.pythonhosted.org/packages/5d/2c/caaeda564cbe103bed145ea557cb86795b18651b0f6b3ff6a10e84e5a33f/ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71", size = 11326315, upload-time = "2025-06-05T20:59:54.469Z" }, - { url = "https://files.pythonhosted.org/packages/75/f0/782e7d681d660eda8c536962920c41309e6dd4ebcea9a2714ed5127d44bd/ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9", size = 11555640, upload-time = "2025-06-05T20:59:56.986Z" }, - { url = "https://files.pythonhosted.org/packages/5d/d4/3d580c616316c7f07fb3c99dbecfe01fbaea7b6fd9a82b801e72e5de742a/ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc", size = 10507364, upload-time = "2025-06-05T20:59:59.154Z" }, - { url = "https://files.pythonhosted.org/packages/5a/dc/195e6f17d7b3ea6b12dc4f3e9de575db7983db187c378d44606e5d503319/ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7", size = 10141462, upload-time = "2025-06-05T21:00:01.481Z" }, - { url = "https://files.pythonhosted.org/packages/f4/8e/39a094af6967faa57ecdeacb91bedfb232474ff8c3d20f16a5514e6b3534/ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432", size = 11121028, upload-time = "2025-06-05T21:00:04.06Z" }, - { url = "https://files.pythonhosted.org/packages/5a/c0/b0b508193b0e8a1654ec683ebab18d309861f8bd64e3a2f9648b80d392cb/ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492", size = 11602992, upload-time = "2025-06-05T21:00:06.249Z" }, - { url = "https://files.pythonhosted.org/packages/7c/91/263e33ab93ab09ca06ce4f8f8547a858cc198072f873ebc9be7466790bae/ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250", size = 10474944, upload-time = "2025-06-05T21:00:08.459Z" }, - { url = "https://files.pythonhosted.org/packages/46/f4/7c27734ac2073aae8efb0119cae6931b6fb48017adf048fdf85c19337afc/ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3", size = 11548669, upload-time = "2025-06-05T21:00:11.147Z" }, - { url = "https://files.pythonhosted.org/packages/ec/bf/b273dd11673fed8a6bd46032c0ea2a04b2ac9bfa9c628756a5856ba113b0/ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b", size = 10683928, upload-time = "2025-06-05T21:00:13.758Z" }, + { url = "https://files.pythonhosted.org/packages/74/b6/2098d0126d2d3318fd5bec3ad40d06c25d377d95749f7a0c5af17129b3b1/ruff-0.12.2-py3-none-linux_armv6l.whl", hash = "sha256:093ea2b221df1d2b8e7ad92fc6ffdca40a2cb10d8564477a987b44fd4008a7be", size = 10369761, upload-time = "2025-07-03T16:39:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/b1/4b/5da0142033dbe155dc598cfb99262d8ee2449d76920ea92c4eeb9547c208/ruff-0.12.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:09e4cf27cc10f96b1708100fa851e0daf21767e9709e1649175355280e0d950e", size = 11155659, upload-time = "2025-07-03T16:39:42.294Z" }, + { url = "https://files.pythonhosted.org/packages/3e/21/967b82550a503d7c5c5c127d11c935344b35e8c521f52915fc858fb3e473/ruff-0.12.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8ae64755b22f4ff85e9c52d1f82644abd0b6b6b6deedceb74bd71f35c24044cc", size = 10537769, upload-time = "2025-07-03T16:39:44.75Z" }, + { url = "https://files.pythonhosted.org/packages/33/91/00cff7102e2ec71a4890fb7ba1803f2cdb122d82787c7d7cf8041fe8cbc1/ruff-0.12.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eb3a6b2db4d6e2c77e682f0b988d4d61aff06860158fdb413118ca133d57922", size = 10717602, upload-time = "2025-07-03T16:39:47.652Z" }, + { url = "https://files.pythonhosted.org/packages/9b/eb/928814daec4e1ba9115858adcda44a637fb9010618721937491e4e2283b8/ruff-0.12.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73448de992d05517170fc37169cbca857dfeaeaa8c2b9be494d7bcb0d36c8f4b", size = 10198772, upload-time = "2025-07-03T16:39:49.641Z" }, + { url = "https://files.pythonhosted.org/packages/50/fa/f15089bc20c40f4f72334f9145dde55ab2b680e51afb3b55422effbf2fb6/ruff-0.12.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b8b94317cbc2ae4a2771af641739f933934b03555e51515e6e021c64441532d", size = 11845173, upload-time = "2025-07-03T16:39:52.069Z" }, + { url = "https://files.pythonhosted.org/packages/43/9f/1f6f98f39f2b9302acc161a4a2187b1e3a97634fe918a8e731e591841cf4/ruff-0.12.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:45fc42c3bf1d30d2008023a0a9a0cfb06bf9835b147f11fe0679f21ae86d34b1", size = 12553002, upload-time = "2025-07-03T16:39:54.551Z" }, + { url = "https://files.pythonhosted.org/packages/d8/70/08991ac46e38ddd231c8f4fd05ef189b1b94be8883e8c0c146a025c20a19/ruff-0.12.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce48f675c394c37e958bf229fb5c1e843e20945a6d962cf3ea20b7a107dcd9f4", size = 12171330, upload-time = "2025-07-03T16:39:57.55Z" }, + { url = "https://files.pythonhosted.org/packages/88/a9/5a55266fec474acfd0a1c73285f19dd22461d95a538f29bba02edd07a5d9/ruff-0.12.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793d8859445ea47591272021a81391350205a4af65a9392401f418a95dfb75c9", size = 11774717, upload-time = "2025-07-03T16:39:59.78Z" }, + { url = "https://files.pythonhosted.org/packages/87/e5/0c270e458fc73c46c0d0f7cf970bb14786e5fdb88c87b5e423a4bd65232b/ruff-0.12.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6932323db80484dda89153da3d8e58164d01d6da86857c79f1961934354992da", size = 11646659, upload-time = "2025-07-03T16:40:01.934Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b6/45ab96070c9752af37f0be364d849ed70e9ccede07675b0ec4e3ef76b63b/ruff-0.12.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6aa7e623a3a11538108f61e859ebf016c4f14a7e6e4eba1980190cacb57714ce", size = 10604012, upload-time = "2025-07-03T16:40:04.363Z" }, + { url = "https://files.pythonhosted.org/packages/86/91/26a6e6a424eb147cc7627eebae095cfa0b4b337a7c1c413c447c9ebb72fd/ruff-0.12.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2a4a20aeed74671b2def096bdf2eac610c7d8ffcbf4fb0e627c06947a1d7078d", size = 10176799, upload-time = "2025-07-03T16:40:06.514Z" }, + { url = "https://files.pythonhosted.org/packages/f5/0c/9f344583465a61c8918a7cda604226e77b2c548daf8ef7c2bfccf2b37200/ruff-0.12.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:71a4c550195612f486c9d1f2b045a600aeba851b298c667807ae933478fcef04", size = 11241507, upload-time = "2025-07-03T16:40:08.708Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b7/99c34ded8fb5f86c0280278fa89a0066c3760edc326e935ce0b1550d315d/ruff-0.12.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4987b8f4ceadf597c927beee65a5eaf994c6e2b631df963f86d8ad1bdea99342", size = 11717609, upload-time = "2025-07-03T16:40:10.836Z" }, + { url = "https://files.pythonhosted.org/packages/51/de/8589fa724590faa057e5a6d171e7f2f6cffe3287406ef40e49c682c07d89/ruff-0.12.2-py3-none-win32.whl", hash = "sha256:369ffb69b70cd55b6c3fc453b9492d98aed98062db9fec828cdfd069555f5f1a", size = 10523823, upload-time = "2025-07-03T16:40:13.203Z" }, + { url = "https://files.pythonhosted.org/packages/94/47/8abf129102ae4c90cba0c2199a1a9b0fa896f6f806238d6f8c14448cc748/ruff-0.12.2-py3-none-win_amd64.whl", hash = "sha256:dca8a3b6d6dc9810ed8f328d406516bf4d660c00caeaef36eb831cf4871b0639", size = 11629831, upload-time = "2025-07-03T16:40:15.478Z" }, + { url = "https://files.pythonhosted.org/packages/e2/1f/72d2946e3cc7456bb837e88000eb3437e55f80db339c840c04015a11115d/ruff-0.12.2-py3-none-win_arm64.whl", hash = "sha256:48d6c6bfb4761df68bc05ae630e24f506755e702d4fb08f08460be778c7ccb12", size = 10735334, upload-time = "2025-07-03T16:40:17.677Z" }, ] [[package]] @@ -2503,27 +2504,27 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.21.1" +version = "0.21.2" 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, upload-time = "2025-03-13T10:51:18.189Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/2d/b0fce2b8201635f60e8c95990080f58461cc9ca3d5026de2e900f38a7f21/tokenizers-0.21.2.tar.gz", hash = "sha256:fdc7cffde3e2113ba0e6cc7318c40e3438a4d74bbc62bf04bcc63bdfb082ac77", size = 351545, upload-time = "2025-06-24T10:24:52.449Z" } 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, 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" }, + { url = "https://files.pythonhosted.org/packages/1d/cc/2936e2d45ceb130a21d929743f1e9897514691bec123203e10837972296f/tokenizers-0.21.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:342b5dfb75009f2255ab8dec0041287260fed5ce00c323eb6bab639066fef8ec", size = 2875206, upload-time = "2025-06-24T10:24:42.755Z" }, + { url = "https://files.pythonhosted.org/packages/6c/e6/33f41f2cc7861faeba8988e7a77601407bf1d9d28fc79c5903f8f77df587/tokenizers-0.21.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:126df3205d6f3a93fea80c7a8a266a78c1bd8dd2fe043386bafdd7736a23e45f", size = 2732655, upload-time = "2025-06-24T10:24:41.56Z" }, + { url = "https://files.pythonhosted.org/packages/33/2b/1791eb329c07122a75b01035b1a3aa22ad139f3ce0ece1b059b506d9d9de/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a32cd81be21168bd0d6a0f0962d60177c447a1aa1b1e48fa6ec9fc728ee0b12", size = 3019202, upload-time = "2025-06-24T10:24:31.791Z" }, + { url = "https://files.pythonhosted.org/packages/05/15/fd2d8104faa9f86ac68748e6f7ece0b5eb7983c7efc3a2c197cb98c99030/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8bd8999538c405133c2ab999b83b17c08b7fc1b48c1ada2469964605a709ef91", size = 2934539, upload-time = "2025-06-24T10:24:34.567Z" }, + { url = "https://files.pythonhosted.org/packages/a5/2e/53e8fd053e1f3ffbe579ca5f9546f35ac67cf0039ed357ad7ec57f5f5af0/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e9944e61239b083a41cf8fc42802f855e1dca0f499196df37a8ce219abac6eb", size = 3248665, upload-time = "2025-06-24T10:24:39.024Z" }, + { url = "https://files.pythonhosted.org/packages/00/15/79713359f4037aa8f4d1f06ffca35312ac83629da062670e8830917e2153/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:514cd43045c5d546f01142ff9c79a96ea69e4b5cda09e3027708cb2e6d5762ab", size = 3451305, upload-time = "2025-06-24T10:24:36.133Z" }, + { url = "https://files.pythonhosted.org/packages/38/5f/959f3a8756fc9396aeb704292777b84f02a5c6f25c3fc3ba7530db5feb2c/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1b9405822527ec1e0f7d8d2fdb287a5730c3a6518189c968254a8441b21faae", size = 3214757, upload-time = "2025-06-24T10:24:37.784Z" }, + { url = "https://files.pythonhosted.org/packages/c5/74/f41a432a0733f61f3d21b288de6dfa78f7acff309c6f0f323b2833e9189f/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed9a4d51c395103ad24f8e7eb976811c57fbec2af9f133df471afcd922e5020", size = 3121887, upload-time = "2025-06-24T10:24:40.293Z" }, + { url = "https://files.pythonhosted.org/packages/3c/6a/bc220a11a17e5d07b0dfb3b5c628621d4dcc084bccd27cfaead659963016/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c41862df3d873665ec78b6be36fcc30a26e3d4902e9dd8608ed61d49a48bc19", size = 9091965, upload-time = "2025-06-24T10:24:44.431Z" }, + { url = "https://files.pythonhosted.org/packages/6c/bd/ac386d79c4ef20dc6f39c4706640c24823dca7ebb6f703bfe6b5f0292d88/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed21dc7e624e4220e21758b2e62893be7101453525e3d23264081c9ef9a6d00d", size = 9053372, upload-time = "2025-06-24T10:24:46.455Z" }, + { url = "https://files.pythonhosted.org/packages/63/7b/5440bf203b2a5358f074408f7f9c42884849cd9972879e10ee6b7a8c3b3d/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:0e73770507e65a0e0e2a1affd6b03c36e3bc4377bd10c9ccf51a82c77c0fe365", size = 9298632, upload-time = "2025-06-24T10:24:48.446Z" }, + { url = "https://files.pythonhosted.org/packages/a4/d2/faa1acac3f96a7427866e94ed4289949b2524f0c1878512516567d80563c/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:106746e8aa9014a12109e58d540ad5465b4c183768ea96c03cbc24c44d329958", size = 9470074, upload-time = "2025-06-24T10:24:50.378Z" }, + { url = "https://files.pythonhosted.org/packages/d8/a5/896e1ef0707212745ae9f37e84c7d50269411aef2e9ccd0de63623feecdf/tokenizers-0.21.2-cp39-abi3-win32.whl", hash = "sha256:cabda5a6d15d620b6dfe711e1af52205266d05b379ea85a8a301b3593c60e962", size = 2330115, upload-time = "2025-06-24T10:24:55.069Z" }, + { url = "https://files.pythonhosted.org/packages/13/c3/cc2755ee10be859c4338c962a35b9a663788c0c0b50c0bdd8078fb6870cf/tokenizers-0.21.2-cp39-abi3-win_amd64.whl", hash = "sha256:58747bb898acdb1007f37a7bbe614346e98dc28708ffb66a3fd50ce169ac6c98", size = 2509918, upload-time = "2025-06-24T10:24:53.71Z" }, ] [[package]] @@ -2558,14 +2559,14 @@ wheels = [ [[package]] name = "types-requests" -version = "2.32.0.20250602" +version = "2.32.4.20250611" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/48/b0/5321e6eeba5d59e4347fcf9bf06a5052f085c3aa0f4876230566d6a4dc97/types_requests-2.32.0.20250602.tar.gz", hash = "sha256:ee603aeefec42051195ae62ca7667cd909a2f8128fdf8aad9e8a5219ecfab3bf", size = 23042, upload-time = "2025-06-02T03:15:02.958Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/7f/73b3a04a53b0fd2a911d4ec517940ecd6600630b559e4505cc7b68beb5a0/types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826", size = 23118, upload-time = "2025-06-11T03:11:41.272Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/18/9b782980e575c6581d5c0c1c99f4c6f89a1d7173dad072ee96b2756c02e6/types_requests-2.32.0.20250602-py3-none-any.whl", hash = "sha256:f4f335f87779b47ce10b8b8597b409130299f6971ead27fead4fe7ba6ea3e726", size = 20638, upload-time = "2025-06-02T03:15:01.959Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ea/0be9258c5a4fa1ba2300111aa5a0767ee6d18eb3fd20e91616c12082284d/types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072", size = 20643, upload-time = "2025-06-11T03:11:40.186Z" }, ] [[package]] @@ -2627,16 +2628,16 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.34.3" +version = "0.35.0" 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/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, ] [package.optional-dependencies] diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index b338b7b758..2c52595a26 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -56,7 +56,7 @@ custom_lint: allowed: # required / wanted - 'lib/infrastructure/repositories/album_media.repository.dart' - - 'lib/infrastructure/repositories/storage.repository.dart' + - 'lib/infrastructure/repositories/{storage,asset_media}.repository.dart' - 'lib/repositories/{album,asset,file}_media.repository.dart' # acceptable exceptions for the time being - lib/entities/asset.entity.dart # to provide local AssetEntity for now @@ -146,6 +146,7 @@ dart_code_metrics: # - no-empty-block # - no-equal-then-else # - prefer-correct-test-file-name + - prefer-const-border-radius # - prefer-match-file-name # - prefer-return-await # - avoid-self-assignment @@ -290,7 +291,8 @@ dart_code_metrics: # Style # - prefer-trailing-comma # - unnecessary-trailing-comma - # - prefer-declaring-const-constructor + - prefer-declaring-const-constructor # - prefer-single-widget-per-file + - prefer-switch-expression # - prefer-prefixed-global-constants # - prefer-correct-callback-field-name diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index 2179c9eb3c..6a96aa68e4 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -90,6 +90,35 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt index 6ae4c99bd7..b5ef90310e 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt @@ -87,7 +87,8 @@ data class PlatformAsset ( val updatedAt: Long? = null, val width: Long? = null, val height: Long? = null, - val durationInSeconds: Long + val durationInSeconds: Long, + val orientation: Long ) { companion object { @@ -100,7 +101,8 @@ data class PlatformAsset ( val width = pigeonVar_list[5] as Long? val height = pigeonVar_list[6] as Long? val durationInSeconds = pigeonVar_list[7] as Long - return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds) + val orientation = pigeonVar_list[8] as Long + return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation) } } fun toList(): List { @@ -113,6 +115,7 @@ data class PlatformAsset ( width, height, durationInSeconds, + orientation, ) } override fun equals(other: Any?): Boolean { diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt index 7f8ad531be..02cd54b8c3 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt @@ -5,6 +5,7 @@ import android.content.Context import android.database.Cursor import android.provider.MediaStore import android.util.Log +import androidx.core.database.getStringOrNull import java.io.File import java.io.FileInputStream import java.security.MessageDigest @@ -39,7 +40,8 @@ open class NativeSyncApiImplBase(context: Context) { MediaStore.MediaColumns.BUCKET_ID, MediaStore.MediaColumns.WIDTH, MediaStore.MediaColumns.HEIGHT, - MediaStore.MediaColumns.DURATION + MediaStore.MediaColumns.DURATION, + MediaStore.MediaColumns.ORIENTATION, ) const val HASH_BUFFER_SIZE = 2 * 1024 * 1024 @@ -73,6 +75,8 @@ open class NativeSyncApiImplBase(context: Context) { val widthColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.WIDTH) val heightColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.HEIGHT) val durationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION) + val orientationColumn = + c.getColumnIndexOrThrow(MediaStore.MediaColumns.ORIENTATION) while (c.moveToNext()) { val id = c.getLong(idColumn).toString() @@ -83,7 +87,11 @@ open class NativeSyncApiImplBase(context: Context) { continue } - val mediaType = c.getInt(mediaTypeColumn) + val mediaType = when (c.getInt(mediaTypeColumn)) { + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE -> 1 + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO -> 2 + else -> 0 + } val name = c.getString(nameColumn) // Date taken is milliseconds since epoch, Date added is seconds since epoch val createdAt = (c.getLong(dateTakenColumn).takeIf { it > 0 }?.div(1000)) @@ -96,6 +104,7 @@ open class NativeSyncApiImplBase(context: Context) { val duration = if (mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) 0 else c.getLong(durationColumn) / 1000 val bucketId = c.getString(bucketIdColumn) + val orientation = c.getInt(orientationColumn) val asset = PlatformAsset( id, @@ -105,7 +114,8 @@ open class NativeSyncApiImplBase(context: Context) { modifiedAt, width, height, - duration + duration, + orientation.toLong(), ) yield(AssetResult.ValidAsset(asset, bucketId)) } @@ -148,7 +158,8 @@ open class NativeSyncApiImplBase(context: Context) { continue } - val name = cursor.getString(bucketNameColumn) + // MediaStore might return null for bucket name (commonly for the Root Directory), so default to "Internal Storage" + val name = cursor.getStringOrNull(bucketNameColumn) ?: "Internal Storage" val updatedAt = cursor.getLong(dateModified) albums.add(PlatformAlbum(id, name, updatedAt, false, 0)) albumsCount[id] = 1 diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 20dfaaffad..31203fe55f 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" => 200, - "android.injected.version.name" => "1.134.0", + "android.injected.version.code" => 204, + "android.injected.version.name" => "1.135.3", } ) 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/android/gradle.properties b/mobile/android/gradle.properties index 78c37cc2a3..f63dcd33e1 100644 --- a/mobile/android/gradle.properties +++ b/mobile/android/gradle.properties @@ -2,4 +2,6 @@ org.gradle.jvmargs=-Xmx4096M android.useAndroidX=true android.enableJetifier=true android.nonTransitiveRClass=false -android.nonFinalResIds=false \ No newline at end of file +android.nonFinalResIds=false +org.gradle.caching=true +org.gradle.parallel=true diff --git a/mobile/build.yaml b/mobile/build.yaml index d5de77a377..76cc0a9988 100644 --- a/mobile/build.yaml +++ b/mobile/build.yaml @@ -17,6 +17,7 @@ targets: main: lib/infrastructure/repositories/db.repository.dart generate_for: &drift_generate_for - lib/infrastructure/entities/*.dart + - lib/infrastructure/entities/*.drift - lib/infrastructure/repositories/db.repository.dart drift_dev:modular: enabled: true diff --git a/mobile/drift_schemas/main/drift_schema_v1.json b/mobile/drift_schemas/main/drift_schema_v1.json index 0d147b9b22..4960cd0a0d 100644 --- a/mobile/drift_schemas/main/drift_schema_v1.json +++ b/mobile/drift_schemas/main/drift_schema_v1.json @@ -1 +1 @@ -{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"preferences","getter_name":"preferences","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userPreferenceConverter","dart_type_name":"UserPreferences"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[4,3],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":7,"references":[6],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":8,"references":[4],"type":"index","data":{"on":4,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[6],"type":"index","data":{"on":6,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}}]} \ No newline at end of file +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":4,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":6,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"preferences","getter_name":"preferences","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userPreferenceConverter","dart_type_name":"UserPreferences"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id"]}},{"id":7,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":8,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":9,"references":[2,8],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":10,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":11,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":12,"references":[1,11],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":13,"references":[11,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":14,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":15,"references":[1,14],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":16,"references":[0,1],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id)"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/integration_test/test_utils/login_helper.dart b/mobile/integration_test/test_utils/login_helper.dart index b3a867af68..cfbc5a9214 100644 --- a/mobile/integration_test/test_utils/login_helper.dart +++ b/mobile/integration_test/test_utils/login_helper.dart @@ -7,7 +7,7 @@ import 'general_helper.dart'; class ImmichTestLoginHelper { final WidgetTester tester; - ImmichTestLoginHelper(this.tester); + const ImmichTestLoginHelper(this.tester); Future waitForLoginScreen() async { await pumpUntilFound(tester, find.text("Login")); @@ -60,11 +60,11 @@ class ImmichTestLoginHelper { await tester.tap(button); } - Future assertLoginSuccess({int timeoutSeconds = 15}) async { + Future assertLoginSuccess() async { await pumpUntilFound(tester, find.text("home_page_building_timeline".tr())); } - Future assertLoginFailed({int timeoutSeconds = 15}) async { + Future assertLoginFailed() async { await pumpUntilFound(tester, find.text("login_form_failed_login".tr())); } } diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index 36ec03dd0e..09bd36022b 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -58,6 +58,8 @@ PODS: - Flutter - geolocator_apple (1.2.0): - Flutter + - home_widget (0.0.1): + - Flutter - image_picker_ios (0.0.1): - Flutter - integration_test (0.0.1): @@ -144,6 +146,7 @@ DEPENDENCIES: - flutter_web_auth_2 (from `.symlinks/plugins/flutter_web_auth_2/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`) + - home_widget (from `.symlinks/plugins/home_widget/ios`) - 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`) @@ -201,6 +204,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/fluttertoast/ios" geolocator_apple: :path: ".symlinks/plugins/geolocator_apple/ios" + home_widget: + :path: ".symlinks/plugins/home_widget/ios" image_picker_ios: :path: ".symlinks/plugins/image_picker_ios/ios" integration_test: @@ -256,6 +261,7 @@ SPEC CHECKSUMS: flutter_web_auth_2: 5c8d9dcd7848b5a9efb086d24e7a9adcae979c80 fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 geolocator_apple: 1560c3c875af2a412242c7a923e15d0d401966ff + home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e isar_flutter_libs: bc909e72c3d756c2759f14c8776c13b5b0556e26 diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index 3cbbf83f01..fb0908e8b6 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -17,12 +17,23 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; D218389C4A4C4693F141F7D1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */; }; + F02538E92DFBCBDD008C3FA3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + F0B57D3A2DF764BD00DC5BCC /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */; }; + F0B57D3C2DF764BD00DC5BCC /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0B57D3B2DF764BD00DC5BCC /* SwiftUI.framework */; }; + F0B57D492DF764BE00DC5BCC /* WidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = F0B57D382DF764BD00DC5BCC /* WidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; FAC6F89B2D287C890078CB2F /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = FAC6F8902D287C890078CB2F /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; FAC6F8B72D287F120078CB2F /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC6F8B52D287F120078CB2F /* ShareViewController.swift */; }; FAC6F8B92D287F120078CB2F /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FAC6F8B32D287F120078CB2F /* MainInterface.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + F0B57D472DF764BE00DC5BCC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = F0B57D372DF764BD00DC5BCC; + remoteInfo = WidgetExtension; + }; FAC6F8982D287C890078CB2F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 97C146E61CF9000F007C117D /* Project object */; @@ -49,6 +60,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + F0B57D492DF764BE00DC5BCC /* WidgetExtension.appex in Embed Foundation Extensions */, FAC6F89B2D287C890078CB2F /* ShareExtension.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; @@ -78,6 +90,9 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.profile.xcconfig"; sourceTree = ""; }; E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + F0B57D382DF764BD00DC5BCC /* WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + F0B57D3B2DF764BD00DC5BCC /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; F7101BB0391A314774615E89 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; F8A35EA3C3E01BD66AFDE0E5 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = ""; }; FA9973382CF6DF4B000EF859 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; @@ -89,6 +104,16 @@ FAC7416727DB9F5500C668D8 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + F0B57D4D2DF764BE00DC5BCC /* Exceptions for "WidgetExtension" folder in "WidgetExtension" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = F0B57D372DF764BD00DC5BCC /* WidgetExtension */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + /* Begin PBXFileSystemSynchronizedRootGroup section */ B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = { isa = PBXFileSystemSynchronizedRootGroup; @@ -97,6 +122,14 @@ path = Sync; sourceTree = ""; }; + F0B57D3D2DF764BD00DC5BCC /* WidgetExtension */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + F0B57D4D2DF764BE00DC5BCC /* Exceptions for "WidgetExtension" folder in "WidgetExtension" target */, + ); + path = WidgetExtension; + sourceTree = ""; + }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -108,6 +141,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F0B57D352DF764BD00DC5BCC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F0B57D3C2DF764BD00DC5BCC /* SwiftUI.framework in Frameworks */, + F0B57D3A2DF764BD00DC5BCC /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; FAC6F88D2D287C890078CB2F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -137,6 +179,8 @@ children = ( 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */, 357FC57E54FD0F51795CF28A /* Pods_ShareExtension.framework */, + F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */, + F0B57D3B2DF764BD00DC5BCC /* SwiftUI.framework */, ); name = Frameworks; sourceTree = ""; @@ -167,6 +211,7 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, FAC6F8B62D287F120078CB2F /* ShareExtension */, + F0B57D3D2DF764BD00DC5BCC /* WidgetExtension */, 97C146EF1CF9000F007C117D /* Products */, 0FB772A5B9601143383626CA /* Pods */, 1754452DD81DA6620E279E51 /* Frameworks */, @@ -178,6 +223,7 @@ children = ( 97C146EE1CF9000F007C117D /* Immich-Debug.app */, FAC6F8902D287C890078CB2F /* ShareExtension.appex */, + F0B57D382DF764BD00DC5BCC /* WidgetExtension.appex */, ); name = Products; sourceTree = ""; @@ -234,6 +280,7 @@ ); dependencies = ( FAC6F8992D287C890078CB2F /* PBXTargetDependency */, + F0B57D482DF764BE00DC5BCC /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( B2CF7F8C2DDE4EBB00744BF6 /* Sync */, @@ -243,6 +290,26 @@ productReference = 97C146EE1CF9000F007C117D /* Immich-Debug.app */; productType = "com.apple.product-type.application"; }; + F0B57D372DF764BD00DC5BCC /* WidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = F0B57D4E2DF764BE00DC5BCC /* Build configuration list for PBXNativeTarget "WidgetExtension" */; + buildPhases = ( + F0B57D342DF764BD00DC5BCC /* Sources */, + F0B57D352DF764BD00DC5BCC /* Frameworks */, + F0B57D362DF764BD00DC5BCC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + F0B57D3D2DF764BD00DC5BCC /* WidgetExtension */, + ); + name = WidgetExtension; + productName = WidgetExtension; + productReference = F0B57D382DF764BD00DC5BCC /* WidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; FAC6F88F2D287C890078CB2F /* ShareExtension */ = { isa = PBXNativeTarget; buildConfigurationList = FAC6F8A02D287C890078CB2F /* Build configuration list for PBXNativeTarget "ShareExtension" */; @@ -268,7 +335,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastSwiftUpdateCheck = 1600; + LastSwiftUpdateCheck = 1640; LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { @@ -277,6 +344,10 @@ LastSwiftMigration = 1100; ProvisioningStyle = Automatic; }; + F0B57D372DF764BD00DC5BCC = { + CreatedOnToolsVersion = 16.4; + ProvisioningStyle = Automatic; + }; FAC6F88F2D287C890078CB2F = { CreatedOnToolsVersion = 16.0; ProvisioningStyle = Automatic; @@ -298,6 +369,7 @@ targets = ( 97C146ED1CF9000F007C117D /* Runner */, FAC6F88F2D287C890078CB2F /* ShareExtension */, + F0B57D372DF764BD00DC5BCC /* WidgetExtension */, ); }; /* End PBXProject section */ @@ -314,6 +386,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F0B57D362DF764BD00DC5BCC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F02538E92DFBCBDD008C3FA3 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; FAC6F88E2D287C890078CB2F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -448,6 +528,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F0B57D342DF764BD00DC5BCC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; FAC6F88C2D287C890078CB2F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -459,6 +546,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + F0B57D482DF764BE00DC5BCC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F0B57D372DF764BD00DC5BCC /* WidgetExtension */; + targetProxy = F0B57D472DF764BE00DC5BCC /* PBXContainerItemProxy */; + }; FAC6F8992D287C890078CB2F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = FAC6F88F2D287C890078CB2F /* ShareExtension */; @@ -557,7 +649,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 208; + CURRENT_PROJECT_VERSION = 210; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -701,7 +793,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 208; + CURRENT_PROJECT_VERSION = 210; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -731,7 +823,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 208; + CURRENT_PROJECT_VERSION = 210; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -751,6 +843,129 @@ }; name = Release; }; + F0B57D4A2DF764BE00DC5BCC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 210; + DEVELOPMENT_TEAM = 2F67MQ8R79; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WidgetExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Widget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.Widget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + F0B57D4B2DF764BE00DC5BCC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 210; + DEVELOPMENT_TEAM = 2F67MQ8R79; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WidgetExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Widget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.Widget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + F0B57D4C2DF764BE00DC5BCC /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 210; + DEVELOPMENT_TEAM = 2F67MQ8R79; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WidgetExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Widget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.Widget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Profile; + }; FAC6F89C2D287C890078CB2F /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = F8A35EA3C3E01BD66AFDE0E5 /* Pods-ShareExtension.debug.xcconfig */; @@ -764,7 +979,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 208; + CURRENT_PROJECT_VERSION = 210; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -808,7 +1023,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 208; + CURRENT_PROJECT_VERSION = 210; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -849,7 +1064,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 208; + CURRENT_PROJECT_VERSION = 210; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -900,6 +1115,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F0B57D4E2DF764BE00DC5BCC /* Build configuration list for PBXNativeTarget "WidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F0B57D4A2DF764BE00DC5BCC /* Debug */, + F0B57D4B2DF764BE00DC5BCC /* Release */, + F0B57D4C2DF764BE00DC5BCC /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; FAC6F8A02D287C890078CB2F /* Build configuration list for PBXNativeTarget "ShareExtension" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index c59b4c4295..4237813dfc 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -78,7 +78,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.134.0 + 1.135.1 CFBundleSignature ???? CFBundleURLTypes @@ -86,14 +86,26 @@ CFBundleTypeRole Editor + CFBundleURLName + Share Extension CFBundleURLSchemes ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER) + + CFBundleTypeRole + Editor + CFBundleURLName + Deep Link + CFBundleURLSchemes + + immich + + CFBundleVersion - 208 + 210 FLTEnableImpeller ITSAppUsesNonExemptEncryption @@ -115,11 +127,13 @@ NSBonjourServices - _googlecast._tcp - _CC1AD845._googlecast._tcp + _googlecast._tcp + _CC1AD845._googlecast._tcp NSCameraUsageDescription We need to access the camera to let you take beautiful video using this app + NSFaceIDUsageDescription + We need to use FaceID to allow access to your locked folder NSLocationAlwaysAndWhenInUseUsageDescription We require this permission to access the local WiFi name for background upload mechanism NSLocationUsageDescription @@ -166,7 +180,8 @@ io.flutter.embedded_views_preview - NSFaceIDUsageDescription - We need to use FaceID to allow access to your locked folder + NSLocalNetworkUsageDescription + We need local network permission to connect to the local server using IP address and + allow the casting feature to work - + \ No newline at end of file diff --git a/mobile/ios/Runner/Runner.entitlements b/mobile/ios/Runner/Runner.entitlements index d558e35e0a..e5862cb213 100644 --- a/mobile/ios/Runner/Runner.entitlements +++ b/mobile/ios/Runner/Runner.entitlements @@ -2,6 +2,10 @@ + com.apple.developer.associated-domains + + applinks:my.immich.app + com.apple.developer.networking.wifi-info com.apple.security.application-groups diff --git a/mobile/ios/Runner/RunnerProfile.entitlements b/mobile/ios/Runner/RunnerProfile.entitlements index d44633db40..6a5c086baf 100644 --- a/mobile/ios/Runner/RunnerProfile.entitlements +++ b/mobile/ios/Runner/RunnerProfile.entitlements @@ -4,6 +4,10 @@ aps-environment development + com.apple.developer.associated-domains + + applinks:my.immich.app + com.apple.developer.networking.wifi-info com.apple.security.application-groups diff --git a/mobile/ios/Runner/Sync/Messages.g.swift b/mobile/ios/Runner/Sync/Messages.g.swift index 89eb092a16..e629604d6a 100644 --- a/mobile/ios/Runner/Sync/Messages.g.swift +++ b/mobile/ios/Runner/Sync/Messages.g.swift @@ -138,6 +138,7 @@ struct PlatformAsset: Hashable { var width: Int64? = nil var height: Int64? = nil var durationInSeconds: Int64 + var orientation: Int64 // swift-format-ignore: AlwaysUseLowerCamelCase @@ -150,6 +151,7 @@ struct PlatformAsset: Hashable { let width: Int64? = nilOrValue(pigeonVar_list[5]) let height: Int64? = nilOrValue(pigeonVar_list[6]) let durationInSeconds = pigeonVar_list[7] as! Int64 + let orientation = pigeonVar_list[8] as! Int64 return PlatformAsset( id: id, @@ -159,7 +161,8 @@ struct PlatformAsset: Hashable { updatedAt: updatedAt, width: width, height: height, - durationInSeconds: durationInSeconds + durationInSeconds: durationInSeconds, + orientation: orientation ) } func toList() -> [Any?] { @@ -172,6 +175,7 @@ struct PlatformAsset: Hashable { width, height, durationInSeconds, + orientation, ] } static func == (lhs: PlatformAsset, rhs: PlatformAsset) -> Bool { diff --git a/mobile/ios/Runner/Sync/MessagesImpl.swift b/mobile/ios/Runner/Sync/MessagesImpl.swift index 861e8196c5..459e29fa5a 100644 --- a/mobile/ios/Runner/Sync/MessagesImpl.swift +++ b/mobile/ios/Runner/Sync/MessagesImpl.swift @@ -27,7 +27,8 @@ extension PHAsset { updatedAt: modificationDate.map { Int64($0.timeIntervalSince1970) }, width: Int64(pixelWidth), height: Int64(pixelHeight), - durationInSeconds: Int64(duration) + durationInSeconds: Int64(duration), + orientation: 0 ) } } @@ -36,6 +37,7 @@ class NativeSyncApiImpl: NativeSyncApi { private let defaults: UserDefaults private let changeTokenKey = "immich:changeToken" private let albumTypes: [PHAssetCollectionType] = [.album, .smartAlbum] + private let recoveredAlbumSubType = 1000000219 private let hashBufferSize = 2 * 1024 * 1024 @@ -91,9 +93,17 @@ class NativeSyncApiImpl: NativeSyncApi { albumTypes.forEach { type in let collections = PHAssetCollection.fetchAssetCollections(with: type, subtype: .any, options: nil) - collections.enumerateObjects { (album, _, _) in + for i in 0.. %@ OR modificationDate > %@", date, date) + options.includeHiddenAssets = false let assets = PHAsset.fetchAssets(in: album, options: options) return Int64(assets.count) } @@ -230,6 +247,7 @@ class NativeSyncApiImpl: NativeSyncApi { } let options = PHFetchOptions() + options.includeHiddenAssets = false if(updatedTimeCond != nil) { let date = NSDate(timeIntervalSince1970: TimeInterval(updatedTimeCond!)) options.predicate = NSPredicate(format: "creationDate > %@ OR modificationDate > %@", date, date) diff --git a/mobile/ios/WidgetExtension/Assets.xcassets/Contents.json b/mobile/ios/WidgetExtension/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/mobile/ios/WidgetExtension/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/mobile/ios/WidgetExtension/ImageEntry.swift b/mobile/ios/WidgetExtension/ImageEntry.swift new file mode 100644 index 0000000000..5239702d4b --- /dev/null +++ b/mobile/ios/WidgetExtension/ImageEntry.swift @@ -0,0 +1,161 @@ +import SwiftUI +import WidgetKit + +typealias EntryMetadata = ImageEntry.Metadata + +struct ImageEntry: TimelineEntry { + let date: Date + var image: UIImage? + var metadata: Metadata = Metadata() + + struct Metadata: Codable { + var subtitle: String? = nil + var error: WidgetError? = nil + var deepLink: URL? = nil + } + + // Resizes the stored image to a maximum width of 450 pixels + mutating func resize() { + if image == nil || image!.size.height < 450 || image!.size.width < 450 { + return + } + + image = image?.resized(toWidth: 450) + + if image == nil { + metadata.error = .unableToResize + } + } + + static func build( + api: ImmichAPI, + asset: Asset, + dateOffset: Int, + subtitle: String? = nil + ) + async throws -> Self + { + let entryDate = Calendar.current.date( + byAdding: .minute, + value: dateOffset * 20, + to: Date.now + )! + let image = try await api.fetchImage(asset: asset) + + return Self( + date: entryDate, + image: image, + metadata: EntryMetadata( + subtitle: subtitle, + deepLink: asset.deepLink + ) + ) + } + + func cache(for key: String) throws { + if let containerURL = FileManager.default.containerURL( + forSecurityApplicationGroupIdentifier: IMMICH_SHARE_GROUP + ) { + let imageURL = containerURL.appendingPathComponent("\(key)_image.png") + let metadataURL = containerURL.appendingPathComponent( + "\(key)_metadata.json" + ) + + // build metadata JSON + let entryMetadata = try JSONEncoder().encode(self.metadata) + + // write to disk + try self.image?.pngData()?.write(to: imageURL, options: .atomic) + try entryMetadata.write(to: metadataURL, options: .atomic) + } + } + + static func loadCached(for key: String, at date: Date = Date.now) + -> ImageEntry? + { + if let containerURL = FileManager.default.containerURL( + forSecurityApplicationGroupIdentifier: IMMICH_SHARE_GROUP + ) { + let imageURL = containerURL.appendingPathComponent("\(key)_image.png") + let metadataURL = containerURL.appendingPathComponent( + "\(key)_metadata.json" + ) + + guard let imageData = try? Data(contentsOf: imageURL), + let metadataJSON = try? Data(contentsOf: metadataURL), + let decodedMetadata = try? JSONDecoder().decode(Metadata.self, from: metadataJSON) + else { + return nil + } + + return ImageEntry( + date: date, + image: UIImage(data: imageData), + metadata: decodedMetadata + ) + } + + return nil + } + + static func handleCacheFallback( + for key: String, + error: WidgetError = .fetchFailed + ) -> Timeline { + var timelineEntry = ImageEntry( + date: Date.now, + image: nil, + metadata: EntryMetadata(error: error) + ) + + // skip cache if album not found or no login + // we want to show these errors to the user since without intervention, + // it will never succeed + if error != .noLogin && error != .albumNotFound { + if let cachedEntry = ImageEntry.loadCached(for: key) { + timelineEntry = cachedEntry + } + } + + return Timeline(entries: [timelineEntry], policy: .atEnd) + } +} + +func generateRandomEntries( + api: ImmichAPI, + now: Date, + count: Int, + albumId: String? = nil, + subtitle: String? = nil +) + async throws -> [ImageEntry] +{ + + var entries: [ImageEntry] = [] + let albumIds = albumId != nil ? [albumId!] : [] + + let randomAssets = try await api.fetchSearchResults( + with: SearchFilters(size: count, albumIds: albumIds) + ) + + await withTaskGroup(of: ImageEntry?.self) { group in + for (dateOffset, asset) in randomAssets.enumerated() { + group.addTask { + return try? await ImageEntry.build( + api: api, + asset: asset, + dateOffset: dateOffset, + subtitle: subtitle + ) + } + } + + for await result in group { + if let entry = result { + entries.append(entry) + } + } + } + + return entries +} diff --git a/mobile/ios/WidgetExtension/ImageWidgetView.swift b/mobile/ios/WidgetExtension/ImageWidgetView.swift new file mode 100644 index 0000000000..24072b0607 --- /dev/null +++ b/mobile/ios/WidgetExtension/ImageWidgetView.swift @@ -0,0 +1,57 @@ +import SwiftUI +import WidgetKit + +struct ImmichWidgetView: View { + var entry: ImageEntry + + var body: some View { + if entry.image == nil { + VStack { + Image("LaunchImage") + Text(entry.metadata.error?.errorDescription ?? "") + .minimumScaleFactor(0.25) + .multilineTextAlignment(.center) + .foregroundStyle(.secondary) + } + .padding(16) + } else { + ZStack(alignment: .leading) { + Color.clear.overlay( + Image(uiImage: entry.image!) + .resizable() + .scaledToFill() + ) + VStack { + Spacer() + if let subtitle = entry.metadata.subtitle { + Text(subtitle) + .foregroundColor(.white) + .padding(8) + .background(Color.black.opacity(0.6)) + .cornerRadius(8) + .font(.system(size: 16)) + } + } + .padding(16) + } + .widgetURL(entry.metadata.deepLink) + } + } +} + +#Preview( + as: .systemMedium, + widget: { + ImmichRandomWidget() + }, + timeline: { + let date = Date() + ImageEntry( + date: date, + image: UIImage(named: "ImmichLogo"), + metadata: EntryMetadata( + subtitle: "1 year ago" + ) + ) + } +) diff --git a/mobile/ios/WidgetExtension/ImmichAPI.swift b/mobile/ios/WidgetExtension/ImmichAPI.swift new file mode 100644 index 0000000000..7cefd9d5ee --- /dev/null +++ b/mobile/ios/WidgetExtension/ImmichAPI.swift @@ -0,0 +1,266 @@ +import Foundation +import SwiftUI +import WidgetKit + +enum WidgetError: Error, Codable { + case noLogin + case fetchFailed + case unknown + case albumNotFound + case unableToResize + case invalidImage + case invalidURL +} + +extension WidgetError: LocalizedError { + public var errorDescription: String? { + switch self { + case .noLogin: + return "Login to Immich" + + case .fetchFailed: + return "Unable to connect to your Immich instance" + + case .albumNotFound: + return "Album not found" + + case .invalidURL: + return "An invalid URL was used" + + case .invalidImage: + return "An invalid image was received" + + default: + return "An unknown error occured" + } + } +} + +enum AssetType: String, Codable { + case image = "IMAGE" + case video = "VIDEO" + case audio = "AUDIO" + case other = "OTHER" +} + +struct Asset: Codable { + let id: String + let type: AssetType + + var deepLink: URL? { + return URL(string: "immich://asset?id=\(id)") + } +} + +struct SearchFilters: Codable { + var type: AssetType = .image + let size: Int + var albumIds: [String] = [] +} + +struct MemoryResult: Codable { + let id: String + var assets: [Asset] + let type: String + + struct MemoryData: Codable { + let year: Int + } + + let data: MemoryData +} + +struct Album: Codable { + let id: String + let albumName: String +} + +let IMMICH_SHARE_GROUP = "group.app.immich.share" + +// MARK: API + +class ImmichAPI { + struct ServerConfig { + let serverEndpoint: String + let sessionKey: String + } + let serverConfig: ServerConfig + + init() async throws { + // fetch the credentials from the UserDefaults store that dart placed here + guard let defaults = UserDefaults(suiteName: IMMICH_SHARE_GROUP), + let serverURL = defaults.string(forKey: "widget_server_url"), + let sessionKey = defaults.string(forKey: "widget_auth_token") + else { + throw WidgetError.noLogin + } + + if serverURL == "" || sessionKey == "" { + throw WidgetError.noLogin + } + + serverConfig = ServerConfig( + serverEndpoint: serverURL, + sessionKey: sessionKey + ) + } + + private func buildRequestURL( + serverConfig: ServerConfig, + endpoint: String, + params: [URLQueryItem] = [] + ) -> URL? { + guard let baseURL = URL(string: serverConfig.serverEndpoint) else { + fatalError("Invalid base URL") + } + + // Combine the base URL and API path + let fullPath = baseURL.appendingPathComponent( + endpoint.trimmingCharacters(in: CharacterSet(charactersIn: "/")) + ) + + // Add the session key as a query parameter + var components = URLComponents( + url: fullPath, + resolvingAgainstBaseURL: false + ) + components?.queryItems = [ + URLQueryItem(name: "sessionKey", value: serverConfig.sessionKey) + ] + components?.queryItems?.append(contentsOf: params) + + return components?.url + } + + func fetchSearchResults(with filters: SearchFilters) async throws + -> [Asset] + { + // get URL + guard + let searchURL = buildRequestURL( + serverConfig: serverConfig, + endpoint: "/search/random" + ) + else { + throw URLError(.badURL) + } + + var request = URLRequest(url: searchURL) + request.httpMethod = "POST" + request.httpBody = try JSONEncoder().encode(filters) + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + let (data, _) = try await URLSession.shared.data(for: request) + + // decode data + return try JSONDecoder().decode([Asset].self, from: data) + } + + func fetchMemory(for date: Date) async throws -> [MemoryResult] { + // get URL + let memoryParams = [URLQueryItem(name: "for", value: date.ISO8601Format())] + guard + let searchURL = buildRequestURL( + serverConfig: serverConfig, + endpoint: "/memories", + params: memoryParams + ) + else { + throw URLError(.badURL) + } + + var request = URLRequest(url: searchURL) + request.httpMethod = "GET" + + let (data, _) = try await URLSession.shared.data(for: request) + + // decode data + return try JSONDecoder().decode([MemoryResult].self, from: data) + } + + func fetchImage(asset: Asset) async throws(WidgetError) -> UIImage { + let thumbnailParams = [URLQueryItem(name: "size", value: "preview")] + let assetEndpoint = "/assets/" + asset.id + "/thumbnail" + + guard + let fetchURL = buildRequestURL( + serverConfig: serverConfig, + endpoint: assetEndpoint, + params: thumbnailParams + ) + else { + throw .invalidURL + } + + guard let imageSource = CGImageSourceCreateWithURL(fetchURL as CFURL, nil) + else { + throw .invalidURL + } + + let decodeOptions: [NSString: Any] = [ + kCGImageSourceCreateThumbnailFromImageAlways: true, + kCGImageSourceThumbnailMaxPixelSize: 400, + kCGImageSourceCreateThumbnailWithTransform: true, + ] + + guard + let thumbnail = CGImageSourceCreateThumbnailAtIndex( + imageSource, + 0, + decodeOptions as CFDictionary + ) + else { + throw .fetchFailed + } + + return UIImage(cgImage: thumbnail) + } + + func fetchAlbums() async throws -> [Album] { + // get URL + guard + let searchURL = buildRequestURL( + serverConfig: serverConfig, + endpoint: "/albums" + ) + else { + throw URLError(.badURL) + } + + var request = URLRequest(url: searchURL) + request.httpMethod = "GET" + + let (data, _) = try await URLSession.shared.data(for: request) + + // decode data + return try JSONDecoder().decode([Album].self, from: data) + } +} + +// We need a shared cache for albums to efficiently handle the album picker queries +actor AlbumCache { + static let shared = AlbumCache() + + private var api: ImmichAPI? = nil + private var albums: [Album]? = nil + + func getAlbums(refresh: Bool = false) async throws -> [Album] { + // Check the API before we try to show cached albums + // Sometimes iOS caches this object and keeps it around + // even after nuking the timeline + + api = try? await ImmichAPI() + + guard api != nil else { + throw WidgetError.noLogin + } + + if let albums, !refresh { + return albums + } + + let fetched = try await api!.fetchAlbums() + albums = fetched + return fetched + } +} diff --git a/mobile/ios/WidgetExtension/Info.plist b/mobile/ios/WidgetExtension/Info.plist new file mode 100644 index 0000000000..0f118fb75e --- /dev/null +++ b/mobile/ios/WidgetExtension/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/mobile/ios/WidgetExtension/UIImage+Resize.swift b/mobile/ios/WidgetExtension/UIImage+Resize.swift new file mode 100644 index 0000000000..030f354ca4 --- /dev/null +++ b/mobile/ios/WidgetExtension/UIImage+Resize.swift @@ -0,0 +1,23 @@ +// +// Utils.swift +// Runner +// +// Created by Alex Tran and Brandon Wees on 6/16/25. +// +import UIKit + +extension UIImage { + /// Crops the image to ensure width and height do not exceed maxSize. + /// Keeps original aspect ratio and crops excess equally from edges (center crop). + func resized(toWidth width: CGFloat, isOpaque: Bool = true) -> UIImage? { + let canvas = CGSize( + width: width, + height: CGFloat(ceil(width / size.width * size.height)) + ) + let format = imageRendererFormat + format.opaque = isOpaque + return UIGraphicsImageRenderer(size: canvas, format: format).image { + _ in draw(in: CGRect(origin: .zero, size: canvas)) + } + } +} diff --git a/mobile/ios/WidgetExtension/WidgetBundle.swift b/mobile/ios/WidgetExtension/WidgetBundle.swift new file mode 100644 index 0000000000..2f125608e4 --- /dev/null +++ b/mobile/ios/WidgetExtension/WidgetBundle.swift @@ -0,0 +1,10 @@ +import SwiftUI +import WidgetKit + +@main +struct ImmichWidgetBundle: WidgetBundle { + var body: some Widget { + ImmichRandomWidget() + ImmichMemoryWidget() + } +} diff --git a/mobile/ios/WidgetExtension/WidgetExtension.entitlements b/mobile/ios/WidgetExtension/WidgetExtension.entitlements new file mode 100644 index 0000000000..4ad1a257d8 --- /dev/null +++ b/mobile/ios/WidgetExtension/WidgetExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.app.immich.share + + + diff --git a/mobile/ios/WidgetExtension/widgets/MemoryWidget.swift b/mobile/ios/WidgetExtension/widgets/MemoryWidget.swift new file mode 100644 index 0000000000..da7b887c37 --- /dev/null +++ b/mobile/ios/WidgetExtension/widgets/MemoryWidget.swift @@ -0,0 +1,169 @@ +import AppIntents +import SwiftUI +import WidgetKit + +struct ImmichMemoryProvider: TimelineProvider { + func getYearDifferenceSubtitle(assetYear: Int) -> String { + let currentYear = Calendar.current.component(.year, from: Date.now) + // construct a "X years ago" subtitle + let yearDifference = currentYear - assetYear + + return "\(yearDifference) year\(yearDifference == 1 ? "" : "s") ago" + } + + func placeholder(in context: Context) -> ImageEntry { + ImageEntry(date: Date(), image: nil) + } + + func getSnapshot( + in context: Context, + completion: @escaping @Sendable (ImageEntry) -> Void + ) { + let cacheKey = "memory_\(context.family.rawValue)" + + Task { + guard let api = try? await ImmichAPI() else { + completion(ImageEntry.handleCacheFallback(for: cacheKey, error: .noLogin).entries.first!) + return + } + + guard let memories = try? await api.fetchMemory(for: Date.now) + else { + completion(ImageEntry.handleCacheFallback(for: cacheKey).entries.first!) + return + } + + for memory in memories { + if let asset = memory.assets.first(where: { $0.type == .image }), + var entry = try? await ImageEntry.build( + api: api, + asset: asset, + dateOffset: 0, + subtitle: getYearDifferenceSubtitle(assetYear: memory.data.year) + ) + { + entry.resize() + completion(entry) + return + } + } + + // fallback to random image + guard + let randomImage = try? await api.fetchSearchResults( + with: SearchFilters(size: 1) + ).first, + var imageEntry = try? await ImageEntry.build( + api: api, + asset: randomImage, + dateOffset: 0 + ) + else { + completion(ImageEntry.handleCacheFallback(for: cacheKey).entries.first!) + return + } + + imageEntry.resize() + completion(imageEntry) + } + } + + func getTimeline( + in context: Context, + completion: @escaping @Sendable (Timeline) -> Void + ) { + Task { + var entries: [ImageEntry] = [] + let now = Date() + + let cacheKey = "memory_\(context.family.rawValue)" + + guard let api = try? await ImmichAPI() else { + completion( + ImageEntry.handleCacheFallback(for: cacheKey, error: .noLogin) + ) + return + } + + let memories = try await api.fetchMemory(for: Date.now) + + await withTaskGroup(of: ImageEntry?.self) { group in + var totalAssets = 0 + + for memory in memories { + for asset in memory.assets { + if asset.type == .image && totalAssets < 12 { + group.addTask { + try? await ImageEntry.build( + api: api, + asset: asset, + dateOffset: totalAssets, + subtitle: getYearDifferenceSubtitle( + assetYear: memory.data.year + ) + ) + } + + totalAssets += 1 + } + } + } + + for await result in group { + if let entry = result { + entries.append(entry) + } + } + } + + // If we didnt add any memory images (some failure occured or no images in memory), + // default to 12 hours of random photos + if entries.count == 0 { + entries.append( + contentsOf: (try? await generateRandomEntries( + api: api, + now: now, + count: 12 + )) ?? [] + ) + } + + // If we fail to fetch images, we still want to add an entry + // with a nil image and an error + if entries.count == 0 { + completion(ImageEntry.handleCacheFallback(for: cacheKey)) + return + } + + // Resize all images to something that can be stored by iOS + for i in entries.indices { + entries[i].resize() + } + + // cache the last image + try? entries.last!.cache(for: cacheKey) + + completion(Timeline(entries: entries, policy: .atEnd)) + } + } +} + +struct ImmichMemoryWidget: Widget { + let kind: String = "com.immich.widget.memory" + + var body: some WidgetConfiguration { + StaticConfiguration( + kind: kind, + provider: ImmichMemoryProvider() + ) { entry in + ImmichWidgetView(entry: entry) + .containerBackground(.regularMaterial, for: .widget) + } + // allow image to take up entire widget + .contentMarginsDisabled() + + // widget picker info + .configurationDisplayName("Memories") + .description("See memories from Immich.") + } +} diff --git a/mobile/ios/WidgetExtension/widgets/RandomWidget.swift b/mobile/ios/WidgetExtension/widgets/RandomWidget.swift new file mode 100644 index 0000000000..8f9143cedd --- /dev/null +++ b/mobile/ios/WidgetExtension/widgets/RandomWidget.swift @@ -0,0 +1,174 @@ +import AppIntents +import SwiftUI +import WidgetKit + +// MARK: Widget Configuration + +extension Album: @unchecked Sendable, AppEntity, Identifiable { + + struct AlbumQuery: EntityQuery { + func entities(for identifiers: [Album.ID]) async throws -> [Album] { + // use cached albums to search + var albums = (try? await AlbumCache.shared.getAlbums()) ?? [] + albums.insert(NO_ALBUM, at: 0) + + return albums.filter { + identifiers.contains($0.id) + } + } + + func suggestedEntities() async throws -> [Album] { + var albums = (try? await AlbumCache.shared.getAlbums(refresh: true)) ?? [] + albums.insert(NO_ALBUM, at: 0) + + return albums + } + } + + static var defaultQuery = AlbumQuery() + static var typeDisplayRepresentation = TypeDisplayRepresentation( + name: "Album" + ) + + var displayRepresentation: DisplayRepresentation { + DisplayRepresentation(title: "\(albumName)") + } +} + +let NO_ALBUM = Album(id: "NONE", albumName: "None") + +struct RandomConfigurationAppIntent: WidgetConfigurationIntent { + static var title: LocalizedStringResource { "Select Album" } + static var description: IntentDescription { + "Choose an album to show images from" + } + + @Parameter(title: "Album") + var album: Album? + + @Parameter(title: "Show Album Name", default: false) + var showAlbumName: Bool +} + +// MARK: Provider + +struct ImmichRandomProvider: AppIntentTimelineProvider { + func placeholder(in context: Context) -> ImageEntry { + ImageEntry(date: Date()) + } + + func snapshot( + for configuration: RandomConfigurationAppIntent, + in context: Context + ) async + -> ImageEntry + { + let cacheKey = "random_none_\(context.family.rawValue)" + + guard let api = try? await ImmichAPI() else { + return ImageEntry.handleCacheFallback(for: cacheKey, error: .noLogin).entries.first! + } + + guard + let randomImage = try? await api.fetchSearchResults( + with: SearchFilters(size: 1) + ).first, + var entry = try? await ImageEntry.build( + api: api, + asset: randomImage, + dateOffset: 0 + ) + else { + return ImageEntry.handleCacheFallback(for: cacheKey).entries.first! + } + + entry.resize() + + return entry + } + + func timeline( + for configuration: RandomConfigurationAppIntent, + in context: Context + ) async + -> Timeline + { + var entries: [ImageEntry] = [] + let now = Date() + + // nil if album is NONE or nil + let albumId = + configuration.album?.id != "NONE" ? configuration.album?.id : nil + let albumName: String? = + albumId != nil ? configuration.album?.albumName : nil + + let cacheKey = "random_\(albumId ?? "none")_\(context.family.rawValue)" + + // If we don't have a server config, return an entry with an error + guard let api = try? await ImmichAPI() else { + return ImageEntry.handleCacheFallback(for: cacheKey, error: .noLogin) + } + + if albumId != nil { + // make sure the album exists on server, otherwise show error + guard let albums = try? await api.fetchAlbums() else { + return ImageEntry.handleCacheFallback(for: cacheKey) + } + + if !albums.contains(where: { $0.id == albumId }) { + return ImageEntry.handleCacheFallback( + for: cacheKey, + error: .albumNotFound + ) + } + } + + // build entries + entries.append( + contentsOf: (try? await generateRandomEntries( + api: api, + now: now, + count: 12, + albumId: albumId, + subtitle: configuration.showAlbumName ? albumName : nil + )) + ?? [] + ) + + // Load or save a cached asset for when network conditions are bad + if entries.count == 0 { + return ImageEntry.handleCacheFallback(for: cacheKey) + } + + // Resize all images to something that can be stored by iOS + for i in entries.indices { + entries[i].resize() + } + + // cache the last image + try? entries.last!.cache(for: cacheKey) + + return Timeline(entries: entries, policy: .atEnd) + } +} + +struct ImmichRandomWidget: Widget { + let kind: String = "com.immich.widget.random" + + var body: some WidgetConfiguration { + AppIntentConfiguration( + kind: kind, + intent: RandomConfigurationAppIntent.self, + provider: ImmichRandomProvider() + ) { entry in + ImmichWidgetView(entry: entry) + .containerBackground(.regularMaterial, for: .widget) + } + // allow image to take up entire widget + .contentMarginsDisabled() + + // widget picker info + .configurationDisplayName("Random") + .description("View a random image from your library or a specific album.") + } +} diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 1d93a13568..4c60a2e831 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.134.0" + version_number: "1.135.3" ) 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 8c95922a3a..6d98152efc 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -15,3 +15,20 @@ const int kBatchHashSizeLimit = 1024 * 1024 * 1024; // 1GB // Secure storage keys const String kSecuredPinCode = "secured_pin_code"; + +// Timeline constants +const int kTimelineNoneSegmentSize = 120; +const int kTimelineAssetLoadBatchSize = 256; +const int kTimelineAssetLoadOppositeSize = 64; + +// Widget keys +const String kWidgetAuthToken = "widget_auth_token"; +const String kWidgetServerEndpoint = "widget_server_url"; +const String appShareGroupId = "group.app.immich.share"; + +// add widget identifiers here for new widgets +// these are used to force a widget refresh +const List kWidgetNames = [ + 'com.immich.widget.random', + 'com.immich.widget.memory', +]; diff --git a/mobile/lib/constants/enums.dart b/mobile/lib/constants/enums.dart index a691263a1e..febc71032e 100644 --- a/mobile/lib/constants/enums.dart +++ b/mobile/lib/constants/enums.dart @@ -10,3 +10,7 @@ enum TextSearchType { } enum AssetVisibilityEnum { timeline, hidden, archive, locked } + +enum SortUserBy { id } + +enum ActionSource { timeline, viewer } diff --git a/mobile/lib/constants/errors.dart b/mobile/lib/constants/errors.dart index 3d1f775033..0ee1195737 100644 --- a/mobile/lib/constants/errors.dart +++ b/mobile/lib/constants/errors.dart @@ -4,6 +4,8 @@ sealed class ImmichErrors { } class NoResponseDtoError extends ImmichErrors implements Exception { + const NoResponseDtoError(); + @override String toString() => "Response Dto is null"; } diff --git a/mobile/lib/constants/locales.dart b/mobile/lib/constants/locales.dart index 658242ea3a..b2d8df525b 100644 --- a/mobile/lib/constants/locales.dart +++ b/mobile/lib/constants/locales.dart @@ -11,6 +11,7 @@ const Map locales = { Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'), 'Chinese Traditional (zh_TW)': Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), + 'Croatian (hr)': Locale('hr'), 'Czech (cs)': Locale('cs'), 'Danish (da)': Locale('da'), 'Dutch (nl)': Locale('nl'), diff --git a/mobile/lib/domain/interfaces/device_asset.interface.dart b/mobile/lib/domain/interfaces/device_asset.interface.dart deleted file mode 100644 index 1df8cc2250..0000000000 --- a/mobile/lib/domain/interfaces/device_asset.interface.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'dart:async'; - -import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/domain/models/device_asset.model.dart'; - -abstract interface class IDeviceAssetRepository implements IDatabaseRepository { - Future updateAll(List assetHash); - - Future> getByIds(List localIds); - - Future deleteIds(List ids); -} diff --git a/mobile/lib/domain/interfaces/exif.interface.dart b/mobile/lib/domain/interfaces/exif.interface.dart deleted file mode 100644 index a5de6167e9..0000000000 --- a/mobile/lib/domain/interfaces/exif.interface.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/domain/models/exif.model.dart'; - -abstract interface class IExifInfoRepository implements IDatabaseRepository { - Future get(int assetId); - - Future update(ExifInfo exifInfo); - - Future> updateAll(List exifInfos); - - Future delete(int assetId); - - Future deleteAll(); -} diff --git a/mobile/lib/domain/interfaces/local_album.interface.dart b/mobile/lib/domain/interfaces/local_album.interface.dart deleted file mode 100644 index 1df62954d2..0000000000 --- a/mobile/lib/domain/interfaces/local_album.interface.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/domain/models/local_album.model.dart'; - -abstract interface class ILocalAlbumRepository implements IDatabaseRepository { - Future> getAll({Set sortBy = const {}}); - - Future> getAssets(String albumId); - - Future> getAssetIds(String albumId); - - Future upsert( - LocalAlbum album, { - Iterable toUpsert = const [], - Iterable toDelete = const [], - }); - - Future updateAll(Iterable albums); - - Future delete(String albumId); - - Future processDelta({ - required List updates, - required List deletes, - required Map> assetAlbums, - }); - - Future syncDeletes(String albumId, Iterable assetIdsToKeep); - - Future> getAssetsToHash(String albumId); -} - -enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum } diff --git a/mobile/lib/domain/interfaces/local_asset.interface.dart b/mobile/lib/domain/interfaces/local_asset.interface.dart deleted file mode 100644 index 5792ebe5d9..0000000000 --- a/mobile/lib/domain/interfaces/local_asset.interface.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; - -abstract interface class ILocalAssetRepository implements IDatabaseRepository { - Future updateHashes(Iterable hashes); -} diff --git a/mobile/lib/domain/interfaces/log.interface.dart b/mobile/lib/domain/interfaces/log.interface.dart deleted file mode 100644 index 27e91c5488..0000000000 --- a/mobile/lib/domain/interfaces/log.interface.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'dart:async'; - -import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/domain/models/log.model.dart'; - -abstract interface class ILogRepository implements IDatabaseRepository { - Future insert(LogMessage log); - - Future insertAll(Iterable logs); - - Future> getAll(); - - Future deleteAll(); - - /// Truncates the logs to the most recent [limit]. Defaults to recent 250 logs - Future truncate({int limit = 250}); -} diff --git a/mobile/lib/domain/interfaces/storage.interface.dart b/mobile/lib/domain/interfaces/storage.interface.dart deleted file mode 100644 index ea6513e7f2..0000000000 --- a/mobile/lib/domain/interfaces/storage.interface.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'dart:io'; - -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; - -abstract interface class IStorageRepository { - Future getFileForAsset(LocalAsset asset); -} diff --git a/mobile/lib/domain/interfaces/store.interface.dart b/mobile/lib/domain/interfaces/store.interface.dart deleted file mode 100644 index b0a6762566..0000000000 --- a/mobile/lib/domain/interfaces/store.interface.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/domain/models/store.model.dart'; - -abstract interface class IStoreRepository implements IDatabaseRepository { - Future insert(StoreKey key, T value); - - Future tryGet(StoreKey key); - - Future>> getAll(); - - Stream watch(StoreKey key); - - Stream> watchAll(); - - Future update(StoreKey key, T value); - - Future delete(StoreKey key); - - Future deleteAll(); -} diff --git a/mobile/lib/domain/interfaces/sync_api.interface.dart b/mobile/lib/domain/interfaces/sync_api.interface.dart deleted file mode 100644 index 57abed2e7f..0000000000 --- a/mobile/lib/domain/interfaces/sync_api.interface.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:http/http.dart' as http; -import 'package:immich_mobile/domain/models/sync_event.model.dart'; - -abstract interface class ISyncApiRepository { - Future ack(List data); - - 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 deleted file mode 100644 index 502cf0ad46..0000000000 --- a/mobile/lib/domain/interfaces/sync_stream.interface.dart +++ /dev/null @@ -1,27 +0,0 @@ -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); - - Future updateAlbumsV1(Iterable data); - Future deleteAlbumsV1(Iterable data); - - // Future updateAlbumAssetsV1(Iterable data); - // Future deleteAlbumAssetsV1(Iterable data); - - Future updateAlbumUsersV1(Iterable data); - Future deleteAlbumUsersV1(Iterable data); -} diff --git a/mobile/lib/domain/interfaces/user.interface.dart b/mobile/lib/domain/interfaces/user.interface.dart deleted file mode 100644 index 05176731fd..0000000000 --- a/mobile/lib/domain/interfaces/user.interface.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/domain/models/user.model.dart'; - -abstract interface class IUserRepository implements IDatabaseRepository { - Future insert(UserDto user); - - Future getByUserId(String id); - - Future> getByUserIds(List ids); - - Future> getAll({SortUserBy? sortBy}); - - Future updateAll(List users); - - Future update(UserDto user); - - Future delete(List ids); - - Future deleteAll(); -} - -enum SortUserBy { id } diff --git a/mobile/lib/domain/interfaces/user_api.interface.dart b/mobile/lib/domain/interfaces/user_api.interface.dart deleted file mode 100644 index 9889654a52..0000000000 --- a/mobile/lib/domain/interfaces/user_api.interface.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'dart:typed_data'; - -import 'package:immich_mobile/domain/models/user.model.dart'; - -abstract interface class IUserApiRepository { - Future getMyUser(); - - Future> getAll(); - - /// Saves the [data] in the server and uses it as the current users profile image - Future createProfileImage({ - required String name, - required Uint8List data, - }); -} diff --git a/mobile/lib/domain/models/album/album.model.dart b/mobile/lib/domain/models/album/album.model.dart index 47683121ba..29f75f29ef 100644 --- a/mobile/lib/domain/models/album/album.model.dart +++ b/mobile/lib/domain/models/album/album.model.dart @@ -1,42 +1,57 @@ -enum AssetOrder { +enum AlbumAssetOrder { // do not change this order! asc, desc, } +enum AlbumUserRole { + // do not change this order! + editor, + viewer, +} + // Model for an album stored in the server class Album { final String id; final String name; + final String ownerId; final String description; final DateTime createdAt; final DateTime updatedAt; final String? thumbnailAssetId; final bool isActivityEnabled; - final AssetOrder order; + final AlbumAssetOrder order; + final int assetCount; + final String ownerName; const Album({ required this.id, required this.name, + required this.ownerId, required this.description, required this.createdAt, required this.updatedAt, this.thumbnailAssetId, required this.isActivityEnabled, required this.order, + required this.assetCount, + required this.ownerName, }); @override String toString() { return '''Album { - id: $id, - name: $name, - description: $description, - createdAt: $createdAt, - updatedAt: $updatedAt, - isActivityEnabled: $isActivityEnabled, - order: $order, - thumbnailAssetId: ${thumbnailAssetId ?? ""} + id: $id, + name: $name, + ownerId: $ownerId, + description: $description, + createdAt: $createdAt, + updatedAt: $updatedAt, + isActivityEnabled: $isActivityEnabled, + order: $order, + thumbnailAssetId: ${thumbnailAssetId ?? ""} + assetCount: $assetCount + ownerName: $ownerName }'''; } @@ -46,23 +61,29 @@ class Album { if (identical(this, other)) return true; return id == other.id && name == other.name && + ownerId == other.ownerId && description == other.description && createdAt == other.createdAt && updatedAt == other.updatedAt && thumbnailAssetId == other.thumbnailAssetId && isActivityEnabled == other.isActivityEnabled && - order == other.order; + order == other.order && + assetCount == other.assetCount && + ownerName == other.ownerName; } @override int get hashCode { return id.hashCode ^ name.hashCode ^ + ownerId.hashCode ^ description.hashCode ^ createdAt.hashCode ^ updatedAt.hashCode ^ thumbnailAssetId.hashCode ^ isActivityEnabled.hashCode ^ - order.hashCode; + order.hashCode ^ + assetCount.hashCode ^ + ownerName.hashCode; } } diff --git a/mobile/lib/domain/models/local_album.model.dart b/mobile/lib/domain/models/album/local_album.model.dart similarity index 100% rename from mobile/lib/domain/models/local_album.model.dart rename to mobile/lib/domain/models/album/local_album.model.dart diff --git a/mobile/lib/domain/models/album_user.model.dart b/mobile/lib/domain/models/album_user.model.dart deleted file mode 100644 index 10c03a7c4b..0000000000 --- a/mobile/lib/domain/models/album_user.model.dart +++ /dev/null @@ -1,5 +0,0 @@ -enum AlbumUserRole { - // do not change this order! - editor, - viewer, -} diff --git a/mobile/lib/domain/models/asset/asset.model.dart b/mobile/lib/domain/models/asset/asset.model.dart deleted file mode 100644 index c170f7f848..0000000000 --- a/mobile/lib/domain/models/asset/asset.model.dart +++ /dev/null @@ -1,61 +0,0 @@ -part of 'base_asset.model.dart'; - -enum AssetVisibility { - timeline, - hidden, - archive, - locked, -} - -// Model for an asset stored in the server -class Asset extends BaseAsset { - final String id; - final String? localId; - final AssetVisibility visibility; - - const Asset({ - required this.id, - this.localId, - required super.name, - required super.checksum, - required super.type, - required super.createdAt, - required super.updatedAt, - super.width, - super.height, - super.durationInSeconds, - super.isFavorite = false, - this.visibility = AssetVisibility.timeline, - }); - - @override - String toString() { - return '''Asset { - id: $id, - name: $name, - type: $type, - createdAt: $createdAt, - updatedAt: $updatedAt, - width: ${width ?? ""}, - height: ${height ?? ""}, - durationInSeconds: ${durationInSeconds ?? ""}, - localId: ${localId ?? ""}, - isFavorite: $isFavorite, - visibility: $visibility, - }'''; - } - - @override - bool operator ==(Object other) { - if (other is! Asset) return false; - if (identical(this, other)) return true; - return super == other && - id == other.id && - localId == other.localId && - visibility == other.visibility; - } - - @override - int get hashCode => - super.hashCode ^ id.hashCode ^ localId.hashCode ^ visibility.hashCode; -} diff --git a/mobile/lib/domain/models/asset/base_asset.model.dart b/mobile/lib/domain/models/asset/base_asset.model.dart index fb95437659..fad1f1961a 100644 --- a/mobile/lib/domain/models/asset/base_asset.model.dart +++ b/mobile/lib/domain/models/asset/base_asset.model.dart @@ -1,5 +1,5 @@ -part 'asset.model.dart'; part 'local_asset.model.dart'; +part 'remote_asset.model.dart'; enum AssetType { // do not change this order! @@ -9,6 +9,12 @@ enum AssetType { audio, } +enum AssetState { + local, + remote, + merged, +} + sealed class BaseAsset { final String name; final String? checksum; @@ -19,6 +25,7 @@ sealed class BaseAsset { final int? height; final int? durationInSeconds; final bool isFavorite; + final String? livePhotoVideoId; const BaseAsset({ required this.name, @@ -30,8 +37,31 @@ sealed class BaseAsset { this.height, this.durationInSeconds, this.isFavorite = false, + this.livePhotoVideoId, }); + bool get isImage => type == AssetType.image; + bool get isVideo => type == AssetType.video; + + Duration get duration { + final durationInSeconds = this.durationInSeconds; + if (durationInSeconds != null) { + return Duration(seconds: durationInSeconds); + } + return const Duration(); + } + + bool get hasRemote => + storage == AssetState.remote || storage == AssetState.merged; + bool get hasLocal => + storage == AssetState.local || storage == AssetState.merged; + bool get isLocalOnly => storage == AssetState.local; + bool get isRemoteOnly => storage == AssetState.remote; + + // Overridden in subclasses + AssetState get storage; + String get heroTag; + @override String toString() { return '''BaseAsset { diff --git a/mobile/lib/domain/models/asset/local_asset.model.dart b/mobile/lib/domain/models/asset/local_asset.model.dart index 25e617d8ed..0c3e8fa942 100644 --- a/mobile/lib/domain/models/asset/local_asset.model.dart +++ b/mobile/lib/domain/models/asset/local_asset.model.dart @@ -3,6 +3,7 @@ part of 'base_asset.model.dart'; class LocalAsset extends BaseAsset { final String id; final String? remoteId; + final int orientation; const LocalAsset({ required this.id, @@ -16,8 +17,17 @@ class LocalAsset extends BaseAsset { super.height, super.durationInSeconds, super.isFavorite = false, + super.livePhotoVideoId, + this.orientation = 0, }); + @override + AssetState get storage => + remoteId == null ? AssetState.local : AssetState.merged; + + @override + String get heroTag => '${id}_${remoteId ?? checksum}'; + @override String toString() { return '''LocalAsset { @@ -31,6 +41,7 @@ class LocalAsset extends BaseAsset { durationInSeconds: ${durationInSeconds ?? ""}, remoteId: ${remoteId ?? ""} isFavorite: $isFavorite, + orientation: $orientation, }'''; } @@ -38,11 +49,12 @@ class LocalAsset extends BaseAsset { bool operator ==(Object other) { if (other is! LocalAsset) return false; if (identical(this, other)) return true; - return super == other && id == other.id && remoteId == other.remoteId; + return super == other && id == other.id && orientation == other.orientation; } @override - int get hashCode => super.hashCode ^ id.hashCode ^ remoteId.hashCode; + int get hashCode => + super.hashCode ^ id.hashCode ^ remoteId.hashCode ^ orientation.hashCode; LocalAsset copyWith({ String? id, @@ -56,6 +68,7 @@ class LocalAsset extends BaseAsset { int? height, int? durationInSeconds, bool? isFavorite, + int? orientation, }) { return LocalAsset( id: id ?? this.id, @@ -69,6 +82,7 @@ class LocalAsset extends BaseAsset { height: height ?? this.height, durationInSeconds: durationInSeconds ?? this.durationInSeconds, isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, ); } } diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart new file mode 100644 index 0000000000..587eabc00b --- /dev/null +++ b/mobile/lib/domain/models/asset/remote_asset.model.dart @@ -0,0 +1,115 @@ +part of 'base_asset.model.dart'; + +enum AssetVisibility { + timeline, + hidden, + archive, + locked, +} + +// Model for an asset stored in the server +class RemoteAsset extends BaseAsset { + final String id; + final String? localId; + final String? thumbHash; + final AssetVisibility visibility; + final String ownerId; + + const RemoteAsset({ + required this.id, + this.localId, + required super.name, + required this.ownerId, + required super.checksum, + required super.type, + required super.createdAt, + required super.updatedAt, + super.width, + super.height, + super.durationInSeconds, + super.isFavorite = false, + this.thumbHash, + this.visibility = AssetVisibility.timeline, + super.livePhotoVideoId, + }); + + @override + AssetState get storage => + localId == null ? AssetState.remote : AssetState.merged; + + @override + String get heroTag => '${localId ?? checksum}_$id'; + + @override + String toString() { + return '''Asset { + id: $id, + name: $name, + ownerId: $ownerId, + type: $type, + createdAt: $createdAt, + updatedAt: $updatedAt, + width: ${width ?? ""}, + height: ${height ?? ""}, + durationInSeconds: ${durationInSeconds ?? ""}, + localId: ${localId ?? ""}, + isFavorite: $isFavorite, + thumbHash: ${thumbHash ?? ""}, + visibility: $visibility, + }'''; + } + + @override + bool operator ==(Object other) { + if (other is! RemoteAsset) return false; + if (identical(this, other)) return true; + return super == other && + id == other.id && + ownerId == other.ownerId && + thumbHash == other.thumbHash && + visibility == other.visibility; + } + + @override + int get hashCode => + super.hashCode ^ + id.hashCode ^ + ownerId.hashCode ^ + localId.hashCode ^ + thumbHash.hashCode ^ + visibility.hashCode; + + RemoteAsset copyWith({ + String? id, + String? localId, + String? name, + String? ownerId, + String? checksum, + AssetType? type, + DateTime? createdAt, + DateTime? updatedAt, + int? width, + int? height, + int? durationInSeconds, + bool? isFavorite, + String? thumbHash, + AssetVisibility? visibility, + }) { + return RemoteAsset( + id: id ?? this.id, + localId: localId ?? this.localId, + name: name ?? this.name, + ownerId: ownerId ?? this.ownerId, + checksum: checksum ?? this.checksum, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + isFavorite: isFavorite ?? this.isFavorite, + thumbHash: thumbHash ?? this.thumbHash, + visibility: visibility ?? this.visibility, + ); + } +} diff --git a/mobile/lib/domain/models/exif.model.dart b/mobile/lib/domain/models/exif.model.dart index e95653ca4e..b73aa4cae1 100644 --- a/mobile/lib/domain/models/exif.model.dart +++ b/mobile/lib/domain/models/exif.model.dart @@ -3,6 +3,8 @@ class ExifInfo { final int? fileSize; final String? description; final bool isFlipped; + final double? width; + final double? height; final String? orientation; final String? timeZone; final DateTime? dateTimeOriginal; @@ -45,6 +47,8 @@ class ExifInfo { this.fileSize, this.description, this.orientation, + this.width, + this.height, this.timeZone, this.dateTimeOriginal, this.isFlipped = false, @@ -68,6 +72,9 @@ class ExifInfo { return other.fileSize == fileSize && other.description == description && + other.isFlipped == isFlipped && + other.width == width && + other.height == height && other.orientation == orientation && other.timeZone == timeZone && other.dateTimeOriginal == dateTimeOriginal && @@ -91,6 +98,9 @@ class ExifInfo { return fileSize.hashCode ^ description.hashCode ^ orientation.hashCode ^ + isFlipped.hashCode ^ + width.hashCode ^ + height.hashCode ^ timeZone.hashCode ^ dateTimeOriginal.hashCode ^ latitude.hashCode ^ @@ -114,6 +124,9 @@ class ExifInfo { fileSize: ${fileSize ?? 'NA'}, description: ${description ?? 'NA'}, orientation: ${orientation ?? 'NA'}, +width: ${width ?? 'NA'}, +height: ${height ?? 'NA'}, +isFlipped: $isFlipped, timeZone: ${timeZone ?? 'NA'}, dateTimeOriginal: ${dateTimeOriginal ?? 'NA'}, latitude: ${latitude ?? 'NA'}, diff --git a/mobile/lib/domain/models/memory.model.dart b/mobile/lib/domain/models/memory.model.dart new file mode 100644 index 0000000000..ba2a43428f --- /dev/null +++ b/mobile/lib/domain/models/memory.model.dart @@ -0,0 +1,166 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +import 'package:collection/collection.dart'; + +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; + +enum MemoryTypeEnum { + // do not change this order! + onThisDay, +} + +class MemoryData { + final int year; + + const MemoryData({ + required this.year, + }); + + MemoryData copyWith({ + int? year, + }) { + return MemoryData( + year: year ?? this.year, + ); + } + + Map toMap() { + return { + 'year': year, + }; + } + + factory MemoryData.fromMap(Map map) { + return MemoryData( + year: map['year'] as int, + ); + } + + String toJson() => json.encode(toMap()); + + factory MemoryData.fromJson(String source) => + MemoryData.fromMap(json.decode(source) as Map); + + @override + String toString() => 'MemoryData(year: $year)'; + + @override + bool operator ==(covariant MemoryData other) { + if (identical(this, other)) return true; + + return other.year == year; + } + + @override + int get hashCode => year.hashCode; +} + +// Model for a memory stored in the server +class DriftMemory { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + + // enum + final MemoryTypeEnum type; + final MemoryData data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + final List assets; + + const DriftMemory({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + required this.assets, + }); + + DriftMemory copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + DateTime? deletedAt, + String? ownerId, + MemoryTypeEnum? type, + MemoryData? data, + bool? isSaved, + DateTime? memoryAt, + DateTime? seenAt, + DateTime? showAt, + DateTime? hideAt, + List? assets, + }) { + return DriftMemory( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + assets: assets ?? this.assets, + ); + } + + @override + String toString() { + return 'Memory(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, ownerId: $ownerId, type: $type, data: $data, isSaved: $isSaved, memoryAt: $memoryAt, seenAt: $seenAt, showAt: $showAt, hideAt: $hideAt, assets: $assets)'; + } + + @override + bool operator ==(covariant DriftMemory other) { + if (identical(this, other)) return true; + final listEquals = const DeepCollectionEquality().equals; + + return other.id == id && + other.createdAt == createdAt && + other.updatedAt == updatedAt && + other.deletedAt == deletedAt && + other.ownerId == ownerId && + other.type == type && + other.data == data && + other.isSaved == isSaved && + other.memoryAt == memoryAt && + other.seenAt == seenAt && + other.showAt == showAt && + other.hideAt == hideAt && + listEquals(other.assets, assets); + } + + @override + int get hashCode { + return id.hashCode ^ + createdAt.hashCode ^ + updatedAt.hashCode ^ + deletedAt.hashCode ^ + ownerId.hashCode ^ + type.hashCode ^ + data.hashCode ^ + isSaved.hashCode ^ + memoryAt.hashCode ^ + seenAt.hashCode ^ + showAt.hashCode ^ + hideAt.hashCode ^ + assets.hashCode; + } +} diff --git a/mobile/lib/interfaces/person_api.interface.dart b/mobile/lib/domain/models/person.model.dart similarity index 91% rename from mobile/lib/interfaces/person_api.interface.dart rename to mobile/lib/domain/models/person.model.dart index 9d127ad765..10453f768d 100644 --- a/mobile/lib/interfaces/person_api.interface.dart +++ b/mobile/lib/domain/models/person.model.dart @@ -1,13 +1,7 @@ -// ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:convert'; -abstract interface class IPersonApiRepository { - Future> getAll(); - Future update(String id, {String? name}); -} - class Person { - Person({ + const Person({ required this.id, this.birthDate, required this.isHidden, diff --git a/mobile/lib/domain/models/setting.model.dart b/mobile/lib/domain/models/setting.model.dart new file mode 100644 index 0000000000..99246c31a1 --- /dev/null +++ b/mobile/lib/domain/models/setting.model.dart @@ -0,0 +1,17 @@ +import 'package:immich_mobile/domain/models/store.model.dart'; + +enum Setting { + tilesPerRow(StoreKey.tilesPerRow, 4), + groupAssetsBy(StoreKey.groupAssetsBy, 0), + showStorageIndicator(StoreKey.storageIndicator, true), + loadOriginal(StoreKey.loadOriginal, false), + loadOriginalVideo(StoreKey.loadOriginalVideo, false), + preferRemoteImage(StoreKey.preferRemoteImage, false), + advancedTroubleshooting(StoreKey.advancedTroubleshooting, false), + ; + + const Setting(this.storeKey, this.defaultValue); + + final StoreKey storeKey; + final T defaultValue; +} diff --git a/mobile/lib/domain/models/stack.model.dart b/mobile/lib/domain/models/stack.model.dart new file mode 100644 index 0000000000..5404eb8f42 --- /dev/null +++ b/mobile/lib/domain/models/stack.model.dart @@ -0,0 +1,84 @@ +import 'dart:convert'; + +// Model for a stack stored in the server +class Stack { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + + const Stack({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + + Stack copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) { + return Stack( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + Map toMap() { + return { + 'id': id, + 'createdAt': createdAt.millisecondsSinceEpoch, + 'updatedAt': updatedAt.millisecondsSinceEpoch, + 'ownerId': ownerId, + 'primaryAssetId': primaryAssetId, + }; + } + + factory Stack.fromMap(Map map) { + return Stack( + id: map['id'] as String, + createdAt: DateTime.fromMillisecondsSinceEpoch(map['createdAt'] as int), + updatedAt: DateTime.fromMillisecondsSinceEpoch(map['updatedAt'] as int), + ownerId: map['ownerId'] as String, + primaryAssetId: map['primaryAssetId'] as String, + ); + } + + String toJson() => json.encode(toMap()); + + factory Stack.fromJson(String source) => + Stack.fromMap(json.decode(source) as Map); + + @override + String toString() { + return 'Stack(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, ownerId: $ownerId, primaryAssetId: $primaryAssetId)'; + } + + @override + bool operator ==(covariant Stack other) { + if (identical(this, other)) return true; + + return other.id == id && + other.createdAt == createdAt && + other.updatedAt == updatedAt && + other.ownerId == ownerId && + other.primaryAssetId == primaryAssetId; + } + + @override + int get hashCode { + return id.hashCode ^ + createdAt.hashCode ^ + updatedAt.hashCode ^ + ownerId.hashCode ^ + primaryAssetId.hashCode; + } +} diff --git a/mobile/lib/domain/models/timeline.model.dart b/mobile/lib/domain/models/timeline.model.dart new file mode 100644 index 0000000000..4a49708b74 --- /dev/null +++ b/mobile/lib/domain/models/timeline.model.dart @@ -0,0 +1,40 @@ +enum GroupAssetsBy { + day, + month, + none; +} + +enum HeaderType { + none, + month, + day, + monthAndDay; +} + +class Bucket { + final int assetCount; + + const Bucket({required this.assetCount}); + + @override + bool operator ==(covariant Bucket other) { + return assetCount == other.assetCount; + } + + @override + int get hashCode => assetCount.hashCode; +} + +class TimeBucket extends Bucket { + final DateTime date; + + const TimeBucket({required this.date, required super.assetCount}); + + @override + bool operator ==(covariant TimeBucket other) { + return super == other && date == other.date; + } + + @override + int get hashCode => super.hashCode ^ date.hashCode; +} diff --git a/mobile/lib/domain/services/asset.service.dart b/mobile/lib/domain/services/asset.service.dart new file mode 100644 index 0000000000..a210c3d991 --- /dev/null +++ b/mobile/lib/domain/services/asset.service.dart @@ -0,0 +1,64 @@ +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/utils/exif.converter.dart'; +import 'package:platform/platform.dart'; + +class AssetService { + final RemoteAssetRepository _remoteAssetRepository; + final DriftLocalAssetRepository _localAssetRepository; + final Platform _platform; + + const AssetService({ + required RemoteAssetRepository remoteAssetRepository, + required DriftLocalAssetRepository localAssetRepository, + }) : _remoteAssetRepository = remoteAssetRepository, + _localAssetRepository = localAssetRepository, + _platform = const LocalPlatform(); + + Stream watchAsset(BaseAsset asset) { + final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).id; + return asset is LocalAsset + ? _localAssetRepository.watchAsset(id) + : _remoteAssetRepository.watchAsset(id); + } + + Future getExif(BaseAsset asset) async { + if (!asset.hasRemote) { + return null; + } + + final id = + asset is LocalAsset ? asset.remoteId! : (asset as RemoteAsset).id; + return _remoteAssetRepository.getExif(id); + } + + Future getAspectRatio(BaseAsset asset) async { + bool isFlipped; + double? width; + double? height; + + if (asset.hasRemote) { + final exif = await getExif(asset); + isFlipped = ExifDtoConverter.isOrientationFlipped(exif?.orientation); + width = exif?.width ?? asset.width?.toDouble(); + height = exif?.height ?? asset.height?.toDouble(); + } else if (asset is LocalAsset) { + isFlipped = _platform.isAndroid && + (asset.orientation == 90 || asset.orientation == 270); + width = asset.width?.toDouble(); + height = asset.height?.toDouble(); + } else { + isFlipped = false; + } + + final orientedWidth = isFlipped ? height : width; + final orientedHeight = isFlipped ? width : height; + if (orientedWidth != null && orientedHeight != null && orientedHeight > 0) { + return orientedWidth / orientedHeight; + } + + return 1.0; + } +} diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index d8ad3dc2dc..d004041276 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -1,10 +1,10 @@ import 'dart:convert'; import 'package:immich_mobile/constants/constants.dart'; -import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; -import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart'; -import 'package:immich_mobile/domain/interfaces/storage.interface.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:logging/logging.dart'; @@ -12,16 +12,16 @@ import 'package:logging/logging.dart'; class HashService { final int batchSizeLimit; final int batchFileLimit; - final ILocalAlbumRepository _localAlbumRepository; - final ILocalAssetRepository _localAssetRepository; - final IStorageRepository _storageRepository; + final DriftLocalAlbumRepository _localAlbumRepository; + final DriftLocalAssetRepository _localAssetRepository; + final StorageRepository _storageRepository; final NativeSyncApi _nativeSyncApi; final _log = Logger('HashService'); HashService({ - required ILocalAlbumRepository localAlbumRepository, - required ILocalAssetRepository localAssetRepository, - required IStorageRepository storageRepository, + required DriftLocalAlbumRepository localAlbumRepository, + required DriftLocalAssetRepository localAssetRepository, + required StorageRepository storageRepository, required NativeSyncApi nativeSyncApi, this.batchSizeLimit = kBatchHashSizeLimit, this.batchFileLimit = kBatchHashFileLimit, @@ -61,7 +61,7 @@ class HashService { final toHash = <_AssetToPath>[]; for (final asset in assetsToHash) { - final file = await _storageRepository.getFileForAsset(asset); + final file = await _storageRepository.getFileForAsset(asset.id); if (file == null) { continue; } diff --git a/mobile/lib/domain/services/local_album.service.dart b/mobile/lib/domain/services/local_album.service.dart new file mode 100644 index 0000000000..9af12ce595 --- /dev/null +++ b/mobile/lib/domain/services/local_album.service.dart @@ -0,0 +1,17 @@ +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; + +class LocalAlbumService { + final DriftLocalAlbumRepository _repository; + + const LocalAlbumService(this._repository); + + Future> getAll() { + return _repository.getAll(); + } + + Future getThumbnail(String albumId) { + return _repository.getThumbnail(albumId); + } +} diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index ff77ebd83e..8d3b747b37 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -2,11 +2,9 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; -import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/domain/models/local_album.model.dart'; -import 'package:immich_mobile/domain/models/store.model.dart'; -import 'package:immich_mobile/domain/services/store.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:immich_mobile/utils/diff.dart'; @@ -14,25 +12,19 @@ import 'package:logging/logging.dart'; import 'package:platform/platform.dart'; class LocalSyncService { - final ILocalAlbumRepository _localAlbumRepository; + final DriftLocalAlbumRepository _localAlbumRepository; final NativeSyncApi _nativeSyncApi; final Platform _platform; - final StoreService _storeService; final Logger _log = Logger("DeviceSyncService"); LocalSyncService({ - required ILocalAlbumRepository localAlbumRepository, + required DriftLocalAlbumRepository localAlbumRepository, required NativeSyncApi nativeSyncApi, - required StoreService storeService, Platform? platform, }) : _localAlbumRepository = localAlbumRepository, _nativeSyncApi = nativeSyncApi, - _storeService = storeService, _platform = platform ?? const LocalPlatform(); - bool get _ignoreIcloudAssets => - _storeService.get(StoreKey.ignoreIcloudAssets, false) == true; - Future sync({bool full = false}) async { final Stopwatch stopwatch = Stopwatch()..start(); try { @@ -84,11 +76,7 @@ class LocalSyncService { ); continue; } - if (_ignoreIcloudAssets) { - await removeAlbum(dbAlbum); - } else { - await updateAlbum(dbAlbum, album); - } + await updateAlbum(dbAlbum, album); } } @@ -106,12 +94,7 @@ class LocalSyncService { try { final Stopwatch stopwatch = Stopwatch()..start(); - List deviceAlbums = - List.of(await _nativeSyncApi.getAlbums()); - if (_platform.isIOS && _ignoreIcloudAssets) { - deviceAlbums.removeWhere((album) => album.isCloud); - } - + final deviceAlbums = await _nativeSyncApi.getAlbums(); final dbAlbums = await _localAlbumRepository.getAll(sortBy: {SortLocalAlbumsBy.id}); @@ -376,6 +359,7 @@ extension on Iterable { width: e.width, height: e.height, durationInSeconds: e.durationInSeconds, + orientation: e.orientation, ), ).toList(); } diff --git a/mobile/lib/domain/services/log.service.dart b/mobile/lib/domain/services/log.service.dart index a25147d185..c52665f093 100644 --- a/mobile/lib/domain/services/log.service.dart +++ b/mobile/lib/domain/services/log.service.dart @@ -2,10 +2,10 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:immich_mobile/constants/constants.dart'; -import 'package:immich_mobile/domain/interfaces/log.interface.dart'; -import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:logging/logging.dart'; /// Service responsible for handling application logging. @@ -14,8 +14,8 @@ import 'package:logging/logging.dart'; /// writes them to a persistent [ILogRepository], and manages log levels /// via [IStoreRepository] class LogService { - final ILogRepository _logRepository; - final IStoreRepository _storeRepository; + final IsarLogRepository _logRepository; + final IsarStoreRepository _storeRepository; final List _msgBuffer = []; @@ -37,8 +37,8 @@ class LogService { } static Future init({ - required ILogRepository logRepository, - required IStoreRepository storeRepository, + required IsarLogRepository logRepository, + required IsarStoreRepository storeRepository, bool shouldBuffer = true, }) async { _instance ??= await create( @@ -50,8 +50,8 @@ class LogService { } static Future create({ - required ILogRepository logRepository, - required IStoreRepository storeRepository, + required IsarLogRepository logRepository, + required IsarStoreRepository storeRepository, bool shouldBuffer = true, }) async { final instance = LogService._(logRepository, storeRepository, shouldBuffer); diff --git a/mobile/lib/domain/services/memory.service.dart b/mobile/lib/domain/services/memory.service.dart new file mode 100644 index 0000000000..c94b8a9f0a --- /dev/null +++ b/mobile/lib/domain/services/memory.service.dart @@ -0,0 +1,15 @@ +import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/memory.repository.dart'; +import 'package:logging/logging.dart'; + +class DriftMemoryService { + final log = Logger("DriftMemoryService"); + + final DriftMemoryRepository _repository; + + DriftMemoryService(this._repository); + + Future> getMemoryLane(String ownerId) { + return _repository.getAll(ownerId); + } +} diff --git a/mobile/lib/domain/services/remote_album.service.dart b/mobile/lib/domain/services/remote_album.service.dart new file mode 100644 index 0000000000..f3106470d2 --- /dev/null +++ b/mobile/lib/domain/services/remote_album.service.dart @@ -0,0 +1,60 @@ +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; +import 'package:immich_mobile/models/albums/album_search.model.dart'; +import 'package:immich_mobile/utils/remote_album.utils.dart'; + +class RemoteAlbumService { + final DriftRemoteAlbumRepository _repository; + + const RemoteAlbumService(this._repository); + + Future> getAll() { + return _repository.getAll(); + } + + List sortAlbums( + List albums, + RemoteAlbumSortMode sortMode, { + bool isReverse = false, + }) { + return sortMode.sortFn(albums, isReverse); + } + + List searchAlbums( + List albums, + String query, + String? userId, [ + QuickFilterMode filterMode = QuickFilterMode.all, + ]) { + final lowerQuery = query.toLowerCase(); + List filtered = albums; + + // Apply text search filter + if (query.isNotEmpty) { + filtered = filtered + .where( + (album) => + album.name.toLowerCase().contains(lowerQuery) || + album.description.toLowerCase().contains(lowerQuery), + ) + .toList(); + } + + if (userId != null) { + switch (filterMode) { + case QuickFilterMode.myAlbums: + filtered = + filtered.where((album) => album.ownerId == userId).toList(); + break; + case QuickFilterMode.sharedWithMe: + filtered = + filtered.where((album) => album.ownerId != userId).toList(); + break; + case QuickFilterMode.all: + break; + } + } + + return filtered; + } +} diff --git a/mobile/lib/domain/services/setting.service.dart b/mobile/lib/domain/services/setting.service.dart new file mode 100644 index 0000000000..8f91e9c66b --- /dev/null +++ b/mobile/lib/domain/services/setting.service.dart @@ -0,0 +1,24 @@ +import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/domain/services/store.service.dart'; + +// Singleton instance of SettingsService, to use in places +// where reactivity is not required +// ignore: non_constant_identifier_names +final AppSetting = SettingsService(storeService: StoreService.I); + +class SettingsService { + final StoreService _storeService; + + const SettingsService({required StoreService storeService}) + : _storeService = storeService; + + T get(Setting setting) => + _storeService.get(setting.storeKey, setting.defaultValue); + + Future set(Setting setting, T value) => + _storeService.put(setting.storeKey, value); + + Stream watch(Setting setting) => _storeService + .watch(setting.storeKey) + .map((v) => v ?? setting.defaultValue); +} diff --git a/mobile/lib/domain/services/store.service.dart b/mobile/lib/domain/services/store.service.dart index 7b71acd254..359ce8cf60 100644 --- a/mobile/lib/domain/services/store.service.dart +++ b/mobile/lib/domain/services/store.service.dart @@ -1,18 +1,18 @@ import 'dart:async'; -import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; /// Provides access to a persistent key-value store with an in-memory cache. /// Listens for repository changes to keep the cache updated. class StoreService { - final IStoreRepository _storeRepository; + final IsarStoreRepository _storeRepository; /// In-memory cache. Keys are [StoreKey.id] final Map _cache = {}; late final StreamSubscription _storeUpdateSubscription; - StoreService._({required IStoreRepository storeRepository}) + StoreService._({required IsarStoreRepository storeRepository}) : _storeRepository = storeRepository; // TODO: Temporary typedef to make minimal changes. Remove this and make the presentation layer access store through a provider @@ -26,14 +26,14 @@ class StoreService { // TODO: Replace the implementation with the one from create after removing the typedef static Future init({ - required IStoreRepository storeRepository, + required IsarStoreRepository storeRepository, }) async { _instance ??= await create(storeRepository: storeRepository); return _instance!; } static Future create({ - required IStoreRepository storeRepository, + required IsarStoreRepository storeRepository, }) async { final instance = StoreService._(storeRepository: storeRepository); await instance._populateCache(); diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index f997bef139..ee0ec6c44f 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -1,21 +1,21 @@ import 'dart:async'; -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/infrastructure/repositories/sync_api.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.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 SyncApiRepository _syncApiRepository; + final SyncStreamRepository _syncStreamRepository; final bool Function()? _cancelChecker; SyncStreamService({ - required ISyncApiRepository syncApiRepository, - required ISyncStreamRepository syncStreamRepository, + required SyncApiRepository syncApiRepository, + required SyncStreamRepository syncStreamRepository, bool Function()? cancelChecker, }) : _syncApiRepository = syncApiRepository, _syncStreamRepository = syncStreamRepository, @@ -76,23 +76,103 @@ class SyncStreamService { case SyncEntityType.assetExifV1: return _syncStreamRepository.updateAssetsExifV1(data.cast()); case SyncEntityType.partnerAssetV1: - return _syncStreamRepository.updatePartnerAssetsV1(data.cast()); + return _syncStreamRepository.updateAssetsV1( + data.cast(), + debugLabel: 'partner', + ); + case SyncEntityType.partnerAssetBackfillV1: + return _syncStreamRepository.updateAssetsV1( + data.cast(), + debugLabel: 'partner backfill', + ); case SyncEntityType.partnerAssetDeleteV1: - return _syncStreamRepository.deletePartnerAssetsV1(data.cast()); + return _syncStreamRepository.deleteAssetsV1( + data.cast(), + debugLabel: "partner", + ); case SyncEntityType.partnerAssetExifV1: - return _syncStreamRepository.updatePartnerAssetsExifV1(data.cast()); + return _syncStreamRepository.updateAssetsExifV1( + data.cast(), + debugLabel: 'partner', + ); + case SyncEntityType.partnerAssetExifBackfillV1: + return _syncStreamRepository.updateAssetsExifV1( + data.cast(), + debugLabel: 'partner backfill', + ); case SyncEntityType.albumV1: return _syncStreamRepository.updateAlbumsV1(data.cast()); case SyncEntityType.albumDeleteV1: return _syncStreamRepository.deleteAlbumsV1(data.cast()); - // case SyncEntityType.albumAssetV1: - // return _syncStreamRepository.updateAlbumAssetsV1(data.cast()); - // case SyncEntityType.albumAssetDeleteV1: - // return _syncStreamRepository.deleteAlbumAssetsV1(data.cast()); case SyncEntityType.albumUserV1: return _syncStreamRepository.updateAlbumUsersV1(data.cast()); + case SyncEntityType.albumUserBackfillV1: + return _syncStreamRepository.updateAlbumUsersV1( + data.cast(), + debugLabel: 'backfill', + ); case SyncEntityType.albumUserDeleteV1: return _syncStreamRepository.deleteAlbumUsersV1(data.cast()); + case SyncEntityType.albumAssetV1: + return _syncStreamRepository.updateAssetsV1( + data.cast(), + debugLabel: 'album', + ); + case SyncEntityType.albumAssetBackfillV1: + return _syncStreamRepository.updateAssetsV1( + data.cast(), + debugLabel: 'album backfill', + ); + case SyncEntityType.albumAssetExifV1: + return _syncStreamRepository.updateAssetsExifV1( + data.cast(), + debugLabel: 'album', + ); + case SyncEntityType.albumAssetExifBackfillV1: + return _syncStreamRepository.updateAssetsExifV1( + data.cast(), + debugLabel: 'album backfill', + ); + case SyncEntityType.albumToAssetV1: + return _syncStreamRepository.updateAlbumToAssetsV1(data.cast()); + case SyncEntityType.albumToAssetBackfillV1: + return _syncStreamRepository.updateAlbumToAssetsV1( + data.cast(), + debugLabel: 'backfill', + ); + case SyncEntityType.albumToAssetDeleteV1: + return _syncStreamRepository.deleteAlbumToAssetsV1(data.cast()); + // No-op. SyncAckV1 entities are checkpoints in the sync stream + // to acknowledge that the client has processed all the backfill events + case SyncEntityType.syncAckV1: + return; + case SyncEntityType.memoryV1: + return _syncStreamRepository.updateMemoriesV1(data.cast()); + case SyncEntityType.memoryDeleteV1: + return _syncStreamRepository.deleteMemoriesV1(data.cast()); + case SyncEntityType.memoryToAssetV1: + return _syncStreamRepository.updateMemoryAssetsV1(data.cast()); + case SyncEntityType.memoryToAssetDeleteV1: + return _syncStreamRepository.deleteMemoryAssetsV1(data.cast()); + case SyncEntityType.stackV1: + return _syncStreamRepository.updateStacksV1(data.cast()); + case SyncEntityType.stackDeleteV1: + return _syncStreamRepository.deleteStacksV1(data.cast()); + case SyncEntityType.partnerStackV1: + return _syncStreamRepository.updateStacksV1( + data.cast(), + debugLabel: 'partner', + ); + case SyncEntityType.partnerStackBackfillV1: + return _syncStreamRepository.updateStacksV1( + data.cast(), + debugLabel: 'partner backfill', + ); + case SyncEntityType.partnerStackDeleteV1: + return _syncStreamRepository.deleteStacksV1( + data.cast(), + debugLabel: 'partner', + ); default: _logger.warning("Unknown sync data type: $type"); } diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart new file mode 100644 index 0000000000..b3d6b51402 --- /dev/null +++ b/mobile/lib/domain/services/timeline.service.dart @@ -0,0 +1,228 @@ +import 'dart:async'; +import 'dart:math' as math; + +import 'package:collection/collection.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/services/setting.service.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; +import 'package:immich_mobile/utils/async_mutex.dart'; + +typedef TimelineAssetSource = Future> Function( + int index, + int count, +); + +typedef TimelineBucketSource = Stream> Function(); + +class TimelineFactory { + final DriftTimelineRepository _timelineRepository; + final SettingsService _settingsService; + + const TimelineFactory({ + required DriftTimelineRepository timelineRepository, + required SettingsService settingsService, + }) : _timelineRepository = timelineRepository, + _settingsService = settingsService; + + GroupAssetsBy get groupBy => + GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)]; + + TimelineService main(List timelineUsers) => TimelineService( + assetSource: (offset, count) => _timelineRepository + .getMainBucketAssets(timelineUsers, offset: offset, count: count), + bucketSource: () => _timelineRepository.watchMainBucket( + timelineUsers, + groupBy: groupBy, + ), + ); + + TimelineService localAlbum({required String albumId}) => TimelineService( + assetSource: (offset, count) => _timelineRepository + .getLocalAlbumBucketAssets(albumId, offset: offset, count: count), + bucketSource: () => _timelineRepository.watchLocalAlbumBucket( + albumId, + groupBy: groupBy, + ), + ); + + TimelineService remoteAlbum({required String albumId}) => TimelineService( + assetSource: (offset, count) => _timelineRepository + .getRemoteAlbumBucketAssets(albumId, offset: offset, count: count), + bucketSource: () => _timelineRepository.watchRemoteAlbumBucket( + albumId, + groupBy: groupBy, + ), + ); + + TimelineService remoteAssets(String ownerId) => TimelineService( + assetSource: (offset, count) => _timelineRepository + .getRemoteBucketAssets(ownerId, offset: offset, count: count), + bucketSource: () => _timelineRepository.watchRemoteBucket( + ownerId, + groupBy: GroupAssetsBy.month, + ), + ); + + TimelineService favorite(String userId) => TimelineService( + assetSource: (offset, count) => _timelineRepository + .getFavoriteBucketAssets(userId, offset: offset, count: count), + bucketSource: () => + _timelineRepository.watchFavoriteBucket(userId, groupBy: groupBy), + ); + + TimelineService trash(String userId) => TimelineService( + assetSource: (offset, count) => _timelineRepository + .getTrashBucketAssets(userId, offset: offset, count: count), + bucketSource: () => + _timelineRepository.watchTrashBucket(userId, groupBy: groupBy), + ); + + TimelineService archive(String userId) => TimelineService( + assetSource: (offset, count) => _timelineRepository + .getArchiveBucketAssets(userId, offset: offset, count: count), + bucketSource: () => + _timelineRepository.watchArchiveBucket(userId, groupBy: groupBy), + ); + + TimelineService lockedFolder(String userId) => TimelineService( + assetSource: (offset, count) => _timelineRepository + .getLockedFolderBucketAssets(userId, offset: offset, count: count), + bucketSource: () => _timelineRepository.watchLockedFolderBucket( + userId, + groupBy: groupBy, + ), + ); + + TimelineService video(String userId) => TimelineService( + assetSource: (offset, count) => _timelineRepository + .getVideoBucketAssets(userId, offset: offset, count: count), + bucketSource: () => + _timelineRepository.watchVideoBucket(userId, groupBy: groupBy), + ); +} + +class TimelineService { + final TimelineAssetSource _assetSource; + final TimelineBucketSource _bucketSource; + final AsyncMutex _mutex = AsyncMutex(); + int _bufferOffset = 0; + List _buffer = []; + StreamSubscription? _bucketSubscription; + + int _totalAssets = 0; + int get totalAssets => _totalAssets; + + TimelineService({ + required TimelineAssetSource assetSource, + required TimelineBucketSource bucketSource, + }) : _assetSource = assetSource, + _bucketSource = bucketSource { + _bucketSubscription = _bucketSource().listen((buckets) { + _mutex.run(() async { + final totalAssets = + buckets.fold(0, (acc, bucket) => acc + bucket.assetCount); + + if (totalAssets == 0) { + _bufferOffset = 0; + _buffer.clear(); + } else { + final int offset; + final int count; + // When the buffer is empty or the old bufferOffset is greater than the new total assets, + // we need to reset the buffer and load the first batch of assets. + if (_bufferOffset >= totalAssets || _buffer.isEmpty) { + offset = 0; + count = kTimelineAssetLoadBatchSize; + } else { + offset = _bufferOffset; + count = math.min( + _buffer.length, + totalAssets - _bufferOffset, + ); + } + _buffer = await _assetSource(offset, count); + _bufferOffset = offset; + } + + // change the state's total assets count only after the buffer is reloaded + _totalAssets = totalAssets; + EventStream.shared.emit(const TimelineReloadEvent()); + }); + }); + } + + Stream> Function() get watchBuckets => _bucketSource; + + Future> loadAssets(int index, int count) => + _mutex.run(() => _loadAssets(index, count)); + + Future> _loadAssets(int index, int count) async { + if (hasRange(index, count)) { + return getAssets(index, count); + } + + // if the requested offset is greater than the cached offset, the user scrolls forward "down" + final bool forward = _bufferOffset < index; + + // make sure to load a meaningful amount of data (and not only the requested slice) + // otherwise, each call to [loadAssets] would result in DB call trashing performance + // fills small requests to [kTimelineAssetLoadBatchSize], adds some legroom into the opposite scroll direction for large requests + final len = math.max( + kTimelineAssetLoadBatchSize, + count + kTimelineAssetLoadOppositeSize, + ); + // when scrolling forward, start shortly before the requested offset + // when scrolling backward, end shortly after the requested offset to guard against the user scrolling + // in the other direction a tiny bit resulting in another required load from the DB + final start = math.max( + 0, + forward + ? index - kTimelineAssetLoadOppositeSize + : (len > kTimelineAssetLoadBatchSize ? index : index + count - len), + ); + + _buffer = await _assetSource(start, len); + _bufferOffset = start; + + return getAssets(index, count); + } + + bool hasRange(int index, int count) => + index >= 0 && + index < _totalAssets && + index >= _bufferOffset && + index + count <= _bufferOffset + _buffer.length && + index + count <= _totalAssets; + + List getAssets(int index, int count) { + if (!hasRange(index, count)) { + throw RangeError('TimelineService::getAssets Index out of range'); + } + int start = index - _bufferOffset; + return _buffer.slice(start, start + count); + } + + // Pre-cache assets around the given index for asset viewer + Future preCacheAssets(int index) => + _mutex.run(() => _loadAssets(index, math.min(5, _totalAssets - index))); + + BaseAsset getAsset(int index) { + if (!hasRange(index, 1)) { + throw RangeError( + 'TimelineService::getAsset Index $index not in buffer range [$_bufferOffset, ${_bufferOffset + _buffer.length})', + ); + } + return _buffer.elementAt(index - _bufferOffset); + } + + Future dispose() async { + await _bucketSubscription?.cancel(); + _bucketSubscription = null; + _buffer.clear(); + _bufferOffset = 0; + } +} diff --git a/mobile/lib/domain/services/user.service.dart b/mobile/lib/domain/services/user.service.dart index 6410d4ef5f..14fed9fb93 100644 --- a/mobile/lib/domain/services/user.service.dart +++ b/mobile/lib/domain/services/user.service.dart @@ -1,24 +1,24 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:immich_mobile/domain/interfaces/user.interface.dart'; -import 'package:immich_mobile/domain/interfaces/user_api.interface.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/store.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart'; import 'package:logging/logging.dart'; class UserService { final Logger _log = Logger("UserService"); - final IUserRepository _userRepository; - final IUserApiRepository _userApiRepository; + final IsarUserRepository _isarUserRepository; + final UserApiRepository _userApiRepository; final StoreService _storeService; UserService({ - required IUserRepository userRepository, - required IUserApiRepository userApiRepository, + required IsarUserRepository isarUserRepository, + required UserApiRepository userApiRepository, required StoreService storeService, - }) : _userRepository = userRepository, + }) : _isarUserRepository = isarUserRepository, _userApiRepository = userApiRepository, _storeService = storeService; @@ -38,7 +38,7 @@ class UserService { final user = await _userApiRepository.getMyUser(); if (user == null) return null; await _storeService.put(StoreKey.currentUser, user); - await _userRepository.update(user); + await _isarUserRepository.update(user); return user; } @@ -50,7 +50,7 @@ class UserService { ); final updatedUser = getMyUser().copyWith(profileImagePath: path); await _storeService.put(StoreKey.currentUser, updatedUser); - await _userRepository.update(updatedUser); + await _isarUserRepository.update(updatedUser); return path; } catch (e) { _log.warning("Failed to upload profile image", e); @@ -59,10 +59,10 @@ class UserService { } Future> getAll() async { - return await _userRepository.getAll(); + return await _isarUserRepository.getAll(); } Future deleteAll() { - return _userRepository.deleteAll(); + return _isarUserRepository.deleteAll(); } } diff --git a/mobile/lib/domain/utils/event_stream.dart b/mobile/lib/domain/utils/event_stream.dart new file mode 100644 index 0000000000..65ee17e12b --- /dev/null +++ b/mobile/lib/domain/utils/event_stream.dart @@ -0,0 +1,52 @@ +import 'dart:async'; + +sealed class Event { + const Event(); +} + +class TimelineReloadEvent extends Event { + const TimelineReloadEvent(); +} + +class ViewerOpenBottomSheetEvent extends Event { + const ViewerOpenBottomSheetEvent(); +} + +class EventStream { + EventStream._(); + + static final EventStream shared = EventStream._(); + + final StreamController _controller = + StreamController.broadcast(); + + void emit(Event event) { + _controller.add(event); + } + + Stream where() { + if (T == Event) { + return _controller.stream as Stream; + } + return _controller.stream.where((event) => event is T).cast(); + } + + StreamSubscription listen( + void Function(T event)? onData, { + Function? onError, + void Function()? onDone, + bool? cancelOnError, + }) { + return where().listen( + onData, + onError: onError, + onDone: onDone, + cancelOnError: cancelOnError, + ); + } + + /// Closes the stream controller + void dispose() { + _controller.close(); + } +} diff --git a/mobile/lib/entities/asset.entity.dart b/mobile/lib/entities/asset.entity.dart index d8d2bd23c3..d754b11dc5 100644 --- a/mobile/lib/entities/asset.entity.dart +++ b/mobile/lib/entities/asset.entity.dart @@ -554,18 +554,12 @@ class Asset { }"""; } - 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; - } - } + static getVisibility(AssetVisibility visibility) => switch (visibility) { + AssetVisibility.archive => AssetVisibilityEnum.archive, + AssetVisibility.hidden => AssetVisibilityEnum.hidden, + AssetVisibility.locked => AssetVisibilityEnum.locked, + AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline, + }; } enum AssetType { diff --git a/mobile/lib/entities/store.entity.dart b/mobile/lib/entities/store.entity.dart index ed955352e2..c937697149 100644 --- a/mobile/lib/entities/store.entity.dart +++ b/mobile/lib/entities/store.entity.dart @@ -11,7 +11,7 @@ class SSLClientCertStoreVal { final Uint8List data; final String? password; - SSLClientCertStoreVal(this.data, this.password); + const SSLClientCertStoreVal(this.data, this.password); void save() { final b64Str = base64Encode(data); diff --git a/mobile/lib/extensions/asyncvalue_extensions.dart b/mobile/lib/extensions/asyncvalue_extensions.dart index f30b5481bd..554c3a8a8a 100644 --- a/mobile/lib/extensions/asyncvalue_extensions.dart +++ b/mobile/lib/extensions/asyncvalue_extensions.dart @@ -23,9 +23,7 @@ extension LogOnError on AsyncValue { if (!skip) { return onLoading?.call() ?? - const Center( - child: ImmichLoadingIndicator(), - ); + const Center(child: ImmichLoadingIndicator()); } } diff --git a/mobile/lib/extensions/duration_extensions.dart b/mobile/lib/extensions/duration_extensions.dart index ca5ba8310c..492627a727 100644 --- a/mobile/lib/extensions/duration_extensions.dart +++ b/mobile/lib/extensions/duration_extensions.dart @@ -3,3 +3,15 @@ extension TZOffsetExtension on Duration { String formatAsOffset() => "${isNegative ? '-' : '+'}${inHours.abs().toString().padLeft(2, '0')}:${inMinutes.abs().remainder(60).toString().padLeft(2, '0')}"; } + +extension DurationFormatExtension on Duration { + String format() { + final seconds = inSeconds.remainder(60).toString().padLeft(2, '0'); + final minutes = inMinutes.remainder(60).toString().padLeft(2, '0'); + if (inHours == 0) { + return "$minutes:$seconds"; + } + final hours = inHours.toString().padLeft(2, '0'); + return "$hours:$minutes:$seconds"; + } +} diff --git a/mobile/lib/extensions/translate_extensions.dart b/mobile/lib/extensions/translate_extensions.dart new file mode 100644 index 0000000000..122830843d --- /dev/null +++ b/mobile/lib/extensions/translate_extensions.dart @@ -0,0 +1,50 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:intl/message_format.dart'; +import 'package:flutter/material.dart'; + +extension StringTranslateExtension on String { + String t({BuildContext? context, Map? args}) { + return _translateHelper(context, this, args); + } +} + +extension TextTranslateExtension on Text { + Text t({BuildContext? context, Map? args}) { + return Text( + _translateHelper(context, data ?? '', args), + key: key, + style: style, + strutStyle: strutStyle, + textAlign: textAlign, + textDirection: textDirection, + locale: locale, + softWrap: softWrap, + overflow: overflow, + textScaler: textScaler, + maxLines: maxLines, + semanticsLabel: semanticsLabel, + textWidthBasis: textWidthBasis, + textHeightBehavior: textHeightBehavior, + ); + } +} + +String _translateHelper( + BuildContext? context, + String key, [ + Map? args, +]) { + if (key.isEmpty) { + return ''; + } + try { + final translatedMessage = key.tr(context: context); + return args != null + ? MessageFormat(translatedMessage, locale: Intl.defaultLocale ?? 'en') + .format(args) + : translatedMessage; + } catch (e) { + debugPrint('Translation failed for key "$key". Error: $e'); + return key; + } +} diff --git a/mobile/lib/infrastructure/entities/exif.entity.dart b/mobile/lib/infrastructure/entities/exif.entity.dart index 11730b7761..2ec8f0023f 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.dart @@ -1,5 +1,6 @@ import 'package:drift/drift.dart' hide Query; import 'package:immich_mobile/domain/models/exif.model.dart' as domain; +import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/exif.converter.dart'; @@ -116,15 +117,15 @@ class RemoteExifEntity extends Table with DriftDefaultsMixin { TextColumn get exposureTime => text().nullable()(); - IntColumn get fNumber => integer().nullable()(); + RealColumn get fNumber => real().nullable()(); IntColumn get fileSize => integer().nullable()(); - IntColumn get focalLength => integer().nullable()(); + RealColumn get focalLength => real().nullable()(); - IntColumn get latitude => integer().nullable()(); + RealColumn get latitude => real().nullable()(); - IntColumn get longitude => integer().nullable()(); + RealColumn get longitude => real().nullable()(); IntColumn get iso => integer().nullable()(); @@ -132,6 +133,8 @@ class RemoteExifEntity extends Table with DriftDefaultsMixin { TextColumn get model => text().nullable()(); + TextColumn get lens => text().nullable()(); + TextColumn get orientation => text().nullable()(); TextColumn get timeZone => text().nullable()(); @@ -143,3 +146,27 @@ class RemoteExifEntity extends Table with DriftDefaultsMixin { @override Set get primaryKey => {assetId}; } + +extension RemoteExifEntityDataDomainEx on RemoteExifEntityData { + domain.ExifInfo toDto() => domain.ExifInfo( + fileSize: fileSize, + dateTimeOriginal: dateTimeOriginal, + timeZone: timeZone, + make: make, + model: model, + iso: iso, + city: city, + state: state, + country: country, + description: description, + orientation: orientation, + latitude: latitude, + longitude: longitude, + f: fNumber?.toDouble(), + mm: focalLength?.toDouble(), + lens: lens, + width: width?.toDouble(), + height: height?.toDouble(), + isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), + ); +} diff --git a/mobile/lib/infrastructure/entities/exif.entity.drift.dart b/mobile/lib/infrastructure/entities/exif.entity.drift.dart index 10025d9cb8..10712948ea 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.drift.dart @@ -19,14 +19,15 @@ typedef $$RemoteExifEntityTableCreateCompanionBuilder i0.Value height, i0.Value width, i0.Value exposureTime, - i0.Value fNumber, + i0.Value fNumber, i0.Value fileSize, - i0.Value focalLength, - i0.Value latitude, - i0.Value longitude, + i0.Value focalLength, + i0.Value latitude, + i0.Value longitude, i0.Value iso, i0.Value make, i0.Value model, + i0.Value lens, i0.Value orientation, i0.Value timeZone, i0.Value rating, @@ -43,14 +44,15 @@ typedef $$RemoteExifEntityTableUpdateCompanionBuilder i0.Value height, i0.Value width, i0.Value exposureTime, - i0.Value fNumber, + i0.Value fNumber, i0.Value fileSize, - i0.Value focalLength, - i0.Value latitude, - i0.Value longitude, + i0.Value focalLength, + i0.Value latitude, + i0.Value longitude, i0.Value iso, i0.Value make, i0.Value model, + i0.Value lens, i0.Value orientation, i0.Value timeZone, i0.Value rating, @@ -125,20 +127,20 @@ class $$RemoteExifEntityTableFilterComposer column: $table.exposureTime, builder: (column) => i0.ColumnFilters(column)); - i0.ColumnFilters get fNumber => $composableBuilder( + i0.ColumnFilters get fNumber => $composableBuilder( column: $table.fNumber, builder: (column) => i0.ColumnFilters(column)); i0.ColumnFilters get fileSize => $composableBuilder( column: $table.fileSize, builder: (column) => i0.ColumnFilters(column)); - i0.ColumnFilters get focalLength => $composableBuilder( + i0.ColumnFilters get focalLength => $composableBuilder( column: $table.focalLength, builder: (column) => i0.ColumnFilters(column)); - i0.ColumnFilters get latitude => $composableBuilder( + i0.ColumnFilters get latitude => $composableBuilder( column: $table.latitude, builder: (column) => i0.ColumnFilters(column)); - i0.ColumnFilters get longitude => $composableBuilder( + i0.ColumnFilters get longitude => $composableBuilder( column: $table.longitude, builder: (column) => i0.ColumnFilters(column)); i0.ColumnFilters get iso => $composableBuilder( @@ -150,6 +152,9 @@ class $$RemoteExifEntityTableFilterComposer i0.ColumnFilters get model => $composableBuilder( column: $table.model, builder: (column) => i0.ColumnFilters(column)); + i0.ColumnFilters get lens => $composableBuilder( + column: $table.lens, builder: (column) => i0.ColumnFilters(column)); + i0.ColumnFilters get orientation => $composableBuilder( column: $table.orientation, builder: (column) => i0.ColumnFilters(column)); @@ -223,20 +228,20 @@ class $$RemoteExifEntityTableOrderingComposer column: $table.exposureTime, builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings get fNumber => $composableBuilder( + i0.ColumnOrderings get fNumber => $composableBuilder( column: $table.fNumber, builder: (column) => i0.ColumnOrderings(column)); i0.ColumnOrderings get fileSize => $composableBuilder( column: $table.fileSize, builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings get focalLength => $composableBuilder( + i0.ColumnOrderings get focalLength => $composableBuilder( column: $table.focalLength, builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings get latitude => $composableBuilder( + i0.ColumnOrderings get latitude => $composableBuilder( column: $table.latitude, builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings get longitude => $composableBuilder( + i0.ColumnOrderings get longitude => $composableBuilder( column: $table.longitude, builder: (column) => i0.ColumnOrderings(column)); @@ -249,6 +254,9 @@ class $$RemoteExifEntityTableOrderingComposer i0.ColumnOrderings get model => $composableBuilder( column: $table.model, builder: (column) => i0.ColumnOrderings(column)); + i0.ColumnOrderings get lens => $composableBuilder( + column: $table.lens, builder: (column) => i0.ColumnOrderings(column)); + i0.ColumnOrderings get orientation => $composableBuilder( column: $table.orientation, builder: (column) => i0.ColumnOrderings(column)); @@ -321,19 +329,19 @@ class $$RemoteExifEntityTableAnnotationComposer i0.GeneratedColumn get exposureTime => $composableBuilder( column: $table.exposureTime, builder: (column) => column); - i0.GeneratedColumn get fNumber => + i0.GeneratedColumn get fNumber => $composableBuilder(column: $table.fNumber, builder: (column) => column); i0.GeneratedColumn get fileSize => $composableBuilder(column: $table.fileSize, builder: (column) => column); - i0.GeneratedColumn get focalLength => $composableBuilder( + i0.GeneratedColumn get focalLength => $composableBuilder( column: $table.focalLength, builder: (column) => column); - i0.GeneratedColumn get latitude => + i0.GeneratedColumn get latitude => $composableBuilder(column: $table.latitude, builder: (column) => column); - i0.GeneratedColumn get longitude => + i0.GeneratedColumn get longitude => $composableBuilder(column: $table.longitude, builder: (column) => column); i0.GeneratedColumn get iso => @@ -345,6 +353,9 @@ class $$RemoteExifEntityTableAnnotationComposer i0.GeneratedColumn get model => $composableBuilder(column: $table.model, builder: (column) => column); + i0.GeneratedColumn get lens => + $composableBuilder(column: $table.lens, builder: (column) => column); + i0.GeneratedColumn get orientation => $composableBuilder( column: $table.orientation, builder: (column) => column); @@ -416,14 +427,15 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< i0.Value height = const i0.Value.absent(), i0.Value width = const i0.Value.absent(), i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), i0.Value iso = const i0.Value.absent(), i0.Value make = const i0.Value.absent(), i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), i0.Value orientation = const i0.Value.absent(), i0.Value timeZone = const i0.Value.absent(), i0.Value rating = const i0.Value.absent(), @@ -447,6 +459,7 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< iso: iso, make: make, model: model, + lens: lens, orientation: orientation, timeZone: timeZone, rating: rating, @@ -462,14 +475,15 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< i0.Value height = const i0.Value.absent(), i0.Value width = const i0.Value.absent(), i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), i0.Value iso = const i0.Value.absent(), i0.Value make = const i0.Value.absent(), i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), i0.Value orientation = const i0.Value.absent(), i0.Value timeZone = const i0.Value.absent(), i0.Value rating = const i0.Value.absent(), @@ -493,6 +507,7 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< iso: iso, make: make, model: model, + lens: lens, orientation: orientation, timeZone: timeZone, rating: rating, @@ -622,9 +637,9 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity static const i0.VerificationMeta _fNumberMeta = const i0.VerificationMeta('fNumber'); @override - late final i0.GeneratedColumn fNumber = i0.GeneratedColumn( + late final i0.GeneratedColumn fNumber = i0.GeneratedColumn( 'f_number', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + type: i0.DriftSqlType.double, requiredDuringInsert: false); static const i0.VerificationMeta _fileSizeMeta = const i0.VerificationMeta('fileSize'); @override @@ -634,21 +649,21 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity static const i0.VerificationMeta _focalLengthMeta = const i0.VerificationMeta('focalLength'); @override - late final i0.GeneratedColumn focalLength = i0.GeneratedColumn( - 'focal_length', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + late final i0.GeneratedColumn focalLength = + i0.GeneratedColumn('focal_length', aliasedName, true, + type: i0.DriftSqlType.double, requiredDuringInsert: false); static const i0.VerificationMeta _latitudeMeta = const i0.VerificationMeta('latitude'); @override - late final i0.GeneratedColumn latitude = i0.GeneratedColumn( + late final i0.GeneratedColumn latitude = i0.GeneratedColumn( 'latitude', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + type: i0.DriftSqlType.double, requiredDuringInsert: false); static const i0.VerificationMeta _longitudeMeta = const i0.VerificationMeta('longitude'); @override - late final i0.GeneratedColumn longitude = i0.GeneratedColumn( + late final i0.GeneratedColumn longitude = i0.GeneratedColumn( 'longitude', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + type: i0.DriftSqlType.double, requiredDuringInsert: false); static const i0.VerificationMeta _isoMeta = const i0.VerificationMeta('iso'); @override late final i0.GeneratedColumn iso = i0.GeneratedColumn( @@ -666,6 +681,12 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity late final i0.GeneratedColumn model = i0.GeneratedColumn( 'model', aliasedName, true, type: i0.DriftSqlType.string, requiredDuringInsert: false); + static const i0.VerificationMeta _lensMeta = + const i0.VerificationMeta('lens'); + @override + late final i0.GeneratedColumn lens = i0.GeneratedColumn( + 'lens', aliasedName, true, + type: i0.DriftSqlType.string, requiredDuringInsert: false); static const i0.VerificationMeta _orientationMeta = const i0.VerificationMeta('orientation'); @override @@ -709,6 +730,7 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity iso, make, model, + lens, orientation, timeZone, rating, @@ -803,6 +825,10 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity context.handle( _modelMeta, model.isAcceptableOrUnknown(data['model']!, _modelMeta)); } + if (data.containsKey('lens')) { + context.handle( + _lensMeta, lens.isAcceptableOrUnknown(data['lens']!, _lensMeta)); + } if (data.containsKey('orientation')) { context.handle( _orientationMeta, @@ -853,21 +879,23 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity exposureTime: attachedDatabase.typeMapping.read( i0.DriftSqlType.string, data['${effectivePrefix}exposure_time']), fNumber: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}f_number']), + .read(i0.DriftSqlType.double, data['${effectivePrefix}f_number']), fileSize: attachedDatabase.typeMapping .read(i0.DriftSqlType.int, data['${effectivePrefix}file_size']), focalLength: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}focal_length']), + .read(i0.DriftSqlType.double, data['${effectivePrefix}focal_length']), latitude: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}latitude']), + .read(i0.DriftSqlType.double, data['${effectivePrefix}latitude']), longitude: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}longitude']), + .read(i0.DriftSqlType.double, data['${effectivePrefix}longitude']), iso: attachedDatabase.typeMapping .read(i0.DriftSqlType.int, data['${effectivePrefix}iso']), make: attachedDatabase.typeMapping .read(i0.DriftSqlType.string, data['${effectivePrefix}make']), model: attachedDatabase.typeMapping .read(i0.DriftSqlType.string, data['${effectivePrefix}model']), + lens: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}lens']), orientation: attachedDatabase.typeMapping .read(i0.DriftSqlType.string, data['${effectivePrefix}orientation']), timeZone: attachedDatabase.typeMapping @@ -901,14 +929,15 @@ class RemoteExifEntityData extends i0.DataClass final int? height; final int? width; final String? exposureTime; - final int? fNumber; + final double? fNumber; final int? fileSize; - final int? focalLength; - final int? latitude; - final int? longitude; + final double? focalLength; + final double? latitude; + final double? longitude; final int? iso; final String? make; final String? model; + final String? lens; final String? orientation; final String? timeZone; final int? rating; @@ -931,6 +960,7 @@ class RemoteExifEntityData extends i0.DataClass this.iso, this.make, this.model, + this.lens, this.orientation, this.timeZone, this.rating, @@ -964,19 +994,19 @@ class RemoteExifEntityData extends i0.DataClass map['exposure_time'] = i0.Variable(exposureTime); } if (!nullToAbsent || fNumber != null) { - map['f_number'] = i0.Variable(fNumber); + map['f_number'] = i0.Variable(fNumber); } if (!nullToAbsent || fileSize != null) { map['file_size'] = i0.Variable(fileSize); } if (!nullToAbsent || focalLength != null) { - map['focal_length'] = i0.Variable(focalLength); + map['focal_length'] = i0.Variable(focalLength); } if (!nullToAbsent || latitude != null) { - map['latitude'] = i0.Variable(latitude); + map['latitude'] = i0.Variable(latitude); } if (!nullToAbsent || longitude != null) { - map['longitude'] = i0.Variable(longitude); + map['longitude'] = i0.Variable(longitude); } if (!nullToAbsent || iso != null) { map['iso'] = i0.Variable(iso); @@ -987,6 +1017,9 @@ class RemoteExifEntityData extends i0.DataClass if (!nullToAbsent || model != null) { map['model'] = i0.Variable(model); } + if (!nullToAbsent || lens != null) { + map['lens'] = i0.Variable(lens); + } if (!nullToAbsent || orientation != null) { map['orientation'] = i0.Variable(orientation); } @@ -1016,14 +1049,15 @@ class RemoteExifEntityData extends i0.DataClass height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), exposureTime: serializer.fromJson(json['exposureTime']), - fNumber: serializer.fromJson(json['fNumber']), + fNumber: serializer.fromJson(json['fNumber']), fileSize: serializer.fromJson(json['fileSize']), - focalLength: serializer.fromJson(json['focalLength']), - latitude: serializer.fromJson(json['latitude']), - longitude: serializer.fromJson(json['longitude']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), iso: serializer.fromJson(json['iso']), make: serializer.fromJson(json['make']), model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), orientation: serializer.fromJson(json['orientation']), timeZone: serializer.fromJson(json['timeZone']), rating: serializer.fromJson(json['rating']), @@ -1043,14 +1077,15 @@ class RemoteExifEntityData extends i0.DataClass 'height': serializer.toJson(height), 'width': serializer.toJson(width), 'exposureTime': serializer.toJson(exposureTime), - 'fNumber': serializer.toJson(fNumber), + 'fNumber': serializer.toJson(fNumber), 'fileSize': serializer.toJson(fileSize), - 'focalLength': serializer.toJson(focalLength), - 'latitude': serializer.toJson(latitude), - 'longitude': serializer.toJson(longitude), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), 'iso': serializer.toJson(iso), 'make': serializer.toJson(make), 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), 'orientation': serializer.toJson(orientation), 'timeZone': serializer.toJson(timeZone), 'rating': serializer.toJson(rating), @@ -1068,14 +1103,15 @@ class RemoteExifEntityData extends i0.DataClass i0.Value height = const i0.Value.absent(), i0.Value width = const i0.Value.absent(), i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), i0.Value iso = const i0.Value.absent(), i0.Value make = const i0.Value.absent(), i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), i0.Value orientation = const i0.Value.absent(), i0.Value timeZone = const i0.Value.absent(), i0.Value rating = const i0.Value.absent(), @@ -1101,6 +1137,7 @@ class RemoteExifEntityData extends i0.DataClass iso: iso.present ? iso.value : this.iso, make: make.present ? make.value : this.make, model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, orientation: orientation.present ? orientation.value : this.orientation, timeZone: timeZone.present ? timeZone.value : this.timeZone, rating: rating.present ? rating.value : this.rating, @@ -1132,6 +1169,7 @@ class RemoteExifEntityData extends i0.DataClass iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, orientation: data.orientation.present ? data.orientation.value : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, @@ -1162,6 +1200,7 @@ class RemoteExifEntityData extends i0.DataClass ..write('iso: $iso, ') ..write('make: $make, ') ..write('model: $model, ') + ..write('lens: $lens, ') ..write('orientation: $orientation, ') ..write('timeZone: $timeZone, ') ..write('rating: $rating, ') @@ -1189,6 +1228,7 @@ class RemoteExifEntityData extends i0.DataClass iso, make, model, + lens, orientation, timeZone, rating, @@ -1215,6 +1255,7 @@ class RemoteExifEntityData extends i0.DataClass other.iso == this.iso && other.make == this.make && other.model == this.model && + other.lens == this.lens && other.orientation == this.orientation && other.timeZone == this.timeZone && other.rating == this.rating && @@ -1232,14 +1273,15 @@ class RemoteExifEntityCompanion final i0.Value height; final i0.Value width; final i0.Value exposureTime; - final i0.Value fNumber; + final i0.Value fNumber; final i0.Value fileSize; - final i0.Value focalLength; - final i0.Value latitude; - final i0.Value longitude; + final i0.Value focalLength; + final i0.Value latitude; + final i0.Value longitude; final i0.Value iso; final i0.Value make; final i0.Value model; + final i0.Value lens; final i0.Value orientation; final i0.Value timeZone; final i0.Value rating; @@ -1262,6 +1304,7 @@ class RemoteExifEntityCompanion this.iso = const i0.Value.absent(), this.make = const i0.Value.absent(), this.model = const i0.Value.absent(), + this.lens = const i0.Value.absent(), this.orientation = const i0.Value.absent(), this.timeZone = const i0.Value.absent(), this.rating = const i0.Value.absent(), @@ -1285,6 +1328,7 @@ class RemoteExifEntityCompanion this.iso = const i0.Value.absent(), this.make = const i0.Value.absent(), this.model = const i0.Value.absent(), + this.lens = const i0.Value.absent(), this.orientation = const i0.Value.absent(), this.timeZone = const i0.Value.absent(), this.rating = const i0.Value.absent(), @@ -1300,14 +1344,15 @@ class RemoteExifEntityCompanion i0.Expression? height, i0.Expression? width, i0.Expression? exposureTime, - i0.Expression? fNumber, + i0.Expression? fNumber, i0.Expression? fileSize, - i0.Expression? focalLength, - i0.Expression? latitude, - i0.Expression? longitude, + i0.Expression? focalLength, + i0.Expression? latitude, + i0.Expression? longitude, i0.Expression? iso, i0.Expression? make, i0.Expression? model, + i0.Expression? lens, i0.Expression? orientation, i0.Expression? timeZone, i0.Expression? rating, @@ -1331,6 +1376,7 @@ class RemoteExifEntityCompanion if (iso != null) 'iso': iso, if (make != null) 'make': make, if (model != null) 'model': model, + if (lens != null) 'lens': lens, if (orientation != null) 'orientation': orientation, if (timeZone != null) 'time_zone': timeZone, if (rating != null) 'rating': rating, @@ -1348,14 +1394,15 @@ class RemoteExifEntityCompanion i0.Value? height, i0.Value? width, i0.Value? exposureTime, - i0.Value? fNumber, + i0.Value? fNumber, i0.Value? fileSize, - i0.Value? focalLength, - i0.Value? latitude, - i0.Value? longitude, + i0.Value? focalLength, + i0.Value? latitude, + i0.Value? longitude, i0.Value? iso, i0.Value? make, i0.Value? model, + i0.Value? lens, i0.Value? orientation, i0.Value? timeZone, i0.Value? rating, @@ -1378,6 +1425,7 @@ class RemoteExifEntityCompanion iso: iso ?? this.iso, make: make ?? this.make, model: model ?? this.model, + lens: lens ?? this.lens, orientation: orientation ?? this.orientation, timeZone: timeZone ?? this.timeZone, rating: rating ?? this.rating, @@ -1416,19 +1464,19 @@ class RemoteExifEntityCompanion map['exposure_time'] = i0.Variable(exposureTime.value); } if (fNumber.present) { - map['f_number'] = i0.Variable(fNumber.value); + map['f_number'] = i0.Variable(fNumber.value); } if (fileSize.present) { map['file_size'] = i0.Variable(fileSize.value); } if (focalLength.present) { - map['focal_length'] = i0.Variable(focalLength.value); + map['focal_length'] = i0.Variable(focalLength.value); } if (latitude.present) { - map['latitude'] = i0.Variable(latitude.value); + map['latitude'] = i0.Variable(latitude.value); } if (longitude.present) { - map['longitude'] = i0.Variable(longitude.value); + map['longitude'] = i0.Variable(longitude.value); } if (iso.present) { map['iso'] = i0.Variable(iso.value); @@ -1439,6 +1487,9 @@ class RemoteExifEntityCompanion if (model.present) { map['model'] = i0.Variable(model.value); } + if (lens.present) { + map['lens'] = i0.Variable(lens.value); + } if (orientation.present) { map['orientation'] = i0.Variable(orientation.value); } @@ -1474,6 +1525,7 @@ class RemoteExifEntityCompanion ..write('iso: $iso, ') ..write('make: $make, ') ..write('model: $model, ') + ..write('lens: $lens, ') ..write('orientation: $orientation, ') ..write('timeZone: $timeZone, ') ..write('rating: $rating, ') diff --git a/mobile/lib/infrastructure/entities/local_album.entity.dart b/mobile/lib/infrastructure/entities/local_album.entity.dart index 9657173c3c..398c5d4e44 100644 --- a/mobile/lib/infrastructure/entities/local_album.entity.dart +++ b/mobile/lib/infrastructure/entities/local_album.entity.dart @@ -1,5 +1,5 @@ import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/models/local_album.model.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class LocalAlbumEntity extends Table with DriftDefaultsMixin { diff --git a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart index ff6226ba3f..06f65e25d8 100644 --- a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart @@ -3,7 +3,7 @@ import 'package:drift/drift.dart' as i0; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' as i1; -import 'package:immich_mobile/domain/models/local_album.model.dart' as i2; +import 'package:immich_mobile/domain/models/album/local_album.model.dart' as i2; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart' as i3; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart index ff5ee74818..204d5d6a80 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart @@ -1,4 +1,6 @@ import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; @@ -12,6 +14,25 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { // Only used during backup to mirror the favorite status of the asset in the server BoolColumn get isFavorite => boolean().withDefault(const Constant(false))(); + IntColumn get orientation => integer().withDefault(const Constant(0))(); + @override Set get primaryKey => {id}; } + +extension LocalAssetEntityDataDomainEx on LocalAssetEntityData { + LocalAsset toDto() => LocalAsset( + id: id, + name: name, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + isFavorite: isFavorite, + height: height, + width: width, + remoteId: null, + orientation: orientation, + ); +} diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart index a3c79b2e2e..e9c5961aa5 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart @@ -20,6 +20,7 @@ typedef $$LocalAssetEntityTableCreateCompanionBuilder required String id, i0.Value checksum, i0.Value isFavorite, + i0.Value orientation, }); typedef $$LocalAssetEntityTableUpdateCompanionBuilder = i1.LocalAssetEntityCompanion Function({ @@ -33,6 +34,7 @@ typedef $$LocalAssetEntityTableUpdateCompanionBuilder i0.Value id, i0.Value checksum, i0.Value isFavorite, + i0.Value orientation, }); class $$LocalAssetEntityTableFilterComposer @@ -76,6 +78,10 @@ class $$LocalAssetEntityTableFilterComposer i0.ColumnFilters get isFavorite => $composableBuilder( column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get orientation => $composableBuilder( + column: $table.orientation, + builder: (column) => i0.ColumnFilters(column)); } class $$LocalAssetEntityTableOrderingComposer @@ -120,6 +126,10 @@ class $$LocalAssetEntityTableOrderingComposer i0.ColumnOrderings get isFavorite => $composableBuilder( column: $table.isFavorite, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get orientation => $composableBuilder( + column: $table.orientation, + builder: (column) => i0.ColumnOrderings(column)); } class $$LocalAssetEntityTableAnnotationComposer @@ -160,6 +170,9 @@ class $$LocalAssetEntityTableAnnotationComposer i0.GeneratedColumn get isFavorite => $composableBuilder( column: $table.isFavorite, builder: (column) => column); + + i0.GeneratedColumn get orientation => $composableBuilder( + column: $table.orientation, builder: (column) => column); } class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< @@ -201,6 +214,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< i0.Value id = const i0.Value.absent(), i0.Value checksum = const i0.Value.absent(), i0.Value isFavorite = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), }) => i1.LocalAssetEntityCompanion( name: name, @@ -213,6 +227,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< id: id, checksum: checksum, isFavorite: isFavorite, + orientation: orientation, ), createCompanionCallback: ({ required String name, @@ -225,6 +240,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< required String id, i0.Value checksum = const i0.Value.absent(), i0.Value isFavorite = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), }) => i1.LocalAssetEntityCompanion.insert( name: name, @@ -237,6 +253,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< id: id, checksum: checksum, isFavorite: isFavorite, + orientation: orientation, ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) @@ -337,6 +354,14 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity defaultConstraints: i0.GeneratedColumn.constraintIsAlways( 'CHECK ("is_favorite" IN (0, 1))'), defaultValue: const i4.Constant(false)); + static const i0.VerificationMeta _orientationMeta = + const i0.VerificationMeta('orientation'); + @override + late final i0.GeneratedColumn orientation = i0.GeneratedColumn( + 'orientation', aliasedName, false, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const i4.Constant(0)); @override List get $columns => [ name, @@ -348,7 +373,8 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity durationInSeconds, id, checksum, - isFavorite + isFavorite, + orientation ]; @override String get aliasedName => _alias ?? actualTableName; @@ -404,6 +430,12 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity isFavorite.isAcceptableOrUnknown( data['is_favorite']!, _isFavoriteMeta)); } + if (data.containsKey('orientation')) { + context.handle( + _orientationMeta, + orientation.isAcceptableOrUnknown( + data['orientation']!, _orientationMeta)); + } return context; } @@ -435,6 +467,8 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity .read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']), isFavorite: attachedDatabase.typeMapping .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + orientation: attachedDatabase.typeMapping + .read(i0.DriftSqlType.int, data['${effectivePrefix}orientation'])!, ); } @@ -463,6 +497,7 @@ class LocalAssetEntityData extends i0.DataClass final String id; final String? checksum; final bool isFavorite; + final int orientation; const LocalAssetEntityData( {required this.name, required this.type, @@ -473,7 +508,8 @@ class LocalAssetEntityData extends i0.DataClass this.durationInSeconds, required this.id, this.checksum, - required this.isFavorite}); + required this.isFavorite, + required this.orientation}); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -498,6 +534,7 @@ class LocalAssetEntityData extends i0.DataClass map['checksum'] = i0.Variable(checksum); } map['is_favorite'] = i0.Variable(isFavorite); + map['orientation'] = i0.Variable(orientation); return map; } @@ -516,6 +553,7 @@ class LocalAssetEntityData extends i0.DataClass id: serializer.fromJson(json['id']), checksum: serializer.fromJson(json['checksum']), isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), ); } @override @@ -533,6 +571,7 @@ class LocalAssetEntityData extends i0.DataClass 'id': serializer.toJson(id), 'checksum': serializer.toJson(checksum), 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), }; } @@ -546,7 +585,8 @@ class LocalAssetEntityData extends i0.DataClass i0.Value durationInSeconds = const i0.Value.absent(), String? id, i0.Value checksum = const i0.Value.absent(), - bool? isFavorite}) => + bool? isFavorite, + int? orientation}) => i1.LocalAssetEntityData( name: name ?? this.name, type: type ?? this.type, @@ -560,6 +600,7 @@ class LocalAssetEntityData extends i0.DataClass id: id ?? this.id, checksum: checksum.present ? checksum.value : this.checksum, isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, ); LocalAssetEntityData copyWithCompanion(i1.LocalAssetEntityCompanion data) { return LocalAssetEntityData( @@ -576,6 +617,8 @@ class LocalAssetEntityData extends i0.DataClass checksum: data.checksum.present ? data.checksum.value : this.checksum, isFavorite: data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + orientation: + data.orientation.present ? data.orientation.value : this.orientation, ); } @@ -591,14 +634,15 @@ class LocalAssetEntityData extends i0.DataClass ..write('durationInSeconds: $durationInSeconds, ') ..write('id: $id, ') ..write('checksum: $checksum, ') - ..write('isFavorite: $isFavorite') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') ..write(')')) .toString(); } @override int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, - height, durationInSeconds, id, checksum, isFavorite); + height, durationInSeconds, id, checksum, isFavorite, orientation); @override bool operator ==(Object other) => identical(this, other) || @@ -612,7 +656,8 @@ class LocalAssetEntityData extends i0.DataClass other.durationInSeconds == this.durationInSeconds && other.id == this.id && other.checksum == this.checksum && - other.isFavorite == this.isFavorite); + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); } class LocalAssetEntityCompanion @@ -627,6 +672,7 @@ class LocalAssetEntityCompanion final i0.Value id; final i0.Value checksum; final i0.Value isFavorite; + final i0.Value orientation; const LocalAssetEntityCompanion({ this.name = const i0.Value.absent(), this.type = const i0.Value.absent(), @@ -638,6 +684,7 @@ class LocalAssetEntityCompanion this.id = const i0.Value.absent(), this.checksum = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(), + this.orientation = const i0.Value.absent(), }); LocalAssetEntityCompanion.insert({ required String name, @@ -650,6 +697,7 @@ class LocalAssetEntityCompanion required String id, this.checksum = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(), + this.orientation = const i0.Value.absent(), }) : name = i0.Value(name), type = i0.Value(type), id = i0.Value(id); @@ -664,6 +712,7 @@ class LocalAssetEntityCompanion i0.Expression? id, i0.Expression? checksum, i0.Expression? isFavorite, + i0.Expression? orientation, }) { return i0.RawValuesInsertable({ if (name != null) 'name': name, @@ -676,6 +725,7 @@ class LocalAssetEntityCompanion if (id != null) 'id': id, if (checksum != null) 'checksum': checksum, if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, }); } @@ -689,7 +739,8 @@ class LocalAssetEntityCompanion i0.Value? durationInSeconds, i0.Value? id, i0.Value? checksum, - i0.Value? isFavorite}) { + i0.Value? isFavorite, + i0.Value? orientation}) { return i1.LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -701,6 +752,7 @@ class LocalAssetEntityCompanion id: id ?? this.id, checksum: checksum ?? this.checksum, isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, ); } @@ -738,6 +790,9 @@ class LocalAssetEntityCompanion if (isFavorite.present) { map['is_favorite'] = i0.Variable(isFavorite.value); } + if (orientation.present) { + map['orientation'] = i0.Variable(orientation.value); + } return map; } @@ -753,7 +808,8 @@ class LocalAssetEntityCompanion ..write('durationInSeconds: $durationInSeconds, ') ..write('id: $id, ') ..write('checksum: $checksum, ') - ..write('isFavorite: $isFavorite') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') ..write(')')) .toString(); } diff --git a/mobile/lib/infrastructure/entities/memory.entity.dart b/mobile/lib/infrastructure/entities/memory.entity.dart new file mode 100644 index 0000000000..0e19802103 --- /dev/null +++ b/mobile/lib/infrastructure/entities/memory.entity.dart @@ -0,0 +1,36 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class MemoryEntity extends Table with DriftDefaultsMixin { + const MemoryEntity(); + + TextColumn get id => text()(); + + DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); + + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); + + DateTimeColumn get deletedAt => dateTime().nullable()(); + + TextColumn get ownerId => + text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + + IntColumn get type => intEnum()(); + + TextColumn get data => text()(); + + BoolColumn get isSaved => boolean().withDefault(const Constant(false))(); + + DateTimeColumn get memoryAt => dateTime()(); + + DateTimeColumn get seenAt => dateTime().nullable()(); + + DateTimeColumn get showAt => dateTime().nullable()(); + + DateTimeColumn get hideAt => dateTime().nullable()(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/memory.entity.drift.dart b/mobile/lib/infrastructure/entities/memory.entity.drift.dart new file mode 100644 index 0000000000..cb88651ba4 --- /dev/null +++ b/mobile/lib/infrastructure/entities/memory.entity.drift.dart @@ -0,0 +1,970 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart' + as i1; +import 'package:immich_mobile/domain/models/memory.model.dart' as i2; +import 'package:immich_mobile/infrastructure/entities/memory.entity.dart' as i3; +import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; +import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' + as i5; +import 'package:drift/internal/modular.dart' as i6; + +typedef $$MemoryEntityTableCreateCompanionBuilder = i1.MemoryEntityCompanion + Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value deletedAt, + required String ownerId, + required i2.MemoryTypeEnum type, + required String data, + i0.Value isSaved, + required DateTime memoryAt, + i0.Value seenAt, + i0.Value showAt, + i0.Value hideAt, +}); +typedef $$MemoryEntityTableUpdateCompanionBuilder = i1.MemoryEntityCompanion + Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value deletedAt, + i0.Value ownerId, + i0.Value type, + i0.Value data, + i0.Value isSaved, + i0.Value memoryAt, + i0.Value seenAt, + i0.Value showAt, + i0.Value hideAt, +}); + +final class $$MemoryEntityTableReferences extends i0.BaseReferences< + i0.GeneratedDatabase, i1.$MemoryEntityTable, i1.MemoryEntityData> { + $$MemoryEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => + i6.ReadDatabaseContainer(db) + .resultSet('user_entity') + .createAlias(i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer(db) + .resultSet('memory_entity') + .ownerId, + i6.ReadDatabaseContainer(db) + .resultSet('user_entity') + .id)); + + i5.$$UserEntityTableProcessedTableManager get ownerId { + final $_column = $_itemColumn('owner_id')!; + + final manager = i5 + .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer($_db) + .resultSet('user_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } +} + +class $$MemoryEntityTableFilterComposer + extends i0.Composer { + $$MemoryEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get updatedAt => $composableBuilder( + column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get deletedAt => $composableBuilder( + column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnWithTypeConverterFilters + get type => $composableBuilder( + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + + i0.ColumnFilters get data => $composableBuilder( + column: $table.data, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get isSaved => $composableBuilder( + column: $table.isSaved, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get memoryAt => $composableBuilder( + column: $table.memoryAt, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get seenAt => $composableBuilder( + column: $table.seenAt, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get showAt => $composableBuilder( + column: $table.showAt, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get hideAt => $composableBuilder( + column: $table.hideAt, builder: (column) => i0.ColumnFilters(column)); + + i5.$$UserEntityTableFilterComposer get ownerId { + final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$MemoryEntityTableOrderingComposer + extends i0.Composer { + $$MemoryEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get deletedAt => $composableBuilder( + column: $table.deletedAt, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get type => $composableBuilder( + column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get data => $composableBuilder( + column: $table.data, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get isSaved => $composableBuilder( + column: $table.isSaved, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get memoryAt => $composableBuilder( + column: $table.memoryAt, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get seenAt => $composableBuilder( + column: $table.seenAt, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get showAt => $composableBuilder( + column: $table.showAt, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get hideAt => $composableBuilder( + column: $table.hideAt, builder: (column) => i0.ColumnOrderings(column)); + + i5.$$UserEntityTableOrderingComposer get ownerId { + final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$MemoryEntityTableAnnotationComposer + extends i0.Composer { + $$MemoryEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + i0.GeneratedColumn get updatedAt => + $composableBuilder(column: $table.updatedAt, builder: (column) => column); + + i0.GeneratedColumn get deletedAt => + $composableBuilder(column: $table.deletedAt, builder: (column) => column); + + i0.GeneratedColumnWithTypeConverter get type => + $composableBuilder(column: $table.type, builder: (column) => column); + + i0.GeneratedColumn get data => + $composableBuilder(column: $table.data, builder: (column) => column); + + i0.GeneratedColumn get isSaved => + $composableBuilder(column: $table.isSaved, builder: (column) => column); + + i0.GeneratedColumn get memoryAt => + $composableBuilder(column: $table.memoryAt, builder: (column) => column); + + i0.GeneratedColumn get seenAt => + $composableBuilder(column: $table.seenAt, builder: (column) => column); + + i0.GeneratedColumn get showAt => + $composableBuilder(column: $table.showAt, builder: (column) => column); + + i0.GeneratedColumn get hideAt => + $composableBuilder(column: $table.hideAt, builder: (column) => column); + + i5.$$UserEntityTableAnnotationComposer get ownerId { + final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$MemoryEntityTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData, + i1.$$MemoryEntityTableFilterComposer, + i1.$$MemoryEntityTableOrderingComposer, + i1.$$MemoryEntityTableAnnotationComposer, + $$MemoryEntityTableCreateCompanionBuilder, + $$MemoryEntityTableUpdateCompanionBuilder, + (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), + i1.MemoryEntityData, + i0.PrefetchHooks Function({bool ownerId})> { + $$MemoryEntityTableTableManager( + i0.GeneratedDatabase db, i1.$MemoryEntityTable table) + : super(i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$MemoryEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$MemoryEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + i1.$$MemoryEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value data = const i0.Value.absent(), + i0.Value isSaved = const i0.Value.absent(), + i0.Value memoryAt = const i0.Value.absent(), + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => + i1.MemoryEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + ownerId: ownerId, + type: type, + data: data, + isSaved: isSaved, + memoryAt: memoryAt, + seenAt: seenAt, + showAt: showAt, + hideAt: hideAt, + ), + createCompanionCallback: ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + required String ownerId, + required i2.MemoryTypeEnum type, + required String data, + i0.Value isSaved = const i0.Value.absent(), + required DateTime memoryAt, + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => + i1.MemoryEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + ownerId: ownerId, + type: type, + data: data, + isSaved: isSaved, + memoryAt: memoryAt, + seenAt: seenAt, + showAt: showAt, + hideAt: hideAt, + ), + withReferenceMapper: (p0) => p0 + .map((e) => ( + e.readTable(table), + i1.$$MemoryEntityTableReferences(db, table, e) + )) + .toList(), + prefetchHooksCallback: ({ownerId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (ownerId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: + i1.$$MemoryEntityTableReferences._ownerIdTable(db), + referencedColumn: + i1.$$MemoryEntityTableReferences._ownerIdTable(db).id, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + )); +} + +typedef $$MemoryEntityTableProcessedTableManager = i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData, + i1.$$MemoryEntityTableFilterComposer, + i1.$$MemoryEntityTableOrderingComposer, + i1.$$MemoryEntityTableAnnotationComposer, + $$MemoryEntityTableCreateCompanionBuilder, + $$MemoryEntityTableUpdateCompanionBuilder, + (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), + i1.MemoryEntityData, + i0.PrefetchHooks Function({bool ownerId})>; + +class $MemoryEntityTable extends i3.MemoryEntity + with i0.TableInfo<$MemoryEntityTable, i1.MemoryEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $MemoryEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + static const i0.VerificationMeta _createdAtMeta = + const i0.VerificationMeta('createdAt'); + @override + late final i0.GeneratedColumn createdAt = + i0.GeneratedColumn('created_at', aliasedName, false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime); + static const i0.VerificationMeta _updatedAtMeta = + const i0.VerificationMeta('updatedAt'); + @override + late final i0.GeneratedColumn updatedAt = + i0.GeneratedColumn('updated_at', aliasedName, false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime); + static const i0.VerificationMeta _deletedAtMeta = + const i0.VerificationMeta('deletedAt'); + @override + late final i0.GeneratedColumn deletedAt = + i0.GeneratedColumn('deleted_at', aliasedName, true, + type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + static const i0.VerificationMeta _ownerIdMeta = + const i0.VerificationMeta('ownerId'); + @override + late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( + 'owner_id', aliasedName, false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + @override + late final i0.GeneratedColumnWithTypeConverter type = + i0.GeneratedColumn('type', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true) + .withConverter( + i1.$MemoryEntityTable.$convertertype); + static const i0.VerificationMeta _dataMeta = + const i0.VerificationMeta('data'); + @override + late final i0.GeneratedColumn data = i0.GeneratedColumn( + 'data', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + static const i0.VerificationMeta _isSavedMeta = + const i0.VerificationMeta('isSaved'); + @override + late final i0.GeneratedColumn isSaved = i0.GeneratedColumn( + 'is_saved', aliasedName, false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + i0.GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), + defaultValue: const i4.Constant(false)); + static const i0.VerificationMeta _memoryAtMeta = + const i0.VerificationMeta('memoryAt'); + @override + late final i0.GeneratedColumn memoryAt = + i0.GeneratedColumn('memory_at', aliasedName, false, + type: i0.DriftSqlType.dateTime, requiredDuringInsert: true); + static const i0.VerificationMeta _seenAtMeta = + const i0.VerificationMeta('seenAt'); + @override + late final i0.GeneratedColumn seenAt = i0.GeneratedColumn( + 'seen_at', aliasedName, true, + type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + static const i0.VerificationMeta _showAtMeta = + const i0.VerificationMeta('showAt'); + @override + late final i0.GeneratedColumn showAt = i0.GeneratedColumn( + 'show_at', aliasedName, true, + type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + static const i0.VerificationMeta _hideAtMeta = + const i0.VerificationMeta('hideAt'); + @override + late final i0.GeneratedColumn hideAt = i0.GeneratedColumn( + 'hide_at', aliasedName, true, + type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, + {bool isInserting = false}) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('created_at')) { + context.handle(_createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + } + if (data.containsKey('updated_at')) { + context.handle(_updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + } + if (data.containsKey('deleted_at')) { + context.handle(_deletedAtMeta, + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); + } + if (data.containsKey('owner_id')) { + context.handle(_ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + } else if (isInserting) { + context.missing(_ownerIdMeta); + } + if (data.containsKey('data')) { + context.handle( + _dataMeta, this.data.isAcceptableOrUnknown(data['data']!, _dataMeta)); + } else if (isInserting) { + context.missing(_dataMeta); + } + if (data.containsKey('is_saved')) { + context.handle(_isSavedMeta, + isSaved.isAcceptableOrUnknown(data['is_saved']!, _isSavedMeta)); + } + if (data.containsKey('memory_at')) { + context.handle(_memoryAtMeta, + memoryAt.isAcceptableOrUnknown(data['memory_at']!, _memoryAtMeta)); + } else if (isInserting) { + context.missing(_memoryAtMeta); + } + if (data.containsKey('seen_at')) { + context.handle(_seenAtMeta, + seenAt.isAcceptableOrUnknown(data['seen_at']!, _seenAtMeta)); + } + if (data.containsKey('show_at')) { + context.handle(_showAtMeta, + showAt.isAcceptableOrUnknown(data['show_at']!, _showAtMeta)); + } + if (data.containsKey('hide_at')) { + context.handle(_hideAtMeta, + hideAt.isAcceptableOrUnknown(data['hide_at']!, _hideAtMeta)); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.MemoryEntityData( + id: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + deletedAt: attachedDatabase.typeMapping + .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + ownerId: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + type: i1.$MemoryEntityTable.$convertertype.fromSql(attachedDatabase + .typeMapping + .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), + data: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}data'])!, + isSaved: attachedDatabase.typeMapping + .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, + memoryAt: attachedDatabase.typeMapping + .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, + seenAt: attachedDatabase.typeMapping + .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), + showAt: attachedDatabase.typeMapping + .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}show_at']), + hideAt: attachedDatabase.typeMapping + .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + ); + } + + @override + $MemoryEntityTable createAlias(String alias) { + return $MemoryEntityTable(attachedDatabase, alias); + } + + static i0.JsonTypeConverter2 $convertertype = + const i0.EnumIndexConverter(i2.MemoryTypeEnum.values); + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends i0.DataClass + implements i0.Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final i2.MemoryTypeEnum type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['created_at'] = i0.Variable(createdAt); + map['updated_at'] = i0.Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = i0.Variable(deletedAt); + } + map['owner_id'] = i0.Variable(ownerId); + { + map['type'] = + i0.Variable(i1.$MemoryEntityTable.$convertertype.toSql(type)); + } + map['data'] = i0.Variable(data); + map['is_saved'] = i0.Variable(isSaved); + map['memory_at'] = i0.Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = i0.Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = i0.Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = i0.Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson(Map json, + {i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: i1.$MemoryEntityTable.$convertertype + .fromJson(serializer.fromJson(json['type'])), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer + .toJson(i1.$MemoryEntityTable.$convertertype.toJson(type)), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + i1.MemoryEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value deletedAt = const i0.Value.absent(), + String? ownerId, + i2.MemoryTypeEnum? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent()}) => + i1.MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(i1.MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, + type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value createdAt; + final i0.Value updatedAt; + final i0.Value deletedAt; + final i0.Value ownerId; + final i0.Value type; + final i0.Value data; + final i0.Value isSaved; + final i0.Value memoryAt; + final i0.Value seenAt; + final i0.Value showAt; + final i0.Value hideAt; + const MemoryEntityCompanion({ + this.id = const i0.Value.absent(), + this.createdAt = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + this.deletedAt = const i0.Value.absent(), + this.ownerId = const i0.Value.absent(), + this.type = const i0.Value.absent(), + this.data = const i0.Value.absent(), + this.isSaved = const i0.Value.absent(), + this.memoryAt = const i0.Value.absent(), + this.seenAt = const i0.Value.absent(), + this.showAt = const i0.Value.absent(), + this.hideAt = const i0.Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + this.deletedAt = const i0.Value.absent(), + required String ownerId, + required i2.MemoryTypeEnum type, + required String data, + this.isSaved = const i0.Value.absent(), + required DateTime memoryAt, + this.seenAt = const i0.Value.absent(), + this.showAt = const i0.Value.absent(), + this.hideAt = const i0.Value.absent(), + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + type = i0.Value(type), + data = i0.Value(data), + memoryAt = i0.Value(memoryAt); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? createdAt, + i0.Expression? updatedAt, + i0.Expression? deletedAt, + i0.Expression? ownerId, + i0.Expression? type, + i0.Expression? data, + i0.Expression? isSaved, + i0.Expression? memoryAt, + i0.Expression? seenAt, + i0.Expression? showAt, + i0.Expression? hideAt, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + i1.MemoryEntityCompanion copyWith( + {i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? deletedAt, + i0.Value? ownerId, + i0.Value? type, + i0.Value? data, + i0.Value? isSaved, + i0.Value? memoryAt, + i0.Value? seenAt, + i0.Value? showAt, + i0.Value? hideAt}) { + return i1.MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = i0.Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = i0.Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = i0.Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = i0.Variable(ownerId.value); + } + if (type.present) { + map['type'] = i0.Variable( + i1.$MemoryEntityTable.$convertertype.toSql(type.value)); + } + if (data.present) { + map['data'] = i0.Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = i0.Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = i0.Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = i0.Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = i0.Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = i0.Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/memory_asset.entity.dart b/mobile/lib/infrastructure/entities/memory_asset.entity.dart new file mode 100644 index 0000000000..c304b03724 --- /dev/null +++ b/mobile/lib/infrastructure/entities/memory_asset.entity.dart @@ -0,0 +1,17 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/memory.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class MemoryAssetEntity extends Table with DriftDefaultsMixin { + const MemoryAssetEntity(); + + TextColumn get assetId => + text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + + TextColumn get memoryId => + text().references(MemoryEntity, #id, onDelete: KeyAction.cascade)(); + + @override + Set get primaryKey => {assetId, memoryId}; +} diff --git a/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart new file mode 100644 index 0000000000..9253e8bc05 --- /dev/null +++ b/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart @@ -0,0 +1,550 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart' + as i1; +import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart' + as i2; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' + as i3; +import 'package:drift/internal/modular.dart' as i4; +import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart' + as i5; + +typedef $$MemoryAssetEntityTableCreateCompanionBuilder + = i1.MemoryAssetEntityCompanion Function({ + required String assetId, + required String memoryId, +}); +typedef $$MemoryAssetEntityTableUpdateCompanionBuilder + = i1.MemoryAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value memoryId, +}); + +final class $$MemoryAssetEntityTableReferences extends i0.BaseReferences< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData> { + $$MemoryAssetEntityTableReferences( + super.$_db, super.$_table, super.$_typedResult); + + static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => + i4.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .createAlias(i0.$_aliasNameGenerator( + i4.ReadDatabaseContainer(db) + .resultSet('memory_asset_entity') + .assetId, + i4.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .id)); + + i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { + final $_column = $_itemColumn('asset_id')!; + + final manager = i3 + .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer($_db) + .resultSet('remote_asset_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } + + static i5.$MemoryEntityTable _memoryIdTable(i0.GeneratedDatabase db) => + i4.ReadDatabaseContainer(db) + .resultSet('memory_entity') + .createAlias(i0.$_aliasNameGenerator( + i4.ReadDatabaseContainer(db) + .resultSet('memory_asset_entity') + .memoryId, + i4.ReadDatabaseContainer(db) + .resultSet('memory_entity') + .id)); + + i5.$$MemoryEntityTableProcessedTableManager get memoryId { + final $_column = $_itemColumn('memory_id')!; + + final manager = i5 + .$$MemoryEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer($_db) + .resultSet('memory_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_memoryIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } +} + +class $$MemoryAssetEntityTableFilterComposer + extends i0.Composer { + $$MemoryAssetEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i3.$$RemoteAssetEntityTableFilterComposer get assetId { + final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$MemoryEntityTableFilterComposer get memoryId { + final i5.$$MemoryEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$MemoryEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$MemoryAssetEntityTableOrderingComposer + extends i0.Composer { + $$MemoryAssetEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i3.$$RemoteAssetEntityTableOrderingComposer get assetId { + final i3.$$RemoteAssetEntityTableOrderingComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet( + 'remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$MemoryEntityTableOrderingComposer get memoryId { + final i5.$$MemoryEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$MemoryEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$MemoryAssetEntityTableAnnotationComposer + extends i0.Composer { + $$MemoryAssetEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { + final i3.$$RemoteAssetEntityTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet( + 'remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$MemoryEntityTableAnnotationComposer get memoryId { + final i5.$$MemoryEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$MemoryEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData, + i1.$$MemoryAssetEntityTableFilterComposer, + i1.$$MemoryAssetEntityTableOrderingComposer, + i1.$$MemoryAssetEntityTableAnnotationComposer, + $$MemoryAssetEntityTableCreateCompanionBuilder, + $$MemoryAssetEntityTableUpdateCompanionBuilder, + (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), + i1.MemoryAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool memoryId})> { + $$MemoryAssetEntityTableTableManager( + i0.GeneratedDatabase db, i1.$MemoryAssetEntityTable table) + : super(i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$MemoryAssetEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => i1 + .$$MemoryAssetEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + i1.$$MemoryAssetEntityTableAnnotationComposer( + $db: db, $table: table), + updateCompanionCallback: ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value memoryId = const i0.Value.absent(), + }) => + i1.MemoryAssetEntityCompanion( + assetId: assetId, + memoryId: memoryId, + ), + createCompanionCallback: ({ + required String assetId, + required String memoryId, + }) => + i1.MemoryAssetEntityCompanion.insert( + assetId: assetId, + memoryId: memoryId, + ), + withReferenceMapper: (p0) => p0 + .map((e) => ( + e.readTable(table), + i1.$$MemoryAssetEntityTableReferences(db, table, e) + )) + .toList(), + prefetchHooksCallback: ({assetId = false, memoryId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (assetId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: + i1.$$MemoryAssetEntityTableReferences._assetIdTable(db), + referencedColumn: i1.$$MemoryAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) as T; + } + if (memoryId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.memoryId, + referencedTable: i1.$$MemoryAssetEntityTableReferences + ._memoryIdTable(db), + referencedColumn: i1.$$MemoryAssetEntityTableReferences + ._memoryIdTable(db) + .id, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + )); +} + +typedef $$MemoryAssetEntityTableProcessedTableManager + = i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData, + i1.$$MemoryAssetEntityTableFilterComposer, + i1.$$MemoryAssetEntityTableOrderingComposer, + i1.$$MemoryAssetEntityTableAnnotationComposer, + $$MemoryAssetEntityTableCreateCompanionBuilder, + $$MemoryAssetEntityTableUpdateCompanionBuilder, + (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), + i1.MemoryAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool memoryId})>; + +class $MemoryAssetEntityTable extends i2.MemoryAssetEntity + with i0.TableInfo<$MemoryAssetEntityTable, i1.MemoryAssetEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $MemoryAssetEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _assetIdMeta = + const i0.VerificationMeta('assetId'); + @override + late final i0.GeneratedColumn assetId = i0.GeneratedColumn( + 'asset_id', aliasedName, false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + static const i0.VerificationMeta _memoryIdMeta = + const i0.VerificationMeta('memoryId'); + @override + late final i0.GeneratedColumn memoryId = i0.GeneratedColumn( + 'memory_id', aliasedName, false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, + {bool isInserting = false}) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('asset_id')) { + context.handle(_assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + } else if (isInserting) { + context.missing(_assetIdMeta); + } + if (data.containsKey('memory_id')) { + context.handle(_memoryIdMeta, + memoryId.isAcceptableOrUnknown(data['memory_id']!, _memoryIdMeta)); + } else if (isInserting) { + context.missing(_memoryIdMeta); + } + return context; + } + + @override + Set get $primaryKey => {assetId, memoryId}; + @override + i1.MemoryAssetEntityData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + memoryId: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + ); + } + + @override + $MemoryAssetEntityTable createAlias(String alias) { + return $MemoryAssetEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends i0.DataClass + implements i0.Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = i0.Variable(assetId); + map['memory_id'] = i0.Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson(Map json, + {i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + i1.MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + i1.MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(i1.MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends i0.UpdateCompanion { + final i0.Value assetId; + final i0.Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const i0.Value.absent(), + this.memoryId = const i0.Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = i0.Value(assetId), + memoryId = i0.Value(memoryId); + static i0.Insertable custom({ + i0.Expression? assetId, + i0.Expression? memoryId, + }) { + return i0.RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + i1.MemoryAssetEntityCompanion copyWith( + {i0.Value? assetId, i0.Value? memoryId}) { + return i1.MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = i0.Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = i0.Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift new file mode 100644 index 0000000000..d6451db8d8 --- /dev/null +++ b/mobile/lib/infrastructure/entities/merged_asset.drift @@ -0,0 +1,83 @@ +import 'remote_asset.entity.dart'; +import 'local_asset.entity.dart'; + +mergedAsset: SELECT * FROM +( + SELECT + rae.id as remote_id, + lae.id as local_id, + rae.name, + rae."type", + rae.created_at, + rae.updated_at, + rae.width, + rae.height, + rae.duration_in_seconds, + rae.is_favorite, + rae.thumb_hash, + rae.checksum, + rae.owner_id, + 0 as orientation + FROM + remote_asset_entity rae + LEFT JOIN + local_asset_entity lae ON rae.checksum = lae.checksum + WHERE + rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id in ? + UNION ALL + SELECT + NULL as remote_id, + lae.id as local_id, + lae.name, + lae."type", + lae.created_at, + lae.updated_at, + lae.width, + lae.height, + lae.duration_in_seconds, + lae.is_favorite, + NULL as thumb_hash, + lae.checksum, + NULL as owner_id, + lae.orientation + FROM + local_asset_entity lae + LEFT JOIN + remote_asset_entity rae ON rae.checksum = lae.checksum + WHERE + rae.id IS NULL +) +ORDER BY created_at DESC +LIMIT $limit; + +mergedBucket(:group_by AS INTEGER): +SELECT + COUNT(*) as asset_count, + CASE + WHEN :group_by = 0 THEN STRFTIME('%Y-%m-%d', created_at, 'localtime') -- day + WHEN :group_by = 1 THEN STRFTIME('%Y-%m', created_at, 'localtime') -- month + END AS bucket_date +FROM +( + SELECT + rae.name, + rae.created_at + FROM + remote_asset_entity rae + LEFT JOIN + local_asset_entity lae ON rae.checksum = lae.checksum + WHERE + rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id in ? + UNION ALL + SELECT + lae.name, + lae.created_at + FROM + local_asset_entity lae + LEFT JOIN + remote_asset_entity rae ON rae.checksum = lae.checksum + WHERE + rae.id IS NULL +) +GROUP BY bucket_date +ORDER BY bucket_date DESC; diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart new file mode 100644 index 0000000000..b289083a2b --- /dev/null +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -0,0 +1,117 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:drift/internal/modular.dart' as i1; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' + as i4; + +class MergedAssetDrift extends i1.ModularAccessor { + MergedAssetDrift(i0.GeneratedDatabase db) : super(db); + i0.Selectable mergedAsset(List var1, + {required i0.Limit limit}) { + var $arrayStartIndex = 1; + final expandedvar1 = $expandVar($arrayStartIndex, var1.length); + $arrayStartIndex += var1.length; + final generatedlimit = $write(limit, startIndex: $arrayStartIndex); + $arrayStartIndex += generatedlimit.amountOfVariables; + return customSelect( + 'SELECT * FROM (SELECT rae.id AS remote_id, lae.id AS local_id, rae.name, rae.type, rae.created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, 0 AS orientation FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, lae.orientation FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}', + variables: [ + for (var $ in var1) i0.Variable($), + ...generatedlimit.introducedVariables + ], + readsFrom: { + remoteAssetEntity, + localAssetEntity, + ...generatedlimit.watchedTables, + }).map((i0.QueryRow row) => MergedAssetResult( + remoteId: row.readNullable('remote_id'), + localId: row.readNullable('local_id'), + name: row.read('name'), + type: i3.$RemoteAssetEntityTable.$convertertype + .fromSql(row.read('type')), + createdAt: row.read('created_at'), + updatedAt: row.read('updated_at'), + width: row.readNullable('width'), + height: row.readNullable('height'), + durationInSeconds: row.readNullable('duration_in_seconds'), + isFavorite: row.read('is_favorite'), + thumbHash: row.readNullable('thumb_hash'), + checksum: row.readNullable('checksum'), + ownerId: row.readNullable('owner_id'), + orientation: row.read('orientation'), + )); + } + + i0.Selectable mergedBucket(List var2, + {required int groupBy}) { + var $arrayStartIndex = 2; + final expandedvar2 = $expandVar($arrayStartIndex, var2.length); + $arrayStartIndex += var2.length; + return customSelect( + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.name, rae.created_at FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) UNION ALL SELECT lae.name, lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) GROUP BY bucket_date ORDER BY bucket_date DESC', + variables: [ + i0.Variable(groupBy), + for (var $ in var2) i0.Variable($) + ], + readsFrom: { + remoteAssetEntity, + localAssetEntity, + }).map((i0.QueryRow row) => MergedBucketResult( + assetCount: row.read('asset_count'), + bucketDate: row.read('bucket_date'), + )); + } + + i3.$RemoteAssetEntityTable get remoteAssetEntity => + i1.ReadDatabaseContainer(attachedDatabase) + .resultSet('remote_asset_entity'); + i4.$LocalAssetEntityTable get localAssetEntity => + i1.ReadDatabaseContainer(attachedDatabase) + .resultSet('local_asset_entity'); +} + +class MergedAssetResult { + final String? remoteId; + final String? localId; + final String name; + final i2.AssetType type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final bool isFavorite; + final String? thumbHash; + final String? checksum; + final String? ownerId; + final int orientation; + MergedAssetResult({ + this.remoteId, + this.localId, + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.isFavorite, + this.thumbHash, + this.checksum, + this.ownerId, + required this.orientation, + }); +} + +class MergedBucketResult { + final int assetCount; + final String bucketDate; + MergedBucketResult({ + required this.assetCount, + required this.bucketDate, + }); +} diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.dart b/mobile/lib/infrastructure/entities/remote_album.entity.dart index 7f9f1efa67..377d67446f 100644 --- a/mobile/lib/infrastructure/entities/remote_album.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_album.entity.dart @@ -11,7 +11,7 @@ class RemoteAlbumEntity extends Table with DriftDefaultsMixin { TextColumn get name => text()(); - TextColumn get description => text()(); + TextColumn get description => text().withDefault(const Constant(''))(); DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); @@ -27,7 +27,7 @@ class RemoteAlbumEntity extends Table with DriftDefaultsMixin { BoolColumn get isActivityEnabled => boolean().withDefault(const Constant(true))(); - IntColumn get order => intEnum()(); + IntColumn get order => intEnum()(); @override Set get primaryKey => {id}; diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart index cc7b642c9b..bc13c8cb5c 100644 --- a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart @@ -17,13 +17,13 @@ typedef $$RemoteAlbumEntityTableCreateCompanionBuilder = i1.RemoteAlbumEntityCompanion Function({ required String id, required String name, - required String description, + i0.Value description, i0.Value createdAt, i0.Value updatedAt, required String ownerId, i0.Value thumbnailAssetId, i0.Value isActivityEnabled, - required i2.AssetOrder order, + required i2.AlbumAssetOrder order, }); typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder = i1.RemoteAlbumEntityCompanion Function({ @@ -35,7 +35,7 @@ typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder i0.Value ownerId, i0.Value thumbnailAssetId, i0.Value isActivityEnabled, - i0.Value order, + i0.Value order, }); final class $$RemoteAlbumEntityTableReferences extends i0.BaseReferences< @@ -128,7 +128,7 @@ class $$RemoteAlbumEntityTableFilterComposer column: $table.isActivityEnabled, builder: (column) => i0.ColumnFilters(column)); - i0.ColumnWithTypeConverterFilters + i0.ColumnWithTypeConverterFilters get order => $composableBuilder( column: $table.order, builder: (column) => i0.ColumnWithTypeConverterFilters(column)); @@ -286,7 +286,7 @@ class $$RemoteAlbumEntityTableAnnotationComposer i0.GeneratedColumn get isActivityEnabled => $composableBuilder( column: $table.isActivityEnabled, builder: (column) => column); - i0.GeneratedColumnWithTypeConverter get order => + i0.GeneratedColumnWithTypeConverter get order => $composableBuilder(column: $table.order, builder: (column) => column); i5.$$UserEntityTableAnnotationComposer get ownerId { @@ -369,7 +369,7 @@ class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< i0.Value ownerId = const i0.Value.absent(), i0.Value thumbnailAssetId = const i0.Value.absent(), i0.Value isActivityEnabled = const i0.Value.absent(), - i0.Value order = const i0.Value.absent(), + i0.Value order = const i0.Value.absent(), }) => i1.RemoteAlbumEntityCompanion( id: id, @@ -385,13 +385,13 @@ class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< createCompanionCallback: ({ required String id, required String name, - required String description, + i0.Value description = const i0.Value.absent(), i0.Value createdAt = const i0.Value.absent(), i0.Value updatedAt = const i0.Value.absent(), required String ownerId, i0.Value thumbnailAssetId = const i0.Value.absent(), i0.Value isActivityEnabled = const i0.Value.absent(), - required i2.AssetOrder order, + required i2.AlbumAssetOrder order, }) => i1.RemoteAlbumEntityCompanion.insert( id: id, @@ -496,7 +496,9 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity @override late final i0.GeneratedColumn description = i0.GeneratedColumn('description', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const i4.Constant('')); static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta('createdAt'); @override @@ -542,10 +544,10 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity 'CHECK ("is_activity_enabled" IN (0, 1))'), defaultValue: const i4.Constant(true)); @override - late final i0.GeneratedColumnWithTypeConverter order = - i0.GeneratedColumn('order', aliasedName, false, + late final i0.GeneratedColumnWithTypeConverter + order = i0.GeneratedColumn('order', aliasedName, false, type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( + .withConverter( i1.$RemoteAlbumEntityTable.$converterorder); @override List get $columns => [ @@ -586,8 +588,6 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity _descriptionMeta, description.isAcceptableOrUnknown( data['description']!, _descriptionMeta)); - } else if (isInserting) { - context.missing(_descriptionMeta); } if (data.containsKey('created_at')) { context.handle(_createdAtMeta, @@ -652,8 +652,9 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity return $RemoteAlbumEntityTable(attachedDatabase, alias); } - static i0.JsonTypeConverter2 $converterorder = - const i0.EnumIndexConverter(i2.AssetOrder.values); + static i0.JsonTypeConverter2 $converterorder = + const i0.EnumIndexConverter( + i2.AlbumAssetOrder.values); @override bool get withoutRowId => true; @override @@ -670,7 +671,7 @@ class RemoteAlbumEntityData extends i0.DataClass final String ownerId; final String? thumbnailAssetId; final bool isActivityEnabled; - final i2.AssetOrder order; + final i2.AlbumAssetOrder order; const RemoteAlbumEntityData( {required this.id, required this.name, @@ -743,7 +744,7 @@ class RemoteAlbumEntityData extends i0.DataClass String? ownerId, i0.Value thumbnailAssetId = const i0.Value.absent(), bool? isActivityEnabled, - i2.AssetOrder? order}) => + i2.AlbumAssetOrder? order}) => i1.RemoteAlbumEntityData( id: id ?? this.id, name: name ?? this.name, @@ -820,7 +821,7 @@ class RemoteAlbumEntityCompanion final i0.Value ownerId; final i0.Value thumbnailAssetId; final i0.Value isActivityEnabled; - final i0.Value order; + final i0.Value order; const RemoteAlbumEntityCompanion({ this.id = const i0.Value.absent(), this.name = const i0.Value.absent(), @@ -835,16 +836,15 @@ class RemoteAlbumEntityCompanion RemoteAlbumEntityCompanion.insert({ required String id, required String name, - required String description, + this.description = const i0.Value.absent(), this.createdAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(), required String ownerId, this.thumbnailAssetId = const i0.Value.absent(), this.isActivityEnabled = const i0.Value.absent(), - required i2.AssetOrder order, + required i2.AlbumAssetOrder order, }) : id = i0.Value(id), name = i0.Value(name), - description = i0.Value(description), ownerId = i0.Value(ownerId), order = i0.Value(order); static i0.Insertable custom({ @@ -880,7 +880,7 @@ class RemoteAlbumEntityCompanion i0.Value? ownerId, i0.Value? thumbnailAssetId, i0.Value? isActivityEnabled, - i0.Value? order}) { + i0.Value? order}) { return i1.RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, diff --git a/mobile/lib/infrastructure/entities/album_user.entity.dart b/mobile/lib/infrastructure/entities/remote_album_user.entity.dart similarity index 77% rename from mobile/lib/infrastructure/entities/album_user.entity.dart rename to mobile/lib/infrastructure/entities/remote_album_user.entity.dart index c9bbfbd22f..4198fb7e46 100644 --- a/mobile/lib/infrastructure/entities/album_user.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_album_user.entity.dart @@ -1,11 +1,11 @@ import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/models/album_user.model.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -class AlbumUserEntity extends Table with DriftDefaultsMixin { - const AlbumUserEntity(); +class RemoteAlbumUserEntity extends Table with DriftDefaultsMixin { + const RemoteAlbumUserEntity(); TextColumn get albumId => text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)(); diff --git a/mobile/lib/infrastructure/entities/album_user.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart similarity index 75% rename from mobile/lib/infrastructure/entities/album_user.entity.drift.dart rename to mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart index 008e0bb52b..7ec1151a8a 100644 --- a/mobile/lib/infrastructure/entities/album_user.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart @@ -1,10 +1,10 @@ // dart format width=80 // ignore_for_file: type=lint import 'package:drift/drift.dart' as i0; -import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart' +import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart' as i1; -import 'package:immich_mobile/domain/models/album_user.model.dart' as i2; -import 'package:immich_mobile/infrastructure/entities/album_user.entity.dart' +import 'package:immich_mobile/domain/models/album/album.model.dart' as i2; +import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart' as i3; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart' as i4; @@ -12,22 +12,24 @@ import 'package:drift/internal/modular.dart' as i5; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i6; -typedef $$AlbumUserEntityTableCreateCompanionBuilder - = i1.AlbumUserEntityCompanion Function({ +typedef $$RemoteAlbumUserEntityTableCreateCompanionBuilder + = i1.RemoteAlbumUserEntityCompanion Function({ required String albumId, required String userId, required i2.AlbumUserRole role, }); -typedef $$AlbumUserEntityTableUpdateCompanionBuilder - = i1.AlbumUserEntityCompanion Function({ +typedef $$RemoteAlbumUserEntityTableUpdateCompanionBuilder + = i1.RemoteAlbumUserEntityCompanion Function({ i0.Value albumId, i0.Value userId, i0.Value role, }); -final class $$AlbumUserEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$AlbumUserEntityTable, i1.AlbumUserEntityData> { - $$AlbumUserEntityTableReferences( +final class $$RemoteAlbumUserEntityTableReferences extends i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData> { + $$RemoteAlbumUserEntityTableReferences( super.$_db, super.$_table, super.$_typedResult); static i4.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => @@ -35,7 +37,8 @@ final class $$AlbumUserEntityTableReferences extends i0.BaseReferences< .resultSet('remote_album_entity') .createAlias(i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) - .resultSet('album_user_entity') + .resultSet( + 'remote_album_user_entity') .albumId, i5.ReadDatabaseContainer(db) .resultSet('remote_album_entity') @@ -61,7 +64,8 @@ final class $$AlbumUserEntityTableReferences extends i0.BaseReferences< .resultSet('user_entity') .createAlias(i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) - .resultSet('album_user_entity') + .resultSet( + 'remote_album_user_entity') .userId, i5.ReadDatabaseContainer(db) .resultSet('user_entity') @@ -83,9 +87,9 @@ final class $$AlbumUserEntityTableReferences extends i0.BaseReferences< } } -class $$AlbumUserEntityTableFilterComposer - extends i0.Composer { - $$AlbumUserEntityTableFilterComposer({ +class $$RemoteAlbumUserEntityTableFilterComposer + extends i0.Composer { + $$RemoteAlbumUserEntityTableFilterComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -142,9 +146,9 @@ class $$AlbumUserEntityTableFilterComposer } } -class $$AlbumUserEntityTableOrderingComposer - extends i0.Composer { - $$AlbumUserEntityTableOrderingComposer({ +class $$RemoteAlbumUserEntityTableOrderingComposer + extends i0.Composer { + $$RemoteAlbumUserEntityTableOrderingComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -201,9 +205,9 @@ class $$AlbumUserEntityTableOrderingComposer } } -class $$AlbumUserEntityTableAnnotationComposer - extends i0.Composer { - $$AlbumUserEntityTableAnnotationComposer({ +class $$RemoteAlbumUserEntityTableAnnotationComposer + extends i0.Composer { + $$RemoteAlbumUserEntityTableAnnotationComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -260,35 +264,38 @@ class $$AlbumUserEntityTableAnnotationComposer } } -class $$AlbumUserEntityTableTableManager extends i0.RootTableManager< +class $$RemoteAlbumUserEntityTableTableManager extends i0.RootTableManager< i0.GeneratedDatabase, - i1.$AlbumUserEntityTable, - i1.AlbumUserEntityData, - i1.$$AlbumUserEntityTableFilterComposer, - i1.$$AlbumUserEntityTableOrderingComposer, - i1.$$AlbumUserEntityTableAnnotationComposer, - $$AlbumUserEntityTableCreateCompanionBuilder, - $$AlbumUserEntityTableUpdateCompanionBuilder, - (i1.AlbumUserEntityData, i1.$$AlbumUserEntityTableReferences), - i1.AlbumUserEntityData, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableFilterComposer, + i1.$$RemoteAlbumUserEntityTableOrderingComposer, + i1.$$RemoteAlbumUserEntityTableAnnotationComposer, + $$RemoteAlbumUserEntityTableCreateCompanionBuilder, + $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumUserEntityData, i1.$$RemoteAlbumUserEntityTableReferences), + i1.RemoteAlbumUserEntityData, i0.PrefetchHooks Function({bool albumId, bool userId})> { - $$AlbumUserEntityTableTableManager( - i0.GeneratedDatabase db, i1.$AlbumUserEntityTable table) + $$RemoteAlbumUserEntityTableTableManager( + i0.GeneratedDatabase db, i1.$RemoteAlbumUserEntityTable table) : super(i0.TableManagerState( db: db, table: table, createFilteringComposer: () => - i1.$$AlbumUserEntityTableFilterComposer($db: db, $table: table), + i1.$$RemoteAlbumUserEntityTableFilterComposer( + $db: db, $table: table), createOrderingComposer: () => - i1.$$AlbumUserEntityTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => i1 - .$$AlbumUserEntityTableAnnotationComposer($db: db, $table: table), + i1.$$RemoteAlbumUserEntityTableOrderingComposer( + $db: db, $table: table), + createComputedFieldComposer: () => + i1.$$RemoteAlbumUserEntityTableAnnotationComposer( + $db: db, $table: table), updateCompanionCallback: ({ i0.Value albumId = const i0.Value.absent(), i0.Value userId = const i0.Value.absent(), i0.Value role = const i0.Value.absent(), }) => - i1.AlbumUserEntityCompanion( + i1.RemoteAlbumUserEntityCompanion( albumId: albumId, userId: userId, role: role, @@ -298,7 +305,7 @@ class $$AlbumUserEntityTableTableManager extends i0.RootTableManager< required String userId, required i2.AlbumUserRole role, }) => - i1.AlbumUserEntityCompanion.insert( + i1.RemoteAlbumUserEntityCompanion.insert( albumId: albumId, userId: userId, role: role, @@ -306,7 +313,7 @@ class $$AlbumUserEntityTableTableManager extends i0.RootTableManager< withReferenceMapper: (p0) => p0 .map((e) => ( e.readTable(table), - i1.$$AlbumUserEntityTableReferences(db, table, e) + i1.$$RemoteAlbumUserEntityTableReferences(db, table, e) )) .toList(), prefetchHooksCallback: ({albumId = false, userId = false}) { @@ -330,9 +337,9 @@ class $$AlbumUserEntityTableTableManager extends i0.RootTableManager< state = state.withJoin( currentTable: table, currentColumn: table.albumId, - referencedTable: - i1.$$AlbumUserEntityTableReferences._albumIdTable(db), - referencedColumn: i1.$$AlbumUserEntityTableReferences + referencedTable: i1.$$RemoteAlbumUserEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1.$$RemoteAlbumUserEntityTableReferences ._albumIdTable(db) .id, ) as T; @@ -341,10 +348,11 @@ class $$AlbumUserEntityTableTableManager extends i0.RootTableManager< state = state.withJoin( currentTable: table, currentColumn: table.userId, - referencedTable: - i1.$$AlbumUserEntityTableReferences._userIdTable(db), - referencedColumn: - i1.$$AlbumUserEntityTableReferences._userIdTable(db).id, + referencedTable: i1.$$RemoteAlbumUserEntityTableReferences + ._userIdTable(db), + referencedColumn: i1.$$RemoteAlbumUserEntityTableReferences + ._userIdTable(db) + .id, ) as T; } @@ -358,25 +366,31 @@ class $$AlbumUserEntityTableTableManager extends i0.RootTableManager< )); } -typedef $$AlbumUserEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$AlbumUserEntityTable, - i1.AlbumUserEntityData, - i1.$$AlbumUserEntityTableFilterComposer, - i1.$$AlbumUserEntityTableOrderingComposer, - i1.$$AlbumUserEntityTableAnnotationComposer, - $$AlbumUserEntityTableCreateCompanionBuilder, - $$AlbumUserEntityTableUpdateCompanionBuilder, - (i1.AlbumUserEntityData, i1.$$AlbumUserEntityTableReferences), - i1.AlbumUserEntityData, - i0.PrefetchHooks Function({bool albumId, bool userId})>; +typedef $$RemoteAlbumUserEntityTableProcessedTableManager + = i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableFilterComposer, + i1.$$RemoteAlbumUserEntityTableOrderingComposer, + i1.$$RemoteAlbumUserEntityTableAnnotationComposer, + $$RemoteAlbumUserEntityTableCreateCompanionBuilder, + $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, + ( + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableReferences + ), + i1.RemoteAlbumUserEntityData, + i0.PrefetchHooks Function({bool albumId, bool userId})>; -class $AlbumUserEntityTable extends i3.AlbumUserEntity - with i0.TableInfo<$AlbumUserEntityTable, i1.AlbumUserEntityData> { +class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity + with + i0 + .TableInfo<$RemoteAlbumUserEntityTable, i1.RemoteAlbumUserEntityData> { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; - $AlbumUserEntityTable(this.attachedDatabase, [this._alias]); + $RemoteAlbumUserEntityTable(this.attachedDatabase, [this._alias]); static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta('albumId'); @override @@ -400,17 +414,17 @@ class $AlbumUserEntityTable extends i3.AlbumUserEntity i0.GeneratedColumn('role', aliasedName, false, type: i0.DriftSqlType.int, requiredDuringInsert: true) .withConverter( - i1.$AlbumUserEntityTable.$converterrole); + i1.$RemoteAlbumUserEntityTable.$converterrole); @override List get $columns => [albumId, userId, role]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; - static const String $name = 'album_user_entity'; + static const String $name = 'remote_album_user_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, + i0.Insertable instance, {bool isInserting = false}) { final context = i0.VerificationContext(); final data = instance.toColumns(true); @@ -432,22 +446,23 @@ class $AlbumUserEntityTable extends i3.AlbumUserEntity @override Set get $primaryKey => {albumId, userId}; @override - i1.AlbumUserEntityData map(Map data, {String? tablePrefix}) { + i1.RemoteAlbumUserEntityData map(Map data, + {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return i1.AlbumUserEntityData( + return i1.RemoteAlbumUserEntityData( albumId: attachedDatabase.typeMapping .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, userId: attachedDatabase.typeMapping .read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: i1.$AlbumUserEntityTable.$converterrole.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}role'])!), + role: i1.$RemoteAlbumUserEntityTable.$converterrole.fromSql( + attachedDatabase.typeMapping + .read(i0.DriftSqlType.int, data['${effectivePrefix}role'])!), ); } @override - $AlbumUserEntityTable createAlias(String alias) { - return $AlbumUserEntityTable(attachedDatabase, alias); + $RemoteAlbumUserEntityTable createAlias(String alias) { + return $RemoteAlbumUserEntityTable(attachedDatabase, alias); } static i0.JsonTypeConverter2 $converterrole = @@ -458,12 +473,12 @@ class $AlbumUserEntityTable extends i3.AlbumUserEntity bool get isStrict => true; } -class AlbumUserEntityData extends i0.DataClass - implements i0.Insertable { +class RemoteAlbumUserEntityData extends i0.DataClass + implements i0.Insertable { final String albumId; final String userId; final i2.AlbumUserRole role; - const AlbumUserEntityData( + const RemoteAlbumUserEntityData( {required this.albumId, required this.userId, required this.role}); @override Map toColumns(bool nullToAbsent) { @@ -471,19 +486,19 @@ class AlbumUserEntityData extends i0.DataClass map['album_id'] = i0.Variable(albumId); map['user_id'] = i0.Variable(userId); { - map['role'] = - i0.Variable(i1.$AlbumUserEntityTable.$converterrole.toSql(role)); + map['role'] = i0.Variable( + i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role)); } return map; } - factory AlbumUserEntityData.fromJson(Map json, + factory RemoteAlbumUserEntityData.fromJson(Map json, {i0.ValueSerializer? serializer}) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; - return AlbumUserEntityData( + return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), userId: serializer.fromJson(json['userId']), - role: i1.$AlbumUserEntityTable.$converterrole + role: i1.$RemoteAlbumUserEntityTable.$converterrole .fromJson(serializer.fromJson(json['role'])), ); } @@ -493,20 +508,21 @@ class AlbumUserEntityData extends i0.DataClass return { 'albumId': serializer.toJson(albumId), 'userId': serializer.toJson(userId), - 'role': serializer - .toJson(i1.$AlbumUserEntityTable.$converterrole.toJson(role)), + 'role': serializer.toJson( + i1.$RemoteAlbumUserEntityTable.$converterrole.toJson(role)), }; } - i1.AlbumUserEntityData copyWith( + i1.RemoteAlbumUserEntityData copyWith( {String? albumId, String? userId, i2.AlbumUserRole? role}) => - i1.AlbumUserEntityData( + i1.RemoteAlbumUserEntityData( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, role: role ?? this.role, ); - AlbumUserEntityData copyWithCompanion(i1.AlbumUserEntityCompanion data) { - return AlbumUserEntityData( + RemoteAlbumUserEntityData copyWithCompanion( + i1.RemoteAlbumUserEntityCompanion data) { + return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, role: data.role.present ? data.role.value : this.role, @@ -515,7 +531,7 @@ class AlbumUserEntityData extends i0.DataClass @override String toString() { - return (StringBuffer('AlbumUserEntityData(') + return (StringBuffer('RemoteAlbumUserEntityData(') ..write('albumId: $albumId, ') ..write('userId: $userId, ') ..write('role: $role') @@ -528,30 +544,30 @@ class AlbumUserEntityData extends i0.DataClass @override bool operator ==(Object other) => identical(this, other) || - (other is i1.AlbumUserEntityData && + (other is i1.RemoteAlbumUserEntityData && other.albumId == this.albumId && other.userId == this.userId && other.role == this.role); } -class AlbumUserEntityCompanion - extends i0.UpdateCompanion { +class RemoteAlbumUserEntityCompanion + extends i0.UpdateCompanion { final i0.Value albumId; final i0.Value userId; final i0.Value role; - const AlbumUserEntityCompanion({ + const RemoteAlbumUserEntityCompanion({ this.albumId = const i0.Value.absent(), this.userId = const i0.Value.absent(), this.role = const i0.Value.absent(), }); - AlbumUserEntityCompanion.insert({ + RemoteAlbumUserEntityCompanion.insert({ required String albumId, required String userId, required i2.AlbumUserRole role, }) : albumId = i0.Value(albumId), userId = i0.Value(userId), role = i0.Value(role); - static i0.Insertable custom({ + static i0.Insertable custom({ i0.Expression? albumId, i0.Expression? userId, i0.Expression? role, @@ -563,11 +579,11 @@ class AlbumUserEntityCompanion }); } - i1.AlbumUserEntityCompanion copyWith( + i1.RemoteAlbumUserEntityCompanion copyWith( {i0.Value? albumId, i0.Value? userId, i0.Value? role}) { - return i1.AlbumUserEntityCompanion( + return i1.RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, role: role ?? this.role, @@ -585,14 +601,14 @@ class AlbumUserEntityCompanion } if (role.present) { map['role'] = i0.Variable( - i1.$AlbumUserEntityTable.$converterrole.toSql(role.value)); + i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role.value)); } return map; } @override String toString() { - return (StringBuffer('AlbumUserEntityCompanion(') + return (StringBuffer('RemoteAlbumUserEntityCompanion(') ..write('albumId: $albumId, ') ..write('userId: $userId, ') ..write('role: $role') diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.dart index 96f4077a2a..c08401356c 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.dart @@ -1,5 +1,6 @@ import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; @@ -9,6 +10,7 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; columns: {#checksum, #ownerId}, unique: true, ) +@TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum}) class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { const RemoteAssetEntity(); @@ -33,3 +35,22 @@ class RemoteAssetEntity extends Table @override Set get primaryKey => {id}; } + +extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData { + RemoteAsset toDto() => RemoteAsset( + id: id, + name: name, + ownerId: ownerId, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + isFavorite: isFavorite, + height: height, + width: width, + thumbHash: thumbHash, + visibility: visibility, + localId: null, + ); +} diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart index c244b5a09a..4a13b74f5d 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart @@ -1178,3 +1178,6 @@ class RemoteAssetEntityCompanion .toString(); } } + +i0.Index get idxRemoteAssetChecksum => i0.Index('idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); diff --git a/mobile/lib/infrastructure/entities/stack.entity.dart b/mobile/lib/infrastructure/entities/stack.entity.dart new file mode 100644 index 0000000000..92375f19db --- /dev/null +++ b/mobile/lib/infrastructure/entities/stack.entity.dart @@ -0,0 +1,22 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class StackEntity extends Table with DriftDefaultsMixin { + const StackEntity(); + + TextColumn get id => text()(); + + DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); + + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); + + TextColumn get ownerId => + text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + + TextColumn get primaryAssetId => text().references(RemoteAssetEntity, #id)(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/stack.entity.drift.dart b/mobile/lib/infrastructure/entities/stack.entity.drift.dart new file mode 100644 index 0000000000..c0d000e02a --- /dev/null +++ b/mobile/lib/infrastructure/entities/stack.entity.drift.dart @@ -0,0 +1,706 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' + as i1; +import 'package:immich_mobile/infrastructure/entities/stack.entity.dart' as i2; +import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3; +import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' + as i4; +import 'package:drift/internal/modular.dart' as i5; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' + as i6; + +typedef $$StackEntityTableCreateCompanionBuilder = i1.StackEntityCompanion + Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + required String primaryAssetId, +}); +typedef $$StackEntityTableUpdateCompanionBuilder = i1.StackEntityCompanion + Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value primaryAssetId, +}); + +final class $$StackEntityTableReferences extends i0.BaseReferences< + i0.GeneratedDatabase, i1.$StackEntityTable, i1.StackEntityData> { + $$StackEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => + i5.ReadDatabaseContainer(db) + .resultSet('user_entity') + .createAlias(i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer(db) + .resultSet('stack_entity') + .ownerId, + i5.ReadDatabaseContainer(db) + .resultSet('user_entity') + .id)); + + i4.$$UserEntityTableProcessedTableManager get ownerId { + final $_column = $_itemColumn('owner_id')!; + + final manager = i4 + .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer($_db) + .resultSet('user_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } + + static i6.$RemoteAssetEntityTable _primaryAssetIdTable( + i0.GeneratedDatabase db) => + i5.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .createAlias(i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer(db) + .resultSet('stack_entity') + .primaryAssetId, + i5.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .id)); + + i6.$$RemoteAssetEntityTableProcessedTableManager get primaryAssetId { + final $_column = $_itemColumn('primary_asset_id')!; + + final manager = i6 + .$$RemoteAssetEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer($_db) + .resultSet('remote_asset_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_primaryAssetIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } +} + +class $$StackEntityTableFilterComposer + extends i0.Composer { + $$StackEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get updatedAt => $composableBuilder( + column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + + i4.$$UserEntityTableFilterComposer get ownerId { + final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i6.$$RemoteAssetEntityTableFilterComposer get primaryAssetId { + final i6.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.primaryAssetId, + referencedTable: i5.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i6.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$StackEntityTableOrderingComposer + extends i0.Composer { + $$StackEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column)); + + i4.$$UserEntityTableOrderingComposer get ownerId { + final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i6.$$RemoteAssetEntityTableOrderingComposer get primaryAssetId { + final i6.$$RemoteAssetEntityTableOrderingComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.primaryAssetId, + referencedTable: i5.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i6.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer($db) + .resultSet( + 'remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$StackEntityTableAnnotationComposer + extends i0.Composer { + $$StackEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + i0.GeneratedColumn get updatedAt => + $composableBuilder(column: $table.updatedAt, builder: (column) => column); + + i4.$$UserEntityTableAnnotationComposer get ownerId { + final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i6.$$RemoteAssetEntityTableAnnotationComposer get primaryAssetId { + final i6.$$RemoteAssetEntityTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.primaryAssetId, + referencedTable: i5.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i6.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer($db) + .resultSet( + 'remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$StackEntityTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData, + i1.$$StackEntityTableFilterComposer, + i1.$$StackEntityTableOrderingComposer, + i1.$$StackEntityTableAnnotationComposer, + $$StackEntityTableCreateCompanionBuilder, + $$StackEntityTableUpdateCompanionBuilder, + (i1.StackEntityData, i1.$$StackEntityTableReferences), + i1.StackEntityData, + i0.PrefetchHooks Function({bool ownerId, bool primaryAssetId})> { + $$StackEntityTableTableManager( + i0.GeneratedDatabase db, i1.$StackEntityTable table) + : super(i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$StackEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$StackEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + i1.$$StackEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value primaryAssetId = const i0.Value.absent(), + }) => + i1.StackEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + primaryAssetId: primaryAssetId, + ), + createCompanionCallback: ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + required String primaryAssetId, + }) => + i1.StackEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + primaryAssetId: primaryAssetId, + ), + withReferenceMapper: (p0) => p0 + .map((e) => ( + e.readTable(table), + i1.$$StackEntityTableReferences(db, table, e) + )) + .toList(), + prefetchHooksCallback: ({ownerId = false, primaryAssetId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (ownerId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: + i1.$$StackEntityTableReferences._ownerIdTable(db), + referencedColumn: + i1.$$StackEntityTableReferences._ownerIdTable(db).id, + ) as T; + } + if (primaryAssetId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.primaryAssetId, + referencedTable: i1.$$StackEntityTableReferences + ._primaryAssetIdTable(db), + referencedColumn: i1.$$StackEntityTableReferences + ._primaryAssetIdTable(db) + .id, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + )); +} + +typedef $$StackEntityTableProcessedTableManager = i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData, + i1.$$StackEntityTableFilterComposer, + i1.$$StackEntityTableOrderingComposer, + i1.$$StackEntityTableAnnotationComposer, + $$StackEntityTableCreateCompanionBuilder, + $$StackEntityTableUpdateCompanionBuilder, + (i1.StackEntityData, i1.$$StackEntityTableReferences), + i1.StackEntityData, + i0.PrefetchHooks Function({bool ownerId, bool primaryAssetId})>; + +class $StackEntityTable extends i2.StackEntity + with i0.TableInfo<$StackEntityTable, i1.StackEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $StackEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + static const i0.VerificationMeta _createdAtMeta = + const i0.VerificationMeta('createdAt'); + @override + late final i0.GeneratedColumn createdAt = + i0.GeneratedColumn('created_at', aliasedName, false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime); + static const i0.VerificationMeta _updatedAtMeta = + const i0.VerificationMeta('updatedAt'); + @override + late final i0.GeneratedColumn updatedAt = + i0.GeneratedColumn('updated_at', aliasedName, false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime); + static const i0.VerificationMeta _ownerIdMeta = + const i0.VerificationMeta('ownerId'); + @override + late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( + 'owner_id', aliasedName, false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + static const i0.VerificationMeta _primaryAssetIdMeta = + const i0.VerificationMeta('primaryAssetId'); + @override + late final i0.GeneratedColumn primaryAssetId = + i0.GeneratedColumn( + 'primary_asset_id', aliasedName, false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)')); + @override + List get $columns => + [id, createdAt, updatedAt, ownerId, primaryAssetId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, + {bool isInserting = false}) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('created_at')) { + context.handle(_createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + } + if (data.containsKey('updated_at')) { + context.handle(_updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + } + if (data.containsKey('owner_id')) { + context.handle(_ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + } else if (isInserting) { + context.missing(_ownerIdMeta); + } + if (data.containsKey('primary_asset_id')) { + context.handle( + _primaryAssetIdMeta, + primaryAssetId.isAcceptableOrUnknown( + data['primary_asset_id']!, _primaryAssetIdMeta)); + } else if (isInserting) { + context.missing(_primaryAssetIdMeta); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.StackEntityData( + id: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + primaryAssetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + ); + } + + @override + $StackEntityTable createAlias(String alias) { + return $StackEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends i0.DataClass + implements i0.Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['created_at'] = i0.Variable(createdAt); + map['updated_at'] = i0.Variable(updatedAt); + map['owner_id'] = i0.Variable(ownerId); + map['primary_asset_id'] = i0.Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson(Map json, + {i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + i1.StackEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId}) => + i1.StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(i1.StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value createdAt; + final i0.Value updatedAt; + final i0.Value ownerId; + final i0.Value primaryAssetId; + const StackEntityCompanion({ + this.id = const i0.Value.absent(), + this.createdAt = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + this.ownerId = const i0.Value.absent(), + this.primaryAssetId = const i0.Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + primaryAssetId = i0.Value(primaryAssetId); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? createdAt, + i0.Expression? updatedAt, + i0.Expression? ownerId, + i0.Expression? primaryAssetId, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + i1.StackEntityCompanion copyWith( + {i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? primaryAssetId}) { + return i1.StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = i0.Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = i0.Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = i0.Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = i0.Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/repositories/api.repository.dart b/mobile/lib/infrastructure/repositories/api.repository.dart index 56c64c5512..15696c65b7 100644 --- a/mobile/lib/infrastructure/repositories/api.repository.dart +++ b/mobile/lib/infrastructure/repositories/api.repository.dart @@ -5,7 +5,7 @@ class ApiRepository { Future checkNull(Future future) async { final response = await future; - if (response == null) throw NoResponseDtoError(); + if (response == null) throw const NoResponseDtoError(); return response; } } diff --git a/mobile/lib/infrastructure/repositories/asset_media.repository.dart b/mobile/lib/infrastructure/repositories/asset_media.repository.dart new file mode 100644 index 0000000000..e8bf9ace43 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/asset_media.repository.dart @@ -0,0 +1,26 @@ +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:photo_manager/photo_manager.dart'; + +class AssetMediaRepository { + const AssetMediaRepository(); + + Future getThumbnail( + String id, { + int quality = 80, + Size size = const Size.square(256), + }) => + AssetEntity( + id: id, + // The below fields are not used in thumbnailDataWithSize but are required + // to create an AssetEntity instance. It is faster to create a dummy AssetEntity + // instance than to fetch the asset from the device first. + typeInt: AssetType.image.index, + width: size.width.toInt(), + height: size.height.toInt(), + ).thumbnailDataWithSize( + ThumbnailSize(size.width.toInt(), size.height.toInt()), + quality: quality, + ); +} diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index f414cd9f67..a7920cf7b2 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -3,15 +3,18 @@ import 'dart:async'; import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/infrastructure/entities/album_user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/memory.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/stack.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'; import 'package:isar/isar.dart'; @@ -41,12 +44,18 @@ class IsarDatabaseRepository implements IDatabaseRepository { LocalAlbumEntity, LocalAssetEntity, LocalAlbumAssetEntity, - RemoteExifEntity, RemoteAssetEntity, + RemoteExifEntity, RemoteAlbumEntity, RemoteAlbumAssetEntity, - AlbumUserEntity, + RemoteAlbumUserEntity, + MemoryEntity, + MemoryAssetEntity, + StackEntity, ], + include: { + 'package:immich_mobile/infrastructure/entities/merged_asset.drift', + }, ) class Drift extends $Drift implements IDatabaseRepository { Drift([QueryExecutor? executor]) diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 54c12ecde5..15d445d226 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -3,17 +3,17 @@ import 'package:drift/drift.dart' as i0; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i1; -import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' - as i2; -import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' - as i3; -import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' - as i4; -import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' - as i5; -import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' - as i6; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' + as i2; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' + as i4; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' + as i5; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' + as i6; +import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' as i7; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart' as i8; @@ -21,56 +21,82 @@ import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift. as i9; import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart' as i10; -import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart' +import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart' as i11; +import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart' + as i12; +import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart' + as i13; +import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' + as i14; +import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' + as i15; +import 'package:drift/internal/modular.dart' as i16; abstract class $Drift extends i0.GeneratedDatabase { $Drift(i0.QueryExecutor e) : super(e); $DriftManager get managers => $DriftManager(this); late final i1.$UserEntityTable userEntity = i1.$UserEntityTable(this); - late final i2.$UserMetadataEntityTable userMetadataEntity = - i2.$UserMetadataEntityTable(this); - late final i3.$PartnerEntityTable partnerEntity = - i3.$PartnerEntityTable(this); - late final i4.$LocalAlbumEntityTable localAlbumEntity = - i4.$LocalAlbumEntityTable(this); - late final i5.$LocalAssetEntityTable localAssetEntity = - i5.$LocalAssetEntityTable(this); - late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity = - i6.$LocalAlbumAssetEntityTable(this); - late final i7.$RemoteAssetEntityTable remoteAssetEntity = - i7.$RemoteAssetEntityTable(this); + late final i2.$RemoteAssetEntityTable remoteAssetEntity = + i2.$RemoteAssetEntityTable(this); + late final i3.$LocalAssetEntityTable localAssetEntity = + i3.$LocalAssetEntityTable(this); + late final i4.$UserMetadataEntityTable userMetadataEntity = + i4.$UserMetadataEntityTable(this); + late final i5.$PartnerEntityTable partnerEntity = + i5.$PartnerEntityTable(this); + late final i6.$LocalAlbumEntityTable localAlbumEntity = + i6.$LocalAlbumEntityTable(this); + late final i7.$LocalAlbumAssetEntityTable localAlbumAssetEntity = + i7.$LocalAlbumAssetEntityTable(this); late final i8.$RemoteExifEntityTable remoteExifEntity = i8.$RemoteExifEntityTable(this); late final i9.$RemoteAlbumEntityTable remoteAlbumEntity = i9.$RemoteAlbumEntityTable(this); late final i10.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = i10.$RemoteAlbumAssetEntityTable(this); - late final i11.$AlbumUserEntityTable albumUserEntity = - i11.$AlbumUserEntityTable(this); + late final i11.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = + i11.$RemoteAlbumUserEntityTable(this); + late final i12.$MemoryEntityTable memoryEntity = i12.$MemoryEntityTable(this); + late final i13.$MemoryAssetEntityTable memoryAssetEntity = + i13.$MemoryAssetEntityTable(this); + late final i14.$StackEntityTable stackEntity = i14.$StackEntityTable(this); + i15.MergedAssetDrift get mergedAssetDrift => i16.ReadDatabaseContainer(this) + .accessor(i15.MergedAssetDrift.new); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ userEntity, + remoteAssetEntity, + localAssetEntity, + i3.idxLocalAssetChecksum, + i2.uQRemoteAssetOwnerChecksum, + i2.idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, localAlbumEntity, - localAssetEntity, localAlbumAssetEntity, - remoteAssetEntity, remoteExifEntity, remoteAlbumEntity, remoteAlbumAssetEntity, - albumUserEntity, - i5.idxLocalAssetChecksum, - i7.uQRemoteAssetOwnerChecksum + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + stackEntity ]; @override i0.StreamQueryUpdateRules get streamUpdateRules => const i0.StreamQueryUpdateRules( [ + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('user_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete), + ], + ), i0.WritePropagation( on: i0.TableUpdateQuery.onTableName('user_entity', limitUpdateKind: i0.UpdateKind.delete), @@ -109,13 +135,6 @@ abstract class $Drift extends i0.GeneratedDatabase { kind: i0.UpdateKind.delete), ], ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete), - ], - ), i0.WritePropagation( on: i0.TableUpdateQuery.onTableName('remote_asset_entity', limitUpdateKind: i0.UpdateKind.delete), @@ -157,14 +176,44 @@ abstract class $Drift extends i0.GeneratedDatabase { on: i0.TableUpdateQuery.onTableName('remote_album_entity', limitUpdateKind: i0.UpdateKind.delete), result: [ - i0.TableUpdate('album_user_entity', kind: i0.UpdateKind.delete), + i0.TableUpdate('remote_album_user_entity', + kind: i0.UpdateKind.delete), ], ), i0.WritePropagation( on: i0.TableUpdateQuery.onTableName('user_entity', limitUpdateKind: i0.UpdateKind.delete), result: [ - i0.TableUpdate('album_user_entity', kind: i0.UpdateKind.delete), + i0.TableUpdate('remote_album_user_entity', + kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('user_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('memory_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('memory_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('user_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete), ], ), ], @@ -179,18 +228,18 @@ class $DriftManager { $DriftManager(this._db); i1.$$UserEntityTableTableManager get userEntity => i1.$$UserEntityTableTableManager(_db, _db.userEntity); - i2.$$UserMetadataEntityTableTableManager get userMetadataEntity => - i2.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); - i3.$$PartnerEntityTableTableManager get partnerEntity => - i3.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); - i4.$$LocalAlbumEntityTableTableManager get localAlbumEntity => - i4.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); - i5.$$LocalAssetEntityTableTableManager get localAssetEntity => - i5.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); - i6.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i6 + i2.$$RemoteAssetEntityTableTableManager get remoteAssetEntity => + i2.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity); + i3.$$LocalAssetEntityTableTableManager get localAssetEntity => + i3.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); + i4.$$UserMetadataEntityTableTableManager get userMetadataEntity => + i4.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); + i5.$$PartnerEntityTableTableManager get partnerEntity => + i5.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); + i6.$$LocalAlbumEntityTableTableManager get localAlbumEntity => + i6.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); + i7.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i7 .$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity); - i7.$$RemoteAssetEntityTableTableManager get remoteAssetEntity => - i7.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity); i8.$$RemoteExifEntityTableTableManager get remoteExifEntity => i8.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity); i9.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity => @@ -198,6 +247,12 @@ class $DriftManager { i10.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity => i10.$$RemoteAlbumAssetEntityTableTableManager( _db, _db.remoteAlbumAssetEntity); - i11.$$AlbumUserEntityTableTableManager get albumUserEntity => - i11.$$AlbumUserEntityTableTableManager(_db, _db.albumUserEntity); + i11.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i11 + .$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity); + i12.$$MemoryEntityTableTableManager get memoryEntity => + i12.$$MemoryEntityTableTableManager(_db, _db.memoryEntity); + i13.$$MemoryAssetEntityTableTableManager get memoryAssetEntity => + i13.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity); + i14.$$StackEntityTableTableManager get stackEntity => + i14.$$StackEntityTableTableManager(_db, _db.stackEntity); } diff --git a/mobile/lib/infrastructure/repositories/device_asset.repository.dart b/mobile/lib/infrastructure/repositories/device_asset.repository.dart index 87784ecaab..4e72d4a7f6 100644 --- a/mobile/lib/infrastructure/repositories/device_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/device_asset.repository.dart @@ -1,23 +1,19 @@ -import 'package:immich_mobile/domain/interfaces/device_asset.interface.dart'; import 'package:immich_mobile/domain/models/device_asset.model.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:isar/isar.dart'; -class IsarDeviceAssetRepository extends IsarDatabaseRepository - implements IDeviceAssetRepository { +class IsarDeviceAssetRepository extends IsarDatabaseRepository { final Isar _db; const IsarDeviceAssetRepository(this._db) : super(_db); - @override Future deleteIds(List ids) { return transaction(() async { await _db.deviceAssetEntitys.deleteAllByAssetId(ids.toList()); }); } - @override Future> getByIds(List localIds) { return _db.deviceAssetEntitys .where() @@ -26,7 +22,6 @@ class IsarDeviceAssetRepository extends IsarDatabaseRepository .then((value) => value.map((e) => e.toModel()).toList()); } - @override Future updateAll(List assetHash) { return transaction(() async { await _db.deviceAssetEntitys diff --git a/mobile/lib/infrastructure/repositories/exif.repository.dart b/mobile/lib/infrastructure/repositories/exif.repository.dart index 2b4276dd57..0012e329ca 100644 --- a/mobile/lib/infrastructure/repositories/exif.repository.dart +++ b/mobile/lib/infrastructure/repositories/exif.repository.dart @@ -1,36 +1,30 @@ -import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' as entity; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:isar/isar.dart'; -class IsarExifRepository extends IsarDatabaseRepository - implements IExifInfoRepository { +class IsarExifRepository extends IsarDatabaseRepository { final Isar _db; const IsarExifRepository(this._db) : super(_db); - @override Future delete(int assetId) async { await transaction(() async { await _db.exifInfos.delete(assetId); }); } - @override Future deleteAll() async { await transaction(() async { await _db.exifInfos.clear(); }); } - @override Future get(int assetId) async { return (await _db.exifInfos.get(assetId))?.toDto(); } - @override Future update(ExifInfo exifInfo) { return transaction(() async { await _db.exifInfos.put(entity.ExifInfo.fromDto(exifInfo)); @@ -38,7 +32,6 @@ class IsarExifRepository extends IsarDatabaseRepository }); } - @override Future> updateAll(List exifInfos) { return transaction(() async { await _db.exifInfos.putAll( diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index 4f46b9b408..44ebe7f7ca 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -1,22 +1,21 @@ import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/domain/models/local_album.model.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:platform/platform.dart'; -class DriftLocalAlbumRepository extends DriftDatabaseRepository - implements ILocalAlbumRepository { +enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum } + +class DriftLocalAlbumRepository extends DriftDatabaseRepository { final Drift _db; final Platform _platform; const DriftLocalAlbumRepository(this._db, {Platform? platform}) : _platform = platform ?? const LocalPlatform(), super(_db); - @override Future> getAll({Set sortBy = const {}}) { final assetCount = _db.localAlbumAssetEntity.assetId.count(); @@ -56,7 +55,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository .get(); } - @override Future delete(String albumId) => transaction(() async { // Remove all assets that are only in this particular album // We cannot remove all assets in the album because they might be in other albums in iOS @@ -72,7 +70,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository .delete(); }); - @override Future syncDeletes( String albumId, Iterable assetIdsToKeep, @@ -101,7 +98,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository await deleteSmt.go(); } - @override Future upsert( LocalAlbum localAlbum, { Iterable toUpsert = const [], @@ -134,7 +130,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository }); } - @override Future updateAll(Iterable albums) { return _db.transaction(() async { await _db.localAlbumEntity @@ -185,7 +180,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository }); } - @override Future> getAssets(String albumId) { final query = _db.localAlbumAssetEntity.select().join( [ @@ -202,7 +196,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository .get(); } - @override Future> getAssetIds(String albumId) { final query = _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) @@ -212,7 +205,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository .get(); } - @override Future processDelta({ required List updates, required List deletes, @@ -253,7 +245,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository }); } - @override Future> getAssetsToHash(String albumId) { final query = _db.localAlbumAssetEntity.select().join( [ @@ -290,6 +281,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository height: Value(asset.height), durationInSeconds: Value(asset.durationInSeconds), id: asset.id, + orientation: Value(asset.orientation), checksum: const Value(null), ); batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>( @@ -370,6 +362,24 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository batch.deleteWhere(_db.localAssetEntity, (f) => f.id.isIn(ids)); }); } + + Future getThumbnail(String albumId) async { + final query = _db.localAlbumAssetEntity.select().join([ + innerJoin( + _db.localAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + ), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]) + ..limit(1); + + final results = await query + .map((row) => row.readTable(_db.localAssetEntity).toDto()) + .get(); + + return results.isNotEmpty ? results.first : null; + } } extension on LocalAlbumEntityData { diff --git a/mobile/lib/infrastructure/repositories/local_asset.repository.dart b/mobile/lib/infrastructure/repositories/local_asset.repository.dart index 350a8dcd32..cb6871cd22 100644 --- a/mobile/lib/infrastructure/repositories/local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_asset.repository.dart @@ -1,15 +1,33 @@ import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; -class DriftLocalAssetRepository extends DriftDatabaseRepository - implements ILocalAssetRepository { +class DriftLocalAssetRepository extends DriftDatabaseRepository { final Drift _db; const DriftLocalAssetRepository(this._db) : super(_db); - @override + Stream watchAsset(String id) { + final query = _db.localAssetEntity + .select() + .addColumns([_db.remoteAssetEntity.id]).join([ + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where(_db.localAssetEntity.id.equals(id)); + + return query.map((row) { + final asset = row.readTable(_db.localAssetEntity).toDto(); + return asset.copyWith( + remoteId: row.read(_db.remoteAssetEntity.id), + ); + }).watchSingleOrNull(); + } + Future updateHashes(Iterable hashes) { if (hashes.isEmpty) { return Future.value(); diff --git a/mobile/lib/infrastructure/repositories/log.repository.dart b/mobile/lib/infrastructure/repositories/log.repository.dart index 6ff128f93b..717900910a 100644 --- a/mobile/lib/infrastructure/repositories/log.repository.dart +++ b/mobile/lib/infrastructure/repositories/log.repository.dart @@ -1,28 +1,23 @@ -import 'package:immich_mobile/domain/interfaces/log.interface.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:isar/isar.dart'; -class IsarLogRepository extends IsarDatabaseRepository - implements ILogRepository { +class IsarLogRepository extends IsarDatabaseRepository { final Isar _db; const IsarLogRepository(super.db) : _db = db; - @override Future deleteAll() async { await transaction(() async => await _db.loggerMessages.clear()); return true; } - @override Future> getAll() async { final logs = await _db.loggerMessages.where().sortByCreatedAtDesc().findAll(); return logs.map((l) => l.toDto()).toList(); } - @override Future insert(LogMessage log) async { final logEntity = LoggerMessage.fromDto(log); await transaction(() async { @@ -31,7 +26,6 @@ class IsarLogRepository extends IsarDatabaseRepository return true; } - @override Future insertAll(Iterable logs) async { await transaction(() async { final logEntities = @@ -41,7 +35,6 @@ class IsarLogRepository extends IsarDatabaseRepository return true; } - @override Future truncate({int limit = 250}) async { await transaction(() async { final count = await _db.loggerMessages.count(); diff --git a/mobile/lib/infrastructure/repositories/memory.repository.dart b/mobile/lib/infrastructure/repositories/memory.repository.dart new file mode 100644 index 0000000000..ff5f75c2ac --- /dev/null +++ b/mobile/lib/infrastructure/repositories/memory.repository.dart @@ -0,0 +1,81 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftMemoryRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftMemoryRepository(this._db) : super(_db); + + Future> getAll(String ownerId) async { + final now = DateTime.now(); + final localUtc = DateTime.utc(now.year, now.month, now.day, 0, 0, 0); + + final query = _db.select(_db.memoryEntity).join([ + leftOuterJoin( + _db.memoryAssetEntity, + _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id), + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility + .equalsValue(AssetVisibility.timeline), + ), + ]) + ..where(_db.memoryEntity.ownerId.equals(ownerId)) + ..where(_db.memoryEntity.deletedAt.isNull()) + ..where( + _db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc), + ) + ..where( + _db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc), + ) + ..orderBy([ + OrderingTerm.desc(_db.memoryEntity.memoryAt), + OrderingTerm.asc(_db.remoteAssetEntity.createdAt), + ]); + + final rows = await query.get(); + + final Map memoriesMap = {}; + + for (final row in rows) { + final memory = row.readTable(_db.memoryEntity); + final asset = row.readTable(_db.remoteAssetEntity); + + final existingMemory = memoriesMap[memory.id]; + if (existingMemory != null) { + existingMemory.assets.add(asset.toDto()); + } else { + final assets = [asset.toDto()]; + memoriesMap[memory.id] = memory.toDto().copyWith(assets: assets); + } + } + + return memoriesMap.values.toList(); + } +} + +extension on MemoryEntityData { + DriftMemory toDto() { + return DriftMemory( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + ownerId: ownerId, + type: type, + data: MemoryData.fromJson(data), + isSaved: isSaved, + memoryAt: memoryAt, + seenAt: seenAt, + showAt: showAt, + hideAt: hideAt, + assets: [], + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/remote_album.repository.dart b/mobile/lib/infrastructure/repositories/remote_album.repository.dart new file mode 100644 index 0000000000..b1b73b4cdc --- /dev/null +++ b/mobile/lib/infrastructure/repositories/remote_album.repository.dart @@ -0,0 +1,77 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +enum SortRemoteAlbumsBy { id } + +class DriftRemoteAlbumRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftRemoteAlbumRepository(this._db) : super(_db); + + Future> getAll({Set sortBy = const {}}) { + final assetCount = _db.remoteAlbumAssetEntity.assetId.count(); + + final query = _db.remoteAlbumEntity.select().join([ + leftOuterJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), + useColumns: false, + ), + leftOuterJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), + useColumns: false, + ), + ]); + query + ..where(_db.remoteAssetEntity.deletedAt.isNull()) + ..addColumns([assetCount]) + ..addColumns([_db.userEntity.name]) + ..groupBy([_db.remoteAlbumEntity.id]); + + if (sortBy.isNotEmpty) { + final orderings = []; + for (final sort in sortBy) { + orderings.add( + switch (sort) { + SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id), + }, + ); + } + query.orderBy(orderings); + } + + return query + .map( + (row) => row.readTable(_db.remoteAlbumEntity).toDto( + assetCount: row.read(assetCount) ?? 0, + ownerName: row.read(_db.userEntity.name)!, + ), + ) + .get(); + } +} + +extension on RemoteAlbumEntityData { + Album toDto({int assetCount = 0, required String ownerName}) { + return Album( + id: id, + name: name, + ownerId: ownerId, + createdAt: createdAt, + updatedAt: updatedAt, + description: description, + thumbnailAssetId: thumbnailAssetId, + isActivityEnabled: isActivityEnabled, + order: order, + assetCount: assetCount, + ownerName: ownerName, + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart new file mode 100644 index 0000000000..95de34d1b4 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -0,0 +1,113 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' + hide ExifInfo; +import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +class RemoteAssetRepository extends DriftDatabaseRepository { + final Drift _db; + const RemoteAssetRepository(this._db) : super(_db); + + /// For testing purposes + Future> getSome(String userId) { + final query = _db.remoteAssetEntity.select() + ..where( + (row) => + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility + .equalsValue(AssetVisibility.timeline), + ) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(10); + + return query.map((row) => row.toDto()).get(); + } + + Stream watchAsset(String id) { + final query = _db.remoteAssetEntity + .select() + .addColumns([_db.localAssetEntity.id]).join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.id.equals(id)); + + return query.map((row) { + final asset = row.readTable(_db.remoteAssetEntity).toDto(); + return asset.copyWith( + localId: row.read(_db.localAssetEntity.id), + ); + }).watchSingleOrNull(); + } + + Future getExif(String id) { + return _db.managers.remoteExifEntity + .filter((row) => row.assetId.id.equals(id)) + .map((row) => row.toDto()) + .getSingleOrNull(); + } + + Future updateFavorite(List ids, bool isFavorite) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteAssetEntity, + RemoteAssetEntityCompanion(isFavorite: Value(isFavorite)), + where: (e) => e.id.equals(id), + ); + } + }); + } + + Future updateVisibility(List ids, AssetVisibility visibility) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteAssetEntity, + RemoteAssetEntityCompanion(visibility: Value(visibility)), + where: (e) => e.id.equals(id), + ); + } + }); + } + + Future trash(List ids) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteAssetEntity, + RemoteAssetEntityCompanion(deletedAt: Value(DateTime.now())), + where: (e) => e.id.equals(id), + ); + } + }); + } + + Future delete(List ids) { + return _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(ids)); + } + + Future updateLocation(List ids, LatLng location) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteExifEntity, + RemoteExifEntityCompanion( + latitude: Value(location.latitude), + longitude: Value(location.longitude), + ), + where: (e) => e.assetId.equals(id), + ); + } + }); + } +} diff --git a/mobile/lib/infrastructure/repositories/stack.repository.dart b/mobile/lib/infrastructure/repositories/stack.repository.dart new file mode 100644 index 0000000000..7f97f3d9ae --- /dev/null +++ b/mobile/lib/infrastructure/repositories/stack.repository.dart @@ -0,0 +1,30 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/stack.model.dart'; +import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftStackRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftStackRepository(this._db) : super(_db); + + Future> getAll(String userId) { + final query = _db.stackEntity.select() + ..where((e) => e.ownerId.equals(userId)); + + return query.map((stack) { + return stack.toDto(); + }).get(); + } +} + +extension on StackEntityData { + Stack toDto() { + return Stack( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + primaryAssetId: primaryAssetId, + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/storage.repository.dart b/mobile/lib/infrastructure/repositories/storage.repository.dart index 57dfc42135..5b511709cd 100644 --- a/mobile/lib/infrastructure/repositories/storage.repository.dart +++ b/mobile/lib/infrastructure/repositories/storage.repository.dart @@ -1,30 +1,22 @@ import 'dart:io'; -import 'package:immich_mobile/domain/interfaces/storage.interface.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:logging/logging.dart'; import 'package:photo_manager/photo_manager.dart'; -class StorageRepository implements IStorageRepository { - final _log = Logger('StorageRepository'); +class StorageRepository { + const StorageRepository(); - @override - Future getFileForAsset(LocalAsset asset) async { + Future getFileForAsset(String assetId) async { + final log = Logger('StorageRepository'); File? file; try { - final entity = await AssetEntity.fromId(asset.id); + final entity = await AssetEntity.fromId(assetId); file = await entity?.originFile; if (file == null) { - _log.warning( - "Cannot get file for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}", - ); + log.warning("Cannot get file for asset $assetId"); } } catch (error, stackTrace) { - _log.warning( - "Error getting file for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}", - error, - stackTrace, - ); + log.warning("Error getting file for asset $assetId", error, stackTrace); } return file; } diff --git a/mobile/lib/infrastructure/repositories/store.repository.dart b/mobile/lib/infrastructure/repositories/store.repository.dart index fec36193bc..400c8ccf7d 100644 --- a/mobile/lib/infrastructure/repositories/store.repository.dart +++ b/mobile/lib/infrastructure/repositories/store.repository.dart @@ -1,4 +1,3 @@ -import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; @@ -6,14 +5,12 @@ import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; import 'package:isar/isar.dart'; -class IsarStoreRepository extends IsarDatabaseRepository - implements IStoreRepository { +class IsarStoreRepository extends IsarDatabaseRepository { final Isar _db; final validStoreKeys = StoreKey.values.map((e) => e.id).toSet(); IsarStoreRepository(super.db) : _db = db; - @override Future deleteAll() async { return await transaction(() async { await _db.storeValues.clear(); @@ -21,7 +18,6 @@ class IsarStoreRepository extends IsarDatabaseRepository }); } - @override Stream> watchAll() { return _db.storeValues .filter() @@ -34,12 +30,10 @@ class IsarStoreRepository extends IsarDatabaseRepository ); } - @override Future delete(StoreKey key) async { return await transaction(() async => await _db.storeValues.delete(key.id)); } - @override Future insert(StoreKey key, T value) async { return await transaction(() async { await _db.storeValues.put(await _fromValue(key, value)); @@ -47,7 +41,6 @@ class IsarStoreRepository extends IsarDatabaseRepository }); } - @override Future tryGet(StoreKey key) async { final entity = (await _db.storeValues.get(key.id)); if (entity == null) { @@ -56,7 +49,6 @@ class IsarStoreRepository extends IsarDatabaseRepository return await _toValue(key, entity); } - @override Future update(StoreKey key, T value) async { return await transaction(() async { await _db.storeValues.put(await _fromValue(key, value)); @@ -64,7 +56,6 @@ class IsarStoreRepository extends IsarDatabaseRepository }); } - @override Stream watch(StoreKey key) async* { yield* _db.storeValues .watchObject(key.id, fireImmediately: true) @@ -109,7 +100,6 @@ class IsarStoreRepository extends IsarDatabaseRepository return StoreValue(key.id, intValue: intValue, strValue: strValue); } - @override Future>> getAll() async { final entities = await _db.storeValues .filter() diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index 70f940c10b..d43f786a29 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -3,24 +3,21 @@ import 'dart:convert'; 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/presentation/pages/dev/dev_logger.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; -class SyncApiRepository implements ISyncApiRepository { +class SyncApiRepository { final Logger _logger = Logger('SyncApiRepository'); final ApiService _api; SyncApiRepository(this._api); - @override Future ack(List data) { return _api.syncApi.sendSyncAck(SyncAckSetDto(acks: data)); } - @override Future streamChanges( Function(List, Function() abort) onData, { int batchSize = kSyncEventBatchSize, @@ -45,14 +42,20 @@ class SyncApiRepository implements ISyncApiRepository { SyncStreamDto( types: [ SyncRequestType.usersV1, - SyncRequestType.partnersV1, SyncRequestType.assetsV1, - SyncRequestType.partnerAssetsV1, SyncRequestType.assetExifsV1, + SyncRequestType.partnersV1, + SyncRequestType.partnerAssetsV1, SyncRequestType.partnerAssetExifsV1, SyncRequestType.albumsV1, - // SyncRequestType.albumAssetsV1, SyncRequestType.albumUsersV1, + SyncRequestType.albumAssetsV1, + SyncRequestType.albumAssetExifsV1, + SyncRequestType.albumToAssetsV1, + SyncRequestType.memoriesV1, + SyncRequestType.memoryToAssetsV1, + SyncRequestType.stacksV1, + SyncRequestType.partnerStacksV1, ], ).toJson(), ); @@ -141,12 +144,34 @@ const _kResponseMap = { SyncEntityType.assetDeleteV1: SyncAssetDeleteV1.fromJson, SyncEntityType.assetExifV1: SyncAssetExifV1.fromJson, SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson, + SyncEntityType.partnerAssetBackfillV1: SyncAssetV1.fromJson, SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson, SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson, + SyncEntityType.partnerAssetExifBackfillV1: SyncAssetExifV1.fromJson, SyncEntityType.albumV1: SyncAlbumV1.fromJson, SyncEntityType.albumDeleteV1: SyncAlbumDeleteV1.fromJson, - // SyncEntityType.albumAssetV1: SyncAlbumAssetV1.fromJson, - // SyncEntityType.albumAssetDeleteV1: SyncAlbumAssetDeleteV1.fromJson, SyncEntityType.albumUserV1: SyncAlbumUserV1.fromJson, + SyncEntityType.albumUserBackfillV1: SyncAlbumUserV1.fromJson, SyncEntityType.albumUserDeleteV1: SyncAlbumUserDeleteV1.fromJson, + SyncEntityType.albumAssetV1: SyncAssetV1.fromJson, + SyncEntityType.albumAssetBackfillV1: SyncAssetV1.fromJson, + SyncEntityType.albumAssetExifV1: SyncAssetExifV1.fromJson, + SyncEntityType.albumAssetExifBackfillV1: SyncAssetExifV1.fromJson, + SyncEntityType.albumToAssetV1: SyncAlbumToAssetV1.fromJson, + SyncEntityType.albumToAssetBackfillV1: SyncAlbumToAssetV1.fromJson, + SyncEntityType.albumToAssetDeleteV1: SyncAlbumToAssetDeleteV1.fromJson, + SyncEntityType.syncAckV1: _SyncAckV1.fromJson, + SyncEntityType.memoryV1: SyncMemoryV1.fromJson, + SyncEntityType.memoryDeleteV1: SyncMemoryDeleteV1.fromJson, + SyncEntityType.memoryToAssetV1: SyncMemoryAssetV1.fromJson, + SyncEntityType.memoryToAssetDeleteV1: SyncMemoryAssetDeleteV1.fromJson, + SyncEntityType.stackV1: SyncStackV1.fromJson, + SyncEntityType.stackDeleteV1: SyncStackDeleteV1.fromJson, + SyncEntityType.partnerStackV1: SyncStackV1.fromJson, + SyncEntityType.partnerStackBackfillV1: SyncStackV1.fromJson, + SyncEntityType.partnerStackDeleteV1: SyncStackDeleteV1.fromJson, }; + +class _SyncAckV1 { + static _SyncAckV1? fromJson(dynamic _) => _SyncAckV1(); +} diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index 472bc9a6b5..89f5c2f59a 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -1,47 +1,40 @@ +import 'dart:convert'; + import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; -import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; -import 'package:immich_mobile/domain/models/album_user.model.dart'; -import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'; -// import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/stack.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' as api - show AssetVisibility, AssetOrder, AlbumUserRole; -import 'package:openapi/api.dart' - hide AssetVisibility, AssetOrder, AlbumUserRole; +import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole; +import 'package:openapi/api.dart' hide AssetVisibility, AlbumUserRole; -class DriftSyncStreamRepository extends DriftDatabaseRepository - implements ISyncStreamRepository { +class SyncStreamRepository extends DriftDatabaseRepository { final Logger _logger = Logger('DriftSyncStreamRepository'); final Drift _db; - DriftSyncStreamRepository(super.db) : _db = db; + SyncStreamRepository(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)), - ); - } - }); + await _db.userEntity + .deleteWhere((row) => row.id.isIn(data.map((e) => e.userId))); } catch (error, stack) { - _logger.severe('Error while processing SyncUserDeleteV1', error, stack); + _logger.severe('Error: SyncUserDeleteV1', error, stack); rethrow; } } - @override Future updateUsersV1(Iterable data) async { try { await _db.batch((batch) { @@ -59,12 +52,11 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository } }); } catch (error, stack) { - _logger.severe('Error while processing SyncUserV1', error, stack); + _logger.severe('Error: SyncUserV1', error, stack); rethrow; } } - @override Future deletePartnerV1(Iterable data) async { try { await _db.batch((batch) { @@ -78,13 +70,12 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository ); } }); - } catch (e, s) { - _logger.severe('Error while processing SyncPartnerDeleteV1', e, s); + } catch (error, stack) { + _logger.severe('Error: SyncPartnerDeleteV1', error, stack); rethrow; } } - @override Future updatePartnerV1(Iterable data) async { try { await _db.batch((batch) { @@ -102,210 +93,40 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository ); } }); - } catch (e, s) { - _logger.severe('Error while processing SyncPartnerV1', e, s); + } catch (error, stack) { + _logger.severe('Error: SyncPartnerV1', error, stack); rethrow; } } - @override - Future deleteAssetsV1(Iterable data) async { + Future deleteAssetsV1( + Iterable data, { + String debugLabel = 'user', + }) async { try { - await _deleteAssetsV1(data); - } catch (e, s) { - _logger.severe('Error while processing deleteAssetsV1', e, s); + await _db.remoteAssetEntity.deleteWhere( + (row) => row.id.isIn(data.map((e) => e.assetId)), + ); + } catch (error, stack) { + _logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack); rethrow; } } - @override - Future updateAssetsV1(Iterable data) async { - try { - await _updateAssetsV1(data); - } catch (e, s) { - _logger.severe('Error while processing updateAssetsV1', e, s); - rethrow; - } - } - - @override - Future deletePartnerAssetsV1(Iterable data) async { - try { - await _deleteAssetsV1(data); - } catch (e, s) { - _logger.severe('Error while processing deletePartnerAssetsV1', e, s); - rethrow; - } - } - - @override - Future updatePartnerAssetsV1(Iterable data) async { - try { - await _updateAssetsV1(data); - } catch (e, s) { - _logger.severe('Error while processing updatePartnerAssetsV1', e, s); - rethrow; - } - } - - @override - Future updateAssetsExifV1(Iterable data) async { - try { - await _updateAssetExifV1(data); - } catch (e, s) { - _logger.severe('Error while processing updateAssetsExifV1', e, s); - rethrow; - } - } - - @override - Future updatePartnerAssetsExifV1(Iterable data) async { - try { - await _updateAssetExifV1(data); - } catch (e, s) { - _logger.severe('Error while processing updatePartnerAssetsExifV1', e, s); - rethrow; - } - } - - @override - Future updateAlbumsV1(Iterable data) async { + Future updateAssetsV1( + Iterable data, { + String debugLabel = 'user', + }) async { try { await _db.batch((batch) { - for (final album in data) { - final companion = RemoteAlbumEntityCompanion( - name: Value(album.name), - description: Value(album.description), - ownerId: Value(album.ownerId), - thumbnailAssetId: Value(album.thumbnailAssetId), - createdAt: Value(album.createdAt), - updatedAt: Value(album.updatedAt), - isActivityEnabled: Value(album.isActivityEnabled), - order: Value(album.order.toAssetOrder()), - ); - - batch.insert( - _db.remoteAlbumEntity, - companion.copyWith(id: Value(album.id)), - onConflict: DoUpdate((_) => companion), - ); - } - }); - } catch (e, s) { - _logger.severe('Error while processing updateAlbumsV1', e, s); - rethrow; - } - } - - @override - Future deleteAlbumsV1(Iterable data) async { - try { - _db.batch((batch) { - for (final album in data) { - batch.delete( - _db.remoteAlbumEntity, - RemoteAlbumEntityCompanion(id: Value(album.albumId)), - ); - } - }); - } catch (e, s) { - _logger.severe('Error while processing deleteAlbumsV1', e, s); - rethrow; - } - } - - // @override - // Future updateAlbumAssetsV1(Iterable data) async { - // try { - // await _db.remoteAlbumAssetEntity.insertAll( - // data.map( - // (albumAsset) => RemoteAlbumAssetEntityCompanion.insert( - // albumId: albumAsset.albumId, - // assetId: albumAsset.assetId, - // ), - // ), - // mode: InsertMode.insertOrIgnore, - // ); - // } catch (e, s) { - // _logger.severe('Error while processing updateAlbumAssetsV1', e, s); - // rethrow; - // } - // } - - // @override - // Future deleteAlbumAssetsV1(Iterable data) async { - // try { - // await _db.batch((batch) { - // for (final albumAsset in data) { - // batch.delete( - // _db.remoteAlbumAssetEntity, - // RemoteAlbumAssetEntityCompanion( - // albumId: Value(albumAsset.albumId), - // assetId: Value(albumAsset.assetId), - // ), - // ); - // } - // }); - // } catch (e, s) { - // _logger.severe('Error while processing deleteAlbumAssetsV1', e, s); - // rethrow; - // } - // } - - @override - Future updateAlbumUsersV1(Iterable data) async { - try { - await _db.batch((batch) { - for (final albumUser in data) { - final companion = AlbumUserEntityCompanion( - role: Value(albumUser.role.toAlbumUserRole()), - ); - - batch.insert( - _db.albumUserEntity, - companion.copyWith( - albumId: Value(albumUser.albumId), - userId: Value(albumUser.userId), - ), - onConflict: DoUpdate((_) => companion), - ); - } - }); - } catch (e, s) { - _logger.severe('Error while processing updateAlbumUsersV1', e, s); - rethrow; - } - } - - @override - Future deleteAlbumUsersV1(Iterable data) async { - try { - await _db.batch((batch) { - for (final albumUser in data) { - batch.delete( - _db.albumUserEntity, - AlbumUserEntityCompanion( - albumId: Value(albumUser.albumId), - userId: Value(albumUser.userId), - ), - ); - } - }); - } catch (e, s) { - _logger.severe('Error while processing deleteAlbumUsersV1', e, s); - rethrow; - } - } - - Future _updateAssetsV1(Iterable data) => - _db.batch((batch) { for (final asset in data) { final companion = RemoteAssetEntityCompanion( name: Value(asset.originalFileName), type: Value(asset.type.toAssetType()), createdAt: Value.absentIfNull(asset.fileCreatedAt), updatedAt: Value.absentIfNull(asset.fileModifiedAt), - durationInSeconds: const Value(0), + durationInSeconds: + Value(asset.duration?.toDuration()?.inSeconds ?? 0), checksum: Value(asset.checksum), isFavorite: Value(asset.isFavorite), ownerId: Value(asset.ownerId), @@ -322,19 +143,18 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository ); } }); + } catch (error, stack) { + _logger.severe('Error: updateAssetsV1 - $debugLabel', error, stack); + rethrow; + } + } - Future _deleteAssetsV1(Iterable assets) => - _db.batch((batch) { - for (final asset in assets) { - batch.delete( - _db.remoteAssetEntity, - RemoteAssetEntityCompanion(id: Value(asset.assetId)), - ); - } - }); - - Future _updateAssetExifV1(Iterable data) => - _db.batch((batch) { + Future updateAssetsExifV1( + Iterable data, { + String debugLabel = 'user', + }) async { + try { + await _db.batch((batch) { for (final exif in data) { final companion = RemoteExifEntityCompanion( city: Value(exif.city), @@ -348,8 +168,8 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository fNumber: Value(exif.fNumber), fileSize: Value(exif.fileSizeInByte), focalLength: Value(exif.focalLength), - latitude: Value(exif.latitude), - longitude: Value(exif.longitude), + latitude: Value(exif.latitude?.toDouble()), + longitude: Value(exif.longitude?.toDouble()), iso: Value(exif.iso), make: Value(exif.make), model: Value(exif.model), @@ -357,6 +177,7 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository timeZone: Value(exif.timeZone), rating: Value(exif.rating), projectionType: Value(exif.projectionType), + lens: Value(exif.lensModel), ); batch.insert( @@ -366,6 +187,279 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository ); } }); + } catch (error, stack) { + _logger.severe( + 'Error: updateAssetsExifV1 - $debugLabel', + error, + stack, + ); + rethrow; + } + } + + Future deleteAlbumsV1(Iterable data) async { + try { + await _db.remoteAlbumEntity.deleteWhere( + (row) => row.id.isIn(data.map((e) => e.albumId)), + ); + } catch (error, stack) { + _logger.severe('Error: deleteAlbumsV1', error, stack); + rethrow; + } + } + + Future updateAlbumsV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final album in data) { + final companion = RemoteAlbumEntityCompanion( + name: Value(album.name), + description: Value(album.description), + isActivityEnabled: Value(album.isActivityEnabled), + order: Value(album.order.toAlbumAssetOrder()), + thumbnailAssetId: Value(album.thumbnailAssetId), + ownerId: Value(album.ownerId), + createdAt: Value(album.createdAt), + updatedAt: Value(album.updatedAt), + ); + + batch.insert( + _db.remoteAlbumEntity, + companion.copyWith(id: Value(album.id)), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: updateAlbumsV1', error, stack); + rethrow; + } + } + + Future deleteAlbumUsersV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final album in data) { + batch.delete( + _db.remoteAlbumUserEntity, + RemoteAlbumUserEntityCompanion( + albumId: Value(album.albumId), + userId: Value(album.userId), + ), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: deleteAlbumUsersV1', error, stack); + rethrow; + } + } + + Future updateAlbumUsersV1( + Iterable data, { + String debugLabel = 'user', + }) async { + try { + await _db.batch((batch) { + for (final album in data) { + final companion = RemoteAlbumUserEntityCompanion( + role: Value(album.role.toAlbumUserRole()), + ); + + batch.insert( + _db.remoteAlbumUserEntity, + companion.copyWith( + albumId: Value(album.albumId), + userId: Value(album.userId), + ), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe( + 'Error: updateAlbumUsersV1 - $debugLabel', + error, + stack, + ); + rethrow; + } + } + + Future deleteAlbumToAssetsV1( + Iterable data, + ) async { + try { + await _db.batch((batch) { + for (final album in data) { + batch.delete( + _db.remoteAlbumAssetEntity, + RemoteAlbumAssetEntityCompanion( + albumId: Value(album.albumId), + assetId: Value(album.assetId), + ), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: deleteAlbumToAssetsV1', error, stack); + rethrow; + } + } + + Future updateAlbumToAssetsV1( + Iterable data, { + String debugLabel = 'user', + }) async { + try { + await _db.batch((batch) { + for (final album in data) { + final companion = RemoteAlbumAssetEntityCompanion( + albumId: Value(album.albumId), + assetId: Value(album.assetId), + ); + + batch.insert( + _db.remoteAlbumAssetEntity, + companion, + onConflict: DoNothing(), + ); + } + }); + } catch (error, stack) { + _logger.severe( + 'Error: updateAlbumToAssetsV1 - $debugLabel', + error, + stack, + ); + rethrow; + } + } + + Future updateMemoriesV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final memory in data) { + final companion = MemoryEntityCompanion( + createdAt: Value(memory.createdAt), + deletedAt: Value(memory.deletedAt), + ownerId: Value(memory.ownerId), + type: Value(memory.type.toMemoryType()), + data: Value(jsonEncode(memory.data)), + isSaved: Value(memory.isSaved), + memoryAt: Value(memory.memoryAt), + seenAt: Value.absentIfNull(memory.seenAt), + showAt: Value.absentIfNull(memory.showAt), + hideAt: Value.absentIfNull(memory.hideAt), + ); + + batch.insert( + _db.memoryEntity, + companion.copyWith(id: Value(memory.id)), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: updateMemoriesV1', error, stack); + rethrow; + } + } + + Future deleteMemoriesV1(Iterable data) async { + try { + await _db.memoryEntity.deleteWhere( + (row) => row.id.isIn(data.map((e) => e.memoryId)), + ); + } catch (error, stack) { + _logger.severe('Error: deleteMemoriesV1', error, stack); + rethrow; + } + } + + Future updateMemoryAssetsV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final asset in data) { + final companion = MemoryAssetEntityCompanion( + memoryId: Value(asset.memoryId), + assetId: Value(asset.assetId), + ); + + batch.insert( + _db.memoryAssetEntity, + companion, + onConflict: DoNothing(), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: updateMemoryAssetsV1', error, stack); + rethrow; + } + } + + Future deleteMemoryAssetsV1( + Iterable data, + ) async { + try { + await _db.batch((batch) { + for (final asset in data) { + batch.delete( + _db.memoryAssetEntity, + MemoryAssetEntityCompanion( + memoryId: Value(asset.memoryId), + assetId: Value(asset.assetId), + ), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: deleteMemoryAssetsV1', error, stack); + rethrow; + } + } + + Future updateStacksV1( + Iterable data, { + String debugLabel = 'user', + }) async { + try { + await _db.batch((batch) { + for (final stack in data) { + final companion = StackEntityCompanion( + createdAt: Value(stack.createdAt), + updatedAt: Value(stack.updatedAt), + ownerId: Value(stack.ownerId), + primaryAssetId: Value(stack.primaryAssetId), + ); + + batch.insert( + _db.stackEntity, + companion.copyWith(id: Value(stack.id)), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: updateStacksV1 - $debugLabel', error, stack); + rethrow; + } + } + + Future deleteStacksV1( + Iterable data, { + String debugLabel = 'user', + }) async { + try { + await _db.stackEntity.deleteWhere( + (row) => row.id.isIn(data.map((e) => e.stackId)), + ); + } catch (error, stack) { + _logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack); + rethrow; + } + } } extension on AssetTypeEnum { @@ -378,6 +472,29 @@ extension on AssetTypeEnum { }; } +extension on AssetOrder { + AlbumAssetOrder toAlbumAssetOrder() => switch (this) { + AssetOrder.asc => AlbumAssetOrder.asc, + AssetOrder.desc => AlbumAssetOrder.desc, + _ => throw Exception('Unknown AssetOrder value: $this'), + }; +} + +extension on MemoryType { + MemoryTypeEnum toMemoryType() => switch (this) { + MemoryType.onThisDay => MemoryTypeEnum.onThisDay, + _ => throw Exception('Unknown MemoryType value: $this'), + }; +} + +extension on api.AlbumUserRole { + AlbumUserRole toAlbumUserRole() => switch (this) { + api.AlbumUserRole.editor => AlbumUserRole.editor, + api.AlbumUserRole.viewer => AlbumUserRole.viewer, + _ => throw Exception('Unknown AlbumUserRole value: $this'), + }; +} + extension on api.AssetVisibility { AssetVisibility toAssetVisibility() => switch (this) { api.AssetVisibility.timeline => AssetVisibility.timeline, @@ -388,18 +505,16 @@ extension on api.AssetVisibility { }; } -extension on api.AssetOrder { - AssetOrder toAssetOrder() => switch (this) { - api.AssetOrder.asc => AssetOrder.asc, - api.AssetOrder.desc => AssetOrder.desc, - _ => throw Exception('Unknown AssetOrder value: $this'), - }; -} +extension on String { + Duration? toDuration() { + try { + final parts = split(':') + .map((e) => double.parse(e).toInt()) + .toList(growable: false); -extension on api.AlbumUserRole { - AlbumUserRole toAlbumUserRole() => switch (this) { - api.AlbumUserRole.editor => AlbumUserRole.editor, - api.AlbumUserRole.viewer => AlbumUserRole.viewer, - _ => throw Exception('Unknown AlbumUserRole value: $this'), - }; + return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]); + } catch (_) { + return null; + } + } } diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart new file mode 100644 index 0000000000..569df0aed8 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -0,0 +1,584 @@ +import 'dart:async'; + +import 'package:drift/drift.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:stream_transform/stream_transform.dart'; + +class DriftTimelineRepository extends DriftDatabaseRepository { + final Drift _db; + + const DriftTimelineRepository(super._db) : _db = _db; + + Stream> watchTimelineUserIds(String userId) { + final query = _db.partnerEntity.selectOnly() + ..addColumns([_db.partnerEntity.sharedById]) + ..where( + _db.partnerEntity.inTimeline.equals(true) & + _db.partnerEntity.sharedWithId.equals(userId), + ); + + return query + .map((row) => row.read(_db.partnerEntity.sharedById)!) + .watch() + // Add current user ID to the list + .map((users) => users..add(userId)); + } + + List _generateBuckets(int count) { + final numBuckets = (count / kTimelineNoneSegmentSize).floor(); + final buckets = List.generate( + numBuckets, + (_) => const Bucket(assetCount: kTimelineNoneSegmentSize), + ); + if (count % kTimelineNoneSegmentSize != 0) { + buckets.add(Bucket(assetCount: count % kTimelineNoneSegmentSize)); + } + return buckets; + } + + Stream> watchMainBucket( + List userIds, { + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + if (groupBy == GroupAssetsBy.none) { + throw UnsupportedError( + "GroupAssetsBy.none is not supported for watchMainBucket", + ); + } + + return _db.mergedAssetDrift + .mergedBucket(userIds, groupBy: groupBy.index) + .map((row) { + final date = row.bucketDate.dateFmt(groupBy); + return TimeBucket(date: date, assetCount: row.assetCount); + }) + .watch() + .throttle(const Duration(seconds: 3), trailing: true); + } + + Future> getMainBucketAssets( + List userIds, { + required int offset, + required int count, + }) { + return _db.mergedAssetDrift + .mergedAsset(userIds, limit: Limit(count, offset)) + .map( + (row) { + return row.remoteId != null && row.ownerId != null + ? RemoteAsset( + id: row.remoteId!, + localId: row.localId, + name: row.name, + ownerId: row.ownerId!, + checksum: row.checksum, + type: row.type, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + thumbHash: row.thumbHash, + width: row.width, + height: row.height, + isFavorite: row.isFavorite, + durationInSeconds: row.durationInSeconds, + ) + : LocalAsset( + id: row.localId!, + remoteId: row.remoteId, + name: row.name, + checksum: row.checksum, + type: row.type, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + width: row.width, + height: row.height, + isFavorite: row.isFavorite, + durationInSeconds: row.durationInSeconds, + orientation: row.orientation, + ); + }, + ).get(); + } + + Stream> watchLocalAlbumBucket( + String albumId, { + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + if (groupBy == GroupAssetsBy.none) { + return _db.localAlbumAssetEntity + .count(where: (row) => row.albumId.equals(albumId)) + .map(_generateBuckets) + .watchSingle(); + } + + final assetCountExp = _db.localAssetEntity.id.count(); + final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.localAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> getLocalAlbumBucketAssets( + String albumId, { + required int offset, + required int count, + }) { + final query = _db.localAssetEntity.select().join( + [ + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + ], + ) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) + ..limit(count, offset: offset); + return query + .map((row) => row.readTable(_db.localAssetEntity).toDto()) + .get(); + } + + Stream> watchRemoteAlbumBucket( + String albumId, { + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + if (groupBy == GroupAssetsBy.none) { + return _db.remoteAlbumAssetEntity + .count(where: (row) => row.albumId.equals(albumId)) + .map(_generateBuckets) + .watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ + innerJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.assetId + .equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAlbumAssetEntity.albumId.equals(albumId), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> getRemoteAlbumBucketAssets( + String albumId, { + required int offset, + required int count, + }) { + final query = _db.remoteAssetEntity.select().join( + [ + innerJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.assetId + .equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ], + ) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAlbumAssetEntity.albumId.equals(albumId), + ) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); + return query + .map((row) => row.readTable(_db.remoteAssetEntity).toDto()) + .get(); + } + + Stream> watchRemoteBucket( + String ownerId, { + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + if (groupBy == GroupAssetsBy.none) { + return _db.remoteAssetEntity + .count( + where: (row) => + row.deletedAt.isNull() & + row.visibility.equalsValue(AssetVisibility.timeline) & + row.ownerId.equals(ownerId), + ) + .map(_generateBuckets) + .watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility + .equalsValue(AssetVisibility.timeline) & + _db.remoteAssetEntity.ownerId.equals(ownerId), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> getRemoteBucketAssets( + String ownerId, { + required int offset, + required int count, + }) { + final query = _db.remoteAssetEntity.select() + ..where( + (row) => + row.deletedAt.isNull() & + row.visibility.equalsValue(AssetVisibility.timeline) & + row.ownerId.equals(ownerId), + ) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.toDto()).get(); + } + + Stream> watchFavoriteBucket( + String userId, { + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + if (groupBy == GroupAssetsBy.none) { + return _db.remoteAssetEntity + .count( + where: (row) => + row.deletedAt.isNull() & + row.isFavorite.equals(true) & + row.ownerId.equals(userId), + ) + .map(_generateBuckets) + .watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.isFavorite.equals(true), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> getFavoriteBucketAssets( + String userId, { + required int offset, + required int count, + }) { + final query = _db.remoteAssetEntity.select() + ..where( + (row) => + row.deletedAt.isNull() & + row.isFavorite.equals(true) & + row.ownerId.equals(userId), + ) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.toDto()).get(); + } + + Stream> watchTrashBucket( + String userId, { + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + if (groupBy == GroupAssetsBy.none) { + return _db.remoteAssetEntity + .count( + where: (row) => + row.deletedAt.isNotNull() & row.ownerId.equals(userId), + ) + .map(_generateBuckets) + .watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..where( + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.deletedAt.isNotNull(), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> getTrashBucketAssets( + String userId, { + required int offset, + required int count, + }) { + final query = _db.remoteAssetEntity.select() + ..where( + (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), + ) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.toDto()).get(); + } + + Stream> watchArchiveBucket( + String userId, { + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + if (groupBy == GroupAssetsBy.none) { + return _db.remoteAssetEntity + .count( + where: (row) => + row.deletedAt.isNull() & + row.visibility.equalsValue(AssetVisibility.archive) & + row.ownerId.equals(userId), + ) + .map(_generateBuckets) + .watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility + .equalsValue(AssetVisibility.archive), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> getArchiveBucketAssets( + String userId, { + required int offset, + required int count, + }) { + final query = _db.remoteAssetEntity.select() + ..where( + (row) => + row.deletedAt.isNull() & + row.ownerId.equals(userId) & + row.visibility.equalsValue(AssetVisibility.archive), + ) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.toDto()).get(); + } + + Stream> watchLockedFolderBucket( + String userId, { + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + if (groupBy == GroupAssetsBy.none) { + return _db.remoteAssetEntity + .count( + where: (row) => + row.deletedAt.isNull() & + row.visibility.equalsValue(AssetVisibility.locked) & + row.ownerId.equals(userId), + ) + .map(_generateBuckets) + .watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility + .equalsValue(AssetVisibility.locked), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> getLockedFolderBucketAssets( + String userId, { + required int offset, + required int count, + }) { + final query = _db.remoteAssetEntity.select() + ..where( + (row) => + row.deletedAt.isNull() & + row.visibility.equalsValue(AssetVisibility.locked) & + row.ownerId.equals(userId), + ) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.toDto()).get(); + } + + Stream> watchVideoBucket( + String userId, { + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + if (groupBy == GroupAssetsBy.none) { + return _db.remoteAssetEntity + .count( + where: (row) => + row.deletedAt.isNull() & + row.type.equalsValue(AssetType.video) & + row.visibility.equalsValue(AssetVisibility.timeline) & + row.ownerId.equals(userId), + ) + .map(_generateBuckets) + .watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.type.equalsValue(AssetType.video) & + _db.remoteAssetEntity.visibility + .equalsValue(AssetVisibility.timeline), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> getVideoBucketAssets( + String userId, { + required int offset, + required int count, + }) { + final query = _db.remoteAssetEntity.select() + ..where( + (row) => + row.deletedAt.isNull() & + row.type.equalsValue(AssetType.video) & + row.visibility.equalsValue(AssetVisibility.timeline) & + row.ownerId.equals(userId), + ) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.toDto()).get(); + } +} + +extension on Expression { + Expression dateFmt(GroupAssetsBy groupBy) { + // DateTimes are stored in UTC, so we need to convert them to local time inside the query before formatting + // to create the correct time bucket + final localTimeExp = modify(const DateTimeModifier.localTime()); + return switch (groupBy) { + GroupAssetsBy.day => localTimeExp.date, + GroupAssetsBy.month => localTimeExp.strftime("%Y-%m"), + GroupAssetsBy.none => throw ArgumentError( + "GroupAssetsBy.none is not supported for date formatting", + ), + }; + } +} + +extension on String { + DateTime dateFmt(GroupAssetsBy groupBy) { + final format = switch (groupBy) { + GroupAssetsBy.day => "y-M-d", + GroupAssetsBy.month => "y-M", + GroupAssetsBy.none => throw ArgumentError( + "GroupAssetsBy.none is not supported for date formatting", + ), + }; + try { + return DateFormat(format).parse(this); + } catch (e) { + throw FormatException("Invalid date format: $this", e); + } + } +} diff --git a/mobile/lib/infrastructure/repositories/user.repository.dart b/mobile/lib/infrastructure/repositories/user.repository.dart index f12b95f64b..4ccfccbdcc 100644 --- a/mobile/lib/infrastructure/repositories/user.repository.dart +++ b/mobile/lib/infrastructure/repositories/user.repository.dart @@ -1,30 +1,26 @@ -import 'package:immich_mobile/domain/interfaces/user.interface.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:isar/isar.dart'; -class IsarUserRepository extends IsarDatabaseRepository - implements IUserRepository { +class IsarUserRepository extends IsarDatabaseRepository { final Isar _db; const IsarUserRepository(super.db) : _db = db; - @override Future delete(List ids) async { await transaction(() async { await _db.users.deleteAllById(ids); }); } - @override Future deleteAll() async { await transaction(() async { await _db.users.clear(); }); } - @override Future> getAll({SortUserBy? sortBy}) async { return (await _db.users .where() @@ -39,17 +35,14 @@ class IsarUserRepository extends IsarDatabaseRepository .toList(); } - @override Future getByUserId(String id) async { return (await _db.users.getById(id))?.toDto(); } - @override Future> getByUserIds(List ids) async { return (await _db.users.getAllById(ids)).map((u) => u?.toDto()).toList(); } - @override Future insert(UserDto user) async { await transaction(() async { await _db.users.put(entity.User.fromDto(user)); @@ -57,7 +50,6 @@ class IsarUserRepository extends IsarDatabaseRepository return true; } - @override Future update(UserDto user) async { await transaction(() async { await _db.users.put(entity.User.fromDto(user)); @@ -65,7 +57,6 @@ class IsarUserRepository extends IsarDatabaseRepository return user; } - @override Future updateAll(List users) async { await transaction(() async { await _db.users.putAll(users.map(entity.User.fromDto).toList()); diff --git a/mobile/lib/infrastructure/repositories/user_api.repository.dart b/mobile/lib/infrastructure/repositories/user_api.repository.dart index b922f93ca5..e990b9d8d1 100644 --- a/mobile/lib/infrastructure/repositories/user_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/user_api.repository.dart @@ -1,17 +1,15 @@ import 'dart:typed_data'; import 'package:http/http.dart'; -import 'package:immich_mobile/domain/interfaces/user_api.interface.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/repositories/api.repository.dart'; import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; import 'package:openapi/api.dart'; -class UserApiRepository extends ApiRepository implements IUserApiRepository { +class UserApiRepository extends ApiRepository { final UsersApi _api; const UserApiRepository(this._api); - @override Future getMyUser() async { final (adminDto, preferenceDto) = await (_api.getMyUser(), _api.getMyPreferences()).wait; @@ -20,7 +18,6 @@ class UserApiRepository extends ApiRepository implements IUserApiRepository { return UserConverter.fromAdminDto(adminDto, preferenceDto); } - @override Future createProfileImage({ required String name, required Uint8List data, @@ -33,7 +30,6 @@ class UserApiRepository extends ApiRepository implements IUserApiRepository { return res.profileImagePath; } - @override Future> getAll() async { final dto = await checkNull(_api.searchUsers()); return dto.map(UserConverter.fromSimpleUserDto).toList(); diff --git a/mobile/lib/interfaces/activity_api.interface.dart b/mobile/lib/interfaces/activity_api.interface.dart deleted file mode 100644 index 99aef6f4d4..0000000000 --- a/mobile/lib/interfaces/activity_api.interface.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:immich_mobile/models/activities/activity.model.dart'; - -abstract interface class IActivityApiRepository { - Future> getAll( - String albumId, { - String? assetId, - }); - Future create( - String albumId, - ActivityType type, { - String? assetId, - String? comment, - }); - Future delete(String id); - Future getStats(String albumId, {String? assetId}); -} diff --git a/mobile/lib/interfaces/album.interface.dart b/mobile/lib/interfaces/album.interface.dart deleted file mode 100644 index c1696eda80..0000000000 --- a/mobile/lib/interfaces/album.interface.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/entities/album.entity.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/interfaces/database.interface.dart'; -import 'package:immich_mobile/models/albums/album_search.model.dart'; - -abstract interface class IAlbumRepository implements IDatabaseRepository { - Future create(Album album); - - Future get(int id); - - Future getByName( - String name, { - bool? shared, - bool? remote, - bool? owner, - }); - - Future> getAll({ - bool? shared, - bool? remote, - int? ownerId, - AlbumSort? sortBy, - }); - - Future update(Album album); - - Future delete(int albumId); - - Future deleteAllLocal(); - - Future count({bool? local}); - - Future addUsers(Album album, List users); - - Future removeUsers(Album album, List users); - - Future addAssets(Album album, List assets); - - Future removeAssets(Album album, List assets); - - Future recalculateMetadata(Album album); - - Future> search(String searchTerm, QuickFilterMode filterMode); - - Stream> watchRemoteAlbums(); - - Stream> watchLocalAlbums(); - - Stream watchAlbum(int id); - - Future clearTable(); -} - -enum AlbumSort { remoteId, localId } diff --git a/mobile/lib/interfaces/album_api.interface.dart b/mobile/lib/interfaces/album_api.interface.dart deleted file mode 100644 index b751ccc170..0000000000 --- a/mobile/lib/interfaces/album_api.interface.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/entities/album.entity.dart'; - -abstract interface class IAlbumApiRepository { - Future get(String id); - - Future> getAll({bool? shared}); - - Future create( - String name, { - required Iterable assetIds, - Iterable sharedUserIds = const [], - }); - - Future update( - String albumId, { - String? name, - String? thumbnailAssetId, - String? description, - bool? activityEnabled, - SortOrder? sortOrder, - }); - - Future delete(String albumId); - - Future<({List added, List duplicates})> addAssets( - String albumId, - Iterable assetIds, - ); - - Future<({List removed, List failed})> removeAssets( - String albumId, - Iterable assetIds, - ); - - Future addUsers( - String albumId, - Iterable userIds, - ); - - Future removeUser(String albumId, {required String userId}); -} diff --git a/mobile/lib/interfaces/album_media.interface.dart b/mobile/lib/interfaces/album_media.interface.dart deleted file mode 100644 index fd5f3c8af1..0000000000 --- a/mobile/lib/interfaces/album_media.interface.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:immich_mobile/entities/album.entity.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; - -abstract interface class IAlbumMediaRepository { - Future> getAll(); - - Future> getAssetIds(String albumId); - - Future getAssetCount(String albumId); - - Future> getAssets( - String albumId, { - int start = 0, - int end = 0x7fffffffffffffff, - DateTime? modifiedFrom, - DateTime? modifiedUntil, - bool orderByModificationDate = false, - }); - - Future get(String id); -} diff --git a/mobile/lib/interfaces/asset.interface.dart b/mobile/lib/interfaces/asset.interface.dart deleted file mode 100644 index ca9e9d64fb..0000000000 --- a/mobile/lib/interfaces/asset.interface.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:immich_mobile/entities/album.entity.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/interfaces/database.interface.dart'; - -abstract interface class IAssetRepository implements IDatabaseRepository { - Future getByRemoteId(String id); - - Future getByOwnerIdChecksum(int ownerId, String checksum); - - Future> getAllByRemoteId( - Iterable ids, { - AssetState? state, - }); - - Future> getAllByOwnerIdChecksum( - List ids, - List checksums, - ); - - Future> getAll({ - required String ownerId, - AssetState? state, - AssetSort? sortBy, - int? limit, - }); - - Future> getAllLocal(); - - Future> getByAlbum( - Album album, { - Iterable notOwnedBy = const [], - String? ownerId, - AssetState? state, - AssetSort? sortBy, - }); - - Future update(Asset asset); - - Future> updateAll(List assets); - - Future deleteAllByRemoteId(List ids, {AssetState? state}); - - Future deleteByIds(List ids); - - Future> getMatches({ - required List assets, - required String ownerId, - AssetState? state, - int limit = 100, - }); - - Future upsertDuplicatedAssets(Iterable duplicatedAssets); - - Future> getAllDuplicatedAssetIds(); - - Future> getStackAssets(String stackId); - - Future clearTable(); - - Stream watchAsset(int id, {bool fireImmediately = false}); - - Future> getTrashAssets(String userId); - - Future> getRecentlyTakenAssets(String userId); - Future> getMotionAssets(String userId); -} - -enum AssetSort { checksum, ownerIdChecksum } diff --git a/mobile/lib/interfaces/asset_api.interface.dart b/mobile/lib/interfaces/asset_api.interface.dart deleted file mode 100644 index 71ee993a6b..0000000000 --- a/mobile/lib/interfaces/asset_api.interface.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; - -abstract interface class IAssetApiRepository { - // Future get(String id); - - // Future> getAll(); - - // Future create(Asset asset); - - Future update( - String id, { - String? description, - }); - - // Future delete(String id); - - Future> search({List personIds = const []}); - - Future updateVisibility( - List list, - AssetVisibilityEnum visibility, - ); - - Future getAssetMIMEType(String id); -} diff --git a/mobile/lib/interfaces/asset_media.interface.dart b/mobile/lib/interfaces/asset_media.interface.dart deleted file mode 100644 index 2606d5c23c..0000000000 --- a/mobile/lib/interfaces/asset_media.interface.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:immich_mobile/entities/asset.entity.dart'; - -abstract interface class IAssetMediaRepository { - Future> deleteAll(List ids); - - Future get(String id); - - /// Obtaining the correct original filename of the asset - Future getOriginalFilename(String id); -} diff --git a/mobile/lib/interfaces/auth.interface.dart b/mobile/lib/interfaces/auth.interface.dart deleted file mode 100644 index 57088f4569..0000000000 --- a/mobile/lib/interfaces/auth.interface.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:immich_mobile/interfaces/database.interface.dart'; -import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; - -abstract interface class IAuthRepository implements IDatabaseRepository { - Future clearLocalData(); - String getAccessToken(); - bool getEndpointSwitchingFeature(); - String? getPreferredWifiName(); - String? getLocalEndpoint(); - List getExternalEndpointList(); -} diff --git a/mobile/lib/interfaces/auth_api.interface.dart b/mobile/lib/interfaces/auth_api.interface.dart deleted file mode 100644 index bb9a8b5a2c..0000000000 --- a/mobile/lib/interfaces/auth_api.interface.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:immich_mobile/models/auth/login_response.model.dart'; - -abstract interface class IAuthApiRepository { - Future login(String email, String password); - - Future logout(); - - Future changePassword(String newPassword); - - Future unlockPinCode(String pinCode); - Future lockPinCode(); - - Future setupPinCode(String pinCode); -} diff --git a/mobile/lib/interfaces/backup_album.interface.dart b/mobile/lib/interfaces/backup_album.interface.dart deleted file mode 100644 index f98adb6821..0000000000 --- a/mobile/lib/interfaces/backup_album.interface.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:immich_mobile/entities/backup_album.entity.dart'; -import 'package:immich_mobile/interfaces/database.interface.dart'; - -abstract interface class IBackupAlbumRepository implements IDatabaseRepository { - Future> getAll({BackupAlbumSort? sort}); - - Future> getIdsBySelection(BackupSelection backup); - - Future> getAllBySelection(BackupSelection backup); - - Future updateAll(List backupAlbums); - - Future deleteAll(List ids); -} - -enum BackupAlbumSort { id } diff --git a/mobile/lib/interfaces/biometric.interface.dart b/mobile/lib/interfaces/biometric.interface.dart deleted file mode 100644 index e410c8e26e..0000000000 --- a/mobile/lib/interfaces/biometric.interface.dart +++ /dev/null @@ -1,6 +0,0 @@ -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/cast_destination_service.interface.dart b/mobile/lib/interfaces/cast_destination_service.interface.dart deleted file mode 100644 index add8ad7c51..0000000000 --- a/mobile/lib/interfaces/cast_destination_service.interface.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/models/cast/cast_manager_state.dart'; - -abstract interface class ICastDestinationService { - Future initialize(); - CastDestinationType getType(); - - void Function(bool)? onConnectionState; - - void Function(Duration)? onCurrentTime; - void Function(Duration)? onDuration; - - void Function(String)? onReceiverName; - void Function(CastState)? onCastState; - - Future connect(dynamic device); - - void loadMedia(Asset asset, bool reload); - - void play(); - void pause(); - void seekTo(Duration position); - void stop(); - Future disconnect(); - - Future> getDevices(); -} diff --git a/mobile/lib/interfaces/download.interface.dart b/mobile/lib/interfaces/download.interface.dart deleted file mode 100644 index beb063d6a2..0000000000 --- a/mobile/lib/interfaces/download.interface.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:background_downloader/background_downloader.dart'; - -abstract interface class IDownloadRepository { - void Function(TaskStatusUpdate)? onImageDownloadStatus; - void Function(TaskStatusUpdate)? onVideoDownloadStatus; - void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus; - void Function(TaskProgressUpdate)? onTaskProgress; - - Future> getLiveVideoTasks(); - Future> downloadAll(List tasks); - - Future cancel(String id); - Future deleteAllTrackingRecords(); - Future deleteRecordsWithIds(List id); -} diff --git a/mobile/lib/interfaces/etag.interface.dart b/mobile/lib/interfaces/etag.interface.dart deleted file mode 100644 index 8b4b5806c9..0000000000 --- a/mobile/lib/interfaces/etag.interface.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:immich_mobile/entities/etag.entity.dart'; -import 'package:immich_mobile/interfaces/database.interface.dart'; - -abstract interface class IETagRepository implements IDatabaseRepository { - Future get(String id); - - Future getById(String id); - - Future> getAllIds(); - - Future upsertAll(List etags); - - Future deleteByIds(List ids); - - Future clearTable(); -} diff --git a/mobile/lib/interfaces/file_media.interface.dart b/mobile/lib/interfaces/file_media.interface.dart deleted file mode 100644 index ea01819dc3..0000000000 --- a/mobile/lib/interfaces/file_media.interface.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:immich_mobile/entities/asset.entity.dart'; - -abstract interface class IFileMediaRepository { - Future saveImage( - Uint8List data, { - required String title, - String? relativePath, - }); - - Future saveImageWithFile( - String filePath, { - String? title, - String? relativePath, - }); - - Future saveVideo( - File file, { - required String title, - String? relativePath, - }); - - Future saveLivePhoto({ - required File image, - required File video, - required String title, - }); - - Future clearFileCache(); - - Future enableBackgroundAccess(); - - Future requestExtendedPermissions(); -} diff --git a/mobile/lib/interfaces/folder_api.interface.dart b/mobile/lib/interfaces/folder_api.interface.dart deleted file mode 100644 index 68c1652e21..0000000000 --- a/mobile/lib/interfaces/folder_api.interface.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:immich_mobile/entities/asset.entity.dart'; - -abstract interface class IFolderApiRepository { - Future> getAllUniquePaths(); - Future> getAssetsForPath(String? path); -} diff --git a/mobile/lib/interfaces/local_files_manager.interface.dart b/mobile/lib/interfaces/local_files_manager.interface.dart deleted file mode 100644 index 07274b7e29..0000000000 --- a/mobile/lib/interfaces/local_files_manager.interface.dart +++ /dev/null @@ -1,5 +0,0 @@ -abstract interface class ILocalFilesManager { - Future moveToTrash(List mediaUrls); - Future restoreFromTrash(String fileName, int type); - Future requestManageMediaPermission(); -} diff --git a/mobile/lib/interfaces/network.interface.dart b/mobile/lib/interfaces/network.interface.dart deleted file mode 100644 index 098d67a27b..0000000000 --- a/mobile/lib/interfaces/network.interface.dart +++ /dev/null @@ -1,4 +0,0 @@ -abstract interface class INetworkRepository { - Future getWifiName(); - Future getWifiIp(); -} diff --git a/mobile/lib/interfaces/partner.interface.dart b/mobile/lib/interfaces/partner.interface.dart deleted file mode 100644 index 8e5fcb7a97..0000000000 --- a/mobile/lib/interfaces/partner.interface.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:immich_mobile/domain/models/user.model.dart'; - -abstract class IPartnerRepository { - Future> getSharedWith(); - Future> getSharedBy(); - Stream> watchSharedWith(); - Stream> watchSharedBy(); -} diff --git a/mobile/lib/interfaces/partner_api.interface.dart b/mobile/lib/interfaces/partner_api.interface.dart deleted file mode 100644 index 01149f473c..0000000000 --- a/mobile/lib/interfaces/partner_api.interface.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:immich_mobile/domain/models/user.model.dart'; - -abstract interface class IPartnerApiRepository { - Future> getAll(Direction direction); - Future create(String id); - Future update(String id, {required bool inTimeline}); - Future delete(String id); -} - -enum Direction { - sharedWithMe, - sharedByMe, -} diff --git a/mobile/lib/interfaces/secure_storage.interface.dart b/mobile/lib/interfaces/secure_storage.interface.dart deleted file mode 100644 index 81230e0abd..0000000000 --- a/mobile/lib/interfaces/secure_storage.interface.dart +++ /dev/null @@ -1,5 +0,0 @@ -abstract interface class ISecureStorageRepository { - Future read(String key); - Future write(String key, String value); - Future delete(String key); -} diff --git a/mobile/lib/interfaces/sessions_api.interface.dart b/mobile/lib/interfaces/sessions_api.interface.dart deleted file mode 100644 index 4b90b77829..0000000000 --- a/mobile/lib/interfaces/sessions_api.interface.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:immich_mobile/models/sessions/session_create_response.model.dart'; - -abstract interface class ISessionAPIRepository { - Future createSession( - String deviceName, - String deviceOS, { - int? duration, - }); -} diff --git a/mobile/lib/interfaces/share_handler.interface.dart b/mobile/lib/interfaces/share_handler.interface.dart deleted file mode 100644 index 6d0eb9170c..0000000000 --- a/mobile/lib/interfaces/share_handler.interface.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; - -abstract interface class IShareHandlerRepository { - void Function(List)? onSharedMedia; - - Future init(); -} diff --git a/mobile/lib/interfaces/timeline.interface.dart b/mobile/lib/interfaces/timeline.interface.dart deleted file mode 100644 index ecdc0dcf0c..0000000000 --- a/mobile/lib/interfaces/timeline.interface.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:immich_mobile/entities/album.entity.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; - -abstract class ITimelineRepository { - Future> getTimelineUserIds(String id); - - Stream> watchTimelineUsers(String id); - - Stream watchArchiveTimeline(String userId); - Stream watchFavoriteTimeline(String userId); - Stream watchTrashTimeline(String userId); - Stream watchAlbumTimeline( - Album album, - GroupAssetsBy groupAssetsBy, - ); - Stream watchAllVideosTimeline(String userId); - - Stream watchHomeTimeline( - String userId, - GroupAssetsBy groupAssetsBy, - ); - Stream watchMultiUsersTimeline( - List userIds, - GroupAssetsBy groupAssetsBy, - ); - - Future getTimelineFromAssets( - List assets, - GroupAssetsBy getGroupByOption, - ); - - Stream watchAssetSelectionTimeline(String userId); - - Stream watchLockedTimeline( - String userId, - GroupAssetsBy groupAssetsBy, - ); -} diff --git a/mobile/lib/interfaces/upload.interface.dart b/mobile/lib/interfaces/upload.interface.dart deleted file mode 100644 index d4b2298a14..0000000000 --- a/mobile/lib/interfaces/upload.interface.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:background_downloader/background_downloader.dart'; - -abstract interface class IUploadRepository { - void Function(TaskStatusUpdate)? onUploadStatus; - void Function(TaskProgressUpdate)? onTaskProgress; - - Future upload(UploadTask task); - Future cancel(String id); - Future deleteAllTrackingRecords(); - Future deleteRecordsWithIds(List id); -} diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index bc9edcd46e..1f18ecdf5a 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:auto_route/auto_route.dart'; import 'package:background_downloader/background_downloader.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -17,6 +18,7 @@ import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provide import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/locale_provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/theme.provider.dart'; import 'package:immich_mobile/routing/app_navigation_observer.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -26,6 +28,7 @@ import 'package:immich_mobile/theme/dynamic_theme.dart'; 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/services/deep_link.service.dart'; import 'package:immich_mobile/utils/download.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:immich_mobile/utils/migration.dart'; @@ -169,6 +172,31 @@ class ImmichAppState extends ConsumerState ); } + Future _deepLinkBuilder(PlatformDeepLink deepLink) async { + final deepLinkHandler = ref.read(deepLinkServiceProvider); + final currentRouteName = ref.read(currentRouteNameProvider.notifier).state; + + if (deepLink.uri.scheme == "immich") { + final proposedRoute = await deepLinkHandler.handleScheme( + deepLink, + currentRouteName == SplashScreenRoute.name, + ); + + return proposedRoute; + } + + if (deepLink.uri.host == "my.immich.app") { + final proposedRoute = await deepLinkHandler.handleMyImmichApp( + deepLink, + currentRouteName == SplashScreenRoute.name, + ); + + return proposedRoute; + } + + return DeepLink.path(deepLink.path); + } + @override void didChangeDependencies() { super.didChangeDependencies(); @@ -220,8 +248,8 @@ class ImmichAppState extends ConsumerState colorScheme: immichTheme.light, locale: context.locale, ), - routeInformationParser: router.defaultRouteParser(), - routerDelegate: router.delegate( + routerConfig: router.config( + deepLinkBuilder: _deepLinkBuilder, navigatorObservers: () => [AppNavigationObserver(ref: ref)], ), ), diff --git a/mobile/lib/models/albums/album_viewer_page_state.model.dart b/mobile/lib/models/albums/album_viewer_page_state.model.dart index 10a8183ddc..9fd5da1b28 100644 --- a/mobile/lib/models/albums/album_viewer_page_state.model.dart +++ b/mobile/lib/models/albums/album_viewer_page_state.model.dart @@ -5,7 +5,7 @@ class AlbumViewerPageState { final String editTitleText; final String editDescriptionText; - AlbumViewerPageState({ + const AlbumViewerPageState({ required this.isEditAlbum, required this.editTitleText, required this.editDescriptionText, diff --git a/mobile/lib/models/albums/asset_selection_page_result.model.dart b/mobile/lib/models/albums/asset_selection_page_result.model.dart index 04934f7a72..2a4daba64a 100644 --- a/mobile/lib/models/albums/asset_selection_page_result.model.dart +++ b/mobile/lib/models/albums/asset_selection_page_result.model.dart @@ -4,7 +4,7 @@ import 'package:immich_mobile/entities/asset.entity.dart'; class AssetSelectionPageResult { final Set selectedAssets; - AssetSelectionPageResult({ + const AssetSelectionPageResult({ required this.selectedAssets, }); @override diff --git a/mobile/lib/models/auth/auth_state.model.dart b/mobile/lib/models/auth/auth_state.model.dart index fb65850f1d..0d8357d66d 100644 --- a/mobile/lib/models/auth/auth_state.model.dart +++ b/mobile/lib/models/auth/auth_state.model.dart @@ -7,7 +7,7 @@ class AuthState { final bool isAdmin; final String profileImagePath; - AuthState({ + const AuthState({ required this.deviceId, required this.userId, required this.userEmail, diff --git a/mobile/lib/models/auth/auxilary_endpoint.model.dart b/mobile/lib/models/auth/auxilary_endpoint.model.dart index 89aba60913..f49d0f692c 100644 --- a/mobile/lib/models/auth/auxilary_endpoint.model.dart +++ b/mobile/lib/models/auth/auxilary_endpoint.model.dart @@ -5,7 +5,7 @@ class AuxilaryEndpoint { final String url; final AuxCheckStatus status; - AuxilaryEndpoint({ + const AuxilaryEndpoint({ required this.url, required this.status, }); @@ -55,7 +55,7 @@ class AuxilaryEndpoint { class AuxCheckStatus { final String name; - AuxCheckStatus({ + const AuxCheckStatus({ required this.name, }); const AuxCheckStatus._(this.name); diff --git a/mobile/lib/models/auth/login_response.model.dart b/mobile/lib/models/auth/login_response.model.dart index f1398418ca..0aa9c0b349 100644 --- a/mobile/lib/models/auth/login_response.model.dart +++ b/mobile/lib/models/auth/login_response.model.dart @@ -13,7 +13,7 @@ class LoginResponse { final String userId; - LoginResponse({ + const LoginResponse({ required this.accessToken, required this.isAdmin, required this.name, diff --git a/mobile/lib/models/backup/available_album.model.dart b/mobile/lib/models/backup/available_album.model.dart index c75d6446d8..e34d2b1abe 100644 --- a/mobile/lib/models/backup/available_album.model.dart +++ b/mobile/lib/models/backup/available_album.model.dart @@ -4,7 +4,7 @@ class AvailableAlbum { final Album album; final int assetCount; final DateTime? lastBackup; - AvailableAlbum({ + const AvailableAlbum({ required this.album, required this.assetCount, this.lastBackup, diff --git a/mobile/lib/models/backup/current_upload_asset.model.dart b/mobile/lib/models/backup/current_upload_asset.model.dart index 787f117269..edaac06987 100644 --- a/mobile/lib/models/backup/current_upload_asset.model.dart +++ b/mobile/lib/models/backup/current_upload_asset.model.dart @@ -9,7 +9,7 @@ class CurrentUploadAsset { final int? fileSize; final bool? iCloudAsset; - CurrentUploadAsset({ + const CurrentUploadAsset({ required this.id, required this.fileCreatedAt, required this.fileName, diff --git a/mobile/lib/models/backup/success_upload_asset.model.dart b/mobile/lib/models/backup/success_upload_asset.model.dart index 045715e8cb..2b39dde5ea 100644 --- a/mobile/lib/models/backup/success_upload_asset.model.dart +++ b/mobile/lib/models/backup/success_upload_asset.model.dart @@ -5,7 +5,7 @@ class SuccessUploadAsset { final String remoteAssetId; final bool isDuplicate; - SuccessUploadAsset({ + const SuccessUploadAsset({ required this.candidate, required this.remoteAssetId, required this.isDuplicate, diff --git a/mobile/lib/models/download/download_state.model.dart b/mobile/lib/models/download/download_state.model.dart index edd2fa183e..c032c73dcd 100644 --- a/mobile/lib/models/download/download_state.model.dart +++ b/mobile/lib/models/download/download_state.model.dart @@ -10,7 +10,7 @@ class DownloadInfo { // enum final TaskStatus status; - DownloadInfo({ + const DownloadInfo({ required this.fileName, required this.progress, required this.status, @@ -71,7 +71,7 @@ class DownloadState { final TaskStatus downloadStatus; final Map taskProgress; final bool showProgress; - DownloadState({ + const DownloadState({ required this.downloadStatus, required this.taskProgress, required this.showProgress, diff --git a/mobile/lib/models/folder/recursive_folder.model.dart b/mobile/lib/models/folder/recursive_folder.model.dart index 5b54a2e1bf..62ec670fed 100644 --- a/mobile/lib/models/folder/recursive_folder.model.dart +++ b/mobile/lib/models/folder/recursive_folder.model.dart @@ -3,7 +3,7 @@ import 'package:immich_mobile/models/folder/root_folder.model.dart'; class RecursiveFolder extends RootFolder { final String name; - RecursiveFolder({ + const RecursiveFolder({ required this.name, required super.path, required super.subfolders, diff --git a/mobile/lib/models/folder/root_folder.model.dart b/mobile/lib/models/folder/root_folder.model.dart index 8f72a539c0..567093ecd5 100644 --- a/mobile/lib/models/folder/root_folder.model.dart +++ b/mobile/lib/models/folder/root_folder.model.dart @@ -4,7 +4,7 @@ class RootFolder { final List subfolders; final String path; - RootFolder({ + const RootFolder({ required this.subfolders, required this.path, }); diff --git a/mobile/lib/models/map/map_event.model.dart b/mobile/lib/models/map/map_event.model.dart index dd9fec06e6..a57fcb4c36 100644 --- a/mobile/lib/models/map/map_event.model.dart +++ b/mobile/lib/models/map/map_event.model.dart @@ -8,4 +8,6 @@ class MapAssetsInBoundsUpdated extends MapEvent { const MapAssetsInBoundsUpdated(this.assetRemoteIds); } -class MapCloseBottomSheet extends MapEvent {} +class MapCloseBottomSheet extends MapEvent { + const MapCloseBottomSheet(); +} diff --git a/mobile/lib/models/map/map_marker.model.dart b/mobile/lib/models/map/map_marker.model.dart index c9253a37cc..7ab925464a 100644 --- a/mobile/lib/models/map/map_marker.model.dart +++ b/mobile/lib/models/map/map_marker.model.dart @@ -4,7 +4,7 @@ import 'package:openapi/api.dart'; class MapMarker { final LatLng latLng; final String assetRemoteId; - MapMarker({ + const MapMarker({ required this.latLng, required this.assetRemoteId, }); diff --git a/mobile/lib/models/map/map_state.model.dart b/mobile/lib/models/map/map_state.model.dart index 973b925aa8..78747e770d 100644 --- a/mobile/lib/models/map/map_state.model.dart +++ b/mobile/lib/models/map/map_state.model.dart @@ -11,7 +11,7 @@ class MapState { final AsyncValue lightStyleFetched; final AsyncValue darkStyleFetched; - MapState({ + const MapState({ this.themeMode = ThemeMode.system, this.showFavoriteOnly = false, this.includeArchived = false, diff --git a/mobile/lib/models/memories/memory.model.dart b/mobile/lib/models/memories/memory.model.dart index 34691d3b55..c8451dda64 100644 --- a/mobile/lib/models/memories/memory.model.dart +++ b/mobile/lib/models/memories/memory.model.dart @@ -7,7 +7,7 @@ import 'package:immich_mobile/entities/asset.entity.dart'; class Memory { final String title; final List assets; - Memory({ + const Memory({ required this.title, required this.assets, }); diff --git a/mobile/lib/models/search/search_curated_content.model.dart b/mobile/lib/models/search/search_curated_content.model.dart index a3d74941b3..aff61097e2 100644 --- a/mobile/lib/models/search/search_curated_content.model.dart +++ b/mobile/lib/models/search/search_curated_content.model.dart @@ -14,7 +14,7 @@ class SearchCuratedContent { /// The id to lookup the asset from the server final String id; - SearchCuratedContent({ + const SearchCuratedContent({ required this.label, required this.id, this.subtitle, diff --git a/mobile/lib/models/search/search_filter.model.dart b/mobile/lib/models/search/search_filter.model.dart index 598b71ef4e..835e6aff8f 100644 --- a/mobile/lib/models/search/search_filter.model.dart +++ b/mobile/lib/models/search/search_filter.model.dart @@ -1,8 +1,8 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:convert'; +import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/interfaces/person_api.interface.dart'; class SearchLocationFilter { String? country; diff --git a/mobile/lib/models/search/search_result.model.dart b/mobile/lib/models/search/search_result.model.dart index f51353ad61..458a9b4abc 100644 --- a/mobile/lib/models/search/search_result.model.dart +++ b/mobile/lib/models/search/search_result.model.dart @@ -6,7 +6,7 @@ class SearchResult { final List assets; final int? nextPage; - SearchResult({ + const SearchResult({ required this.assets, this.nextPage, }); diff --git a/mobile/lib/models/search/search_result_page_state.model.dart b/mobile/lib/models/search/search_result_page_state.model.dart index 00895c4586..2fd4dcbcd3 100644 --- a/mobile/lib/models/search/search_result_page_state.model.dart +++ b/mobile/lib/models/search/search_result_page_state.model.dart @@ -8,7 +8,7 @@ class SearchResultPageState { final bool isSmart; final List searchResult; - SearchResultPageState({ + const SearchResultPageState({ required this.isLoading, required this.isSuccess, required this.isError, diff --git a/mobile/lib/models/server_info/server_info.model.dart b/mobile/lib/models/server_info/server_info.model.dart index 8a70e13883..aafab56e4c 100644 --- a/mobile/lib/models/server_info/server_info.model.dart +++ b/mobile/lib/models/server_info/server_info.model.dart @@ -13,7 +13,7 @@ class ServerInfo { final bool isNewReleaseAvailable; final String versionMismatchErrorMessage; - ServerInfo({ + const ServerInfo({ required this.serverVersion, required this.latestVersion, required this.serverFeatures, diff --git a/mobile/lib/pages/albums/albums.page.dart b/mobile/lib/pages/albums/albums.page.dart index 9d8ebb7673..3fc628afd3 100644 --- a/mobile/lib/pages/albums/albums.page.dart +++ b/mobile/lib/pages/albums/albums.page.dart @@ -8,13 +8,13 @@ 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/extensions/translate_extensions.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/pages/common/large_leading_tile.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/utils/translation.dart'; import 'package:immich_mobile/widgets/album/album_thumbnail_card.dart'; import 'package:immich_mobile/widgets/common/immich_app_bar.dart'; import 'package:immich_mobile/widgets/common/immich_thumbnail.dart'; @@ -105,7 +105,9 @@ class AlbumsPage extends HookConsumerWidget { color: context.colorScheme.onSurface.withAlpha(0), width: 0, ), - borderRadius: BorderRadius.circular(24), + borderRadius: const BorderRadius.all( + Radius.circular(24), + ), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -230,11 +232,17 @@ class AlbumsPage extends HookConsumerWidget { ), subtitle: sorted[index].ownerId != null ? Text( - '${t('items_count', { - 'count': sorted[index].assetCount, - })} â€ĸ ${sorted[index].ownerId != userId ? t('shared_by_user', { - 'user': sorted[index].ownerName!, - }) : 'owned'.tr()}', + '${'items_count'.t( + context: context, + args: { + 'count': sorted[index].assetCount, + }, + )} â€ĸ ${sorted[index].ownerId != userId ? 'shared_by_user'.t( + context: context, + args: { + 'user': sorted[index].ownerName!, + }, + ) : 'owned'.t(context: context)}', overflow: TextOverflow.ellipsis, style: context.textTheme.bodyMedium?.copyWith( @@ -295,7 +303,9 @@ class QuickFilterButton extends StatelessWidget { ), shape: WidgetStateProperty.all( RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), side: BorderSide( color: context.colorScheme.onSurface.withAlpha(25), width: 1, @@ -328,8 +338,10 @@ class SortButton extends ConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(24), + const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(24), + ), ), ), padding: const WidgetStatePropertyAll( @@ -378,8 +390,10 @@ class SortButton extends ConsumerWidget { : Colors.transparent, ), shape: WidgetStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(24), + const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(24), + ), ), ), ), diff --git a/mobile/lib/pages/backup/backup_album_selection.page.dart b/mobile/lib/pages/backup/backup_album_selection.page.dart index c4124efb52..e51d259969 100644 --- a/mobile/lib/pages/backup/backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/backup_album_selection.page.dart @@ -246,8 +246,10 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { context: context, builder: (BuildContext context) { return AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10), + ), ), elevation: 5, title: Text( diff --git a/mobile/lib/pages/backup/backup_controller.page.dart b/mobile/lib/pages/backup/backup_controller.page.dart index 6cbf172ce5..ca094437ac 100644 --- a/mobile/lib/pages/backup/backup_controller.page.dart +++ b/mobile/lib/pages/backup/backup_controller.page.dart @@ -147,7 +147,9 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Card( shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), side: BorderSide( color: context.colorScheme.outlineVariant, width: 1, diff --git a/mobile/lib/pages/backup/failed_backup_status.page.dart b/mobile/lib/pages/backup/failed_backup_status.page.dart index 551555d75e..167b0f2ebd 100644 --- a/mobile/lib/pages/backup/failed_backup_status.page.dart +++ b/mobile/lib/pages/backup/failed_backup_status.page.dart @@ -42,9 +42,11 @@ class FailedBackupStatusPage extends HookConsumerWidget { vertical: 4, ), child: Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15), // if you need this - side: const BorderSide( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(15), // if you need this + ), + side: BorderSide( color: Colors.black12, width: 1, ), diff --git a/mobile/lib/pages/common/app_log_detail.page.dart b/mobile/lib/pages/common/app_log_detail.page.dart index 1bfea44ba1..b88d6aeb3a 100644 --- a/mobile/lib/pages/common/app_log_detail.page.dart +++ b/mobile/lib/pages/common/app_log_detail.page.dart @@ -60,7 +60,9 @@ class AppLogDetailPage extends HookConsumerWidget { Container( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(15.0), + borderRadius: const BorderRadius.all( + Radius.circular(15.0), + ), ), child: Padding( padding: const EdgeInsets.all(8.0), @@ -99,7 +101,9 @@ class AppLogDetailPage extends HookConsumerWidget { Container( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(15.0), + borderRadius: const BorderRadius.all( + Radius.circular(15.0), + ), ), child: Padding( padding: const EdgeInsets.all(8.0), diff --git a/mobile/lib/pages/common/create_album.page.dart b/mobile/lib/pages/common/create_album.page.dart index f5c6321451..faf1671a9e 100644 --- a/mobile/lib/pages/common/create_album.page.dart +++ b/mobile/lib/pages/common/create_album.page.dart @@ -120,8 +120,10 @@ class CreateAlbumPage extends HookConsumerWidget { alignment: Alignment.centerLeft, padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10), + ), ), backgroundColor: context.colorScheme.surfaceContainerHigh, ), diff --git a/mobile/lib/pages/common/download_panel.dart b/mobile/lib/pages/common/download_panel.dart index 5cc6e5b8d6..cc543c9e4e 100644 --- a/mobile/lib/pages/common/download_panel.dart +++ b/mobile/lib/pages/common/download_panel.dart @@ -90,8 +90,10 @@ class DownloadTaskTile extends StatelessWidget { width: context.width - 32, child: Card( clipBehavior: Clip.antiAlias, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(16), + ), ), child: ListTile( minVerticalPadding: 18, diff --git a/mobile/lib/pages/common/gallery_viewer.page.dart b/mobile/lib/pages/common/gallery_viewer.page.dart index 77b734ce0b..6fdbecced1 100644 --- a/mobile/lib/pages/common/gallery_viewer.page.dart +++ b/mobile/lib/pages/common/gallery_viewer.page.dart @@ -238,7 +238,7 @@ class GalleryViewerPage extends HookConsumerWidget { PhotoViewGalleryPageOptions buildImage(Asset asset) { return PhotoViewGalleryPageOptions( - onDragStart: (_, details, __) { + onDragStart: (_, details, __, ___) { localPosition.value = details.localPosition; }, onDragUpdate: (_, details, __) { @@ -267,7 +267,7 @@ class GalleryViewerPage extends HookConsumerWidget { PhotoViewGalleryPageOptions buildVideo(BuildContext context, Asset asset) { return PhotoViewGalleryPageOptions.customChild( - onDragStart: (_, details, __) => + onDragStart: (_, details, __, ___) => localPosition.value = details.localPosition, onDragUpdate: (_, details, __) => handleSwipeUpDown(details), heroAttributes: _getHeroAttributes(asset), @@ -370,7 +370,7 @@ class GalleryViewerPage extends HookConsumerWidget { ), itemCount: totalAssets.value, scrollDirection: Axis.horizontal, - onPageChanged: (value) { + onPageChanged: (value, _) { final next = currentIndex.value < value ? value + 1 : value - 1; ref.read(hapticFeedbackProvider.notifier).selectionClick(); diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index 05c7606970..6fc4cf5d88 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/settings/advanced_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart'; @@ -11,7 +12,6 @@ import 'package:immich_mobile/widgets/settings/language_settings.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart'; import 'package:immich_mobile/widgets/settings/notification_setting.dart'; import 'package:immich_mobile/widgets/settings/preference_settings/preference_setting.dart'; -import 'package:immich_mobile/routing/router.dart'; enum SettingSection { advanced( @@ -85,12 +85,13 @@ class SettingsPage extends StatelessWidget { centerTitle: false, title: const Text('settings').tr(), ), - body: context.isMobile ? _MobileLayout() : _TabletLayout(), + body: context.isMobile ? const _MobileLayout() : const _TabletLayout(), ); } } class _MobileLayout extends StatelessWidget { + const _MobileLayout(); @override Widget build(BuildContext context) { return ListView( @@ -147,6 +148,7 @@ class _MobileLayout extends StatelessWidget { } class _TabletLayout extends HookWidget { + const _TabletLayout(); @override Widget build(BuildContext context) { final selectedSection = diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index b640aaa3ed..4b7a10d612 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -72,7 +72,7 @@ class SplashScreenPageState extends ConsumerState { return; } - if (context.router.current.name != ShareIntentRoute.name) { + if (context.router.current.name == SplashScreenRoute.name) { context.replaceRoute(const TabControllerRoute()); } diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart new file mode 100644 index 0000000000..d5d53360b0 --- /dev/null +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -0,0 +1,202 @@ +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/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/asset_viewer/scroll_notifier.provider.dart'; +import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; +import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; +import 'package:immich_mobile/providers/tab.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; + +@RoutePage() +class TabShellPage extends ConsumerWidget { + const TabShellPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isScreenLandscape = context.orientation == Orientation.landscape; + + Widget buildIcon({required Widget icon, required bool isProcessing}) { + if (!isProcessing) return icon; + return Stack( + alignment: Alignment.center, + clipBehavior: Clip.none, + children: [ + icon, + Positioned( + right: -18, + child: SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation( + context.primaryColor, + ), + ), + ), + ), + ], + ); + } + + final navigationDestinations = [ + NavigationDestination( + label: 'photos'.tr(), + icon: const Icon( + Icons.photo_library_outlined, + ), + selectedIcon: buildIcon( + isProcessing: false, + icon: Icon( + Icons.photo_library, + color: context.primaryColor, + ), + ), + ), + NavigationDestination( + label: 'search'.tr(), + icon: const Icon( + Icons.search_rounded, + ), + selectedIcon: Icon( + Icons.search, + color: context.primaryColor, + ), + ), + NavigationDestination( + label: 'albums'.tr(), + icon: const Icon( + Icons.photo_album_outlined, + ), + selectedIcon: buildIcon( + isProcessing: false, + icon: Icon( + Icons.photo_album_rounded, + color: context.primaryColor, + ), + ), + ), + NavigationDestination( + label: 'library'.tr(), + icon: const Icon( + Icons.space_dashboard_outlined, + ), + selectedIcon: buildIcon( + isProcessing: false, + icon: Icon( + Icons.space_dashboard_rounded, + color: context.primaryColor, + ), + ), + ), + ]; + + Widget navigationRail(TabsRouter tabsRouter) { + return NavigationRail( + destinations: navigationDestinations + .map( + (e) => NavigationRailDestination( + icon: e.icon, + label: Text(e.label), + selectedIcon: e.selectedIcon, + ), + ) + .toList(), + onDestinationSelected: (index) => + _onNavigationSelected(tabsRouter, index, ref), + selectedIndex: tabsRouter.activeIndex, + labelType: NavigationRailLabelType.all, + groupAlignment: 0.0, + ); + } + + return AutoTabsRouter( + routes: [ + const MainTimelineRoute(), + SearchRoute(), + const DriftAlbumsRoute(), + const DriftLibraryRoute(), + ], + duration: const Duration(milliseconds: 600), + transitionBuilder: (context, child, animation) => FadeTransition( + opacity: animation, + child: child, + ), + builder: (context, child) { + final tabsRouter = AutoTabsRouter.of(context); + final heroedChild = HeroControllerScope( + controller: HeroController(), + child: child, + ); + return PopScope( + canPop: tabsRouter.activeIndex == 0, + onPopInvokedWithResult: (didPop, _) => + !didPop ? tabsRouter.setActiveIndex(0) : null, + child: Scaffold( + resizeToAvoidBottomInset: false, + body: isScreenLandscape + ? Row( + children: [ + navigationRail(tabsRouter), + const VerticalDivider(), + Expanded(child: heroedChild), + ], + ) + : heroedChild, + bottomNavigationBar: _BottomNavigationBar( + tabsRouter: tabsRouter, + destinations: navigationDestinations, + ), + ), + ); + }, + ); + } +} + +void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) { + // On Photos page menu tapped + if (router.activeIndex == 0 && index == 0) { + scrollToTopNotifierProvider.scrollToTop(); + } + + // On Search page tapped + if (router.activeIndex == 1 && index == 1) { + ref.read(searchInputFocusProvider).requestFocus(); + } + + ref.read(hapticFeedbackProvider.notifier).selectionClick(); + router.setActiveIndex(index); + ref.read(tabProvider.notifier).state = TabEnum.values[index]; +} + +class _BottomNavigationBar extends ConsumerWidget { + const _BottomNavigationBar({ + required this.tabsRouter, + required this.destinations, + }); + + final List destinations; + final TabsRouter tabsRouter; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isScreenLandscape = context.orientation == Orientation.landscape; + final isMultiselectEnabled = + ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + + if (isScreenLandscape || isMultiselectEnabled) { + return const SizedBox.shrink(); + } + + return NavigationBar( + selectedIndex: tabsRouter.activeIndex, + onDestinationSelected: (index) => + _onNavigationSelected(tabsRouter, index, ref), + destinations: destinations, + ); + } +} diff --git a/mobile/lib/pages/editing/edit.page.dart b/mobile/lib/pages/editing/edit.page.dart index 39524df024..d37941f4fe 100644 --- a/mobile/lib/pages/editing/edit.page.dart +++ b/mobile/lib/pages/editing/edit.page.dart @@ -124,7 +124,9 @@ class EditImagePage extends ConsumerWidget { ), child: Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(7), + borderRadius: const BorderRadius.all( + Radius.circular(7), + ), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.2), @@ -135,7 +137,9 @@ class EditImagePage extends ConsumerWidget { ], ), child: ClipRRect( - borderRadius: BorderRadius.circular(7), + borderRadius: const BorderRadius.all( + Radius.circular(7), + ), child: Image( image: image.image, fit: BoxFit.contain, @@ -149,7 +153,9 @@ class EditImagePage extends ConsumerWidget { margin: const EdgeInsets.only(bottom: 60, right: 10, left: 10, top: 10), decoration: BoxDecoration( color: context.scaffoldBackgroundColor, - borderRadius: BorderRadius.circular(30), + borderRadius: const BorderRadius.all( + Radius.circular(30), + ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, diff --git a/mobile/lib/pages/editing/filter.page.dart b/mobile/lib/pages/editing/filter.page.dart index f8b1f180df..9af065489d 100644 --- a/mobile/lib/pages/editing/filter.page.dart +++ b/mobile/lib/pages/editing/filter.page.dart @@ -162,13 +162,17 @@ class _FilterButton extends StatelessWidget { width: 80, height: 80, decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), + borderRadius: const BorderRadius.all( + Radius.circular(10), + ), border: isSelected ? Border.all(color: context.primaryColor, width: 3) : null, ), child: ClipRRect( - borderRadius: BorderRadius.circular(10), + borderRadius: const BorderRadius.all( + Radius.circular(10), + ), child: ColorFiltered( colorFilter: filter, child: FittedBox( diff --git a/mobile/lib/pages/library/library.page.dart b/mobile/lib/pages/library/library.page.dart index 37b5e28797..a9ef479e2c 100644 --- a/mobile/lib/pages/library/library.page.dart +++ b/mobile/lib/pages/library/library.page.dart @@ -105,7 +105,9 @@ class QuickAccessButtons extends ConsumerWidget { color: context.colorScheme.onSurface.withAlpha(10), width: 1, ), - borderRadius: BorderRadius.circular(20), + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -240,7 +242,9 @@ class PeopleCollectionCard extends ConsumerWidget { height: size, width: size, decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(30), diff --git a/mobile/lib/pages/library/local_albums.page.dart b/mobile/lib/pages/library/local_albums.page.dart index 5ce6d453ae..9eceaca205 100644 --- a/mobile/lib/pages/library/local_albums.page.dart +++ b/mobile/lib/pages/library/local_albums.page.dart @@ -2,9 +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/extensions/translate_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'; @@ -46,7 +46,10 @@ class LocalAlbumsPage extends HookConsumerWidget { ), ), subtitle: Text( - t('items_count', {'count': albums[index].assetCount}), + 'items_count'.t( + context: context, + args: {'count': albums[index].assetCount}, + ), style: context.textTheme.bodyMedium?.copyWith( color: context.colorScheme.onSurfaceSecondary, ), diff --git a/mobile/lib/pages/library/partner/partner_detail.page.dart b/mobile/lib/pages/library/partner/partner_detail.page.dart index 618d31affa..94e098b973 100644 --- a/mobile/lib/pages/library/partner/partner_detail.page.dart +++ b/mobile/lib/pages/library/partner/partner_detail.page.dart @@ -80,7 +80,9 @@ class PartnerDetailPage extends HookConsumerWidget { color: context.colorScheme.onSurface.withAlpha(10), width: 1, ), - borderRadius: BorderRadius.circular(20), + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), diff --git a/mobile/lib/pages/library/places/places_collection.page.dart b/mobile/lib/pages/library/places/places_collection.page.dart index 5f2dea0dec..b98537d515 100644 --- a/mobile/lib/pages/library/places/places_collection.page.dart +++ b/mobile/lib/pages/library/places/places_collection.page.dart @@ -143,7 +143,9 @@ class PlaceTile extends StatelessWidget { ), ), leading: ClipRRect( - borderRadius: BorderRadius.circular(20), + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), child: CachedNetworkImage( width: 80, height: 80, diff --git a/mobile/lib/pages/search/map/map.page.dart b/mobile/lib/pages/search/map/map.page.dart index 022bf5da5f..9f32b1877b 100644 --- a/mobile/lib/pages/search/map/map.page.dart +++ b/mobile/lib/pages/search/map/map.page.dart @@ -156,7 +156,7 @@ class MapPage extends HookConsumerWidget { } else { // If no asset was previously selected and no new asset is available, close the bottom sheet if (selectedMarker.value == null) { - bottomSheetStreamController.add(MapCloseBottomSheet()); + bottomSheetStreamController.add(const MapCloseBottomSheet()); } selectedMarker.value = null; } diff --git a/mobile/lib/pages/search/search.page.dart b/mobile/lib/pages/search/search.page.dart index 017ced9a19..dba3199fac 100644 --- a/mobile/lib/pages/search/search.page.dart +++ b/mobile/lib/pages/search/search.page.dart @@ -6,9 +6,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/interfaces/person_api.interface.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:immich_mobile/providers/search/paginated_search.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; @@ -511,16 +511,11 @@ class SearchPage extends HookConsumerWidget { search(); } - IconData getSearchPrefixIcon() { - switch (textSearchType.value) { - case TextSearchType.context: - return Icons.image_search_rounded; - case TextSearchType.filename: - return Icons.abc_rounded; - case TextSearchType.description: - return Icons.text_snippet_outlined; - } - } + IconData getSearchPrefixIcon() => switch (textSearchType.value) { + TextSearchType.context => Icons.image_search_rounded, + TextSearchType.filename => Icons.abc_rounded, + TextSearchType.description => Icons.text_snippet_outlined, + }; return Scaffold( resizeToAvoidBottomInset: false, @@ -533,8 +528,10 @@ class SearchPage extends HookConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(24), + const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(24), + ), ), ), padding: const WidgetStatePropertyAll( @@ -631,7 +628,9 @@ class SearchPage extends HookConsumerWidget { color: context.colorScheme.onSurface.withAlpha(0), width: 0, ), - borderRadius: BorderRadius.circular(24), + borderRadius: const BorderRadius.all( + Radius.circular(24), + ), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -823,7 +822,9 @@ class QuickLinkList extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), border: Border.all( color: context.colorScheme.outline.withAlpha(10), width: 1, diff --git a/mobile/lib/platform/native_sync_api.g.dart b/mobile/lib/platform/native_sync_api.g.dart index dd6e545f88..46599eb016 100644 --- a/mobile/lib/platform/native_sync_api.g.dart +++ b/mobile/lib/platform/native_sync_api.g.dart @@ -40,6 +40,7 @@ class PlatformAsset { this.width, this.height, required this.durationInSeconds, + required this.orientation, }); String id; @@ -58,6 +59,8 @@ class PlatformAsset { int durationInSeconds; + int orientation; + List _toList() { return [ id, @@ -68,6 +71,7 @@ class PlatformAsset { width, height, durationInSeconds, + orientation, ]; } @@ -86,6 +90,7 @@ class PlatformAsset { width: result[5] as int?, height: result[6] as int?, durationInSeconds: result[7]! as int, + orientation: result[8]! as int, ); } diff --git a/mobile/lib/presentation/pages/dev/drift_archive.page.dart b/mobile/lib/presentation/pages/dev/drift_archive.page.dart new file mode 100644 index 0000000000..14657f7149 --- /dev/null +++ b/mobile/lib/presentation/pages/dev/drift_archive.page.dart @@ -0,0 +1,33 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +@RoutePage() +class DriftArchivePage extends StatelessWidget { + const DriftArchivePage({super.key}); + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith( + (ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access archive'); + } + + final timelineService = + ref.watch(timelineFactoryProvider).archive(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }, + ), + ], + child: const Timeline(), + ); + } +} diff --git a/mobile/lib/presentation/pages/dev/drift_favorite.page.dart b/mobile/lib/presentation/pages/dev/drift_favorite.page.dart new file mode 100644 index 0000000000..4055ad863b --- /dev/null +++ b/mobile/lib/presentation/pages/dev/drift_favorite.page.dart @@ -0,0 +1,33 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +@RoutePage() +class DriftFavoritePage extends StatelessWidget { + const DriftFavoritePage({super.key}); + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith( + (ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access favorite'); + } + + final timelineService = + ref.watch(timelineFactoryProvider).favorite(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }, + ), + ], + child: const Timeline(), + ); + } +} diff --git a/mobile/lib/presentation/pages/dev/drift_local_album.page.dart b/mobile/lib/presentation/pages/dev/drift_local_album.page.dart new file mode 100644 index 0000000000..f47811b6da --- /dev/null +++ b/mobile/lib/presentation/pages/dev/drift_local_album.page.dart @@ -0,0 +1,116 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.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/extensions/translate_extensions.dart'; +import 'package:immich_mobile/pages/common/large_leading_tile.dart'; +import 'package:immich_mobile/presentation/widgets/images/local_album_thumbnail.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/common/local_album_sliver_app_bar.dart'; + +@RoutePage() +class DriftLocalAlbumsPage extends StatelessWidget { + const DriftLocalAlbumsPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + body: CustomScrollView( + slivers: [ + LocalAlbumsSliverAppBar(), + _AlbumList(), + ], + ), + ); + } +} + +class _AlbumList extends ConsumerWidget { + const _AlbumList(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final albums = ref.watch(localAlbumProvider); + + return albums.when( + loading: () => const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(20.0), + child: CircularProgressIndicator(), + ), + ), + ), + error: (error, stack) => SliverToBoxAdapter( + child: Center( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Text( + 'Error loading albums: $error, stack: $stack', + style: TextStyle( + color: context.colorScheme.error, + ), + ), + ), + ), + ), + data: (albums) { + if (albums.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(20.0), + child: Text('No albums found'), + ), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.all(18.0), + sliver: SliverList.builder( + itemBuilder: (_, index) { + final album = albums[index]; + + return Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: LargeLeadingTile( + leadingPadding: const EdgeInsets.only( + right: 16, + ), + leading: SizedBox( + width: 80, + height: 80, + child: LocalAlbumThumbnail( + albumId: album.id, + ), + ), + title: Text( + album.name, + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + subtitle: Text( + 'items_count'.t( + context: context, + args: {'count': album.assetCount}, + ), + style: context.textTheme.bodyMedium?.copyWith( + color: context.colorScheme.onSurfaceSecondary, + ), + ), + onTap: () => + context.pushRoute(LocalTimelineRoute(albumId: album.id)), + ), + ); + }, + itemCount: albums.length, + ), + ); + }, + ); + } +} diff --git a/mobile/lib/presentation/pages/dev/drift_locked_folder.page.dart b/mobile/lib/presentation/pages/dev/drift_locked_folder.page.dart new file mode 100644 index 0000000000..5ab7c71347 --- /dev/null +++ b/mobile/lib/presentation/pages/dev/drift_locked_folder.page.dart @@ -0,0 +1,33 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +@RoutePage() +class DriftLockedFolderPage extends StatelessWidget { + const DriftLockedFolderPage({super.key}); + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith( + (ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access locked folder'); + } + + final timelineService = + ref.watch(timelineFactoryProvider).lockedFolder(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }, + ), + ], + child: const Timeline(), + ); + } +} diff --git a/mobile/lib/presentation/pages/dev/drift_partner_detail.page.dart b/mobile/lib/presentation/pages/dev/drift_partner_detail.page.dart new file mode 100644 index 0000000000..4b62387cb4 --- /dev/null +++ b/mobile/lib/presentation/pages/dev/drift_partner_detail.page.dart @@ -0,0 +1,29 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; + +@RoutePage() +class DriftPartnerDetailPage extends StatelessWidget { + final String partnerId; + + const DriftPartnerDetailPage({super.key, required this.partnerId}); + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith( + (ref) { + final timelineService = + ref.watch(timelineFactoryProvider).remoteAssets(partnerId); + ref.onDispose(timelineService.dispose); + return timelineService; + }, + ), + ], + child: const Timeline(), + ); + } +} diff --git a/mobile/lib/presentation/pages/dev/drift_recently_taken.page.dart b/mobile/lib/presentation/pages/dev/drift_recently_taken.page.dart new file mode 100644 index 0000000000..e2972fad56 --- /dev/null +++ b/mobile/lib/presentation/pages/dev/drift_recently_taken.page.dart @@ -0,0 +1,35 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +@RoutePage() +class DriftRecentlyTakenPage extends StatelessWidget { + const DriftRecentlyTakenPage({super.key}); + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith( + (ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception( + 'User must be logged in to access recently taken', + ); + } + + final timelineService = + ref.watch(timelineFactoryProvider).remoteAssets(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }, + ), + ], + child: const Timeline(), + ); + } +} diff --git a/mobile/lib/presentation/pages/dev/drift_trash.page.dart b/mobile/lib/presentation/pages/dev/drift_trash.page.dart new file mode 100644 index 0000000000..cbcfe50112 --- /dev/null +++ b/mobile/lib/presentation/pages/dev/drift_trash.page.dart @@ -0,0 +1,33 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +@RoutePage() +class DriftTrashPage extends StatelessWidget { + const DriftTrashPage({super.key}); + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith( + (ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access trash'); + } + + final timelineService = + ref.watch(timelineFactoryProvider).trash(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }, + ), + ], + child: const Timeline(), + ); + } +} diff --git a/mobile/lib/presentation/pages/dev/drift_video.page.dart b/mobile/lib/presentation/pages/dev/drift_video.page.dart new file mode 100644 index 0000000000..488d027177 --- /dev/null +++ b/mobile/lib/presentation/pages/dev/drift_video.page.dart @@ -0,0 +1,33 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +@RoutePage() +class DriftVideoPage extends StatelessWidget { + const DriftVideoPage({super.key}); + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith( + (ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to video'); + } + + final timelineService = + ref.watch(timelineFactoryProvider).video(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }, + ), + ], + child: const Timeline(), + ); + } +} diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index 08ead96e82..566c10c70a 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -5,15 +5,43 @@ import 'package:drift/drift.dart' hide Column; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; final _features = [ + _Feature( + name: 'Selection Mode Timeline', + icon: Icons.developer_mode_rounded, + onTap: (ctx, ref) async { + final user = ref.watch(currentUserProvider); + if (user == null) { + return Future.value(); + } + + final assets = + await ref.read(remoteAssetRepositoryProvider).getSome(user.id); + + final selectedAssets = await ctx.pushRoute>( + DriftAssetSelectionTimelineRoute( + lockedSelectionAssets: assets.toSet(), + ), + ); + + DLog.log( + "Selected ${selectedAssets?.length ?? 0} assets", + ); + + return Future.value(); + }, + ), _Feature( name: 'Sync Local', icon: Icons.photo_album_rounded, @@ -61,10 +89,14 @@ final _features = [ icon: Icons.delete_sweep_rounded, onTap: (_, ref) async { final db = ref.read(driftProvider); - await db.remoteExifEntity.deleteAll(); await db.remoteAssetEntity.deleteAll(); + await db.remoteExifEntity.deleteAll(); await db.remoteAlbumEntity.deleteAll(); + await db.remoteAlbumUserEntity.deleteAll(); await db.remoteAlbumAssetEntity.deleteAll(); + await db.memoryEntity.deleteAll(); + await db.memoryAssetEntity.deleteAll(); + await db.stackEntity.deleteAll(); }, ), _Feature( @@ -90,6 +122,21 @@ final _features = [ } }, ), + _Feature( + name: 'Main Timeline', + icon: Icons.timeline_rounded, + onTap: (ctx, _) => ctx.pushRoute(const TabShellRoute()), + ), + _Feature( + name: 'Video', + icon: Icons.video_collection_outlined, + onTap: (ctx, _) => ctx.pushRoute(const DriftVideoRoute()), + ), + _Feature( + name: 'Recently Taken', + icon: Icons.schedule_outlined, + onTap: (ctx, _) => ctx.pushRoute(const DriftRecentlyTakenRoute()), + ), ]; @RoutePage() diff --git a/mobile/lib/presentation/pages/dev/local_timeline.page.dart b/mobile/lib/presentation/pages/dev/local_timeline.page.dart new file mode 100644 index 0000000000..3a98a81e9e --- /dev/null +++ b/mobile/lib/presentation/pages/dev/local_timeline.page.dart @@ -0,0 +1,29 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; + +@RoutePage() +class LocalTimelinePage extends StatelessWidget { + final String albumId; + + const LocalTimelinePage({super.key, required this.albumId}); + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith( + (ref) { + final timelineService = + ref.watch(timelineFactoryProvider).localAlbum(albumId: albumId); + ref.onDispose(timelineService.dispose); + return timelineService; + }, + ), + ], + child: const Timeline(), + ); + } +} diff --git a/mobile/lib/presentation/pages/dev/main_timeline.page.dart b/mobile/lib/presentation/pages/dev/main_timeline.page.dart new file mode 100644 index 0000000000..9ec8002463 --- /dev/null +++ b/mobile/lib/presentation/pages/dev/main_timeline.page.dart @@ -0,0 +1,32 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/presentation/widgets/memory/memory_lane.widget.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; + +@RoutePage() +class MainTimelinePage extends ConsumerWidget { + const MainTimelinePage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final memoryLaneProvider = ref.watch(driftMemoryFutureProvider); + + return memoryLaneProvider.when( + data: (memories) { + return memories.isEmpty + ? const Timeline() + : Timeline( + topSliverWidget: SliverToBoxAdapter( + key: Key('memory-lane-${memories.first.assets.first.id}'), + child: DriftMemoryLane(memories: memories), + ), + topSliverWidgetHeight: 200, + ); + }, + loading: () => const Timeline(), + error: (error, stackTrace) => const Timeline(), + ); + } +} diff --git a/mobile/lib/presentation/pages/dev/media_stat.page.dart b/mobile/lib/presentation/pages/dev/media_stat.page.dart index 360506e691..e5745fa629 100644 --- a/mobile/lib/presentation/pages/dev/media_stat.page.dart +++ b/mobile/lib/presentation/pages/dev/media_stat.page.dart @@ -6,6 +6,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; class _Stat { const _Stat({required this.name, required this.load}); @@ -16,9 +17,16 @@ class _Stat { class _Summary extends StatelessWidget { final String name; + final Widget? leading; final Future countFuture; + final void Function()? onTap; - const _Summary({required this.name, required this.countFuture}); + const _Summary({ + required this.name, + required this.countFuture, + this.leading, + this.onTap, + }); @override Widget build(BuildContext context) { @@ -32,9 +40,17 @@ class _Summary extends StatelessWidget { } else if (snapshot.hasError) { subtitle = const Icon(Icons.error_rounded); } else { - subtitle = Text('${snapshot.data ?? 0}'); + subtitle = Text( + '${snapshot.data ?? 0}', + style: ctx.textTheme.bodyLarge, + ); } - return ListTile(title: Text(name), trailing: subtitle); + return ListTile( + leading: leading, + title: Text(name), + trailing: subtitle, + onTap: onTap, + ); }, ); } @@ -105,8 +121,12 @@ class LocalMediaSummaryPage extends StatelessWidget { .filter((f) => f.albumId.id.equals(album.id)) .count(); return _Summary( + leading: const Icon(Icons.photo_album_rounded), name: album.name, countFuture: countFuture, + onTap: () => context.router.push( + LocalTimelineRoute(albumId: album.id), + ), ); }, itemCount: albums.length, @@ -134,6 +154,18 @@ final _remoteStats = [ name: 'Remote Albums', load: (db) => db.managers.remoteAlbumEntity.count(), ), + _Stat( + name: 'Memories', + load: (db) => db.managers.memoryEntity.count(), + ), + _Stat( + name: 'Memories Assets', + load: (db) => db.managers.memoryAssetEntity.count(), + ), + _Stat( + name: 'Stacks', + load: (db) => db.managers.stackEntity.count(), + ), ]; @RoutePage() @@ -147,6 +179,7 @@ class RemoteMediaSummaryPage extends StatelessWidget { body: Consumer( builder: (ctx, ref, __) { final db = ref.watch(driftProvider); + final albumsFuture = ref.watch(remoteAlbumRepository).getAll(); return CustomScrollView( slivers: [ @@ -158,6 +191,49 @@ class RemoteMediaSummaryPage extends StatelessWidget { }, itemCount: _remoteStats.length, ), + SliverToBoxAdapter( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Divider(), + Padding( + padding: const EdgeInsets.only(left: 15), + child: Text( + "Album summary", + style: ctx.textTheme.titleMedium, + ), + ), + ], + ), + ), + FutureBuilder( + future: albumsFuture, + builder: (_, snap) { + final albums = snap.data ?? []; + if (albums.isEmpty) { + return const SliverToBoxAdapter(child: SizedBox.shrink()); + } + + albums.sortBy((a) => a.name); + return SliverList.builder( + itemBuilder: (_, index) { + final album = albums[index]; + final countFuture = db.managers.remoteAlbumAssetEntity + .filter((f) => f.albumId.id.equals(album.id)) + .count(); + return _Summary( + leading: const Icon(Icons.photo_album_rounded), + name: album.name, + countFuture: countFuture, + onTap: () => context.router.push( + RemoteTimelineRoute(albumId: album.id), + ), + ); + }, + itemCount: albums.length, + ); + }, + ), ], ); }, diff --git a/mobile/lib/presentation/pages/dev/remote_timeline.page.dart b/mobile/lib/presentation/pages/dev/remote_timeline.page.dart new file mode 100644 index 0000000000..6568f0f74f --- /dev/null +++ b/mobile/lib/presentation/pages/dev/remote_timeline.page.dart @@ -0,0 +1,30 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; + +@RoutePage() +class RemoteTimelinePage extends StatelessWidget { + final String albumId; + + const RemoteTimelinePage({super.key, required this.albumId}); + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith( + (ref) { + final timelineService = ref + .watch(timelineFactoryProvider) + .remoteAlbum(albumId: albumId); + ref.onDispose(timelineService.dispose); + return timelineService; + }, + ), + ], + child: const Timeline(), + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_album.page.dart b/mobile/lib/presentation/pages/drift_album.page.dart new file mode 100644 index 0000000000..3298f12b65 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_album.page.dart @@ -0,0 +1,767 @@ +import 'dart:async'; +import 'dart:math'; + +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/domain/models/album/album.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/models/albums/album_search.model.dart'; +import 'package:immich_mobile/pages/common/large_leading_tile.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/utils/remote_album.utils.dart'; +import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart'; +import 'package:immich_mobile/widgets/common/search_field.dart'; + +@RoutePage() +class DriftAlbumsPage extends ConsumerStatefulWidget { + const DriftAlbumsPage({super.key}); + + @override + ConsumerState createState() => _DriftAlbumsPageState(); +} + +class _DriftAlbumsPageState extends ConsumerState { + bool isGrid = false; + final searchController = TextEditingController(); + QuickFilterMode filterMode = QuickFilterMode.all; + final searchFocusNode = FocusNode(); + + @override + void initState() { + super.initState(); + + // Load albums when component mounts + WidgetsBinding.instance.addPostFrameCallback((_) { + ref.read(remoteAlbumProvider.notifier).getAll(); + }); + + searchController.addListener(() { + onSearch(searchController.text, filterMode); + }); + } + + void onSearch(String searchTerm, QuickFilterMode sortMode) { + final userId = ref.watch(currentUserProvider)?.id; + ref + .read(remoteAlbumProvider.notifier) + .searchAlbums(searchTerm, userId, sortMode); + } + + Future onRefresh() async { + await ref.read(remoteAlbumProvider.notifier).refresh(); + } + + void toggleViewMode() { + setState(() { + isGrid = !isGrid; + }); + } + + void changeFilter(QuickFilterMode sortMode) { + setState(() { + filterMode = sortMode; + }); + } + + void clearSearch() { + setState(() { + filterMode = QuickFilterMode.all; + searchController.clear(); + ref.read(remoteAlbumProvider.notifier).clearSearch(); + }); + } + + @override + void dispose() { + searchController.dispose(); + searchFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final albumState = ref.watch(remoteAlbumProvider); + final albums = albumState.filteredAlbums; + final isLoading = albumState.isLoading; + final error = albumState.error; + final userId = ref.watch(currentUserProvider)?.id; + + return RefreshIndicator( + onRefresh: onRefresh, + child: CustomScrollView( + slivers: [ + const ImmichSliverAppBar(), + _SearchBar( + searchController: searchController, + searchFocusNode: searchFocusNode, + onSearch: onSearch, + filterMode: filterMode, + onClearSearch: clearSearch, + ), + _QuickFilterButtonRow( + filterMode: filterMode, + onChangeFilter: changeFilter, + onSearch: onSearch, + searchController: searchController, + ), + _QuickSortAndViewMode( + isGrid: isGrid, + onToggleViewMode: toggleViewMode, + ), + isGrid + ? _AlbumGrid( + albums: albums, + userId: userId, + isLoading: isLoading, + error: error, + ) + : _AlbumList( + albums: albums, + userId: userId, + isLoading: isLoading, + error: error, + ), + ], + ), + ); + } +} + +class _SortButton extends ConsumerStatefulWidget { + const _SortButton(); + + @override + ConsumerState<_SortButton> createState() => _SortButtonState(); +} + +class _SortButtonState extends ConsumerState<_SortButton> { + RemoteAlbumSortMode albumSortOption = RemoteAlbumSortMode.lastModified; + bool albumSortIsReverse = true; + + void onMenuTapped(RemoteAlbumSortMode sortMode) { + final selected = albumSortOption == sortMode; + // Switch direction + if (selected) { + setState(() { + albumSortIsReverse = !albumSortIsReverse; + }); + ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums( + sortMode, + isReverse: albumSortIsReverse, + ); + } else { + setState(() { + albumSortOption = sortMode; + }); + ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums( + sortMode, + isReverse: albumSortIsReverse, + ); + } + } + + @override + Widget build(BuildContext context) { + return MenuAnchor( + style: MenuStyle( + elevation: const WidgetStatePropertyAll(1), + shape: WidgetStateProperty.all( + const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(24), + ), + ), + ), + padding: const WidgetStatePropertyAll( + EdgeInsets.all(4), + ), + ), + consumeOutsideTap: true, + menuChildren: RemoteAlbumSortMode.values + .map( + (sortMode) => MenuItemButton( + leadingIcon: albumSortOption == sortMode + ? albumSortIsReverse + ? Icon( + Icons.keyboard_arrow_down, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : Icon( + Icons.keyboard_arrow_up_rounded, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : const Icon(Icons.abc, color: Colors.transparent), + onPressed: () => onMenuTapped(sortMode), + style: ButtonStyle( + padding: WidgetStateProperty.all( + const EdgeInsets.fromLTRB(16, 16, 32, 16), + ), + backgroundColor: WidgetStateProperty.all( + albumSortOption == sortMode + ? context.colorScheme.primary + : Colors.transparent, + ), + shape: WidgetStateProperty.all( + const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(24), + ), + ), + ), + ), + child: Text( + sortMode.key.t(context: context), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface.withAlpha(185), + ), + ), + ), + ) + .toList(), + builder: (context, controller, child) { + return GestureDetector( + onTap: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 5), + child: albumSortIsReverse + ? const Icon( + Icons.keyboard_arrow_down, + ) + : const Icon( + Icons.keyboard_arrow_up_rounded, + ), + ), + Text( + albumSortOption.key.t(context: context), + style: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.onSurface.withAlpha(225), + ), + ), + ], + ), + ); + }, + ); + } +} + +class _SearchBar extends StatelessWidget { + const _SearchBar({ + required this.searchController, + required this.searchFocusNode, + required this.onSearch, + required this.filterMode, + required this.onClearSearch, + }); + + final TextEditingController searchController; + final FocusNode searchFocusNode; + final void Function(String, QuickFilterMode) onSearch; + final QuickFilterMode filterMode; + final VoidCallback onClearSearch; + + @override + Widget build(BuildContext context) { + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + sliver: SliverToBoxAdapter( + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: context.colorScheme.onSurface.withAlpha(0), + width: 0, + ), + borderRadius: const BorderRadius.all( + Radius.circular(24), + ), + gradient: LinearGradient( + colors: [ + context.colorScheme.primary.withValues(alpha: 0.075), + context.colorScheme.primary.withValues(alpha: 0.09), + context.colorScheme.primary.withValues(alpha: 0.075), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + transform: const GradientRotation(0.5 * pi), + ), + ), + child: SearchField( + autofocus: false, + contentPadding: const EdgeInsets.all(16), + hintText: 'search_albums'.tr(), + prefixIcon: const Icon(Icons.search_rounded), + suffixIcon: searchController.text.isNotEmpty + ? IconButton( + icon: const Icon(Icons.clear_rounded), + onPressed: onClearSearch, + ) + : null, + controller: searchController, + onChanged: (_) => onSearch(searchController.text, filterMode), + focusNode: searchFocusNode, + onTapOutside: (_) => searchFocusNode.unfocus(), + ), + ), + ), + ); + } +} + +class _QuickFilterButtonRow extends StatelessWidget { + const _QuickFilterButtonRow({ + required this.filterMode, + required this.onChangeFilter, + required this.onSearch, + required this.searchController, + }); + + final QuickFilterMode filterMode; + final void Function(QuickFilterMode) onChangeFilter; + final void Function(String, QuickFilterMode) onSearch; + final TextEditingController searchController; + + @override + Widget build(BuildContext context) { + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: Wrap( + spacing: 4, + runSpacing: 4, + children: [ + _QuickFilterButton( + label: 'all'.tr(), + isSelected: filterMode == QuickFilterMode.all, + onTap: () { + onChangeFilter(QuickFilterMode.all); + onSearch(searchController.text, QuickFilterMode.all); + }, + ), + _QuickFilterButton( + label: 'shared_with_me'.tr(), + isSelected: filterMode == QuickFilterMode.sharedWithMe, + onTap: () { + onChangeFilter(QuickFilterMode.sharedWithMe); + onSearch( + searchController.text, + QuickFilterMode.sharedWithMe, + ); + }, + ), + _QuickFilterButton( + label: 'my_albums'.tr(), + isSelected: filterMode == QuickFilterMode.myAlbums, + onTap: () { + onChangeFilter(QuickFilterMode.myAlbums); + onSearch( + searchController.text, + QuickFilterMode.myAlbums, + ); + }, + ), + ], + ), + ), + ); + } +} + +class _QuickFilterButton extends StatelessWidget { + const _QuickFilterButton({ + required this.isSelected, + required this.onTap, + required this.label, + }); + + final bool isSelected; + final VoidCallback onTap; + final String label; + + @override + Widget build(BuildContext context) { + return TextButton( + onPressed: onTap, + style: ButtonStyle( + backgroundColor: WidgetStateProperty.all( + isSelected ? context.colorScheme.primary : Colors.transparent, + ), + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), + side: BorderSide( + color: context.colorScheme.onSurface.withAlpha(25), + width: 1, + ), + ), + ), + ), + child: Text( + label, + style: TextStyle( + color: isSelected + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + fontSize: 14, + ), + ), + ); + } +} + +class _QuickSortAndViewMode extends StatelessWidget { + const _QuickSortAndViewMode({ + required this.isGrid, + required this.onToggleViewMode, + }); + + final bool isGrid; + final VoidCallback onToggleViewMode; + + @override + Widget build(BuildContext context) { + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const _SortButton(), + IconButton( + icon: Icon( + isGrid ? Icons.view_list_outlined : Icons.grid_view_outlined, + size: 24, + ), + onPressed: onToggleViewMode, + ), + ], + ), + ), + ); + } +} + +class _AlbumList extends StatelessWidget { + const _AlbumList({ + required this.isLoading, + required this.error, + required this.albums, + required this.userId, + }); + + final bool isLoading; + final String? error; + final List albums; + final String? userId; + + @override + Widget build(BuildContext context) { + if (isLoading) { + return const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(20.0), + child: CircularProgressIndicator(), + ), + ), + ); + } + + if (error != null) { + return SliverToBoxAdapter( + child: Center( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Text( + 'Error loading albums: $error', + style: TextStyle( + color: context.colorScheme.error, + ), + ), + ), + ), + ); + } + + if (albums.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(20.0), + child: Text('No albums found'), + ), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + sliver: SliverList.builder( + itemBuilder: (_, index) { + final album = albums[index]; + + return Padding( + padding: const EdgeInsets.only( + bottom: 8.0, + ), + child: LargeLeadingTile( + title: Text( + album.name, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + subtitle: Text( + '${'items_count'.t( + context: context, + args: { + 'count': album.assetCount, + }, + )} â€ĸ ${album.ownerId != userId ? 'shared_by_user'.t( + context: context, + args: { + 'user': album.ownerName, + }, + ) : 'owned'.t(context: context)}', + overflow: TextOverflow.ellipsis, + style: context.textTheme.bodyMedium?.copyWith( + color: context.colorScheme.onSurfaceSecondary, + ), + ), + onTap: () => context.router.push( + RemoteTimelineRoute(albumId: album.id), + ), + leadingPadding: const EdgeInsets.only( + right: 16, + ), + leading: album.thumbnailAssetId != null + ? ClipRRect( + borderRadius: const BorderRadius.all( + Radius.circular(15), + ), + child: SizedBox( + width: 80, + height: 80, + child: Thumbnail( + remoteId: album.thumbnailAssetId, + ), + ), + ) + : const SizedBox( + width: 80, + height: 80, + child: Icon( + Icons.photo_album_rounded, + size: 40, + color: Colors.grey, + ), + ), + ), + ); + }, + itemCount: albums.length, + ), + ); + } +} + +class _AlbumGrid extends StatelessWidget { + const _AlbumGrid({ + required this.albums, + required this.userId, + required this.isLoading, + required this.error, + }); + + final List albums; + final String? userId; + final bool isLoading; + final String? error; + + @override + Widget build(BuildContext context) { + if (isLoading) { + return const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(20.0), + child: CircularProgressIndicator(), + ), + ), + ); + } + + if (error != null) { + return SliverToBoxAdapter( + child: Center( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Text( + 'Error loading albums: $error', + style: TextStyle( + color: context.colorScheme.error, + ), + ), + ), + ), + ); + } + + if (albums.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(20.0), + child: Text('No albums found'), + ), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + sliver: SliverGrid( + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 250, + mainAxisSpacing: 4, + crossAxisSpacing: 4, + childAspectRatio: .7, + ), + delegate: SliverChildBuilderDelegate( + (context, index) { + final album = albums[index]; + return _GridAlbumCard( + album: album, + userId: userId, + ); + }, + childCount: albums.length, + ), + ), + ); + } +} + +class _GridAlbumCard extends StatelessWidget { + const _GridAlbumCard({ + required this.album, + required this.userId, + }); + + final Album album; + final String? userId; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => context.router.push( + RemoteTimelineRoute(albumId: album.id), + ), + child: Card( + elevation: 0, + color: context.colorScheme.surfaceBright, + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all( + Radius.circular(16), + ), + side: BorderSide( + color: context.colorScheme.onSurface.withAlpha(25), + width: 1, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 2, + child: ClipRRect( + borderRadius: const BorderRadius.vertical( + top: Radius.circular(15), + ), + child: SizedBox( + width: double.infinity, + child: album.thumbnailAssetId != null + ? Thumbnail( + remoteId: album.thumbnailAssetId, + ) + : Container( + color: context.colorScheme.surfaceContainerHighest, + child: const Icon( + Icons.photo_album_rounded, + size: 40, + color: Colors.grey, + ), + ), + ), + ), + ), + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + album.name, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + Text( + '${'items_count'.t( + context: context, + args: { + 'count': album.assetCount, + }, + )} â€ĸ ${album.ownerId != userId ? 'shared_by_user'.t( + context: context, + args: { + 'user': album.ownerName, + }, + ) : 'owned'.t(context: context)}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: context.textTheme.labelMedium?.copyWith( + color: context.colorScheme.onSurfaceSecondary, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart b/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart new file mode 100644 index 0000000000..df6211c338 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart @@ -0,0 +1,50 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +@RoutePage() +class DriftAssetSelectionTimelinePage extends ConsumerWidget { + final Set lockedSelectionAssets; + const DriftAssetSelectionTimelinePage({ + super.key, + this.lockedSelectionAssets = const {}, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [ + multiSelectProvider.overrideWith( + () => MultiSelectNotifier( + MultiSelectState( + selectedAssets: {}, + lockedSelectionAssets: lockedSelectionAssets, + forceEnable: true, + ), + ), + ), + timelineServiceProvider.overrideWith( + (ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception( + 'User must be logged in to access recently taken', + ); + } + + final timelineService = + ref.watch(timelineFactoryProvider).remoteAssets(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }, + ), + ], + child: const Timeline(), + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_library.page.dart b/mobile/lib/presentation/pages/drift_library.page.dart new file mode 100644 index 0000000000..0efa8040e7 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_library.page.dart @@ -0,0 +1,516 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/images/local_album_thumbnail.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/partner.provider.dart'; +import 'package:immich_mobile/providers/search/people.provider.dart'; +import 'package:immich_mobile/providers/server_info.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/immich_sliver_app_bar.dart'; +import 'package:immich_mobile/widgets/common/user_avatar.dart'; +import 'package:immich_mobile/widgets/map/map_thumbnail.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +@RoutePage() +class DriftLibraryPage extends ConsumerWidget { + const DriftLibraryPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return const Scaffold( + body: CustomScrollView( + slivers: [ + ImmichSliverAppBar(), + _ActionButtonGrid(), + _CollectionCards(), + _QuickAccessButtonList(), + ], + ), + ); + } +} + +class _ActionButtonGrid extends ConsumerWidget { + const _ActionButtonGrid(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isTrashEnable = ref.watch( + serverInfoProvider.select((state) => state.serverFeatures.trash), + ); + + return SliverPadding( + padding: const EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 12), + sliver: SliverToBoxAdapter( + child: Column( + children: [ + Row( + children: [ + _ActionButton( + icon: Icons.favorite_outline_rounded, + onTap: () => context.pushRoute(const DriftFavoriteRoute()), + label: 'favorites'.t(context: context), + ), + const SizedBox(width: 8), + _ActionButton( + icon: Icons.archive_outlined, + onTap: () => context.pushRoute(const DriftArchiveRoute()), + label: 'archived'.t(context: context), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + _ActionButton( + icon: Icons.link_outlined, + onTap: () => context.pushRoute(const SharedLinkRoute()), + label: 'shared_links'.t(context: context), + ), + isTrashEnable + ? const SizedBox(width: 8) + : const SizedBox.shrink(), + isTrashEnable + ? _ActionButton( + icon: Icons.delete_outline_rounded, + onTap: () => context.pushRoute(const DriftTrashRoute()), + label: 'trash'.t(context: context), + ) + : const SizedBox.shrink(), + ], + ), + ], + ), + ), + ); + } +} + +class _ActionButton extends StatelessWidget { + const _ActionButton({ + required this.icon, + required this.onTap, + required this.label, + }); + + final IconData icon; + final VoidCallback onTap; + final String label; + + @override + Widget build(BuildContext context) { + return Expanded( + child: FilledButton.icon( + onPressed: onTap, + label: Padding( + padding: const EdgeInsets.only(left: 4.0), + child: Text( + label, + style: TextStyle( + color: context.colorScheme.onSurface, + fontSize: 15, + ), + ), + ), + style: FilledButton.styleFrom( + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + backgroundColor: context.colorScheme.surfaceContainerLow, + alignment: Alignment.centerLeft, + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(25)), + side: BorderSide( + color: context.colorScheme.onSurface.withAlpha(10), + width: 1, + ), + ), + ), + icon: Icon( + icon, + color: context.primaryColor, + ), + ), + ); + } +} + +class _CollectionCards extends StatelessWidget { + const _CollectionCards(); + + @override + Widget build(BuildContext context) { + return const SliverPadding( + padding: EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + _PeopleCollectionCard(), + _PlacesCollectionCard(), + _LocalAlbumsCollectionCard(), + ], + ), + ), + ); + } +} + +class _PeopleCollectionCard extends ConsumerWidget { + const _PeopleCollectionCard(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final people = ref.watch(getAllPeopleProvider); + + return LayoutBuilder( + builder: (context, constraints) { + final isTablet = constraints.maxWidth > 600; + final widthFactor = isTablet ? 0.25 : 0.5; + final size = context.width * widthFactor - 20.0; + + return GestureDetector( + onTap: () => context.pushRoute(const PeopleCollectionRoute()), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: size, + width: size, + 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: people.widgetWhen( + onLoading: () => const Center( + child: CircularProgressIndicator(), + ), + onData: (people) { + return GridView.count( + crossAxisCount: 2, + padding: const EdgeInsets.all(12), + crossAxisSpacing: 8, + mainAxisSpacing: 8, + physics: const NeverScrollableScrollPhysics(), + children: people.take(4).map((person) { + return CircleAvatar( + backgroundImage: NetworkImage( + getFaceThumbnailUrl(person.id), + headers: ApiService.getRequestHeaders(), + ), + ); + }).toList(), + ); + }, + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'people'.t(context: context), + style: context.textTheme.titleSmall?.copyWith( + color: context.colorScheme.onSurface, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + ); + }, + ); + } +} + +class _PlacesCollectionCard extends StatelessWidget { + const _PlacesCollectionCard(); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + final isTablet = constraints.maxWidth > 600; + final widthFactor = isTablet ? 0.25 : 0.5; + final size = context.width * widthFactor - 20.0; + + return GestureDetector( + onTap: () => context.pushRoute( + PlacesCollectionRoute( + currentLocation: null, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: size, + width: size, + 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, + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'places'.t(), + style: context.textTheme.titleSmall?.copyWith( + color: context.colorScheme.onSurface, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + ); + }, + ); + } +} + +class _LocalAlbumsCollectionCard extends ConsumerWidget { + const _LocalAlbumsCollectionCard(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final albums = ref.watch(localAlbumProvider); + + return LayoutBuilder( + builder: (context, constraints) { + final isTablet = constraints.maxWidth > 600; + final widthFactor = isTablet ? 0.25 : 0.5; + final size = context.width * widthFactor - 20.0; + + return GestureDetector( + onTap: () => context.pushRoute(const DriftLocalAlbumsRoute()), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: size, + width: size, + 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.when( + data: (data) { + return data.take(4).map((album) { + return LocalAlbumThumbnail( + albumId: album.id, + ); + }).toList(); + }, + error: (error, _) { + return [ + Center( + child: Text('Error: $error'), + ), + ]; + }, + loading: () { + return [ + const Center( + child: CircularProgressIndicator(), + ), + ]; + }, + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'on_this_device'.t(context: context), + style: context.textTheme.titleSmall?.copyWith( + color: context.colorScheme.onSurface, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + ); + }, + ); + } +} + +class _QuickAccessButtonList extends ConsumerWidget { + const _QuickAccessButtonList(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final partners = ref.watch(partnerSharedWithProvider); + + return SliverPadding( + padding: const EdgeInsets.only(left: 16, top: 12, right: 16, bottom: 32), + sliver: SliverToBoxAdapter( + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: context.colorScheme.onSurface.withAlpha(10), + width: 1, + ), + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), + gradient: LinearGradient( + colors: [ + context.colorScheme.primary.withAlpha(10), + context.colorScheme.primary.withAlpha(15), + context.colorScheme.primary.withAlpha(20), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: ListView( + shrinkWrap: true, + padding: const EdgeInsets.all(0), + physics: const NeverScrollableScrollPhysics(), + children: [ + ListTile( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: const Radius.circular(20), + topRight: const Radius.circular(20), + bottomLeft: Radius.circular(partners.isEmpty ? 20 : 0), + bottomRight: Radius.circular(partners.isEmpty ? 20 : 0), + ), + ), + leading: const Icon( + Icons.folder_outlined, + size: 26, + ), + title: Text( + 'folders'.t(context: context), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w500, + ), + ), + onTap: () => context.pushRoute(FolderRoute()), + ), + ListTile( + leading: const Icon( + Icons.lock_outline_rounded, + size: 26, + ), + title: Text( + 'locked_folder'.t(context: context), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w500, + ), + ), + // TODO: PIN code is needed + onTap: () => context.pushRoute(const DriftLockedFolderRoute()), + ), + ListTile( + leading: const Icon( + Icons.group_outlined, + size: 26, + ), + title: Text( + 'partners'.t(context: context), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w500, + ), + ), + onTap: () => context.pushRoute(const PartnerRoute()), + ), + _PartnerList(partners: partners), + ], + ), + ), + ), + ); + } +} + +class _PartnerList extends StatelessWidget { + const _PartnerList({required this.partners}); + + final List partners; + + @override + Widget build(BuildContext context) { + return ListView.builder( + padding: const EdgeInsets.all(0), + physics: const NeverScrollableScrollPhysics(), + itemCount: partners.length, + shrinkWrap: true, + itemBuilder: (context, index) { + final partner = partners[index]; + final isLastItem = index == partners.length - 1; + return ListTile( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(isLastItem ? 20 : 0), + bottomRight: Radius.circular(isLastItem ? 20 : 0), + ), + ), + contentPadding: const EdgeInsets.only( + left: 12.0, + right: 18.0, + ), + leading: userAvatar(context, partner, radius: 16), + title: const Text( + "partner_list_user_photos", + style: TextStyle( + fontWeight: FontWeight.w500, + ), + ).t(context: context, args: {'user': partner.name}), + onTap: () => + context.pushRoute(DriftPartnerDetailRoute(partnerId: partner.id)), + ); + }, + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_memory.page.dart b/mobile/lib/presentation/pages/drift_memory.page.dart new file mode 100644 index 0000000000..7da2d1a4c7 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_memory.page.dart @@ -0,0 +1,394 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/memory/memory_bottom_info.widget.dart'; +import 'package:immich_mobile/presentation/widgets/memory/memory_card.widget.dart'; +import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; +import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/widgets/memories/memory_epilogue.dart'; +import 'package:immich_mobile/widgets/memories/memory_progress_indicator.dart'; + +/// Expects [currentAssetNotifier] to be set before navigating to this page +@RoutePage() +class DriftMemoryPage extends HookConsumerWidget { + final List memories; + final int memoryIndex; + + const DriftMemoryPage({ + required this.memories, + required this.memoryIndex, + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final currentMemory = useState(memories[memoryIndex]); + final currentAssetPage = useState(0); + final currentMemoryIndex = useState(memoryIndex); + final assetProgress = useState( + "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}", + ); + const bgColor = Colors.black; + final currentAsset = useState(null); + + /// The list of all of the asset page controllers + final memoryAssetPageControllers = + List.generate(memories.length, (i) => usePageController()); + + /// The main vertically scrolling page controller with each list of memories + final memoryPageController = usePageController(initialPage: memoryIndex); + + useEffect(() { + // Memories is an immersive activity + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); + return () { + // Clean up to normal edge to edge when we are done + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + }; + }); + + toNextMemory() { + memoryPageController.nextPage( + duration: const Duration(milliseconds: 500), + curve: Curves.easeIn, + ); + } + + void toPreviousMemory() { + if (currentMemoryIndex.value > 0) { + // Move to the previous memory page + memoryPageController.previousPage( + duration: const Duration(milliseconds: 500), + curve: Curves.easeIn, + ); + + // Wait for the next frame to ensure the page is built + SchedulerBinding.instance.addPostFrameCallback((_) { + final previousIndex = currentMemoryIndex.value - 1; + final previousMemoryController = + memoryAssetPageControllers[previousIndex]; + + // Ensure the controller is attached + if (previousMemoryController.hasClients) { + previousMemoryController + .jumpToPage(memories[previousIndex].assets.length - 1); + } else { + // Wait for the next frame until it is attached + SchedulerBinding.instance.addPostFrameCallback((_) { + if (previousMemoryController.hasClients) { + previousMemoryController + .jumpToPage(memories[previousIndex].assets.length - 1); + } + }); + } + }); + } + } + + toNextAsset(int currentAssetIndex) { + if (currentAssetIndex + 1 < currentMemory.value.assets.length) { + // Go to the next asset + PageController controller = + memoryAssetPageControllers[currentMemoryIndex.value]; + + controller.nextPage( + curve: Curves.easeInOut, + duration: const Duration(milliseconds: 500), + ); + } else { + // Go to the next memory since we are at the end of our assets + toNextMemory(); + } + } + + toPreviousAsset(int currentAssetIndex) { + if (currentAssetIndex > 0) { + // Go to the previous asset + PageController controller = + memoryAssetPageControllers[currentMemoryIndex.value]; + + controller.previousPage( + curve: Curves.easeInOut, + duration: const Duration(milliseconds: 500), + ); + } else { + // Go to the previous memory since we are at the end of our assets + toPreviousMemory(); + } + } + + updateProgressText() { + assetProgress.value = + "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"; + } + + /// Downloads and caches the image for the asset at this [currentMemory]'s index + precacheAsset(int index) async { + // Guard index out of range + if (index < 0) { + return; + } + + // Context might be removed due to popping out of Memory Lane during Scroll handling + if (!context.mounted) { + return; + } + + late RemoteAsset asset; + if (index < currentMemory.value.assets.length) { + // Uses the next asset in this current memory + asset = currentMemory.value.assets[index]; + } else { + // Precache the first asset in the next memory if available + final currentMemoryIndex = memories.indexOf(currentMemory.value); + + // Guard no memory found + if (currentMemoryIndex == -1) { + return; + } + + final nextMemoryIndex = currentMemoryIndex + 1; + // Guard no next memory + if (nextMemoryIndex >= memories.length) { + return; + } + + // Get the first asset from the next memory + asset = memories[nextMemoryIndex].assets.first; + } + + // Precache the asset + final size = MediaQuery.sizeOf(context); + await precacheImage( + getFullImageProvider( + asset, + size: Size(size.width, size.height), + ), + context, + size: size, + ); + } + + // Precache the next page right away if we are on the first page + if (currentAssetPage.value == 0) { + Future.delayed(const Duration(milliseconds: 200)) + .then((_) => precacheAsset(1)); + } + + Future onAssetChanged(int otherIndex) async { + ref.read(hapticFeedbackProvider.notifier).selectionClick(); + currentAssetPage.value = otherIndex; + updateProgressText(); + + // Wait for page change animation to finish + await Future.delayed(const Duration(milliseconds: 400)); + // And then precache the next asset + await precacheAsset(otherIndex + 1); + + final asset = currentMemory.value.assets[otherIndex]; + currentAsset.value = asset; + ref.read(currentAssetNotifier.notifier).setAsset(asset); + // if (asset.isVideo || asset.isMotionPhoto) { + if (asset.isVideo) { + ref.read(videoPlaybackValueProvider.notifier).reset(); + } + } + + /* Notification listener is used instead of OnPageChanged callback since OnPageChanged is called + * when the page in the **center** of the viewer changes. We want to reset currentAssetPage only when the final + * page during the end of scroll is different than the current page + */ + return NotificationListener( + onNotification: (ScrollNotification notification) { + // Calculate OverScroll manually using the number of pixels away from maxScrollExtent + // maxScrollExtend contains the sum of horizontal pixels of all assets for depth = 1 + // or sum of vertical pixels of all memories for depth = 0 + if (notification is ScrollUpdateNotification) { + final isEpiloguePage = + (memoryPageController.page?.floor() ?? 0) >= memories.length; + + final offset = notification.metrics.pixels; + if (isEpiloguePage && + (offset > notification.metrics.maxScrollExtent + 150)) { + context.maybePop(); + return true; + } + } + + return false; + }, + child: Scaffold( + backgroundColor: bgColor, + body: SafeArea( + child: PageView.builder( + physics: const BouncingScrollPhysics( + parent: AlwaysScrollableScrollPhysics(), + ), + scrollDirection: Axis.vertical, + controller: memoryPageController, + onPageChanged: (pageNumber) { + ref.read(hapticFeedbackProvider.notifier).mediumImpact(); + if (pageNumber < memories.length) { + currentMemoryIndex.value = pageNumber; + currentMemory.value = memories[pageNumber]; + } + + currentAssetPage.value = 0; + + updateProgressText(); + }, + itemCount: memories.length + 1, + itemBuilder: (context, mIndex) { + // Build last page + if (mIndex == memories.length) { + return MemoryEpilogue( + onStartOver: () => memoryPageController.animateToPage( + 0, + duration: const Duration(seconds: 1), + curve: Curves.easeInOut, + ), + ); + } + + final yearsAgo = DateTime.now().year - memories[mIndex].data.year; + final title = 'years_ago'.t( + context: context, + args: { + 'years': yearsAgo.toString(), + }, + ); + // Build horizontal page + final assetController = memoryAssetPageControllers[mIndex]; + return Column( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 24.0, + right: 24.0, + top: 8.0, + bottom: 2.0, + ), + child: AnimatedBuilder( + animation: assetController, + builder: (context, child) { + double value = 0.0; + if (assetController.hasClients) { + // We can only access [page] if this has clients + value = assetController.page ?? 0; + } + return MemoryProgressIndicator( + ticks: memories[mIndex].assets.length, + value: (value + 1) / memories[mIndex].assets.length, + ); + }, + ), + ), + Expanded( + child: Stack( + children: [ + PageView.builder( + physics: const BouncingScrollPhysics( + parent: AlwaysScrollableScrollPhysics(), + ), + controller: assetController, + onPageChanged: onAssetChanged, + scrollDirection: Axis.horizontal, + itemCount: memories[mIndex].assets.length, + itemBuilder: (context, index) { + final asset = memories[mIndex].assets[index]; + return Stack( + children: [ + Container( + color: Colors.black, + child: DriftMemoryCard( + asset: asset, + title: title, + showTitle: index == 0, + ), + ), + Positioned.fill( + child: Row( + children: [ + // Left side of the screen + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + toPreviousAsset(index); + }, + ), + ), + + // Right side of the screen + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + toNextAsset(index); + }, + ), + ), + ], + ), + ), + ], + ); + }, + ), + Positioned( + top: 8, + left: 8, + child: MaterialButton( + minWidth: 0, + onPressed: () { + // auto_route doesn't invoke pop scope, so + // turn off full screen mode here + // https://github.com/Milad-Akarie/auto_route_library/issues/1799 + context.maybePop(); + SystemChrome.setEnabledSystemUIMode( + SystemUiMode.edgeToEdge, + ); + }, + shape: const CircleBorder(), + color: Colors.white.withValues(alpha: 0.2), + elevation: 0, + child: const Icon( + Icons.close_rounded, + color: Colors.white, + ), + ), + ), + if (currentAsset.value != null && + currentAsset.value!.isVideo) + Positioned( + bottom: 24, + right: 32, + child: Icon( + Icons.videocam_outlined, + color: Colors.grey[200], + ), + ), + ], + ), + ), + DriftMemoryBottomInfo( + memory: memories[mIndex], + title: title, + ), + ], + ); + }, + ), + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart new file mode 100644 index 0000000000..86537816e3 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart @@ -0,0 +1,49 @@ +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/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class ArchiveActionButton extends ConsumerWidget { + final ActionSource source; + + const ArchiveActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).archive(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'archive_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: Icons.archive_outlined, + label: "archive".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart new file mode 100644 index 0000000000..94e3610a57 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; + +class BaseActionButton extends StatelessWidget { + const BaseActionButton({ + super.key, + required this.label, + required this.iconData, + this.onPressed, + this.onLongPressed, + this.maxWidth = 90.0, + this.minWidth, + this.menuItem = false, + }); + + final String label; + final IconData iconData; + final double maxWidth; + final double? minWidth; + final bool menuItem; + final void Function()? onPressed; + final void Function()? onLongPressed; + + @override + Widget build(BuildContext context) { + final miniWidth = + minWidth ?? (context.isMobile ? context.width / 4.5 : 75.0); + final iconTheme = IconTheme.of(context); + final iconSize = iconTheme.size ?? 24.0; + final iconColor = iconTheme.color ?? context.themeData.iconTheme.color; + final textColor = context.themeData.textTheme.labelLarge?.color; + + if (menuItem) { + return IconButton( + onPressed: onPressed, + icon: Icon(iconData, size: iconSize, color: iconColor), + ); + } + + return ConstrainedBox( + constraints: BoxConstraints( + maxWidth: maxWidth, + ), + child: MaterialButton( + padding: const EdgeInsets.all(10), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + textColor: textColor, + onPressed: onPressed, + onLongPress: onLongPressed, + minWidth: miniWidth, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(iconData, size: iconSize, color: iconColor), + const SizedBox(height: 8), + Text( + label, + style: const TextStyle( + fontSize: 14.0, + fontWeight: FontWeight.w400, + ), + maxLines: 3, + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart new file mode 100644 index 0000000000..d81f998a7b --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart @@ -0,0 +1,50 @@ +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/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class DeletePermanentActionButton extends ConsumerWidget { + final ActionSource source; + + const DeletePermanentActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).delete(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'delete_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + maxWidth: 110.0, + iconData: Icons.delete_forever, + label: "delete_dialog_title".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart new file mode 100644 index 0000000000..dfc84b4190 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; + +class DeleteLocalActionButton extends ConsumerWidget { + const DeleteLocalActionButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + maxWidth: 95.0, + iconData: Icons.no_cell_outlined, + label: "control_bottom_app_bar_delete_from_local".t(context: context), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart new file mode 100644 index 0000000000..53ea5d4946 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; + +class DownloadActionButton extends ConsumerWidget { + const DownloadActionButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: Icons.download, + label: "download".t(context: context), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart new file mode 100644 index 0000000000..3db3dde44d --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; + +class EditDateTimeActionButton extends ConsumerWidget { + const EditDateTimeActionButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + maxWidth: 95.0, + iconData: Icons.edit_calendar_outlined, + label: "control_bottom_app_bar_edit_time".t(context: context), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart new file mode 100644 index 0000000000..c7279995ff --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart @@ -0,0 +1,54 @@ +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/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class EditLocationActionButton extends ConsumerWidget { + final ActionSource source; + + const EditLocationActionButton({super.key, required this.source}); + + _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = + await ref.read(actionProvider.notifier).editLocation(source, context); + if (result == null) { + return; + } + + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'edit_location_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: Icons.edit_location_alt_outlined, + label: "control_bottom_app_bar_edit_location".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart new file mode 100644 index 0000000000..39d059c2d1 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart @@ -0,0 +1,60 @@ +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/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class FavoriteActionButton extends ConsumerWidget { + final ActionSource source; + final bool menuItem; + + const FavoriteActionButton({ + super.key, + required this.source, + this.menuItem = false, + }); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).favorite(source); + + if (source == ActionSource.viewer) { + return; + } + + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'favorite_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: Icons.favorite_border_rounded, + label: "favorite".t(context: context), + menuItem: menuItem, + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart new file mode 100644 index 0000000000..7546f07961 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart @@ -0,0 +1,51 @@ +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/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class MoveToLockFolderActionButton extends ConsumerWidget { + final ActionSource source; + + const MoveToLockFolderActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = + await ref.read(actionProvider.notifier).moveToLockFolder(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'move_to_lock_folder_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + maxWidth: 100.0, + iconData: Icons.lock_outline_rounded, + label: "move_to_locked_folder".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart new file mode 100644 index 0000000000..b18e10ebba --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; + +class RemoveFromAlbumActionButton extends ConsumerWidget { + const RemoveFromAlbumActionButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: Icons.remove_circle_outline, + label: "remove_from_album".t(context: context), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart new file mode 100644 index 0000000000..20fb62013f --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart @@ -0,0 +1,51 @@ +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/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class RemoveFromLockFolderActionButton extends ConsumerWidget { + final ActionSource source; + + const RemoveFromLockFolderActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = + await ref.read(actionProvider.notifier).removeFromLockFolder(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'remove_from_lock_folder_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + maxWidth: 100.0, + iconData: Icons.lock_open_rounded, + label: "remove_from_locked_folder".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart new file mode 100644 index 0000000000..6a8864c14f --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart @@ -0,0 +1,19 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; + +class ShareActionButton extends ConsumerWidget { + const ShareActionButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: + Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded, + label: 'share'.t(context: context), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/share_link_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/share_link_action_button.widget.dart new file mode 100644 index 0000000000..4a9f6d9bd6 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/share_link_action_button.widget.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; + +class ShareLinkActionButton extends ConsumerWidget { + final ActionSource source; + + const ShareLinkActionButton({super.key, required this.source}); + + _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + await ref.read(actionProvider.notifier).shareLink(source, context); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: Icons.link_rounded, + label: "share_link".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart new file mode 100644 index 0000000000..dc42eb96f1 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; + +class StackActionButton extends ConsumerWidget { + const StackActionButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: Icons.filter_none_rounded, + label: "stack".t(context: context), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart new file mode 100644 index 0000000000..449b688550 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart @@ -0,0 +1,50 @@ +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/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class TrashActionButton extends ConsumerWidget { + final ActionSource source; + + const TrashActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).trash(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'trash_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + maxWidth: 85.0, + iconData: Icons.delete_outline_rounded, + label: "control_bottom_app_bar_trash_from_immich".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart new file mode 100644 index 0000000000..a58d9f1ee1 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart @@ -0,0 +1,49 @@ +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/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class UnarchiveActionButton extends ConsumerWidget { + final ActionSource source; + + const UnarchiveActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).unArchive(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'unarchive_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: Icons.unarchive_outlined, + label: "unarchive".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart new file mode 100644 index 0000000000..b465643796 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart @@ -0,0 +1,60 @@ +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/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class UnFavoriteActionButton extends ConsumerWidget { + final ActionSource source; + final bool menuItem; + + const UnFavoriteActionButton({ + super.key, + required this.source, + this.menuItem = false, + }); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).unFavorite(source); + + if (source == ActionSource.viewer) { + return; + } + + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'unfavorite_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: Icons.favorite_rounded, + label: "unfavorite".t(context: context), + onPressed: () => _onTap(context, ref), + menuItem: menuItem, + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart new file mode 100644 index 0000000000..66467e44a3 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; + +class UploadActionButton extends ConsumerWidget { + const UploadActionButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: Icons.backup_outlined, + label: "upload".t(context: context), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart new file mode 100644 index 0000000000..aea821d945 --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -0,0 +1,591 @@ +import 'dart:async'; + +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/scroll_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_bar.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/top_app_bar.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart'; +import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/widgets/photo_view/photo_view.dart'; +import 'package:immich_mobile/widgets/photo_view/photo_view_gallery.dart'; +import 'package:platform/platform.dart'; + +@RoutePage() +class AssetViewerPage extends StatelessWidget { + final int initialIndex; + final TimelineService timelineService; + + const AssetViewerPage({ + super.key, + required this.initialIndex, + required this.timelineService, + }); + + @override + Widget build(BuildContext context) { + // This is necessary to ensure that the timeline service is available + // since the Timeline and AssetViewer are on different routes / Widget subtrees. + return ProviderScope( + overrides: [timelineServiceProvider.overrideWithValue(timelineService)], + child: AssetViewer(initialIndex: initialIndex), + ); + } +} + +class AssetViewer extends ConsumerStatefulWidget { + final int initialIndex; + final Platform? platform; + + const AssetViewer({ + super.key, + required this.initialIndex, + this.platform, + }); + + @override + ConsumerState createState() => _AssetViewerState(); +} + +const double _kBottomSheetMinimumExtent = 0.4; +const double _kBottomSheetSnapExtent = 0.7; + +class _AssetViewerState extends ConsumerState { + late PageController pageController; + late DraggableScrollableController bottomSheetController; + PersistentBottomSheetController? sheetCloseController; + // PhotoViewGallery takes care of disposing it's controllers + PhotoViewControllerBase? viewController; + StreamSubscription? reloadSubscription; + + late Platform platform; + late PhotoViewControllerValue initialPhotoViewState; + bool? hasDraggedDown; + bool isSnapping = false; + bool blockGestures = false; + bool dragInProgress = false; + bool shouldPopOnDrag = false; + double? initialScale; + double previousExtent = _kBottomSheetMinimumExtent; + Offset dragDownPosition = Offset.zero; + int totalAssets = 0; + BuildContext? scaffoldContext; + Map videoPlayerKeys = {}; + + // Delayed operations that should be cancelled on disposal + final List _delayedOperations = []; + + @override + void initState() { + super.initState(); + pageController = PageController(initialPage: widget.initialIndex); + platform = widget.platform ?? const LocalPlatform(); + totalAssets = ref.read(timelineServiceProvider).totalAssets; + bottomSheetController = DraggableScrollableController(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _onAssetChanged(widget.initialIndex); + }); + reloadSubscription = EventStream.shared.listen(_onEvent); + } + + @override + void dispose() { + pageController.dispose(); + bottomSheetController.dispose(); + _cancelTimers(); + reloadSubscription?.cancel(); + super.dispose(); + } + + bool get showingBottomSheet => + ref.read(assetViewerProvider.select((s) => s.showingBottomSheet)); + + Color get backgroundColor { + final opacity = + ref.read(assetViewerProvider.select((s) => s.backgroundOpacity)); + return Colors.black.withAlpha(opacity); + } + + void _cancelTimers() { + for (final timer in _delayedOperations) { + timer.cancel(); + } + _delayedOperations.clear(); + } + + // This is used to calculate the scale of the asset when the bottom sheet is showing. + // It is a small increment to ensure that the asset is slightly zoomed in when the + // bottom sheet is showing, which emulates the zoom effect. + double get _getScaleForBottomSheet => + (viewController?.prevValue.scale ?? viewController?.value.scale ?? 1.0) + + 0.01; + + double _getVerticalOffsetForBottomSheet(double extent) => + (context.height * extent) - (context.height * _kBottomSheetMinimumExtent); + + Future _precacheImage(int index) async { + if (!mounted || index < 0 || index >= totalAssets) { + return; + } + + final asset = ref.read(timelineServiceProvider).getAsset(index); + final screenSize = Size(context.width, context.height); + + // Precache both thumbnail and full image for smooth transitions + unawaited( + Future.wait([ + precacheImage( + getThumbnailImageProvider(asset: asset, size: screenSize), + context, + onError: (_, __) {}, + ), + precacheImage( + getFullImageProvider(asset, size: screenSize), + context, + onError: (_, __) {}, + ), + ]), + ); + } + + void _onAssetChanged(int index) { + final asset = ref.read(timelineServiceProvider).getAsset(index); + ref.read(currentAssetNotifier.notifier).setAsset(asset); + if (asset.isVideo) { + ref.read(videoPlaybackValueProvider.notifier).reset(); + ref.read(videoPlayerControlsProvider.notifier).pause(); + } + + unawaited(ref.read(timelineServiceProvider).preCacheAssets(index)); + _cancelTimers(); + // This will trigger the pre-caching of adjacent assets ensuring + // that they are ready when the user navigates to them. + final timer = Timer(Durations.medium4, () { + // Check if widget is still mounted before proceeding + if (!mounted) return; + + for (final offset in [-1, 1]) { + unawaited(_precacheImage(index + offset)); + } + }); + _delayedOperations.add(timer); + } + + void _onPageBuild(PhotoViewControllerBase controller) { + viewController ??= controller; + if (showingBottomSheet) { + final verticalOffset = (context.height * bottomSheetController.size) - + (context.height * _kBottomSheetMinimumExtent); + controller.position = Offset(0, -verticalOffset); + } + } + + void _onPageChanged(int index, PhotoViewControllerBase? controller) { + _onAssetChanged(index); + viewController = controller; + + // If the bottom sheet is showing, we need to adjust scale the asset to + // emulate the zoom effect + if (showingBottomSheet) { + initialScale = controller?.scale; + controller?.scale = _getScaleForBottomSheet; + } + } + + void _onDragStart( + _, + DragStartDetails details, + PhotoViewControllerBase controller, + PhotoViewScaleStateController scaleStateController, + ) { + viewController = controller; + dragDownPosition = details.localPosition; + initialPhotoViewState = controller.value; + final isZoomed = + scaleStateController.scaleState == PhotoViewScaleState.zoomedIn || + scaleStateController.scaleState == PhotoViewScaleState.covering; + if (!showingBottomSheet && isZoomed) { + blockGestures = true; + } + } + + void _onDragEnd(BuildContext ctx, _, __) { + dragInProgress = false; + + if (shouldPopOnDrag) { + // Dismiss immediately without state updates to avoid rebuilds + ctx.maybePop(); + return; + } + + // Do not reset the state if the bottom sheet is showing + if (showingBottomSheet) { + _snapBottomSheet(); + return; + } + + // If the gestures are blocked, do not reset the state + if (blockGestures) { + blockGestures = false; + return; + } + + shouldPopOnDrag = false; + hasDraggedDown = null; + viewController?.animateMultiple( + position: initialPhotoViewState.position, + scale: initialPhotoViewState.scale, + rotation: initialPhotoViewState.rotation, + ); + ref.read(assetViewerProvider.notifier).setOpacity(255); + } + + void _onDragUpdate(BuildContext ctx, DragUpdateDetails details, _) { + if (blockGestures) { + return; + } + + dragInProgress = true; + final delta = details.localPosition - dragDownPosition; + hasDraggedDown ??= delta.dy > 0; + if (!hasDraggedDown! || showingBottomSheet) { + _handleDragUp(ctx, delta); + return; + } + + _handleDragDown(ctx, delta); + } + + void _handleDragUp(BuildContext ctx, Offset delta) { + const double openThreshold = 50; + + final position = initialPhotoViewState.position + Offset(0, delta.dy); + final distanceToOrigin = position.distance; + + viewController?.updateMultiple(position: position); + // Moves the bottom sheet when the asset is being dragged up + if (showingBottomSheet && bottomSheetController.isAttached) { + final centre = (ctx.height * _kBottomSheetMinimumExtent); + bottomSheetController.jumpTo((centre + distanceToOrigin) / ctx.height); + } + + if (distanceToOrigin > openThreshold && !showingBottomSheet) { + _openBottomSheet(ctx); + } + } + + void _handleDragDown(BuildContext ctx, Offset delta) { + const double dragRatio = 0.2; + const double popThreshold = 75; + + final distance = delta.distance; + shouldPopOnDrag = delta.dy > 0 && distance > popThreshold; + + final maxScaleDistance = ctx.height * 0.5; + final scaleReduction = (distance / maxScaleDistance).clamp(0.0, dragRatio); + double? updatedScale; + if (initialPhotoViewState.scale != null) { + updatedScale = initialPhotoViewState.scale! * (1.0 - scaleReduction); + } + + final backgroundOpacity = + (255 * (1.0 - (scaleReduction / dragRatio))).round(); + + viewController?.updateMultiple( + position: initialPhotoViewState.position + delta, + scale: updatedScale, + ); + ref.read(assetViewerProvider.notifier).setOpacity(backgroundOpacity); + } + + void _onTapDown(_, __, ___) { + if (!showingBottomSheet) { + ref.read(assetViewerProvider.notifier).toggleControls(); + } + } + + bool _onNotification(Notification delta) { + if (delta is DraggableScrollableNotification) { + _handleDraggableNotification(delta); + } + + // Handle sheet snap manually so that the it snaps only at _kBottomSheetSnapExtent but not after + // the isSnapping guard is to prevent the notification from recursively handling the + // notification, eventually resulting in a heap overflow + if (!isSnapping && delta is ScrollEndNotification) { + _snapBottomSheet(); + } + return false; + } + + void _handleDraggableNotification(DraggableScrollableNotification delta) { + final currentExtent = delta.extent; + final isDraggingDown = currentExtent < previousExtent; + previousExtent = currentExtent; + // Closes the bottom sheet if the user is dragging down + if (isDraggingDown && delta.extent < 0.5) { + if (dragInProgress) { + blockGestures = true; + } + sheetCloseController?.close(); + } + + // If the asset is being dragged down, we do not want to update the asset position again + if (dragInProgress) { + return; + } + + final verticalOffset = _getVerticalOffsetForBottomSheet(delta.extent); + // Moves the asset when the bottom sheet is being dragged + if (verticalOffset > 0) { + viewController?.position = Offset(0, -verticalOffset); + } + } + + void _onEvent(Event event) { + if (event is TimelineReloadEvent) { + _onTimelineReload(event); + return; + } + + if (event is ViewerOpenBottomSheetEvent) { + final extent = _kBottomSheetMinimumExtent + 0.3; + _openBottomSheet(scaffoldContext!, extent: extent); + final offset = _getVerticalOffsetForBottomSheet(extent); + viewController?.position = Offset(0, -offset); + return; + } + } + + void _onTimelineReload(_) { + setState(() { + totalAssets = ref.read(timelineServiceProvider).totalAssets; + if (totalAssets == 0) { + context.maybePop(); + return; + } + + final index = pageController.page?.round() ?? 0; + final newAsset = ref.read(timelineServiceProvider).getAsset(index); + final currentAsset = ref.read(currentAssetNotifier); + // Do not reload / close the bottom sheet if the asset has not changed + if (newAsset.heroTag == currentAsset?.heroTag) { + return; + } + + _onAssetChanged(pageController.page!.round()); + sheetCloseController?.close(); + }); + } + + void _openBottomSheet( + BuildContext ctx, { + double extent = _kBottomSheetMinimumExtent, + }) { + ref.read(assetViewerProvider.notifier).setBottomSheet(true); + initialScale = viewController?.scale; + viewController?.updateMultiple(scale: _getScaleForBottomSheet); + previousExtent = _kBottomSheetMinimumExtent; + sheetCloseController = showBottomSheet( + context: ctx, + sheetAnimationStyle: AnimationStyle( + duration: Durations.short4, + reverseDuration: Durations.short2, + ), + constraints: const BoxConstraints(maxWidth: double.infinity), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), + ), + backgroundColor: ctx.colorScheme.surfaceContainerLowest, + builder: (_) { + return NotificationListener( + onNotification: _onNotification, + child: AssetDetailBottomSheet( + controller: bottomSheetController, + initialChildSize: extent, + ), + ); + }, + ); + sheetCloseController?.closed.then((_) => _handleSheetClose()); + } + + void _handleSheetClose() { + viewController?.animateMultiple(position: Offset.zero); + viewController?.updateMultiple(scale: initialScale); + ref.read(assetViewerProvider.notifier).setBottomSheet(false); + sheetCloseController = null; + shouldPopOnDrag = false; + hasDraggedDown = null; + } + + void _snapBottomSheet() { + if (bottomSheetController.size > _kBottomSheetSnapExtent || + bottomSheetController.size < 0.4) { + return; + } + isSnapping = true; + bottomSheetController.animateTo( + _kBottomSheetSnapExtent, + duration: Durations.short3, + curve: Curves.easeOut, + ); + } + + Widget _placeholderBuilder( + BuildContext ctx, + ImageChunkEvent? progress, + int index, + ) { + final asset = ref.read(timelineServiceProvider).getAsset(index); + return Container( + width: double.infinity, + height: double.infinity, + color: backgroundColor, + child: Thumbnail( + asset: asset, + fit: BoxFit.contain, + size: Size( + ctx.width, + ctx.height, + ), + ), + ); + } + + void _onScaleStateChanged(PhotoViewScaleState scaleState) { + if (scaleState != PhotoViewScaleState.initial) { + ref.read(videoPlayerControlsProvider.notifier).pause(); + } + } + + PhotoViewGalleryPageOptions _assetBuilder(BuildContext ctx, int index) { + scaffoldContext ??= ctx; + final asset = ref.read(timelineServiceProvider).getAsset(index); + + if (asset.isImage) { + return _imageBuilder(ctx, asset); + } + + return _videoBuilder(ctx, asset); + } + + PhotoViewGalleryPageOptions _imageBuilder(BuildContext ctx, BaseAsset asset) { + final size = Size(ctx.width, ctx.height); + return PhotoViewGalleryPageOptions( + key: ValueKey(asset.heroTag), + imageProvider: getFullImageProvider(asset, size: size), + heroAttributes: PhotoViewHeroAttributes(tag: asset.heroTag), + filterQuality: FilterQuality.high, + tightMode: true, + initialScale: PhotoViewComputedScale.contained * 0.999, + minScale: PhotoViewComputedScale.contained * 0.999, + disableScaleGestures: showingBottomSheet, + onDragStart: _onDragStart, + onDragUpdate: _onDragUpdate, + onDragEnd: _onDragEnd, + onTapDown: _onTapDown, + errorBuilder: (_, __, ___) => Container( + width: ctx.width, + height: ctx.height, + color: backgroundColor, + child: Thumbnail( + asset: asset, + fit: BoxFit.contain, + size: size, + ), + ), + ); + } + + GlobalKey _getVideoPlayerKey(String id) { + videoPlayerKeys.putIfAbsent(id, () => GlobalKey()); + return videoPlayerKeys[id]!; + } + + PhotoViewGalleryPageOptions _videoBuilder(BuildContext ctx, BaseAsset asset) { + return PhotoViewGalleryPageOptions.customChild( + onDragStart: _onDragStart, + onDragUpdate: _onDragUpdate, + onDragEnd: _onDragEnd, + onTapDown: _onTapDown, + heroAttributes: PhotoViewHeroAttributes(tag: asset.heroTag), + filterQuality: FilterQuality.high, + initialScale: PhotoViewComputedScale.contained * 0.99, + maxScale: 1.0, + minScale: PhotoViewComputedScale.contained * 0.99, + basePosition: Alignment.center, + child: SizedBox( + width: ctx.width, + height: ctx.height, + child: NativeVideoViewer( + key: _getVideoPlayerKey(asset.heroTag), + asset: asset, + image: Image( + key: ValueKey(asset), + image: + getFullImageProvider(asset, size: Size(ctx.width, ctx.height)), + fit: BoxFit.contain, + height: ctx.height, + width: ctx.width, + alignment: Alignment.center, + ), + ), + ), + ); + } + + void _onPop(bool didPop, T? result) { + ref.read(currentAssetNotifier.notifier).dispose(); + } + + @override + Widget build(BuildContext context) { + // Rebuild the widget when the asset viewer state changes + // Using multiple selectors to avoid unnecessary rebuilds for other state changes + ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); + ref.watch(assetViewerProvider.select((s) => s.backgroundOpacity)); + + // Currently it is not possible to scroll the asset when the bottom sheet is open all the way. + // Issue: https://github.com/flutter/flutter/issues/109037 + // TODO: Add a custom scrum builder once the fix lands on stable + return PopScope( + onPopInvokedWithResult: _onPop, + child: Scaffold( + backgroundColor: backgroundColor, + appBar: const ViewerTopAppBar(), + extendBody: true, + extendBodyBehindAppBar: true, + body: PhotoViewGallery.builder( + gaplessPlayback: true, + loadingBuilder: _placeholderBuilder, + pageController: pageController, + scrollPhysics: platform.isIOS + ? const FastScrollPhysics() // Use bouncing physics for iOS + : const FastClampingScrollPhysics() // Use heavy physics for Android + , + itemCount: totalAssets, + onPageChanged: _onPageChanged, + onPageBuild: _onPageBuild, + scaleStateChangedCallback: _onScaleStateChanged, + builder: _assetBuilder, + backgroundDecoration: BoxDecoration(color: backgroundColor), + enablePanAlways: true, + ), + bottomNavigationBar: const ViewerBottomBar(), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart new file mode 100644 index 0000000000..020d1d9b2c --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart @@ -0,0 +1,84 @@ +import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +class AssetViewerState { + final int backgroundOpacity; + final bool showingBottomSheet; + final bool showingControls; + + const AssetViewerState({ + this.backgroundOpacity = 255, + this.showingBottomSheet = false, + this.showingControls = true, + }); + + AssetViewerState copyWith({ + int? backgroundOpacity, + bool? showingBottomSheet, + bool? showingControls, + }) { + return AssetViewerState( + backgroundOpacity: backgroundOpacity ?? this.backgroundOpacity, + showingBottomSheet: showingBottomSheet ?? this.showingBottomSheet, + showingControls: showingControls ?? this.showingControls, + ); + } + + @override + String toString() { + return 'AssetViewerState(opacity: $backgroundOpacity, bottomSheet: $showingBottomSheet, controls: $showingControls)'; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other.runtimeType != runtimeType) return false; + return other is AssetViewerState && + other.backgroundOpacity == backgroundOpacity && + other.showingBottomSheet == showingBottomSheet && + other.showingControls == showingControls; + } + + @override + int get hashCode => + backgroundOpacity.hashCode ^ + showingBottomSheet.hashCode ^ + showingControls.hashCode; +} + +class AssetViewerStateNotifier extends AutoDisposeNotifier { + @override + AssetViewerState build() { + return const AssetViewerState(); + } + + void setOpacity(int opacity) { + state = state.copyWith( + backgroundOpacity: opacity, + showingControls: opacity == 255 ? true : state.showingControls, + ); + } + + void setBottomSheet(bool showing) { + state = state.copyWith( + showingBottomSheet: showing, + showingControls: showing ? true : state.showingControls, + ); + if (showing) { + ref.read(videoPlayerControlsProvider.notifier).pause(); + } + } + + void setControls(bool isShowing) { + state = state.copyWith(showingControls: isShowing); + } + + void toggleControls() { + state = state.copyWith(showingControls: !state.showingControls); + } +} + +final assetViewerProvider = + AutoDisposeNotifierProvider( + AssetViewerStateNotifier.new, +); diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart new file mode 100644 index 0000000000..a28d5eafa4 --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -0,0 +1,100 @@ +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/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart'; + +class ViewerBottomBar extends ConsumerWidget { + const ViewerBottomBar({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final asset = ref.watch(currentAssetNotifier); + if (asset == null) { + return const SizedBox.shrink(); + } + + final user = ref.watch(currentUserProvider); + final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; + final isSheetOpen = ref.watch( + assetViewerProvider.select((s) => s.showingBottomSheet), + ); + int opacity = ref.watch( + assetViewerProvider.select((state) => state.backgroundOpacity), + ); + final showControls = + ref.watch(assetViewerProvider.select((s) => s.showingControls)); + + if (!showControls) { + opacity = 0; + } + + final actions = [ + const ShareActionButton(), + const _EditActionButton(), + if (asset.hasRemote && isOwner) + const ArchiveActionButton(source: ActionSource.viewer), + ]; + + return IgnorePointer( + ignoring: opacity < 255, + child: AnimatedOpacity( + opacity: opacity / 255, + duration: Durations.short2, + child: AnimatedSwitcher( + duration: Durations.short4, + child: isSheetOpen + ? const SizedBox.shrink() + : SafeArea( + child: Theme( + data: context.themeData.copyWith( + iconTheme: + const IconThemeData(size: 22, color: Colors.white), + textTheme: context.themeData.textTheme.copyWith( + labelLarge: + context.themeData.textTheme.labelLarge?.copyWith( + color: Colors.white, + ), + ), + ), + child: Container( + height: asset.isVideo ? 160 : 80, + color: Colors.black.withAlpha(125), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (asset.isVideo) const VideoControls(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: actions, + ), + ], + ), + ), + ), + ), + ), + ), + ); + } +} + +class _EditActionButton extends ConsumerWidget { + const _EditActionButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + iconData: Icons.tune_outlined, + label: 'edit'.t(context: context), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart new file mode 100644 index 0000000000..d0bdc28d10 --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -0,0 +1,253 @@ +import 'package:easy_localization/easy_localization.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/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_app_bar/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; +import 'package:immich_mobile/utils/bytes_units.dart'; + +const _kSeparator = ' â€ĸ '; + +class AssetDetailBottomSheet extends ConsumerWidget { + final DraggableScrollableController? controller; + final double initialChildSize; + + const AssetDetailBottomSheet({ + this.controller, + this.initialChildSize = 0.35, + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final asset = ref.watch(currentAssetNotifier); + if (asset == null) { + return const SizedBox.shrink(); + } + + final isTrashEnable = ref.watch( + serverInfoProvider.select((state) => state.serverFeatures.trash), + ); + + final actions = [ + const ShareActionButton(), + if (asset.hasRemote) ...[ + const ShareLinkActionButton(source: ActionSource.viewer), + const ArchiveActionButton(source: ActionSource.viewer), + if (!asset.hasLocal) const DownloadActionButton(), + isTrashEnable + ? const TrashActionButton(source: ActionSource.viewer) + : const DeletePermanentActionButton(source: ActionSource.viewer), + const MoveToLockFolderActionButton( + source: ActionSource.viewer, + ), + ], + if (asset.storage == AssetState.local) ...[ + const DeleteLocalActionButton(), + const UploadActionButton(), + ], + ]; + + return BaseBottomSheet( + actions: actions, + slivers: const [_AssetDetailBottomSheet()], + controller: controller, + initialChildSize: initialChildSize, + minChildSize: 0.1, + maxChildSize: 0.88, + expand: false, + shouldCloseOnMinExtent: false, + resizeOnScroll: false, + ); + } +} + +class _AssetDetailBottomSheet extends ConsumerWidget { + const _AssetDetailBottomSheet(); + + String _getDateTime(BuildContext ctx, BaseAsset asset) { + final dateTime = asset.createdAt.toLocal(); + final date = DateFormat.yMMMEd(ctx.locale.toLanguageTag()).format(dateTime); + final time = DateFormat.jm(ctx.locale.toLanguageTag()).format(dateTime); + return '$date$_kSeparator$time'; + } + + String _getFileInfo(BaseAsset asset, ExifInfo? exifInfo) { + final height = asset.height ?? exifInfo?.height; + final width = asset.width ?? exifInfo?.width; + final resolution = + (width != null && height != null) ? "$width x $height" : null; + final fileSize = + exifInfo?.fileSize != null ? formatBytes(exifInfo!.fileSize!) : null; + + return switch ((fileSize, resolution)) { + (null, null) => '', + (String fileSize, null) => fileSize, + (null, String resolution) => resolution, + (String fileSize, String resolution) => + '$fileSize$_kSeparator$resolution', + }; + } + + String? _getCameraInfoTitle(ExifInfo? exifInfo) { + if (exifInfo == null) { + return null; + } + + return switch ((exifInfo.make, exifInfo.model)) { + (null, null) => null, + (String make, null) => make, + (null, String model) => model, + (String make, String model) => '$make $model', + }; + } + + String? _getCameraInfoSubtitle(ExifInfo? exifInfo) { + if (exifInfo == null) { + return null; + } + + final fNumber = + exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null; + final exposureTime = + exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null; + final focalLength = + exifInfo.focalLength.isNotEmpty ? '${exifInfo.focalLength} mm' : null; + final iso = exifInfo.iso != null ? 'ISO ${exifInfo.iso}' : null; + + return [fNumber, exposureTime, focalLength, iso] + .where((spec) => spec != null && spec.isNotEmpty) + .join(_kSeparator); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final asset = ref.watch(currentAssetNotifier); + if (asset == null) { + return const SliverToBoxAdapter(child: SizedBox.shrink()); + } + + final exifInfo = ref.watch(currentAssetExifProvider).valueOrNull; + final cameraTitle = _getCameraInfoTitle(exifInfo); + + return SliverList.list( + children: [ + // Asset Date and Time + _SheetTile( + title: _getDateTime(context, asset), + titleStyle: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + fontSize: 16, + ), + ), + const SheetLocationDetails(), + // Details header + _SheetTile( + title: 'exif_bottom_sheet_details'.t(context: context), + titleStyle: context.textTheme.labelLarge, + ), + // File info + _SheetTile( + title: asset.name, + titleStyle: context.textTheme.labelLarge + ?.copyWith(fontWeight: FontWeight.w600), + leading: Icon( + asset.isImage ? Icons.image_outlined : Icons.videocam_outlined, + size: 30, + color: context.textTheme.labelLarge?.color, + ), + subtitle: _getFileInfo(asset, exifInfo), + subtitleStyle: context.textTheme.labelLarge?.copyWith( + color: context.textTheme.labelLarge?.color?.withAlpha(200), + ), + ), + // Camera info + if (cameraTitle != null) + _SheetTile( + title: cameraTitle, + titleStyle: context.textTheme.labelLarge + ?.copyWith(fontWeight: FontWeight.w600), + leading: Icon( + Icons.camera_outlined, + size: 30, + color: context.textTheme.labelLarge?.color, + ), + subtitle: _getCameraInfoSubtitle(exifInfo), + subtitleStyle: context.textTheme.labelLarge?.copyWith( + color: context.textTheme.labelLarge?.color?.withAlpha(200), + ), + ), + ], + ); + } +} + +class _SheetTile extends StatelessWidget { + final String title; + final Widget? leading; + final String? subtitle; + final TextStyle? titleStyle; + final TextStyle? subtitleStyle; + + const _SheetTile({ + required this.title, + this.titleStyle, + this.leading, + this.subtitle, + this.subtitleStyle, + }); + + @override + Widget build(BuildContext context) { + final Widget titleWidget; + if (leading == null) { + titleWidget = LimitedBox( + maxWidth: double.infinity, + child: Text(title, style: titleStyle), + ); + } else { + titleWidget = Container( + width: double.infinity, + padding: const EdgeInsets.only(left: 15), + child: Text(title, style: titleStyle), + ); + } + + final Widget? subtitleWidget; + if (leading == null && subtitle != null) { + subtitleWidget = Text(subtitle!, style: subtitleStyle); + } else if (leading != null && subtitle != null) { + subtitleWidget = Padding( + padding: const EdgeInsets.only(left: 15), + child: Text(subtitle!, style: subtitleStyle), + ); + } else { + subtitleWidget = null; + } + + return ListTile( + dense: true, + visualDensity: VisualDensity.compact, + title: titleWidget, + titleAlignment: ListTileTitleAlignment.center, + leading: leading, + contentPadding: leading == null ? null : const EdgeInsets.only(left: 25), + subtitle: subtitleWidget, + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart new file mode 100644 index 0000000000..2d22d063bd --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart @@ -0,0 +1,127 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/widgets/asset_viewer/detail_panel/exif_map.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +class SheetLocationDetails extends ConsumerStatefulWidget { + const SheetLocationDetails({super.key}); + + @override + ConsumerState createState() => _SheetLocationDetailsState(); +} + +class _SheetLocationDetailsState extends ConsumerState { + BaseAsset? asset; + ExifInfo? exifInfo; + MapLibreMapController? _mapController; + + String? _getLocationName(ExifInfo? exifInfo) { + if (exifInfo == null) { + return null; + } + + final cityName = exifInfo.city; + final stateName = exifInfo.state; + + if (cityName != null && stateName != null) { + return "$cityName, $stateName"; + } + return null; + } + + void _onMapCreated(MapLibreMapController controller) { + _mapController = controller; + } + + void _onExifChanged( + AsyncValue? previous, + AsyncValue current, + ) { + asset = ref.read(currentAssetNotifier); + setState(() { + exifInfo = current.valueOrNull; + final hasCoordinates = exifInfo?.hasCoordinates ?? false; + if (exifInfo != null && hasCoordinates) { + _mapController?.moveCamera( + CameraUpdate.newLatLng( + LatLng(exifInfo!.latitude!, exifInfo!.longitude!), + ), + ); + } + }); + } + + @override + void initState() { + super.initState(); + ref.listenManual( + currentAssetExifProvider, + _onExifChanged, + fireImmediately: true, + ); + } + + @override + Widget build(BuildContext context) { + final hasCoordinates = exifInfo?.hasCoordinates ?? false; + + // Guard no lat/lng + if (!hasCoordinates || + (asset is LocalAsset && !(asset as LocalAsset).hasRemote)) { + return const SizedBox.shrink(); + } + + final remoteId = asset is LocalAsset + ? (asset as LocalAsset).remoteId + : (asset as RemoteAsset).id; + final locationName = _getLocationName(exifInfo); + final coordinates = + "${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo!.longitude!.toStringAsFixed(4)}"; + + return Padding( + padding: EdgeInsets.symmetric( + vertical: 16.0, + horizontal: context.isMobile ? 16.0 : 56.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: Text( + "exif_bottom_sheet_location".t(context: context), + style: context.textTheme.labelLarge, + ), + ), + ExifMap( + exifInfo: exifInfo!, + markerId: remoteId, + onMapCreated: _onMapCreated, + ), + const SizedBox(height: 15), + if (locationName != null) + Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: Text( + locationName, + style: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + ), + ), + ), + Text( + coordinates, + style: context.textTheme.labelLarge?.copyWith( + color: context.textTheme.labelLarge?.color?.withAlpha(150), + ), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart new file mode 100644 index 0000000000..b7e8477073 --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart @@ -0,0 +1,114 @@ +import 'package:auto_route/auto_route.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/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/favorite_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { + const ViewerTopAppBar({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final asset = ref.watch(currentAssetNotifier); + if (asset == null) { + return const SizedBox.shrink(); + } + + final user = ref.watch(currentUserProvider); + final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; + + final isShowingSheet = ref + .watch(assetViewerProvider.select((state) => state.showingBottomSheet)); + int opacity = ref.watch( + assetViewerProvider.select((state) => state.backgroundOpacity), + ); + final showControls = + ref.watch(assetViewerProvider.select((s) => s.showingControls)); + + if (!showControls) { + opacity = 0; + } + + final actions = [ + if (asset.hasRemote && isOwner && !asset.isFavorite) + const FavoriteActionButton(source: ActionSource.viewer, menuItem: true), + if (asset.hasRemote && isOwner && asset.isFavorite) + const UnFavoriteActionButton( + source: ActionSource.viewer, + menuItem: true, + ), + const _KebabMenu(), + ]; + + return IgnorePointer( + ignoring: opacity < 255, + child: AnimatedOpacity( + opacity: opacity / 255, + duration: Durations.short2, + child: AppBar( + backgroundColor: + isShowingSheet ? Colors.transparent : Colors.black.withAlpha(125), + leading: const _AppBarBackButton(), + iconTheme: const IconThemeData(size: 22, color: Colors.white), + actionsIconTheme: const IconThemeData(size: 22, color: Colors.white), + shape: const Border(), + actions: isShowingSheet ? null : actions, + ), + ), + ); + } + + @override + Size get preferredSize => const Size.fromHeight(60.0); +} + +class _KebabMenu extends ConsumerWidget { + const _KebabMenu(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return IconButton( + onPressed: () { + EventStream.shared.emit(const ViewerOpenBottomSheetEvent()); + }, + icon: const Icon(Icons.more_vert_rounded), + ); + } +} + +class _AppBarBackButton extends ConsumerWidget { + const _AppBarBackButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isShowingSheet = ref + .watch(assetViewerProvider.select((state) => state.showingBottomSheet)); + final backgroundColor = + isShowingSheet && !context.isDarkTheme ? Colors.white : Colors.black; + final foregroundColor = + isShowingSheet && !context.isDarkTheme ? Colors.black : Colors.white; + + return Padding( + padding: const EdgeInsets.only(left: 12.0), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: backgroundColor, + shape: const CircleBorder(), + iconSize: 22, + iconColor: foregroundColor, + padding: EdgeInsets.zero, + elevation: isShowingSheet ? 4 : 0, + ), + onPressed: context.maybePop, + child: const Icon(Icons.arrow_back_rounded), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart new file mode 100644 index 0000000000..60cb61b255 --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -0,0 +1,429 @@ +import 'dart:async'; +import 'dart:io'; + +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/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; +import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart'; +import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; +import 'package:immich_mobile/providers/cast.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/debounce.dart'; +import 'package:immich_mobile/utils/hooks/interval_hook.dart'; +import 'package:logging/logging.dart'; +import 'package:native_video_player/native_video_player.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; + +class NativeVideoViewer extends HookConsumerWidget { + final BaseAsset asset; + final bool showControls; + final int playbackDelayFactor; + final Widget image; + + const NativeVideoViewer({ + super.key, + required this.asset, + required this.image, + this.showControls = true, + this.playbackDelayFactor = 1, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final controller = useState(null); + final lastVideoPosition = useRef(-1); + final isBuffering = useRef(false); + + // Used to track whether the video should play when the app + // is brought back to the foreground + final shouldPlayOnForeground = useRef(true); + + // When a video is opened through the timeline, `isCurrent` will immediately be true. + // When swiping from video A to video B, `isCurrent` will initially be true for video A and false for video B. + // If the swipe is completed, `isCurrent` will be true for video B after a delay. + // If the swipe is canceled, `currentAsset` will not have changed and video A will continue to play. + final currentAsset = useState(ref.read(currentAssetNotifier)); + final isCurrent = currentAsset.value == asset; + + // Used to show the placeholder during hero animations for remote videos to avoid a stutter + final isVisible = useState(Platform.isIOS && asset.hasLocal); + + final log = Logger('NativeVideoViewerPage'); + + final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); + + Future createSource() async { + if (!context.mounted) { + return null; + } + + try { + if (asset.hasLocal && asset.livePhotoVideoId == null) { + final id = asset is LocalAsset + ? (asset as LocalAsset).id + : (asset as RemoteAsset).localId!; + final file = await const StorageRepository().getFileForAsset(id); + if (file == null) { + throw Exception('No file found for the video'); + } + + final source = await VideoSource.init( + path: file.path, + type: VideoSourceType.file, + ); + return source; + } + + final remoteId = (asset as RemoteAsset).id; + + // Use a network URL for the video player controller + final serverEndpoint = Store.get(StoreKey.serverEndpoint); + final isOriginalVideo = + ref.read(settingsProvider).get(Setting.loadOriginalVideo); + final String postfixUrl = + isOriginalVideo ? 'original' : 'video/playback'; + final String videoUrl = asset.livePhotoVideoId != null + ? '$serverEndpoint/assets/${asset.livePhotoVideoId}/$postfixUrl' + : '$serverEndpoint/assets/$remoteId/$postfixUrl'; + + final source = await VideoSource.init( + path: videoUrl, + type: VideoSourceType.network, + headers: ApiService.getRequestHeaders(), + ); + return source; + } catch (error) { + log.severe( + 'Error creating video source for asset ${asset.name}: $error', + ); + return null; + } + } + + final videoSource = useMemoized>(() => createSource()); + final aspectRatio = useState(null); + useMemoized( + () async { + if (!context.mounted || aspectRatio.value != null) { + return null; + } + + try { + aspectRatio.value = + await ref.read(assetServiceProvider).getAspectRatio(asset); + } catch (error) { + log.severe( + 'Error getting aspect ratio for asset ${asset.name}: $error', + ); + } + }, + [asset.heroTag], + ); + + void checkIfBuffering() { + if (!context.mounted) { + return; + } + + final videoPlayback = ref.read(videoPlaybackValueProvider); + if ((isBuffering.value || + videoPlayback.state == VideoPlaybackState.initializing) && + videoPlayback.state != VideoPlaybackState.buffering) { + ref.read(videoPlaybackValueProvider.notifier).value = + videoPlayback.copyWith(state: VideoPlaybackState.buffering); + } + } + + // Timer to mark videos as buffering if the position does not change + useInterval(const Duration(seconds: 5), checkIfBuffering); + + // When the position changes, seek to the position + // Debounce the seek to avoid seeking too often + // But also don't delay the seek too much to maintain visual feedback + final seekDebouncer = useDebouncer( + interval: const Duration(milliseconds: 100), + maxWaitTime: const Duration(milliseconds: 200), + ); + ref.listen(videoPlayerControlsProvider, (oldControls, newControls) async { + final playerController = controller.value; + if (playerController == null) { + return; + } + + final playbackInfo = playerController.playbackInfo; + if (playbackInfo == null) { + return; + } + + final oldSeek = (oldControls?.position ?? 0) ~/ 1; + final newSeek = newControls.position ~/ 1; + if (oldSeek != newSeek || newControls.restarted) { + seekDebouncer.run(() => playerController.seekTo(newSeek)); + } + + if (oldControls?.pause != newControls.pause || newControls.restarted) { + // Make sure the last seek is complete before pausing or playing + // Otherwise, `onPlaybackPositionChanged` can receive outdated events + if (seekDebouncer.isActive) { + await seekDebouncer.drain(); + } + + try { + if (newControls.pause) { + await playerController.pause(); + } else { + await playerController.play(); + } + } catch (error) { + log.severe('Error pausing or playing video: $error'); + } + } + }); + + void onPlaybackReady() async { + final videoController = controller.value; + if (videoController == null || !isCurrent || !context.mounted) { + return; + } + + final videoPlayback = + VideoPlaybackValue.fromNativeController(videoController); + ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback; + + if (ref.read(assetViewerProvider.select((s) => s.showingBottomSheet))) { + return; + } + + try { + await videoController.play(); + await videoController.setVolume(0.9); + } catch (error) { + log.severe('Error playing video: $error'); + } + } + + void onPlaybackStatusChanged() { + final videoController = controller.value; + if (videoController == null || !context.mounted) { + return; + } + + final videoPlayback = + VideoPlaybackValue.fromNativeController(videoController); + if (videoPlayback.state == VideoPlaybackState.playing) { + // Sync with the controls playing + WakelockPlus.enable(); + } else { + // Sync with the controls pause + WakelockPlus.disable(); + } + + ref.read(videoPlaybackValueProvider.notifier).status = + videoPlayback.state; + } + + void onPlaybackPositionChanged() { + // When seeking, these events sometimes move the slider to an older position + if (seekDebouncer.isActive) { + return; + } + + final videoController = controller.value; + if (videoController == null || !context.mounted) { + return; + } + + final playbackInfo = videoController.playbackInfo; + if (playbackInfo == null) { + return; + } + + ref.read(videoPlaybackValueProvider.notifier).position = + Duration(seconds: playbackInfo.position); + + // Check if the video is buffering + if (playbackInfo.status == PlaybackStatus.playing) { + isBuffering.value = lastVideoPosition.value == playbackInfo.position; + lastVideoPosition.value = playbackInfo.position; + } else { + isBuffering.value = false; + lastVideoPosition.value = -1; + } + } + + void onPlaybackEnded() { + final videoController = controller.value; + if (videoController == null || !context.mounted) { + return; + } + + if (videoController.playbackInfo?.status == PlaybackStatus.stopped && + !ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.loopVideo)) { + ref.read(isPlayingMotionVideoProvider.notifier).playing = false; + } + } + + void removeListeners(NativeVideoPlayerController controller) { + controller.onPlaybackPositionChanged + .removeListener(onPlaybackPositionChanged); + controller.onPlaybackStatusChanged + .removeListener(onPlaybackStatusChanged); + controller.onPlaybackReady.removeListener(onPlaybackReady); + controller.onPlaybackEnded.removeListener(onPlaybackEnded); + } + + void initController(NativeVideoPlayerController nc) async { + if (controller.value != null || !context.mounted) { + return; + } + ref.read(videoPlayerControlsProvider.notifier).reset(); + ref.read(videoPlaybackValueProvider.notifier).reset(); + + final source = await videoSource; + if (source == null) { + return; + } + + nc.onPlaybackPositionChanged.addListener(onPlaybackPositionChanged); + nc.onPlaybackStatusChanged.addListener(onPlaybackStatusChanged); + nc.onPlaybackReady.addListener(onPlaybackReady); + nc.onPlaybackEnded.addListener(onPlaybackEnded); + + nc.loadVideoSource(source).catchError((error) { + log.severe('Error loading video source: $error'); + }); + final loopVideo = ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.loopVideo); + nc.setLoop(loopVideo); + + controller.value = nc; + Timer(const Duration(milliseconds: 200), checkIfBuffering); + } + + ref.listen(currentAssetNotifier, (_, value) { + final playerController = controller.value; + if (playerController != null && value != asset) { + removeListeners(playerController); + } + + final curAsset = currentAsset.value; + if (curAsset == asset) { + return; + } + + final imageToVideo = curAsset != null && !curAsset.isVideo; + + // No need to delay video playback when swiping from an image to a video + if (imageToVideo && Platform.isIOS) { + currentAsset.value = value; + onPlaybackReady(); + return; + } + + // Delay the video playback to avoid a stutter in the swipe animation + // Note, in some circumstances a longer delay is needed (eg: memories), + // the playbackDelayFactor can be used for this + // This delay seems like a hacky way to resolve underlying bugs in video + // playback, but other resolutions failed thus far + Timer( + Platform.isIOS + ? Duration(milliseconds: 300 * playbackDelayFactor) + : imageToVideo + ? Duration(milliseconds: 200 * playbackDelayFactor) + : Duration(milliseconds: 400 * playbackDelayFactor), () { + if (!context.mounted) { + return; + } + + currentAsset.value = value; + if (currentAsset.value == asset) { + onPlaybackReady(); + } + }); + }); + + useEffect( + () { + // If opening a remote video from a hero animation, delay visibility to avoid a stutter + final timer = isVisible.value + ? null + : Timer( + const Duration(milliseconds: 300), + () => isVisible.value = true, + ); + + return () { + timer?.cancel(); + final playerController = controller.value; + if (playerController == null) { + return; + } + removeListeners(playerController); + playerController.stop().catchError((error) { + log.fine('Error stopping video: $error'); + }); + + WakelockPlus.disable(); + }; + }, + const [], + ); + + useOnAppLifecycleStateChange((_, state) async { + if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { + controller.value?.play(); + } else if (state == AppLifecycleState.paused) { + final videoPlaying = await controller.value?.isPlaying(); + if (videoPlaying ?? true) { + shouldPlayOnForeground.value = true; + controller.value?.pause(); + } else { + shouldPlayOnForeground.value = false; + } + } + }); + + return Stack( + children: [ + // This remains under the video to avoid flickering + // For motion videos, this is the image portion of the asset + Center(key: ValueKey(asset.heroTag), child: image), + if (aspectRatio.value != null && !isCasting) + Visibility.maintain( + key: ValueKey(asset), + visible: isVisible.value, + child: Center( + key: ValueKey(asset), + child: AspectRatio( + key: ValueKey(asset), + aspectRatio: aspectRatio.value!, + child: isCurrent + ? NativeVideoPlayerView( + key: ValueKey(asset), + onViewReady: initController, + ) + : null, + ), + ), + ), + if (showControls) const Center(child: VideoViewerControls()), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart new file mode 100644 index 0000000000..883169ae83 --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart @@ -0,0 +1,131 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/models/cast/cast_manager_state.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; +import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart'; +import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; +import 'package:immich_mobile/providers/cast.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/utils/hooks/timer_hook.dart'; +import 'package:immich_mobile/widgets/asset_viewer/center_play_button.dart'; +import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; + +class VideoViewerControls extends HookConsumerWidget { + final Duration hideTimerDuration; + + const VideoViewerControls({ + super.key, + this.hideTimerDuration = const Duration(seconds: 5), + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final assetIsVideo = ref.watch( + currentAssetNotifier.select((asset) => asset != null && asset.isVideo), + ); + bool showControls = + ref.watch(assetViewerProvider.select((s) => s.showingControls)); + final showBottomSheet = + ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); + if (showBottomSheet) { + showControls = false; + } + final VideoPlaybackState state = + ref.watch(videoPlaybackValueProvider.select((value) => value.state)); + + final cast = ref.watch(castProvider); + + // A timer to hide the controls + final hideTimer = useTimer( + hideTimerDuration, + () { + if (!context.mounted) { + return; + } + final state = ref.read(videoPlaybackValueProvider).state; + + // Do not hide on paused + if (state != VideoPlaybackState.paused && + state != VideoPlaybackState.completed && + assetIsVideo) { + ref.read(assetViewerProvider.notifier).setControls(false); + } + }, + ); + final showBuffering = + state == VideoPlaybackState.buffering && !cast.isCasting; + + /// Shows the controls and starts the timer to hide them + void showControlsAndStartHideTimer() { + hideTimer.reset(); + ref.read(assetViewerProvider.notifier).setControls(true); + } + + // When we change position, show or hide timer + ref.listen(videoPlayerControlsProvider.select((v) => v.position), + (previous, next) { + showControlsAndStartHideTimer(); + }); + + /// Toggles between playing and pausing depending on the state of the video + void togglePlay() { + showControlsAndStartHideTimer(); + + if (cast.isCasting) { + if (cast.castState == CastState.playing) { + ref.read(castProvider.notifier).pause(); + } else if (cast.castState == CastState.paused) { + ref.read(castProvider.notifier).play(); + } else if (cast.castState == CastState.idle) { + // resend the play command since its finished + final asset = ref.read(currentAssetNotifier); + if (asset == null) { + return; + } + // ref.read(castProvider.notifier).loadMedia(asset, true); + } + return; + } + + if (state == VideoPlaybackState.playing) { + ref.read(videoPlayerControlsProvider.notifier).pause(); + } else if (state == VideoPlaybackState.completed) { + ref.read(videoPlayerControlsProvider.notifier).restart(); + } else { + ref.read(videoPlayerControlsProvider.notifier).play(); + } + } + + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: showControlsAndStartHideTimer, + child: AbsorbPointer( + absorbing: !showControls, + child: Stack( + children: [ + if (showBuffering) + const Center( + child: DelayedLoadingIndicator( + fadeInDuration: Duration(milliseconds: 400), + ), + ) + else + GestureDetector( + onTap: () => + ref.read(assetViewerProvider.notifier).setControls(false), + child: CenterPlayButton( + backgroundColor: Colors.black54, + iconColor: Colors.white, + isFinished: state == VideoPlaybackState.completed, + isPlaying: state == VideoPlaybackState.playing || + (cast.isCasting && cast.castState == CastState.playing), + show: assetIsVideo && showControls, + onPressed: togglePlay, + ), + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/bottom_app_bar/base_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_app_bar/base_bottom_sheet.widget.dart new file mode 100644 index 0000000000..2db8ae2b4c --- /dev/null +++ b/mobile/lib/presentation/widgets/bottom_app_bar/base_bottom_sheet.widget.dart @@ -0,0 +1,129 @@ +import 'package:flutter/material.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/presentation/widgets/timeline/timeline.state.dart'; + +class BaseBottomSheet extends ConsumerStatefulWidget { + final List actions; + final DraggableScrollableController? controller; + final List? slivers; + final double initialChildSize; + final double minChildSize; + final double maxChildSize; + final bool expand; + final bool shouldCloseOnMinExtent; + final bool resizeOnScroll; + + const BaseBottomSheet({ + super.key, + required this.actions, + this.slivers, + this.controller, + this.initialChildSize = 0.35, + this.minChildSize = 0.15, + this.maxChildSize = 0.65, + this.expand = true, + this.shouldCloseOnMinExtent = true, + this.resizeOnScroll = true, + }); + + @override + ConsumerState createState() => + _BaseDraggableScrollableSheetState(); +} + +class _BaseDraggableScrollableSheetState + extends ConsumerState { + late DraggableScrollableController _controller; + + @override + void initState() { + super.initState(); + _controller = widget.controller ?? DraggableScrollableController(); + } + + @override + Widget build(BuildContext context) { + ref.listen(timelineStateProvider, (previous, next) { + if (!widget.resizeOnScroll) { + return; + } + + if (previous?.isInteracting != true && next.isInteracting) { + _controller.animateTo( + widget.minChildSize, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + ); + } + }); + + return DraggableScrollableSheet( + controller: _controller, + initialChildSize: widget.initialChildSize, + minChildSize: widget.minChildSize, + maxChildSize: widget.maxChildSize, + snap: false, + expand: widget.expand, + shouldCloseOnMinExtent: widget.shouldCloseOnMinExtent, + builder: (BuildContext context, ScrollController scrollController) { + return Card( + color: context.colorScheme.surfaceContainerHigh, + surfaceTintColor: context.colorScheme.surfaceContainerHigh, + borderOnForeground: false, + clipBehavior: Clip.antiAlias, + elevation: 6.0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(18)), + ), + margin: const EdgeInsets.symmetric(horizontal: 0), + child: CustomScrollView( + controller: scrollController, + slivers: [ + SliverToBoxAdapter( + child: Column( + children: [ + const SizedBox(height: 10), + const _DragHandle(), + const SizedBox(height: 14), + if (widget.actions.isNotEmpty) + SizedBox( + height: 115, + child: ListView( + shrinkWrap: true, + scrollDirection: Axis.horizontal, + children: widget.actions, + ), + ), + if (widget.actions.isNotEmpty) ...[ + const Divider(indent: 16, endIndent: 16), + const SizedBox(height: 16), + ], + ], + ), + ), + ...(widget.slivers ?? []), + ], + ), + ); + }, + ); + } +} + +class _DragHandle extends StatelessWidget { + const _DragHandle(); + + @override + Widget build(BuildContext context) { + return Container( + height: 6, + width: 32, + decoration: BoxDecoration( + color: context.themeData.dividerColor.lighten(amount: 0.6), + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/bottom_app_bar/home_bottom_app_bar.widget.dart b/mobile/lib/presentation/widgets/bottom_app_bar/home_bottom_app_bar.widget.dart new file mode 100644 index 0000000000..f5ba759130 --- /dev/null +++ b/mobile/lib/presentation/widgets/bottom_app_bar/home_bottom_app_bar.widget.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/edit_location_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/favorite_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_app_bar/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; + +class HomeBottomAppBar extends ConsumerWidget { + const HomeBottomAppBar({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final multiselect = ref.watch(multiSelectProvider); + final isTrashEnable = ref.watch( + serverInfoProvider.select((state) => state.serverFeatures.trash), + ); + + return BaseBottomSheet( + initialChildSize: 0.25, + shouldCloseOnMinExtent: false, + actions: [ + if (multiselect.isEnabled) const ShareActionButton(), + if (multiselect.hasRemote) ...[ + const ShareLinkActionButton(source: ActionSource.timeline), + const ArchiveActionButton(source: ActionSource.timeline), + const FavoriteActionButton(source: ActionSource.timeline), + const DownloadActionButton(), + isTrashEnable + ? const TrashActionButton(source: ActionSource.timeline) + : const DeletePermanentActionButton( + source: ActionSource.timeline, + ), + const EditDateTimeActionButton(), + const EditLocationActionButton(source: ActionSource.timeline), + const MoveToLockFolderActionButton( + source: ActionSource.timeline, + ), + const StackActionButton(), + ], + if (multiselect.hasLocal) ...[ + const DeleteLocalActionButton(), + const UploadActionButton(), + ], + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/images/full_image.widget.dart b/mobile/lib/presentation/widgets/images/full_image.widget.dart new file mode 100644 index 0000000000..77ea996b89 --- /dev/null +++ b/mobile/lib/presentation/widgets/images/full_image.widget.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart'; +import 'package:octo_image/octo_image.dart'; + +class FullImage extends StatelessWidget { + const FullImage( + this.asset, { + required this.size, + this.fit = BoxFit.cover, + this.placeholder = const ThumbnailPlaceholder(), + super.key, + }); + + final BaseAsset asset; + final Size size; + final Widget? placeholder; + final BoxFit fit; + + @override + Widget build(BuildContext context) { + final provider = getFullImageProvider(asset, size: size); + return OctoImage( + fadeInDuration: const Duration(milliseconds: 0), + fadeOutDuration: const Duration(milliseconds: 100), + placeholderBuilder: placeholder != null ? (_) => placeholder! : null, + image: provider, + width: size.width, + height: size.height, + fit: fit, + errorBuilder: (context, error, stackTrace) { + provider.evict(); + return const Icon(Icons.image_not_supported_outlined, size: 32); + }, + ); + } +} diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart new file mode 100644 index 0000000000..e79665baf7 --- /dev/null +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -0,0 +1,63 @@ +import 'package:flutter/widgets.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/domain/services/setting.service.dart'; +import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; + +ImageProvider getFullImageProvider( + BaseAsset asset, { + Size size = const Size(1080, 1920), +}) { + // Create new provider and cache it + final ImageProvider provider; + if (_shouldUseLocalAsset(asset)) { + provider = LocalFullImageProvider(asset: asset as LocalAsset, size: size); + } else { + final String assetId; + if (asset is LocalAsset && asset.hasRemote) { + assetId = asset.remoteId!; + } else if (asset is RemoteAsset) { + assetId = asset.id; + } else { + throw ArgumentError("Unsupported asset type: ${asset.runtimeType}"); + } + provider = RemoteFullImageProvider(assetId: assetId); + } + + return provider; +} + +ImageProvider getThumbnailImageProvider({ + BaseAsset? asset, + String? remoteId, + Size size = const Size.square(256), +}) { + assert( + asset != null || remoteId != null, + 'Either asset or remoteId must be provided', + ); + + if (remoteId != null) { + return RemoteThumbProvider(assetId: remoteId); + } + + if (_shouldUseLocalAsset(asset!)) { + return LocalThumbProvider(asset: asset as LocalAsset, size: size); + } + + final String assetId; + if (asset is LocalAsset && asset.hasRemote) { + assetId = asset.remoteId!; + } else if (asset is RemoteAsset) { + assetId = asset.id; + } else { + throw ArgumentError("Unsupported asset type: ${asset.runtimeType}"); + } + + return RemoteThumbProvider(assetId: assetId); +} + +bool _shouldUseLocalAsset(BaseAsset asset) => + asset is LocalAsset && + (!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage)); diff --git a/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart new file mode 100644 index 0000000000..dcf0f28527 --- /dev/null +++ b/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; + +class LocalAlbumThumbnail extends ConsumerWidget { + const LocalAlbumThumbnail({ + super.key, + required this.albumId, + }); + + final String albumId; + @override + Widget build(BuildContext context, WidgetRef ref) { + final localAlbumThumbnail = ref.watch(localAlbumThumbnailProvider(albumId)); + return localAlbumThumbnail.when( + data: (data) { + if (data == null) { + return Container( + decoration: BoxDecoration( + color: context.colorScheme.surfaceContainer, + borderRadius: const BorderRadius.all(Radius.circular(16)), + border: Border.all( + color: context.colorScheme.outline.withAlpha(50), + width: 1, + ), + ), + child: Icon( + Icons.collections, + size: 24, + color: context.primaryColor, + ), + ); + } + + return ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16)), + child: Thumbnail( + asset: data, + ), + ); + }, + error: (error, stack) { + return const Icon(Icons.error, size: 24); + }, + loading: () => const SizedBox( + width: 24, + height: 24, + child: Center(child: CircularProgressIndicator()), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart new file mode 100644 index 0000000000..f046fcad47 --- /dev/null +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -0,0 +1,241 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:ui'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/domain/services/setting.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/asset_media.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; +import 'package:immich_mobile/providers/image/cache/thumbnail_image_cache_manager.dart'; +import 'package:immich_mobile/providers/image/exceptions/image_loading_exception.dart'; +import 'package:logging/logging.dart'; + +class LocalThumbProvider extends ImageProvider { + final AssetMediaRepository _assetMediaRepository = + const AssetMediaRepository(); + final CacheManager? cacheManager; + + final LocalAsset asset; + final Size size; + + const LocalThumbProvider({ + required this.asset, + this.size = const Size.square(kTimelineFixedTileExtent), + this.cacheManager, + }); + + @override + Future obtainKey(ImageConfiguration configuration) { + return SynchronousFuture(this); + } + + @override + ImageStreamCompleter loadImage( + LocalThumbProvider key, + ImageDecoderCallback decode, + ) { + final cache = cacheManager ?? ThumbnailImageCacheManager(); + return MultiFrameImageStreamCompleter( + codec: _codec(key, cache, decode), + scale: 1.0, + informationCollector: () => [ + DiagnosticsProperty('Image provider', this), + DiagnosticsProperty('Asset', key.asset), + ], + ); + } + + Future _codec( + LocalThumbProvider key, + CacheManager cache, + ImageDecoderCallback decode, + ) async { + final cacheKey = + '${key.asset.id}-${key.asset.updatedAt}-${key.size.width}x${key.size.height}'; + + final fileFromCache = await cache.getFileFromCache(cacheKey); + if (fileFromCache != null) { + try { + final buffer = + await ImmutableBuffer.fromFilePath(fileFromCache.file.path); + return decode(buffer); + } catch (_) {} + } + + final thumbnailBytes = + await _assetMediaRepository.getThumbnail(key.asset.id, size: key.size); + if (thumbnailBytes == null) { + PaintingBinding.instance.imageCache.evict(key); + throw StateError( + "Loading thumb for local photo ${key.asset.name} failed", + ); + } + + final buffer = await ImmutableBuffer.fromUint8List(thumbnailBytes); + unawaited(cache.putFile(cacheKey, thumbnailBytes)); + return decode(buffer); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is LocalThumbProvider) { + return asset.id == other.asset.id && + asset.updatedAt == other.asset.updatedAt; + } + return false; + } + + @override + int get hashCode => asset.id.hashCode ^ asset.updatedAt.hashCode; +} + +class LocalFullImageProvider extends ImageProvider { + final AssetMediaRepository _assetMediaRepository = + const AssetMediaRepository(); + final StorageRepository _storageRepository = const StorageRepository(); + + final LocalAsset asset; + final Size size; + + const LocalFullImageProvider({ + required this.asset, + required this.size, + }); + + @override + Future obtainKey(ImageConfiguration configuration) { + return SynchronousFuture(this); + } + + @override + ImageStreamCompleter loadImage( + LocalFullImageProvider key, + ImageDecoderCallback decode, + ) { + return MultiImageStreamCompleter( + codec: _codec(key, decode), + scale: 1.0, + informationCollector: () sync* { + yield ErrorDescription(asset.name); + }, + ); + } + + // Streams in each stage of the image as we ask for it + Stream _codec( + LocalFullImageProvider key, + ImageDecoderCallback decode, + ) async* { + try { + switch (key.asset.type) { + case AssetType.image: + yield* _decodeProgressive(key, decode); + break; + case AssetType.video: + final codec = await _getThumbnailCodec(key, decode); + if (codec == null) { + throw StateError("Failed to load preview for ${key.asset.name}"); + } + yield codec; + break; + case AssetType.other: + case AssetType.audio: + throw StateError('Unsupported asset type ${key.asset.type}'); + } + } catch (error, stack) { + Logger('ImmichLocalImageProvider') + .severe('Error loading local image ${key.asset.name}', error, stack); + throw const ImageLoadingException( + 'Could not load image from local storage', + ); + } + } + + Future _getThumbnailCodec( + LocalFullImageProvider key, + ImageDecoderCallback decode, + ) async { + final thumbBytes = + await _assetMediaRepository.getThumbnail(key.asset.id, size: key.size); + if (thumbBytes == null) { + return null; + } + final buffer = await ImmutableBuffer.fromUint8List(thumbBytes); + return decode(buffer); + } + + Stream _decodeProgressive( + LocalFullImageProvider key, + ImageDecoderCallback decode, + ) async* { + final file = await _storageRepository.getFileForAsset(key.asset.id); + if (file == null) { + throw StateError("Opening file for asset ${key.asset.name} failed"); + } + + final fileSize = await file.length(); + final devicePixelRatio = + PlatformDispatcher.instance.views.first.devicePixelRatio; + final isLargeFile = fileSize > 20 * 1024 * 1024; // 20MB + final isHEIC = file.path.toLowerCase().contains(RegExp(r'\.(heic|heif)$')); + final isProgressive = isLargeFile || (isHEIC && !Platform.isIOS); + + if (isProgressive) { + try { + final progressiveMultiplier = devicePixelRatio >= 3.0 ? 1.3 : 1.2; + final size = Size( + (key.size.width * progressiveMultiplier).clamp(256, 1024), + (key.size.height * progressiveMultiplier).clamp(256, 1024), + ); + final mediumThumb = + await _assetMediaRepository.getThumbnail(key.asset.id, size: size); + if (mediumThumb != null) { + final mediumBuffer = await ImmutableBuffer.fromUint8List(mediumThumb); + yield await decode(mediumBuffer); + } + } catch (_) {} + } + + // Load original only when the file is smaller or if the user wants to load original images + // Or load a slightly larger image for progressive loading + if (isProgressive && !(AppSetting.get(Setting.loadOriginal))) { + final progressiveMultiplier = devicePixelRatio >= 3.0 ? 2.0 : 1.6; + final size = Size( + (key.size.width * progressiveMultiplier).clamp(512, 2048), + (key.size.height * progressiveMultiplier).clamp(512, 2048), + ); + final highThumb = + await _assetMediaRepository.getThumbnail(key.asset.id, size: size); + if (highThumb != null) { + final highBuffer = await ImmutableBuffer.fromUint8List(highThumb); + yield await decode(highBuffer); + } + return; + } + + final buffer = await ImmutableBuffer.fromFilePath(file.path); + yield await decode(buffer); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is LocalFullImageProvider) { + return asset.id == other.asset.id && + asset.updatedAt == other.asset.updatedAt && + size == other.size; + } + return false; + } + + @override + int get hashCode => + asset.id.hashCode ^ asset.updatedAt.hashCode ^ size.hashCode; +} diff --git a/mobile/lib/presentation/widgets/images/remote_image_provider.dart b/mobile/lib/presentation/widgets/images/remote_image_provider.dart new file mode 100644 index 0000000000..14d13a08d8 --- /dev/null +++ b/mobile/lib/presentation/widgets/images/remote_image_provider.dart @@ -0,0 +1,142 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/painting.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/domain/services/setting.service.dart'; +import 'package:immich_mobile/providers/image/cache/image_loader.dart'; +import 'package:immich_mobile/providers/image/cache/remote_image_cache_manager.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; + +class RemoteThumbProvider extends ImageProvider { + final String assetId; + final CacheManager? cacheManager; + + const RemoteThumbProvider({ + required this.assetId, + this.cacheManager, + }); + + @override + Future obtainKey(ImageConfiguration configuration) { + return SynchronousFuture(this); + } + + @override + ImageStreamCompleter loadImage( + RemoteThumbProvider key, + ImageDecoderCallback decode, + ) { + final cache = cacheManager ?? RemoteImageCacheManager(); + final chunkController = StreamController(); + return MultiFrameImageStreamCompleter( + codec: _codec(key, cache, decode, chunkController), + scale: 1.0, + chunkEvents: chunkController.stream, + informationCollector: () => [ + DiagnosticsProperty('Image provider', this), + DiagnosticsProperty('Asset Id', key.assetId), + ], + ); + } + + Future _codec( + RemoteThumbProvider key, + CacheManager cache, + ImageDecoderCallback decode, + StreamController chunkController, + ) async { + final preview = getThumbnailUrlForRemoteId( + key.assetId, + ); + + return ImageLoader.loadImageFromCache( + preview, + cache: cache, + decode: decode, + chunkEvents: chunkController, + ).whenComplete(chunkController.close); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is RemoteThumbProvider) { + return assetId == other.assetId; + } + + return false; + } + + @override + int get hashCode => assetId.hashCode; +} + +class RemoteFullImageProvider extends ImageProvider { + final String assetId; + final CacheManager? cacheManager; + + const RemoteFullImageProvider({ + required this.assetId, + this.cacheManager, + }); + + @override + Future obtainKey(ImageConfiguration configuration) { + return SynchronousFuture(this); + } + + @override + ImageStreamCompleter loadImage( + RemoteFullImageProvider key, + ImageDecoderCallback decode, + ) { + final cache = cacheManager ?? RemoteImageCacheManager(); + final chunkEvents = StreamController(); + return MultiImageStreamCompleter( + codec: _codec(key, cache, decode, chunkEvents), + scale: 1.0, + chunkEvents: chunkEvents.stream, + ); + } + + Stream _codec( + RemoteFullImageProvider key, + CacheManager cache, + ImageDecoderCallback decode, + StreamController chunkController, + ) async* { + yield await ImageLoader.loadImageFromCache( + getPreviewUrlForRemoteId(key.assetId), + cache: cache, + decode: decode, + chunkEvents: chunkController, + ); + + if (AppSetting.get(Setting.loadOriginal)) { + yield await ImageLoader.loadImageFromCache( + getOriginalUrlForRemoteId(key.assetId), + cache: cache, + decode: decode, + chunkEvents: chunkController, + ); + } + await chunkController.close(); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is RemoteFullImageProvider) { + return assetId == other.assetId; + } + + return false; + } + + @override + int get hashCode => assetId.hashCode; +} diff --git a/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart b/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart new file mode 100644 index 0000000000..cd286a4cdf --- /dev/null +++ b/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart @@ -0,0 +1,50 @@ +import 'dart:convert' hide Codec; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; +import 'package:thumbhash/thumbhash.dart'; + +class ThumbHashProvider extends ImageProvider { + final String thumbHash; + + const ThumbHashProvider({ + required this.thumbHash, + }); + + @override + Future obtainKey(ImageConfiguration configuration) { + return SynchronousFuture(this); + } + + @override + ImageStreamCompleter loadImage( + ThumbHashProvider key, + ImageDecoderCallback decode, + ) { + return MultiFrameImageStreamCompleter( + codec: _loadCodec(key, decode), + scale: 1.0, + ); + } + + Future _loadCodec( + ThumbHashProvider key, + ImageDecoderCallback decode, + ) async { + final image = thumbHashToRGBA(base64Decode(key.thumbHash)); + return decode(await ImmutableBuffer.fromUint8List(rgbaToBmp(image))); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is ThumbHashProvider) { + return thumbHash == other.thumbHash; + } + return false; + } + + @override + int get hashCode => thumbHash.hashCode; +} diff --git a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart new file mode 100644 index 0000000000..f54c32dac1 --- /dev/null +++ b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumb_hash_provider.dart'; +import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart'; +import 'package:immich_mobile/widgets/common/fade_in_placeholder_image.dart'; +import 'package:logging/logging.dart'; +import 'package:octo_image/octo_image.dart'; + +class Thumbnail extends StatelessWidget { + const Thumbnail({ + this.asset, + this.remoteId, + this.size = const Size.square(256), + this.fit = BoxFit.cover, + super.key, + }) : assert( + asset != null || remoteId != null, + 'Either asset or remoteId must be provided', + ); + + final BaseAsset? asset; + final String? remoteId; + final Size size; + final BoxFit fit; + + @override + Widget build(BuildContext context) { + final thumbHash = + asset is RemoteAsset ? (asset as RemoteAsset).thumbHash : null; + final provider = + getThumbnailImageProvider(asset: asset, remoteId: remoteId, size: size); + + return OctoImage.fromSet( + image: provider, + octoSet: OctoSet( + placeholderBuilder: _blurHashPlaceholderBuilder(thumbHash, fit: fit), + errorBuilder: _blurHashErrorBuilder( + thumbHash, + provider: provider, + fit: fit, + asset: asset, + ), + ), + fadeOutDuration: const Duration(milliseconds: 100), + fadeInDuration: Duration.zero, + width: size.width, + height: size.height, + fit: fit, + placeholderFadeInDuration: Duration.zero, + ); + } +} + +OctoPlaceholderBuilder _blurHashPlaceholderBuilder( + String? thumbHash, { + BoxFit? fit, +}) { + return (context) => thumbHash == null + ? const ThumbnailPlaceholder() + : FadeInPlaceholderImage( + placeholder: const ThumbnailPlaceholder(), + image: ThumbHashProvider(thumbHash: thumbHash), + fit: fit ?? BoxFit.cover, + ); +} + +OctoErrorBuilder _blurHashErrorBuilder( + String? blurhash, { + BaseAsset? asset, + ImageProvider? provider, + BoxFit? fit, +}) => + (context, e, s) { + Logger("ImThumbnail") + .warning("Error loading thumbnail for ${asset?.name}", e, s); + provider?.evict(); + return Stack( + alignment: Alignment.center, + children: [ + _blurHashPlaceholderBuilder(blurhash, fit: fit)(context), + const Opacity( + opacity: 0.75, + child: Icon(Icons.error_outline_rounded), + ), + ], + ); + }; diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart new file mode 100644 index 0000000000..d7261c927e --- /dev/null +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -0,0 +1,235 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/duration_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; + +class ThumbnailTile extends ConsumerWidget { + const ThumbnailTile( + this.asset, { + this.size = const Size.square(256), + this.fit = BoxFit.cover, + this.showStorageIndicator = true, + this.lockSelection = false, + super.key, + }); + + final BaseAsset asset; + final Size size; + final BoxFit fit; + final bool showStorageIndicator; + final bool lockSelection; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final assetContainerColor = context.isDarkTheme + ? context.primaryColor.darken(amount: 0.4) + : context.primaryColor.lighten(amount: 0.75); + + final isSelected = ref.watch( + multiSelectProvider.select( + (multiselect) => multiselect.selectedAssets.contains(asset), + ), + ); + + final borderStyle = lockSelection + ? BoxDecoration( + color: context.colorScheme.surfaceContainerHighest, + border: Border.all( + color: context.colorScheme.surfaceContainerHighest, + width: 6, + ), + ) + : isSelected + ? BoxDecoration( + color: assetContainerColor, + border: Border.all(color: assetContainerColor, width: 6), + ) + : const BoxDecoration(); + + return Stack( + children: [ + AnimatedContainer( + duration: Durations.short4, + curve: Curves.decelerate, + decoration: borderStyle, + child: ClipRRect( + borderRadius: isSelected || lockSelection + ? const BorderRadius.all(Radius.circular(15.0)) + : BorderRadius.zero, + child: Stack( + children: [ + Positioned.fill( + child: Hero( + tag: asset.heroTag, + child: Thumbnail( + asset: asset, + fit: fit, + size: size, + ), + ), + ), + if (asset.isVideo) + Align( + alignment: Alignment.topRight, + child: Padding( + padding: const EdgeInsets.only(right: 10.0, top: 6.0), + child: _VideoIndicator(asset.duration), + ), + ), + if (showStorageIndicator) + switch (asset.storage) { + AssetState.local => const Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_off_outlined), + ), + ), + AssetState.remote => const Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_outlined), + ), + ), + AssetState.merged => const Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_done_outlined), + ), + ), + }, + if (asset.isFavorite) + const Align( + alignment: Alignment.bottomLeft, + child: Padding( + padding: EdgeInsets.only(left: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.favorite_rounded), + ), + ), + ], + ), + ), + ), + if (isSelected || lockSelection) + Padding( + padding: const EdgeInsets.all(3.0), + child: Align( + alignment: Alignment.topLeft, + child: _SelectionIndicator( + isSelected: isSelected, + isLocked: lockSelection, + color: lockSelection + ? context.colorScheme.surfaceContainerHighest + : assetContainerColor, + ), + ), + ), + ], + ); + } +} + +class _SelectionIndicator extends StatelessWidget { + final bool isSelected; + final bool isLocked; + final Color? color; + + const _SelectionIndicator({ + required this.isSelected, + required this.isLocked, + this.color, + }); + + @override + Widget build(BuildContext context) { + if (isLocked) { + return DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: color, + ), + child: const Icon( + Icons.check_circle_rounded, + color: Colors.grey, + ), + ); + } else if (isSelected) { + return DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: color, + ), + child: Icon( + Icons.check_circle_rounded, + color: context.primaryColor, + ), + ); + } else { + return const Icon( + Icons.circle_outlined, + color: Colors.white, + ); + } + } +} + +class _VideoIndicator extends StatelessWidget { + final Duration duration; + const _VideoIndicator(this.duration); + + @override + Widget build(BuildContext context) { + return Row( + spacing: 3, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + // CrossAxisAlignment.end looks more centered vertically than CrossAxisAlignment.center + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + duration.format(), + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.bold, + shadows: [ + Shadow( + blurRadius: 5.0, + color: Color.fromRGBO(0, 0, 0, 0.6), + ), + ], + ), + ), + const _TileOverlayIcon(Icons.play_circle_outline_rounded), + ], + ); + } +} + +class _TileOverlayIcon extends StatelessWidget { + final IconData icon; + + const _TileOverlayIcon(this.icon); + + @override + Widget build(BuildContext context) { + return Icon( + icon, + color: Colors.white, + size: 16, + shadows: [ + const Shadow( + blurRadius: 5.0, + color: Color.fromRGBO(0, 0, 0, 0.6), + offset: Offset(0.0, 0.0), + ), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart b/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart new file mode 100644 index 0000000000..79e6288a72 --- /dev/null +++ b/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart @@ -0,0 +1,64 @@ +// ignore_for_file: require_trailing_commas + +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/domain/models/memory.model.dart'; + +import 'package:immich_mobile/providers/asset_viewer/scroll_to_date_notifier.provider.dart'; + +class DriftMemoryBottomInfo extends StatelessWidget { + final DriftMemory memory; + final String title; + const DriftMemoryBottomInfo({ + super.key, + required this.memory, + required this.title, + }); + + @override + Widget build(BuildContext context) { + final df = DateFormat.yMMMMd(); + final fileCreatedDate = memory.assets.first.createdAt; + return Padding( + padding: const EdgeInsets.all(16.0), + child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + color: Colors.grey[400], + fontSize: 13.0, + fontWeight: FontWeight.w500, + ), + ), + Text( + df.format(fileCreatedDate), + style: const TextStyle( + color: Colors.white, + fontSize: 15.0, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + MaterialButton( + minWidth: 0, + onPressed: () { + context.maybePop(); + scrollToDateNotifierProvider.scrollToDate(fileCreatedDate); + }, + shape: const CircleBorder(), + color: Colors.white.withValues(alpha: 0.2), + elevation: 0, + child: const Icon( + Icons.open_in_new, + color: Colors.white, + ), + ), + ]), + ); + } +} diff --git a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart new file mode 100644 index 0000000000..a6262bd2e5 --- /dev/null +++ b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart @@ -0,0 +1,154 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart'; +import 'package:immich_mobile/presentation/widgets/images/full_image.widget.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/utils/hooks/blurhash_hook.dart'; + +class DriftMemoryCard extends StatelessWidget { + final RemoteAsset asset; + final String title; + final bool showTitle; + final Function()? onVideoEnded; + + const DriftMemoryCard({ + required this.asset, + required this.title, + required this.showTitle, + this.onVideoEnded, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Card( + color: Colors.black, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(25.0)), + side: BorderSide( + color: Colors.black, + width: 1.0, + ), + ), + clipBehavior: Clip.hardEdge, + child: Stack( + children: [ + SizedBox.expand( + child: _BlurredBackdrop(asset: asset), + ), + LayoutBuilder( + builder: (context, constraints) { + // Determine the fit using the aspect ratio + BoxFit fit = BoxFit.contain; + if (asset.width != null && asset.height != null) { + final aspectRatio = asset.width! / asset.height!; + final phoneAspectRatio = + constraints.maxWidth / constraints.maxHeight; + // Look for a 25% difference in either direction + if (phoneAspectRatio * .75 < aspectRatio && + phoneAspectRatio * 1.25 > aspectRatio) { + // Cover to look nice if we have nearly the same aspect ratio + fit = BoxFit.cover; + } + } + + if (asset.isImage) { + return Hero( + tag: 'memory-${asset.id}', + child: FullImage( + asset, + fit: fit, + size: const Size(double.infinity, double.infinity), + ), + ); + } else { + return Hero( + tag: 'memory-${asset.id}', + child: SizedBox( + width: context.width, + height: context.height, + child: NativeVideoViewer( + key: ValueKey(asset.id), + asset: asset, + showControls: false, + playbackDelayFactor: 2, + image: FullImage( + asset, + size: Size(context.width, context.height), + fit: BoxFit.contain, + ), + ), + ), + ); + } + }, + ), + if (showTitle) + Positioned( + left: 18.0, + bottom: 18.0, + child: Text( + title, + style: context.textTheme.headlineMedium?.copyWith( + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + ); + } +} + +class _BlurredBackdrop extends HookWidget { + final RemoteAsset asset; + + const _BlurredBackdrop({required this.asset}); + + @override + Widget build(BuildContext context) { + final blurhash = useDriftBlurHashRef(asset).value; + if (blurhash != null) { + // Use a nice cheap blur hash image decoration + return Container( + decoration: BoxDecoration( + image: DecorationImage( + image: MemoryImage( + blurhash, + ), + fit: BoxFit.cover, + ), + ), + child: Container( + color: Colors.black.withValues(alpha: 0.2), + ), + ); + } else { + // Fall back to using a more expensive image filtered + // Since the ImmichImage is already precached, we can + // safely use that as the image provider + return ImageFiltered( + imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: getFullImageProvider( + asset, + size: Size(context.width, context.height), + ), + fit: BoxFit.cover, + ), + ), + child: Container( + color: Colors.black.withValues(alpha: 0.2), + ), + ), + ); + } + } +} diff --git a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart new file mode 100644 index 0000000000..aa21f36dd1 --- /dev/null +++ b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart @@ -0,0 +1,115 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; +import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; + +class DriftMemoryLane extends ConsumerWidget { + final List memories; + + const DriftMemoryLane({super.key, required this.memories}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ConstrainedBox( + constraints: const BoxConstraints( + maxHeight: 200, + ), + child: CarouselView( + itemExtent: 145.0, + shrinkExtent: 1.0, + elevation: 2, + backgroundColor: Colors.black, + overlayColor: WidgetStateProperty.all( + Colors.white.withValues(alpha: 0.1), + ), + onTap: (index) { + ref.read(hapticFeedbackProvider.notifier).heavyImpact(); + + if (memories[index].assets.isNotEmpty) { + final asset = memories[index].assets[0]; + ref.read(currentAssetNotifier.notifier).setAsset(asset); + + if (asset.isVideo) { + ref.read(videoPlaybackValueProvider.notifier).reset(); + } + } + + context.pushRoute( + DriftMemoryRoute( + memories: memories, + memoryIndex: index, + ), + ); + }, + children: + memories.map((memory) => DriftMemoryCard(memory: memory)).toList(), + ), + ); + } +} + +class DriftMemoryCard extends ConsumerWidget { + const DriftMemoryCard({ + super.key, + required this.memory, + }); + + final DriftMemory memory; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final yearsAgo = DateTime.now().year - memory.data.year; + final title = 'years_ago'.t( + context: context, + args: { + 'years': yearsAgo.toString(), + }, + ); + return Center( + child: Stack( + children: [ + ColorFiltered( + colorFilter: ColorFilter.mode( + Colors.black.withValues(alpha: 0.2), + BlendMode.darken, + ), + child: Hero( + tag: 'memory-${memory.assets[0].id}', + child: SizedBox( + width: 205, + height: 200, + child: Thumbnail( + remoteId: memory.assets[0].id, + fit: BoxFit.cover, + ), + ), + ), + ), + Positioned( + bottom: 16, + left: 16, + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 114, + ), + child: Text( + title, + style: const TextStyle( + fontWeight: FontWeight.w600, + color: Colors.white, + fontSize: 15, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/timeline/constants.dart b/mobile/lib/presentation/widgets/timeline/constants.dart new file mode 100644 index 0000000000..fb9034f179 --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/constants.dart @@ -0,0 +1,7 @@ +const double kTimelineHeaderExtent = 80.0; +const double kTimelineFixedTileExtent = 256; +const double kTimelineSpacing = 2.0; +const int kTimelineColumnCount = 3; + +const Duration kTimelineScrubberFadeInDuration = Duration(milliseconds: 300); +const Duration kTimelineScrubberFadeOutDuration = Duration(milliseconds: 800); diff --git a/mobile/lib/presentation/widgets/timeline/fixed/row.dart b/mobile/lib/presentation/widgets/timeline/fixed/row.dart new file mode 100644 index 0000000000..1062c00740 --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/fixed/row.dart @@ -0,0 +1,154 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +class FixedTimelineRow extends MultiChildRenderObjectWidget { + final double dimension; + final double spacing; + final TextDirection textDirection; + + const FixedTimelineRow({ + super.key, + required this.dimension, + required this.spacing, + required this.textDirection, + required super.children, + }); + + @override + RenderObject createRenderObject(BuildContext context) { + return RenderFixedRow( + dimension: dimension, + spacing: spacing, + textDirection: textDirection, + ); + } + + @override + void updateRenderObject(BuildContext context, RenderFixedRow renderObject) { + renderObject.dimension = dimension; + renderObject.spacing = spacing; + renderObject.textDirection = textDirection; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DoubleProperty('dimension', dimension)); + properties.add(DoubleProperty('spacing', spacing)); + properties.add(EnumProperty('textDirection', textDirection)); + } +} + +class _RowParentData extends ContainerBoxParentData {} + +class RenderFixedRow extends RenderBox + with + ContainerRenderObjectMixin, + RenderBoxContainerDefaultsMixin { + RenderFixedRow({ + List? children, + required double dimension, + required double spacing, + required TextDirection textDirection, + }) : _dimension = dimension, + _spacing = spacing, + _textDirection = textDirection { + addAll(children); + } + + double get dimension => _dimension; + double _dimension; + + set dimension(double value) { + if (_dimension == value) return; + _dimension = value; + markNeedsLayout(); + } + + double get spacing => _spacing; + double _spacing; + + set spacing(double value) { + if (_spacing == value) return; + _spacing = value; + markNeedsLayout(); + } + + TextDirection get textDirection => _textDirection; + TextDirection _textDirection; + + set textDirection(TextDirection value) { + if (_textDirection == value) return; + _textDirection = value; + markNeedsLayout(); + } + + @override + void setupParentData(RenderBox child) { + if (child.parentData is! _RowParentData) { + child.parentData = _RowParentData(); + } + } + + double get intrinsicWidth => + dimension * childCount + spacing * (childCount - 1); + + @override + double computeMinIntrinsicWidth(double height) => intrinsicWidth; + + @override + double computeMaxIntrinsicWidth(double height) => intrinsicWidth; + + @override + double computeMinIntrinsicHeight(double width) => dimension; + + @override + double computeMaxIntrinsicHeight(double width) => dimension; + + @override + double? computeDistanceToActualBaseline(TextBaseline baseline) { + return defaultComputeDistanceToHighestActualBaseline(baseline); + } + + @override + bool hitTestChildren(BoxHitTestResult result, {required Offset position}) { + return defaultHitTestChildren(result, position: position); + } + + @override + void paint(PaintingContext context, Offset offset) { + defaultPaint(context, offset); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DoubleProperty('dimension', dimension)); + properties.add(DoubleProperty('spacing', spacing)); + properties.add(EnumProperty('textDirection', textDirection)); + } + + @override + void performLayout() { + RenderBox? child = firstChild; + if (child == null) { + size = constraints.smallest; + return; + } + // Use the entire width of the parent for the row. + size = Size(constraints.maxWidth, dimension); + // Each tile is forced to be dimension x dimension. + final childConstraints = BoxConstraints.tight(Size(dimension, dimension)); + final flipMainAxis = textDirection == TextDirection.rtl; + Offset offset = Offset(flipMainAxis ? size.width - dimension : 0, 0); + final dx = (flipMainAxis ? -1 : 1) * (dimension + spacing); + // Layout each child horizontally. + while (child != null) { + child.layout(childConstraints, parentUsesSize: false); + final childParentData = child.parentData! as _RowParentData; + childParentData.offset = offset; + offset += Offset(dx, 0); + child = childParentData.nextSibling; + } + } +} diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart new file mode 100644 index 0000000000..14b1a4616d --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -0,0 +1,224 @@ +import 'dart:math' as math; + +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail_tile.widget.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/fixed/row.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/header.widget.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; + +class FixedSegment extends Segment { + final double tileHeight; + final int columnCount; + final double mainAxisExtend; + + const FixedSegment({ + required super.firstIndex, + required super.lastIndex, + required super.startOffset, + required super.endOffset, + required super.firstAssetIndex, + required super.bucket, + required this.tileHeight, + required this.columnCount, + required super.headerExtent, + required super.spacing, + required super.header, + }) : assert(tileHeight != 0), + mainAxisExtend = tileHeight + spacing; + + @override + double indexToLayoutOffset(int index) { + final relativeIndex = index - gridIndex; + return relativeIndex < 0 + ? startOffset + : gridOffset + (mainAxisExtend * relativeIndex); + } + + @override + int getMinChildIndexForScrollOffset(double scrollOffset) { + final adjustedOffset = scrollOffset - gridOffset; + if (!adjustedOffset.isFinite || adjustedOffset < 0) return firstIndex; + return gridIndex + (adjustedOffset / mainAxisExtend).floor(); + } + + @override + int getMaxChildIndexForScrollOffset(double scrollOffset) { + final adjustedOffset = scrollOffset - gridOffset; + if (!adjustedOffset.isFinite || adjustedOffset < 0) return firstIndex; + return gridIndex + (adjustedOffset / mainAxisExtend).ceil() - 1; + } + + @override + Widget builder(BuildContext context, int index) { + final rowIndexInSegment = index - (firstIndex + 1); + final assetIndex = rowIndexInSegment * columnCount; + final assetCount = bucket.assetCount; + final numberOfAssets = math.min(columnCount, assetCount - assetIndex); + + if (index == firstIndex) { + return TimelineHeader( + bucket: bucket, + header: header, + height: headerExtent, + assetOffset: firstAssetIndex, + ); + } + + return _FixedSegmentRow( + assetIndex: firstAssetIndex + assetIndex, + assetCount: numberOfAssets, + tileHeight: tileHeight, + spacing: spacing, + ); + } +} + +class _FixedSegmentRow extends ConsumerWidget { + final int assetIndex; + final int assetCount; + final double tileHeight; + final double spacing; + + const _FixedSegmentRow({ + required this.assetIndex, + required this.assetCount, + required this.tileHeight, + required this.spacing, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isScrubbing = + ref.watch(timelineStateProvider.select((s) => s.isScrubbing)); + final timelineService = ref.read(timelineServiceProvider); + + if (isScrubbing) { + return _buildPlaceholder(context); + } + + if (timelineService.hasRange(assetIndex, assetCount)) { + return _buildAssetRow( + context, + timelineService.getAssets(assetIndex, assetCount), + ); + } + + return FutureBuilder>( + future: timelineService.loadAssets(assetIndex, assetCount), + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return _buildPlaceholder(context); + } + return _buildAssetRow(context, snapshot.requireData); + }, + ); + } + + Widget _buildPlaceholder(BuildContext context) { + return SegmentBuilder.buildPlaceholder( + context, + assetCount, + size: Size.square(tileHeight), + spacing: spacing, + ); + } + + Widget _buildAssetRow(BuildContext context, List assets) { + return FixedTimelineRow( + dimension: tileHeight, + spacing: spacing, + textDirection: Directionality.of(context), + children: [ + for (int i = 0; i < assets.length; i++) + _AssetTileWidget( + key: ValueKey(assets[i].heroTag), + asset: assets[i], + assetIndex: assetIndex + i, + ), + ], + ); + } +} + +class _AssetTileWidget extends ConsumerWidget { + final BaseAsset asset; + final int assetIndex; + + const _AssetTileWidget({ + super.key, + required this.asset, + required this.assetIndex, + }); + + void _handleOnTap( + BuildContext ctx, + WidgetRef ref, + int assetIndex, + BaseAsset asset, + ) { + final multiSelectState = ref.read(multiSelectProvider); + + if (multiSelectState.forceEnable || multiSelectState.isEnabled) { + ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset); + } else { + ctx.pushRoute( + AssetViewerRoute( + initialIndex: assetIndex, + timelineService: ref.read(timelineServiceProvider), + ), + ); + } + } + + void _handleOnLongPress(WidgetRef ref, BaseAsset asset) { + final multiSelectState = ref.read(multiSelectProvider); + if (multiSelectState.isEnabled || multiSelectState.forceEnable) { + return; + } + + ref.read(hapticFeedbackProvider.notifier).heavyImpact(); + ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset); + } + + bool _getLockSelectionStatus(WidgetRef ref) { + final lockSelectionAssets = ref.read( + multiSelectProvider.select( + (state) => state.lockedSelectionAssets, + ), + ); + + if (lockSelectionAssets.isEmpty) { + return false; + } + + return lockSelectionAssets.contains(asset); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final lockSelection = _getLockSelectionStatus(ref); + + return RepaintBoundary( + child: GestureDetector( + onTap: () => lockSelection + ? null + : _handleOnTap(context, ref, assetIndex, asset), + onLongPress: () => + lockSelection ? null : _handleOnLongPress(ref, asset), + child: ThumbnailTile( + asset, + lockSelection: lockSelection, + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart new file mode 100644 index 0000000000..327e690267 --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart @@ -0,0 +1,75 @@ +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/fixed/segment.model.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart'; + +class FixedSegmentBuilder extends SegmentBuilder { + final double tileHeight; + final int columnCount; + + const FixedSegmentBuilder({ + required super.buckets, + required this.tileHeight, + required this.columnCount, + super.spacing, + super.groupBy, + }); + + List generate() { + final segments = []; + int firstIndex = 0; + double startOffset = 0; + int assetIndex = 0; + DateTime? previousDate; + + for (int i = 0; i < buckets.length; i++) { + final bucket = buckets[i]; + + final assetCount = bucket.assetCount; + final numberOfRows = (assetCount / columnCount).ceil(); + final segmentCount = numberOfRows + 1; + + final segmentFirstIndex = firstIndex; + firstIndex += segmentCount; + final segmentLastIndex = firstIndex - 1; + + final timelineHeader = switch (groupBy) { + GroupAssetsBy.month => HeaderType.month, + GroupAssetsBy.day => + bucket is TimeBucket && bucket.date.month != previousDate?.month + ? HeaderType.monthAndDay + : HeaderType.day, + GroupAssetsBy.none => HeaderType.none, + }; + final headerExtent = SegmentBuilder.headerExtent(timelineHeader); + + final segmentStartOffset = startOffset; + startOffset += headerExtent + + (tileHeight * numberOfRows) + + spacing * (numberOfRows - 1); + final segmentEndOffset = startOffset; + + segments.add( + FixedSegment( + firstIndex: segmentFirstIndex, + lastIndex: segmentLastIndex, + startOffset: segmentStartOffset, + endOffset: segmentEndOffset, + firstAssetIndex: assetIndex, + bucket: bucket, + tileHeight: tileHeight, + columnCount: columnCount, + headerExtent: headerExtent, + spacing: spacing, + header: timelineHeader, + ), + ); + + assetIndex += assetCount; + if (bucket is TimeBucket) { + previousDate = bucket.date; + } + } + return segments; + } +} diff --git a/mobile/lib/presentation/widgets/timeline/header.widget.dart b/mobile/lib/presentation/widgets/timeline/header.widget.dart new file mode 100644 index 0000000000..c6c10c26c9 --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/header.widget.dart @@ -0,0 +1,144 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; + +class TimelineHeader extends StatelessWidget { + final Bucket bucket; + final HeaderType header; + final double height; + final int assetOffset; + + const TimelineHeader({ + super.key, + required this.bucket, + required this.header, + required this.height, + required this.assetOffset, + }); + + String _formatMonth(BuildContext context, DateTime date) { + final formatter = date.year == DateTime.now().year + ? DateFormat.MMMM(context.locale.toLanguageTag()) + : DateFormat.yMMMM(context.locale.toLanguageTag()); + return formatter.format(date); + } + + String _formatDay(BuildContext context, DateTime date) { + final formatter = DateFormat.yMMMEd(context.locale.toLanguageTag()); + return formatter.format(date); + } + + @override + Widget build(BuildContext context) { + if (bucket is! TimeBucket || header == HeaderType.none) { + return const SizedBox.shrink(); + } + + final date = (bucket as TimeBucket).date; + + final isMonthHeader = + header == HeaderType.month || header == HeaderType.monthAndDay; + final isDayHeader = + header == HeaderType.day || header == HeaderType.monthAndDay; + + return Padding( + padding: EdgeInsets.only( + top: isMonthHeader ? 8.0 : 0.0, + left: 12.0, + right: 12.0, + ), + child: SizedBox( + height: height, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if (isMonthHeader) + Row( + children: [ + Text( + _formatMonth(context, date), + style: context.textTheme.labelLarge?.copyWith(fontSize: 24), + ), + const Spacer(), + if (header != HeaderType.monthAndDay) + _BulkSelectIconButton( + bucket: bucket, + assetOffset: assetOffset, + ), + ], + ), + if (isDayHeader) + Row( + children: [ + Text( + _formatDay(context, date), + style: context.textTheme.labelLarge?.copyWith( + fontSize: 15, + ), + ), + const Spacer(), + _BulkSelectIconButton( + bucket: bucket, + assetOffset: assetOffset, + ), + ], + ), + ], + ), + ), + ); + } +} + +class _BulkSelectIconButton extends ConsumerWidget { + final Bucket bucket; + final int assetOffset; + + const _BulkSelectIconButton({ + required this.bucket, + required this.assetOffset, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + List bucketAssets; + try { + bucketAssets = ref + .watch(timelineServiceProvider) + .getAssets(assetOffset, bucket.assetCount); + } catch (e) { + bucketAssets = []; + } + + final isAllSelected = ref.watch(bucketSelectionProvider(bucketAssets)); + + return IconButton( + onPressed: () { + ref.read(multiSelectProvider.notifier).toggleBucketSelection( + assetOffset, + bucket.assetCount, + ); + ref.read(hapticFeedbackProvider.notifier).heavyImpact(); + }, + icon: isAllSelected + ? Icon( + Icons.check_circle_rounded, + size: 26, + color: context.primaryColor, + ) + : Icon( + Icons.check_circle_outline_rounded, + size: 26, + color: context.colorScheme.onSurfaceSecondary, + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart new file mode 100644 index 0000000000..248fc6b579 --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart @@ -0,0 +1,640 @@ +import 'dart:async'; + +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:intl/intl.dart' hide TextDirection; + +/// A widget that will display a BoxScrollView with a ScrollThumb that can be dragged +/// for quick navigation of the BoxScrollView. +class Scrubber extends ConsumerStatefulWidget { + /// The view that will be scrolled with the scroll thumb + final CustomScrollView child; + + /// The segments of the timeline + final List layoutSegments; + + final double timelineHeight; + + final double topPadding; + + final double bottomPadding; + + final double? monthSegmentSnappingOffset; + + Scrubber({ + super.key, + Key? scrollThumbKey, + required this.layoutSegments, + required this.timelineHeight, + this.topPadding = 0, + this.bottomPadding = 0, + this.monthSegmentSnappingOffset, + required this.child, + }) : assert(child.scrollDirection == Axis.vertical); + + @override + ConsumerState createState() => ScrubberState(); +} + +List<_Segment> _buildSegments({ + required List layoutSegments, + required double timelineHeight, +}) { + const double offsetThreshold = 20.0; + + final segments = <_Segment>[]; + if (layoutSegments.isEmpty || layoutSegments.first.bucket is! TimeBucket) { + return []; + } + + final formatter = DateFormat.yMMM(); + DateTime? lastDate; + double lastOffset = -offsetThreshold; + for (final layoutSegment in layoutSegments) { + final scrollPercentage = + layoutSegment.startOffset / layoutSegments.last.endOffset; + final startOffset = scrollPercentage * timelineHeight; + + final date = (layoutSegment.bucket as TimeBucket).date; + final label = formatter.format(date); + + final showSegment = lastOffset + offsetThreshold <= startOffset && + (lastDate == null || date.year != lastDate.year); + + segments.add( + _Segment( + date: date, + startOffset: startOffset, + scrollLabel: label, + showSegment: showSegment, + ), + ); + lastDate = date; + if (showSegment) { + lastOffset = startOffset; + } + } + + return segments; +} + +class ScrubberState extends ConsumerState + with TickerProviderStateMixin { + double _thumbTopOffset = 0.0; + bool _isDragging = false; + List<_Segment> _segments = []; + + late AnimationController _thumbAnimationController; + Timer? _fadeOutTimer; + late Animation _thumbAnimation; + + late AnimationController _labelAnimationController; + late Animation _labelAnimation; + + double get _scrubberHeight => + widget.timelineHeight - widget.topPadding - widget.bottomPadding; + + late ScrollController _scrollController; + + double get _currentOffset { + if (_scrollController.hasClients != true) return 0.0; + + return _scrollController.offset * + _scrubberHeight / + _scrollController.position.maxScrollExtent; + } + + @override + void initState() { + super.initState(); + _isDragging = false; + _segments = _buildSegments( + layoutSegments: widget.layoutSegments, + timelineHeight: _scrubberHeight, + ); + _thumbAnimationController = AnimationController( + vsync: this, + duration: kTimelineScrubberFadeInDuration, + ); + _thumbAnimation = CurvedAnimation( + parent: _thumbAnimationController, + curve: Curves.fastEaseInToSlowEaseOut, + ); + _labelAnimationController = AnimationController( + vsync: this, + duration: kTimelineScrubberFadeInDuration, + ); + + _labelAnimation = CurvedAnimation( + parent: _labelAnimationController, + curve: Curves.fastOutSlowIn, + ); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _scrollController = PrimaryScrollController.of(context); + } + + @override + void didUpdateWidget(covariant Scrubber oldWidget) { + super.didUpdateWidget(oldWidget); + + if (oldWidget.layoutSegments.lastOrNull?.endOffset != + widget.layoutSegments.lastOrNull?.endOffset) { + _segments = _buildSegments( + layoutSegments: widget.layoutSegments, + timelineHeight: _scrubberHeight, + ); + } + } + + @override + void dispose() { + _thumbAnimationController.dispose(); + _labelAnimationController.dispose(); + _fadeOutTimer?.cancel(); + super.dispose(); + } + + void _resetThumbTimer() { + _fadeOutTimer?.cancel(); + _fadeOutTimer = Timer(kTimelineScrubberFadeOutDuration, () { + _thumbAnimationController.reverse(); + _fadeOutTimer = null; + }); + } + + bool _onScrollNotification(ScrollNotification notification) { + if (_isDragging) { + // If the user is dragging the thumb, we don't want to update the position + return false; + } + + if (notification is ScrollStartNotification || + notification is ScrollUpdateNotification) { + ref.read(timelineStateProvider.notifier).setScrolling(true); + } else if (notification is ScrollEndNotification) { + ref.read(timelineStateProvider.notifier).setScrolling(false); + } + + setState(() { + if (notification is ScrollUpdateNotification) { + _thumbTopOffset = _currentOffset; + if (_labelAnimation.status != AnimationStatus.reverse) { + _labelAnimationController.reverse(); + } + if (_thumbAnimationController.status != AnimationStatus.forward) { + _thumbAnimationController.forward(); + } + } + _resetThumbTimer(); + }); + + return false; + } + + void _onDragStart(DragStartDetails _) { + ref.read(timelineStateProvider.notifier).setScrubbing(true); + setState(() { + _isDragging = true; + _labelAnimationController.forward(); + _fadeOutTimer?.cancel(); + }); + } + + void _onDragUpdate(DragUpdateDetails details) { + if (!_isDragging) { + return; + } + + if (_thumbAnimationController.status != AnimationStatus.forward) { + _thumbAnimationController.forward(); + } + + final dragPosition = _calculateDragPosition(details); + final nearestMonthSegment = _findNearestMonthSegment(dragPosition); + + if (nearestMonthSegment != null) { + _snapToSegment(nearestMonthSegment); + } + } + + /// Calculate the drag position relative to the scrubber area + /// + /// This method converts the global drag coordinates from the gesture detector + /// into a position relative to the scrubber's active area (excluding padding). + /// + /// The scrubber has padding at the top and bottom, so we need to: + /// 1. Calculate the actual draggable area (timelineHeight - topPadding - bottomPadding) + /// 2. Convert the global Y position to a position within this draggable area + /// 3. Clamp the result to ensure it stays within bounds (0 to dragAreaHeight) + /// + /// Example: + /// - If timelineHeight = 800, topPadding = 50, bottomPadding = 50 + /// - Then dragAreaHeight = 700 (the actual scrubber area) + /// - If user drags to global Y position that's 100 pixels from the top + /// - The relative position would be 100 - 50 = 50 (50 pixels into the scrubber area) + double _calculateDragPosition(DragUpdateDetails details) { + final dragAreaTop = widget.topPadding; + final dragAreaBottom = widget.timelineHeight - widget.bottomPadding; + final dragAreaHeight = dragAreaBottom - dragAreaTop; + + final relativePosition = details.globalPosition.dy - dragAreaTop; + + // Make sure the position stays within the scrubber's bounds + return relativePosition.clamp(0.0, dragAreaHeight); + } + + /// Find the segment closest to the given position + _Segment? _findNearestMonthSegment(double position) { + _Segment? nearestSegment; + double minDistance = double.infinity; + + for (final segment in _segments) { + final distance = (segment.startOffset - position).abs(); + if (distance < minDistance) { + minDistance = distance; + nearestSegment = segment; + } + } + + return nearestSegment; + } + + /// Snap the scrubber thumb and scroll view to the given segment + void _snapToSegment(_Segment segment) { + setState(() { + _thumbTopOffset = segment.startOffset; + + final layoutSegmentIndex = _findLayoutSegmentIndex(segment); + + if (layoutSegmentIndex >= 0) { + _scrollToLayoutSegment(layoutSegmentIndex); + } + }); + } + + int _findLayoutSegmentIndex(_Segment segment) { + return widget.layoutSegments.indexWhere( + (layoutSegment) { + final bucket = layoutSegment.bucket as TimeBucket; + return bucket.date.year == segment.date.year && + bucket.date.month == segment.date.month; + }, + ); + } + + void _scrollToLayoutSegment(int layoutSegmentIndex) { + final layoutSegment = widget.layoutSegments[layoutSegmentIndex]; + final maxScrollExtent = _scrollController.position.maxScrollExtent; + final viewportHeight = _scrollController.position.viewportDimension; + + final targetScrollOffset = layoutSegment.startOffset; + final centeredOffset = targetScrollOffset - + (viewportHeight / 4) + + 100 + + (widget.monthSegmentSnappingOffset ?? 0.0); + + _scrollController.jumpTo(centeredOffset.clamp(0.0, maxScrollExtent)); + } + + void _onDragEnd(DragEndDetails _) { + ref.read(timelineStateProvider.notifier).setScrubbing(false); + _labelAnimationController.reverse(); + setState(() { + _isDragging = false; + }); + + _resetThumbTimer(); + } + + @override + Widget build(BuildContext ctx) { + Text? label; + if (_scrollController.hasClients == true) { + // Cache to avoid multiple calls to [_currentOffset] + final scrollOffset = _currentOffset; + final labelText = _segments + .lastWhereOrNull( + (segment) => segment.startOffset <= scrollOffset, + ) + ?.scrollLabel ?? + _segments.firstOrNull?.scrollLabel; + label = labelText != null + ? Text( + labelText, + style: ctx.textTheme.bodyLarge?.copyWith( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ) + : null; + } + + return NotificationListener( + onNotification: _onScrollNotification, + child: Stack( + children: [ + RepaintBoundary(child: widget.child), + // Scroll Segments - wrapped in RepaintBoundary for better performance + RepaintBoundary( + child: _SegmentsLayer( + key: ValueKey('segments_${_isDragging}_${_segments.length}'), + segments: _segments, + topPadding: widget.topPadding, + isDragging: _isDragging, + ), + ), + if (_scrollController.hasClients && + _scrollController.position.maxScrollExtent > 0) + PositionedDirectional( + top: _thumbTopOffset + widget.topPadding, + end: 0, + child: RepaintBoundary( + child: GestureDetector( + onVerticalDragStart: _onDragStart, + onVerticalDragUpdate: _onDragUpdate, + onVerticalDragEnd: _onDragEnd, + child: _Scrubber( + thumbAnimation: _thumbAnimation, + labelAnimation: _labelAnimation, + label: label, + ), + ), + ), + ), + ], + ), + ); + } +} + +class _SegmentsLayer extends StatelessWidget { + final List<_Segment> segments; + final double topPadding; + final bool isDragging; + + const _SegmentsLayer({ + super.key, + required this.segments, + required this.topPadding, + required this.isDragging, + }); + + @override + Widget build(BuildContext context) { + return Visibility( + visible: isDragging, + child: Stack( + children: segments + .where((segment) => segment.showSegment) + .map( + (segment) => PositionedDirectional( + key: ValueKey('segment_${segment.date.millisecondsSinceEpoch}'), + top: topPadding + segment.startOffset, + end: 100, + child: RepaintBoundary( + child: _SegmentWidget(segment), + ), + ), + ) + .toList(), + ), + ); + } +} + +class _SegmentWidget extends StatelessWidget { + final _Segment _segment; + + const _SegmentWidget(this._segment); + + @override + Widget build(BuildContext context) { + return IgnorePointer( + child: Container( + margin: const EdgeInsets.only(right: 12.0), + child: Material( + color: context.colorScheme.surface, + borderRadius: const BorderRadius.all(Radius.circular(16.0)), + child: Container( + constraints: const BoxConstraints(maxHeight: 28), + padding: const EdgeInsets.symmetric(horizontal: 10.0), + alignment: Alignment.center, + child: Text( + _segment.date.year.toString(), + style: context.textTheme.labelMedium?.copyWith( + fontFamily: "OverpassMono", + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ), + ); + } +} + +class _ScrollLabel extends StatelessWidget { + final Text label; + final Color backgroundColor; + final Animation animation; + + const _ScrollLabel({ + required this.label, + required this.backgroundColor, + required this.animation, + }); + + @override + Widget build(BuildContext context) { + return IgnorePointer( + child: FadeTransition( + opacity: animation, + child: Container( + margin: const EdgeInsets.only(right: 12.0), + child: Material( + elevation: 4.0, + color: backgroundColor, + borderRadius: const BorderRadius.all(Radius.circular(16.0)), + child: Container( + constraints: const BoxConstraints(maxHeight: 28), + padding: const EdgeInsets.symmetric(horizontal: 10.0), + alignment: Alignment.center, + child: label, + ), + ), + ), + ), + ); + } +} + +class _Scrubber extends StatelessWidget { + final Text? label; + final Animation thumbAnimation; + final Animation labelAnimation; + + const _Scrubber({ + this.label, + required this.thumbAnimation, + required this.labelAnimation, + }); + + @override + Widget build(BuildContext context) { + final backgroundColor = context.isDarkTheme + ? context.colorScheme.primary.darken(amount: .5) + : context.colorScheme.primary; + + return _SlideFadeTransition( + animation: thumbAnimation, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (label != null) + _ScrollLabel( + label: label!, + backgroundColor: backgroundColor, + animation: labelAnimation, + ), + _CircularThumb(backgroundColor), + ], + ), + ); + } +} + +class _CircularThumb extends StatelessWidget { + final Color backgroundColor; + + const _CircularThumb(this.backgroundColor); + + @override + Widget build(BuildContext context) { + return CustomPaint( + foregroundPainter: const _ArrowPainter(Colors.white), + child: Material( + elevation: 4.0, + color: backgroundColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(48.0), + bottomLeft: Radius.circular(48.0), + topRight: Radius.circular(4.0), + bottomRight: Radius.circular(4.0), + ), + child: Container( + constraints: BoxConstraints.tight(const Size(48.0 * 0.6, 48.0)), + ), + ), + ); + } +} + +class _ArrowPainter extends CustomPainter { + final Color color; + + const _ArrowPainter(this.color); + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint()..color = color; + const width = 12.0; + const height = 8.0; + final baseX = size.width / 2; + final baseY = size.height / 2; + + canvas.drawPath( + _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), + paint, + ); + canvas.drawPath( + _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), + paint, + ); + } + + static Path _trianglePath(Offset o, double width, double height, bool isUp) { + return Path() + ..moveTo(o.dx, o.dy) + ..lineTo(o.dx + width, o.dy) + ..lineTo(o.dx + (width / 2), isUp ? o.dy - height : o.dy + height) + ..close(); + } +} + +class _SlideFadeTransition extends StatelessWidget { + final Animation _animation; + final Widget _child; + + const _SlideFadeTransition({ + required Animation animation, + required Widget child, + }) : _animation = animation, + _child = child; + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _animation, + builder: (context, child) => + _animation.value == 0.0 ? const SizedBox() : child!, + child: SlideTransition( + position: Tween( + begin: const Offset(0.3, 0.0), + end: const Offset(0.0, 0.0), + ).animate(_animation), + child: FadeTransition( + opacity: _animation, + child: _child, + ), + ), + ); + } +} + +class _Segment { + final DateTime date; + final double startOffset; + final String scrollLabel; + final bool showSegment; + + const _Segment({ + required this.date, + required this.startOffset, + required this.scrollLabel, + this.showSegment = false, + }); + + _Segment copyWith({ + DateTime? date, + double? startOffset, + String? scrollLabel, + bool? showSegment, + }) { + return _Segment( + date: date ?? this.date, + startOffset: startOffset ?? this.startOffset, + scrollLabel: scrollLabel ?? this.scrollLabel, + showSegment: showSegment ?? this.showSegment, + ); + } + + @override + String toString() { + return 'Segment(scrollLabel: $scrollLabel, date: $date)'; + } +} diff --git a/mobile/lib/presentation/widgets/timeline/segment.model.dart b/mobile/lib/presentation/widgets/timeline/segment.model.dart new file mode 100644 index 0000000000..09d892f69a --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/segment.model.dart @@ -0,0 +1,100 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/widgets.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; + +// Segments are the time groups buckets in the timeline view. +// Each segment contains a header and a list of asset rows. +abstract class Segment { + // The index of the first row of the segment, usually the header, but if not, it can be any asset. + final int firstIndex; + // The index of the last asset of the segment. + final int lastIndex; + // The offset of the first row from beginning of the timeline. + final double startOffset; + // The offset of the last row from beginning of the timeline. + final double endOffset; + // The spacing between the header and the first row of the segment. + final double spacing; + final double headerExtent; + // the start index of the asset of this segment from the beginning of the timeline. + final int firstAssetIndex; + final Bucket bucket; + + // The index of the row after the header + final int gridIndex; + // The offset of the row after the header + final double gridOffset; + // The type of the header + final HeaderType header; + + const Segment({ + required this.firstIndex, + required this.lastIndex, + required this.startOffset, + required this.endOffset, + required this.firstAssetIndex, + required this.bucket, + required this.headerExtent, + required this.spacing, + required this.header, + }) : gridIndex = firstIndex + 1, + gridOffset = startOffset + headerExtent + spacing; + + bool containsIndex(int index) => firstIndex <= index && index <= lastIndex; + + bool isWithinOffset(double offset) => + startOffset <= offset && offset <= endOffset; + + int getMinChildIndexForScrollOffset(double scrollOffset); + int getMaxChildIndexForScrollOffset(double scrollOffset); + double indexToLayoutOffset(int index); + + Widget builder(BuildContext context, int index); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Segment && + other.firstIndex == firstIndex && + other.lastIndex == lastIndex && + other.startOffset == startOffset && + other.endOffset == endOffset && + other.spacing == spacing && + other.firstAssetIndex == firstAssetIndex && + other.headerExtent == headerExtent && + other.gridIndex == gridIndex && + other.gridOffset == gridOffset && + other.header == header; + } + + @override + int get hashCode => + firstIndex.hashCode ^ + lastIndex.hashCode ^ + startOffset.hashCode ^ + endOffset.hashCode ^ + spacing.hashCode ^ + headerExtent.hashCode ^ + firstAssetIndex.hashCode ^ + gridIndex.hashCode ^ + gridOffset.hashCode ^ + header.hashCode; + + @override + String toString() { + return 'Segment(firstIndex: $firstIndex, lastIndex: $lastIndex)'; + } +} + +extension SegmentListExtension on List { + bool equals(List other) => + length == other.length && + lastOrNull?.endOffset == other.lastOrNull?.endOffset; + + Segment? findByIndex(int index) => + firstWhereOrNull((s) => s.containsIndex(index)); + + Segment? findByOffset(double offset) => + firstWhereOrNull((s) => s.isWithinOffset(offset)) ?? lastOrNull; +} diff --git a/mobile/lib/presentation/widgets/timeline/segment_builder.dart b/mobile/lib/presentation/widgets/timeline/segment_builder.dart new file mode 100644 index 0000000000..a746eab243 --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/segment_builder.dart @@ -0,0 +1,42 @@ +import 'package:flutter/widgets.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/fixed/row.dart'; +import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart'; + +abstract class SegmentBuilder { + final List buckets; + final double spacing; + final GroupAssetsBy groupBy; + + const SegmentBuilder({ + required this.buckets, + this.spacing = kTimelineSpacing, + this.groupBy = GroupAssetsBy.day, + }); + + static double headerExtent(HeaderType header) => switch (header) { + HeaderType.month => kTimelineHeaderExtent, + HeaderType.day => kTimelineHeaderExtent * 0.90, + HeaderType.monthAndDay => kTimelineHeaderExtent * 1.6, + HeaderType.none => 0.0, + }; + + static Widget buildPlaceholder( + BuildContext context, + int count, { + Size size = const Size.square(kTimelineFixedTileExtent), + double spacing = kTimelineSpacing, + }) => + RepaintBoundary( + child: FixedTimelineRow( + dimension: size.height, + spacing: spacing, + textDirection: Directionality.of(context), + children: List.generate( + count, + (_) => ThumbnailPlaceholder(width: size.width, height: size.height), + ), + ), + ); +} diff --git a/mobile/lib/presentation/widgets/timeline/timeline.state.dart b/mobile/lib/presentation/widgets/timeline/timeline.state.dart new file mode 100644 index 0000000000..629fac7831 --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/timeline.state.dart @@ -0,0 +1,116 @@ +import 'dart:math' as math; + +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/fixed/segment_builder.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; + +class TimelineArgs { + final double maxWidth; + final double maxHeight; + final double spacing; + final int columnCount; + + const TimelineArgs({ + required this.maxWidth, + required this.maxHeight, + this.spacing = kTimelineSpacing, + this.columnCount = kTimelineColumnCount, + }); + + @override + bool operator ==(covariant TimelineArgs other) { + return spacing == other.spacing && + maxWidth == other.maxWidth && + maxHeight == other.maxHeight && + columnCount == other.columnCount; + } + + @override + int get hashCode => + maxWidth.hashCode ^ + maxHeight.hashCode ^ + spacing.hashCode ^ + columnCount.hashCode; +} + +class TimelineState { + final bool isScrubbing; + final bool isScrolling; + + const TimelineState({ + this.isScrubbing = false, + this.isScrolling = false, + }); + + bool get isInteracting => isScrubbing || isScrolling; + + @override + bool operator ==(covariant TimelineState other) { + return isScrubbing == other.isScrubbing && isScrolling == other.isScrolling; + } + + @override + int get hashCode => isScrubbing.hashCode ^ isScrolling.hashCode; + + TimelineState copyWith({bool? isScrubbing, bool? isScrolling}) { + return TimelineState( + isScrubbing: isScrubbing ?? this.isScrubbing, + isScrolling: isScrolling ?? this.isScrolling, + ); + } +} + +class TimelineStateNotifier extends Notifier { + TimelineStateNotifier(); + + void setScrubbing(bool isScrubbing) { + state = state.copyWith(isScrubbing: isScrubbing); + } + + void setScrolling(bool isScrolling) { + state = state.copyWith(isScrolling: isScrolling); + } + + @override + TimelineState build() => const TimelineState( + isScrubbing: false, + isScrolling: false, + ); +} + +// This provider watches the buckets from the timeline service & args and serves the segments. +// It should be used only after the timeline service and timeline args provider is overridden +final timelineSegmentProvider = StreamProvider.autoDispose>( + (ref) async* { + final args = ref.watch(timelineArgsProvider); + final columnCount = args.columnCount; + final spacing = args.spacing; + final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1)); + final tileExtent = math.max(0, availableTileWidth) / columnCount; + + final groupBy = GroupAssetsBy + .values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)]; + + final timelineService = ref.watch(timelineServiceProvider); + yield* timelineService.watchBuckets().map((buckets) { + return FixedSegmentBuilder( + buckets: buckets, + tileHeight: tileExtent, + columnCount: columnCount, + spacing: spacing, + groupBy: groupBy, + ).generate(); + }); + }, + dependencies: [timelineServiceProvider, timelineArgsProvider], +); + +final timelineStateProvider = + NotifierProvider( + TimelineStateNotifier.new, +); diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart new file mode 100644 index 0000000000..9fb164e2dc --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -0,0 +1,478 @@ +import 'dart:async'; +import 'dart:math' as math; + +import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_app_bar/home_bottom_app_bar.widget.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/scrubber.widget.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart'; +import 'package:immich_mobile/widgets/common/selection_sliver_app_bar.dart'; + +class Timeline extends StatelessWidget { + const Timeline({ + super.key, + this.topSliverWidget, + this.topSliverWidgetHeight, + }); + + final Widget? topSliverWidget; + final double? topSliverWidgetHeight; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: LayoutBuilder( + builder: (_, constraints) => ProviderScope( + overrides: [ + timelineArgsProvider.overrideWith( + (ref) => TimelineArgs( + maxWidth: constraints.maxWidth, + maxHeight: constraints.maxHeight, + columnCount: ref.watch( + settingsProvider.select((s) => s.get(Setting.tilesPerRow)), + ), + ), + ), + ], + child: _SliverTimeline( + topSliverWidget: topSliverWidget, + topSliverWidgetHeight: topSliverWidgetHeight, + ), + ), + ), + ); + } +} + +class _SliverTimeline extends ConsumerStatefulWidget { + const _SliverTimeline({ + this.topSliverWidget, + this.topSliverWidgetHeight, + }); + + final Widget? topSliverWidget; + final double? topSliverWidgetHeight; + + @override + ConsumerState createState() => _SliverTimelineState(); +} + +class _SliverTimelineState extends ConsumerState<_SliverTimeline> { + final _scrollController = ScrollController(); + StreamSubscription? _reloadSubscription; + + @override + void initState() { + super.initState(); + _reloadSubscription = + EventStream.shared.listen((_) => setState(() {})); + } + + @override + void dispose() { + _scrollController.dispose(); + _reloadSubscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext _) { + final asyncSegments = ref.watch(timelineSegmentProvider); + final maxHeight = + ref.watch(timelineArgsProvider.select((args) => args.maxHeight)); + final isSelectionMode = ref.watch( + multiSelectProvider.select((s) => s.forceEnable), + ); + + return asyncSegments.widgetWhen( + onData: (segments) { + final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1; + final statusBarHeight = context.padding.top; + final totalAppBarHeight = statusBarHeight + kToolbarHeight; + const scrubberBottomPadding = 100.0; + + return PrimaryScrollController( + controller: _scrollController, + child: Stack( + children: [ + Scrubber( + layoutSegments: segments, + timelineHeight: maxHeight, + topPadding: totalAppBarHeight + 10, + bottomPadding: context.padding.bottom + scrubberBottomPadding, + monthSegmentSnappingOffset: widget.topSliverWidgetHeight, + child: CustomScrollView( + primary: true, + cacheExtent: maxHeight * 2, + slivers: [ + if (isSelectionMode) + const SelectionSliverAppBar() + else + const ImmichSliverAppBar( + floating: true, + pinned: false, + snap: false, + ), + if (widget.topSliverWidget != null) widget.topSliverWidget!, + _SliverSegmentedList( + segments: segments, + delegate: SliverChildBuilderDelegate( + (ctx, index) { + if (index >= childCount) return null; + final segment = segments.findByIndex(index); + return segment?.builder(ctx, index) ?? + const SizedBox.shrink(); + }, + childCount: childCount, + addAutomaticKeepAlives: false, + // We add repaint boundary around tiles, so skip the auto boundaries + addRepaintBoundaries: false, + ), + ), + const SliverPadding( + padding: EdgeInsets.only( + bottom: scrubberBottomPadding, + ), + ), + ], + ), + ), + if (!isSelectionMode) ...[ + Consumer( + builder: (_, consumerRef, child) { + final isMultiSelectEnabled = consumerRef.watch( + multiSelectProvider.select( + (s) => s.isEnabled, + ), + ); + + if (isMultiSelectEnabled) { + return child!; + } + return const SizedBox.shrink(); + }, + child: const Positioned( + top: 60, + left: 25, + child: _MultiSelectStatusButton(), + ), + ), + Consumer( + builder: (_, consumerRef, child) { + final isMultiSelectEnabled = consumerRef.watch( + multiSelectProvider.select( + (s) => s.isEnabled, + ), + ); + + if (isMultiSelectEnabled) { + return child!; + } + return const SizedBox.shrink(); + }, + child: const HomeBottomAppBar(), + ), + ], + ], + ), + ); + }, + ); + } +} + +class _SliverSegmentedList extends SliverMultiBoxAdaptorWidget { + final List _segments; + + const _SliverSegmentedList({ + required List segments, + required super.delegate, + }) : _segments = segments; + + @override + _RenderSliverTimelineBoxAdaptor createRenderObject(BuildContext context) => + _RenderSliverTimelineBoxAdaptor( + childManager: context as SliverMultiBoxAdaptorElement, + segments: _segments, + ); + + @override + void updateRenderObject( + BuildContext context, + _RenderSliverTimelineBoxAdaptor renderObject, + ) { + renderObject.segments = _segments; + } +} + +/// Modified version of [RenderSliverFixedExtentBoxAdaptor] to use precomputed offsets +class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { + List _segments; + + set segments(List updatedSegments) { + if (_segments.equals(updatedSegments)) { + return; + } + _segments = updatedSegments; + markNeedsLayout(); + } + + _RenderSliverTimelineBoxAdaptor({ + required super.childManager, + required List segments, + }) : _segments = segments; + + int getMinChildIndexForScrollOffset(double offset) => + _segments.findByOffset(offset)?.getMinChildIndexForScrollOffset(offset) ?? + 0; + + int getMaxChildIndexForScrollOffset(double offset) => + _segments.findByOffset(offset)?.getMaxChildIndexForScrollOffset(offset) ?? + 0; + + double indexToLayoutOffset(int index) => + (_segments.findByIndex(index) ?? _segments.lastOrNull) + ?.indexToLayoutOffset(index) ?? + 0; + + double estimateMaxScrollOffset() => _segments.lastOrNull?.endOffset ?? 0; + + double computeMaxScrollOffset() => _segments.lastOrNull?.endOffset ?? 0; + + @override + void performLayout() { + childManager.didStartLayout(); + // Assume initially that we have enough children to fill the viewport/cache area. + childManager.setDidUnderflow(false); + + final double scrollOffset = + constraints.scrollOffset + constraints.cacheOrigin; + assert(scrollOffset >= 0.0); + + final double remainingExtent = constraints.remainingCacheExtent; + assert(remainingExtent >= 0.0); + + final double targetScrollOffset = scrollOffset + remainingExtent; + + // Find the index of the first child that should be visible or in the leading cache area. + final int firstRequiredChildIndex = + getMinChildIndexForScrollOffset(scrollOffset); + + // Find the index of the last child that should be visible or in the trailing cache area. + final int? lastRequiredChildIndex = targetScrollOffset.isFinite + ? getMaxChildIndexForScrollOffset(targetScrollOffset) + : null; + + // Remove children that are no longer visible or within the cache area. + if (firstChild == null) { + collectGarbage(0, 0); + } else { + final int leadingChildrenToRemove = + calculateLeadingGarbage(firstIndex: firstRequiredChildIndex); + final int trailingChildrenToRemove = lastRequiredChildIndex == null + ? 0 + : calculateTrailingGarbage(lastIndex: lastRequiredChildIndex); + collectGarbage(leadingChildrenToRemove, trailingChildrenToRemove); + } + + // If there are currently no children laid out (e.g., initial load), + // try to add the first child needed for the current scroll offset. + if (firstChild == null) { + final double firstChildLayoutOffset = + indexToLayoutOffset(firstRequiredChildIndex); + final bool childAdded = addInitialChild( + index: firstRequiredChildIndex, + layoutOffset: firstChildLayoutOffset, + ); + + if (!childAdded) { + // There are either no children, or we are past the end of all our children. + final double max = + firstRequiredChildIndex <= 0 ? 0.0 : computeMaxScrollOffset(); + geometry = SliverGeometry(scrollExtent: max, maxPaintExtent: max); + childManager.didFinishLayout(); + return; + } + } + + // Layout children that might have scrolled into view from the top (before the current firstChild). + RenderBox? highestLaidOutChild; + final childConstraints = constraints.asBoxConstraints(); + + for (int currentIndex = indexOf(firstChild!) - 1; + currentIndex >= firstRequiredChildIndex; + --currentIndex) { + final RenderBox? newLeadingChild = + insertAndLayoutLeadingChild(childConstraints); + if (newLeadingChild == null) { + // If a child is missing where we expect one, it indicates + // an inconsistency in offset that needs correction. + final Segment? segment = + _segments.findByIndex(currentIndex) ?? _segments.firstOrNull; + geometry = SliverGeometry( + // Request a scroll correction based on where the missing child should have been. + scrollOffsetCorrection: + segment?.indexToLayoutOffset(currentIndex) ?? 0.0, + ); + // Parent will re-layout everything. + return; + } + final childParentData = + newLeadingChild.parentData! as SliverMultiBoxAdaptorParentData; + childParentData.layoutOffset = indexToLayoutOffset(currentIndex); + assert(childParentData.index == currentIndex); + highestLaidOutChild ??= newLeadingChild; + } + + // If the loop above didn't run (meaning the firstChild was already the correct [firstRequiredChildIndex]), + // or even if it did, we need to ensure the first visible child is correctly laid out + // and establish our starting point for laying out trailing children. + + // If [highestLaidOutChild] is still null, it means the loop above didn't add any new leading children. + // The [firstChild] that existed at the start of performLayout is still the first one we need. + if (highestLaidOutChild == null) { + firstChild!.layout(childConstraints); + final childParentData = + firstChild!.parentData! as SliverMultiBoxAdaptorParentData; + childParentData.layoutOffset = + indexToLayoutOffset(firstRequiredChildIndex); + highestLaidOutChild = firstChild; + } + + RenderBox? mostRecentlyLaidOutChild = highestLaidOutChild; + + // Starting from the child after [mostRecentlyLaidOutChild], layout subsequent children + // until we reach the [lastRequiredChildIndex] or run out of children. + double calculatedMaxScrollOffset = double.infinity; + + for (int currentIndex = indexOf(mostRecentlyLaidOutChild!) + 1; + lastRequiredChildIndex == null || + currentIndex <= lastRequiredChildIndex; + ++currentIndex) { + RenderBox? child = childAfter(mostRecentlyLaidOutChild!); + + if (child == null || indexOf(child) != currentIndex) { + child = insertAndLayoutChild( + childConstraints, + after: mostRecentlyLaidOutChild, + ); + if (child == null) { + final Segment? segment = + _segments.findByIndex(currentIndex) ?? _segments.lastOrNull; + calculatedMaxScrollOffset = + segment?.indexToLayoutOffset(currentIndex) ?? + computeMaxScrollOffset(); + break; + } + } else { + child.layout(childConstraints); + } + + mostRecentlyLaidOutChild = child; + final childParentData = mostRecentlyLaidOutChild.parentData! + as SliverMultiBoxAdaptorParentData; + assert(childParentData.index == currentIndex); + childParentData.layoutOffset = indexToLayoutOffset(currentIndex); + } + + final int lastLaidOutChildIndex = indexOf(lastChild!); + final double leadingScrollOffset = + indexToLayoutOffset(firstRequiredChildIndex); + final double trailingScrollOffset = + indexToLayoutOffset(lastLaidOutChildIndex + 1); + + assert( + firstRequiredChildIndex == 0 || + (childScrollOffset(firstChild!) ?? -1.0) - scrollOffset <= + precisionErrorTolerance, + ); + assert(debugAssertChildListIsNonEmptyAndContiguous()); + assert(indexOf(firstChild!) == firstRequiredChildIndex); + assert( + lastRequiredChildIndex == null || + lastLaidOutChildIndex <= lastRequiredChildIndex, + ); + + calculatedMaxScrollOffset = math.min( + calculatedMaxScrollOffset, + estimateMaxScrollOffset(), + ); + + final double paintExtent = calculatePaintOffset( + constraints, + from: leadingScrollOffset, + to: trailingScrollOffset, + ); + + final double cacheExtent = calculateCacheOffset( + constraints, + from: leadingScrollOffset, + to: trailingScrollOffset, + ); + + final double targetEndScrollOffsetForPaint = + constraints.scrollOffset + constraints.remainingPaintExtent; + final int? targetLastIndexForPaint = targetEndScrollOffsetForPaint.isFinite + ? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint) + : null; + + final maxPaintExtent = math.max(paintExtent, calculatedMaxScrollOffset); + + geometry = SliverGeometry( + scrollExtent: calculatedMaxScrollOffset, + paintExtent: paintExtent, + maxPaintExtent: maxPaintExtent, + // Indicates if there's content scrolled off-screen. + // This is true if the last child needed for painting is actually laid out, + // or if the first child is partially visible. + hasVisualOverflow: (targetLastIndexForPaint != null && + lastLaidOutChildIndex >= targetLastIndexForPaint) || + constraints.scrollOffset > 0.0, + cacheExtent: cacheExtent, + ); + + // We may have started the layout while scrolled to the end, which would not + // expose a new child. + if (calculatedMaxScrollOffset == trailingScrollOffset) { + childManager.setDidUnderflow(true); + } + + childManager.didFinishLayout(); + } +} + +class _MultiSelectStatusButton extends ConsumerWidget { + const _MultiSelectStatusButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final selectCount = + ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length)); + return ElevatedButton.icon( + onPressed: () => ref.read(multiSelectProvider.notifier).reset(), + icon: Icon( + Icons.close_rounded, + color: context.colorScheme.onPrimary, + ), + label: Text( + selectCount.toString(), + style: context.textTheme.titleMedium?.copyWith( + height: 2.5, + color: context.colorScheme.onPrimary, + ), + ), + ); + } +} diff --git a/mobile/lib/providers/album/album_viewer.provider.dart b/mobile/lib/providers/album/album_viewer.provider.dart index cf7344d321..d1290df298 100644 --- a/mobile/lib/providers/album/album_viewer.provider.dart +++ b/mobile/lib/providers/album/album_viewer.provider.dart @@ -1,12 +1,12 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/models/albums/album_viewer_page_state.model.dart'; import 'package:immich_mobile/services/album.service.dart'; -import 'package:immich_mobile/entities/album.entity.dart'; class AlbumViewerNotifier extends StateNotifier { AlbumViewerNotifier(this.ref) : super( - AlbumViewerPageState( + const AlbumViewerPageState( editTitleText: "", isEditAlbum: false, editDescriptionText: "", diff --git a/mobile/lib/providers/app_settings.provider.dart b/mobile/lib/providers/app_settings.provider.dart index 2d4bdd0eef..109218a07c 100644 --- a/mobile/lib/providers/app_settings.provider.dart +++ b/mobile/lib/providers/app_settings.provider.dart @@ -5,4 +5,4 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'app_settings.provider.g.dart'; @Riverpod(keepAlive: true) -AppSettingsService appSettingsService(Ref _) => AppSettingsService(); +AppSettingsService appSettingsService(Ref _) => const AppSettingsService(); diff --git a/mobile/lib/providers/app_settings.provider.g.dart b/mobile/lib/providers/app_settings.provider.g.dart index 66814abd49..c959861c04 100644 --- a/mobile/lib/providers/app_settings.provider.g.dart +++ b/mobile/lib/providers/app_settings.provider.g.dart @@ -7,7 +7,7 @@ part of 'app_settings.provider.dart'; // ************************************************************************** String _$appSettingsServiceHash() => - r'2aa16d76a8df869c39486325efc1d08b2d2c284c'; + r'89cece3a19e06612f5639ae290120e854a0c5a31'; /// See also [appSettingsService]. @ProviderFor(appSettingsService) diff --git a/mobile/lib/providers/asset_viewer/download.provider.dart b/mobile/lib/providers/asset_viewer/download.provider.dart index 3daed6f686..7f0d913a02 100644 --- a/mobile/lib/providers/asset_viewer/download.provider.dart +++ b/mobile/lib/providers/asset_viewer/download.provider.dart @@ -23,7 +23,7 @@ class DownloadStateNotifier extends StateNotifier { this._shareService, this._albumService, ) : super( - DownloadState( + const DownloadState( downloadStatus: TaskStatus.complete, showProgress: false, taskProgress: {}, diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index 5207858f99..2d3de11257 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -13,6 +13,7 @@ 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/services/widget.service.dart'; import 'package:immich_mobile/utils/hash.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -23,6 +24,7 @@ final authProvider = StateNotifierProvider((ref) { ref.watch(apiServiceProvider), ref.watch(userServiceProvider), ref.watch(secureStorageServiceProvider), + ref.watch(widgetServiceProvider), ); }); @@ -31,6 +33,7 @@ class AuthNotifier extends StateNotifier { final ApiService _apiService; final UserService _userService; final SecureStorageService _secureStorageService; + final WidgetService _widgetService; final _log = Logger("AuthenticationNotifier"); static const Duration _timeoutDuration = Duration(seconds: 7); @@ -40,8 +43,9 @@ class AuthNotifier extends StateNotifier { this._apiService, this._userService, this._secureStorageService, + this._widgetService, ) : super( - AuthState( + const AuthState( deviceId: "", userId: "", userEmail: "", @@ -76,6 +80,8 @@ class AuthNotifier extends StateNotifier { Future logout() async { try { await _secureStorageService.delete(kSecuredPinCode); + await _widgetService.clearCredentials(); + await _authService.logout(); } finally { await _cleanUp(); @@ -83,7 +89,7 @@ class AuthNotifier extends StateNotifier { } Future _cleanUp() async { - state = AuthState( + state = const AuthState( deviceId: "", userId: "", userEmail: "", @@ -112,6 +118,11 @@ class AuthNotifier extends StateNotifier { }) async { await _apiService.setAccessToken(accessToken); + await _widgetService.writeCredentials( + Store.get(StoreKey.serverEndpoint), + accessToken, + ); + // Get the deviceid from the store if it exists, otherwise generate a new one String deviceId = Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid; diff --git a/mobile/lib/providers/backup/backup.provider.dart b/mobile/lib/providers/backup/backup.provider.dart index a4f4fea45c..69246398ed 100644 --- a/mobile/lib/providers/backup/backup.provider.dart +++ b/mobile/lib/providers/backup/backup.provider.dart @@ -9,9 +9,6 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/interfaces/album_media.interface.dart'; -import 'package:immich_mobile/interfaces/backup_album.interface.dart'; -import 'package:immich_mobile/interfaces/file_media.interface.dart'; import 'package:immich_mobile/models/auth/auth_state.model.dart'; import 'package:immich_mobile/models/backup/available_album.model.dart'; import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; @@ -25,6 +22,7 @@ import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/repositories/album_media.repository.dart'; +import 'package:immich_mobile/repositories/backup.repository.dart'; import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/services/background.service.dart'; import 'package:immich_mobile/services/backup.service.dart'; @@ -108,8 +106,8 @@ class BackupNotifier extends StateNotifier { final AuthState _authState; final BackgroundService _backgroundService; final GalleryPermissionNotifier _galleryPermissionNotifier; - final IAlbumMediaRepository _albumMediaRepository; - final IFileMediaRepository _fileMediaRepository; + final AlbumMediaRepository _albumMediaRepository; + final FileMediaRepository _fileMediaRepository; final BackupAlbumService _backupAlbumService; final Ref ref; diff --git a/mobile/lib/providers/backup/ios_background_settings.provider.dart b/mobile/lib/providers/backup/ios_background_settings.provider.dart index 7605d73650..d70e674845 100644 --- a/mobile/lib/providers/backup/ios_background_settings.provider.dart +++ b/mobile/lib/providers/backup/ios_background_settings.provider.dart @@ -7,7 +7,7 @@ class IOSBackgroundSettings { final DateTime? timeOfLastFetch; final DateTime? timeOfLastProcessing; - IOSBackgroundSettings({ + const IOSBackgroundSettings({ required this.appRefreshEnabled, required this.numberOfBackgroundTasksQueued, this.timeOfLastFetch, diff --git a/mobile/lib/providers/cast.provider.dart b/mobile/lib/providers/cast.provider.dart index c80789d2e0..f70bdad9dc 100644 --- a/mobile/lib/providers/cast.provider.dart +++ b/mobile/lib/providers/cast.provider.dart @@ -1,6 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/interfaces/cast_destination_service.interface.dart'; import 'package:immich_mobile/models/cast/cast_manager_state.dart'; import 'package:immich_mobile/services/gcast.service.dart'; @@ -10,7 +9,7 @@ final castProvider = StateNotifierProvider( class CastNotifier extends StateNotifier { // more cast providers can be added here (ie Fcast) - final ICastDestinationService _gCastService; + final GCastService _gCastService; List<(String, CastDestinationType, dynamic)> discovered = List.empty(); diff --git a/mobile/lib/providers/image/cache/image_loader.dart b/mobile/lib/providers/image/cache/image_loader.dart index 6e83e9af64..f88d54e4f1 100644 --- a/mobile/lib/providers/image/cache/image_loader.dart +++ b/mobile/lib/providers/image/cache/image_loader.dart @@ -37,12 +37,11 @@ class ImageLoader { } else if (result is FileInfo) { // We have the file final buffer = await ui.ImmutableBuffer.fromFilePath(result.file.path); - final decoded = await decode(buffer); - return decoded; + return decode(buffer); } } // If we get here, the image failed to load from the cache stream - throw ImageLoadingException('Could not load image from stream'); + throw const ImageLoadingException('Could not load image from stream'); } } diff --git a/mobile/lib/providers/image/exceptions/image_loading_exception.dart b/mobile/lib/providers/image/exceptions/image_loading_exception.dart index 5e5ff72359..98f633a88f 100644 --- a/mobile/lib/providers/image/exceptions/image_loading_exception.dart +++ b/mobile/lib/providers/image/exceptions/image_loading_exception.dart @@ -1,5 +1,5 @@ /// An exception for the [ImageLoader] and the Immich image providers class ImageLoadingException implements Exception { final String message; - ImageLoadingException(this.message); + const ImageLoadingException(this.message); } diff --git a/mobile/lib/providers/image/immich_remote_image_provider.dart b/mobile/lib/providers/image/immich_remote_image_provider.dart index d5189fa4fc..fe0811583c 100644 --- a/mobile/lib/providers/image/immich_remote_image_provider.dart +++ b/mobile/lib/providers/image/immich_remote_image_provider.dart @@ -23,7 +23,7 @@ class ImmichRemoteImageProvider /// The image cache manager final CacheManager? cacheManager; - ImmichRemoteImageProvider({ + const ImmichRemoteImageProvider({ required this.assetId, this.cacheManager, }); diff --git a/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart b/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart index a1e9c737b0..8533cb6f08 100644 --- a/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart +++ b/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart @@ -24,7 +24,7 @@ class ImmichRemoteThumbnailProvider /// The image cache manager final CacheManager? cacheManager; - ImmichRemoteThumbnailProvider({ + const ImmichRemoteThumbnailProvider({ required this.assetId, this.height, this.width, diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart new file mode 100644 index 0000000000..0c197ca683 --- /dev/null +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -0,0 +1,240 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/services/action.service.dart'; +import 'package:immich_mobile/services/timeline.service.dart'; +import 'package:logging/logging.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +final actionProvider = NotifierProvider( + ActionNotifier.new, + dependencies: [ + multiSelectProvider, + timelineServiceProvider, + ], +); + +class ActionResult { + final int count; + final bool success; + final String? error; + + const ActionResult({required this.count, required this.success, this.error}); + + @override + String toString() => + 'ActionResult(count: $count, success: $success, error: $error)'; +} + +class ActionNotifier extends Notifier { + final Logger _logger = Logger('ActionNotifier'); + late ActionService _service; + + ActionNotifier() : super(); + + @override + void build() { + _service = ref.watch(actionServiceProvider); + } + + List _getRemoteIdsForSource(ActionSource source) { + return _getIdsForSource(source).toIds().toList(); + } + + List _getOwnedRemoteForSource(ActionSource source) { + final ownerId = ref.read(currentUserProvider)?.id; + return _getIdsForSource(source) + .ownedAssets(ownerId) + .toIds() + .toList(); + } + + Iterable _getIdsForSource(ActionSource source) { + final Set assets = switch (source) { + ActionSource.timeline => + ref.read(multiSelectProvider.select((s) => s.selectedAssets)), + ActionSource.viewer => switch (ref.read(currentAssetNotifier)) { + BaseAsset asset => {asset}, + null => {}, + }, + }; + + return switch (T) { + const (RemoteAsset) => assets.whereType(), + const (LocalAsset) => assets.whereType(), + _ => [], + } as Iterable; + } + + Future shareLink( + ActionSource source, + BuildContext context, + ) async { + final ids = _getRemoteIdsForSource(source); + try { + await _service.shareLink(ids, context); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to create shared link for assets', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } + + Future favorite(ActionSource source) async { + final ids = _getOwnedRemoteForSource(source); + try { + await _service.favorite(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to favorite assets', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } + + Future unFavorite(ActionSource source) async { + final ids = _getOwnedRemoteForSource(source); + try { + await _service.unFavorite(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to unfavorite assets', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } + + Future archive(ActionSource source) async { + final ids = _getOwnedRemoteForSource(source); + try { + await _service.archive(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to archive assets', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } + + Future unArchive(ActionSource source) async { + final ids = _getOwnedRemoteForSource(source); + try { + await _service.unArchive(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to unarchive assets', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } + + Future moveToLockFolder(ActionSource source) async { + final ids = _getOwnedRemoteForSource(source); + try { + await _service.moveToLockFolder(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to move assets to lock folder', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } + + Future removeFromLockFolder(ActionSource source) async { + final ids = _getOwnedRemoteForSource(source); + try { + await _service.removeFromLockFolder(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to remove assets from lock folder', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } + + Future trash(ActionSource source) async { + final ids = _getOwnedRemoteForSource(source); + try { + await _service.trash(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to trash assets', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } + + Future delete(ActionSource source) async { + final ids = _getOwnedRemoteForSource(source); + try { + await _service.delete(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to delete assets', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } + + Future editLocation( + ActionSource source, + BuildContext context, + ) async { + final ids = _getOwnedRemoteForSource(source); + try { + final isEdited = await _service.editLocation(ids, context); + if (!isEdited) { + return null; + } + + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to edit location for assets', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } +} + +extension on Iterable { + Iterable toIds() => map((e) => e.id); + + Iterable ownedAssets(String? ownerId) { + if (ownerId == null) return []; + return whereType().where((a) => a.ownerId == ownerId); + } +} diff --git a/mobile/lib/providers/infrastructure/album.provider.dart b/mobile/lib/providers/infrastructure/album.provider.dart index cb4aadb8a7..4a6db50697 100644 --- a/mobile/lib/providers/infrastructure/album.provider.dart +++ b/mobile/lib/providers/infrastructure/album.provider.dart @@ -1,8 +1,41 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/services/local_album.service.dart'; +import 'package:immich_mobile/domain/services/remote_album.service.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart'; -final localAlbumRepository = Provider( +final localAlbumRepository = Provider( (ref) => DriftLocalAlbumRepository(ref.watch(driftProvider)), ); + +final localAlbumServiceProvider = Provider( + (ref) => LocalAlbumService(ref.watch(localAlbumRepository)), +); + +final localAlbumProvider = FutureProvider>( + (ref) => LocalAlbumService(ref.watch(localAlbumRepository)).getAll(), +); + +final localAlbumThumbnailProvider = FutureProvider.family( + (ref, albumId) => + LocalAlbumService(ref.watch(localAlbumRepository)).getThumbnail(albumId), +); + +final remoteAlbumRepository = Provider( + (ref) => DriftRemoteAlbumRepository(ref.watch(driftProvider)), +); + +final remoteAlbumServiceProvider = Provider( + (ref) => RemoteAlbumService(ref.watch(remoteAlbumRepository)), + dependencies: [remoteAlbumRepository], +); + +final remoteAlbumProvider = + NotifierProvider( + RemoteAlbumNotifier.new, + dependencies: [remoteAlbumServiceProvider], +); diff --git a/mobile/lib/providers/infrastructure/asset.provider.dart b/mobile/lib/providers/infrastructure/asset.provider.dart index d714571473..0015986243 100644 --- a/mobile/lib/providers/infrastructure/asset.provider.dart +++ b/mobile/lib/providers/infrastructure/asset.provider.dart @@ -1,8 +1,20 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart'; +import 'package:immich_mobile/domain/services/asset.service.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -final localAssetRepository = Provider( +final localAssetRepository = Provider( (ref) => DriftLocalAssetRepository(ref.watch(driftProvider)), ); + +final remoteAssetRepositoryProvider = Provider( + (ref) => RemoteAssetRepository(ref.watch(driftProvider)), +); + +final assetServiceProvider = Provider( + (ref) => AssetService( + remoteAssetRepository: ref.watch(remoteAssetRepositoryProvider), + localAssetRepository: ref.watch(localAssetRepository), + ), +); diff --git a/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart b/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart new file mode 100644 index 0000000000..996d5d816f --- /dev/null +++ b/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart @@ -0,0 +1,48 @@ +import 'dart:async'; + +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; + +final currentAssetNotifier = + AutoDisposeNotifierProvider( + CurrentAssetNotifier.new, +); + +class CurrentAssetNotifier extends AutoDisposeNotifier { + KeepAliveLink? _keepAliveLink; + StreamSubscription? _assetSubscription; + + @override + BaseAsset? build() => null; + + void setAsset(BaseAsset asset) { + _keepAliveLink?.close(); + _assetSubscription?.cancel(); + state = asset; + _assetSubscription = ref + .watch(assetServiceProvider) + .watchAsset(asset) + .listen((updatedAsset) { + if (updatedAsset != null) { + state = updatedAsset; + } + }); + _keepAliveLink = ref.keepAlive(); + } + + void dispose() { + _keepAliveLink?.close(); + _assetSubscription?.cancel(); + } +} + +final currentAssetExifProvider = FutureProvider.autoDispose( + (ref) { + final currentAsset = ref.watch(currentAssetNotifier); + if (currentAsset == null) { + return null; + } + return ref.watch(assetServiceProvider).getExif(currentAsset); + }, +); diff --git a/mobile/lib/providers/infrastructure/device_asset.provider.dart b/mobile/lib/providers/infrastructure/device_asset.provider.dart index 5fa532b9ec..7854af016a 100644 --- a/mobile/lib/providers/infrastructure/device_asset.provider.dart +++ b/mobile/lib/providers/infrastructure/device_asset.provider.dart @@ -1,8 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/device_asset.interface.dart'; import 'package:immich_mobile/infrastructure/repositories/device_asset.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -final deviceAssetRepositoryProvider = Provider( +final deviceAssetRepositoryProvider = Provider( (ref) => IsarDeviceAssetRepository(ref.watch(isarProvider)), ); diff --git a/mobile/lib/providers/infrastructure/exif.provider.dart b/mobile/lib/providers/infrastructure/exif.provider.dart index ecb67dd2fe..59ad632927 100644 --- a/mobile/lib/providers/infrastructure/exif.provider.dart +++ b/mobile/lib/providers/infrastructure/exif.provider.dart @@ -1,5 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -7,5 +6,5 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'exif.provider.g.dart'; @Riverpod(keepAlive: true) -IExifInfoRepository exifRepository(Ref ref) => +IsarExifRepository exifRepository(Ref ref) => IsarExifRepository(ref.watch(isarProvider)); diff --git a/mobile/lib/providers/infrastructure/exif.provider.g.dart b/mobile/lib/providers/infrastructure/exif.provider.g.dart index 053abf18cc..0261558707 100644 --- a/mobile/lib/providers/infrastructure/exif.provider.g.dart +++ b/mobile/lib/providers/infrastructure/exif.provider.g.dart @@ -6,11 +6,11 @@ part of 'exif.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$exifRepositoryHash() => r'f0abe778ed61fbb257001fdf2ac6e17814011fee'; +String _$exifRepositoryHash() => r'bf4a3f6a50d954a23d317659b4f3e2f381066463'; /// See also [exifRepository]. @ProviderFor(exifRepository) -final exifRepositoryProvider = Provider.internal( +final exifRepositoryProvider = Provider.internal( exifRepository, name: r'exifRepositoryProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -22,6 +22,6 @@ final exifRepositoryProvider = Provider.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef ExifRepositoryRef = ProviderRef; +typedef ExifRepositoryRef = ProviderRef; // 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/infrastructure/memory.provider.dart b/mobile/lib/providers/infrastructure/memory.provider.dart new file mode 100644 index 0000000000..0e58943f55 --- /dev/null +++ b/mobile/lib/providers/infrastructure/memory.provider.dart @@ -0,0 +1,27 @@ +import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/domain/services/memory.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/memory.repository.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import 'db.provider.dart'; + +final driftMemoryRepositoryProvider = Provider( + (ref) => DriftMemoryRepository(ref.watch(driftProvider)), +); + +final driftMemoryServiceProvider = Provider( + (ref) => DriftMemoryService(ref.watch(driftMemoryRepositoryProvider)), +); + +final driftMemoryFutureProvider = + FutureProvider.autoDispose>((ref) async { + final user = ref.watch(currentUserProvider); + if (user == null) { + return []; + } + + final service = ref.watch(driftMemoryServiceProvider); + + return service.getMemoryLane(user.id); +}); diff --git a/mobile/lib/providers/infrastructure/remote_album.provider.dart b/mobile/lib/providers/infrastructure/remote_album.provider.dart new file mode 100644 index 0000000000..2e5a475b9c --- /dev/null +++ b/mobile/lib/providers/infrastructure/remote_album.provider.dart @@ -0,0 +1,121 @@ +import 'package:collection/collection.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/services/remote_album.service.dart'; +import 'package:immich_mobile/models/albums/album_search.model.dart'; +import 'package:immich_mobile/utils/remote_album.utils.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import 'album.provider.dart'; + +class RemoteAlbumState { + final List albums; + final List filteredAlbums; + final bool isLoading; + final String? error; + + const RemoteAlbumState({ + required this.albums, + List? filteredAlbums, + this.isLoading = false, + this.error, + }) : filteredAlbums = filteredAlbums ?? albums; + + RemoteAlbumState copyWith({ + List? albums, + List? filteredAlbums, + bool? isLoading, + String? error, + }) { + return RemoteAlbumState( + albums: albums ?? this.albums, + filteredAlbums: filteredAlbums ?? this.filteredAlbums, + isLoading: isLoading ?? this.isLoading, + error: error ?? this.error, + ); + } + + @override + String toString() => + 'RemoteAlbumState(albums: ${albums.length}, filteredAlbums: ${filteredAlbums.length}, isLoading: $isLoading, error: $error)'; + + @override + bool operator ==(covariant RemoteAlbumState other) { + if (identical(this, other)) return true; + final listEquals = const DeepCollectionEquality().equals; + + return listEquals(other.albums, albums) && + listEquals(other.filteredAlbums, filteredAlbums) && + other.isLoading == isLoading && + other.error == error; + } + + @override + int get hashCode => + albums.hashCode ^ + filteredAlbums.hashCode ^ + isLoading.hashCode ^ + error.hashCode; +} + +class RemoteAlbumNotifier extends Notifier { + late final RemoteAlbumService _remoteAlbumService; + + @override + RemoteAlbumState build() { + _remoteAlbumService = ref.read(remoteAlbumServiceProvider); + return const RemoteAlbumState(albums: [], filteredAlbums: []); + } + + Future> getAll() async { + state = state.copyWith(isLoading: true, error: null); + + try { + final albums = await _remoteAlbumService.getAll(); + state = state.copyWith( + albums: albums, + filteredAlbums: albums, + isLoading: false, + ); + return albums; + } catch (e) { + state = state.copyWith(isLoading: false, error: e.toString()); + rethrow; + } + } + + Future refresh() async { + await getAll(); + } + + void searchAlbums( + String query, + String? userId, [ + QuickFilterMode filterMode = QuickFilterMode.all, + ]) { + final filtered = _remoteAlbumService.searchAlbums( + state.albums, + query, + userId, + filterMode, + ); + + state = state.copyWith( + filteredAlbums: filtered, + ); + } + + void clearSearch() { + state = state.copyWith( + filteredAlbums: state.albums, + ); + } + + void sortFilteredAlbums( + RemoteAlbumSortMode sortMode, { + bool isReverse = false, + }) { + final sortedAlbums = _remoteAlbumService + .sortAlbums(state.filteredAlbums, sortMode, isReverse: isReverse); + state = state.copyWith(filteredAlbums: sortedAlbums); + } +} diff --git a/mobile/lib/providers/infrastructure/setting.provider.dart b/mobile/lib/providers/infrastructure/setting.provider.dart new file mode 100644 index 0000000000..ad0af8282e --- /dev/null +++ b/mobile/lib/providers/infrastructure/setting.provider.dart @@ -0,0 +1,22 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/domain/services/setting.service.dart'; +import 'package:immich_mobile/providers/infrastructure/store.provider.dart'; + +class SettingsNotifier extends Notifier { + @override + SettingsService build() => + SettingsService(storeService: ref.read(storeServiceProvider)); + + T get(Setting setting) => state.get(setting); + + Future set(Setting setting, T value) async { + await state.set(setting, value); + ref.invalidateSelf(); + } + + Stream watch(Setting setting) => state.watch(setting); +} + +final settingsProvider = + NotifierProvider(SettingsNotifier.new); diff --git a/mobile/lib/providers/infrastructure/storage.provider.dart b/mobile/lib/providers/infrastructure/storage.provider.dart index d8ac79f1c1..5bbbe51497 100644 --- a/mobile/lib/providers/infrastructure/storage.provider.dart +++ b/mobile/lib/providers/infrastructure/storage.provider.dart @@ -1,7 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/storage.interface.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; -final storageRepositoryProvider = Provider( - (ref) => StorageRepository(), +final storageRepositoryProvider = Provider( + (ref) => const StorageRepository(), ); diff --git a/mobile/lib/providers/infrastructure/store.provider.dart b/mobile/lib/providers/infrastructure/store.provider.dart index c7f0c04a4f..6ae7ff987b 100644 --- a/mobile/lib/providers/infrastructure/store.provider.dart +++ b/mobile/lib/providers/infrastructure/store.provider.dart @@ -1,5 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; @@ -8,7 +7,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'store.provider.g.dart'; @Riverpod(keepAlive: true) -IStoreRepository storeRepository(Ref ref) => +IsarStoreRepository storeRepository(Ref ref) => IsarStoreRepository(ref.watch(isarProvider)); @Riverpod(keepAlive: true) diff --git a/mobile/lib/providers/infrastructure/store.provider.g.dart b/mobile/lib/providers/infrastructure/store.provider.g.dart index ffdcd291b6..22b783013a 100644 --- a/mobile/lib/providers/infrastructure/store.provider.g.dart +++ b/mobile/lib/providers/infrastructure/store.provider.g.dart @@ -6,11 +6,11 @@ part of 'store.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$storeRepositoryHash() => r'99d24875d30c5e86b1c6caa352a0026167114e62'; +String _$storeRepositoryHash() => r'659cb134466e4b0d5f04e2fc93e426350d99545f'; /// See also [storeRepository]. @ProviderFor(storeRepository) -final storeRepositoryProvider = Provider.internal( +final storeRepositoryProvider = Provider.internal( storeRepository, name: r'storeRepositoryProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -22,7 +22,7 @@ final storeRepositoryProvider = Provider.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef StoreRepositoryRef = ProviderRef; +typedef StoreRepositoryRef = ProviderRef; String _$storeServiceHash() => r'250e10497c42df360e9e1f9a618d0b19c1b5b0a0'; /// See also [storeService]. diff --git a/mobile/lib/providers/infrastructure/sync.provider.dart b/mobile/lib/providers/infrastructure/sync.provider.dart index 359af63232..2406c37fa8 100644 --- a/mobile/lib/providers/infrastructure/sync.provider.dart +++ b/mobile/lib/providers/infrastructure/sync.provider.dart @@ -11,7 +11,6 @@ import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/store.provider.dart'; final syncStreamServiceProvider = Provider( (ref) => SyncStreamService( @@ -26,14 +25,13 @@ final syncApiRepositoryProvider = Provider( ); final syncStreamRepositoryProvider = Provider( - (ref) => DriftSyncStreamRepository(ref.watch(driftProvider)), + (ref) => SyncStreamRepository(ref.watch(driftProvider)), ); final localSyncServiceProvider = Provider( (ref) => LocalSyncService( localAlbumRepository: ref.watch(localAlbumRepository), nativeSyncApi: ref.watch(nativeSyncApiProvider), - storeService: ref.watch(storeServiceProvider), ), ); diff --git a/mobile/lib/providers/infrastructure/timeline.provider.dart b/mobile/lib/providers/infrastructure/timeline.provider.dart new file mode 100644 index 0000000000..bbc04349de --- /dev/null +++ b/mobile/lib/providers/infrastructure/timeline.provider.dart @@ -0,0 +1,49 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +final timelineRepositoryProvider = Provider( + (ref) => DriftTimelineRepository(ref.watch(driftProvider)), +); + +final timelineArgsProvider = Provider.autoDispose( + (ref) => + throw UnimplementedError('Will be overridden through a ProviderScope.'), +); + +final timelineServiceProvider = Provider( + (ref) { + final timelineUsers = ref.watch(timelineUsersProvider).valueOrNull ?? []; + final timelineService = + ref.watch(timelineFactoryProvider).main(timelineUsers); + ref.onDispose(timelineService.dispose); + return timelineService; + }, + // Empty dependencies to inform the framework that this provider + // might be used in a ProviderScope + dependencies: [], +); + +final timelineFactoryProvider = Provider( + (ref) => TimelineFactory( + timelineRepository: ref.watch(timelineRepositoryProvider), + settingsService: ref.watch(settingsProvider), + ), +); + +final timelineUsersProvider = StreamProvider>( + (ref) { + final currentUserId = ref.watch(currentUserProvider.select((u) => u?.id)); + if (currentUserId == null) { + return Stream.value([]); + } + + return ref + .watch(timelineRepositoryProvider) + .watchTimelineUserIds(currentUserId); + }, +); diff --git a/mobile/lib/providers/infrastructure/user.provider.dart b/mobile/lib/providers/infrastructure/user.provider.dart index 504fe28cab..ca65f8be14 100644 --- a/mobile/lib/providers/infrastructure/user.provider.dart +++ b/mobile/lib/providers/infrastructure/user.provider.dart @@ -1,6 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/user.interface.dart'; -import 'package:immich_mobile/domain/interfaces/user_api.interface.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart'; @@ -12,16 +10,16 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'user.provider.g.dart'; @Riverpod(keepAlive: true) -IUserRepository userRepository(Ref ref) => +IsarUserRepository userRepository(Ref ref) => IsarUserRepository(ref.watch(isarProvider)); @Riverpod(keepAlive: true) -IUserApiRepository userApiRepository(Ref ref) => +UserApiRepository userApiRepository(Ref ref) => UserApiRepository(ref.watch(apiServiceProvider).usersApi); @Riverpod(keepAlive: true) UserService userService(Ref ref) => UserService( - userRepository: ref.watch(userRepositoryProvider), + isarUserRepository: ref.watch(userRepositoryProvider), userApiRepository: ref.watch(userApiRepositoryProvider), storeService: ref.watch(storeServiceProvider), ); diff --git a/mobile/lib/providers/infrastructure/user.provider.g.dart b/mobile/lib/providers/infrastructure/user.provider.g.dart index fdb4223ee8..7664b15fd5 100644 --- a/mobile/lib/providers/infrastructure/user.provider.g.dart +++ b/mobile/lib/providers/infrastructure/user.provider.g.dart @@ -6,11 +6,11 @@ part of 'user.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$userRepositoryHash() => r'1a2ac726bcc44397dcaecf449084fefd336696d4'; +String _$userRepositoryHash() => r'538791a4ad126ed086c9db682c67fc5c654d54f3'; /// See also [userRepository]. @ProviderFor(userRepository) -final userRepositoryProvider = Provider.internal( +final userRepositoryProvider = Provider.internal( userRepository, name: r'userRepositoryProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -22,12 +22,12 @@ final userRepositoryProvider = Provider.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef UserRepositoryRef = ProviderRef; -String _$userApiRepositoryHash() => r'6b19f2c99fb83162a5ceb91adb8589eaae01bc92'; +typedef UserRepositoryRef = ProviderRef; +String _$userApiRepositoryHash() => r'8a7340ca4544c8c6b20225c65bff2abb9e96baa2'; /// See also [userApiRepository]. @ProviderFor(userApiRepository) -final userApiRepositoryProvider = Provider.internal( +final userApiRepositoryProvider = Provider.internal( userApiRepository, name: r'userApiRepositoryProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -39,8 +39,8 @@ final userApiRepositoryProvider = Provider.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef UserApiRepositoryRef = ProviderRef; -String _$userServiceHash() => r'4a0873357b7115b4d6bfa8e89b847c0b74ce0d93'; +typedef UserApiRepositoryRef = ProviderRef; +String _$userServiceHash() => r'181414dddc7891be6237e13d568c287a804228d1'; /// See also [userService]. @ProviderFor(userService) diff --git a/mobile/lib/providers/routes.provider.dart b/mobile/lib/providers/routes.provider.dart index a5b903e312..74d86f4767 100644 --- a/mobile/lib/providers/routes.provider.dart +++ b/mobile/lib/providers/routes.provider.dart @@ -1,3 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; final inLockedViewProvider = StateProvider((ref) => false); +final currentRouteNameProvider = StateProvider((ref) => null); diff --git a/mobile/lib/providers/search/paginated_search.provider.dart b/mobile/lib/providers/search/paginated_search.provider.dart index bac5c5e77e..65afb39b7c 100644 --- a/mobile/lib/providers/search/paginated_search.provider.dart +++ b/mobile/lib/providers/search/paginated_search.provider.dart @@ -17,7 +17,7 @@ class PaginatedSearchNotifier extends StateNotifier { final SearchService _searchService; PaginatedSearchNotifier(this._searchService) - : super(SearchResult(assets: [], nextPage: 1)); + : super(const SearchResult(assets: [], nextPage: 1)); Future search(SearchFilter filter) async { if (state.nextPage == null) { @@ -39,7 +39,7 @@ class PaginatedSearchNotifier extends StateNotifier { } clear() { - state = SearchResult(assets: [], nextPage: 1); + state = const SearchResult(assets: [], nextPage: 1); } } diff --git a/mobile/lib/providers/search/people.provider.dart b/mobile/lib/providers/search/people.provider.dart index d1370f498f..d03d533aaf 100644 --- a/mobile/lib/providers/search/people.provider.dart +++ b/mobile/lib/providers/search/people.provider.dart @@ -1,5 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/person_api.interface.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/services/person.service.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; diff --git a/mobile/lib/providers/server_info.provider.dart b/mobile/lib/providers/server_info.provider.dart index a793acb3f6..aabf6a5390 100644 --- a/mobile/lib/providers/server_info.provider.dart +++ b/mobile/lib/providers/server_info.provider.dart @@ -1,36 +1,35 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; - -import 'package:immich_mobile/models/server_info/server_info.model.dart'; -import 'package:immich_mobile/services/server_info.service.dart'; import 'package:immich_mobile/models/server_info/server_config.model.dart'; +import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; import 'package:immich_mobile/models/server_info/server_features.model.dart'; +import 'package:immich_mobile/models/server_info/server_info.model.dart'; import 'package:immich_mobile/models/server_info/server_version.model.dart'; +import 'package:immich_mobile/services/server_info.service.dart'; import 'package:logging/logging.dart'; import 'package:package_info_plus/package_info_plus.dart'; class ServerInfoNotifier extends StateNotifier { ServerInfoNotifier(this._serverInfoService) : super( - ServerInfo( - serverVersion: const ServerVersion( + const ServerInfo( + serverVersion: ServerVersion( major: 0, minor: 0, patch: 0, ), - latestVersion: const ServerVersion( + latestVersion: ServerVersion( major: 0, minor: 0, patch: 0, ), - serverFeatures: const ServerFeatures( + serverFeatures: ServerFeatures( map: true, trash: true, oauthEnabled: false, passwordLogin: true, ), - serverConfig: const ServerConfig( + serverConfig: ServerConfig( trashDays: 30, oauthButtonText: '', externalDomain: '', @@ -38,7 +37,7 @@ class ServerInfoNotifier extends StateNotifier { 'https://tiles.immich.cloud/v1/style/light.json', mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json', ), - serverDiskInfo: const ServerDiskInfo( + serverDiskInfo: ServerDiskInfo( diskAvailable: "0", diskSize: "0", diskUse: "0", diff --git a/mobile/lib/providers/stack.provider.dart b/mobile/lib/providers/stack.provider.dart new file mode 100644 index 0000000000..71abd1e87a --- /dev/null +++ b/mobile/lib/providers/stack.provider.dart @@ -0,0 +1,7 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/infrastructure/repositories/stack.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; + +final driftStackProvider = Provider( + (ref) => DriftStackRepository(ref.watch(driftProvider)), +); diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart new file mode 100644 index 0000000000..b1a926545d --- /dev/null +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -0,0 +1,185 @@ +import 'package:collection/collection.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; + +final multiSelectProvider = + NotifierProvider( + MultiSelectNotifier.new, + dependencies: [timelineServiceProvider], +); + +class MultiSelectState { + final Set selectedAssets; + final Set lockedSelectionAssets; + final bool forceEnable; + + const MultiSelectState({ + required this.selectedAssets, + required this.lockedSelectionAssets, + this.forceEnable = false, + }); + + bool get isEnabled => selectedAssets.isNotEmpty; + bool get hasRemote => selectedAssets.any( + (asset) => + asset.storage == AssetState.remote || + asset.storage == AssetState.merged, + ); + bool get hasLocal => selectedAssets.any( + (asset) => asset.storage == AssetState.local, + ); + + MultiSelectState copyWith({ + Set? selectedAssets, + Set? lockedSelectionAssets, + bool? forceEnable, + }) { + return MultiSelectState( + selectedAssets: selectedAssets ?? this.selectedAssets, + lockedSelectionAssets: + lockedSelectionAssets ?? this.lockedSelectionAssets, + forceEnable: forceEnable ?? this.forceEnable, + ); + } + + @override + String toString() => + 'MultiSelectState(selectedAssets: $selectedAssets, lockedSelectionAssets: $lockedSelectionAssets, forceEnable: $forceEnable)'; + + @override + bool operator ==(covariant MultiSelectState other) { + if (identical(this, other)) return true; + final setEquals = const DeepCollectionEquality().equals; + + return setEquals(other.selectedAssets, selectedAssets) && + setEquals(other.lockedSelectionAssets, lockedSelectionAssets) && + other.forceEnable == forceEnable; + } + + @override + int get hashCode => + selectedAssets.hashCode ^ + lockedSelectionAssets.hashCode ^ + forceEnable.hashCode; +} + +class MultiSelectNotifier extends Notifier { + MultiSelectNotifier([this._defaultState]); + final MultiSelectState? _defaultState; + + TimelineService get _timelineService => ref.read(timelineServiceProvider); + + @override + MultiSelectState build() { + return _defaultState ?? + const MultiSelectState( + selectedAssets: {}, + lockedSelectionAssets: {}, + forceEnable: false, + ); + } + + void selectAsset(BaseAsset asset) { + if (state.selectedAssets.contains(asset)) { + return; + } + + state = state.copyWith( + selectedAssets: {...state.selectedAssets, asset}, + ); + } + + void deselectAsset(BaseAsset asset) { + if (!state.selectedAssets.contains(asset)) { + return; + } + + state = state.copyWith( + selectedAssets: state.selectedAssets.where((a) => a != asset).toSet(), + ); + } + + void toggleAssetSelection(BaseAsset asset) { + if (state.selectedAssets.contains(asset)) { + deselectAsset(asset); + } else { + selectAsset(asset); + } + } + + void reset() { + state = const MultiSelectState( + selectedAssets: {}, + lockedSelectionAssets: {}, + forceEnable: false, + ); + } + + /// Bucket bulk operations + void selectBucket(int offset, int bucketCount) async { + final assets = await _timelineService.loadAssets(offset, bucketCount); + final selectedAssets = state.selectedAssets.toSet(); + + selectedAssets.addAll(assets); + + state = state.copyWith( + selectedAssets: selectedAssets, + ); + } + + void deselectBucket(int offset, int bucketCount) async { + final assets = await _timelineService.loadAssets(offset, bucketCount); + final selectedAssets = state.selectedAssets.toSet(); + + selectedAssets.removeAll(assets); + + state = state.copyWith(selectedAssets: selectedAssets); + } + + void toggleBucketSelection(int offset, int bucketCount) async { + final assets = await _timelineService.loadAssets(offset, bucketCount); + toggleBucketSelectionByAssets(assets); + } + + void toggleBucketSelectionByAssets(List bucketAssets) { + if (bucketAssets.isEmpty) return; + + // Check if all assets in this bucket are currently selected + final allSelected = + bucketAssets.every((asset) => state.selectedAssets.contains(asset)); + + final selectedAssets = state.selectedAssets.toSet(); + + if (allSelected) { + // If all assets in this bucket are selected, deselect them + selectedAssets.removeAll(bucketAssets); + } else { + // If not all assets in this bucket are selected, select them all + selectedAssets.addAll(bucketAssets); + } + + state = state.copyWith(selectedAssets: selectedAssets); + } + + void setLockedSelectionAssets(Set assets) { + state = state.copyWith( + lockedSelectionAssets: assets, + ); + } +} + +final bucketSelectionProvider = Provider.family>( + (ref, bucketAssets) { + final selectedAssets = + ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); + + if (bucketAssets.isEmpty) return false; + + // Check if all assets in the bucket are selected + return bucketAssets.every((asset) => selectedAssets.contains(asset)); + }, + dependencies: [multiSelectProvider, timelineServiceProvider], +); diff --git a/mobile/lib/providers/upload_profile_image.provider.dart b/mobile/lib/providers/upload_profile_image.provider.dart index 10aa645654..fe13944ef6 100644 --- a/mobile/lib/providers/upload_profile_image.provider.dart +++ b/mobile/lib/providers/upload_profile_image.provider.dart @@ -17,7 +17,7 @@ class UploadProfileImageState { // enum final UploadProfileStatus status; final String profileImagePath; - UploadProfileImageState({ + const UploadProfileImageState({ required this.status, required this.profileImagePath, }); @@ -74,7 +74,7 @@ class UploadProfileImageNotifier extends StateNotifier { UploadProfileImageNotifier(this._userService) : super( - UploadProfileImageState( + const UploadProfileImageState( profileImagePath: '', status: UploadProfileStatus.idle, ), diff --git a/mobile/lib/providers/websocket.provider.dart b/mobile/lib/providers/websocket.provider.dart index 72dbda8b6f..2600c6d8ee 100644 --- a/mobile/lib/providers/websocket.provider.dart +++ b/mobile/lib/providers/websocket.provider.dart @@ -56,7 +56,7 @@ class WebsocketState { final bool isConnected; final List pendingChanges; - WebsocketState({ + const WebsocketState({ this.socket, required this.isConnected, required this.pendingChanges, @@ -94,7 +94,11 @@ class WebsocketState { class WebsocketNotifier extends StateNotifier { WebsocketNotifier(this._ref) : super( - WebsocketState(socket: null, isConnected: false, pendingChanges: []), + const WebsocketState( + socket: null, + isConnected: false, + pendingChanges: [], + ), ); final _log = Logger('WebsocketNotifier'); diff --git a/mobile/lib/repositories/activity_api.repository.dart b/mobile/lib/repositories/activity_api.repository.dart index 868415caf9..1ee92b2e2f 100644 --- a/mobile/lib/repositories/activity_api.repository.dart +++ b/mobile/lib/repositories/activity_api.repository.dart @@ -1,6 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; -import 'package:immich_mobile/interfaces/activity_api.interface.dart'; import 'package:immich_mobile/models/activities/activity.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; @@ -10,20 +9,17 @@ final activityApiRepositoryProvider = Provider( (ref) => ActivityApiRepository(ref.watch(apiServiceProvider).activitiesApi), ); -class ActivityApiRepository extends ApiRepository - implements IActivityApiRepository { +class ActivityApiRepository extends ApiRepository { final ActivitiesApi _api; ActivityApiRepository(this._api); - @override Future> getAll(String albumId, {String? assetId}) async { final response = await checkNull(_api.getActivities(albumId, assetId: assetId)); return response.map(_toActivity).toList(); } - @override Future create( String albumId, ActivityType type, { @@ -42,12 +38,10 @@ class ActivityApiRepository extends ApiRepository return _toActivity(response); } - @override Future delete(String id) { return checkNull(_api.deleteActivity(id)); } - @override Future getStats(String albumId, {String? assetId}) async { final response = await checkNull(_api.getActivityStatistics(albumId, assetId: assetId)); diff --git a/mobile/lib/repositories/album.repository.dart b/mobile/lib/repositories/album.repository.dart index 8c50c54382..1d0349bb2a 100644 --- a/mobile/lib/repositories/album.repository.dart +++ b/mobile/lib/repositories/album.repository.dart @@ -6,20 +6,20 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; -import 'package:immich_mobile/interfaces/album.interface.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:immich_mobile/utils/hash.dart'; import 'package:isar/isar.dart'; +enum AlbumSort { remoteId, localId } + final albumRepositoryProvider = Provider((ref) => AlbumRepository(ref.watch(dbProvider))); -class AlbumRepository extends DatabaseRepository implements IAlbumRepository { - AlbumRepository(super.db); +class AlbumRepository extends DatabaseRepository { + const AlbumRepository(super.db); - @override Future count({bool? local}) { final baseQuery = db.albums.where(); final QueryBuilder query = switch (local) { @@ -30,10 +30,8 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository { return query.count(); } - @override Future create(Album album) => txn(() => db.albums.store(album)); - @override Future getByName( String name, { bool? shared, @@ -58,13 +56,10 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository { return query.findFirst(); } - @override Future update(Album album) => txn(() => db.albums.store(album)); - @override Future delete(int albumId) => txn(() => db.albums.delete(albumId)); - @override Future> getAll({ bool? shared, bool? remote, @@ -96,23 +91,22 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository { return query.findAll(); } - @override Future get(int id) => db.albums.get(id); - @override + Future getByRemoteId(String remoteId) { + return db.albums.filter().remoteIdEqualTo(remoteId).findFirst(); + } + Future removeUsers(Album album, List users) => txn( () => album.sharedUsers.update(unlink: users.map(entity.User.fromDto)), ); - @override Future addAssets(Album album, List assets) => txn(() => album.assets.update(link: assets)); - @override Future removeAssets(Album album, List assets) => txn(() => album.assets.update(unlink: assets)); - @override Future recalculateMetadata(Album album) async { album.startDate = await album.assets.filter().fileCreatedAtProperty().min(); album.endDate = await album.assets.filter().fileCreatedAtProperty().max(); @@ -121,15 +115,12 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository { return album; } - @override Future addUsers(Album album, List users) => txn(() => album.sharedUsers.update(link: users.map(entity.User.fromDto))); - @override Future deleteAllLocal() => txn(() => db.albums.where().localIdIsNotNull().deleteAll()); - @override Future> search( String searchTerm, QuickFilterMode filterMode, @@ -152,24 +143,20 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository { return await query.findAll(); } - @override Future clearTable() async { await txn(() async { await db.albums.clear(); }); } - @override Stream> watchRemoteAlbums() { return db.albums.where().remoteIdIsNotNull().watch(); } - @override Stream> watchLocalAlbums() { return db.albums.where().localIdIsNotNull().watch(); } - @override Stream watchAlbum(int id) { return db.albums.watchObject(id, fireImmediately: true); } diff --git a/mobile/lib/repositories/album_api.repository.dart b/mobile/lib/repositories/album_api.repository.dart index e2ac73bd9b..019e4dc63c 100644 --- a/mobile/lib/repositories/album_api.repository.dart +++ b/mobile/lib/repositories/album_api.repository.dart @@ -5,7 +5,6 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; -import 'package:immich_mobile/interfaces/album_api.interface.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; @@ -14,24 +13,21 @@ final albumApiRepositoryProvider = Provider( (ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi), ); -class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository { +class AlbumApiRepository extends ApiRepository { final AlbumsApi _api; AlbumApiRepository(this._api); - @override Future get(String id) async { final dto = await checkNull(_api.getAlbumInfo(id)); return _toAlbum(dto); } - @override Future> getAll({bool? shared}) async { final dtos = await checkNull(_api.getAllAlbums(shared: shared)); return dtos.map(_toAlbum).toList(); } - @override Future create( String name, { required Iterable assetIds, @@ -54,7 +50,6 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository { return _toAlbum(responseDto); } - @override Future update( String albumId, { String? name, @@ -84,12 +79,10 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository { return _toAlbum(response); } - @override Future delete(String albumId) { return _api.deleteAlbum(albumId); } - @override Future<({List added, List duplicates})> addAssets( String albumId, Iterable assetIds, @@ -114,7 +107,6 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository { return (added: added, duplicates: duplicates); } - @override Future<({List removed, List failed})> removeAssets( String albumId, Iterable assetIds, @@ -136,7 +128,6 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository { return (removed: removed, failed: failed); } - @override Future addUsers(String albumId, Iterable userIds) async { final albumUsers = userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); @@ -149,7 +140,6 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository { return _toAlbum(response); } - @override Future removeUser(String albumId, {required String userId}) { return _api.removeUserFromAlbum(albumId, userId); } diff --git a/mobile/lib/repositories/album_media.repository.dart b/mobile/lib/repositories/album_media.repository.dart index 9cba072580..94f1d0581e 100644 --- a/mobile/lib/repositories/album_media.repository.dart +++ b/mobile/lib/repositories/album_media.repository.dart @@ -4,18 +4,17 @@ import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; -import 'package:immich_mobile/interfaces/album_media.interface.dart'; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:photo_manager/photo_manager.dart' hide AssetType; final albumMediaRepositoryProvider = Provider((ref) => const AlbumMediaRepository()); -class AlbumMediaRepository implements IAlbumMediaRepository { +class AlbumMediaRepository { const AlbumMediaRepository(); bool get useCustomFilter => - Store.get(StoreKey.photoManagerCustomFilter, false); + Store.get(StoreKey.photoManagerCustomFilter, true); FilterOptionGroup? _getAlbumFilter({ DateTimeCond? updateTimeCond, @@ -41,7 +40,6 @@ class AlbumMediaRepository implements IAlbumMediaRepository { ) : null; - @override Future> getAll() async { final filter = useCustomFilter ? CustomFilter.sql(where: '${CustomColumns.base.width} > 0') @@ -52,7 +50,6 @@ class AlbumMediaRepository implements IAlbumMediaRepository { return assetPathEntities.map(_toAlbum).toList(); } - @override Future> getAssetIds(String albumId) async { final album = await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter()); @@ -61,14 +58,12 @@ class AlbumMediaRepository implements IAlbumMediaRepository { return assets.map((e) => e.id).toList(); } - @override Future getAssetCount(String albumId) async { final album = await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter()); return album.assetCountAsync; } - @override Future> getAssets( String albumId, { int start = 0, @@ -97,12 +92,7 @@ class AlbumMediaRepository implements IAlbumMediaRepository { return assets.map(AssetMediaRepository.toAsset).toList().cast(); } - @override - Future get( - String id, { - DateTime? modifiedFrom, - DateTime? modifiedUntil, - }) async { + Future get(String id) async { final assetPathEntity = await AssetPathEntity.fromId( id, filterOption: _getAlbumFilter(containsPathModified: true), diff --git a/mobile/lib/repositories/api.repository.dart b/mobile/lib/repositories/api.repository.dart index b454c77f9b..646e2480e9 100644 --- a/mobile/lib/repositories/api.repository.dart +++ b/mobile/lib/repositories/api.repository.dart @@ -3,7 +3,7 @@ import 'package:immich_mobile/constants/errors.dart'; abstract class ApiRepository { Future checkNull(Future future) async { final response = await future; - if (response == null) throw NoResponseDtoError(); + if (response == null) throw const NoResponseDtoError(); return response; } } diff --git a/mobile/lib/repositories/asset.repository.dart b/mobile/lib/repositories/asset.repository.dart index c6f8539167..1e3defae08 100644 --- a/mobile/lib/repositories/asset.repository.dart +++ b/mobile/lib/repositories/asset.repository.dart @@ -4,19 +4,19 @@ 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'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:immich_mobile/utils/hash.dart'; import 'package:isar/isar.dart'; +enum AssetSort { checksum, ownerIdChecksum } + final assetRepositoryProvider = Provider((ref) => AssetRepository(ref.watch(dbProvider))); -class AssetRepository extends DatabaseRepository implements IAssetRepository { - AssetRepository(super.db); +class AssetRepository extends DatabaseRepository { + const AssetRepository(super.db); - @override Future> getByAlbum( Album album, { Iterable notOwnedBy = const [], @@ -54,16 +54,13 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { return sortedQuery.findAll(); } - @override Future deleteByIds(List ids) => txn(() async { await db.assets.deleteAll(ids); await db.exifInfos.deleteAll(ids); }); - @override Future getByRemoteId(String id) => db.assets.getByRemoteId(id); - @override Future> getAllByRemoteId( Iterable ids, { AssetState? state, @@ -88,7 +85,6 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { }; } - @override Future> getAll({ required String ownerId, AssetState? state, @@ -127,13 +123,11 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { return limit == null ? query.findAll() : query.limit(limit).findAll(); } - @override Future> updateAll(List assets) async { await txn(() => db.assets.putAll(assets)); return assets; } - @override Future> getMatches({ required List assets, required String ownerId, @@ -154,42 +148,34 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { return _getMatchesImpl(query, fastHash(ownerId), assets, limit); } - @override Future update(Asset asset) async { await txn(() => asset.put(db)); return asset; } - @override Future upsertDuplicatedAssets(Iterable duplicatedAssets) => txn( () => db.duplicatedAssets .putAll(duplicatedAssets.map(DuplicatedAsset.new).toList()), ); - @override Future> getAllDuplicatedAssetIds() => db.duplicatedAssets.where().idProperty().findAll(); - @override Future getByOwnerIdChecksum(int ownerId, String checksum) => db.assets.getByOwnerIdChecksum(ownerId, checksum); - @override Future> getAllByOwnerIdChecksum( List ownerIds, List checksums, ) => db.assets.getAllByOwnerIdChecksum(ownerIds, checksums); - @override Future> getAllLocal() => db.assets.where().localIdIsNotNull().findAll(); - @override Future deleteAllByRemoteId(List ids, {AssetState? state}) => txn(() => _getAllByRemoteIdImpl(ids, state).deleteAll()); - @override Future> getStackAssets(String stackId) { return db.assets .filter() @@ -202,19 +188,16 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { .findAll(); } - @override Future clearTable() async { await txn(() async { await db.assets.clear(); }); } - @override Stream watchAsset(int id, {bool fireImmediately = false}) { return db.assets.watchObject(id, fireImmediately: fireImmediately); } - @override Future> getTrashAssets(String userId) { return db.assets .where() @@ -225,7 +208,6 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { .findAll(); } - @override Future> getRecentlyTakenAssets(String userId) { return db.assets .where() @@ -236,7 +218,6 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { .findAll(); } - @override Future> getMotionAssets(String userId) { return db.assets .where() diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index f82df4b774..0dff309172 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -1,9 +1,9 @@ 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'; import 'package:immich_mobile/repositories/api.repository.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:openapi/api.dart'; final assetApiRepositoryProvider = Provider( @@ -13,13 +13,12 @@ final assetApiRepositoryProvider = Provider( ), ); -class AssetApiRepository extends ApiRepository implements IAssetApiRepository { +class AssetApiRepository extends ApiRepository { final AssetsApi _api; final SearchApi _searchApi; AssetApiRepository(this._api, this._searchApi); - @override Future update(String id, {String? description}) async { final response = await checkNull( _api.updateAsset(id, UpdateAssetDto(description: description)), @@ -27,7 +26,6 @@ class AssetApiRepository extends ApiRepository implements IAssetApiRepository { return Asset.remote(response); } - @override Future> search({List personIds = const []}) async { // TODO this always fetches all assets, change API and usage to actually do pagination final List result = []; @@ -50,7 +48,10 @@ class AssetApiRepository extends ApiRepository implements IAssetApiRepository { return result; } - @override + Future delete(List ids, bool force) async { + return _api.deleteAssets(AssetBulkDeleteDto(ids: ids, force: force)); + } + Future updateVisibility( List ids, AssetVisibilityEnum visibility, @@ -60,20 +61,35 @@ class AssetApiRepository extends ApiRepository implements IAssetApiRepository { ); } - _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; - } + Future updateFavorite( + List ids, + bool isFavorite, + ) async { + return _api.updateAssets( + AssetBulkUpdateDto(ids: ids, isFavorite: isFavorite), + ); } - @override + Future updateLocation( + List ids, + LatLng location, + ) async { + return _api.updateAssets( + AssetBulkUpdateDto( + ids: ids, + latitude: location.latitude, + longitude: location.longitude, + ), + ); + } + + _mapVisibility(AssetVisibilityEnum visibility) => switch (visibility) { + AssetVisibilityEnum.timeline => AssetVisibility.timeline, + AssetVisibilityEnum.hidden => AssetVisibility.hidden, + AssetVisibilityEnum.locked => AssetVisibility.locked, + AssetVisibilityEnum.archive => AssetVisibility.archive, + }; + Future getAssetMIMEType(String assetId) async { final response = await checkNull(_api.getAssetInfo(assetId)); diff --git a/mobile/lib/repositories/asset_media.repository.dart b/mobile/lib/repositories/asset_media.repository.dart index 7df26455cd..bb1e6f414f 100644 --- a/mobile/lib/repositories/asset_media.repository.dart +++ b/mobile/lib/repositories/asset_media.repository.dart @@ -3,18 +3,17 @@ import 'package:immich_mobile/domain/models/exif.model.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'; -import 'package:immich_mobile/interfaces/asset_media.interface.dart'; import 'package:immich_mobile/utils/hash.dart'; import 'package:photo_manager/photo_manager.dart' hide AssetType; -final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository()); +final assetMediaRepositoryProvider = + Provider((ref) => const AssetMediaRepository()); -class AssetMediaRepository implements IAssetMediaRepository { - @override +class AssetMediaRepository { + const AssetMediaRepository(); Future> deleteAll(List ids) => PhotoManager.editor.deleteWithIds(ids); - @override Future get(String id) async { final entity = await AssetEntity.fromId(id); return toAsset(entity); @@ -47,7 +46,6 @@ class AssetMediaRepository implements IAssetMediaRepository { return asset; } - @override Future getOriginalFilename(String id) async { final entity = await AssetEntity.fromId(id); diff --git a/mobile/lib/repositories/auth.repository.dart b/mobile/lib/repositories/auth.repository.dart index 69a2fa0244..4ee4d8c131 100644 --- a/mobile/lib/repositories/auth.repository.dart +++ b/mobile/lib/repositories/auth.repository.dart @@ -10,23 +10,20 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; -import 'package:immich_mobile/interfaces/auth.interface.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; -final authRepositoryProvider = Provider( - (ref) => - AuthRepository(ref.watch(dbProvider), drift: ref.watch(driftProvider)), +final authRepositoryProvider = Provider( + (ref) => AuthRepository(ref.watch(dbProvider), ref.watch(driftProvider)), ); -class AuthRepository extends DatabaseRepository implements IAuthRepository { +class AuthRepository extends DatabaseRepository { final Drift _drift; - AuthRepository(super.db, {required Drift drift}) : _drift = drift; + const AuthRepository(super.db, this._drift); - @override Future clearLocalData() { return db.writeTxn(() { return Future.wait([ @@ -35,35 +32,37 @@ class AuthRepository extends DatabaseRepository implements IAuthRepository { db.albums.clear(), db.eTags.clear(), db.users.clear(), - _drift.remoteExifEntity.deleteAll(), _drift.remoteAssetEntity.deleteAll(), + _drift.remoteExifEntity.deleteAll(), + _drift.userEntity.deleteAll(), + _drift.userMetadataEntity.deleteAll(), + _drift.partnerEntity.deleteAll(), _drift.remoteAlbumEntity.deleteAll(), _drift.remoteAlbumAssetEntity.deleteAll(), + _drift.remoteAlbumUserEntity.deleteAll(), + _drift.memoryEntity.deleteAll(), + _drift.memoryAssetEntity.deleteAll(), + _drift.stackEntity.deleteAll(), ]); }); } - @override String getAccessToken() { return Store.get(StoreKey.accessToken); } - @override bool getEndpointSwitchingFeature() { return Store.tryGet(StoreKey.autoEndpointSwitching) ?? false; } - @override String? getPreferredWifiName() { return Store.tryGet(StoreKey.preferredWifiName); } - @override String? getLocalEndpoint() { return Store.tryGet(StoreKey.localEndpoint); } - @override List getExternalEndpointList() { final jsonString = Store.tryGet(StoreKey.externalEndpointList); diff --git a/mobile/lib/repositories/auth_api.repository.dart b/mobile/lib/repositories/auth_api.repository.dart index 4015ffd7bc..4b68867506 100644 --- a/mobile/lib/repositories/auth_api.repository.dart +++ b/mobile/lib/repositories/auth_api.repository.dart @@ -1,5 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/auth_api.interface.dart'; import 'package:immich_mobile/models/auth/login_response.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; @@ -9,12 +8,11 @@ import 'package:openapi/api.dart'; final authApiRepositoryProvider = Provider((ref) => AuthApiRepository(ref.watch(apiServiceProvider))); -class AuthApiRepository extends ApiRepository implements IAuthApiRepository { +class AuthApiRepository extends ApiRepository { final ApiService _apiService; AuthApiRepository(this._apiService); - @override Future changePassword(String newPassword) async { await _apiService.usersApi.updateMyUser( UserUpdateMeDto( @@ -23,7 +21,6 @@ class AuthApiRepository extends ApiRepository implements IAuthApiRepository { ); } - @override Future login(String email, String password) async { final loginResponseDto = await checkNull( _apiService.authenticationApi.login( @@ -37,7 +34,6 @@ class AuthApiRepository extends ApiRepository implements IAuthApiRepository { return _mapLoginReponse(loginResponseDto); } - @override Future logout() async { await _apiService.authenticationApi .logout() @@ -56,7 +52,6 @@ class AuthApiRepository extends ApiRepository implements IAuthApiRepository { ); } - @override Future unlockPinCode(String pinCode) async { try { await _apiService.authenticationApi @@ -67,13 +62,11 @@ class AuthApiRepository extends ApiRepository implements IAuthApiRepository { } } - @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/backup.repository.dart b/mobile/lib/repositories/backup.repository.dart index f7f3051f46..05d2047add 100644 --- a/mobile/lib/repositories/backup.repository.dart +++ b/mobile/lib/repositories/backup.repository.dart @@ -1,18 +1,17 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; -import 'package:immich_mobile/interfaces/backup_album.interface.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:isar/isar.dart'; +enum BackupAlbumSort { id } + final backupAlbumRepositoryProvider = Provider((ref) => BackupAlbumRepository(ref.watch(dbProvider))); -class BackupAlbumRepository extends DatabaseRepository - implements IBackupAlbumRepository { - BackupAlbumRepository(super.db); +class BackupAlbumRepository extends DatabaseRepository { + const BackupAlbumRepository(super.db); - @override Future> getAll({BackupAlbumSort? sort}) { final baseQuery = db.backupAlbums.where(); final QueryBuilder query = @@ -23,19 +22,15 @@ class BackupAlbumRepository extends DatabaseRepository return query.findAll(); } - @override Future> getIdsBySelection(BackupSelection backup) => db.backupAlbums.filter().selectionEqualTo(backup).idProperty().findAll(); - @override Future> getAllBySelection(BackupSelection backup) => db.backupAlbums.filter().selectionEqualTo(backup).findAll(); - @override Future deleteAll(List ids) => txn(() => db.backupAlbums.deleteAll(ids)); - @override Future updateAll(List backupAlbums) => txn(() => db.backupAlbums.putAll(backupAlbums)); } diff --git a/mobile/lib/repositories/biometric.repository.dart b/mobile/lib/repositories/biometric.repository.dart index 588fa44797..94540c8249 100644 --- a/mobile/lib/repositories/biometric.repository.dart +++ b/mobile/lib/repositories/biometric.repository.dart @@ -1,18 +1,16 @@ 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 { +class BiometricRepository { final LocalAuthentication _localAuth; - BiometricRepository(this._localAuth); + const BiometricRepository(this._localAuth); - @override Future getStatus() async { final bool canAuthenticateWithBiometrics = await _localAuth.canCheckBiometrics; @@ -26,7 +24,6 @@ class BiometricRepository implements IBiometricRepository { ); } - @override Future authenticate(String? message) async { return _localAuth.authenticate( localizedReason: message ?? 'please_auth_to_access'.tr(), diff --git a/mobile/lib/repositories/database.repository.dart b/mobile/lib/repositories/database.repository.dart index 3eb74621fa..c644dae668 100644 --- a/mobile/lib/repositories/database.repository.dart +++ b/mobile/lib/repositories/database.repository.dart @@ -7,7 +7,7 @@ const Symbol _zoneTxn = #zoneTxn; abstract class DatabaseRepository implements IDatabaseRepository { final Isar db; - DatabaseRepository(this.db); + const DatabaseRepository(this.db); bool get inTxn => Zone.current[_zoneTxn] != null; diff --git a/mobile/lib/repositories/download.repository.dart b/mobile/lib/repositories/download.repository.dart index f7ba612045..72f7e065ca 100644 --- a/mobile/lib/repositories/download.repository.dart +++ b/mobile/lib/repositories/download.repository.dart @@ -1,21 +1,16 @@ import 'package:background_downloader/background_downloader.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/download.interface.dart'; import 'package:immich_mobile/utils/download.dart'; final downloadRepositoryProvider = Provider((ref) => DownloadRepository()); -class DownloadRepository implements IDownloadRepository { - @override +class DownloadRepository { void Function(TaskStatusUpdate)? onImageDownloadStatus; - @override void Function(TaskStatusUpdate)? onVideoDownloadStatus; - @override void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus; - @override void Function(TaskProgressUpdate)? onTaskProgress; DownloadRepository() { @@ -38,22 +33,18 @@ class DownloadRepository implements IDownloadRepository { ); } - @override Future> downloadAll(List tasks) { return FileDownloader().enqueueAll(tasks); } - @override Future deleteAllTrackingRecords() { return FileDownloader().database.deleteAllRecords(); } - @override Future cancel(String id) { return FileDownloader().cancelTaskWithId(id); } - @override Future> getLiveVideoTasks() { return FileDownloader().database.allRecordsWithStatus( TaskStatus.complete, @@ -61,7 +52,6 @@ class DownloadRepository implements IDownloadRepository { ); } - @override Future deleteRecordsWithIds(List ids) { return FileDownloader().database.deleteRecordsWithIds(ids); } diff --git a/mobile/lib/repositories/etag.repository.dart b/mobile/lib/repositories/etag.repository.dart index e8e0624a89..b8382efdfc 100644 --- a/mobile/lib/repositories/etag.repository.dart +++ b/mobile/lib/repositories/etag.repository.dart @@ -1,6 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; -import 'package:immich_mobile/interfaces/etag.interface.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:isar/isar.dart'; @@ -8,26 +7,20 @@ import 'package:isar/isar.dart'; final etagRepositoryProvider = Provider((ref) => ETagRepository(ref.watch(dbProvider))); -class ETagRepository extends DatabaseRepository implements IETagRepository { - ETagRepository(super.db); +class ETagRepository extends DatabaseRepository { + const ETagRepository(super.db); - @override Future> getAllIds() => db.eTags.where().idProperty().findAll(); - @override Future get(String id) => db.eTags.getById(id); - @override Future upsertAll(List etags) => txn(() => db.eTags.putAll(etags)); - @override Future deleteByIds(List ids) => txn(() => db.eTags.deleteAllById(ids)); - @override Future getById(String id) => db.eTags.getById(id); - @override Future clearTable() async { await txn(() async { await db.eTags.clear(); diff --git a/mobile/lib/repositories/file_media.repository.dart b/mobile/lib/repositories/file_media.repository.dart index 15f7a51e15..2bccb05210 100644 --- a/mobile/lib/repositories/file_media.repository.dart +++ b/mobile/lib/repositories/file_media.repository.dart @@ -3,14 +3,14 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/interfaces/file_media.interface.dart'; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:photo_manager/photo_manager.dart' hide AssetType; -final fileMediaRepositoryProvider = Provider((ref) => FileMediaRepository()); +final fileMediaRepositoryProvider = + Provider((ref) => const FileMediaRepository()); -class FileMediaRepository implements IFileMediaRepository { - @override +class FileMediaRepository { + const FileMediaRepository(); Future saveImage( Uint8List data, { required String title, @@ -25,7 +25,6 @@ class FileMediaRepository implements IFileMediaRepository { return AssetMediaRepository.toAsset(entity); } - @override Future saveImageWithFile( String filePath, { String? title, @@ -39,7 +38,6 @@ class FileMediaRepository implements IFileMediaRepository { return AssetMediaRepository.toAsset(entity); } - @override Future saveLivePhoto({ required File image, required File video, @@ -53,7 +51,6 @@ class FileMediaRepository implements IFileMediaRepository { return AssetMediaRepository.toAsset(entity); } - @override Future saveVideo( File file, { required String title, @@ -67,14 +64,11 @@ class FileMediaRepository implements IFileMediaRepository { return AssetMediaRepository.toAsset(entity); } - @override Future clearFileCache() => PhotoManager.clearFileCache(); - @override Future enableBackgroundAccess() => PhotoManager.setIgnorePermissionCheck(true); - @override Future requestExtendedPermissions() => PhotoManager.requestPermissionExtend(); } diff --git a/mobile/lib/repositories/folder_api.repository.dart b/mobile/lib/repositories/folder_api.repository.dart index bd7b035157..9fcb57ae21 100644 --- a/mobile/lib/repositories/folder_api.repository.dart +++ b/mobile/lib/repositories/folder_api.repository.dart @@ -1,6 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/interfaces/folder_api.interface.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:logging/logging.dart'; @@ -12,14 +11,12 @@ final folderApiRepositoryProvider = Provider( ), ); -class FolderApiRepository extends ApiRepository - implements IFolderApiRepository { +class FolderApiRepository extends ApiRepository { final ViewApi _api; final Logger _log = Logger("FolderApiRepository"); FolderApiRepository(this._api); - @override Future> getAllUniquePaths() async { try { final list = await _api.getUniqueOriginalPaths(); @@ -30,7 +27,6 @@ class FolderApiRepository extends ApiRepository } } - @override Future> getAssetsForPath(String? path) async { try { final list = await _api.getAssetsByOriginalPath(path ?? '/'); diff --git a/mobile/lib/repositories/local_files_manager.repository.dart b/mobile/lib/repositories/local_files_manager.repository.dart index c2e234d14d..35c59acc2f 100644 --- a/mobile/lib/repositories/local_files_manager.repository.dart +++ b/mobile/lib/repositories/local_files_manager.repository.dart @@ -1,25 +1,25 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/local_files_manager.interface.dart'; -import 'package:immich_mobile/utils/local_files_manager.dart'; +import 'package:immich_mobile/services/local_files_manager.service.dart'; -final localFilesManagerRepositoryProvider = - Provider((ref) => const LocalFilesManagerRepository()); +final localFilesManagerRepositoryProvider = Provider( + (ref) => + LocalFilesManagerRepository(ref.watch(localFileManagerServiceProvider)), +); -class LocalFilesManagerRepository implements ILocalFilesManager { - const LocalFilesManagerRepository(); +class LocalFilesManagerRepository { + const LocalFilesManagerRepository(this._service); + + final LocalFilesManagerService _service; - @override Future moveToTrash(List mediaUrls) async { - return await LocalFilesManager.moveToTrash(mediaUrls); + return await _service.moveToTrash(mediaUrls); } - @override Future restoreFromTrash(String fileName, int type) async { - return await LocalFilesManager.restoreFromTrash(fileName, type); + return await _service.restoreFromTrash(fileName, type); } - @override Future requestManageMediaPermission() async { - return await LocalFilesManager.requestManageMediaPermission(); + return await _service.requestManageMediaPermission(); } } diff --git a/mobile/lib/repositories/network.repository.dart b/mobile/lib/repositories/network.repository.dart index 54f527afb1..2f859e9452 100644 --- a/mobile/lib/repositories/network.repository.dart +++ b/mobile/lib/repositories/network.repository.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/network.interface.dart'; import 'package:network_info_plus/network_info_plus.dart'; final networkRepositoryProvider = Provider((_) { @@ -10,12 +9,11 @@ final networkRepositoryProvider = Provider((_) { return NetworkRepository(networkInfo); }); -class NetworkRepository implements INetworkRepository { +class NetworkRepository { final NetworkInfo _networkInfo; - NetworkRepository(this._networkInfo); + const NetworkRepository(this._networkInfo); - @override Future getWifiName() { if (Platform.isAndroid) { // remove quote around the return value on Android @@ -30,7 +28,6 @@ class NetworkRepository implements INetworkRepository { return _networkInfo.getWifiName(); } - @override Future getWifiIp() { return _networkInfo.getWifiIP(); } diff --git a/mobile/lib/repositories/partner.repository.dart b/mobile/lib/repositories/partner.repository.dart index 5ea10f98c8..eb41391ba2 100644 --- a/mobile/lib/repositories/partner.repository.dart +++ b/mobile/lib/repositories/partner.repository.dart @@ -2,7 +2,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; -import 'package:immich_mobile/interfaces/partner.interface.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:isar/isar.dart'; @@ -11,11 +10,9 @@ final partnerRepositoryProvider = Provider( (ref) => PartnerRepository(ref.watch(dbProvider)), ); -class PartnerRepository extends DatabaseRepository - implements IPartnerRepository { - PartnerRepository(super.db); +class PartnerRepository extends DatabaseRepository { + const PartnerRepository(super.db); - @override Future> getSharedBy() async { return (await db.users .filter() @@ -26,7 +23,6 @@ class PartnerRepository extends DatabaseRepository .toList(); } - @override Future> getSharedWith() async { return (await db.users .filter() @@ -37,13 +33,11 @@ class PartnerRepository extends DatabaseRepository .toList(); } - @override Stream> watchSharedBy() { return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch()) .map((users) => users.map((u) => u.toDto()).toList()); } - @override Stream> watchSharedWith() { return (db.users .filter() diff --git a/mobile/lib/repositories/partner_api.repository.dart b/mobile/lib/repositories/partner_api.repository.dart index 367e2a58d7..836a708e3a 100644 --- a/mobile/lib/repositories/partner_api.repository.dart +++ b/mobile/lib/repositories/partner_api.repository.dart @@ -1,24 +1,26 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; -import 'package:immich_mobile/interfaces/partner_api.interface.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; +enum Direction { + sharedWithMe, + sharedByMe, +} + final partnerApiRepositoryProvider = Provider( (ref) => PartnerApiRepository( ref.watch(apiServiceProvider).partnersApi, ), ); -class PartnerApiRepository extends ApiRepository - implements IPartnerApiRepository { +class PartnerApiRepository extends ApiRepository { final PartnersApi _api; PartnerApiRepository(this._api); - @override Future> getAll(Direction direction) async { final response = await checkNull( _api.getPartners( @@ -30,16 +32,13 @@ class PartnerApiRepository extends ApiRepository return response.map(UserConverter.fromPartnerDto).toList(); } - @override Future create(String id) async { final dto = await checkNull(_api.createPartner(id)); return UserConverter.fromPartnerDto(dto); } - @override Future delete(String id) => _api.removePartner(id); - @override Future update(String id, {required bool inTimeline}) async { final dto = await checkNull( _api.updatePartner( diff --git a/mobile/lib/repositories/permission.repository.dart b/mobile/lib/repositories/permission.repository.dart index f825c36075..74230a3652 100644 --- a/mobile/lib/repositories/permission.repository.dart +++ b/mobile/lib/repositories/permission.repository.dart @@ -2,11 +2,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:permission_handler/permission_handler.dart'; final permissionRepositoryProvider = Provider((_) { - return PermissionRepository(); + return const PermissionRepository(); }); class PermissionRepository implements IPermissionRepository { - PermissionRepository(); + const PermissionRepository(); @override Future hasLocationWhenInUsePermission() { diff --git a/mobile/lib/repositories/person_api.repository.dart b/mobile/lib/repositories/person_api.repository.dart index d324a03edb..a2a6e2489b 100644 --- a/mobile/lib/repositories/person_api.repository.dart +++ b/mobile/lib/repositories/person_api.repository.dart @@ -1,5 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/person_api.interface.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; @@ -8,19 +8,16 @@ final personApiRepositoryProvider = Provider( (ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi), ); -class PersonApiRepository extends ApiRepository - implements IPersonApiRepository { +class PersonApiRepository extends ApiRepository { final PeopleApi _api; PersonApiRepository(this._api); - @override Future> getAll() async { final dto = await checkNull(_api.getAllPeople()); return dto.people.map(_toPerson).toList(); } - @override Future update(String id, {String? name}) async { final dto = await checkNull( _api.updatePerson(id, PersonUpdateDto(name: name)), diff --git a/mobile/lib/repositories/secure_storage.repository.dart b/mobile/lib/repositories/secure_storage.repository.dart index fc641bcc91..5660258a2d 100644 --- a/mobile/lib/repositories/secure_storage.repository.dart +++ b/mobile/lib/repositories/secure_storage.repository.dart @@ -1,26 +1,22 @@ 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())); + Provider((ref) => const SecureStorageRepository(FlutterSecureStorage())); -class SecureStorageRepository implements ISecureStorageRepository { +class SecureStorageRepository { final FlutterSecureStorage _secureStorage; - SecureStorageRepository(this._secureStorage); + const 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/sessions_api.repository.dart b/mobile/lib/repositories/sessions_api.repository.dart index e36331eb79..8ef43d54f2 100644 --- a/mobile/lib/repositories/sessions_api.repository.dart +++ b/mobile/lib/repositories/sessions_api.repository.dart @@ -1,5 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/sessions_api.interface.dart'; import 'package:immich_mobile/models/sessions/session_create_response.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; @@ -11,13 +10,11 @@ final sessionsAPIRepositoryProvider = Provider( ), ); -class SessionsAPIRepository extends ApiRepository - implements ISessionAPIRepository { +class SessionsAPIRepository extends ApiRepository { final SessionsApi _api; SessionsAPIRepository(this._api); - @override Future createSession( String deviceType, String deviceOS, { diff --git a/mobile/lib/repositories/share_handler.repository.dart b/mobile/lib/repositories/share_handler.repository.dart index 4c07b662a8..e739ebe6ae 100644 --- a/mobile/lib/repositories/share_handler.repository.dart +++ b/mobile/lib/repositories/share_handler.repository.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/share_handler.interface.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:share_handler/share_handler.dart'; @@ -9,13 +8,11 @@ final shareHandlerRepositoryProvider = Provider( (ref) => ShareHandlerRepository(), ); -class ShareHandlerRepository implements IShareHandlerRepository { +class ShareHandlerRepository { ShareHandlerRepository(); - @override void Function(List attachments)? onSharedMedia; - @override Future init() async { final handler = ShareHandlerPlatform.instance; final media = await handler.getInitialSharedMedia(); diff --git a/mobile/lib/repositories/timeline.repository.dart b/mobile/lib/repositories/timeline.repository.dart index aa5bdeb4e4..ea4cb63753 100644 --- a/mobile/lib/repositories/timeline.repository.dart +++ b/mobile/lib/repositories/timeline.repository.dart @@ -3,7 +3,6 @@ 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/infrastructure/entities/user.entity.dart'; -import 'package:immich_mobile/interfaces/timeline.interface.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:immich_mobile/utils/hash.dart'; @@ -13,11 +12,9 @@ import 'package:isar/isar.dart'; final timelineRepositoryProvider = Provider((ref) => TimelineRepository(ref.watch(dbProvider))); -class TimelineRepository extends DatabaseRepository - implements ITimelineRepository { - TimelineRepository(super.db); +class TimelineRepository extends DatabaseRepository { + const TimelineRepository(super.db); - @override Future> getTimelineUserIds(String id) { return db.users .filter() @@ -28,7 +25,6 @@ class TimelineRepository extends DatabaseRepository .findAll(); } - @override Stream> watchTimelineUsers(String id) { return db.users .filter() @@ -39,7 +35,6 @@ class TimelineRepository extends DatabaseRepository .watch(); } - @override Stream watchArchiveTimeline(String userId) { final query = db.assets .where() @@ -52,7 +47,6 @@ class TimelineRepository extends DatabaseRepository return _watchRenderList(query, GroupAssetsBy.none); } - @override Stream watchFavoriteTimeline(String userId) { final query = db.assets .where() @@ -67,7 +61,6 @@ class TimelineRepository extends DatabaseRepository return _watchRenderList(query, GroupAssetsBy.none); } - @override Stream watchAlbumTimeline( Album album, GroupAssetsBy groupAssetByOption, @@ -86,7 +79,6 @@ class TimelineRepository extends DatabaseRepository return _watchRenderList(withSortedOption, groupAssetByOption); } - @override Stream watchTrashTimeline(String userId) { final query = db.assets .filter() @@ -97,7 +89,6 @@ class TimelineRepository extends DatabaseRepository return _watchRenderList(query, GroupAssetsBy.none); } - @override Stream watchAllVideosTimeline(String userId) { final query = db.assets .where() @@ -111,7 +102,6 @@ class TimelineRepository extends DatabaseRepository return _watchRenderList(query, GroupAssetsBy.none); } - @override Stream watchHomeTimeline( String userId, GroupAssetsBy groupAssetByOption, @@ -128,7 +118,6 @@ class TimelineRepository extends DatabaseRepository return _watchRenderList(query, groupAssetByOption); } - @override Stream watchMultiUsersTimeline( List userIds, GroupAssetsBy groupAssetByOption, @@ -145,7 +134,6 @@ class TimelineRepository extends DatabaseRepository return _watchRenderList(query, groupAssetByOption); } - @override Future getTimelineFromAssets( List assets, GroupAssetsBy getGroupByOption, @@ -153,7 +141,6 @@ class TimelineRepository extends DatabaseRepository return RenderList.fromAssets(assets, getGroupByOption); } - @override Stream watchAssetSelectionTimeline(String userId) { final query = db.assets .where() @@ -168,7 +155,6 @@ class TimelineRepository extends DatabaseRepository return _watchRenderList(query, GroupAssetsBy.none); } - @override Stream watchLockedTimeline( String userId, GroupAssetsBy getGroupByOption, diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index 6445d144f6..4f840fa3c6 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -1,15 +1,12 @@ import 'package:background_downloader/background_downloader.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/upload.interface.dart'; import 'package:immich_mobile/utils/upload.dart'; final uploadRepositoryProvider = Provider((ref) => UploadRepository()); -class UploadRepository implements IUploadRepository { - @override +class UploadRepository { void Function(TaskStatusUpdate)? onUploadStatus; - @override void Function(TaskProgressUpdate)? onTaskProgress; UploadRepository() { @@ -20,22 +17,18 @@ class UploadRepository implements IUploadRepository { ); } - @override Future upload(UploadTask task) { return FileDownloader().enqueue(task); } - @override Future deleteAllTrackingRecords() { return FileDownloader().database.deleteAllRecords(); } - @override Future cancel(String id) { return FileDownloader().cancelTaskWithId(id); } - @override Future deleteRecordsWithIds(List ids) { return FileDownloader().database.deleteRecordsWithIds(ids); } diff --git a/mobile/lib/repositories/widget.repository.dart b/mobile/lib/repositories/widget.repository.dart new file mode 100644 index 0000000000..be314a281e --- /dev/null +++ b/mobile/lib/repositories/widget.repository.dart @@ -0,0 +1,20 @@ +import 'package:home_widget/home_widget.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +final widgetRepositoryProvider = Provider((_) => const WidgetRepository()); + +class WidgetRepository { + const WidgetRepository(); + + Future saveData(String key, String value) async { + await HomeWidget.saveWidgetData(key, value); + } + + Future refresh(String name) async { + await HomeWidget.updateWidget(name: name, iOSName: name); + } + + Future setAppGroupId(String appGroupId) async { + await HomeWidget.setAppGroupId(appGroupId); + } +} diff --git a/mobile/lib/routing/app_navigation_observer.dart b/mobile/lib/routing/app_navigation_observer.dart index 44662c0b8b..047e897c8e 100644 --- a/mobile/lib/routing/app_navigation_observer.dart +++ b/mobile/lib/routing/app_navigation_observer.dart @@ -25,6 +25,11 @@ class AppNavigationObserver extends AutoRouterObserver { @override void didPush(Route route, Route? previousRoute) { _handleLockedViewState(route, previousRoute); + + Future( + () => ref.read(currentRouteNameProvider.notifier).state = + route.settings.name, + ); } _handleLockedViewState(Route route, Route? previousRoute) { diff --git a/mobile/lib/routing/backup_permission_guard.dart b/mobile/lib/routing/backup_permission_guard.dart index 57a0c7a927..245a4b27af 100644 --- a/mobile/lib/routing/backup_permission_guard.dart +++ b/mobile/lib/routing/backup_permission_guard.dart @@ -5,7 +5,7 @@ import 'package:immich_mobile/routing/router.dart'; class BackupPermissionGuard extends AutoRouteGuard { final GalleryPermissionNotifier _permission; - BackupPermissionGuard(this._permission); + const BackupPermissionGuard(this._permission); @override void onNavigation(NavigationResolver resolver, StackRouter router) async { diff --git a/mobile/lib/routing/duplicate_guard.dart b/mobile/lib/routing/duplicate_guard.dart index 217ff1cbf4..efc649bc41 100644 --- a/mobile/lib/routing/duplicate_guard.dart +++ b/mobile/lib/routing/duplicate_guard.dart @@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart'; /// Guards against duplicate navigation to this route class DuplicateGuard extends AutoRouteGuard { - DuplicateGuard(); + const DuplicateGuard(); @override void onNavigation(NavigationResolver resolver, StackRouter router) async { // Duplicate navigation diff --git a/mobile/lib/routing/gallery_guard.dart b/mobile/lib/routing/gallery_guard.dart new file mode 100644 index 0000000000..fcac142271 --- /dev/null +++ b/mobile/lib/routing/gallery_guard.dart @@ -0,0 +1,31 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:immich_mobile/routing/router.dart'; + +/// Handles duplicate navigation to this route (primarily for deep linking) +class GalleryGuard extends AutoRouteGuard { + const GalleryGuard(); + @override + void onNavigation(NavigationResolver resolver, StackRouter router) async { + final newRouteName = resolver.route.name; + final currentTopRouteName = + router.stack.isNotEmpty ? router.stack.last.name : null; + + if (currentTopRouteName == newRouteName) { + // Replace instead of pushing duplicate + final args = resolver.route.args as GalleryViewerRouteArgs; + + router.replace( + GalleryViewerRoute( + renderList: args.renderList, + initialIndex: args.initialIndex, + heroOffset: args.heroOffset, + showStack: args.showStack, + ), + ); + // Prevent further navigation since we replaced the route + resolver.next(false); + return; + } + resolver.next(true); + } +} diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 1f14aaa5bf..18aa937a9d 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -1,8 +1,11 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; +import 'package:immich_mobile/domain/models/memory.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/models/folder/recursive_folder.model.dart'; @@ -31,6 +34,7 @@ import 'package:immich_mobile/pages/common/native_video_viewer.page.dart'; import 'package:immich_mobile/pages/common/settings.page.dart'; import 'package:immich_mobile/pages/common/splash_screen.page.dart'; import 'package:immich_mobile/pages/common/tab_controller.page.dart'; +import 'package:immich_mobile/pages/common/tab_shell.page.dart'; import 'package:immich_mobile/pages/editing/crop.page.dart'; import 'package:immich_mobile/pages/editing/edit.page.dart'; import 'package:immich_mobile/pages/editing/filter.page.dart'; @@ -63,19 +67,37 @@ import 'package:immich_mobile/pages/search/person_result.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/presentation/pages/dev/drift_favorite.page.dart'; +import 'package:immich_mobile/presentation/pages/dev/drift_partner_detail.page.dart'; +import 'package:immich_mobile/presentation/pages/dev/drift_local_album.page.dart'; +import 'package:immich_mobile/presentation/pages/dev/drift_recently_taken.page.dart'; +import 'package:immich_mobile/presentation/pages/dev/drift_video.page.dart'; +import 'package:immich_mobile/presentation/pages/dev/drift_trash.page.dart'; +import 'package:immich_mobile/presentation/pages/dev/drift_archive.page.dart'; +import 'package:immich_mobile/presentation/pages/dev/drift_locked_folder.page.dart'; import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart'; +import 'package:immich_mobile/presentation/pages/dev/local_timeline.page.dart'; +import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart'; import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart'; +import 'package:immich_mobile/presentation/pages/dev/remote_timeline.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_album.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_library.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_asset_selection_timeline.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_memory.page.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; 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/gallery_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'; @@ -95,6 +117,7 @@ class AppRouter extends RootStackRouter { late final DuplicateGuard _duplicateGuard; late final BackupPermissionGuard _backupPermissionGuard; late final LockedGuard _lockedGuard; + late final GalleryGuard _galleryGuard; AppRouter( ApiService apiService, @@ -103,10 +126,11 @@ class AppRouter extends RootStackRouter { LocalAuthService localAuthService, ) { _authGuard = AuthGuard(apiService); - _duplicateGuard = DuplicateGuard(); + _duplicateGuard = const DuplicateGuard(); _lockedGuard = LockedGuard(apiService, secureStorageService, localAuthService); _backupPermissionGuard = BackupPermissionGuard(galleryPermissionNotifier); + _galleryGuard = const GalleryGuard(); } @override @@ -151,8 +175,32 @@ class AppRouter extends RootStackRouter { transitionsBuilder: TransitionsBuilders.fadeIn, ), CustomRoute( - page: GalleryViewerRoute.page, + page: TabShellRoute.page, guards: [_authGuard, _duplicateGuard], + children: [ + AutoRoute( + page: MainTimelineRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: SearchRoute.page, + guards: [_authGuard, _duplicateGuard], + maintainState: false, + ), + AutoRoute( + page: DriftLibraryRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftAlbumsRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + ], + transitionsBuilder: TransitionsBuilders.fadeIn, + ), + CustomRoute( + page: GalleryViewerRoute.page, + guards: [_authGuard, _galleryGuard], transitionsBuilder: CustomTransitionsBuilders.zoomedPage, ), AutoRoute( @@ -330,5 +378,76 @@ class AppRouter extends RootStackRouter { page: RemoteMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard], ), + AutoRoute( + page: LocalTimelineRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: MainTimelineRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: RemoteTimelineRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: AssetViewerRoute.page, + guards: [_authGuard, _duplicateGuard], + type: RouteType.custom( + customRouteBuilder: (context, child, page) => PageRouteBuilder( + fullscreenDialog: page.fullscreenDialog, + settings: page, + pageBuilder: (_, __, ___) => child, + opaque: false, + ), + ), + ), + AutoRoute( + page: DriftMemoryRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftFavoriteRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftTrashRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftArchiveRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftLockedFolderRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftVideoRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftLibraryRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftAssetSelectionTimelineRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftPartnerDetailRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftRecentlyTakenRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + AutoRoute( + page: DriftLocalAlbumsRoute.page, + guards: [_authGuard, _duplicateGuard], + ), + // required to handle all deeplinks in deep_link.service.dart + // auto_route_library#1722 + RedirectRoute(path: '*', redirectTo: '/'), ]; } diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 0c57949f04..b13320b1c0 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -403,6 +403,58 @@ class ArchiveRoute extends PageRouteInfo { ); } +/// generated route for +/// [AssetViewerPage] +class AssetViewerRoute extends PageRouteInfo { + AssetViewerRoute({ + Key? key, + required int initialIndex, + required TimelineService timelineService, + List? children, + }) : super( + AssetViewerRoute.name, + args: AssetViewerRouteArgs( + key: key, + initialIndex: initialIndex, + timelineService: timelineService, + ), + initialChildren: children, + ); + + static const String name = 'AssetViewerRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return AssetViewerPage( + key: args.key, + initialIndex: args.initialIndex, + timelineService: args.timelineService, + ); + }, + ); +} + +class AssetViewerRouteArgs { + const AssetViewerRouteArgs({ + this.key, + required this.initialIndex, + required this.timelineService, + }); + + final Key? key; + + final int initialIndex; + + final TimelineService timelineService; + + @override + String toString() { + return 'AssetViewerRouteArgs{key: $key, initialIndex: $initialIndex, timelineService: $timelineService}'; + } +} + /// generated route for /// [BackupAlbumSelectionPage] class BackupAlbumSelectionRoute extends PageRouteInfo { @@ -550,6 +602,289 @@ class CropImageRouteArgs { } } +/// generated route for +/// [DriftAlbumsPage] +class DriftAlbumsRoute extends PageRouteInfo { + const DriftAlbumsRoute({List? children}) + : super(DriftAlbumsRoute.name, initialChildren: children); + + static const String name = 'DriftAlbumsRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftAlbumsPage(); + }, + ); +} + +/// generated route for +/// [DriftArchivePage] +class DriftArchiveRoute extends PageRouteInfo { + const DriftArchiveRoute({List? children}) + : super(DriftArchiveRoute.name, initialChildren: children); + + static const String name = 'DriftArchiveRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftArchivePage(); + }, + ); +} + +/// generated route for +/// [DriftAssetSelectionTimelinePage] +class DriftAssetSelectionTimelineRoute + extends PageRouteInfo { + DriftAssetSelectionTimelineRoute({ + Key? key, + Set lockedSelectionAssets = const {}, + List? children, + }) : super( + DriftAssetSelectionTimelineRoute.name, + args: DriftAssetSelectionTimelineRouteArgs( + key: key, + lockedSelectionAssets: lockedSelectionAssets, + ), + initialChildren: children, + ); + + static const String name = 'DriftAssetSelectionTimelineRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs( + orElse: () => const DriftAssetSelectionTimelineRouteArgs(), + ); + return DriftAssetSelectionTimelinePage( + key: args.key, + lockedSelectionAssets: args.lockedSelectionAssets, + ); + }, + ); +} + +class DriftAssetSelectionTimelineRouteArgs { + const DriftAssetSelectionTimelineRouteArgs({ + this.key, + this.lockedSelectionAssets = const {}, + }); + + final Key? key; + + final Set lockedSelectionAssets; + + @override + String toString() { + return 'DriftAssetSelectionTimelineRouteArgs{key: $key, lockedSelectionAssets: $lockedSelectionAssets}'; + } +} + +/// generated route for +/// [DriftFavoritePage] +class DriftFavoriteRoute extends PageRouteInfo { + const DriftFavoriteRoute({List? children}) + : super(DriftFavoriteRoute.name, initialChildren: children); + + static const String name = 'DriftFavoriteRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftFavoritePage(); + }, + ); +} + +/// generated route for +/// [DriftLibraryPage] +class DriftLibraryRoute extends PageRouteInfo { + const DriftLibraryRoute({List? children}) + : super(DriftLibraryRoute.name, initialChildren: children); + + static const String name = 'DriftLibraryRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftLibraryPage(); + }, + ); +} + +/// generated route for +/// [DriftLocalAlbumsPage] +class DriftLocalAlbumsRoute extends PageRouteInfo { + const DriftLocalAlbumsRoute({List? children}) + : super(DriftLocalAlbumsRoute.name, initialChildren: children); + + static const String name = 'DriftLocalAlbumsRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftLocalAlbumsPage(); + }, + ); +} + +/// generated route for +/// [DriftLockedFolderPage] +class DriftLockedFolderRoute extends PageRouteInfo { + const DriftLockedFolderRoute({List? children}) + : super(DriftLockedFolderRoute.name, initialChildren: children); + + static const String name = 'DriftLockedFolderRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftLockedFolderPage(); + }, + ); +} + +/// generated route for +/// [DriftMemoryPage] +class DriftMemoryRoute extends PageRouteInfo { + DriftMemoryRoute({ + required List memories, + required int memoryIndex, + Key? key, + List? children, + }) : super( + DriftMemoryRoute.name, + args: DriftMemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); + + static const String name = 'DriftMemoryRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftMemoryPage( + memories: args.memories, + memoryIndex: args.memoryIndex, + key: args.key, + ); + }, + ); +} + +class DriftMemoryRouteArgs { + const DriftMemoryRouteArgs({ + required this.memories, + required this.memoryIndex, + this.key, + }); + + final List memories; + + final int memoryIndex; + + final Key? key; + + @override + String toString() { + return 'DriftMemoryRouteArgs{memories: $memories, memoryIndex: $memoryIndex, key: $key}'; + } +} + +/// generated route for +/// [DriftPartnerDetailPage] +class DriftPartnerDetailRoute + extends PageRouteInfo { + DriftPartnerDetailRoute({ + Key? key, + required String partnerId, + List? children, + }) : super( + DriftPartnerDetailRoute.name, + args: DriftPartnerDetailRouteArgs(key: key, partnerId: partnerId), + initialChildren: children, + ); + + static const String name = 'DriftPartnerDetailRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftPartnerDetailPage(key: args.key, partnerId: args.partnerId); + }, + ); +} + +class DriftPartnerDetailRouteArgs { + const DriftPartnerDetailRouteArgs({this.key, required this.partnerId}); + + final Key? key; + + final String partnerId; + + @override + String toString() { + return 'DriftPartnerDetailRouteArgs{key: $key, partnerId: $partnerId}'; + } +} + +/// generated route for +/// [DriftRecentlyTakenPage] +class DriftRecentlyTakenRoute extends PageRouteInfo { + const DriftRecentlyTakenRoute({List? children}) + : super(DriftRecentlyTakenRoute.name, initialChildren: children); + + static const String name = 'DriftRecentlyTakenRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftRecentlyTakenPage(); + }, + ); +} + +/// generated route for +/// [DriftTrashPage] +class DriftTrashRoute extends PageRouteInfo { + const DriftTrashRoute({List? children}) + : super(DriftTrashRoute.name, initialChildren: children); + + static const String name = 'DriftTrashRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftTrashPage(); + }, + ); +} + +/// generated route for +/// [DriftVideoPage] +class DriftVideoRoute extends PageRouteInfo { + const DriftVideoRoute({List? children}) + : super(DriftVideoRoute.name, initialChildren: children); + + static const String name = 'DriftVideoRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftVideoPage(); + }, + ); +} + /// generated route for /// [EditImagePage] class EditImageRoute extends PageRouteInfo { @@ -871,6 +1206,43 @@ class LocalMediaSummaryRoute extends PageRouteInfo { ); } +/// generated route for +/// [LocalTimelinePage] +class LocalTimelineRoute extends PageRouteInfo { + LocalTimelineRoute({ + Key? key, + required String albumId, + List? children, + }) : super( + LocalTimelineRoute.name, + args: LocalTimelineRouteArgs(key: key, albumId: albumId), + initialChildren: children, + ); + + static const String name = 'LocalTimelineRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return LocalTimelinePage(key: args.key, albumId: args.albumId); + }, + ); +} + +class LocalTimelineRouteArgs { + const LocalTimelineRouteArgs({this.key, required this.albumId}); + + final Key? key; + + final String albumId; + + @override + String toString() { + return 'LocalTimelineRouteArgs{key: $key, albumId: $albumId}'; + } +} + /// generated route for /// [LockedPage] class LockedRoute extends PageRouteInfo { @@ -903,6 +1275,22 @@ class LoginRoute extends PageRouteInfo { ); } +/// generated route for +/// [MainTimelinePage] +class MainTimelineRoute extends PageRouteInfo { + const MainTimelineRoute({List? children}) + : super(MainTimelineRoute.name, initialChildren: children); + + static const String name = 'MainTimelineRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const MainTimelinePage(); + }, + ); +} + /// generated route for /// [MapLocationPickerPage] class MapLocationPickerRoute extends PageRouteInfo { @@ -1372,6 +1760,43 @@ class RemoteMediaSummaryRoute extends PageRouteInfo { ); } +/// generated route for +/// [RemoteTimelinePage] +class RemoteTimelineRoute extends PageRouteInfo { + RemoteTimelineRoute({ + Key? key, + required String albumId, + List? children, + }) : super( + RemoteTimelineRoute.name, + args: RemoteTimelineRouteArgs(key: key, albumId: albumId), + initialChildren: children, + ); + + static const String name = 'RemoteTimelineRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return RemoteTimelinePage(key: args.key, albumId: args.albumId); + }, + ); +} + +class RemoteTimelineRouteArgs { + const RemoteTimelineRouteArgs({this.key, required this.albumId}); + + final Key? key; + + final String albumId; + + @override + String toString() { + return 'RemoteTimelineRouteArgs{key: $key, albumId: $albumId}'; + } +} + /// generated route for /// [SearchPage] class SearchRoute extends PageRouteInfo { @@ -1609,6 +2034,22 @@ class TabControllerRoute extends PageRouteInfo { ); } +/// generated route for +/// [TabShellPage] +class TabShellRoute extends PageRouteInfo { + const TabShellRoute({List? children}) + : super(TabShellRoute.name, initialChildren: children); + + static const String name = 'TabShellRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const TabShellPage(); + }, + ); +} + /// generated route for /// [TrashPage] class TrashRoute extends PageRouteInfo { diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart new file mode 100644 index 0000000000..2f4c8cc926 --- /dev/null +++ b/mobile/lib/services/action.service.dart @@ -0,0 +1,134 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/repositories/asset_api.repository.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/common/location_picker.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +final actionServiceProvider = Provider( + (ref) => ActionService( + ref.watch(assetApiRepositoryProvider), + ref.watch(remoteAssetRepositoryProvider), + ), +); + +class ActionService { + final AssetApiRepository _assetApiRepository; + final RemoteAssetRepository _remoteAssetRepository; + + const ActionService( + this._assetApiRepository, + this._remoteAssetRepository, + ); + + Future shareLink(List remoteIds, BuildContext context) async { + context.pushRoute( + SharedLinkEditRoute( + assetsList: remoteIds, + ), + ); + } + + Future favorite(List remoteIds) async { + await _assetApiRepository.updateFavorite(remoteIds, true); + await _remoteAssetRepository.updateFavorite(remoteIds, true); + } + + Future unFavorite(List remoteIds) async { + await _assetApiRepository.updateFavorite(remoteIds, false); + await _remoteAssetRepository.updateFavorite(remoteIds, false); + } + + Future archive(List remoteIds) async { + await _assetApiRepository.updateVisibility( + remoteIds, + AssetVisibilityEnum.archive, + ); + await _remoteAssetRepository.updateVisibility( + remoteIds, + AssetVisibility.archive, + ); + } + + Future unArchive(List remoteIds) async { + await _assetApiRepository.updateVisibility( + remoteIds, + AssetVisibilityEnum.timeline, + ); + await _remoteAssetRepository.updateVisibility( + remoteIds, + AssetVisibility.timeline, + ); + } + + Future moveToLockFolder(List remoteIds) async { + await _assetApiRepository.updateVisibility( + remoteIds, + AssetVisibilityEnum.locked, + ); + await _remoteAssetRepository.updateVisibility( + remoteIds, + AssetVisibility.locked, + ); + } + + Future removeFromLockFolder(List remoteIds) async { + await _assetApiRepository.updateVisibility( + remoteIds, + AssetVisibilityEnum.timeline, + ); + await _remoteAssetRepository.updateVisibility( + remoteIds, + AssetVisibility.timeline, + ); + } + + Future trash(List remoteIds) async { + await _assetApiRepository.delete(remoteIds, false); + await _remoteAssetRepository.trash(remoteIds); + } + + Future delete(List remoteIds) async { + await _assetApiRepository.delete(remoteIds, true); + await _remoteAssetRepository.delete(remoteIds); + } + + Future editLocation( + List remoteIds, + BuildContext context, + ) async { + LatLng? initialLatLng; + if (remoteIds.length == 1) { + final exif = await _remoteAssetRepository.getExif(remoteIds[0]); + + if (exif?.latitude != null && exif?.longitude != null) { + initialLatLng = LatLng(exif!.latitude!, exif.longitude!); + } + } + + final location = await showLocationPicker( + context: context, + initialLatLng: initialLatLng, + ); + + if (location == null) { + return false; + } + + await _assetApiRepository.updateLocation( + remoteIds, + location, + ); + await _remoteAssetRepository.updateLocation( + remoteIds, + location, + ); + + return true; + } +} diff --git a/mobile/lib/services/activity.service.dart b/mobile/lib/services/activity.service.dart index 5496041416..611d985afe 100644 --- a/mobile/lib/services/activity.service.dart +++ b/mobile/lib/services/activity.service.dart @@ -1,10 +1,10 @@ -import 'package:immich_mobile/interfaces/activity_api.interface.dart'; import 'package:immich_mobile/mixins/error_logger.mixin.dart'; import 'package:immich_mobile/models/activities/activity.model.dart'; +import 'package:immich_mobile/repositories/activity_api.repository.dart'; import 'package:logging/logging.dart'; class ActivityService with ErrorLoggerMixin { - final IActivityApiRepository _activityApiRepository; + final ActivityApiRepository _activityApiRepository; @override final Logger logger = Logger("ActivityService"); diff --git a/mobile/lib/services/album.service.dart b/mobile/lib/services/album.service.dart index f1e8721040..6733ec41b2 100644 --- a/mobile/lib/services/album.service.dart +++ b/mobile/lib/services/album.service.dart @@ -13,11 +13,6 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; -import 'package:immich_mobile/interfaces/album.interface.dart'; -import 'package:immich_mobile/interfaces/album_api.interface.dart'; -import 'package:immich_mobile/interfaces/album_media.interface.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; -import 'package:immich_mobile/interfaces/backup_album.interface.dart'; import 'package:immich_mobile/models/albums/album_add_asset_response.model.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; @@ -48,11 +43,11 @@ class AlbumService { final SyncService _syncService; final UserService _userService; final EntityService _entityService; - final IAlbumRepository _albumRepository; - final IAssetRepository _assetRepository; - final IBackupAlbumRepository _backupAlbumRepository; - final IAlbumMediaRepository _albumMediaRepository; - final IAlbumApiRepository _albumApiRepository; + final AlbumRepository _albumRepository; + final AssetRepository _assetRepository; + final BackupAlbumRepository _backupAlbumRepository; + final AlbumMediaRepository _albumMediaRepository; + final AlbumApiRepository _albumApiRepository; final Logger _log = Logger('AlbumService'); Completer _localCompleter = Completer()..complete(false); Completer _remoteCompleter = Completer()..complete(false); @@ -491,6 +486,10 @@ class AlbumService { return _albumRepository.get(id); } + Future getAlbumByRemoteId(String remoteId) { + return _albumRepository.getByRemoteId(remoteId); + } + Stream watchAlbum(int id) { return _albumRepository.watchAlbum(id); } diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index b6c675b636..8d1f7c24a5 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -88,7 +88,7 @@ enum AppSettingsEnum { photoManagerCustomFilter( StoreKey.photoManagerCustomFilter, null, - false, + true, ), ; @@ -100,6 +100,7 @@ enum AppSettingsEnum { } class AppSettingsService { + const AppSettingsService(); T getSetting(AppSettingsEnum setting) { return Store.get(setting.storeKey, setting.defaultValue); } diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart index a52d6e6368..cb7f59e3a9 100644 --- a/mobile/lib/services/asset.service.dart +++ b/mobile/lib/services/asset.service.dart @@ -4,17 +4,12 @@ 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'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; -import 'package:immich_mobile/interfaces/asset_api.interface.dart'; -import 'package:immich_mobile/interfaces/asset_media.interface.dart'; -import 'package:immich_mobile/interfaces/backup_album.interface.dart'; -import 'package:immich_mobile/interfaces/etag.interface.dart'; +import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/infrastructure/exif.provider.dart'; @@ -50,25 +45,25 @@ final assetServiceProvider = Provider( ); class AssetService { - final IAssetApiRepository _assetApiRepository; - final IAssetRepository _assetRepository; - final IExifInfoRepository _exifInfoRepository; - final IUserRepository _userRepository; - final IETagRepository _etagRepository; - final IBackupAlbumRepository _backupRepository; + final AssetApiRepository _assetApiRepository; + final AssetRepository _assetRepository; + final IsarExifRepository _exifInfoRepository; + final IsarUserRepository _isarUserRepository; + final ETagRepository _etagRepository; + final BackupAlbumRepository _backupRepository; final ApiService _apiService; final SyncService _syncService; final BackupService _backupService; final AlbumService _albumService; final UserService _userService; - final IAssetMediaRepository _assetMediaRepository; + final AssetMediaRepository _assetMediaRepository; final log = Logger('AssetService'); AssetService( this._assetApiRepository, this._assetRepository, this._exifInfoRepository, - this._userRepository, + this._isarUserRepository, this._etagRepository, this._backupRepository, this._apiService, @@ -85,7 +80,9 @@ class AssetService { final syncedUserIds = await _etagRepository.getAllIds(); final List syncedUsers = syncedUserIds.isEmpty ? [] - : (await _userRepository.getByUserIds(syncedUserIds)).nonNulls.toList(); + : (await _isarUserRepository.getByUserIds(syncedUserIds)) + .nonNulls + .toList(); final Stopwatch sw = Stopwatch()..start(); final bool changes = await _syncService.syncRemoteAssetsToDb( users: syncedUsers, @@ -552,4 +549,9 @@ class AssetService { await _assetRepository.updateAll(updatedAssets); } + + Future getAssetByRemoteId(String remoteId) async { + final assets = await _assetRepository.getAllByRemoteId([remoteId]); + return assets.isNotEmpty ? assets.first : null; + } } diff --git a/mobile/lib/services/auth.service.dart b/mobile/lib/services/auth.service.dart index 41709b714c..0eec253ee1 100644 --- a/mobile/lib/services/auth.service.dart +++ b/mobile/lib/services/auth.service.dart @@ -5,8 +5,6 @@ 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'; @@ -29,8 +27,8 @@ final authServiceProvider = Provider( ); class AuthService { - final IAuthApiRepository _authApiRepository; - final IAuthRepository _authRepository; + final AuthApiRepository _authApiRepository; + final AuthRepository _authRepository; final ApiService _apiService; final NetworkService _networkService; final BackgroundSyncManager _backgroundSyncManager; diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index 4c4a9b1cff..97acf2941f 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -14,7 +14,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/interfaces/backup_album.interface.dart'; import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/error_upload_asset.model.dart'; diff --git a/mobile/lib/services/backup.service.dart b/mobile/lib/services/backup.service.dart index 596ad8dc2e..370b64398d 100644 --- a/mobile/lib/services/backup.service.dart +++ b/mobile/lib/services/backup.service.dart @@ -11,10 +11,6 @@ import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/interfaces/album_media.interface.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; -import 'package:immich_mobile/interfaces/asset_media.interface.dart'; -import 'package:immich_mobile/interfaces/file_media.interface.dart'; import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/error_upload_asset.model.dart'; @@ -52,10 +48,10 @@ class BackupService { final Logger _log = Logger("BackupService"); final AppSettingsService _appSetting; final AlbumService _albumService; - final IAlbumMediaRepository _albumMediaRepository; - final IFileMediaRepository _fileMediaRepository; - final IAssetRepository _assetRepository; - final IAssetMediaRepository _assetMediaRepository; + final AlbumMediaRepository _albumMediaRepository; + final FileMediaRepository _fileMediaRepository; + final AssetRepository _assetRepository; + final AssetMediaRepository _assetMediaRepository; BackupService( this._apiService, diff --git a/mobile/lib/services/backup_album.service.dart b/mobile/lib/services/backup_album.service.dart index 8030d66937..ef9d1031de 100644 --- a/mobile/lib/services/backup_album.service.dart +++ b/mobile/lib/services/backup_album.service.dart @@ -1,6 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; -import 'package:immich_mobile/interfaces/backup_album.interface.dart'; import 'package:immich_mobile/repositories/backup.repository.dart'; final backupAlbumServiceProvider = Provider((ref) { @@ -8,9 +7,9 @@ final backupAlbumServiceProvider = Provider((ref) { }); class BackupAlbumService { - final IBackupAlbumRepository _backupAlbumRepository; + final BackupAlbumRepository _backupAlbumRepository; - BackupAlbumService(this._backupAlbumRepository); + const BackupAlbumService(this._backupAlbumRepository); Future> getAll({BackupAlbumSort? sort}) { return _backupAlbumRepository.getAll(sort: sort); diff --git a/mobile/lib/services/backup_verification.service.dart b/mobile/lib/services/backup_verification.service.dart index 408ac51d74..6d6884eb00 100644 --- a/mobile/lib/services/backup_verification.service.dart +++ b/mobile/lib/services/backup_verification.service.dart @@ -5,15 +5,13 @@ import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; import 'package:immich_mobile/domain/models/exif.model.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'; import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart'; import 'package:immich_mobile/infrastructure/utils/exif.converter.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; -import 'package:immich_mobile/interfaces/file_media.interface.dart'; import 'package:immich_mobile/providers/infrastructure/exif.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; @@ -25,9 +23,9 @@ import 'package:immich_mobile/utils/diff.dart'; /// Finds duplicates originating from missing EXIF information class BackupVerificationService { final UserService _userService; - final IFileMediaRepository _fileMediaRepository; - final IAssetRepository _assetRepository; - final IExifInfoRepository _exifInfoRepository; + final FileMediaRepository _fileMediaRepository; + final AssetRepository _assetRepository; + final IsarExifRepository _exifInfoRepository; const BackupVerificationService( this._userService, @@ -123,7 +121,7 @@ class BackupVerificationService { String auth, String endpoint, RootIsolateToken rootIsolateToken, - IFileMediaRepository fileMediaRepository, + FileMediaRepository fileMediaRepository, }) tuple, ) async { assert(tuple.deleteCandidates.length == tuple.originals.length); diff --git a/mobile/lib/services/deep_link.service.dart b/mobile/lib/services/deep_link.service.dart new file mode 100644 index 0000000000..e97a370967 --- /dev/null +++ b/mobile/lib/services/deep_link.service.dart @@ -0,0 +1,135 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:immich_mobile/providers/album/current_album.provider.dart'; +import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/album.service.dart'; +import 'package:immich_mobile/services/asset.service.dart'; +import 'package:immich_mobile/services/memory.service.dart'; +import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +final deepLinkServiceProvider = Provider( + (ref) => DeepLinkService( + ref.watch(memoryServiceProvider), + ref.watch(assetServiceProvider), + ref.watch(albumServiceProvider), + ref.watch(currentAssetProvider.notifier), + ref.watch(currentAlbumProvider.notifier), + ), +); + +class DeepLinkService { + final MemoryService _memoryService; + final AssetService _assetService; + final AlbumService _albumService; + final CurrentAsset _currentAsset; + final CurrentAlbum _currentAlbum; + + const DeepLinkService( + this._memoryService, + this._assetService, + this._albumService, + this._currentAsset, + this._currentAlbum, + ); + + Future handleScheme(PlatformDeepLink link, bool isColdStart) async { + // get everything after the scheme, since Uri cannot parse path + final intent = link.uri.host; + final queryParams = link.uri.queryParameters; + + PageRouteInfo? deepLinkRoute = switch (intent) { + "memory" => await _buildMemoryDeepLink(queryParams['id'] ?? ''), + "asset" => await _buildAssetDeepLink(queryParams['id'] ?? ''), + "album" => await _buildAlbumDeepLink(queryParams['id'] ?? ''), + _ => null, + }; + + // Deep link resolution failed, safely handle it based on the app state + if (deepLinkRoute == null) { + if (isColdStart) { + return DeepLink.defaultPath; + } + + return DeepLink.none; + } + + return DeepLink([ + // we need something to segue back to if the app was cold started + if (isColdStart) const PhotosRoute(), + deepLinkRoute, + ]); + } + + Future handleMyImmichApp( + PlatformDeepLink link, + bool isColdStart, + ) async { + final path = link.uri.path; + + const uuidRegex = + r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; + final assetRegex = RegExp('/photos/($uuidRegex)'); + final albumRegex = RegExp('/albums/($uuidRegex)'); + + PageRouteInfo? deepLinkRoute; + if (assetRegex.hasMatch(path)) { + final assetId = assetRegex.firstMatch(path)?.group(1) ?? ''; + deepLinkRoute = await _buildAssetDeepLink(assetId); + } else if (albumRegex.hasMatch(path)) { + final albumId = albumRegex.firstMatch(path)?.group(1) ?? ''; + deepLinkRoute = await _buildAlbumDeepLink(albumId); + } + + // Deep link resolution failed, safely handle it based on the app state + if (deepLinkRoute == null) { + if (isColdStart) return DeepLink.defaultPath; + return DeepLink.none; + } + + return DeepLink([ + // we need something to segue back to if the app was cold started + if (isColdStart) const PhotosRoute(), + deepLinkRoute, + ]); + } + + Future _buildMemoryDeepLink(String memoryId) async { + final memory = await _memoryService.getMemoryById(memoryId); + + if (memory == null) { + return null; + } + + return MemoryRoute(memories: [memory], memoryIndex: 0); + } + + Future _buildAssetDeepLink(String assetId) async { + final asset = await _assetService.getAssetByRemoteId(assetId); + if (asset == null) { + return null; + } + + _currentAsset.set(asset); + final renderList = await RenderList.fromAssets([asset], GroupAssetsBy.auto); + + return GalleryViewerRoute( + renderList: renderList, + initialIndex: 0, + heroOffset: 0, + showStack: true, + ); + } + + Future _buildAlbumDeepLink(String albumId) async { + final album = await _albumService.getAlbumByRemoteId(albumId); + + if (album == null) { + return null; + } + + _currentAlbum.set(album); + + return AlbumViewerRoute(albumId: album.id); + } +} diff --git a/mobile/lib/services/device.service.dart b/mobile/lib/services/device.service.dart index 393274608b..50a0d93b24 100644 --- a/mobile/lib/services/device.service.dart +++ b/mobile/lib/services/device.service.dart @@ -3,10 +3,10 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -final deviceServiceProvider = Provider((ref) => DeviceService()); +final deviceServiceProvider = Provider((ref) => const DeviceService()); class DeviceService { - DeviceService(); + const DeviceService(); createDeviceId() { return FlutterUdid.consistentUdid; diff --git a/mobile/lib/services/download.service.dart b/mobile/lib/services/download.service.dart index e7ecff175c..98f1765d04 100644 --- a/mobile/lib/services/download.service.dart +++ b/mobile/lib/services/download.service.dart @@ -6,8 +6,6 @@ import 'package:hooks_riverpod/hooks_riverpod.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'; -import 'package:immich_mobile/interfaces/download.interface.dart'; -import 'package:immich_mobile/interfaces/file_media.interface.dart'; import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/repositories/download.repository.dart'; import 'package:immich_mobile/repositories/file_media.repository.dart'; @@ -23,8 +21,8 @@ final downloadServiceProvider = Provider( ); class DownloadService { - final IDownloadRepository _downloadRepository; - final IFileMediaRepository _fileMediaRepository; + final DownloadRepository _downloadRepository; + final FileMediaRepository _fileMediaRepository; final Logger _log = Logger("DownloadService"); void Function(TaskStatusUpdate)? onImageDownloadStatus; void Function(TaskStatusUpdate)? onVideoDownloadStatus; diff --git a/mobile/lib/services/entity.service.dart b/mobile/lib/services/entity.service.dart index 9e61366b94..8ffead40fa 100644 --- a/mobile/lib/services/entity.service.dart +++ b/mobile/lib/services/entity.service.dart @@ -1,24 +1,23 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/user.interface.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; +import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; class EntityService { - final IAssetRepository _assetRepository; - final IUserRepository _userRepository; - EntityService( + final AssetRepository _assetRepository; + final IsarUserRepository _isarUserRepository; + const EntityService( this._assetRepository, - this._userRepository, + this._isarUserRepository, ); Future fillAlbumWithDatabaseEntities(Album album) async { final ownerId = album.ownerId; if (ownerId != null) { // replace owner with user from database - final user = await _userRepository.getByUserId(ownerId); + final user = await _isarUserRepository.getByUserId(ownerId); album.owner.value = user == null ? null : User.fromDto(user); } final thumbnailAssetId = @@ -30,7 +29,7 @@ class EntityService { } if (album.remoteUsers.isNotEmpty) { // replace all users with users from database - final users = await _userRepository + final users = await _isarUserRepository .getByUserIds(album.remoteUsers.map((user) => user.id).toList()); album.sharedUsers.clear(); album.sharedUsers.addAll(users.nonNulls.map(User.fromDto)); diff --git a/mobile/lib/services/etag.service.dart b/mobile/lib/services/etag.service.dart index 6dd8a76bb3..4b6f2279ed 100644 --- a/mobile/lib/services/etag.service.dart +++ b/mobile/lib/services/etag.service.dart @@ -1,14 +1,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/etag.interface.dart'; import 'package:immich_mobile/repositories/etag.repository.dart'; final etagServiceProvider = Provider((ref) => ETagService(ref.watch(etagRepositoryProvider))); class ETagService { - final IETagRepository _eTagRepository; + final ETagRepository _eTagRepository; - ETagService(this._eTagRepository); + const ETagService(this._eTagRepository); Future clearTable() { return _eTagRepository.clearTable(); diff --git a/mobile/lib/services/exif.service.dart b/mobile/lib/services/exif.service.dart index 973f04303e..4d21614ec5 100644 --- a/mobile/lib/services/exif.service.dart +++ b/mobile/lib/services/exif.service.dart @@ -1,12 +1,12 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; +import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart'; import 'package:immich_mobile/providers/infrastructure/exif.provider.dart'; final exifServiceProvider = Provider((ref) => ExifService(ref.watch(exifRepositoryProvider))); class ExifService { - final IExifInfoRepository _exifInfoRepository; + final IsarExifRepository _exifInfoRepository; const ExifService(this._exifInfoRepository); diff --git a/mobile/lib/services/gcast.service.dart b/mobile/lib/services/gcast.service.dart index 60c94c712c..5a8c27b0db 100644 --- a/mobile/lib/services/gcast.service.dart +++ b/mobile/lib/services/gcast.service.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:cast/session.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/interfaces/cast_destination_service.interface.dart'; import 'package:immich_mobile/models/cast/cast_manager_state.dart'; import 'package:immich_mobile/models/sessions/session_create_response.model.dart'; import 'package:immich_mobile/repositories/asset_api.repository.dart'; @@ -21,7 +20,7 @@ final gCastServiceProvider = Provider( ), ); -class GCastService implements ICastDestinationService { +class GCastService { final GCastRepository _gCastRepository; final SessionsAPIRepository _sessionsApiService; final AssetApiRepository _assetApiRepository; @@ -32,15 +31,14 @@ class GCastService implements ICastDestinationService { int? _sessionId; Timer? _mediaStatusPollingTimer; - @override void Function(bool)? onConnectionState; - @override + void Function(Duration)? onCurrentTime; - @override + void Function(Duration)? onDuration; - @override + void Function(String)? onReceiverName; - @override + void Function(CastState)? onCastState; GCastService( @@ -120,25 +118,21 @@ class GCastService implements ICastDestinationService { } } - @override Future connect(dynamic device) async { await _gCastRepository.connect(device); onReceiverName?.call(device.extras["fn"] ?? "Google Cast"); } - @override CastDestinationType getType() { return CastDestinationType.googleCast; } - @override Future initialize() async { // there is nothing blocking us from using Google Cast that we can check for return true; } - @override Future disconnect() async { onReceiverName?.call(""); currentAssetId = null; @@ -162,7 +156,6 @@ class GCastService implements ICastDestinationService { return bufferedExpiration.isAfter(DateTime.now()); } - @override void loadMedia(Asset asset, bool reload) async { if (!isConnected) { return; @@ -234,7 +227,6 @@ class GCastService implements ICastDestinationService { } } - @override void play() { _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { "type": "PLAY", @@ -242,7 +234,6 @@ class GCastService implements ICastDestinationService { }); } - @override void pause() { _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { "type": "PAUSE", @@ -250,7 +241,6 @@ class GCastService implements ICastDestinationService { }); } - @override void seekTo(Duration position) { _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { "type": "SEEK", @@ -259,7 +249,6 @@ class GCastService implements ICastDestinationService { }); } - @override void stop() { _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { "type": "STOP", @@ -273,7 +262,6 @@ class GCastService implements ICastDestinationService { // 0x01 is display capability bitmask bool isDisplay(int ca) => (ca & 0x01) != 0; - @override Future> getDevices() async { final dests = await _gCastRepository.listDestinations(); diff --git a/mobile/lib/services/hash.service.dart b/mobile/lib/services/hash.service.dart index f8a09471e4..f0554bf00b 100644 --- a/mobile/lib/services/hash.service.dart +++ b/mobile/lib/services/hash.service.dart @@ -4,23 +4,23 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; -import 'package:immich_mobile/domain/interfaces/device_asset.interface.dart'; import 'package:immich_mobile/domain/models/device_asset.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/device_asset.repository.dart'; import 'package:immich_mobile/providers/infrastructure/device_asset.provider.dart'; import 'package:immich_mobile/services/background.service.dart'; import 'package:logging/logging.dart'; class HashService { HashService({ - required IDeviceAssetRepository deviceAssetRepository, + required IsarDeviceAssetRepository deviceAssetRepository, required BackgroundService backgroundService, this.batchSizeLimit = kBatchHashSizeLimit, this.batchFileLimit = kBatchHashFileLimit, }) : _deviceAssetRepository = deviceAssetRepository, _backgroundService = backgroundService; - final IDeviceAssetRepository _deviceAssetRepository; + final IsarDeviceAssetRepository _deviceAssetRepository; final BackgroundService _backgroundService; final int batchSizeLimit; final int batchFileLimit; diff --git a/mobile/lib/services/local_auth.service.dart b/mobile/lib/services/local_auth.service.dart index f797e9065a..12da5f256b 100644 --- a/mobile/lib/services/local_auth.service.dart +++ b/mobile/lib/services/local_auth.service.dart @@ -1,5 +1,4 @@ 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'; @@ -10,11 +9,9 @@ final localAuthServiceProvider = Provider( ); class LocalAuthService { - // final _log = Logger("LocalAuthService"); + final BiometricRepository _biometricRepository; - final IBiometricRepository _biometricRepository; - - LocalAuthService(this._biometricRepository); + const LocalAuthService(this._biometricRepository); Future getStatus() { return _biometricRepository.getStatus(); diff --git a/mobile/lib/utils/local_files_manager.dart b/mobile/lib/services/local_files_manager.service.dart similarity index 67% rename from mobile/lib/utils/local_files_manager.dart rename to mobile/lib/services/local_files_manager.service.dart index a4cf41a6e6..6ba8b43fbf 100644 --- a/mobile/lib/utils/local_files_manager.dart +++ b/mobile/lib/services/local_files_manager.service.dart @@ -1,11 +1,17 @@ import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logging/logging.dart'; -abstract final class LocalFilesManager { +final localFileManagerServiceProvider = Provider( + (ref) => const LocalFilesManagerService(), +); + +class LocalFilesManagerService { + const LocalFilesManagerService(); static final Logger _logger = Logger('LocalFilesManager'); static const MethodChannel _channel = MethodChannel('file_trash'); - static Future moveToTrash(List mediaUrls) async { + Future moveToTrash(List mediaUrls) async { try { return await _channel .invokeMethod('moveToTrash', {'mediaUrls': mediaUrls}); @@ -15,7 +21,7 @@ abstract final class LocalFilesManager { } } - static Future restoreFromTrash(String fileName, int type) async { + Future restoreFromTrash(String fileName, int type) async { try { return await _channel.invokeMethod( 'restoreFromTrash', @@ -27,7 +33,7 @@ abstract final class LocalFilesManager { } } - static Future requestManageMediaPermission() async { + Future requestManageMediaPermission() async { try { return await _channel.invokeMethod('requestManageMediaPermission'); } catch (e, s) { diff --git a/mobile/lib/services/memory.service.dart b/mobile/lib/services/memory.service.dart index d6c44278c7..a44a5ad1bd 100644 --- a/mobile/lib/services/memory.service.dart +++ b/mobile/lib/services/memory.service.dart @@ -1,10 +1,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; +import 'package:immich_mobile/extensions/translate_extensions.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) { @@ -18,7 +17,7 @@ class MemoryService { final log = Logger("MemoryService"); final ApiService _apiService; - final IAssetRepository _assetRepository; + final AssetRepository _assetRepository; MemoryService(this._apiService, this._assetRepository); @@ -40,7 +39,11 @@ class MemoryService { .getAllByRemoteId(memory.assets.map((e) => e.id)); final yearsAgo = now.year - memory.data.year; if (dbAssets.isNotEmpty) { - final String title = t('years_ago', {'years': yearsAgo.toString()}); + final String title = 'years_ago'.t( + args: { + 'years': yearsAgo.toString(), + }, + ); memories.add( Memory( title: title, @@ -56,4 +59,34 @@ class MemoryService { return null; } } + + Future getMemoryById(String id) async { + try { + final memoryResponse = await _apiService.memoriesApi.getMemory(id); + + if (memoryResponse == null) { + return null; + } + final dbAssets = await _assetRepository + .getAllByRemoteId(memoryResponse.assets.map((e) => e.id)); + if (dbAssets.isEmpty) { + log.warning("No assets found for memory with ID: $id"); + return null; + } + final yearsAgo = DateTime.now().year - memoryResponse.data.year; + final String title = 'years_ago'.t( + args: { + 'years': yearsAgo.toString(), + }, + ); + + return Memory( + title: title, + assets: dbAssets, + ); + } catch (error, stack) { + log.severe("Cannot get memory with ID: $id", error, stack); + return null; + } + } } diff --git a/mobile/lib/services/network.service.dart b/mobile/lib/services/network.service.dart index f2d2de325d..de55da8d7c 100644 --- a/mobile/lib/services/network.service.dart +++ b/mobile/lib/services/network.service.dart @@ -1,5 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/interfaces/network.interface.dart'; import 'package:immich_mobile/repositories/network.repository.dart'; import 'package:immich_mobile/repositories/permission.repository.dart'; @@ -11,10 +10,10 @@ final networkServiceProvider = Provider((ref) { }); class NetworkService { - final INetworkRepository _repository; + final NetworkRepository _repository; final IPermissionRepository _permissionRepository; - NetworkService(this._repository, this._permissionRepository); + const NetworkService(this._repository, this._permissionRepository); Future getLocationWhenInUserPermission() { return _permissionRepository.hasLocationWhenInUsePermission(); diff --git a/mobile/lib/services/partner.service.dart b/mobile/lib/services/partner.service.dart index 78d4d71e99..ec210fd587 100644 --- a/mobile/lib/services/partner.service.dart +++ b/mobile/lib/services/partner.service.dart @@ -1,8 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/user.interface.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/interfaces/partner.interface.dart'; -import 'package:immich_mobile/interfaces/partner_api.interface.dart'; +import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/partner.repository.dart'; import 'package:immich_mobile/repositories/partner_api.repository.dart'; @@ -17,14 +15,14 @@ final partnerServiceProvider = Provider( ); class PartnerService { - final IPartnerApiRepository _partnerApiRepository; - final IPartnerRepository _partnerRepository; - final IUserRepository _userRepository; + final PartnerApiRepository _partnerApiRepository; + final PartnerRepository _partnerRepository; + final IsarUserRepository _isarUserRepository; final Logger _log = Logger("PartnerService"); PartnerService( this._partnerApiRepository, - this._userRepository, + this._isarUserRepository, this._partnerRepository, ); @@ -47,7 +45,8 @@ class PartnerService { Future removePartner(UserDto partner) async { try { await _partnerApiRepository.delete(partner.id); - await _userRepository.update(partner.copyWith(isPartnerSharedBy: false)); + await _isarUserRepository + .update(partner.copyWith(isPartnerSharedBy: false)); } catch (e) { _log.warning("Failed to remove partner ${partner.id}", e); return false; @@ -58,7 +57,8 @@ class PartnerService { Future addPartner(UserDto partner) async { try { await _partnerApiRepository.create(partner.id); - await _userRepository.update(partner.copyWith(isPartnerSharedBy: true)); + await _isarUserRepository + .update(partner.copyWith(isPartnerSharedBy: true)); return true; } catch (e) { _log.warning("Failed to add partner ${partner.id}", e); @@ -75,7 +75,7 @@ class PartnerService { partner.id, inTimeline: inTimeline, ); - await _userRepository + await _isarUserRepository .update(partner.copyWith(inTimeline: dto.inTimeline)); return true; } catch (e) { diff --git a/mobile/lib/services/person.service.dart b/mobile/lib/services/person.service.dart index a8289ac37d..a591ad4f27 100644 --- a/mobile/lib/services/person.service.dart +++ b/mobile/lib/services/person.service.dart @@ -1,8 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; -import 'package:immich_mobile/interfaces/asset_api.interface.dart'; -import 'package:immich_mobile/interfaces/person_api.interface.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; import 'package:immich_mobile/repositories/asset_api.repository.dart'; import 'package:immich_mobile/repositories/person_api.repository.dart'; @@ -20,9 +18,9 @@ PersonService personService(Ref ref) => PersonService( class PersonService { final Logger _log = Logger("PersonService"); - final IPersonApiRepository _personApiRepository; - final IAssetApiRepository _assetApiRepository; - final IAssetRepository _assetRepository; + final PersonApiRepository _personApiRepository; + final AssetApiRepository _assetApiRepository; + final AssetRepository _assetRepository; PersonService( this._personApiRepository, diff --git a/mobile/lib/services/search.service.dart b/mobile/lib/services/search.service.dart index bcf67889c0..5d3b08aaed 100644 --- a/mobile/lib/services/search.service.dart +++ b/mobile/lib/services/search.service.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/string_extensions.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/models/search/search_result.model.dart'; @@ -20,7 +19,7 @@ final searchServiceProvider = Provider( class SearchService { final ApiService _apiService; - final IAssetRepository _assetRepository; + final AssetRepository _assetRepository; final _log = Logger("SearchService"); SearchService(this._apiService, this._assetRepository); diff --git a/mobile/lib/services/secure_storage.service.dart b/mobile/lib/services/secure_storage.service.dart index 77803f29c3..38e6deb0d4 100644 --- a/mobile/lib/services/secure_storage.service.dart +++ b/mobile/lib/services/secure_storage.service.dart @@ -1,5 +1,4 @@ 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( @@ -9,11 +8,9 @@ final secureStorageServiceProvider = Provider( ); class SecureStorageService { - // final _log = Logger("LocalAuthService"); + final SecureStorageRepository _secureStorageRepository; - final ISecureStorageRepository _secureStorageRepository; - - SecureStorageService(this._secureStorageRepository); + const SecureStorageService(this._secureStorageRepository); Future write(String key, String value) async { await _secureStorageRepository.write(key, value); diff --git a/mobile/lib/services/server_info.service.dart b/mobile/lib/services/server_info.service.dart index e2b7db2fce..75ce68a73c 100644 --- a/mobile/lib/services/server_info.service.dart +++ b/mobile/lib/services/server_info.service.dart @@ -16,7 +16,7 @@ final serverInfoServiceProvider = Provider( class ServerInfoService { final ApiService _apiService; - ServerInfoService(this._apiService); + const ServerInfoService(this._apiService); Future getDiskInfo() async { try { diff --git a/mobile/lib/services/share.service.dart b/mobile/lib/services/share.service.dart index d44d75931d..77afa10fb6 100644 --- a/mobile/lib/services/share.service.dart +++ b/mobile/lib/services/share.service.dart @@ -30,7 +30,7 @@ class ShareService { for (var asset in assets) { if (asset.isLocal) { // Prefer local assets to share - File? f = await asset.local!.file; + File? f = await asset.local!.originFile; downloadedXFiles.add(XFile(f!.path)); } else if (asset.isRemote) { // Download remote asset otherwise diff --git a/mobile/lib/services/stack.service.dart b/mobile/lib/services/stack.service.dart index 1ca56ff279..e2ab6c804b 100644 --- a/mobile/lib/services/stack.service.dart +++ b/mobile/lib/services/stack.service.dart @@ -1,17 +1,16 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/interfaces/asset.interface.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:openapi/api.dart'; class StackService { - StackService(this._api, this._assetRepository); + const StackService(this._api, this._assetRepository); final ApiService _api; - final IAssetRepository _assetRepository; + final AssetRepository _assetRepository; Future getStack(String stackId) async { try { diff --git a/mobile/lib/services/sync.service.dart b/mobile/lib/services/sync.service.dart index e08d7de8b9..5a95be2237 100644 --- a/mobile/lib/services/sync.service.dart +++ b/mobile/lib/services/sync.service.dart @@ -3,23 +3,16 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; -import 'package:immich_mobile/domain/interfaces/user.interface.dart'; -import 'package:immich_mobile/domain/interfaces/user_api.interface.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/extensions/collection_extensions.dart'; -import 'package:immich_mobile/interfaces/album.interface.dart'; -import 'package:immich_mobile/interfaces/album_api.interface.dart'; -import 'package:immich_mobile/interfaces/album_media.interface.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; -import 'package:immich_mobile/interfaces/etag.interface.dart'; -import 'package:immich_mobile/interfaces/local_files_manager.interface.dart'; -import 'package:immich_mobile/interfaces/partner.interface.dart'; -import 'package:immich_mobile/interfaces/partner_api.interface.dart'; +import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/infrastructure/exif.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; @@ -63,21 +56,21 @@ final syncServiceProvider = Provider( class SyncService { final HashService _hashService; final EntityService _entityService; - final IAlbumMediaRepository _albumMediaRepository; - final IAlbumApiRepository _albumApiRepository; - final IAlbumRepository _albumRepository; - final IAssetRepository _assetRepository; - final IExifInfoRepository _exifInfoRepository; - final IUserRepository _userRepository; + final AlbumMediaRepository _albumMediaRepository; + final AlbumApiRepository _albumApiRepository; + final AlbumRepository _albumRepository; + final AssetRepository _assetRepository; + final IsarExifRepository _exifInfoRepository; + final IsarUserRepository _isarUserRepository; final UserService _userService; - final IPartnerRepository _partnerRepository; - final IETagRepository _eTagRepository; - final IPartnerApiRepository _partnerApiRepository; - final IUserApiRepository _userApiRepository; + final PartnerRepository _partnerRepository; + final ETagRepository _eTagRepository; + final PartnerApiRepository _partnerApiRepository; + final UserApiRepository _userApiRepository; final AsyncMutex _lock = AsyncMutex(); final Logger _log = Logger('SyncService'); final AppSettingsService _appSettingsService; - final ILocalFilesManager _localFilesManager; + final LocalFilesManagerRepository _localFilesManager; SyncService( this._hashService, @@ -88,7 +81,7 @@ class SyncService { this._assetRepository, this._exifInfoRepository, this._partnerRepository, - this._userRepository, + this._isarUserRepository, this._userService, this._eTagRepository, this._appSettingsService, @@ -165,7 +158,7 @@ class SyncService { /// Returns `true`if there were any changes Future _syncUsersFromServer(List users) async { users.sortBy((u) => u.id); - final dbUsers = await _userRepository.getAll(sortBy: SortUserBy.id); + final dbUsers = await _isarUserRepository.getAll(sortBy: SortUserBy.id); final List toDelete = []; final List toUpsert = []; final changes = diffSortedListsSync( @@ -186,9 +179,9 @@ class SyncService { onlySecond: (UserDto b) => toDelete.add(b.id), ); if (changes) { - await _userRepository.transaction(() async { - await _userRepository.delete(toDelete); - await _userRepository.updateAll(toUpsert); + await _isarUserRepository.transaction(() async { + await _isarUserRepository.delete(toDelete); + await _isarUserRepository.updateAll(toUpsert); }); } return changes; @@ -448,7 +441,7 @@ class SyncService { final (existingInDb, updated) = await _linkWithExistingFromDb(toAdd); await upsertAssetsWithExif(updated); final assetsToLink = existingInDb + updated; - final usersToLink = await _userRepository.getByUserIds(userIdsToAdd); + final usersToLink = await _isarUserRepository.getByUserIds(userIdsToAdd); album.name = dto.name; album.description = dto.description; diff --git a/mobile/lib/services/timeline.service.dart b/mobile/lib/services/timeline.service.dart index d7d1e7cf25..47ad17fc25 100644 --- a/mobile/lib/services/timeline.service.dart +++ b/mobile/lib/services/timeline.service.dart @@ -2,7 +2,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/interfaces/timeline.interface.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/timeline.repository.dart'; @@ -18,7 +17,7 @@ final timelineServiceProvider = Provider((ref) { }); class TimelineService { - final ITimelineRepository _timelineRepository; + final TimelineRepository _timelineRepository; final AppSettingsService _appSettingsService; final UserService _userService; diff --git a/mobile/lib/services/trash.service.dart b/mobile/lib/services/trash.service.dart index 478ad65db0..6cd7dfc641 100644 --- a/mobile/lib/services/trash.service.dart +++ b/mobile/lib/services/trash.service.dart @@ -1,7 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; @@ -18,7 +17,7 @@ final trashServiceProvider = Provider((ref) { class TrashService { final ApiService _apiService; - final IAssetRepository _assetRepository; + final AssetRepository _assetRepository; final UserService _userService; const TrashService( diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index 0734e57212..18f90ab844 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -4,7 +4,6 @@ import 'package:background_downloader/background_downloader.dart'; 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/interfaces/upload.interface.dart'; import 'package:immich_mobile/repositories/upload.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/utils/upload.dart'; @@ -18,7 +17,7 @@ final uploadServiceProvider = Provider( ); class UploadService { - final IUploadRepository _uploadRepository; + final UploadRepository _uploadRepository; // final Logger _log = Logger("UploadService"); void Function(TaskStatusUpdate)? onUploadStatus; void Function(TaskProgressUpdate)? onTaskProgress; diff --git a/mobile/lib/services/widget.service.dart b/mobile/lib/services/widget.service.dart new file mode 100644 index 0000000000..02ddedbe89 --- /dev/null +++ b/mobile/lib/services/widget.service.dart @@ -0,0 +1,42 @@ +import 'dart:io'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/repositories/widget.repository.dart'; + +final widgetServiceProvider = Provider((ref) { + return WidgetService( + ref.watch(widgetRepositoryProvider), + ); +}); + +class WidgetService { + final WidgetRepository _repository; + + const WidgetService(this._repository); + + Future writeCredentials(String serverURL, String sessionKey) async { + await _repository.setAppGroupId(appShareGroupId); + await _repository.saveData(kWidgetServerEndpoint, serverURL); + await _repository.saveData(kWidgetAuthToken, sessionKey); + + // wait 3 seconds to ensure the widget is updated, dont block + Future.delayed(const Duration(seconds: 3), refreshWidgets); + } + + Future clearCredentials() async { + await _repository.setAppGroupId(appShareGroupId); + await _repository.saveData(kWidgetServerEndpoint, ""); + await _repository.saveData(kWidgetAuthToken, ""); + + // wait 3 seconds to ensure the widget is updated, dont block + Future.delayed(const Duration(seconds: 3), refreshWidgets); + } + + Future refreshWidgets() async { + if (Platform.isAndroid) return; + + for (final name in kWidgetNames) { + await _repository.refresh(name); + } + } +} diff --git a/mobile/lib/theme/dynamic_theme.dart b/mobile/lib/theme/dynamic_theme.dart index 39d6b6ee45..8ebf783469 100644 --- a/mobile/lib/theme/dynamic_theme.dart +++ b/mobile/lib/theme/dynamic_theme.dart @@ -4,7 +4,7 @@ import 'package:dynamic_color/dynamic_color.dart'; import 'package:immich_mobile/theme/theme_data.dart'; abstract final class DynamicTheme { - DynamicTheme._(); + const DynamicTheme._(); static ImmichTheme? _theme; // Method to fetch dynamic system colors diff --git a/mobile/lib/utils/cache/custom_image_cache.dart b/mobile/lib/utils/cache/custom_image_cache.dart index dcb8dacb0d..8c70472765 100644 --- a/mobile/lib/utils/cache/custom_image_cache.dart +++ b/mobile/lib/utils/cache/custom_image_cache.dart @@ -1,4 +1,6 @@ import 'package:flutter/painting.dart'; +import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; import 'package:immich_mobile/providers/image/immich_local_image_provider.dart'; import 'package:immich_mobile/providers/image/immich_local_thumbnail_provider.dart'; import 'package:immich_mobile/providers/image/immich_remote_image_provider.dart'; @@ -37,10 +39,12 @@ final class CustomImageCache implements ImageCache { /// Gets the cache for the given key /// [_large] is used for [ImmichLocalImageProvider] and [ImmichRemoteImageProvider] /// [_small] is used for [ImmichLocalThumbnailProvider] and [ImmichRemoteThumbnailProvider] - ImageCache _cacheForKey(Object key) => - (key is ImmichLocalImageProvider || key is ImmichRemoteImageProvider) - ? _large - : _small; + ImageCache _cacheForKey(Object key) => (key is ImmichLocalImageProvider || + key is ImmichRemoteImageProvider || + key is LocalFullImageProvider || + key is RemoteFullImageProvider) + ? _large + : _small; @override bool containsKey(Object key) { diff --git a/mobile/lib/utils/hooks/blurhash_hook.dart b/mobile/lib/utils/hooks/blurhash_hook.dart index 9231e2d972..62208c4cf5 100644 --- a/mobile/lib/utils/hooks/blurhash_hook.dart +++ b/mobile/lib/utils/hooks/blurhash_hook.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:thumbhash/thumbhash.dart' as thumbhash; @@ -15,3 +16,15 @@ ObjectRef useBlurHashRef(Asset? asset) { return useRef(thumbhash.rgbaToBmp(rbga)); } + +ObjectRef useDriftBlurHashRef(RemoteAsset? asset) { + if (asset?.thumbHash == null) { + return useRef(null); + } + + final rbga = thumbhash.thumbHashToRGBA( + base64Decode(asset!.thumbHash!), + ); + + return useRef(thumbhash.rgbaToBmp(rbga)); +} diff --git a/mobile/lib/utils/image_url_builder.dart b/mobile/lib/utils/image_url_builder.dart index 50218eaffd..bde50f3a90 100644 --- a/mobile/lib/utils/image_url_builder.dart +++ b/mobile/lib/utils/image_url_builder.dart @@ -73,6 +73,9 @@ String getThumbnailUrlForRemoteId( return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${type.value}'; } +String getPreviewUrlForRemoteId(final String id) => + '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${AssetMediaSize.preview}'; + String getPlaybackUrlForRemoteId(final String id) { return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/video/playback?'; } diff --git a/mobile/lib/utils/map_utils.dart b/mobile/lib/utils/map_utils.dart index df1ff28d8f..39443fb225 100644 --- a/mobile/lib/utils/map_utils.dart +++ b/mobile/lib/utils/map_utils.dart @@ -7,7 +7,7 @@ import 'package:logging/logging.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; class MapUtils { - MapUtils._(); + const MapUtils._(); static final Logger _log = Logger("MapUtils"); static const defaultSourceId = 'asset-map-markers'; diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index 52c2fc2306..3ec8ce5bbc 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -23,7 +23,7 @@ import 'package:isar/isar.dart'; // ignore: import_rule_photo_manager import 'package:photo_manager/photo_manager.dart'; -const int targetVersion = 12; +const int targetVersion = 13; Future migrateDatabaseIfNeeded(Isar db) async { final int version = Store.get(StoreKey.version, targetVersion); @@ -56,7 +56,17 @@ Future migrateDatabaseIfNeeded(Isar db) async { await drift.close(); } + if (version < 13) { + await Store.put(StoreKey.photoManagerCustomFilter, true); + } + + if (targetVersion >= 12) { + await Store.put(StoreKey.version, targetVersion); + return; + } + final shouldTruncate = version < 8 || version < targetVersion; + if (shouldTruncate) { await _migrateTo(db, targetVersion); } @@ -166,25 +176,33 @@ Future _migrateDeviceAsset(Isar db) async { } Future _migrateDeviceAssetToSqlite(Isar db, Drift drift) async { - final isarDeviceAssets = - await db.deviceAssetEntitys.where().sortByAssetId().findAll(); - await drift.batch((batch) { - for (final deviceAsset in isarDeviceAssets) { - final companion = LocalAssetEntityCompanion( - updatedAt: Value(deviceAsset.modifiedTime), - id: Value(deviceAsset.assetId), - checksum: Value(base64.encode(deviceAsset.hash)), - ); - batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>( - drift.localAssetEntity, - companion, - onConflict: DoUpdate( - (_) => companion, - where: (old) => old.updatedAt.equals(deviceAsset.modifiedTime), - ), + try { + final isarDeviceAssets = + await db.deviceAssetEntitys.where().sortByAssetId().findAll(); + await drift.batch((batch) { + for (final deviceAsset in isarDeviceAssets) { + final companion = LocalAssetEntityCompanion( + updatedAt: Value(deviceAsset.modifiedTime), + id: Value(deviceAsset.assetId), + checksum: Value(base64.encode(deviceAsset.hash)), + ); + batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>( + drift.localAssetEntity, + companion, + onConflict: DoUpdate( + (_) => companion, + where: (old) => old.updatedAt.equals(deviceAsset.modifiedTime), + ), + ); + } + }); + } catch (error) { + if (kDebugMode) { + debugPrint( + "[MIGRATION] Error while migrating device assets to SQLite: $error", ); } - }); + } } class _DeviceAsset { diff --git a/mobile/lib/utils/remote_album.utils.dart b/mobile/lib/utils/remote_album.utils.dart new file mode 100644 index 0000000000..4fc7ba5f74 --- /dev/null +++ b/mobile/lib/utils/remote_album.utils.dart @@ -0,0 +1,71 @@ +import 'package:collection/collection.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; + +typedef AlbumSortFn = List Function(List albums, bool isReverse); + +class _RemoteAlbumSortHandlers { + const _RemoteAlbumSortHandlers._(); + + static const AlbumSortFn created = _sortByCreated; + static List _sortByCreated(List albums, bool isReverse) { + final sorted = albums.sortedBy((album) => album.createdAt); + return (isReverse ? sorted.reversed : sorted).toList(); + } + + static const AlbumSortFn title = _sortByTitle; + static List _sortByTitle(List albums, bool isReverse) { + final sorted = albums.sortedBy((album) => album.name); + return (isReverse ? sorted.reversed : sorted).toList(); + } + + static const AlbumSortFn lastModified = _sortByLastModified; + static List _sortByLastModified(List albums, bool isReverse) { + final sorted = albums.sortedBy((album) => album.updatedAt); + return (isReverse ? sorted.reversed : sorted).toList(); + } + + static const AlbumSortFn assetCount = _sortByAssetCount; + static List _sortByAssetCount(List albums, bool isReverse) { + final sorted = + albums.sorted((a, b) => a.assetCount.compareTo(b.assetCount)); + return (isReverse ? sorted.reversed : sorted).toList(); + } + + static const AlbumSortFn mostRecent = _sortByMostRecent; + static List _sortByMostRecent(List albums, bool isReverse) { + final sorted = albums.sorted((a, b) { + // For most recent, we sort by updatedAt in descending order + return b.updatedAt.compareTo(a.updatedAt); + }); + return (isReverse ? sorted.reversed : sorted).toList(); + } + + static const AlbumSortFn mostOldest = _sortByMostOldest; + static List _sortByMostOldest(List albums, bool isReverse) { + final sorted = albums.sorted((a, b) { + // For oldest, we sort by createdAt in ascending order + return a.createdAt.compareTo(b.createdAt); + }); + return (isReverse ? sorted.reversed : sorted).toList(); + } +} + +enum RemoteAlbumSortMode { + title("library_page_sort_title", _RemoteAlbumSortHandlers.title), + assetCount( + "library_page_sort_asset_count", + _RemoteAlbumSortHandlers.assetCount, + ), + lastModified( + "library_page_sort_last_modified", + _RemoteAlbumSortHandlers.lastModified, + ), + created("library_page_sort_created", _RemoteAlbumSortHandlers.created), + mostRecent("sort_recent", _RemoteAlbumSortHandlers.mostRecent), + mostOldest("sort_oldest", _RemoteAlbumSortHandlers.mostOldest); + + final String key; + final AlbumSortFn sortFn; + + const RemoteAlbumSortMode(this.key, this.sortFn); +} diff --git a/mobile/lib/utils/selection_handlers.dart b/mobile/lib/utils/selection_handlers.dart index e22076aae9..a5466c83a2 100644 --- a/mobile/lib/utils/selection_handlers.dart +++ b/mobile/lib/utils/selection_handlers.dart @@ -6,10 +6,10 @@ 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/extensions/translate_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'; @@ -59,10 +59,11 @@ Future handleArchiveAssets( await ref .read(assetProvider.notifier) .toggleArchive(selection, shouldArchive); - final message = shouldArchive - ? t('moved_to_archive', {'count': selection.length}) - : t('moved_to_library', {'count': selection.length}); + ? 'moved_to_archive' + .t(context: context, args: {'count': selection.length}) + : 'moved_to_library' + .t(context: context, args: {'count': selection.length}); if (context.mounted) { ImmichToast.show( context: context, diff --git a/mobile/lib/utils/storage_indicator.dart b/mobile/lib/utils/storage_indicator.dart deleted file mode 100644 index a7dad063ca..0000000000 --- a/mobile/lib/utils/storage_indicator.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; - -/// Returns the suitable [IconData] to represent an [Asset]s storage location -IconData storageIcon(Asset asset) => switch (asset.storage) { - AssetState.local => Icons.cloud_off_outlined, - AssetState.remote => Icons.cloud_outlined, - AssetState.merged => Icons.cloud_done_outlined, - }; diff --git a/mobile/lib/utils/thumbnail_utils.dart b/mobile/lib/utils/thumbnail_utils.dart new file mode 100644 index 0000000000..33dd916980 --- /dev/null +++ b/mobile/lib/utils/thumbnail_utils.dart @@ -0,0 +1,55 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; + +String getAltText( + ExifInfo? exifInfo, + DateTime fileCreatedAt, + AssetType type, + List peopleNames, +) { + if (exifInfo?.description != null && exifInfo!.description!.isNotEmpty) { + return exifInfo.description!; + } + final (template, args) = + getAltTextTemplate(exifInfo, fileCreatedAt, type, peopleNames); + return template.t(args: args); +} + +(String, Map) getAltTextTemplate( + ExifInfo? exifInfo, + DateTime fileCreatedAt, + AssetType type, + List peopleNames, +) { + final isVideo = type == AssetType.video; + final hasLocation = exifInfo?.city != null && exifInfo?.country != null; + final date = DateFormat.yMMMMd().format(fileCreatedAt); + final args = { + "isVideo": isVideo.toString(), + "date": date, + "city": exifInfo?.city ?? "", + "country": exifInfo?.country ?? "", + "person1": peopleNames.elementAtOrNull(0) ?? "", + "person2": peopleNames.elementAtOrNull(1) ?? "", + "person3": peopleNames.elementAtOrNull(2) ?? "", + "additionalCount": (peopleNames.length - 3).toString(), + }; + final template = hasLocation + ? (switch (peopleNames.length) { + 0 => "image_alt_text_date_place", + 1 => "image_alt_text_date_place_1_person", + 2 => "image_alt_text_date_place_2_people", + 3 => "image_alt_text_date_place_3_people", + _ => "image_alt_text_date_place_4_or_more_people" + }) + : (switch (peopleNames.length) { + 0 => "image_alt_text_date", + 1 => "image_alt_text_date_1_person", + 2 => "image_alt_text_date_2_people", + 3 => "image_alt_text_date_3_people", + _ => "image_alt_text_date_4_or_more_people" + }); + return (template, args); +} diff --git a/mobile/lib/utils/translation.dart b/mobile/lib/utils/translation.dart deleted file mode 100644 index 1a33161dbc..0000000000 --- a/mobile/lib/utils/translation.dart +++ /dev/null @@ -1,15 +0,0 @@ -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, locale: Intl.defaultLocale ?? 'en') - .format(args); - } - return message; - } catch (e) { - return key; - } -} diff --git a/mobile/lib/widgets/album/album_thumbnail_card.dart b/mobile/lib/widgets/album/album_thumbnail_card.dart index 9f78b6066d..d78f391754 100644 --- a/mobile/lib/widgets/album/album_thumbnail_card.dart +++ b/mobile/lib/widgets/album/album_thumbnail_card.dart @@ -4,8 +4,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/user.provider.dart'; -import 'package:immich_mobile/utils/translation.dart'; import 'package:immich_mobile/widgets/common/immich_thumbnail.dart'; class AlbumThumbnailCard extends ConsumerWidget { @@ -62,7 +62,12 @@ class AlbumThumbnailCard extends ConsumerWidget { if (album.ownerId == ref.read(currentUserProvider)?.id) { owner = 'owned'.tr(); } else if (album.ownerName != null) { - owner = t('shared_by_user', {'user': album.ownerName!}); + owner = 'shared_by_user'.t( + context: context, + args: { + 'user': album.ownerName!, + }, + ); } } @@ -70,7 +75,12 @@ class AlbumThumbnailCard extends ConsumerWidget { TextSpan( children: [ TextSpan( - text: t('items_count', {'count': album.assetCount}), + text: 'items_count'.t( + context: context, + args: { + 'count': album.assetCount, + }, + ), ), if (owner != null) const TextSpan(text: ' â€ĸ '), if (owner != null) TextSpan(text: owner), @@ -96,7 +106,9 @@ class AlbumThumbnailCard extends ConsumerWidget { width: cardSize, height: cardSize, child: ClipRRect( - borderRadius: BorderRadius.circular(20), + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), child: album.thumbnail.value == null ? buildEmptyThumbnail() : buildAlbumThumbnail(), diff --git a/mobile/lib/widgets/album/album_thumbnail_listtile.dart b/mobile/lib/widgets/album/album_thumbnail_listtile.dart index 11ef5d329b..f35d4b7ede 100644 --- a/mobile/lib/widgets/album/album_thumbnail_listtile.dart +++ b/mobile/lib/widgets/album/album_thumbnail_listtile.dart @@ -4,10 +4,10 @@ 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/extensions/translate_extensions.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/utils/translation.dart'; import 'package:openapi/api.dart'; class AlbumThumbnailListTile extends StatelessWidget { @@ -91,7 +91,12 @@ class AlbumThumbnailListTile extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - t('items_count', {'count': album.assetCount}), + 'items_count'.t( + context: context, + args: { + 'count': album.assetCount, + }, + ), style: const TextStyle( fontSize: 12, ), diff --git a/mobile/lib/widgets/album/album_title_text_field.dart b/mobile/lib/widgets/album/album_title_text_field.dart index c19c827846..7807a6e6ae 100644 --- a/mobile/lib/widgets/album/album_title_text_field.dart +++ b/mobile/lib/widgets/album/album_title_text_field.dart @@ -59,13 +59,17 @@ class AlbumTitleTextField extends ConsumerWidget { splashRadius: 10, ) : null, - enabledBorder: OutlineInputBorder( - borderSide: const BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.circular(10), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Colors.transparent), + borderRadius: BorderRadius.all( + Radius.circular(10), + ), ), - focusedBorder: OutlineInputBorder( - borderSide: const BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.circular(10), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Colors.transparent), + borderRadius: BorderRadius.all( + Radius.circular(10), + ), ), hintText: 'add_a_title'.tr(), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( diff --git a/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart b/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart index 64803046ef..6b94c04c4d 100644 --- a/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart +++ b/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart @@ -23,7 +23,7 @@ class RenderAssetGridElement { final int offset; final int totalCount; - RenderAssetGridElement( + const RenderAssetGridElement( this.type, { this.title, required this.date, 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 309de3ae28..3283b90b21 100644 --- a/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart +++ b/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart @@ -80,7 +80,7 @@ class ControlBottomAppBar extends HookConsumerWidget { final albums = ref.watch(albumProvider).where((a) => a.isRemote).toList(); final sharedAlbums = ref.watch(albumProvider).where((a) => a.shared).toList(); - const bottomPadding = 0.20; + const bottomPadding = 0.24; final scrollController = useDraggableScrollController(); final isInLockedView = ref.watch(inLockedViewProvider); @@ -131,15 +131,14 @@ class ControlBottomAppBar extends HookConsumerWidget { List renderActionButtons() { return [ - if (hasRemote) - ControlBoxButton( - iconData: Platform.isAndroid - ? Icons.share_rounded - : Icons.ios_share_rounded, - label: "share".tr(), - onPressed: enabled ? () => onShare(true) : null, - ), - if (!isInLockedView) + ControlBoxButton( + iconData: Platform.isAndroid + ? Icons.share_rounded + : Icons.ios_share_rounded, + label: "share".tr(), + onPressed: enabled ? () => onShare(true) : null, + ), + if (!isInLockedView && hasRemote) ControlBoxButton( iconData: Icons.link_rounded, label: "share_link".tr(), @@ -314,7 +313,7 @@ class ControlBottomAppBar extends HookConsumerWidget { getInitialSize() { if (isInLockedView) { - return 0.20; + return bottomPadding; } if (hasRemote) { return 0.35; @@ -324,7 +323,7 @@ class ControlBottomAppBar extends HookConsumerWidget { getMaxChildSize() { if (isInLockedView) { - return 0.20; + return bottomPadding; } if (hasRemote) { return 0.65; @@ -333,15 +332,12 @@ class ControlBottomAppBar extends HookConsumerWidget { } return DraggableScrollableSheet( - controller: scrollController, initialChildSize: getInitialSize(), minChildSize: bottomPadding, maxChildSize: getMaxChildSize(), snap: true, - builder: ( - BuildContext context, - ScrollController scrollController, - ) { + controller: scrollController, + builder: (BuildContext context, ScrollController scrollController) { return Card( color: context.colorScheme.surfaceContainerHigh, surfaceTintColor: context.colorScheme.surfaceContainerHigh, diff --git a/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart b/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart index 6eddf58adb..779da8add7 100644 --- a/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart +++ b/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart @@ -210,7 +210,7 @@ class DraggableScrollbar extends StatefulWidget { BoxConstraints? labelConstraints, }) { final scrollThumb = ClipPath( - clipper: ArrowClipper(), + clipper: const ArrowClipper(), child: Container( height: height, width: 20.0, @@ -570,6 +570,7 @@ class ArrowCustomPainter extends CustomPainter { ///This cut 2 lines in arrow shape class ArrowClipper extends CustomClipper { + const ArrowClipper(); @override Path getClip(Size size) { Path path = Path(); diff --git a/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart b/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart index 746bbde6ef..06954f4ff4 100644 --- a/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart +++ b/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart @@ -479,6 +479,7 @@ class ArrowCustomPainter extends CustomPainter { ///This cut 2 lines in arrow shape class ArrowClipper extends CustomClipper { + const ArrowClipper(); @override Path getClip(Size size) { Path path = Path(); diff --git a/mobile/lib/widgets/asset_grid/group_divider_title.dart b/mobile/lib/widgets/asset_grid/group_divider_title.dart index 3a411c09db..b9fe8e3c1d 100644 --- a/mobile/lib/widgets/asset_grid/group_divider_title.dart +++ b/mobile/lib/widgets/asset_grid/group_divider_title.dart @@ -1,3 +1,4 @@ +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'; @@ -74,10 +75,14 @@ class GroupDividerTitle extends HookConsumerWidget { ? Icon( Icons.check_circle_rounded, color: context.primaryColor, + semanticLabel: + "unselect_all_in".tr(namedArgs: {"group": text}), ) : Icon( Icons.check_circle_outline_rounded, color: context.colorScheme.onSurfaceSecondary, + semanticLabel: + "select_all_in".tr(namedArgs: {"group": text}), ), ), ], diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid.dart b/mobile/lib/widgets/asset_grid/multiselect_grid.dart index 9904447569..98b1c6f601 100644 --- a/mobile/lib/widgets/asset_grid/multiselect_grid.dart +++ b/mobile/lib/widgets/asset_grid/multiselect_grid.dart @@ -11,6 +11,7 @@ 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/extensions/translate_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'; @@ -24,7 +25,6 @@ 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/utils/translation.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'; @@ -257,13 +257,19 @@ class MultiselectGrid extends HookConsumerWidget { final failedCount = totalCount - successCount; final msg = failedCount > 0 - ? t('assets_downloaded_failed', { - 'count': successCount, - 'error': failedCount, - }) - : t('assets_downloaded_successfully', { - 'count': successCount, - }); + ? 'assets_downloaded_failed'.t( + context: context, + args: { + 'count': successCount, + 'error': failedCount, + }, + ) + : 'assets_downloaded_successfully'.t( + context: context, + args: { + 'count': successCount, + }, + ); ImmichToast.show( context: context, diff --git a/mobile/lib/widgets/asset_grid/thumbnail_image.dart b/mobile/lib/widgets/asset_grid/thumbnail_image.dart index 25f65b448c..5b98c8a756 100644 --- a/mobile/lib/widgets/asset_grid/thumbnail_image.dart +++ b/mobile/lib/widgets/asset_grid/thumbnail_image.dart @@ -1,13 +1,12 @@ import 'package:flutter/material.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/entities/asset.entity.dart'; +import 'package:immich_mobile/extensions/duration_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/widgets/common/immich_thumbnail.dart'; -import 'package:immich_mobile/utils/storage_indicator.dart'; -class ThumbnailImage extends ConsumerWidget { +class ThumbnailImage extends StatelessWidget { /// The asset to show the thumbnail image for final Asset asset; @@ -41,144 +40,10 @@ class ThumbnailImage extends ConsumerWidget { }); @override - Widget build(BuildContext context, WidgetRef ref) { + Widget build(BuildContext context) { final assetContainerColor = context.isDarkTheme ? context.primaryColor.darken(amount: 0.6) : context.primaryColor.lighten(amount: 0.8); - // Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id - final isFromDto = asset.id == noDbId; - - Widget buildSelectionIcon() { - if (isSelected) { - return Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: assetContainerColor, - ), - child: Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - ), - ); - } else { - return const Icon( - Icons.circle_outlined, - color: Colors.white, - ); - } - } - - Widget buildVideoIcon() { - final minutes = asset.duration.inMinutes; - final durationString = asset.duration.toString(); - return Positioned( - top: 5, - right: 8, - child: Row( - children: [ - Text( - minutes > 59 - ? durationString.substring(0, 7) // h:mm:ss - : minutes > 0 - ? durationString.substring(2, 7) // mm:ss - : durationString.substring(3, 7), // m:ss - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox( - width: 3, - ), - const Icon( - Icons.play_circle_fill_rounded, - color: Colors.white, - size: 18, - ), - ], - ), - ); - } - - Widget buildStackIcon() { - return Positioned( - top: !asset.isImage ? 28 : 5, - right: 8, - child: Row( - children: [ - if (asset.stackCount > 1) - Text( - "${asset.stackCount}", - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), - ), - if (asset.stackCount > 1) - const SizedBox( - width: 3, - ), - const Icon( - Icons.burst_mode_rounded, - color: Colors.white, - size: 18, - ), - ], - ), - ); - } - - Widget buildImage() { - final image = SizedBox.expand( - child: Hero( - tag: isFromDto - ? '${asset.remoteId}-$heroOffset' - : asset.id + heroOffset, - child: Stack( - children: [ - SizedBox.expand( - child: ImmichThumbnail( - asset: asset, - height: 250, - width: 250, - ), - ), - Container( - decoration: const BoxDecoration( - gradient: LinearGradient( - colors: [ - Color.fromRGBO(0, 0, 0, 0.1), - Colors.transparent, - Colors.transparent, - Color.fromRGBO(0, 0, 0, 0.1), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - stops: [0, 0.3, 0.6, 1], - ), - ), - ), - ], - ), - ), - ); - if (!multiselectEnabled || !isSelected) { - return image; - } - return Container( - decoration: BoxDecoration( - color: canDeselect ? assetContainerColor : Colors.grey, - ), - child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), - child: image, - ), - ); - } return Stack( children: [ @@ -187,32 +52,30 @@ class ThumbnailImage extends ConsumerWidget { curve: Curves.decelerate, decoration: BoxDecoration( border: multiselectEnabled && isSelected - ? Border.all( - color: canDeselect ? assetContainerColor : Colors.grey, - width: 8, - ) + ? canDeselect + ? Border.all( + color: assetContainerColor, + width: 8, + ) + : const Border( + top: BorderSide(color: Colors.grey, width: 8), + right: BorderSide(color: Colors.grey, width: 8), + bottom: BorderSide(color: Colors.grey, width: 8), + left: BorderSide(color: Colors.grey, width: 8), + ) : const Border(), ), child: Stack( children: [ - buildImage(), - if (showStorageIndicator) - Positioned( - right: 8, - bottom: 5, - child: Icon( - storageIcon(asset), - color: Colors.white.withValues(alpha: .8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Colors.black.withValues(alpha: 0.6), - offset: const Offset(0.0, 0.0), - ), - ], - ), - ), + _ImageIcon( + heroOffset: heroOffset, + asset: asset, + assetContainerColor: assetContainerColor, + multiselectEnabled: multiselectEnabled, + canDeselect: canDeselect, + isSelected: isSelected, + ), + if (showStorageIndicator) _StorageIcon(storage: asset.storage), if (asset.isFavorite) const Positioned( left: 8, @@ -223,20 +86,249 @@ class ThumbnailImage extends ConsumerWidget { size: 16, ), ), - if (!asset.isImage) buildVideoIcon(), - if (asset.stackCount > 0) buildStackIcon(), + if (asset.isVideo) _VideoIcon(duration: asset.duration), + if (asset.stackCount > 0) + _StackIcon( + isVideo: asset.isVideo, + stackCount: asset.stackCount, + ), ], ), ), if (multiselectEnabled) - Padding( - padding: const EdgeInsets.all(3.0), - child: Align( - alignment: Alignment.topLeft, - child: buildSelectionIcon(), - ), - ), + isSelected + ? const Padding( + padding: EdgeInsets.all(3.0), + child: Align( + alignment: Alignment.topLeft, + child: _SelectedIcon(), + ), + ) + : const Icon( + Icons.circle_outlined, + color: Colors.white, + ), ], ); } } + +class _SelectedIcon extends StatelessWidget { + const _SelectedIcon(); + + @override + Widget build(BuildContext context) { + final assetContainerColor = context.isDarkTheme + ? context.primaryColor.darken(amount: 0.6) + : context.primaryColor.lighten(amount: 0.8); + + return DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: assetContainerColor, + ), + child: Icon( + Icons.check_circle_rounded, + color: context.primaryColor, + ), + ); + } +} + +class _VideoIcon extends StatelessWidget { + final Duration duration; + + const _VideoIcon({required this.duration}); + + @override + Widget build(BuildContext context) { + return Positioned( + top: 5, + right: 8, + child: Row( + children: [ + Text( + duration.format(), + style: const TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 3), + const Icon( + Icons.play_circle_fill_rounded, + color: Colors.white, + size: 18, + ), + ], + ), + ); + } +} + +class _StackIcon extends StatelessWidget { + final bool isVideo; + final int stackCount; + + const _StackIcon({required this.isVideo, required this.stackCount}); + + @override + Widget build(BuildContext context) { + return Positioned( + top: isVideo ? 28 : 5, + right: 8, + child: Row( + children: [ + if (stackCount > 1) + Text( + "$stackCount", + style: const TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), + if (stackCount > 1) + const SizedBox( + width: 3, + ), + const Icon( + Icons.burst_mode_rounded, + color: Colors.white, + size: 18, + ), + ], + ), + ); + } +} + +class _StorageIcon extends StatelessWidget { + final AssetState storage; + + const _StorageIcon({required this.storage}); + + @override + Widget build(BuildContext context) { + return switch (storage) { + AssetState.local => const Positioned( + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_off_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [ + Shadow( + blurRadius: 5.0, + color: Color.fromRGBO(0, 0, 0, 0.6), + offset: Offset(0.0, 0.0), + ), + ], + ), + ), + AssetState.remote => const Positioned( + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [ + Shadow( + blurRadius: 5.0, + color: Color.fromRGBO(0, 0, 0, 0.6), + offset: Offset(0.0, 0.0), + ), + ], + ), + ), + AssetState.merged => const Positioned( + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_done_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [ + Shadow( + blurRadius: 5.0, + color: Color.fromRGBO(0, 0, 0, 0.6), + offset: Offset(0.0, 0.0), + ), + ], + ), + ), + }; + } +} + +class _ImageIcon extends StatelessWidget { + final int heroOffset; + final Asset asset; + final Color assetContainerColor; + final bool multiselectEnabled; + final bool canDeselect; + final bool isSelected; + + const _ImageIcon({ + required this.heroOffset, + required this.asset, + required this.assetContainerColor, + required this.multiselectEnabled, + required this.canDeselect, + required this.isSelected, + }); + + @override + Widget build(BuildContext context) { + // Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id + final isDto = asset.id == noDbId; + final image = SizedBox.expand( + child: Hero( + tag: isDto ? '${asset.remoteId}-$heroOffset' : asset.id + heroOffset, + child: Stack( + children: [ + SizedBox.expand( + child: ImmichThumbnail( + asset: asset, + height: 250, + width: 250, + ), + ), + const DecoratedBox( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Color.fromRGBO(0, 0, 0, 0.1), + Colors.transparent, + Colors.transparent, + Color.fromRGBO(0, 0, 0, 0.1), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + stops: [0, 0.3, 0.6, 1], + ), + ), + ), + ], + ), + ), + ); + + if (!multiselectEnabled || !isSelected) { + return image; + } + + return DecoratedBox( + decoration: canDeselect + ? BoxDecoration(color: assetContainerColor) + : const BoxDecoration(color: Colors.grey), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(15.0)), + child: image, + ), + ); + } +} diff --git a/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart b/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart index 1e6aba2bda..f6a617bf1c 100644 --- a/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart +++ b/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart @@ -38,7 +38,9 @@ class AdvancedBottomSheet extends HookConsumerWidget { color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[200], - borderRadius: BorderRadius.circular(15.0), + borderRadius: const BorderRadius.all( + Radius.circular(15.0), + ), ), child: Padding( padding: const EdgeInsets.only( diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart index f3f72dfd87..7b6325cf2c 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart @@ -9,11 +9,13 @@ import 'package:url_launcher/url_launcher.dart'; class ExifMap extends StatelessWidget { final ExifInfo exifInfo; final String? markerId; + final MapCreatedCallback? onMapCreated; const ExifMap({ super.key, required this.exifInfo, this.markerId = 'marker', + this.onMapCreated, }); @override @@ -82,6 +84,7 @@ class ExifMap extends StatelessWidget { debugPrint('Opening Map Uri: $uri'); launchUrl(uri); }, + onCreated: onMapCreated, ); }, ); diff --git a/mobile/lib/widgets/asset_viewer/formatted_duration.dart b/mobile/lib/widgets/asset_viewer/formatted_duration.dart index a34aab7d12..d18dc92575 100644 --- a/mobile/lib/widgets/asset_viewer/formatted_duration.dart +++ b/mobile/lib/widgets/asset_viewer/formatted_duration.dart @@ -1,15 +1,5 @@ import 'package:flutter/material.dart'; - -@pragma('vm:prefer-inline') -String _formatDuration(Duration position) { - final seconds = position.inSeconds.remainder(60).toString().padLeft(2, "0"); - final minutes = position.inMinutes.remainder(60).toString().padLeft(2, "0"); - if (position.inHours == 0) { - return "$minutes:$seconds"; - } - final hours = position.inHours.toString().padLeft(2, '0'); - return "$hours:$minutes:$seconds"; -} +import 'package:immich_mobile/extensions/duration_extensions.dart'; class FormattedDuration extends StatelessWidget { final Duration data; @@ -20,7 +10,7 @@ class FormattedDuration extends StatelessWidget { return SizedBox( width: data.inHours > 0 ? 70 : 60, // use a fixed width to prevent jitter child: Text( - _formatDuration(data), + data.format(), style: const TextStyle( fontSize: 14.0, color: Colors.white, diff --git a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart index 4ef55f4f76..f47e73ea78 100644 --- a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart +++ b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart @@ -83,8 +83,10 @@ class GalleryAppBar extends ConsumerWidget { addToAlbum(Asset addToAlbumAsset) { showModalBottomSheet( elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15.0), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(15.0), + ), ), context: context, builder: (BuildContext _) { diff --git a/mobile/lib/widgets/backup/album_info_card.dart b/mobile/lib/widgets/backup/album_info_card.dart index 526d2199be..fa113c6291 100644 --- a/mobile/lib/widgets/backup/album_info_card.dart +++ b/mobile/lib/widgets/backup/album_info_card.dart @@ -8,8 +8,8 @@ import 'package:immich_mobile/models/backup/available_album.model.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; -import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -46,7 +46,9 @@ class AlbumInfoCard extends HookConsumerWidget { if (isSelected) { return Chip( visualDensity: VisualDensity.compact, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(5)), + ), label: Text( "album_info_card_backup_album_included", style: TextStyle( @@ -60,7 +62,9 @@ class AlbumInfoCard extends HookConsumerWidget { } else if (isExcluded) { return Chip( visualDensity: VisualDensity.compact, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(5)), + ), label: Text( "album_info_card_backup_album_excluded", style: TextStyle( @@ -125,7 +129,9 @@ class AlbumInfoCard extends HookConsumerWidget { clipBehavior: Clip.hardEdge, margin: const EdgeInsets.all(1), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), // if you need this + borderRadius: const BorderRadius.all( + Radius.circular(12), // if you need this + ), side: BorderSide( color: isDarkTheme ? const Color.fromARGB(255, 37, 35, 35) diff --git a/mobile/lib/widgets/backup/backup_info_card.dart b/mobile/lib/widgets/backup/backup_info_card.dart index 58fc89cb65..54551da35a 100644 --- a/mobile/lib/widgets/backup/backup_info_card.dart +++ b/mobile/lib/widgets/backup/backup_info_card.dart @@ -18,7 +18,9 @@ class BackupInfoCard extends StatelessWidget { Widget build(BuildContext context) { return Card( shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), // if you need this + borderRadius: const BorderRadius.all( + Radius.circular(20), // if you need this + ), side: BorderSide( color: context.colorScheme.outlineVariant, width: 1, diff --git a/mobile/lib/widgets/backup/ios_debug_info_tile.dart b/mobile/lib/widgets/backup/ios_debug_info_tile.dart index 04be0c00dc..de80b3bfd1 100644 --- a/mobile/lib/widgets/backup/ios_debug_info_tile.dart +++ b/mobile/lib/widgets/backup/ios_debug_info_tile.dart @@ -1,9 +1,9 @@ -import 'package:easy_localization/easy_localization.dart'; +import 'package:intl/intl.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/backup/ios_background_settings.provider.dart'; -import 'package:immich_mobile/utils/translation.dart'; /// This is a simple debug widget which should be removed later on when we are /// more confident about background sync @@ -22,29 +22,28 @@ class IosDebugInfoTile extends HookConsumerWidget { final String title; if (processes == 0) { - title = 'ios_debug_info_no_processes_queued'.tr(); + title = 'ios_debug_info_no_processes_queued'.t(context: context); } else { - title = t('ios_debug_info_processes_queued', {'count': processes}); + title = 'ios_debug_info_processes_queued' + .t(context: context, args: {'count': processes}); } final df = DateFormat.yMd().add_jm(); final String subtitle; if (fetch == null && processing == null) { - subtitle = 'ios_debug_info_no_sync_yet'.tr(); + subtitle = 'ios_debug_info_no_sync_yet'.t(context: context); } else if (fetch != null && processing == null) { - subtitle = - t('ios_debug_info_fetch_ran_at', {'dateTime': df.format(fetch)}); + subtitle = 'ios_debug_info_fetch_ran_at' + .t(context: context, args: {'dateTime': df.format(fetch)}); } else if (processing != null && fetch == null) { - subtitle = t( - 'ios_debug_info_processing_ran_at', - {'dateTime': df.format(processing)}, - ); + subtitle = 'ios_debug_info_processing_ran_at' + .t(context: context, args: {'dateTime': df.format(processing)}); } else { final fetchOrProcessing = fetch!.isAfter(processing!) ? fetch : processing; - subtitle = t( - 'ios_debug_info_last_sync_at', - {'dateTime': df.format(fetchOrProcessing)}, + subtitle = 'ios_debug_info_last_sync_at'.t( + context: context, + args: {'dateTime': df.format(fetchOrProcessing)}, ); } 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 cc14ffa5fe..9d8be7c6ce 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 @@ -274,8 +274,10 @@ class ImmichAppBarDialog extends HookConsumerWidget { right: horizontalPadding, bottom: isHorizontal ? 20 : 100, ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20), + ), ), child: SizedBox( child: SingleChildScrollView( diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart index 93cdcd0e6f..0224ff030f 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart @@ -96,8 +96,8 @@ class AppBarProfileInfoBox extends HookConsumerWidget { child: Material( color: context.colorScheme.surfaceContainerHighest, elevation: 3, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50.0), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(50.0)), ), child: Padding( padding: const EdgeInsets.all(5.0), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart index 0f8ae8b8e1..a867a11e00 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart @@ -1,10 +1,10 @@ +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/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/models/server_info/server_info.model.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/utils/url_helper.dart'; @@ -173,7 +173,8 @@ class AppBarServerInfo extends HookConsumerWidget { verticalOffset: 0, decoration: BoxDecoration( color: context.primaryColor.withValues(alpha: 0.9), - borderRadius: BorderRadius.circular(10), + borderRadius: + const BorderRadius.all(Radius.circular(10)), ), textStyle: TextStyle( color: diff --git a/mobile/lib/widgets/common/date_time_picker.dart b/mobile/lib/widgets/common/date_time_picker.dart index 22384ebe8e..064c4845fe 100644 --- a/mobile/lib/widgets/common/date_time_picker.dart +++ b/mobile/lib/widgets/common/date_time_picker.dart @@ -172,11 +172,15 @@ class _DateTimePicker extends HookWidget { ListTile( tileColor: context.colorScheme.surfaceContainerHighest, shape: ShapeBorder.lerp( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10), + ), ), - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10), + ), ), 1, ), diff --git a/mobile/lib/widgets/common/drag_sheet.dart b/mobile/lib/widgets/common/drag_sheet.dart index 923050bcc6..a98e000e51 100644 --- a/mobile/lib/widgets/common/drag_sheet.dart +++ b/mobile/lib/widgets/common/drag_sheet.dart @@ -33,6 +33,9 @@ class ControlBoxButton extends StatelessWidget { @override Widget build(BuildContext context) { + final minWidth = + context.isMobile ? MediaQuery.sizeOf(context).width / 4.5 : 75.0; + return MaterialButton( padding: const EdgeInsets.all(10), shape: const RoundedRectangleBorder( @@ -40,7 +43,7 @@ class ControlBoxButton extends StatelessWidget { ), onPressed: onPressed, onLongPress: onLongPressed, - minWidth: 75.0, + minWidth: minWidth, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, diff --git a/mobile/lib/widgets/common/immich_app_bar.dart b/mobile/lib/widgets/common/immich_app_bar.dart index 812de58416..dbf088c112 100644 --- a/mobile/lib/widgets/common/immich_app_bar.dart +++ b/mobile/lib/widgets/common/immich_app_bar.dart @@ -42,7 +42,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog(), ), - borderRadius: BorderRadius.circular(12), + borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( decoration: BoxDecoration( @@ -66,10 +66,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { Icons.face_outlined, size: widgetSize, ) - : UserCircleAvatar( - radius: 17, - size: 31, - user: user, + : Semantics( + label: "logged_in_as".tr(namedArgs: {"user": user.name}), + child: UserCircleAvatar( + radius: 17, + size: 31, + user: user, + ), ), ), ); @@ -117,7 +120,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { return InkWell( onTap: () => context.pushRoute(const BackupControllerRoute()), - borderRadius: BorderRadius.circular(12), + borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( width: widgetSize / 2, diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart new file mode 100644 index 0000000000..ff0e88e5d7 --- /dev/null +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -0,0 +1,270 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.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/cast.provider.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/asset_viewer/cast_dialog.dart'; +import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_dialog.dart'; +import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; + +class ImmichSliverAppBar extends ConsumerWidget { + final List? actions; + final bool showUploadButton; + final bool floating; + final bool pinned; + final bool snap; + final Widget? title; + final double? expandedHeight; + + const ImmichSliverAppBar({ + super.key, + this.actions, + this.showUploadButton = true, + this.floating = true, + this.pinned = false, + this.snap = true, + this.title, + this.expandedHeight, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); + final isMultiSelectEnabled = + ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + + return SliverAnimatedOpacity( + duration: Durations.medium1, + opacity: isMultiSelectEnabled ? 0 : 1, + sliver: SliverAppBar( + floating: floating, + pinned: pinned, + snap: snap, + expandedHeight: expandedHeight, + backgroundColor: context.colorScheme.surfaceContainer, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(5), + ), + ), + automaticallyImplyLeading: false, + centerTitle: false, + title: title ?? const _ImmichLogoWithText(), + actions: [ + if (actions != null) + ...actions!.map( + (action) => Padding( + padding: const EdgeInsets.only(right: 16), + child: action, + ), + ), + IconButton( + icon: const Icon(Icons.swipe_left_alt_rounded), + onPressed: () => context.pop(), + ), + IconButton( + onPressed: () => ref.read(backgroundSyncProvider).syncRemote(), + icon: const Icon( + Icons.sync, + ), + ), + if (isCasting) + Padding( + padding: const EdgeInsets.only(right: 12), + child: IconButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => const CastDialog(), + ); + }, + icon: Icon( + isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, + ), + ), + ), + if (showUploadButton) + const Padding( + padding: EdgeInsets.only(right: 20), + child: _BackupIndicator(), + ), + const Padding( + padding: EdgeInsets.only(right: 20), + child: _ProfileIndicator(), + ), + ], + ), + ); + } +} + +class _ImmichLogoWithText extends StatelessWidget { + const _ImmichLogoWithText(); + + @override + Widget build(BuildContext context) { + return Builder( + builder: (BuildContext context) { + return Row( + children: [ + Builder( + builder: (context) { + return Padding( + padding: const EdgeInsets.only(top: 3.0), + child: SvgPicture.asset( + context.isDarkTheme + ? 'assets/immich-logo-inline-dark.svg' + : 'assets/immich-logo-inline-light.svg', + height: 40, + ), + ); + }, + ), + ], + ); + }, + ); + } +} + +class _ProfileIndicator extends ConsumerWidget { + const _ProfileIndicator(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final ServerInfo serverInfoState = ref.watch(serverInfoProvider); + final user = ref.watch(currentUserProvider); + const widgetSize = 30.0; + + return InkWell( + onTap: () => showDialog( + context: context, + useRootNavigator: false, + builder: (ctx) => const ImmichAppBarDialog(), + ), + borderRadius: const BorderRadius.all(Radius.circular(12)), + child: Badge( + label: Container( + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(widgetSize / 2), + ), + child: const Icon( + Icons.info, + color: Color.fromARGB(255, 243, 188, 106), + size: widgetSize / 2, + ), + ), + backgroundColor: Colors.transparent, + alignment: Alignment.bottomRight, + isLabelVisible: serverInfoState.isVersionMismatch || + ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable), + offset: const Offset(-2, -12), + child: user == null + ? const Icon( + Icons.face_outlined, + size: widgetSize, + ) + : Semantics( + label: "logged_in_as".tr(namedArgs: {"user": user.name}), + child: UserCircleAvatar( + radius: 17, + size: 31, + user: user, + ), + ), + ), + ); + } +} + +class _BackupIndicator extends ConsumerWidget { + const _BackupIndicator(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + const widgetSize = 30.0; + final indicatorIcon = _getBackupBadgeIcon(context, ref); + final badgeBackground = context.colorScheme.surfaceContainer; + + return InkWell( + onTap: () => context.pushRoute(const BackupControllerRoute()), + borderRadius: const BorderRadius.all(Radius.circular(12)), + child: Badge( + label: Container( + width: widgetSize / 2, + height: widgetSize / 2, + decoration: BoxDecoration( + color: badgeBackground, + border: Border.all( + color: context.colorScheme.outline.withValues(alpha: .3), + ), + borderRadius: BorderRadius.circular(widgetSize / 2), + ), + child: indicatorIcon, + ), + backgroundColor: Colors.transparent, + alignment: Alignment.bottomRight, + isLabelVisible: indicatorIcon != null, + offset: const Offset(-2, -12), + child: Icon( + Icons.backup_rounded, + size: widgetSize, + color: context.primaryColor, + ), + ), + ); + } + + Widget? _getBackupBadgeIcon(BuildContext context, WidgetRef ref) { + final BackUpState backupState = ref.watch(backupProvider); + final bool isEnableAutoBackup = + backupState.backgroundBackup || backupState.autoBackup; + final isDarkTheme = context.isDarkTheme; + final iconColor = isDarkTheme ? Colors.white : Colors.black; + + if (isEnableAutoBackup) { + if (backupState.backupProgress == BackUpProgressEnum.inProgress) { + return Container( + padding: const EdgeInsets.all(3.5), + child: CircularProgressIndicator( + strokeWidth: 2, + strokeCap: StrokeCap.round, + valueColor: AlwaysStoppedAnimation(iconColor), + semanticsLabel: 'backup_controller_page_backup'.tr(), + ), + ); + } else if (backupState.backupProgress != + BackUpProgressEnum.inBackground && + backupState.backupProgress != BackUpProgressEnum.manualInProgress) { + return Icon( + Icons.check_outlined, + size: 9, + color: iconColor, + semanticLabel: 'backup_controller_page_backup'.tr(), + ); + } + } + + if (!isEnableAutoBackup) { + return Icon( + Icons.cloud_off_rounded, + size: 9, + color: iconColor, + semanticLabel: 'backup_controller_page_backup'.tr(), + ); + } + + return null; + } +} diff --git a/mobile/lib/widgets/common/immich_thumbnail.dart b/mobile/lib/widgets/common/immich_thumbnail.dart index 58613a43ec..0918348f4c 100644 --- a/mobile/lib/widgets/common/immich_thumbnail.dart +++ b/mobile/lib/widgets/common/immich_thumbnail.dart @@ -6,6 +6,7 @@ import 'package:immich_mobile/providers/image/immich_local_thumbnail_provider.da import 'package:immich_mobile/providers/image/immich_remote_thumbnail_provider.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/utils/hooks/blurhash_hook.dart'; +import 'package:immich_mobile/utils/thumbnail_utils.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'; @@ -77,6 +78,13 @@ class ImmichThumbnail extends HookConsumerWidget { ); } + final assetAltText = getAltText( + asset!.exifInfo, + asset!.fileCreatedAt, + asset!.type, + [], + ); + final thumbnailProviderInstance = ImmichThumbnail.imageProvider( asset: asset, userId: userId, @@ -90,18 +98,21 @@ class ImmichThumbnail extends HookConsumerWidget { return originalErrorWidgetBuilder(ctx, error, stackTrace); } - return OctoImage.fromSet( - placeholderFadeInDuration: Duration.zero, - fadeInDuration: Duration.zero, - fadeOutDuration: const Duration(milliseconds: 100), - octoSet: OctoSet( - placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit), - errorBuilder: customErrorBuilder, + return Semantics( + label: assetAltText, + child: OctoImage.fromSet( + placeholderFadeInDuration: Duration.zero, + fadeInDuration: Duration.zero, + fadeOutDuration: const Duration(milliseconds: 100), + octoSet: OctoSet( + placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit), + errorBuilder: customErrorBuilder, + ), + image: thumbnailProviderInstance, + width: width, + height: height, + fit: fit, ), - image: thumbnailProviderInstance, - width: width, - height: height, - fit: fit, ); } } diff --git a/mobile/lib/widgets/common/local_album_sliver_app_bar.dart b/mobile/lib/widgets/common/local_album_sliver_app_bar.dart new file mode 100644 index 0000000000..4880865e66 --- /dev/null +++ b/mobile/lib/widgets/common/local_album_sliver_app_bar.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; + +class LocalAlbumsSliverAppBar extends StatelessWidget { + const LocalAlbumsSliverAppBar({super.key}); + + @override + Widget build(BuildContext context) { + return SliverAppBar( + floating: true, + pinned: true, + snap: false, + backgroundColor: context.colorScheme.surfaceContainer, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(5)), + ), + automaticallyImplyLeading: true, + centerTitle: true, + title: Text( + "on_this_device".t(context: context), + ), + ); + } +} diff --git a/mobile/lib/widgets/common/search_field.dart b/mobile/lib/widgets/common/search_field.dart index 5d71082b24..97ac75a63b 100644 --- a/mobile/lib/widgets/common/search_field.dart +++ b/mobile/lib/widgets/common/search_field.dart @@ -47,25 +47,33 @@ class SearchField extends StatelessWidget { color: context.themeData.colorScheme.onSurfaceSecondary, ), border: OutlineInputBorder( - borderRadius: BorderRadius.circular(25), + borderRadius: const BorderRadius.all( + Radius.circular(25), + ), borderSide: BorderSide( color: context.colorScheme.surfaceDim, ), ), enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(25), + borderRadius: const BorderRadius.all( + Radius.circular(25), + ), borderSide: BorderSide( color: context.colorScheme.surfaceContainer, ), ), disabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(25), + borderRadius: const BorderRadius.all( + Radius.circular(25), + ), borderSide: BorderSide( color: context.colorScheme.surfaceDim, ), ), focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(25), + borderRadius: const BorderRadius.all( + Radius.circular(25), + ), borderSide: BorderSide( color: context.colorScheme.primary.withAlpha(100), ), diff --git a/mobile/lib/widgets/common/selection_sliver_app_bar.dart b/mobile/lib/widgets/common/selection_sliver_app_bar.dart new file mode 100644 index 0000000000..2f06934bc0 --- /dev/null +++ b/mobile/lib/widgets/common/selection_sliver_app_bar.dart @@ -0,0 +1,77 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; + +class SelectionSliverAppBar extends ConsumerStatefulWidget { + const SelectionSliverAppBar({ + super.key, + }); + + @override + ConsumerState createState() => + _SelectionSliverAppBarState(); +} + +class _SelectionSliverAppBarState extends ConsumerState { + @override + Widget build(BuildContext context) { + final selection = ref.watch( + multiSelectProvider.select((s) => s.selectedAssets), + ); + + final toExclude = ref.watch( + multiSelectProvider.select((s) => s.lockedSelectionAssets), + ); + + final filteredAssets = selection.where((asset) { + return !toExclude.contains(asset); + }).toSet(); + + onDone(Set selected) { + ref.read(multiSelectProvider.notifier).reset(); + context.maybePop>(selected); + } + + return SliverAppBar( + floating: true, + pinned: true, + snap: false, + backgroundColor: context.colorScheme.surfaceContainer, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(5)), + ), + automaticallyImplyLeading: false, + leading: IconButton( + icon: const Icon(Icons.close_rounded), + onPressed: () { + ref.read(multiSelectProvider.notifier).reset(); + context.pop>(null); + }, + ), + centerTitle: true, + title: Text( + "Select {count}".t( + context: context, + args: { + 'count': filteredAssets.length.toString(), + }, + ), + ), + actions: [ + TextButton( + onPressed: () => onDone(filteredAssets), + child: Text( + 'done'.t(context: context), + style: context.textTheme.titleSmall?.copyWith( + color: context.colorScheme.primary, + ), + ), + ), + ], + ); + } +} diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart index 8866cb01b0..e8501f1184 100644 --- a/mobile/lib/widgets/common/user_circle_avatar.dart +++ b/mobile/lib/widgets/common/user_circle_avatar.dart @@ -5,9 +5,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/domain/models/user_metadata.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/widgets/common/transparent_image.dart'; @@ -26,7 +24,7 @@ class UserCircleAvatar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - bool isDarkTheme = context.themeData.brightness == Brightness.dark; + final userAvatarColor = user.avatarColor.toColor(); final profileImageUrl = '${Store.get(StoreKey.serverEndpoint)}/users/${user.id}/profile-image?d=${Random().nextInt(1024)}'; @@ -34,14 +32,14 @@ class UserCircleAvatar extends ConsumerWidget { style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12, - color: isDarkTheme && user.avatarColor == AvatarColor.primary + color: userAvatarColor.computeLuminance() > 0.5 ? Colors.black : Colors.white, ), child: Text(user.name[0].toUpperCase()), ); return CircleAvatar( - backgroundColor: user.avatarColor.toColor(), + backgroundColor: userAvatarColor, radius: radius, child: user.profileImagePath == null ? textIcon diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart index 5374d1ef33..8ccd69930a 100644 --- a/mobile/lib/widgets/forms/login/login_form.dart +++ b/mobile/lib/widgets/forms/login/login_form.dart @@ -394,7 +394,9 @@ class LoginForm extends HookConsumerWidget { decoration: BoxDecoration( color: context.isDarkTheme ? Colors.red.shade700 : Colors.red.shade100, - borderRadius: BorderRadius.circular(8), + borderRadius: const BorderRadius.all( + Radius.circular(8), + ), border: Border.all( color: context.isDarkTheme ? Colors.red.shade900 : Colors.red[200]!, diff --git a/mobile/lib/widgets/map/map_app_bar.dart b/mobile/lib/widgets/map/map_app_bar.dart index 4de5721486..ccadd2ad15 100644 --- a/mobile/lib/widgets/map/map_app_bar.dart +++ b/mobile/lib/widgets/map/map_app_bar.dart @@ -4,12 +4,12 @@ import 'package:auto_route/auto_route.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/providers/map/map_state.provider.dart'; -import 'package:immich_mobile/widgets/map/map_settings_sheet.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/utils/immich_loading_overlay.dart'; import 'package:immich_mobile/utils/selection_handlers.dart'; +import 'package:immich_mobile/widgets/map/map_settings_sheet.dart'; class MapAppBar extends HookWidget implements PreferredSizeWidget { final ValueNotifier> selectedAssets; @@ -24,7 +24,7 @@ class MapAppBar extends HookWidget implements PreferredSizeWidget { valueListenable: selectedAssets, builder: (ctx, value, child) => value.isNotEmpty ? _SelectionRow(selectedAssets: selectedAssets) - : _NonSelectionRow(), + : const _NonSelectionRow(), ), ); } @@ -34,6 +34,8 @@ class MapAppBar extends HookWidget implements PreferredSizeWidget { } class _NonSelectionRow extends StatelessWidget { + const _NonSelectionRow(); + @override Widget build(BuildContext context) { void onSettingsPressed() { diff --git a/mobile/lib/widgets/map/map_asset_grid.dart b/mobile/lib/widgets/map/map_asset_grid.dart index 4a3bb69cc0..c8c0816551 100644 --- a/mobile/lib/widgets/map/map_asset_grid.dart +++ b/mobile/lib/widgets/map/map_asset_grid.dart @@ -185,7 +185,7 @@ class MapAssetGrid extends HookConsumerWidget { }, loading: () => const SizedBox.shrink(), ) - : _MapNoAssetsInSheet(), + : const _MapNoAssetsInSheet(), ), ), _MapSheetDragRegion( @@ -201,6 +201,8 @@ class MapAssetGrid extends HookConsumerWidget { } class _MapNoAssetsInSheet extends StatelessWidget { + const _MapNoAssetsInSheet(); + @override Widget build(BuildContext context) { const image = Image( diff --git a/mobile/lib/widgets/map/map_thumbnail.dart b/mobile/lib/widgets/map/map_thumbnail.dart index b225a2edcb..06935cd4b5 100644 --- a/mobile/lib/widgets/map/map_thumbnail.dart +++ b/mobile/lib/widgets/map/map_thumbnail.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/maplibrecontroller_extensions.dart'; import 'package:immich_mobile/widgets/map/map_theme_override.dart'; import 'package:immich_mobile/widgets/map/positioned_asset_marker_icon.dart'; @@ -24,6 +25,7 @@ class MapThumbnail extends HookConsumerWidget { final double width; final ThemeMode? themeMode; final bool showAttribution; + final MapCreatedCallback? onCreated; const MapThumbnail({ super.key, @@ -36,16 +38,19 @@ class MapThumbnail extends HookConsumerWidget { this.showMarkerPin = false, this.themeMode, this.showAttribution = true, + this.onCreated, }); @override Widget build(BuildContext context, WidgetRef ref) { final offsettedCentre = LatLng(centre.latitude + 0.002, centre.longitude); final controller = useRef(null); + final styleLoaded = useState(false); final position = useValueNotifier?>(null); Future onMapCreated(MapLibreMapController mapController) async { controller.value = mapController; + styleLoaded.value = false; if (assetMarkerRemoteId != null) { // The iOS impl returns wrong toScreenLocation without the delay Future.delayed( @@ -54,17 +59,26 @@ class MapThumbnail extends HookConsumerWidget { position.value = await mapController.toScreenLocation(centre), ); } + onCreated?.call(mapController); } Future onStyleLoaded() async { if (showMarkerPin && controller.value != null) { await controller.value?.addMarkerAtLatLng(centre); } + styleLoaded.value = true; } return MapThemeOverride( themeMode: themeMode, - mapBuilder: (style) => SizedBox( + mapBuilder: (style) => AnimatedContainer( + duration: Durations.medium2, + curve: Curves.easeOut, + foregroundDecoration: BoxDecoration( + color: context.colorScheme.inverseSurface + .withAlpha(styleLoaded.value ? 0 : 200), + borderRadius: const BorderRadius.all(Radius.circular(15)), + ), height: height, width: width, child: ClipRRect( diff --git a/mobile/lib/widgets/map/positioned_asset_marker_icon.dart b/mobile/lib/widgets/map/positioned_asset_marker_icon.dart index 2cf82517ae..5ad49b42f8 100644 --- a/mobile/lib/widgets/map/positioned_asset_marker_icon.dart +++ b/mobile/lib/widgets/map/positioned_asset_marker_icon.dart @@ -108,7 +108,7 @@ class _PinPainter extends CustomPainter { final double primaryRadius; final double secondaryRadius; - _PinPainter({ + const _PinPainter({ required this.primaryColor, required this.secondaryColor, required this.primaryRadius, diff --git a/mobile/lib/widgets/memories/memory_card.dart b/mobile/lib/widgets/memories/memory_card.dart index abe3586194..31f4d5ed94 100644 --- a/mobile/lib/widgets/memories/memory_card.dart +++ b/mobile/lib/widgets/memories/memory_card.dart @@ -26,9 +26,9 @@ class MemoryCard extends StatelessWidget { Widget build(BuildContext context) { return Card( color: Colors.black, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(25.0), - side: const BorderSide( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(25.0)), + side: BorderSide( color: Colors.black, width: 1.0, ), diff --git a/mobile/lib/widgets/photo_view/photo_view.dart b/mobile/lib/widgets/photo_view/photo_view.dart index f72d1e298f..30e08748b8 100644 --- a/mobile/lib/widgets/photo_view/photo_view.dart +++ b/mobile/lib/widgets/photo_view/photo_view.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; - import 'package:immich_mobile/widgets/photo_view/src/controller/photo_view_controller.dart'; import 'package:immich_mobile/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart'; import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_core.dart'; @@ -16,6 +15,11 @@ export 'src/photo_view_computed_scale.dart'; export 'src/photo_view_scale_state.dart'; export 'src/utils/photo_view_hero_attributes.dart'; +typedef PhotoViewControllerCallback = PhotoViewControllerBase Function(); +typedef PhotoViewControllerCallbackBuilder = void Function( + PhotoViewControllerCallback photoViewMethod, +); + /// A [StatefulWidget] that contains all the photo view rendering elements. /// /// Sample code to use within an image: @@ -239,8 +243,11 @@ class PhotoView extends StatefulWidget { this.wantKeepAlive = false, this.gaplessPlayback = false, this.heroAttributes, + this.onPageBuild, + this.controllerCallbackBuilder, this.scaleStateChangedCallback, this.enableRotation = false, + this.semanticLabel, this.controller, this.scaleStateController, this.maxScale, @@ -260,6 +267,7 @@ class PhotoView extends StatefulWidget { this.tightMode, this.filterQuality, this.disableGestures, + this.disableScaleGestures, this.errorBuilder, this.enablePanAlways, }) : child = null, @@ -278,6 +286,8 @@ class PhotoView extends StatefulWidget { this.backgroundDecoration, this.wantKeepAlive = false, this.heroAttributes, + this.onPageBuild, + this.controllerCallbackBuilder, this.scaleStateChangedCallback, this.enableRotation = false, this.controller, @@ -298,9 +308,11 @@ class PhotoView extends StatefulWidget { this.gestureDetectorBehavior, this.tightMode, this.filterQuality, + this.disableScaleGestures, this.disableGestures, this.enablePanAlways, - }) : errorBuilder = null, + }) : semanticLabel = null, + errorBuilder = null, imageProvider = null, gaplessPlayback = false, loadingBuilder = null, @@ -325,6 +337,11 @@ class PhotoView extends StatefulWidget { /// `true` -> keeps the state final bool wantKeepAlive; + /// A Semantic description of the image. + /// + /// Used to provide a description of the image to TalkBack on Android, and VoiceOver on iOS. + final String? semanticLabel; + /// This is used to continue showing the old image (`true`), or briefly show /// nothing (`false`), when the `imageProvider` changes. By default it's set /// to `false`. @@ -338,6 +355,12 @@ class PhotoView extends StatefulWidget { /// by default it is `MediaQuery.of(context).size`. final Size? customSize; + // Called when a new PhotoView widget is built + final ValueChanged? onPageBuild; + + // Called from the parent during page change to get the new controller + final PhotoViewControllerCallbackBuilder? controllerCallbackBuilder; + /// A [Function] to be called whenever the scaleState changes, this happens when the user double taps the content ou start to pinch-in. final ValueChanged? scaleStateChangedCallback; @@ -419,6 +442,9 @@ class PhotoView extends StatefulWidget { // Useful when custom gesture detector is used in child widget. final bool? disableGestures; + /// Mirror to [PhotoView.disableGestures] + final bool? disableScaleGestures; + /// Enable pan the widget even if it's smaller than the hole parent widget. /// Useful when you want to drag a widget without restrictions. final bool? enablePanAlways; @@ -452,6 +478,7 @@ class _PhotoViewState extends State if (widget.controller == null) { _controlledController = true; _controller = PhotoViewController(); + widget.onPageBuild?.call(_controller); } else { _controlledController = false; _controller = widget.controller!; @@ -466,6 +493,8 @@ class _PhotoViewState extends State } _scaleStateController.outputScaleStateStream.listen(scaleStateListener); + // Pass a ref to the method back to the gallery so it can fetch the controller on page changes + widget.controllerCallbackBuilder?.call(_controllerGetter); } @override @@ -474,6 +503,7 @@ class _PhotoViewState extends State if (!_controlledController) { _controlledController = true; _controller = PhotoViewController(); + widget.onPageBuild?.call(_controller); } } else { _controlledController = false; @@ -509,6 +539,8 @@ class _PhotoViewState extends State } } + PhotoViewControllerBase _controllerGetter() => _controller; + @override Widget build(BuildContext context) { super.build(context); @@ -547,6 +579,7 @@ class _PhotoViewState extends State tightMode: widget.tightMode, filterQuality: widget.filterQuality, disableGestures: widget.disableGestures, + disableScaleGestures: widget.disableScaleGestures, enablePanAlways: widget.enablePanAlways, child: widget.child, ) @@ -554,6 +587,7 @@ class _PhotoViewState extends State imageProvider: widget.imageProvider!, loadingBuilder: widget.loadingBuilder, backgroundDecoration: backgroundDecoration, + semanticLabel: widget.semanticLabel, gaplessPlayback: widget.gaplessPlayback, heroAttributes: widget.heroAttributes, scaleStateChangedCallback: widget.scaleStateChangedCallback, @@ -577,6 +611,7 @@ class _PhotoViewState extends State tightMode: widget.tightMode, filterQuality: widget.filterQuality, disableGestures: widget.disableGestures, + disableScaleGestures: widget.disableScaleGestures, errorBuilder: widget.errorBuilder, enablePanAlways: widget.enablePanAlways, index: widget.index, @@ -625,7 +660,8 @@ typedef PhotoViewImageTapDownCallback = Function( typedef PhotoViewImageDragStartCallback = Function( BuildContext context, DragStartDetails details, - PhotoViewControllerValue controllerValue, + PhotoViewControllerBase controllerValue, + PhotoViewScaleStateController scaleStateController, ); /// A type definition for a callback when the user drags diff --git a/mobile/lib/widgets/photo_view/photo_view_gallery.dart b/mobile/lib/widgets/photo_view/photo_view_gallery.dart index b8918309bc..1cd4d4b217 100644 --- a/mobile/lib/widgets/photo_view/photo_view_gallery.dart +++ b/mobile/lib/widgets/photo_view/photo_view_gallery.dart @@ -4,15 +4,15 @@ import 'package:immich_mobile/widgets/photo_view/photo_view.dart' show LoadingBuilder, PhotoView, + PhotoViewControllerCallback, + PhotoViewImageDragEndCallback, + PhotoViewImageDragStartCallback, + PhotoViewImageDragUpdateCallback, + PhotoViewImageLongPressStartCallback, + PhotoViewImageScaleEndCallback, PhotoViewImageTapDownCallback, PhotoViewImageTapUpCallback, - PhotoViewImageDragStartCallback, - PhotoViewImageDragEndCallback, - PhotoViewImageDragUpdateCallback, - PhotoViewImageScaleEndCallback, - PhotoViewImageLongPressStartCallback, ScaleStateCycle; - import 'package:immich_mobile/widgets/photo_view/src/controller/photo_view_controller.dart'; import 'package:immich_mobile/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart'; import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_gesture_detector.dart'; @@ -20,7 +20,10 @@ import 'package:immich_mobile/widgets/photo_view/src/photo_view_scale_state.dart import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_hero_attributes.dart'; /// A type definition for a [Function] that receives a index after a page change in [PhotoViewGallery] -typedef PhotoViewGalleryPageChangedCallback = void Function(int index); +typedef PhotoViewGalleryPageChangedCallback = void Function( + int index, + PhotoViewControllerBase? controller, +); /// A type definition for a [Function] that defines a page in [PhotoViewGallery.build] typedef PhotoViewGalleryBuilder = PhotoViewGalleryPageOptions Function( @@ -115,12 +118,14 @@ class PhotoViewGallery extends StatefulWidget { this.reverse = false, this.pageController, this.onPageChanged, + this.onPageBuild, this.scaleStateChangedCallback, this.enableRotation = false, this.scrollPhysics, this.scrollDirection = Axis.horizontal, this.customSize, this.allowImplicitScrolling = false, + this.enablePanAlways = false, }) : itemCount = null, builder = null; @@ -138,12 +143,14 @@ class PhotoViewGallery extends StatefulWidget { this.reverse = false, this.pageController, this.onPageChanged, + this.onPageBuild, this.scaleStateChangedCallback, this.enableRotation = false, this.scrollPhysics, this.scrollDirection = Axis.horizontal, this.customSize, this.allowImplicitScrolling = false, + this.enablePanAlways = false, }) : pageOptions = null, assert(itemCount != null), assert(builder != null); @@ -169,6 +176,9 @@ class PhotoViewGallery extends StatefulWidget { /// Mirror to [PhotoView.wantKeepAlive] final bool wantKeepAlive; + /// Mirror to [PhotoView.enablePanAlways] + final bool enablePanAlways; + /// Mirror to [PhotoView.gaplessPlayback] final bool gaplessPlayback; @@ -181,6 +191,9 @@ class PhotoViewGallery extends StatefulWidget { /// An callback to be called on a page change final PhotoViewGalleryPageChangedCallback? onPageChanged; + /// Mirror to [PhotoView.onPageBuild] + final ValueChanged? onPageBuild; + /// Mirror to [PhotoView.scaleStateChangedCallback] final ValueChanged? scaleStateChangedCallback; @@ -207,6 +220,7 @@ class PhotoViewGallery extends StatefulWidget { class _PhotoViewGalleryState extends State { late final PageController _controller = widget.pageController ?? PageController(); + PhotoViewControllerCallback? _getController; void scaleStateChangedCallback(PhotoViewScaleState scaleState) { if (widget.scaleStateChangedCallback != null) { @@ -225,6 +239,14 @@ class _PhotoViewGalleryState extends State { return widget.pageOptions!.length; } + void _getControllerCallbackBuilder(PhotoViewControllerCallback method) { + _getController = method; + } + + void _onPageChange(int page) { + widget.onPageChanged?.call(page, _getController?.call()); + } + @override Widget build(BuildContext context) { // Enable corner hit test @@ -233,7 +255,7 @@ class _PhotoViewGalleryState extends State { child: PageView.builder( reverse: widget.reverse, controller: _controller, - onPageChanged: widget.onPageChanged, + onPageChanged: _onPageChange, itemCount: itemCount, itemBuilder: _buildItem, scrollDirection: widget.scrollDirection, @@ -249,13 +271,15 @@ class _PhotoViewGalleryState extends State { final PhotoView photoView = isCustomChild ? PhotoView.customChild( - key: ObjectKey(index), + key: pageOption.key ?? ObjectKey(index), childSize: pageOption.childSize, backgroundDecoration: widget.backgroundDecoration, wantKeepAlive: widget.wantKeepAlive, controller: pageOption.controller, scaleStateController: pageOption.scaleStateController, customSize: widget.customSize, + onPageBuild: widget.onPageBuild, + controllerCallbackBuilder: _getControllerCallbackBuilder, scaleStateChangedCallback: scaleStateChangedCallback, enableRotation: widget.enableRotation, initialScale: pageOption.initialScale, @@ -274,17 +298,22 @@ class _PhotoViewGalleryState extends State { filterQuality: pageOption.filterQuality, basePosition: pageOption.basePosition, disableGestures: pageOption.disableGestures, + disableScaleGestures: pageOption.disableScaleGestures, heroAttributes: pageOption.heroAttributes, + enablePanAlways: widget.enablePanAlways, child: pageOption.child, ) : PhotoView( - key: ObjectKey(index), + key: pageOption.key ?? ObjectKey(index), index: index, imageProvider: pageOption.imageProvider, loadingBuilder: widget.loadingBuilder, backgroundDecoration: widget.backgroundDecoration, + semanticLabel: pageOption.semanticLabel, wantKeepAlive: widget.wantKeepAlive, controller: pageOption.controller, + onPageBuild: widget.onPageBuild, + controllerCallbackBuilder: _getControllerCallbackBuilder, scaleStateController: pageOption.scaleStateController, customSize: widget.customSize, gaplessPlayback: widget.gaplessPlayback, @@ -306,6 +335,8 @@ class _PhotoViewGalleryState extends State { filterQuality: pageOption.filterQuality, basePosition: pageOption.basePosition, disableGestures: pageOption.disableGestures, + disableScaleGestures: pageOption.disableScaleGestures, + enablePanAlways: widget.enablePanAlways, errorBuilder: pageOption.errorBuilder, heroAttributes: pageOption.heroAttributes, ); @@ -332,9 +363,10 @@ class _PhotoViewGalleryState extends State { /// class PhotoViewGalleryPageOptions { PhotoViewGalleryPageOptions({ - Key? key, + this.key, required this.imageProvider, this.heroAttributes, + this.semanticLabel, this.minScale, this.maxScale, this.initialScale, @@ -352,15 +384,18 @@ class PhotoViewGalleryPageOptions { this.gestureDetectorBehavior, this.tightMode, this.filterQuality, + this.disableScaleGestures, this.disableGestures, this.errorBuilder, }) : child = null, childSize = null, assert(imageProvider != null); - PhotoViewGalleryPageOptions.customChild({ + const PhotoViewGalleryPageOptions.customChild({ + this.key, required this.child, this.childSize, + this.semanticLabel, this.heroAttributes, this.minScale, this.maxScale, @@ -379,16 +414,22 @@ class PhotoViewGalleryPageOptions { this.gestureDetectorBehavior, this.tightMode, this.filterQuality, + this.disableScaleGestures, this.disableGestures, }) : errorBuilder = null, imageProvider = null; + final Key? key; + /// Mirror to [PhotoView.imageProvider] final ImageProvider? imageProvider; /// Mirror to [PhotoView.heroAttributes] final PhotoViewHeroAttributes? heroAttributes; + /// Mirror to [PhotoView.semanticLabel] + final String? semanticLabel; + /// Mirror to [PhotoView.minScale] final dynamic minScale; @@ -446,6 +487,9 @@ class PhotoViewGalleryPageOptions { /// Mirror to [PhotoView.disableGestures] final bool? disableGestures; + /// Mirror to [PhotoView.disableGestures] + final bool? disableScaleGestures; + /// Quality levels for image filters. final FilterQuality? filterQuality; diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart index e26708bb41..37d1c78de1 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart @@ -37,6 +37,13 @@ abstract class PhotoViewControllerBase { /// Closes streams and removes eventual listeners. void dispose(); + void positionAnimationBuilder(void Function(Offset)? value); + void scaleAnimationBuilder(void Function(double)? value); + void rotationAnimationBuilder(void Function(double)? value); + + /// Animates multiple fields of the state + void animateMultiple({Offset? position, double? scale, double? rotation}); + /// Add a listener that will ignore updates made internally /// /// Since it is made for internal use, it is not performatic to use more than one @@ -147,12 +154,31 @@ class PhotoViewController late StreamController _outputCtrl; + late void Function(Offset)? _animatePosition; + late void Function(double)? _animateScale; + late void Function(double)? _animateRotation; + @override Stream get outputStateStream => _outputCtrl.stream; @override late PhotoViewControllerValue prevValue; + @override + void positionAnimationBuilder(void Function(Offset)? value) { + _animatePosition = value; + } + + @override + void scaleAnimationBuilder(void Function(double)? value) { + _animateScale = value; + } + + @override + void rotationAnimationBuilder(void Function(double)? value) { + _animateRotation = value; + } + @override void reset() { value = initial; @@ -172,6 +198,21 @@ class PhotoViewController _valueNotifier.removeIgnorableListener(callback); } + @override + void animateMultiple({Offset? position, double? scale, double? rotation}) { + if (position != null && _animatePosition != null) { + _animatePosition!(position); + } + + if (scale != null && _animateScale != null) { + _animateScale!(scale); + } + + if (rotation != null && _animateRotation != null) { + _animateRotation!(rotation); + } + } + @override void dispose() { _outputCtrl.close(); diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart index 968ac652e7..e2e668199a 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart @@ -111,6 +111,16 @@ mixin PhotoViewControllerDelegate on State { ); } + PhotoViewScaleState getScaleStateFromNewScale(double newScale) { + PhotoViewScaleState newScaleState = PhotoViewScaleState.initial; + if (scale != scaleBoundaries.initialScale) { + newScaleState = (newScale > scaleBoundaries.initialScale) + ? PhotoViewScaleState.zoomedIn + : PhotoViewScaleState.zoomedOut; + } + return newScaleState; + } + void updateScaleStateFromNewScale(double newScale) { PhotoViewScaleState newScaleState = PhotoViewScaleState.initial; if (scale != scaleBoundaries.initialScale) { diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart index 16021ceab1..dea8be1a0f 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart @@ -26,6 +26,8 @@ class PhotoViewScaleStateController { StreamController.broadcast() ..sink.add(PhotoViewScaleState.initial); + bool _hasZoomedOutManually = false; + /// The output for state/value updates Stream get outputScaleStateStream => _outputScaleStateCtrl.stream; @@ -42,10 +44,20 @@ class PhotoViewScaleStateController { return; } + if (newValue == PhotoViewScaleState.zoomedOut) { + _hasZoomedOutManually = true; + } + + if (newValue == PhotoViewScaleState.initial) { + _hasZoomedOutManually = false; + } + prevScaleState = _scaleStateNotifier.value; _scaleStateNotifier.value = newValue; } + bool get hasZoomedOutManually => _hasZoomedOutManually; + /// Checks if its actual value is different than previousValue bool get hasChanged => prevScaleState != scaleState; @@ -71,6 +83,15 @@ class PhotoViewScaleStateController { if (_scaleStateNotifier.value == newValue) { return; } + + if (newValue == PhotoViewScaleState.zoomedOut) { + _hasZoomedOutManually = true; + } + + if (newValue == PhotoViewScaleState.initial) { + _hasZoomedOutManually = false; + } + prevScaleState = _scaleStateNotifier.value; _scaleStateNotifier.updateIgnoring(newValue); } 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 bb892737f6..6b6e5067c5 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 @@ -29,6 +29,7 @@ class PhotoViewCore extends StatefulWidget { super.key, required this.imageProvider, required this.backgroundDecoration, + required this.semanticLabel, required this.gaplessPlayback, required this.heroAttributes, required this.enableRotation, @@ -48,6 +49,7 @@ class PhotoViewCore extends StatefulWidget { required this.tightMode, required this.filterQuality, required this.disableGestures, + required this.disableScaleGestures, required this.enablePanAlways, }) : customChild = null; @@ -73,12 +75,15 @@ class PhotoViewCore extends StatefulWidget { required this.tightMode, required this.filterQuality, required this.disableGestures, + required this.disableScaleGestures, required this.enablePanAlways, - }) : imageProvider = null, + }) : semanticLabel = null, + imageProvider = null, gaplessPlayback = false; final Decoration? backgroundDecoration; final ImageProvider? imageProvider; + final String? semanticLabel; final bool? gaplessPlayback; final PhotoViewHeroAttributes? heroAttributes; final bool enableRotation; @@ -103,6 +108,7 @@ class PhotoViewCore extends StatefulWidget { final HitTestBehavior? gestureDetectorBehavior; final bool tightMode; final bool disableGestures; + final bool disableScaleGestures; final bool enablePanAlways; final FilterQuality filterQuality; @@ -120,6 +126,7 @@ class PhotoViewCoreState extends State TickerProviderStateMixin, PhotoViewControllerDelegate, HitCornersDetector { + Offset? _normalizedPosition; double? _scaleBefore; double? _rotationBefore; @@ -152,32 +159,33 @@ class PhotoViewCoreState extends State void onScaleStart(ScaleStartDetails details) { _rotationBefore = controller.rotation; _scaleBefore = scale; + _normalizedPosition = details.focalPoint - controller.position; _scaleAnimationController.stop(); _positionAnimationController.stop(); _rotationAnimationController.stop(); } + bool _shouldAllowPanRotate() => switch (scaleStateController.scaleState) { + PhotoViewScaleState.zoomedIn => + scaleStateController.hasZoomedOutManually, + _ => true, + }; + void onScaleUpdate(ScaleUpdateDetails details) { - final centeredFocalPoint = Offset( - details.focalPoint.dx - scaleBoundaries.outerSize.width / 2, - details.focalPoint.dy - scaleBoundaries.outerSize.height / 2, - ); final double newScale = _scaleBefore! * details.scale; - final double scaleDelta = newScale / scale; - final Offset newPosition = - (controller.position + details.focalPointDelta) * scaleDelta - - centeredFocalPoint * (scaleDelta - 1); + Offset delta = details.focalPoint - _normalizedPosition!; updateScaleStateFromNewScale(newScale); + final panEnabled = widget.enablePanAlways && _shouldAllowPanRotate(); + final rotationEnabled = widget.enableRotation && _shouldAllowPanRotate(); + updateMultiple( scale: newScale, - position: widget.enablePanAlways - ? newPosition - : clampPosition(position: newPosition), - rotation: - widget.enableRotation ? _rotationBefore! + details.rotation : null, - rotationFocusPoint: widget.enableRotation ? details.focalPoint : null, + position: + panEnabled ? delta : clampPosition(position: delta * details.scale), + rotation: rotationEnabled ? _rotationBefore! + details.rotation : null, + rotationFocusPoint: rotationEnabled ? details.focalPoint : null, ); } @@ -189,6 +197,16 @@ class PhotoViewCoreState extends State widget.onScaleEnd?.call(context, details, controller.value); + final scaleState = getScaleStateFromNewScale(scale); + if (scaleState == PhotoViewScaleState.zoomedOut) { + scaleStateController.scaleState = PhotoViewScaleState.originalSize; + } else if (scaleState == PhotoViewScaleState.zoomedIn) { + animateRotation(controller.rotation, 0); + if (_shouldAllowPanRotate()) { + animatePosition(controller.position, Offset.zero); + } + } + //animate back to maxScale if gesture exceeded the maxScale specified if (s > maxScale) { final double scaleComebackRatio = maxScale / s; @@ -232,6 +250,9 @@ class PhotoViewCoreState extends State } void animateScale(double from, double to) { + if (!mounted) { + return; + } _scaleAnimation = Tween( begin: from, end: to, @@ -242,6 +263,9 @@ class PhotoViewCoreState extends State } void animatePosition(Offset from, Offset to) { + if (!mounted) { + return; + } _positionAnimation = Tween(begin: from, end: to) .animate(_positionAnimationController); _positionAnimationController @@ -250,6 +274,9 @@ class PhotoViewCoreState extends State } void animateRotation(double from, double to) { + if (!mounted) { + return; + } _rotationAnimation = Tween(begin: from, end: to) .animate(_rotationAnimationController); _rotationAnimationController @@ -271,11 +298,28 @@ class PhotoViewCoreState extends State } } + void _animateControllerPosition(Offset position) { + animatePosition(controller.position, position); + } + + void _animateControllerScale(double scale) { + if (controller.scale != null) { + animateScale(controller.scale!, scale); + } + } + + void _animateControllerRotation(double rotation) { + animateRotation(controller.rotation, rotation); + } + @override void initState() { super.initState(); initDelegate(); addAnimateOnScaleStateUpdate(animateOnScaleStateUpdate); + controller.positionAnimationBuilder(_animateControllerPosition); + controller.scaleAnimationBuilder(_animateControllerScale); + controller.rotationAnimationBuilder(_animateControllerRotation); cachedScaleBoundaries = widget.scaleBoundaries; @@ -341,7 +385,7 @@ class PhotoViewCoreState extends State basePosition, useImageScale, ), - child: _buildHero(), + child: _buildHero(_buildChild()), ); final child = Container( @@ -363,18 +407,29 @@ class PhotoViewCoreState extends State } return PhotoViewGestureDetector( - onDoubleTap: nextScaleState, - onScaleStart: onScaleStart, - onScaleUpdate: onScaleUpdate, - onScaleEnd: onScaleEnd, + disableScaleGestures: widget.disableScaleGestures, + onDoubleTap: widget.disableScaleGestures ? null : onDoubleTap, + onScaleStart: widget.disableScaleGestures ? null : onScaleStart, + onScaleUpdate: widget.disableScaleGestures ? null : onScaleUpdate, + onScaleEnd: widget.disableScaleGestures ? null : onScaleEnd, onDragStart: widget.onDragStart != null - ? (details) => widget.onDragStart!(context, details, value) + ? (details) => widget.onDragStart!( + context, + details, + widget.controller, + widget.scaleStateController, + ) : null, onDragEnd: widget.onDragEnd != null - ? (details) => widget.onDragEnd!(context, details, value) + ? (details) => + widget.onDragEnd!(context, details, widget.controller.value) : null, onDragUpdate: widget.onDragUpdate != null - ? (details) => widget.onDragUpdate!(context, details, value) + ? (details) => widget.onDragUpdate!( + context, + details, + widget.controller.value, + ) : null, hitDetector: this, onTapUp: widget.onTapUp != null @@ -395,7 +450,7 @@ class PhotoViewCoreState extends State ); } - Widget _buildHero() { + Widget _buildHero(Widget child) { return heroAttributes != null ? Hero( tag: heroAttributes!.tag, @@ -403,16 +458,20 @@ class PhotoViewCoreState extends State flightShuttleBuilder: heroAttributes!.flightShuttleBuilder, placeholderBuilder: heroAttributes!.placeholderBuilder, transitionOnUserGestures: heroAttributes!.transitionOnUserGestures, - child: _buildChild(), + child: child, ) - : _buildChild(); + : child; } Widget _buildChild() { return widget.hasCustomChild ? widget.customChild! : Image( + key: widget.heroAttributes?.tag != null + ? ObjectKey(widget.heroAttributes!.tag) + : null, image: widget.imageProvider!, + semanticLabel: widget.semanticLabel, gaplessPlayback: widget.gaplessPlayback ?? false, filterQuality: widget.filterQuality, width: scaleBoundaries.childSize.width * scale, @@ -442,6 +501,7 @@ class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate { final double offsetX = halfWidth * (basePosition.x + 1); final double offsetY = halfHeight * (basePosition.y + 1); + return Offset(offsetX, offsetY); } diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart index 2eef5e6742..93fd1526da 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart @@ -21,6 +21,7 @@ class PhotoViewGestureDetector extends StatelessWidget { this.onTapUp, this.onTapDown, this.behavior, + this.disableScaleGestures = false, }); final GestureDoubleTapCallback? onDoubleTap; @@ -43,6 +44,8 @@ class PhotoViewGestureDetector extends StatelessWidget { final HitTestBehavior? behavior; + final bool disableScaleGestures; + @override Widget build(BuildContext context) { final scope = PhotoViewGestureDetectorScope.of(context); @@ -96,9 +99,11 @@ class PhotoViewGestureDetector extends StatelessWidget { ), (PhotoViewGestureRecognizer instance) { instance + ..dragStartBehavior = DragStartBehavior.start ..onStart = onScaleStart ..onUpdate = onScaleUpdate - ..onEnd = onScaleEnd; + ..onEnd = onScaleEnd + ..disableScaleGestures = disableScaleGestures; }, ); @@ -124,10 +129,12 @@ class PhotoViewGestureRecognizer extends ScaleGestureRecognizer { this.validateAxis, this.touchSlopFactor = 1, PointerDeviceKind? kind, + this.disableScaleGestures = false, }) : super(supportedDevices: null); final HitCornersDetector? hitDetector; final Axis? validateAxis; final double touchSlopFactor; + bool disableScaleGestures; Map _pointerLocations = {}; @@ -155,7 +162,7 @@ class PhotoViewGestureRecognizer extends ScaleGestureRecognizer { @override void handleEvent(PointerEvent event) { - if (validateAxis != null) { + if (validateAxis != null && !disableScaleGestures) { bool didChangeConfiguration = false; if (event is PointerMoveEvent) { if (!event.synthesized) { diff --git a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart index 57496f3777..d4afe85d2b 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart @@ -11,6 +11,7 @@ class ImageWrapper extends StatefulWidget { required this.imageProvider, required this.loadingBuilder, required this.backgroundDecoration, + required this.semanticLabel, required this.gaplessPlayback, required this.heroAttributes, required this.scaleStateChangedCallback, @@ -34,6 +35,7 @@ class ImageWrapper extends StatefulWidget { required this.tightMode, required this.filterQuality, required this.disableGestures, + this.disableScaleGestures, required this.errorBuilder, required this.enablePanAlways, required this.index, @@ -43,6 +45,7 @@ class ImageWrapper extends StatefulWidget { final LoadingBuilder? loadingBuilder; final ImageErrorWidgetBuilder? errorBuilder; final BoxDecoration backgroundDecoration; + final String? semanticLabel; final bool gaplessPlayback; final PhotoViewHeroAttributes? heroAttributes; final ValueChanged? scaleStateChangedCallback; @@ -66,6 +69,7 @@ class ImageWrapper extends StatefulWidget { final bool? tightMode; final FilterQuality? filterQuality; final bool? disableGestures; + final bool? disableScaleGestures; final bool? enablePanAlways; final int index; @@ -193,6 +197,7 @@ class _ImageWrapperState extends State { return PhotoViewCore( imageProvider: widget.imageProvider, backgroundDecoration: widget.backgroundDecoration, + semanticLabel: widget.semanticLabel, gaplessPlayback: widget.gaplessPlayback, enableRotation: widget.enableRotation, heroAttributes: widget.heroAttributes, @@ -212,6 +217,7 @@ class _ImageWrapperState extends State { tightMode: widget.tightMode ?? false, filterQuality: widget.filterQuality ?? FilterQuality.none, disableGestures: widget.disableGestures ?? false, + disableScaleGestures: widget.disableScaleGestures ?? false, enablePanAlways: widget.enablePanAlways ?? false, ); } @@ -266,6 +272,7 @@ class CustomChildWrapper extends StatelessWidget { required this.tightMode, required this.filterQuality, required this.disableGestures, + this.disableScaleGestures, required this.enablePanAlways, }); @@ -296,6 +303,7 @@ class CustomChildWrapper extends StatelessWidget { final HitTestBehavior? gestureDetectorBehavior; final bool? tightMode; final FilterQuality? filterQuality; + final bool? disableScaleGestures; final bool? disableGestures; final bool? enablePanAlways; @@ -330,6 +338,7 @@ class CustomChildWrapper extends StatelessWidget { tightMode: tightMode ?? false, filterQuality: filterQuality ?? FilterQuality.none, disableGestures: disableGestures ?? false, + disableScaleGestures: disableScaleGestures ?? false, enablePanAlways: enablePanAlways ?? false, ); } diff --git a/mobile/lib/widgets/search/person_name_edit_form.dart b/mobile/lib/widgets/search/person_name_edit_form.dart index 76d6e80832..886f17b2cc 100644 --- a/mobile/lib/widgets/search/person_name_edit_form.dart +++ b/mobile/lib/widgets/search/person_name_edit_form.dart @@ -9,7 +9,7 @@ class PersonNameEditFormResult { final bool success; final String updatedName; - PersonNameEditFormResult(this.success, this.updatedName); + const PersonNameEditFormResult(this.success, this.updatedName); } class PersonNameEditForm extends HookConsumerWidget { @@ -47,7 +47,7 @@ class PersonNameEditForm extends HookConsumerWidget { actions: [ TextButton( onPressed: () => context.pop( - PersonNameEditFormResult(false, ''), + const PersonNameEditFormResult(false, ''), ), child: Text( "cancel", diff --git a/mobile/lib/widgets/search/search_filter/common/dropdown.dart b/mobile/lib/widgets/search/search_filter/common/dropdown.dart index dd8785459f..dd0ec44e45 100644 --- a/mobile/lib/widgets/search/search_filter/common/dropdown.dart +++ b/mobile/lib/widgets/search/search_filter/common/dropdown.dart @@ -18,10 +18,10 @@ class SearchDropdown extends StatelessWidget { @override Widget build(BuildContext context) { - final menuStyle = MenuStyle( + final menuStyle = const MenuStyle( shape: WidgetStatePropertyAll( RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15), + borderRadius: BorderRadius.all(Radius.circular(15)), ), ), ); diff --git a/mobile/lib/widgets/search/search_filter/people_picker.dart b/mobile/lib/widgets/search/search_filter/people_picker.dart index ed7bcadd79..44d01d274e 100644 --- a/mobile/lib/widgets/search/search_filter/people_picker.dart +++ b/mobile/lib/widgets/search/search_filter/people_picker.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/interfaces/person_api.interface.dart'; import 'package:immich_mobile/pages/common/large_leading_tile.dart'; import 'package:immich_mobile/providers/search/people.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; diff --git a/mobile/lib/widgets/settings/backup_settings/background_settings.dart b/mobile/lib/widgets/settings/backup_settings/background_settings.dart index d628309050..619a410545 100644 --- a/mobile/lib/widgets/settings/backup_settings/background_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/background_settings.dart @@ -97,7 +97,7 @@ class BackgroundBackupSettings extends ConsumerWidget { onBatteryInfo: showBatteryOptimizationInfoToUser, ), if (Platform.isIOS && iosSettings?.appRefreshEnabled != true) - _IOSBackgroundRefreshDisabled(), + const _IOSBackgroundRefreshDisabled(), if (Platform.isIOS && iosSettings != null) IosDebugInfoTile(settings: iosSettings), ], @@ -106,6 +106,8 @@ class BackgroundBackupSettings extends ConsumerWidget { } class _IOSBackgroundRefreshDisabled extends StatelessWidget { + const _IOSBackgroundRefreshDisabled(); + @override Widget build(BuildContext context) { return SettingsButtonListTile( diff --git a/mobile/lib/widgets/settings/language_settings.dart b/mobile/lib/widgets/settings/language_settings.dart index 7dc7f89ea1..4d41d5b19b 100644 --- a/mobile/lib/widgets/settings/language_settings.dart +++ b/mobile/lib/widgets/settings/language_settings.dart @@ -4,6 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:immich_mobile/constants/locales.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/services/localization.service.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/widgets/common/search_field.dart'; @@ -91,6 +92,7 @@ class LanguageSettings extends HookConsumerWidget { padding: const EdgeInsets.all(8), itemCount: filteredLocaleEntries.value.length, itemExtent: 64.0, + cacheExtent: 100, itemBuilder: (context, index) { final countryName = filteredLocaleEntries.value[index].key; @@ -100,6 +102,7 @@ class LanguageSettings extends HookConsumerWidget { selectedLocale.value == localeValue; return _LanguageItem( + key: ValueKey(localeValue.toString()), countryName: countryName, localeValue: localeValue, isSelected: isSelected, @@ -162,7 +165,7 @@ class _LanguageSearchBar extends StatelessWidget { child: SearchField( autofocus: false, contentPadding: const EdgeInsets.all(12), - hintText: 'language_search_hint'.tr(), + hintText: 'language_search_hint'.t(context: context), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: controller.text.isNotEmpty ? IconButton( @@ -196,14 +199,14 @@ class _LanguageNotFound extends StatelessWidget { ), const SizedBox(height: 8), Text( - 'language_no_results_title'.tr(), + 'language_no_results_title'.t(context: context), style: context.textTheme.titleMedium?.copyWith( color: context.colorScheme.onSurface, ), ), const SizedBox(height: 4), Text( - 'language_no_results_subtitle'.tr(), + 'language_no_results_subtitle'.t(context: context), style: context.textTheme.bodyMedium?.copyWith( color: context.colorScheme.onSurface.withValues(alpha: 0.8), ), @@ -246,7 +249,7 @@ class _LanguageApplyButton extends StatelessWidget { ), ) : Text( - 'setting_languages_apply'.tr(), + 'setting_languages_apply'.t(context: context), style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 16.0, @@ -261,6 +264,7 @@ class _LanguageApplyButton extends StatelessWidget { class _LanguageItem extends StatelessWidget { const _LanguageItem({ + super.key, required this.countryName, required this.localeValue, required this.isSelected, diff --git a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart index 633d84c9c8..d38ba06d76 100644 --- a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart @@ -17,8 +17,9 @@ class ExternalNetworkPreference extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final entries = - useState([AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown)]); + final entries = useState( + [const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown)], + ); final canSave = useState(false); saveEndpointList() { @@ -169,7 +170,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { ? () { entries.value = [ ...entries.value, - AuxilaryEndpoint( + const AuxilaryEndpoint( url: '', status: AuxCheckStatus.unknown, ), diff --git a/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart b/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart index af34ab9e16..011904c310 100644 --- a/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart @@ -7,9 +7,9 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/providers/theme.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/theme/color_scheme.dart'; import 'package:immich_mobile/theme/dynamic_theme.dart'; +import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class PrimaryColorSetting extends HookConsumerWidget { const PrimaryColorSetting({ @@ -136,8 +136,8 @@ class PrimaryColorSetting extends HookConsumerWidget { dense: true, activeColor: context.primaryColor, tileColor: context.colorScheme.surfaceContainerHigh, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(15)), ), title: Text( 'theme_setting_system_primary_color_title'.tr(), diff --git a/mobile/lib/widgets/settings/settings_radio_list_tile.dart b/mobile/lib/widgets/settings/settings_radio_list_tile.dart index 1c26682a65..3f3a6cbe69 100644 --- a/mobile/lib/widgets/settings/settings_radio_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_radio_list_tile.dart @@ -5,7 +5,7 @@ class SettingsRadioGroup { final String title; final T value; - SettingsRadioGroup({required this.title, required this.value}); + const SettingsRadioGroup({required this.title, required this.value}); } class SettingsRadioListTile extends StatelessWidget { diff --git a/mobile/lib/widgets/shared_link/shared_link_item.dart b/mobile/lib/widgets/shared_link/shared_link_item.dart index 69e763ea09..a9707eb0d3 100644 --- a/mobile/lib/widgets/shared_link/shared_link_item.dart +++ b/mobile/lib/widgets/shared_link/shared_link_item.dart @@ -240,7 +240,7 @@ class SharedLinkItem extends ConsumerWidget { verticalOffset: 0, decoration: BoxDecoration( color: colorScheme.primary.withValues(alpha: 0.9), - borderRadius: BorderRadius.circular(10), + borderRadius: const BorderRadius.all(Radius.circular(10)), ), textStyle: TextStyle( color: isDarkMode ? Colors.black : Colors.white, @@ -268,7 +268,7 @@ class SharedLinkItem extends ConsumerWidget { verticalOffset: 0, decoration: BoxDecoration( color: colorScheme.primary.withValues(alpha: 0.9), - borderRadius: BorderRadius.circular(10), + borderRadius: const BorderRadius.all(Radius.circular(10)), ), textStyle: TextStyle( color: isDarkMode ? Colors.black : Colors.white, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index ee646354d1..1beaec0ae6 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.134.0 +- API version: 1.135.3 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen @@ -122,6 +122,8 @@ Class | Method | HTTP request | Description *DeprecatedApi* | [**getRandom**](doc//DeprecatedApi.md#getrandom) | **GET** /assets/random | *DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive | *DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info | +*DuplicatesApi* | [**deleteDuplicate**](doc//DuplicatesApi.md#deleteduplicate) | **DELETE** /duplicates/{id} | +*DuplicatesApi* | [**deleteDuplicates**](doc//DuplicatesApi.md#deleteduplicates) | **DELETE** /duplicates | *DuplicatesApi* | [**getAssetDuplicates**](doc//DuplicatesApi.md#getassetduplicates) | **GET** /duplicates | *FacesApi* | [**createFace**](doc//FacesApi.md#createface) | **POST** /faces | *FacesApi* | [**deleteFace**](doc//FacesApi.md#deleteface) | **DELETE** /faces/{id} | @@ -144,6 +146,7 @@ Class | Method | HTTP request | Description *MemoriesApi* | [**createMemory**](doc//MemoriesApi.md#creatememory) | **POST** /memories | *MemoriesApi* | [**deleteMemory**](doc//MemoriesApi.md#deletememory) | **DELETE** /memories/{id} | *MemoriesApi* | [**getMemory**](doc//MemoriesApi.md#getmemory) | **GET** /memories/{id} | +*MemoriesApi* | [**memoriesStatistics**](doc//MemoriesApi.md#memoriesstatistics) | **GET** /memories/statistics | *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} | @@ -166,6 +169,8 @@ Class | Method | HTTP request | Description *PartnersApi* | [**removePartner**](doc//PartnersApi.md#removepartner) | **DELETE** /partners/{id} | *PartnersApi* | [**updatePartner**](doc//PartnersApi.md#updatepartner) | **PUT** /partners/{id} | *PeopleApi* | [**createPerson**](doc//PeopleApi.md#createperson) | **POST** /people | +*PeopleApi* | [**deletePeople**](doc//PeopleApi.md#deletepeople) | **DELETE** /people | +*PeopleApi* | [**deletePerson**](doc//PeopleApi.md#deleteperson) | **DELETE** /people/{id} | *PeopleApi* | [**getAllPeople**](doc//PeopleApi.md#getallpeople) | **GET** /people | *PeopleApi* | [**getPerson**](doc//PeopleApi.md#getperson) | **GET** /people/{id} | *PeopleApi* | [**getPersonStatistics**](doc//PeopleApi.md#getpersonstatistics) | **GET** /people/{id}/statistics | @@ -375,6 +380,7 @@ Class | Method | HTTP request | Description - [MemoriesUpdate](doc//MemoriesUpdate.md) - [MemoryCreateDto](doc//MemoryCreateDto.md) - [MemoryResponseDto](doc//MemoryResponseDto.md) + - [MemoryStatisticsResponseDto](doc//MemoryStatisticsResponseDto.md) - [MemoryType](doc//MemoryType.md) - [MemoryUpdateDto](doc//MemoryUpdateDto.md) - [MergePersonDto](doc//MergePersonDto.md) @@ -460,6 +466,8 @@ Class | Method | HTTP request | Description - [SyncAckDto](doc//SyncAckDto.md) - [SyncAckSetDto](doc//SyncAckSetDto.md) - [SyncAlbumDeleteV1](doc//SyncAlbumDeleteV1.md) + - [SyncAlbumToAssetDeleteV1](doc//SyncAlbumToAssetDeleteV1.md) + - [SyncAlbumToAssetV1](doc//SyncAlbumToAssetV1.md) - [SyncAlbumUserDeleteV1](doc//SyncAlbumUserDeleteV1.md) - [SyncAlbumUserV1](doc//SyncAlbumUserV1.md) - [SyncAlbumV1](doc//SyncAlbumV1.md) @@ -467,9 +475,15 @@ Class | Method | HTTP request | Description - [SyncAssetExifV1](doc//SyncAssetExifV1.md) - [SyncAssetV1](doc//SyncAssetV1.md) - [SyncEntityType](doc//SyncEntityType.md) + - [SyncMemoryAssetDeleteV1](doc//SyncMemoryAssetDeleteV1.md) + - [SyncMemoryAssetV1](doc//SyncMemoryAssetV1.md) + - [SyncMemoryDeleteV1](doc//SyncMemoryDeleteV1.md) + - [SyncMemoryV1](doc//SyncMemoryV1.md) - [SyncPartnerDeleteV1](doc//SyncPartnerDeleteV1.md) - [SyncPartnerV1](doc//SyncPartnerV1.md) - [SyncRequestType](doc//SyncRequestType.md) + - [SyncStackDeleteV1](doc//SyncStackDeleteV1.md) + - [SyncStackV1](doc//SyncStackV1.md) - [SyncStreamDto](doc//SyncStreamDto.md) - [SyncUserDeleteV1](doc//SyncUserDeleteV1.md) - [SyncUserV1](doc//SyncUserV1.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 9bf4026320..3998961720 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -163,6 +163,7 @@ part 'model/memories_response.dart'; part 'model/memories_update.dart'; part 'model/memory_create_dto.dart'; part 'model/memory_response_dto.dart'; +part 'model/memory_statistics_response_dto.dart'; part 'model/memory_type.dart'; part 'model/memory_update_dto.dart'; part 'model/merge_person_dto.dart'; @@ -248,6 +249,8 @@ 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_to_asset_delete_v1.dart'; +part 'model/sync_album_to_asset_v1.dart'; part 'model/sync_album_user_delete_v1.dart'; part 'model/sync_album_user_v1.dart'; part 'model/sync_album_v1.dart'; @@ -255,9 +258,15 @@ part 'model/sync_asset_delete_v1.dart'; part 'model/sync_asset_exif_v1.dart'; part 'model/sync_asset_v1.dart'; part 'model/sync_entity_type.dart'; +part 'model/sync_memory_asset_delete_v1.dart'; +part 'model/sync_memory_asset_v1.dart'; +part 'model/sync_memory_delete_v1.dart'; +part 'model/sync_memory_v1.dart'; part 'model/sync_partner_delete_v1.dart'; part 'model/sync_partner_v1.dart'; part 'model/sync_request_type.dart'; +part 'model/sync_stack_delete_v1.dart'; +part 'model/sync_stack_v1.dart'; part 'model/sync_stream_dto.dart'; part 'model/sync_user_delete_v1.dart'; part 'model/sync_user_v1.dart'; diff --git a/mobile/openapi/lib/api/assets_api.dart b/mobile/openapi/lib/api/assets_api.dart index 06965e1f8b..db6a2e78a3 100644 --- a/mobile/openapi/lib/api/assets_api.dart +++ b/mobile/openapi/lib/api/assets_api.dart @@ -542,7 +542,9 @@ class AssetsApi { /// * [String] key: /// /// * [String] duration: - Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, }) async { + /// + /// * [String] filename: + Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/original' .replaceAll('{id}', id); @@ -587,6 +589,10 @@ class AssetsApi { hasFields = true; mp.fields[r'fileModifiedAt'] = parameterToString(fileModifiedAt); } + if (filename != null) { + hasFields = true; + mp.fields[r'filename'] = parameterToString(filename); + } if (hasFields) { postBody = mp; } @@ -623,8 +629,10 @@ class AssetsApi { /// * [String] key: /// /// * [String] duration: - Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, }) async { - final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, duration: duration, ); + /// + /// * [String] filename: + Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async { + final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, duration: duration, filename: filename, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -788,6 +796,8 @@ class AssetsApi { /// /// * [String] duration: /// + /// * [String] filename: + /// /// * [bool] isFavorite: /// /// * [String] livePhotoVideoId: @@ -795,7 +805,7 @@ class AssetsApi { /// * [MultipartFile] sidecarData: /// /// * [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 { + Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets'; @@ -843,6 +853,10 @@ class AssetsApi { hasFields = true; mp.fields[r'fileModifiedAt'] = parameterToString(fileModifiedAt); } + if (filename != null) { + hasFields = true; + mp.fields[r'filename'] = parameterToString(filename); + } if (isFavorite != null) { hasFields = true; mp.fields[r'isFavorite'] = parameterToString(isFavorite); @@ -894,6 +908,8 @@ class AssetsApi { /// /// * [String] duration: /// + /// * [String] filename: + /// /// * [bool] isFavorite: /// /// * [String] livePhotoVideoId: @@ -901,8 +917,8 @@ class AssetsApi { /// * [MultipartFile] 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, ); + Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, 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/duplicates_api.dart b/mobile/openapi/lib/api/duplicates_api.dart index 715c6d6112..d8b45d21a2 100644 --- a/mobile/openapi/lib/api/duplicates_api.dart +++ b/mobile/openapi/lib/api/duplicates_api.dart @@ -16,6 +16,85 @@ class DuplicatesApi { final ApiClient apiClient; + /// Performs an HTTP 'DELETE /duplicates/{id}' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + Future deleteDuplicateWithHttpInfo(String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/duplicates/{id}' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'DELETE', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + Future deleteDuplicate(String id,) async { + final response = await deleteDuplicateWithHttpInfo(id,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + + /// Performs an HTTP 'DELETE /duplicates' operation and returns the [Response]. + /// Parameters: + /// + /// * [BulkIdsDto] bulkIdsDto (required): + Future deleteDuplicatesWithHttpInfo(BulkIdsDto bulkIdsDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/duplicates'; + + // ignore: prefer_final_locals + Object? postBody = bulkIdsDto; + + 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: + /// + /// * [BulkIdsDto] bulkIdsDto (required): + Future deleteDuplicates(BulkIdsDto bulkIdsDto,) async { + final response = await deleteDuplicatesWithHttpInfo(bulkIdsDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + /// Performs an HTTP 'GET /duplicates' operation and returns the [Response]. Future getAssetDuplicatesWithHttpInfo() async { // ignore: prefer_const_declarations diff --git a/mobile/openapi/lib/api/memories_api.dart b/mobile/openapi/lib/api/memories_api.dart index 88897d3038..9b62cce9c0 100644 --- a/mobile/openapi/lib/api/memories_api.dart +++ b/mobile/openapi/lib/api/memories_api.dart @@ -206,6 +206,78 @@ class MemoriesApi { return null; } + /// Performs an HTTP 'GET /memories/statistics' operation and returns the [Response]. + /// Parameters: + /// + /// * [DateTime] for_: + /// + /// * [bool] isSaved: + /// + /// * [bool] isTrashed: + /// + /// * [MemoryType] type: + Future memoriesStatisticsWithHttpInfo({ DateTime? for_, bool? isSaved, bool? isTrashed, MemoryType? type, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/memories/statistics'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (for_ != null) { + queryParams.addAll(_queryParams('', 'for', for_)); + } + if (isSaved != null) { + queryParams.addAll(_queryParams('', 'isSaved', isSaved)); + } + if (isTrashed != null) { + queryParams.addAll(_queryParams('', 'isTrashed', isTrashed)); + } + if (type != null) { + queryParams.addAll(_queryParams('', 'type', type)); + } + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [DateTime] for_: + /// + /// * [bool] isSaved: + /// + /// * [bool] isTrashed: + /// + /// * [MemoryType] type: + Future memoriesStatistics({ DateTime? for_, bool? isSaved, bool? isTrashed, MemoryType? type, }) async { + final response = await memoriesStatisticsWithHttpInfo( for_: for_, isSaved: isSaved, isTrashed: isTrashed, type: type, ); + 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), 'MemoryStatisticsResponseDto',) as MemoryStatisticsResponseDto; + + } + return null; + } + /// Performs an HTTP 'DELETE /memories/{id}/assets' operation and returns the [Response]. /// Parameters: /// diff --git a/mobile/openapi/lib/api/people_api.dart b/mobile/openapi/lib/api/people_api.dart index 1cdb878852..35dbac4e97 100644 --- a/mobile/openapi/lib/api/people_api.dart +++ b/mobile/openapi/lib/api/people_api.dart @@ -63,6 +63,85 @@ class PeopleApi { return null; } + /// Performs an HTTP 'DELETE /people' operation and returns the [Response]. + /// Parameters: + /// + /// * [BulkIdsDto] bulkIdsDto (required): + Future deletePeopleWithHttpInfo(BulkIdsDto bulkIdsDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/people'; + + // ignore: prefer_final_locals + Object? postBody = bulkIdsDto; + + 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: + /// + /// * [BulkIdsDto] bulkIdsDto (required): + Future deletePeople(BulkIdsDto bulkIdsDto,) async { + final response = await deletePeopleWithHttpInfo(bulkIdsDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + + /// Performs an HTTP 'DELETE /people/{id}' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + Future deletePersonWithHttpInfo(String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/people/{id}' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'DELETE', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + Future deletePerson(String id,) async { + final response = await deletePersonWithHttpInfo(id,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + /// Performs an HTTP 'GET /people' operation and returns the [Response]. /// Parameters: /// diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index bc7f48255c..0edc2638bc 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -382,6 +382,8 @@ class ApiClient { return MemoryCreateDto.fromJson(value); case 'MemoryResponseDto': return MemoryResponseDto.fromJson(value); + case 'MemoryStatisticsResponseDto': + return MemoryStatisticsResponseDto.fromJson(value); case 'MemoryType': return MemoryTypeTypeTransformer().decode(value); case 'MemoryUpdateDto': @@ -552,6 +554,10 @@ class ApiClient { return SyncAckSetDto.fromJson(value); case 'SyncAlbumDeleteV1': return SyncAlbumDeleteV1.fromJson(value); + case 'SyncAlbumToAssetDeleteV1': + return SyncAlbumToAssetDeleteV1.fromJson(value); + case 'SyncAlbumToAssetV1': + return SyncAlbumToAssetV1.fromJson(value); case 'SyncAlbumUserDeleteV1': return SyncAlbumUserDeleteV1.fromJson(value); case 'SyncAlbumUserV1': @@ -566,12 +572,24 @@ class ApiClient { return SyncAssetV1.fromJson(value); case 'SyncEntityType': return SyncEntityTypeTypeTransformer().decode(value); + case 'SyncMemoryAssetDeleteV1': + return SyncMemoryAssetDeleteV1.fromJson(value); + case 'SyncMemoryAssetV1': + return SyncMemoryAssetV1.fromJson(value); + case 'SyncMemoryDeleteV1': + return SyncMemoryDeleteV1.fromJson(value); + case 'SyncMemoryV1': + return SyncMemoryV1.fromJson(value); case 'SyncPartnerDeleteV1': return SyncPartnerDeleteV1.fromJson(value); case 'SyncPartnerV1': return SyncPartnerV1.fromJson(value); case 'SyncRequestType': return SyncRequestTypeTypeTransformer().decode(value); + case 'SyncStackDeleteV1': + return SyncStackDeleteV1.fromJson(value); + case 'SyncStackV1': + return SyncStackV1.fromJson(value); case 'SyncStreamDto': return SyncStreamDto.fromJson(value); case 'SyncUserDeleteV1': diff --git a/mobile/openapi/lib/model/memory_statistics_response_dto.dart b/mobile/openapi/lib/model/memory_statistics_response_dto.dart new file mode 100644 index 0000000000..a9a10ad327 --- /dev/null +++ b/mobile/openapi/lib/model/memory_statistics_response_dto.dart @@ -0,0 +1,99 @@ +// +// 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 MemoryStatisticsResponseDto { + /// Returns a new [MemoryStatisticsResponseDto] instance. + MemoryStatisticsResponseDto({ + required this.total, + }); + + int total; + + @override + bool operator ==(Object other) => identical(this, other) || other is MemoryStatisticsResponseDto && + other.total == total; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (total.hashCode); + + @override + String toString() => 'MemoryStatisticsResponseDto[total=$total]'; + + Map toJson() { + final json = {}; + json[r'total'] = this.total; + return json; + } + + /// Returns a new [MemoryStatisticsResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static MemoryStatisticsResponseDto? fromJson(dynamic value) { + upgradeDto(value, "MemoryStatisticsResponseDto"); + if (value is Map) { + final json = value.cast(); + + return MemoryStatisticsResponseDto( + total: mapValueOfType(json, r'total')!, + ); + } + 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 = MemoryStatisticsResponseDto.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 = MemoryStatisticsResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of MemoryStatisticsResponseDto-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] = MemoryStatisticsResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'total', + }; +} + diff --git a/mobile/openapi/lib/model/metadata_search_dto.dart b/mobile/openapi/lib/model/metadata_search_dto.dart index 520777a45d..b7e637d4b4 100644 --- a/mobile/openapi/lib/model/metadata_search_dto.dart +++ b/mobile/openapi/lib/model/metadata_search_dto.dart @@ -241,7 +241,7 @@ class MetadataSearchDto { String? state; - List tagIds; + List? tagIds; /// /// Please note: This property should have been non-nullable! Since the specification file @@ -425,7 +425,7 @@ class MetadataSearchDto { (rating == null ? 0 : rating!.hashCode) + (size == null ? 0 : size!.hashCode) + (state == null ? 0 : state!.hashCode) + - (tagIds.hashCode) + + (tagIds == null ? 0 : tagIds!.hashCode) + (takenAfter == null ? 0 : takenAfter!.hashCode) + (takenBefore == null ? 0 : takenBefore!.hashCode) + (thumbnailPath == null ? 0 : thumbnailPath!.hashCode) + @@ -578,7 +578,11 @@ class MetadataSearchDto { } else { // json[r'state'] = null; } + if (this.tagIds != null) { json[r'tagIds'] = this.tagIds; + } else { + // json[r'tagIds'] = null; + } if (this.takenAfter != null) { json[r'takenAfter'] = this.takenAfter!.toUtc().toIso8601String(); } else { diff --git a/mobile/openapi/lib/model/random_search_dto.dart b/mobile/openapi/lib/model/random_search_dto.dart index c5914f9fa3..98cc715af4 100644 --- a/mobile/openapi/lib/model/random_search_dto.dart +++ b/mobile/openapi/lib/model/random_search_dto.dart @@ -155,7 +155,7 @@ class RandomSearchDto { String? state; - List tagIds; + List? tagIds; /// /// Please note: This property should have been non-nullable! Since the specification file @@ -310,7 +310,7 @@ class RandomSearchDto { (rating == null ? 0 : rating!.hashCode) + (size == null ? 0 : size!.hashCode) + (state == null ? 0 : state!.hashCode) + - (tagIds.hashCode) + + (tagIds == null ? 0 : tagIds!.hashCode) + (takenAfter == null ? 0 : takenAfter!.hashCode) + (takenBefore == null ? 0 : takenBefore!.hashCode) + (trashedAfter == null ? 0 : trashedAfter!.hashCode) + @@ -416,7 +416,11 @@ class RandomSearchDto { } else { // json[r'state'] = null; } + if (this.tagIds != null) { json[r'tagIds'] = this.tagIds; + } else { + // json[r'tagIds'] = null; + } if (this.takenAfter != null) { json[r'takenAfter'] = this.takenAfter!.toUtc().toIso8601String(); } else { diff --git a/mobile/openapi/lib/model/smart_search_dto.dart b/mobile/openapi/lib/model/smart_search_dto.dart index c221340553..0d16b56d74 100644 --- a/mobile/openapi/lib/model/smart_search_dto.dart +++ b/mobile/openapi/lib/model/smart_search_dto.dart @@ -175,7 +175,7 @@ class SmartSearchDto { String? state; - List tagIds; + List? tagIds; /// /// Please note: This property should have been non-nullable! Since the specification file @@ -318,7 +318,7 @@ class SmartSearchDto { (rating == null ? 0 : rating!.hashCode) + (size == null ? 0 : size!.hashCode) + (state == null ? 0 : state!.hashCode) + - (tagIds.hashCode) + + (tagIds == null ? 0 : tagIds!.hashCode) + (takenAfter == null ? 0 : takenAfter!.hashCode) + (takenBefore == null ? 0 : takenBefore!.hashCode) + (trashedAfter == null ? 0 : trashedAfter!.hashCode) + @@ -433,7 +433,11 @@ class SmartSearchDto { } else { // json[r'state'] = null; } + if (this.tagIds != null) { json[r'tagIds'] = this.tagIds; + } else { + // json[r'tagIds'] = null; + } if (this.takenAfter != null) { json[r'takenAfter'] = this.takenAfter!.toUtc().toIso8601String(); } else { diff --git a/mobile/openapi/lib/model/statistics_search_dto.dart b/mobile/openapi/lib/model/statistics_search_dto.dart index 55de23ba32..73d80c9e36 100644 --- a/mobile/openapi/lib/model/statistics_search_dto.dart +++ b/mobile/openapi/lib/model/statistics_search_dto.dart @@ -149,7 +149,7 @@ class StatisticsSearchDto { String? state; - List tagIds; + List? tagIds; /// /// Please note: This property should have been non-nullable! Since the specification file @@ -268,7 +268,7 @@ class StatisticsSearchDto { (personIds.hashCode) + (rating == null ? 0 : rating!.hashCode) + (state == null ? 0 : state!.hashCode) + - (tagIds.hashCode) + + (tagIds == null ? 0 : tagIds!.hashCode) + (takenAfter == null ? 0 : takenAfter!.hashCode) + (takenBefore == null ? 0 : takenBefore!.hashCode) + (trashedAfter == null ? 0 : trashedAfter!.hashCode) + @@ -370,7 +370,11 @@ class StatisticsSearchDto { } else { // json[r'state'] = null; } + if (this.tagIds != null) { json[r'tagIds'] = this.tagIds; + } else { + // json[r'tagIds'] = null; + } if (this.takenAfter != null) { json[r'takenAfter'] = this.takenAfter!.toUtc().toIso8601String(); } else { diff --git a/mobile/openapi/lib/model/sync_album_to_asset_delete_v1.dart b/mobile/openapi/lib/model/sync_album_to_asset_delete_v1.dart new file mode 100644 index 0000000000..d18c850b2a --- /dev/null +++ b/mobile/openapi/lib/model/sync_album_to_asset_delete_v1.dart @@ -0,0 +1,107 @@ +// +// 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 SyncAlbumToAssetDeleteV1 { + /// Returns a new [SyncAlbumToAssetDeleteV1] instance. + SyncAlbumToAssetDeleteV1({ + required this.albumId, + required this.assetId, + }); + + String albumId; + + String assetId; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAlbumToAssetDeleteV1 && + other.albumId == albumId && + other.assetId == assetId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (albumId.hashCode) + + (assetId.hashCode); + + @override + String toString() => 'SyncAlbumToAssetDeleteV1[albumId=$albumId, assetId=$assetId]'; + + Map toJson() { + final json = {}; + json[r'albumId'] = this.albumId; + json[r'assetId'] = this.assetId; + return json; + } + + /// Returns a new [SyncAlbumToAssetDeleteV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAlbumToAssetDeleteV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAlbumToAssetDeleteV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAlbumToAssetDeleteV1( + albumId: mapValueOfType(json, r'albumId')!, + assetId: mapValueOfType(json, r'assetId')!, + ); + } + 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 = SyncAlbumToAssetDeleteV1.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 = SyncAlbumToAssetDeleteV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAlbumToAssetDeleteV1-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] = SyncAlbumToAssetDeleteV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'albumId', + 'assetId', + }; +} + diff --git a/mobile/openapi/lib/model/sync_album_to_asset_v1.dart b/mobile/openapi/lib/model/sync_album_to_asset_v1.dart new file mode 100644 index 0000000000..6908f320f8 --- /dev/null +++ b/mobile/openapi/lib/model/sync_album_to_asset_v1.dart @@ -0,0 +1,107 @@ +// +// 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 SyncAlbumToAssetV1 { + /// Returns a new [SyncAlbumToAssetV1] instance. + SyncAlbumToAssetV1({ + required this.albumId, + required this.assetId, + }); + + String albumId; + + String assetId; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAlbumToAssetV1 && + other.albumId == albumId && + other.assetId == assetId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (albumId.hashCode) + + (assetId.hashCode); + + @override + String toString() => 'SyncAlbumToAssetV1[albumId=$albumId, assetId=$assetId]'; + + Map toJson() { + final json = {}; + json[r'albumId'] = this.albumId; + json[r'assetId'] = this.assetId; + return json; + } + + /// Returns a new [SyncAlbumToAssetV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAlbumToAssetV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAlbumToAssetV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAlbumToAssetV1( + albumId: mapValueOfType(json, r'albumId')!, + assetId: mapValueOfType(json, r'assetId')!, + ); + } + 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 = SyncAlbumToAssetV1.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 = SyncAlbumToAssetV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAlbumToAssetV1-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] = SyncAlbumToAssetV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'albumId', + 'assetId', + }; +} + diff --git a/mobile/openapi/lib/model/sync_asset_exif_v1.dart b/mobile/openapi/lib/model/sync_asset_exif_v1.dart index b0fef28b76..d4fdc9249d 100644 --- a/mobile/openapi/lib/model/sync_asset_exif_v1.dart +++ b/mobile/openapi/lib/model/sync_asset_exif_v1.dart @@ -56,21 +56,21 @@ class SyncAssetExifV1 { String? exposureTime; - int? fNumber; + double? fNumber; int? fileSizeInByte; - int? focalLength; + double? focalLength; - int? fps; + double? fps; int? iso; - int? latitude; + double? latitude; String? lensModel; - int? longitude; + double? longitude; String? make; @@ -293,14 +293,14 @@ class SyncAssetExifV1 { exifImageHeight: mapValueOfType(json, r'exifImageHeight'), exifImageWidth: mapValueOfType(json, r'exifImageWidth'), exposureTime: mapValueOfType(json, r'exposureTime'), - fNumber: mapValueOfType(json, r'fNumber'), + fNumber: (mapValueOfType(json, r'fNumber'))?.toDouble(), fileSizeInByte: mapValueOfType(json, r'fileSizeInByte'), - focalLength: mapValueOfType(json, r'focalLength'), - fps: mapValueOfType(json, r'fps'), + focalLength: (mapValueOfType(json, r'focalLength'))?.toDouble(), + fps: (mapValueOfType(json, r'fps'))?.toDouble(), iso: mapValueOfType(json, r'iso'), - latitude: mapValueOfType(json, r'latitude'), + latitude: (mapValueOfType(json, r'latitude'))?.toDouble(), lensModel: mapValueOfType(json, r'lensModel'), - longitude: mapValueOfType(json, r'longitude'), + longitude: (mapValueOfType(json, r'longitude'))?.toDouble(), make: mapValueOfType(json, r'make'), model: mapValueOfType(json, r'model'), modifyDate: mapDateTime(json, r'modifyDate', r''), diff --git a/mobile/openapi/lib/model/sync_asset_v1.dart b/mobile/openapi/lib/model/sync_asset_v1.dart index 27042325ee..1ca6e20cff 100644 --- a/mobile/openapi/lib/model/sync_asset_v1.dart +++ b/mobile/openapi/lib/model/sync_asset_v1.dart @@ -15,6 +15,7 @@ class SyncAssetV1 { SyncAssetV1({ required this.checksum, required this.deletedAt, + required this.duration, required this.fileCreatedAt, required this.fileModifiedAt, required this.id, @@ -31,6 +32,8 @@ class SyncAssetV1 { DateTime? deletedAt; + String? duration; + DateTime? fileCreatedAt; DateTime? fileModifiedAt; @@ -55,6 +58,7 @@ class SyncAssetV1 { bool operator ==(Object other) => identical(this, other) || other is SyncAssetV1 && other.checksum == checksum && other.deletedAt == deletedAt && + other.duration == duration && other.fileCreatedAt == fileCreatedAt && other.fileModifiedAt == fileModifiedAt && other.id == id && @@ -71,6 +75,7 @@ class SyncAssetV1 { // ignore: unnecessary_parenthesis (checksum.hashCode) + (deletedAt == null ? 0 : deletedAt!.hashCode) + + (duration == null ? 0 : duration!.hashCode) + (fileCreatedAt == null ? 0 : fileCreatedAt!.hashCode) + (fileModifiedAt == null ? 0 : fileModifiedAt!.hashCode) + (id.hashCode) + @@ -83,7 +88,7 @@ class SyncAssetV1 { (visibility.hashCode); @override - String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, localDateTime=$localDateTime, originalFileName=$originalFileName, ownerId=$ownerId, thumbhash=$thumbhash, type=$type, visibility=$visibility]'; + String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, duration=$duration, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, localDateTime=$localDateTime, originalFileName=$originalFileName, ownerId=$ownerId, thumbhash=$thumbhash, type=$type, visibility=$visibility]'; Map toJson() { final json = {}; @@ -93,6 +98,11 @@ class SyncAssetV1 { } else { // json[r'deletedAt'] = null; } + if (this.duration != null) { + json[r'duration'] = this.duration; + } else { + // json[r'duration'] = null; + } if (this.fileCreatedAt != null) { json[r'fileCreatedAt'] = this.fileCreatedAt!.toUtc().toIso8601String(); } else { @@ -133,6 +143,7 @@ class SyncAssetV1 { return SyncAssetV1( checksum: mapValueOfType(json, r'checksum')!, deletedAt: mapDateTime(json, r'deletedAt', r''), + duration: mapValueOfType(json, r'duration'), fileCreatedAt: mapDateTime(json, r'fileCreatedAt', r''), fileModifiedAt: mapDateTime(json, r'fileModifiedAt', r''), id: mapValueOfType(json, r'id')!, @@ -192,6 +203,7 @@ class SyncAssetV1 { static const requiredKeys = { 'checksum', 'deletedAt', + 'duration', 'fileCreatedAt', 'fileModifiedAt', 'id', diff --git a/mobile/openapi/lib/model/sync_entity_type.dart b/mobile/openapi/lib/model/sync_entity_type.dart index 600371545a..ecaadc9c31 100644 --- a/mobile/openapi/lib/model/sync_entity_type.dart +++ b/mobile/openapi/lib/model/sync_entity_type.dart @@ -25,35 +25,75 @@ class SyncEntityType { static const userV1 = SyncEntityType._(r'UserV1'); static const userDeleteV1 = SyncEntityType._(r'UserDeleteV1'); - static const partnerV1 = SyncEntityType._(r'PartnerV1'); - static const partnerDeleteV1 = SyncEntityType._(r'PartnerDeleteV1'); static const assetV1 = SyncEntityType._(r'AssetV1'); static const assetDeleteV1 = SyncEntityType._(r'AssetDeleteV1'); static const assetExifV1 = SyncEntityType._(r'AssetExifV1'); + static const partnerV1 = SyncEntityType._(r'PartnerV1'); + static const partnerDeleteV1 = SyncEntityType._(r'PartnerDeleteV1'); static const partnerAssetV1 = SyncEntityType._(r'PartnerAssetV1'); + static const partnerAssetBackfillV1 = SyncEntityType._(r'PartnerAssetBackfillV1'); static const partnerAssetDeleteV1 = SyncEntityType._(r'PartnerAssetDeleteV1'); static const partnerAssetExifV1 = SyncEntityType._(r'PartnerAssetExifV1'); + static const partnerAssetExifBackfillV1 = SyncEntityType._(r'PartnerAssetExifBackfillV1'); + static const partnerStackBackfillV1 = SyncEntityType._(r'PartnerStackBackfillV1'); + static const partnerStackDeleteV1 = SyncEntityType._(r'PartnerStackDeleteV1'); + static const partnerStackV1 = SyncEntityType._(r'PartnerStackV1'); static const albumV1 = SyncEntityType._(r'AlbumV1'); static const albumDeleteV1 = SyncEntityType._(r'AlbumDeleteV1'); static const albumUserV1 = SyncEntityType._(r'AlbumUserV1'); + static const albumUserBackfillV1 = SyncEntityType._(r'AlbumUserBackfillV1'); static const albumUserDeleteV1 = SyncEntityType._(r'AlbumUserDeleteV1'); + static const albumAssetV1 = SyncEntityType._(r'AlbumAssetV1'); + static const albumAssetBackfillV1 = SyncEntityType._(r'AlbumAssetBackfillV1'); + static const albumAssetExifV1 = SyncEntityType._(r'AlbumAssetExifV1'); + static const albumAssetExifBackfillV1 = SyncEntityType._(r'AlbumAssetExifBackfillV1'); + static const albumToAssetV1 = SyncEntityType._(r'AlbumToAssetV1'); + static const albumToAssetDeleteV1 = SyncEntityType._(r'AlbumToAssetDeleteV1'); + static const albumToAssetBackfillV1 = SyncEntityType._(r'AlbumToAssetBackfillV1'); + static const memoryV1 = SyncEntityType._(r'MemoryV1'); + static const memoryDeleteV1 = SyncEntityType._(r'MemoryDeleteV1'); + static const memoryToAssetV1 = SyncEntityType._(r'MemoryToAssetV1'); + static const memoryToAssetDeleteV1 = SyncEntityType._(r'MemoryToAssetDeleteV1'); + static const stackV1 = SyncEntityType._(r'StackV1'); + static const stackDeleteV1 = SyncEntityType._(r'StackDeleteV1'); + static const syncAckV1 = SyncEntityType._(r'SyncAckV1'); /// List of all possible values in this [enum][SyncEntityType]. static const values = [ userV1, userDeleteV1, - partnerV1, - partnerDeleteV1, assetV1, assetDeleteV1, assetExifV1, + partnerV1, + partnerDeleteV1, partnerAssetV1, + partnerAssetBackfillV1, partnerAssetDeleteV1, partnerAssetExifV1, + partnerAssetExifBackfillV1, + partnerStackBackfillV1, + partnerStackDeleteV1, + partnerStackV1, albumV1, albumDeleteV1, albumUserV1, + albumUserBackfillV1, albumUserDeleteV1, + albumAssetV1, + albumAssetBackfillV1, + albumAssetExifV1, + albumAssetExifBackfillV1, + albumToAssetV1, + albumToAssetDeleteV1, + albumToAssetBackfillV1, + memoryV1, + memoryDeleteV1, + memoryToAssetV1, + memoryToAssetDeleteV1, + stackV1, + stackDeleteV1, + syncAckV1, ]; static SyncEntityType? fromJson(dynamic value) => SyncEntityTypeTypeTransformer().decode(value); @@ -94,18 +134,38 @@ class SyncEntityTypeTypeTransformer { switch (data) { case r'UserV1': return SyncEntityType.userV1; case r'UserDeleteV1': return SyncEntityType.userDeleteV1; - case r'PartnerV1': return SyncEntityType.partnerV1; - case r'PartnerDeleteV1': return SyncEntityType.partnerDeleteV1; case r'AssetV1': return SyncEntityType.assetV1; case r'AssetDeleteV1': return SyncEntityType.assetDeleteV1; case r'AssetExifV1': return SyncEntityType.assetExifV1; + case r'PartnerV1': return SyncEntityType.partnerV1; + case r'PartnerDeleteV1': return SyncEntityType.partnerDeleteV1; case r'PartnerAssetV1': return SyncEntityType.partnerAssetV1; + case r'PartnerAssetBackfillV1': return SyncEntityType.partnerAssetBackfillV1; case r'PartnerAssetDeleteV1': return SyncEntityType.partnerAssetDeleteV1; case r'PartnerAssetExifV1': return SyncEntityType.partnerAssetExifV1; + case r'PartnerAssetExifBackfillV1': return SyncEntityType.partnerAssetExifBackfillV1; + case r'PartnerStackBackfillV1': return SyncEntityType.partnerStackBackfillV1; + case r'PartnerStackDeleteV1': return SyncEntityType.partnerStackDeleteV1; + case r'PartnerStackV1': return SyncEntityType.partnerStackV1; case r'AlbumV1': return SyncEntityType.albumV1; case r'AlbumDeleteV1': return SyncEntityType.albumDeleteV1; case r'AlbumUserV1': return SyncEntityType.albumUserV1; + case r'AlbumUserBackfillV1': return SyncEntityType.albumUserBackfillV1; case r'AlbumUserDeleteV1': return SyncEntityType.albumUserDeleteV1; + case r'AlbumAssetV1': return SyncEntityType.albumAssetV1; + case r'AlbumAssetBackfillV1': return SyncEntityType.albumAssetBackfillV1; + case r'AlbumAssetExifV1': return SyncEntityType.albumAssetExifV1; + case r'AlbumAssetExifBackfillV1': return SyncEntityType.albumAssetExifBackfillV1; + case r'AlbumToAssetV1': return SyncEntityType.albumToAssetV1; + case r'AlbumToAssetDeleteV1': return SyncEntityType.albumToAssetDeleteV1; + case r'AlbumToAssetBackfillV1': return SyncEntityType.albumToAssetBackfillV1; + case r'MemoryV1': return SyncEntityType.memoryV1; + case r'MemoryDeleteV1': return SyncEntityType.memoryDeleteV1; + case r'MemoryToAssetV1': return SyncEntityType.memoryToAssetV1; + case r'MemoryToAssetDeleteV1': return SyncEntityType.memoryToAssetDeleteV1; + case r'StackV1': return SyncEntityType.stackV1; + case r'StackDeleteV1': return SyncEntityType.stackDeleteV1; + case r'SyncAckV1': return SyncEntityType.syncAckV1; default: if (!allowNull) { throw ArgumentError('Unknown enum value to decode: $data'); diff --git a/mobile/openapi/lib/model/sync_memory_asset_delete_v1.dart b/mobile/openapi/lib/model/sync_memory_asset_delete_v1.dart new file mode 100644 index 0000000000..a9af77e929 --- /dev/null +++ b/mobile/openapi/lib/model/sync_memory_asset_delete_v1.dart @@ -0,0 +1,107 @@ +// +// 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 SyncMemoryAssetDeleteV1 { + /// Returns a new [SyncMemoryAssetDeleteV1] instance. + SyncMemoryAssetDeleteV1({ + required this.assetId, + required this.memoryId, + }); + + String assetId; + + String memoryId; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncMemoryAssetDeleteV1 && + other.assetId == assetId && + other.memoryId == memoryId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetId.hashCode) + + (memoryId.hashCode); + + @override + String toString() => 'SyncMemoryAssetDeleteV1[assetId=$assetId, memoryId=$memoryId]'; + + Map toJson() { + final json = {}; + json[r'assetId'] = this.assetId; + json[r'memoryId'] = this.memoryId; + return json; + } + + /// Returns a new [SyncMemoryAssetDeleteV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncMemoryAssetDeleteV1? fromJson(dynamic value) { + upgradeDto(value, "SyncMemoryAssetDeleteV1"); + if (value is Map) { + final json = value.cast(); + + return SyncMemoryAssetDeleteV1( + assetId: mapValueOfType(json, r'assetId')!, + memoryId: mapValueOfType(json, r'memoryId')!, + ); + } + 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 = SyncMemoryAssetDeleteV1.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 = SyncMemoryAssetDeleteV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncMemoryAssetDeleteV1-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] = SyncMemoryAssetDeleteV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetId', + 'memoryId', + }; +} + diff --git a/mobile/openapi/lib/model/sync_memory_asset_v1.dart b/mobile/openapi/lib/model/sync_memory_asset_v1.dart new file mode 100644 index 0000000000..d26e3c9a29 --- /dev/null +++ b/mobile/openapi/lib/model/sync_memory_asset_v1.dart @@ -0,0 +1,107 @@ +// +// 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 SyncMemoryAssetV1 { + /// Returns a new [SyncMemoryAssetV1] instance. + SyncMemoryAssetV1({ + required this.assetId, + required this.memoryId, + }); + + String assetId; + + String memoryId; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncMemoryAssetV1 && + other.assetId == assetId && + other.memoryId == memoryId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetId.hashCode) + + (memoryId.hashCode); + + @override + String toString() => 'SyncMemoryAssetV1[assetId=$assetId, memoryId=$memoryId]'; + + Map toJson() { + final json = {}; + json[r'assetId'] = this.assetId; + json[r'memoryId'] = this.memoryId; + return json; + } + + /// Returns a new [SyncMemoryAssetV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncMemoryAssetV1? fromJson(dynamic value) { + upgradeDto(value, "SyncMemoryAssetV1"); + if (value is Map) { + final json = value.cast(); + + return SyncMemoryAssetV1( + assetId: mapValueOfType(json, r'assetId')!, + memoryId: mapValueOfType(json, r'memoryId')!, + ); + } + 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 = SyncMemoryAssetV1.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 = SyncMemoryAssetV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncMemoryAssetV1-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] = SyncMemoryAssetV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetId', + 'memoryId', + }; +} + diff --git a/mobile/openapi/lib/model/sync_memory_delete_v1.dart b/mobile/openapi/lib/model/sync_memory_delete_v1.dart new file mode 100644 index 0000000000..9702da5aaf --- /dev/null +++ b/mobile/openapi/lib/model/sync_memory_delete_v1.dart @@ -0,0 +1,99 @@ +// +// 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 SyncMemoryDeleteV1 { + /// Returns a new [SyncMemoryDeleteV1] instance. + SyncMemoryDeleteV1({ + required this.memoryId, + }); + + String memoryId; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncMemoryDeleteV1 && + other.memoryId == memoryId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (memoryId.hashCode); + + @override + String toString() => 'SyncMemoryDeleteV1[memoryId=$memoryId]'; + + Map toJson() { + final json = {}; + json[r'memoryId'] = this.memoryId; + return json; + } + + /// Returns a new [SyncMemoryDeleteV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncMemoryDeleteV1? fromJson(dynamic value) { + upgradeDto(value, "SyncMemoryDeleteV1"); + if (value is Map) { + final json = value.cast(); + + return SyncMemoryDeleteV1( + memoryId: mapValueOfType(json, r'memoryId')!, + ); + } + 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 = SyncMemoryDeleteV1.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 = SyncMemoryDeleteV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncMemoryDeleteV1-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] = SyncMemoryDeleteV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'memoryId', + }; +} + diff --git a/mobile/openapi/lib/model/sync_memory_v1.dart b/mobile/openapi/lib/model/sync_memory_v1.dart new file mode 100644 index 0000000000..2ae2b01fd7 --- /dev/null +++ b/mobile/openapi/lib/model/sync_memory_v1.dart @@ -0,0 +1,203 @@ +// +// 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 SyncMemoryV1 { + /// Returns a new [SyncMemoryV1] instance. + SyncMemoryV1({ + required this.createdAt, + required this.data, + required this.deletedAt, + required this.hideAt, + required this.id, + required this.isSaved, + required this.memoryAt, + required this.ownerId, + required this.seenAt, + required this.showAt, + required this.type, + required this.updatedAt, + }); + + DateTime createdAt; + + Object data; + + DateTime? deletedAt; + + DateTime? hideAt; + + String id; + + bool isSaved; + + DateTime memoryAt; + + String ownerId; + + DateTime? seenAt; + + DateTime? showAt; + + MemoryType type; + + DateTime updatedAt; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncMemoryV1 && + other.createdAt == createdAt && + other.data == data && + other.deletedAt == deletedAt && + other.hideAt == hideAt && + other.id == id && + other.isSaved == isSaved && + other.memoryAt == memoryAt && + other.ownerId == ownerId && + other.seenAt == seenAt && + other.showAt == showAt && + other.type == type && + other.updatedAt == updatedAt; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (createdAt.hashCode) + + (data.hashCode) + + (deletedAt == null ? 0 : deletedAt!.hashCode) + + (hideAt == null ? 0 : hideAt!.hashCode) + + (id.hashCode) + + (isSaved.hashCode) + + (memoryAt.hashCode) + + (ownerId.hashCode) + + (seenAt == null ? 0 : seenAt!.hashCode) + + (showAt == null ? 0 : showAt!.hashCode) + + (type.hashCode) + + (updatedAt.hashCode); + + @override + String toString() => 'SyncMemoryV1[createdAt=$createdAt, data=$data, deletedAt=$deletedAt, hideAt=$hideAt, id=$id, isSaved=$isSaved, memoryAt=$memoryAt, ownerId=$ownerId, seenAt=$seenAt, showAt=$showAt, type=$type, updatedAt=$updatedAt]'; + + Map toJson() { + final json = {}; + json[r'createdAt'] = this.createdAt.toUtc().toIso8601String(); + json[r'data'] = this.data; + if (this.deletedAt != null) { + json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String(); + } else { + // json[r'deletedAt'] = null; + } + if (this.hideAt != null) { + json[r'hideAt'] = this.hideAt!.toUtc().toIso8601String(); + } else { + // json[r'hideAt'] = null; + } + json[r'id'] = this.id; + json[r'isSaved'] = this.isSaved; + json[r'memoryAt'] = this.memoryAt.toUtc().toIso8601String(); + json[r'ownerId'] = this.ownerId; + if (this.seenAt != null) { + json[r'seenAt'] = this.seenAt!.toUtc().toIso8601String(); + } else { + // json[r'seenAt'] = null; + } + if (this.showAt != null) { + json[r'showAt'] = this.showAt!.toUtc().toIso8601String(); + } else { + // json[r'showAt'] = null; + } + json[r'type'] = this.type; + json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String(); + return json; + } + + /// Returns a new [SyncMemoryV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncMemoryV1? fromJson(dynamic value) { + upgradeDto(value, "SyncMemoryV1"); + if (value is Map) { + final json = value.cast(); + + return SyncMemoryV1( + createdAt: mapDateTime(json, r'createdAt', r'')!, + data: mapValueOfType(json, r'data')!, + deletedAt: mapDateTime(json, r'deletedAt', r''), + hideAt: mapDateTime(json, r'hideAt', r''), + id: mapValueOfType(json, r'id')!, + isSaved: mapValueOfType(json, r'isSaved')!, + memoryAt: mapDateTime(json, r'memoryAt', r'')!, + ownerId: mapValueOfType(json, r'ownerId')!, + seenAt: mapDateTime(json, r'seenAt', r''), + showAt: mapDateTime(json, r'showAt', r''), + type: MemoryType.fromJson(json[r'type'])!, + 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 = SyncMemoryV1.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 = SyncMemoryV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncMemoryV1-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] = SyncMemoryV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'createdAt', + 'data', + 'deletedAt', + 'hideAt', + 'id', + 'isSaved', + 'memoryAt', + 'ownerId', + 'seenAt', + 'showAt', + 'type', + 'updatedAt', + }; +} + diff --git a/mobile/openapi/lib/model/sync_request_type.dart b/mobile/openapi/lib/model/sync_request_type.dart index c149c329de..c13d791d84 100644 --- a/mobile/openapi/lib/model/sync_request_type.dart +++ b/mobile/openapi/lib/model/sync_request_type.dart @@ -23,25 +23,39 @@ class SyncRequestType { String toJson() => value; - static const usersV1 = SyncRequestType._(r'UsersV1'); - static const partnersV1 = SyncRequestType._(r'PartnersV1'); - static const assetsV1 = SyncRequestType._(r'AssetsV1'); - 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'); + static const albumToAssetsV1 = SyncRequestType._(r'AlbumToAssetsV1'); + static const albumAssetsV1 = SyncRequestType._(r'AlbumAssetsV1'); + static const albumAssetExifsV1 = SyncRequestType._(r'AlbumAssetExifsV1'); + static const assetsV1 = SyncRequestType._(r'AssetsV1'); + static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1'); + static const memoriesV1 = SyncRequestType._(r'MemoriesV1'); + static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1'); + static const partnersV1 = SyncRequestType._(r'PartnersV1'); + static const partnerAssetsV1 = SyncRequestType._(r'PartnerAssetsV1'); + static const partnerAssetExifsV1 = SyncRequestType._(r'PartnerAssetExifsV1'); + static const partnerStacksV1 = SyncRequestType._(r'PartnerStacksV1'); + static const stacksV1 = SyncRequestType._(r'StacksV1'); + static const usersV1 = SyncRequestType._(r'UsersV1'); /// List of all possible values in this [enum][SyncRequestType]. static const values = [ - usersV1, - partnersV1, - assetsV1, - assetExifsV1, - partnerAssetsV1, - partnerAssetExifsV1, albumsV1, albumUsersV1, + albumToAssetsV1, + albumAssetsV1, + albumAssetExifsV1, + assetsV1, + assetExifsV1, + memoriesV1, + memoryToAssetsV1, + partnersV1, + partnerAssetsV1, + partnerAssetExifsV1, + partnerStacksV1, + stacksV1, + usersV1, ]; static SyncRequestType? fromJson(dynamic value) => SyncRequestTypeTypeTransformer().decode(value); @@ -80,14 +94,21 @@ class SyncRequestTypeTypeTransformer { SyncRequestType? decode(dynamic data, {bool allowNull = true}) { if (data != null) { switch (data) { - case r'UsersV1': return SyncRequestType.usersV1; - case r'PartnersV1': return SyncRequestType.partnersV1; - case r'AssetsV1': return SyncRequestType.assetsV1; - 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; + case r'AlbumToAssetsV1': return SyncRequestType.albumToAssetsV1; + case r'AlbumAssetsV1': return SyncRequestType.albumAssetsV1; + case r'AlbumAssetExifsV1': return SyncRequestType.albumAssetExifsV1; + case r'AssetsV1': return SyncRequestType.assetsV1; + case r'AssetExifsV1': return SyncRequestType.assetExifsV1; + case r'MemoriesV1': return SyncRequestType.memoriesV1; + case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1; + case r'PartnersV1': return SyncRequestType.partnersV1; + case r'PartnerAssetsV1': return SyncRequestType.partnerAssetsV1; + case r'PartnerAssetExifsV1': return SyncRequestType.partnerAssetExifsV1; + case r'PartnerStacksV1': return SyncRequestType.partnerStacksV1; + case r'StacksV1': return SyncRequestType.stacksV1; + case r'UsersV1': return SyncRequestType.usersV1; default: if (!allowNull) { throw ArgumentError('Unknown enum value to decode: $data'); diff --git a/mobile/openapi/lib/model/sync_stack_delete_v1.dart b/mobile/openapi/lib/model/sync_stack_delete_v1.dart new file mode 100644 index 0000000000..22c6d99a52 --- /dev/null +++ b/mobile/openapi/lib/model/sync_stack_delete_v1.dart @@ -0,0 +1,99 @@ +// +// 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 SyncStackDeleteV1 { + /// Returns a new [SyncStackDeleteV1] instance. + SyncStackDeleteV1({ + required this.stackId, + }); + + String stackId; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncStackDeleteV1 && + other.stackId == stackId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (stackId.hashCode); + + @override + String toString() => 'SyncStackDeleteV1[stackId=$stackId]'; + + Map toJson() { + final json = {}; + json[r'stackId'] = this.stackId; + return json; + } + + /// Returns a new [SyncStackDeleteV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncStackDeleteV1? fromJson(dynamic value) { + upgradeDto(value, "SyncStackDeleteV1"); + if (value is Map) { + final json = value.cast(); + + return SyncStackDeleteV1( + stackId: mapValueOfType(json, r'stackId')!, + ); + } + 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 = SyncStackDeleteV1.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 = SyncStackDeleteV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncStackDeleteV1-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] = SyncStackDeleteV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'stackId', + }; +} + diff --git a/mobile/openapi/lib/model/sync_stack_v1.dart b/mobile/openapi/lib/model/sync_stack_v1.dart new file mode 100644 index 0000000000..c65affe8c0 --- /dev/null +++ b/mobile/openapi/lib/model/sync_stack_v1.dart @@ -0,0 +1,131 @@ +// +// 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 SyncStackV1 { + /// Returns a new [SyncStackV1] instance. + SyncStackV1({ + required this.createdAt, + required this.id, + required this.ownerId, + required this.primaryAssetId, + required this.updatedAt, + }); + + DateTime createdAt; + + String id; + + String ownerId; + + String primaryAssetId; + + DateTime updatedAt; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncStackV1 && + other.createdAt == createdAt && + other.id == id && + other.ownerId == ownerId && + other.primaryAssetId == primaryAssetId && + other.updatedAt == updatedAt; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (createdAt.hashCode) + + (id.hashCode) + + (ownerId.hashCode) + + (primaryAssetId.hashCode) + + (updatedAt.hashCode); + + @override + String toString() => 'SyncStackV1[createdAt=$createdAt, id=$id, ownerId=$ownerId, primaryAssetId=$primaryAssetId, updatedAt=$updatedAt]'; + + Map toJson() { + final json = {}; + json[r'createdAt'] = this.createdAt.toUtc().toIso8601String(); + json[r'id'] = this.id; + json[r'ownerId'] = this.ownerId; + json[r'primaryAssetId'] = this.primaryAssetId; + json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String(); + return json; + } + + /// Returns a new [SyncStackV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncStackV1? fromJson(dynamic value) { + upgradeDto(value, "SyncStackV1"); + if (value is Map) { + final json = value.cast(); + + return SyncStackV1( + createdAt: mapDateTime(json, r'createdAt', r'')!, + id: mapValueOfType(json, r'id')!, + ownerId: mapValueOfType(json, r'ownerId')!, + primaryAssetId: mapValueOfType(json, r'primaryAssetId')!, + 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 = SyncStackV1.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 = SyncStackV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncStackV1-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] = SyncStackV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'createdAt', + 'id', + 'ownerId', + 'primaryAssetId', + 'updatedAt', + }; +} + 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 24384a47b1..c8f91be1f1 100644 --- a/mobile/openapi/lib/model/system_config_o_auth_dto.dart +++ b/mobile/openapi/lib/model/system_config_o_auth_dto.dart @@ -24,6 +24,7 @@ class SystemConfigOAuthDto { required this.mobileOverrideEnabled, required this.mobileRedirectUri, required this.profileSigningAlgorithm, + required this.roleClaim, required this.scope, required this.signingAlgorithm, required this.storageLabelClaim, @@ -43,7 +44,7 @@ class SystemConfigOAuthDto { String clientSecret; /// Minimum value: 0 - num defaultStorageQuota; + int? defaultStorageQuota; bool enabled; @@ -55,6 +56,8 @@ class SystemConfigOAuthDto { String profileSigningAlgorithm; + String roleClaim; + String scope; String signingAlgorithm; @@ -81,6 +84,7 @@ class SystemConfigOAuthDto { other.mobileOverrideEnabled == mobileOverrideEnabled && other.mobileRedirectUri == mobileRedirectUri && other.profileSigningAlgorithm == profileSigningAlgorithm && + other.roleClaim == roleClaim && other.scope == scope && other.signingAlgorithm == signingAlgorithm && other.storageLabelClaim == storageLabelClaim && @@ -96,12 +100,13 @@ class SystemConfigOAuthDto { (buttonText.hashCode) + (clientId.hashCode) + (clientSecret.hashCode) + - (defaultStorageQuota.hashCode) + + (defaultStorageQuota == null ? 0 : defaultStorageQuota!.hashCode) + (enabled.hashCode) + (issuerUrl.hashCode) + (mobileOverrideEnabled.hashCode) + (mobileRedirectUri.hashCode) + (profileSigningAlgorithm.hashCode) + + (roleClaim.hashCode) + (scope.hashCode) + (signingAlgorithm.hashCode) + (storageLabelClaim.hashCode) + @@ -110,7 +115,7 @@ class SystemConfigOAuthDto { (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, timeout=$timeout, tokenEndpointAuthMethod=$tokenEndpointAuthMethod]'; + 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, roleClaim=$roleClaim, scope=$scope, signingAlgorithm=$signingAlgorithm, storageLabelClaim=$storageLabelClaim, storageQuotaClaim=$storageQuotaClaim, timeout=$timeout, tokenEndpointAuthMethod=$tokenEndpointAuthMethod]'; Map toJson() { final json = {}; @@ -119,12 +124,17 @@ class SystemConfigOAuthDto { json[r'buttonText'] = this.buttonText; json[r'clientId'] = this.clientId; json[r'clientSecret'] = this.clientSecret; + if (this.defaultStorageQuota != null) { json[r'defaultStorageQuota'] = this.defaultStorageQuota; + } else { + // json[r'defaultStorageQuota'] = null; + } json[r'enabled'] = this.enabled; json[r'issuerUrl'] = this.issuerUrl; json[r'mobileOverrideEnabled'] = this.mobileOverrideEnabled; json[r'mobileRedirectUri'] = this.mobileRedirectUri; json[r'profileSigningAlgorithm'] = this.profileSigningAlgorithm; + json[r'roleClaim'] = this.roleClaim; json[r'scope'] = this.scope; json[r'signingAlgorithm'] = this.signingAlgorithm; json[r'storageLabelClaim'] = this.storageLabelClaim; @@ -148,12 +158,13 @@ class SystemConfigOAuthDto { buttonText: mapValueOfType(json, r'buttonText')!, clientId: mapValueOfType(json, r'clientId')!, clientSecret: mapValueOfType(json, r'clientSecret')!, - defaultStorageQuota: num.parse('${json[r'defaultStorageQuota']}'), + defaultStorageQuota: mapValueOfType(json, r'defaultStorageQuota'), enabled: mapValueOfType(json, r'enabled')!, issuerUrl: mapValueOfType(json, r'issuerUrl')!, mobileOverrideEnabled: mapValueOfType(json, r'mobileOverrideEnabled')!, mobileRedirectUri: mapValueOfType(json, r'mobileRedirectUri')!, profileSigningAlgorithm: mapValueOfType(json, r'profileSigningAlgorithm')!, + roleClaim: mapValueOfType(json, r'roleClaim')!, scope: mapValueOfType(json, r'scope')!, signingAlgorithm: mapValueOfType(json, r'signingAlgorithm')!, storageLabelClaim: mapValueOfType(json, r'storageLabelClaim')!, @@ -218,6 +229,7 @@ class SystemConfigOAuthDto { 'mobileOverrideEnabled', 'mobileRedirectUri', 'profileSigningAlgorithm', + 'roleClaim', 'scope', 'signingAlgorithm', 'storageLabelClaim', diff --git a/mobile/openapi/lib/model/user_admin_create_dto.dart b/mobile/openapi/lib/model/user_admin_create_dto.dart index 1477c82ca1..8c8b70fbce 100644 --- a/mobile/openapi/lib/model/user_admin_create_dto.dart +++ b/mobile/openapi/lib/model/user_admin_create_dto.dart @@ -15,6 +15,7 @@ class UserAdminCreateDto { UserAdminCreateDto({ this.avatarColor, required this.email, + this.isAdmin, required this.name, this.notify, required this.password, @@ -27,6 +28,14 @@ class UserAdminCreateDto { String email; + /// + /// 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? isAdmin; + String name; /// @@ -56,6 +65,7 @@ class UserAdminCreateDto { bool operator ==(Object other) => identical(this, other) || other is UserAdminCreateDto && other.avatarColor == avatarColor && other.email == email && + other.isAdmin == isAdmin && other.name == name && other.notify == notify && other.password == password && @@ -68,6 +78,7 @@ class UserAdminCreateDto { // ignore: unnecessary_parenthesis (avatarColor == null ? 0 : avatarColor!.hashCode) + (email.hashCode) + + (isAdmin == null ? 0 : isAdmin!.hashCode) + (name.hashCode) + (notify == null ? 0 : notify!.hashCode) + (password.hashCode) + @@ -76,7 +87,7 @@ class UserAdminCreateDto { (storageLabel == null ? 0 : storageLabel!.hashCode); @override - String toString() => 'UserAdminCreateDto[avatarColor=$avatarColor, email=$email, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; + String toString() => 'UserAdminCreateDto[avatarColor=$avatarColor, email=$email, isAdmin=$isAdmin, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; Map toJson() { final json = {}; @@ -86,6 +97,11 @@ class UserAdminCreateDto { // json[r'avatarColor'] = null; } json[r'email'] = this.email; + if (this.isAdmin != null) { + json[r'isAdmin'] = this.isAdmin; + } else { + // json[r'isAdmin'] = null; + } json[r'name'] = this.name; if (this.notify != null) { json[r'notify'] = this.notify; @@ -122,6 +138,7 @@ class UserAdminCreateDto { return UserAdminCreateDto( avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), email: mapValueOfType(json, r'email')!, + isAdmin: mapValueOfType(json, r'isAdmin'), name: mapValueOfType(json, r'name')!, notify: mapValueOfType(json, r'notify'), password: mapValueOfType(json, r'password')!, diff --git a/mobile/openapi/lib/model/user_admin_update_dto.dart b/mobile/openapi/lib/model/user_admin_update_dto.dart index ee5c006840..9605552d20 100644 --- a/mobile/openapi/lib/model/user_admin_update_dto.dart +++ b/mobile/openapi/lib/model/user_admin_update_dto.dart @@ -15,6 +15,7 @@ class UserAdminUpdateDto { UserAdminUpdateDto({ this.avatarColor, this.email, + this.isAdmin, this.name, this.password, this.pinCode, @@ -33,6 +34,14 @@ class UserAdminUpdateDto { /// String? email; + /// + /// 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? isAdmin; + /// /// 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 @@ -68,6 +77,7 @@ class UserAdminUpdateDto { bool operator ==(Object other) => identical(this, other) || other is UserAdminUpdateDto && other.avatarColor == avatarColor && other.email == email && + other.isAdmin == isAdmin && other.name == name && other.password == password && other.pinCode == pinCode && @@ -80,6 +90,7 @@ class UserAdminUpdateDto { // ignore: unnecessary_parenthesis (avatarColor == null ? 0 : avatarColor!.hashCode) + (email == null ? 0 : email!.hashCode) + + (isAdmin == null ? 0 : isAdmin!.hashCode) + (name == null ? 0 : name!.hashCode) + (password == null ? 0 : password!.hashCode) + (pinCode == null ? 0 : pinCode!.hashCode) + @@ -88,7 +99,7 @@ class UserAdminUpdateDto { (storageLabel == null ? 0 : storageLabel!.hashCode); @override - String toString() => 'UserAdminUpdateDto[avatarColor=$avatarColor, email=$email, name=$name, password=$password, pinCode=$pinCode, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; + String toString() => 'UserAdminUpdateDto[avatarColor=$avatarColor, email=$email, isAdmin=$isAdmin, name=$name, password=$password, pinCode=$pinCode, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; Map toJson() { final json = {}; @@ -102,6 +113,11 @@ class UserAdminUpdateDto { } else { // json[r'email'] = null; } + if (this.isAdmin != null) { + json[r'isAdmin'] = this.isAdmin; + } else { + // json[r'isAdmin'] = null; + } if (this.name != null) { json[r'name'] = this.name; } else { @@ -146,6 +162,7 @@ class UserAdminUpdateDto { return UserAdminUpdateDto( avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), email: mapValueOfType(json, r'email'), + isAdmin: mapValueOfType(json, r'isAdmin'), name: mapValueOfType(json, r'name'), password: mapValueOfType(json, r'password'), pinCode: mapValueOfType(json, r'pinCode'), diff --git a/mobile/pigeon/native_sync_api.dart b/mobile/pigeon/native_sync_api.dart index 33e2429405..4f14b7a0b9 100644 --- a/mobile/pigeon/native_sync_api.dart +++ b/mobile/pigeon/native_sync_api.dart @@ -23,6 +23,7 @@ class PlatformAsset { final int? width; final int? height; final int durationInSeconds; + final int orientation; const PlatformAsset({ required this.id, @@ -33,6 +34,7 @@ class PlatformAsset { this.width, this.height, this.durationInSeconds = 0, + this.orientation = 0, }); } diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index a02379a6bd..3be12d497c 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -863,6 +863,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + home_widget: + dependency: "direct main" + description: + name: home_widget + sha256: ad9634ef5894f3bac73f04d59e2e5151a39798f49985399fd928dadc828d974a + url: "https://pub.dev" + source: hosted + version: "0.8.0" hooks_riverpod: dependency: "direct main" description: @@ -1810,7 +1818,7 @@ packages: source: hosted version: "2.1.4" stream_transform: - dependency: transitive + dependency: "direct main" description: name: stream_transform sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 52b04e0d9a..56aa09e1df 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.134.0+200 +version: 1.135.3+204 environment: sdk: '>=3.3.0 <4.0.0' @@ -40,6 +40,7 @@ dependencies: fluttertoast: ^8.2.12 geolocator: ^14.0.0 hooks_riverpod: ^2.6.1 + home_widget: ^0.8.0 http: ^1.3.0 image_picker: ^1.1.2 intl: ^0.19.0 @@ -63,6 +64,7 @@ dependencies: share_handler: ^0.0.22 share_plus: ^10.1.4 socket_io_client: ^2.0.3+1 + stream_transform: ^2.1.1 thumbhash: 0.1.0+1 timezone: ^0.9.4 url_launcher: ^6.3.1 diff --git a/mobile/test/domain/services/hash_service_test.dart b/mobile/test/domain/services/hash_service_test.dart index 623aed5409..b2ab803ac2 100644 --- a/mobile/test/domain/services/hash_service_test.dart +++ b/mobile/test/domain/services/hash_service_test.dart @@ -3,9 +3,9 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; -import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/services/hash.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:mocktail/mocktail.dart'; import '../../fixtures/album.stub.dart'; @@ -68,7 +68,7 @@ void main() { .thenAnswer((_) async => [album]); when(() => mockAlbumRepo.getAssetsToHash(album.id)) .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset)) + when(() => mockStorageRepo.getFileForAsset(asset.id)) .thenAnswer((_) async => null); await sut.hashAssets(); @@ -89,7 +89,7 @@ void main() { .thenAnswer((_) async => [album]); when(() => mockAlbumRepo.getAssetsToHash(album.id)) .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset)) + when(() => mockStorageRepo.getFileForAsset(asset.id)) .thenAnswer((_) async => mockFile); when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer( (_) async => [hash], @@ -116,7 +116,7 @@ void main() { .thenAnswer((_) async => [album]); when(() => mockAlbumRepo.getAssetsToHash(album.id)) .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset)) + when(() => mockStorageRepo.getFileForAsset(asset.id)) .thenAnswer((_) async => mockFile); when(() => mockNativeApi.hashPaths(['image-path'])) .thenAnswer((_) async => [null]); @@ -141,7 +141,7 @@ void main() { .thenAnswer((_) async => [album]); when(() => mockAlbumRepo.getAssetsToHash(album.id)) .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset)) + when(() => mockStorageRepo.getFileForAsset(asset.id)) .thenAnswer((_) async => mockFile); final invalidHash = Uint8List.fromList([1, 2, 3]); @@ -180,9 +180,9 @@ void main() { .thenAnswer((_) async => [album]); when(() => mockAlbumRepo.getAssetsToHash(album.id)) .thenAnswer((_) async => [asset1, asset2]); - when(() => mockStorageRepo.getFileForAsset(asset1)) + when(() => mockStorageRepo.getFileForAsset(asset1.id)) .thenAnswer((_) async => mockFile1); - when(() => mockStorageRepo.getFileForAsset(asset2)) + when(() => mockStorageRepo.getFileForAsset(asset2.id)) .thenAnswer((_) async => mockFile2); final hash = Uint8List.fromList(List.generate(20, (i) => i)); @@ -220,9 +220,9 @@ void main() { .thenAnswer((_) async => [album]); when(() => mockAlbumRepo.getAssetsToHash(album.id)) .thenAnswer((_) async => [asset1, asset2]); - when(() => mockStorageRepo.getFileForAsset(asset1)) + when(() => mockStorageRepo.getFileForAsset(asset1.id)) .thenAnswer((_) async => mockFile1); - when(() => mockStorageRepo.getFileForAsset(asset2)) + when(() => mockStorageRepo.getFileForAsset(asset2.id)) .thenAnswer((_) async => mockFile2); final hash = Uint8List.fromList(List.generate(20, (i) => i)); @@ -252,9 +252,9 @@ void main() { .thenAnswer((_) async => [album]); when(() => mockAlbumRepo.getAssetsToHash(album.id)) .thenAnswer((_) async => [asset1, asset2]); - when(() => mockStorageRepo.getFileForAsset(asset1)) + when(() => mockStorageRepo.getFileForAsset(asset1.id)) .thenAnswer((_) async => mockFile1); - when(() => mockStorageRepo.getFileForAsset(asset2)) + when(() => mockStorageRepo.getFileForAsset(asset2.id)) .thenAnswer((_) async => mockFile2); final validHash = Uint8List.fromList(List.generate(20, (i) => i)); diff --git a/mobile/test/domain/services/log_service_test.dart b/mobile/test/domain/services/log_service_test.dart index fd9a86ce4e..3efa717f97 100644 --- a/mobile/test/domain/services/log_service_test.dart +++ b/mobile/test/domain/services/log_service_test.dart @@ -1,11 +1,11 @@ import 'package:collection/collection.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/constants/constants.dart'; -import 'package:immich_mobile/domain/interfaces/log.interface.dart'; -import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/services/log.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:logging/logging.dart'; import 'package:mocktail/mocktail.dart'; @@ -28,8 +28,8 @@ final _kWarnLog = LogMessage( void main() { late LogService sut; - late ILogRepository mockLogRepo; - late IStoreRepository mockStoreRepo; + late IsarLogRepository mockLogRepo; + late IsarStoreRepository mockStoreRepo; setUp(() async { mockLogRepo = MockLogRepository(); @@ -121,7 +121,6 @@ void main() { time.elapse(const Duration(seconds: 6)); final insert = verify(() => mockLogRepo.insertAll(captureAny())); insert.called(1); - // ignore: prefer-correct-json-casts final captured = insert.captured.firstOrNull as List; expect(captured.firstOrNull?.message, _kInfoLog.message); expect(captured.firstOrNull?.logger, _kInfoLog.logger); diff --git a/mobile/test/domain/services/store_service_test.dart b/mobile/test/domain/services/store_service_test.dart index 8f4749ac62..7eab532ef3 100644 --- a/mobile/test/domain/services/store_service_test.dart +++ b/mobile/test/domain/services/store_service_test.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; -import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/services/store.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:mocktail/mocktail.dart'; import '../../infrastructure/repository.mock.dart'; @@ -15,7 +15,7 @@ final _kBackupFailedSince = DateTime.utc(2023); void main() { late StoreService sut; - late IStoreRepository mockStoreRepo; + late IsarStoreRepository mockStoreRepo; late StreamController> controller; setUp(() async { diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart index b78a44342b..27cd8c5b21 100644 --- a/mobile/test/domain/services/sync_stream_service_test.dart +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -1,12 +1,10 @@ -// 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:immich_mobile/infrastructure/repositories/sync_api.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; import 'package:mocktail/mocktail.dart'; import '../../fixtures/sync_stream.stub.dart'; @@ -30,8 +28,8 @@ class _MockCancellationWrapper extends Mock implements _CancellationWrapper {} void main() { late SyncStreamService sut; - late ISyncStreamRepository mockSyncStreamRepo; - late ISyncApiRepository mockSyncApiRepo; + late SyncStreamRepository mockSyncStreamRepo; + late SyncApiRepository mockSyncApiRepo; late Function(List, Function()) handleEventsCallback; late _MockAbortCallbackWrapper mockAbortCallbackWrapper; @@ -46,7 +44,6 @@ void main() { when(() => mockSyncApiRepo.streamChanges(any())) .thenAnswer((invocation) async { - // ignore: avoid-unsafe-collection-methods handleEventsCallback = invocation.positionalArguments.first; }); @@ -62,16 +59,48 @@ void main() { .thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateAssetsV1(any())) .thenAnswer(successHandler); + when( + () => mockSyncStreamRepo.updateAssetsV1( + any(), + debugLabel: any(named: 'debugLabel'), + ), + ).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteAssetsV1(any())) .thenAnswer(successHandler); + when( + () => mockSyncStreamRepo.deleteAssetsV1( + any(), + debugLabel: any(named: 'debugLabel'), + ), + ).thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateAssetsExifV1(any())) .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updatePartnerAssetsV1(any())) + when( + () => mockSyncStreamRepo.updateAssetsExifV1( + any(), + debugLabel: any(named: 'debugLabel'), + ), + ).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateMemoriesV1(any())) .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deletePartnerAssetsV1(any())) + when(() => mockSyncStreamRepo.deleteMemoriesV1(any())) .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updatePartnerAssetsExifV1(any())) + when(() => mockSyncStreamRepo.updateMemoryAssetsV1(any())) .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteMemoryAssetsV1(any())) + .thenAnswer(successHandler); + when( + () => mockSyncStreamRepo.updateStacksV1( + any(), + debugLabel: any(named: 'debugLabel'), + ), + ).thenAnswer(successHandler); + when( + () => mockSyncStreamRepo.deleteStacksV1( + any(), + debugLabel: any(named: 'debugLabel'), + ), + ).thenAnswer(successHandler); sut = SyncStreamService( syncApiRepository: mockSyncApiRepo, @@ -218,5 +247,94 @@ void main() { verify(() => mockSyncApiRepo.ack(["2"])).called(1); }, ); + + test("processes memory sync events successfully", () async { + final events = [ + SyncStreamStub.memoryV1, + SyncStreamStub.memoryDeleteV1, + SyncStreamStub.memoryToAssetV1, + SyncStreamStub.memoryToAssetDeleteV1, + ]; + + await simulateEvents(events); + + verifyInOrder([ + () => mockSyncStreamRepo.updateMemoriesV1(any()), + () => mockSyncApiRepo.ack(["5"]), + () => mockSyncStreamRepo.deleteMemoriesV1(any()), + () => mockSyncApiRepo.ack(["6"]), + () => mockSyncStreamRepo.updateMemoryAssetsV1(any()), + () => mockSyncApiRepo.ack(["7"]), + () => mockSyncStreamRepo.deleteMemoryAssetsV1(any()), + () => mockSyncApiRepo.ack(["8"]), + ]); + verifyNever(() => mockAbortCallbackWrapper()); + }); + + test("processes mixed memory and user events in correct order", () async { + final events = [ + SyncStreamStub.memoryDeleteV1, + SyncStreamStub.userV1Admin, + SyncStreamStub.memoryToAssetV1, + SyncStreamStub.memoryV1, + ]; + + await simulateEvents(events); + + verifyInOrder([ + () => mockSyncStreamRepo.deleteMemoriesV1(any()), + () => mockSyncApiRepo.ack(["6"]), + () => mockSyncStreamRepo.updateUsersV1(any()), + () => mockSyncApiRepo.ack(["1"]), + () => mockSyncStreamRepo.updateMemoryAssetsV1(any()), + () => mockSyncApiRepo.ack(["7"]), + () => mockSyncStreamRepo.updateMemoriesV1(any()), + () => mockSyncApiRepo.ack(["5"]), + ]); + verifyNever(() => mockAbortCallbackWrapper()); + }); + + test("handles memory sync failure gracefully", () async { + when(() => mockSyncStreamRepo.updateMemoriesV1(any())) + .thenThrow(Exception("Memory sync failed")); + + final events = [ + SyncStreamStub.memoryV1, + SyncStreamStub.userV1Admin, + ]; + + expect( + () async => await simulateEvents(events), + throwsA(isA()), + ); + }); + + test("processes memory asset events with correct data types", () async { + final events = [SyncStreamStub.memoryToAssetV1]; + + await simulateEvents(events); + + verify(() => mockSyncStreamRepo.updateMemoryAssetsV1(any())).called(1); + verify(() => mockSyncApiRepo.ack(["7"])).called(1); + }); + + test("processes memory delete events with correct data types", () async { + final events = [SyncStreamStub.memoryDeleteV1]; + + await simulateEvents(events); + + verify(() => mockSyncStreamRepo.deleteMemoriesV1(any())).called(1); + verify(() => mockSyncApiRepo.ack(["6"])).called(1); + }); + + test("processes memory create/update events with correct data types", + () async { + final events = [SyncStreamStub.memoryV1]; + + await simulateEvents(events); + + verify(() => mockSyncStreamRepo.updateMemoriesV1(any())).called(1); + verify(() => mockSyncApiRepo.ack(["5"])).called(1); + }); }); } diff --git a/mobile/test/domain/services/user_service_test.dart b/mobile/test/domain/services/user_service_test.dart index 8e2d160fd5..5cce565477 100644 --- a/mobile/test/domain/services/user_service_test.dart +++ b/mobile/test/domain/services/user_service_test.dart @@ -1,11 +1,11 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; -import 'package:immich_mobile/domain/interfaces/user.interface.dart'; -import 'package:immich_mobile/domain/interfaces/user_api.interface.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart'; import 'package:mocktail/mocktail.dart'; import '../../fixtures/user.stub.dart'; @@ -14,16 +14,16 @@ import '../service.mock.dart'; void main() { late UserService sut; - late IUserRepository mockUserRepo; - late IUserApiRepository mockUserApiRepo; + late IsarUserRepository mockUserRepo; + late UserApiRepository mockUserApiRepo; late StoreService mockStoreService; setUp(() { - mockUserRepo = MockUserRepository(); + mockUserRepo = MockIsarUserRepository(); mockUserApiRepo = MockUserApiRepository(); mockStoreService = MockStoreService(); sut = UserService( - userRepository: mockUserRepo, + isarUserRepository: mockUserRepo, userApiRepository: mockUserApiRepo, storeService: mockStoreService, ); diff --git a/mobile/test/fixtures/album.stub.dart b/mobile/test/fixtures/album.stub.dart index 1432d35901..1e79f62faf 100644 --- a/mobile/test/fixtures/album.stub.dart +++ b/mobile/test/fixtures/album.stub.dart @@ -1,4 +1,4 @@ -import 'package:immich_mobile/domain/models/local_album.model.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart index ba97f1434a..de2d58bc9d 100644 --- a/mobile/test/fixtures/sync_stream.stub.dart +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -42,4 +42,47 @@ abstract final class SyncStreamStub { data: SyncPartnerDeleteV1(sharedById: "3", sharedWithId: "4"), ack: "4", ); + + static final memoryV1 = SyncEvent( + type: SyncEntityType.memoryV1, + data: SyncMemoryV1( + createdAt: DateTime(2023, 1, 1), + data: {"year": 2023, "title": "Test Memory"}, + deletedAt: null, + hideAt: null, + id: "memory-1", + isSaved: false, + memoryAt: DateTime(2023, 1, 1), + ownerId: "user-1", + seenAt: null, + showAt: DateTime(2023, 1, 1), + type: MemoryType.onThisDay, + updatedAt: DateTime(2023, 1, 1), + ), + ack: "5", + ); + + static final memoryDeleteV1 = SyncEvent( + type: SyncEntityType.memoryDeleteV1, + data: SyncMemoryDeleteV1(memoryId: "memory-2"), + ack: "6", + ); + + static final memoryToAssetV1 = SyncEvent( + type: SyncEntityType.memoryToAssetV1, + data: SyncMemoryAssetV1( + assetId: "asset-1", + memoryId: "memory-1", + ), + ack: "7", + ); + + static final memoryToAssetDeleteV1 = SyncEvent( + type: SyncEntityType.memoryToAssetDeleteV1, + data: SyncMemoryAssetDeleteV1( + assetId: "asset-2", + memoryId: "memory-1", + ), + ack: "8", + ); } diff --git a/mobile/test/infrastructure/repositories/exif_repository_test.dart b/mobile/test/infrastructure/repositories/exif_repository_test.dart index e267d2dac4..4e7ee4d79d 100644 --- a/mobile/test/infrastructure/repositories/exif_repository_test.dart +++ b/mobile/test/infrastructure/repositories/exif_repository_test.dart @@ -1,5 +1,4 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart'; import 'package:isar/isar.dart'; @@ -20,7 +19,7 @@ Future _populateExifTable(Isar db) async { void main() { late Isar db; - late IExifInfoRepository sut; + late IsarExifRepository sut; setUp(() async { db = await TestUtils.initIsar(); diff --git a/mobile/test/infrastructure/repositories/local_album_repository_test.dart b/mobile/test/infrastructure/repositories/local_album_repository_test.dart index 827d81c79b..bab25de52a 100644 --- a/mobile/test/infrastructure/repositories/local_album_repository_test.dart +++ b/mobile/test/infrastructure/repositories/local_album_repository_test.dart @@ -1,9 +1,9 @@ import 'package:drift/drift.dart'; import 'package:drift/native.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; -import 'package:immich_mobile/domain/models/local_album.model.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import '../../test_utils/medium_factory.dart'; @@ -24,7 +24,7 @@ void main() { group('getAll', () { test('sorts albums by backupSelection & isIosSharedAlbum', () async { final localAlbumRepo = - mediumFactory.getRepository(); + mediumFactory.getRepository(); await localAlbumRepo.upsert( mediumFactory.localAlbum( id: '1', diff --git a/mobile/test/infrastructure/repositories/store_repository_test.dart b/mobile/test/infrastructure/repositories/store_repository_test.dart index 528e17ba3d..ce13c1ecdd 100644 --- a/mobile/test/infrastructure/repositories/store_repository_test.dart +++ b/mobile/test/infrastructure/repositories/store_repository_test.dart @@ -1,5 +1,4 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; @@ -42,7 +41,7 @@ Future _populateStore(Isar db) async { void main() { late Isar db; - late IStoreRepository sut; + late IsarStoreRepository sut; setUp(() async { db = await TestUtils.initIsar(); diff --git a/mobile/test/infrastructure/repository.mock.dart b/mobile/test/infrastructure/repository.mock.dart index 0dc241ca94..1fde303863 100644 --- a/mobile/test/infrastructure/repository.mock.dart +++ b/mobile/test/infrastructure/repository.mock.dart @@ -1,33 +1,35 @@ -import 'package:immich_mobile/domain/interfaces/device_asset.interface.dart'; -import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; -import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart'; -import 'package:immich_mobile/domain/interfaces/log.interface.dart'; -import 'package:immich_mobile/domain/interfaces/storage.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:immich_mobile/infrastructure/repositories/device_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/store.repository.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/infrastructure/repositories/user.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart'; import 'package:mocktail/mocktail.dart'; -class MockStoreRepository extends Mock implements IStoreRepository {} +class MockStoreRepository extends Mock implements IsarStoreRepository {} -class MockLogRepository extends Mock implements ILogRepository {} +class MockLogRepository extends Mock implements IsarLogRepository {} -class MockUserRepository extends Mock implements IUserRepository {} +class MockIsarUserRepository extends Mock implements IsarUserRepository {} class MockDeviceAssetRepository extends Mock - implements IDeviceAssetRepository {} + implements IsarDeviceAssetRepository {} -class MockSyncStreamRepository extends Mock implements ISyncStreamRepository {} +class MockSyncStreamRepository extends Mock implements SyncStreamRepository {} -class MockLocalAlbumRepository extends Mock implements ILocalAlbumRepository {} +class MockLocalAlbumRepository extends Mock + implements DriftLocalAlbumRepository {} -class MockLocalAssetRepository extends Mock implements ILocalAssetRepository {} +class MockLocalAssetRepository extends Mock + implements DriftLocalAssetRepository {} -class MockStorageRepository extends Mock implements IStorageRepository {} +class MockStorageRepository extends Mock implements StorageRepository {} // API Repos -class MockUserApiRepository extends Mock implements IUserApiRepository {} +class MockUserApiRepository extends Mock implements UserApiRepository {} -class MockSyncApiRepository extends Mock implements ISyncApiRepository {} +class MockSyncApiRepository extends Mock implements SyncApiRepository {} diff --git a/mobile/test/modules/map/map_theme_override_test.dart b/mobile/test/modules/map/map_theme_override_test.dart index 5a6b163c04..db133d5c34 100644 --- a/mobile/test/modules/map/map_theme_override_test.dart +++ b/mobile/test/modules/map/map_theme_override_test.dart @@ -28,7 +28,7 @@ void main() { }); setUp(() async { - mapState = MapState(themeMode: ThemeMode.dark); + mapState = const MapState(themeMode: ThemeMode.dark); mapStateNotifier = MockMapStateNotifier(mapState); await StoreService.init(storeRepository: IsarStoreRepository(db)); overrides = [ diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart index 2029ade018..a78f65af67 100644 --- a/mobile/test/modules/shared/sync_service_test.dart +++ b/mobile/test/modules/shared/sync_service_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:immich_mobile/domain/interfaces/user.interface.dart'; +import 'package:immich_mobile/constants/enums.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/log.service.dart'; @@ -10,8 +10,8 @@ import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; -import 'package:immich_mobile/interfaces/partner_api.interface.dart'; +import 'package:immich_mobile/repositories/partner_api.repository.dart'; +import 'package:immich_mobile/repositories/asset.repository.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:mocktail/mocktail.dart'; @@ -55,7 +55,7 @@ void main() { final MockAlbumRepository albumRepository = MockAlbumRepository(); final MockAssetRepository assetRepository = MockAssetRepository(); final MockExifInfoRepository exifInfoRepository = MockExifInfoRepository(); - final MockUserRepository userRepository = MockUserRepository(); + final MockIsarUserRepository userRepository = MockIsarUserRepository(); final MockETagRepository eTagRepository = MockETagRepository(); final MockAlbumMediaRepository albumMediaRepository = MockAlbumMediaRepository(); @@ -240,7 +240,9 @@ void main() { [initialAssets[1].remoteId!, initialAssets[2].remoteId!], state: AssetState.remote, ), - ).thenAnswer((_) async {}); + ).thenAnswer((_) async { + return; + }); when( () => assetRepository .getAllByRemoteId(["2-1", "1-1"], state: AssetState.merged), diff --git a/mobile/test/modules/utils/thumbnail_utils_test.dart b/mobile/test/modules/utils/thumbnail_utils_test.dart new file mode 100644 index 0000000000..6fa0127f16 --- /dev/null +++ b/mobile/test/modules/utils/thumbnail_utils_test.dart @@ -0,0 +1,78 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/utils/thumbnail_utils.dart'; + +void main() { + final dateTime = DateTime(2025, 04, 25, 12, 13, 14); + final dateTimeString = DateFormat.yMMMMd().format(dateTime); + + test('returns description if it has one', () { + final result = getAltText( + const ExifInfo(description: 'description'), + dateTime, + AssetType.image, + [], + ); + expect(result, 'description'); + }); + + test('returns image alt text with date if no location', () { + final (template, args) = getAltTextTemplate( + const ExifInfo(), + dateTime, + AssetType.image, + [], + ); + expect(template, "image_alt_text_date"); + expect(args["isVideo"], "false"); + expect(args["date"], dateTimeString); + }); + + test('returns image alt text with date and place', () { + final (template, args) = getAltTextTemplate( + const ExifInfo(city: 'city', country: 'country'), + dateTime, + AssetType.video, + [], + ); + expect(template, "image_alt_text_date_place"); + expect(args["isVideo"], "true"); + expect(args["date"], dateTimeString); + expect(args["city"], "city"); + expect(args["country"], "country"); + }); + + test('returns image alt text with date and some people', () { + final (template, args) = getAltTextTemplate( + const ExifInfo(), + dateTime, + AssetType.image, + ["Alice", "Bob"], + ); + expect(template, "image_alt_text_date_2_people"); + expect(args["isVideo"], "false"); + expect(args["date"], dateTimeString); + expect(args["person1"], "Alice"); + expect(args["person2"], "Bob"); + }); + + test('returns image alt text with date and location and many people', () { + final (template, args) = getAltTextTemplate( + const ExifInfo(city: "city", country: 'country'), + dateTime, + AssetType.video, + ["Alice", "Bob", "Carol", "David", "Eve"], + ); + expect(template, "image_alt_text_date_place_4_or_more_people"); + expect(args["isVideo"], "true"); + expect(args["date"], dateTimeString); + expect(args["city"], "city"); + expect(args["country"], "country"); + expect(args["person1"], "Alice"); + expect(args["person2"], "Bob"); + expect(args["person3"], "Carol"); + expect(args["additionalCount"], "2"); + }); +} diff --git a/mobile/test/repository.mocks.dart b/mobile/test/repository.mocks.dart index d2f0da4231..54a7e2d4a4 100644 --- a/mobile/test/repository.mocks.dart +++ b/mobile/test/repository.mocks.dart @@ -1,50 +1,49 @@ -import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; -import 'package:immich_mobile/interfaces/album.interface.dart'; -import 'package:immich_mobile/interfaces/album_api.interface.dart'; -import 'package:immich_mobile/interfaces/album_media.interface.dart'; -import 'package:immich_mobile/interfaces/asset.interface.dart'; -import 'package:immich_mobile/interfaces/asset_api.interface.dart'; -import 'package:immich_mobile/interfaces/asset_media.interface.dart'; -import 'package:immich_mobile/interfaces/auth.interface.dart'; -import 'package:immich_mobile/interfaces/auth_api.interface.dart'; -import 'package:immich_mobile/interfaces/backup_album.interface.dart'; -import 'package:immich_mobile/interfaces/etag.interface.dart'; -import 'package:immich_mobile/interfaces/file_media.interface.dart'; -import 'package:immich_mobile/interfaces/local_files_manager.interface.dart'; -import 'package:immich_mobile/interfaces/partner.interface.dart'; -import 'package:immich_mobile/interfaces/partner_api.interface.dart'; +import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart'; +import 'package:immich_mobile/repositories/partner_api.repository.dart'; +import 'package:immich_mobile/repositories/album_media.repository.dart'; +import 'package:immich_mobile/repositories/album_api.repository.dart'; +import 'package:immich_mobile/repositories/partner.repository.dart'; +import 'package:immich_mobile/repositories/etag.repository.dart'; +import 'package:immich_mobile/repositories/local_files_manager.repository.dart'; +import 'package:immich_mobile/repositories/file_media.repository.dart'; +import 'package:immich_mobile/repositories/backup.repository.dart'; +import 'package:immich_mobile/repositories/auth.repository.dart'; +import 'package:immich_mobile/repositories/auth_api.repository.dart'; +import 'package:immich_mobile/repositories/asset.repository.dart'; +import 'package:immich_mobile/repositories/asset_media.repository.dart'; +import 'package:immich_mobile/repositories/album.repository.dart'; +import 'package:immich_mobile/repositories/asset_api.repository.dart'; import 'package:mocktail/mocktail.dart'; -class MockAlbumRepository extends Mock implements IAlbumRepository {} +class MockAlbumRepository extends Mock implements AlbumRepository {} -class MockAssetRepository extends Mock implements IAssetRepository {} +class MockAssetRepository extends Mock implements AssetRepository {} -class MockBackupRepository extends Mock implements IBackupAlbumRepository {} +class MockBackupRepository extends Mock implements BackupAlbumRepository {} -class MockExifInfoRepository extends Mock implements IExifInfoRepository {} +class MockExifInfoRepository extends Mock implements IsarExifRepository {} -class MockETagRepository extends Mock implements IETagRepository {} +class MockETagRepository extends Mock implements ETagRepository {} -class MockAlbumMediaRepository extends Mock implements IAlbumMediaRepository {} +class MockAlbumMediaRepository extends Mock implements AlbumMediaRepository {} -class MockBackupAlbumRepository extends Mock - implements IBackupAlbumRepository {} +class MockBackupAlbumRepository extends Mock implements BackupAlbumRepository {} -class MockAssetApiRepository extends Mock implements IAssetApiRepository {} +class MockAssetApiRepository extends Mock implements AssetApiRepository {} -class MockAssetMediaRepository extends Mock implements IAssetMediaRepository {} +class MockAssetMediaRepository extends Mock implements AssetMediaRepository {} -class MockFileMediaRepository extends Mock implements IFileMediaRepository {} +class MockFileMediaRepository extends Mock implements FileMediaRepository {} -class MockAlbumApiRepository extends Mock implements IAlbumApiRepository {} +class MockAlbumApiRepository extends Mock implements AlbumApiRepository {} -class MockAuthApiRepository extends Mock implements IAuthApiRepository {} +class MockAuthApiRepository extends Mock implements AuthApiRepository {} -class MockAuthRepository extends Mock implements IAuthRepository {} +class MockAuthRepository extends Mock implements AuthRepository {} -class MockPartnerRepository extends Mock implements IPartnerRepository {} +class MockPartnerRepository extends Mock implements PartnerRepository {} -class MockPartnerApiRepository extends Mock implements IPartnerApiRepository {} +class MockPartnerApiRepository extends Mock implements PartnerApiRepository {} class MockLocalFilesManagerRepository extends Mock - implements ILocalFilesManager {} + implements LocalFilesManagerRepository {} diff --git a/mobile/test/services/asset.service_test.dart b/mobile/test/services/asset.service_test.dart index 63546e39f1..293e5ec76b 100644 --- a/mobile/test/services/asset.service_test.dart +++ b/mobile/test/services/asset.service_test.dart @@ -22,7 +22,7 @@ void main() { late MockExifInfoRepository exifInfoRepository; late MockETagRepository eTagRepository; late MockBackupAlbumRepository backupAlbumRepository; - late MockUserRepository userRepository; + late MockIsarUserRepository userRepository; late MockAssetMediaRepository assetMediaRepository; late MockApiService apiService; @@ -35,7 +35,7 @@ void main() { assetRepository = MockAssetRepository(); assetApiRepository = MockAssetApiRepository(); exifInfoRepository = MockExifInfoRepository(); - userRepository = MockUserRepository(); + userRepository = MockIsarUserRepository(); eTagRepository = MockETagRepository(); backupAlbumRepository = MockBackupAlbumRepository(); apiService = MockApiService(); diff --git a/mobile/test/services/auth.service_test.dart b/mobile/test/services/auth.service_test.dart index 4ada98a6c9..46ad8bbc4c 100644 --- a/mobile/test/services/auth.service_test.dart +++ b/mobile/test/services/auth.service_test.dart @@ -187,7 +187,7 @@ void main() { when(() => authRepository.getPreferredWifiName()) .thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - AuxilaryEndpoint( + const AuxilaryEndpoint( url: 'https://external.endpoint', status: AuxCheckStatus.valid, ), @@ -214,11 +214,11 @@ void main() { when(() => authRepository.getPreferredWifiName()) .thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - AuxilaryEndpoint( + const AuxilaryEndpoint( url: 'https://external.endpoint', status: AuxCheckStatus.valid, ), - AuxilaryEndpoint( + const AuxilaryEndpoint( url: 'https://external.endpoint2', status: AuxCheckStatus.valid, ), @@ -249,11 +249,11 @@ void main() { when(() => authRepository.getPreferredWifiName()) .thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - AuxilaryEndpoint( + const AuxilaryEndpoint( url: 'https://external.endpoint', status: AuxCheckStatus.valid, ), - AuxilaryEndpoint( + const AuxilaryEndpoint( url: 'https://external.endpoint2', status: AuxCheckStatus.valid, ), @@ -302,7 +302,7 @@ void main() { when(() => authRepository.getPreferredWifiName()) .thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - AuxilaryEndpoint( + const AuxilaryEndpoint( url: 'https://external.endpoint', status: AuxCheckStatus.valid, ), diff --git a/mobile/test/services/entity.service_test.dart b/mobile/test/services/entity.service_test.dart index 190b2bc2b5..1642be7fb3 100644 --- a/mobile/test/services/entity.service_test.dart +++ b/mobile/test/services/entity.service_test.dart @@ -12,11 +12,11 @@ import '../repository.mocks.dart'; void main() { late EntityService sut; late MockAssetRepository assetRepository; - late MockUserRepository userRepository; + late MockIsarUserRepository userRepository; setUp(() { assetRepository = MockAssetRepository(); - userRepository = MockUserRepository(); + userRepository = MockIsarUserRepository(); sut = EntityService(assetRepository, userRepository); }); diff --git a/mobile/test/services/hash_service_test.dart b/mobile/test/services/hash_service_test.dart index e278199e4f..2ba9c356a0 100644 --- a/mobile/test/services/hash_service_test.dart +++ b/mobile/test/services/hash_service_test.dart @@ -6,9 +6,9 @@ import 'package:collection/collection.dart'; import 'package:file/memory.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:immich_mobile/domain/interfaces/device_asset.interface.dart'; import 'package:immich_mobile/domain/models/device_asset.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/device_asset.repository.dart'; import 'package:immich_mobile/services/background.service.dart'; import 'package:immich_mobile/services/hash.service.dart'; import 'package:mocktail/mocktail.dart'; @@ -25,7 +25,7 @@ class MockAssetEntity extends Mock implements AssetEntity {} void main() { late HashService sut; late BackgroundService mockBackgroundService; - late IDeviceAssetRepository mockDeviceAssetRepository; + late IsarDeviceAssetRepository mockDeviceAssetRepository; setUp(() { mockBackgroundService = MockBackgroundService(); diff --git a/mobile/test/test_utils.dart b/mobile/test/test_utils.dart index c0f789795c..596d3bcd1c 100644 --- a/mobile/test/test_utils.dart +++ b/mobile/test/test_utils.dart @@ -24,7 +24,6 @@ import 'mock_http_override.dart'; // Listener Mock to test when a provider notifies its listeners class ListenerMock extends Mock { - // ignore: avoid-declaring-call-method void call(T? previous, T next); } diff --git a/mobile/test/test_utils/medium_factory.dart b/mobile/test/test_utils/medium_factory.dart index 64c23321aa..8dafc564c1 100644 --- a/mobile/test/test_utils/medium_factory.dart +++ b/mobile/test/test_utils/medium_factory.dart @@ -1,8 +1,7 @@ import 'dart:math'; -import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/domain/models/local_album.model.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; @@ -56,7 +55,7 @@ class MediumFactory { T getRepository() { switch (T) { - case const (ILocalAlbumRepository): + case const (DriftLocalAlbumRepository): return DriftLocalAlbumRepository(_db) as T; default: throw Exception('Unknown repository: $T'); diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 775fe117ad..1624d0ed7e 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -2698,6 +2698,39 @@ } }, "/duplicates": { + "delete": { + "operationId": "deleteDuplicates", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BulkIdsDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Duplicates" + ] + }, "get": { "operationId": "getAssetDuplicates", "parameters": [], @@ -2732,6 +2765,41 @@ ] } }, + "/duplicates/{id}": { + "delete": { + "operationId": "deleteDuplicate", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Duplicates" + ] + } + }, "/faces": { "get": { "operationId": "getFaces", @@ -3599,6 +3667,72 @@ ] } }, + "/memories/statistics": { + "get": { + "operationId": "memoriesStatistics", + "parameters": [ + { + "name": "for", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "isSaved", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isTrashed", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "type", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/MemoryType" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemoryStatisticsResponseDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Memories" + ] + } + }, "/memories/{id}": { "delete": { "operationId": "deleteMemory", @@ -4412,6 +4546,39 @@ } }, "/people": { + "delete": { + "operationId": "deletePeople", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BulkIdsDto" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "People" + ] + }, "get": { "operationId": "getAllPeople", "parameters": [ @@ -4577,6 +4744,39 @@ } }, "/people/{id}": { + "delete": { + "operationId": "deletePerson", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "People" + ] + }, "get": { "operationId": "getPerson", "parameters": [ @@ -8369,7 +8569,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.134.0", + "version": "1.135.3", "contact": {} }, "tags": [], @@ -9335,6 +9535,9 @@ "format": "date-time", "type": "string" }, + "filename": { + "type": "string" + }, "isFavorite": { "type": "boolean" }, @@ -9385,6 +9588,9 @@ "fileModifiedAt": { "format": "date-time", "type": "string" + }, + "filename": { + "type": "string" } }, "required": [ @@ -10827,6 +11033,17 @@ ], "type": "object" }, + "MemoryStatisticsResponseDto": { + "properties": { + "total": { + "type": "integer" + } + }, + "required": [ + "total" + ], + "type": "object" + }, "MemoryType": { "enum": [ "on_this_day" @@ -10986,6 +11203,7 @@ "format": "uuid", "type": "string" }, + "nullable": true, "type": "array" }, "takenAfter": { @@ -11875,6 +12093,7 @@ "format": "uuid", "type": "string" }, + "nullable": true, "type": "array" }, "takenAfter": { @@ -12940,6 +13159,7 @@ "format": "uuid", "type": "string" }, + "nullable": true, "type": "array" }, "takenAfter": { @@ -13131,6 +13351,7 @@ "format": "uuid", "type": "string" }, + "nullable": true, "type": "array" }, "takenAfter": { @@ -13210,6 +13431,7 @@ "items": { "type": "string" }, + "maxItems": 1000, "type": "array" } }, @@ -13218,6 +13440,10 @@ ], "type": "object" }, + "SyncAckV1": { + "properties": {}, + "type": "object" + }, "SyncAlbumDeleteV1": { "properties": { "albumId": { @@ -13229,6 +13455,36 @@ ], "type": "object" }, + "SyncAlbumToAssetDeleteV1": { + "properties": { + "albumId": { + "type": "string" + }, + "assetId": { + "type": "string" + } + }, + "required": [ + "albumId", + "assetId" + ], + "type": "object" + }, + "SyncAlbumToAssetV1": { + "properties": { + "albumId": { + "type": "string" + }, + "assetId": { + "type": "string" + } + }, + "required": [ + "albumId", + "assetId" + ], + "type": "object" + }, "SyncAlbumUserDeleteV1": { "properties": { "albumId": { @@ -13363,36 +13619,41 @@ "type": "string" }, "fNumber": { + "format": "double", "nullable": true, - "type": "integer" + "type": "number" }, "fileSizeInByte": { "nullable": true, "type": "integer" }, "focalLength": { + "format": "double", "nullable": true, - "type": "integer" + "type": "number" }, "fps": { + "format": "double", "nullable": true, - "type": "integer" + "type": "number" }, "iso": { "nullable": true, "type": "integer" }, "latitude": { + "format": "double", "nullable": true, - "type": "integer" + "type": "number" }, "lensModel": { "nullable": true, "type": "string" }, "longitude": { + "format": "double", "nullable": true, - "type": "integer" + "type": "number" }, "make": { "nullable": true, @@ -13471,6 +13732,10 @@ "nullable": true, "type": "string" }, + "duration": { + "nullable": true, + "type": "string" + }, "fileCreatedAt": { "format": "date-time", "nullable": true, @@ -13520,6 +13785,7 @@ "required": [ "checksum", "deletedAt", + "duration", "fileCreatedAt", "fileModifiedAt", "id", @@ -13537,21 +13803,152 @@ "enum": [ "UserV1", "UserDeleteV1", - "PartnerV1", - "PartnerDeleteV1", "AssetV1", "AssetDeleteV1", "AssetExifV1", + "PartnerV1", + "PartnerDeleteV1", "PartnerAssetV1", + "PartnerAssetBackfillV1", "PartnerAssetDeleteV1", "PartnerAssetExifV1", + "PartnerAssetExifBackfillV1", + "PartnerStackBackfillV1", + "PartnerStackDeleteV1", + "PartnerStackV1", "AlbumV1", "AlbumDeleteV1", "AlbumUserV1", - "AlbumUserDeleteV1" + "AlbumUserBackfillV1", + "AlbumUserDeleteV1", + "AlbumAssetV1", + "AlbumAssetBackfillV1", + "AlbumAssetExifV1", + "AlbumAssetExifBackfillV1", + "AlbumToAssetV1", + "AlbumToAssetDeleteV1", + "AlbumToAssetBackfillV1", + "MemoryV1", + "MemoryDeleteV1", + "MemoryToAssetV1", + "MemoryToAssetDeleteV1", + "StackV1", + "StackDeleteV1", + "SyncAckV1" ], "type": "string" }, + "SyncMemoryAssetDeleteV1": { + "properties": { + "assetId": { + "type": "string" + }, + "memoryId": { + "type": "string" + } + }, + "required": [ + "assetId", + "memoryId" + ], + "type": "object" + }, + "SyncMemoryAssetV1": { + "properties": { + "assetId": { + "type": "string" + }, + "memoryId": { + "type": "string" + } + }, + "required": [ + "assetId", + "memoryId" + ], + "type": "object" + }, + "SyncMemoryDeleteV1": { + "properties": { + "memoryId": { + "type": "string" + } + }, + "required": [ + "memoryId" + ], + "type": "object" + }, + "SyncMemoryV1": { + "properties": { + "createdAt": { + "format": "date-time", + "type": "string" + }, + "data": { + "type": "object" + }, + "deletedAt": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "hideAt": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "id": { + "type": "string" + }, + "isSaved": { + "type": "boolean" + }, + "memoryAt": { + "format": "date-time", + "type": "string" + }, + "ownerId": { + "type": "string" + }, + "seenAt": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "showAt": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/MemoryType" + } + ] + }, + "updatedAt": { + "format": "date-time", + "type": "string" + } + }, + "required": [ + "createdAt", + "data", + "deletedAt", + "hideAt", + "id", + "isSaved", + "memoryAt", + "ownerId", + "seenAt", + "showAt", + "type", + "updatedAt" + ], + "type": "object" + }, "SyncPartnerDeleteV1": { "properties": { "sharedById": { @@ -13588,17 +13985,64 @@ }, "SyncRequestType": { "enum": [ - "UsersV1", - "PartnersV1", + "AlbumsV1", + "AlbumUsersV1", + "AlbumToAssetsV1", + "AlbumAssetsV1", + "AlbumAssetExifsV1", "AssetsV1", "AssetExifsV1", + "MemoriesV1", + "MemoryToAssetsV1", + "PartnersV1", "PartnerAssetsV1", "PartnerAssetExifsV1", - "AlbumsV1", - "AlbumUsersV1" + "PartnerStacksV1", + "StacksV1", + "UsersV1" ], "type": "string" }, + "SyncStackDeleteV1": { + "properties": { + "stackId": { + "type": "string" + } + }, + "required": [ + "stackId" + ], + "type": "object" + }, + "SyncStackV1": { + "properties": { + "createdAt": { + "format": "date-time", + "type": "string" + }, + "id": { + "type": "string" + }, + "ownerId": { + "type": "string" + }, + "primaryAssetId": { + "type": "string" + }, + "updatedAt": { + "format": "date-time", + "type": "string" + } + }, + "required": [ + "createdAt", + "id", + "ownerId", + "primaryAssetId", + "updatedAt" + ], + "type": "object" + }, "SyncStreamDto": { "properties": { "types": { @@ -14193,8 +14637,10 @@ "type": "string" }, "defaultStorageQuota": { + "format": "int64", "minimum": 0, - "type": "number" + "nullable": true, + "type": "integer" }, "enabled": { "type": "boolean" @@ -14212,6 +14658,9 @@ "profileSigningAlgorithm": { "type": "string" }, + "roleClaim": { + "type": "string" + }, "scope": { "type": "string" }, @@ -14248,6 +14697,7 @@ "mobileOverrideEnabled", "mobileRedirectUri", "profileSigningAlgorithm", + "roleClaim", "scope", "signingAlgorithm", "storageLabelClaim", @@ -15054,6 +15504,9 @@ "format": "email", "type": "string" }, + "isAdmin": { + "type": "boolean" + }, "name": { "type": "string" }, @@ -15204,6 +15657,9 @@ "format": "email", "type": "string" }, + "isAdmin": { + "type": "boolean" + }, "name": { "type": "string" }, diff --git a/open-api/templates/mobile/serialization/native/native_class.mustache b/open-api/templates/mobile/serialization/native/native_class.mustache index 9f40d5b0bf..2d6e6d24f3 100644 --- a/open-api/templates/mobile/serialization/native/native_class.mustache +++ b/open-api/templates/mobile/serialization/native/native_class.mustache @@ -206,7 +206,7 @@ class {{{classname}}} { : {{/isNullable}}{{{datatypeWithEnum}}}.parse('${json[r'{{{baseName}}}']}'), {{/isNumber}} {{#isDouble}} - {{{name}}}: (mapValueOfType(json, r'{{{baseName}}}'){{#required}}{{^isNullable}}!{{/isNullable}}{{/required}}{{^required}}{{#defaultValue}} ?? {{{.}}}{{/defaultValue}}{{/required}}).toDouble(), + {{{name}}}: (mapValueOfType(json, r'{{{baseName}}}'){{#required}}{{^isNullable}}!{{/isNullable}}{{/required}}{{^required}}{{#defaultValue}} ?? {{{.}}}{{/defaultValue}}{{/required}}){{#isNullable}}?{{/isNullable}}.toDouble(), {{/isDouble}} {{^isDouble}} {{^isNumber}} diff --git a/open-api/templates/mobile/serialization/native/native_class.mustache.patch b/open-api/templates/mobile/serialization/native/native_class.mustache.patch index 4ba6594966..8eeefdad97 100644 --- a/open-api/templates/mobile/serialization/native/native_class.mustache.patch +++ b/open-api/templates/mobile/serialization/native/native_class.mustache.patch @@ -1,5 +1,5 @@ ---- native_class.mustache 2024-09-19 11:41:07.855683995 -0400 -+++ native_class_temp.mustache 2024-09-19 11:41:57.113249395 -0400 +--- native_class.mustache 2025-07-01 08:29:23.968133163 +0800 ++++ native_class_temp.mustache 2025-07-01 08:29:44.225850583 +0800 @@ -91,14 +91,14 @@ {{/isDateTime}} {{#isNullable}} @@ -44,7 +44,7 @@ : {{/isNullable}}{{{datatypeWithEnum}}}.parse('${json[r'{{{baseName}}}']}'), {{/isNumber}} + {{#isDouble}} -+ {{{name}}}: (mapValueOfType(json, r'{{{baseName}}}'){{#required}}{{^isNullable}}!{{/isNullable}}{{/required}}{{^required}}{{#defaultValue}} ?? {{{.}}}{{/defaultValue}}{{/required}}).toDouble(), ++ {{{name}}}: (mapValueOfType(json, r'{{{baseName}}}'){{#required}}{{^isNullable}}!{{/isNullable}}{{/required}}{{^required}}{{#defaultValue}} ?? {{{.}}}{{/defaultValue}}{{/required}}){{#isNullable}}?{{/isNullable}}.toDouble(), + {{/isDouble}} + {{^isDouble}} {{^isNumber}} diff --git a/open-api/typescript-sdk/.nvmrc b/open-api/typescript-sdk/.nvmrc index 5b540673a8..fc37597bcc 100644 --- a/open-api/typescript-sdk/.nvmrc +++ b/open-api/typescript-sdk/.nvmrc @@ -1 +1 @@ -22.16.0 +22.17.0 diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index b8e9986772..ab9f3b1fd7 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.134.0", + "version": "1.135.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.134.0", + "version": "1.135.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.29", + "@types/node": "^22.15.33", "typescript": "^5.3.3" } }, @@ -23,9 +23,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", - "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", + "version": "22.15.34", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", + "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 3299a3a5be..f2885f4a7b 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.134.0", + "version": "1.135.3", "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.15.29", + "@types/node": "^22.15.33", "typescript": "^5.3.3" }, "repository": { @@ -28,6 +28,6 @@ "directory": "open-api/typescript-sdk" }, "volta": { - "node": "22.16.0" + "node": "22.17.0" } } diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index fee00acffd..55991b8599 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.134.0 + * 1.135.3 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ @@ -109,6 +109,7 @@ export type UserAdminResponseDto = { export type UserAdminCreateDto = { avatarColor?: (UserAvatarColor) | null; email: string; + isAdmin?: boolean; name: string; notify?: boolean; password: string; @@ -122,6 +123,7 @@ export type UserAdminDeleteDto = { export type UserAdminUpdateDto = { avatarColor?: (UserAvatarColor) | null; email?: string; + isAdmin?: boolean; name?: string; password?: string; pinCode?: string | null; @@ -442,6 +444,7 @@ export type AssetMediaCreateDto = { duration?: string; fileCreatedAt: string; fileModifiedAt: string; + filename?: string; isFavorite?: boolean; livePhotoVideoId?: string; sidecarData?: Blob; @@ -508,6 +511,7 @@ export type AssetMediaReplaceDto = { duration?: string; fileCreatedAt: string; fileModifiedAt: string; + filename?: string; }; export type SignUpDto = { email: string; @@ -742,6 +746,9 @@ export type MemoryCreateDto = { seenAt?: string; "type": MemoryType; }; +export type MemoryStatisticsResponseDto = { + total: number; +}; export type MemoryUpdateDto = { isSaved?: boolean; memoryAt?: string; @@ -882,7 +889,7 @@ export type MetadataSearchDto = { rating?: number; size?: number; state?: string | null; - tagIds?: string[]; + tagIds?: string[] | null; takenAfter?: string; takenBefore?: string; thumbnailPath?: string; @@ -949,7 +956,7 @@ export type RandomSearchDto = { rating?: number; size?: number; state?: string | null; - tagIds?: string[]; + tagIds?: string[] | null; takenAfter?: string; takenBefore?: string; trashedAfter?: string; @@ -986,7 +993,7 @@ export type SmartSearchDto = { rating?: number; size?: number; state?: string | null; - tagIds?: string[]; + tagIds?: string[] | null; takenAfter?: string; takenBefore?: string; trashedAfter?: string; @@ -1018,7 +1025,7 @@ export type StatisticsSearchDto = { personIds?: string[]; rating?: number; state?: string | null; - tagIds?: string[]; + tagIds?: string[] | null; takenAfter?: string; takenBefore?: string; trashedAfter?: string; @@ -1385,12 +1392,13 @@ export type SystemConfigOAuthDto = { buttonText: string; clientId: string; clientSecret: string; - defaultStorageQuota: number; + defaultStorageQuota: number | null; enabled: boolean; issuerUrl: string; mobileOverrideEnabled: boolean; mobileRedirectUri: string; profileSigningAlgorithm: string; + roleClaim: string; scope: string; signingAlgorithm: string; storageLabelClaim: string; @@ -2279,6 +2287,15 @@ export function getDownloadInfo({ key, downloadInfoDto }: { body: downloadInfoDto }))); } +export function deleteDuplicates({ bulkIdsDto }: { + bulkIdsDto: BulkIdsDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText("/duplicates", oazapfts.json({ + ...opts, + method: "DELETE", + body: bulkIdsDto + }))); +} export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2287,6 +2304,14 @@ export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) { ...opts })); } +export function deleteDuplicate({ id }: { + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/duplicates/${encodeURIComponent(id)}`, { + ...opts, + method: "DELETE" + })); +} export function getFaces({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2509,6 +2534,24 @@ export function createMemory({ memoryCreateDto }: { body: memoryCreateDto }))); } +export function memoriesStatistics({ $for, isSaved, isTrashed, $type }: { + $for?: string; + isSaved?: boolean; + isTrashed?: boolean; + $type?: MemoryType; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: MemoryStatisticsResponseDto; + }>(`/memories/statistics${QS.query(QS.explode({ + "for": $for, + isSaved, + isTrashed, + "type": $type + }))}`, { + ...opts + })); +} export function deleteMemory({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2727,6 +2770,15 @@ export function updatePartner({ id, updatePartnerDto }: { body: updatePartnerDto }))); } +export function deletePeople({ bulkIdsDto }: { + bulkIdsDto: BulkIdsDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText("/people", oazapfts.json({ + ...opts, + method: "DELETE", + body: bulkIdsDto + }))); +} export function getAllPeople({ closestAssetId, closestPersonId, page, size, withHidden }: { closestAssetId?: string; closestPersonId?: string; @@ -2771,6 +2823,14 @@ export function updatePeople({ peopleUpdateDto }: { body: peopleUpdateDto }))); } +export function deletePerson({ id }: { + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/people/${encodeURIComponent(id)}`, { + ...opts, + method: "DELETE" + })); +} export function getPerson({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -4004,28 +4064,55 @@ export enum Error2 { export enum SyncEntityType { UserV1 = "UserV1", UserDeleteV1 = "UserDeleteV1", - PartnerV1 = "PartnerV1", - PartnerDeleteV1 = "PartnerDeleteV1", AssetV1 = "AssetV1", AssetDeleteV1 = "AssetDeleteV1", AssetExifV1 = "AssetExifV1", + PartnerV1 = "PartnerV1", + PartnerDeleteV1 = "PartnerDeleteV1", PartnerAssetV1 = "PartnerAssetV1", + PartnerAssetBackfillV1 = "PartnerAssetBackfillV1", PartnerAssetDeleteV1 = "PartnerAssetDeleteV1", PartnerAssetExifV1 = "PartnerAssetExifV1", + PartnerAssetExifBackfillV1 = "PartnerAssetExifBackfillV1", + PartnerStackBackfillV1 = "PartnerStackBackfillV1", + PartnerStackDeleteV1 = "PartnerStackDeleteV1", + PartnerStackV1 = "PartnerStackV1", AlbumV1 = "AlbumV1", AlbumDeleteV1 = "AlbumDeleteV1", AlbumUserV1 = "AlbumUserV1", - AlbumUserDeleteV1 = "AlbumUserDeleteV1" + AlbumUserBackfillV1 = "AlbumUserBackfillV1", + AlbumUserDeleteV1 = "AlbumUserDeleteV1", + AlbumAssetV1 = "AlbumAssetV1", + AlbumAssetBackfillV1 = "AlbumAssetBackfillV1", + AlbumAssetExifV1 = "AlbumAssetExifV1", + AlbumAssetExifBackfillV1 = "AlbumAssetExifBackfillV1", + AlbumToAssetV1 = "AlbumToAssetV1", + AlbumToAssetDeleteV1 = "AlbumToAssetDeleteV1", + AlbumToAssetBackfillV1 = "AlbumToAssetBackfillV1", + MemoryV1 = "MemoryV1", + MemoryDeleteV1 = "MemoryDeleteV1", + MemoryToAssetV1 = "MemoryToAssetV1", + MemoryToAssetDeleteV1 = "MemoryToAssetDeleteV1", + StackV1 = "StackV1", + StackDeleteV1 = "StackDeleteV1", + SyncAckV1 = "SyncAckV1" } export enum SyncRequestType { - UsersV1 = "UsersV1", - PartnersV1 = "PartnersV1", + AlbumsV1 = "AlbumsV1", + AlbumUsersV1 = "AlbumUsersV1", + AlbumToAssetsV1 = "AlbumToAssetsV1", + AlbumAssetsV1 = "AlbumAssetsV1", + AlbumAssetExifsV1 = "AlbumAssetExifsV1", AssetsV1 = "AssetsV1", AssetExifsV1 = "AssetExifsV1", + MemoriesV1 = "MemoriesV1", + MemoryToAssetsV1 = "MemoryToAssetsV1", + PartnersV1 = "PartnersV1", PartnerAssetsV1 = "PartnerAssetsV1", PartnerAssetExifsV1 = "PartnerAssetExifsV1", - AlbumsV1 = "AlbumsV1", - AlbumUsersV1 = "AlbumUsersV1" + PartnerStacksV1 = "PartnerStacksV1", + StacksV1 = "StacksV1", + UsersV1 = "UsersV1" } export enum TranscodeHWAccel { Nvenc = "nvenc", diff --git a/server/.nvmrc b/server/.nvmrc index 5b540673a8..fc37597bcc 100644 --- a/server/.nvmrc +++ b/server/.nvmrc @@ -1 +1 @@ -22.16.0 +22.17.0 diff --git a/server/Dockerfile b/server/Dockerfile index fcf16ea139..2f2749ada0 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,10 +1,8 @@ # dev build -FROM ghcr.io/immich-app/base-server-dev:202505131114@sha256:cf4507bbbf307e9b6d8ee9418993321f2b85867da8ce14d0a20ccaf9574cb995 AS dev +FROM ghcr.io/immich-app/base-server-dev:202507091427@sha256:733e510024e03bc2450608a1f3622f45bf287b70d89a06e60acbccea4bf4fd8c 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 @@ -16,6 +14,63 @@ ENV PATH="${PATH}:/usr/src/app/bin" \ NVIDIA_VISIBLE_DEVICES=all ENTRYPOINT ["tini", "--", "/bin/sh"] +FROM dev AS dev-container-server + +RUN rm -rf /usr/src/app +RUN apt-get update && \ + apt-get install sudo inetutils-ping openjdk-11-jre-headless \ + vim nano \ + -y --no-install-recommends --fix-missing + +RUN usermod -aG sudo node +RUN echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers +RUN mkdir -p /workspaces/immich +RUN chown node -R /workspaces +COPY --chown=node:node --chmod=777 ../.devcontainer/server/*.sh /immich-devcontainer/ + +USER node +COPY --chown=node:node .. /tmp/create-dep-cache/ +WORKDIR /tmp/create-dep-cache +RUN make ci-all && rm -rf /tmp/create-dep-cache + + +FROM dev-container-server AS dev-container-mobile +USER root +# Enable multiarch for arm64 if necessary +RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \ + dpkg --add-architecture amd64 && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + qemu-user-static \ + libc6:amd64 \ + libstdc++6:amd64 \ + libgcc1:amd64; \ + fi + +# Flutter SDK +# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux +ENV FLUTTER_CHANNEL="stable" +ENV FLUTTER_VERSION="3.29.3" +ENV FLUTTER_HOME=/flutter +ENV PATH=${PATH}:${FLUTTER_HOME}/bin + +# Flutter SDK +RUN mkdir -p ${FLUTTER_HOME} \ + && curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ + && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \ + && rm flutter.tar.xz \ + && chown -R node ${FLUTTER_HOME} + +USER node +RUN sudo apt-get update \ + && wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg \ + && echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list \ + && sudo apt-get update \ + && sudo apt-get install dcm -y + +COPY --chmod=777 ../.devcontainer/mobile/container-mobile-post-create.sh /immich-devcontainer/container-mobile-post-create.sh + +RUN dart --disable-analytics FROM dev AS prod @@ -43,7 +98,7 @@ RUN npm run build # prod build -FROM ghcr.io/immich-app/base-server-prod:202505061115@sha256:9971d3a089787f0bd01f4682141d3665bcf5efb3e101a88e394ffd25bee4eedb +FROM ghcr.io/immich-app/base-server-prod:202507091427@sha256:ad10451acd8eda05a006a2586b6b0425cdaaca97c413fb4c1a10896712528bdc WORKDIR /usr/src/app ENV NODE_ENV=production \ diff --git a/server/package-lock.json b/server/package-lock.json index 6f40c42fbf..ea45d89257 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,13 +1,12 @@ { "name": "immich", - "version": "1.134.0", + "version": "1.135.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.134.0", - "hasInstallScript": true, + "version": "1.135.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^11.0.1", @@ -16,46 +15,61 @@ "@nestjs/event-emitter": "^3.0.0", "@nestjs/platform-express": "^11.0.4", "@nestjs/platform-socket.io": "^11.0.4", - "@nestjs/schedule": "^5.0.0", + "@nestjs/schedule": "^6.0.0", "@nestjs/swagger": "^11.0.2", "@nestjs/websockets": "^11.0.4", - "@opentelemetry/auto-instrumentations-node": "^0.59.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/auto-instrumentations-node": "^0.60.0", "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/exporter-prometheus": "^0.201.0", - "@opentelemetry/sdk-node": "^0.201.0", - "@react-email/components": "^0.0.41", + "@opentelemetry/exporter-prometheus": "^0.202.0", + "@opentelemetry/instrumentation-http": "^0.202.0", + "@opentelemetry/instrumentation-ioredis": "^0.50.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.48.0", + "@opentelemetry/instrumentation-pg": "^0.54.0", + "@opentelemetry/resources": "^2.0.1", + "@opentelemetry/sdk-metrics": "^2.0.1", + "@opentelemetry/sdk-node": "^0.202.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@react-email/components": "^0.1.0", + "@react-email/render": "^1.1.2", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", "async-lock": "^1.4.0", "bcrypt": "^6.0.0", + "body-parser": "^2.2.0", "bullmq": "^5.51.0", - "chokidar": "^3.5.3", + "chokidar": "^4.0.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", + "cron": "4.3.0", + "exiftool-vendored": "^28.8.0", + "express": "^5.1.0", "fast-glob": "^3.3.2", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", "handlebars": "^4.7.8", "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", - "joi": "^17.10.0", "js-yaml": "^4.1.0", "kysely": "^0.28.0", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", + "mnemonist": "^0.40.3", + "multer": "^2.0.1", "nest-commander": "^3.16.0", "nestjs-cls": "^5.0.0", - "nestjs-kysely": "^1.1.0", - "nestjs-otel": "^6.0.0", + "nestjs-kysely": "^3.0.0", + "nestjs-otel": "^7.0.0", "nodemailer": "^7.0.0", "openid-client": "^6.3.3", "pg": "^8.11.3", + "pg-connection-string": "^2.9.1", "picomatch": "^4.0.2", + "postgres": "3.4.7", "react": "^19.0.0", "react-dom": "^19.0.0", "react-email": "^4.0.0", @@ -66,7 +80,8 @@ "semver": "^7.6.2", "sharp": "^0.34.2", "sirv": "^3.0.0", - "tailwindcss-preset-email": "^1.3.2", + "socket.io": "^4.8.1", + "tailwindcss-preset-email": "^1.4.0", "thumbhash": "^0.1.1", "typeorm": "^0.3.17", "ua-parser-js": "^2.0.0", @@ -84,15 +99,17 @@ "@types/archiver": "^6.0.0", "@types/async-lock": "^1.4.2", "@types/bcrypt": "^5.0.0", + "@types/body-parser": "^1.19.6", "@types/compression": "^1.7.5", "@types/cookie-parser": "^1.4.8", "@types/express": "^5.0.0", "@types/fluent-ffmpeg": "^2.1.21", "@types/js-yaml": "^4.0.9", "@types/lodash": "^4.14.197", + "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^22.15.29", + "@types/node": "^22.15.33", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", @@ -101,17 +118,17 @@ "@types/semver": "^7.5.8", "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", + "@types/validator": "^13.15.2", "@vitest/coverage-v8": "^3.0.0", + "canvas": "^3.1.0", "eslint": "^9.14.0", "eslint-config-prettier": "^10.0.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", - "jsdom": "^26.1.0", "mock-fs": "^5.2.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", "prettier-plugin-organize-imports": "^4.0.0", @@ -119,6 +136,7 @@ "source-map-support": "^0.5.21", "sql-formatter": "^15.0.0", "supertest": "^7.1.0", + "tailwindcss": "^3.4.0", "testcontainers": "^11.0.0", "tsconfig-paths": "^4.2.0", "typescript": "^5.3.3", @@ -134,7 +152,6 @@ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -157,13 +174,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.6.tgz", - "integrity": "sha512-YTAxNnT++5eflx19OUHmOWu597/TbTel+QARiZCv1xQw99+X8DCKKOUXtqBRd53CAHlREDI33Rn/JLY3NYgMLQ==", + "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.6", + "@angular-devkit/core": "19.2.8", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "5.4.1", @@ -226,25 +243,6 @@ } } }, - "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", @@ -292,24 +290,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@angular-devkit/schematics-cli/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@angular-devkit/schematics-cli/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -317,22 +297,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@angular-devkit/schematics-cli/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@angular-devkit/schematics-cli/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -354,9 +318,9 @@ } }, "node_modules/@angular-devkit/schematics/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": { @@ -398,24 +362,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@angular-devkit/schematics/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@angular-devkit/schematics/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -423,22 +369,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@angular-devkit/schematics/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@angular-devkit/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -460,11 +390,13 @@ } }, "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==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", @@ -474,27 +406,33 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, "node_modules/@babel/generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", - "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0", + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -522,9 +460,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.3.tgz", - "integrity": "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", "license": "MIT", "dependencies": { "@babel/types": "^7.27.3" @@ -537,30 +475,30 @@ } }, "node_modules/@babel/template": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", - "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", - "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.27.0", - "@babel/parser": "^7.27.0", - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -578,9 +516,9 @@ } }, "node_modules/@babel/types": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", - "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -634,14 +572,16 @@ } ], "license": "MIT-0", + "optional": true, + "peer": true, "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==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", "dev": true, "funding": [ { @@ -654,18 +594,20 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "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==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", + "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", "dev": true, "funding": [ { @@ -678,22 +620,24 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.3" + "@csstools/css-calc": "^2.1.4" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "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==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", "dev": true, "funding": [ { @@ -706,17 +650,19 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-tokenizer": "^3.0.4" } }, "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==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", "dev": true, "funding": [ { @@ -729,6 +675,8 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18" } @@ -1186,9 +1134,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1201,9 +1149,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", - "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1261,9 +1209,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.27.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", - "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", + "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", "dev": true, "license": "MIT", "engines": { @@ -1284,19 +1232,32 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", + "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.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.15.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", + "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", + "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/@golevelup/nestjs-discovery": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-4.0.3.tgz", @@ -1311,9 +1272,9 @@ } }, "node_modules/@grpc/grpc-js": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.3.tgz", - "integrity": "sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz", + "integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==", "license": "Apache-2.0", "dependencies": { "@grpc/proto-loader": "^0.7.13", @@ -1324,9 +1285,9 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.7.13", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", - "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", "license": "Apache-2.0", "dependencies": { "lodash.camelcase": "^4.3.0", @@ -1341,21 +1302,6 @@ "node": ">=6" } }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1409,9 +1355,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1819,15 +1765,15 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.5.tgz", - "integrity": "sha512-swPczVU+at65xa5uPfNP9u3qx/alNwiaykiI/ExpsmMSQW55trmZcwhYWzw/7fj+n6Q8z1eENvR7vFfq9oPSAQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.8.tgz", + "integrity": "sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.10", - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.6", + "@inquirer/core": "^10.1.13", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -1844,14 +1790,14 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.9.tgz", - "integrity": "sha512-NgQCnHqFTjF7Ys2fsqK2WtnA8X1kHyInyG+nMIuHowVTIgIuS10T4AznI/PvbqSpJqjCUqNBlKGh1v3bwLFL4w==", + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.12.tgz", + "integrity": "sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.10", - "@inquirer/type": "^3.0.6" + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7" }, "engines": { "node": ">=18" @@ -1866,14 +1812,14 @@ } }, "node_modules/@inquirer/core": { - "version": "10.1.10", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.10.tgz", - "integrity": "sha512-roDaKeY1PYY0aCqhRmXihrHjoSW2A00pV3Ke5fTpMCkzcGF64R8e0lw3dK+eLEHwS4vB5RnW1wuQmvzoRul8Mw==", + "version": "10.1.13", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.13.tgz", + "integrity": "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.6", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", @@ -1894,14 +1840,14 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.10.tgz", - "integrity": "sha512-5GVWJ+qeI6BzR6TIInLP9SXhWCEcvgFQYmcRG6d6RIlhFjM5TyG18paTGBgRYyEouvCmzeco47x9zX9tQEofkw==", + "version": "4.2.13", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.13.tgz", + "integrity": "sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.10", - "@inquirer/type": "^3.0.6", + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7", "external-editor": "^3.1.0" }, "engines": { @@ -1917,14 +1863,14 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.12.tgz", - "integrity": "sha512-jV8QoZE1fC0vPe6TnsOfig+qwu7Iza1pkXoUJ3SroRagrt2hxiL+RbM432YAihNR7m7XnU0HWl/WQ35RIGmXHw==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.15.tgz", + "integrity": "sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.10", - "@inquirer/type": "^3.0.6", + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1940,9 +1886,9 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.11.tgz", - "integrity": "sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", + "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", "dev": true, "license": "MIT", "engines": { @@ -1950,14 +1896,14 @@ } }, "node_modules/@inquirer/input": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.9.tgz", - "integrity": "sha512-mshNG24Ij5KqsQtOZMgj5TwEjIf+F2HOESk6bjMwGWgcH5UBe8UoljwzNFHqdMbGYbgAf6v2wU/X9CAdKJzgOA==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.12.tgz", + "integrity": "sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.10", - "@inquirer/type": "^3.0.6" + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7" }, "engines": { "node": ">=18" @@ -1972,14 +1918,14 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.12.tgz", - "integrity": "sha512-7HRFHxbPCA4e4jMxTQglHJwP+v/kpFsCf2szzfBHy98Wlc3L08HL76UDiA87TOdX5fwj2HMOLWqRWv9Pnn+Z5Q==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.15.tgz", + "integrity": "sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.10", - "@inquirer/type": "^3.0.6" + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7" }, "engines": { "node": ">=18" @@ -1994,14 +1940,14 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.12.tgz", - "integrity": "sha512-FlOB0zvuELPEbnBYiPaOdJIaDzb2PmJ7ghi/SVwIHDDSQ2K4opGBkF+5kXOg6ucrtSUQdLhVVY5tycH0j0l+0g==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.15.tgz", + "integrity": "sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.10", - "@inquirer/type": "^3.0.6", + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7", "ansi-escapes": "^4.3.2" }, "engines": { @@ -2047,14 +1993,14 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.12.tgz", - "integrity": "sha512-wNPJZy8Oc7RyGISPxp9/MpTOqX8lr0r+lCCWm7hQra+MDtYRgINv1hxw7R+vKP71Bu/3LszabxOodfV/uTfsaA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.3.tgz", + "integrity": "sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.10", - "@inquirer/type": "^3.0.6", + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -2070,15 +2016,15 @@ } }, "node_modules/@inquirer/search": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.12.tgz", - "integrity": "sha512-H/kDJA3kNlnNIjB8YsaXoQI0Qccgf0Na14K1h8ExWhNmUg2E941dyFPrZeugihEa9AZNW5NdsD/NcvUME83OPQ==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.15.tgz", + "integrity": "sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.10", - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.6", + "@inquirer/core": "^10.1.13", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -2094,15 +2040,15 @@ } }, "node_modules/@inquirer/select": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.1.1.tgz", - "integrity": "sha512-IUXzzTKVdiVNMA+2yUvPxWsSgOG4kfX93jOM4Zb5FgujeInotv5SPIJVeXQ+fO4xu7tW8VowFhdG5JRmmCyQ1Q==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.2.3.tgz", + "integrity": "sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.10", - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.6", + "@inquirer/core": "^10.1.13", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -2119,9 +2065,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.6.tgz", - "integrity": "sha512-/mKVCtVpyBu3IDarv0G+59KC4stsD5mDsGpYh+GKs1NZT88Jh52+cuoA1AtLk2Q0r/quNl+1cSUyLRHBFeD0XA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", + "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", "dev": true, "license": "MIT", "engines": { @@ -2142,6 +2088,27 @@ "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", "license": "MIT" }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2333,6 +2300,62 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@mapbox/node-pre-gyp/node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/fs-minipass/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/@mapbox/node-pre-gyp/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2355,6 +2378,112 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/minizlib/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/@mapbox/node-pre-gyp/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2372,12 +2501,89 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@mapbox/node-pre-gyp/node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/@microsoft/tsdoc": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", "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", @@ -2391,6 +2597,19 @@ "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", @@ -2493,25 +2712,6 @@ } } }, - "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", @@ -2529,22 +2729,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@nestjs/cli/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@nestjs/cli/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -2552,20 +2736,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@nestjs/cli/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@nestjs/cli/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -2587,12 +2757,12 @@ } }, "node_modules/@nestjs/common": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.1.tgz", - "integrity": "sha512-crzp+1qeZ5EGL0nFTPy9NrVMAaUWewV5AwtQyv6SQ9yQPXwRl9W9hm1pt0nAtUu5QbYMbSuo7lYcF81EjM+nCA==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.3.tgz", + "integrity": "sha512-ogEK+GriWodIwCw6buQ1rpcH4Kx+G7YQ9EwuPySI3rS05pSdtQ++UhucjusSI9apNidv+QURBztJkRecwwJQXg==", "license": "MIT", "dependencies": { - "file-type": "20.5.0", + "file-type": "21.0.0", "iterare": "1.2.1", "load-esm": "1.0.2", "tslib": "2.8.1", @@ -2618,9 +2788,9 @@ } }, "node_modules/@nestjs/core": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.1.tgz", - "integrity": "sha512-UFoUAgLKFT+RwHTANJdr0dF7p0qS9QjkaUPjg8aafnjM/qxxxrUVDB49nVvyMlk+Hr1+vvcNaOHbWWQBxoZcHA==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.3.tgz", + "integrity": "sha512-5lTni0TCh8x7bXETRD57pQFnKnEg1T6M+VLE7wAmyQRIecKQU+2inRGZD+A4v2DC1I04eA0WffP0GKLxjOKlzw==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2692,14 +2862,14 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.1.tgz", - "integrity": "sha512-IUxk380qnUtz0PCRQ5i+o9UHlGMrFzGPIJxDwyt3JZZwx2AngOlcEcm5e+7YeJQEr2QYX2QyC4tUQg0zde+D7A==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.3.tgz", + "integrity": "sha512-hEDNMlaPiBO72fxxX/CuRQL3MEhKRc/sIYGVoXjrnw6hTxZdezvvM6A95UaLsYknfmcZZa/CdG1SMBZOu9agHQ==", "license": "MIT", "dependencies": { "cors": "2.8.5", "express": "5.1.0", - "multer": "1.4.5-lts.2", + "multer": "2.0.1", "path-to-regexp": "8.2.0", "tslib": "2.8.1" }, @@ -2713,9 +2883,9 @@ } }, "node_modules/@nestjs/platform-socket.io": { - "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==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.3.tgz", + "integrity": "sha512-jQ+ccprmh3kKolBp+bb97zoaS3vKaiyeNqyctGqV4CSG8P6mXSaaUObWxAsw6Jdgn5YQAVEBWJ6FhvF4s6QZbg==", "license": "MIT", "dependencies": { "socket.io": "4.8.1", @@ -2732,12 +2902,12 @@ } }, "node_modules/@nestjs/schedule": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-5.0.1.tgz", - "integrity": "sha512-kFoel84I4RyS2LNPH6yHYTKxB16tb3auAEciFuc788C3ph6nABkUfzX5IE+unjVaRX+3GuruJwurNepMlHXpQg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.0.0.tgz", + "integrity": "sha512-aQySMw6tw2nhitELXd3EiRacQRgzUKD9mFcUZVOJ7jPLqIBvXOyvRWLsK9SdurGA+jjziAlMef7iB5ZEFFoQpw==", "license": "MIT", "dependencies": { - "cron": "3.5.0" + "cron": "4.3.0" }, "peerDependencies": { "@nestjs/common": "^10.0.0 || ^11.0.0", @@ -2789,6 +2959,25 @@ } } }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.6.tgz", + "integrity": "sha512-YTAxNnT++5eflx19OUHmOWu597/TbTel+QARiZCv1xQw99+X8DCKKOUXtqBRd53CAHlREDI33Rn/JLY3NYgMLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.6", + "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/schematics/node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -2806,24 +2995,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@nestjs/schematics/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@nestjs/schematics/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -2831,22 +3002,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@nestjs/schematics/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@nestjs/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -2901,9 +3056,9 @@ } }, "node_modules/@nestjs/testing": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.1.tgz", - "integrity": "sha512-stzm8YrLDGAijHYQw+8Z9dD6lGdvahL0hIjGVZ/0KBxLZht0/rvRjgV31UK+DUqXaF7yhJTw9ryrPaITxI1J6A==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.3.tgz", + "integrity": "sha512-CeXG6/eEqgFIkPkmU00y18Dd3DLOIDFhPItzJK1SWckKo6IhcnfoRJzGx75bmuvUMjb51j6An96S/+MJ2ty9jA==", "dev": true, "license": "MIT", "dependencies": { @@ -2929,9 +3084,9 @@ } }, "node_modules/@nestjs/websockets": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.1.tgz", - "integrity": "sha512-gxwQoGx5bW5IvparzrX1UOGXz87eqY0fK5Y6yb14z6tSSubQTciNjCDm5osDEkRyRCG6ZB0F+eXF6dRUjwTlBQ==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.3.tgz", + "integrity": "sha512-IjhWKfRf0D247JxYIEs8USblJJbcxUsKJpzbCPaZ7TrVy4LrpG3IRQDlSTOw599TRIYP5ixyH9C0+v5DyaI9uA==", "license": "MIT", "dependencies": { "iterare": "1.2.1", @@ -2952,15 +3107,15 @@ } }, "node_modules/@next/env": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.3.tgz", - "integrity": "sha512-OdiMrzCl2Xi0VTjiQQUK0Xh7bJHnOuET2s+3V+Y40WJBAXrJeGA3f+I8MZJ/YQ3mVGi5XGR1L66oFlgqXhQ4Vw==", + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.4.tgz", + "integrity": "sha512-ZkdYzBseS6UjYzz6ylVKPOK+//zLWvD6Ta+vpoye8cW11AjiQjGYVibF0xuvT4L0iJfAPfZLFidaEzAOywyOAQ==", "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.3.tgz", - "integrity": "sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg==", + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.4.tgz", + "integrity": "sha512-z0qIYTONmPRbwHWvpyrFXJd5F9YWLCsw3Sjrzj2ZvMYy9NPQMPZ1NjOJh4ojr4oQzcGYwgJKfidzehaNa1BpEg==", "cpu": [ "arm64" ], @@ -2974,9 +3129,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.3.tgz", - "integrity": "sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw==", + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.4.tgz", + "integrity": "sha512-Z0FYJM8lritw5Wq+vpHYuCIzIlEMjewG2aRkc3Hi2rcbULknYL/xqfpBL23jQnCSrDUGAo/AEv0Z+s2bff9Zkw==", "cpu": [ "x64" ], @@ -2990,9 +3145,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.3.tgz", - "integrity": "sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw==", + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.4.tgz", + "integrity": "sha512-l8ZQOCCg7adwmsnFm8m5q9eIPAHdaB2F3cxhufYtVo84pymwKuWfpYTKcUiFcutJdp9xGHC+F1Uq3xnFU1B/7g==", "cpu": [ "arm64" ], @@ -3006,9 +3161,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.3.tgz", - "integrity": "sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA==", + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.4.tgz", + "integrity": "sha512-wFyZ7X470YJQtpKot4xCY3gpdn8lE9nTlldG07/kJYexCUpX1piX+MBfZdvulo+t1yADFVEuzFfVHfklfEx8kw==", "cpu": [ "arm64" ], @@ -3022,9 +3177,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.3.tgz", - "integrity": "sha512-jJ8HRiF3N8Zw6hGlytCj5BiHyG/K+fnTKVDEKvUCyiQ/0r5tgwO7OgaRiOjjRoIx2vwLR+Rz8hQoPrnmFbJdfw==", + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.4.tgz", + "integrity": "sha512-gEbH9rv9o7I12qPyvZNVTyP/PWKqOp8clvnoYZQiX800KkqsaJZuOXkWgMa7ANCCh/oEN2ZQheh3yH8/kWPSEg==", "cpu": [ "x64" ], @@ -3038,9 +3193,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.3.tgz", - "integrity": "sha512-HrUcTr4N+RgiiGn3jjeT6Oo208UT/7BuTr7K0mdKRBtTbT4v9zJqCDKO97DUqqoBK1qyzP1RwvrWTvU6EPh/Cw==", + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.4.tgz", + "integrity": "sha512-Cf8sr0ufuC/nu/yQ76AnarbSAXcwG/wj+1xFPNbyNo8ltA6kw5d5YqO8kQuwVIxk13SBdtgXrNyom3ZosHAy4A==", "cpu": [ "x64" ], @@ -3054,9 +3209,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.3.tgz", - "integrity": "sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ==", + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.4.tgz", + "integrity": "sha512-ay5+qADDN3rwRbRpEhTOreOn1OyJIXS60tg9WMYTWCy3fB6rGoyjLVxc4dR9PYjEdR2iDYsaF5h03NA+XuYPQQ==", "cpu": [ "arm64" ], @@ -3070,9 +3225,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.3.tgz", - "integrity": "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==", + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.4.tgz", + "integrity": "sha512-4kDt31Bc9DGyYs41FTL1/kNpDeHyha2TC0j5sRRoKCyrhNcfZ/nRQkAUlF27mETwm8QyHqIjHJitfcza2Iykfg==", "cpu": [ "x64" ], @@ -3150,30 +3305,6 @@ "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", @@ -3213,9 +3344,9 @@ } }, "node_modules/@opentelemetry/api-logs": { - "version": "0.201.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.201.1.tgz", - "integrity": "sha512-IxcFDP1IGMDemVFG2by/AMK+/o6EuBQ8idUq3xZ6MxgQGeumYZuX5OwR0h9HuvcUc/JPjQGfU5OHKIKYDJcXeA==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.202.0.tgz", + "integrity": "sha512-fTBjMqKCfotFWfLzaKyhjLvyEyq5vDKTTFfBmx21btv3gvy8Lq6N5Dh2OzqeuN4DjtpSvNT1uNVfg08eD2Rfxw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0" @@ -3225,60 +3356,60 @@ } }, "node_modules/@opentelemetry/auto-instrumentations-node": { - "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==", + "version": "0.60.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.60.1.tgz", + "integrity": "sha512-oMBVXiun0qWhj693Y24Ie+75q45YXHRFeH9vX/XBWKRNJIM/02ufjmNvmOdoHY0EPxU9rBmWCW82Uidf54iSPA==", "license": "Apache-2.0", "dependencies": { - "@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/instrumentation": "^0.202.0", + "@opentelemetry/instrumentation-amqplib": "^0.49.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.53.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.54.0", + "@opentelemetry/instrumentation-bunyan": "^0.48.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.48.0", + "@opentelemetry/instrumentation-connect": "^0.46.0", + "@opentelemetry/instrumentation-cucumber": "^0.17.0", + "@opentelemetry/instrumentation-dataloader": "^0.19.0", + "@opentelemetry/instrumentation-dns": "^0.46.0", + "@opentelemetry/instrumentation-express": "^0.51.0", + "@opentelemetry/instrumentation-fastify": "^0.47.0", + "@opentelemetry/instrumentation-fs": "^0.22.0", + "@opentelemetry/instrumentation-generic-pool": "^0.46.0", + "@opentelemetry/instrumentation-graphql": "^0.50.0", + "@opentelemetry/instrumentation-grpc": "^0.202.0", + "@opentelemetry/instrumentation-hapi": "^0.49.0", + "@opentelemetry/instrumentation-http": "^0.202.0", + "@opentelemetry/instrumentation-ioredis": "^0.50.0", + "@opentelemetry/instrumentation-kafkajs": "^0.11.0", + "@opentelemetry/instrumentation-knex": "^0.47.0", + "@opentelemetry/instrumentation-koa": "^0.50.1", + "@opentelemetry/instrumentation-lru-memoizer": "^0.47.0", + "@opentelemetry/instrumentation-memcached": "^0.46.0", + "@opentelemetry/instrumentation-mongodb": "^0.55.1", + "@opentelemetry/instrumentation-mongoose": "^0.49.0", + "@opentelemetry/instrumentation-mysql": "^0.48.0", + "@opentelemetry/instrumentation-mysql2": "^0.48.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.48.0", + "@opentelemetry/instrumentation-net": "^0.46.1", + "@opentelemetry/instrumentation-oracledb": "^0.28.0", + "@opentelemetry/instrumentation-pg": "^0.54.0", + "@opentelemetry/instrumentation-pino": "^0.49.0", + "@opentelemetry/instrumentation-redis": "^0.49.1", + "@opentelemetry/instrumentation-redis-4": "^0.49.0", + "@opentelemetry/instrumentation-restify": "^0.48.1", + "@opentelemetry/instrumentation-router": "^0.47.0", + "@opentelemetry/instrumentation-runtime-node": "^0.16.0", + "@opentelemetry/instrumentation-socket.io": "^0.49.0", + "@opentelemetry/instrumentation-tedious": "^0.21.0", + "@opentelemetry/instrumentation-undici": "^0.13.1", + "@opentelemetry/instrumentation-winston": "^0.47.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.31.2", + "@opentelemetry/resource-detector-aws": "^2.2.0", + "@opentelemetry/resource-detector-azure": "^0.9.0", + "@opentelemetry/resource-detector-container": "^0.7.2", + "@opentelemetry/resource-detector-gcp": "^0.36.0", "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-node": "^0.201.0" + "@opentelemetry/sdk-node": "^0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3316,17 +3447,17 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.202.0.tgz", + "integrity": "sha512-Y84L8Yja/A2qjGEzC/To0yrMUXHrtwJzHtZ2za1/ulZplRe5QFsLNyHixIS42ZYUKuNyWMDgOFhnN2Pz5uThtg==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", "@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" + "@opentelemetry/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0", + "@opentelemetry/sdk-logs": "0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3336,16 +3467,16 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-http": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.202.0.tgz", + "integrity": "sha512-mJWLkmoG+3r+SsYQC+sbWoy1rjowJhMhFvFULeIPTxSI+EZzKPya0+NZ3+vhhgx2UTybGQlye3FBtCH3o6Rejg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/api-logs": "0.202.0", "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.201.1", - "@opentelemetry/otlp-transformer": "0.201.1", - "@opentelemetry/sdk-logs": "0.201.1" + "@opentelemetry/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0", + "@opentelemetry/sdk-logs": "0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3355,17 +3486,17 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-proto": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.202.0.tgz", + "integrity": "sha512-qYwbmNWPkP7AbzX8o4DRu5bb/a0TWYNcpZc1NEAOhuV7pgBpAUPEClxRWPN94ulIia+PfQjzFGMaRwmLGmNP6g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/api-logs": "0.202.0", "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.201.1", - "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0", "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-logs": "0.201.1", + "@opentelemetry/sdk-logs": "0.202.0", "@opentelemetry/sdk-trace-base": "2.0.1" }, "engines": { @@ -3376,17 +3507,17 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.202.0.tgz", + "integrity": "sha512-/dq/rf4KCkTYoP+NyPXTE+5wjvfhAHSqK62vRsJ/IalG61VPQvwaL18yWcavbI+44ImQwtMeZxfIJSox7oQL0w==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", "@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/exporter-metrics-otlp-http": "0.202.0", + "@opentelemetry/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-metrics": "2.0.1" }, @@ -3398,14 +3529,14 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-http": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.202.0.tgz", + "integrity": "sha512-ooYcrf/m9ZuVGpQnER7WRH+JZbDPD389HG7VS/EnvIEF5WpNYEqf+NdmtaAcs51d81QrytTYAubc5bVWi//28w==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.201.1", - "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-metrics": "2.0.1" }, @@ -3417,15 +3548,15 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.202.0.tgz", + "integrity": "sha512-X0RpPpPjyCAmIq9tySZm0Hk3Ltw8KWsqeNq5I7gS9AR9RzbVHb/l+eiMI1CqSRvW9R47HXcUu/epmEzY8ebFAg==", "license": "Apache-2.0", "dependencies": { "@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/exporter-metrics-otlp-http": "0.202.0", + "@opentelemetry/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-metrics": "2.0.1" }, @@ -3437,9 +3568,9 @@ } }, "node_modules/@opentelemetry/exporter-prometheus": { - "version": "0.201.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.201.1.tgz", - "integrity": "sha512-J6/4KgljApWda/2YBMHHZg6vaZ6H8BjFInO8YQW+N0al1LjGAAq3pFRCEHpU6GI7ZlkphCxKy6MUjXOZVM8KWQ==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.202.0.tgz", + "integrity": "sha512-6RvQqZHAPFiwL1OKRJe4ta6SgJx/g8or41B+OovVVEie3HeCDhDGL9S1VJNkBozUz6wTY8a47fQwdMrCOUdMhQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.0.1", @@ -3454,16 +3585,16 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.202.0.tgz", + "integrity": "sha512-d5wLdbNA3ahpSeD0I34vbDFMTh4vPsXemH0bKDXLeCVULCAjOJXuZmEiuRammiDgVvvX7CAb/IGLDz8d2QHvoA==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", "@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/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1" }, @@ -3475,14 +3606,14 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.202.0.tgz", + "integrity": "sha512-/hKE8DaFCJuaQqE1IxpgkcjOolUIwgi3TgHElPVKGdGRBSmJMTmN/cr6vWa55pCJIXPyhKvcMrbrya7DZ3VmzA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.201.1", - "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1" }, @@ -3494,14 +3625,14 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-proto": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.202.0.tgz", + "integrity": "sha512-z3vzdMclCETGIn8uUBgpz7w651ftCiH2qh3cewhBk+rF0EYPNQ3mJvyxktLnKIBZ/ci0zUknAzzYC7LIIZmggQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.201.1", - "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1" }, @@ -3531,31 +3662,29 @@ } }, "node_modules/@opentelemetry/host-metrics": { - "version": "0.35.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/host-metrics/-/host-metrics-0.35.5.tgz", - "integrity": "sha512-Zf9Cjl7H6JalspnK5KD1+LLKSVecSinouVctNmUxRy+WP+20KwHq+qg4hADllkEmJ99MZByLLmEmzrr7s92V6g==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/host-metrics/-/host-metrics-0.36.0.tgz", + "integrity": "sha512-14lNY57qa21V3ZOl6xrqLMHR0HGlnPIApR6hr3oCw/Dqs5IzxhTwt2X8Stn82vWJJis7j/ezn11oODsizHj2dQ==", "license": "Apache-2.0", "dependencies": { "systeminformation": "5.23.8" }, "engines": { - "node": ">=14" + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "node_modules/@opentelemetry/instrumentation": { - "version": "0.201.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.201.1.tgz", - "integrity": "sha512-6EOSoT2zcyBM3VryAzn35ytjRrOMeaWZyzQ/PHVfxoXp5rMf7UUgVToqxOhQffKOHtC7Dma4bHt+DuwIBBZyZA==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.202.0.tgz", + "integrity": "sha512-Uz3BxZWPgDwgHM2+vCKEQRh0R8WKrd/q6Tus1vThRClhlPO39Dyz7mDrOr6KuqGXAlBQ1e5Tnymzri4RMZNaWA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.201.1", - "@types/shimmer": "^1.2.0", + "@opentelemetry/api-logs": "0.202.0", "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1", - "shimmer": "^1.2.1" + "require-in-the-middle": "^7.1.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3565,13 +3694,13 @@ } }, "node_modules/@opentelemetry/instrumentation-amqplib": { - "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==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.49.0.tgz", + "integrity": "sha512-OCGkE+1JoUN+gOzs3u0GSa7GV//KX6NMKzaPchedae7ZwFVyyBQ8VECJngHgW3k/FLABFnq9Oiym2WZGiWugVQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3582,12 +3711,12 @@ } }, "node_modules/@opentelemetry/instrumentation-aws-lambda": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.52.0.tgz", - "integrity": "sha512-xGVhBxxO7OuOl72XNwt1MOgaA6d3pSKI2Y5r3OfGNkx602KzW1t2vBHzJf8s4DAJYdMd5/RJLRi1z87CBu7yyg==", + "version": "0.53.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.53.0.tgz", + "integrity": "sha512-dZywDIc4t7o28eU9W4QMB+mNhRdH5/kVxVmxRtB46/diHg8Im6RFncuiCVJ1l9ig/RUtwR3dU9LX1huFBwxkPw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/aws-lambda": "8.10.147" }, @@ -3599,14 +3728,14 @@ } }, "node_modules/@opentelemetry/instrumentation-aws-sdk": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.53.0.tgz", - "integrity": "sha512-CXB2cu0qnp5lHtNZRpvz0oOZrIKiWfHOiNVGWln9KY0m9sBheEqc58x3Ptpi5lMyso67heVCGDAc9+KbLAZwTQ==", + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.54.0.tgz", + "integrity": "sha512-4XnXfpACX8fpOnt/D8d/1AFg3uOwBTG9TopQBuikDZJYUrLUSdT7UiotCFqAM/Z6hQJh72Jy3591C/OrmKct7A==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0", - "@opentelemetry/propagation-utils": "^0.31.1", + "@opentelemetry/instrumentation": "^0.202.0", + "@opentelemetry/propagation-utils": "^0.31.2", "@opentelemetry/semantic-conventions": "^1.31.0" }, "engines": { @@ -3617,13 +3746,13 @@ } }, "node_modules/@opentelemetry/instrumentation-bunyan": { - "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==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.48.0.tgz", + "integrity": "sha512-Q6ay5CXIKuyejadPoLboz+jKumB3Zuxyk35ycFh9vfIeww3+mNRyMVj6KxHRS0Imbv9zhNbP3uyrUpvEMMyHuw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.201.0", - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/api-logs": "^0.202.0", + "@opentelemetry/instrumentation": "^0.202.0", "@types/bunyan": "1.8.11" }, "engines": { @@ -3634,12 +3763,12 @@ } }, "node_modules/@opentelemetry/instrumentation-cassandra-driver": { - "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==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.48.0.tgz", + "integrity": "sha512-0dcX8Kx0S6ZAOknrbA+BBh1j5lg5F20W18m5VYoGUxkuLIUbWkQA3uaqeTfqbOwmnBmb1upDPUWPR+g5N12B4Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3650,13 +3779,13 @@ } }, "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.45.0.tgz", - "integrity": "sha512-OHdp71gsRnm0lVD7SEtYSJFfvq4r6QN/5lgRK+Vrife1DHy+Insm66JJZN2Frt1waIzmDNn3VLCCafTnItfVcA==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.46.0.tgz", + "integrity": "sha512-YNq/7M1JXnWRkpKPC9dbYZA36cg547gY0p1bijW7vuZJ9t5f3alo6w8TWtZwV/hOFtBGHDXVhKVfp2Mh6zVHjQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/connect": "3.4.38" }, @@ -3668,12 +3797,12 @@ } }, "node_modules/@opentelemetry/instrumentation-cucumber": { - "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==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.17.0.tgz", + "integrity": "sha512-TTfQ9DmUlbeBsYZjNdJqs8mlcn1uY3t/AsTsALDBEFg6tWV+S1ADM9kVmKnscfbCwcQX2x17f/6a1Kpq5p91ww==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3684,12 +3813,12 @@ } }, "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.18.0.tgz", - "integrity": "sha512-egPb8OcGZP6GUU/dbB8NnVgnSIqlM0nHS8KkADq51rVaMkzBcevtinYDFYTQu9tuQ6GEwaSdiQxiQORpYaVeQw==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.19.0.tgz", + "integrity": "sha512-zIVRnRs3zDZCqStQcpIdRx3Dz9WXFSVj9qimqI7CRuKao9qnrZYUVQHvvVlLZX3JAg+nDC6JRS95zvbq50hj4A==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0" + "@opentelemetry/instrumentation": "^0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3699,12 +3828,12 @@ } }, "node_modules/@opentelemetry/instrumentation-dns": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.45.0.tgz", - "integrity": "sha512-gE02Jj97aaYUdZIvp2RwWPy3DLN86k15YvPRzkMaPWZKVwsKrHcA+xVX8k3rh9o0g64PC/U2f+LXiJr14PyVLg==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.46.0.tgz", + "integrity": "sha512-m8u72x2fSIjhP1ITJX9Ims3eR4Qn8ze+QWy9NHYO01JlmiMamoc9TfIOd4dyOtxVja4tjnkWceKQdlEH9F9BoA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0" + "@opentelemetry/instrumentation": "^0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3714,13 +3843,13 @@ } }, "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.50.0.tgz", - "integrity": "sha512-0VF7HM8hTe0B5oXqCfBljMYFeQ3WKKqs0kCTRT02/Pjnmj5bOmR62r2dstjxbxnGKoeFRUHD/QAown9gyf659A==", + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.51.0.tgz", + "integrity": "sha512-v1mgfvyeQh7yfsZ8wZlr+jgFGk9FxzLfNH0EH0UYGO9das8fCIkixsEasZMWhjwAJKjlf+ElTZ2jE2pT7I3DyQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3731,13 +3860,13 @@ } }, "node_modules/@opentelemetry/instrumentation-fastify": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.46.0.tgz", - "integrity": "sha512-tib8SH5RCqhYRw9Qcpep9tP6ABxyXFDljdRy2aKpklHaFAyDELr3EpEAkGdkMZtO5Y3/QhUsmzYZp1np9jkjUg==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.47.0.tgz", + "integrity": "sha512-dLld0pI63WR1BXvNiGKFWzqrnhgItiIDNsRf/vVOhKV20HQNUQk5FfzcX0eUyiJtW/+u95Txh/vdfeQRwLELcA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3748,13 +3877,13 @@ } }, "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.21.0.tgz", - "integrity": "sha512-p2Fn78KSSbSSIJOOTn9FbxEzNRIIsYn9KTemKhABuunVqHixIqQ3hUjChbR+RbjPNZQthDC/0GHDeihRoyLdLQ==", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.22.0.tgz", + "integrity": "sha512-ktQVFD6pd8eAIW6t2DtDuXj2lxq+wnQ8WUkJLNZzl3rEE2TZEiHg7wIkWVoxl4Cz4pJ2YZJbdU2fHAizuDebDw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0" + "@opentelemetry/instrumentation": "^0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3764,12 +3893,12 @@ } }, "node_modules/@opentelemetry/instrumentation-generic-pool": { - "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==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.46.0.tgz", + "integrity": "sha512-QJUH9n5Ld0xz54gX1k3L2RDoSyJjeZaASA17Zvm0uVa40v+s8oMfCa1/4y9TONFSVbL0fPbAGojVsRRtg6dJ5w==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0" + "@opentelemetry/instrumentation": "^0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3779,12 +3908,12 @@ } }, "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.49.0.tgz", - "integrity": "sha512-FZaOS/BmE5npzk95X3Iqfo80a6wEJlkAtk7wLUJG/VZaB8RbBjJow4g0YdtvK8GNGEQW02KiQ+VtzdPGRemlwg==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.50.0.tgz", + "integrity": "sha512-Nn3vBS5T0Dv4+9WF1dGR0Lgsxuz6ztQmTsxoHvesm6YAAXiHffnwsxBEJUKEJcjxfXzjO1SVuLDkv1bAeQ3NFw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0" + "@opentelemetry/instrumentation": "^0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3794,12 +3923,12 @@ } }, "node_modules/@opentelemetry/instrumentation-grpc": { - "version": "0.201.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.201.1.tgz", - "integrity": "sha512-OIkXkVnilh8E6YKz/PiQtWeERqbcbjtVppMc7A2h39eaoaKnckXxom3YXhX+/PMhfmjbUnqw6k/KvmUr9zig1Q==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.202.0.tgz", + "integrity": "sha512-dWvefHNAyAfaHVmxQ/ySLQSI2hGKLgK1sBtvae4w9xruqU08bBMtvmVeGMA/5whfiUDU8ftp1/84U4Zoe5N56A==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "0.201.1", + "@opentelemetry/instrumentation": "0.202.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -3810,13 +3939,13 @@ } }, "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.47.0.tgz", - "integrity": "sha512-0BCiQl2+oAuhSzbZrgpZgRvg7PclTfb7GxuBqWmWj9XkRk6cKla18S0pBqRCtl+qluRIaZ7tyXKmdtlsXj0QIw==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.49.0.tgz", + "integrity": "sha512-d4BcCjbW7Pfg4FpbAAF0cK/ue3dN02WMw0uO2G792KzDjxj05MtZm3eBTz672j3ejV9hM0HvPPhUHUsIC0H6Gw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3827,13 +3956,13 @@ } }, "node_modules/@opentelemetry/instrumentation-http": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.202.0.tgz", + "integrity": "sha512-oX+jyY2KBg4/nVH3vZhSWDbhywkHgE0fq3YinhUBx0jv+YUWC2UKA7qLkxr/CSzfKsFi/Km0NKV+llH17yYGKw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.0.1", - "@opentelemetry/instrumentation": "0.201.1", + "@opentelemetry/instrumentation": "0.202.0", "@opentelemetry/semantic-conventions": "^1.29.0", "forwarded-parse": "2.1.2" }, @@ -3845,12 +3974,12 @@ } }, "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.49.0.tgz", - "integrity": "sha512-CcbA9ylntqK7/lo7NUD/I+Uj6xcIiFFk1O2RnY23MugJunqZIFufvYkdh1mdG2bvBKdIVvA2nkVVt1Igw0uw1A==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.50.0.tgz", + "integrity": "sha512-f2e+3xPxMRdlt1rjZpRhxuqrfumlWe3NX0Y+W857RBBV11HhbeZZaYbO5MMaxV3xBZv4dwPSGx96GjExUWY0WA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/redis-common": "^0.37.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, @@ -3862,12 +3991,12 @@ } }, "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.10.0.tgz", - "integrity": "sha512-0roBjhMaW5li1gXVqrBRjzeLPWUiym8TPQi3iXqMA3GizPzilE4hwhIVI7GxtMHAdS15TgkUce6WVYVOBFrrbg==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.11.0.tgz", + "integrity": "sha512-+i9VqVEPNObB1tkwcLV6zAafnve72h2Iwo48E11M/kVXMNXlgGhiYckYCmzba8c2u5XD/V98XZDrCIyO8CLCNA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "engines": { @@ -3878,13 +4007,13 @@ } }, "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.46.0.tgz", - "integrity": "sha512-+AxDwDdLJB467mEPOQKHod/1NDzX8msUAOEiViMkM7xAJoUsHTrP6EKlbjrCKkK+X2Eqh2pTO0ibeLkhG96oNA==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.47.0.tgz", + "integrity": "sha512-OjqjnzXD5+FXVGkOznbRAz9yByb4UWzIUhXjuHvOQ50IUY8mv3rM2Gj6Ar7m5JsENiS5DtAy2Vfwk4e9zNC0ng==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@opentelemetry/instrumentation": "^0.202.0", + "@opentelemetry/semantic-conventions": "^1.33.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3894,13 +4023,13 @@ } }, "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.49.0.tgz", - "integrity": "sha512-LO2pdZ5SF2LzWZLwrPTja/sQN8Kl4Wu5QvWSFJJLLGpeVKQWC4n41qjPUAAu668w43s42xqfs9bC4hWmQe7o8g==", + "version": "0.50.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.50.1.tgz", + "integrity": "sha512-HoQ9OuzLx4z6/BfA4medM6cj5+UXWQWakQVCd/Xd+gU+gA1eCxwdoECH44p+mTl3GFS7/icgfGE1if/lguaG0Q==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3911,12 +4040,12 @@ } }, "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "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==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.47.0.tgz", + "integrity": "sha512-UJ2UlCAIF+N4zNkiHdMr4O0caN0K6YboAso3/zaFdG1QiPR2zqZcbWAGFBikZ9HSByU+NwbxTXDzlpkcDZIqWg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0" + "@opentelemetry/instrumentation": "^0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3926,12 +4055,12 @@ } }, "node_modules/@opentelemetry/instrumentation-memcached": { - "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==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.46.0.tgz", + "integrity": "sha512-FFDcOVJUxZQqbg57gVskZGXRfEsZXwOvCaPv6/qIZRw5glLXPTulpnfG/s8NAltsj2buXSvS4eKFo+0HKH0apw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/memcached": "^2.2.6" }, @@ -3943,12 +4072,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.54.0.tgz", - "integrity": "sha512-xTECmvFNfavpNz7btxmmvkCZKdHphQSSf0J4tSw4OOT0CSTythB/IWo41mYBd6GIutkmeA12dkKPd8zAU7zzyA==", + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.55.1.tgz", + "integrity": "sha512-Wb13YixWm8nB27ZSQW3h070UWkivoh6bjeyDUY6lLimSUulALr+YHBn0t71U1aTcUeaZv3IBNaPRimFXhz6gBA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3959,13 +4088,13 @@ } }, "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.48.0.tgz", - "integrity": "sha512-kvopwp/kb1wN8jd0HhIBx/ZxbSmwqhN7LLvl9a7fXYACYlewUtCnVJLG80kwuG+rexRZlxeDfjoacFRDQSf9XA==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.49.0.tgz", + "integrity": "sha512-nF+43QFe8IoW20TmTJZdxZhnVZGEglODUvzAo3fRmaBFAkwUXRGzRgABS255PCjIbScEaRRDCXc6EAsSkwRNPg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3976,12 +4105,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.47.0.tgz", - "integrity": "sha512-QWJNDNW0JyHj3cGtQOeNBcrDeOY35yX/JnDg8jEvxzmoEABHyj0EqI8fHPdOQmdctTjKTjzbqwtuAzLYIfkdAA==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.48.0.tgz", + "integrity": "sha512-o7DwkkRn3eLWfzJdbXrlCS1EhbIOgB0W74eucbP+5Lk0XDGixy4yURTkmNclCcsemgzRZfEq0YvYQV29Yhpo5A==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/mysql": "2.15.26" }, @@ -3993,12 +4122,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.47.0.tgz", - "integrity": "sha512-rVKuKJ6HFVTNXNo8WuC3lBL/9zQ0OZfga/2dLseg/jlQZzUlWijsA57trnA92pcYxs32HBPSfKpuA88ZAVBFpA==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.48.0.tgz", + "integrity": "sha512-eCRpv0WV2s0Pa6CpjPWzZiLZDqx8kqZJopJESd4ywoUwtijXzBiTRidp/8aL9k+kl4drhm2GVNr4thUCMlEOSA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@opentelemetry/sql-common": "^0.41.0" }, @@ -4010,12 +4139,12 @@ } }, "node_modules/@opentelemetry/instrumentation-nestjs-core": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.47.0.tgz", - "integrity": "sha512-xTtWbqdvlxRfhYidLEq0XvQUGqqgT4Fom21nxJ7XYvOoUJ4KNOxFBnfGW9RcXtFHDkux6rIjNP5CiPCYMZ007g==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.48.0.tgz", + "integrity": "sha512-ytK4ABSkWcD9vyMU8GpinvodAGaRxBFuxybP/m7sgLtEboXMJjdWnEHb7lH/CX1ICiVKRXWdYg9npdu6yBCW5Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "engines": { @@ -4026,12 +4155,12 @@ } }, "node_modules/@opentelemetry/instrumentation-net": { - "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==", + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.46.1.tgz", + "integrity": "sha512-r7Buqem+odrTTPlWfT7EqS24QnDAL4U+c4e38RzcRtdZF00Z34oqEpge7TZcQLo0vEASWbHQ/WjWNR7ZYKFKBA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -4042,12 +4171,12 @@ } }, "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==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.28.0.tgz", + "integrity": "sha512-VObbQRd3g8nDLLOeGjm5l6TnB9dtEaJoedLfLwMGrlD6lkai+hdfalYh6FOF5dce+dJouZdW6NUUAaBj4f4KcA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/oracledb": "6.5.2" }, @@ -4059,16 +4188,16 @@ } }, "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.53.0.tgz", - "integrity": "sha512-riWbJvSviTAsjeuq8fn7Y7+CXEYf3sGR18WfLeM7GgSnptTOur1++SLTN7XogqiwP3LFFQ0GLoYe+hxVOEyEpw==", + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.54.0.tgz", + "integrity": "sha512-KQnEGwm65p1zFZGjKGw+oMilGcR4l1q3qgRmETO7ySEfMddH3t6jwlbqmcjO3N3bVcPkYgjioGVQGvdpvz7O1w==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@opentelemetry/sql-common": "^0.41.0", - "@types/pg": "8.6.1", + "@types/pg": "8.15.1", "@types/pg-pool": "2.0.6" }, "engines": { @@ -4079,14 +4208,14 @@ } }, "node_modules/@opentelemetry/instrumentation-pino": { - "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==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.49.0.tgz", + "integrity": "sha512-nngcqUnIeVnDvRMf6fixYwlMbTNzCVGv93CacyR/8TL/pjyumje020PC5q7b6CfcTdToiD5GMTMKvWBiTd08cA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.201.0", + "@opentelemetry/api-logs": "^0.202.0", "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0" + "@opentelemetry/instrumentation": "^0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4096,12 +4225,12 @@ } }, "node_modules/@opentelemetry/instrumentation-redis": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.48.0.tgz", - "integrity": "sha512-bp82CqAcBNk0+nneAX2L+wbCKiNHTnTEJlppOEjxESIR8AocSKO7gnWpotTh5Bki2UULUn62MBXJmRnIzj0ikw==", + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.49.1.tgz", + "integrity": "sha512-Ds5Ke9qE9kTlDThqLSJJntkIvuMQCBPiFKwHntocb/3q/9q5D47BNwawO5Mj9sVMV6zkld5M5Pb9Av39iieuOg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/redis-common": "^0.37.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, @@ -4113,12 +4242,12 @@ } }, "node_modules/@opentelemetry/instrumentation-redis-4": { - "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==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.49.0.tgz", + "integrity": "sha512-i+Wsl7M2LXEDA2yXouNJ3fttSzzb5AhlehvSBVRIFuinY51XrrKSH66biO0eox+pYQMwAlPxJ778XcMQffN78A==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/redis-common": "^0.37.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, @@ -4130,13 +4259,13 @@ } }, "node_modules/@opentelemetry/instrumentation-restify": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.47.0.tgz", - "integrity": "sha512-A1VixeXnRAQQfWidjnNqOwqGp1K5/r6fIyCdL+1Yvde11HiruMQOf6B71D7wWJHRtNKpLhq3o8JzeNGJoBEMpA==", + "version": "0.48.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.48.1.tgz", + "integrity": "sha512-0KY7mWpm0TJJ8ajhsNsLUmsBE/yNr70o128Crn30eDmnyRQkG7uS0xfDi6keExjF7SKzXQabs3Gtx7SuFmE80Q==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -4147,12 +4276,12 @@ } }, "node_modules/@opentelemetry/instrumentation-router": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.46.0.tgz", - "integrity": "sha512-p98dJcw0reSyfkhRwzx8HrhyjcKmyguIE0KCLcxBnvQFnPL7EfUR2up2M9ggceFiZO5GUo1gk+r/mP+B9VBsQw==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.47.0.tgz", + "integrity": "sha512-U0zA1LTDqtTWyd5e4SdoqQA/8QUOhc4LDv9U7b+8FMFTty95OF84apUdatl09Dzc51XeWPWIV7VutmSCd/zsUg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -4163,12 +4292,12 @@ } }, "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==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.16.0.tgz", + "integrity": "sha512-Q/GB9LsKLrRCEIPLAQTDQvydnLmLXBSRkYkWzwKzY/LCkOs+Cl8YiJG08p6D4CaJ6lvP0iG4kwPHk1ydNbdehg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0" + "@opentelemetry/instrumentation": "^0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4178,12 +4307,12 @@ } }, "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==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.49.0.tgz", + "integrity": "sha512-DpMtNBEcaLCcbP1WVBPCSgRiBs31igTQkal1gUm40VL/XAv5GUqRAUnvHZrQh3yPipOqzV65pdb0jJXdps/tug==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -4194,12 +4323,12 @@ } }, "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.20.0.tgz", - "integrity": "sha512-8OqIj554Rh8sll9myfDaFD1cYY8XKpxK3SMzCTZGc4BqS61gU0kd7UEydZeplrkQHDgySP4nvtFfkQCaZyTS4Q==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.21.0.tgz", + "integrity": "sha512-pt37kHYGQ8D2vBOQwyB/TKUqLPF8Q4rfTNu3whZsPOsc6QHDPXpfQISIupWAnMjAaeujF/Spg6IA04W6jXrzRQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/tedious": "^4.0.14" }, @@ -4211,13 +4340,13 @@ } }, "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.12.0.tgz", - "integrity": "sha512-SLqTWPWWwqSZVYZw3a9sdcNXsahJfimvDpYaoDd6ryvQGDlOrHVKr56gL5qD3XDVa67DmV5ZQrxRrnYUdlp3BQ==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.13.1.tgz", + "integrity": "sha512-w0e7q983oNa+dQiWOEgU+1R6H48ks6mICZKrIxY08KqZPFroPUYbH4Db7X6p8m4QhuHgI2/wEAgLf9h03ILzcg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.201.0" + "@opentelemetry/instrumentation": "^0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4227,13 +4356,13 @@ } }, "node_modules/@opentelemetry/instrumentation-winston": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.46.0.tgz", - "integrity": "sha512-/nvmsLSON9Ki8C32kOMAkzsCpFfpjI2Fvr51uAY8/8bwG258MUUN8fCbAOMaiaPEKiB807wsE/aym83LYiB0ng==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.47.0.tgz", + "integrity": "sha512-r+GqnZU/aFldQyB5QdOlxsMlH9KZ4+zJfnYplz3lbC9f9ozAIlVAeoshvWTtbv7Oxp2NnK64EfnNP1pClaGEqA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.201.0", - "@opentelemetry/instrumentation": "^0.201.0" + "@opentelemetry/api-logs": "^0.202.0", + "@opentelemetry/instrumentation": "^0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4243,13 +4372,13 @@ } }, "node_modules/@opentelemetry/otlp-exporter-base": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.202.0.tgz", + "integrity": "sha512-nMEOzel+pUFYuBJg2znGmHJWbmvMbdX5/RhoKNKowguMbURhz0fwik5tUKplLcUtl8wKPL1y9zPnPxeBn65N0Q==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-transformer": "0.201.1" + "@opentelemetry/otlp-transformer": "0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4259,15 +4388,15 @@ } }, "node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "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==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.202.0.tgz", + "integrity": "sha512-yIEHVxFA5dmYif7lZbbB66qulLLhrklj6mI2X3cuGW5hYPyUErztEmbroM+6teu/XobBi9bLHid2VT4NIaRuGg==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.201.1", - "@opentelemetry/otlp-transformer": "0.201.1" + "@opentelemetry/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4277,15 +4406,15 @@ } }, "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.201.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.201.1.tgz", - "integrity": "sha512-+q/8Yuhtu9QxCcjEAXEO8fXLjlSnrnVwfzi9jiWaMAppQp69MoagHHomQj02V2WnGjvBod5ajgkbK4IoWab50A==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.202.0.tgz", + "integrity": "sha512-5XO77QFzs9WkexvJQL9ksxL8oVFb/dfi9NWQSq7Sv0Efr9x3N+nb1iklP1TeVgxqJ7m1xWiC/Uv3wupiQGevMw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/api-logs": "0.202.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-logs": "0.201.1", + "@opentelemetry/sdk-logs": "0.202.0", "@opentelemetry/sdk-metrics": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "protobufjs": "^7.3.0" @@ -4298,9 +4427,9 @@ } }, "node_modules/@opentelemetry/propagation-utils": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.31.1.tgz", - "integrity": "sha512-YLNt7SWy4HZwI9d+4+OevQs2Gmof27TkjR3v029UGw8zFOcyONyIQhHHx7doyRbrLpWZtUc91cnCA4mKhArCXw==", + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.31.2.tgz", + "integrity": "sha512-FlJzdZ0cQY8qqOsJ/A+L/t05LvZtnsMq6vbamunVMYRi9TAy+xq37t+nT/dx3dKJ/2k409jDj9eA0Yhj9RtTug==", "license": "Apache-2.0", "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4349,9 +4478,9 @@ } }, "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { - "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==", + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.31.2.tgz", + "integrity": "sha512-Itp6duMXkAIQzmDHIf1kc6Llj/fa0BxilaELp0K6Fp9y+b0ex9LksNAQfTDFPHNine7tFoXauvvHbJFXIB6mqw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -4366,9 +4495,9 @@ } }, "node_modules/@opentelemetry/resource-detector-aws": { - "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==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.2.0.tgz", + "integrity": "sha512-6k7//RWAv4U1PeZhv0Too0Sv7sp7/A6s6g9h5ZYauPcroh2t4gOmkQSspSLYCynn34YZwn3FGbuaMwTDjHEJig==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -4383,9 +4512,9 @@ } }, "node_modules/@opentelemetry/resource-detector-azure": { - "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==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.9.0.tgz", + "integrity": "sha512-5wJwAAW2vhbqIhgaRisU1y0F5mUco59F/dKgmnnnT6YNbxjrbdUZYxKF5Wl7deJoACVdL5wi/3N97GCXPEwwCQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -4400,9 +4529,9 @@ } }, "node_modules/@opentelemetry/resource-detector-container": { - "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==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.7.2.tgz", + "integrity": "sha512-St3Krrbpvq7k0UoUNlm7Z4Xqf9HdS9R5yPslwl/WPaZpj/Bf/54WZTPmNQat+93Ey6PTX0ISKg26DfcjPemUhg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -4417,9 +4546,9 @@ } }, "node_modules/@opentelemetry/resource-detector-gcp": { - "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==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.36.0.tgz", + "integrity": "sha512-mWnEcg4tA+IDPrkETWo42psEsDN20dzYZSm4ZH8m8uiQALnNksVmf5C3An0GUEj5zrrxMasjSuv4zEH1gI40XQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -4451,12 +4580,12 @@ } }, "node_modules/@opentelemetry/sdk-logs": { - "version": "0.201.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.201.1.tgz", - "integrity": "sha512-Ug8gtpssUNUnfpotB9ZhnSsPSGDu+7LngTMgKl31mmVJwLAKyl6jC8diZrMcGkSgBh0o5dbg9puvLyR25buZfw==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.202.0.tgz", + "integrity": "sha512-pv8QiQLQzk4X909YKm0lnW4hpuQg4zHwJ4XBd5bZiXcd9urvrJNoNVKnxGHPiDVX/GiLFvr5DMYsDBQbZCypRQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/api-logs": "0.202.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1" }, @@ -4484,29 +4613,29 @@ } }, "node_modules/@opentelemetry/sdk-node": { - "version": "0.201.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.201.1.tgz", - "integrity": "sha512-OdkYe6ZEFbPq+YXhebuiYpPECIBrrKgFJoAQVATllKlB5RDQDTE4J84/8LwGfQqSxBiSK2u1aSaFpzgBVoBrKA==", + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.202.0.tgz", + "integrity": "sha512-SF9vXWVd9I5CZ69mW3GfwfLI2SHgyvEqntcg0en5y8kRp5+2PPoa3Mkgj0WzFLrbSgTw4PsXn7c7H6eSdrtV0w==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/api-logs": "0.202.0", "@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-logs-otlp-grpc": "0.202.0", + "@opentelemetry/exporter-logs-otlp-http": "0.202.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.202.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.202.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.202.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.202.0", + "@opentelemetry/exporter-prometheus": "0.202.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.202.0", + "@opentelemetry/exporter-trace-otlp-http": "0.202.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.202.0", "@opentelemetry/exporter-zipkin": "2.0.1", - "@opentelemetry/instrumentation": "0.201.1", + "@opentelemetry/instrumentation": "0.202.0", "@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-logs": "0.202.0", "@opentelemetry/sdk-metrics": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "@opentelemetry/sdk-trace-node": "2.0.1", @@ -4554,9 +4683,9 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.32.0.tgz", - "integrity": "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ==", + "version": "1.34.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.34.0.tgz", + "integrity": "sha512-aKcOkyrorBGlajjRdVoJWHTxfxO1vCNHLJVlSDaRHDIdjU+pX8IYQPvPDkYiujKLbRnWU+1TBwEt0QRgSm4SGA==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -4604,9 +4733,9 @@ } }, "node_modules/@pkgr/core": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz", - "integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", + "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", "dev": true, "license": "MIT", "engines": { @@ -4696,9 +4825,9 @@ } }, "node_modules/@react-email/button": { - "version": "0.0.19", - "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.0.19.tgz", - "integrity": "sha512-HYHrhyVGt7rdM/ls6FuuD6XE7fa7bjZTJqB2byn6/oGsfiEZaogY77OtoLL/mrQHjHjZiJadtAMSik9XLcm7+A==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.1.0.tgz", + "integrity": "sha512-fg4LtgTu5zXxaRSly9cuv6sHVF/hi1lElbRaIA8EPx5coWOBhCto6rCPfawcXpaN2oER7rNHUrcNBkI+lz5F9A==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4708,9 +4837,9 @@ } }, "node_modules/@react-email/code-block": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.0.13.tgz", - "integrity": "sha512-4DE4yPSgKEOnZMzcrDvRuD6mxsNxOex0hCYEG9F9q23geYgb2WCCeGBvIUXVzK69l703Dg4Vzrd5qUjl+JfcwA==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.1.0.tgz", + "integrity": "sha512-jSpHFsgqnQXxDIssE4gvmdtFncaFQz5D6e22BnVjcCPk/udK+0A9jRwGFEG8JD2si9ZXBmU4WsuqQEczuZn4ww==", "license": "MIT", "dependencies": { "prismjs": "^1.30.0" @@ -4747,14 +4876,14 @@ } }, "node_modules/@react-email/components": { - "version": "0.0.41", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.0.41.tgz", - "integrity": "sha512-WUI3wHwra3QS0pwrovSU6b0I0f3TvY33ph0y44LuhSYDSQlMRyeOzgoT6HRDY5FXMDF57cHYq9WoKwpwP0yd7Q==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.1.1.tgz", + "integrity": "sha512-NyPuoqd1o77HL4+58pBrpk3e5/0UZLNU11/OBnE59F2MCZ8IaZX+55Ce+HR5itKTMdqlgfncz7sGA1TDITo41g==", "license": "MIT", "dependencies": { "@react-email/body": "0.0.11", - "@react-email/button": "0.0.19", - "@react-email/code-block": "0.0.13", + "@react-email/button": "0.1.0", + "@react-email/code-block": "0.1.0", "@react-email/code-inline": "0.0.5", "@react-email/column": "0.0.13", "@react-email/container": "0.0.15", @@ -4767,11 +4896,11 @@ "@react-email/link": "0.0.12", "@react-email/markdown": "0.0.15", "@react-email/preview": "0.0.13", - "@react-email/render": "1.1.2", + "@react-email/render": "1.1.3", "@react-email/row": "0.0.12", "@react-email/section": "0.0.16", "@react-email/tailwind": "1.0.5", - "@react-email/text": "0.1.4" + "@react-email/text": "0.1.5" }, "engines": { "node": ">=18.0.0" @@ -4901,9 +5030,9 @@ } }, "node_modules/@react-email/render": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.1.2.tgz", - "integrity": "sha512-RnRehYN3v9gVlNMehHPHhyp2RQo7+pSkHDtXPvg3s0GbzM9SQMW4Qrf8GRNvtpLC4gsI+Wt0VatNRUFqjvevbw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.1.3.tgz", + "integrity": "sha512-TjjF1tdTmOqYEIWWg9wMx5q9JbQRbWmnG7owQbSGEHkNfc/c/vBu7hjfrki907lgQEAkYac9KPTyIjOKhvhJCg==", "license": "MIT", "dependencies": { "html-to-text": "^9.0.5", @@ -4955,9 +5084,9 @@ } }, "node_modules/@react-email/text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.4.tgz", - "integrity": "sha512-cMNE02y8172DocpNGh97uV5HSTawaS4CKG/zOku8Pu+m6ehBKbAjgtQZDIxhgstw8+TWraFB8ltS1DPjfG8nLA==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.5.tgz", + "integrity": "sha512-o5PNHFSE085VMXayxH+SJ1LSOtGsTv+RpNKnTiJDrJUwoBu77G3PlKOsZZQHCNyD28WsQpl9v2WcJLbQudqwPg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4967,9 +5096,9 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", - "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", "dev": true, "license": "MIT", "dependencies": { @@ -4989,10 +5118,227 @@ } } }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz", + "integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz", + "integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz", + "integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz", + "integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz", + "integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz", + "integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz", + "integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz", + "integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz", + "integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz", + "integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz", + "integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz", + "integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz", + "integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz", + "integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz", + "integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==", + "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", - "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz", + "integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==", "cpu": [ "x64" ], @@ -5004,9 +5350,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.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz", + "integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==", "cpu": [ "x64" ], @@ -5017,6 +5363,48 @@ "linux" ] }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz", + "integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz", + "integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz", + "integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==", + "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", @@ -5037,27 +5425,6 @@ "url": "https://ko-fi.com/killymxi" } }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "license": "BSD-3-Clause" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "license": "BSD-3-Clause" - }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", @@ -5105,15 +5472,15 @@ "license": "MIT" }, "node_modules/@swc/core": { - "version": "1.11.29", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.29.tgz", - "integrity": "sha512-g4mThMIpWbNhV8G2rWp5a5/Igv8/2UFRJx2yImrLGMgrDDYZIopqZ/z0jZxDgqNA1QDx93rpwNF7jGsxVWcMlA==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.12.9.tgz", + "integrity": "sha512-O+LfT2JlVMsIMWG9x+rdxg8GzpzeGtCZQfXV7cKc1PjIKUkLFf1QJ7okuseA4f/9vncu37dQ2ZcRrPKy0Ndd5g==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.21" + "@swc/types": "^0.1.23" }, "engines": { "node": ">=10" @@ -5123,16 +5490,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@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" + "@swc/core-darwin-arm64": "1.12.9", + "@swc/core-darwin-x64": "1.12.9", + "@swc/core-linux-arm-gnueabihf": "1.12.9", + "@swc/core-linux-arm64-gnu": "1.12.9", + "@swc/core-linux-arm64-musl": "1.12.9", + "@swc/core-linux-x64-gnu": "1.12.9", + "@swc/core-linux-x64-musl": "1.12.9", + "@swc/core-win32-arm64-msvc": "1.12.9", + "@swc/core-win32-ia32-msvc": "1.12.9", + "@swc/core-win32-x64-msvc": "1.12.9" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -5144,9 +5511,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "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==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.9.tgz", + "integrity": "sha512-GACFEp4nD6V+TZNR2JwbMZRHB+Yyvp14FrcmB6UCUYmhuNWjkxi+CLnEvdbuiKyQYv0zA+TRpCHZ+whEs6gwfA==", "cpu": [ "arm64" ], @@ -5161,9 +5528,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "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==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.12.9.tgz", + "integrity": "sha512-hv2kls7Ilkm2EpeJz+I9MCil7pGS3z55ZAgZfxklEuYsxpICycxeH+RNRv4EraggN44ms+FWCjtZFu0LGg2V3g==", "cpu": [ "x64" ], @@ -5178,9 +5545,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "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==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.9.tgz", + "integrity": "sha512-od9tDPiG+wMU9wKtd6y3nYJdNqgDOyLdgRRcrj1/hrbHoUPOM8wZQZdwQYGarw63iLXGgsw7t5HAF9Yc51ilFA==", "cpu": [ "arm" ], @@ -5195,9 +5562,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "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==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.9.tgz", + "integrity": "sha512-6qx1ka9LHcLzxIgn2Mros+CZLkHK2TawlXzi/h7DJeNnzi8F1Hw0Yzjp8WimxNCg6s2n+o3jnmin1oXB7gg8rw==", "cpu": [ "arm64" ], @@ -5212,9 +5579,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "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==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.9.tgz", + "integrity": "sha512-yghFZWKPVVGbUdqiD7ft23G0JX6YFGDJPz9YbLLAwGuKZ9th3/jlWoQDAw1Naci31LQhVC+oIji6ozihSuwB2A==", "cpu": [ "arm64" ], @@ -5229,9 +5596,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "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==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.9.tgz", + "integrity": "sha512-SFUxyhWLZRNL8QmgGNqdi2Q43PNyFVkRZ2zIif30SOGFSxnxcf2JNeSeBgKIGVgaLSuk6xFVVCtJ3KIeaStgRg==", "cpu": [ "x64" ], @@ -5246,9 +5613,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "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==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.9.tgz", + "integrity": "sha512-9FB0wM+6idCGTI20YsBNBg9xSWtkDBymnpaTCsZM3qDc0l4uOpJMqbfWhQvp17x7r/ulZfb2QY8RDvQmCL6AcQ==", "cpu": [ "x64" ], @@ -5263,9 +5630,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "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==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.9.tgz", + "integrity": "sha512-zHOusMVbOH9ik5RtRrMiGzLpKwxrPXgXkBm3SbUCa65HAdjV33NZ0/R9Rv1uPESALtEl2tzMYLUxYA5ECFDFhA==", "cpu": [ "arm64" ], @@ -5280,9 +5647,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "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==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.9.tgz", + "integrity": "sha512-aWZf0PqE0ot7tCuhAjRkDFf41AzzSQO0x2xRfTbnhpROp57BRJ/N5eee1VULO/UA2PIJRG7GKQky5bSGBYlFug==", "cpu": [ "ia32" ], @@ -5297,9 +5664,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "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==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.9.tgz", + "integrity": "sha512-C25fYftXOras3P3anSUeXXIpxmEkdAcsIL9yrr0j1xepTZ/yKwpnQ6g3coj8UXdeJy4GTVlR6+Ow/QiBgZQNOg==", "cpu": [ "x64" ], @@ -5332,9 +5699,9 @@ } }, "node_modules/@swc/types": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.21.tgz", - "integrity": "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==", + "version": "0.1.23", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.23.tgz", + "integrity": "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5342,23 +5709,23 @@ } }, "node_modules/@testcontainers/postgresql": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-11.0.2.tgz", - "integrity": "sha512-Z7ZrFRAien1W1NlYxCK3PP0zUk468UHwtJubJRmktDPHTE6DLITmn+U565I7roMk66NjN3eWhly1zoUM48PtmA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-11.0.3.tgz", + "integrity": "sha512-nMq0lBLguZecp9JDV4J8nlPWwxOQ4XiXUIGkV5Lvj1CMdVqbqQJe4eVrNHVC3rIM/w8nadNp5ecEoxnwkhG33w==", "dev": true, "license": "MIT", "dependencies": { - "testcontainers": "^11.0.2" + "testcontainers": "^11.0.3" } }, "node_modules/@testcontainers/redis": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-11.0.2.tgz", - "integrity": "sha512-hLaTvjv74HtAWZFz2U/GJc5cYCIyB37TR3yCBjtBZ+dp3wZ0gc1PtsKs6qMw4fOfUVPzFm1f1pj32DF55F1+KQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-11.0.3.tgz", + "integrity": "sha512-0TTXdnzvH55nMc7wj5s+3e/h29M5EEZ7cQAoCR16ZzQgzcLlXFTCIzSi02EkSZYrxayaToXrBdWCc6skisFzZw==", "dev": true, "license": "MIT", "dependencies": { - "testcontainers": "^11.0.2" + "testcontainers": "^11.0.3" } }, "node_modules/@tokenizer/inflate": { @@ -5462,9 +5829,9 @@ } }, "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, "license": "MIT", "dependencies": { @@ -5481,10 +5848,20 @@ "@types/node": "*" } }, + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, "node_modules/@types/compression": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.0.tgz", - "integrity": "sha512-g4vmPIwbTii9dX1HVioHbOolubEaf4re4vDxuzpKrzz9uI7uarBExi9begX0cXyIB85jXZ5X2A/v8rsHZxSAPw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==", "dev": true, "license": "MIT", "dependencies": { @@ -5502,9 +5879,9 @@ } }, "node_modules/@types/cookie-parser": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.8.tgz", - "integrity": "sha512-l37JqFrOJ9yQfRQkljb41l0xVphc7kg5JTjjr+pLRZ0IyZ49V4BQ8vbF4Ut2C2e+WH4al3xD3ZwYwIUfnbT4NQ==", + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.9.tgz", + "integrity": "sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5519,14 +5896,21 @@ "license": "MIT" }, "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", "license": "MIT", "dependencies": { "@types/node": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/docker-modem": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", @@ -5539,9 +5923,9 @@ } }, "node_modules/@types/dockerode": { - "version": "3.3.40", - "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.40.tgz", - "integrity": "sha512-O1ckSFYbcYv/KcnAHMLCnKQYY8/5+6CRzpsOPcQIePHRX2jG4Gmz8uXPMCXIxTGN9OYkE5eox/L67l2sGY1UYg==", + "version": "3.3.41", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.41.tgz", + "integrity": "sha512-5kOi6bcnEjqfJ68ZNV/bBvSMLNIucc0XbRmBO4hg5OoFCoP99eSRcbMysjkzV7ZxQEmmc/zMnv4A7odwuKFzDA==", "dev": true, "license": "MIT", "dependencies": { @@ -5573,16 +5957,16 @@ } }, "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, "node_modules/@types/express": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.2.tgz", - "integrity": "sha512-BtjL3ZwbCQriyb0DGw+Rt12qAXPiBTPs815lsUvtt1Grk0vLRMZNMUZ741d5rjk+UQOxfDiBZ3dxpX00vSkK3g==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", "dev": true, "license": "MIT", "dependencies": { @@ -5621,16 +6005,16 @@ "license": "MIT" }, "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true, "license": "MIT" }, "node_modules/@types/inquirer": { - "version": "8.2.10", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.2.10.tgz", - "integrity": "sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA==", + "version": "8.2.11", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.2.11.tgz", + "integrity": "sha512-15UboTvxb9SOaPG7CcXZ9dkv8lNqfiAwuh/5WxJDLjmElBt9tbx1/FDsEnJddUBKvN4mlPKvr8FyO1rAmBanzg==", "license": "MIT", "peer": true, "dependencies": { @@ -5653,16 +6037,16 @@ "license": "MIT" }, "node_modules/@types/lodash": { - "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==", + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", "dev": true, "license": "MIT" }, "node_modules/@types/luxon": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", - "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", + "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", "license": "MIT" }, "node_modules/@types/memcached": { @@ -5699,9 +6083,9 @@ } }, "node_modules/@types/multer": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", - "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", + "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", "dev": true, "license": "MIT", "dependencies": { @@ -5718,9 +6102,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", - "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", + "version": "22.15.34", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", + "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -5756,14 +6140,14 @@ } }, "node_modules/@types/pg": { - "version": "8.6.1", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", - "integrity": "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==", + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.1.tgz", + "integrity": "sha512-YKHrkGWBX5+ivzvOQ66I0fdqsQTsvxqM0AGP2i0XrVZ9DP5VA/deEbTf7VuLPGpY7fJB9uGbkZ6KjVhuHcrTkQ==", "license": "MIT", "dependencies": { "@types/node": "*", "pg-protocol": "*", - "pg-types": "^2.2.0" + "pg-types": "^4.0.1" } }, "node_modules/@types/pg-pool": { @@ -5793,9 +6177,9 @@ } }, "node_modules/@types/qs": { - "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", "dev": true, "license": "MIT" }, @@ -5807,9 +6191,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz", - "integrity": "sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==", + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", + "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", "dev": true, "license": "MIT", "dependencies": { @@ -5844,9 +6228,9 @@ "license": "MIT" }, "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", "dev": true, "license": "MIT", "dependencies": { @@ -5855,9 +6239,9 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", "dev": true, "license": "MIT", "dependencies": { @@ -5866,12 +6250,6 @@ "@types/send": "*" } }, - "node_modules/@types/shimmer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", - "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==", - "license": "MIT" - }, "node_modules/@types/ssh2": { "version": "1.15.5", "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz", @@ -5893,9 +6271,9 @@ } }, "node_modules/@types/ssh2/node_modules/@types/node": { - "version": "18.19.86", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.86.tgz", - "integrity": "sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ==", + "version": "18.19.112", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.112.tgz", + "integrity": "sha512-i+Vukt9POdS/MBI7YrrkkI5fMfwFtOjphSmt4WXYLfwqsfr6z/HdCx7LqT9M7JktGob8WNgj8nFB4TbGNE4Cog==", "dev": true, "license": "MIT", "dependencies": { @@ -5960,23 +6338,23 @@ "license": "MIT" }, "node_modules/@types/validator": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.0.tgz", - "integrity": "sha512-nh7nrWhLr6CBq9ldtw0wx+z9wKnnv/uTVLA9g/3/TcOYxbpOSZE+MhKPmWqU+K0NvThjhv12uD8MuqijB0WzEA==", + "version": "13.15.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.2.tgz", + "integrity": "sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q==", "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "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==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz", + "integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@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", + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/type-utils": "8.35.1", + "@typescript-eslint/utils": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -5990,15 +6368,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "@typescript-eslint/parser": "^8.35.1", "eslint": "^8.57.0 || ^9.0.0", "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==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -6006,16 +6384,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", - "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz", + "integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==", "dev": true, "license": "MIT", "dependencies": { - "@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", + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/typescript-estree": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "debug": "^4.3.4" }, "engines": { @@ -6030,15 +6408,37 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", - "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz", + "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.1", - "@typescript-eslint/visitor-keys": "8.32.1" + "@typescript-eslint/tsconfig-utils": "^8.35.1", + "@typescript-eslint/types": "^8.35.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz", + "integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6048,15 +6448,32 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz", + "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/@typescript-eslint/type-utils": { - "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==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz", + "integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.32.1", - "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/typescript-estree": "8.35.1", + "@typescript-eslint/utils": "8.35.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -6073,9 +6490,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", - "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz", + "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==", "dev": true, "license": "MIT", "engines": { @@ -6087,14 +6504,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", - "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz", + "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.1", - "@typescript-eslint/visitor-keys": "8.32.1", + "@typescript-eslint/project-service": "8.35.1", + "@typescript-eslint/tsconfig-utils": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -6114,9 +6533,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6140,16 +6559,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", - "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz", + "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==", "dev": true, "license": "MIT", "dependencies": { "@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" + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/typescript-estree": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6164,14 +6583,14 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", - "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz", + "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.1", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.35.1", + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6182,15 +6601,16 @@ } }, "node_modules/@vitest/coverage-v8": { - "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==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", + "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^1.0.2", - "debug": "^4.4.0", + "ast-v8-to-istanbul": "^0.3.3", + "debug": "^4.4.1", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", @@ -6205,8 +6625,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.4", - "vitest": "3.1.4" + "@vitest/browser": "3.2.4", + "vitest": "3.2.4" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -6215,14 +6635,15 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz", - "integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.4", - "@vitest/utils": "3.1.4", + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -6231,13 +6652,13 @@ } }, "node_modules/@vitest/mocker": { - "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==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.4", + "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -6246,7 +6667,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "peerDependenciesMeta": { "msw": { @@ -6257,20 +6678,10 @@ } } }, - "node_modules/@vitest/mocker/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, "node_modules/@vitest/pretty-format": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz", - "integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, "license": "MIT", "dependencies": { @@ -6281,27 +6692,28 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz", - "integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.4", - "pathe": "^2.0.3" + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz", - "integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.4", + "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -6310,27 +6722,27 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz", - "integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^3.0.2" + "tinyspy": "^4.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz", - "integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.4", - "loupe": "^3.1.3", + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" }, "funding": { @@ -6512,19 +6924,15 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "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" + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } }, "node_modules/abort-controller": { "version": "3.0.0", @@ -6551,10 +6959,19 @@ "node": ">= 0.6" } }, + "node_modules/accepts/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -6583,16 +7000,12 @@ } }, "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "license": "MIT", - "dependencies": { - "debug": "4" - }, "engines": { - "node": ">= 6.0.0" + "node": ">= 14" } }, "node_modules/ajv": { @@ -6729,8 +7142,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", @@ -6816,9 +7228,9 @@ } }, "node_modules/archiver-utils/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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -6924,8 +7336,7 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", @@ -6973,6 +7384,18 @@ "node": ">=12" } }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", + "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "estree-walker": "^3.0.3", + "js-tokens": "^9.0.1" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -6991,16 +7414,6 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/b4a": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", @@ -7021,9 +7434,9 @@ "optional": true }, "node_modules/bare-fs": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.2.tgz", - "integrity": "sha512-8wSeOia5B7LwD4+h465y73KOdj5QHsbbuoUfPBi+pXgFJIPuG7SsiOdJuijWMyfid49eD+WivpfY7KT8gbAzBA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.5.tgz", + "integrity": "sha512-1zccWBMypln0jEE05LzZt+V/8y8AQsQQqxtklqaIyg5nu6OAYFhZxPXinJTSG+kU5qyNmeLgcn9AW7eHiCHVLA==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -7152,9 +7565,9 @@ } }, "node_modules/bignumber.js": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.2.1.tgz", - "integrity": "sha512-+NzaKgOUvInq9TIUZ1+DRspzf/HApkCwD4btfuasFTdrfnOxqx853TgDpMolp+uv4RpRp7bPcEU2zKr9+fRmyw==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", + "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", "license": "MIT", "engines": { "node": "*" @@ -7218,9 +7631,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -7241,9 +7654,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", "dev": true, "funding": [ { @@ -7261,10 +7674,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -7336,9 +7749,9 @@ } }, "node_modules/bullmq": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.53.0.tgz", - "integrity": "sha512-AbzcwR+9GdgrenolOC9kApF+TkUKZpUCMiFbXgRYw9ivWhOfLCqKeajIptM7NdwhY7cpXgv+QpbweUuQZUxkyA==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.0.tgz", + "integrity": "sha512-j5ct2tdc9M8PKcjhJw+euO24BsO1wXBAkNGXYI1R1qvh7FvRldZ5wtLixLWqQ4/crafj0Vrwi+y1kXFXMwBJFA==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", @@ -7415,38 +7828,15 @@ } }, "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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "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", @@ -7500,35 +7890,6 @@ "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", @@ -7546,53 +7907,6 @@ "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", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -7636,15 +7950,14 @@ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001713", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001713.tgz", - "integrity": "sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==", + "version": "1.0.30001724", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz", + "integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==", "funding": [ { "type": "opencollective", @@ -7661,6 +7974,28 @@ ], "license": "CC-BY-4.0" }, + "node_modules/canvas": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.1.2.tgz", + "integrity": "sha512-Z/tzFAcBzoCvJlOSlCnoekh1Gu8YMn0J51+UAuXJAbW1Z6I9l2mZgdD7738MepoeeIcUdDtbMnOg6cC7GJxy/g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.3" + }, + "engines": { + "node": "^18.12.0 || >= 20.9.0" + } + }, + "node_modules/canvas/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/chai": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", @@ -7711,37 +8046,28 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "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" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" } }, "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "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": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/chrome-trace-event": { @@ -8102,15 +8428,6 @@ "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", @@ -8229,13 +8546,13 @@ "license": "MIT" }, "node_modules/core-js-compat": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", - "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", + "version": "3.43.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz", + "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.4" + "browserslist": "^4.25.0" }, "funding": { "type": "opencollective", @@ -8328,13 +8645,16 @@ } }, "node_modules/cron": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/cron/-/cron-3.5.0.tgz", - "integrity": "sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-4.3.0.tgz", + "integrity": "sha512-ciiYNLfSlF9MrDqnbMdRWFiA6oizSF7kA1osPP9lRzNu0Uu+AWog1UKy7SkckiDY2irrNjeO6qLyKnXC8oxmrw==", "license": "MIT", "dependencies": { - "@types/luxon": "~3.4.0", - "luxon": "~3.5.0" + "@types/luxon": "~3.6.0", + "luxon": "~3.6.0" + }, + "engines": { + "node": ">=18.x" } }, "node_modules/cron-parser": { @@ -8349,15 +8669,6 @@ "node": ">=12.0.0" } }, - "node_modules/cron/node_modules/luxon": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", - "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -8377,7 +8688,6 @@ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "license": "MIT", - "peer": true, "bin": { "cssesc": "bin/cssesc" }, @@ -8386,13 +8696,15 @@ } }, "node_modules/cssstyle": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.1.tgz", - "integrity": "sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.5.0.tgz", + "integrity": "sha512-/7gw8TGrvH/0g564EnhgFZogTMVe+lifpB7LWU+PEsiq5o83TUXR3fDbzTRXOJhoJwck5IS9ez3Em5LNMMO2aw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@asamuzakjp/css-color": "^3.1.2", + "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" }, "engines": { @@ -8412,6 +8724,8 @@ "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" @@ -8420,43 +8734,6 @@ "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", @@ -8464,9 +8741,9 @@ "license": "MIT" }, "node_modules/debounce": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.0.0.tgz", - "integrity": "sha512-xRetU6gL1VJbs85Mc4FoEGSjQxzpdxRyFhe3lmWFyy2EzydIcD4xzUvRJMD+NPDfMwKNhxa3PvsIOU32luIWeA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.2.0.tgz", + "integrity": "sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==", "license": "MIT", "engines": { "node": ">=18" @@ -8497,7 +8774,25 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/dedent": { "version": "1.6.0", @@ -8523,6 +8818,16 @@ "node": ">=6" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -8551,24 +8856,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -8653,8 +8940,7 @@ "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", - "peer": true + "license": "Apache-2.0" }, "node_modules/discontinuous-range": { "version": "1.0.0", @@ -8667,8 +8953,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/docker-compose": { "version": "1.2.0", @@ -8756,9 +9041,9 @@ } }, "node_modules/dockerode/node_modules/tar-fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", - "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", "dev": true, "license": "MIT", "dependencies": { @@ -8893,9 +9178,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.137", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", - "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", + "version": "1.5.171", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.171.tgz", + "integrity": "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==", "dev": true, "license": "ISC" }, @@ -8925,9 +9210,9 @@ } }, "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "dev": true, "license": "MIT", "dependencies": { @@ -9032,6 +9317,27 @@ "node": ">= 0.6" } }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "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/enhanced-resolve": { "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", @@ -9204,19 +9510,19 @@ } }, "node_modules/eslint": { - "version": "9.27.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", - "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", + "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", "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.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.27.0", + "@eslint/js": "9.30.1", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -9228,9 +9534,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -9281,14 +9587,14 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz", - "integrity": "sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", + "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", "dev": true, "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.11.0" + "synckit": "^0.11.7" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -9374,9 +9680,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -9391,9 +9697,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -9417,15 +9723,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9485,11 +9791,14 @@ } }, "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } }, "node_modules/esutils": { "version": "2.0.3", @@ -9551,6 +9860,16 @@ "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", @@ -9561,6 +9880,16 @@ "!win32" ] }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/expect-type": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", @@ -9753,9 +10082,9 @@ } }, "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", "dev": true, "license": "MIT", "peerDependencies": { @@ -9820,18 +10149,18 @@ } }, "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==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.0.0.tgz", + "integrity": "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==", "license": "MIT", "dependencies": { - "@tokenizer/inflate": "^0.2.6", - "strtok3": "^10.2.0", + "@tokenizer/inflate": "^0.2.7", + "strtok3": "^10.2.2", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sindresorhus/file-type?sponsor=1" @@ -9896,16 +10225,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/find-yarn-workspace-root": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", - "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "micromatch": "^4.0.2" - } - }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -9931,6 +10250,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "license": "MIT", "dependencies": { "async": "^0.2.9", @@ -10001,45 +10321,16 @@ "webpack": "^5.11.0" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -10132,29 +10423,16 @@ } }, "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "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": "^3.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/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": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/fs-monkey": { @@ -10171,6 +10449,20 @@ "dev": true, "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", @@ -10248,28 +10540,6 @@ "node": ">=14" } }, - "node_modules/gaxios/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/gaxios/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==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/gcp-metadata": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", @@ -10389,6 +10659,13 @@ "node": ">= 0.4" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT" + }, "node_modules/glob": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", @@ -10431,22 +10708,13 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/glob/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/glob/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { "node": "20 || >=22" @@ -10456,9 +10724,9 @@ } }, "node_modules/globals": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", - "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", "dev": true, "license": "MIT", "engines": { @@ -10549,19 +10817,6 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -10623,6 +10878,8 @@ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "whatwg-encoding": "^3.1.1" }, @@ -10673,9 +10930,9 @@ } }, "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==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", "dev": true, "license": "BSD-2-Clause" }, @@ -10695,6 +10952,15 @@ "node": ">= 0.8" } }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "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", @@ -10709,28 +10975,17 @@ "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", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "dependencies": { - "agent-base": "6", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/i18n-iso-countries": { @@ -10804,9 +11059,9 @@ } }, "node_modules/import-in-the-middle": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.13.1.tgz", - "integrity": "sha512-k2V9wNm9B+ysuelDTHjI9d5KPc4l8zAZTGqj+pcynvWkypZd857ryzN8jNC7Pg2YZXNMJcHRPpaDyCBbNyVRpA==", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.2.tgz", + "integrity": "sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==", "license": "Apache-2.0", "dependencies": { "acorn": "^8.14.0", @@ -10856,6 +11111,13 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, "node_modules/inquirer": { "version": "8.2.6", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", @@ -11014,22 +11276,6 @@ "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", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -11092,7 +11338,9 @@ "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" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/is-promise": { "version": "4.0.0", @@ -11144,26 +11392,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -11195,22 +11423,6 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/istanbul-lib-source-maps": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", @@ -11250,9 +11462,9 @@ } }, "node_modules/jackspeak": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", - "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -11300,37 +11512,24 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } }, - "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, "node_modules/jose": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.10.tgz", - "integrity": "sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw==", + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.11.tgz", + "integrity": "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } }, "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -11358,6 +11557,8 @@ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", @@ -11392,89 +11593,6 @@ } } }, - "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", @@ -11516,26 +11634,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-stable-stringify": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.2.1.tgz", - "integrity": "sha512-Lp6HbbBgosLmJbjx0pBLbgvx68FaFU1sdkmBuckmhhJ88kL13OA51CDtR2yJB50eCNMH9wRqtQNNiAqQH4YXnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "isarray": "^2.0.5", - "jsonify": "^0.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -11547,7 +11645,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -11576,16 +11673,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsonify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", - "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", - "dev": true, - "license": "Public Domain", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -11596,16 +11683,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/klaw-sync": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", - "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.11" - } - }, "node_modules/kysely": { "version": "0.28.2", "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.2.tgz", @@ -11697,9 +11774,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.12.6", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.6.tgz", - "integrity": "sha512-PJiS4ETaUfCOFLpmtKzAbqZQjCCKVu2OhTV4SVNNE7c2nu/dACvtCqj4L0i/KWNnIgRv7yrILvBj5Lonv5Ncxw==", + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.9.tgz", + "integrity": "sha512-VWwAdNeJgN7jFOD+wN4qx83DTPMVPPAUyx9/TUkBXKLiNkuWWk6anV0439tgdtwaJDrEdqkvdN22iA6J4bUCZg==", "license": "MIT" }, "node_modules/lilconfig": { @@ -11707,7 +11784,6 @@ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "license": "MIT", - "peer": true, "engines": { "node": ">=14" }, @@ -11824,15 +11900,15 @@ } }, "node_modules/long": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", - "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", + "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", "dev": true, "license": "MIT" }, @@ -11874,31 +11950,21 @@ } }, "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", "dependencies": { - "semver": "^6.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "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", @@ -11922,6 +11988,16 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/marked": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.4.tgz", @@ -12095,6 +12171,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -12167,19 +12256,6 @@ "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", @@ -12206,6 +12282,13 @@ "node": ">=8" } }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", @@ -12232,6 +12315,13 @@ "node": ">=8" } }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/minipass-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", @@ -12258,31 +12348,24 @@ "node": ">=8" } }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "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": "^3.0.0", - "yallist": "^4.0.0" + "minipass": "^7.1.2" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/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": ">= 18" } }, "node_modules/mkdirp": { @@ -12304,6 +12387,15 @@ "dev": true, "license": "MIT" }, + "node_modules/mnemonist": { + "version": "0.40.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.40.3.tgz", + "integrity": "sha512-Vjyr90sJ23CKKH/qPAgUKicw/v6pRoamxIEDFOF8uSgFME7DqPRpHgRTejWVjkdGg5dXj0/NyxZHZ9bcjH+2uQ==", + "license": "MIT", + "dependencies": { + "obliterator": "^2.0.4" + } + }, "node_modules/mock-fs": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.5.0.tgz", @@ -12315,9 +12407,9 @@ } }, "node_modules/module-details-from-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", - "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", "license": "MIT" }, "node_modules/moo": { @@ -12343,9 +12435,9 @@ "license": "MIT" }, "node_modules/msgpackr": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.2.tgz", - "integrity": "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==", + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.4.tgz", + "integrity": "sha512-uaff7RG9VIC4jacFW9xzL3jc0iM32DNHe4jYVycBcjUePT/Klnfj7pqtWJt9khvDFizmjN2TlYniYmSS2LIaZg==", "license": "MIT", "optionalDependencies": { "msgpackr-extract": "^3.0.2" @@ -12374,44 +12466,23 @@ } }, "node_modules/multer": { - "version": "1.4.5-lts.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", - "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz", + "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" + "type-is": "^1.6.18", + "xtend": "^4.0.2" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 10.16.0" } }, - "node_modules/multer/node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/multer/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, "node_modules/multer/node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -12442,36 +12513,6 @@ "node": ">= 0.6" } }, - "node_modules/multer/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/multer/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/multer/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/multer/node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -12500,7 +12541,6 @@ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "license": "MIT", - "peer": true, "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -12533,6 +12573,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -12571,9 +12618,9 @@ "license": "MIT" }, "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "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" @@ -12640,9 +12687,9 @@ } }, "node_modules/nestjs-kysely": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/nestjs-kysely/-/nestjs-kysely-1.2.0.tgz", - "integrity": "sha512-KseCGb0SXCzIYC+Hx3Z3d+kPAfSZCSK6j9UoqUV/gcBCPad9utC7itmoUw0/w5sV+Jf9pc1DKpgClP1IkflA4w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/nestjs-kysely/-/nestjs-kysely-3.0.0.tgz", + "integrity": "sha512-YA6tHBgXQYPNpMBPII2OvUOiaWjCCoh5pP5dUHirQcMUHxNFzInBL6MDk8y74rk2z/5IvAK9AUlsdPyJtToO6g==", "license": "MIT", "peerDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", @@ -12652,27 +12699,30 @@ } }, "node_modules/nestjs-otel": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/nestjs-otel/-/nestjs-otel-6.2.0.tgz", - "integrity": "sha512-F15GnWNrmHxDRdn0o2/cDx65gR7+s3xou1mEJ5vVONfOOYeneIJi1Mkf6h/Qu6NfO4SHPFPKGMXoovvTX1D8Iw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/nestjs-otel/-/nestjs-otel-7.0.0.tgz", + "integrity": "sha512-BjuzY+fJrlbooIZds15XOHvdv2LrtUiVjIBcV3DMw/VrIXK9szWe+njjMZS6Pqft9hV78M8lJE+Ux2ZowDQ/Hw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/host-metrics": "^0.35.5", + "@opentelemetry/host-metrics": "^0.36.0", "response-time": "^2.3.3" }, + "engines": { + "node": ">= 20" + }, "peerDependencies": { - "@nestjs/common": ">= 10 < 12", - "@nestjs/core": ">= 10 < 12" + "@nestjs/common": ">= 11 < 12", + "@nestjs/core": ">= 11 < 12" } }, "node_modules/next": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/next/-/next-15.3.3.tgz", - "integrity": "sha512-JqNj29hHNmCLtNvd090SyRbXJiivQ+58XjCcrC50Crb5g5u2zi7Y2YivbsEfzk6AtVI80akdOQbaMZwWB1Hthw==", + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/next/-/next-15.3.4.tgz", + "integrity": "sha512-mHKd50C+mCjam/gcnwqL1T1vPx/XQNFlXqFIVdgQdVAFY9iIQtY0IfaVflEYzKiqjeA7B0cYYMaCrmAYFjs4rA==", "license": "MIT", "dependencies": { - "@next/env": "15.3.3", + "@next/env": "15.3.4", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", @@ -12687,14 +12737,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.3.3", - "@next/swc-darwin-x64": "15.3.3", - "@next/swc-linux-arm64-gnu": "15.3.3", - "@next/swc-linux-arm64-musl": "15.3.3", - "@next/swc-linux-x64-gnu": "15.3.3", - "@next/swc-linux-x64-musl": "15.3.3", - "@next/swc-win32-arm64-msvc": "15.3.3", - "@next/swc-win32-x64-msvc": "15.3.3", + "@next/swc-darwin-arm64": "15.3.4", + "@next/swc-darwin-x64": "15.3.4", + "@next/swc-linux-arm64-gnu": "15.3.4", + "@next/swc-linux-arm64-musl": "15.3.4", + "@next/swc-linux-x64-gnu": "15.3.4", + "@next/swc-linux-x64-musl": "15.3.4", + "@next/swc-win32-arm64-msvc": "15.3.4", + "@next/swc-win32-x64-msvc": "15.3.4", "sharp": "^0.34.1" }, "peerDependencies": { @@ -12729,6 +12779,19 @@ "tslib": "^2.8.0" } }, + "node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -12736,9 +12799,9 @@ "license": "MIT" }, "node_modules/node-addon-api": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", - "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz", + "integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==", "license": "MIT", "engines": { "node": "^18 || ^20 || >= 21" @@ -12774,6 +12837,28 @@ } } }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-gyp": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.2.0.tgz", @@ -12825,26 +12910,6 @@ "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", @@ -12855,69 +12920,6 @@ "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", @@ -12934,16 +12936,6 @@ "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", @@ -12952,28 +12944,28 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.3.tgz", - "integrity": "sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.4.tgz", + "integrity": "sha512-9O00Vh89/Ld2EcVCqJ/etd7u20UhME0f/NToPfArwPEe1Don1zy4mAIz6ariRr7mJ2RDxtaDzN0WJVdVXPtZaw==", "license": "MIT-0", "engines": { "node": ">=6.0.0" } }, "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "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": "1" + "abbrev": "^3.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": ">=6" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/normalize-path": { @@ -13010,12 +13002,14 @@ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/oauth4webapi": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.5.1.tgz", - "integrity": "sha512-txg/jZQwcbaF7PMJgY7aoxc9QuCxHVFMiEkDIJ60DwDz3PbtXPQnrzo+3X4IRYGChIwWLabRBRpf1k9hO9+xrQ==", + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.5.5.tgz", + "integrity": "sha512-1K88D2GiAydGblHo39NBro5TebGXa+7tYoyIbxvqv3+haDDry7CBE1eSYuNbOSsYCCU6y0gdynVZAkm4YPw4hg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -13051,15 +13045,17 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } + "node_modules/obliterator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", + "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", + "license": "MIT" + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "license": "MIT" }, "node_modules/on-finished": { "version": "2.4.1", @@ -13106,31 +13102,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/openid-client": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.5.0.tgz", - "integrity": "sha512-fAfYaTnOYE2kQCqEJGX9KDObW2aw7IQy4jWpU/+3D3WoCFLbix5Hg6qIPQ6Js9r7f8jDUmsnnguRNCSw4wU/IQ==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.6.2.tgz", + "integrity": "sha512-Xya5TNMnnZuTM6DbHdB4q0S3ig2NTAELnii/ASie1xDEr8iiB8zZbO871OWBdrw++sd3hW6bqWjgcmSy1RTWHA==", "license": "MIT", "dependencies": { - "jose": "^6.0.10", - "oauth4webapi": "^3.5.1" + "jose": "^6.0.11", + "oauth4webapi": "^3.5.4" }, "funding": { "url": "https://github.com/sponsors/panva" @@ -13300,6 +13279,8 @@ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "entities": "^6.0.0" }, @@ -13308,11 +13289,13 @@ } }, "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==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "engines": { "node": ">=0.12" }, @@ -13342,105 +13325,6 @@ "node": ">= 0.8" } }, - "node_modules/patch-package": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", - "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@yarnpkg/lockfile": "^1.1.0", - "chalk": "^4.1.2", - "ci-info": "^3.7.0", - "cross-spawn": "^7.0.3", - "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", - "json-stable-stringify": "^1.0.2", - "klaw-sync": "^6.0.0", - "minimist": "^1.2.6", - "open": "^7.4.2", - "rimraf": "^2.6.3", - "semver": "^7.5.3", - "slash": "^2.0.0", - "tmp": "^0.0.33", - "yaml": "^2.2.2" - }, - "bin": { - "patch-package": "index.js" - }, - "engines": { - "node": ">=14", - "npm": ">5" - } - }, - "node_modules/patch-package/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/patch-package/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/patch-package/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/patch-package/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -13568,36 +13452,23 @@ "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.16.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.0.tgz", - "integrity": "sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg==", + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "license": "MIT", "dependencies": { - "pg-connection-string": "^2.9.0", - "pg-pool": "^3.10.0", - "pg-protocol": "^1.10.0", + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 16.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.2.5" + "pg-cloudflare": "^1.2.7" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -13609,16 +13480,16 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz", - "integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.0.tgz", - "integrity": "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", "license": "MIT" }, "node_modules/pg-int8": { @@ -13630,22 +13501,49 @@ "node": ">=4.0.0" } }, + "node_modules/pg-numeric": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", + "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, "node_modules/pg-pool": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.0.tgz", - "integrity": "sha512-DzZ26On4sQ0KmqnO34muPcmKbhrjmyiO4lCCR0VwEd7MjmiKf5NTg/6+apUEu0NF7ESa37CGzFxH513CoUmWnA==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.0.tgz", - "integrity": "sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", "license": "MIT" }, "node_modules/pg-types": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", + "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pg/node_modules/pg-types": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", @@ -13661,6 +13559,45 @@ "node": ">=4" } }, + "node_modules/pg/node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pg/node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pg/node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pg/node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pgpass": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", @@ -13693,7 +13630,6 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -13703,7 +13639,6 @@ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 6" } @@ -13770,7 +13705,6 @@ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "license": "MIT", - "peer": true, "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -13788,7 +13722,6 @@ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", "license": "MIT", - "peer": true, "dependencies": { "camelcase-css": "^2.0.1" }, @@ -13818,7 +13751,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" @@ -13854,7 +13786,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "postcss-selector-parser": "^6.1.1" }, @@ -13870,7 +13801,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13883,15 +13813,13 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/postgres": { - "version": "3.4.5", - "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.5.tgz", - "integrity": "sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==", + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz", + "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", "license": "Unlicense", - "peer": true, "engines": { "node": ">=12" }, @@ -13901,42 +13829,127 @@ } }, "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", + "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==", "license": "MIT", "engines": { - "node": ">=4" + "node": ">=12" } }, "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", "license": "MIT", + "dependencies": { + "obuf": "~1.1.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-range": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", + "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", + "license": "MIT" + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "dev": true, "license": "MIT", "dependencies": { - "xtend": "^4.0.0" + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, + "node_modules/prebuild-install/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prebuild-install/node_modules/tar-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/prebuild-install/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" } }, "node_modules/prelude-ls": { @@ -13950,9 +13963,9 @@ } }, "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -14092,9 +14105,9 @@ } }, "node_modules/protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -14135,9 +14148,9 @@ } }, "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "dev": true, "license": "MIT", "dependencies": { @@ -14245,6 +14258,32 @@ "node": ">= 0.8" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", @@ -14267,9 +14306,9 @@ } }, "node_modules/react-email": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.0.16.tgz", - "integrity": "sha512-auhFU+nQxAkKkP6lQhPyGsa9exwfUEzp2BwZnjHokCwphZlg30tu4t1LgdKRwGPYsi7XNGy6asbVLAUhOVpzzg==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.0.17.tgz", + "integrity": "sha512-Wppdxgio/QKNe3piccIhk6jvgiLwfOTAwud5t/NlWL8wepsirgXo09OyZCz62Qb9flFNBw+Hz5ahOkpk3JaizQ==", "license": "MIT", "dependencies": { "@babel/parser": "^7.27.0", @@ -14285,7 +14324,8 @@ "next": "^15.3.1", "normalize-path": "^3.0.0", "ora": "^8.0.0", - "socket.io": "^4.8.1" + "socket.io": "^4.8.1", + "tsconfig-paths": "4.2.0" }, "bin": { "email": "dist/cli/index.mjs" @@ -14306,21 +14346,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/react-email/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/react-email/node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -14457,19 +14482,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-email/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/react-email/node_modules/restore-cursor": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", @@ -14523,7 +14535,6 @@ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", "license": "MIT", - "peer": true, "dependencies": { "pify": "^2.3.0" } @@ -14578,9 +14589,9 @@ } }, "node_modules/readdir-glob/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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -14599,27 +14610,16 @@ } }, "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_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">= 14.18.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/redis-errors": { @@ -14855,13 +14855,13 @@ "license": "Unlicense" }, "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.44.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz", + "integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.7" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -14871,26 +14871,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.44.0", + "@rollup/rollup-android-arm64": "4.44.0", + "@rollup/rollup-darwin-arm64": "4.44.0", + "@rollup/rollup-darwin-x64": "4.44.0", + "@rollup/rollup-freebsd-arm64": "4.44.0", + "@rollup/rollup-freebsd-x64": "4.44.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", + "@rollup/rollup-linux-arm-musleabihf": "4.44.0", + "@rollup/rollup-linux-arm64-gnu": "4.44.0", + "@rollup/rollup-linux-arm64-musl": "4.44.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-musl": "4.44.0", + "@rollup/rollup-linux-s390x-gnu": "4.44.0", + "@rollup/rollup-linux-x64-gnu": "4.44.0", + "@rollup/rollup-linux-x64-musl": "4.44.0", + "@rollup/rollup-win32-arm64-msvc": "4.44.0", + "@rollup/rollup-win32-ia32-msvc": "4.44.0", + "@rollup/rollup-win32-x64-msvc": "4.44.0", "fsevents": "~2.3.2" } }, @@ -14915,7 +14915,9 @@ "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/run-async": { "version": "2.4.1", @@ -15013,6 +15015,8 @@ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "xmlchars": "^2.2.0" }, @@ -15123,24 +15127,6 @@ "dev": true, "license": "ISC" }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -15246,12 +15232,6 @@ "node": ">=8" } }, - "node_modules/shimmer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", - "license": "BSD-2-Clause" - }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -15343,6 +15323,53 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -15372,16 +15399,6 @@ "node": ">=18" } }, - "node_modules/slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/slice-source": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/slice-source/-/slice-source-0.4.1.tgz", @@ -15444,6 +15461,27 @@ } } }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "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/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -15535,9 +15573,9 @@ } }, "node_modules/socks": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", - "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", + "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==", "dev": true, "license": "MIT", "dependencies": { @@ -15564,16 +15602,6 @@ "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", @@ -15627,9 +15655,9 @@ "license": "BSD-3-Clause" }, "node_modules/sql-formatter": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.6.2.tgz", - "integrity": "sha512-ZjqOfJGuB97UeHzTJoTbadlM0h9ynehtSTHNUbGfXR4HZ4rCIoD2oIW91W+A5oE76k8hl0Uz5GD8Sx3Pt9Xa3w==", + "version": "15.6.6", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.6.6.tgz", + "integrity": "sha512-bZydXEXhaNDQBr8xYHC3a8thwcaMuTBp0CkKGjwGYDsIB26tnlWeWPwJtSQ0TEwiJcz9iJJON5mFPkx7XroHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -15641,9 +15669,9 @@ } }, "node_modules/sql-highlight": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.0.0.tgz", - "integrity": "sha512-+fLpbAbWkQ+d0JEchJT/NrRRXbYRNbG15gFpANx73EwxQB1PRjj+k/OI0GTU0J63g8ikGkJECQp9z8XEJZvPRw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", + "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", "funding": [ "https://github.com/scriptcoded/sql-highlight?sponsor=1", { @@ -15723,9 +15751,9 @@ "license": "MIT" }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -15765,9 +15793,9 @@ } }, "node_modules/streamx": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", - "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", + "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", "license": "MIT", "dependencies": { "fast-fifo": "^1.3.2", @@ -15898,7 +15926,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -15933,14 +15960,26 @@ "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==", + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, "license": "MIT", "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^7.0.0" + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strtok3": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.1.tgz", + "integrity": "sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" }, "engines": { "node": ">=18" @@ -15978,7 +16017,6 @@ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -15997,11 +16035,10 @@ } }, "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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -16011,7 +16048,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "license": "ISC", - "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -16032,7 +16068,6 @@ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "license": "BlueOak-1.0.0", - "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -16048,7 +16083,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -16064,7 +16098,6 @@ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "license": "BlueOak-1.0.0", - "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -16159,17 +16192,18 @@ "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" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/synckit": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz", - "integrity": "sha512-Q/XQKRaJiLiFIBNN+mndW7S/RHxvwzuZS6ZwmRzUBqJBv/5QIKCEwkBC8GBf8EQJKYnaFs0wOZbKTXBPj8L9oQ==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", "dev": true, "license": "MIT", "dependencies": { - "@pkgr/core": "^0.2.3", - "tslib": "^2.8.1" + "@pkgr/core": "^0.2.4" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -16209,7 +16243,6 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -16279,12 +16312,47 @@ "tailwindcss": ">=3.4.17" } }, + "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/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "license": "ISC", - "peer": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -16292,10 +16360,22 @@ "node": ">=10.13.0" } }, + "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": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -16311,9 +16391,8 @@ } ], "license": "MIT", - "peer": true, "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -16321,10 +16400,22 @@ "node": "^10 || ^12 || >=14" } }, + "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_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "dev": true, "license": "MIT", "engines": { @@ -16332,27 +16423,27 @@ } }, "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "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": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@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": ">=10" + "node": ">=18" } }, "node_modules/tar-fs": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.9.tgz", - "integrity": "sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.10.tgz", + "integrity": "sha512-C1SwlQGNLe/jPNqapK8epDsXME7CAJR5RL3GcE6KWx1d9OUByzoHVcbu1VPI8tevg9H8Alae0AApHHFGzrD5zA==", "dev": true, "license": "MIT", "dependencies": { @@ -16375,38 +16466,31 @@ "streamx": "^2.15.0" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "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": "bin/cmd.js" + "mkdirp": "dist/cjs/src/bin.js" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/terser": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", - "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -16508,9 +16592,9 @@ "license": "MIT" }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16550,9 +16634,9 @@ } }, "node_modules/test-exclude/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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16630,9 +16714,9 @@ } }, "node_modules/testcontainers": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.0.2.tgz", - "integrity": "sha512-rI+CiMYGxd1t/fTW+xs79OHkhBIVLCh7Xstm6duYhKH+LZGyoWtXJsljLvkvWV0J8y8FovcG2Z6/1j1KPqM3rA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.0.3.tgz", + "integrity": "sha512-Xu6ZAaE1FaLyHzFSYdCsd+xMPxUegUjkum0r6zgO8SinnFDHRX/PllIHMt1D+DVUmJqBvPQI6vge/J5jgE5vng==", "dev": true, "license": "MIT", "dependencies": { @@ -16643,7 +16727,7 @@ "byline": "^5.0.0", "debug": "^4.4.1", "docker-compose": "^1.2.0", - "dockerode": "^4.0.6", + "dockerode": "^4.0.7", "get-port": "^7.1.0", "proper-lockfile": "^4.1.2", "properties-reader": "^2.3.0", @@ -16684,7 +16768,6 @@ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "license": "MIT", - "peer": true, "dependencies": { "any-promise": "^1.0.0" } @@ -16694,7 +16777,6 @@ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "license": "MIT", - "peer": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -16729,9 +16811,9 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "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": { @@ -16746,9 +16828,9 @@ } }, "node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", "engines": { @@ -16766,9 +16848,9 @@ } }, "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", "dev": true, "license": "MIT", "engines": { @@ -16781,6 +16863,8 @@ "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tldts-core": "^6.1.86" }, @@ -16793,7 +16877,9 @@ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/tmp": { "version": "0.0.33", @@ -16860,6 +16946,8 @@ "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { "tldts": "^6.1.32" }, @@ -16868,10 +16956,19 @@ } }, "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } }, "node_modules/tree-kill": { "version": "1.2.2", @@ -16909,13 +17006,12 @@ "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", - "peer": true + "license": "Apache-2.0" }, "node_modules/tsconfck": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.5.tgz", - "integrity": "sha512-CLDfGgUp7XPswWnezWwsCRxNmgQjhYq3VXHM0/XIRxhVrKw0M1if9agzryh1QS3nxjCROvV+xWxoJO1YctzzWg==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", + "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", "dev": true, "license": "MIT", "bin": { @@ -16937,7 +17033,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, "license": "MIT", "dependencies": { "json5": "^2.2.2", @@ -16970,6 +17065,19 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -17023,9 +17131,9 @@ "license": "MIT" }, "node_modules/typeorm": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.24.tgz", - "integrity": "sha512-4IrHG7A0tY8l5gEGXfW56VOMfUVWEkWlH/h5wmcyZ+V8oCiLj7iTPp0lEjMEZVrxEkGSdP9ErgTKHKXQApl/oA==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.25.tgz", + "integrity": "sha512-fTKDFzWXKwAaBdEMU4k661seZewbNYET4r1J/z3Jwf+eAvlzMVpTLKAVcAzg75WwQk7GDmtsmkZ5MfkmXCiFWg==", "license": "MIT", "dependencies": { "@sqltools/formatter": "^1.2.5", @@ -17129,9 +17237,9 @@ } }, "node_modules/typeorm/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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -17255,15 +17363,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.32.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz", - "integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.1.tgz", + "integrity": "sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.32.1", - "@typescript-eslint/parser": "8.32.1", - "@typescript-eslint/utils": "8.32.1" + "@typescript-eslint/eslint-plugin": "8.35.1", + "@typescript-eslint/parser": "8.35.1", + "@typescript-eslint/utils": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -17298,9 +17406,9 @@ "license": "MIT" }, "node_modules/ua-parser-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-2.0.3.tgz", - "integrity": "sha512-LZyXZdNttONW8LjzEH3Z8+6TE7RfrEiJqDKyh0R11p/kxvrV2o9DrT2FGZO+KVNs3k+drcIQ6C3En6wLnzJGpw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-2.0.4.tgz", + "integrity": "sha512-XiBOnM/UpUq21ZZ91q2AVDOnGROE6UQd37WrO9WBgw4u2eGvUCNOheMmZ3EfEUj7DLHr8tre+Um/436Of/Vwzg==", "funding": [ { "type": "opencollective", @@ -17438,9 +17546,9 @@ } }, "node_modules/unplugin": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.4.tgz", - "integrity": "sha512-m4PjxTurwpWfpMomp8AptjD5yj8qEZN5uQjjGM3TAs9MWWD2tXSSNNj6jGR2FoVGod4293ytyV6SwBbertfyJg==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.5.tgz", + "integrity": "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==", "dev": true, "license": "MIT", "dependencies": { @@ -17453,15 +17561,15 @@ } }, "node_modules/unplugin-swc": { - "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==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/unplugin-swc/-/unplugin-swc-1.5.5.tgz", + "integrity": "sha512-BahYtYvQ/KSgOqHoy5FfQgp/oZNAB7jwERxNeFVeN/PtJhg4fpK/ybj9OwKtqGPseOadS7+TGbq6tH2DmDAYvA==", "dev": true, "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^5.1.4", + "@rollup/pluginutils": "^5.2.0", "load-tsconfig": "^0.2.5", - "unplugin": "^2.3.4" + "unplugin": "^2.3.5" }, "peerDependencies": { "@swc/core": "^1.2.108" @@ -17556,9 +17664,9 @@ } }, "node_modules/validator": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.0.tgz", - "integrity": "sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==", + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -17574,15 +17682,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" @@ -17646,17 +17757,17 @@ } }, "node_modules/vite-node": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz", - "integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.4.0", + "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" @@ -17689,9 +17800,9 @@ } }, "node_modules/vite/node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -17709,7 +17820,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -17718,32 +17829,34 @@ } }, "node_modules/vitest": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz", - "integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", "dependencies": { - "@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", + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", "chai": "^5.2.0", - "debug": "^4.4.0", + "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", + "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.13", - "tinypool": "^1.0.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.4", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -17759,8 +17872,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.4", - "@vitest/ui": "3.1.4", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, @@ -17794,6 +17907,8 @@ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "xml-name-validator": "^5.0.0" }, @@ -17802,9 +17917,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "dev": true, "license": "MIT", "dependencies": { @@ -17825,10 +17940,16 @@ } }, "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" + "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", + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + } }, "node_modules/webpack": { "version": "5.99.6", @@ -17888,9 +18009,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, "license": "MIT", "engines": { @@ -18007,9 +18128,9 @@ } }, "node_modules/webpack/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", "dev": true, "license": "MIT", "dependencies": { @@ -18032,6 +18153,8 @@ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "iconv-lite": "0.6.3" }, @@ -18045,18 +18168,26 @@ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18" } }, "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "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", + "optional": true, + "peer": true, "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/which": { @@ -18198,10 +18329,13 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -18224,6 +18358,8 @@ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": ">=18" } @@ -18233,7 +18369,9 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/xtend": { "version": "4.0.2", @@ -18254,22 +18392,25 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "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": "ISC" + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } }, "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==", "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" } }, "node_modules/yargs": { diff --git a/server/package.json b/server/package.json index d2fd777be3..ab8424329f 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.134.0", + "version": "1.135.3", "description": "", "author": "", "private": true, @@ -29,11 +29,9 @@ "migrations:run": "node ./dist/bin/migrations.js 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", - "email:dev": "email dev -p 3050 --dir src/emails", - "postinstall": "patch-package" + "email:dev": "email dev -p 3050 --dir src/emails" }, "dependencies": { "@nestjs/bullmq": "^11.0.1", @@ -42,46 +40,61 @@ "@nestjs/event-emitter": "^3.0.0", "@nestjs/platform-express": "^11.0.4", "@nestjs/platform-socket.io": "^11.0.4", - "@nestjs/schedule": "^5.0.0", + "@nestjs/schedule": "^6.0.0", "@nestjs/swagger": "^11.0.2", "@nestjs/websockets": "^11.0.4", - "@opentelemetry/auto-instrumentations-node": "^0.59.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/auto-instrumentations-node": "^0.60.0", "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/exporter-prometheus": "^0.201.0", - "@opentelemetry/sdk-node": "^0.201.0", - "@react-email/components": "^0.0.41", + "@opentelemetry/exporter-prometheus": "^0.202.0", + "@opentelemetry/instrumentation-http": "^0.202.0", + "@opentelemetry/instrumentation-ioredis": "^0.50.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.48.0", + "@opentelemetry/instrumentation-pg": "^0.54.0", + "@opentelemetry/resources": "^2.0.1", + "@opentelemetry/sdk-metrics": "^2.0.1", + "@opentelemetry/sdk-node": "^0.202.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@react-email/components": "^0.1.0", + "@react-email/render": "^1.1.2", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", "async-lock": "^1.4.0", "bcrypt": "^6.0.0", + "body-parser": "^2.2.0", "bullmq": "^5.51.0", - "chokidar": "^3.5.3", + "chokidar": "^4.0.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", + "cron": "4.3.0", + "exiftool-vendored": "^28.8.0", + "express": "^5.1.0", "fast-glob": "^3.3.2", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", "handlebars": "^4.7.8", "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", - "joi": "^17.10.0", "js-yaml": "^4.1.0", "kysely": "^0.28.0", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", + "mnemonist": "^0.40.3", + "multer": "^2.0.1", "nest-commander": "^3.16.0", "nestjs-cls": "^5.0.0", - "nestjs-kysely": "^1.1.0", - "nestjs-otel": "^6.0.0", + "nestjs-kysely": "^3.0.0", + "nestjs-otel": "^7.0.0", "nodemailer": "^7.0.0", "openid-client": "^6.3.3", "pg": "^8.11.3", + "pg-connection-string": "^2.9.1", "picomatch": "^4.0.2", + "postgres": "3.4.7", "react": "^19.0.0", "react-dom": "^19.0.0", "react-email": "^4.0.0", @@ -92,13 +105,15 @@ "semver": "^7.6.2", "sharp": "^0.34.2", "sirv": "^3.0.0", - "tailwindcss-preset-email": "^1.3.2", + "socket.io": "^4.8.1", + "tailwindcss-preset-email": "^1.4.0", "thumbhash": "^0.1.1", "typeorm": "^0.3.17", "ua-parser-js": "^2.0.0", "validator": "^13.12.0" }, "devDependencies": { + "canvas": "^3.1.0", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.8.0", "@nestjs/cli": "^11.0.2", @@ -110,15 +125,17 @@ "@types/archiver": "^6.0.0", "@types/async-lock": "^1.4.2", "@types/bcrypt": "^5.0.0", + "@types/body-parser": "^1.19.6", "@types/compression": "^1.7.5", "@types/cookie-parser": "^1.4.8", "@types/express": "^5.0.0", "@types/fluent-ffmpeg": "^2.1.21", "@types/js-yaml": "^4.0.9", "@types/lodash": "^4.14.197", + "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^22.15.29", + "@types/node": "^22.15.33", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", @@ -127,17 +144,16 @@ "@types/semver": "^7.5.8", "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", + "@types/validator": "^13.15.2", "@vitest/coverage-v8": "^3.0.0", "eslint": "^9.14.0", "eslint-config-prettier": "^10.0.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", - "jsdom": "^26.1.0", "mock-fs": "^5.2.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", "prettier-plugin-organize-imports": "^4.0.0", @@ -145,6 +161,7 @@ "source-map-support": "^0.5.21", "sql-formatter": "^15.0.0", "supertest": "^7.1.0", + "tailwindcss": "^3.4.0", "testcontainers": "^11.0.0", "tsconfig-paths": "^4.2.0", "typescript": "^5.3.3", @@ -155,7 +172,7 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.16.0" + "node": "22.17.0" }, "overrides": { "sharp": "^0.34.2" diff --git a/server/patches/postgres+3.4.5.patch b/server/patches/postgres+3.4.5.patch deleted file mode 100644 index 019ef9df78..0000000000 --- a/server/patches/postgres+3.4.5.patch +++ /dev/null @@ -1,48 +0,0 @@ -diff --git a/node_modules/postgres/cf/src/connection.js b/node_modules/postgres/cf/src/connection.js -index ee8b1e6..acf4566 100644 ---- a/node_modules/postgres/cf/src/connection.js -+++ b/node_modules/postgres/cf/src/connection.js -@@ -387,8 +387,10 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose - } - - function queryError(query, err) { -+ if (!query || typeof query !== 'object' || !query.reject) throw err -+ - 'query' in err || 'parameters' in err || Object.defineProperties(err, { -- stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug }, -+ stack: { value: err.stack + (query.origin || '').replace(/.*\n/, '\n'), enumerable: options.debug }, - query: { value: query.string, enumerable: options.debug }, - parameters: { value: query.parameters, enumerable: options.debug }, - args: { value: query.args, enumerable: options.debug }, -diff --git a/node_modules/postgres/cjs/src/connection.js b/node_modules/postgres/cjs/src/connection.js -index f7f58d1..b7f2d65 100644 ---- a/node_modules/postgres/cjs/src/connection.js -+++ b/node_modules/postgres/cjs/src/connection.js -@@ -385,8 +385,10 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose - } - - function queryError(query, err) { -+ if (!query || typeof query !== 'object' || !query.reject) throw err -+ - 'query' in err || 'parameters' in err || Object.defineProperties(err, { -- stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug }, -+ stack: { value: err.stack + (query.origin || '').replace(/.*\n/, '\n'), enumerable: options.debug }, - query: { value: query.string, enumerable: options.debug }, - parameters: { value: query.parameters, enumerable: options.debug }, - args: { value: query.args, enumerable: options.debug }, -diff --git a/node_modules/postgres/src/connection.js b/node_modules/postgres/src/connection.js -index 97cc97e..26f508e 100644 ---- a/node_modules/postgres/src/connection.js -+++ b/node_modules/postgres/src/connection.js -@@ -385,8 +385,10 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose - } - - function queryError(query, err) { -+ if (!query || typeof query !== 'object' || !query.reject) throw err -+ - 'query' in err || 'parameters' in err || Object.defineProperties(err, { -- stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug }, -+ stack: { value: err.stack + (query.origin || '').replace(/.*\n/, '\n'), enumerable: options.debug }, - query: { value: query.string, enumerable: options.debug }, - parameters: { value: query.parameters, enumerable: options.debug }, - args: { value: query.args, enumerable: options.debug }, diff --git a/server/src/bin/migrations.ts b/server/src/bin/migrations.ts index b3329e6331..81f5c7e0ab 100644 --- a/server/src/bin/migrations.ts +++ b/server/src/bin/migrations.ts @@ -107,25 +107,21 @@ const compare = async () => { const { database } = configRepository.getEnv(); const db = postgres(asPostgresConnectionConfig(database.config)); - const source = schemaFromCode(); + const source = schemaFromCode({ overrides: true }); const target = await schemaFromDatabase(db, {}); - const sourceParams = new Set(source.parameters.map(({ name }) => name)); - target.parameters = target.parameters.filter(({ name }) => sourceParams.has(name)); - - const sourceTables = new Set(source.tables.map(({ name }) => name)); - target.tables = target.tables.filter(({ name }) => sourceTables.has(name)); - console.log(source.warnings.join('\n')); const up = schemaDiff(source, target, { tables: { ignoreExtra: true }, functions: { ignoreExtra: false }, + parameters: { ignoreExtra: true }, }); const down = schemaDiff(target, source, { - tables: { ignoreExtra: false }, + tables: { ignoreExtra: false, ignoreMissing: true }, functions: { ignoreExtra: false }, - extension: { ignoreMissing: true }, + extensions: { ignoreMissing: true }, + parameters: { ignoreMissing: true }, }); return { up, down }; diff --git a/server/src/bin/sync-sql.ts b/server/src/bin/sync-sql.ts index a114830e09..6d3cb42fae 100644 --- a/server/src/bin/sync-sql.ts +++ b/server/src/bin/sync-sql.ts @@ -15,6 +15,7 @@ import { repositories } from 'src/repositories'; import { AccessRepository } from 'src/repositories/access.repository'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { SyncRepository } from 'src/repositories/sync.repository'; import { AuthService } from 'src/services/auth.service'; import { getKyselyConfig } from 'src/utils/database'; @@ -111,7 +112,7 @@ class SqlGenerator { data.push(...(await this.runTargets(instance, `${Repository.name}`))); // nested repositories - if (Repository.name === AccessRepository.name) { + if (Repository.name === AccessRepository.name || Repository.name === SyncRepository.name) { for (const key of Object.keys(instance)) { const subInstance = (instance as any)[key]; data.push(...(await this.runTargets(subInstance, `${Repository.name}.${key}`))); diff --git a/server/src/commands/grant-admin.ts b/server/src/commands/grant-admin.ts new file mode 100644 index 0000000000..41fd4f2340 --- /dev/null +++ b/server/src/commands/grant-admin.ts @@ -0,0 +1,67 @@ +import { Command, CommandRunner, InquirerService, Question, QuestionSet } from 'nest-commander'; +import { CliService } from 'src/services/cli.service'; + +const prompt = (inquirer: InquirerService) => { + return function ask(): Promise { + return inquirer.ask<{ email: string }>('prompt-email', {}).then(({ email }: { email: string }) => email); + }; +}; + +@Command({ + name: 'grant-admin', + description: 'Grant admin privileges to a user (by email)', +}) +export class GrantAdminCommand extends CommandRunner { + constructor( + private service: CliService, + private inquirer: InquirerService, + ) { + super(); + } + + async run(): Promise { + try { + const email = await prompt(this.inquirer)(); + await this.service.grantAdminAccess(email); + console.debug('Admin access has been granted to', email); + } catch (error) { + console.error(error); + console.error('Unable to grant admin access to user'); + } + } +} + +@Command({ + name: 'revoke-admin', + description: 'Revoke admin privileges from a user (by email)', +}) +export class RevokeAdminCommand extends CommandRunner { + constructor( + private service: CliService, + private inquirer: InquirerService, + ) { + super(); + } + + async run(): Promise { + try { + const email = await prompt(this.inquirer)(); + await this.service.revokeAdminAccess(email); + console.debug('Admin access has been revoked from', email); + } catch (error) { + console.error(error); + console.error('Unable to revoke admin access from user'); + } + } +} + +@QuestionSet({ name: 'prompt-email' }) +export class PromptEmailQuestion { + @Question({ + message: 'Please enter the user email: ', + name: 'email', + }) + parseEmail(value: string) { + return value; + } +} diff --git a/server/src/commands/index.ts b/server/src/commands/index.ts index 59846628bf..ce085f6e34 100644 --- a/server/src/commands/index.ts +++ b/server/src/commands/index.ts @@ -1,3 +1,4 @@ +import { GrantAdminCommand, PromptEmailQuestion, RevokeAdminCommand } from 'src/commands/grant-admin'; import { ListUsersCommand } from 'src/commands/list-users.command'; import { DisableOAuthLogin, EnableOAuthLogin } from 'src/commands/oauth-login'; import { DisablePasswordLoginCommand, EnablePasswordLoginCommand } from 'src/commands/password-login'; @@ -7,10 +8,13 @@ import { VersionCommand } from 'src/commands/version.command'; export const commands = [ ResetAdminPasswordCommand, PromptPasswordQuestions, + PromptEmailQuestion, EnablePasswordLoginCommand, DisablePasswordLoginCommand, EnableOAuthLogin, DisableOAuthLogin, ListUsersCommand, VersionCommand, + GrantAdminCommand, + RevokeAdminCommand, ]; diff --git a/server/src/config.ts b/server/src/config.ts index a9fdffbd62..1fcc2e9782 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -89,7 +89,7 @@ export interface SystemConfig { buttonText: string; clientId: string; clientSecret: string; - defaultStorageQuota: number; + defaultStorageQuota: number | null; enabled: boolean; issuerUrl: string; mobileOverrideEnabled: boolean; @@ -101,6 +101,7 @@ export interface SystemConfig { timeout: number; storageLabelClaim: string; storageQuotaClaim: string; + roleClaim: string; }; passwordLogin: { enabled: boolean; @@ -253,7 +254,7 @@ export const defaults = Object.freeze({ buttonText: 'Login with OAuth', clientId: '', clientSecret: '', - defaultStorageQuota: 0, + defaultStorageQuota: null, enabled: false, issuerUrl: '', mobileOverrideEnabled: false, @@ -263,6 +264,7 @@ export const defaults = Object.freeze({ profileSigningAlgorithm: 'none', storageLabelClaim: 'preferred_username', storageQuotaClaim: 'immich_quota', + roleClaim: 'immich_role', tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod.CLIENT_SECRET_POST, timeout: 30_000, }, diff --git a/server/src/controllers/auth.controller.spec.ts b/server/src/controllers/auth.controller.spec.ts index 4129b24124..031ef460c2 100644 --- a/server/src/controllers/auth.controller.spec.ts +++ b/server/src/controllers/auth.controller.spec.ts @@ -2,6 +2,7 @@ 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 { mediumFactory } from 'test/medium.factory'; import { errorDto } from 'test/medium/responses'; import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; @@ -132,6 +133,50 @@ describe(AuthController.name, () => { expect(status).toEqual(201); expect(service.login).toHaveBeenCalledWith(expect.objectContaining({ email: 'admin@local' }), expect.anything()); }); + + it('should auth cookies on a secure connection', async () => { + const loginResponse = mediumFactory.loginResponse(); + service.login.mockResolvedValue(loginResponse); + const { status, body, headers } = await request(ctx.getHttpServer()) + .post('/auth/login') + .send({ name: 'admin', email: 'admin@local', password: 'password' }); + + expect(status).toEqual(201); + expect(body).toEqual(loginResponse); + + const cookies = headers['set-cookie']; + expect(cookies).toHaveLength(3); + expect(cookies[0].split(';').map((item) => item.trim())).toEqual([ + `immich_access_token=${loginResponse.accessToken}`, + 'Max-Age=34560000', + 'Path=/', + expect.stringContaining('Expires='), + 'HttpOnly', + 'SameSite=Lax', + ]); + expect(cookies[1].split(';').map((item) => item.trim())).toEqual([ + 'immich_auth_type=password', + 'Max-Age=34560000', + 'Path=/', + expect.stringContaining('Expires='), + 'HttpOnly', + 'SameSite=Lax', + ]); + expect(cookies[2].split(';').map((item) => item.trim())).toEqual([ + 'immich_is_authenticated=true', + 'Max-Age=34560000', + 'Path=/', + expect.stringContaining('Expires='), + 'SameSite=Lax', + ]); + }); + }); + + describe('POST /auth/logout', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/auth/logout'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); }); describe('POST /auth/change-password', () => { diff --git a/server/src/controllers/duplicate.controller.ts b/server/src/controllers/duplicate.controller.ts index f62d29d077..f6b09e6e7a 100644 --- a/server/src/controllers/duplicate.controller.ts +++ b/server/src/controllers/duplicate.controller.ts @@ -1,9 +1,11 @@ -import { Controller, Get } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; +import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { DuplicateResponseDto } from 'src/dtos/duplicate.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { DuplicateService } from 'src/services/duplicate.service'; +import { UUIDParamDto } from 'src/validation'; @ApiTags('Duplicates') @Controller('duplicates') @@ -15,4 +17,16 @@ export class DuplicateController { getAssetDuplicates(@Auth() auth: AuthDto): Promise { return this.service.getDuplicates(auth); } + + @Delete() + @Authenticated() + deleteDuplicates(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise { + return this.service.deleteAll(auth, dto); + } + + @Delete(':id') + @Authenticated() + deleteDuplicate(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.delete(auth, id); + } } diff --git a/server/src/controllers/memory.controller.spec.ts b/server/src/controllers/memory.controller.spec.ts new file mode 100644 index 0000000000..ac96e54a5b --- /dev/null +++ b/server/src/controllers/memory.controller.spec.ts @@ -0,0 +1,132 @@ +import { MemoryController } from 'src/controllers/memory.controller'; +import { MemoryService } from 'src/services/memory.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(MemoryController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(MemoryService); + + beforeAll(async () => { + ctx = await controllerSetup(MemoryController, [{ provide: MemoryService, useValue: service }]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('GET /memories', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/memories'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('POST /memories', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/memories'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should validate data when type is on this day', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/memories') + .send({ + type: 'on_this_day', + data: {}, + memoryAt: new Date(2021).toISOString(), + }); + + expect(status).toBe(400); + expect(body).toEqual( + errorDto.badRequest(['data.year must be a positive number', 'data.year must be an integer number']), + ); + }); + }); + + describe('GET /memories/statistics', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/memories/statistics'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /memories/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/memories/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).get(`/memories/invalid`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); + }); + }); + + describe('PUT /memories/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/memories/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).put(`/memories/invalid`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); + }); + }); + + describe('DELETE /memories/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete(`/memories/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('PUT /memories/:id/assets', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/memories/${factory.uuid()}/assets`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).put(`/memories/invalid/assets`).send({ ids: [] }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); + }); + + it('should require a valid asset id', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/memories/${factory.uuid()}/assets`) + .send({ ids: ['invalid'] }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['each value in ids must be a UUID'])); + }); + }); + + describe('DELETE /memories/:id/assets', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete(`/memories/${factory.uuid()}/assets`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).delete(`/memories/invalid/assets`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); + }); + + it('should require a valid asset id', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .delete(`/memories/${factory.uuid()}/assets`) + .send({ ids: ['invalid'] }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['each value in ids must be a UUID'])); + }); + }); +}); diff --git a/server/src/controllers/memory.controller.ts b/server/src/controllers/memory.controller.ts index 1f848ad705..d33c5ec22c 100644 --- a/server/src/controllers/memory.controller.ts +++ b/server/src/controllers/memory.controller.ts @@ -2,7 +2,13 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, import { ApiTags } from '@nestjs/swagger'; import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { MemoryCreateDto, MemoryResponseDto, MemorySearchDto, MemoryUpdateDto } from 'src/dtos/memory.dto'; +import { + MemoryCreateDto, + MemoryResponseDto, + MemorySearchDto, + MemoryStatisticsResponseDto, + MemoryUpdateDto, +} from 'src/dtos/memory.dto'; import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { MemoryService } from 'src/services/memory.service'; @@ -25,6 +31,12 @@ export class MemoryController { return this.service.create(auth, dto); } + @Get('statistics') + @Authenticated({ permission: Permission.MEMORY_READ }) + memoriesStatistics(@Auth() auth: AuthDto, @Query() dto: MemorySearchDto): Promise { + return this.service.statistics(auth, dto); + } + @Get(':id') @Authenticated({ permission: Permission.MEMORY_READ }) getMemory(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { diff --git a/server/src/controllers/person.controller.spec.ts b/server/src/controllers/person.controller.spec.ts index 0366829336..5b63fcc6cd 100644 --- a/server/src/controllers/person.controller.spec.ts +++ b/server/src/controllers/person.controller.spec.ts @@ -60,6 +60,29 @@ describe(PersonController.name, () => { }); }); + describe('DELETE /people', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete('/people'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require uuids in the body', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .delete('/people') + .send({ ids: ['invalid'] }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([expect.stringContaining('must be a UUID')])); + }); + + it('should respond with 204', async () => { + const { status } = await request(ctx.getHttpServer()) + .delete(`/people`) + .send({ ids: [factory.uuid()] }); + expect(status).toBe(204); + expect(service.deleteAll).toHaveBeenCalled(); + }); + }); + describe('GET /people/:id', () => { it('should be an authenticated route', async () => { await request(ctx.getHttpServer()).get(`/people/${factory.uuid()}`); @@ -156,6 +179,25 @@ describe(PersonController.name, () => { }); }); + describe('DELETE /people/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete(`/people/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid uuid', async () => { + const { status, body } = await request(ctx.getHttpServer()).delete(`/people/invalid`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([expect.stringContaining('must be a UUID')])); + }); + + it('should respond with 204', async () => { + const { status } = await request(ctx.getHttpServer()).delete(`/people/${factory.uuid()}`); + expect(status).toBe(204); + expect(service.delete).toHaveBeenCalled(); + }); + }); + describe('POST /people/:id/merge', () => { it('should be an authenticated route', async () => { await request(ctx.getHttpServer()).post(`/people/${factory.uuid()}/merge`); diff --git a/server/src/controllers/person.controller.ts b/server/src/controllers/person.controller.ts index 3440042eda..a9f6616426 100644 --- a/server/src/controllers/person.controller.ts +++ b/server/src/controllers/person.controller.ts @@ -1,7 +1,20 @@ -import { Body, Controller, Get, Next, Param, Post, Put, Query, Res } from '@nestjs/common'; +import { + Body, + Controller, + Delete, + Get, + HttpCode, + HttpStatus, + Next, + Param, + Post, + Put, + Query, + Res, +} from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { NextFunction, Response } from 'express'; -import { BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto'; +import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { AssetFaceUpdateDto, @@ -49,6 +62,13 @@ export class PersonController { return this.service.updateAll(auth, dto); } + @Delete() + @HttpCode(HttpStatus.NO_CONTENT) + @Authenticated({ permission: Permission.PERSON_DELETE }) + deletePeople(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise { + return this.service.deleteAll(auth, dto); + } + @Get(':id') @Authenticated({ permission: Permission.PERSON_READ }) getPerson(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { @@ -65,6 +85,13 @@ export class PersonController { return this.service.update(auth, id, dto); } + @Delete(':id') + @HttpCode(HttpStatus.NO_CONTENT) + @Authenticated({ permission: Permission.PERSON_DELETE }) + deletePerson(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.delete(auth, id); + } + @Get(':id/statistics') @Authenticated({ permission: Permission.PERSON_STATISTICS }) getPersonStatistics(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { diff --git a/server/src/controllers/sync.controller.spec.ts b/server/src/controllers/sync.controller.spec.ts new file mode 100644 index 0000000000..c1f19ddd66 --- /dev/null +++ b/server/src/controllers/sync.controller.spec.ts @@ -0,0 +1,84 @@ +import { SyncController } from 'src/controllers/sync.controller'; +import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter'; +import { SyncService } from 'src/services/sync.service'; +import request from 'supertest'; +import { errorDto } from 'test/medium/responses'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(SyncController.name, () => { + let ctx: ControllerContext; + const syncService = mockBaseService(SyncService); + const errorService = { handleError: vi.fn() }; + + beforeAll(async () => { + ctx = await controllerSetup(SyncController, [ + { provide: SyncService, useValue: syncService }, + { provide: GlobalExceptionFilter, useValue: errorService }, + ]); + return () => ctx.close(); + }); + + beforeEach(() => { + syncService.resetAllMocks(); + errorService.handleError.mockReset(); + ctx.reset(); + }); + + describe('POST /sync/stream', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/sync/stream'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require sync request type enums', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/sync/stream') + .send({ types: ['invalid'] }); + expect(status).toBe(400); + expect(body).toEqual( + errorDto.badRequest([expect.stringContaining('each value in types must be one of the following values')]), + ); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /sync/ack', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/sync/ack'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('POST /sync/ack', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/sync/ack'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should not allow more than 1,000 entries', async () => { + const acks = Array.from({ length: 1001 }, (_, i) => `ack-${i}`); + const { status, body } = await request(ctx.getHttpServer()).post('/sync/ack').send({ acks }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['acks must contain no more than 1000 elements'])); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('DELETE /sync/ack', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete('/sync/ack'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require sync response type enums', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .delete('/sync/ack') + .send({ types: ['invalid'] }); + expect(status).toBe(400); + expect(body).toEqual( + errorDto.badRequest([expect.stringContaining('each value in types must be one of the following values')]), + ); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); +}); diff --git a/server/src/controllers/timeline.controller.spec.ts b/server/src/controllers/timeline.controller.spec.ts new file mode 100644 index 0000000000..6d0276c6a3 --- /dev/null +++ b/server/src/controllers/timeline.controller.spec.ts @@ -0,0 +1,41 @@ +import { TimelineController } from 'src/controllers/timeline.controller'; +import { TimelineService } from 'src/services/timeline.service'; +import request from 'supertest'; +import { errorDto } from 'test/medium/responses'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(TimelineController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(TimelineService); + + beforeAll(async () => { + ctx = await controllerSetup(TimelineController, [{ provide: TimelineService, useValue: service }]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('GET /timeline/buckets', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/timeline/buckets'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /timeline/bucket', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/timeline/bucket?timeBucket=1900-01-01'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + // TODO enable date string validation while still accepting 5 digit years + it.fails('should fail if time bucket is invalid', async () => { + const { status, body } = await request(ctx.getHttpServer()).get('/timeline/bucket').query({ timeBucket: 'foo' }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest('Invalid time bucket format')); + }); + }); +}); diff --git a/server/src/database.ts b/server/src/database.ts index 4e40e5c241..acd6980985 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -1,5 +1,4 @@ import { Selectable } from 'kysely'; -import { Albums, Exif as DatabaseExif } from 'src/db'; import { MapAsset } from 'src/dtos/asset-response.dto'; import { AlbumUserRole, @@ -13,7 +12,9 @@ import { UserAvatarColor, UserStatus, } from 'src/enum'; -import { OnThisDayData, UserMetadataItem } from 'src/types'; +import { AlbumTable } from 'src/schema/tables/album.table'; +import { ExifTable } from 'src/schema/tables/exif.table'; +import { UserMetadataItem } from 'src/types'; export type AuthUser = { id: string; @@ -95,7 +96,7 @@ export type Memory = { showAt: Date | null; hideAt: Date | null; type: MemoryType; - data: OnThisDayData; + data: object; ownerId: string; isSaved: boolean; assets: MapAsset[]; @@ -193,7 +194,7 @@ export type SharedLink = { userId: string; }; -export type Album = Selectable & { +export type Album = Selectable & { owner: User; assets: MapAsset[]; }; @@ -209,6 +210,7 @@ export type Partner = { sharedWithId: string; sharedWith: User; createdAt: Date; + createId: string; updatedAt: Date; updateId: string; inTimeline: boolean; @@ -238,7 +240,7 @@ export type Session = { pinExpiresAt: Date | null; }; -export type Exif = Omit, 'updatedAt' | 'updateId'>; +export type Exif = Omit, 'updatedAt' | 'updateId'>; export type Person = { createdAt: Date; @@ -339,19 +341,27 @@ export const columns = { apiKey: ['id', 'name', 'userId', 'createdAt', 'updatedAt', 'permissions'], notification: ['id', 'createdAt', 'level', 'type', 'title', 'description', 'data', 'readAt'], syncAsset: [ - 'id', - 'ownerId', - 'originalFileName', - 'thumbhash', - 'checksum', - 'fileCreatedAt', - 'fileModifiedAt', - 'localDateTime', - 'type', - 'deletedAt', - 'isFavorite', - 'visibility', - 'updateId', + 'assets.id', + 'assets.ownerId', + 'assets.originalFileName', + 'assets.thumbhash', + 'assets.checksum', + 'assets.fileCreatedAt', + 'assets.fileModifiedAt', + 'assets.localDateTime', + 'assets.type', + 'assets.deletedAt', + 'assets.isFavorite', + 'assets.visibility', + 'assets.duration', + ], + syncAlbumUser: ['album_users.albumsId as albumId', 'album_users.usersId as userId', 'album_users.role'], + syncStack: [ + 'asset_stack.id', + 'asset_stack.createdAt', + 'asset_stack.updatedAt', + 'asset_stack.primaryAssetId', + 'asset_stack.ownerId', ], stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'], syncAssetExif: [ @@ -380,7 +390,6 @@ export const columns = { 'exif.profileDescription', 'exif.rating', 'exif.fps', - 'exif.updateId', ], exif: [ 'exif.assetId', diff --git a/server/src/db.d.ts b/server/src/db.d.ts deleted file mode 100644 index af1dd964fd..0000000000 --- a/server/src/db.d.ts +++ /dev/null @@ -1,527 +0,0 @@ -/** - * This file was generated by kysely-codegen. - * Please do not edit it manually. - */ - -import type { ColumnType } from 'kysely'; -import { - AlbumUserRole, - AssetFileType, - AssetOrder, - AssetStatus, - AssetType, - AssetVisibility, - MemoryType, - NotificationLevel, - NotificationType, - Permission, - SharedLinkType, - SourceType, - SyncEntityType, -} from 'src/enum'; -import { UserTable } from 'src/schema/tables/user.table'; -import { OnThisDayData, UserMetadataItem } from 'src/types'; - -export type ArrayType = ArrayTypeImpl extends (infer U)[] ? U[] : ArrayTypeImpl; - -export type ArrayTypeImpl = T extends ColumnType ? ColumnType : T[]; - -export type Generated = - T extends ColumnType ? ColumnType : ColumnType; - -export type Int8 = ColumnType; - -export type Json = JsonValue; - -export type JsonArray = JsonValue[]; - -export type JsonObject = { - [x: string]: JsonValue | undefined; -}; - -export type JsonPrimitive = boolean | number | string | null; - -export type JsonValue = JsonArray | JsonObject | JsonPrimitive; - -export type Timestamp = ColumnType; - -export interface Activity { - albumId: string; - assetId: string | null; - comment: string | null; - createdAt: Generated; - id: Generated; - isLiked: Generated; - updatedAt: Generated; - updateId: Generated; - userId: string; -} - -export interface Albums { - albumName: Generated; - /** - * Asset ID to be used as thumbnail - */ - albumThumbnailAssetId: string | null; - createdAt: Generated; - deletedAt: Timestamp | null; - description: Generated; - id: Generated; - isActivityEnabled: Generated; - order: Generated; - ownerId: string; - updatedAt: Generated; - 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; - createdAt: Generated; -} - -export interface AlbumsSharedUsersUsers { - albumsId: string; - role: Generated; - usersId: string; - updatedAt: Generated; - updateId: Generated; -} - -export interface ApiKeys { - createdAt: Generated; - id: Generated; - key: string; - name: string; - permissions: Permission[]; - updatedAt: Generated; - updateId: Generated; - userId: string; -} - -export interface AssetFaces { - assetId: string; - boundingBoxX1: Generated; - boundingBoxX2: Generated; - boundingBoxY1: Generated; - boundingBoxY2: Generated; - deletedAt: Timestamp | null; - id: Generated; - imageHeight: Generated; - imageWidth: Generated; - personId: string | null; - sourceType: Generated; -} - -export interface AssetFiles { - assetId: string; - createdAt: Generated; - id: Generated; - path: string; - type: AssetFileType; - updatedAt: Generated; - updateId: Generated; -} - -export interface AssetJobStatus { - assetId: string; - duplicatesDetectedAt: Timestamp | null; - facesRecognizedAt: Timestamp | null; - metadataExtractedAt: Timestamp | null; - previewAt: Timestamp | null; - thumbnailAt: Timestamp | null; -} - -export interface AssetsAudit { - deletedAt: Generated; - id: Generated; - assetId: string; - ownerId: string; -} - -export interface Assets { - checksum: Buffer; - createdAt: Generated; - deletedAt: Timestamp | null; - deviceAssetId: string; - deviceId: string; - duplicateId: string | null; - duration: string | null; - encodedVideoPath: Generated; - fileCreatedAt: Timestamp; - fileModifiedAt: Timestamp; - id: Generated; - isExternal: Generated; - isFavorite: Generated; - isOffline: Generated; - visibility: Generated; - libraryId: string | null; - livePhotoVideoId: string | null; - localDateTime: Timestamp; - originalFileName: string; - originalPath: string; - ownerId: string; - sidecarPath: string | null; - stackId: string | null; - status: Generated; - thumbhash: Buffer | null; - type: AssetType; - updatedAt: Generated; - updateId: Generated; -} - -export interface AssetStack { - id: Generated; - ownerId: string; - primaryAssetId: string; -} - -export interface Audit { - action: string; - createdAt: Generated; - entityId: string; - entityType: string; - id: Generated; - ownerId: string; -} - -export interface Exif { - assetId: string; - updateId: Generated; - updatedAt: Generated; - autoStackId: string | null; - bitsPerSample: number | null; - city: string | null; - colorspace: string | null; - country: string | null; - dateTimeOriginal: Timestamp | null; - description: Generated; - exifImageHeight: number | null; - exifImageWidth: number | null; - exposureTime: string | null; - fileSizeInByte: Int8 | null; - fNumber: number | null; - focalLength: number | null; - fps: number | null; - iso: number | null; - latitude: number | null; - lensModel: string | null; - livePhotoCID: string | null; - longitude: number | null; - make: string | null; - model: string | null; - modifyDate: Timestamp | null; - orientation: string | null; - profileDescription: string | null; - projectionType: string | null; - rating: number | null; - state: string | null; - timeZone: string | null; -} - -export interface FaceSearch { - embedding: string; - faceId: string; -} - -export interface GeodataPlaces { - admin1Code: string | null; - admin1Name: string | null; - admin2Code: string | null; - admin2Name: string | null; - alternateNames: string | null; - countryCode: string; - id: number; - latitude: number; - longitude: number; - modificationDate: Timestamp; - name: string; -} - -export interface Libraries { - createdAt: Generated; - deletedAt: Timestamp | null; - exclusionPatterns: string[]; - id: Generated; - importPaths: string[]; - name: string; - ownerId: string; - refreshedAt: Timestamp | null; - updatedAt: Generated; - updateId: Generated; -} - -export interface Memories { - createdAt: Generated; - data: OnThisDayData; - deletedAt: Timestamp | null; - hideAt: Timestamp | null; - id: Generated; - isSaved: Generated; - memoryAt: Timestamp; - ownerId: string; - seenAt: Timestamp | null; - showAt: Timestamp | null; - type: MemoryType; - updatedAt: Generated; - 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; -} - -export interface Migrations { - id: Generated; - name: string; - timestamp: Int8; -} - -export interface MoveHistory { - entityId: string; - id: Generated; - newPath: string; - oldPath: string; - pathType: string; -} - -export interface NaturalearthCountries { - admin: string; - admin_a3: string; - coordinates: string; - id: Generated; - type: string; -} - -export interface PartnersAudit { - deletedAt: Generated; - id: Generated; - sharedById: string; - sharedWithId: string; -} - -export interface Partners { - createdAt: Generated; - inTimeline: Generated; - sharedById: string; - sharedWithId: string; - updatedAt: Generated; - updateId: Generated; -} - -export interface Person { - birthDate: Timestamp | null; - color: string | null; - createdAt: Generated; - faceAssetId: string | null; - id: Generated; - isFavorite: Generated; - isHidden: Generated; - name: Generated; - ownerId: string; - thumbnailPath: Generated; - updatedAt: Generated; - updateId: Generated; -} - -export interface Sessions { - createdAt: Generated; - 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 { - ack: string; - createdAt: Generated; - sessionId: string; - type: SyncEntityType; - updatedAt: Generated; - updateId: Generated; -} - -export interface SharedLinkAsset { - assetsId: string; - sharedLinksId: string; -} - -export interface SharedLinks { - albumId: string | null; - allowDownload: Generated; - allowUpload: Generated; - createdAt: Generated; - description: string | null; - expiresAt: Timestamp | null; - id: Generated; - key: Buffer; - password: string | null; - showExif: Generated; - type: SharedLinkType; - userId: string; -} - -export interface SmartSearch { - assetId: string; - embedding: string; -} - -export interface SocketIoAttachments { - created_at: Generated; - id: Generated; - payload: Buffer | null; -} - -export interface SystemConfig { - key: string; - value: string | null; -} - -export interface SystemMetadata { - key: string; - value: Json; -} - -export interface TagAsset { - assetsId: string; - tagsId: string; -} - -export interface Tags { - color: string | null; - createdAt: Generated; - id: Generated; - parentId: string | null; - updatedAt: Generated; - updateId: Generated; - userId: string; - value: string; -} - -export interface TagsClosure { - id_ancestor: string; - id_descendant: string; -} - -export interface TypeormMetadata { - database: string | null; - name: string | null; - schema: string | null; - table: string | null; - type: string; - value: string | null; -} - -export interface UserMetadata extends UserMetadataItem { - userId: string; -} - -export interface UsersAudit { - id: Generated; - userId: string; - deletedAt: Generated; -} - -export interface VectorsPgVectorIndexStat { - idx_growing: ArrayType | null; - idx_indexing: boolean | null; - idx_options: string | null; - idx_sealed: ArrayType | null; - idx_size: Int8 | null; - idx_status: string | null; - idx_tuples: Int8 | null; - idx_write: Int8 | null; - indexname: string | null; - indexrelid: number | null; - tablename: string | null; - tablerelid: number | null; -} - -export interface VersionHistory { - createdAt: Generated; - id: Generated; - version: string; -} - -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; - asset_job_status: AssetJobStatus; - asset_stack: AssetStack; - assets: Assets; - assets_audit: AssetsAudit; - audit: Audit; - exif: Exif; - face_search: FaceSearch; - geodata_places: GeodataPlaces; - libraries: Libraries; - memories: Memories; - memories_assets_assets: MemoriesAssetsAssets; - migrations: Migrations; - notifications: Notifications; - move_history: MoveHistory; - naturalearth_countries: NaturalearthCountries; - partners_audit: PartnersAudit; - partners: Partners; - person: Person; - sessions: Sessions; - session_sync_checkpoints: SessionSyncCheckpoints; - shared_link__asset: SharedLinkAsset; - shared_links: SharedLinks; - smart_search: SmartSearch; - socket_io_attachments: SocketIoAttachments; - system_config: SystemConfig; - system_metadata: SystemMetadata; - tag_asset: TagAsset; - tags: Tags; - tags_closure: TagsClosure; - typeorm_metadata: TypeormMetadata; - user_metadata: UserMetadata; - users: UserTable; - users_audit: UsersAudit; - 'vectors.pg_vector_index_stat': VectorsPgVectorIndexStat; - version_history: VersionHistory; -} diff --git a/server/src/decorators.ts b/server/src/decorators.ts index 6b34ffcafe..766e7c70b9 100644 --- a/server/src/decorators.ts +++ b/server/src/decorators.ts @@ -14,6 +14,9 @@ const GeneratedUuidV7Column = (options: Omit = {}) => GeneratedUuidV7Column(options); +export const CreateIdColumn = (options: Omit = {}) => + GeneratedUuidV7Column(options); + export const PrimaryGeneratedUuidV7Column = () => GeneratedUuidV7Column({ primary: true }); export const UpdatedAtTrigger = (name: string) => diff --git a/server/src/dtos/asset-media.dto.ts b/server/src/dtos/asset-media.dto.ts index a647b4515f..92e1302864 100644 --- a/server/src/dtos/asset-media.dto.ts +++ b/server/src/dtos/asset-media.dto.ts @@ -46,6 +46,10 @@ class AssetMediaBase { @IsString() duration?: string; + @Optional() + @IsString() + filename?: string; + // The properties below are added to correctly generate the API docs // and client SDKs. Validation should be handled in the controller. @ApiProperty({ type: 'string', format: 'binary' }) diff --git a/server/src/dtos/duplicate.dto.ts b/server/src/dtos/duplicate.dto.ts index b12580ef18..166f18ce8f 100644 --- a/server/src/dtos/duplicate.dto.ts +++ b/server/src/dtos/duplicate.dto.ts @@ -1,14 +1,6 @@ -import { IsNotEmpty } from 'class-validator'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; -import { ValidateUUID } from 'src/validation'; export class DuplicateResponseDto { duplicateId!: string; assets!: AssetResponseDto[]; } - -export class ResolveDuplicatesDto { - @IsNotEmpty() - @ValidateUUID({ each: true }) - assetIds!: string[]; -} diff --git a/server/src/dtos/memory.dto.ts b/server/src/dtos/memory.dto.ts index 98231a9035..675039363b 100644 --- a/server/src/dtos/memory.dto.ts +++ b/server/src/dtos/memory.dto.ts @@ -71,6 +71,11 @@ export class MemoryCreateDto extends MemoryBaseDto { assetIds?: string[]; } +export class MemoryStatisticsResponseDto { + @ApiProperty({ type: 'integer' }) + total!: number; +} + export class MemoryResponseDto { id!: string; createdAt!: Date; diff --git a/server/src/dtos/person.dto.ts b/server/src/dtos/person.dto.ts index c59ab905bd..18da39b6d4 100644 --- a/server/src/dtos/person.dto.ts +++ b/server/src/dtos/person.dto.ts @@ -4,10 +4,10 @@ import { IsArray, IsInt, IsNotEmpty, IsNumber, IsString, Max, Min, ValidateNeste import { Selectable } from 'kysely'; import { DateTime } from 'luxon'; import { AssetFace, Person } from 'src/database'; -import { AssetFaces } from 'src/db'; import { PropertyLifecycle } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; import { SourceType } from 'src/enum'; +import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { asDateString } from 'src/utils/date'; import { IsDateStringFormat, @@ -232,7 +232,7 @@ export function mapPerson(person: Person): PersonResponseDto { }; } -export function mapFacesWithoutPerson(face: Selectable): AssetFaceWithoutPersonResponseDto { +export function mapFacesWithoutPerson(face: Selectable): AssetFaceWithoutPersonResponseDto { return { id: face.id, imageHeight: face.imageHeight, diff --git a/server/src/dtos/search.dto.ts b/server/src/dtos/search.dto.ts index d0427ef322..0024a1b34e 100644 --- a/server/src/dtos/search.dto.ts +++ b/server/src/dtos/search.dto.ts @@ -92,8 +92,8 @@ class BaseSearchDto { @ValidateUUID({ each: true, optional: true }) personIds?: string[]; - @ValidateUUID({ each: true, optional: true }) - tagIds?: string[]; + @ValidateUUID({ each: true, optional: true, nullable: true }) + tagIds?: string[] | null; @ValidateUUID({ each: true, optional: true }) albumIds?: string[]; diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index 3088392c68..db1fd29418 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -1,7 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unsafe-function-type */ import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsInt, IsPositive, IsString } from 'class-validator'; +import { ArrayMaxSize, IsEnum, IsInt, IsPositive, IsString } from 'class-validator'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; -import { AlbumUserRole, AssetOrder, AssetType, AssetVisibility, SyncEntityType, SyncRequestType } from 'src/enum'; +import { + AlbumUserRole, + AssetOrder, + AssetType, + AssetVisibility, + MemoryType, + SyncEntityType, + SyncRequestType, +} from 'src/enum'; import { Optional, ValidateDate, ValidateUUID } from 'src/validation'; export class AssetFullSyncDto { @@ -34,6 +43,15 @@ export class AssetDeltaSyncResponseDto { deleted!: string[]; } +export const extraSyncModels: Function[] = []; + +export const ExtraModel = (): ClassDecorator => { + return (object: Function) => { + extraSyncModels.push(object); + }; +}; + +@ExtraModel() export class SyncUserV1 { id!: string; name!: string; @@ -41,21 +59,25 @@ export class SyncUserV1 { deletedAt!: Date | null; } +@ExtraModel() export class SyncUserDeleteV1 { userId!: string; } +@ExtraModel() export class SyncPartnerV1 { sharedById!: string; sharedWithId!: string; inTimeline!: boolean; } +@ExtraModel() export class SyncPartnerDeleteV1 { sharedById!: string; sharedWithId!: string; } +@ExtraModel() export class SyncAssetV1 { id!: string; ownerId!: string; @@ -65,6 +87,7 @@ export class SyncAssetV1 { fileCreatedAt!: Date | null; fileModifiedAt!: Date | null; localDateTime!: Date | null; + duration!: string | null; @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType }) type!: AssetType; deletedAt!: Date | null; @@ -73,10 +96,12 @@ export class SyncAssetV1 { visibility!: AssetVisibility; } +@ExtraModel() export class SyncAssetDeleteV1 { assetId!: string; } +@ExtraModel() export class SyncAssetExifV1 { assetId!: string; description!: string | null; @@ -90,9 +115,9 @@ export class SyncAssetExifV1 { dateTimeOriginal!: Date | null; modifyDate!: Date | null; timeZone!: string | null; - @ApiProperty({ type: 'integer' }) + @ApiProperty({ type: 'number', format: 'double' }) latitude!: number | null; - @ApiProperty({ type: 'integer' }) + @ApiProperty({ type: 'number', format: 'double' }) longitude!: number | null; projectionType!: string | null; city!: string | null; @@ -101,9 +126,9 @@ export class SyncAssetExifV1 { make!: string | null; model!: string | null; lensModel!: string | null; - @ApiProperty({ type: 'integer' }) + @ApiProperty({ type: 'number', format: 'double' }) fNumber!: number | null; - @ApiProperty({ type: 'integer' }) + @ApiProperty({ type: 'number', format: 'double' }) focalLength!: number | null; @ApiProperty({ type: 'integer' }) iso!: number | null; @@ -111,19 +136,22 @@ export class SyncAssetExifV1 { profileDescription!: string | null; @ApiProperty({ type: 'integer' }) rating!: number | null; - @ApiProperty({ type: 'integer' }) + @ApiProperty({ type: 'number', format: 'double' }) fps!: number | null; } +@ExtraModel() export class SyncAlbumDeleteV1 { albumId!: string; } +@ExtraModel() export class SyncAlbumUserDeleteV1 { albumId!: string; userId!: string; } +@ExtraModel() export class SyncAlbumUserV1 { albumId!: string; userId!: string; @@ -131,6 +159,7 @@ export class SyncAlbumUserV1 { role!: AlbumUserRole; } +@ExtraModel() export class SyncAlbumV1 { id!: string; ownerId!: string; @@ -144,6 +173,69 @@ export class SyncAlbumV1 { order!: AssetOrder; } +@ExtraModel() +export class SyncAlbumToAssetV1 { + albumId!: string; + assetId!: string; +} + +@ExtraModel() +export class SyncAlbumToAssetDeleteV1 { + albumId!: string; + assetId!: string; +} + +@ExtraModel() +export class SyncMemoryV1 { + id!: string; + createdAt!: Date; + updatedAt!: Date; + deletedAt!: Date | null; + ownerId!: string; + @ApiProperty({ enumName: 'MemoryType', enum: MemoryType }) + type!: MemoryType; + data!: object; + isSaved!: boolean; + memoryAt!: Date; + seenAt!: Date | null; + showAt!: Date | null; + hideAt!: Date | null; +} + +@ExtraModel() +export class SyncMemoryDeleteV1 { + memoryId!: string; +} + +@ExtraModel() +export class SyncMemoryAssetV1 { + memoryId!: string; + assetId!: string; +} + +@ExtraModel() +export class SyncMemoryAssetDeleteV1 { + memoryId!: string; + assetId!: string; +} + +@ExtraModel() +export class SyncStackV1 { + id!: string; + createdAt!: Date; + updatedAt!: Date; + primaryAssetId!: string; + ownerId!: string; +} + +@ExtraModel() +export class SyncStackDeleteV1 { + stackId!: string; +} + +@ExtraModel() +export class SyncAckV1 {} + export type SyncItem = { [SyncEntityType.UserV1]: SyncUserV1; [SyncEntityType.UserDeleteV1]: SyncUserDeleteV1; @@ -153,30 +245,34 @@ export type SyncItem = { [SyncEntityType.AssetDeleteV1]: SyncAssetDeleteV1; [SyncEntityType.AssetExifV1]: SyncAssetExifV1; [SyncEntityType.PartnerAssetV1]: SyncAssetV1; + [SyncEntityType.PartnerAssetBackfillV1]: SyncAssetV1; [SyncEntityType.PartnerAssetDeleteV1]: SyncAssetDeleteV1; [SyncEntityType.PartnerAssetExifV1]: SyncAssetExifV1; + [SyncEntityType.PartnerAssetExifBackfillV1]: SyncAssetExifV1; [SyncEntityType.AlbumV1]: SyncAlbumV1; [SyncEntityType.AlbumDeleteV1]: SyncAlbumDeleteV1; [SyncEntityType.AlbumUserV1]: SyncAlbumUserV1; + [SyncEntityType.AlbumUserBackfillV1]: SyncAlbumUserV1; [SyncEntityType.AlbumUserDeleteV1]: SyncAlbumUserDeleteV1; + [SyncEntityType.AlbumAssetV1]: SyncAssetV1; + [SyncEntityType.AlbumAssetBackfillV1]: SyncAssetV1; + [SyncEntityType.AlbumAssetExifV1]: SyncAssetExifV1; + [SyncEntityType.AlbumAssetExifBackfillV1]: SyncAssetExifV1; + [SyncEntityType.AlbumToAssetV1]: SyncAlbumToAssetV1; + [SyncEntityType.AlbumToAssetBackfillV1]: SyncAlbumToAssetV1; + [SyncEntityType.AlbumToAssetDeleteV1]: SyncAlbumToAssetDeleteV1; + [SyncEntityType.MemoryV1]: SyncMemoryV1; + [SyncEntityType.MemoryDeleteV1]: SyncMemoryDeleteV1; + [SyncEntityType.MemoryToAssetV1]: SyncMemoryAssetV1; + [SyncEntityType.MemoryToAssetDeleteV1]: SyncMemoryAssetDeleteV1; + [SyncEntityType.StackV1]: SyncStackV1; + [SyncEntityType.StackDeleteV1]: SyncStackDeleteV1; + [SyncEntityType.PartnerStackBackfillV1]: SyncStackV1; + [SyncEntityType.PartnerStackDeleteV1]: SyncStackDeleteV1; + [SyncEntityType.PartnerStackV1]: SyncStackV1; + [SyncEntityType.SyncAckV1]: SyncAckV1; }; -const responseDtos = [ - SyncUserV1, - SyncUserDeleteV1, - SyncPartnerV1, - SyncPartnerDeleteV1, - SyncAssetV1, - SyncAssetDeleteV1, - SyncAssetExifV1, - SyncAlbumV1, - SyncAlbumDeleteV1, - SyncAlbumUserV1, - SyncAlbumUserDeleteV1, -]; - -export const extraSyncModels = responseDtos; - export class SyncStreamDto { @IsEnum(SyncRequestType, { each: true }) @ApiProperty({ enumName: 'SyncRequestType', enum: SyncRequestType, isArray: true }) @@ -190,6 +286,7 @@ export class SyncAckDto { } export class SyncAckSetDto { + @ArrayMaxSize(1000) @IsString({ each: true }) acks!: string[]; } diff --git a/server/src/dtos/system-config.dto.ts b/server/src/dtos/system-config.dto.ts index 6991baf109..b0385984b4 100644 --- a/server/src/dtos/system-config.dto.ts +++ b/server/src/dtos/system-config.dto.ts @@ -360,7 +360,9 @@ class SystemConfigOAuthDto { @IsNumber() @Min(0) - defaultStorageQuota!: number; + @Optional({ nullable: true }) + @ApiProperty({ type: 'integer', format: 'int64' }) + defaultStorageQuota!: number | null; @ValidateBoolean() enabled!: boolean; @@ -393,6 +395,9 @@ class SystemConfigOAuthDto { @IsString() storageQuotaClaim!: string; + + @IsString() + roleClaim!: string; } class SystemConfigPasswordLoginDto { diff --git a/server/src/dtos/user.dto.ts b/server/src/dtos/user.dto.ts index 9d43e53f89..ed08f7534d 100644 --- a/server/src/dtos/user.dto.ts +++ b/server/src/dtos/user.dto.ts @@ -106,6 +106,10 @@ export class UserAdminCreateDto { @Optional() @IsBoolean() notify?: boolean; + + @Optional() + @IsBoolean() + isAdmin?: boolean; } export class UserAdminUpdateDto { @@ -145,6 +149,10 @@ export class UserAdminUpdateDto { @Min(0) @ApiProperty({ type: 'integer', format: 'int64' }) quotaSizeInBytes?: number | null; + + @Optional() + @IsBoolean() + isAdmin?: boolean; } export class UserAdminDeleteDto { diff --git a/server/src/emails/components/button.component.tsx b/server/src/emails/components/button.component.tsx index 9c229fc16d..b490e36650 100644 --- a/server/src/emails/components/button.component.tsx +++ b/server/src/emails/components/button.component.tsx @@ -2,9 +2,7 @@ import React from 'react'; import { Button, ButtonProps } from '@react-email/components'; -interface ImmichButtonProps extends ButtonProps {} - -export const ImmichButton = ({ children, ...props }: ImmichButtonProps) => ( +export const ImmichButton = ({ children, ...props }: ButtonProps) => ( - - {/each} - - - - - {#if htmlPreview} - - -
- + > + + {$t('admin.template_email_preview')} +
-
-
- {/if} - - + {/each} + + + + diff --git a/web/src/lib/components/admin-page/user/feature-setting.svelte b/web/src/lib/components/admin-page/user/feature-setting.svelte new file mode 100644 index 0000000000..efeb13e22f --- /dev/null +++ b/web/src/lib/components/admin-page/user/feature-setting.svelte @@ -0,0 +1,16 @@ + + +
+ {title} + +
diff --git a/web/src/lib/components/album-page/album-map.svelte b/web/src/lib/components/album-page/album-map.svelte index a3ccc15d4d..fbb831a38b 100644 --- a/web/src/lib/components/album-page/album-map.svelte +++ b/web/src/lib/components/album-page/album-map.svelte @@ -1,13 +1,9 @@ - -{#if albumMapViewManager.isInMapView} - - -
-
- {#await import('../shared-components/map/map.svelte')} - {#await delay(timeToLoadTheMap) then} - -
- -
- {/await} - {:then { default: Map }} - - {/await} -
-
-
-
- - - {#if $showAssetViewer} - {#await import('../../../lib/components/asset-viewer/asset-viewer.svelte') then { default: AssetViewer }} - 1} - onNext={navigateNext} - onPrevious={navigatePrevious} - onRandom={navigateRandom} - onClose={() => { - assetViewingStore.showAssetViewer(false); - handlePromiseError(navigate({ targetRoute: 'current', assetId: null })); - }} - isShared={false} - /> - {/await} - {/if} - -{/if} diff --git a/web/src/lib/components/album-page/album-options.svelte b/web/src/lib/components/album-page/album-options.svelte deleted file mode 100644 index 3a20e10602..0000000000 --- a/web/src/lib/components/album-page/album-options.svelte +++ /dev/null @@ -1,202 +0,0 @@ - - -{#if !selectedRemoveUser} - - -
-
-

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

-
- {#if order} - - {/if} - -
-
-
-
{$t('people').toUpperCase()}
-
- - -
-
- -
-
{user.name}
-
{$t('owner')}
-
- - {#each album.albumUsers as { user, role } (user.id)} -
-
- -
-
{user.name}
- {#if role === AlbumUserRole.Viewer} - {$t('role_viewer')} - {:else} - {$t('role_editor')} - {/if} - {#if user.id !== album.ownerId} - - {#if role === AlbumUserRole.Viewer} - handleUpdateSharedUserRole(user, AlbumUserRole.Editor)} - text={$t('allow_edits')} - /> - {:else} - handleUpdateSharedUserRole(user, AlbumUserRole.Viewer)} - text={$t('disallow_edits')} - /> - {/if} - - handleMenuRemove(user)} text={$t('remove')} /> - - {/if} -
- {/each} -
-
-
-
-
-{/if} - -{#if selectedRemoveUser} - (confirmed ? handleRemoveUser() : (selectedRemoveUser = null))} - /> -{/if} diff --git a/web/src/lib/components/album-page/album-viewer.svelte b/web/src/lib/components/album-page/album-viewer.svelte index 71cd9de932..0866d38557 100644 --- a/web/src/lib/components/album-page/album-viewer.svelte +++ b/web/src/lib/components/album-page/album-viewer.svelte @@ -4,15 +4,16 @@ import AlbumMap from '$lib/components/album-page/album-map.svelte'; import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte'; import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; + import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte'; import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { assetViewingStore } from '$lib/stores/asset-viewing.store'; - import { AssetStore } from '$lib/managers/timeline-manager/asset-store.svelte'; import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store'; import { featureFlags } from '$lib/stores/server-config.store'; import { handlePromiseError } from '$lib/utils'; import { cancelMultiselect, downloadAlbum } from '$lib/utils/asset-utils'; import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader'; import type { AlbumResponseDto, SharedLinkResponseDto, UserResponseDto } from '@immich/sdk'; + import { IconButton } from '@immich/ui'; import { mdiFileImagePlusOutline, mdiFolderDownloadOutline } from '@mdi/js'; import { onDestroy } from 'svelte'; import { t } from 'svelte-i18n'; @@ -22,7 +23,6 @@ import ImmichLogoSmallLink from '../shared-components/immich-logo-small-link.svelte'; import ThemeButton from '../shared-components/theme-button.svelte'; import AlbumSummary from './album-summary.svelte'; - import { IconButton } from '@immich/ui'; interface Props { sharedLink: SharedLinkResponseDto; @@ -35,9 +35,9 @@ let { isViewing: showAssetViewer } = assetViewingStore; - const assetStore = new AssetStore(); - $effect(() => void assetStore.updateOptions({ albumId: album.id, order: album.order })); - onDestroy(() => assetStore.destroy()); + const timelineManager = new TimelineManager(); + $effect(() => void timelineManager.updateOptions({ albumId: album.id, order: album.order })); + onDestroy(() => timelineManager.destroy()); const assetInteraction = new AssetInteraction(); @@ -61,7 +61,7 @@ />
- +

assetInteraction.clearMultiselect()} > - + {#if sharedLink.allowDownload} {/if} diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte index 32cccbc850..f3d14ddb27 100644 --- a/web/src/lib/components/album-page/albums-list.svelte +++ b/web/src/lib/components/album-page/albums-list.svelte @@ -2,7 +2,6 @@ import { goto } from '$app/navigation'; import AlbumCardGroup from '$lib/components/album-page/album-card-group.svelte'; import AlbumsTable from '$lib/components/album-page/albums-table.svelte'; - import EditAlbumForm from '$lib/components/forms/edit-album-form.svelte'; import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; import RightClickContextMenu from '$lib/components/shared-components/context-menu/right-click-context-menu.svelte'; import { @@ -11,6 +10,7 @@ } from '$lib/components/shared-components/notification/notification'; import { AppRoute } from '$lib/constants'; import { modalManager } from '$lib/managers/modal-manager.svelte'; + import AlbumEditModal from '$lib/modals/AlbumEditModal.svelte'; import AlbumShareModal from '$lib/modals/AlbumShareModal.svelte'; import QrCodeModal from '$lib/modals/QrCodeModal.svelte'; import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte'; @@ -258,7 +258,7 @@ const handleEdit = async (album: AlbumResponseDto) => { closeAlbumContextMenu(); - const editedAlbum = await modalManager.show(EditAlbumForm, { + const editedAlbum = await modalManager.show(AlbumEditModal, { album, }); if (editedAlbum) { diff --git a/web/src/lib/components/album-page/albums-table.svelte b/web/src/lib/components/album-page/albums-table.svelte index ed509251df..df263b4011 100644 --- a/web/src/lib/components/album-page/albums-table.svelte +++ b/web/src/lib/components/album-page/albums-table.svelte @@ -45,7 +45,7 @@ {@const isCollapsed = isAlbumGroupCollapsed($albumViewSettings, albumGroup.id)} {@const iconRotation = isCollapsed ? 'rotate-0' : 'rotate-90'} import { shortcut } from '$lib/actions/shortcut'; import type { OnAction } from '$lib/components/asset-viewer/actions/action'; - import AlbumSelectionModal from '$lib/components/shared-components/album-selection/album-selection-modal.svelte'; import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; - import Portal from '$lib/components/shared-components/portal/portal.svelte'; import { AssetAction } from '$lib/constants'; - import { addAssetsToAlbum, addAssetsToNewAlbum } from '$lib/utils/asset-utils'; + import { modalManager } from '$lib/managers/modal-manager.svelte'; + import AlbumPickerModal from '$lib/modals/AlbumPickerModal.svelte'; + import { addAssetsToAlbum } from '$lib/utils/asset-utils'; import { toTimelineAsset } from '$lib/utils/timeline-util'; - import type { AlbumResponseDto, AssetResponseDto } from '@immich/sdk'; + import type { AssetResponseDto } from '@immich/sdk'; import { mdiImageAlbum, mdiShareVariantOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; @@ -19,40 +19,22 @@ let { asset, onAction, shared = false }: Props = $props(); - let showSelectionModal = $state(false); + const onClick = async () => { + const album = await modalManager.show(AlbumPickerModal, { shared }); - const handleAddToNewAlbum = async (albumName: string) => { - showSelectionModal = false; - const album = await addAssetsToNewAlbum(albumName, [asset.id]); - if (album) { - onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toTimelineAsset(asset), album }); + if (!album) { + return; } - }; - const handleAddToAlbum = async (album: AlbumResponseDto) => { - showSelectionModal = false; await addAssetsToAlbum(album.id, [asset.id]); onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toTimelineAsset(asset), album }); }; - (showSelectionModal = true) }} -/> + (showSelectionModal = true)} + {onClick} /> - -{#if showSelectionModal} - - (showSelectionModal = false)} - /> - -{/if} diff --git a/web/src/lib/components/asset-viewer/actions/download-action.svelte b/web/src/lib/components/asset-viewer/actions/download-action.svelte index 9a2e1a9553..e6c96da016 100644 --- a/web/src/lib/components/asset-viewer/actions/download-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/download-action.svelte @@ -23,8 +23,9 @@ {#if !menuItem} import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; - import Portal from '$lib/components/shared-components/portal/portal.svelte'; - import ProfileImageCropper from '$lib/components/shared-components/profile-image-cropper.svelte'; + import { modalManager } from '$lib/managers/modal-manager.svelte'; + import ProfileImageCropperModal from '$lib/modals/ProfileImageCropperModal.svelte'; import type { AssetResponseDto } from '@immich/sdk'; import { mdiAccountCircleOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; @@ -11,18 +11,10 @@ } let { asset }: Props = $props(); - - let showProfileImageCrop = $state(false); (showProfileImageCrop = true)} + onClick={() => modalManager.show(ProfileImageCropperModal, { asset })} text={$t('set_as_profile_picture')} /> - -{#if showProfileImageCrop} - - (showProfileImageCrop = false)} /> - -{/if} diff --git a/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte b/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte index 1b06bf8e21..14126bb8c7 100644 --- a/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte @@ -25,6 +25,7 @@ prompt: isLocked ? $t('remove_from_locked_folder_confirmation') : $t('move_to_locked_folder_confirmation'), confirmText: $t('move'), confirmColor: isLocked ? 'danger' : 'primary', + icon: isLocked ? mdiLockOpenVariantOutline : mdiLockOutline, }); if (!isConfirmed) { diff --git a/web/src/lib/components/asset-viewer/activity-viewer.svelte b/web/src/lib/components/asset-viewer/activity-viewer.svelte index c4aa7098d5..c368a1a8fe 100644 --- a/web/src/lib/components/asset-viewer/activity-viewer.svelte +++ b/web/src/lib/components/asset-viewer/activity-viewer.svelte @@ -247,7 +247,7 @@
- +
diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index a448f96c32..4f793e7b52 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -8,9 +8,9 @@ import { AssetAction, ProjectionType } from '$lib/constants'; import { activityManager } from '$lib/managers/activity-manager.svelte'; import { authManager } from '$lib/managers/auth-manager.svelte'; + import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; import { closeEditorCofirm } from '$lib/stores/asset-editor.store'; import { assetViewingStore } from '$lib/stores/asset-viewing.store'; - import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; import { isShowDetail } from '$lib/stores/preferences.store'; import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store'; import { user } from '$lib/stores/user.store'; @@ -91,6 +91,8 @@ slideshowState, slideshowTransition, } = slideshowStore; + const stackThumbnailSize = 60; + const stackSelectedThumbnailSize = 65; let appearsInAlbums: AlbumResponseDto[] = $state([]); let shouldPlayMotionPhoto = $state(false); @@ -138,16 +140,6 @@ } }; - const updateComments = async () => { - if (album) { - try { - await activityManager.refreshActivities(album.id, asset.id); - } catch (error) { - handleError(error, $t('errors.unable_to_get_comments_number')); - } - } - }; - const onAssetUpdate = ({ asset: assetUpdate }: { event: 'upload' | 'update'; asset: AssetResponseDto }) => { if (assetUpdate.id === asset.id) { asset = assetUpdate; @@ -180,10 +172,6 @@ if (!sharedLink) { await handleGetAllAlbums(); } - - if (album) { - activityManager.init(album.id, asset.id); - } }); onDestroy(() => { @@ -314,8 +302,10 @@ const handleStopSlideshow = async () => { try { + // eslint-disable-next-line tscompat/tscompat if (document.fullscreenElement) { document.body.style.cursor = ''; + // eslint-disable-next-line tscompat/tscompat await document.exitFullscreen(); } } catch (error) { @@ -370,8 +360,8 @@ } }); $effect(() => { - if (isShared && asset.id) { - handlePromiseError(updateComments()); + if (album && isShared && asset.id) { + handlePromiseError(activityManager.init(album.id, asset.id)); } }); $effect(() => { @@ -510,7 +500,7 @@ onVideoStarted={handleVideoStarted} /> {/if} - {#if $slideshowState === SlideshowState.None && isShared && ((album && album.isActivityEnabled) || activityManager.commentCount > 0)} + {#if $slideshowState === SlideshowState.None && isShared && ((album && album.isActivityEnabled) || activityManager.commentCount > 0) && !activityManager.isLoading}
-
+
+
{#each stackedAssets as stackedAsset (stackedAsset.id)}
handleStackedAssetMouseEvent(isMouseOver, stackedAsset)} readonly - thumbnailSize={stackedAsset.id === asset.id ? 65 : 60} + thumbnailSize={stackedAsset.id === asset.id ? stackSelectedThumbnailSize : stackThumbnailSize} showStackedIcon={false} disableLinkMouseOver /> diff --git a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte index 2ce6d3d17f..a6a080d52b 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte @@ -46,7 +46,7 @@ {#each tags as tag (tag.id)}

diff --git a/web/src/lib/components/asset-viewer/editor/crop-tool/crop-area.svelte b/web/src/lib/components/asset-viewer/editor/crop-tool/crop-area.svelte index a264ad8ddd..d61a534ed1 100644 --- a/web/src/lib/components/asset-viewer/editor/crop-tool/crop-area.svelte +++ b/web/src/lib/components/asset-viewer/editor/crop-tool/crop-area.svelte @@ -53,12 +53,10 @@ img.src = getAssetOriginalUrl({ id: asset.id, cacheKey: asset.thumbhash }); - img.addEventListener('load', () => onImageLoad(true)); - img.addEventListener('error', (error) => { - handleError(error, $t('error_loading_image')); - }); + img.addEventListener('load', () => onImageLoad(true), { passive: true }); + img.addEventListener('error', (error) => handleError(error, $t('error_loading_image')), { passive: true }); - globalThis.addEventListener('mousemove', handleMouseMove); + globalThis.addEventListener('mousemove', handleMouseMove, { passive: true }); }); onDestroy(() => { diff --git a/web/src/lib/components/asset-viewer/editor/crop-tool/image-loading.ts b/web/src/lib/components/asset-viewer/editor/crop-tool/image-loading.ts index bce90efd9e..63a42b8b96 100644 --- a/web/src/lib/components/asset-viewer/editor/crop-tool/image-loading.ts +++ b/web/src/lib/components/asset-viewer/editor/crop-tool/image-loading.ts @@ -31,8 +31,8 @@ export function onImageLoad(resetSize: boolean = false) { cropFrameEl?.classList.add('transition'); cropSettings.update((crop) => normalizeCropArea(crop, img, scale)); cropFrameEl?.classList.add('transition'); - cropFrameEl?.addEventListener('transitionend', () => { - cropFrameEl?.classList.remove('transition'); + cropFrameEl?.addEventListener('transitionend', () => cropFrameEl?.classList.remove('transition'), { + passive: true, }); } cropImageScale.set(scale); diff --git a/web/src/lib/components/asset-viewer/editor/crop-tool/mouse-handlers.ts b/web/src/lib/components/asset-viewer/editor/crop-tool/mouse-handlers.ts index b00f5331b2..832f0e4339 100644 --- a/web/src/lib/components/asset-viewer/editor/crop-tool/mouse-handlers.ts +++ b/web/src/lib/components/asset-viewer/editor/crop-tool/mouse-handlers.ts @@ -58,7 +58,7 @@ export function handleMouseDown(e: MouseEvent) { } document.body.style.userSelect = 'none'; - globalThis.addEventListener('mouseup', handleMouseUp); + globalThis.addEventListener('mouseup', handleMouseUp, { passive: true }); } export function handleMouseMove(e: MouseEvent) { diff --git a/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte b/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte index c88afc765a..0336e0a74d 100644 --- a/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte +++ b/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte @@ -57,6 +57,7 @@ canvas = new Canvas(canvasEl); configureControlStyle(); + // eslint-disable-next-line tscompat/tscompat faceRect = new Rect({ fill: 'rgba(66,80,175,0.25)', stroke: 'rgb(66,80,175)', diff --git a/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte b/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte index 517e630dc9..820c7f3fd4 100644 --- a/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte +++ b/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte @@ -7,9 +7,9 @@ type AdapterConstructor, type PluginConstructor, } from '@photo-sphere-viewer/core'; - import { SettingsPlugin } from '@photo-sphere-viewer/settings-plugin'; - import { ResolutionPlugin } from '@photo-sphere-viewer/resolution-plugin'; import '@photo-sphere-viewer/core/index.css'; + import { ResolutionPlugin } from '@photo-sphere-viewer/resolution-plugin'; + import { SettingsPlugin } from '@photo-sphere-viewer/settings-plugin'; import '@photo-sphere-viewer/settings-plugin/index.css'; import { onDestroy, onMount } from 'svelte'; @@ -68,18 +68,20 @@ fisheye: false, }); const resolutionPlugin = viewer.getPlugin(ResolutionPlugin) as ResolutionPlugin; + const zoomHandler = ({ zoomLevel }: events.ZoomUpdatedEvent) => { + // zoomLevel range: [0, 100] + if (Math.round(zoomLevel) >= 75) { + // Replace the preview with the original + void resolutionPlugin.setResolution('original'); + viewer.removeEventListener(events.ZoomUpdatedEvent.type, zoomHandler); + } + }; if (originalPanorama && !$alwaysLoadOriginalFile) { - const zoomHandler = ({ zoomLevel }: events.ZoomUpdatedEvent) => { - // zoomLevel range: [0, 100] - if (Math.round(zoomLevel) >= 75) { - // Replace the preview with the original - void resolutionPlugin.setResolution('original'); - viewer.removeEventListener(events.ZoomUpdatedEvent.type, zoomHandler); - } - }; - viewer.addEventListener(events.ZoomUpdatedEvent.type, zoomHandler); + viewer.addEventListener(events.ZoomUpdatedEvent.type, zoomHandler, { passive: true }); } + + return () => viewer.removeEventListener(events.ZoomUpdatedEvent.type, zoomHandler); }); onDestroy(() => { diff --git a/web/src/lib/components/asset-viewer/photo-viewer.svelte b/web/src/lib/components/asset-viewer/photo-viewer.svelte index 9cb80fb2dd..5b3c84ed5a 100644 --- a/web/src/lib/components/asset-viewer/photo-viewer.svelte +++ b/web/src/lib/components/asset-viewer/photo-viewer.svelte @@ -3,9 +3,10 @@ import { zoomImageAction } from '$lib/actions/zoom-image'; import FaceEditor from '$lib/components/asset-viewer/face-editor/face-editor.svelte'; import BrokenAsset from '$lib/components/assets/broken-asset.svelte'; + import { assetViewerFadeDuration } from '$lib/constants'; import { castManager } from '$lib/managers/cast-manager.svelte'; - import { photoViewerImgElement } from '$lib/stores/assets-store.svelte'; import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; + import { photoViewerImgElement } from '$lib/stores/assets-store.svelte'; import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; import { boundingBoxesArray } from '$lib/stores/people.store'; import { alwaysLoadOriginalFile } from '$lib/stores/preferences.store'; @@ -192,8 +193,8 @@ if (loader?.complete) { onload(); } - loader?.addEventListener('load', onload); - loader?.addEventListener('error', onerror); + loader?.addEventListener('load', onload, { passive: true }); + loader?.addEventListener('error', onerror, { passive: true }); return () => { loader?.removeEventListener('load', onload); loader?.removeEventListener('error', onerror); @@ -240,7 +241,7 @@ use:swipe={() => ({})} onswipe={onSwipe} class="h-full w-full" - transition:fade={{ duration: haveFadeTransition ? 150 : 0 }} + transition:fade={{ duration: haveFadeTransition ? assetViewerFadeDuration : 0 }} > {#if $slideshowState !== SlideshowState.None && $slideshowLook === SlideshowLook.BlurredBackground} { + // eslint-disable-next-line tscompat/tscompat if (document.fullscreenElement) { + // eslint-disable-next-line tscompat/tscompat await document.exitFullscreen(); } - await modalManager.show(SlideshowSettingsModal, {}); + await modalManager.show(SlideshowSettingsModal); }; + + onMount(() => { + function exitFullscreenHandler() { + const doc = document as Document & { + webkitIsFullScreen?: boolean; + }; + + if ( + // eslint-disable-next-line tscompat/tscompat + !document.fullscreenElement && + !doc.webkitIsFullScreen + ) { + onClose(); + } + } + + document.addEventListener('fullscreenchange', exitFullscreenHandler); + document.addEventListener('webkitfullscreenchange', exitFullscreenHandler); + + return () => { + document.removeEventListener('fullscreenchange', exitFullscreenHandler); + document.removeEventListener('webkitfullscreenchange', exitFullscreenHandler); + }; + }); { + // Show video after mount to ensure fading in. + showVideo = true; assetFileUrl = getAssetPlaybackUrl({ id: assetId, cacheKey }); if (videoPlayer) { forceMuted = false; @@ -102,59 +106,61 @@ }); -

- {#if castManager.isCasting} -
- -
- {:else} - - - {#if isLoading} -
- +{#if showVideo} +
+ {#if castManager.isCasting} +
+
- {/if} + {:else} + - {#if isFaceEditMode.value} - + {#if isLoading} +
+ +
+ {/if} + + {#if isFaceEditMode.value} + + {/if} {/if} - {/if} -
+
+{/if} diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index 00fef98c09..04f6d94e0f 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -123,17 +123,21 @@ mouseOver = false; }; - let timer: ReturnType; + let timer: ReturnType | null = null; const preventContextMenu = (evt: Event) => evt.preventDefault(); - let disposeables: (() => void)[] = []; + const disposeables: (() => void)[] = []; const clearLongPressTimer = () => { + if (!timer) { + return; + } clearTimeout(timer); + timer = null; for (const dispose of disposeables) { dispose(); } - disposeables = []; + disposeables.length = 0; }; let startX: number = 0; @@ -162,7 +166,7 @@ }; element.addEventListener('click', click); element.addEventListener('pointerdown', start, true); - element.addEventListener('pointerup', clearLongPressTimer, true); + element.addEventListener('pointerup', clearLongPressTimer, { capture: true, passive: true }); return { destroy: () => { element.removeEventListener('click', click); @@ -172,17 +176,15 @@ }; } function moveHandler(e: PointerEvent) { - var diffX = Math.abs(startX - e.clientX); - var diffY = Math.abs(startY - e.clientY); - if (diffX >= 10 || diffY >= 10) { + if (Math.abs(startX - e.clientX) >= 10 || Math.abs(startY - e.clientY) >= 10) { clearLongPressTimer(); } } onMount(() => { - document.addEventListener('scroll', clearLongPressTimer, true); - document.addEventListener('wheel', clearLongPressTimer, true); - document.addEventListener('contextmenu', clearLongPressTimer, true); - document.addEventListener('pointermove', moveHandler, true); + document.addEventListener('scroll', clearLongPressTimer, { capture: true, passive: true }); + document.addEventListener('wheel', clearLongPressTimer, { capture: true, passive: true }); + document.addEventListener('contextmenu', clearLongPressTimer, { capture: true, passive: true }); + document.addEventListener('pointermove', moveHandler, { capture: true, passive: true }); return () => { document.removeEventListener('scroll', clearLongPressTimer, true); document.removeEventListener('wheel', clearLongPressTimer, true); @@ -373,7 +375,7 @@ {:else if selected}
- +
{:else} diff --git a/web/src/lib/components/elements/badge.svelte b/web/src/lib/components/elements/badge.svelte index 0db6e3fa40..b19136aab5 100644 --- a/web/src/lib/components/elements/badge.svelte +++ b/web/src/lib/components/elements/badge.svelte @@ -1,29 +1,16 @@ - - diff --git a/web/src/lib/components/forms/library-import-paths-form.svelte b/web/src/lib/components/forms/library-import-paths-form.svelte index c6042d5ce5..f83057bf67 100644 --- a/web/src/lib/components/forms/library-import-paths-form.svelte +++ b/web/src/lib/components/forms/library-import-paths-form.svelte @@ -169,19 +169,9 @@ > {#if validatedPath.isValid} - + {:else} - + {/if} diff --git a/web/src/lib/components/layouts/user-page-layout.svelte b/web/src/lib/components/layouts/user-page-layout.svelte index 4fc8f5d12e..19381e07c4 100644 --- a/web/src/lib/components/layouts/user-page-layout.svelte +++ b/web/src/lib/components/layouts/user-page-layout.svelte @@ -35,7 +35,7 @@ children, }: Props = $props(); - let scrollbarClass = $derived(scrollbar ? 'immich-scrollbar p-2' : 'scrollbar-hidden'); + let scrollbarClass = $derived(scrollbar ? 'immich-scrollbar' : 'scrollbar-hidden'); let hasTitleClass = $derived(title ? 'top-16 h-[calc(100%-(--spacing(16)))]' : 'top-0 h-full'); @@ -60,7 +60,7 @@ {/if}
-
+
{@render children?.()}
diff --git a/web/src/lib/components/memory-page/memory-photo-viewer.svelte b/web/src/lib/components/memory-page/memory-photo-viewer.svelte new file mode 100644 index 0000000000..b0b1dc98a6 --- /dev/null +++ b/web/src/lib/components/memory-page/memory-photo-viewer.svelte @@ -0,0 +1,69 @@ + + +{#if !imageLoaded} + + +{/if} + +{#if !imageLoaded} +
+ +
+{:else if imageLoaded} +
+ {$getAltText(asset)} +
+{/if} + + diff --git a/web/src/lib/components/memory-page/memory-video-viewer.svelte b/web/src/lib/components/memory-page/memory-video-viewer.svelte new file mode 100644 index 0000000000..7758b067f3 --- /dev/null +++ b/web/src/lib/components/memory-page/memory-video-viewer.svelte @@ -0,0 +1,40 @@ + + +{#if showVideo} +
+ +
+{/if} diff --git a/web/src/lib/components/memory-page/memory-viewer.svelte b/web/src/lib/components/memory-page/memory-viewer.svelte index b36082b500..f1a15f4429 100644 --- a/web/src/lib/components/memory-page/memory-viewer.svelte +++ b/web/src/lib/components/memory-page/memory-viewer.svelte @@ -4,6 +4,8 @@ import { intersectionObserver } from '$lib/actions/intersection-observer'; import { resizeObserver } from '$lib/actions/resize-observer'; import { shortcuts } from '$lib/actions/shortcut'; + import MemoryPhotoViewer from '$lib/components/memory-page/memory-photo-viewer.svelte'; + import MemoryVideoViewer from '$lib/components/memory-page/memory-video-viewer.svelte'; import AddToAlbum from '$lib/components/photos-page/actions/add-to-album.svelte'; import ArchiveAction from '$lib/components/photos-page/actions/archive-action.svelte'; import ChangeDate from '$lib/components/photos-page/actions/change-date-action.svelte'; @@ -25,16 +27,14 @@ } from '$lib/components/shared-components/notification/notification'; import { AppRoute, QueryParameter } from '$lib/constants'; import { authManager } from '$lib/managers/auth-manager.svelte'; + import type { TimelineAsset, Viewport } from '$lib/managers/timeline-manager/types'; import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { assetViewingStore } from '$lib/stores/asset-viewing.store'; - import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; - import type { Viewport } from '$lib/managers/timeline-manager/types'; import { type MemoryAsset, memoryStore } from '$lib/stores/memory.store.svelte'; import { locale, videoViewerMuted, videoViewerVolume } from '$lib/stores/preferences.store'; import { preferences } from '$lib/stores/user.store'; - import { getAssetPlaybackUrl, getAssetThumbnailUrl, handlePromiseError, memoryLaneTitle } from '$lib/utils'; + import { getAssetThumbnailUrl, handlePromiseError, memoryLaneTitle } from '$lib/utils'; import { cancelMultiselect } from '$lib/utils/asset-utils'; - import { getAltText } from '$lib/utils/thumbnail-util'; import { fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util'; import { AssetMediaSize, getAssetInfo } from '@immich/sdk'; import { IconButton } from '@immich/ui'; @@ -60,7 +60,6 @@ import { DateTime } from 'luxon'; import { t } from 'svelte-i18n'; import { Tween } from 'svelte/motion'; - import { fade } from 'svelte/transition'; let memoryGallery: HTMLElement | undefined = $state(); let memoryWrapper: HTMLElement | undefined = $state(); @@ -261,12 +260,7 @@ playerInitialized = true; }; - afterNavigate(({ from, to, type }) => { - if (type === 'enter') { - // afterNavigate triggers twice on first page load (once when mounted with 'enter' and then a second time - // with the actual 'goto' to URL). - return; - } + afterNavigate(({ from, to }) => { memoryStore.initialize().then( () => { let target = null; @@ -369,15 +363,16 @@ {/snippet}
- ($videoViewerMuted = !$videoViewerMuted)} - /> + +
+ ($videoViewerMuted = !$videoViewerMuted)} + /> +
{#if galleryInView}
@@ -415,7 +413,6 @@ >
{#key current.asset.id} -
- {#if current.asset.isVideo} - - {:else} - {$getAltText(current.asset)} - {/if} -
+ {#if current.asset.isVideo} + + {:else} + + {/if} {/key}
{#if current.previous} -
+
{/if} {#if current.next} -
+
@@ -632,13 +617,12 @@
>(); @@ -14,14 +14,12 @@ config = await getConfig(); }); - export const save = async () => { - await adminSettingsComponent?.handleSave({ storageTemplate: config?.storageTemplate }); - }; + onDestroy(() => adminSettingsComponent?.handleSave({ storageTemplate: config?.storageTemplate }));

- + {#snippet children({ message })} {message} {/snippet} diff --git a/web/src/lib/components/photos-page/actions/add-to-album.svelte b/web/src/lib/components/photos-page/actions/add-to-album.svelte index 1918dc6c20..c00fd2e1c9 100644 --- a/web/src/lib/components/photos-page/actions/add-to-album.svelte +++ b/web/src/lib/components/photos-page/actions/add-to-album.svelte @@ -1,12 +1,12 @@ (showAlbumPicker = true)} + {onClick} text={shared ? $t('add_to_shared_album') : $t('add_to_album')} icon={shared ? mdiShareVariantOutline : mdiImageAlbum} shortcut={{ key: 'l', shift: shared }} /> - -{#if showAlbumPicker} - -{/if} diff --git a/web/src/lib/components/photos-page/actions/archive-action.svelte b/web/src/lib/components/photos-page/actions/archive-action.svelte index e36038972f..150e7c201f 100644 --- a/web/src/lib/components/photos-page/actions/archive-action.svelte +++ b/web/src/lib/components/photos-page/actions/archive-action.svelte @@ -2,11 +2,11 @@ import type { OnArchive } from '$lib/utils/actions'; import { archiveAssets } from '$lib/utils/asset-utils'; import { AssetVisibility } from '@immich/sdk'; + import { IconButton } from '@immich/ui'; import { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline, mdiTimerSand } from '@mdi/js'; import { t } from 'svelte-i18n'; import MenuOption from '../../shared-components/context-menu/menu-option.svelte'; import { getAssetControlContext } from '../asset-select-control-bar.svelte'; - import { IconButton } from '@immich/ui'; interface Props { onArchive?: OnArchive; diff --git a/web/src/lib/components/photos-page/actions/change-description-action.svelte b/web/src/lib/components/photos-page/actions/change-description-action.svelte index 129d327fb9..740bed78ef 100644 --- a/web/src/lib/components/photos-page/actions/change-description-action.svelte +++ b/web/src/lib/components/photos-page/actions/change-description-action.svelte @@ -18,7 +18,7 @@ const { clearSelect, getOwnedAssets } = getAssetControlContext(); const handleUpdateDescription = async () => { - const description = await modalManager.show(AssetUpdateDecriptionConfirmModal, {}); + const description = await modalManager.show(AssetUpdateDecriptionConfirmModal); if (description) { const ids = getSelectedAssets(getOwnedAssets(), $user); diff --git a/web/src/lib/components/photos-page/actions/focus-actions.ts b/web/src/lib/components/photos-page/actions/focus-actions.ts index 3a75820cd8..f0f9e2e50c 100644 --- a/web/src/lib/components/photos-page/actions/focus-actions.ts +++ b/web/src/lib/components/photos-page/actions/focus-actions.ts @@ -1,4 +1,4 @@ -import { AssetStore } from '$lib/managers/timeline-manager/asset-store.svelte'; +import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte'; import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; import { moveFocus } from '$lib/utils/focus-util'; import { InvocationTracker } from '$lib/utils/invocationTracker'; @@ -31,7 +31,7 @@ export const setFocusToAsset = (scrollToAsset: (asset: TimelineAsset) => boolean export const setFocusTo = async ( scrollToAsset: (asset: TimelineAsset) => boolean, - store: AssetStore, + store: TimelineManager, direction: 'earlier' | 'later', interval: 'day' | 'month' | 'year' | 'asset', ) => { diff --git a/web/src/lib/components/photos-page/actions/select-all-assets.svelte b/web/src/lib/components/photos-page/actions/select-all-assets.svelte index d8a5ab2fdf..883ec1d54e 100644 --- a/web/src/lib/components/photos-page/actions/select-all-assets.svelte +++ b/web/src/lib/components/photos-page/actions/select-all-assets.svelte @@ -1,6 +1,6 @@ -{#each filterIntersecting(bucket.dateGroups) as dateGroup, groupIndex (dateGroup.day)} - {@const absoluteWidth = dateGroup.left} +{#each filterIntersecting(monthGroup.dayGroups) as dayGroup, groupIndex (dayGroup.day)} + {@const absoluteWidth = dayGroup.left}

{ isMouseOverGroup = true; - assetMouseEventHandler(dateGroup.groupTitle, null); + assetMouseEventHandler(dayGroup.groupTitle, null); }} onmouseleave={() => { isMouseOverGroup = false; - assetMouseEventHandler(dateGroup.groupTitle, null); + assetMouseEventHandler(dayGroup.groupTitle, null); }} >
- {#if !singleSelect && ((hoveredDateGroup === dateGroup.groupTitle && isMouseOverGroup) || assetInteraction.selectedGroup.has(dateGroup.groupTitle))} + {#if !singleSelect && ((hoveredDayGroup === dayGroup.groupTitle && isMouseOverGroup) || assetInteraction.selectedGroup.has(dayGroup.groupTitle))}
handleSelectGroup(dateGroup.groupTitle, assetsSnapshot(dateGroup.getAssets()))} - onkeydown={() => handleSelectGroup(dateGroup.groupTitle, assetsSnapshot(dateGroup.getAssets()))} + class="inline-block pe-2 hover:cursor-pointer" + onclick={() => handleSelectGroup(dayGroup.groupTitle, assetsSnapshot(dayGroup.getAssets()))} + onkeydown={() => handleSelectGroup(dayGroup.groupTitle, assetsSnapshot(dayGroup.getAssets()))} > - {#if assetInteraction.selectedGroup.has(dateGroup.groupTitle)} - + {#if assetInteraction.selectedGroup.has(dayGroup.groupTitle)} + {:else} {/if}
{/if} - - {dateGroup.groupTitle} + + {dayGroup.groupTitle}
@@ -159,14 +166,14 @@
- {#each filterIntersecting(dateGroup.intersectingAssets) as intersectingAsset (intersectingAsset.id)} - {@const position = intersectingAsset.position!} - {@const asset = intersectingAsset.asset!} + {#each filterIntersecting(dayGroup.viewerAssets) as viewerAsset (viewerAsset.id)} + {@const position = viewerAsset.position!} + {@const asset = viewerAsset.asset!} - +
onClick(assetStore, dateGroup.getAssets(), dateGroup.groupTitle, asset)} - onSelect={(asset) => assetSelectHandler(assetStore, asset, dateGroup.getAssets(), dateGroup.groupTitle)} - onMouseEvent={() => assetMouseEventHandler(dateGroup.groupTitle, assetSnapshot(asset))} - selected={assetInteraction.hasSelectedAsset(asset.id) || dateGroup.bucket.store.albumAssets.has(asset.id)} + onClick={(asset) => onClick(timelineManager, dayGroup.getAssets(), dayGroup.groupTitle, asset)} + onSelect={(asset) => assetSelectHandler(timelineManager, asset, dayGroup.getAssets(), dayGroup.groupTitle)} + onMouseEvent={() => assetMouseEventHandler(dayGroup.groupTitle, assetSnapshot(asset))} + selected={assetInteraction.hasSelectedAsset(asset.id) || + dayGroup.monthGroup.timelineManager.albumAssets.has(asset.id)} selectionCandidate={assetInteraction.hasSelectionCandidate(asset.id)} - disabled={dateGroup.bucket.store.albumAssets.has(asset.id)} + disabled={dayGroup.monthGroup.timelineManager.albumAssets.has(asset.id)} thumbnailWidth={position.width} thumbnailHeight={position.height} /> diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index 1b96353d37..2d40857f99 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -12,26 +12,30 @@ import ChangeDate from '$lib/components/shared-components/change-date.svelte'; import Scrubber from '$lib/components/shared-components/scrubber/scrubber.svelte'; import { AppRoute, AssetAction } from '$lib/constants'; - import { albumMapViewManager } from '$lib/managers/album-view-map.manager.svelte'; import { authManager } from '$lib/managers/auth-manager.svelte'; import { modalManager } from '$lib/managers/modal-manager.svelte'; + import type { MonthGroup } from '$lib/managers/timeline-manager/month-group.svelte'; + import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte'; + import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; + import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte'; import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte'; import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { assetViewingStore } from '$lib/stores/asset-viewing.store'; import { isSelectingAllAssets } from '$lib/stores/assets-store.svelte'; - import { AssetStore } from '$lib/managers/timeline-manager/asset-store.svelte'; - import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; - import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte'; import { mobileDevice } from '$lib/stores/mobile-device.svelte'; import { showDeleteModal } from '$lib/stores/preferences.store'; import { searchStore } from '$lib/stores/search.svelte'; import { featureFlags } from '$lib/stores/server-config.store'; - import type { AssetBucket } from '$lib/managers/timeline-manager/asset-bucket.svelte'; import { handlePromiseError } from '$lib/utils'; import { deleteAssets, updateStackedAssetInTimeline, updateUnstackedAssetInTimeline } from '$lib/utils/actions'; import { archiveAssets, cancelMultiselect, selectAllAssets, stackAssets } from '$lib/utils/asset-utils'; import { navigate } from '$lib/utils/navigation'; - import { toTimelineAsset, type ScrubberListener, type TimelinePlainYearMonth } from '$lib/utils/timeline-util'; + import { + getTimes, + toTimelineAsset, + type ScrubberListener, + type TimelinePlainYearMonth, + } from '$lib/utils/timeline-util'; import { AssetVisibility, getAssetInfo, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk'; import { DateTime } from 'luxon'; import { onMount, type Snippet } from 'svelte'; @@ -47,7 +51,7 @@ `AssetViewingStore.gridScrollTarget` and load and scroll to the asset specified, and additionally, update the page location/url with the asset as the asset-grid is scrolled */ enableRouting: boolean; - assetStore: AssetStore; + timelineManager: TimelineManager; assetInteraction: AssetInteraction; removeAction?: | AssetAction.UNARCHIVE @@ -72,7 +76,7 @@ isSelectionMode = false, singleSelect = false, enableRouting, - assetStore = $bindable(), + timelineManager = $bindable(), assetInteraction, removeAction = null, withStacked = false, @@ -87,15 +91,15 @@ empty, }: Props = $props(); - let { isViewing: showAssetViewer, asset: viewingAsset, preloadAssets, gridScrollTarget } = assetViewingStore; + let { isViewing: showAssetViewer, asset: viewingAsset, preloadAssets, gridScrollTarget, mutex } = assetViewingStore; let element: HTMLElement | undefined = $state(); let timelineElement: HTMLElement | undefined = $state(); let showSkeleton = $state(true); let isShowSelectDate = $state(false); - let scrubBucketPercent = $state(0); - let scrubBucket: TimelinePlainYearMonth | undefined = $state(); + let scrubberMonthPercent = $state(0); + let scrubberMonth: { year: number; month: number } | undefined = $state(undefined); let scrubOverallPercent: number = $state(0); let scrubberWidth = $state(0); @@ -116,7 +120,7 @@ rowHeight: 235, headerHeight: 48, }; - assetStore.setLayoutOptions(layoutOptions); + timelineManager.setLayoutOptions(layoutOptions); }); const scrollTo = (top: number) => { @@ -138,35 +142,35 @@ scrollTo(0); }; - const getAssetHeight = (assetId: string, bucket: AssetBucket) => { + const getAssetHeight = (assetId: string, monthGroup: MonthGroup) => { // the following method may trigger any layouts, so need to // handle any scroll compensation that may have been set - const height = bucket!.findAssetAbsolutePosition(assetId); + const height = monthGroup!.findAssetAbsolutePosition(assetId); - while (assetStore.scrollCompensation.bucket) { - handleScrollCompensation(assetStore.scrollCompensation); - assetStore.clearScrollCompensation(); + while (timelineManager.scrollCompensation.monthGroup) { + handleScrollCompensation(timelineManager.scrollCompensation); + timelineManager.clearScrollCompensation(); } return height; }; const scrollToAssetId = async (assetId: string) => { - const bucket = await assetStore.findBucketForAsset(assetId); - if (!bucket) { + const monthGroup = await timelineManager.findMonthGroupForAsset(assetId); + if (!monthGroup) { return false; } - const height = getAssetHeight(assetId, bucket); + const height = getAssetHeight(assetId, monthGroup); scrollTo(height); updateSlidingWindow(); return true; }; const scrollToAsset = (asset: TimelineAsset) => { - const bucket = assetStore.getBucketIndexByAssetId(asset.id); - if (!bucket) { + const monthGroup = timelineManager.getMonthGroupByAssetId(asset.id); + if (!monthGroup) { return false; } - const height = getAssetHeight(asset.id, bucket); + const height = getAssetHeight(asset.id, monthGroup); scrollTo(height); updateSlidingWindow(); return true; @@ -185,7 +189,7 @@ showSkeleton = false; }; - beforeNavigate(() => (assetStore.suspendTransitions = true)); + beforeNavigate(() => (timelineManager.suspendTransitions = true)); afterNavigate((nav) => { const { complete } = nav; @@ -224,7 +228,7 @@ import.meta.hot?.on('vite:beforeUpdate', (payload) => { const assetGridUpdate = payload.updates.some((update) => update.path.endsWith('asset-grid.svelte')); if (assetGridUpdate) { - assetStore.destroy(); + timelineManager.destroy(); } }); @@ -233,9 +237,9 @@ return () => void 0; }; - const updateIsScrolling = () => (assetStore.scrolling = true); + const updateIsScrolling = () => (timelineManager.scrolling = true); // note: don't throttle, debounch, or otherwise do this function async - it causes flicker - const updateSlidingWindow = () => assetStore.updateSlidingWindow(element?.scrollTop || 0); + const updateSlidingWindow = () => timelineManager.updateSlidingWindow(element?.scrollTop || 0); const handleScrollCompensation = ({ heightDelta, scrollTop }: { heightDelta?: number; scrollTop?: number }) => { if (heightDelta !== undefined) { @@ -245,12 +249,12 @@ } // Yes, updateSlideWindow() is called by the onScroll event triggered as a result of // the above calls. However, this delay is enough time to set the intersecting property - // of the bucket to false, then true, which causes the DOM nodes to be recreated, + // of the monthGroup to false, then true, which causes the DOM nodes to be recreated, // causing bad perf, and also, disrupting focus of those elements. updateSlidingWindow(); }; - const topSectionResizeObserver: OnResizeCallback = ({ height }) => (assetStore.topSectionHeight = height); + const topSectionResizeObserver: OnResizeCallback = ({ height }) => (timelineManager.topSectionHeight = height); onMount(() => { if (!enableRouting) { @@ -263,21 +267,23 @@ }); const getMaxScrollPercent = () => { - const totalHeight = assetStore.timelineHeight + bottomSectionHeight + assetStore.topSectionHeight; - return (totalHeight - assetStore.viewportHeight) / totalHeight; + const totalHeight = timelineManager.timelineHeight + bottomSectionHeight + timelineManager.topSectionHeight; + return (totalHeight - timelineManager.viewportHeight) / totalHeight; }; const getMaxScroll = () => { if (!element || !timelineElement) { return 0; } - return assetStore.topSectionHeight + bottomSectionHeight + (timelineElement.clientHeight - element.clientHeight); + return ( + timelineManager.topSectionHeight + bottomSectionHeight + (timelineElement.clientHeight - element.clientHeight) + ); }; - const scrollToBucketAndOffset = (bucket: AssetBucket, bucketScrollPercent: number) => { - const topOffset = bucket.top; + const scrollToMonthGroupAndOffset = (monthGroup: MonthGroup, monthGroupScrollPercent: number) => { + const topOffset = monthGroup.top; const maxScrollPercent = getMaxScrollPercent(); - const delta = bucket.bucketHeight * bucketScrollPercent; + const delta = monthGroup.height * monthGroupScrollPercent; const scrollToTop = (topOffset + delta) * maxScrollPercent; scrollTop(scrollToTop); @@ -285,23 +291,23 @@ // note: don't throttle, debounch, or otherwise make this function async - it causes flicker const onScrub: ScrubberListener = ( - bucketDate: { year: number; month: number } | undefined, - scrollPercent: number, - bucketScrollPercent: number, + scrubMonth: { year: number; month: number }, + overallScrollPercent: number, + scrubberMonthScrollPercent: number, ) => { - if (!bucketDate || assetStore.timelineHeight < assetStore.viewportHeight * 2) { + if (!scrubMonth || timelineManager.timelineHeight < timelineManager.viewportHeight * 2) { // edge case - scroll limited due to size of content, must adjust - use use the overall percent instead const maxScroll = getMaxScroll(); - const offset = maxScroll * scrollPercent; + const offset = maxScroll * overallScrollPercent; scrollTop(offset); } else { - const bucket = assetStore.buckets.find( - (bucket) => bucket.yearMonth.year === bucketDate.year && bucket.yearMonth.month === bucketDate.month, + const monthGroup = timelineManager.months.find( + ({ yearMonth: { year, month } }) => year === scrubMonth.year && month === scrubMonth.month, ); - if (!bucket) { + if (!monthGroup) { return; } - scrollToBucketAndOffset(bucket, bucketScrollPercent); + scrollToMonthGroupAndOffset(monthGroup, scrubberMonthScrollPercent); } }; @@ -313,19 +319,19 @@ return; } - if (assetStore.timelineHeight < assetStore.viewportHeight * 2) { + if (timelineManager.timelineHeight < timelineManager.viewportHeight * 2) { // edge case - scroll limited due to size of content, must adjust - use the overall percent instead const maxScroll = getMaxScroll(); scrubOverallPercent = Math.min(1, element.scrollTop / maxScroll); - scrubBucket = undefined; - scrubBucketPercent = 0; + scrubberMonth = undefined; + scrubberMonthPercent = 0; } else { let top = element.scrollTop; - if (top < assetStore.topSectionHeight) { + if (top < timelineManager.topSectionHeight) { // in the lead-in area - scrubBucket = undefined; - scrubBucketPercent = 0; + scrubberMonth = undefined; + scrubberMonthPercent = 0; const maxScroll = getMaxScroll(); scrubOverallPercent = Math.min(1, element.scrollTop / maxScroll); @@ -335,33 +341,33 @@ let maxScrollPercent = getMaxScrollPercent(); let found = false; - const bucketsLength = assetStore.buckets.length; - for (let i = -1; i < bucketsLength + 1; i++) { - let bucket: TimelinePlainYearMonth | undefined; - let bucketHeight = 0; + const monthsLength = timelineManager.months.length; + for (let i = -1; i < monthsLength + 1; i++) { + let monthGroup: TimelinePlainYearMonth | undefined; + let monthGroupHeight = 0; if (i === -1) { // lead-in - bucketHeight = assetStore.topSectionHeight; - } else if (i === bucketsLength) { + monthGroupHeight = timelineManager.topSectionHeight; + } else if (i === monthsLength) { // lead-out - bucketHeight = bottomSectionHeight; + monthGroupHeight = bottomSectionHeight; } else { - bucket = assetStore.buckets[i].yearMonth; - bucketHeight = assetStore.buckets[i].bucketHeight; + monthGroup = timelineManager.months[i].yearMonth; + monthGroupHeight = timelineManager.months[i].height; } - let next = top - bucketHeight * maxScrollPercent; + let next = top - monthGroupHeight * maxScrollPercent; // instead of checking for < 0, add a little wiggle room for subpixel resolution - if (next < -1 && bucket) { - scrubBucket = bucket; + if (next < -1 && monthGroup) { + scrubberMonth = monthGroup; // allowing next to be at least 1 may cause percent to go negative, so ensure positive percentage - scrubBucketPercent = Math.max(0, top / (bucketHeight * maxScrollPercent)); + scrubberMonthPercent = Math.max(0, top / (monthGroupHeight * maxScrollPercent)); // compensate for lost precision/rounding errors advance to the next bucket, if present - if (scrubBucketPercent > 0.9999 && i + 1 < bucketsLength - 1) { - scrubBucket = assetStore.buckets[i + 1].yearMonth; - scrubBucketPercent = 0; + if (scrubberMonthPercent > 0.9999 && i + 1 < monthsLength - 1) { + scrubberMonth = timelineManager.months[i + 1].yearMonth; + scrubberMonthPercent = 0; } found = true; @@ -371,8 +377,8 @@ } if (!found) { leadout = true; - scrubBucket = undefined; - scrubBucketPercent = 0; + scrubberMonth = undefined; + scrubberMonthPercent = 0; scrubOverallPercent = 1; } } @@ -382,9 +388,9 @@ isShowDeleteConfirmation = false; await deleteAssets( !(isTrashEnabled && !force), - (assetIds) => assetStore.removeAssets(assetIds), + (assetIds) => timelineManager.removeAssets(assetIds), assetInteraction.selectedAssets, - !isTrashEnabled || force ? undefined : (assets) => assetStore.addAssets(assets), + !isTrashEnabled || force ? undefined : (assets) => timelineManager.addAssets(assets), ); assetInteraction.clearMultiselect(); }; @@ -410,53 +416,59 @@ const onStackAssets = async () => { const result = await stackAssets(assetInteraction.selectedAssets); - updateStackedAssetInTimeline(assetStore, result); + updateStackedAssetInTimeline(timelineManager, result); onEscape(); }; const toggleArchive = async () => { - await archiveAssets( - assetInteraction.selectedAssets, - assetInteraction.isAllArchived ? AssetVisibility.Timeline : AssetVisibility.Archive, - ); - assetStore.updateAssets(assetInteraction.selectedAssets); + const visibility = assetInteraction.isAllArchived ? AssetVisibility.Timeline : AssetVisibility.Archive; + const ids = await archiveAssets(assetInteraction.selectedAssets, visibility); + timelineManager.updateAssetOperation(ids, (asset) => { + asset.visibility = visibility; + return { remove: false }; + }); deselectAllAssets(); }; const handleSelectAsset = (asset: TimelineAsset) => { - if (!assetStore.albumAssets.has(asset.id)) { + if (!timelineManager.albumAssets.has(asset.id)) { assetInteraction.selectAsset(asset); } }; const handlePrevious = async () => { - const laterAsset = await assetStore.getLaterAsset($viewingAsset); + const release = await mutex.acquire(); + const laterAsset = await timelineManager.getLaterAsset($viewingAsset); if (laterAsset) { - const preloadAsset = await assetStore.getLaterAsset(laterAsset); + const preloadAsset = await timelineManager.getLaterAsset(laterAsset); const asset = await getAssetInfo({ id: laterAsset.id, key: authManager.key }); assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []); await navigate({ targetRoute: 'current', assetId: laterAsset.id }); } + release(); return !!laterAsset; }; const handleNext = async () => { - const earlierAsset = await assetStore.getEarlierAsset($viewingAsset); + const release = await mutex.acquire(); + const earlierAsset = await timelineManager.getEarlierAsset($viewingAsset); + if (earlierAsset) { - const preloadAsset = await assetStore.getEarlierAsset(earlierAsset); + const preloadAsset = await timelineManager.getEarlierAsset(earlierAsset); const asset = await getAssetInfo({ id: earlierAsset.id, key: authManager.key }); assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []); await navigate({ targetRoute: 'current', assetId: earlierAsset.id }); } + release(); return !!earlierAsset; }; const handleRandom = async () => { - const randomAsset = await assetStore.getRandomAsset(); + const randomAsset = await timelineManager.getRandomAsset(); if (randomAsset) { const asset = await getAssetInfo({ id: randomAsset.id, key: authManager.key }); @@ -487,7 +499,7 @@ (await handleNext()) || (await handlePrevious()) || (await handleClose(action.asset)); // delete after find the next one - assetStore.removeAssets([action.asset.id]); + timelineManager.removeAssets([action.asset.id]); break; } } @@ -498,26 +510,26 @@ case AssetAction.UNARCHIVE: case AssetAction.FAVORITE: case AssetAction.UNFAVORITE: { - assetStore.updateAssets([action.asset]); + timelineManager.updateAssets([action.asset]); break; } case AssetAction.ADD: { - assetStore.addAssets([action.asset]); + timelineManager.addAssets([action.asset]); break; } case AssetAction.UNSTACK: { - updateUnstackedAssetInTimeline(assetStore, action.assets); + updateUnstackedAssetInTimeline(timelineManager, action.assets); break; } case AssetAction.SET_STACK_PRIMARY_ASSET: { //Have to unstack then restack assets in timeline in order for the currently removed new primary asset to be made visible. updateUnstackedAssetInTimeline( - assetStore, + timelineManager, action.stack.assets.map((asset) => toTimelineAsset(asset)), ); - updateStackedAssetInTimeline(assetStore, { + updateStackedAssetInTimeline(timelineManager, { stack: action.stack, toDeleteIds: action.stack.assets .filter((asset) => asset.id !== action.stack.primaryAssetId) @@ -565,7 +577,7 @@ lastAssetMouseEvent = asset; }; - const handleGroupSelect = (assetStore: AssetStore, group: string, assets: TimelineAsset[]) => { + const handleGroupSelect = (timelineManager: TimelineManager, group: string, assets: TimelineAsset[]) => { if (assetInteraction.selectedGroup.has(group)) { assetInteraction.removeGroupFromMultiselectGroup(group); for (const asset of assets) { @@ -578,7 +590,7 @@ } } - if (assetStore.count == assetInteraction.selectedAssets.length) { + if (timelineManager.assetCount == assetInteraction.selectedAssets.length) { isSelectingAllAssets.set(true); } else { isSelectingAllAssets.set(false); @@ -615,8 +627,8 @@ assetInteraction.clearAssetSelectionCandidates(); if (assetInteraction.assetSelectionStart && rangeSelection) { - let startBucket = assetStore.getBucketIndexByAssetId(assetInteraction.assetSelectionStart.id); - let endBucket = assetStore.getBucketIndexByAssetId(asset.id); + let startBucket = timelineManager.getMonthGroupByAssetId(assetInteraction.assetSelectionStart.id); + let endBucket = timelineManager.getMonthGroupByAssetId(asset.id); if (startBucket === null || endBucket === null) { return; @@ -624,13 +636,13 @@ // Select/deselect assets in range (start,end) let started = false; - for (const bucket of assetStore.buckets) { - if (bucket === endBucket) { + for (const monthGroup of timelineManager.months) { + if (monthGroup === endBucket) { break; } if (started) { - await assetStore.loadBucket(bucket.yearMonth); - for (const asset of bucket.assetsIterator()) { + await timelineManager.loadMonthGroup(monthGroup.yearMonth); + for (const asset of monthGroup.assetsIterator()) { if (deselect) { assetInteraction.removeAssetFromMultiselectGroup(asset.id); } else { @@ -638,29 +650,29 @@ } } } - if (bucket === startBucket) { + if (monthGroup === startBucket) { started = true; } } // Update date group selection in range [start,end] started = false; - for (const bucket of assetStore.buckets) { - if (bucket === startBucket) { + for (const monthGroup of timelineManager.months) { + if (monthGroup === startBucket) { started = true; } if (started) { - // Split bucket into date groups and check each group - for (const dateGroup of bucket.dateGroups) { - const dateGroupTitle = dateGroup.groupTitle; - if (dateGroup.getAssets().every((a) => assetInteraction.hasSelectedAsset(a.id))) { - assetInteraction.addGroupToMultiselectGroup(dateGroupTitle); + // Split month group into day groups and check each group + for (const dayGroup of monthGroup.dayGroups) { + const dayGroupTitle = dayGroup.groupTitle; + if (dayGroup.getAssets().every((a) => assetInteraction.hasSelectedAsset(a.id))) { + assetInteraction.addGroupToMultiselectGroup(dayGroupTitle); } else { - assetInteraction.removeGroupFromMultiselectGroup(dateGroupTitle); + assetInteraction.removeGroupFromMultiselectGroup(dayGroupTitle); } } } - if (bucket === endBucket) { + if (monthGroup === endBucket) { break; } } @@ -679,7 +691,7 @@ return; } - const assets = assetsSnapshot(await assetStore.retrieveRange(startAsset, endAsset)); + const assets = assetsSnapshot(await timelineManager.retrieveRange(startAsset, endAsset)); assetInteraction.setAssetSelectionCandidates(assets); }; @@ -690,7 +702,7 @@ }; let isTrashEnabled = $derived($featureFlags.loaded && $featureFlags.trash); - let isEmpty = $derived(assetStore.isInitialized && assetStore.buckets.length === 0); + let isEmpty = $derived(timelineManager.isInitialized && timelineManager.months.length === 0); let idsSelectedAssets = $derived(assetInteraction.selectedAssets.map(({ id }) => id)); let isShortcutModalOpen = false; @@ -700,7 +712,7 @@ } isShortcutModalOpen = true; - await modalManager.show(ShortcutsModal, {}); + await modalManager.show(ShortcutsModal); isShortcutModalOpen = false; }; @@ -710,7 +722,7 @@ } }); - const setFocusTo = setFocusToInit.bind(undefined, scrollToAsset, assetStore); + const setFocusTo = setFocusToInit.bind(undefined, scrollToAsset, timelineManager); const setFocusAsset = setFocusAssetInit.bind(undefined, scrollToAsset); let shortcutList = $derived( @@ -723,7 +735,7 @@ { shortcut: { key: 'Escape' }, onShortcut: onEscape }, { shortcut: { key: '?', shift: true }, onShortcut: handleOpenShortcutModal }, { shortcut: { key: '/' }, onShortcut: () => goto(AppRoute.EXPLORE) }, - { shortcut: { key: 'A', ctrl: true }, onShortcut: () => selectAllAssets(assetStore, assetInteraction) }, + { shortcut: { key: 'A', ctrl: true }, onShortcut: () => selectAllAssets(timelineManager, assetInteraction) }, { shortcut: { key: 'ArrowRight' }, onShortcut: () => setFocusTo('earlier', 'asset') }, { shortcut: { key: 'ArrowLeft' }, onShortcut: () => setFocusTo('later', 'asset') }, { shortcut: { key: 'D' }, onShortcut: () => setFocusTo('earlier', 'day') }, @@ -766,6 +778,13 @@ void selectAssetCandidates(lastAssetMouseEvent); } }); + + $effect(() => { + if ($showAssetViewer) { + const { localDateTime } = getTimes($viewingAsset.fileCreatedAt, DateTime.local().offset / 60); + void timelineManager.loadMonthGroup({ year: localDateTime.year, month: localDateTime.month }); + } + }); @@ -785,7 +804,9 @@ timezoneInput={false} onConfirm={async (dateString: string) => { isShowSelectDate = false; - const asset = await assetStore.getClosestAssetToDate((DateTime.fromISO(dateString) as DateTime).toObject()); + const asset = await timelineManager.getClosestAssetToDate( + (DateTime.fromISO(dateString) as DateTime).toObject(), + ); if (asset) { setFocusAsset(asset); } @@ -794,16 +815,16 @@ /> {/if} -{#if assetStore.buckets.length > 0} +{#if timelineManager.months.length > 0} { @@ -824,14 +845,14 @@ /> {/if} - +
((assetStore.viewportWidth = v), updateSlidingWindow())} + bind:clientHeight={timelineManager.viewportHeight} + bind:clientWidth={null, (v) => ((timelineManager.viewportWidth = v), updateSlidingWindow())} bind:this={element} onscroll={() => (handleTimelineScroll(), updateSlidingWindow(), updateIsScrolling())} > @@ -839,7 +860,7 @@ bind:this={timelineElement} id="virtual-timeline" class:invisible={showSkeleton} - style:height={assetStore.timelineHeight + 'px'} + style:height={timelineManager.timelineHeight + 'px'} >
- {#each assetStore.buckets as bucket (bucket.viewId)} - {@const display = bucket.intersecting} - {@const absoluteHeight = bucket.top} + {#each timelineManager.months as monthGroup (monthGroup.viewId)} + {@const display = monthGroup.intersecting} + {@const absoluteHeight = monthGroup.top} - {#if !bucket.isLoaded} + {#if !monthGroup.isLoaded}
- +
{:else if display}
handleGroupSelect(assetStore, title, assets)} + {monthGroup} + onSelect={({ title, assets }) => handleGroupSelect(timelineManager, title, assets)} onSelectAssetCandidates={handleSelectAssetCandidates} onSelectAssets={handleSelectAssets} onScrollCompensation={handleScrollCompensation} @@ -898,33 +922,31 @@ style:position="absolute" style:left="0" style:right="0" - style:transform={`translate3d(0,${assetStore.timelineHeight}px,0)`} + style:transform={`translate3d(0,${timelineManager.timelineHeight}px,0)`} >
-{#if !albumMapViewManager.isInMapView} - - {#if $showAssetViewer} - {#await import('../asset-viewer/asset-viewer.svelte') then { default: AssetViewer }} - - {/await} - {/if} - -{/if} + + {#if $showAssetViewer} + {#await import('../asset-viewer/asset-viewer.svelte') then { default: AssetViewer }} + + {/await} + {/if} +