Compare commits
3 Commits
dev/recogn
...
29
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48bf63addb | ||
|
|
c334eba9b7 | ||
|
|
5b7d8cb3ef |
@@ -1,4 +1,4 @@
|
|||||||
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:a20b8a3538313487ac9266875bbf733e544c1aa2091df2bb99ab592a6d4f7399
|
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:b0b88ef6a5abf21194343d2c5b2829dddd9be1142f65f6a5e4390a51d5a70dd8
|
||||||
FROM ${BASEIMAGE}
|
FROM ${BASEIMAGE}
|
||||||
|
|
||||||
# Flutter SDK
|
# Flutter SDK
|
||||||
|
|||||||
118
.github/actions/image-build/action.yml
vendored
118
.github/actions/image-build/action.yml
vendored
@@ -1,118 +0,0 @@
|
|||||||
name: 'Single arch image build'
|
|
||||||
description: 'Build single-arch image on platform appropriate runner'
|
|
||||||
inputs:
|
|
||||||
image:
|
|
||||||
description: 'Name of the image to build'
|
|
||||||
required: true
|
|
||||||
ghcr-token:
|
|
||||||
description: 'GitHub Container Registry token'
|
|
||||||
required: true
|
|
||||||
platform:
|
|
||||||
description: 'Platform to build for'
|
|
||||||
required: true
|
|
||||||
artifact-key-base:
|
|
||||||
description: 'Base key for artifact name'
|
|
||||||
required: true
|
|
||||||
context:
|
|
||||||
description: 'Path to build context'
|
|
||||||
required: true
|
|
||||||
dockerfile:
|
|
||||||
description: 'Path to Dockerfile'
|
|
||||||
required: true
|
|
||||||
build-args:
|
|
||||||
description: 'Docker build arguments'
|
|
||||||
required: false
|
|
||||||
runs:
|
|
||||||
using: 'composite'
|
|
||||||
steps:
|
|
||||||
- name: Prepare
|
|
||||||
id: prepare
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
PLATFORM: ${{ inputs.platform }}
|
|
||||||
run: |
|
|
||||||
echo "platform-pair=${PLATFORM//\//-}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ inputs.ghcr-token }}
|
|
||||||
|
|
||||||
- name: Generate cache key suffix
|
|
||||||
id: cache-key-suffix
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
REF: ${{ github.ref_name }}
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
|
||||||
echo "cache-key-suffix=pr-${{ github.event.number }}" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
SUFFIX=$(echo "${REF}" | sed 's/[^a-zA-Z0-9]/-/g')
|
|
||||||
echo "suffix=${SUFFIX}" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate cache target
|
|
||||||
id: cache-target
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
BUILD_ARGS: ${{ inputs.build-args }}
|
|
||||||
IMAGE: ${{ inputs.image }}
|
|
||||||
SUFFIX: ${{ steps.cache-key-suffix.outputs.suffix }}
|
|
||||||
PLATFORM_PAIR: ${{ steps.prepare.outputs.platform-pair }}
|
|
||||||
run: |
|
|
||||||
HASH=$(sha256sum <<< "${BUILD_ARGS}" | cut -d' ' -f1)
|
|
||||||
CACHE_KEY="${PLATFORM_PAIR}-${HASH}"
|
|
||||||
echo "cache-key-base=${CACHE_KEY}" >> $GITHUB_OUTPUT
|
|
||||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
|
||||||
# Essentially just ignore the cache output (forks can't write to registry cache)
|
|
||||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "cache-to=type=registry,ref=${IMAGE}-build-cache:${CACHE_KEY}-${SUFFIX},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
id: build
|
|
||||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
|
||||||
with:
|
|
||||||
context: ${{ inputs.context }}
|
|
||||||
file: ${{ inputs.dockerfile }}
|
|
||||||
platforms: ${{ inputs.platform }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
|
||||||
cache-from: |
|
|
||||||
type=registry,ref=${{ inputs.image }}-build-cache:${{ steps.cache-target.outputs.cache-key-base }}-${{ steps.cache-key-suffix.outputs.suffix }}
|
|
||||||
type=registry,ref=${{ inputs.image }}-build-cache:${{ steps.cache-target.outputs.cache-key-base }}-main
|
|
||||||
outputs: type=image,"name=${{ inputs.image }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
build-args: |
|
|
||||||
BUILD_ID=${{ github.run_id }}
|
|
||||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.meta.outputs.tags }}
|
|
||||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
|
||||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
|
||||||
${{ inputs.build-args }}
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
shell: bash
|
|
||||||
run: | # zizmor: ignore[template-injection]
|
|
||||||
mkdir -p ${{ runner.temp }}/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.artifact-key-base }}-${{ steps.cache-target.outputs.cache-key-base }}
|
|
||||||
path: ${{ runner.temp }}/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
35
.github/workflows/build-mobile.yml
vendored
35
.github/workflows/build-mobile.yml
vendored
@@ -7,15 +7,6 @@ on:
|
|||||||
ref:
|
ref:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
secrets:
|
|
||||||
KEY_JKS:
|
|
||||||
required: true
|
|
||||||
ALIAS:
|
|
||||||
required: true
|
|
||||||
ANDROID_KEY_PASSWORD:
|
|
||||||
required: true
|
|
||||||
ANDROID_STORE_PASSWORD:
|
|
||||||
required: true
|
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
@@ -24,21 +15,14 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-job:
|
pre-job:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
outputs:
|
outputs:
|
||||||
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
with:
|
with:
|
||||||
@@ -54,19 +38,24 @@ jobs:
|
|||||||
build-sign-android:
|
build-sign-android:
|
||||||
name: Build and sign Android
|
name: Build and sign Android
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
# Skip when PR from a fork
|
# 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' }}
|
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: macos-14
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Determine ref
|
||||||
|
id: get-ref
|
||||||
|
run: |
|
||||||
|
input_ref="${{ inputs.ref }}"
|
||||||
|
github_ref="${{ github.sha }}"
|
||||||
|
ref="${input_ref:-$github_ref}"
|
||||||
|
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.ref || github.sha }}
|
ref: ${{ steps.get-ref.outputs.ref }}
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
|
- uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
@@ -89,10 +78,6 @@ jobs:
|
|||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
|
|
||||||
- name: Generate translation file
|
|
||||||
run: make translation
|
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Build Android App Bundle
|
- name: Build Android App Bundle
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
env:
|
env:
|
||||||
|
|||||||
17
.github/workflows/cache-cleanup.yml
vendored
17
.github/workflows/cache-cleanup.yml
vendored
@@ -8,38 +8,31 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cleanup:
|
cleanup:
|
||||||
name: Cleanup
|
name: Cleanup
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
actions: write
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Cleanup
|
- name: Cleanup
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
REF: ${{ github.ref }}
|
|
||||||
run: |
|
run: |
|
||||||
gh extension install actions/gh-actions-cache
|
gh extension install actions/gh-actions-cache
|
||||||
|
|
||||||
REPO=${{ github.repository }}
|
REPO=${{ github.repository }}
|
||||||
|
BRANCH=${{ github.ref }}
|
||||||
|
|
||||||
echo "Fetching list of cache keys"
|
echo "Fetching list of cache keys"
|
||||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B ${REF} -L 100 | cut -f 1 )
|
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
|
||||||
|
|
||||||
## Setting this to not fail the workflow while deleting cache keys.
|
## Setting this to not fail the workflow while deleting cache keys.
|
||||||
set +e
|
set +e
|
||||||
echo "Deleting caches..."
|
echo "Deleting caches..."
|
||||||
for cacheKey in $cacheKeysForPR
|
for cacheKey in $cacheKeysForPR
|
||||||
do
|
do
|
||||||
gh actions-cache delete $cacheKey -R "$REPO" -B "${REF}" --confirm
|
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
|
||||||
done
|
done
|
||||||
echo "Done"
|
echo "Done"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
17
.github/workflows/cli.yml
vendored
17
.github/workflows/cli.yml
vendored
@@ -16,25 +16,21 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions: {}
|
permissions:
|
||||||
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
name: CLI Publish
|
name: CLI Publish
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./cli
|
working-directory: ./cli
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
@@ -52,16 +48,11 @@ jobs:
|
|||||||
docker:
|
docker:
|
||||||
name: Docker
|
name: Docker
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
needs: publish
|
needs: publish
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||||
@@ -96,7 +87,7 @@ jobs:
|
|||||||
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||||
with:
|
with:
|
||||||
file: cli/Dockerfile
|
file: cli/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|||||||
10
.github/workflows/codeql-analysis.yml
vendored
10
.github/workflows/codeql-analysis.yml
vendored
@@ -24,8 +24,6 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
@@ -45,12 +43,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3
|
uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -63,7 +59,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3
|
uses: github/codeql-action/autobuild@45775bd8235c68ba998cffa5171334d58593da47 # v3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ 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
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
@@ -76,6 +72,6 @@ jobs:
|
|||||||
# ./location_of_script_within_repo/buildscript.sh
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3
|
uses: github/codeql-action/analyze@45775bd8235c68ba998cffa5171334d58593da47 # v3
|
||||||
with:
|
with:
|
||||||
category: '/language:${{matrix.language}}'
|
category: '/language:${{matrix.language}}'
|
||||||
|
|||||||
452
.github/workflows/docker.yml
vendored
452
.github/workflows/docker.yml
vendored
@@ -12,21 +12,18 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions: {}
|
permissions:
|
||||||
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-job:
|
pre-job:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
outputs:
|
outputs:
|
||||||
should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
with:
|
with:
|
||||||
@@ -40,8 +37,6 @@ jobs:
|
|||||||
- 'machine-learning/**'
|
- 'machine-learning/**'
|
||||||
workflow:
|
workflow:
|
||||||
- '.github/workflows/docker.yml'
|
- '.github/workflows/docker.yml'
|
||||||
- '.github/workflows/multi-runner-build.yml'
|
|
||||||
- '.github/actions/image-build'
|
|
||||||
|
|
||||||
- name: Check if we should force jobs to run
|
- name: Check if we should force jobs to run
|
||||||
id: should_force
|
id: should_force
|
||||||
@@ -50,9 +45,6 @@ jobs:
|
|||||||
retag_ml:
|
retag_ml:
|
||||||
name: Re-Tag ML
|
name: Re-Tag ML
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'false' && !github.event.pull_request.head.repo.fork }}
|
if: ${{ needs.pre-job.outputs.should_run_ml == 'false' && !github.event.pull_request.head.repo.fork }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
@@ -66,22 +58,18 @@ jobs:
|
|||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Re-tag image
|
- name: Re-tag image
|
||||||
env:
|
|
||||||
REGISTRY_NAME: 'ghcr.io'
|
|
||||||
REPOSITORY: ${{ github.repository_owner }}/immich-machine-learning
|
|
||||||
TAG_OLD: main${{ matrix.suffix }}
|
|
||||||
TAG_PR: ${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
|
||||||
TAG_COMMIT: commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_PR}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
REGISTRY_NAME="ghcr.io"
|
||||||
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_COMMIT}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
REPOSITORY=${{ github.repository_owner }}/immich-machine-learning
|
||||||
|
TAG_OLD=main${{ matrix.suffix }}
|
||||||
|
TAG_PR=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||||
|
TAG_COMMIT=commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
||||||
|
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_PR $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||||
|
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_COMMIT $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||||
|
|
||||||
retag_server:
|
retag_server:
|
||||||
name: Re-Tag Server
|
name: Re-Tag Server
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'false' && !github.event.pull_request.head.repo.fork }}
|
if: ${{ needs.pre-job.outputs.should_run_server == 'false' && !github.event.pull_request.head.repo.fork }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
@@ -95,85 +83,370 @@ jobs:
|
|||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Re-tag image
|
- name: Re-tag image
|
||||||
env:
|
|
||||||
REGISTRY_NAME: 'ghcr.io'
|
|
||||||
REPOSITORY: ${{ github.repository_owner }}/immich-server
|
|
||||||
TAG_OLD: main${{ matrix.suffix }}
|
|
||||||
TAG_PR: ${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
|
||||||
TAG_COMMIT: commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_PR}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
REGISTRY_NAME="ghcr.io"
|
||||||
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_COMMIT}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
REPOSITORY=${{ github.repository_owner }}/immich-server
|
||||||
|
TAG_OLD=main${{ matrix.suffix }}
|
||||||
|
TAG_PR=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||||
|
TAG_COMMIT=commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
||||||
|
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_PR $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||||
|
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_COMMIT $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||||
|
|
||||||
machine-learning:
|
build_and_push_ml:
|
||||||
name: Build and Push ML
|
name: Build and Push ML
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
env:
|
||||||
|
image: immich-machine-learning
|
||||||
|
context: machine-learning
|
||||||
|
file: machine-learning/Dockerfile
|
||||||
|
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning
|
||||||
|
strategy:
|
||||||
|
# Prevent a failure in one image from stopping the other builds
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- platform: linux/amd64
|
||||||
|
runner: ubuntu-latest
|
||||||
|
device: cpu
|
||||||
|
|
||||||
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
device: cpu
|
||||||
|
|
||||||
|
- platform: linux/amd64
|
||||||
|
runner: ubuntu-latest
|
||||||
|
device: cuda
|
||||||
|
suffix: -cuda
|
||||||
|
|
||||||
|
- platform: linux/amd64
|
||||||
|
runner: mich
|
||||||
|
device: rocm
|
||||||
|
suffix: -rocm
|
||||||
|
|
||||||
|
- platform: linux/amd64
|
||||||
|
runner: ubuntu-latest
|
||||||
|
device: openvino
|
||||||
|
suffix: -openvino
|
||||||
|
|
||||||
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
device: armnn
|
||||||
|
suffix: -armnn
|
||||||
|
|
||||||
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
device: rknn
|
||||||
|
suffix: -rknn
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
platform=${{ matrix.platform }}
|
||||||
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate cache key suffix
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||||
|
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "CACHE_KEY_SUFFIX=$(echo ${{ github.ref_name }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate cache target
|
||||||
|
id: cache-target
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||||
|
# Essentially just ignore the cache output (forks can't write to registry cache)
|
||||||
|
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "cache-to=type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
id: build
|
||||||
|
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||||
|
with:
|
||||||
|
context: ${{ env.context }}
|
||||||
|
file: ${{ env.file }}
|
||||||
|
platforms: ${{ matrix.platforms }}
|
||||||
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
|
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||||
|
cache-from: |
|
||||||
|
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }}
|
||||||
|
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-main
|
||||||
|
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
build-args: |
|
||||||
|
DEVICE=${{ matrix.device }}
|
||||||
|
BUILD_ID=${{ github.run_id }}
|
||||||
|
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
||||||
|
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||||
|
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Export digest
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ runner.temp }}/digests
|
||||||
|
digest="${{ steps.build.outputs.digest }}"
|
||||||
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
|
with:
|
||||||
|
name: ml-digests-${{ matrix.device }}-${{ env.PLATFORM_PAIR }}
|
||||||
|
path: ${{ runner.temp }}/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
merge_ml:
|
||||||
|
name: Merge & Push ML
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' && !github.event.pull_request.head.repo.fork }}
|
||||||
|
env:
|
||||||
|
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning
|
||||||
|
DOCKER_REPO: altran1502/immich-machine-learning
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- device: cpu
|
||||||
|
- device: cuda
|
||||||
|
suffix: -cuda
|
||||||
|
- device: rocm
|
||||||
|
suffix: -rocm
|
||||||
|
- device: openvino
|
||||||
|
suffix: -openvino
|
||||||
|
- device: armnn
|
||||||
|
suffix: -armnn
|
||||||
|
- device: rknn
|
||||||
|
suffix: -rknn
|
||||||
|
needs:
|
||||||
|
- build_and_push_ml
|
||||||
|
steps:
|
||||||
|
- name: Download digests
|
||||||
|
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||||
|
with:
|
||||||
|
path: ${{ runner.temp }}/digests
|
||||||
|
pattern: ml-digests-${{ matrix.device }}-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GHCR
|
||||||
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||||
|
|
||||||
|
- name: Generate docker image tags
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||||
|
env:
|
||||||
|
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||||
|
with:
|
||||||
|
flavor: |
|
||||||
|
# Disable latest tag
|
||||||
|
latest=false
|
||||||
|
suffix=${{ matrix.suffix }}
|
||||||
|
images: |
|
||||||
|
name=${{ env.GHCR_REPO }}
|
||||||
|
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
||||||
|
tags: |
|
||||||
|
# Tag with branch name
|
||||||
|
type=ref,event=branch
|
||||||
|
# Tag with pr-number
|
||||||
|
type=ref,event=pr
|
||||||
|
# Tag with long commit sha hash
|
||||||
|
type=sha,format=long,prefix=commit-
|
||||||
|
# Tag with git tag on release
|
||||||
|
type=ref,event=tag
|
||||||
|
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
||||||
|
|
||||||
|
- name: Create manifest list and push
|
||||||
|
working-directory: ${{ runner.temp }}/digests
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||||
|
$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *)
|
||||||
|
|
||||||
|
build_and_push_server:
|
||||||
|
name: Build and Push Server
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||||
|
env:
|
||||||
|
image: immich-server
|
||||||
|
context: .
|
||||||
|
file: server/Dockerfile
|
||||||
|
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- device: cpu
|
- platform: linux/amd64
|
||||||
tag-suffix: ''
|
runner: ubuntu-latest
|
||||||
- device: cuda
|
- platform: linux/arm64
|
||||||
tag-suffix: '-cuda'
|
runner: ubuntu-24.04-arm
|
||||||
platforms: linux/amd64
|
steps:
|
||||||
- device: openvino
|
- name: Prepare
|
||||||
tag-suffix: '-openvino'
|
run: |
|
||||||
platforms: linux/amd64
|
platform=${{ matrix.platform }}
|
||||||
- device: armnn
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
tag-suffix: '-armnn'
|
|
||||||
platforms: linux/arm64
|
|
||||||
- device: rknn
|
|
||||||
tag-suffix: '-rknn'
|
|
||||||
platforms: linux/arm64
|
|
||||||
- device: rocm
|
|
||||||
tag-suffix: '-rocm'
|
|
||||||
platforms: linux/amd64
|
|
||||||
runner-mapping: '{"linux/amd64": "mich"}'
|
|
||||||
uses: ./.github/workflows/multi-runner-build.yml
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
actions: read
|
|
||||||
packages: write
|
|
||||||
secrets:
|
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
image: immich-machine-learning
|
|
||||||
context: machine-learning
|
|
||||||
dockerfile: machine-learning/Dockerfile
|
|
||||||
platforms: ${{ matrix.platforms }}
|
|
||||||
runner-mapping: ${{ matrix.runner-mapping }}
|
|
||||||
tag-suffix: ${{ matrix.tag-suffix }}
|
|
||||||
dockerhub-push: ${{ github.event_name == 'release' }}
|
|
||||||
build-args: |
|
|
||||||
DEVICE=${{ matrix.device }}
|
|
||||||
|
|
||||||
server:
|
- name: Checkout
|
||||||
name: Build and Push Server
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
needs: pre-job
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
- name: Set up Docker Buildx
|
||||||
uses: ./.github/workflows/multi-runner-build.yml
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||||
permissions:
|
|
||||||
contents: read
|
- name: Login to GitHub Container Registry
|
||||||
actions: read
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
packages: write
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
secrets:
|
with:
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
registry: ghcr.io
|
||||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
username: ${{ github.repository_owner }}
|
||||||
with:
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
image: immich-server
|
|
||||||
context: .
|
- name: Generate cache key suffix
|
||||||
dockerfile: server/Dockerfile
|
run: |
|
||||||
dockerhub-push: ${{ github.event_name == 'release' }}
|
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||||
build-args: |
|
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
||||||
DEVICE=cpu
|
else
|
||||||
|
echo "CACHE_KEY_SUFFIX=$(echo ${{ github.ref_name }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate cache target
|
||||||
|
id: cache-target
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||||
|
# Essentially just ignore the cache output (forks can't write to registry cache)
|
||||||
|
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "cache-to=type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
id: build
|
||||||
|
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||||
|
with:
|
||||||
|
context: ${{ env.context }}
|
||||||
|
file: ${{ env.file }}
|
||||||
|
platforms: ${{ matrix.platform }}
|
||||||
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
|
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||||
|
cache-from: |
|
||||||
|
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ env.CACHE_KEY_SUFFIX }}
|
||||||
|
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-main
|
||||||
|
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
build-args: |
|
||||||
|
DEVICE=cpu
|
||||||
|
BUILD_ID=${{ github.run_id }}
|
||||||
|
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
||||||
|
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||||
|
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Export digest
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ runner.temp }}/digests
|
||||||
|
digest="${{ steps.build.outputs.digest }}"
|
||||||
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
|
with:
|
||||||
|
name: server-digests-${{ env.PLATFORM_PAIR }}
|
||||||
|
path: ${{ runner.temp }}/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
merge_server:
|
||||||
|
name: Merge & Push Server
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' && !github.event.pull_request.head.repo.fork }}
|
||||||
|
env:
|
||||||
|
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
||||||
|
DOCKER_REPO: altran1502/immich-server
|
||||||
|
needs:
|
||||||
|
- build_and_push_server
|
||||||
|
steps:
|
||||||
|
- name: Download digests
|
||||||
|
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||||
|
with:
|
||||||
|
path: ${{ runner.temp }}/digests
|
||||||
|
pattern: server-digests-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GHCR
|
||||||
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||||
|
|
||||||
|
- name: Generate docker image tags
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||||
|
env:
|
||||||
|
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||||
|
with:
|
||||||
|
flavor: |
|
||||||
|
# Disable latest tag
|
||||||
|
latest=false
|
||||||
|
suffix=${{ matrix.suffix }}
|
||||||
|
images: |
|
||||||
|
name=${{ env.GHCR_REPO }}
|
||||||
|
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
||||||
|
tags: |
|
||||||
|
# Tag with branch name
|
||||||
|
type=ref,event=branch
|
||||||
|
# Tag with pr-number
|
||||||
|
type=ref,event=pr
|
||||||
|
# Tag with long commit sha hash
|
||||||
|
type=sha,format=long,prefix=commit-
|
||||||
|
# Tag with git tag on release
|
||||||
|
type=ref,event=tag
|
||||||
|
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
||||||
|
|
||||||
|
- name: Create manifest list and push
|
||||||
|
working-directory: ${{ runner.temp }}/digests
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||||
|
$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *)
|
||||||
|
|
||||||
success-check-server:
|
success-check-server:
|
||||||
name: Docker Build & Push Server Success
|
name: Docker Build & Push Server Success
|
||||||
needs: [server, retag_server]
|
needs: [merge_server, retag_server]
|
||||||
permissions: {}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -182,13 +455,11 @@ jobs:
|
|||||||
run: exit 1
|
run: exit 1
|
||||||
- name: All jobs passed or skipped
|
- name: All jobs passed or skipped
|
||||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||||
# zizmor: ignore[template-injection]
|
|
||||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||||
|
|
||||||
success-check-ml:
|
success-check-ml:
|
||||||
name: Docker Build & Push ML Success
|
name: Docker Build & Push ML Success
|
||||||
needs: [machine-learning, retag_ml]
|
needs: [merge_ml, retag_ml]
|
||||||
permissions: {}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -197,5 +468,4 @@ jobs:
|
|||||||
run: exit 1
|
run: exit 1
|
||||||
- name: All jobs passed or skipped
|
- name: All jobs passed or skipped
|
||||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||||
# zizmor: ignore[template-injection]
|
|
||||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||||
|
|||||||
13
.github/workflows/docs-build.yml
vendored
13
.github/workflows/docs-build.yml
vendored
@@ -10,20 +10,14 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-job:
|
pre-job:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
outputs:
|
outputs:
|
||||||
should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
with:
|
with:
|
||||||
@@ -39,8 +33,6 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
name: Docs Build
|
name: Docs Build
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
@@ -50,11 +42,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './docs/.nvmrc'
|
node-version-file: './docs/.nvmrc'
|
||||||
|
|
||||||
@@ -72,5 +62,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: docs-build-output
|
name: docs-build-output
|
||||||
path: docs/build/
|
path: docs/build/
|
||||||
include-hidden-files: true
|
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|||||||
34
.github/workflows/docs-deploy.yml
vendored
34
.github/workflows/docs-deploy.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
name: Docs deploy
|
name: Docs deploy
|
||||||
on:
|
on:
|
||||||
workflow_run: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
workflow_run:
|
||||||
workflows: ['Docs build']
|
workflows: ['Docs build']
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
@@ -9,9 +9,6 @@ jobs:
|
|||||||
checks:
|
checks:
|
||||||
name: Docs Deploy Checks
|
name: Docs Deploy Checks
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
pull-requests: read
|
|
||||||
outputs:
|
outputs:
|
||||||
parameters: ${{ steps.parameters.outputs.result }}
|
parameters: ${{ steps.parameters.outputs.result }}
|
||||||
artifact: ${{ steps.get-artifact.outputs.result }}
|
artifact: ${{ steps.get-artifact.outputs.result }}
|
||||||
@@ -39,8 +36,6 @@ jobs:
|
|||||||
- name: Determine deploy parameters
|
- name: Determine deploy parameters
|
||||||
id: parameters
|
id: parameters
|
||||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||||
env:
|
|
||||||
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const eventType = context.payload.workflow_run.event;
|
const eventType = context.payload.workflow_run.event;
|
||||||
@@ -62,8 +57,7 @@ jobs:
|
|||||||
} else if (eventType == "pull_request") {
|
} else if (eventType == "pull_request") {
|
||||||
let pull_number = context.payload.workflow_run.pull_requests[0]?.number;
|
let pull_number = context.payload.workflow_run.pull_requests[0]?.number;
|
||||||
if(!pull_number) {
|
if(!pull_number) {
|
||||||
const {HEAD_SHA} = process.env;
|
const response = await github.rest.search.issuesAndPullRequests({q: 'repo:${{ github.repository }} is:pr sha:${{ github.event.workflow_run.head_sha }}',per_page: 1,})
|
||||||
const response = await github.rest.search.issuesAndPullRequests({q: `repo:${{ github.repository }} is:pr sha:${HEAD_SHA}`,per_page: 1,})
|
|
||||||
const items = response.data.items
|
const items = response.data.items
|
||||||
if (items.length < 1) {
|
if (items.length < 1) {
|
||||||
throw new Error("No pull request found for the commit")
|
throw new Error("No pull request found for the commit")
|
||||||
@@ -101,36 +95,30 @@ jobs:
|
|||||||
name: Docs Deploy
|
name: Docs Deploy
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: checks
|
needs: checks
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
actions: read
|
|
||||||
pull-requests: write
|
|
||||||
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Load parameters
|
- name: Load parameters
|
||||||
id: parameters
|
id: parameters
|
||||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||||
env:
|
|
||||||
PARAM_JSON: ${{ needs.checks.outputs.parameters }}
|
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const parameters = JSON.parse(process.env.PARAM_JSON);
|
const json = `${{ needs.checks.outputs.parameters }}`;
|
||||||
|
const parameters = JSON.parse(json);
|
||||||
core.setOutput("event", parameters.event);
|
core.setOutput("event", parameters.event);
|
||||||
core.setOutput("name", parameters.name);
|
core.setOutput("name", parameters.name);
|
||||||
core.setOutput("shouldDeploy", parameters.shouldDeploy);
|
core.setOutput("shouldDeploy", parameters.shouldDeploy);
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
echo "Starting docs deployment for ${{ steps.parameters.outputs.event }} ${{ steps.parameters.outputs.name }}"
|
||||||
|
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||||
env:
|
|
||||||
ARTIFACT_JSON: ${{ needs.checks.outputs.artifact }}
|
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
let artifact = JSON.parse(process.env.ARTIFACT_JSON);
|
let artifact = ${{ needs.checks.outputs.artifact }};
|
||||||
let download = await github.rest.actions.downloadArtifact({
|
let download = await github.rest.actions.downloadArtifact({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
@@ -174,11 +162,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Output Cleaning
|
- name: Output Cleaning
|
||||||
id: clean
|
id: clean
|
||||||
env:
|
|
||||||
TG_OUTPUT: ${{ steps.docs-output.outputs.tg_action_output }}
|
|
||||||
run: |
|
run: |
|
||||||
CLEANED=$(echo "$TG_OUTPUT" | sed 's|%0A|\n|g ; s|%3C|<|g' | jq -c .)
|
TG_OUT=$(echo '${{ steps.docs-output.outputs.tg_action_output }}' | sed 's|%0A|\n|g ; s|%3C|<|g' | jq -c .)
|
||||||
echo "output=$CLEANED" >> $GITHUB_OUTPUT
|
echo "output=$TG_OUT" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Publish to Cloudflare Pages
|
- name: Publish to Cloudflare Pages
|
||||||
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1
|
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1
|
||||||
|
|||||||
9
.github/workflows/docs-destroy.yml
vendored
9
.github/workflows/docs-destroy.yml
vendored
@@ -1,22 +1,15 @@
|
|||||||
name: Docs destroy
|
name: Docs destroy
|
||||||
on:
|
on:
|
||||||
pull_request_target: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
pull_request_target:
|
||||||
types: [closed]
|
types: [closed]
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
name: Docs Destroy
|
name: Docs Destroy
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Destroy Docs Subdomain
|
- name: Destroy Docs Subdomain
|
||||||
env:
|
env:
|
||||||
|
|||||||
8
.github/workflows/fix-format.yml
vendored
8
.github/workflows/fix-format.yml
vendored
@@ -4,19 +4,16 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
types: [labeled]
|
types: [labeled]
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
fix-formatting:
|
fix-formatting:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event.label.name == 'fix:formatting' }}
|
if: ${{ github.event.label.name == 'fix:formatting' }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2
|
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -26,10 +23,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
ref: ${{ github.event.pull_request.head.ref }}
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
persist-credentials: true
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
|
|||||||
185
.github/workflows/multi-runner-build.yml
vendored
185
.github/workflows/multi-runner-build.yml
vendored
@@ -1,185 +0,0 @@
|
|||||||
name: 'Multi-runner container image build'
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
image:
|
|
||||||
description: 'Name of the image'
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
context:
|
|
||||||
description: 'Path to build context'
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
dockerfile:
|
|
||||||
description: 'Path to Dockerfile'
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
tag-suffix:
|
|
||||||
description: 'Suffix to append to the image tag'
|
|
||||||
type: string
|
|
||||||
default: ''
|
|
||||||
dockerhub-push:
|
|
||||||
description: 'Push to Docker Hub'
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
build-args:
|
|
||||||
description: 'Docker build arguments'
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
platforms:
|
|
||||||
description: 'Platforms to build for'
|
|
||||||
type: string
|
|
||||||
runner-mapping:
|
|
||||||
description: 'Mapping from platforms to runners'
|
|
||||||
type: string
|
|
||||||
secrets:
|
|
||||||
DOCKERHUB_USERNAME:
|
|
||||||
required: false
|
|
||||||
DOCKERHUB_TOKEN:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
env:
|
|
||||||
GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ inputs.image }}
|
|
||||||
DOCKERHUB_IMAGE: altran1502/${{ inputs.image }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
matrix:
|
|
||||||
name: 'Generate matrix'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
matrix: ${{ steps.matrix.outputs.matrix }}
|
|
||||||
key: ${{ steps.artifact-key.outputs.base }}
|
|
||||||
steps:
|
|
||||||
- name: Generate build matrix
|
|
||||||
id: matrix
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
PLATFORMS: ${{ inputs.platforms || 'linux/amd64,linux/arm64' }}
|
|
||||||
RUNNER_MAPPING: ${{ inputs.runner-mapping || '{"linux/amd64":"ubuntu-latest","linux/arm64":"ubuntu-24.04-arm"}' }}
|
|
||||||
run: |
|
|
||||||
matrix=$(jq -R -c \
|
|
||||||
--argjson runner_mapping "${RUNNER_MAPPING}" \
|
|
||||||
'split(",") | map({platform: ., runner: $runner_mapping[.]})' \
|
|
||||||
<<< "${PLATFORMS}")
|
|
||||||
echo "${matrix}"
|
|
||||||
echo "matrix=${matrix}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Determine artifact key
|
|
||||||
id: artifact-key
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
IMAGE: ${{ inputs.image }}
|
|
||||||
SUFFIX: ${{ inputs.tag-suffix }}
|
|
||||||
run: |
|
|
||||||
if [[ -n "${SUFFIX}" ]]; then
|
|
||||||
base="${IMAGE}${SUFFIX}-digests"
|
|
||||||
else
|
|
||||||
base="${IMAGE}-digests"
|
|
||||||
fi
|
|
||||||
echo "${base}"
|
|
||||||
echo "base=${base}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
build:
|
|
||||||
needs: matrix
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include: ${{ fromJson(needs.matrix.outputs.matrix) }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- uses: ./.github/actions/image-build
|
|
||||||
with:
|
|
||||||
context: ${{ inputs.context }}
|
|
||||||
dockerfile: ${{ inputs.dockerfile }}
|
|
||||||
image: ${{ env.GHCR_IMAGE }}
|
|
||||||
ghcr-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
platform: ${{ matrix.platform }}
|
|
||||||
artifact-key-base: ${{ needs.matrix.outputs.key }}
|
|
||||||
build-args: ${{ inputs.build-args }}
|
|
||||||
|
|
||||||
merge:
|
|
||||||
needs: [matrix, build]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
actions: read
|
|
||||||
packages: write
|
|
||||||
steps:
|
|
||||||
- name: Download digests
|
|
||||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
|
||||||
with:
|
|
||||||
path: ${{ runner.temp }}/digests
|
|
||||||
pattern: ${{ needs.matrix.outputs.key }}-*
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
if: ${{ inputs.dockerhub-push }}
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Login to GHCR
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
with:
|
|
||||||
flavor: |
|
|
||||||
# Disable latest tag
|
|
||||||
latest=false
|
|
||||||
suffix=${{ inputs.tag-suffix }}
|
|
||||||
images: |
|
|
||||||
name=${{ env.GHCR_IMAGE }}
|
|
||||||
name=${{ env.DOCKERHUB_IMAGE }},enable=${{ inputs.dockerhub-push }}
|
|
||||||
tags: |
|
|
||||||
# Tag with branch name
|
|
||||||
type=ref,event=branch
|
|
||||||
# Tag with pr-number
|
|
||||||
type=ref,event=pr
|
|
||||||
# Tag with long commit sha hash
|
|
||||||
type=sha,format=long,prefix=commit-
|
|
||||||
# Tag with git tag on release
|
|
||||||
type=ref,event=tag
|
|
||||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: ${{ runner.temp }}/digests
|
|
||||||
run: |
|
|
||||||
# Process annotations
|
|
||||||
declare -a ANNOTATIONS=()
|
|
||||||
if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then
|
|
||||||
while IFS= read -r annotation; do
|
|
||||||
# Extract key and value by removing the manifest: prefix
|
|
||||||
if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then
|
|
||||||
key="${BASH_REMATCH[1]}"
|
|
||||||
value="${BASH_REMATCH[2]}"
|
|
||||||
# Use array to properly handle arguments with spaces
|
|
||||||
ANNOTATIONS+=(--annotation "index:$key=$value")
|
|
||||||
fi
|
|
||||||
done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
|
||||||
fi
|
|
||||||
|
|
||||||
TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
SOURCE_ARGS=$(printf "${GHCR_IMAGE}@sha256:%s " *)
|
|
||||||
|
|
||||||
docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS
|
|
||||||
4
.github/workflows/pr-label-validation.yml
vendored
4
.github/workflows/pr-label-validation.yml
vendored
@@ -1,11 +1,9 @@
|
|||||||
name: PR Label Validation
|
name: PR Label Validation
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request_target: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
pull_request_target:
|
||||||
types: [opened, labeled, unlabeled, synchronize]
|
types: [opened, labeled, unlabeled, synchronize]
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate-release-label:
|
validate-release-label:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
4
.github/workflows/pr-labeler.yml
vendored
4
.github/workflows/pr-labeler.yml
vendored
@@ -1,8 +1,6 @@
|
|||||||
name: 'Pull Request Labeler'
|
name: 'Pull Request Labeler'
|
||||||
on:
|
on:
|
||||||
- pull_request_target # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
- pull_request_target
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
|
|||||||
@@ -4,13 +4,9 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened, edited]
|
types: [opened, synchronize, reopened, edited]
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate-pr-title:
|
validate-pr-title:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
steps:
|
||||||
- name: PR Conventional Commit Validation
|
- name: PR Conventional Commit Validation
|
||||||
uses: ytanikin/PRConventionalCommits@b628c5a234cc32513014b7bfdd1e47b532124d98 # 1.3.0
|
uses: ytanikin/PRConventionalCommits@b628c5a234cc32513014b7bfdd1e47b532124d98 # 1.3.0
|
||||||
|
|||||||
35
.github/workflows/prepare-release.yml
vendored
35
.github/workflows/prepare-release.yml
vendored
@@ -21,18 +21,17 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}-root
|
group: ${{ github.workflow }}-${{ github.ref }}-root
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
bump_version:
|
bump_version:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
ref: ${{ steps.push-tag.outputs.commit_long_sha }}
|
ref: ${{ steps.push-tag.outputs.commit_long_sha }}
|
||||||
permissions: {} # No job-level permissions are needed because it uses the app-token
|
|
||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2
|
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -41,16 +40,12 @@ jobs:
|
|||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
persist-credentials: true
|
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
|
||||||
|
|
||||||
- name: Bump version
|
- name: Bump version
|
||||||
env:
|
run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}"
|
||||||
SERVER_BUMP: ${{ inputs.serverBump }}
|
|
||||||
MOBILE_BUMP: ${{ inputs.mobileBump }}
|
|
||||||
run: misc/release/pump-version.sh -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}"
|
|
||||||
|
|
||||||
- name: Commit and tag
|
- name: Commit and tag
|
||||||
id: push-tag
|
id: push-tag
|
||||||
@@ -64,26 +59,18 @@ jobs:
|
|||||||
build_mobile:
|
build_mobile:
|
||||||
uses: ./.github/workflows/build-mobile.yml
|
uses: ./.github/workflows/build-mobile.yml
|
||||||
needs: bump_version
|
needs: bump_version
|
||||||
permissions:
|
secrets: inherit
|
||||||
contents: read
|
|
||||||
secrets:
|
|
||||||
KEY_JKS: ${{ secrets.KEY_JKS }}
|
|
||||||
ALIAS: ${{ secrets.ALIAS }}
|
|
||||||
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
|
||||||
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
|
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.bump_version.outputs.ref }}
|
ref: ${{ needs.bump_version.outputs.ref }}
|
||||||
|
|
||||||
prepare_release:
|
prepare_release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build_mobile
|
needs: build_mobile
|
||||||
permissions:
|
|
||||||
actions: read # To download the app artifact
|
|
||||||
# No content permissions are needed because it uses the app-token
|
|
||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2
|
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -92,19 +79,17 @@ jobs:
|
|||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Download APK
|
- name: Download APK
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||||
with:
|
with:
|
||||||
name: release-apk-signed
|
name: release-apk-signed
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Create draft release
|
||||||
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2
|
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
tag_name: ${{ env.IMMICH_VERSION }}
|
tag_name: ${{ env.IMMICH_VERSION }}
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
body_path: misc/release/notes.tmpl
|
body_path: misc/release/notes.tmpl
|
||||||
files: |
|
files: |
|
||||||
|
|||||||
2
.github/workflows/preview-label.yaml
vendored
2
.github/workflows/preview-label.yaml
vendored
@@ -4,8 +4,6 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
types: [labeled, closed]
|
types: [labeled, closed]
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
comment-status:
|
comment-status:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
10
.github/workflows/sdk.yml
vendored
10
.github/workflows/sdk.yml
vendored
@@ -4,24 +4,20 @@ on:
|
|||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
permissions: {}
|
permissions:
|
||||||
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
name: Publish `@immich/sdk`
|
name: Publish `@immich/sdk`
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./open-api/typescript-sdk
|
working-directory: ./open-api/typescript-sdk
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|||||||
47
.github/workflows/static_analysis.yml
vendored
47
.github/workflows/static_analysis.yml
vendored
@@ -9,20 +9,14 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-job:
|
pre-job:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
outputs:
|
outputs:
|
||||||
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
with:
|
with:
|
||||||
@@ -39,14 +33,12 @@ jobs:
|
|||||||
name: Run Dart Code Analysis
|
name: Run Dart Code Analysis
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
||||||
@@ -58,10 +50,6 @@ jobs:
|
|||||||
run: dart pub get
|
run: dart pub get
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
- name: Generate translation file
|
|
||||||
run: make translation; dart format lib/generated/codegen_loader.g.dart
|
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Run Build Runner
|
- name: Run Build Runner
|
||||||
run: make build
|
run: make build
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
@@ -77,11 +65,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Verify files have not changed
|
- name: Verify files have not changed
|
||||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||||
env:
|
|
||||||
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
|
|
||||||
run: |
|
run: |
|
||||||
echo "ERROR: Generated files not up to date! Run make_build inside the mobile directory"
|
echo "ERROR: Generated files not up to date! Run make_build inside the mobile directory"
|
||||||
echo "Changed files: ${CHANGED_FILES}"
|
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Run dart analyze
|
- name: Run dart analyze
|
||||||
@@ -95,30 +81,3 @@ jobs:
|
|||||||
- name: Run dart custom_lint
|
- name: Run dart custom_lint
|
||||||
run: dart run custom_lint
|
run: dart run custom_lint
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
zizmor:
|
|
||||||
name: zizmor
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
security-events: write
|
|
||||||
contents: read
|
|
||||||
actions: read
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Install the latest version of uv
|
|
||||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
|
||||||
|
|
||||||
- name: Run zizmor 🌈
|
|
||||||
run: uvx zizmor --format=sarif . > results.sarif
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Upload SARIF file
|
|
||||||
uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3
|
|
||||||
with:
|
|
||||||
sarif_file: results.sarif
|
|
||||||
category: zizmor
|
|
||||||
|
|||||||
176
.github/workflows/test.yml
vendored
176
.github/workflows/test.yml
vendored
@@ -9,13 +9,9 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-job:
|
pre-job:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
outputs:
|
outputs:
|
||||||
should_run_web: ${{ steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_web: ${{ steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
@@ -29,9 +25,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
with:
|
with:
|
||||||
@@ -65,8 +58,6 @@ jobs:
|
|||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./server
|
working-directory: ./server
|
||||||
@@ -74,11 +65,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -106,8 +95,6 @@ jobs:
|
|||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./cli
|
working-directory: ./cli
|
||||||
@@ -115,11 +102,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
|
||||||
@@ -151,8 +136,6 @@ jobs:
|
|||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }}
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./cli
|
working-directory: ./cli
|
||||||
@@ -160,11 +143,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
|
||||||
@@ -184,13 +165,11 @@ jobs:
|
|||||||
run: npm run test:cov
|
run: npm run test:cov
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
web-lint:
|
web-unit-tests:
|
||||||
name: Lint Web
|
name: Test & Lint Web
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_web == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_web == 'true' }}
|
||||||
runs-on: mich
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./web
|
working-directory: ./web
|
||||||
@@ -198,11 +177,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
|
|
||||||
@@ -214,7 +191,7 @@ jobs:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: npm run lint:p
|
run: npm run lint
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
@@ -225,35 +202,6 @@ jobs:
|
|||||||
run: npm run check:svelte
|
run: npm run check:svelte
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
web-unit-tests:
|
|
||||||
name: Test Web
|
|
||||||
needs: pre-job
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_web == 'true' }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./web
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
||||||
with:
|
|
||||||
node-version-file: './web/.nvmrc'
|
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
|
||||||
run: npm ci && npm run build
|
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
|
|
||||||
- name: Run npm install
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: npm run check:typescript
|
run: npm run check:typescript
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
@@ -267,8 +215,6 @@ jobs:
|
|||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_e2e == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_e2e == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./e2e
|
working-directory: ./e2e
|
||||||
@@ -276,11 +222,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -310,8 +254,6 @@ jobs:
|
|||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./server
|
working-directory: ./server
|
||||||
@@ -319,11 +261,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -338,25 +278,19 @@ jobs:
|
|||||||
name: End-to-End Tests (Server & CLI)
|
name: End-to-End Tests (Server & CLI)
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_e2e_server_cli == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_e2e_server_cli == 'true' }}
|
||||||
runs-on: ${{ matrix.runner }}
|
runs-on: mich
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./e2e
|
working-directory: ./e2e
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -386,25 +320,19 @@ jobs:
|
|||||||
name: End-to-End Tests (Web)
|
name: End-to-End Tests (Web)
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_e2e_web == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_e2e_web == 'true' }}
|
||||||
runs-on: ${{ matrix.runner }}
|
runs-on: mich
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./e2e
|
working-directory: ./e2e
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -429,33 +357,13 @@ jobs:
|
|||||||
run: npx playwright test
|
run: npx playwright test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
success-check-e2e:
|
|
||||||
name: End-to-End Tests Success
|
|
||||||
needs: [e2e-tests-server-cli, e2e-tests-web]
|
|
||||||
permissions: {}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: always()
|
|
||||||
steps:
|
|
||||||
- name: Any jobs failed?
|
|
||||||
if: ${{ contains(needs.*.result, 'failure') }}
|
|
||||||
run: exit 1
|
|
||||||
- name: All jobs passed or skipped
|
|
||||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
|
||||||
# zizmor: ignore[template-injection]
|
|
||||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
|
||||||
|
|
||||||
mobile-unit-tests:
|
mobile-unit-tests:
|
||||||
name: Unit Test Mobile
|
name: Unit Test Mobile
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_mobile == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_mobile == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
||||||
with:
|
with:
|
||||||
@@ -470,19 +378,14 @@ jobs:
|
|||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./machine-learning
|
working-directory: ./machine-learning
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
|
||||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
|
||||||
# TODO: add caching when supported (https://github.com/actions/setup-python/pull/818)
|
# TODO: add caching when supported (https://github.com/actions/setup-python/pull/818)
|
||||||
# with:
|
# with:
|
||||||
# python-version: 3.11
|
# python-version: 3.11
|
||||||
@@ -508,8 +411,6 @@ jobs:
|
|||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs['should_run_.github'] == 'true' }}
|
if: ${{ needs.pre-job.outputs['should_run_.github'] == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./.github
|
working-directory: ./.github
|
||||||
@@ -517,11 +418,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './.github/.nvmrc'
|
node-version-file: './.github/.nvmrc'
|
||||||
|
|
||||||
@@ -535,34 +434,25 @@ jobs:
|
|||||||
shellcheck:
|
shellcheck:
|
||||||
name: ShellCheck
|
name: ShellCheck
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Run ShellCheck
|
- name: Run ShellCheck
|
||||||
uses: ludeeus/action-shellcheck@master
|
uses: ludeeus/action-shellcheck@master
|
||||||
with:
|
with:
|
||||||
ignore_paths: >-
|
ignore_paths: >-
|
||||||
**/open-api/**
|
**/open-api/**
|
||||||
**/openapi**
|
**/openapi/**
|
||||||
**/node_modules/**
|
**/node_modules/**
|
||||||
|
|
||||||
generated-api-up-to-date:
|
generated-api-up-to-date:
|
||||||
name: OpenAPI Clients
|
name: OpenAPI Clients
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -586,21 +476,17 @@ jobs:
|
|||||||
|
|
||||||
- name: Verify files have not changed
|
- name: Verify files have not changed
|
||||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||||
env:
|
|
||||||
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
|
|
||||||
run: |
|
run: |
|
||||||
echo "ERROR: Generated files not up to date!"
|
echo "ERROR: Generated files not up to date!"
|
||||||
echo "Changed files: ${CHANGED_FILES}"
|
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
sql-schema-up-to-date:
|
generated-typeorm-migrations-up-to-date:
|
||||||
name: SQL Schema Checks
|
name: TypeORM Checks
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: tensorchord/vchord-postgres:pg14-v0.3.0
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
env:
|
env:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
@@ -619,11 +505,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -637,11 +521,11 @@ jobs:
|
|||||||
run: npm run migrations:run
|
run: npm run migrations:run
|
||||||
|
|
||||||
- name: Test npm run schema:reset command works
|
- name: Test npm run schema:reset command works
|
||||||
run: npm run schema:reset
|
run: npm run typeorm:schema:reset
|
||||||
|
|
||||||
- name: Generate new migrations
|
- name: Generate new migrations
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: npm run migrations:generate src/TestMigration
|
run: npm run migrations:generate TestMigration
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
||||||
@@ -651,11 +535,9 @@ jobs:
|
|||||||
server/src
|
server/src
|
||||||
- name: Verify migration files have not changed
|
- name: Verify migration files have not changed
|
||||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||||
env:
|
|
||||||
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
|
|
||||||
run: |
|
run: |
|
||||||
echo "ERROR: Generated migration files not up to date!"
|
echo "ERROR: Generated migration files not up to date!"
|
||||||
echo "Changed files: ${CHANGED_FILES}"
|
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||||
cat ./src/*-TestMigration.ts
|
cat ./src/*-TestMigration.ts
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
@@ -673,11 +555,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Verify SQL files have not changed
|
- name: Verify SQL files have not changed
|
||||||
if: steps.verify-changed-sql-files.outputs.files_changed == 'true'
|
if: steps.verify-changed-sql-files.outputs.files_changed == 'true'
|
||||||
env:
|
|
||||||
CHANGED_FILES: ${{ steps.verify-changed-sql-files.outputs.changed_files }}
|
|
||||||
run: |
|
run: |
|
||||||
echo "ERROR: Generated SQL files not up to date!"
|
echo "ERROR: Generated SQL files not up to date!"
|
||||||
echo "Changed files: ${CHANGED_FILES}"
|
echo "Changed files: ${{ steps.verify-changed-sql-files.outputs.changed_files }}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
# mobile-integration-tests:
|
# mobile-integration-tests:
|
||||||
|
|||||||
14
.github/workflows/weblate-lock.yml
vendored
14
.github/workflows/weblate-lock.yml
vendored
@@ -4,32 +4,30 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-job:
|
pre-job:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
outputs:
|
outputs:
|
||||||
should_run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}
|
should_run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
i18n:
|
i18n:
|
||||||
- 'i18n/!(en)**\.json'
|
- 'i18n/!(en)**\.json'
|
||||||
|
- name: Debug
|
||||||
|
run: |
|
||||||
|
echo "Should run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}"
|
||||||
|
echo "Found i18n paths: ${{ steps.found_paths.outputs.i18n }}"
|
||||||
|
echo "Head ref: ${{ github.head_ref }}"
|
||||||
|
|
||||||
enforce-lock:
|
enforce-lock:
|
||||||
name: Check Weblate Lock
|
name: Check Weblate Lock
|
||||||
needs: [pre-job]
|
needs: [pre-job]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions: {}
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check weblate lock
|
- name: Check weblate lock
|
||||||
@@ -49,7 +47,6 @@ jobs:
|
|||||||
name: Weblate Lock Check Success
|
name: Weblate Lock Check Success
|
||||||
needs: [enforce-lock]
|
needs: [enforce-lock]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions: {}
|
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- name: Any jobs failed?
|
- name: Any jobs failed?
|
||||||
@@ -57,5 +54,4 @@ jobs:
|
|||||||
run: exit 1
|
run: exit 1
|
||||||
- name: All jobs passed or skipped
|
- name: All jobs passed or skipped
|
||||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||||
# zizmor: ignore[template-injection]
|
|
||||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||||
|
|||||||
80
.vscode/settings.json
vendored
80
.vscode/settings.json
vendored
@@ -1,63 +1,45 @@
|
|||||||
{
|
{
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
},
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
},
|
||||||
"[css]": {
|
"[css]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.tabSize": 2,
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
},
|
||||||
|
"[svelte]": {
|
||||||
|
"editor.defaultFormatter": "svelte.svelte-vscode",
|
||||||
"editor.tabSize": 2
|
"editor.tabSize": 2
|
||||||
},
|
},
|
||||||
|
"svelte.enable-ts-plugin": true,
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"svelte"
|
||||||
|
],
|
||||||
|
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||||
"[dart]": {
|
"[dart]": {
|
||||||
"editor.defaultFormatter": "Dart-Code.dart-code",
|
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.selectionHighlight": false,
|
"editor.selectionHighlight": false,
|
||||||
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
||||||
"editor.suggestSelection": "first",
|
"editor.suggestSelection": "first",
|
||||||
"editor.tabCompletion": "onlySnippets",
|
"editor.tabCompletion": "onlySnippets",
|
||||||
"editor.wordBasedSuggestions": "off"
|
"editor.wordBasedSuggestions": "off",
|
||||||
|
"editor.defaultFormatter": "Dart-Code.dart-code"
|
||||||
},
|
},
|
||||||
"[javascript]": {
|
"cSpell.words": [
|
||||||
"editor.codeActionsOnSave": {
|
"immich"
|
||||||
"source.organizeImports": "explicit",
|
],
|
||||||
"source.removeUnusedImports": "explicit"
|
|
||||||
},
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.tabSize": 2
|
|
||||||
},
|
|
||||||
"[json]": {
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.tabSize": 2
|
|
||||||
},
|
|
||||||
"[jsonc]": {
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.tabSize": 2
|
|
||||||
},
|
|
||||||
"[svelte]": {
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.organizeImports": "explicit",
|
|
||||||
"source.removeUnusedImports": "explicit"
|
|
||||||
},
|
|
||||||
"editor.defaultFormatter": "svelte.svelte-vscode",
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.tabSize": 2
|
|
||||||
},
|
|
||||||
"[typescript]": {
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.organizeImports": "explicit",
|
|
||||||
"source.removeUnusedImports": "explicit"
|
|
||||||
},
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.tabSize": 2
|
|
||||||
},
|
|
||||||
"cSpell.words": ["immich"],
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"eslint.validate": ["javascript", "svelte"],
|
|
||||||
"explorer.fileNesting.enabled": true,
|
"explorer.fileNesting.enabled": true,
|
||||||
"explorer.fileNesting.patterns": {
|
"explorer.fileNesting.patterns": {
|
||||||
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
|
"*.ts": "${capture}.spec.ts,${capture}.mock.ts",
|
||||||
"*.ts": "${capture}.spec.ts,${capture}.mock.ts"
|
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart"
|
||||||
},
|
}
|
||||||
"svelte.enable-ts-plugin": true,
|
}
|
||||||
"typescript.preferences.importModuleSpecifier": "non-relative"
|
|
||||||
}
|
|
||||||
3
Makefile
3
Makefile
@@ -17,9 +17,6 @@ e2e:
|
|||||||
prod:
|
prod:
|
||||||
docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans
|
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:
|
prod-scale:
|
||||||
docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans
|
docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:22.15.0-alpine3.20@sha256:686b8892b69879ef5bfd6047589666933508f9a5451c67320df3070ba0e9807b AS core
|
FROM node:22.14.0-alpine3.20@sha256:40be979442621049f40b1d51a26b55e281246b5de4e5f51a18da7beb6e17e3f9 AS core
|
||||||
|
|
||||||
WORKDIR /usr/src/open-api/typescript-sdk
|
WORKDIR /usr/src/open-api/typescript-sdk
|
||||||
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
||||||
|
|||||||
2325
cli/package-lock.json
generated
2325
cli/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.65",
|
"version": "2.2.61",
|
||||||
"description": "Command Line Interface (CLI) for Immich",
|
"description": "Command Line Interface (CLI) for Immich",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": "./dist/index.js",
|
"exports": "./dist/index.js",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/micromatch": "^4.0.9",
|
"@types/micromatch": "^4.0.9",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.14.0",
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
"byte-size": "^9.0.0",
|
"byte-size": "^9.0.0",
|
||||||
"cli-progress": "^3.12.0",
|
"cli-progress": "^3.12.0",
|
||||||
|
|||||||
@@ -116,13 +116,13 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:4a9f847af90037d59b34cd4d4ad14c6e055f46540cf4ff757aaafb266060fa28
|
image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
@@ -134,6 +134,24 @@ services:
|
|||||||
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
healthcheck:
|
||||||
|
test: >-
|
||||||
|
pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1;
|
||||||
|
Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align
|
||||||
|
--command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')";
|
||||||
|
echo "checksum failure count is $$Chksum";
|
||||||
|
[ "$$Chksum" = '0' ] || exit 1
|
||||||
|
interval: 5m
|
||||||
|
start_interval: 30s
|
||||||
|
start_period: 5m
|
||||||
|
command: >-
|
||||||
|
postgres
|
||||||
|
-c shared_preload_libraries=vectors.so
|
||||||
|
-c 'search_path="$$user", public, vectors'
|
||||||
|
-c logging_collector=on
|
||||||
|
-c max_wal_size=2GB
|
||||||
|
-c shared_buffers=512MB
|
||||||
|
-c wal_compression=on
|
||||||
|
|
||||||
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
||||||
# immich-prometheus:
|
# immich-prometheus:
|
||||||
|
|||||||
@@ -56,14 +56,14 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:4a9f847af90037d59b34cd4d4ad14c6e055f46540cf4ff757aaafb266060fa28
|
image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
@@ -75,6 +75,14 @@ services:
|
|||||||
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
healthcheck:
|
||||||
|
test: >-
|
||||||
|
pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
|
||||||
|
interval: 5m
|
||||||
|
start_interval: 30s
|
||||||
|
start_period: 5m
|
||||||
|
command: >-
|
||||||
|
postgres -c shared_preload_libraries=vectors.so -c 'search_path="$$user", public, vectors' -c logging_collector=on -c max_wal_size=2GB -c shared_buffers=512MB -c wal_compression=on
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
||||||
@@ -82,7 +90,7 @@ services:
|
|||||||
container_name: immich_prometheus
|
container_name: immich_prometheus
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- 9090:9090
|
||||||
image: prom/prometheus@sha256:e2b8aa62b64855956e3ec1e18b4f9387fb6203174a4471936f4662f437f04405
|
image: prom/prometheus@sha256:502ad90314c7485892ce696cb14a99fceab9fc27af29f4b427f41bd39701a199
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
@@ -94,7 +102,7 @@ services:
|
|||||||
command: [ './run.sh', '-disable-reporting' ]
|
command: [ './run.sh', '-disable-reporting' ]
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
image: grafana/grafana:11.6.1-ubuntu@sha256:6fc273288470ef499dd3c6b36aeade093170d4f608f864c5dd3a7fabeae77b50
|
image: grafana/grafana:11.6.0-ubuntu@sha256:fd8fa48213c624e1a95122f1d93abbf1cf1cbe85fc73212c1e599dbd76c63ff8
|
||||||
volumes:
|
volumes:
|
||||||
- grafana-data:/var/lib/grafana
|
- grafana-data:/var/lib/grafana
|
||||||
|
|
||||||
|
|||||||
@@ -49,14 +49,14 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:4a9f847af90037d59b34cd4d4ad14c6e055f46540cf4ff757aaafb266060fa28
|
image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0
|
image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
POSTGRES_USER: ${DB_USERNAME}
|
POSTGRES_USER: ${DB_USERNAME}
|
||||||
@@ -65,8 +65,14 @@ services:
|
|||||||
volumes:
|
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
|
# 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
|
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
||||||
# change ssd below to hdd if you are using a hard disk drive or other slow storage
|
healthcheck:
|
||||||
command: postgres -c config_file=/etc/postgresql/postgresql.ssd.conf
|
test: >-
|
||||||
|
pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
|
||||||
|
interval: 5m
|
||||||
|
start_interval: 30s
|
||||||
|
start_period: 5m
|
||||||
|
command: >-
|
||||||
|
postgres -c shared_preload_libraries=vectors.so -c 'search_path="$$user", public, vectors' -c logging_collector=on -c max_wal_size=2GB -c shared_buffers=512MB -c wal_compression=on
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -23,32 +23,23 @@ Refer to the official [postgres documentation](https://www.postgresql.org/docs/c
|
|||||||
It is not recommended to directly backup the `DB_DATA_LOCATION` folder. Doing so while the database is running can lead to a corrupted backup that cannot be restored.
|
It is not recommended to directly backup the `DB_DATA_LOCATION` folder. Doing so while the database is running can lead to a corrupted backup that cannot be restored.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Automatic Database Dumps
|
### Automatic Database Backups
|
||||||
|
|
||||||
:::warning
|
For convenience, Immich will automatically create database backups by default. The backups are stored in `UPLOAD_LOCATION/backups`.
|
||||||
The automatic database dumps can be used to restore the database in the event of damage to the Postgres database files.
|
As mentioned above, you should make your own backup of these together with the asset folders as noted below.
|
||||||
There is no monitoring for these dumps and you will not be notified if they are unsuccessful.
|
You can adjust the schedule and amount of kept backups in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
|
||||||
:::
|
By default, Immich will keep the last 14 backups and create a new backup every day at 2:00 AM.
|
||||||
|
|
||||||
:::caution
|
#### Trigger Backup
|
||||||
The database dumps do **NOT** contain any pictures or videos, only metadata. They are only usable with a copy of the other files in `UPLOAD_LOCATION` as outlined below.
|
|
||||||
:::
|
|
||||||
|
|
||||||
For disaster-recovery purposes, Immich will automatically create database dumps. The dumps are stored in `UPLOAD_LOCATION/backups`.
|
You are able to trigger a backup in the [admin job status page](http://my.immich.app/admin/jobs-status).
|
||||||
Please be sure to make your own, independent backup of the database together with the asset folders as noted below.
|
Visit the page, open the "Create job" modal from the top right, select "Backup Database" and click "Confirm".
|
||||||
You can adjust the schedule and amount of kept database dumps in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
|
A job will run and trigger a backup, you can verify this worked correctly by checking the logs or the backup folder.
|
||||||
By default, Immich will keep the last 14 database dumps and create a new dump every day at 2:00 AM.
|
This backup will count towards the last X backups that will be kept based on your settings.
|
||||||
|
|
||||||
#### Trigger Dump
|
|
||||||
|
|
||||||
You are able to trigger a database dump in the [admin job status page](http://my.immich.app/admin/jobs-status).
|
|
||||||
Visit the page, open the "Create job" modal from the top right, select "Create Database Dump" and click "Confirm".
|
|
||||||
A job will run and trigger a dump, you can verify this worked correctly by checking the logs or the `backups/` folder.
|
|
||||||
This dumps will count towards the last `X` dumps that will be kept based on your settings.
|
|
||||||
|
|
||||||
#### Restoring
|
#### Restoring
|
||||||
|
|
||||||
We hope to make restoring simpler in future versions, for now you can find the database dumps in the `UPLOAD_LOCATION/backups` folder on your host.
|
We hope to make restoring simpler in future versions, for now you can find the backups in the `UPLOAD_LOCATION/backups` folder on your host.
|
||||||
Then please follow the steps in the following section for restoring the database.
|
Then please follow the steps in the following section for restoring the database.
|
||||||
|
|
||||||
### Manual Backup and Restore
|
### Manual Backup and Restore
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ Running with a pre-existing Postgres server can unlock powerful administrative f
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
You must install VectorChord into your instance of Postgres using their [instructions][vchord-install]. After installation, add `shared_preload_libraries = 'vchord.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vchord.so'`.
|
You must install pgvecto.rs into your instance of Postgres using their [instructions][vectors-install]. After installation, add `shared_preload_libraries = 'vectors.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vectors.so'`.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
Immich is known to work with Postgres versions 14, 15, 16 and 17. Earlier versions are unsupported.
|
Immich is known to work with Postgres versions 14, 15, and 16. Earlier versions are unsupported. Postgres 17 is nominally compatible, but pgvecto.rs does not have prebuilt images or packages for it as of writing.
|
||||||
|
|
||||||
Make sure the installed version of VectorChord is compatible with your version of Immich. The current accepted range for VectorChord is `>= 0.3.0, < 0.4.0`.
|
Make sure the installed version of pgvecto.rs is compatible with your version of Immich. The current accepted range for pgvecto.rs is `>= 0.2.0, < 0.4.0`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Specifying the connection URL
|
## Specifying the connection URL
|
||||||
@@ -53,75 +53,16 @@ CREATE DATABASE <immichdatabasename>;
|
|||||||
\c <immichdatabasename>
|
\c <immichdatabasename>
|
||||||
BEGIN;
|
BEGIN;
|
||||||
ALTER DATABASE <immichdatabasename> OWNER TO <immichdbusername>;
|
ALTER DATABASE <immichdatabasename> OWNER TO <immichdbusername>;
|
||||||
CREATE EXTENSION vchord CASCADE;
|
CREATE EXTENSION vectors;
|
||||||
CREATE EXTENSION earthdistance CASCADE;
|
CREATE EXTENSION earthdistance CASCADE;
|
||||||
|
ALTER DATABASE <immichdatabasename> SET search_path TO "$user", public, vectors;
|
||||||
|
ALTER SCHEMA vectors OWNER TO <immichdbusername>;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Updating VectorChord
|
### Updating pgvecto.rs
|
||||||
|
|
||||||
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 pgvecto.rs, you will need to manually update the extension by connecting to the Immich database and running `ALTER EXTENSION vectors UPDATE;`.
|
||||||
|
|
||||||
## Migrating to VectorChord
|
|
||||||
|
|
||||||
VectorChord is the successor extension to pgvecto.rs, allowing for higher performance, lower memory usage and higher quality results for smart search and facial recognition.
|
|
||||||
|
|
||||||
### Migrating from pgvecto.rs
|
|
||||||
|
|
||||||
Support for pgvecto.rs will be dropped in a later release, hence we recommend all users currently using pgvecto.rs to migrate to VectorChord at their convenience. There are two primary approaches to do so.
|
|
||||||
|
|
||||||
The easiest option is to have both extensions installed during the migration:
|
|
||||||
|
|
||||||
1. Ensure you still have pgvecto.rs installed
|
|
||||||
2. [Install VectorChord][vchord-install]
|
|
||||||
3. Add `shared_preload_libraries= 'vchord.so, vectors.so'` to your `postgresql.conf`, making sure to include _both_ `vchord.so` and `vectors.so`. You may include other libraries here as well if needed
|
|
||||||
4. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;` using psql or your choice of database client
|
|
||||||
5. Start Immich and wait for the logs `Reindexed face_index` and `Reindexed clip_index` to be output
|
|
||||||
6. Remove the `vectors.so` entry from the `shared_preload_libraries` setting
|
|
||||||
7. Uninstall pgvecto.rs (e.g. `apt-get purge vectors-pg14` on Debian-based environments, replacing `pg14` as appropriate)
|
|
||||||
|
|
||||||
If it is not possible to have both VectorChord and pgvector.s installed at the same time, you can perform the migration with more manual steps:
|
|
||||||
|
|
||||||
1. While pgvecto.rs is still installed, run the following SQL command using psql or your choice of database client. Take note of the number outputted by this command as you will need it later
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT atttypmod as dimsize
|
|
||||||
FROM pg_attribute f
|
|
||||||
JOIN pg_class c ON c.oid = f.attrelid
|
|
||||||
WHERE c.relkind = 'r'::char
|
|
||||||
AND f.attnum > 0
|
|
||||||
AND c.relname = 'smart_search'::text
|
|
||||||
AND f.attname = 'embedding'::text;
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Remove references to pgvecto.rs using the below SQL commands
|
|
||||||
|
|
||||||
```sql
|
|
||||||
DROP INDEX IF EXISTS clip_index;
|
|
||||||
DROP INDEX IF EXISTS face_index;
|
|
||||||
ALTER TABLE smart_search ALTER COLUMN embedding SET DATA TYPE real[];
|
|
||||||
ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE real[];
|
|
||||||
```
|
|
||||||
|
|
||||||
3. [Install VectorChord][vchord-install]
|
|
||||||
4. Change the columns back to the appropriate vector types, replacing `<number>` with the number from step 1
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vchord CASCADE;
|
|
||||||
ALTER TABLE smart_search ALTER COLUMN embedding SET DATA TYPE vector(<number>);
|
|
||||||
ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512);
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Start Immich and let it create new indices using VectorChord
|
|
||||||
|
|
||||||
### Migrating from pgvector
|
|
||||||
|
|
||||||
1. Ensure you have at least 0.7.0 of pgvector installed. If it is below that, please upgrade it and run the SQL command `ALTER EXTENSION vector UPDATE;` using psql or your choice of database client
|
|
||||||
2. Follow the Prerequisites to install VectorChord
|
|
||||||
3. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;`
|
|
||||||
4. 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.
|
|
||||||
|
|
||||||
### Common errors
|
### Common errors
|
||||||
|
|
||||||
@@ -129,4 +70,4 @@ Note that VectorChord itself uses pgvector types, so you should not uninstall pg
|
|||||||
|
|
||||||
If you get the error `driverError: error: permission denied for view pg_vector_index_stat`, you can fix this by connecting to the Immich database and running `GRANT SELECT ON TABLE pg_vector_index_stat TO <immichdbusername>;`.
|
If you get the error `driverError: error: permission denied for view pg_vector_index_stat`, you can fix this by connecting to the Immich database and running `GRANT SELECT ON TABLE pg_vector_index_stat TO <immichdbusername>;`.
|
||||||
|
|
||||||
[vchord-install]: https://docs.vectorchord.ai/vectorchord/getting-started/installation.html
|
[vectors-install]: https://docs.vectorchord.ai/getting-started/installation.html
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ server {
|
|||||||
client_max_body_size 50000M;
|
client_max_body_size 50000M;
|
||||||
|
|
||||||
# Set headers
|
# Set headers
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $http_host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# Database Migrations
|
# Database Migrations
|
||||||
|
|
||||||
After making any changes in the `server/src/schema`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration.
|
After making any changes in the `server/src/entities`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration.
|
||||||
|
|
||||||
1. Run the command
|
1. Run the command
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run migrations:generate <migration-name>
|
npm run typeorm:migrations:generate <migration-name>
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Check if the migration file makes sense.
|
2. Check if the migration file makes sense.
|
||||||
3. Move the migration file to folder `./server/src/schema/migrations` in your code editor.
|
3. Move the migration file to folder `./server/src/migrations` in your code editor.
|
||||||
|
|
||||||
The server will automatically detect `*.ts` file changes and restart. Part of the server start-up process includes running any new migrations, so it will be applied immediately.
|
The server will automatically detect `*.ts` file changes and restart. Part of the server start-up process includes running any new migrations, so it will be applied immediately.
|
||||||
|
|||||||
@@ -83,20 +83,9 @@ To see local changes to `@immich/ui` in Immich, do the following:
|
|||||||
|
|
||||||
### Mobile app
|
### Mobile app
|
||||||
|
|
||||||
#### Setup
|
The mobile app `(/mobile)` will required Flutter toolchain 3.13.x and FVM to be installed on your system.
|
||||||
|
|
||||||
1. Setup Flutter toolchain using FVM.
|
Please refer to the [Flutter's official documentation](https://flutter.dev/docs/get-started/install) for more information on setting up the toolchain on your machine.
|
||||||
2. Run `flutter pub get` to install the dependencies.
|
|
||||||
3. Run `make translation` to generate the translation file.
|
|
||||||
4. Run `fvm flutter run` to start the app.
|
|
||||||
|
|
||||||
#### Translation
|
|
||||||
|
|
||||||
To add a new translation text, enter the key-value pair in the `i18n/en.json` in the root of the immich project. Then, from the `mobile/` directory, run
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make translation
|
|
||||||
```
|
|
||||||
|
|
||||||
The mobile app asks you what backend to connect to. You can utilize the demo backend (https://demo.immich.app/) if you don't need to change server code or upload photos. Alternatively, you can run the server yourself per the instructions above.
|
The mobile app asks you what backend to connect to. You can utilize the demo backend (https://demo.immich.app/) if you don't need to change server code or upload photos. Alternatively, you can run the server yourself per the instructions above.
|
||||||
|
|
||||||
|
|||||||
@@ -42,12 +42,6 @@ docker run -it -v "$(pwd)":/import:ro -e IMMICH_INSTANCE_URL=https://your-immich
|
|||||||
|
|
||||||
Please modify the `IMMICH_INSTANCE_URL` and `IMMICH_API_KEY` environment variables as suitable. You can also use a Docker env file to store your sensitive API key.
|
Please modify the `IMMICH_INSTANCE_URL` and `IMMICH_API_KEY` environment variables as suitable. You can also use a Docker env file to store your sensitive API key.
|
||||||
|
|
||||||
This `docker run` command will directly run the command `immich` inside the container. You can directly append the desired parameters (see under "usage") to the commandline like this:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run -it -v "$(pwd)":/import:ro -e IMMICH_INSTANCE_URL=https://your-immich-instance/api -e IMMICH_API_KEY=your-api-key ghcr.io/immich-app/immich-cli:latest upload -a -c 5 --recursive directory/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ In rare cases, the library watcher can hang, preventing Immich from starting up.
|
|||||||
|
|
||||||
### Nightly job
|
### Nightly job
|
||||||
|
|
||||||
There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page.
|
There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library managment page.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele
|
|||||||
|
|
||||||
- The GPU must have compute capability 5.2 or greater.
|
- The GPU must have compute capability 5.2 or greater.
|
||||||
- The server must have the official NVIDIA driver installed.
|
- The server must have the official NVIDIA driver installed.
|
||||||
- The installed driver must be >= 545 (it must support CUDA 12.3).
|
- The installed driver must be >= 535 (it must support CUDA 12.2).
|
||||||
- On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed.
|
- On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed.
|
||||||
|
|
||||||
#### ROCm
|
#### ROCm
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import TabItem from '@theme/TabItem';
|
|||||||
|
|
||||||
Immich uses Postgres as its search database for both metadata and contextual CLIP search.
|
Immich uses Postgres as its search database for both metadata and contextual CLIP search.
|
||||||
|
|
||||||
Contextual CLIP search is powered by the [VectorChord](https://github.com/tensorchord/VectorChord) extension, utilizing machine learning models like [CLIP](https://openai.com/research/clip) to provide relevant search results. This allows for freeform searches without requiring specific keywords in the image or video metadata.
|
Contextual CLIP search is powered by the [pgvecto.rs](https://github.com/tensorchord/pgvecto.rs) extension, utilizing machine learning models like [CLIP](https://openai.com/research/clip) to provide relevant search results. This allows for freeform searches without requiring specific keywords in the image or video metadata.
|
||||||
|
|
||||||
## Advanced Search Filters
|
## Advanced Search Filters
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
|||||||
|
|
||||||
**Execution Time (ms)**: After warming up the model with one pass, the mean execution time of 100 passes with the same input.
|
**Execution Time (ms)**: After warming up the model with one pass, the mean execution time of 100 passes with the same input.
|
||||||
|
|
||||||
**Memory (MiB)**: The peak RSS usage of the process after performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors.
|
**Memory (MiB)**: The peak RSS usage of the process afer performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors.
|
||||||
|
|
||||||
**Recall (%)**: Evaluated on Crossmodal-3600, the average of the recall@1, recall@5 and recall@10 results for zeroshot image retrieval. Chinese (Simplified), English, French, German, Italian, Japanese, Korean, Polish, Russian, Spanish and Turkish are additionally tested on XTD-10. Chinese (Simplified) and English are additionally tested on Flickr30k. The recall metrics are the average across all tested datasets.
|
**Recall (%)**: Evaluated on Crossmodal-3600, the average of the recall@1, recall@5 and recall@10 results for zeroshot image retrieval. Chinese (Simplified), English, French, German, Italian, Japanese, Korean, Polish, Russian, Spanish and Turkish are additionally tested on XTD-10. Chinese (Simplified) and English are additionally tested on Flickr30k. The recall metrics are the average across all tested datasets.
|
||||||
|
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ online generators you can use.
|
|||||||
2. Paste the link to your JSON style in either the **Light Style** or **Dark Style**. (You can add different styles which will help make the map style more appropriate depending on whether you set **Immich** to Light or Dark mode.)
|
2. Paste the link to your JSON style in either the **Light Style** or **Dark Style**. (You can add different styles which will help make the map style more appropriate depending on whether you set **Immich** to Light or Dark mode.)
|
||||||
3. Save your selections. Reload the map, and enjoy your custom map style!
|
3. Save your selections. Reload the map, and enjoy your custom map style!
|
||||||
|
|
||||||
## Use MapTiler to build a custom style
|
## Use Maptiler to build a custom style
|
||||||
|
|
||||||
Customizing the map style can be done easily using MapTiler, if you do not want to write an entire JSON document by hand.
|
Customizing the map style can be done easily using Maptiler, if you do not want to write an entire JSON document by hand.
|
||||||
|
|
||||||
1. Create a free account at https://cloud.maptiler.com
|
1. Create a free account at https://cloud.maptiler.com
|
||||||
2. Once logged in, you can either create a brand new map by clicking on **New Map**, selecting a starter map, and then clicking **Customize**, OR by selecting a **Standard Map** and customizing it from there.
|
2. Once logged in, you can either create a brand new map by clicking on **New Map**, selecting a starter map, and then clicking **Customize**, OR by selecting a **Standard Map** and customizing it from there.
|
||||||
3. The **editor** interface is self-explanatory. You can change colors, remove visible layers, or add optional layers (e.g., administrative, topo, hydro, etc.) in the composer.
|
3. The **editor** interface is self-explanatory. You can change colors, remove visible layers, or add optional layers (e.g., administrative, topo, hydro, etc.) in the composer.
|
||||||
4. Once you have your map composed, click on **Save** at the top right. Give it a unique name to save it to your account.
|
4. Once you have your map composed, click on **Save** at the top right. Give it a unique name to save it to your account.
|
||||||
5. Next, **Publish** your style using the **Publish** button at the top right. This will deploy it to production, which means it is able to be exposed over the Internet. MapTiler will present an interactive side-by-side map with the original and your changes prior to publication.<br/>
|
5. Next, **Publish** your style using the **Publish** button at the top right. This will deploy it to production, which means it is able to be exposed over the Internet. Maptiler will present an interactive side-by-side map with the original and your changes prior to publication.<br/>
|
||||||
6. MapTiler will warn you that changing the map will change it across all apps using the map. Since no apps are using the map yet, this is okay.
|
6. Maptiler will warn you that changing the map will change it across all apps using the map. Since no apps are using the map yet, this is okay.
|
||||||
7. Clicking on the name of your new map at the top left will bring you to the item's **details** page. From here, copy the link to the JSON style under **Use vector style**. This link will automatically contain your personal API key to MapTiler.
|
7. Clicking on the name of your new map at the top left will bring you to the item's **details** page. From here, copy the link to the JSON style under **Use vector style**. This link will automatically contain your personal API key to Maptiler.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Database Queries
|
# Database Queries
|
||||||
|
|
||||||
:::danger
|
:::danger
|
||||||
Keep in mind that mucking around in the database might set the Moon on fire. Avoid modifying the database directly when possible, and always have current backups.
|
Keep in mind that mucking around in the database might set the moon on fire. Avoid modifying the database directly when possible, and always have current backups.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
|
|||||||
@@ -2,13 +2,53 @@
|
|||||||
sidebar_position: 30
|
sidebar_position: 30
|
||||||
---
|
---
|
||||||
|
|
||||||
|
import CodeBlock from '@theme/CodeBlock';
|
||||||
|
import ExampleEnv from '!!raw-loader!../../../docker/example.env';
|
||||||
|
|
||||||
# Docker Compose [Recommended]
|
# Docker Compose [Recommended]
|
||||||
|
|
||||||
Docker Compose is the recommended method to run Immich in production. Below are the steps to deploy Immich with Docker Compose.
|
Docker Compose is the recommended method to run Immich in production. Below are the steps to deploy Immich with Docker Compose.
|
||||||
|
|
||||||
import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx';
|
## Step 1 - Download the required files
|
||||||
|
|
||||||
<DockerComposeSteps />
|
Create a directory of your choice (e.g. `./immich-app`) to hold the `docker-compose.yml` and `.env` files.
|
||||||
|
|
||||||
|
```bash title="Move to the directory you created"
|
||||||
|
mkdir ./immich-app
|
||||||
|
cd ./immich-app
|
||||||
|
```
|
||||||
|
|
||||||
|
Download [`docker-compose.yml`][compose-file] and [`example.env`][env-file] by running the following commands:
|
||||||
|
|
||||||
|
```bash title="Get docker-compose.yml file"
|
||||||
|
wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash title="Get .env file"
|
||||||
|
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||||
|
```
|
||||||
|
|
||||||
|
You can alternatively download these two files from your browser and move them to the directory that you created, in which case ensure that you rename `example.env` to `.env`.
|
||||||
|
|
||||||
|
## Step 2 - Populate the .env file with custom values
|
||||||
|
|
||||||
|
<CodeBlock language="bash" title="Default environmental variable content">
|
||||||
|
{ExampleEnv}
|
||||||
|
</CodeBlock>
|
||||||
|
|
||||||
|
- Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets. It should be a new directory on the server with enough free space.
|
||||||
|
- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publicly exposed, so this password is only used for local authentication.
|
||||||
|
To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`. `pwgen` is a handy utility for this.
|
||||||
|
- Set your timezone by uncommenting the `TZ=` line.
|
||||||
|
- Populate custom database information if necessary.
|
||||||
|
|
||||||
|
## Step 3 - Start the containers
|
||||||
|
|
||||||
|
From the directory you created in Step 1 (which should now contain your customized `docker-compose.yml` and `.env` files), run the following command to start Immich as a background service:
|
||||||
|
|
||||||
|
```bash title="Start the containers"
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
:::info Docker version
|
:::info Docker version
|
||||||
If you get an error such as `unknown shorthand flag: 'd' in -d` or `open <location of your .env file>: permission denied`, you are probably running the wrong Docker version. (This happens, for example, with the docker.io package in Ubuntu 22.04.3 LTS.) You can correct the problem by following the complete [Docker Engine install](https://docs.docker.com/engine/install/) procedure for your distribution, crucially the "Uninstall old versions" and "Install using the apt/rpm repository" sections. These replace the distro's Docker packages with Docker's official ones.
|
If you get an error such as `unknown shorthand flag: 'd' in -d` or `open <location of your .env file>: permission denied`, you are probably running the wrong Docker version. (This happens, for example, with the docker.io package in Ubuntu 22.04.3 LTS.) You can correct the problem by following the complete [Docker Engine install](https://docs.docker.com/engine/install/) procedure for your distribution, crucially the "Uninstall old versions" and "Install using the apt/rpm repository" sections. These replace the distro's Docker packages with Docker's official ones.
|
||||||
@@ -30,3 +70,6 @@ If you get an error `can't set healthcheck.start_interval as feature require Doc
|
|||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
Read the [Post Installation](/docs/install/post-install.mdx) steps and [upgrade instructions](/docs/install/upgrading.md).
|
Read the [Post Installation](/docs/install/post-install.mdx) steps and [upgrade instructions](/docs/install/upgrading.md).
|
||||||
|
|
||||||
|
[compose-file]: https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
|
[env-file]: https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||||
|
|||||||
@@ -72,21 +72,20 @@ Information on the current workers can be found [here](/docs/administration/jobs
|
|||||||
|
|
||||||
## Database
|
## Database
|
||||||
|
|
||||||
| Variable | Description | Default | Containers |
|
| Variable | Description | Default | Containers |
|
||||||
| :---------------------------------- | :--------------------------------------------------------------------------- | :--------: | :----------------------------- |
|
| :---------------------------------- | :----------------------------------------------------------------------- | :----------: | :----------------------------- |
|
||||||
| `DB_URL` | Database URL | | server |
|
| `DB_URL` | Database URL | | server |
|
||||||
| `DB_HOSTNAME` | Database host | `database` | server |
|
| `DB_HOSTNAME` | Database host | `database` | server |
|
||||||
| `DB_PORT` | Database port | `5432` | server |
|
| `DB_PORT` | Database port | `5432` | server |
|
||||||
| `DB_USERNAME` | Database user | `postgres` | server, database<sup>\*1</sup> |
|
| `DB_USERNAME` | Database user | `postgres` | server, database<sup>\*1</sup> |
|
||||||
| `DB_PASSWORD` | Database password | `postgres` | server, database<sup>\*1</sup> |
|
| `DB_PASSWORD` | Database password | `postgres` | server, database<sup>\*1</sup> |
|
||||||
| `DB_DATABASE_NAME` | Database name | `immich` | server, database<sup>\*1</sup> |
|
| `DB_DATABASE_NAME` | Database name | `immich` | server, database<sup>\*1</sup> |
|
||||||
| `DB_SSL_MODE` | Database SSL mode | | server |
|
| `DB_VECTOR_EXTENSION`<sup>\*2</sup> | Database vector extension (one of [`pgvector`, `pgvecto.rs`]) | `pgvecto.rs` | server |
|
||||||
| `DB_VECTOR_EXTENSION`<sup>\*2</sup> | 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_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server |
|
|
||||||
|
|
||||||
\*1: The values of `DB_USERNAME`, `DB_PASSWORD`, and `DB_DATABASE_NAME` are passed to the Postgres container as the variables `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` in `docker-compose.yml`.
|
\*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.
|
\*2: This setting cannot be changed after the server has successfully started up.
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/la
|
|||||||
|
|
||||||
## Step 2 - Populate the .env file with custom values
|
## Step 2 - Populate the .env file with custom values
|
||||||
|
|
||||||
Follow [Step 2 in Docker Compose](/docs/install/docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue.
|
Follow [Step 2 in Docker Compose](./docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue.
|
||||||
|
|
||||||
## Step 3 - Create a new project in Container Manager
|
## Step 3 - Create a new project in Container Manager
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
sidebar_position: 80
|
sidebar_position: 80
|
||||||
---
|
---
|
||||||
|
|
||||||
# TrueNAS [Community]
|
# TrueNAS SCALE [Community]
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
This is a community contribution and not officially supported by the Immich team, but included here for convenience.
|
This is a community contribution and not officially supported by the Immich team, but included here for convenience.
|
||||||
@@ -12,17 +12,17 @@ Community support can be found in the dedicated channel on the [Discord Server](
|
|||||||
**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/charts/tree/master/community/immich).**
|
**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/charts/tree/master/community/immich).**
|
||||||
:::
|
:::
|
||||||
|
|
||||||
Immich can easily be installed on TrueNAS Community Edition via the **Community** train application.
|
Immich can easily be installed on TrueNAS SCALE via the **Community** train application.
|
||||||
Consider reviewing the TrueNAS [Apps resources](https://apps.truenas.com/getting-started/) if you have not previously configured applications on your system.
|
Consider reviewing the TrueNAS [Apps tutorial](https://www.truenas.com/docs/scale/scaletutorials/apps/) if you have not previously configured applications on your system.
|
||||||
|
|
||||||
TrueNAS Community Edition makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries.
|
TrueNAS SCALE makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries.
|
||||||
|
|
||||||
## First Steps
|
## First Steps
|
||||||
|
|
||||||
The Immich app in TrueNAS Community Edition installs, completes the initial configuration, then starts the Immich web portal.
|
The Immich app in TrueNAS SCALE installs, completes the initial configuration, then starts the Immich web portal.
|
||||||
When updates become available, TrueNAS alerts and provides easy updates.
|
When updates become available, SCALE alerts and provides easy updates.
|
||||||
|
|
||||||
Before installing the Immich app in TrueNAS, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation.
|
Before installing the Immich app in SCALE, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation.
|
||||||
You may also configure environment variables at any time after deploying the application.
|
You may also configure environment variables at any time after deploying the application.
|
||||||
|
|
||||||
### Setting up Storage Datasets
|
### Setting up Storage Datasets
|
||||||
@@ -126,9 +126,9 @@ className="border rounded-xl"
|
|||||||
|
|
||||||
Accept the default port `30041` in **WebUI Port** or enter a custom port number.
|
Accept the default port `30041` in **WebUI Port** or enter a custom port number.
|
||||||
:::info Allowed Port Numbers
|
:::info Allowed Port Numbers
|
||||||
Only numbers within the range 9000-65535 may be used on TrueNAS versions below TrueNAS Community Edition 24.10 Electric Eel.
|
Only numbers within the range 9000-65535 may be used on SCALE versions below TrueNAS Scale 24.10 Electric Eel.
|
||||||
|
|
||||||
Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/solutions/optimizations/security/#truenas-default-ports).
|
Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/references/defaultports/).
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Storage Configuration
|
### Storage Configuration
|
||||||
@@ -173,7 +173,7 @@ className="border rounded-xl"
|
|||||||
|
|
||||||
You may configure [External Libraries](/docs/features/libraries) by mounting them using **Additional Storage**.
|
You may configure [External Libraries](/docs/features/libraries) by mounting them using **Additional Storage**.
|
||||||
The **Mount Path** is the location you will need to copy and paste into the External Library settings within Immich.
|
The **Mount Path** is the location you will need to copy and paste into the External Library settings within Immich.
|
||||||
The **Host Path** is the location on the TrueNAS Community Edition server where your external library is located.
|
The **Host Path** is the location on the TrueNAS SCALE server where your external library is located.
|
||||||
|
|
||||||
<!-- A section for Labels would go here but I don't know what they do. -->
|
<!-- A section for Labels would go here but I don't know what they do. -->
|
||||||
|
|
||||||
@@ -188,17 +188,17 @@ className="border rounded-xl"
|
|||||||
|
|
||||||
Accept the default **CPU** limit of `2` threads or specify the number of threads (CPUs with Multi-/Hyper-threading have 2 threads per core).
|
Accept the default **CPU** limit of `2` threads or specify the number of threads (CPUs with Multi-/Hyper-threading have 2 threads per core).
|
||||||
|
|
||||||
Specify the **Memory** limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB.
|
Accept the default **Memory** limit of `4096` MB or specify the number of MB of RAM. If you're using Machine Learning you should probably set this above 8000 MB.
|
||||||
|
|
||||||
:::info Older TrueNAS Versions
|
:::info Older SCALE Versions
|
||||||
Before TrueNAS Community Edition version 24.10 Electric Eel:
|
Before TrueNAS SCALE version 24.10 Electric Eel:
|
||||||
|
|
||||||
The **CPU** value was specified in a different format with a default of `4000m` which is 4 threads.
|
The **CPU** value was specified in a different format with a default of `4000m` which is 4 threads.
|
||||||
|
|
||||||
The **Memory** value was specified in a different format with a default of `8Gi` which is 8 GiB of RAM. The value was specified in bytes or a number with a measurement suffix. Examples: `129M`, `123Mi`, `1000000000`
|
The **Memory** value was specified in a different format with a default of `8Gi` which is 8 GiB of RAM. The value was specified in bytes or a number with a measurement suffix. Examples: `129M`, `123Mi`, `1000000000`
|
||||||
:::
|
:::
|
||||||
|
|
||||||
Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough)
|
Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://www.truenas.com/docs/truenasapps/#gpu-passthrough)
|
||||||
|
|
||||||
### Install
|
### Install
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ className="border rounded-xl"
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
Some Environment Variables are not available for the TrueNAS Community Edition app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings).
|
Some Environment Variables are not available for the TrueNAS SCALE app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings).
|
||||||
|
|
||||||
Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`.
|
Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`.
|
||||||
:::
|
:::
|
||||||
@@ -251,7 +251,7 @@ Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`
|
|||||||
Make sure to read the general [upgrade instructions](/docs/install/upgrading.md).
|
Make sure to read the general [upgrade instructions](/docs/install/upgrading.md).
|
||||||
:::
|
:::
|
||||||
|
|
||||||
When updates become available, TrueNAS alerts and provides easy updates.
|
When updates become available, SCALE alerts and provides easy updates.
|
||||||
To update the app to the latest version:
|
To update the app to the latest version:
|
||||||
|
|
||||||
- Go to the **Installed Applications** screen and select Immich from the list of installed applications.
|
- Go to the **Installed Applications** screen and select Immich from the list of installed applications.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
sidebar_position: 3
|
sidebar_position: 2
|
||||||
---
|
---
|
||||||
|
|
||||||
# Comparison
|
# Comparison
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 233 KiB |
@@ -2,13 +2,9 @@
|
|||||||
sidebar_position: 1
|
sidebar_position: 1
|
||||||
---
|
---
|
||||||
|
|
||||||
# Welcome to Immich
|
# Introduction
|
||||||
|
|
||||||
<img
|
<img src={require('./img/feature-panel.webp').default} alt="Immich - Self-hosted photos and videos backup tool" />
|
||||||
src={require('./img/social-preview-light.webp').default}
|
|
||||||
alt="Immich - Self-hosted photos and videos backup tool"
|
|
||||||
data-theme="light"
|
|
||||||
/>
|
|
||||||
|
|
||||||
## Welcome!
|
## Welcome!
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
sidebar_position: 2
|
sidebar_position: 3
|
||||||
---
|
---
|
||||||
|
|
||||||
# Quick start
|
# Quick start
|
||||||
@@ -10,20 +10,11 @@ to install and use it.
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- A system with at least 4GB of RAM and 2 CPU cores.
|
Check the [requirements page](/docs/install/requirements) to get started.
|
||||||
- [Docker](https://docs.docker.com/engine/install/)
|
|
||||||
|
|
||||||
> For a more detailed list of requirements, see the [requirements page](/docs/install/requirements).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Set up the server
|
## Set up the server
|
||||||
|
|
||||||
import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx';
|
Follow the [Docker Compose (Recommended)](/docs/install/docker-compose) instructions to install the server.
|
||||||
|
|
||||||
<DockerComposeSteps />
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Try the web app
|
## Try the web app
|
||||||
|
|
||||||
@@ -35,8 +26,6 @@ Try uploading a picture from your browser.
|
|||||||
|
|
||||||
<img src={require('./img/upload-button.webp').default} title="Upload button" />
|
<img src={require('./img/upload-button.webp').default} title="Upload button" />
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Try the mobile app
|
## Try the mobile app
|
||||||
|
|
||||||
### Download the Mobile App
|
### Download the Mobile App
|
||||||
@@ -67,8 +56,6 @@ You can select the **Jobs** tab to see Immich processing your photos.
|
|||||||
|
|
||||||
<img src={require('/docs/guides/img/jobs-tab.webp').default} title="Jobs tab" width={300} />
|
<img src={require('/docs/guides/img/jobs-tab.webp').default} title="Jobs tab" width={300} />
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Review the database backup and restore process
|
## Review the database backup and restore process
|
||||||
|
|
||||||
Immich has built-in database backups. You can refer to the
|
Immich has built-in database backups. You can refer to the
|
||||||
@@ -78,8 +65,6 @@ Immich has built-in database backups. You can refer to the
|
|||||||
The database only contains metadata and user information. You must setup manual backups of the images and videos stored in `UPLOAD_LOCATION`.
|
The database only contains metadata and user information. You must setup manual backups of the images and videos stored in `UPLOAD_LOCATION`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Where to go from here?
|
## Where to go from here?
|
||||||
|
|
||||||
You may decide you'd like to install the server a different way; the Install category on the left menu provides many options.
|
You may decide you'd like to install the server a different way; the Install category on the left menu provides many options.
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
import CodeBlock from '@theme/CodeBlock';
|
|
||||||
import ExampleEnv from '!!raw-loader!../../../docker/example.env';
|
|
||||||
|
|
||||||
### Step 1 - Download the required files
|
|
||||||
|
|
||||||
Create a directory of your choice (e.g. `./immich-app`) to hold the `docker-compose.yml` and `.env` files.
|
|
||||||
|
|
||||||
```bash title="Move to the directory you created"
|
|
||||||
mkdir ./immich-app
|
|
||||||
cd ./immich-app
|
|
||||||
```
|
|
||||||
|
|
||||||
Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) by running the following commands:
|
|
||||||
|
|
||||||
```bash title="Get docker-compose.yml file"
|
|
||||||
wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash title="Get .env file"
|
|
||||||
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env
|
|
||||||
```
|
|
||||||
|
|
||||||
You can alternatively download these two files from your browser and move them to the directory that you created, in which case ensure that you rename `example.env` to `.env`.
|
|
||||||
|
|
||||||
### Step 2 - Populate the .env file with custom values
|
|
||||||
|
|
||||||
<CodeBlock language="bash" title="Default environmental variable content">
|
|
||||||
{ExampleEnv}
|
|
||||||
</CodeBlock>
|
|
||||||
|
|
||||||
- Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets. It should be a new directory on the server with enough free space.
|
|
||||||
- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publicly exposed, so this password is only used for local authentication.
|
|
||||||
To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`. `pwgen` is a handy utility for this.
|
|
||||||
- Set your timezone by uncommenting the `TZ=` line.
|
|
||||||
- Populate custom database information if necessary.
|
|
||||||
|
|
||||||
### Step 3 - Start the containers
|
|
||||||
|
|
||||||
From the directory you created in Step 1 (which should now contain your customized `docker-compose.yml` and `.env` files), run the following command to start Immich as a background service:
|
|
||||||
|
|
||||||
```bash title="Start the containers"
|
|
||||||
docker compose up -d
|
|
||||||
```
|
|
||||||
@@ -95,7 +95,7 @@ const config = {
|
|||||||
position: 'right',
|
position: 'right',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
to: '/docs/overview/welcome',
|
to: '/docs/overview/introduction',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
label: 'Docs',
|
label: 'Docs',
|
||||||
},
|
},
|
||||||
@@ -124,12 +124,6 @@ const config = {
|
|||||||
label: 'Discord',
|
label: 'Discord',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'html',
|
|
||||||
position: 'right',
|
|
||||||
value:
|
|
||||||
'<a href="https://buy.immich.app" target="_blank" class="no-underline hover:no-underline"><button class="buy-button bg-immich-primary dark:bg-immich-dark-primary text-white dark:text-black rounded-xl">Buy Immich</button></a>',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
@@ -140,7 +134,7 @@ const config = {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'Welcome',
|
label: 'Welcome',
|
||||||
to: '/docs/overview/welcome',
|
to: '/docs/overview/introduction',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Installation',
|
label: 'Installation',
|
||||||
|
|||||||
4988
docs/package-lock.json
generated
4988
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -40,9 +40,8 @@ const projects: CommunityProjectProps[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Lightroom Immich Plugin: lrc-immich-plugin',
|
title: 'Lightroom Immich Plugin: lrc-immich-plugin',
|
||||||
description:
|
description: 'Another Lightroom plugin to publish or export photos from Lightroom to Immich.',
|
||||||
'Lightroom plugin to publish, export photos from Lightroom to Immich. Import from Immich to Lightroom is also supported.',
|
url: 'https://github.com/bmachek/lrc-immich-plugin',
|
||||||
url: 'https://blog.fokuspunk.de/lrc-immich-plugin/',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Immich Duplicate Finder',
|
title: 'Immich Duplicate Finder',
|
||||||
|
|||||||
@@ -7,22 +7,14 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@font-face {
|
@import url('https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/fonts/overpass/Overpass.ttf') format('truetype-variations');
|
|
||||||
font-weight: 1 999;
|
|
||||||
font-style: normal;
|
|
||||||
ascent-override: 106.25%;
|
|
||||||
size-adjust: 106.25%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
body {
|
||||||
font-family: 'Overpass Mono';
|
font-family: 'Be Vietnam Pro', serif;
|
||||||
src: url('/fonts/overpass/OverpassMono.ttf') format('truetype-variations');
|
font-optical-sizing: auto;
|
||||||
font-weight: 1 999;
|
/* font-size: 1.125rem;
|
||||||
font-style: normal;
|
|
||||||
ascent-override: 106.25%;
|
ascent-override: 106.25%;
|
||||||
size-adjust: 106.25%;
|
size-adjust: 106.25%; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumbs__link {
|
.breadcrumbs__link {
|
||||||
@@ -37,7 +29,6 @@ img {
|
|||||||
|
|
||||||
/* You can override the default Infima variables here. */
|
/* You can override the default Infima variables here. */
|
||||||
:root {
|
:root {
|
||||||
font-family: 'Overpass', sans-serif;
|
|
||||||
--ifm-color-primary: #4250af;
|
--ifm-color-primary: #4250af;
|
||||||
--ifm-color-primary-dark: #4250af;
|
--ifm-color-primary-dark: #4250af;
|
||||||
--ifm-color-primary-darker: #4250af;
|
--ifm-color-primary-darker: #4250af;
|
||||||
@@ -68,12 +59,14 @@ div[class^='announcementBar_'] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu__link {
|
.menu__link {
|
||||||
padding: 10px 10px 10px 16px;
|
padding: 10px;
|
||||||
|
padding-left: 16px;
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu__list-item-collapsible {
|
.menu__list-item-collapsible {
|
||||||
|
border-radius: 10px;
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
}
|
}
|
||||||
@@ -90,12 +83,3 @@ div[class*='navbar__items'] > li:has(a[class*='version-switcher-34ab39']) {
|
|||||||
code {
|
code {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.buy-button {
|
|
||||||
padding: 8px 14px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
font-family: 'Overpass', sans-serif;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 0 5px 2px rgba(181, 206, 254, 0.4);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
mdiBug,
|
mdiBug,
|
||||||
mdiCalendarToday,
|
mdiCalendarToday,
|
||||||
mdiCrosshairsOff,
|
mdiCrosshairsOff,
|
||||||
mdiCrop,
|
|
||||||
mdiDatabase,
|
mdiDatabase,
|
||||||
mdiLeadPencil,
|
mdiLeadPencil,
|
||||||
mdiLockOff,
|
mdiLockOff,
|
||||||
@@ -23,18 +22,6 @@ const withLanguage = (date: Date) => (language: string) => date.toLocaleDateStri
|
|||||||
type Item = Omit<TimelineItem, 'done' | 'getDateLabel'> & { date: Date };
|
type Item = Omit<TimelineItem, 'done' | 'getDateLabel'> & { date: Date };
|
||||||
|
|
||||||
const items: Item[] = [
|
const items: Item[] = [
|
||||||
{
|
|
||||||
icon: mdiCrop,
|
|
||||||
iconColor: 'tomato',
|
|
||||||
title: 'Image dimensions in EXIF metadata are cursed',
|
|
||||||
description:
|
|
||||||
'The dimensions in EXIF metadata can be different from the actual dimensions of the image, causing issues with cropping and resizing.',
|
|
||||||
link: {
|
|
||||||
url: 'https://github.com/immich-app/immich/pull/17974',
|
|
||||||
text: '#17974',
|
|
||||||
},
|
|
||||||
date: new Date(2025, 5, 5),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: mdiMicrosoftWindows,
|
icon: mdiMicrosoftWindows,
|
||||||
iconColor: '#357EC7',
|
iconColor: '#357EC7',
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
# Errors
|
|
||||||
|
|
||||||
## TypeORM Upgrade
|
|
||||||
|
|
||||||
The upgrade to Immich `v2.x.x` has a required upgrade path to `v1.132.0+`. This means it is required to start up the application at least once on version `1.132.0` (or later). Doing so will complete database schema upgrades that are required for `v2.0.0`. After Immich has successfully booted on this version, shut the system down and try the `v2.x.x` upgrade again.
|
|
||||||
@@ -4,7 +4,6 @@ import Layout from '@theme/Layout';
|
|||||||
import { discordPath, discordViewBox } from '@site/src/components/svg-paths';
|
import { discordPath, discordViewBox } from '@site/src/components/svg-paths';
|
||||||
import ThemedImage from '@theme/ThemedImage';
|
import ThemedImage from '@theme/ThemedImage';
|
||||||
import Icon from '@mdi/react';
|
import Icon from '@mdi/react';
|
||||||
|
|
||||||
function HomepageHeader() {
|
function HomepageHeader() {
|
||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
@@ -13,14 +12,11 @@ function HomepageHeader() {
|
|||||||
<div className="w-full h-[120vh] absolute left-0 top-0 backdrop-blur-3xl bg-immich-bg/40 dark:bg-transparent"></div>
|
<div className="w-full h-[120vh] absolute left-0 top-0 backdrop-blur-3xl bg-immich-bg/40 dark:bg-transparent"></div>
|
||||||
</div>
|
</div>
|
||||||
<section className="text-center pt-12 sm:pt-24 bg-immich-bg/50 dark:bg-immich-dark-bg/80">
|
<section className="text-center pt-12 sm:pt-24 bg-immich-bg/50 dark:bg-immich-dark-bg/80">
|
||||||
<a href="https://futo.org" target="_blank" rel="noopener noreferrer">
|
<ThemedImage
|
||||||
<ThemedImage
|
sources={{ dark: 'img/logomark-dark.svg', light: 'img/logomark-light.svg' }}
|
||||||
sources={{ dark: 'img/logomark-dark-with-futo.svg', light: 'img/logomark-light-with-futo.svg' }}
|
className="h-[115px] w-[115px] mb-2 antialiased rounded-none"
|
||||||
className="h-[125px] w-[125px] mb-2 antialiased rounded-none"
|
alt="Immich logo"
|
||||||
alt="Immich logo"
|
/>
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
<p className="text-3xl md:text-5xl sm:leading-tight mb-1 font-extrabold text-black/90 dark:text-white px-4">
|
<p className="text-3xl md:text-5xl sm:leading-tight mb-1 font-extrabold text-black/90 dark:text-white px-4">
|
||||||
Self-hosted{' '}
|
Self-hosted{' '}
|
||||||
@@ -31,7 +27,7 @@ function HomepageHeader() {
|
|||||||
solution<span className="block"></span>
|
solution<span className="block"></span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="max-w-1/4 m-auto mt-4 px-4 text-lg text-gray-700 dark:text-gray-100">
|
<p className="max-w-1/4 m-auto mt-4 px-4">
|
||||||
Easily back up, organize, and manage your photos on your own server. Immich helps you
|
Easily back up, organize, and manage your photos on your own server. Immich helps you
|
||||||
<span className="sm:block"></span> browse, search and organize your photos and videos with ease, without
|
<span className="sm:block"></span> browse, search and organize your photos and videos with ease, without
|
||||||
sacrificing your privacy.
|
sacrificing your privacy.
|
||||||
@@ -39,21 +35,27 @@ function HomepageHeader() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col sm:flex-row place-items-center place-content-center mt-9 gap-4 ">
|
<div className="flex flex-col sm:flex-row place-items-center place-content-center mt-9 gap-4 ">
|
||||||
<Link
|
<Link
|
||||||
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary dark:bg-immich-dark-primary rounded-xl no-underline hover:no-underline text-white hover:text-gray-50 dark:text-immich-dark-bg font-bold"
|
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary dark:bg-immich-dark-primary rounded-xl no-underline hover:no-underline text-white hover:text-gray-50 dark:text-immich-dark-bg font-bold uppercase"
|
||||||
to="docs/overview/quick-start"
|
to="docs/overview/introduction"
|
||||||
>
|
>
|
||||||
Get Started
|
Get started
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
className="flex place-items-center place-content-center py-3 px-8 border bg-white/90 dark:bg-gray-300 rounded-xl hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold"
|
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary/10 dark:bg-gray-300 rounded-xl hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold uppercase"
|
||||||
to="https://demo.immich.app/"
|
to="https://demo.immich.app/"
|
||||||
>
|
>
|
||||||
Open Demo
|
Demo
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary/10 dark:bg-gray-300 rounded-xl hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold uppercase"
|
||||||
|
to="https://immich.store"
|
||||||
|
>
|
||||||
|
Buy Merch
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="my-12 flex gap-1 font-medium place-items-center place-content-center text-immich-primary dark:text-immich-dark-primary">
|
||||||
<div className="my-8 flex gap-1 font-medium place-items-center place-content-center text-immich-primary dark:text-immich-dark-primary">
|
|
||||||
<Icon
|
<Icon
|
||||||
path={discordPath}
|
path={discordPath}
|
||||||
viewBox={discordViewBox} /* viewBox may show an error in your IDE but it is normal. */
|
viewBox={discordViewBox} /* viewBox may show an error in your IDE but it is normal. */
|
||||||
@@ -86,18 +88,11 @@ function HomepageHeader() {
|
|||||||
<img className="h-24" alt="Get it on Google Play" src="/img/google-play-badge.png" />
|
<img className="h-24" alt="Get it on Google Play" src="/img/google-play-badge.png" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="h-24">
|
<div className="h-24">
|
||||||
<a href="https://apps.apple.com/sg/app/immich/id1613945652">
|
<a href="https://apps.apple.com/sg/app/immich/id1613945652">
|
||||||
<img className="h-24 sm:p-3.5 p-3" alt="Download on the App Store" src="/img/ios-app-store-badge.svg" />
|
<img className="h-24 sm:p-3.5 p-3" alt="Download on the App Store" src="/img/ios-app-store-badge.svg" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="h-24">
|
|
||||||
<a href="https://github.com/immich-app/immich/releases/latest">
|
|
||||||
<img className="h-24 sm:p-3.5 p-3" alt="Download APK" src="/img/download-apk-github.svg" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<ThemedImage
|
<ThemedImage
|
||||||
sources={{ dark: '/img/app-qr-code-dark.svg', light: '/img/app-qr-code-light.svg' }}
|
sources={{ dark: '/img/app-qr-code-dark.svg', light: '/img/app-qr-code-light.svg' }}
|
||||||
@@ -116,7 +111,7 @@ export default function Home(): JSX.Element {
|
|||||||
<HomepageHeader />
|
<HomepageHeader />
|
||||||
<div className="flex flex-col place-items-center text-center place-content-center dark:bg-immich-dark-bg py-8">
|
<div className="flex flex-col place-items-center text-center place-content-center dark:bg-immich-dark-bg py-8">
|
||||||
<p>This project is available under GNU AGPL v3 license.</p>
|
<p>This project is available under GNU AGPL v3 license.</p>
|
||||||
<p className="text-sm">Privacy should not be a luxury</p>
|
<p className="text-xs">Privacy should not be a luxury</p>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Link from '@docusaurus/Link';
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
function HomepageHeader() {
|
function HomepageHeader() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ import {
|
|||||||
mdiWeb,
|
mdiWeb,
|
||||||
mdiDatabaseOutline,
|
mdiDatabaseOutline,
|
||||||
mdiLinkEdit,
|
mdiLinkEdit,
|
||||||
mdiTagFaces,
|
|
||||||
mdiMovieOpenPlayOutline,
|
mdiMovieOpenPlayOutline,
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
@@ -84,8 +83,6 @@ import React from 'react';
|
|||||||
import { Item, Timeline } from '../components/timeline';
|
import { Item, Timeline } from '../components/timeline';
|
||||||
|
|
||||||
const releases = {
|
const releases = {
|
||||||
'v1.130.0': new Date(2025, 2, 25),
|
|
||||||
'v1.127.0': new Date(2025, 1, 26),
|
|
||||||
'v1.122.0': new Date(2024, 11, 5),
|
'v1.122.0': new Date(2024, 11, 5),
|
||||||
'v1.120.0': new Date(2024, 10, 6),
|
'v1.120.0': new Date(2024, 10, 6),
|
||||||
'v1.114.0': new Date(2024, 8, 6),
|
'v1.114.0': new Date(2024, 8, 6),
|
||||||
@@ -245,13 +242,6 @@ const roadmap: Item[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const milestones: Item[] = [
|
const milestones: Item[] = [
|
||||||
withRelease({
|
|
||||||
icon: mdiFolderMultiple,
|
|
||||||
iconColor: 'brown',
|
|
||||||
title: 'Folders view in the mobile app',
|
|
||||||
description: 'Browse your photos and videos in their folder structure inside the mobile app',
|
|
||||||
release: 'v1.130.0',
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
icon: mdiStar,
|
icon: mdiStar,
|
||||||
iconColor: 'gold',
|
iconColor: 'gold',
|
||||||
@@ -259,14 +249,6 @@ const milestones: Item[] = [
|
|||||||
description: 'Reached 60K Stars on GitHub!',
|
description: 'Reached 60K Stars on GitHub!',
|
||||||
getDateLabel: withLanguage(new Date(2025, 2, 4)),
|
getDateLabel: withLanguage(new Date(2025, 2, 4)),
|
||||||
},
|
},
|
||||||
withRelease({
|
|
||||||
icon: mdiTagFaces,
|
|
||||||
iconColor: 'teal',
|
|
||||||
title: 'Manual face tagging',
|
|
||||||
description:
|
|
||||||
'Manually tag or remove faces in photos and videos, even when automatic detection misses or misidentifies them.',
|
|
||||||
release: 'v1.127.0',
|
|
||||||
}),
|
|
||||||
withRelease({
|
withRelease({
|
||||||
icon: mdiLinkEdit,
|
icon: mdiLinkEdit,
|
||||||
iconColor: 'crimson',
|
iconColor: 'crimson',
|
||||||
@@ -284,8 +266,8 @@ const milestones: Item[] = [
|
|||||||
withRelease({
|
withRelease({
|
||||||
icon: mdiDatabaseOutline,
|
icon: mdiDatabaseOutline,
|
||||||
iconColor: 'brown',
|
iconColor: 'brown',
|
||||||
title: 'Automatic database dumps',
|
title: 'Automatic database backups',
|
||||||
description: 'Database dumps are now integrated into the Immich server',
|
description: 'Database backups are now integrated into the Immich server',
|
||||||
release: 'v1.120.0',
|
release: 'v1.120.0',
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@@ -318,7 +300,7 @@ const milestones: Item[] = [
|
|||||||
withRelease({
|
withRelease({
|
||||||
icon: mdiFolderMultiple,
|
icon: mdiFolderMultiple,
|
||||||
iconColor: 'brown',
|
iconColor: 'brown',
|
||||||
title: 'Folders view',
|
title: 'Folders',
|
||||||
description: 'Browse your photos and videos in their folder structure',
|
description: 'Browse your photos and videos in their folder structure',
|
||||||
release: 'v1.113.0',
|
release: 'v1.113.0',
|
||||||
}),
|
}),
|
||||||
|
|||||||
5
docs/static/.well-known/security.txt
vendored
5
docs/static/.well-known/security.txt
vendored
@@ -1,5 +0,0 @@
|
|||||||
Policy: https://github.com/immich-app/immich/blob/main/SECURITY.md
|
|
||||||
Contact: mailto:security@immich.app
|
|
||||||
Preferred-Languages: en
|
|
||||||
Expires: 2026-05-01T23:59:00.000Z
|
|
||||||
Canonical: https://immich.app/.well-known/security.txt
|
|
||||||
1
docs/static/_redirects
vendored
1
docs/static/_redirects
vendored
@@ -30,4 +30,3 @@
|
|||||||
/docs/guides/api-album-sync /docs/community-projects 307
|
/docs/guides/api-album-sync /docs/community-projects 307
|
||||||
/docs/guides/remove-offline-files /docs/community-projects 307
|
/docs/guides/remove-offline-files /docs/community-projects 307
|
||||||
/milestones /roadmap 307
|
/milestones /roadmap 307
|
||||||
/docs/overview/introduction /docs/overview/welcome 307
|
|
||||||
156
docs/static/archived-versions.json
vendored
156
docs/static/archived-versions.json
vendored
@@ -1,16 +1,36 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"label": "v1.132.3",
|
|
||||||
"url": "https://v1.132.3.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.131.3",
|
"label": "v1.131.3",
|
||||||
"url": "https://v1.131.3.archive.immich.app"
|
"url": "https://v1.131.3.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.131.2",
|
||||||
|
"url": "https://v1.131.2.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.131.1",
|
||||||
|
"url": "https://v1.131.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.131.0",
|
||||||
|
"url": "https://v1.131.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.130.3",
|
"label": "v1.130.3",
|
||||||
"url": "https://v1.130.3.archive.immich.app"
|
"url": "https://v1.130.3.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.130.2",
|
||||||
|
"url": "https://v1.130.2.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.130.1",
|
||||||
|
"url": "https://v1.130.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.130.0",
|
||||||
|
"url": "https://v1.130.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.129.0",
|
"label": "v1.129.0",
|
||||||
"url": "https://v1.129.0.archive.immich.app"
|
"url": "https://v1.129.0.archive.immich.app"
|
||||||
@@ -27,14 +47,46 @@
|
|||||||
"label": "v1.126.1",
|
"label": "v1.126.1",
|
||||||
"url": "https://v1.126.1.archive.immich.app"
|
"url": "https://v1.126.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.126.0",
|
||||||
|
"url": "https://v1.126.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.125.7",
|
"label": "v1.125.7",
|
||||||
"url": "https://v1.125.7.archive.immich.app"
|
"url": "https://v1.125.7.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.125.6",
|
||||||
|
"url": "https://v1.125.6.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.125.5",
|
||||||
|
"url": "https://v1.125.5.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.125.3",
|
||||||
|
"url": "https://v1.125.3.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.125.2",
|
||||||
|
"url": "https://v1.125.2.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.125.1",
|
||||||
|
"url": "https://v1.125.1.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.124.2",
|
"label": "v1.124.2",
|
||||||
"url": "https://v1.124.2.archive.immich.app"
|
"url": "https://v1.124.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.124.1",
|
||||||
|
"url": "https://v1.124.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.124.0",
|
||||||
|
"url": "https://v1.124.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.123.0",
|
"label": "v1.123.0",
|
||||||
"url": "https://v1.123.0.archive.immich.app"
|
"url": "https://v1.123.0.archive.immich.app"
|
||||||
@@ -43,6 +95,18 @@
|
|||||||
"label": "v1.122.3",
|
"label": "v1.122.3",
|
||||||
"url": "https://v1.122.3.archive.immich.app"
|
"url": "https://v1.122.3.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.122.2",
|
||||||
|
"url": "https://v1.122.2.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.122.1",
|
||||||
|
"url": "https://v1.122.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.122.0",
|
||||||
|
"url": "https://v1.122.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.121.0",
|
"label": "v1.121.0",
|
||||||
"url": "https://v1.121.0.archive.immich.app"
|
"url": "https://v1.121.0.archive.immich.app"
|
||||||
@@ -51,14 +115,34 @@
|
|||||||
"label": "v1.120.2",
|
"label": "v1.120.2",
|
||||||
"url": "https://v1.120.2.archive.immich.app"
|
"url": "https://v1.120.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.120.1",
|
||||||
|
"url": "https://v1.120.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.120.0",
|
||||||
|
"url": "https://v1.120.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.119.1",
|
"label": "v1.119.1",
|
||||||
"url": "https://v1.119.1.archive.immich.app"
|
"url": "https://v1.119.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.119.0",
|
||||||
|
"url": "https://v1.119.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.118.2",
|
"label": "v1.118.2",
|
||||||
"url": "https://v1.118.2.archive.immich.app"
|
"url": "https://v1.118.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.118.1",
|
||||||
|
"url": "https://v1.118.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.118.0",
|
||||||
|
"url": "https://v1.118.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.117.0",
|
"label": "v1.117.0",
|
||||||
"url": "https://v1.117.0.archive.immich.app"
|
"url": "https://v1.117.0.archive.immich.app"
|
||||||
@@ -67,6 +151,14 @@
|
|||||||
"label": "v1.116.2",
|
"label": "v1.116.2",
|
||||||
"url": "https://v1.116.2.archive.immich.app"
|
"url": "https://v1.116.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.116.1",
|
||||||
|
"url": "https://v1.116.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.116.0",
|
||||||
|
"url": "https://v1.116.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.115.0",
|
"label": "v1.115.0",
|
||||||
"url": "https://v1.115.0.archive.immich.app"
|
"url": "https://v1.115.0.archive.immich.app"
|
||||||
@@ -79,10 +171,18 @@
|
|||||||
"label": "v1.113.1",
|
"label": "v1.113.1",
|
||||||
"url": "https://v1.113.1.archive.immich.app"
|
"url": "https://v1.113.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.113.0",
|
||||||
|
"url": "https://v1.113.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.112.1",
|
"label": "v1.112.1",
|
||||||
"url": "https://v1.112.1.archive.immich.app"
|
"url": "https://v1.112.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.112.0",
|
||||||
|
"url": "https://v1.112.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.111.0",
|
"label": "v1.111.0",
|
||||||
"url": "https://v1.111.0.archive.immich.app"
|
"url": "https://v1.111.0.archive.immich.app"
|
||||||
@@ -95,6 +195,14 @@
|
|||||||
"label": "v1.109.2",
|
"label": "v1.109.2",
|
||||||
"url": "https://v1.109.2.archive.immich.app"
|
"url": "https://v1.109.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.109.1",
|
||||||
|
"url": "https://v1.109.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.109.0",
|
||||||
|
"url": "https://v1.109.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.108.0",
|
"label": "v1.108.0",
|
||||||
"url": "https://v1.108.0.archive.immich.app"
|
"url": "https://v1.108.0.archive.immich.app"
|
||||||
@@ -103,14 +211,38 @@
|
|||||||
"label": "v1.107.2",
|
"label": "v1.107.2",
|
||||||
"url": "https://v1.107.2.archive.immich.app"
|
"url": "https://v1.107.2.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.107.1",
|
||||||
|
"url": "https://v1.107.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.107.0",
|
||||||
|
"url": "https://v1.107.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.106.4",
|
"label": "v1.106.4",
|
||||||
"url": "https://v1.106.4.archive.immich.app"
|
"url": "https://v1.106.4.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.106.3",
|
||||||
|
"url": "https://v1.106.3.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.106.2",
|
||||||
|
"url": "https://v1.106.2.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.106.1",
|
||||||
|
"url": "https://v1.106.1.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.105.1",
|
"label": "v1.105.1",
|
||||||
"url": "https://v1.105.1.archive.immich.app"
|
"url": "https://v1.105.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.105.0",
|
||||||
|
"url": "https://v1.105.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.104.0",
|
"label": "v1.104.0",
|
||||||
"url": "https://v1.104.0.archive.immich.app"
|
"url": "https://v1.104.0.archive.immich.app"
|
||||||
@@ -119,10 +251,26 @@
|
|||||||
"label": "v1.103.1",
|
"label": "v1.103.1",
|
||||||
"url": "https://v1.103.1.archive.immich.app"
|
"url": "https://v1.103.1.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.103.0",
|
||||||
|
"url": "https://v1.103.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.102.3",
|
"label": "v1.102.3",
|
||||||
"url": "https://v1.102.3.archive.immich.app"
|
"url": "https://v1.102.3.archive.immich.app"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.102.2",
|
||||||
|
"url": "https://v1.102.2.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.102.1",
|
||||||
|
"url": "https://v1.102.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.102.0",
|
||||||
|
"url": "https://v1.102.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.101.0",
|
"label": "v1.101.0",
|
||||||
"url": "https://v1.101.0.archive.immich.app"
|
"url": "https://v1.101.0.archive.immich.app"
|
||||||
|
|||||||
BIN
docs/static/fonts/overpass/Overpass-Italic.ttf
vendored
BIN
docs/static/fonts/overpass/Overpass-Italic.ttf
vendored
Binary file not shown.
BIN
docs/static/fonts/overpass/Overpass.ttf
vendored
BIN
docs/static/fonts/overpass/Overpass.ttf
vendored
Binary file not shown.
BIN
docs/static/fonts/overpass/OverpassMono.ttf
vendored
BIN
docs/static/fonts/overpass/OverpassMono.ttf
vendored
Binary file not shown.
13
docs/static/img/download-apk-github.svg
vendored
13
docs/static/img/download-apk-github.svg
vendored
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 14 KiB |
22
docs/static/img/logomark-dark-with-futo.svg
vendored
22
docs/static/img/logomark-dark-with-futo.svg
vendored
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 75 KiB |
21
docs/static/img/logomark-light-with-futo.svg
vendored
21
docs/static/img/logomark-light-with-futo.svg
vendored
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 75 KiB |
@@ -34,11 +34,11 @@ services:
|
|||||||
- 2285:2285
|
- 2285:2285
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
||||||
|
|
||||||
database:
|
database:
|
||||||
image: tensorchord/vchord-postgres:pg14-v0.3.0
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
command: -c fsync=off -c shared_preload_libraries=vchord.so
|
command: -c fsync=off -c shared_preload_libraries=vectors.so
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
|
|||||||
3283
e2e/package-lock.json
generated
3283
e2e/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.132.3",
|
"version": "1.131.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@playwright/test": "^1.44.1",
|
"@playwright/test": "^1.44.1",
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.14.0",
|
||||||
"@types/oidc-provider": "^8.5.1",
|
"@types/oidc-provider": "^8.5.1",
|
||||||
"@types/pg": "^8.11.0",
|
"@types/pg": "^8.11.0",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
|
|||||||
@@ -46,6 +46,38 @@ describe('/activities', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /activities', () => {
|
describe('GET /activities', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/activities');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require an albumId', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/activities')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toEqual(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject an invalid albumId', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/activities')
|
||||||
|
.query({ albumId: uuidDto.invalid })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toEqual(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject an invalid assetId', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/activities')
|
||||||
|
.query({ albumId: uuidDto.notFound, assetId: uuidDto.invalid })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toEqual(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['assetId must be a UUID'])));
|
||||||
|
});
|
||||||
|
|
||||||
it('should start off empty', async () => {
|
it('should start off empty', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/activities')
|
.get('/activities')
|
||||||
@@ -160,6 +192,30 @@ describe('/activities', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /activities', () => {
|
describe('POST /activities', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).post('/activities');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require an albumId', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/activities')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send({ albumId: uuidDto.invalid });
|
||||||
|
expect(status).toEqual(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require a comment when type is comment', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/activities')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send({ albumId: uuidDto.notFound, type: 'comment', comment: null });
|
||||||
|
expect(status).toEqual(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(['comment must be a string', 'comment should not be empty']));
|
||||||
|
});
|
||||||
|
|
||||||
it('should add a comment to an album', async () => {
|
it('should add a comment to an album', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post('/activities')
|
.post('/activities')
|
||||||
@@ -274,6 +330,20 @@ describe('/activities', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /activities/:id', () => {
|
describe('DELETE /activities/:id', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).delete(`/activities/${uuidDto.notFound}`);
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require a valid uuid', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.delete(`/activities/${uuidDto.invalid}`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||||
|
});
|
||||||
|
|
||||||
it('should remove a comment from an album', async () => {
|
it('should remove a comment from an album', async () => {
|
||||||
const reaction = await createActivity({
|
const reaction = await createActivity({
|
||||||
albumId: album.id,
|
albumId: album.id,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
LoginResponseDto,
|
LoginResponseDto,
|
||||||
SharedLinkType,
|
SharedLinkType,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { createUserDto } from 'src/fixtures';
|
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, asBearerAuth, utils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
@@ -128,6 +128,28 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /albums', () => {
|
describe('GET /albums', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/albums');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject an invalid shared param', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/albums?shared=invalid')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
expect(status).toEqual(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(['shared must be a boolean value']));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject an invalid assetId param', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/albums?assetId=invalid')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
expect(status).toEqual(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(['assetId must be a UUID']));
|
||||||
|
});
|
||||||
|
|
||||||
it("should not show other users' favorites", async () => {
|
it("should not show other users' favorites", async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
|
.get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
|
||||||
@@ -301,6 +323,12 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /albums/:id', () => {
|
describe('GET /albums/:id', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get(`/albums/${user1Albums[0].id}`);
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return album info for own album', async () => {
|
it('should return album info for own album', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
|
.get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
|
||||||
@@ -393,6 +421,12 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /albums/statistics', () => {
|
describe('GET /albums/statistics', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/albums/statistics');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return total count of albums the user has access to', async () => {
|
it('should return total count of albums the user has access to', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/albums/statistics')
|
.get('/albums/statistics')
|
||||||
@@ -404,6 +438,12 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /albums', () => {
|
describe('POST /albums', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).post('/albums').send({ albumName: 'New album' });
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should create an album', async () => {
|
it('should create an album', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post('/albums')
|
.post('/albums')
|
||||||
@@ -431,6 +471,12 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /albums/:id/assets', () => {
|
describe('PUT /albums/:id/assets', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).put(`/albums/${user1Albums[0].id}/assets`);
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should be able to add own asset to own album', async () => {
|
it('should be able to add own asset to own album', async () => {
|
||||||
const asset = await utils.createAsset(user1.accessToken);
|
const asset = await utils.createAsset(user1.accessToken);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@@ -480,6 +526,14 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PATCH /albums/:id', () => {
|
describe('PATCH /albums/:id', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.patch(`/albums/${uuidDto.notFound}`)
|
||||||
|
.send({ albumName: 'New album name' });
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should update an album', async () => {
|
it('should update an album', async () => {
|
||||||
const album = await utils.createAlbum(user1.accessToken, {
|
const album = await utils.createAlbum(user1.accessToken, {
|
||||||
albumName: 'New album',
|
albumName: 'New album',
|
||||||
@@ -522,6 +576,15 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /albums/:id/assets', () => {
|
describe('DELETE /albums/:id/assets', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.delete(`/albums/${user1Albums[0].id}/assets`)
|
||||||
|
.send({ ids: [user1Asset1.id] });
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should require authorization', async () => {
|
it('should require authorization', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/albums/${user1Albums[1].id}/assets`)
|
.delete(`/albums/${user1Albums[1].id}/assets`)
|
||||||
@@ -616,6 +679,13 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).put(`/albums/${user1Albums[0].id}/users`).send({ sharedUserIds: [] });
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should be able to add user to own album', async () => {
|
it('should be able to add user to own album', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/albums/${album.id}/users`)
|
.put(`/albums/${album.id}/users`)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { LoginResponseDto, Permission, createApiKey } from '@immich/sdk';
|
import { LoginResponseDto, Permission, createApiKey } from '@immich/sdk';
|
||||||
import { createUserDto } from 'src/fixtures';
|
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, asBearerAuth, utils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
@@ -24,6 +24,12 @@ describe('/api-keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /api-keys', () => {
|
describe('POST /api-keys', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).post('/api-keys').send({ name: 'API Key' });
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should not work without permission', async () => {
|
it('should not work without permission', async () => {
|
||||||
const { secret } = await create(user.accessToken, [Permission.ApiKeyRead]);
|
const { secret } = await create(user.accessToken, [Permission.ApiKeyRead]);
|
||||||
const { status, body } = await request(app).post('/api-keys').set('x-api-key', secret).send({ name: 'API Key' });
|
const { status, body } = await request(app).post('/api-keys').set('x-api-key', secret).send({ name: 'API Key' });
|
||||||
@@ -93,6 +99,12 @@ describe('/api-keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /api-keys', () => {
|
describe('GET /api-keys', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/api-keys');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should start off empty', async () => {
|
it('should start off empty', async () => {
|
||||||
const { status, body } = await request(app).get('/api-keys').set('Authorization', `Bearer ${admin.accessToken}`);
|
const { status, body } = await request(app).get('/api-keys').set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
expect(body).toEqual([]);
|
expect(body).toEqual([]);
|
||||||
@@ -113,6 +125,12 @@ describe('/api-keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /api-keys/:id', () => {
|
describe('GET /api-keys/:id', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get(`/api-keys/${uuidDto.notFound}`);
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should require authorization', async () => {
|
it('should require authorization', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@@ -122,6 +140,14 @@ describe('/api-keys', () => {
|
|||||||
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should require a valid uuid', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get(`/api-keys/${uuidDto.invalid}`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||||
|
});
|
||||||
|
|
||||||
it('should get api key details', async () => {
|
it('should get api key details', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@@ -139,6 +165,12 @@ describe('/api-keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /api-keys/:id', () => {
|
describe('PUT /api-keys/:id', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).put(`/api-keys/${uuidDto.notFound}`).send({ name: 'new name' });
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should require authorization', async () => {
|
it('should require authorization', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@@ -149,6 +181,15 @@ describe('/api-keys', () => {
|
|||||||
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should require a valid uuid', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.put(`/api-keys/${uuidDto.invalid}`)
|
||||||
|
.send({ name: 'new name' })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||||
|
});
|
||||||
|
|
||||||
it('should update api key details', async () => {
|
it('should update api key details', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@@ -167,6 +208,12 @@ describe('/api-keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /api-keys/:id', () => {
|
describe('DELETE /api-keys/:id', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).delete(`/api-keys/${uuidDto.notFound}`);
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should require authorization', async () => {
|
it('should require authorization', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@@ -176,6 +223,14 @@ describe('/api-keys', () => {
|
|||||||
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should require a valid uuid', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.delete(`/api-keys/${uuidDto.invalid}`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||||
|
});
|
||||||
|
|
||||||
it('should delete an api key', async () => {
|
it('should delete an api key', async () => {
|
||||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||||
const { status } = await request(app)
|
const { status } = await request(app)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
AssetMediaStatus,
|
AssetMediaStatus,
|
||||||
AssetResponseDto,
|
AssetResponseDto,
|
||||||
AssetTypeEnum,
|
AssetTypeEnum,
|
||||||
AssetVisibility,
|
|
||||||
getAssetInfo,
|
getAssetInfo,
|
||||||
getMyUser,
|
getMyUser,
|
||||||
LoginResponseDto,
|
LoginResponseDto,
|
||||||
@@ -23,9 +22,27 @@ import { app, asBearerAuth, tempDir, TEN_TIMES, testAssetDir, utils } from 'src/
|
|||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
const makeUploadDto = (options?: { omit: string }): Record<string, any> => {
|
||||||
|
const dto: Record<string, any> = {
|
||||||
|
deviceAssetId: 'example-image',
|
||||||
|
deviceId: 'TEST',
|
||||||
|
fileCreatedAt: new Date().toISOString(),
|
||||||
|
fileModifiedAt: new Date().toISOString(),
|
||||||
|
isFavorite: 'testing',
|
||||||
|
duration: '0:00:00.000000',
|
||||||
|
};
|
||||||
|
|
||||||
|
const omit = options?.omit;
|
||||||
|
if (omit) {
|
||||||
|
delete dto[omit];
|
||||||
|
}
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
};
|
||||||
|
|
||||||
const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
||||||
const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`;
|
const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`;
|
||||||
const facesAssetDir = `${testAssetDir}/metadata/faces`;
|
const facesAssetFilepath = `${testAssetDir}/metadata/faces/portrait.jpg`;
|
||||||
|
|
||||||
const readTags = async (bytes: Buffer, filename: string) => {
|
const readTags = async (bytes: Buffer, filename: string) => {
|
||||||
const filepath = join(tempDir, filename);
|
const filepath = join(tempDir, filename);
|
||||||
@@ -120,9 +137,9 @@ describe('/asset', () => {
|
|||||||
// stats
|
// stats
|
||||||
utils.createAsset(statsUser.accessToken),
|
utils.createAsset(statsUser.accessToken),
|
||||||
utils.createAsset(statsUser.accessToken, { isFavorite: true }),
|
utils.createAsset(statsUser.accessToken, { isFavorite: true }),
|
||||||
utils.createAsset(statsUser.accessToken, { visibility: AssetVisibility.Archive }),
|
utils.createAsset(statsUser.accessToken, { isArchived: true }),
|
||||||
utils.createAsset(statsUser.accessToken, {
|
utils.createAsset(statsUser.accessToken, {
|
||||||
visibility: AssetVisibility.Archive,
|
isArchived: true,
|
||||||
isFavorite: true,
|
isFavorite: true,
|
||||||
assetData: { filename: 'example.mp4' },
|
assetData: { filename: 'example.mp4' },
|
||||||
}),
|
}),
|
||||||
@@ -143,6 +160,13 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /assets/:id/original', () => {
|
describe('GET /assets/:id/original', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get(`/assets/${uuidDto.notFound}/original`);
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should download the file', async () => {
|
it('should download the file', async () => {
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.get(`/assets/${user1Assets[0].id}/original`)
|
.get(`/assets/${user1Assets[0].id}/original`)
|
||||||
@@ -154,6 +178,20 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /assets/:id', () => {
|
describe('GET /assets/:id', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get(`/assets/${uuidDto.notFound}`);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
expect(status).toBe(401);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require a valid id', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get(`/assets/${uuidDto.invalid}`)
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||||
|
});
|
||||||
|
|
||||||
it('should require access', async () => {
|
it('should require access', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/assets/${user2Assets[0].id}`)
|
.get(`/assets/${user2Assets[0].id}`)
|
||||||
@@ -186,19 +224,27 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('faces', () => {
|
it('should get the asset faces', async () => {
|
||||||
const metadataFaceTests = [
|
const config = await utils.getSystemConfig(admin.accessToken);
|
||||||
{
|
config.metadata.faces.import = true;
|
||||||
description: 'without orientation',
|
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
// asset faces
|
||||||
|
const facesAsset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
filename: 'portrait.jpg',
|
filename: 'portrait.jpg',
|
||||||
|
bytes: await readFile(facesAssetFilepath),
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
description: 'adjusting face regions to orientation',
|
|
||||||
filename: 'portrait-orientation-6.jpg',
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: facesAsset.id });
|
||||||
},
|
|
||||||
];
|
const { status, body } = await request(app)
|
||||||
// should produce same resulting face region coordinates for any orientation
|
.get(`/assets/${facesAsset.id}`)
|
||||||
const expectedFaces = [
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.id).toEqual(facesAsset.id);
|
||||||
|
expect(body.people).toMatchObject([
|
||||||
{
|
{
|
||||||
name: 'Marie Curie',
|
name: 'Marie Curie',
|
||||||
birthDate: null,
|
birthDate: null,
|
||||||
@@ -233,30 +279,7 @@ describe('/asset', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
|
|
||||||
it.each(metadataFaceTests)('should get the asset faces from $filename $description', async ({ filename }) => {
|
|
||||||
const config = await utils.getSystemConfig(admin.accessToken);
|
|
||||||
config.metadata.faces.import = true;
|
|
||||||
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
|
||||||
|
|
||||||
const facesAsset = await utils.createAsset(admin.accessToken, {
|
|
||||||
assetData: {
|
|
||||||
filename,
|
|
||||||
bytes: await readFile(`${facesAssetDir}/${filename}`),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: facesAsset.id });
|
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get(`/assets/${facesAsset.id}`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body.id).toEqual(facesAsset.id);
|
|
||||||
expect(body.people).toMatchObject(expectedFaces);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with a shared link', async () => {
|
it('should work with a shared link', async () => {
|
||||||
@@ -310,7 +333,7 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('disallows viewing archived assets', async () => {
|
it('disallows viewing archived assets', async () => {
|
||||||
const asset = await utils.createAsset(user1.accessToken, { visibility: AssetVisibility.Archive });
|
const asset = await utils.createAsset(user1.accessToken, { isArchived: true });
|
||||||
|
|
||||||
const { status } = await request(app)
|
const { status } = await request(app)
|
||||||
.get(`/assets/${asset.id}`)
|
.get(`/assets/${asset.id}`)
|
||||||
@@ -331,6 +354,13 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /assets/statistics', () => {
|
describe('GET /assets/statistics', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/assets/statistics');
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return stats of all assets', async () => {
|
it('should return stats of all assets', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/assets/statistics')
|
.get('/assets/statistics')
|
||||||
@@ -354,7 +384,7 @@ describe('/asset', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/assets/statistics')
|
.get('/assets/statistics')
|
||||||
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
||||||
.query({ visibility: AssetVisibility.Archive });
|
.query({ isArchived: true });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({ images: 1, videos: 1, total: 2 });
|
expect(body).toEqual({ images: 1, videos: 1, total: 2 });
|
||||||
@@ -364,7 +394,7 @@ describe('/asset', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/assets/statistics')
|
.get('/assets/statistics')
|
||||||
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
||||||
.query({ isFavorite: true, visibility: AssetVisibility.Archive });
|
.query({ isFavorite: true, isArchived: true });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({ images: 0, videos: 1, total: 1 });
|
expect(body).toEqual({ images: 0, videos: 1, total: 1 });
|
||||||
@@ -374,7 +404,7 @@ describe('/asset', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/assets/statistics')
|
.get('/assets/statistics')
|
||||||
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
||||||
.query({ isFavorite: false, visibility: AssetVisibility.Timeline });
|
.query({ isFavorite: false, isArchived: false });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({ images: 1, videos: 0, total: 1 });
|
expect(body).toEqual({ images: 1, videos: 0, total: 1 });
|
||||||
@@ -395,6 +425,13 @@ describe('/asset', () => {
|
|||||||
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/assets/random');
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it.each(TEN_TIMES)('should return 1 random assets', async () => {
|
it.each(TEN_TIMES)('should return 1 random assets', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/assets/random')
|
.get('/assets/random')
|
||||||
@@ -430,9 +467,31 @@ describe('/asset', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual([expect.objectContaining({ id: user2Assets[0].id })]);
|
expect(body).toEqual([expect.objectContaining({ id: user2Assets[0].id })]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return error', async () => {
|
||||||
|
const { status } = await request(app)
|
||||||
|
.get('/assets/random?count=ABC')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(400);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /assets/:id', () => {
|
describe('PUT /assets/:id', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).put(`/assets/:${uuidDto.notFound}`);
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require a valid id', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.put(`/assets/${uuidDto.invalid}`)
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||||
|
});
|
||||||
|
|
||||||
it('should require access', async () => {
|
it('should require access', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/assets/${user2Assets[0].id}`)
|
.put(`/assets/${user2Assets[0].id}`)
|
||||||
@@ -460,7 +519,7 @@ describe('/asset', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/assets/${user1Assets[0].id}`)
|
.put(`/assets/${user1Assets[0].id}`)
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
.send({ visibility: AssetVisibility.Archive });
|
.send({ isArchived: true });
|
||||||
expect(body).toMatchObject({ id: user1Assets[0].id, isArchived: true });
|
expect(body).toMatchObject({ id: user1Assets[0].id, isArchived: true });
|
||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
});
|
});
|
||||||
@@ -560,6 +619,28 @@ describe('/asset', () => {
|
|||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should reject invalid gps coordinates', async () => {
|
||||||
|
for (const test of [
|
||||||
|
{ latitude: 12 },
|
||||||
|
{ longitude: 12 },
|
||||||
|
{ latitude: 12, longitude: 'abc' },
|
||||||
|
{ latitude: 'abc', longitude: 12 },
|
||||||
|
{ latitude: null, longitude: 12 },
|
||||||
|
{ latitude: 12, longitude: null },
|
||||||
|
{ latitude: 91, longitude: 12 },
|
||||||
|
{ latitude: -91, longitude: 12 },
|
||||||
|
{ latitude: 12, longitude: -181 },
|
||||||
|
{ latitude: 12, longitude: 181 },
|
||||||
|
]) {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.put(`/assets/${user1Assets[0].id}`)
|
||||||
|
.send(test)
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('should update gps data', async () => {
|
it('should update gps data', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/assets/${user1Assets[0].id}`)
|
.put(`/assets/${user1Assets[0].id}`)
|
||||||
@@ -631,6 +712,17 @@ describe('/asset', () => {
|
|||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should reject invalid rating', async () => {
|
||||||
|
for (const test of [{ rating: 7 }, { rating: 3.5 }, { rating: null }]) {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.put(`/assets/${user1Assets[0].id}`)
|
||||||
|
.send(test)
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('should return tagged people', async () => {
|
it('should return tagged people', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/assets/${user1Assets[0].id}`)
|
.put(`/assets/${user1Assets[0].id}`)
|
||||||
@@ -654,6 +746,25 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /assets', () => {
|
describe('DELETE /assets', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.delete(`/assets`)
|
||||||
|
.send({ ids: [uuidDto.notFound] });
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require a valid uuid', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.delete(`/assets`)
|
||||||
|
.send({ ids: [uuidDto.invalid] })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(['each value in ids must be a UUID']));
|
||||||
|
});
|
||||||
|
|
||||||
it('should throw an error when the id is not found', async () => {
|
it('should throw an error when the id is not found', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/assets`)
|
.delete(`/assets`)
|
||||||
@@ -766,6 +877,13 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /assets/:id/thumbnail', () => {
|
describe('GET /assets/:id/thumbnail', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get(`/assets/${locationAsset.id}/thumbnail`);
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should not include gps data for webp thumbnails', async () => {
|
it('should not include gps data for webp thumbnails', async () => {
|
||||||
await utils.waitForWebsocketEvent({
|
await utils.waitForWebsocketEvent({
|
||||||
event: 'assetUpload',
|
event: 'assetUpload',
|
||||||
@@ -801,6 +919,13 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /assets/:id/original', () => {
|
describe('GET /assets/:id/original', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get(`/assets/${locationAsset.id}/original`);
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should download the original', async () => {
|
it('should download the original', async () => {
|
||||||
const { status, body, type } = await request(app)
|
const { status, body, type } = await request(app)
|
||||||
.get(`/assets/${locationAsset.id}/original`)
|
.get(`/assets/${locationAsset.id}/original`)
|
||||||
@@ -821,9 +946,43 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('PUT /assets', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).put('/assets');
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('POST /assets', () => {
|
describe('POST /assets', () => {
|
||||||
beforeAll(setupTests, 30_000);
|
beforeAll(setupTests, 30_000);
|
||||||
|
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).post(`/assets`);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
expect(status).toBe(401);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ should: 'require `deviceAssetId`', dto: { ...makeUploadDto({ omit: 'deviceAssetId' }) } },
|
||||||
|
{ should: 'require `deviceId`', dto: { ...makeUploadDto({ omit: 'deviceId' }) } },
|
||||||
|
{ should: 'require `fileCreatedAt`', dto: { ...makeUploadDto({ omit: 'fileCreatedAt' }) } },
|
||||||
|
{ should: 'require `fileModifiedAt`', dto: { ...makeUploadDto({ omit: 'fileModifiedAt' }) } },
|
||||||
|
{ should: 'require `duration`', dto: { ...makeUploadDto({ omit: 'duration' }) } },
|
||||||
|
{ should: 'throw if `isFavorite` is not a boolean', dto: { ...makeUploadDto(), isFavorite: 'not-a-boolean' } },
|
||||||
|
{ should: 'throw if `isVisible` is not a boolean', dto: { ...makeUploadDto(), isVisible: 'not-a-boolean' } },
|
||||||
|
{ should: 'throw if `isArchived` is not a boolean', dto: { ...makeUploadDto(), isArchived: 'not-a-boolean' } },
|
||||||
|
])('should $should', async ({ dto }) => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/assets')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.attach('assetData', makeRandomImage(), 'example.png')
|
||||||
|
.field(dto);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest());
|
||||||
|
});
|
||||||
|
|
||||||
const tests = [
|
const tests = [
|
||||||
{
|
{
|
||||||
input: 'formats/avif/8bit-sRGB.avif',
|
input: 'formats/avif/8bit-sRGB.avif',
|
||||||
@@ -1085,21 +1244,31 @@ describe('/asset', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
it.each(tests)(`should upload and generate a thumbnail for different file types`, async ({ input, expected }) => {
|
it(`should upload and generate a thumbnail for different file types`, async () => {
|
||||||
const filepath = join(testAssetDir, input);
|
// upload in parallel
|
||||||
const response = await utils.createAsset(admin.accessToken, {
|
const assets = await Promise.all(
|
||||||
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
tests.map(async ({ input }) => {
|
||||||
});
|
const filepath = join(testAssetDir, input);
|
||||||
|
return utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
expect(response.status).toBe(AssetMediaStatus.Created);
|
for (const { id, status } of assets) {
|
||||||
const id = response.id;
|
expect(status).toBe(AssetMediaStatus.Created);
|
||||||
// longer timeout as the thumbnail generation from full-size raw files can take a while
|
// longer timeout as the thumbnail generation from full-size raw files can take a while
|
||||||
await utils.waitForWebsocketEvent({ event: 'assetUpload', id });
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id });
|
||||||
|
}
|
||||||
|
|
||||||
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
for (const [i, { id }] of assets.entries()) {
|
||||||
expect(asset.exifInfo).toBeDefined();
|
const { expected } = tests[i];
|
||||||
expect(asset.exifInfo).toMatchObject(expected.exifInfo);
|
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
expect(asset).toMatchObject(expected);
|
|
||||||
|
expect(asset.exifInfo).toBeDefined();
|
||||||
|
expect(asset.exifInfo).toMatchObject(expected.exifInfo);
|
||||||
|
expect(asset).toMatchObject(expected);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle a duplicate', async () => {
|
it('should handle a duplicate', async () => {
|
||||||
|
|||||||
43
e2e/src/api/specs/audit.e2e-spec.ts
Normal file
43
e2e/src/api/specs/audit.e2e-spec.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { deleteAssets, getAuditFiles, updateAsset, type LoginResponseDto } from '@immich/sdk';
|
||||||
|
import { asBearerAuth, utils } from 'src/utils';
|
||||||
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
describe('/audits', () => {
|
||||||
|
let admin: LoginResponseDto;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await utils.resetDatabase();
|
||||||
|
await utils.resetFilesystem();
|
||||||
|
|
||||||
|
admin = await utils.adminSetup();
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Enable these tests again once #7436 is resolved as these were flaky
|
||||||
|
describe.skip('GET :/file-report', () => {
|
||||||
|
it('excludes assets without issues from report', async () => {
|
||||||
|
const [trashedAsset, archivedAsset] = await Promise.all([
|
||||||
|
utils.createAsset(admin.accessToken),
|
||||||
|
utils.createAsset(admin.accessToken),
|
||||||
|
utils.createAsset(admin.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
deleteAssets({ assetBulkDeleteDto: { ids: [trashedAsset.id] } }, { headers: asBearerAuth(admin.accessToken) }),
|
||||||
|
updateAsset(
|
||||||
|
{
|
||||||
|
id: archivedAsset.id,
|
||||||
|
updateAssetDto: { isArchived: true },
|
||||||
|
},
|
||||||
|
{ headers: asBearerAuth(admin.accessToken) },
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const body = await getAuditFiles({
|
||||||
|
headers: asBearerAuth(admin.accessToken),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(body.orphans).toHaveLength(0);
|
||||||
|
expect(body.extras).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -5,7 +5,7 @@ import { app, utils } from 'src/utils';
|
|||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeEach, describe, expect, it } from 'vitest';
|
import { beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
const { email, password } = signupDto.admin;
|
const { name, email, password } = signupDto.admin;
|
||||||
|
|
||||||
describe(`/auth/admin-sign-up`, () => {
|
describe(`/auth/admin-sign-up`, () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -13,12 +13,58 @@ describe(`/auth/admin-sign-up`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /auth/admin-sign-up', () => {
|
describe('POST /auth/admin-sign-up', () => {
|
||||||
|
const invalid = [
|
||||||
|
{
|
||||||
|
should: 'require an email address',
|
||||||
|
data: { name, password },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
should: 'require a password',
|
||||||
|
data: { name, email },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
should: 'require a name',
|
||||||
|
data: { email, password },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
should: 'require a valid email',
|
||||||
|
data: { name, email: 'immich', password },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { should, data } of invalid) {
|
||||||
|
it(`should ${should}`, async () => {
|
||||||
|
const { status, body } = await request(app).post('/auth/admin-sign-up').send(data);
|
||||||
|
expect(status).toEqual(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
it(`should sign up the admin`, async () => {
|
it(`should sign up the admin`, async () => {
|
||||||
const { status, body } = await request(app).post('/auth/admin-sign-up').send(signupDto.admin);
|
const { status, body } = await request(app).post('/auth/admin-sign-up').send(signupDto.admin);
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toEqual(signupResponseDto.admin);
|
expect(body).toEqual(signupResponseDto.admin);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should sign up the admin with a local domain', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/auth/admin-sign-up')
|
||||||
|
.send({ ...signupDto.admin, email: 'admin@local' });
|
||||||
|
expect(status).toEqual(201);
|
||||||
|
expect(body).toEqual({
|
||||||
|
...signupResponseDto.admin,
|
||||||
|
email: 'admin@local',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should transform email to lower case', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/auth/admin-sign-up')
|
||||||
|
.send({ ...signupDto.admin, email: 'aDmIn@IMMICH.cloud' });
|
||||||
|
expect(status).toEqual(201);
|
||||||
|
expect(body).toEqual(signupResponseDto.admin);
|
||||||
|
});
|
||||||
|
|
||||||
it('should not allow a second admin to sign up', async () => {
|
it('should not allow a second admin to sign up', async () => {
|
||||||
await signUpAdmin({ signUpDto: signupDto.admin });
|
await signUpAdmin({ signUpDto: signupDto.admin });
|
||||||
|
|
||||||
@@ -46,6 +92,22 @@ describe('/auth/*', () => {
|
|||||||
expect(body).toEqual(errorDto.incorrectLogin);
|
expect(body).toEqual(errorDto.incorrectLogin);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (const key of Object.keys(loginDto.admin)) {
|
||||||
|
it(`should not allow null ${key}`, async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/auth/login')
|
||||||
|
.send({ ...loginDto.admin, [key]: null });
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject an invalid email', async () => {
|
||||||
|
const { status, body } = await request(app).post('/auth/login').send({ email: [], password });
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.invalidEmail);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
it('should accept a correct password', async () => {
|
it('should accept a correct password', async () => {
|
||||||
const { status, body, headers } = await request(app).post('/auth/login').send({ email, password });
|
const { status, body, headers } = await request(app).post('/auth/login').send({ email, password });
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
@@ -100,6 +162,14 @@ describe('/auth/*', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /auth/change-password', () => {
|
describe('POST /auth/change-password', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post(`/auth/change-password`)
|
||||||
|
.send({ password, newPassword: 'Password1234' });
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should require the current password', async () => {
|
it('should require the current password', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post(`/auth/change-password`)
|
.post(`/auth/change-password`)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
|
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
|
||||||
import { readFile, writeFile } from 'node:fs/promises';
|
import { readFile, writeFile } from 'node:fs/promises';
|
||||||
|
import { errorDto } from 'src/responses';
|
||||||
import { app, tempDir, utils } from 'src/utils';
|
import { app, tempDir, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
@@ -16,6 +17,15 @@ describe('/download', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /download/info', () => {
|
describe('POST /download/info', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post(`/download/info`)
|
||||||
|
.send({ assetIds: [asset1.id] });
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should download info', async () => {
|
it('should download info', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post('/download/info')
|
.post('/download/info')
|
||||||
@@ -32,6 +42,15 @@ describe('/download', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /download/archive', () => {
|
describe('POST /download/archive', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post(`/download/archive`)
|
||||||
|
.send({ assetIds: [asset1.id, asset2.id] });
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should download an archive', async () => {
|
it('should download an archive', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post('/download/archive')
|
.post('/download/archive')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AssetVisibility, LoginResponseDto } from '@immich/sdk';
|
import { LoginResponseDto } from '@immich/sdk';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { basename, join } from 'node:path';
|
import { basename, join } from 'node:path';
|
||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
@@ -44,7 +44,7 @@ describe('/map', () => {
|
|||||||
it('should get map markers for all non-archived assets', async () => {
|
it('should get map markers for all non-archived assets', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/map/markers')
|
.get('/map/markers')
|
||||||
.query({ visibility: AssetVisibility.Timeline })
|
.query({ isArchived: false })
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
startOAuth,
|
startOAuth,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { createHash, randomBytes } from 'node:crypto';
|
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { OAuthClient, OAuthUser } from 'src/setup/auth-server';
|
import { OAuthClient, OAuthUser } from 'src/setup/auth-server';
|
||||||
import { app, asBearerAuth, baseUrl, utils } from 'src/utils';
|
import { app, asBearerAuth, baseUrl, utils } from 'src/utils';
|
||||||
@@ -22,30 +21,18 @@ const mobileOverrideRedirectUri = 'https://photos.immich.app/oauth/mobile-redire
|
|||||||
|
|
||||||
const redirect = async (url: string, cookies?: string[]) => {
|
const redirect = async (url: string, cookies?: string[]) => {
|
||||||
const { headers } = await request(url)
|
const { headers } = await request(url)
|
||||||
.get('')
|
.get('/')
|
||||||
.set('Cookie', cookies || []);
|
.set('Cookie', cookies || []);
|
||||||
return { cookies: (headers['set-cookie'] as unknown as string[]) || [], location: headers.location };
|
return { cookies: (headers['set-cookie'] as unknown as string[]) || [], location: headers.location };
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to generate a code challenge from the verifier
|
|
||||||
const generateCodeChallenge = async (codeVerifier: string): Promise<string> => {
|
|
||||||
const hashed = createHash('sha256').update(codeVerifier).digest();
|
|
||||||
return hashed.toString('base64url');
|
|
||||||
};
|
|
||||||
|
|
||||||
const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) => {
|
const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) => {
|
||||||
const state = randomBytes(16).toString('base64url');
|
const { url } = await startOAuth({ oAuthConfigDto: { redirectUri: redirectUri ?? `${baseUrl}/auth/login` } });
|
||||||
const codeVerifier = randomBytes(64).toString('base64url');
|
|
||||||
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
||||||
|
|
||||||
const { url } = await startOAuth({
|
|
||||||
oAuthConfigDto: { redirectUri: redirectUri ?? `${baseUrl}/auth/login`, state, codeChallenge },
|
|
||||||
});
|
|
||||||
|
|
||||||
// login
|
// login
|
||||||
const response1 = await redirect(url.replace(authServer.internal, authServer.external));
|
const response1 = await redirect(url.replace(authServer.internal, authServer.external));
|
||||||
const response2 = await request(authServer.external + response1.location)
|
const response2 = await request(authServer.external + response1.location)
|
||||||
.post('')
|
.post('/')
|
||||||
.set('Cookie', response1.cookies)
|
.set('Cookie', response1.cookies)
|
||||||
.type('form')
|
.type('form')
|
||||||
.send({ prompt: 'login', login: sub, password: 'password' });
|
.send({ prompt: 'login', login: sub, password: 'password' });
|
||||||
@@ -53,7 +40,7 @@ const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) =>
|
|||||||
// approve
|
// approve
|
||||||
const response3 = await redirect(response2.header.location, response1.cookies);
|
const response3 = await redirect(response2.header.location, response1.cookies);
|
||||||
const response4 = await request(authServer.external + response3.location)
|
const response4 = await request(authServer.external + response3.location)
|
||||||
.post('')
|
.post('/')
|
||||||
.type('form')
|
.type('form')
|
||||||
.set('Cookie', response3.cookies)
|
.set('Cookie', response3.cookies)
|
||||||
.send({ prompt: 'consent' });
|
.send({ prompt: 'consent' });
|
||||||
@@ -64,9 +51,9 @@ const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) =>
|
|||||||
expect(redirectUrl).toBeDefined();
|
expect(redirectUrl).toBeDefined();
|
||||||
const params = new URL(redirectUrl).searchParams;
|
const params = new URL(redirectUrl).searchParams;
|
||||||
expect(params.get('code')).toBeDefined();
|
expect(params.get('code')).toBeDefined();
|
||||||
expect(params.get('state')).toBe(state);
|
expect(params.get('state')).toBeDefined();
|
||||||
|
|
||||||
return { url: redirectUrl, state, codeVerifier };
|
return redirectUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
const setupOAuth = async (token: string, dto: Partial<SystemConfigOAuthDto>) => {
|
const setupOAuth = async (token: string, dto: Partial<SystemConfigOAuthDto>) => {
|
||||||
@@ -132,42 +119,9 @@ describe(`/oauth`, () => {
|
|||||||
expect(body).toEqual(errorDto.badRequest(['url should not be empty']));
|
expect(body).toEqual(errorDto.badRequest(['url should not be empty']));
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should throw an error if the state is not provided`, async () => {
|
|
||||||
const { url } = await loginWithOAuth('oauth-auto-register');
|
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest('OAuth state is missing'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should throw an error if the state mismatches`, async () => {
|
|
||||||
const callbackParams = await loginWithOAuth('oauth-auto-register');
|
|
||||||
const { state } = await loginWithOAuth('oauth-auto-register');
|
|
||||||
const { status } = await request(app)
|
|
||||||
.post('/oauth/callback')
|
|
||||||
.send({ ...callbackParams, state });
|
|
||||||
expect(status).toBeGreaterThanOrEqual(400);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should throw an error if the codeVerifier is not provided`, async () => {
|
|
||||||
const { url, state } = await loginWithOAuth('oauth-auto-register');
|
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url, state });
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest('OAuth code verifier is missing'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should throw an error if the codeVerifier doesn't match the challenge`, async () => {
|
|
||||||
const callbackParams = await loginWithOAuth('oauth-auto-register');
|
|
||||||
const { codeVerifier } = await loginWithOAuth('oauth-auto-register');
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/oauth/callback')
|
|
||||||
.send({ ...callbackParams, codeVerifier });
|
|
||||||
console.log(body);
|
|
||||||
expect(status).toBeGreaterThanOrEqual(400);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should auto register the user by default', async () => {
|
it('should auto register the user by default', async () => {
|
||||||
const callbackParams = await loginWithOAuth('oauth-auto-register');
|
const url = await loginWithOAuth('oauth-auto-register');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
@@ -178,30 +132,16 @@ describe(`/oauth`, () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow passing state and codeVerifier via cookies', async () => {
|
|
||||||
const { url, state, codeVerifier } = await loginWithOAuth('oauth-auto-register');
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/oauth/callback')
|
|
||||||
.set('Cookie', [`immich_oauth_state=${state}`, `immich_oauth_code_verifier=${codeVerifier}`])
|
|
||||||
.send({ url });
|
|
||||||
expect(status).toBe(201);
|
|
||||||
expect(body).toMatchObject({
|
|
||||||
accessToken: expect.any(String),
|
|
||||||
userId: expect.any(String),
|
|
||||||
userEmail: 'oauth-auto-register@immich.app',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle a user without an email', async () => {
|
it('should handle a user without an email', async () => {
|
||||||
const callbackParams = await loginWithOAuth(OAuthUser.NO_EMAIL);
|
const url = await loginWithOAuth(OAuthUser.NO_EMAIL);
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(errorDto.badRequest('OAuth profile does not have an email address'));
|
expect(body).toEqual(errorDto.badRequest('OAuth profile does not have an email address'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the quota from a claim', async () => {
|
it('should set the quota from a claim', async () => {
|
||||||
const callbackParams = await loginWithOAuth(OAuthUser.WITH_QUOTA);
|
const url = await loginWithOAuth(OAuthUser.WITH_QUOTA);
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
@@ -214,8 +154,8 @@ describe(`/oauth`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should set the storage label from a claim', async () => {
|
it('should set the storage label from a claim', async () => {
|
||||||
const callbackParams = await loginWithOAuth(OAuthUser.WITH_USERNAME);
|
const url = await loginWithOAuth(OAuthUser.WITH_USERNAME);
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
@@ -236,8 +176,8 @@ describe(`/oauth`, () => {
|
|||||||
buttonText: 'Login with Immich',
|
buttonText: 'Login with Immich',
|
||||||
signingAlgorithm: 'RS256',
|
signingAlgorithm: 'RS256',
|
||||||
});
|
});
|
||||||
const callbackParams = await loginWithOAuth('oauth-RS256-token');
|
const url = await loginWithOAuth('oauth-RS256-token');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
@@ -256,8 +196,8 @@ describe(`/oauth`, () => {
|
|||||||
buttonText: 'Login with Immich',
|
buttonText: 'Login with Immich',
|
||||||
profileSigningAlgorithm: 'RS256',
|
profileSigningAlgorithm: 'RS256',
|
||||||
});
|
});
|
||||||
const callbackParams = await loginWithOAuth('oauth-signed-profile');
|
const url = await loginWithOAuth('oauth-signed-profile');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
userId: expect.any(String),
|
userId: expect.any(String),
|
||||||
@@ -273,8 +213,8 @@ describe(`/oauth`, () => {
|
|||||||
buttonText: 'Login with Immich',
|
buttonText: 'Login with Immich',
|
||||||
signingAlgorithm: 'something-that-does-not-work',
|
signingAlgorithm: 'something-that-does-not-work',
|
||||||
});
|
});
|
||||||
const callbackParams = await loginWithOAuth('oauth-signed-bad');
|
const url = await loginWithOAuth('oauth-signed-bad');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||||
expect(status).toBe(500);
|
expect(status).toBe(500);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
error: 'Internal Server Error',
|
error: 'Internal Server Error',
|
||||||
@@ -295,8 +235,8 @@ describe(`/oauth`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not auto register the user', async () => {
|
it('should not auto register the user', async () => {
|
||||||
const callbackParams = await loginWithOAuth('oauth-no-auto-register');
|
const url = await loginWithOAuth('oauth-no-auto-register');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(errorDto.badRequest('User does not exist and auto registering is disabled.'));
|
expect(body).toEqual(errorDto.badRequest('User does not exist and auto registering is disabled.'));
|
||||||
});
|
});
|
||||||
@@ -307,8 +247,8 @@ describe(`/oauth`, () => {
|
|||||||
email: 'oauth-user3@immich.app',
|
email: 'oauth-user3@immich.app',
|
||||||
password: 'password',
|
password: 'password',
|
||||||
});
|
});
|
||||||
const callbackParams = await loginWithOAuth('oauth-user3');
|
const url = await loginWithOAuth('oauth-user3');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
userId,
|
userId,
|
||||||
@@ -346,15 +286,13 @@ describe(`/oauth`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should auto register the user by default', async () => {
|
it('should auto register the user by default', async () => {
|
||||||
const callbackParams = await loginWithOAuth('oauth-mobile-override', 'app.immich:///oauth-callback');
|
const url = await loginWithOAuth('oauth-mobile-override', 'app.immich:///oauth-callback');
|
||||||
expect(callbackParams.url).toEqual(expect.stringContaining(mobileOverrideRedirectUri));
|
expect(url).toEqual(expect.stringContaining(mobileOverrideRedirectUri));
|
||||||
|
|
||||||
// simulate redirecting back to mobile app
|
// simulate redirecting back to mobile app
|
||||||
const url = callbackParams.url.replace(mobileOverrideRedirectUri, 'app.immich:///oauth-callback');
|
const redirectUri = url.replace(mobileOverrideRedirectUri, 'app.immich:///oauth-callback');
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app).post('/oauth/callback').send({ url: redirectUri });
|
||||||
.post('/oauth/callback')
|
|
||||||
.send({ ...callbackParams, url });
|
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
import {
|
import { AssetMediaResponseDto, AssetResponseDto, deleteAssets, LoginResponseDto, updateAsset } from '@immich/sdk';
|
||||||
AssetMediaResponseDto,
|
|
||||||
AssetResponseDto,
|
|
||||||
AssetVisibility,
|
|
||||||
deleteAssets,
|
|
||||||
LoginResponseDto,
|
|
||||||
updateAsset,
|
|
||||||
} from '@immich/sdk';
|
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
|
import { errorDto } from 'src/responses';
|
||||||
import { app, asBearerAuth, TEN_TIMES, testAssetDir, utils } from 'src/utils';
|
import { app, asBearerAuth, TEN_TIMES, testAssetDir, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||||
@@ -56,7 +50,7 @@ describe('/search', () => {
|
|||||||
{ filename: '/formats/motionphoto/samsung-one-ui-6.heic' },
|
{ filename: '/formats/motionphoto/samsung-one-ui-6.heic' },
|
||||||
{ filename: '/formats/motionphoto/samsung-one-ui-5.jpg' },
|
{ filename: '/formats/motionphoto/samsung-one-ui-5.jpg' },
|
||||||
|
|
||||||
{ filename: '/metadata/gps-position/thompson-springs.jpg', dto: { visibility: AssetVisibility.Archive } },
|
{ filename: '/metadata/gps-position/thompson-springs.jpg', dto: { isArchived: true } },
|
||||||
|
|
||||||
// used for search suggestions
|
// used for search suggestions
|
||||||
{ filename: '/formats/png/density_plot.png' },
|
{ filename: '/formats/png/density_plot.png' },
|
||||||
@@ -147,6 +141,65 @@ describe('/search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /search/metadata', () => {
|
describe('POST /search/metadata', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).post('/search/metadata');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
const badTests = [
|
||||||
|
{
|
||||||
|
should: 'should reject page as a string',
|
||||||
|
dto: { page: 'abc' },
|
||||||
|
expected: ['page must not be less than 1', 'page must be an integer number'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
should: 'should reject page as a decimal',
|
||||||
|
dto: { page: 1.5 },
|
||||||
|
expected: ['page must be an integer number'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
should: 'should reject page as a negative number',
|
||||||
|
dto: { page: -10 },
|
||||||
|
expected: ['page must not be less than 1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
should: 'should reject page as 0',
|
||||||
|
dto: { page: 0 },
|
||||||
|
expected: ['page must not be less than 1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
should: 'should reject size as a string',
|
||||||
|
dto: { size: 'abc' },
|
||||||
|
expected: [
|
||||||
|
'size must not be greater than 1000',
|
||||||
|
'size must not be less than 1',
|
||||||
|
'size must be an integer number',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
should: 'should reject an invalid size',
|
||||||
|
dto: { size: -1.5 },
|
||||||
|
expected: ['size must not be less than 1', 'size must be an integer number'],
|
||||||
|
},
|
||||||
|
...['isArchived', 'isFavorite', 'isEncoded', 'isOffline', 'isMotion', 'isVisible'].map((value) => ({
|
||||||
|
should: `should reject ${value} not a boolean`,
|
||||||
|
dto: { [value]: 'immich' },
|
||||||
|
expected: [`${value} must be a boolean value`],
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { should, dto, expected } of badTests) {
|
||||||
|
it(should, async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/search/metadata')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send(dto);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest(expected));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const searchTests = [
|
const searchTests = [
|
||||||
{
|
{
|
||||||
should: 'should get my assets',
|
should: 'should get my assets',
|
||||||
@@ -178,12 +231,12 @@ describe('/search', () => {
|
|||||||
deferred: () => ({ dto: { size: 1, isFavorite: false }, assets: [assetLast] }),
|
deferred: () => ({ dto: { size: 1, isFavorite: false }, assets: [assetLast] }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
should: 'should search by visibility (AssetVisibility.Archive)',
|
should: 'should search by isArchived (true)',
|
||||||
deferred: () => ({ dto: { visibility: AssetVisibility.Archive }, assets: [assetSprings] }),
|
deferred: () => ({ dto: { isArchived: true }, assets: [assetSprings] }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
should: 'should search by visibility (AssetVisibility.Timeline)',
|
should: 'should search by isArchived (false)',
|
||||||
deferred: () => ({ dto: { size: 1, visibility: AssetVisibility.Timeline }, assets: [assetLast] }),
|
deferred: () => ({ dto: { size: 1, isArchived: false }, assets: [assetLast] }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
should: 'should search by type (image)',
|
should: 'should search by type (image)',
|
||||||
@@ -192,7 +245,7 @@ describe('/search', () => {
|
|||||||
{
|
{
|
||||||
should: 'should search by type (video)',
|
should: 'should search by type (video)',
|
||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: { type: 'VIDEO', visibility: AssetVisibility.Hidden },
|
dto: { type: 'VIDEO' },
|
||||||
assets: [
|
assets: [
|
||||||
// the three live motion photos
|
// the three live motion photos
|
||||||
{ id: expect.any(String) },
|
{ id: expect.any(String) },
|
||||||
@@ -236,6 +289,13 @@ describe('/search', () => {
|
|||||||
should: 'should search by takenAfter (no results)',
|
should: 'should search by takenAfter (no results)',
|
||||||
deferred: () => ({ dto: { takenAfter: today.plus({ hour: 1 }).toJSDate() }, assets: [] }),
|
deferred: () => ({ dto: { takenAfter: today.plus({ hour: 1 }).toJSDate() }, assets: [] }),
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// should: 'should search by originalPath',
|
||||||
|
// deferred: () => ({
|
||||||
|
// dto: { originalPath: asset1.originalPath },
|
||||||
|
// assets: [asset1],
|
||||||
|
// }),
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
should: 'should search by originalFilename',
|
should: 'should search by originalFilename',
|
||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
@@ -265,7 +325,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
city: '',
|
city: '',
|
||||||
visibility: AssetVisibility.Timeline,
|
isVisible: true,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -276,7 +336,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
city: null,
|
city: null,
|
||||||
visibility: AssetVisibility.Timeline,
|
isVisible: true,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -297,7 +357,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
state: '',
|
state: '',
|
||||||
visibility: AssetVisibility.Timeline,
|
isVisible: true,
|
||||||
withExif: true,
|
withExif: true,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
@@ -309,7 +369,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
state: null,
|
state: null,
|
||||||
visibility: AssetVisibility.Timeline,
|
isVisible: true,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast, assetNotocactus],
|
assets: [assetLast, assetNotocactus],
|
||||||
@@ -330,7 +390,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
country: '',
|
country: '',
|
||||||
visibility: AssetVisibility.Timeline,
|
isVisible: true,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -341,7 +401,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
country: null,
|
country: null,
|
||||||
visibility: AssetVisibility.Timeline,
|
isVisible: true,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -394,6 +454,14 @@ describe('/search', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('POST /search/smart', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).post('/search/smart');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('POST /search/random', () => {
|
describe('POST /search/random', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@@ -408,6 +476,13 @@ describe('/search', () => {
|
|||||||
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).post('/search/random').send({ size: 1 });
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it.each(TEN_TIMES)('should return 1 random assets', async () => {
|
it.each(TEN_TIMES)('should return 1 random assets', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post('/search/random')
|
.post('/search/random')
|
||||||
@@ -437,6 +512,12 @@ describe('/search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /search/explore', () => {
|
describe('GET /search/explore', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/search/explore');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should get explore data', async () => {
|
it('should get explore data', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/search/explore')
|
.get('/search/explore')
|
||||||
@@ -447,6 +528,12 @@ describe('/search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /search/places', () => {
|
describe('GET /search/places', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/search/places');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should get relevant places', async () => {
|
it('should get relevant places', async () => {
|
||||||
const name = 'Paris';
|
const name = 'Paris';
|
||||||
|
|
||||||
@@ -465,6 +552,12 @@ describe('/search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /search/cities', () => {
|
describe('GET /search/cities', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/search/cities');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should get all cities', async () => {
|
it('should get all cities', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/search/cities')
|
.get('/search/cities')
|
||||||
@@ -483,6 +576,12 @@ describe('/search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /search/suggestions', () => {
|
describe('GET /search/suggestions', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/search/suggestions');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should get suggestions for country (including null)', async () => {
|
it('should get suggestions for country (including null)', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/search/suggestions?type=country&includeNull=true')
|
.get('/search/suggestions?type=country&includeNull=true')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AssetMediaResponseDto, AssetVisibility, LoginResponseDto, SharedLinkType, TimeBucketSize } from '@immich/sdk';
|
import { AssetMediaResponseDto, LoginResponseDto, SharedLinkType, TimeBucketSize } from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { createUserDto } from 'src/fixtures';
|
import { createUserDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
@@ -104,7 +104,7 @@ describe('/timeline', () => {
|
|||||||
const req1 = await request(app)
|
const req1 = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, visibility: AssetVisibility.Archive });
|
.query({ size: TimeBucketSize.Month, withPartners: true, isArchived: true });
|
||||||
|
|
||||||
expect(req1.status).toBe(400);
|
expect(req1.status).toBe(400);
|
||||||
expect(req1.body).toEqual(errorDto.badRequest());
|
expect(req1.body).toEqual(errorDto.badRequest());
|
||||||
@@ -112,7 +112,7 @@ describe('/timeline', () => {
|
|||||||
const req2 = await request(app)
|
const req2 = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
.set('Authorization', `Bearer ${user.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, visibility: undefined });
|
.query({ size: TimeBucketSize.Month, withPartners: true, isArchived: undefined });
|
||||||
|
|
||||||
expect(req2.status).toBe(400);
|
expect(req2.status).toBe(400);
|
||||||
expect(req2.body).toEqual(errorDto.badRequest());
|
expect(req2.body).toEqual(errorDto.badRequest());
|
||||||
|
|||||||
@@ -215,19 +215,6 @@ describe('/admin/users', () => {
|
|||||||
const user = await getMyUser({ headers: asBearerAuth(token.accessToken) });
|
const user = await getMyUser({ headers: asBearerAuth(token.accessToken) });
|
||||||
expect(user).toMatchObject({ email: nonAdmin.userEmail });
|
expect(user).toMatchObject({ email: nonAdmin.userEmail });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update the avatar color', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/admin/users/${admin.userId}`)
|
|
||||||
.send({ avatarColor: 'orange' })
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body).toMatchObject({ avatarColor: 'orange' });
|
|
||||||
|
|
||||||
const after = await getUserAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
|
|
||||||
expect(after).toMatchObject({ avatarColor: 'orange' });
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /admin/users/:id/preferences', () => {
|
describe('PUT /admin/users/:id/preferences', () => {
|
||||||
@@ -253,6 +240,19 @@ describe('/admin/users', () => {
|
|||||||
expect(after).toMatchObject({ memories: { enabled: false } });
|
expect(after).toMatchObject({ memories: { enabled: false } });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update the avatar color', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.put(`/admin/users/${admin.userId}/preferences`)
|
||||||
|
.send({ avatar: { color: 'orange' } })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toMatchObject({ avatar: { color: 'orange' } });
|
||||||
|
|
||||||
|
const after = await getUserPreferencesAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
expect(after).toMatchObject({ avatar: { color: 'orange' } });
|
||||||
|
});
|
||||||
|
|
||||||
it('should update download archive size', async () => {
|
it('should update download archive size', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/admin/users/${admin.userId}/preferences`)
|
.put(`/admin/users/${admin.userId}/preferences`)
|
||||||
|
|||||||
@@ -31,7 +31,33 @@ describe('/users', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('GET /users', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/users');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get users', async () => {
|
||||||
|
const { status, body } = await request(app).get('/users').set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toEqual(200);
|
||||||
|
expect(body).toHaveLength(2);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ email: 'admin@immich.cloud' }),
|
||||||
|
expect.objectContaining({ email: 'user2@immich.cloud' }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('GET /users/me', () => {
|
describe('GET /users/me', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get(`/users/me`);
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should not work for shared links', async () => {
|
it('should not work for shared links', async () => {
|
||||||
const album = await utils.createAlbum(admin.accessToken, { albumName: 'Album' });
|
const album = await utils.createAlbum(admin.accessToken, { albumName: 'Album' });
|
||||||
const sharedLink = await utils.createSharedLink(admin.accessToken, {
|
const sharedLink = await utils.createSharedLink(admin.accessToken, {
|
||||||
@@ -73,6 +99,24 @@ describe('/users', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /users/me', () => {
|
describe('PUT /users/me', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).put(`/users/me`);
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const key of ['email', 'name']) {
|
||||||
|
it(`should not allow null ${key}`, async () => {
|
||||||
|
const dto = { [key]: null };
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.put(`/users/me`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send(dto);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
it('should update first and last name', async () => {
|
it('should update first and last name', async () => {
|
||||||
const before = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
|
const before = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
@@ -139,19 +183,6 @@ describe('/users', () => {
|
|||||||
profileChangedAt: expect.anything(),
|
profileChangedAt: expect.anything(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update avatar color', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/users/me`)
|
|
||||||
.send({ avatarColor: 'blue' })
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body).toMatchObject({ avatarColor: 'blue' });
|
|
||||||
|
|
||||||
const after = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
|
|
||||||
expect(after).toMatchObject({ avatarColor: 'blue' });
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /users/me/preferences', () => {
|
describe('PUT /users/me/preferences', () => {
|
||||||
@@ -171,6 +202,19 @@ describe('/users', () => {
|
|||||||
expect(after).toMatchObject({ memories: { enabled: false } });
|
expect(after).toMatchObject({ memories: { enabled: false } });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update avatar color', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.put(`/users/me/preferences`)
|
||||||
|
.send({ avatar: { color: 'blue' } })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toMatchObject({ avatar: { color: 'blue' } });
|
||||||
|
|
||||||
|
const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) });
|
||||||
|
expect(after).toMatchObject({ avatar: { color: 'blue' } });
|
||||||
|
});
|
||||||
|
|
||||||
it('should require an integer for download archive size', async () => {
|
it('should require an integer for download archive size', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/users/me/preferences`)
|
.put(`/users/me/preferences`)
|
||||||
@@ -225,6 +269,11 @@ describe('/users', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /users/:id', () => {
|
describe('GET /users/:id', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status } = await request(app).get(`/users/${admin.userId}`);
|
||||||
|
expect(status).toEqual(401);
|
||||||
|
});
|
||||||
|
|
||||||
it('should get the user', async () => {
|
it('should get the user', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/users/${admin.userId}`)
|
.get(`/users/${admin.userId}`)
|
||||||
@@ -243,6 +292,12 @@ describe('/users', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /server/license', () => {
|
describe('GET /server/license', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).get('/users/me/license');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return the user license', async () => {
|
it('should return the user license', async () => {
|
||||||
await request(app)
|
await request(app)
|
||||||
.put('/users/me/license')
|
.put('/users/me/license')
|
||||||
@@ -260,6 +315,11 @@ describe('/users', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /users/me/license', () => {
|
describe('PUT /users/me/license', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status } = await request(app).put(`/users/me/license`);
|
||||||
|
expect(status).toEqual(401);
|
||||||
|
});
|
||||||
|
|
||||||
it('should set the user license', async () => {
|
it('should set the user license', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/users/me/license`)
|
.put(`/users/me/license`)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
AssetMediaCreateDto,
|
AssetMediaCreateDto,
|
||||||
AssetMediaResponseDto,
|
AssetMediaResponseDto,
|
||||||
AssetResponseDto,
|
AssetResponseDto,
|
||||||
AssetVisibility,
|
|
||||||
CheckExistingAssetsDto,
|
CheckExistingAssetsDto,
|
||||||
CreateAlbumDto,
|
CreateAlbumDto,
|
||||||
CreateLibraryDto,
|
CreateLibraryDto,
|
||||||
@@ -430,10 +429,7 @@ export const utils = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
archiveAssets: (accessToken: string, ids: string[]) =>
|
archiveAssets: (accessToken: string, ids: string[]) =>
|
||||||
updateAssets(
|
updateAssets({ assetBulkUpdateDto: { ids, isArchived: true } }, { headers: asBearerAuth(accessToken) }),
|
||||||
{ assetBulkUpdateDto: { ids, visibility: AssetVisibility.Archive } },
|
|
||||||
{ headers: asBearerAuth(accessToken) },
|
|
||||||
),
|
|
||||||
|
|
||||||
deleteAssets: (accessToken: string, ids: string[]) =>
|
deleteAssets: (accessToken: string, ids: string[]) =>
|
||||||
deleteAssets({ assetBulkDeleteDto: { ids } }, { headers: asBearerAuth(accessToken) }),
|
deleteAssets({ assetBulkDeleteDto: { ids } }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ test.describe('Registration', () => {
|
|||||||
|
|
||||||
// login
|
// login
|
||||||
await expect(page).toHaveTitle(/Login/);
|
await expect(page).toHaveTitle(/Login/);
|
||||||
await page.goto('/auth/login?autoLaunch=0');
|
await page.goto('/auth/login');
|
||||||
await page.getByLabel('Email').fill('admin@immich.app');
|
await page.getByLabel('Email').fill('admin@immich.app');
|
||||||
await page.getByLabel('Password').fill('password');
|
await page.getByLabel('Password').fill('password');
|
||||||
await page.getByRole('button', { name: 'Login' }).click();
|
await page.getByRole('button', { name: 'Login' }).click();
|
||||||
@@ -59,7 +59,7 @@ test.describe('Registration', () => {
|
|||||||
await context.clearCookies();
|
await context.clearCookies();
|
||||||
|
|
||||||
// login
|
// login
|
||||||
await page.goto('/auth/login?autoLaunch=0');
|
await page.goto('/auth/login');
|
||||||
await page.getByLabel('Email').fill('user@immich.cloud');
|
await page.getByLabel('Email').fill('user@immich.cloud');
|
||||||
await page.getByLabel('Password').fill('password');
|
await page.getByLabel('Password').fill('password');
|
||||||
await page.getByRole('button', { name: 'Login' }).click();
|
await page.getByRole('button', { name: 'Login' }).click();
|
||||||
@@ -72,7 +72,7 @@ test.describe('Registration', () => {
|
|||||||
await page.getByRole('button', { name: 'Change password' }).click();
|
await page.getByRole('button', { name: 'Change password' }).click();
|
||||||
|
|
||||||
// login with new password
|
// login with new password
|
||||||
await expect(page).toHaveURL('/auth/login?autoLaunch=0');
|
await expect(page).toHaveURL('/auth/login');
|
||||||
await page.getByLabel('Email').fill('user@immich.cloud');
|
await page.getByLabel('Email').fill('user@immich.cloud');
|
||||||
await page.getByLabel('Password').fill('new-password');
|
await page.getByLabel('Password').fill('new-password');
|
||||||
await page.getByRole('button', { name: 'Login' }).click();
|
await page.getByRole('button', { name: 'Login' }).click();
|
||||||
|
|||||||
@@ -21,9 +21,23 @@ test.describe('Photo Viewer', () => {
|
|||||||
test.beforeEach(async ({ context, page }) => {
|
test.beforeEach(async ({ context, page }) => {
|
||||||
// before each test, login as user
|
// before each test, login as user
|
||||||
await utils.setAuthCookies(context, admin.accessToken);
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
await page.goto('/photos');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('initially shows a loading spinner', async ({ page }) => {
|
||||||
|
await page.route(`/api/assets/${asset.id}/thumbnail**`, async (route) => {
|
||||||
|
// slow down the request for thumbnail, so spinner has chance to show up
|
||||||
|
await new Promise((f) => setTimeout(f, 2000));
|
||||||
|
await route.continue();
|
||||||
|
});
|
||||||
|
await page.goto(`/photos/${asset.id}`);
|
||||||
|
await page.waitForLoadState('load');
|
||||||
|
// this is the spinner
|
||||||
|
await page.waitForSelector('svg[role=status]');
|
||||||
|
await expect(page.getByTestId('loading-spinner')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
test('loads original photo when zoomed', async ({ page }) => {
|
test('loads original photo when zoomed', async ({ page }) => {
|
||||||
await page.goto(`/photos/${asset.id}`);
|
await page.goto(`/photos/${asset.id}`);
|
||||||
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail');
|
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail');
|
||||||
|
|||||||
@@ -47,13 +47,16 @@ test.describe('Shared Links', () => {
|
|||||||
await page.locator(`[data-asset-id="${asset.id}"]`).hover();
|
await page.locator(`[data-asset-id="${asset.id}"]`).hover();
|
||||||
await page.waitForSelector('[data-group] svg');
|
await page.waitForSelector('[data-group] svg');
|
||||||
await page.getByRole('checkbox').click();
|
await page.getByRole('checkbox').click();
|
||||||
await Promise.all([page.waitForEvent('download'), page.getByRole('button', { name: 'Download' }).click()]);
|
await page.getByRole('button', { name: 'Download' }).click();
|
||||||
|
await page.waitForEvent('download');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('download all from shared link', async ({ page }) => {
|
test('download all from shared link', async ({ page }) => {
|
||||||
await page.goto(`/share/${sharedLink.key}`);
|
await page.goto(`/share/${sharedLink.key}`);
|
||||||
await page.getByRole('heading', { name: 'Test Album' }).waitFor();
|
await page.getByRole('heading', { name: 'Test Album' }).waitFor();
|
||||||
await Promise.all([page.waitForEvent('download'), page.getByRole('button', { name: 'Download' }).click()]);
|
await page.getByRole('button', { name: 'Download' }).click();
|
||||||
|
await page.getByText('DOWNLOADING', { exact: true }).waitFor();
|
||||||
|
await page.waitForEvent('download');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('enter password for a shared link', async ({ page }) => {
|
test('enter password for a shared link', async ({ page }) => {
|
||||||
|
|||||||
Submodule e2e/test-assets updated: 8885d6d01c...9e3b964b08
287
i18n/ar.json
287
i18n/ar.json
@@ -4,6 +4,12 @@
|
|||||||
"account_settings": "إعدادات الحساب",
|
"account_settings": "إعدادات الحساب",
|
||||||
"acknowledge": "أُدرك ذلك",
|
"acknowledge": "أُدرك ذلك",
|
||||||
"action": "التحكم",
|
"action": "التحكم",
|
||||||
|
"action_common_back": "خلف",
|
||||||
|
"action_common_cancel": "يلغي",
|
||||||
|
"action_common_clear": "مسح",
|
||||||
|
"action_common_confirm": "تأكيد",
|
||||||
|
"action_common_save": "Save",
|
||||||
|
"action_common_select": "Select",
|
||||||
"action_common_update": "تحديث",
|
"action_common_update": "تحديث",
|
||||||
"actions": "العمليات",
|
"actions": "العمليات",
|
||||||
"active": "نشط",
|
"active": "نشط",
|
||||||
@@ -166,6 +172,7 @@
|
|||||||
"no_pattern_added": "لم يتم إضافة أي أنماط",
|
"no_pattern_added": "لم يتم إضافة أي أنماط",
|
||||||
"note_apply_storage_label_previous_assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها سابقًا، قم بتشغيل",
|
"note_apply_storage_label_previous_assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها سابقًا، قم بتشغيل",
|
||||||
"note_cannot_be_changed_later": "ملاحظة: لا يمكن تغيير هذا لاحقًا!",
|
"note_cannot_be_changed_later": "ملاحظة: لا يمكن تغيير هذا لاحقًا!",
|
||||||
|
"note_unlimited_quota": "ملاحظة: أدخل 0 للحصول على حصة غير محدودة",
|
||||||
"notification_email_from_address": "عنوان المرسل",
|
"notification_email_from_address": "عنوان المرسل",
|
||||||
"notification_email_from_address_description": "عنوان البريد الإلكتروني للمرسل، على سبيل المثال: \"Immich Photo Server noreply@example.com\"",
|
"notification_email_from_address_description": "عنوان البريد الإلكتروني للمرسل، على سبيل المثال: \"Immich Photo Server noreply@example.com\"",
|
||||||
"notification_email_host_description": "مضيف خادم البريد الإلكتروني (مثلًا: smtp.immich.app)",
|
"notification_email_host_description": "مضيف خادم البريد الإلكتروني (مثلًا: smtp.immich.app)",
|
||||||
@@ -187,13 +194,20 @@
|
|||||||
"oauth_auto_register": "التسجيل التلقائي",
|
"oauth_auto_register": "التسجيل التلقائي",
|
||||||
"oauth_auto_register_description": "التسجيل التلقائي للمستخدمين الجدد بعد تسجيل الدخول باستخدام OAuth",
|
"oauth_auto_register_description": "التسجيل التلقائي للمستخدمين الجدد بعد تسجيل الدخول باستخدام OAuth",
|
||||||
"oauth_button_text": "نص الزر",
|
"oauth_button_text": "نص الزر",
|
||||||
|
"oauth_client_id": "معرف العميل",
|
||||||
|
"oauth_client_secret": "الرمز السري للعميل",
|
||||||
"oauth_enable_description": "تسجيل الدخول باستخدام OAuth",
|
"oauth_enable_description": "تسجيل الدخول باستخدام OAuth",
|
||||||
|
"oauth_issuer_url": "عنوان URL الخاص بجهة الإصدار",
|
||||||
"oauth_mobile_redirect_uri": "عنوان URI لإعادة التوجيه على الهاتف",
|
"oauth_mobile_redirect_uri": "عنوان URI لإعادة التوجيه على الهاتف",
|
||||||
"oauth_mobile_redirect_uri_override": "تجاوز عنوان 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_profile_signing_algorithm": "خوارزمية توقيع الملف الشخصي",
|
||||||
|
"oauth_profile_signing_algorithm_description": "الخوارزمية المستخدمة للتوقيع على ملف تعريف المستخدم.",
|
||||||
|
"oauth_scope": "النطاق",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "إدارة إعدادات تسجيل الدخول OAuth",
|
"oauth_settings_description": "إدارة إعدادات تسجيل الدخول OAuth",
|
||||||
"oauth_settings_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى <link>الوثائق</link>.",
|
"oauth_settings_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى <link>الوثائق</link>.",
|
||||||
|
"oauth_signing_algorithm": "خوارزمية التوقيع",
|
||||||
"oauth_storage_label_claim": "المطالبة بتصنيف التخزين",
|
"oauth_storage_label_claim": "المطالبة بتصنيف التخزين",
|
||||||
"oauth_storage_label_claim_description": "قم تلقائيًا بتعيين تصنيف التخزين الخاص بالمستخدم على قيمة هذه المطالبة.",
|
"oauth_storage_label_claim_description": "قم تلقائيًا بتعيين تصنيف التخزين الخاص بالمستخدم على قيمة هذه المطالبة.",
|
||||||
"oauth_storage_quota_claim": "المطالبة بحصة التخزين",
|
"oauth_storage_quota_claim": "المطالبة بحصة التخزين",
|
||||||
@@ -367,6 +381,7 @@
|
|||||||
"advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.",
|
"advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.",
|
||||||
"advanced_settings_self_signed_ssl_title": "السماح بشهادات SSL الموقعة ذاتيًا",
|
"advanced_settings_self_signed_ssl_title": "السماح بشهادات SSL الموقعة ذاتيًا",
|
||||||
"advanced_settings_tile_subtitle": "إعدادات المستخدم المتقدمة",
|
"advanced_settings_tile_subtitle": "إعدادات المستخدم المتقدمة",
|
||||||
|
"advanced_settings_tile_title": "متقدم",
|
||||||
"advanced_settings_troubleshooting_subtitle": "تمكين الميزات الإضافية لاستكشاف الأخطاء وإصلاحها",
|
"advanced_settings_troubleshooting_subtitle": "تمكين الميزات الإضافية لاستكشاف الأخطاء وإصلاحها",
|
||||||
"advanced_settings_troubleshooting_title": "استكشاف الأخطاء وإصلاحها",
|
"advanced_settings_troubleshooting_title": "استكشاف الأخطاء وإصلاحها",
|
||||||
"age_months": "عمر {months, plural, one {# شهر} other {# أشهر}}",
|
"age_months": "عمر {months, plural, one {# شهر} other {# أشهر}}",
|
||||||
@@ -377,8 +392,10 @@
|
|||||||
"album_cover_updated": "تم تحديث غلاف الألبوم",
|
"album_cover_updated": "تم تحديث غلاف الألبوم",
|
||||||
"album_delete_confirmation": "هل أنت متأكد أنك تريد حذف الألبوم {album}؟",
|
"album_delete_confirmation": "هل أنت متأكد أنك تريد حذف الألبوم {album}؟",
|
||||||
"album_delete_confirmation_description": "إذا تمت مشاركة هذا الألبوم، فلن يتمكن المستخدمون الآخرون من الوصول إليه بعد الآن.",
|
"album_delete_confirmation_description": "إذا تمت مشاركة هذا الألبوم، فلن يتمكن المستخدمون الآخرون من الوصول إليه بعد الآن.",
|
||||||
|
"album_info": "{} items",
|
||||||
"album_info_card_backup_album_excluded": "مستبعد",
|
"album_info_card_backup_album_excluded": "مستبعد",
|
||||||
"album_info_card_backup_album_included": "متضمنة",
|
"album_info_card_backup_album_included": "متضمنة",
|
||||||
|
"album_info_shared": "{} items · Shared",
|
||||||
"album_info_updated": "تم تحديث معلومات الألبوم",
|
"album_info_updated": "تم تحديث معلومات الألبوم",
|
||||||
"album_leave": "هل تريد مغادرة الألبوم؟",
|
"album_leave": "هل تريد مغادرة الألبوم؟",
|
||||||
"album_leave_confirmation": "هل أنت متأكد أنك تريد مغادرة {album}؟",
|
"album_leave_confirmation": "هل أنت متأكد أنك تريد مغادرة {album}؟",
|
||||||
@@ -390,17 +407,21 @@
|
|||||||
"album_thumbnail_card_item": "عنصر واحد",
|
"album_thumbnail_card_item": "عنصر واحد",
|
||||||
"album_thumbnail_card_items": "{} items",
|
"album_thumbnail_card_items": "{} items",
|
||||||
"album_thumbnail_card_shared": " · . مشترك",
|
"album_thumbnail_card_shared": " · . مشترك",
|
||||||
|
"album_thumbnail_owned": "مملوكة",
|
||||||
"album_thumbnail_shared_by": "Shared by {}",
|
"album_thumbnail_shared_by": "Shared by {}",
|
||||||
|
"album_thumbnails": "Library page thumbnails ({} assets)",
|
||||||
"album_updated": "تم تحديث الألبوم",
|
"album_updated": "تم تحديث الألبوم",
|
||||||
"album_updated_setting_description": "تلقي إشعارًا عبر البريد الإلكتروني عندما يحتوي الألبوم المشترك على محتويات جديدة",
|
"album_updated_setting_description": "تلقي إشعارًا عبر البريد الإلكتروني عندما يحتوي الألبوم المشترك على محتويات جديدة",
|
||||||
"album_user_left": "تم ترك {album}",
|
"album_user_left": "تم ترك {album}",
|
||||||
"album_user_removed": "تم إزالة {user}",
|
"album_user_removed": "تم إزالة {user}",
|
||||||
"album_viewer_appbar_delete_confirm": "هل أنت متأكد أنك تريد حذف هذا الألبوم من حسابك؟",
|
"album_viewer_appbar_delete_confirm": "هل أنت متأكد أنك تريد حذف هذا الألبوم من حسابك؟",
|
||||||
|
"album_viewer_appbar_share_delete": "حذف الألبوم",
|
||||||
"album_viewer_appbar_share_err_delete": "فشل في حذف الألبوم",
|
"album_viewer_appbar_share_err_delete": "فشل في حذف الألبوم",
|
||||||
"album_viewer_appbar_share_err_leave": "فشل في ترك الألبوم",
|
"album_viewer_appbar_share_err_leave": "فشل في ترك الألبوم",
|
||||||
"album_viewer_appbar_share_err_remove": "هناك مشاكل في إزالة الأصول من الألبوم",
|
"album_viewer_appbar_share_err_remove": "هناك مشاكل في إزالة الأصول من الألبوم",
|
||||||
"album_viewer_appbar_share_err_title": "فشل في تغيير عنوان الألبوم",
|
"album_viewer_appbar_share_err_title": "فشل في تغيير عنوان الألبوم",
|
||||||
"album_viewer_appbar_share_leave": "ترك الألبوم",
|
"album_viewer_appbar_share_leave": "ترك الألبوم",
|
||||||
|
"album_viewer_appbar_share_remove": "إزالة من الألبوم",
|
||||||
"album_viewer_appbar_share_to": "حصة ل",
|
"album_viewer_appbar_share_to": "حصة ل",
|
||||||
"album_viewer_page_share_add_users": "اضافة مستخدمين",
|
"album_viewer_page_share_add_users": "اضافة مستخدمين",
|
||||||
"album_with_link_access": "السماح لأي شخص لديه الرابط برؤية الصور والأشخاص الموجودين في هذا الألبوم.",
|
"album_with_link_access": "السماح لأي شخص لديه الرابط برؤية الصور والأشخاص الموجودين في هذا الألبوم.",
|
||||||
@@ -409,7 +430,9 @@
|
|||||||
"all": "الكل",
|
"all": "الكل",
|
||||||
"all_albums": "جميع الألبومات",
|
"all_albums": "جميع الألبومات",
|
||||||
"all_people": "جميع الأشخاص",
|
"all_people": "جميع الأشخاص",
|
||||||
|
"all_people_page_title": "الناس",
|
||||||
"all_videos": "جميع الفيديوهات",
|
"all_videos": "جميع الفيديوهات",
|
||||||
|
"all_videos_page_title": "أشرطة فيديو",
|
||||||
"allow_dark_mode": "السماح بالوضع المعتم",
|
"allow_dark_mode": "السماح بالوضع المعتم",
|
||||||
"allow_edits": "إسمح بالتعديل",
|
"allow_edits": "إسمح بالتعديل",
|
||||||
"allow_public_user_to_download": "السماح لأي مستخدم عام بالتنزيل",
|
"allow_public_user_to_download": "السماح لأي مستخدم عام بالتنزيل",
|
||||||
@@ -424,6 +447,15 @@
|
|||||||
"app_bar_signout_dialog_ok": "نعم",
|
"app_bar_signout_dialog_ok": "نعم",
|
||||||
"app_bar_signout_dialog_title": "خروج",
|
"app_bar_signout_dialog_title": "خروج",
|
||||||
"app_settings": "إعدادات التطبيق",
|
"app_settings": "إعدادات التطبيق",
|
||||||
|
"appbar_delete_confirm": "هل أنت متأكد أنك تريد حذف هذا الألبوم من حسابك؟",
|
||||||
|
"appbar_share_delete": "حذف الألبوم",
|
||||||
|
"appbar_share_err_delete": "فشل في حذف الألبوم",
|
||||||
|
"appbar_share_err_leave": "فشل في ترك الألبوم",
|
||||||
|
"appbar_share_err_remove": "هناك مشاكل في إزالة الأصول من الألبوم",
|
||||||
|
"appbar_share_err_title": "فشل في تغيير عنوان الألبوم",
|
||||||
|
"appbar_share_leave": "ترك الألبوم",
|
||||||
|
"appbar_share_remove": "إزالة من الألبوم",
|
||||||
|
"appbar_share_to": "حصة ل",
|
||||||
"appears_in": "يظهر في",
|
"appears_in": "يظهر في",
|
||||||
"archive": "الأرشيف",
|
"archive": "الأرشيف",
|
||||||
"archive_or_unarchive_photo": "أرشفة الصورة أو إلغاء أرشفتها",
|
"archive_or_unarchive_photo": "أرشفة الصورة أو إلغاء أرشفتها",
|
||||||
@@ -447,10 +479,13 @@
|
|||||||
"asset_list_layout_settings_dynamic_layout_title": "تخطيط ديناميكي",
|
"asset_list_layout_settings_dynamic_layout_title": "تخطيط ديناميكي",
|
||||||
"asset_list_layout_settings_group_automatically": "تلقائي",
|
"asset_list_layout_settings_group_automatically": "تلقائي",
|
||||||
"asset_list_layout_settings_group_by": "مجموعة الأصول حسب",
|
"asset_list_layout_settings_group_by": "مجموعة الأصول حسب",
|
||||||
|
"asset_list_layout_settings_group_by_month": "شهر",
|
||||||
"asset_list_layout_settings_group_by_month_day": "شهر + يوم",
|
"asset_list_layout_settings_group_by_month_day": "شهر + يوم",
|
||||||
"asset_list_layout_sub_title": "تصميم",
|
"asset_list_layout_sub_title": "تصميم",
|
||||||
"asset_list_settings_subtitle": "إعدادات تخطيط شبكة الصور",
|
"asset_list_settings_subtitle": "إعدادات تخطيط شبكة الصور",
|
||||||
"asset_list_settings_title": "شبكة الصور",
|
"asset_list_settings_title": "شبكة الصور",
|
||||||
|
"asset_list_storage_indicator_title": "عرض مؤشر التخزين على بلاط الأصول",
|
||||||
|
"asset_list_tiles_per_row_title": "Number of assets per row ({})",
|
||||||
"asset_offline": "المحتوى غير اتصال",
|
"asset_offline": "المحتوى غير اتصال",
|
||||||
"asset_offline_description": "لم يعد هذا الأصل الخارجي موجودًا على القرص. يرجى الاتصال بمسؤول Immich للحصول على المساعدة.",
|
"asset_offline_description": "لم يعد هذا الأصل الخارجي موجودًا على القرص. يرجى الاتصال بمسؤول Immich للحصول على المساعدة.",
|
||||||
"asset_restored_successfully": "Asset restored successfully",
|
"asset_restored_successfully": "Asset restored successfully",
|
||||||
@@ -483,8 +518,27 @@
|
|||||||
"automatic_endpoint_switching_title": "Automatic URL switching",
|
"automatic_endpoint_switching_title": "Automatic URL switching",
|
||||||
"back": "خلف",
|
"back": "خلف",
|
||||||
"back_close_deselect": "الرجوع أو الإغلاق أو إلغاء التحديد",
|
"back_close_deselect": "الرجوع أو الإغلاق أو إلغاء التحديد",
|
||||||
|
"background_app_refresh_disabled_content": "قم بتمكين تحديث تطبيق الخلفية في الإعدادات > عام > تحديث تطبيق الخلفية لاستخدام النسخ الاحتياطي في الخلفية.",
|
||||||
|
"background_app_refresh_disabled_title": "تم تعطيل تحديث التطبيق في الخلفية",
|
||||||
|
"background_app_refresh_enable_button_text": "اذهب للاعدادات",
|
||||||
|
"background_battery_info_link": "أرني كيف",
|
||||||
|
"background_battery_info_message": "للحصول على أفضل تجربة نسخ احتياطي في الخلفية، يرجى تعطيل أي تحسينات للبطارية تقيد نشاط الخلفية لـ تطبيق.\n\nنظرًا لأن هذا خاص بالجهاز، يرجى البحث عن المعلومات المطلوبة للشركة المصنعة لجهازك.",
|
||||||
|
"background_battery_info_ok": "نعم",
|
||||||
|
"background_battery_info_title": "تحسين البطارية",
|
||||||
|
"background_charging": "فقط أثناء الشحن",
|
||||||
|
"background_configure_error": "فشل في تكوين خدمة الخلفية",
|
||||||
|
"background_delay": "Delay new assets backup: {}",
|
||||||
|
"background_description": "قم بتشغيل خدمة الخلفية لإجراء نسخ احتياطي لأي أصول جديدة تلقائيًا دون الحاجة إلى فتح التطبيق",
|
||||||
|
"background_is_off": "تم إيقاف النسخ الاحتياطي التلقائي للخلفية",
|
||||||
|
"background_is_on": "النسخ الاحتياطي التلقائي للخلفية قيد التشغيل",
|
||||||
"background_location_permission": "Background location permission",
|
"background_location_permission": "Background location permission",
|
||||||
"background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name",
|
"background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name",
|
||||||
|
"background_turn_off": "قم بإيقاف تشغيل خدمة الخلفية",
|
||||||
|
"background_turn_on": "قم بتشغيل خدمة الخلفية",
|
||||||
|
"background_wifi": "فقط على واي فاي",
|
||||||
|
"backup": "دعم",
|
||||||
|
"backup_album_excluded": "مستبعد",
|
||||||
|
"backup_album_included": "متضمنة",
|
||||||
"backup_album_selection_page_albums_device": "Albums on device ({})",
|
"backup_album_selection_page_albums_device": "Albums on device ({})",
|
||||||
"backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء",
|
"backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء",
|
||||||
"backup_album_selection_page_assets_scatter": "يمكن أن تنتشر الأصول عبر ألبومات متعددة. وبالتالي، يمكن تضمين الألبومات أو استبعادها أثناء عملية النسخ الاحتياطي.",
|
"backup_album_selection_page_assets_scatter": "يمكن أن تنتشر الأصول عبر ألبومات متعددة. وبالتالي، يمكن تضمين الألبومات أو استبعادها أثناء عملية النسخ الاحتياطي.",
|
||||||
@@ -519,6 +573,7 @@
|
|||||||
"backup_controller_page_backup": "دعم",
|
"backup_controller_page_backup": "دعم",
|
||||||
"backup_controller_page_backup_selected": "المحدد: ",
|
"backup_controller_page_backup_selected": "المحدد: ",
|
||||||
"backup_controller_page_backup_sub": "النسخ الاحتياطي للصور ومقاطع الفيديو",
|
"backup_controller_page_backup_sub": "النسخ الاحتياطي للصور ومقاطع الفيديو",
|
||||||
|
"backup_controller_page_cancel": "يلغي",
|
||||||
"backup_controller_page_created": "Created on: {}",
|
"backup_controller_page_created": "Created on: {}",
|
||||||
"backup_controller_page_desc_backup": "قم بتشغيل النسخ الاحتياطي الأمامي لتحميل الأصول الجديدة تلقائيًا إلى الخادم عند فتح التطبيق.",
|
"backup_controller_page_desc_backup": "قم بتشغيل النسخ الاحتياطي الأمامي لتحميل الأصول الجديدة تلقائيًا إلى الخادم عند فتح التطبيق.",
|
||||||
"backup_controller_page_excluded": "مستبعد: ",
|
"backup_controller_page_excluded": "مستبعد: ",
|
||||||
@@ -529,12 +584,14 @@
|
|||||||
"backup_controller_page_none_selected": "لم يتم التحديد",
|
"backup_controller_page_none_selected": "لم يتم التحديد",
|
||||||
"backup_controller_page_remainder": "بقية",
|
"backup_controller_page_remainder": "بقية",
|
||||||
"backup_controller_page_remainder_sub": "الصور ومقاطع الفيديو المتبقية للنسخ الاحتياطي من التحديد",
|
"backup_controller_page_remainder_sub": "الصور ومقاطع الفيديو المتبقية للنسخ الاحتياطي من التحديد",
|
||||||
|
"backup_controller_page_select": "يختار",
|
||||||
"backup_controller_page_server_storage": "ذاكرة الجهاز",
|
"backup_controller_page_server_storage": "ذاكرة الجهاز",
|
||||||
"backup_controller_page_start_backup": "بدء النسخ الاحتياطي",
|
"backup_controller_page_start_backup": "بدء النسخ الاحتياطي",
|
||||||
"backup_controller_page_status_off": "النسخة الاحتياطية التلقائية غير فعالة",
|
"backup_controller_page_status_off": "النسخة الاحتياطية التلقائية غير فعالة",
|
||||||
"backup_controller_page_status_on": "النسخة الاحتياطية التلقائية فعالة",
|
"backup_controller_page_status_on": "النسخة الاحتياطية التلقائية فعالة",
|
||||||
"backup_controller_page_storage_format": "{} of {} used",
|
"backup_controller_page_storage_format": "{} of {} used",
|
||||||
"backup_controller_page_to_backup": "الألبومات الاحتياطية",
|
"backup_controller_page_to_backup": "الألبومات الاحتياطية",
|
||||||
|
"backup_controller_page_total": "المجموع",
|
||||||
"backup_controller_page_total_sub": "جميع الصور ومقاطع الفيديو الفريدة من ألبومات مختارة",
|
"backup_controller_page_total_sub": "جميع الصور ومقاطع الفيديو الفريدة من ألبومات مختارة",
|
||||||
"backup_controller_page_turn_off": "قم بإيقاف تشغيل النسخ الاحتياطي المقدمة",
|
"backup_controller_page_turn_off": "قم بإيقاف تشغيل النسخ الاحتياطي المقدمة",
|
||||||
"backup_controller_page_turn_on": "قم بتشغيل النسخ الاحتياطي المقدمة",
|
"backup_controller_page_turn_on": "قم بتشغيل النسخ الاحتياطي المقدمة",
|
||||||
@@ -542,11 +599,14 @@
|
|||||||
"backup_err_only_album": "لا يمكن إزالة الألبوم الوحيد",
|
"backup_err_only_album": "لا يمكن إزالة الألبوم الوحيد",
|
||||||
"backup_info_card_assets": "أصول",
|
"backup_info_card_assets": "أصول",
|
||||||
"backup_manual_cancelled": "ملغي",
|
"backup_manual_cancelled": "ملغي",
|
||||||
|
"backup_manual_failed": "فشل",
|
||||||
"backup_manual_in_progress": "قيد التحميل حاول مره اخرى",
|
"backup_manual_in_progress": "قيد التحميل حاول مره اخرى",
|
||||||
"backup_manual_success": "نجاح",
|
"backup_manual_success": "نجاح",
|
||||||
"backup_manual_title": "حالة التحميل",
|
"backup_manual_title": "حالة التحميل",
|
||||||
"backup_options_page_title": "خيارات النسخ الاحتياطي",
|
"backup_options_page_title": "خيارات النسخ الاحتياطي",
|
||||||
|
"backup_selected": "المحدد: ",
|
||||||
"backup_setting_subtitle": "Manage background and foreground upload settings",
|
"backup_setting_subtitle": "Manage background and foreground upload settings",
|
||||||
|
"backup_sub": "النسخ الاحتياطي للصور ومقاطع الفيديو",
|
||||||
"backward": "الى الوراء",
|
"backward": "الى الوراء",
|
||||||
"birthdate_saved": "تم حفظ تاريخ الميلاد بنجاح",
|
"birthdate_saved": "تم حفظ تاريخ الميلاد بنجاح",
|
||||||
"birthdate_set_description": "يتم استخدام تاريخ الميلاد لحساب عمر هذا الشخص وقت التقاط الصورة.",
|
"birthdate_set_description": "يتم استخدام تاريخ الميلاد لحساب عمر هذا الشخص وقت التقاط الصورة.",
|
||||||
@@ -585,6 +645,7 @@
|
|||||||
"cannot_merge_people": "لا يمكن دمج الأشخاص",
|
"cannot_merge_people": "لا يمكن دمج الأشخاص",
|
||||||
"cannot_undo_this_action": "لا يمكنك التراجع عن هذا الإجراء!",
|
"cannot_undo_this_action": "لا يمكنك التراجع عن هذا الإجراء!",
|
||||||
"cannot_update_the_description": "لا يمكن تحديث الوصف",
|
"cannot_update_the_description": "لا يمكن تحديث الوصف",
|
||||||
|
"categories": "فئات",
|
||||||
"change_date": "غيّر التاريخ",
|
"change_date": "غيّر التاريخ",
|
||||||
"change_display_order": "Change display order",
|
"change_display_order": "Change display order",
|
||||||
"change_expiration_time": "تغيير وقت انتهاء الصلاحية",
|
"change_expiration_time": "تغيير وقت انتهاء الصلاحية",
|
||||||
@@ -610,6 +671,8 @@
|
|||||||
"clear": "إخلاء",
|
"clear": "إخلاء",
|
||||||
"clear_all": "إخلاء الكل",
|
"clear_all": "إخلاء الكل",
|
||||||
"clear_all_recent_searches": "مسح جميع عمليات البحث الأخيرة",
|
"clear_all_recent_searches": "مسح جميع عمليات البحث الأخيرة",
|
||||||
|
"clear_cache_button": "مسح ذاكرة التخزين المؤقت",
|
||||||
|
"clear_cache_button_title": "يقوم بمسح ذاكرة التخزين المؤقت للتطبيق.سيؤثر هذا بشكل كبير على أداء التطبيق حتى إعادة بناء ذاكرة التخزين المؤقت.",
|
||||||
"clear_message": "إخلاء الرسالة",
|
"clear_message": "إخلاء الرسالة",
|
||||||
"clear_value": "إخلاء القيمة",
|
"clear_value": "إخلاء القيمة",
|
||||||
"client_cert_dialog_msg_confirm": "OK",
|
"client_cert_dialog_msg_confirm": "OK",
|
||||||
@@ -617,6 +680,7 @@
|
|||||||
"client_cert_import": "Import",
|
"client_cert_import": "Import",
|
||||||
"client_cert_import_success_msg": "Client certificate is imported",
|
"client_cert_import_success_msg": "Client certificate is imported",
|
||||||
"client_cert_invalid_msg": "Invalid certificate file or wrong password",
|
"client_cert_invalid_msg": "Invalid certificate file or wrong password",
|
||||||
|
"client_cert_remove": "Remove",
|
||||||
"client_cert_remove_msg": "Client certificate is removed",
|
"client_cert_remove_msg": "Client certificate is removed",
|
||||||
"client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login",
|
"client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login",
|
||||||
"client_cert_title": "SSL Client Certificate",
|
"client_cert_title": "SSL Client Certificate",
|
||||||
@@ -626,12 +690,17 @@
|
|||||||
"collapse_all": "طيّ الكل",
|
"collapse_all": "طيّ الكل",
|
||||||
"color": "اللون",
|
"color": "اللون",
|
||||||
"color_theme": "نمط الألوان",
|
"color_theme": "نمط الألوان",
|
||||||
|
"colorful_interface_subtitle": "Apply primary color to background surfaces.",
|
||||||
|
"colorful_interface_title": "Colorful interface",
|
||||||
"comment_deleted": "تم حذف التعليق",
|
"comment_deleted": "تم حذف التعليق",
|
||||||
"comment_options": "خيارات التعليق",
|
"comment_options": "خيارات التعليق",
|
||||||
"comments_and_likes": "التعليقات والإعجابات",
|
"comments_and_likes": "التعليقات والإعجابات",
|
||||||
"comments_are_disabled": "التعليقات معطلة",
|
"comments_are_disabled": "التعليقات معطلة",
|
||||||
|
"common_add_to_album": "أضف إلى الألبوم",
|
||||||
|
"common_change_password": "تغيير كلمة المرور",
|
||||||
"common_create_new_album": "إنشاء ألبوم جديد",
|
"common_create_new_album": "إنشاء ألبوم جديد",
|
||||||
"common_server_error": "يرجى التحقق من اتصال الشبكة الخاص بك ، والتأكد من أن الجهاز قابل للوصول وإصدارات التطبيق/الجهاز متوافقة.",
|
"common_server_error": "يرجى التحقق من اتصال الشبكة الخاص بك ، والتأكد من أن الجهاز قابل للوصول وإصدارات التطبيق/الجهاز متوافقة.",
|
||||||
|
"common_shared": "مشترك",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
"confirm": "تأكيد",
|
"confirm": "تأكيد",
|
||||||
"confirm_admin_password": "تأكيد كلمة مرور المسؤول",
|
"confirm_admin_password": "تأكيد كلمة مرور المسؤول",
|
||||||
@@ -641,16 +710,29 @@
|
|||||||
"confirm_password": "تأكيد كلمة المرور",
|
"confirm_password": "تأكيد كلمة المرور",
|
||||||
"contain": "محتواة",
|
"contain": "محتواة",
|
||||||
"context": "السياق",
|
"context": "السياق",
|
||||||
|
"contextual_search": "Sunrise on the beach",
|
||||||
"continue": "متابعة",
|
"continue": "متابعة",
|
||||||
|
"control_bottom_app_bar_add_to_album": "أضف إلى الألبوم",
|
||||||
|
"control_bottom_app_bar_album_info": "{} items",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} items · Shared",
|
"control_bottom_app_bar_album_info_shared": "{} items · Shared",
|
||||||
|
"control_bottom_app_bar_archive": "أرشيف",
|
||||||
"control_bottom_app_bar_create_new_album": "إنشاء ألبوم جديد",
|
"control_bottom_app_bar_create_new_album": "إنشاء ألبوم جديد",
|
||||||
|
"control_bottom_app_bar_delete": "يمسح",
|
||||||
"control_bottom_app_bar_delete_from_immich": " حذف منال تطبيق",
|
"control_bottom_app_bar_delete_from_immich": " حذف منال تطبيق",
|
||||||
"control_bottom_app_bar_delete_from_local": "حذف من الجهاز",
|
"control_bottom_app_bar_delete_from_local": "حذف من الجهاز",
|
||||||
|
"control_bottom_app_bar_download": "Download",
|
||||||
|
"control_bottom_app_bar_edit": "Edit",
|
||||||
"control_bottom_app_bar_edit_location": "تحديد الوجهة",
|
"control_bottom_app_bar_edit_location": "تحديد الوجهة",
|
||||||
"control_bottom_app_bar_edit_time": "تحرير التاريخ والوقت",
|
"control_bottom_app_bar_edit_time": "تحرير التاريخ والوقت",
|
||||||
|
"control_bottom_app_bar_favorite": "مفضل",
|
||||||
|
"control_bottom_app_bar_share": "يشارك",
|
||||||
"control_bottom_app_bar_share_link": "Share Link",
|
"control_bottom_app_bar_share_link": "Share Link",
|
||||||
"control_bottom_app_bar_share_to": "مشاركة إلى",
|
"control_bottom_app_bar_share_to": "مشاركة إلى",
|
||||||
|
"control_bottom_app_bar_stack": "تجميع",
|
||||||
"control_bottom_app_bar_trash_from_immich": "حذفه ونقله في سله المهملات",
|
"control_bottom_app_bar_trash_from_immich": "حذفه ونقله في سله المهملات",
|
||||||
|
"control_bottom_app_bar_unarchive": "غير محفوظ في الارشيف",
|
||||||
|
"control_bottom_app_bar_unfavorite": "غير مفضل",
|
||||||
|
"control_bottom_app_bar_upload": "رفع وتحميل",
|
||||||
"copied_image_to_clipboard": "تم نسخ الصورة إلى الحافظة.",
|
"copied_image_to_clipboard": "تم نسخ الصورة إلى الحافظة.",
|
||||||
"copied_to_clipboard": "نسخ إلى الحافظة!",
|
"copied_to_clipboard": "نسخ إلى الحافظة!",
|
||||||
"copy_error": "نسخ الخطأ",
|
"copy_error": "نسخ الخطأ",
|
||||||
@@ -671,9 +753,12 @@
|
|||||||
"create_link_to_share": "إنشاء رابط للمشاركة",
|
"create_link_to_share": "إنشاء رابط للمشاركة",
|
||||||
"create_link_to_share_description": "السماح لأي شخص لديه الرابط بمشاهدة الصورة (الصور) المحددة",
|
"create_link_to_share_description": "السماح لأي شخص لديه الرابط بمشاهدة الصورة (الصور) المحددة",
|
||||||
"create_new": "CREATE NEW",
|
"create_new": "CREATE NEW",
|
||||||
|
"create_new_album": "إنشاء ألبوم جديد",
|
||||||
"create_new_person": "إنشاء شخص جديد",
|
"create_new_person": "إنشاء شخص جديد",
|
||||||
"create_new_person_hint": "تعيين المحتويات المحددة لشخص جديد",
|
"create_new_person_hint": "تعيين المحتويات المحددة لشخص جديد",
|
||||||
"create_new_user": "إنشاء مستخدم جديد",
|
"create_new_user": "إنشاء مستخدم جديد",
|
||||||
|
"create_shared_album_page_create": "انشاء",
|
||||||
|
"create_shared_album_page_share": "يشارك",
|
||||||
"create_shared_album_page_share_add_assets": "إضافة الأصول",
|
"create_shared_album_page_share_add_assets": "إضافة الأصول",
|
||||||
"create_shared_album_page_share_select_photos": "حدد الصور",
|
"create_shared_album_page_share_select_photos": "حدد الصور",
|
||||||
"create_tag": "إنشاء علامة",
|
"create_tag": "إنشاء علامة",
|
||||||
@@ -681,6 +766,7 @@
|
|||||||
"create_user": "إنشاء مستخدم",
|
"create_user": "إنشاء مستخدم",
|
||||||
"created": "تم الإنشاء",
|
"created": "تم الإنشاء",
|
||||||
"crop": "Crop",
|
"crop": "Crop",
|
||||||
|
"curated_location_page_title": "أماكن",
|
||||||
"curated_object_page_title": "أشياء",
|
"curated_object_page_title": "أشياء",
|
||||||
"current_device": "الجهاز الحالي",
|
"current_device": "الجهاز الحالي",
|
||||||
"current_server_address": "Current server address",
|
"current_server_address": "Current server address",
|
||||||
@@ -689,6 +775,7 @@
|
|||||||
"daily_title_text_date": "E ، MMM DD",
|
"daily_title_text_date": "E ، MMM DD",
|
||||||
"daily_title_text_date_year": "E ، MMM DD ، yyyy",
|
"daily_title_text_date_year": "E ، MMM DD ، yyyy",
|
||||||
"dark": "معتم",
|
"dark": "معتم",
|
||||||
|
"dark_mode_switch": "الوضع المظلم",
|
||||||
"date_after": "التارخ بعد",
|
"date_after": "التارخ بعد",
|
||||||
"date_and_time": "التاريخ و الوقت",
|
"date_and_time": "التاريخ و الوقت",
|
||||||
"date_before": "التاريخ قبل",
|
"date_before": "التاريخ قبل",
|
||||||
@@ -710,10 +797,14 @@
|
|||||||
"delete_dialog_alert_local": " العناصر التي تم حذفها من جهازك ولكنها موجوده في تطبيق",
|
"delete_dialog_alert_local": " العناصر التي تم حذفها من جهازك ولكنها موجوده في تطبيق",
|
||||||
"delete_dialog_alert_local_non_backed_up": "بعض العناصر التي سيتم حذفها بشكل دائم ولا يوجد لها نسخه احتياطيه في تطبيق ",
|
"delete_dialog_alert_local_non_backed_up": "بعض العناصر التي سيتم حذفها بشكل دائم ولا يوجد لها نسخه احتياطيه في تطبيق ",
|
||||||
"delete_dialog_alert_remote": "العناصر التي سيتم حذفها بشكل دائم من تطبيق",
|
"delete_dialog_alert_remote": "العناصر التي سيتم حذفها بشكل دائم من تطبيق",
|
||||||
|
"delete_dialog_cancel": "يلغي",
|
||||||
|
"delete_dialog_ok": "يمسح",
|
||||||
"delete_dialog_ok_force": "احذف على أي حال",
|
"delete_dialog_ok_force": "احذف على أي حال",
|
||||||
"delete_dialog_title": "الحذف بشكل نهائي",
|
"delete_dialog_title": "الحذف بشكل نهائي",
|
||||||
"delete_duplicates_confirmation": "هل أنت متأكد أنك تريد حذف هذه التكرارات نهائيًا؟",
|
"delete_duplicates_confirmation": "هل أنت متأكد أنك تريد حذف هذه التكرارات نهائيًا؟",
|
||||||
"delete_face": "حذف الوجه",
|
"delete_face": "حذف الوجه",
|
||||||
|
"delete_from_immich": " حذف منال تطبيق",
|
||||||
|
"delete_from_local": "حذف من الجهاز",
|
||||||
"delete_key": "حذف المفتاح",
|
"delete_key": "حذف المفتاح",
|
||||||
"delete_library": "حذف المكتبة",
|
"delete_library": "حذف المكتبة",
|
||||||
"delete_link": "حذف الرابط",
|
"delete_link": "حذف الرابط",
|
||||||
@@ -721,15 +812,18 @@
|
|||||||
"delete_local_dialog_ok_force": "احذف على أي حال",
|
"delete_local_dialog_ok_force": "احذف على أي حال",
|
||||||
"delete_others": "حذف الأخرى",
|
"delete_others": "حذف الأخرى",
|
||||||
"delete_shared_link": "حذف الرابط المشترك",
|
"delete_shared_link": "حذف الرابط المشترك",
|
||||||
|
"delete_shared_link_dialog_content": "هل أنت متأكد من أنك تريد حذف هذا الرابط المشترك؟",
|
||||||
"delete_shared_link_dialog_title": "حذف الرابط المشترك",
|
"delete_shared_link_dialog_title": "حذف الرابط المشترك",
|
||||||
"delete_tag": "حذف العلامة",
|
"delete_tag": "حذف العلامة",
|
||||||
"delete_tag_confirmation_prompt": "هل أنت متأكد أنك تريد حذف العلامة {tagName}؟",
|
"delete_tag_confirmation_prompt": "هل أنت متأكد أنك تريد حذف العلامة {tagName}؟",
|
||||||
"delete_user": "حذف المستخدم",
|
"delete_user": "حذف المستخدم",
|
||||||
"deleted_shared_link": "تم حذف الرابط المشارك",
|
"deleted_shared_link": "تم حذف الرابط المشارك",
|
||||||
"deletes_missing_assets": "حذف الأصول المفقودة من القرص",
|
"deletes_missing_assets": "حذف الأصول المفقودة من القرص",
|
||||||
|
"desc_backup": "قم بتشغيل النسخ الاحتياطي الأمامي لتحميل الأصول الجديدة تلقائيًا إلى الخادم عند فتح التطبيق.",
|
||||||
"description": "وصف",
|
"description": "وصف",
|
||||||
"description_input_hint_text": "اضف وصفا...",
|
"description_input_hint_text": "اضف وصفا...",
|
||||||
"description_input_submit_error": "خطأ تحديث الوصف ، تحقق من السجل لمزيد من التفاصيل",
|
"description_input_submit_error": "خطأ تحديث الوصف ، تحقق من السجل لمزيد من التفاصيل",
|
||||||
|
"description_search": "Hiking day in Sapa",
|
||||||
"details": "تفاصيل",
|
"details": "تفاصيل",
|
||||||
"direction": "الإتجاه",
|
"direction": "الإتجاه",
|
||||||
"disabled": "معطل",
|
"disabled": "معطل",
|
||||||
@@ -767,6 +861,9 @@
|
|||||||
"downloading_asset_filename": "{filename} قيد التنزيل",
|
"downloading_asset_filename": "{filename} قيد التنزيل",
|
||||||
"downloading_media": "Downloading media",
|
"downloading_media": "Downloading media",
|
||||||
"drop_files_to_upload": "قم بإسقاط الملفات في أي مكان لرفعها",
|
"drop_files_to_upload": "قم بإسقاط الملفات في أي مكان لرفعها",
|
||||||
|
"duplicated_assets_clear_button": "واضح",
|
||||||
|
"duplicated_assets_subtitle": "الصور ومقاطع الفيديو اللتي تم تجاهلها المدرجة في التطبيق",
|
||||||
|
"duplicated_assets_title": "Duplicated Assets ({})",
|
||||||
"duplicates": "التكرارات",
|
"duplicates": "التكرارات",
|
||||||
"duplicates_description": "قم بحل كل مجموعة من خلال الإشارة إلى التكرارات، إن وجدت",
|
"duplicates_description": "قم بحل كل مجموعة من خلال الإشارة إلى التكرارات، إن وجدت",
|
||||||
"duration": "المدة",
|
"duration": "المدة",
|
||||||
@@ -775,8 +872,12 @@
|
|||||||
"edit_avatar": "تعديل الصورة الشخصية",
|
"edit_avatar": "تعديل الصورة الشخصية",
|
||||||
"edit_date": "تعديل التاريخ",
|
"edit_date": "تعديل التاريخ",
|
||||||
"edit_date_and_time": "تعديل التاريخ والوقت",
|
"edit_date_and_time": "تعديل التاريخ والوقت",
|
||||||
|
"edit_date_time_dialog_date_time": "التاريخ و الوقت",
|
||||||
|
"edit_date_time_dialog_search_timezone": "Search timezone...",
|
||||||
|
"edit_date_time_dialog_timezone": "وحدة زمنية",
|
||||||
"edit_exclusion_pattern": "تعديل نمط الاستبعاد",
|
"edit_exclusion_pattern": "تعديل نمط الاستبعاد",
|
||||||
"edit_faces": "تعديل الوجوه",
|
"edit_faces": "تعديل الوجوه",
|
||||||
|
"edit_image_title": "Edit",
|
||||||
"edit_import_path": "تعديل مسار الاستيراد",
|
"edit_import_path": "تعديل مسار الاستيراد",
|
||||||
"edit_import_paths": "تعديل مسارات الاستيراد",
|
"edit_import_paths": "تعديل مسارات الاستيراد",
|
||||||
"edit_key": "تعديل المفتاح",
|
"edit_key": "تعديل المفتاح",
|
||||||
@@ -786,6 +887,7 @@
|
|||||||
"edit_name": "تعديل الاسم",
|
"edit_name": "تعديل الاسم",
|
||||||
"edit_people": "تعديل الأشخاص",
|
"edit_people": "تعديل الأشخاص",
|
||||||
"edit_tag": "تعديل العلامة",
|
"edit_tag": "تعديل العلامة",
|
||||||
|
"edit_time": "تحرير التاريخ والوقت",
|
||||||
"edit_title": "تعديل العنوان",
|
"edit_title": "تعديل العنوان",
|
||||||
"edit_user": "تعديل المستخدم",
|
"edit_user": "تعديل المستخدم",
|
||||||
"edited": "تم التعديل",
|
"edited": "تم التعديل",
|
||||||
@@ -935,10 +1037,12 @@
|
|||||||
"unable_to_update_user": "غير قادر على تحديث المستخدم",
|
"unable_to_update_user": "غير قادر على تحديث المستخدم",
|
||||||
"unable_to_upload_file": "تعذر رفع الملف"
|
"unable_to_upload_file": "تعذر رفع الملف"
|
||||||
},
|
},
|
||||||
|
"excluded": "مستبعد: ",
|
||||||
"exif": "Exif (صيغة ملف صوري قابل للتبادل)",
|
"exif": "Exif (صيغة ملف صوري قابل للتبادل)",
|
||||||
"exif_bottom_sheet_description": "اضف وصفا...",
|
"exif_bottom_sheet_description": "اضف وصفا...",
|
||||||
"exif_bottom_sheet_details": "تفاصيل",
|
"exif_bottom_sheet_details": "تفاصيل",
|
||||||
"exif_bottom_sheet_location": "موقع",
|
"exif_bottom_sheet_location": "موقع",
|
||||||
|
"exif_bottom_sheet_location_add": "إضافة موقع",
|
||||||
"exif_bottom_sheet_people": "الناس",
|
"exif_bottom_sheet_people": "الناس",
|
||||||
"exif_bottom_sheet_person_add_person": "اضف اسما",
|
"exif_bottom_sheet_person_add_person": "اضف اسما",
|
||||||
"exif_bottom_sheet_person_age": "Age {}",
|
"exif_bottom_sheet_person_age": "Age {}",
|
||||||
@@ -971,12 +1075,14 @@
|
|||||||
"favorite_or_unfavorite_photo": "تفضيل أو إلغاء تفضيل الصورة",
|
"favorite_or_unfavorite_photo": "تفضيل أو إلغاء تفضيل الصورة",
|
||||||
"favorites": "المفضلة",
|
"favorites": "المفضلة",
|
||||||
"favorites_page_no_favorites": "لم يتم العثور على الأصول المفضلة",
|
"favorites_page_no_favorites": "لم يتم العثور على الأصول المفضلة",
|
||||||
|
"favorites_page_title": "المفضلة",
|
||||||
"feature_photo_updated": "تم تحديث الصورة المميزة",
|
"feature_photo_updated": "تم تحديث الصورة المميزة",
|
||||||
"features": "الميزات",
|
"features": "الميزات",
|
||||||
"features_setting_description": "إدارة ميزات التطبيق",
|
"features_setting_description": "إدارة ميزات التطبيق",
|
||||||
"file_name": "إسم الملف",
|
"file_name": "إسم الملف",
|
||||||
"file_name_or_extension": "اسم الملف أو امتداده",
|
"file_name_or_extension": "اسم الملف أو امتداده",
|
||||||
"filename": "اسم الملف",
|
"filename": "اسم الملف",
|
||||||
|
"filename_search": "File name or extension",
|
||||||
"filetype": "نوع الملف",
|
"filetype": "نوع الملف",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"filter_people": "تصفية الاشخاص",
|
"filter_people": "تصفية الاشخاص",
|
||||||
@@ -996,6 +1102,7 @@
|
|||||||
"go_to_search": "اذهب إلى البحث",
|
"go_to_search": "اذهب إلى البحث",
|
||||||
"grant_permission": "Grant permission",
|
"grant_permission": "Grant permission",
|
||||||
"group_albums_by": "تجميع الألبومات حسب...",
|
"group_albums_by": "تجميع الألبومات حسب...",
|
||||||
|
"group_by_sub_title": "تنظيم بواسطة",
|
||||||
"group_country": "مجموعة البلد",
|
"group_country": "مجموعة البلد",
|
||||||
"group_no": "بدون تجميع",
|
"group_no": "بدون تجميع",
|
||||||
"group_owner": "تجميع حسب المالك",
|
"group_owner": "تجميع حسب المالك",
|
||||||
@@ -1008,6 +1115,7 @@
|
|||||||
"header_settings_field_validator_msg": "Value cannot be empty",
|
"header_settings_field_validator_msg": "Value cannot be empty",
|
||||||
"header_settings_header_name_input": "Header name",
|
"header_settings_header_name_input": "Header name",
|
||||||
"header_settings_header_value_input": "Header value",
|
"header_settings_header_value_input": "Header value",
|
||||||
|
"header_settings_page_title": "Proxy Headers",
|
||||||
"headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request",
|
"headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request",
|
||||||
"headers_settings_tile_title": "Custom proxy headers",
|
"headers_settings_tile_title": "Custom proxy headers",
|
||||||
"hi_user": "مرحبا {name} ({email})",
|
"hi_user": "مرحبا {name} ({email})",
|
||||||
@@ -1033,6 +1141,7 @@
|
|||||||
"home_page_upload_err_limit": "لا يمكن إلا تحميل 30 أحد الأصول في وقت واحد ، سوف يتخطى",
|
"home_page_upload_err_limit": "لا يمكن إلا تحميل 30 أحد الأصول في وقت واحد ، سوف يتخطى",
|
||||||
"host": "المضيف",
|
"host": "المضيف",
|
||||||
"hour": "ساعة",
|
"hour": "ساعة",
|
||||||
|
"id": "ID: {}",
|
||||||
"ignore_icloud_photos": "Ignore iCloud photos",
|
"ignore_icloud_photos": "Ignore iCloud photos",
|
||||||
"ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server",
|
"ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server",
|
||||||
"image": "صورة",
|
"image": "صورة",
|
||||||
@@ -1046,10 +1155,14 @@
|
|||||||
"image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1} و{person2} في {date}",
|
"image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1} و{person2} في {date}",
|
||||||
"image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1}، {person2}، و{person3} في {date}",
|
"image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1}، {person2}، و{person3} في {date}",
|
||||||
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}, {country} with {person1}, {person2}, مع {additionalCount, number} آخرين في {date}",
|
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}, {country} with {person1}, {person2}, مع {additionalCount, number} آخرين في {date}",
|
||||||
|
"image_cache_size": "Image cache size ({} assets)",
|
||||||
"image_saved_successfully": "Image saved",
|
"image_saved_successfully": "Image saved",
|
||||||
|
"image_viewer_page_state_provider_download_error": "خطا في التحميل",
|
||||||
"image_viewer_page_state_provider_download_started": "بدأ التنزيل",
|
"image_viewer_page_state_provider_download_started": "بدأ التنزيل",
|
||||||
"image_viewer_page_state_provider_download_success": "تم التنزيل بنجاح",
|
"image_viewer_page_state_provider_download_success": "تم التنزيل بنجاح",
|
||||||
"image_viewer_page_state_provider_share_error": "خطأ في المشاركة",
|
"image_viewer_page_state_provider_share_error": "خطأ في المشاركة",
|
||||||
|
"image_viewer_quality_subtitle": "اضبط جودة عارض الصورة التفصيلية",
|
||||||
|
"image_viewer_quality_title": "جودة عارض الصورة",
|
||||||
"immich_logo": "شعار immich",
|
"immich_logo": "شعار immich",
|
||||||
"immich_web_interface": "واجهة ويب immich",
|
"immich_web_interface": "واجهة ويب immich",
|
||||||
"import_from_json": "استيراد من JSON",
|
"import_from_json": "استيراد من JSON",
|
||||||
@@ -1084,17 +1197,29 @@
|
|||||||
"last_seen": "اخر ظهور",
|
"last_seen": "اخر ظهور",
|
||||||
"latest_version": "احدث اصدار",
|
"latest_version": "احدث اصدار",
|
||||||
"latitude": "خط العرض",
|
"latitude": "خط العرض",
|
||||||
|
"layout_settings_dynamic_layout_title": "تخطيط ديناميكي",
|
||||||
|
"layout_settings_group_automatically": "تلقائي",
|
||||||
|
"layout_settings_group_by": "مجموعة الأصول حسب",
|
||||||
|
"layout_settings_group_by_month": "شهر",
|
||||||
|
"layout_settings_group_by_month_day": "شهر + يوم",
|
||||||
|
"layout_sub_title": "تصميم",
|
||||||
"leave": "مغادرة",
|
"leave": "مغادرة",
|
||||||
"lens_model": "نموذج العدسات",
|
"lens_model": "نموذج العدسات",
|
||||||
"let_others_respond": "دع الآخرين يستجيبون",
|
"let_others_respond": "دع الآخرين يستجيبون",
|
||||||
"level": "المستوى",
|
"level": "المستوى",
|
||||||
"library": "مكتبة",
|
"library": "مكتبة",
|
||||||
"library_options": "خيارات المكتبة",
|
"library_options": "خيارات المكتبة",
|
||||||
|
"library_page_albums": "ألبومات",
|
||||||
|
"library_page_archive": "أرشيف",
|
||||||
"library_page_device_albums": "ألبومات على الجهاز",
|
"library_page_device_albums": "ألبومات على الجهاز",
|
||||||
|
"library_page_favorites": "المفضلة",
|
||||||
"library_page_new_album": "البوم جديد",
|
"library_page_new_album": "البوم جديد",
|
||||||
|
"library_page_sharing": "مشاركة",
|
||||||
"library_page_sort_asset_count": "عدد الأصول",
|
"library_page_sort_asset_count": "عدد الأصول",
|
||||||
"library_page_sort_created": "تاريخ الإنشاء",
|
"library_page_sort_created": "تاريخ الإنشاء",
|
||||||
"library_page_sort_last_modified": "آخر تعديل",
|
"library_page_sort_last_modified": "آخر تعديل",
|
||||||
|
"library_page_sort_most_oldest_photo": "أقدم صورة",
|
||||||
|
"library_page_sort_most_recent_photo": "أحدث الصور",
|
||||||
"library_page_sort_title": "عنوان الألبوم",
|
"library_page_sort_title": "عنوان الألبوم",
|
||||||
"light": "المضيئ",
|
"light": "المضيئ",
|
||||||
"like_deleted": "تم حذف الإعجاب",
|
"like_deleted": "تم حذف الإعجاب",
|
||||||
@@ -1110,8 +1235,10 @@
|
|||||||
"location_permission": "Location permission",
|
"location_permission": "Location permission",
|
||||||
"location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name",
|
"location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name",
|
||||||
"location_picker_choose_on_map": "اختر على الخريطة",
|
"location_picker_choose_on_map": "اختر على الخريطة",
|
||||||
|
"location_picker_latitude": "خط العرض",
|
||||||
"location_picker_latitude_error": "أدخل خط عرض صالح",
|
"location_picker_latitude_error": "أدخل خط عرض صالح",
|
||||||
"location_picker_latitude_hint": "أدخل خط العرض الخاص بك هنا",
|
"location_picker_latitude_hint": "أدخل خط العرض الخاص بك هنا",
|
||||||
|
"location_picker_longitude": "خط الطول",
|
||||||
"location_picker_longitude_error": "أدخل خط الطول الصحيح",
|
"location_picker_longitude_error": "أدخل خط الطول الصحيح",
|
||||||
"location_picker_longitude_hint": "أدخل خط الطول هنا",
|
"location_picker_longitude_hint": "أدخل خط الطول هنا",
|
||||||
"log_out": "تسجيل خروج",
|
"log_out": "تسجيل خروج",
|
||||||
@@ -1122,6 +1249,7 @@
|
|||||||
"login_disabled": "تم تعطيل تسجيل الدخول",
|
"login_disabled": "تم تعطيل تسجيل الدخول",
|
||||||
"login_form_api_exception": " استثناء برمجة التطبيقات. يرجى التحقق من عنوان الخادم والمحاولة مرة أخرى ",
|
"login_form_api_exception": " استثناء برمجة التطبيقات. يرجى التحقق من عنوان الخادم والمحاولة مرة أخرى ",
|
||||||
"login_form_back_button_text": "الرجوع للخلف",
|
"login_form_back_button_text": "الرجوع للخلف",
|
||||||
|
"login_form_button_text": "تسجيل الدخول",
|
||||||
"login_form_email_hint": "yoursemail@email.com",
|
"login_form_email_hint": "yoursemail@email.com",
|
||||||
"login_form_endpoint_hint": "http://your-server-ip:port",
|
"login_form_endpoint_hint": "http://your-server-ip:port",
|
||||||
"login_form_endpoint_url": "url نقطة نهاية الخادم",
|
"login_form_endpoint_url": "url نقطة نهاية الخادم",
|
||||||
@@ -1134,6 +1262,9 @@
|
|||||||
"login_form_failed_get_oauth_server_disable": "ميزة OAuth غير متوفرة على هذا الخادم",
|
"login_form_failed_get_oauth_server_disable": "ميزة OAuth غير متوفرة على هذا الخادم",
|
||||||
"login_form_failed_login": "خطأ في تسجيل الدخول ، تحقق من عنوان URL للخادم والبريد الإلكتروني وكلمة المرور",
|
"login_form_failed_login": "خطأ في تسجيل الدخول ، تحقق من عنوان URL للخادم والبريد الإلكتروني وكلمة المرور",
|
||||||
"login_form_handshake_exception": "كان هناك استثناء مصافحة مع الخادم.تمكين دعم الشهادة الموقعة ذاتيا في الإعدادات إذا كنت تستخدم شهادة موقعة ذاتيا.",
|
"login_form_handshake_exception": "كان هناك استثناء مصافحة مع الخادم.تمكين دعم الشهادة الموقعة ذاتيا في الإعدادات إذا كنت تستخدم شهادة موقعة ذاتيا.",
|
||||||
|
"login_form_label_email": "بريد إلكتروني",
|
||||||
|
"login_form_label_password": "كلمة المرور",
|
||||||
|
"login_form_next_button": "التالي",
|
||||||
"login_form_password_hint": "كلمة المرور",
|
"login_form_password_hint": "كلمة المرور",
|
||||||
"login_form_save_login": "ابق متصلا",
|
"login_form_save_login": "ابق متصلا",
|
||||||
"login_form_server_empty": "أدخل عنوان URL الخادم.",
|
"login_form_server_empty": "أدخل عنوان URL الخادم.",
|
||||||
@@ -1160,6 +1291,7 @@
|
|||||||
"map_assets_in_bound": "{} photo",
|
"map_assets_in_bound": "{} photo",
|
||||||
"map_assets_in_bounds": "{} photos",
|
"map_assets_in_bounds": "{} photos",
|
||||||
"map_cannot_get_user_location": "لا يمكن الحصول على موقع المستخدم",
|
"map_cannot_get_user_location": "لا يمكن الحصول على موقع المستخدم",
|
||||||
|
"map_location_dialog_cancel": "يلغي",
|
||||||
"map_location_dialog_yes": "نعم",
|
"map_location_dialog_yes": "نعم",
|
||||||
"map_location_picker_page_use_location": "استخدم هذا الموقع",
|
"map_location_picker_page_use_location": "استخدم هذا الموقع",
|
||||||
"map_location_service_disabled_content": "يجب تمكين خدمة الموقع لعرض الأصول من موقعك الحالي.هل تريد تمكينه الآن؟",
|
"map_location_service_disabled_content": "يجب تمكين خدمة الموقع لعرض الأصول من موقعك الحالي.هل تريد تمكينه الآن؟",
|
||||||
@@ -1171,13 +1303,17 @@
|
|||||||
"map_no_location_permission_title": "تم رفض إذن الموقع",
|
"map_no_location_permission_title": "تم رفض إذن الموقع",
|
||||||
"map_settings": "إعدادات الخريطة",
|
"map_settings": "إعدادات الخريطة",
|
||||||
"map_settings_dark_mode": "الوضع المظلم",
|
"map_settings_dark_mode": "الوضع المظلم",
|
||||||
|
"map_settings_date_range_option_all": "الجميع",
|
||||||
"map_settings_date_range_option_day": "24 ساعة الماضية",
|
"map_settings_date_range_option_day": "24 ساعة الماضية",
|
||||||
"map_settings_date_range_option_days": "Past {} days",
|
"map_settings_date_range_option_days": "Past {} days",
|
||||||
"map_settings_date_range_option_year": "السنة الفائتة",
|
"map_settings_date_range_option_year": "السنة الفائتة",
|
||||||
"map_settings_date_range_option_years": "Past {} years",
|
"map_settings_date_range_option_years": "Past {} years",
|
||||||
|
"map_settings_dialog_cancel": "يلغي",
|
||||||
|
"map_settings_dialog_save": "يحفظ",
|
||||||
"map_settings_dialog_title": "إعدادات الخريطة",
|
"map_settings_dialog_title": "إعدادات الخريطة",
|
||||||
"map_settings_include_show_archived": "تشمل الأرشفة",
|
"map_settings_include_show_archived": "تشمل الأرشفة",
|
||||||
"map_settings_include_show_partners": "تضمين الشركاء",
|
"map_settings_include_show_partners": "تضمين الشركاء",
|
||||||
|
"map_settings_only_relative_range": "نطاق الموعد",
|
||||||
"map_settings_only_show_favorites": "اظهار المفضلة فقط",
|
"map_settings_only_show_favorites": "اظهار المفضلة فقط",
|
||||||
"map_settings_theme_settings": "مظهر الخريطة",
|
"map_settings_theme_settings": "مظهر الخريطة",
|
||||||
"map_zoom_to_see_photos": "قم بتصغيرها لرؤية الصور",
|
"map_zoom_to_see_photos": "قم بتصغيرها لرؤية الصور",
|
||||||
@@ -1207,6 +1343,8 @@
|
|||||||
"month": "شهر",
|
"month": "شهر",
|
||||||
"monthly_title_text_date_format": "ط ط ط",
|
"monthly_title_text_date_format": "ط ط ط",
|
||||||
"more": "المزيد",
|
"more": "المزيد",
|
||||||
|
"motion_photos": "الصور المتحركه",
|
||||||
|
"motion_photos_page_title": "الصور المتحركة",
|
||||||
"moved_to_trash": "تم النقل إلى سلة المهملات",
|
"moved_to_trash": "تم النقل إلى سلة المهملات",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "لا يمكن تعديل تاريخ الأصول (المواد) للقراءة فقط، سوف يتخطى",
|
"multiselect_grid_edit_date_time_err_read_only": "لا يمكن تعديل تاريخ الأصول (المواد) للقراءة فقط، سوف يتخطى",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "لا يمكن تعديل موقع الأصول (المواد) للقراءة فقط، سوف يتخطى",
|
"multiselect_grid_edit_gps_err_read_only": "لا يمكن تعديل موقع الأصول (المواد) للقراءة فقط، سوف يتخطى",
|
||||||
@@ -1239,15 +1377,20 @@
|
|||||||
"no_favorites_message": "أضف المفضلة للعثور بسرعة على أفضل الصور ومقاطع الفيديو",
|
"no_favorites_message": "أضف المفضلة للعثور بسرعة على أفضل الصور ومقاطع الفيديو",
|
||||||
"no_libraries_message": "إنشاء مكتبة خارجية لعرض الصور ومقاطع الفيديو الخاصة بك",
|
"no_libraries_message": "إنشاء مكتبة خارجية لعرض الصور ومقاطع الفيديو الخاصة بك",
|
||||||
"no_name": "لا اسم",
|
"no_name": "لا اسم",
|
||||||
|
"no_objects": "لا توجد معلومات عن أشياء متاحة",
|
||||||
"no_places": "لا أماكن",
|
"no_places": "لا أماكن",
|
||||||
"no_results": "لا يوجد نتائج",
|
"no_results": "لا يوجد نتائج",
|
||||||
"no_results_description": "جرب كلمة رئيسية مرادفة أو أكثر عمومية",
|
"no_results_description": "جرب كلمة رئيسية مرادفة أو أكثر عمومية",
|
||||||
"no_shared_albums_message": "قم بإنشاء ألبوم لمشاركة الصور ومقاطع الفيديو مع الأشخاص في شبكتك",
|
"no_shared_albums_message": "قم بإنشاء ألبوم لمشاركة الصور ومقاطع الفيديو مع الأشخاص في شبكتك",
|
||||||
|
"none_selected": "لم يتم التحديد",
|
||||||
"not_in_any_album": "ليست في أي ألبوم",
|
"not_in_any_album": "ليست في أي ألبوم",
|
||||||
"not_selected": "Not selected",
|
"not_selected": "Not selected",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها مسبقًا، قم بتشغيل",
|
"note_apply_storage_label_to_previously_uploaded assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها مسبقًا، قم بتشغيل",
|
||||||
|
"note_unlimited_quota": "ملاحظة: أدخل 0 للحصة غير المحدودة",
|
||||||
"notes": "ملاحظات",
|
"notes": "ملاحظات",
|
||||||
|
"notification_permission_dialog_cancel": "يلغي",
|
||||||
"notification_permission_dialog_content": "لتمكين الإخطارات ، انتقل إلى الإعدادات و اختار السماح.",
|
"notification_permission_dialog_content": "لتمكين الإخطارات ، انتقل إلى الإعدادات و اختار السماح.",
|
||||||
|
"notification_permission_dialog_settings": "إعدادات",
|
||||||
"notification_permission_list_tile_content": "منح إذن لتمكين الإخطارات.",
|
"notification_permission_list_tile_content": "منح إذن لتمكين الإخطارات.",
|
||||||
"notification_permission_list_tile_enable_button": "تمكين الإخطارات",
|
"notification_permission_list_tile_enable_button": "تمكين الإخطارات",
|
||||||
"notification_permission_list_tile_title": "إذن الإخطار",
|
"notification_permission_list_tile_title": "إذن الإخطار",
|
||||||
@@ -1281,18 +1424,22 @@
|
|||||||
"other_variables": "متغيرات أخرى",
|
"other_variables": "متغيرات أخرى",
|
||||||
"owned": "مملوكة",
|
"owned": "مملوكة",
|
||||||
"owner": "المالك",
|
"owner": "المالك",
|
||||||
|
"page_share_add_users": "اضافة مستخدمين",
|
||||||
"partner": "شريك",
|
"partner": "شريك",
|
||||||
"partner_can_access": "يستطيع {partner} الوصول",
|
"partner_can_access": "يستطيع {partner} الوصول",
|
||||||
"partner_can_access_assets": "جميع الصور ومقاطع الفيديو الخاصة بك باستثناء تلك الموجودة في المؤرشفة والمحذوفة",
|
"partner_can_access_assets": "جميع الصور ومقاطع الفيديو الخاصة بك باستثناء تلك الموجودة في المؤرشفة والمحذوفة",
|
||||||
"partner_can_access_location": "الموقع الذي تم التقاط صورك فيه",
|
"partner_can_access_location": "الموقع الذي تم التقاط صورك فيه",
|
||||||
"partner_list_user_photos": "{user}'s photos",
|
"partner_list_user_photos": "{user}'s photos",
|
||||||
"partner_list_view_all": "عرض الكل",
|
"partner_list_view_all": "عرض الكل",
|
||||||
|
"partner_page_add_partner": "أضف شريكًا",
|
||||||
"partner_page_empty_message": "لم يتم مشاركة صورك بعد مع أي شريك.",
|
"partner_page_empty_message": "لم يتم مشاركة صورك بعد مع أي شريك.",
|
||||||
"partner_page_no_more_users": "لا مزيد من المستخدمين لإضافة",
|
"partner_page_no_more_users": "لا مزيد من المستخدمين لإضافة",
|
||||||
"partner_page_partner_add_failed": "فشل في إضافة شريك",
|
"partner_page_partner_add_failed": "فشل في إضافة شريك",
|
||||||
"partner_page_select_partner": "حدد شريكًا",
|
"partner_page_select_partner": "حدد شريكًا",
|
||||||
"partner_page_shared_to_title": "مشترك ل",
|
"partner_page_shared_to_title": "مشترك ل",
|
||||||
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
||||||
|
"partner_page_stop_sharing_title": "توقف عن مشاركة صورك؟",
|
||||||
|
"partner_page_title": "شريك",
|
||||||
"partner_sharing": "مشاركة الشركاء",
|
"partner_sharing": "مشاركة الشركاء",
|
||||||
"partners": "الشركاء",
|
"partners": "الشركاء",
|
||||||
"password": "كلمة المرور",
|
"password": "كلمة المرور",
|
||||||
@@ -1325,12 +1472,21 @@
|
|||||||
"permission_onboarding_continue_anyway": "تواصل على أي حال",
|
"permission_onboarding_continue_anyway": "تواصل على أي حال",
|
||||||
"permission_onboarding_get_started": "البدء",
|
"permission_onboarding_get_started": "البدء",
|
||||||
"permission_onboarding_go_to_settings": "اذهب للاعدادات",
|
"permission_onboarding_go_to_settings": "اذهب للاعدادات",
|
||||||
|
"permission_onboarding_grant_permission": "يعطي الأذن",
|
||||||
|
"permission_onboarding_log_out": "تسجيل خروج",
|
||||||
"permission_onboarding_permission_denied": "تم رفض الإذن. لاستخدام التطبيق، قم بمنح أذونات الصور والفيديو في الإعدادات ",
|
"permission_onboarding_permission_denied": "تم رفض الإذن. لاستخدام التطبيق، قم بمنح أذونات الصور والفيديو في الإعدادات ",
|
||||||
"permission_onboarding_permission_granted": "تم تأمين التصريح! وضعك تمام.",
|
"permission_onboarding_permission_granted": "تم تأمين التصريح! وضعك تمام.",
|
||||||
"permission_onboarding_permission_limited": "إذن محدود. للسماح بالنسخ الاحتياطي للتطبيق وإدارة مجموعة المعرض بالكامل، امنح أذونات الصور والفيديو في الإعدادات.",
|
"permission_onboarding_permission_limited": "إذن محدود. للسماح بالنسخ الاحتياطي للتطبيق وإدارة مجموعة المعرض بالكامل، امنح أذونات الصور والفيديو في الإعدادات.",
|
||||||
"permission_onboarding_request": "يتطلب التطبيق إذنًا لعرض الصور ومقاطع الفيديو الخاصة بك",
|
"permission_onboarding_request": "يتطلب التطبيق إذنًا لعرض الصور ومقاطع الفيديو الخاصة بك",
|
||||||
"person": "شخص",
|
"person": "شخص",
|
||||||
|
"person_add_name_dialog_cancel": "يلغي",
|
||||||
|
"person_add_name_dialog_hint": "اسم",
|
||||||
|
"person_add_name_dialog_save": "يحفظ",
|
||||||
|
"person_add_name_dialog_title": "أضف اسمًا",
|
||||||
|
"person_add_name_subtitle": "ابحث عنهم سريعًا بالاسم مع البحث",
|
||||||
|
"person_add_name_title": "أضف اسمًا",
|
||||||
"person_birthdate": "تاريخ الميلاد {التاريخ}",
|
"person_birthdate": "تاريخ الميلاد {التاريخ}",
|
||||||
|
"person_edit_name": "تعديل الاسم",
|
||||||
"person_hidden": "{name}{hidden, select, true { (مخفي)} other {}}",
|
"person_hidden": "{name}{hidden, select, true { (مخفي)} other {}}",
|
||||||
"photo_shared_all_users": "يبدو أنك شاركت صورك مع جميع المستخدمين أو ليس لديك أي مستخدم للمشاركة معه.",
|
"photo_shared_all_users": "يبدو أنك شاركت صورك مع جميع المستخدمين أو ليس لديك أي مستخدم للمشاركة معه.",
|
||||||
"photos": "الصور",
|
"photos": "الصور",
|
||||||
@@ -1354,14 +1510,20 @@
|
|||||||
"previous_memory": "الذكرى السابقة",
|
"previous_memory": "الذكرى السابقة",
|
||||||
"previous_or_next_photo": "الصورة السابقة أو التالية",
|
"previous_or_next_photo": "الصورة السابقة أو التالية",
|
||||||
"primary": "أساسي",
|
"primary": "أساسي",
|
||||||
|
"primary_color_subtitle": "Pick a color for primary actions and accents.",
|
||||||
|
"primary_color_title": "Primary color",
|
||||||
"privacy": "الخصوصية",
|
"privacy": "الخصوصية",
|
||||||
"profile_drawer_app_logs": "السجلات",
|
"profile_drawer_app_logs": "السجلات",
|
||||||
"profile_drawer_client_out_of_date_major": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار رئيسي.",
|
"profile_drawer_client_out_of_date_major": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار رئيسي.",
|
||||||
"profile_drawer_client_out_of_date_minor": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار صغير.",
|
"profile_drawer_client_out_of_date_minor": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار صغير.",
|
||||||
"profile_drawer_client_server_up_to_date": "العميل والخادم محدثان",
|
"profile_drawer_client_server_up_to_date": "العميل والخادم محدثان",
|
||||||
|
"profile_drawer_documentation": "توثيق",
|
||||||
"profile_drawer_github": "Github",
|
"profile_drawer_github": "Github",
|
||||||
"profile_drawer_server_out_of_date_major": "الخادم قديم.يرجى التحديث إلى أحدث إصدار رئيسي.",
|
"profile_drawer_server_out_of_date_major": "الخادم قديم.يرجى التحديث إلى أحدث إصدار رئيسي.",
|
||||||
"profile_drawer_server_out_of_date_minor": "الخادم قديم.يرجى التحديث إلى أحدث إصدار صغير.",
|
"profile_drawer_server_out_of_date_minor": "الخادم قديم.يرجى التحديث إلى أحدث إصدار صغير.",
|
||||||
|
"profile_drawer_settings": "إعدادات",
|
||||||
|
"profile_drawer_sign_out": "خروج",
|
||||||
|
"profile_drawer_trash": "نفايات",
|
||||||
"profile_image_of_user": "صورة الملف الشخصي لـ {user}",
|
"profile_image_of_user": "صورة الملف الشخصي لـ {user}",
|
||||||
"profile_picture_set": "مجموعة الصور الشخصية.",
|
"profile_picture_set": "مجموعة الصور الشخصية.",
|
||||||
"public_album": "الألبوم العام",
|
"public_album": "الألبوم العام",
|
||||||
@@ -1411,7 +1573,7 @@
|
|||||||
"recent": "حديث",
|
"recent": "حديث",
|
||||||
"recent-albums": "ألبومات الحديثة",
|
"recent-albums": "ألبومات الحديثة",
|
||||||
"recent_searches": "عمليات البحث الأخيرة",
|
"recent_searches": "عمليات البحث الأخيرة",
|
||||||
"recently_added": "Recently added",
|
"recently_added": "أضيف مؤخرا",
|
||||||
"recently_added_page_title": "أضيف مؤخرا",
|
"recently_added_page_title": "أضيف مؤخرا",
|
||||||
"refresh": "تحديث",
|
"refresh": "تحديث",
|
||||||
"refresh_encoded_videos": "تحديث مقاطع الفيديو المشفرة",
|
"refresh_encoded_videos": "تحديث مقاطع الفيديو المشفرة",
|
||||||
@@ -1424,6 +1586,8 @@
|
|||||||
"refreshing_faces": "جاري تحديث الوجوه",
|
"refreshing_faces": "جاري تحديث الوجوه",
|
||||||
"refreshing_metadata": "جارٍ تحديث البيانات الوصفية",
|
"refreshing_metadata": "جارٍ تحديث البيانات الوصفية",
|
||||||
"regenerating_thumbnails": "جارٍ تجديد الصور المصغرة",
|
"regenerating_thumbnails": "جارٍ تجديد الصور المصغرة",
|
||||||
|
"remainder": "بقية",
|
||||||
|
"remainder_sub": "الصور ومقاطع الفيديو المتبقية للنسخ الاحتياطي من التحديد",
|
||||||
"remove": "إزالة",
|
"remove": "إزالة",
|
||||||
"remove_assets_album_confirmation": "هل أنت متأكد أنك تريد إزالة {count, plural, one {# المحتوى} other {# المحتويات}} من الألبوم ؟",
|
"remove_assets_album_confirmation": "هل أنت متأكد أنك تريد إزالة {count, plural, one {# المحتوى} other {# المحتويات}} من الألبوم ؟",
|
||||||
"remove_assets_shared_link_confirmation": "هل أنت متأكد أنك تريد إزالة {count, plural, one {# المحتوى} other {# المحتويات}} من رابط المشاركة هذا؟",
|
"remove_assets_shared_link_confirmation": "هل أنت متأكد أنك تريد إزالة {count, plural, one {# المحتوى} other {# المحتويات}} من رابط المشاركة هذا؟",
|
||||||
@@ -1479,8 +1643,10 @@
|
|||||||
"scan_library": "مسح",
|
"scan_library": "مسح",
|
||||||
"scan_settings": "إعدادات الفحص",
|
"scan_settings": "إعدادات الفحص",
|
||||||
"scanning_for_album": "جارٍ الفحص عن ألبوم...",
|
"scanning_for_album": "جارٍ الفحص عن ألبوم...",
|
||||||
|
"screenshots": "لقطات الشاشة",
|
||||||
"search": "البحث",
|
"search": "البحث",
|
||||||
"search_albums": "البحث في الألبومات",
|
"search_albums": "البحث في الألبومات",
|
||||||
|
"search_bar_hint": "ابحث عن صورك",
|
||||||
"search_by_context": "البحث حسب السياق",
|
"search_by_context": "البحث حسب السياق",
|
||||||
"search_by_description": "البحث حسب الوصف",
|
"search_by_description": "البحث حسب الوصف",
|
||||||
"search_by_description_example": "يوم المشي لمسافات طويلة في سابا",
|
"search_by_description_example": "يوم المشي لمسافات طويلة في سابا",
|
||||||
@@ -1491,17 +1657,33 @@
|
|||||||
"search_city": "البحث حسب المدينة...",
|
"search_city": "البحث حسب المدينة...",
|
||||||
"search_country": "البحث حسب الدولة...",
|
"search_country": "البحث حسب الدولة...",
|
||||||
"search_filter_apply": "اختار الفلتر ",
|
"search_filter_apply": "اختار الفلتر ",
|
||||||
|
"search_filter_camera": "Camera",
|
||||||
|
"search_filter_camera_make": "صنع",
|
||||||
|
"search_filter_camera_model": "نموذج",
|
||||||
"search_filter_camera_title": "Select camera type",
|
"search_filter_camera_title": "Select camera type",
|
||||||
|
"search_filter_contextual": "Search by context",
|
||||||
"search_filter_date": "Date",
|
"search_filter_date": "Date",
|
||||||
"search_filter_date_interval": "{start} to {end}",
|
"search_filter_date_interval": "{start} to {end}",
|
||||||
"search_filter_date_title": "Select a date range",
|
"search_filter_date_title": "Select a date range",
|
||||||
|
"search_filter_description": "Search by description",
|
||||||
|
"search_filter_display_option_archive": "أرشيف",
|
||||||
|
"search_filter_display_option_favorite": "مفضل",
|
||||||
"search_filter_display_option_not_in_album": "ليس في الألبوم",
|
"search_filter_display_option_not_in_album": "ليس في الألبوم",
|
||||||
"search_filter_display_options": "Display Options",
|
"search_filter_display_options": "Display Options",
|
||||||
|
"search_filter_display_options_title": "Display options",
|
||||||
"search_filter_filename": "Search by file name",
|
"search_filter_filename": "Search by file name",
|
||||||
"search_filter_location": "Location",
|
"search_filter_location": "Location",
|
||||||
|
"search_filter_location_city": "مدينة",
|
||||||
|
"search_filter_location_country": "دولة",
|
||||||
|
"search_filter_location_state": "ولاية",
|
||||||
"search_filter_location_title": "Select location",
|
"search_filter_location_title": "Select location",
|
||||||
"search_filter_media_type": "Media Type",
|
"search_filter_media_type": "Media Type",
|
||||||
|
"search_filter_media_type_all": "الجميع",
|
||||||
|
"search_filter_media_type_image": "صورة",
|
||||||
"search_filter_media_type_title": "Select media type",
|
"search_filter_media_type_title": "Select media type",
|
||||||
|
"search_filter_media_type_video": "شريط فيديو",
|
||||||
|
"search_filter_people": "People",
|
||||||
|
"search_filter_people_hint": "Filter people",
|
||||||
"search_filter_people_title": "Select people",
|
"search_filter_people_title": "Select people",
|
||||||
"search_for": "البحث عن",
|
"search_for": "البحث عن",
|
||||||
"search_for_existing_person": "البحث عن شخص موجود",
|
"search_for_existing_person": "البحث عن شخص موجود",
|
||||||
@@ -1511,17 +1693,30 @@
|
|||||||
"search_no_result": "No results found, try a different search term or combination",
|
"search_no_result": "No results found, try a different search term or combination",
|
||||||
"search_options": "خيارات البحث",
|
"search_options": "خيارات البحث",
|
||||||
"search_page_categories": "فئات",
|
"search_page_categories": "فئات",
|
||||||
|
"search_page_favorites": "المفضلة",
|
||||||
"search_page_motion_photos": "الصور المتحركه",
|
"search_page_motion_photos": "الصور المتحركه",
|
||||||
"search_page_no_objects": "لا توجد معلومات عن أشياء متاحة",
|
"search_page_no_objects": "لا توجد معلومات عن أشياء متاحة",
|
||||||
"search_page_no_places": "لا توجد معلومات متوفرة للأماكن",
|
"search_page_no_places": "لا توجد معلومات متوفرة للأماكن",
|
||||||
|
"search_page_people": "الناس",
|
||||||
|
"search_page_person_add_name_dialog_cancel": "يلغي",
|
||||||
|
"search_page_person_add_name_dialog_hint": "اسم",
|
||||||
|
"search_page_person_add_name_dialog_save": "يحفظ",
|
||||||
|
"search_page_person_add_name_dialog_title": "أضف اسمًا",
|
||||||
|
"search_page_person_add_name_subtitle": "ابحث عنهم سريعًا بالاسم مع البحث",
|
||||||
|
"search_page_person_add_name_title": "أضف اسمًا",
|
||||||
|
"search_page_person_edit_name": "تعديل الاسم",
|
||||||
|
"search_page_places": "أماكن",
|
||||||
|
"search_page_recently_added": "أضيف مؤخرا",
|
||||||
"search_page_screenshots": "لقطات الشاشة",
|
"search_page_screenshots": "لقطات الشاشة",
|
||||||
"search_page_search_photos_videos": "Search for your photos and videos",
|
"search_page_search_photos_videos": "Search for your photos and videos",
|
||||||
"search_page_selfies": " صور ذاتيه",
|
"search_page_selfies": " صور ذاتيه",
|
||||||
"search_page_things": "أشياء",
|
"search_page_things": "أشياء",
|
||||||
|
"search_page_videos": "أشرطة فيديو",
|
||||||
"search_page_view_all_button": "عرض الكل",
|
"search_page_view_all_button": "عرض الكل",
|
||||||
"search_page_your_activity": "نشاطك",
|
"search_page_your_activity": "نشاطك",
|
||||||
"search_page_your_map": "خريطتك",
|
"search_page_your_map": "خريطتك",
|
||||||
"search_people": "البحث عن الأشخاص",
|
"search_people": "البحث عن الأشخاص",
|
||||||
|
"search_photos_videos": "Search for your photos and videos",
|
||||||
"search_places": "البحث عن الأماكن",
|
"search_places": "البحث عن الأماكن",
|
||||||
"search_rating": "البحث حسب التقييم...",
|
"search_rating": "البحث حسب التقييم...",
|
||||||
"search_result_page_new_search_hint": "بحث جديد",
|
"search_result_page_new_search_hint": "بحث جديد",
|
||||||
@@ -1537,6 +1732,7 @@
|
|||||||
"second": "ثانية",
|
"second": "ثانية",
|
||||||
"see_all_people": "عرض جميع الأشخاص",
|
"see_all_people": "عرض جميع الأشخاص",
|
||||||
"select": "إختر",
|
"select": "إختر",
|
||||||
|
"select_additional_user_for_sharing_page_suggestions": "اقتراحات",
|
||||||
"select_album_cover": "تحديد غلاف الألبوم",
|
"select_album_cover": "تحديد غلاف الألبوم",
|
||||||
"select_all": "تحديد الكل",
|
"select_all": "تحديد الكل",
|
||||||
"select_all_duplicates": "تحديد جميع النسخ المكررة",
|
"select_all_duplicates": "تحديد جميع النسخ المكررة",
|
||||||
@@ -1550,16 +1746,21 @@
|
|||||||
"select_photos": "تحديد الصور",
|
"select_photos": "تحديد الصور",
|
||||||
"select_trash_all": "تحديد حذف الكلِ",
|
"select_trash_all": "تحديد حذف الكلِ",
|
||||||
"select_user_for_sharing_page_err_album": "فشل في إنشاء ألبوم",
|
"select_user_for_sharing_page_err_album": "فشل في إنشاء ألبوم",
|
||||||
|
"select_user_for_sharing_page_share_suggestions": "اقتراحات",
|
||||||
"selected": "التحديد",
|
"selected": "التحديد",
|
||||||
"selected_count": "{count, plural, other {# محددة }}",
|
"selected_count": "{count, plural, other {# محددة }}",
|
||||||
|
"selfies": " صور ذاتيه",
|
||||||
"send_message": "إرسال رسالة",
|
"send_message": "إرسال رسالة",
|
||||||
"send_welcome_email": "إرسال بريدًا إلكترونيًا ترحيبيًا",
|
"send_welcome_email": "إرسال بريدًا إلكترونيًا ترحيبيًا",
|
||||||
"server_endpoint": "Server Endpoint",
|
"server_endpoint": "Server Endpoint",
|
||||||
"server_info_box_app_version": "نسخة التطبيق",
|
"server_info_box_app_version": "نسخة التطبيق",
|
||||||
|
"server_info_box_latest_release": "احدث اصدار",
|
||||||
"server_info_box_server_url": "عنوان URL الخادم",
|
"server_info_box_server_url": "عنوان URL الخادم",
|
||||||
|
"server_info_box_server_version": "إصدار الخادم",
|
||||||
"server_offline": "الخادم غير متصل",
|
"server_offline": "الخادم غير متصل",
|
||||||
"server_online": "الخادم متصل",
|
"server_online": "الخادم متصل",
|
||||||
"server_stats": "إحصائيات الخادم",
|
"server_stats": "إحصائيات الخادم",
|
||||||
|
"server_storage": "ذاكرة الجهاز",
|
||||||
"server_version": "إصدار الخادم",
|
"server_version": "إصدار الخادم",
|
||||||
"set": "تحديد",
|
"set": "تحديد",
|
||||||
"set_as_album_cover": "تحديد كغلاف للألبوم",
|
"set_as_album_cover": "تحديد كغلاف للألبوم",
|
||||||
@@ -1586,25 +1787,42 @@
|
|||||||
"setting_notifications_single_progress_subtitle": "معلومات التقدم التفصيلية تحميل لكل أصل",
|
"setting_notifications_single_progress_subtitle": "معلومات التقدم التفصيلية تحميل لكل أصل",
|
||||||
"setting_notifications_single_progress_title": "إظهار تقدم التفاصيل الاحتياطية الخلفية",
|
"setting_notifications_single_progress_title": "إظهار تقدم التفاصيل الاحتياطية الخلفية",
|
||||||
"setting_notifications_subtitle": "اضبط تفضيلات الإخطار",
|
"setting_notifications_subtitle": "اضبط تفضيلات الإخطار",
|
||||||
|
"setting_notifications_title": "إشعارات",
|
||||||
"setting_notifications_total_progress_subtitle": "التقدم التحميل العام (تم القيام به/إجمالي الأصول)",
|
"setting_notifications_total_progress_subtitle": "التقدم التحميل العام (تم القيام به/إجمالي الأصول)",
|
||||||
"setting_notifications_total_progress_title": "إظهار النسخ الاحتياطي الخلفية التقدم المحرز",
|
"setting_notifications_total_progress_title": "إظهار النسخ الاحتياطي الخلفية التقدم المحرز",
|
||||||
|
"setting_pages_app_bar_settings": "إعدادات",
|
||||||
|
"setting_video_viewer_looping_subtitle": "تمكين تكرار مقطع فيديو تلقائيًا في عارض التفاصيل.",
|
||||||
"setting_video_viewer_looping_title": "تكرار مقطع فيديو تلقائيًا",
|
"setting_video_viewer_looping_title": "تكرار مقطع فيديو تلقائيًا",
|
||||||
"setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.",
|
"setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.",
|
||||||
"setting_video_viewer_original_video_title": "Force original video",
|
"setting_video_viewer_original_video_title": "Force original video",
|
||||||
|
"setting_video_viewer_title": "أشرطة فيديو",
|
||||||
"settings": "الإعدادات",
|
"settings": "الإعدادات",
|
||||||
"settings_require_restart": "يرجى إعادة تشغيل لتطبيق هذا الإعداد",
|
"settings_require_restart": "يرجى إعادة تشغيل لتطبيق هذا الإعداد",
|
||||||
"settings_saved": "تم حفظ الإعدادات",
|
"settings_saved": "تم حفظ الإعدادات",
|
||||||
|
"settings_subtitle": "إعدادات تخطيط شبكة الصور",
|
||||||
|
"settings_title": "شبكة الصور",
|
||||||
"share": "مشاركة",
|
"share": "مشاركة",
|
||||||
|
"share_add": "يضيف",
|
||||||
"share_add_photos": "إضافة الصور",
|
"share_add_photos": "إضافة الصور",
|
||||||
|
"share_add_title": "إضافة عنوان",
|
||||||
"share_assets_selected": "{} selected",
|
"share_assets_selected": "{} selected",
|
||||||
|
"share_create_album": "إنشاء ألبوم",
|
||||||
"share_dialog_preparing": "تحضير...",
|
"share_dialog_preparing": "تحضير...",
|
||||||
|
"share_done": "منتهي",
|
||||||
|
"share_invite": "دعوة إلى الألبوم",
|
||||||
|
"share_link": "Share Link",
|
||||||
|
"share_to": "مشاركة إلى",
|
||||||
"shared": "مُشتَرك",
|
"shared": "مُشتَرك",
|
||||||
"shared_album_activities_input_disable": "التعليق معطل",
|
"shared_album_activities_input_disable": "التعليق معطل",
|
||||||
|
"shared_album_activities_input_hint": "قل شيئا",
|
||||||
"shared_album_activity_remove_content": "هل تريد حذف هذا النشاط؟",
|
"shared_album_activity_remove_content": "هل تريد حذف هذا النشاط؟",
|
||||||
"shared_album_activity_remove_title": "حذف النشاط",
|
"shared_album_activity_remove_title": "حذف النشاط",
|
||||||
|
"shared_album_activity_setting_subtitle": "دع الآخرين يستجيبون",
|
||||||
|
"shared_album_activity_setting_title": "التعليقات والأحب",
|
||||||
"shared_album_section_people_action_error": "خطأ ترك/إزالة من الألبوم",
|
"shared_album_section_people_action_error": "خطأ ترك/إزالة من الألبوم",
|
||||||
"shared_album_section_people_action_leave": "إزالة المستخدم من الألبوم",
|
"shared_album_section_people_action_leave": "إزالة المستخدم من الألبوم",
|
||||||
"shared_album_section_people_action_remove_user": "إزالة المستخدم من الألبوم",
|
"shared_album_section_people_action_remove_user": "إزالة المستخدم من الألبوم",
|
||||||
|
"shared_album_section_people_owner_label": "مالك",
|
||||||
"shared_album_section_people_title": "الناس",
|
"shared_album_section_people_title": "الناس",
|
||||||
"shared_by": "تمت مشاركته بواسطة",
|
"shared_by": "تمت مشاركته بواسطة",
|
||||||
"shared_by_user": "تمت المشاركة بواسطة {user}",
|
"shared_by_user": "تمت المشاركة بواسطة {user}",
|
||||||
@@ -1614,8 +1832,17 @@
|
|||||||
"shared_link_app_bar_title": "روابط مشتركة",
|
"shared_link_app_bar_title": "روابط مشتركة",
|
||||||
"shared_link_clipboard_copied_massage": "نسخ إلى الحافظة",
|
"shared_link_clipboard_copied_massage": "نسخ إلى الحافظة",
|
||||||
"shared_link_clipboard_text": "Link: {}\nPassword: {}",
|
"shared_link_clipboard_text": "Link: {}\nPassword: {}",
|
||||||
|
"shared_link_create_app_bar_title": "إنشاء رابط للمشاركة",
|
||||||
"shared_link_create_error": "خطأ أثناء إنشاء رابط مشترك",
|
"shared_link_create_error": "خطأ أثناء إنشاء رابط مشترك",
|
||||||
|
"shared_link_create_info": "دع أي شخص لديه الرابط يرى الصور المحددة)",
|
||||||
|
"shared_link_create_submit_button": "إنشاء رابط",
|
||||||
|
"shared_link_edit_allow_download": "السماح للمستخدم العام بالتنزيل",
|
||||||
|
"shared_link_edit_allow_upload": "السماح للمستخدم العام بالتحميل",
|
||||||
|
"shared_link_edit_app_bar_title": "تغيير الرابط",
|
||||||
|
"shared_link_edit_change_expiry": "تغيير وقت انتهاء الصلاحية",
|
||||||
|
"shared_link_edit_description": "وصف",
|
||||||
"shared_link_edit_description_hint": "أدخل وصف المشاركة",
|
"shared_link_edit_description_hint": "أدخل وصف المشاركة",
|
||||||
|
"shared_link_edit_expire_after": "تنتهي بعد",
|
||||||
"shared_link_edit_expire_after_option_day": "يوم 1",
|
"shared_link_edit_expire_after_option_day": "يوم 1",
|
||||||
"shared_link_edit_expire_after_option_days": "{} days",
|
"shared_link_edit_expire_after_option_days": "{} days",
|
||||||
"shared_link_edit_expire_after_option_hour": "1 ساعة",
|
"shared_link_edit_expire_after_option_hour": "1 ساعة",
|
||||||
@@ -1623,10 +1850,15 @@
|
|||||||
"shared_link_edit_expire_after_option_minute": "1 دقيقة",
|
"shared_link_edit_expire_after_option_minute": "1 دقيقة",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minutes",
|
"shared_link_edit_expire_after_option_minutes": "{} minutes",
|
||||||
"shared_link_edit_expire_after_option_months": "{} months",
|
"shared_link_edit_expire_after_option_months": "{} months",
|
||||||
|
"shared_link_edit_expire_after_option_never": "أبداً",
|
||||||
"shared_link_edit_expire_after_option_year": "{} year",
|
"shared_link_edit_expire_after_option_year": "{} year",
|
||||||
|
"shared_link_edit_password": "كلمة المرور",
|
||||||
"shared_link_edit_password_hint": "أدخل كلمة مرور المشاركة",
|
"shared_link_edit_password_hint": "أدخل كلمة مرور المشاركة",
|
||||||
|
"shared_link_edit_show_meta": "عرض البيانات الوصفية",
|
||||||
"shared_link_edit_submit_button": "تحديث الرابط",
|
"shared_link_edit_submit_button": "تحديث الرابط",
|
||||||
|
"shared_link_empty": "ليس لديك أي روابط مشتركة",
|
||||||
"shared_link_error_server_url_fetch": "لا يمكن جلب عنوان الخادم",
|
"shared_link_error_server_url_fetch": "لا يمكن جلب عنوان الخادم",
|
||||||
|
"shared_link_expired": "منتهي الصلاحية",
|
||||||
"shared_link_expires_day": "Expires in {} day",
|
"shared_link_expires_day": "Expires in {} day",
|
||||||
"shared_link_expires_days": "Expires in {} days",
|
"shared_link_expires_days": "Expires in {} days",
|
||||||
"shared_link_expires_hour": "Expires in {} hour",
|
"shared_link_expires_hour": "Expires in {} hour",
|
||||||
@@ -1637,9 +1869,12 @@
|
|||||||
"shared_link_expires_second": "Expires in {} second",
|
"shared_link_expires_second": "Expires in {} second",
|
||||||
"shared_link_expires_seconds": "Expires in {} seconds",
|
"shared_link_expires_seconds": "Expires in {} seconds",
|
||||||
"shared_link_individual_shared": "Individual shared",
|
"shared_link_individual_shared": "Individual shared",
|
||||||
|
"shared_link_info_chip_download": "تحميل",
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
|
"shared_link_info_chip_upload": "رفع",
|
||||||
"shared_link_manage_links": "إدارة الروابط المشتركة",
|
"shared_link_manage_links": "إدارة الروابط المشتركة",
|
||||||
"shared_link_options": "خيارات الرابط المشترك",
|
"shared_link_options": "خيارات الرابط المشترك",
|
||||||
|
"shared_link_public_album": "الألبوم العام",
|
||||||
"shared_links": "روابط مشتركة",
|
"shared_links": "روابط مشتركة",
|
||||||
"shared_links_description": "وصف الروابط المشتركة",
|
"shared_links_description": "وصف الروابط المشتركة",
|
||||||
"shared_photos_and_videos_count": "{assetCount, plural, other {# الصور ومقاطع الفيديو المُشارَكة.}}",
|
"shared_photos_and_videos_count": "{assetCount, plural, other {# الصور ومقاطع الفيديو المُشارَكة.}}",
|
||||||
@@ -1653,6 +1888,7 @@
|
|||||||
"sharing_sidebar_description": "اعرض رابطًا للمشاركة في الشريط الجانبي",
|
"sharing_sidebar_description": "اعرض رابطًا للمشاركة في الشريط الجانبي",
|
||||||
"sharing_silver_appbar_create_shared_album": "ألبوم مشترك جديد",
|
"sharing_silver_appbar_create_shared_album": "ألبوم مشترك جديد",
|
||||||
"sharing_silver_appbar_share_partner": "شارك مع الشريك",
|
"sharing_silver_appbar_share_partner": "شارك مع الشريك",
|
||||||
|
"sharing_silver_appbar_shared_links": "روابط مشتركة",
|
||||||
"shift_to_permanent_delete": "اضغط على ⇧ لحذف المحتوى نهائيًا",
|
"shift_to_permanent_delete": "اضغط على ⇧ لحذف المحتوى نهائيًا",
|
||||||
"show_album_options": "إظهار خيارات الألبوم",
|
"show_album_options": "إظهار خيارات الألبوم",
|
||||||
"show_albums": "إظهار الألبومات",
|
"show_albums": "إظهار الألبومات",
|
||||||
@@ -1701,17 +1937,28 @@
|
|||||||
"stacked_assets_count": "تم تكديس {count, plural, one {# المحتوى} other {# المحتويات}}",
|
"stacked_assets_count": "تم تكديس {count, plural, one {# المحتوى} other {# المحتويات}}",
|
||||||
"stacktrace": "تتّبُع التكديس",
|
"stacktrace": "تتّبُع التكديس",
|
||||||
"start": "ابدأ",
|
"start": "ابدأ",
|
||||||
|
"start_backup": "بدء النسخ الاحتياطي",
|
||||||
"start_date": "تاريخ البدء",
|
"start_date": "تاريخ البدء",
|
||||||
"state": "الولاية",
|
"state": "الولاية",
|
||||||
|
"statistics_album": "مكتبه الصور المصغره",
|
||||||
|
"statistics_assets": "{} assets ({})",
|
||||||
|
"statistics_full": "صور كاملة",
|
||||||
|
"statistics_shared": "صورة ألبوم مشتركة",
|
||||||
|
"statistics_thumbnail": "الصورة المصغرة",
|
||||||
|
"statistics_title": "استخدام ذاكرة التخزين المؤقت",
|
||||||
"status": "الحالة",
|
"status": "الحالة",
|
||||||
|
"status_off": "النسخة الاحتياطية التلقائية غير فعالة",
|
||||||
|
"status_on": "النسخة الاحتياطية التلقائية فعالة",
|
||||||
"stop_motion_photo": "إيقاف حركة الصورة",
|
"stop_motion_photo": "إيقاف حركة الصورة",
|
||||||
"stop_photo_sharing": "توقف عن مشاركة صورك؟",
|
"stop_photo_sharing": "توقف عن مشاركة صورك؟",
|
||||||
"stop_photo_sharing_description": "لن يتمكن {partner} من الوصول إلى صورك بعد الآن.",
|
"stop_photo_sharing_description": "لن يتمكن {partner} من الوصول إلى صورك بعد الآن.",
|
||||||
"stop_sharing_photos_with_user": "توقف عن مشاركة صورك مع هذا المستخدم",
|
"stop_sharing_photos_with_user": "توقف عن مشاركة صورك مع هذا المستخدم",
|
||||||
"storage": "مساحة التخزين",
|
"storage": "مساحة التخزين",
|
||||||
|
"storage_format": "{} of {} used",
|
||||||
"storage_label": "تسمية التخزين",
|
"storage_label": "تسمية التخزين",
|
||||||
"storage_usage": "{used} من {available} مُستخْدم",
|
"storage_usage": "{used} من {available} مُستخْدم",
|
||||||
"submit": "إرسال",
|
"submit": "إرسال",
|
||||||
|
"subtitle": "تحكم في سلوك التخزين المؤقت لتطبيق الجوال.",
|
||||||
"suggestions": "اقتراحات",
|
"suggestions": "اقتراحات",
|
||||||
"sunrise_on_the_beach": "شروق الشمس على الشاطئ",
|
"sunrise_on_the_beach": "شروق الشمس على الشاطئ",
|
||||||
"support": "الدعم",
|
"support": "الدعم",
|
||||||
@@ -1722,6 +1969,12 @@
|
|||||||
"sync_albums": "Sync albums",
|
"sync_albums": "Sync albums",
|
||||||
"sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums",
|
"sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums",
|
||||||
"sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich",
|
"sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich",
|
||||||
|
"system_primary_color_title": "Use system color",
|
||||||
|
"system_theme_switch": "تلقائي (اتبع إعداد النظام)",
|
||||||
|
"tab_controller_nav_library": "مكتبة",
|
||||||
|
"tab_controller_nav_photos": "الصور",
|
||||||
|
"tab_controller_nav_search": "يبحث",
|
||||||
|
"tab_controller_nav_sharing": "مشاركة",
|
||||||
"tag": "العلامة",
|
"tag": "العلامة",
|
||||||
"tag_assets": "أصول العلامة",
|
"tag_assets": "أصول العلامة",
|
||||||
"tag_created": "تم إنشاء العلامة: {tag}",
|
"tag_created": "تم إنشاء العلامة: {tag}",
|
||||||
@@ -1739,6 +1992,7 @@
|
|||||||
"theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})",
|
||||||
"theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.",
|
"theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.",
|
||||||
"theme_setting_colorful_interface_title": "Colorful interface",
|
"theme_setting_colorful_interface_title": "Colorful interface",
|
||||||
|
"theme_setting_dark_mode_switch": "الوضع المظلم",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "اضبط جودة عارض الصورة التفصيلية",
|
"theme_setting_image_viewer_quality_subtitle": "اضبط جودة عارض الصورة التفصيلية",
|
||||||
"theme_setting_image_viewer_quality_title": "جودة عارض الصورة",
|
"theme_setting_image_viewer_quality_title": "جودة عارض الصورة",
|
||||||
"theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.",
|
"theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.",
|
||||||
@@ -1746,14 +2000,25 @@
|
|||||||
"theme_setting_system_primary_color_title": "Use system color",
|
"theme_setting_system_primary_color_title": "Use system color",
|
||||||
"theme_setting_system_theme_switch": "تلقائي (اتبع إعداد النظام)",
|
"theme_setting_system_theme_switch": "تلقائي (اتبع إعداد النظام)",
|
||||||
"theme_setting_theme_subtitle": "اختر إعدادات مظهر التطبيق",
|
"theme_setting_theme_subtitle": "اختر إعدادات مظهر التطبيق",
|
||||||
|
"theme_setting_theme_title": "مظهر",
|
||||||
"theme_setting_three_stage_loading_subtitle": "قد يزيد التحميل من ثلاث مراحل من أداء التحميل ولكنه يسبب تحميل شبكة أعلى بكثير",
|
"theme_setting_three_stage_loading_subtitle": "قد يزيد التحميل من ثلاث مراحل من أداء التحميل ولكنه يسبب تحميل شبكة أعلى بكثير",
|
||||||
"theme_setting_three_stage_loading_title": "تمكين تحميل ثلاث مراحل",
|
"theme_setting_three_stage_loading_title": "تمكين تحميل ثلاث مراحل",
|
||||||
|
"theme_subtitle": "اختر إعدادات مظهر التطبيق",
|
||||||
|
"theme_title": "مظهر",
|
||||||
"they_will_be_merged_together": "سيتم دمجهم معًا",
|
"they_will_be_merged_together": "سيتم دمجهم معًا",
|
||||||
|
"things": "أشياء",
|
||||||
"third_party_resources": "موارد الطرف الثالث",
|
"third_party_resources": "موارد الطرف الثالث",
|
||||||
|
"three_stage_loading_subtitle": "قد يزيد التحميل من ثلاث مراحل من أداء التحميل ولكنه يسبب تحميل شبكة أعلى بكثير",
|
||||||
|
"three_stage_loading_title": "تمكين تحميل ثلاث مراحل",
|
||||||
|
"thumbnail_size": "Thumbnail cache size ({} assets)",
|
||||||
|
"tile_subtitle": "التحكم في سلوك التخزين المحلي",
|
||||||
|
"tile_title": "التخزين المحلي",
|
||||||
"time_based_memories": "ذكريات استنادًا للوقت",
|
"time_based_memories": "ذكريات استنادًا للوقت",
|
||||||
"timeline": "الخط الزمني",
|
"timeline": "الخط الزمني",
|
||||||
"timezone": "المنطقة الزمنية",
|
"timezone": "المنطقة الزمنية",
|
||||||
|
"title": "إعدادات التخزين المؤقت",
|
||||||
"to_archive": "أرشفة",
|
"to_archive": "أرشفة",
|
||||||
|
"to_backup": "الألبومات الاحتياطية",
|
||||||
"to_change_password": "تغيير كلمة المرور",
|
"to_change_password": "تغيير كلمة المرور",
|
||||||
"to_favorite": "تفضيل",
|
"to_favorite": "تفضيل",
|
||||||
"to_login": "تسجيل الدخول",
|
"to_login": "تسجيل الدخول",
|
||||||
@@ -1762,21 +2027,31 @@
|
|||||||
"toggle_settings": "الإعدادات",
|
"toggle_settings": "الإعدادات",
|
||||||
"toggle_theme": "تبديل المظهر الداكن",
|
"toggle_theme": "تبديل المظهر الداكن",
|
||||||
"total": "الإجمالي",
|
"total": "الإجمالي",
|
||||||
|
"total_sub": "جميع الصور ومقاطع الفيديو الفريدة من ألبومات مختارة",
|
||||||
"total_usage": "الاستخدام الإجمالي",
|
"total_usage": "الاستخدام الإجمالي",
|
||||||
|
"translated_text_options": "خيارات",
|
||||||
"trash": "المهملات",
|
"trash": "المهملات",
|
||||||
"trash_all": "نقل الكل إلى سلة المهملات",
|
"trash_all": "نقل الكل إلى سلة المهملات",
|
||||||
"trash_count": "سلة المحملات {count, number}",
|
"trash_count": "سلة المحملات {count, number}",
|
||||||
"trash_delete_asset": "حذف/نقل المحتوى إلى سلة المهملات",
|
"trash_delete_asset": "حذف/نقل المحتوى إلى سلة المهملات",
|
||||||
"trash_emptied": "Emptied trash",
|
"trash_emptied": "Emptied trash",
|
||||||
|
"trash_from_immich": "حذفه ونقله في سله المهملات",
|
||||||
"trash_no_results_message": "ستظهر هنا الصور ومقاطع الفيديو المحذوفة.",
|
"trash_no_results_message": "ستظهر هنا الصور ومقاطع الفيديو المحذوفة.",
|
||||||
|
"trash_page_delete": "مسح",
|
||||||
"trash_page_delete_all": "حذف الكل",
|
"trash_page_delete_all": "حذف الكل",
|
||||||
|
"trash_page_empty_trash_btn": "افرغ سله المهملات ",
|
||||||
"trash_page_empty_trash_dialog_content": "هل تريد تفريغ أصولك المهملة؟ ستتم إزالة هذه العناصر نهائيًا من التطبيق",
|
"trash_page_empty_trash_dialog_content": "هل تريد تفريغ أصولك المهملة؟ ستتم إزالة هذه العناصر نهائيًا من التطبيق",
|
||||||
|
"trash_page_empty_trash_dialog_ok": "نعم",
|
||||||
"trash_page_info": "Trashed items will be permanently deleted after {} days",
|
"trash_page_info": "Trashed items will be permanently deleted after {} days",
|
||||||
"trash_page_no_assets": "لا توجد اصول في سله المهملات",
|
"trash_page_no_assets": "لا توجد اصول في سله المهملات",
|
||||||
|
"trash_page_restore": "الترجيع من سله المهملات",
|
||||||
"trash_page_restore_all": "استعادة الكل",
|
"trash_page_restore_all": "استعادة الكل",
|
||||||
"trash_page_select_assets_btn": "اختر الأصول ",
|
"trash_page_select_assets_btn": "اختر الأصول ",
|
||||||
|
"trash_page_select_btn": "يختار",
|
||||||
"trash_page_title": "Trash ({})",
|
"trash_page_title": "Trash ({})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "سيتم حذفُ العناصر المحذوفة نِهائيًا بعد {days, plural, one {# يوم} other {# أيام }}.",
|
"trashed_items_will_be_permanently_deleted_after": "سيتم حذفُ العناصر المحذوفة نِهائيًا بعد {days, plural, one {# يوم} other {# أيام }}.",
|
||||||
|
"turn_off": "قم بإيقاف تشغيل النسخ الاحتياطي المقدمة",
|
||||||
|
"turn_on": "قم بتشغيل النسخ الاحتياطي المقدمة",
|
||||||
"type": "النوع",
|
"type": "النوع",
|
||||||
"unarchive": "أخرج من الأرشيف",
|
"unarchive": "أخرج من الأرشيف",
|
||||||
"unarchived_count": "{count, plural, other {غير مؤرشفة #}}",
|
"unarchived_count": "{count, plural, other {غير مؤرشفة #}}",
|
||||||
@@ -1801,10 +2076,13 @@
|
|||||||
"untracked_files": "الملفات التي لم يتم تعقبها",
|
"untracked_files": "الملفات التي لم يتم تعقبها",
|
||||||
"untracked_files_decription": "لا يتم تعقب هذه الملفات بواسطة التطبيق. يمكن أن تكون نتيجةً لعمليات نقل فاشلة، أو عمليات رفع متقطعة، أو يتم تركها في الخلف بسبب خللاً ما",
|
"untracked_files_decription": "لا يتم تعقب هذه الملفات بواسطة التطبيق. يمكن أن تكون نتيجةً لعمليات نقل فاشلة، أو عمليات رفع متقطعة، أو يتم تركها في الخلف بسبب خللاً ما",
|
||||||
"up_next": "التالي",
|
"up_next": "التالي",
|
||||||
|
"update": "تحديث",
|
||||||
"updated_password": "تم تحديث كلمة المرور",
|
"updated_password": "تم تحديث كلمة المرور",
|
||||||
"upload": "رفع",
|
"upload": "رفع",
|
||||||
"upload_concurrency": "الرفع المتزامن",
|
"upload_concurrency": "الرفع المتزامن",
|
||||||
|
"upload_dialog_cancel": "يلغي",
|
||||||
"upload_dialog_info": "هل تريد النسخ الاحتياطي للأصول (الأصول) المحددة إلى الخادم؟",
|
"upload_dialog_info": "هل تريد النسخ الاحتياطي للأصول (الأصول) المحددة إلى الخادم؟",
|
||||||
|
"upload_dialog_ok": "رفع",
|
||||||
"upload_dialog_title": "تحميل الأصول",
|
"upload_dialog_title": "تحميل الأصول",
|
||||||
"upload_errors": "إكتمل الرفع مع {count, plural, one {# خطأ} other {# أخطاء}}, قم بتحديث الصفحة لرؤية المحتويات الجديدة التي تم رفعها.",
|
"upload_errors": "إكتمل الرفع مع {count, plural, one {# خطأ} other {# أخطاء}}, قم بتحديث الصفحة لرؤية المحتويات الجديدة التي تم رفعها.",
|
||||||
"upload_progress": "متبقية {remaining, number} - معالجة {processed, number}/{total, number}",
|
"upload_progress": "متبقية {remaining, number} - معالجة {processed, number}/{total, number}",
|
||||||
@@ -1815,6 +2093,7 @@
|
|||||||
"upload_success": "تم الرفع بنجاح، قم بتحديث الصفحة لرؤية المحتويات المرفوعة الجديدة.",
|
"upload_success": "تم الرفع بنجاح، قم بتحديث الصفحة لرؤية المحتويات المرفوعة الجديدة.",
|
||||||
"upload_to_immich": "Upload to Immich ({})",
|
"upload_to_immich": "Upload to Immich ({})",
|
||||||
"uploading": "Uploading",
|
"uploading": "Uploading",
|
||||||
|
"uploading_file_info": "تحميل معلومات الملف",
|
||||||
"url": "عنوان URL",
|
"url": "عنوان URL",
|
||||||
"usage": "الاستخدام",
|
"usage": "الاستخدام",
|
||||||
"use_current_connection": "use current connection",
|
"use_current_connection": "use current connection",
|
||||||
@@ -1837,6 +2116,7 @@
|
|||||||
"version": "الإصدار",
|
"version": "الإصدار",
|
||||||
"version_announcement_closing": "صديقك، أليكس",
|
"version_announcement_closing": "صديقك، أليكس",
|
||||||
"version_announcement_message": "مرحبًا! يتوفر إصدار جديد من Immich. يُرجى تخصيص بعض الوقت لقراءة <link>ملاحظات الإصدار</link> للتأكد من تحديث إعداداتك لمنع أي أخطاء في التكوين، خاصة إذا كنت تستخدم WatchTower أو أي آلية تتولى تحديث مثيل Immich الخاص بك تلقائيًا.",
|
"version_announcement_message": "مرحبًا! يتوفر إصدار جديد من Immich. يُرجى تخصيص بعض الوقت لقراءة <link>ملاحظات الإصدار</link> للتأكد من تحديث إعداداتك لمنع أي أخطاء في التكوين، خاصة إذا كنت تستخدم WatchTower أو أي آلية تتولى تحديث مثيل Immich الخاص بك تلقائيًا.",
|
||||||
|
"version_announcement_overlay_ack": "يُقرّ",
|
||||||
"version_announcement_overlay_release_notes": "ملاحظات الإصدار",
|
"version_announcement_overlay_release_notes": "ملاحظات الإصدار",
|
||||||
"version_announcement_overlay_text_1": "مرحبًا يا صديقي ، هناك إصدار جديد",
|
"version_announcement_overlay_text_1": "مرحبًا يا صديقي ، هناك إصدار جديد",
|
||||||
"version_announcement_overlay_text_2": "من فضلك خذ وقتك لزيارة",
|
"version_announcement_overlay_text_2": "من فضلك خذ وقتك لزيارة",
|
||||||
@@ -1852,6 +2132,7 @@
|
|||||||
"view": "عرض",
|
"view": "عرض",
|
||||||
"view_album": "عرض الألبوم",
|
"view_album": "عرض الألبوم",
|
||||||
"view_all": "عرض الكل",
|
"view_all": "عرض الكل",
|
||||||
|
"view_all_button": "عرض الكل",
|
||||||
"view_all_users": "عرض كافة المستخدمين",
|
"view_all_users": "عرض كافة المستخدمين",
|
||||||
"view_in_timeline": "عرض في الجدول الزمني",
|
"view_in_timeline": "عرض في الجدول الزمني",
|
||||||
"view_link": "عرض الرابط",
|
"view_link": "عرض الرابط",
|
||||||
@@ -1874,6 +2155,8 @@
|
|||||||
"years_ago": "منذ {years, plural, one {# سنة} other {# سنوات}}",
|
"years_ago": "منذ {years, plural, one {# سنة} other {# سنوات}}",
|
||||||
"yes": "نعم",
|
"yes": "نعم",
|
||||||
"you_dont_have_any_shared_links": "ليس لديك أي روابط مشتركة",
|
"you_dont_have_any_shared_links": "ليس لديك أي روابط مشتركة",
|
||||||
|
"your_activity": "نشاطك",
|
||||||
|
"your_map": "خريطتك",
|
||||||
"your_wifi_name": "Your WiFi name",
|
"your_wifi_name": "Your WiFi name",
|
||||||
"zoom_image": "تكبير الصورة"
|
"zoom_image": "تكبير الصورة"
|
||||||
}
|
}
|
||||||
19
i18n/be.json
19
i18n/be.json
@@ -4,7 +4,6 @@
|
|||||||
"account_settings": "Налады ўліковага запісу",
|
"account_settings": "Налады ўліковага запісу",
|
||||||
"acknowledge": "Пацвердзіць",
|
"acknowledge": "Пацвердзіць",
|
||||||
"action": "Дзеянне",
|
"action": "Дзеянне",
|
||||||
"action_common_update": "Абнавіць",
|
|
||||||
"actions": "Дзеянні",
|
"actions": "Дзеянні",
|
||||||
"active": "Актыўны",
|
"active": "Актыўны",
|
||||||
"activity": "Актыўнасць",
|
"activity": "Актыўнасць",
|
||||||
@@ -14,7 +13,6 @@
|
|||||||
"add_a_location": "Дадаць месца",
|
"add_a_location": "Дадаць месца",
|
||||||
"add_a_name": "Дадаць імя",
|
"add_a_name": "Дадаць імя",
|
||||||
"add_a_title": "Дадаць загаловак",
|
"add_a_title": "Дадаць загаловак",
|
||||||
"add_endpoint": "Дадаць кропку доступу",
|
|
||||||
"add_exclusion_pattern": "Дадаць шаблон выключэння",
|
"add_exclusion_pattern": "Дадаць шаблон выключэння",
|
||||||
"add_import_path": "Дадаць шлях імпарту",
|
"add_import_path": "Дадаць шлях імпарту",
|
||||||
"add_location": "Дадайце месца",
|
"add_location": "Дадайце месца",
|
||||||
@@ -22,10 +20,8 @@
|
|||||||
"add_partner": "Дадаць партнёра",
|
"add_partner": "Дадаць партнёра",
|
||||||
"add_path": "Дадаць шлях",
|
"add_path": "Дадаць шлях",
|
||||||
"add_photos": "Дадаць фота",
|
"add_photos": "Дадаць фота",
|
||||||
"add_to": "Дадаць у…",
|
"add_to": "Дадаць у...",
|
||||||
"add_to_album": "Дадаць у альбом",
|
"add_to_album": "Дадаць у альбом",
|
||||||
"add_to_album_bottom_sheet_added": "Дададзена да {album}",
|
|
||||||
"add_to_album_bottom_sheet_already_exists": "Ужо знаходзіцца ў {album}",
|
|
||||||
"add_to_shared_album": "Дадаць у агульны альбом",
|
"add_to_shared_album": "Дадаць у агульны альбом",
|
||||||
"add_url": "Дадаць URL",
|
"add_url": "Дадаць URL",
|
||||||
"added_to_archive": "Дададзена ў архіў",
|
"added_to_archive": "Дададзена ў архіў",
|
||||||
@@ -43,9 +39,8 @@
|
|||||||
"backup_database_enable_description": "Уключыць рэзерваванне базы даных",
|
"backup_database_enable_description": "Уключыць рэзерваванне базы даных",
|
||||||
"backup_keep_last_amount": "Колькасць папярэдніх рэзервовых копій для захавання",
|
"backup_keep_last_amount": "Колькасць папярэдніх рэзервовых копій для захавання",
|
||||||
"backup_settings": "Налады рэзервовага капіявання",
|
"backup_settings": "Налады рэзервовага капіявання",
|
||||||
"backup_settings_description": "Кіраванне наладамі дампа базы дадзеных. Заўвага: гэтыя задачы не кантралююцца, і ў выпадку няўдачы паведамленне адпраўлена не будзе.",
|
"backup_settings_description": "Кіраванне наладкамі рэзервовага капіявання базы даных",
|
||||||
"check_all": "Праверыць усе",
|
"check_all": "Праверыць усе",
|
||||||
"cleanup": "Ачыстка",
|
|
||||||
"cleared_jobs": "Ачышчаны заданні для: {job}",
|
"cleared_jobs": "Ачышчаны заданні для: {job}",
|
||||||
"config_set_by_file": "Канфігурацыя ў зараз усталявана праз файл канфігурацыі",
|
"config_set_by_file": "Канфігурацыя ў зараз усталявана праз файл канфігурацыі",
|
||||||
"confirm_delete_library": "Вы ўпэўнены што жадаеце выдаліць {library} бібліятэку?",
|
"confirm_delete_library": "Вы ўпэўнены што жадаеце выдаліць {library} бібліятэку?",
|
||||||
@@ -63,18 +58,8 @@
|
|||||||
"external_library_created_at": "Знешняя бібліятэка (створана {date})",
|
"external_library_created_at": "Знешняя бібліятэка (створана {date})",
|
||||||
"external_library_management": "Кіраванне знешняй бібліятэкай",
|
"external_library_management": "Кіраванне знешняй бібліятэкай",
|
||||||
"face_detection": "Выяўленне твараў",
|
"face_detection": "Выяўленне твараў",
|
||||||
"face_detection_description": "Выяўляць твары на фотаздымках і відэа з дапамогай машыннага навучання. Для відэа ўлічваецца толькі мініяцюра. \"Абнавіць\" (пера)апрацоўвае ўсе медыя. \"Скінуць\" дадаткова ачышчае ўсе бягучыя дадзеныя пра твары. \"Адсутнічае\" ставіць у чаргу медыя, якія яшчэ не былі апрацаваныя. Выяўленыя твары будуць пастаўлены ў чаргу для распазнавання асоб пасля завяршэння выяўлення твараў, з групаваннем іх па існуючых або новых людзях.",
|
|
||||||
"facial_recognition_job_description": "Групаваць выяўленыя твары па асобах. Гэты этап выконваецца пасля завяршэння выяўлення твараў. \"Скінуць\" (паўторна) перагрупоўвае ўсе твары. \"Адсутнічае\" ставіць у чаргу твары, якія яшчэ не прыпісаныя да якой-небудзь асобы.",
|
|
||||||
"failed_job_command": "Каманда {command} не выканалася для задання: {job}",
|
|
||||||
"force_delete_user_warning": "ПАПЯРЭДЖАННЕ: Гэта дзеянне неадкладна выдаліць карыстальніка і ўсе аб'екты. Гэта дзеянне не можа быць адроблена і файлы немагчыма будзе аднавіць.",
|
"force_delete_user_warning": "ПАПЯРЭДЖАННЕ: Гэта дзеянне неадкладна выдаліць карыстальніка і ўсе аб'екты. Гэта дзеянне не можа быць адроблена і файлы немагчыма будзе аднавіць.",
|
||||||
"forcing_refresh_library_files": "Прымусовае абнаўленне ўсіх файлаў бібліятэкі",
|
|
||||||
"image_format": "Фармат",
|
"image_format": "Фармат",
|
||||||
"image_format_description": "WebP стварае меншыя файлы, чым JPEG, але павольней кадуе.",
|
|
||||||
"image_fullsize_description": "Выява ў поўным памеры без метаданых, выкарыстоўваецца пры павелічэнні",
|
|
||||||
"image_fullsize_enabled": "Уключыць стварэнне выявы ў поўным памеры",
|
|
||||||
"image_fullsize_enabled_description": "Ствараць выяву ў поўным памеры для фарматаў, што не прыдатныя для вэб. Калі ўключана опцыя \"Аддаваць перавагу ўбудаванай праяве\", прагляды выкарыстоўваюцца непасрэдна без канвертацыі. Не ўплывае на вэб-прыдатныя фарматы, такія як JPEG.",
|
|
||||||
"image_fullsize_quality_description": "Якасць выявы ў поўным памеры ад 1 да 100. Больш высокае значэнне лепшае, але прыводзіць да павелічэння памеру файла.",
|
|
||||||
"image_fullsize_title": "Налады выявы ў поўным памеры",
|
|
||||||
"image_preview_title": "Налады папярэдняга прагляду",
|
"image_preview_title": "Налады папярэдняга прагляду",
|
||||||
"image_quality": "Якасць",
|
"image_quality": "Якасць",
|
||||||
"image_resolution": "Раздзяляльнасць",
|
"image_resolution": "Раздзяляльнасць",
|
||||||
|
|||||||
@@ -162,6 +162,7 @@
|
|||||||
"no_pattern_added": "Няма добавен модел",
|
"no_pattern_added": "Няма добавен модел",
|
||||||
"note_apply_storage_label_previous_assets": "Забележка: За да приложите етикета за съхранение към предварително качени файлове, стартирайте",
|
"note_apply_storage_label_previous_assets": "Забележка: За да приложите етикета за съхранение към предварително качени файлове, стартирайте",
|
||||||
"note_cannot_be_changed_later": "ВНИМАНИЕ: Това не може да бъде променено по-късно!",
|
"note_cannot_be_changed_later": "ВНИМАНИЕ: Това не може да бъде променено по-късно!",
|
||||||
|
"note_unlimited_quota": "Бележка: Въведете 0 за да нямате лимит на квотата",
|
||||||
"notification_email_from_address": "От адрес",
|
"notification_email_from_address": "От адрес",
|
||||||
"notification_email_from_address_description": "Електронна поща на изпращача, например: \"Immich Photo Server <noreply@example.com>\"",
|
"notification_email_from_address_description": "Електронна поща на изпращача, например: \"Immich Photo Server <noreply@example.com>\"",
|
||||||
"notification_email_host_description": "Хост на сървъра за електронна поща (например: smtp.immich.app)",
|
"notification_email_host_description": "Хост на сървъра за електронна поща (например: smtp.immich.app)",
|
||||||
@@ -183,13 +184,20 @@
|
|||||||
"oauth_auto_register": "Автоматична регистрация",
|
"oauth_auto_register": "Автоматична регистрация",
|
||||||
"oauth_auto_register_description": "Автоматично регистриране на нови потребители след влизане с OAuth",
|
"oauth_auto_register_description": "Автоматично регистриране на нови потребители след влизане с OAuth",
|
||||||
"oauth_button_text": "Текст на бутона",
|
"oauth_button_text": "Текст на бутона",
|
||||||
|
"oauth_client_id": "Клиентски ID",
|
||||||
|
"oauth_client_secret": "Клиентска тайна",
|
||||||
"oauth_enable_description": "Влизане с OAuth",
|
"oauth_enable_description": "Влизане с OAuth",
|
||||||
|
"oauth_issuer_url": "URL на издателя",
|
||||||
"oauth_mobile_redirect_uri": "URI за мобилно пренасочване",
|
"oauth_mobile_redirect_uri": "URI за мобилно пренасочване",
|
||||||
"oauth_mobile_redirect_uri_override": "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_profile_signing_algorithm": "Алгоритъм за създаване на профили",
|
||||||
|
"oauth_profile_signing_algorithm_description": "Алгоритъм използван за вписване на потребителски профил.",
|
||||||
|
"oauth_scope": "Област/обхват на приложение",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "Управление на настройките за вход с OAuth",
|
"oauth_settings_description": "Управление на настройките за вход с OAuth",
|
||||||
"oauth_settings_more_details": "За повече информация за функционалността, се потърсете в <link>docs</link>.",
|
"oauth_settings_more_details": "За повече информация за функционалността, се потърсете в <link>docs</link>.",
|
||||||
|
"oauth_signing_algorithm": "Алгоритъм за вписване",
|
||||||
"oauth_storage_label_claim": "Заявка за етикет за съхранение",
|
"oauth_storage_label_claim": "Заявка за етикет за съхранение",
|
||||||
"oauth_storage_label_claim_description": "Автоматично задайте етикета за съхранение на потребителя със стойността от тази заявка.",
|
"oauth_storage_label_claim_description": "Автоматично задайте етикета за съхранение на потребителя със стойността от тази заявка.",
|
||||||
"oauth_storage_quota_claim": "Заявка за квота за съхранение",
|
"oauth_storage_quota_claim": "Заявка за квота за съхранение",
|
||||||
@@ -915,6 +923,7 @@
|
|||||||
"no_shared_albums_message": "Създайте албум, за да споделяте снимки и видеоклипове с хората в мрежата си",
|
"no_shared_albums_message": "Създайте албум, за да споделяте снимки и видеоклипове с хората в мрежата си",
|
||||||
"not_in_any_album": "Не е в никой албум",
|
"not_in_any_album": "Не е в никой албум",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "Забележка: За да приложите етикета за съхранение към предварително качени активи, стартирайте",
|
"note_apply_storage_label_to_previously_uploaded assets": "Забележка: За да приложите етикета за съхранение към предварително качени активи, стартирайте",
|
||||||
|
"note_unlimited_quota": "Забележка: Въведете 0 за неограничена квота",
|
||||||
"notes": "Бележки",
|
"notes": "Бележки",
|
||||||
"notification_toggle_setting_description": "Активиране на имейл известия",
|
"notification_toggle_setting_description": "Активиране на имейл известия",
|
||||||
"notifications": "Известия",
|
"notifications": "Известия",
|
||||||
|
|||||||
@@ -118,6 +118,7 @@
|
|||||||
"no_pattern_added": "",
|
"no_pattern_added": "",
|
||||||
"note_apply_storage_label_previous_assets": "",
|
"note_apply_storage_label_previous_assets": "",
|
||||||
"note_cannot_be_changed_later": "",
|
"note_cannot_be_changed_later": "",
|
||||||
|
"note_unlimited_quota": "",
|
||||||
"notification_email_from_address": "",
|
"notification_email_from_address": "",
|
||||||
"notification_email_from_address_description": "",
|
"notification_email_from_address_description": "",
|
||||||
"notification_email_host_description": "",
|
"notification_email_host_description": "",
|
||||||
@@ -138,12 +139,17 @@
|
|||||||
"oauth_auto_register": "",
|
"oauth_auto_register": "",
|
||||||
"oauth_auto_register_description": "",
|
"oauth_auto_register_description": "",
|
||||||
"oauth_button_text": "",
|
"oauth_button_text": "",
|
||||||
|
"oauth_client_id": "",
|
||||||
|
"oauth_client_secret": "",
|
||||||
"oauth_enable_description": "",
|
"oauth_enable_description": "",
|
||||||
|
"oauth_issuer_url": "",
|
||||||
"oauth_mobile_redirect_uri": "",
|
"oauth_mobile_redirect_uri": "",
|
||||||
"oauth_mobile_redirect_uri_override": "",
|
"oauth_mobile_redirect_uri_override": "",
|
||||||
"oauth_mobile_redirect_uri_override_description": "",
|
"oauth_mobile_redirect_uri_override_description": "",
|
||||||
|
"oauth_scope": "",
|
||||||
"oauth_settings": "",
|
"oauth_settings": "",
|
||||||
"oauth_settings_description": "",
|
"oauth_settings_description": "",
|
||||||
|
"oauth_signing_algorithm": "",
|
||||||
"oauth_storage_label_claim": "",
|
"oauth_storage_label_claim": "",
|
||||||
"oauth_storage_label_claim_description": "",
|
"oauth_storage_label_claim_description": "",
|
||||||
"oauth_storage_quota_claim": "",
|
"oauth_storage_quota_claim": "",
|
||||||
@@ -608,6 +614,7 @@
|
|||||||
"no_shared_albums_message": "",
|
"no_shared_albums_message": "",
|
||||||
"not_in_any_album": "",
|
"not_in_any_album": "",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "",
|
"note_apply_storage_label_to_previously_uploaded assets": "",
|
||||||
|
"note_unlimited_quota": "",
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"notification_toggle_setting_description": "",
|
"notification_toggle_setting_description": "",
|
||||||
"notifications": "",
|
"notifications": "",
|
||||||
|
|||||||
309
i18n/ca.json
309
i18n/ca.json
@@ -4,6 +4,12 @@
|
|||||||
"account_settings": "Configuració del compte",
|
"account_settings": "Configuració del compte",
|
||||||
"acknowledge": "D'acord",
|
"acknowledge": "D'acord",
|
||||||
"action": "Acció",
|
"action": "Acció",
|
||||||
|
"action_common_back": "Enrere",
|
||||||
|
"action_common_cancel": "Cancel·lar",
|
||||||
|
"action_common_clear": "Buida",
|
||||||
|
"action_common_confirm": "Confirmar",
|
||||||
|
"action_common_save": "Desa",
|
||||||
|
"action_common_select": "Selecciona",
|
||||||
"action_common_update": "Actualitzar",
|
"action_common_update": "Actualitzar",
|
||||||
"actions": "Accions",
|
"actions": "Accions",
|
||||||
"active": "Actiu",
|
"active": "Actiu",
|
||||||
@@ -70,11 +76,6 @@
|
|||||||
"forcing_refresh_library_files": "Força l'actualització de tots els fitxers de les biblioteques",
|
"forcing_refresh_library_files": "Força l'actualització de tots els fitxers de les biblioteques",
|
||||||
"image_format": "Format",
|
"image_format": "Format",
|
||||||
"image_format_description": "WebP genera fitxers més petits que JPEG, però codifica més lentament.",
|
"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",
|
|
||||||
"image_fullsize_enabled": "Activa la generació d'imatges a tamany complet",
|
|
||||||
"image_fullsize_enabled_description": "Genera imatges a tamany complet per formats no compatibles amb la web. Quan \"Prefereix vista prèvia incrustada\" està activat, les vistes prèvies incrustades s'utilitzen directament sense conversió. No afecta els formats compatibles amb la web com JPEG.",
|
|
||||||
"image_fullsize_quality_description": "De 1 a 100, qualitat de l'imatge a tamany complet. Un valor més alt és millor, però resulta en fitxers de major tamany.",
|
|
||||||
"image_fullsize_title": "Configuració d'imatges a tamany complet",
|
|
||||||
"image_prefer_embedded_preview": "Prefereix vista prèvia incrustada",
|
"image_prefer_embedded_preview": "Prefereix vista prèvia incrustada",
|
||||||
"image_prefer_embedded_preview_setting_description": "Empra vista prèvia incrustada en les fotografies RAW com a entrada per al processament d'imatge, quan sigui possible. Aquesta acció pot produir colors més acurats en algunes imatges, però la qualitat de la vista prèvia depèn de la càmera i la imatge pot tenir més artefactes de compressió.",
|
"image_prefer_embedded_preview_setting_description": "Empra vista prèvia incrustada en les fotografies RAW com a entrada per al processament d'imatge, quan sigui possible. Aquesta acció pot produir colors més acurats en algunes imatges, però la qualitat de la vista prèvia depèn de la càmera i la imatge pot tenir més artefactes de compressió.",
|
||||||
"image_prefer_wide_gamut": "Prefereix àmplia gamma",
|
"image_prefer_wide_gamut": "Prefereix àmplia gamma",
|
||||||
@@ -171,6 +172,7 @@
|
|||||||
"no_pattern_added": "Cap patró aplicat",
|
"no_pattern_added": "Cap patró aplicat",
|
||||||
"note_apply_storage_label_previous_assets": "Nota: Per aplicar l'etiquetatge d'emmagatzematge a elements pujats prèviament, executeu la",
|
"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!",
|
"note_cannot_be_changed_later": "NOTA: Això és irreversible!",
|
||||||
|
"note_unlimited_quota": "Nota: Intruduïu 0 per a quota il·limitada",
|
||||||
"notification_email_from_address": "Des de l'adreça",
|
"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 <noreply@example.com>\"",
|
"notification_email_from_address_description": "Adreça de correu electrònic del remitent, per exemple: \"Immich Photo Server <noreply@example.com>\"",
|
||||||
"notification_email_host_description": "Amfitrió del servidor de correu electrònic (p.ex. smtp.immich.app)",
|
"notification_email_host_description": "Amfitrió del servidor de correu electrònic (p.ex. smtp.immich.app)",
|
||||||
@@ -192,13 +194,20 @@
|
|||||||
"oauth_auto_register": "Registre automàtic",
|
"oauth_auto_register": "Registre automàtic",
|
||||||
"oauth_auto_register_description": "Registra nous usuaris automàticament després d'iniciar sessió amb OAuth",
|
"oauth_auto_register_description": "Registra nous usuaris automàticament després d'iniciar sessió amb OAuth",
|
||||||
"oauth_button_text": "Text del botó",
|
"oauth_button_text": "Text del botó",
|
||||||
|
"oauth_client_id": "ID Client",
|
||||||
|
"oauth_client_secret": "Secret de Client",
|
||||||
"oauth_enable_description": "Iniciar sessió amb OAuth",
|
"oauth_enable_description": "Iniciar sessió amb OAuth",
|
||||||
|
"oauth_issuer_url": "URL de l'emissor",
|
||||||
"oauth_mobile_redirect_uri": "URI de redirecció mòbil",
|
"oauth_mobile_redirect_uri": "URI de redirecció mòbil",
|
||||||
"oauth_mobile_redirect_uri_override": "Sobreescriu l'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_profile_signing_algorithm": "Algoritme de signatura del perfil",
|
||||||
|
"oauth_profile_signing_algorithm_description": "Algoritme utilitzat per signar el perfil d’usuari.",
|
||||||
|
"oauth_scope": "Abast",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "Gestiona la configuració de l'inici de sessió 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 <link>documentació</link>.",
|
"oauth_settings_more_details": "Per a més detalls sobre aquesta funcionalitat, consulteu la <link>documentació</link>.",
|
||||||
|
"oauth_signing_algorithm": "Algorisme de signatura",
|
||||||
"oauth_storage_label_claim": "Petició d'etiquetatge d'emmagatzematge",
|
"oauth_storage_label_claim": "Petició d'etiquetatge d'emmagatzematge",
|
||||||
"oauth_storage_label_claim_description": "Estableix automàticament l'etiquetatge d'emmagatzematge de l'usuari a aquest valor.",
|
"oauth_storage_label_claim_description": "Estableix automàticament l'etiquetatge d'emmagatzematge de l'usuari a aquest valor.",
|
||||||
"oauth_storage_quota_claim": "Quota d'emmagatzematge reclamada",
|
"oauth_storage_quota_claim": "Quota d'emmagatzematge reclamada",
|
||||||
@@ -372,6 +381,7 @@
|
|||||||
"advanced_settings_self_signed_ssl_subtitle": "Omet la verificació del certificat SSL del servidor. Requerit per a certificats autosignats.",
|
"advanced_settings_self_signed_ssl_subtitle": "Omet la verificació del certificat SSL del servidor. Requerit per a certificats autosignats.",
|
||||||
"advanced_settings_self_signed_ssl_title": "Permet certificats SSL autosignats",
|
"advanced_settings_self_signed_ssl_title": "Permet certificats SSL autosignats",
|
||||||
"advanced_settings_tile_subtitle": "Configuració avançada de l'usuari",
|
"advanced_settings_tile_subtitle": "Configuració avançada de l'usuari",
|
||||||
|
"advanced_settings_tile_title": "Avançat",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Habilita funcions addicionals per a la resolució de problemes",
|
"advanced_settings_troubleshooting_subtitle": "Habilita funcions addicionals per a la resolució de problemes",
|
||||||
"advanced_settings_troubleshooting_title": "Resolució de problemes",
|
"advanced_settings_troubleshooting_title": "Resolució de problemes",
|
||||||
"age_months": "{months, plural, one {# mes} other {# mesos}}",
|
"age_months": "{months, plural, one {# mes} other {# mesos}}",
|
||||||
@@ -382,8 +392,10 @@
|
|||||||
"album_cover_updated": "Portada de l'àlbum actualitzada",
|
"album_cover_updated": "Portada de l'àlbum actualitzada",
|
||||||
"album_delete_confirmation": "Esteu segur que voleu suprimir l'àlbum {album}?",
|
"album_delete_confirmation": "Esteu segur que voleu suprimir l'àlbum {album}?",
|
||||||
"album_delete_confirmation_description": "Si aquest àlbum es comparteix, els altres usuaris ja no podran accedir-hi.",
|
"album_delete_confirmation_description": "Si aquest àlbum es comparteix, els altres usuaris ja no podran accedir-hi.",
|
||||||
|
"album_info": "{} elements",
|
||||||
"album_info_card_backup_album_excluded": "Exclosos",
|
"album_info_card_backup_album_excluded": "Exclosos",
|
||||||
"album_info_card_backup_album_included": "Inclosos",
|
"album_info_card_backup_album_included": "Inclosos",
|
||||||
|
"album_info_shared": "{} elements - Compartits",
|
||||||
"album_info_updated": "Informació de l'àlbum actualitzada",
|
"album_info_updated": "Informació de l'àlbum actualitzada",
|
||||||
"album_leave": "Sortir de l'àlbum?",
|
"album_leave": "Sortir de l'àlbum?",
|
||||||
"album_leave_confirmation": "N'esteu segur que voleu sortir de {album}?",
|
"album_leave_confirmation": "N'esteu segur que voleu sortir de {album}?",
|
||||||
@@ -395,17 +407,21 @@
|
|||||||
"album_thumbnail_card_item": "1 element",
|
"album_thumbnail_card_item": "1 element",
|
||||||
"album_thumbnail_card_items": "{} elements",
|
"album_thumbnail_card_items": "{} elements",
|
||||||
"album_thumbnail_card_shared": " · Compartit",
|
"album_thumbnail_card_shared": " · Compartit",
|
||||||
|
"album_thumbnail_owned": "Propi",
|
||||||
"album_thumbnail_shared_by": "Compartit per {}",
|
"album_thumbnail_shared_by": "Compartit per {}",
|
||||||
|
"album_thumbnails": "Miniatures de la pàgina de la biblioteca ({} elements)",
|
||||||
"album_updated": "Àlbum actualitzat",
|
"album_updated": "Àlbum actualitzat",
|
||||||
"album_updated_setting_description": "Rep una notificació per correu electrònic quan un àlbum compartit tingui recursos nous",
|
"album_updated_setting_description": "Rep una notificació per correu electrònic quan un àlbum compartit tingui recursos nous",
|
||||||
"album_user_left": "Surt de {album}",
|
"album_user_left": "Surt de {album}",
|
||||||
"album_user_removed": "{user} eliminat",
|
"album_user_removed": "{user} eliminat",
|
||||||
"album_viewer_appbar_delete_confirm": "Confirmes que vols suprimir aquest àlbum del teu compte?",
|
"album_viewer_appbar_delete_confirm": "Confirmes que vols suprimir aquest àlbum del teu compte?",
|
||||||
|
"album_viewer_appbar_share_delete": "Esborra l'àlbum",
|
||||||
"album_viewer_appbar_share_err_delete": "Error al esborrar l'àlbum",
|
"album_viewer_appbar_share_err_delete": "Error al esborrar l'àlbum",
|
||||||
"album_viewer_appbar_share_err_leave": "Error al sortir de l'àlbum",
|
"album_viewer_appbar_share_err_leave": "Error al sortir de l'àlbum",
|
||||||
"album_viewer_appbar_share_err_remove": "Hi ha hagut problemes al treure elements de l'àlbum",
|
"album_viewer_appbar_share_err_remove": "Hi ha hagut problemes al treure elements de l'àlbum",
|
||||||
"album_viewer_appbar_share_err_title": "Error al modificar el títol de l'àlbum",
|
"album_viewer_appbar_share_err_title": "Error al modificar el títol de l'àlbum",
|
||||||
"album_viewer_appbar_share_leave": "Surt de l'àlbum",
|
"album_viewer_appbar_share_leave": "Surt de l'àlbum",
|
||||||
|
"album_viewer_appbar_share_remove": "Treu de l'àlbum",
|
||||||
"album_viewer_appbar_share_to": "Comparteix amb",
|
"album_viewer_appbar_share_to": "Comparteix amb",
|
||||||
"album_viewer_page_share_add_users": "Afegeix usuaris",
|
"album_viewer_page_share_add_users": "Afegeix usuaris",
|
||||||
"album_with_link_access": "Permet que qualsevol persona que tingui l'enllaç vegi fotos i persones d'aquest àlbum.",
|
"album_with_link_access": "Permet que qualsevol persona que tingui l'enllaç vegi fotos i persones d'aquest àlbum.",
|
||||||
@@ -414,7 +430,9 @@
|
|||||||
"all": "Tots",
|
"all": "Tots",
|
||||||
"all_albums": "Tots els àlbum",
|
"all_albums": "Tots els àlbum",
|
||||||
"all_people": "Tota la gent",
|
"all_people": "Tota la gent",
|
||||||
|
"all_people_page_title": "Persones",
|
||||||
"all_videos": "Tots els vídeos",
|
"all_videos": "Tots els vídeos",
|
||||||
|
"all_videos_page_title": "Vídeos",
|
||||||
"allow_dark_mode": "Permet el tema fosc",
|
"allow_dark_mode": "Permet el tema fosc",
|
||||||
"allow_edits": "Permet editar",
|
"allow_edits": "Permet editar",
|
||||||
"allow_public_user_to_download": "Permet que l'usuari públic pugui descarregar",
|
"allow_public_user_to_download": "Permet que l'usuari públic pugui descarregar",
|
||||||
@@ -429,6 +447,15 @@
|
|||||||
"app_bar_signout_dialog_ok": "Sí",
|
"app_bar_signout_dialog_ok": "Sí",
|
||||||
"app_bar_signout_dialog_title": "Tanca la sessió",
|
"app_bar_signout_dialog_title": "Tanca la sessió",
|
||||||
"app_settings": "Configuració de l'app",
|
"app_settings": "Configuració de l'app",
|
||||||
|
"appbar_delete_confirm": "Confirmes que vols suprimir aquest àlbum del teu compte?",
|
||||||
|
"appbar_share_delete": "Esborra l'àlbum",
|
||||||
|
"appbar_share_err_delete": "Error al esborrar l'àlbum",
|
||||||
|
"appbar_share_err_leave": "Error al sortir de l'àlbum",
|
||||||
|
"appbar_share_err_remove": "Hi ha hagut problemes al treure elements de l'àlbum",
|
||||||
|
"appbar_share_err_title": "Error al modificar el títol de l'àlbum",
|
||||||
|
"appbar_share_leave": "Surt de l'àlbum",
|
||||||
|
"appbar_share_remove": "Treu de l'àlbum",
|
||||||
|
"appbar_share_to": "Comparteix amb",
|
||||||
"appears_in": "Apareix a",
|
"appears_in": "Apareix a",
|
||||||
"archive": "Arxiu",
|
"archive": "Arxiu",
|
||||||
"archive_or_unarchive_photo": "Arxivar o desarxivar fotografia",
|
"archive_or_unarchive_photo": "Arxivar o desarxivar fotografia",
|
||||||
@@ -452,10 +479,13 @@
|
|||||||
"asset_list_layout_settings_dynamic_layout_title": "Disseny dinàmic",
|
"asset_list_layout_settings_dynamic_layout_title": "Disseny dinàmic",
|
||||||
"asset_list_layout_settings_group_automatically": "Automàtic",
|
"asset_list_layout_settings_group_automatically": "Automàtic",
|
||||||
"asset_list_layout_settings_group_by": "Agrupa elements per",
|
"asset_list_layout_settings_group_by": "Agrupa elements per",
|
||||||
|
"asset_list_layout_settings_group_by_month": "Mes",
|
||||||
"asset_list_layout_settings_group_by_month_day": "Mes + dia",
|
"asset_list_layout_settings_group_by_month_day": "Mes + dia",
|
||||||
"asset_list_layout_sub_title": "Disseny",
|
"asset_list_layout_sub_title": "Disseny",
|
||||||
"asset_list_settings_subtitle": "Configuració del disseny de la graella de fotos",
|
"asset_list_settings_subtitle": "Configuració del disseny de la graella de fotos",
|
||||||
"asset_list_settings_title": "Graella de fotos",
|
"asset_list_settings_title": "Graella de fotos",
|
||||||
|
"asset_list_storage_indicator_title": "Mostra l'indicador d'emmagatzematge als títols dels elements",
|
||||||
|
"asset_list_tiles_per_row_title": "Nombre d'elements per fila ({})",
|
||||||
"asset_offline": "Element fora de línia",
|
"asset_offline": "Element fora de línia",
|
||||||
"asset_offline_description": "Aquest recurs extern ja no es troba al disc. Poseu-vos en contacte amb el vostre administrador d'Immich per obtenir ajuda.",
|
"asset_offline_description": "Aquest recurs extern ja no es troba al disc. Poseu-vos en contacte amb el vostre administrador d'Immich per obtenir ajuda.",
|
||||||
"asset_restored_successfully": "Element recuperat correctament",
|
"asset_restored_successfully": "Element recuperat correctament",
|
||||||
@@ -488,8 +518,27 @@
|
|||||||
"automatic_endpoint_switching_title": "Canvi automàtic d'URL",
|
"automatic_endpoint_switching_title": "Canvi automàtic d'URL",
|
||||||
"back": "Enrere",
|
"back": "Enrere",
|
||||||
"back_close_deselect": "Tornar, tancar o anul·lar la selecció",
|
"back_close_deselect": "Tornar, tancar o anul·lar la selecció",
|
||||||
|
"background_app_refresh_disabled_content": "Activa l'actualització en segon pla de l'aplicació a Configuració > General > Actualització en segon pla per utilitzar la copia de seguretat en segon pla.",
|
||||||
|
"background_app_refresh_disabled_title": "Actualització en segon pla desactivada",
|
||||||
|
"background_app_refresh_enable_button_text": "Vés a configuració",
|
||||||
|
"background_battery_info_link": "Mostra'm com",
|
||||||
|
"background_battery_info_message": "Per obtenir la millor experiència de copia de seguretat en segon pla, desactiveu qualsevol optimització de bateria que restringeixi l'activitat en segon pla per a Immich.\n\nAtès que això és específic del dispositiu, busqueu la informació necessària per al fabricant del vostre dispositiu",
|
||||||
|
"background_battery_info_ok": "D'acord",
|
||||||
|
"background_battery_info_title": "Optimitzacions de bateria",
|
||||||
|
"background_charging": "Només mentre es carrega",
|
||||||
|
"background_configure_error": "No s'ha pogut configurar el servei en segon pla",
|
||||||
|
"background_delay": "Retard en la copia de seguretat de nous elements: {}",
|
||||||
|
"background_description": "Activeu el servei en segon pla per copiar automàticament tots els nous elements sense haver d'obrir l'aplicació.",
|
||||||
|
"background_is_off": "La còpia automàtica en segon pla està desactivada",
|
||||||
|
"background_is_on": "La còpia automàtica en segon pla està activada",
|
||||||
"background_location_permission": "Permís d'ubicació en segon pla",
|
"background_location_permission": "Permís d'ubicació en segon pla",
|
||||||
"background_location_permission_content": "Per canviar de xarxa quan s'executa en segon pla, Immich ha de *sempre* tenir accés a la ubicació precisa perquè l'aplicació pugui llegir el nom de la xarxa Wi-Fi",
|
"background_location_permission_content": "Per canviar de xarxa quan s'executa en segon pla, Immich ha de *sempre* tenir accés a la ubicació precisa perquè l'aplicació pugui llegir el nom de la xarxa Wi-Fi",
|
||||||
|
"background_turn_off": "Desactiva el servei en segon pla",
|
||||||
|
"background_turn_on": "Activa el servei en segon pla",
|
||||||
|
"background_wifi": "Només amb WiFi",
|
||||||
|
"backup": "Còpia",
|
||||||
|
"backup_album_excluded": "Exclosos",
|
||||||
|
"backup_album_included": "Inclosos",
|
||||||
"backup_album_selection_page_albums_device": "Àlbums al dispositiu ({})",
|
"backup_album_selection_page_albums_device": "Àlbums al dispositiu ({})",
|
||||||
"backup_album_selection_page_albums_tap": "Un toc per incloure, doble toc per excloure",
|
"backup_album_selection_page_albums_tap": "Un toc per incloure, doble toc per excloure",
|
||||||
"backup_album_selection_page_assets_scatter": "Els elements poden dispersar-se en diversos àlbums. Per tant, els àlbums es poden incloure o excloure durant el procés de còpia de seguretat.",
|
"backup_album_selection_page_assets_scatter": "Els elements poden dispersar-se en diversos àlbums. Per tant, els àlbums es poden incloure o excloure durant el procés de còpia de seguretat.",
|
||||||
@@ -524,9 +573,10 @@
|
|||||||
"backup_controller_page_backup": "Còpia",
|
"backup_controller_page_backup": "Còpia",
|
||||||
"backup_controller_page_backup_selected": "Seleccionat: ",
|
"backup_controller_page_backup_selected": "Seleccionat: ",
|
||||||
"backup_controller_page_backup_sub": "Fotografies i vídeos copiats",
|
"backup_controller_page_backup_sub": "Fotografies i vídeos copiats",
|
||||||
|
"backup_controller_page_cancel": "Cancel·la",
|
||||||
"backup_controller_page_created": "Creat el: {}",
|
"backup_controller_page_created": "Creat el: {}",
|
||||||
"backup_controller_page_desc_backup": "Activeu la còpia de seguretat per pujar automàticament els nous elements al servidor en obrir l'aplicació.",
|
"backup_controller_page_desc_backup": "Activeu la còpia de seguretat per pujar automàticament els nous elements al servidor en obrir l'aplicació.",
|
||||||
"backup_controller_page_excluded": "Exclosos: ",
|
"backup_controller_page_excluded": "Exclosos:",
|
||||||
"backup_controller_page_failed": "Fallats ({})",
|
"backup_controller_page_failed": "Fallats ({})",
|
||||||
"backup_controller_page_filename": "Nom de l'arxiu: {} [{}]",
|
"backup_controller_page_filename": "Nom de l'arxiu: {} [{}]",
|
||||||
"backup_controller_page_id": "ID: {}",
|
"backup_controller_page_id": "ID: {}",
|
||||||
@@ -534,12 +584,14 @@
|
|||||||
"backup_controller_page_none_selected": "Cap seleccionat",
|
"backup_controller_page_none_selected": "Cap seleccionat",
|
||||||
"backup_controller_page_remainder": "Restant",
|
"backup_controller_page_remainder": "Restant",
|
||||||
"backup_controller_page_remainder_sub": "Fotografies i vídeos restants per copiar de la selecció",
|
"backup_controller_page_remainder_sub": "Fotografies i vídeos restants per copiar de la selecció",
|
||||||
|
"backup_controller_page_select": "Selecciona",
|
||||||
"backup_controller_page_server_storage": "Emmagatzematge del servidor",
|
"backup_controller_page_server_storage": "Emmagatzematge del servidor",
|
||||||
"backup_controller_page_start_backup": "Inicia la còpia",
|
"backup_controller_page_start_backup": "Inicia la còpia",
|
||||||
"backup_controller_page_status_off": "La copia de seguretat està desactivada",
|
"backup_controller_page_status_off": "La copia de seguretat està desactivada",
|
||||||
"backup_controller_page_status_on": "La copia de seguretat està activada",
|
"backup_controller_page_status_on": "La copia de seguretat està activada",
|
||||||
"backup_controller_page_storage_format": "{} de {} utilitzats",
|
"backup_controller_page_storage_format": "{} de {} utilitzats",
|
||||||
"backup_controller_page_to_backup": "Àlbums a copiar",
|
"backup_controller_page_to_backup": "Àlbums a copiar",
|
||||||
|
"backup_controller_page_total": "Total",
|
||||||
"backup_controller_page_total_sub": "Totes les fotografies i vídeos dels àlbums seleccionats",
|
"backup_controller_page_total_sub": "Totes les fotografies i vídeos dels àlbums seleccionats",
|
||||||
"backup_controller_page_turn_off": "Desactiva la còpia de seguretat",
|
"backup_controller_page_turn_off": "Desactiva la còpia de seguretat",
|
||||||
"backup_controller_page_turn_on": "Activa la còpia de seguretat",
|
"backup_controller_page_turn_on": "Activa la còpia de seguretat",
|
||||||
@@ -547,11 +599,14 @@
|
|||||||
"backup_err_only_album": "No es pot eliminar l'únic àlbum",
|
"backup_err_only_album": "No es pot eliminar l'únic àlbum",
|
||||||
"backup_info_card_assets": "elements",
|
"backup_info_card_assets": "elements",
|
||||||
"backup_manual_cancelled": "Cancel·lat",
|
"backup_manual_cancelled": "Cancel·lat",
|
||||||
|
"backup_manual_failed": "Fallat",
|
||||||
"backup_manual_in_progress": "La pujada ja està en curs. Torneu-ho a provar més tard",
|
"backup_manual_in_progress": "La pujada ja està en curs. Torneu-ho a provar més tard",
|
||||||
"backup_manual_success": "Èxit",
|
"backup_manual_success": "Èxit",
|
||||||
"backup_manual_title": "Estat de pujada",
|
"backup_manual_title": "Estat de pujada",
|
||||||
"backup_options_page_title": "Opcions de còpia de seguretat",
|
"backup_options_page_title": "Opcions de còpia de seguretat",
|
||||||
|
"backup_selected": "Seleccionat: ",
|
||||||
"backup_setting_subtitle": "Gestiona la configuració de càrrega en segon pla i en primer pla",
|
"backup_setting_subtitle": "Gestiona la configuració de càrrega en segon pla i en primer pla",
|
||||||
|
"backup_sub": "Fotografies i vídeos copiats",
|
||||||
"backward": "Enrere",
|
"backward": "Enrere",
|
||||||
"birthdate_saved": "Data de naixement guardada amb èxit",
|
"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.",
|
"birthdate_set_description": "La data de naixement s'utilitza per calcular l'edat d'aquesta persona en el moment d'una foto.",
|
||||||
@@ -590,6 +645,7 @@
|
|||||||
"cannot_merge_people": "No es pot fusionar gent",
|
"cannot_merge_people": "No es pot fusionar gent",
|
||||||
"cannot_undo_this_action": "Aquesta acció no es pot desfer!",
|
"cannot_undo_this_action": "Aquesta acció no es pot desfer!",
|
||||||
"cannot_update_the_description": "No es pot actualitzar la descripció",
|
"cannot_update_the_description": "No es pot actualitzar la descripció",
|
||||||
|
"categories": "Categories",
|
||||||
"change_date": "Canvia la data",
|
"change_date": "Canvia la data",
|
||||||
"change_display_order": "Canvia l'ordre de visualització",
|
"change_display_order": "Canvia l'ordre de visualització",
|
||||||
"change_expiration_time": "Canvia la data d'expiració",
|
"change_expiration_time": "Canvia la data d'expiració",
|
||||||
@@ -615,6 +671,8 @@
|
|||||||
"clear": "Buida",
|
"clear": "Buida",
|
||||||
"clear_all": "Neteja-ho tot",
|
"clear_all": "Neteja-ho tot",
|
||||||
"clear_all_recent_searches": "Esborra totes les cerques recents",
|
"clear_all_recent_searches": "Esborra totes les cerques recents",
|
||||||
|
"clear_cache_button": "Neteja la memòria cau",
|
||||||
|
"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.",
|
||||||
"clear_message": "Neteja el missatge",
|
"clear_message": "Neteja el missatge",
|
||||||
"clear_value": "Neteja el valor",
|
"clear_value": "Neteja el valor",
|
||||||
"client_cert_dialog_msg_confirm": "OK",
|
"client_cert_dialog_msg_confirm": "OK",
|
||||||
@@ -622,6 +680,7 @@
|
|||||||
"client_cert_import": "Importar",
|
"client_cert_import": "Importar",
|
||||||
"client_cert_import_success_msg": "S'ha importat el certificat del client",
|
"client_cert_import_success_msg": "S'ha importat el certificat del client",
|
||||||
"client_cert_invalid_msg": "Fitxer de certificat no vàlid o contrasenya incorrecta",
|
"client_cert_invalid_msg": "Fitxer de certificat no vàlid o contrasenya incorrecta",
|
||||||
|
"client_cert_remove": "Eliminar",
|
||||||
"client_cert_remove_msg": "S'ha eliminat el certificat del client",
|
"client_cert_remove_msg": "S'ha eliminat el certificat del client",
|
||||||
"client_cert_subtitle": "Només admet el format PKCS12 (.p12, .pfx). La importació/eliminació de certificats només està disponible abans d'iniciar sessió",
|
"client_cert_subtitle": "Només admet el format PKCS12 (.p12, .pfx). La importació/eliminació de certificats només està disponible abans d'iniciar sessió",
|
||||||
"client_cert_title": "Certificat de client SSL",
|
"client_cert_title": "Certificat de client SSL",
|
||||||
@@ -631,12 +690,17 @@
|
|||||||
"collapse_all": "Redueix-ho tot",
|
"collapse_all": "Redueix-ho tot",
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
"color_theme": "Tema de color",
|
"color_theme": "Tema de color",
|
||||||
|
"colorful_interface_subtitle": "Apliqueu color primari a les superfícies de fons.",
|
||||||
|
"colorful_interface_title": "Interfície colorida",
|
||||||
"comment_deleted": "Comentari esborrat",
|
"comment_deleted": "Comentari esborrat",
|
||||||
"comment_options": "Opcions de comentari",
|
"comment_options": "Opcions de comentari",
|
||||||
"comments_and_likes": "Comentaris i agradaments",
|
"comments_and_likes": "Comentaris i agradaments",
|
||||||
"comments_are_disabled": "Els comentaris estan desactivats",
|
"comments_are_disabled": "Els comentaris estan desactivats",
|
||||||
|
"common_add_to_album": "Afegir a l'àlbum",
|
||||||
|
"common_change_password": "Canvia la contrasenya",
|
||||||
"common_create_new_album": "Crea un àlbum nou",
|
"common_create_new_album": "Crea un àlbum nou",
|
||||||
"common_server_error": "Si us plau, comproveu la vostra connexió de xarxa, assegureu-vos que el servidor és accessible i que les versions de l'aplicació i del servidor són compatibles.",
|
"common_server_error": "Si us plau, comproveu la vostra connexió de xarxa, assegureu-vos que el servidor és accessible i que les versions de l'aplicació i del servidor són compatibles.",
|
||||||
|
"common_shared": "Compartit",
|
||||||
"completed": "Completat",
|
"completed": "Completat",
|
||||||
"confirm": "Confirmar",
|
"confirm": "Confirmar",
|
||||||
"confirm_admin_password": "Confirmeu la contrasenya d'administrador",
|
"confirm_admin_password": "Confirmeu la contrasenya d'administrador",
|
||||||
@@ -646,16 +710,29 @@
|
|||||||
"confirm_password": "Confirmació de contrasenya",
|
"confirm_password": "Confirmació de contrasenya",
|
||||||
"contain": "Contingut",
|
"contain": "Contingut",
|
||||||
"context": "Context",
|
"context": "Context",
|
||||||
|
"contextual_search": "Sortida del sol a la platja",
|
||||||
"continue": "Continuar",
|
"continue": "Continuar",
|
||||||
|
"control_bottom_app_bar_add_to_album": "Afegir a l'àlbum",
|
||||||
|
"control_bottom_app_bar_album_info": "{} elements",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} elements - Compartits",
|
"control_bottom_app_bar_album_info_shared": "{} elements - Compartits",
|
||||||
|
"control_bottom_app_bar_archive": "Arxiu",
|
||||||
"control_bottom_app_bar_create_new_album": "Crea un àlbum nou",
|
"control_bottom_app_bar_create_new_album": "Crea un àlbum nou",
|
||||||
|
"control_bottom_app_bar_delete": "Esborra",
|
||||||
"control_bottom_app_bar_delete_from_immich": "Suprimeix del Immich",
|
"control_bottom_app_bar_delete_from_immich": "Suprimeix del Immich",
|
||||||
"control_bottom_app_bar_delete_from_local": "Suprimeix del dispositiu",
|
"control_bottom_app_bar_delete_from_local": "Suprimeix del dispositiu",
|
||||||
|
"control_bottom_app_bar_download": "Descarrega",
|
||||||
|
"control_bottom_app_bar_edit": "Edita",
|
||||||
"control_bottom_app_bar_edit_location": "Edita la ubicació",
|
"control_bottom_app_bar_edit_location": "Edita la ubicació",
|
||||||
"control_bottom_app_bar_edit_time": "Edita data i hora",
|
"control_bottom_app_bar_edit_time": "Edita data i hora",
|
||||||
|
"control_bottom_app_bar_favorite": "Preferit",
|
||||||
|
"control_bottom_app_bar_share": "Comparteix",
|
||||||
"control_bottom_app_bar_share_link": "Comparteix l'enllaç",
|
"control_bottom_app_bar_share_link": "Comparteix l'enllaç",
|
||||||
"control_bottom_app_bar_share_to": "Comparteix a",
|
"control_bottom_app_bar_share_to": "Comparteix a",
|
||||||
|
"control_bottom_app_bar_stack": "Apilar",
|
||||||
"control_bottom_app_bar_trash_from_immich": "Mou a paperera",
|
"control_bottom_app_bar_trash_from_immich": "Mou a paperera",
|
||||||
|
"control_bottom_app_bar_unarchive": "Desarxiva",
|
||||||
|
"control_bottom_app_bar_unfavorite": "No preferit",
|
||||||
|
"control_bottom_app_bar_upload": "Puja",
|
||||||
"copied_image_to_clipboard": "Imatge copiada a porta-retalls.",
|
"copied_image_to_clipboard": "Imatge copiada a porta-retalls.",
|
||||||
"copied_to_clipboard": "Copiada a porta-retalls!",
|
"copied_to_clipboard": "Copiada a porta-retalls!",
|
||||||
"copy_error": "Error de còpia",
|
"copy_error": "Error de còpia",
|
||||||
@@ -676,9 +753,12 @@
|
|||||||
"create_link_to_share": "Crear enllaç per compartir",
|
"create_link_to_share": "Crear enllaç per compartir",
|
||||||
"create_link_to_share_description": "Deixa que qualsevol persona amb l'enllaç vegi les fotos seleccionades",
|
"create_link_to_share_description": "Deixa que qualsevol persona amb l'enllaç vegi les fotos seleccionades",
|
||||||
"create_new": "CREAR NOU",
|
"create_new": "CREAR NOU",
|
||||||
|
"create_new_album": "Crea un àlbum nou",
|
||||||
"create_new_person": "Crea una nova persona",
|
"create_new_person": "Crea una nova persona",
|
||||||
"create_new_person_hint": "Assigna els elements seleccionats a una persona nova",
|
"create_new_person_hint": "Assigna els elements seleccionats a una persona nova",
|
||||||
"create_new_user": "Crea un usuari nou",
|
"create_new_user": "Crea un usuari nou",
|
||||||
|
"create_shared_album_page_create": "Crea",
|
||||||
|
"create_shared_album_page_share": "Comparteix",
|
||||||
"create_shared_album_page_share_add_assets": "AFEGEIX ELEMENTS",
|
"create_shared_album_page_share_add_assets": "AFEGEIX ELEMENTS",
|
||||||
"create_shared_album_page_share_select_photos": "Escull fotografies",
|
"create_shared_album_page_share_select_photos": "Escull fotografies",
|
||||||
"create_tag": "Crear etiqueta",
|
"create_tag": "Crear etiqueta",
|
||||||
@@ -686,6 +766,7 @@
|
|||||||
"create_user": "Crea un usuari",
|
"create_user": "Crea un usuari",
|
||||||
"created": "Creat",
|
"created": "Creat",
|
||||||
"crop": "Retalla",
|
"crop": "Retalla",
|
||||||
|
"curated_location_page_title": "Localitzacions",
|
||||||
"curated_object_page_title": "Coses",
|
"curated_object_page_title": "Coses",
|
||||||
"current_device": "Dispositiu actual",
|
"current_device": "Dispositiu actual",
|
||||||
"current_server_address": "Adreça actual del servidor",
|
"current_server_address": "Adreça actual del servidor",
|
||||||
@@ -694,6 +775,7 @@
|
|||||||
"daily_title_text_date": "E, dd MMM",
|
"daily_title_text_date": "E, dd MMM",
|
||||||
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
||||||
"dark": "Fosc",
|
"dark": "Fosc",
|
||||||
|
"dark_mode_switch": "Tema fosc",
|
||||||
"date_after": "Data posterior a",
|
"date_after": "Data posterior a",
|
||||||
"date_and_time": "Data i hora",
|
"date_and_time": "Data i hora",
|
||||||
"date_before": "Data anterior a",
|
"date_before": "Data anterior a",
|
||||||
@@ -715,10 +797,14 @@
|
|||||||
"delete_dialog_alert_local": "Aquests elements s'eliminaran permanentment del vostre dispositiu, però encara estaran disponibles al servidor Immich",
|
"delete_dialog_alert_local": "Aquests elements s'eliminaran permanentment del vostre dispositiu, però encara estaran disponibles al servidor Immich",
|
||||||
"delete_dialog_alert_local_non_backed_up": "Alguns dels elements no tenen còpia de seguretat a Immich i s'eliminaran permanentment del dispositiu",
|
"delete_dialog_alert_local_non_backed_up": "Alguns dels elements no tenen còpia de seguretat a Immich i s'eliminaran permanentment del dispositiu",
|
||||||
"delete_dialog_alert_remote": "Aquests elements s'eliminaran permanentment del servidor Immich",
|
"delete_dialog_alert_remote": "Aquests elements s'eliminaran permanentment del servidor Immich",
|
||||||
|
"delete_dialog_cancel": "Cancel·la",
|
||||||
|
"delete_dialog_ok": "Esborra",
|
||||||
"delete_dialog_ok_force": "Suprimeix de totes maneres",
|
"delete_dialog_ok_force": "Suprimeix de totes maneres",
|
||||||
"delete_dialog_title": "Esborra permanentment",
|
"delete_dialog_title": "Esborra permanentment",
|
||||||
"delete_duplicates_confirmation": "Esteu segurs que voleu eliminar aquests duplicats permanentment?",
|
"delete_duplicates_confirmation": "Esteu segurs que voleu eliminar aquests duplicats permanentment?",
|
||||||
"delete_face": "Esborrar cara",
|
"delete_face": "Esborrar cara",
|
||||||
|
"delete_from_immich": "Suprimeix del Immich",
|
||||||
|
"delete_from_local": "Suprimeix del dispositiu",
|
||||||
"delete_key": "Suprimeix la clau",
|
"delete_key": "Suprimeix la clau",
|
||||||
"delete_library": "Suprimeix la Llibreria",
|
"delete_library": "Suprimeix la Llibreria",
|
||||||
"delete_link": "Esborra l'enllaç",
|
"delete_link": "Esborra l'enllaç",
|
||||||
@@ -726,15 +812,18 @@
|
|||||||
"delete_local_dialog_ok_force": "Suprimeix de totes maneres",
|
"delete_local_dialog_ok_force": "Suprimeix de totes maneres",
|
||||||
"delete_others": "Suprimeix altres",
|
"delete_others": "Suprimeix altres",
|
||||||
"delete_shared_link": "Odstranit sdílený odkaz",
|
"delete_shared_link": "Odstranit sdílený odkaz",
|
||||||
|
"delete_shared_link_dialog_content": "Esteu segurs que voleu eliminar aquest enllaç compartit?",
|
||||||
"delete_shared_link_dialog_title": "Suprimeix l'enllaç compartit",
|
"delete_shared_link_dialog_title": "Suprimeix l'enllaç compartit",
|
||||||
"delete_tag": "Eliminar etiqueta",
|
"delete_tag": "Eliminar etiqueta",
|
||||||
"delete_tag_confirmation_prompt": "Estàs segur que vols eliminar l'etiqueta {tagName}?",
|
"delete_tag_confirmation_prompt": "Estàs segur que vols eliminar l'etiqueta {tagName}?",
|
||||||
"delete_user": "Suprimeix l'usuari",
|
"delete_user": "Suprimeix l'usuari",
|
||||||
"deleted_shared_link": "Suprimeix l'enllaç compartit",
|
"deleted_shared_link": "Suprimeix l'enllaç compartit",
|
||||||
"deletes_missing_assets": "Elimina els actius que falten del disc",
|
"deletes_missing_assets": "Elimina els actius que falten del disc",
|
||||||
|
"desc_backup": "Activeu la còpia de seguretat per pujar automàticament els nous elements al servidor en obrir l'aplicació.",
|
||||||
"description": "Descripció",
|
"description": "Descripció",
|
||||||
"description_input_hint_text": "Afegeix descripció...",
|
"description_input_hint_text": "Afegeix descripció...",
|
||||||
"description_input_submit_error": "S'ha produït un error en actualitzar la descripció, comproveu el registre per a més detalls",
|
"description_input_submit_error": "S'ha produït un error en actualitzar la descripció, comproveu el registre per a més detalls",
|
||||||
|
"description_search": "Jornada de senderisme al Pedraforca",
|
||||||
"details": "Detalls",
|
"details": "Detalls",
|
||||||
"direction": "Direcció",
|
"direction": "Direcció",
|
||||||
"disabled": "Desactivat",
|
"disabled": "Desactivat",
|
||||||
@@ -772,6 +861,9 @@
|
|||||||
"downloading_asset_filename": "Descarregant l'element {filename}",
|
"downloading_asset_filename": "Descarregant l'element {filename}",
|
||||||
"downloading_media": "Descàrrega multimèdia",
|
"downloading_media": "Descàrrega multimèdia",
|
||||||
"drop_files_to_upload": "Deixeu els fitxers a qualsevol lloc per carregar-los",
|
"drop_files_to_upload": "Deixeu els fitxers a qualsevol lloc per carregar-los",
|
||||||
|
"duplicated_assets_clear_button": "NETEJA",
|
||||||
|
"duplicated_assets_subtitle": "Fotos i vídeos que estan a la llista negra de l'aplicació.",
|
||||||
|
"duplicated_assets_title": "Elements duplicats ({})",
|
||||||
"duplicates": "Duplicats",
|
"duplicates": "Duplicats",
|
||||||
"duplicates_description": "Resol cada grup indicant quins, si n'hi ha, són duplicats",
|
"duplicates_description": "Resol cada grup indicant quins, si n'hi ha, són duplicats",
|
||||||
"duration": "Duració",
|
"duration": "Duració",
|
||||||
@@ -780,8 +872,12 @@
|
|||||||
"edit_avatar": "Edita l'avatar",
|
"edit_avatar": "Edita l'avatar",
|
||||||
"edit_date": "Edita la data",
|
"edit_date": "Edita la data",
|
||||||
"edit_date_and_time": "Edita data i hora",
|
"edit_date_and_time": "Edita data i hora",
|
||||||
|
"edit_date_time_dialog_date_time": "Data i Hora",
|
||||||
|
"edit_date_time_dialog_search_timezone": "Cerca zona horària...",
|
||||||
|
"edit_date_time_dialog_timezone": "Zona horària",
|
||||||
"edit_exclusion_pattern": "Edita patró d'exclusió",
|
"edit_exclusion_pattern": "Edita patró d'exclusió",
|
||||||
"edit_faces": "Edita les cares",
|
"edit_faces": "Edita les cares",
|
||||||
|
"edit_image_title": "Editar",
|
||||||
"edit_import_path": "Edita la ruta d'importació",
|
"edit_import_path": "Edita la ruta d'importació",
|
||||||
"edit_import_paths": "Edita les rutes d'importació",
|
"edit_import_paths": "Edita les rutes d'importació",
|
||||||
"edit_key": "Edita clau",
|
"edit_key": "Edita clau",
|
||||||
@@ -791,6 +887,7 @@
|
|||||||
"edit_name": "Edita el nom",
|
"edit_name": "Edita el nom",
|
||||||
"edit_people": "Edita la gent",
|
"edit_people": "Edita la gent",
|
||||||
"edit_tag": "Editar etiqueta",
|
"edit_tag": "Editar etiqueta",
|
||||||
|
"edit_time": "Edita data i hora",
|
||||||
"edit_title": "Edita títol",
|
"edit_title": "Edita títol",
|
||||||
"edit_user": "Edita l'usuari",
|
"edit_user": "Edita l'usuari",
|
||||||
"edited": "Editat",
|
"edited": "Editat",
|
||||||
@@ -940,10 +1037,12 @@
|
|||||||
"unable_to_update_user": "No es pot actualitzar l'usuari",
|
"unable_to_update_user": "No es pot actualitzar l'usuari",
|
||||||
"unable_to_upload_file": "No es pot carregar el fitxer"
|
"unable_to_upload_file": "No es pot carregar el fitxer"
|
||||||
},
|
},
|
||||||
|
"excluded": "Exclosos:",
|
||||||
"exif": "Exif",
|
"exif": "Exif",
|
||||||
"exif_bottom_sheet_description": "Afegeix descripció",
|
"exif_bottom_sheet_description": "Afegeix descripció",
|
||||||
"exif_bottom_sheet_details": "DETALLS",
|
"exif_bottom_sheet_details": "DETALLS",
|
||||||
"exif_bottom_sheet_location": "UBICACIÓ",
|
"exif_bottom_sheet_location": "UBICACIÓ",
|
||||||
|
"exif_bottom_sheet_location_add": "Afegeix una ubicació",
|
||||||
"exif_bottom_sheet_people": "PERSONES",
|
"exif_bottom_sheet_people": "PERSONES",
|
||||||
"exif_bottom_sheet_person_add_person": "Afegir nom",
|
"exif_bottom_sheet_person_add_person": "Afegir nom",
|
||||||
"exif_bottom_sheet_person_age": "Edat {}",
|
"exif_bottom_sheet_person_age": "Edat {}",
|
||||||
@@ -976,12 +1075,14 @@
|
|||||||
"favorite_or_unfavorite_photo": "Foto preferida o no preferida",
|
"favorite_or_unfavorite_photo": "Foto preferida o no preferida",
|
||||||
"favorites": "Preferits",
|
"favorites": "Preferits",
|
||||||
"favorites_page_no_favorites": "No s'han trobat preferits",
|
"favorites_page_no_favorites": "No s'han trobat preferits",
|
||||||
|
"favorites_page_title": "Preferits",
|
||||||
"feature_photo_updated": "Foto destacada actualitzada",
|
"feature_photo_updated": "Foto destacada actualitzada",
|
||||||
"features": "Característiques",
|
"features": "Característiques",
|
||||||
"features_setting_description": "Administrar les funcions de l'aplicació",
|
"features_setting_description": "Administrar les funcions de l'aplicació",
|
||||||
"file_name": "Nom de l'arxiu",
|
"file_name": "Nom de l'arxiu",
|
||||||
"file_name_or_extension": "Nom de l'arxiu o extensió",
|
"file_name_or_extension": "Nom de l'arxiu o extensió",
|
||||||
"filename": "Nom del fitxer",
|
"filename": "Nom del fitxer",
|
||||||
|
"filename_search": "Nom o extensió del fitxer",
|
||||||
"filetype": "Tipus d'arxiu",
|
"filetype": "Tipus d'arxiu",
|
||||||
"filter": "Filtrar",
|
"filter": "Filtrar",
|
||||||
"filter_people": "Filtra persones",
|
"filter_people": "Filtra persones",
|
||||||
@@ -1001,6 +1102,7 @@
|
|||||||
"go_to_search": "Vés a cercar",
|
"go_to_search": "Vés a cercar",
|
||||||
"grant_permission": "Concedir permís",
|
"grant_permission": "Concedir permís",
|
||||||
"group_albums_by": "Agrupa àlbums per...",
|
"group_albums_by": "Agrupa àlbums per...",
|
||||||
|
"group_by_sub_title": "Agrupar per",
|
||||||
"group_country": "Agrupar per país",
|
"group_country": "Agrupar per país",
|
||||||
"group_no": "Cap agrupació",
|
"group_no": "Cap agrupació",
|
||||||
"group_owner": "Agrupar per propietari",
|
"group_owner": "Agrupar per propietari",
|
||||||
@@ -1013,6 +1115,7 @@
|
|||||||
"header_settings_field_validator_msg": "El valor no pot estar buit",
|
"header_settings_field_validator_msg": "El valor no pot estar buit",
|
||||||
"header_settings_header_name_input": "Nom de la capçalera",
|
"header_settings_header_name_input": "Nom de la capçalera",
|
||||||
"header_settings_header_value_input": "Valor de la capçalera",
|
"header_settings_header_value_input": "Valor de la capçalera",
|
||||||
|
"header_settings_page_title": "Capçaleres de proxy",
|
||||||
"headers_settings_tile_subtitle": "Definiu les capçaleres de proxy que l'aplicació hauria d'enviar amb cada sol·licitud de xarxa",
|
"headers_settings_tile_subtitle": "Definiu les capçaleres de proxy que l'aplicació hauria d'enviar amb cada sol·licitud de xarxa",
|
||||||
"headers_settings_tile_title": "Capçaleres proxy personalitzades",
|
"headers_settings_tile_title": "Capçaleres proxy personalitzades",
|
||||||
"hi_user": "Hola {name} ({email})",
|
"hi_user": "Hola {name} ({email})",
|
||||||
@@ -1038,6 +1141,7 @@
|
|||||||
"home_page_upload_err_limit": "Només es poden pujar un màxim de 30 elements alhora, ometent",
|
"home_page_upload_err_limit": "Només es poden pujar un màxim de 30 elements alhora, ometent",
|
||||||
"host": "Amfitrió",
|
"host": "Amfitrió",
|
||||||
"hour": "Hora",
|
"hour": "Hora",
|
||||||
|
"id": "ID: {}",
|
||||||
"ignore_icloud_photos": "Ignora fotos d'iCloud",
|
"ignore_icloud_photos": "Ignora fotos d'iCloud",
|
||||||
"ignore_icloud_photos_description": "Les fotos emmagatzemades a iCloud no es penjaran al servidor Immich",
|
"ignore_icloud_photos_description": "Les fotos emmagatzemades a iCloud no es penjaran al servidor Immich",
|
||||||
"image": "Imatge",
|
"image": "Imatge",
|
||||||
@@ -1051,10 +1155,14 @@
|
|||||||
"image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} pres/a a {city}, {country} amb {person1} i {person2} el {date}",
|
"image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} pres/a a {city}, {country} amb {person1} i {person2} el {date}",
|
||||||
"image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} pres/a a {city}, {country} amb {person1}, {person2}, i {person3} el {date}",
|
"image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} pres/a a {city}, {country} amb {person1}, {person2}, i {person3} el {date}",
|
||||||
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} pres/a a {city}, {country} amb {person1}, {person2}, i {additionalCount, number} altres el {date}",
|
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} pres/a a {city}, {country} amb {person1}, {person2}, i {additionalCount, number} altres el {date}",
|
||||||
|
"image_cache_size": "Mida de la memòria cau de imatges ({} elements)",
|
||||||
"image_saved_successfully": "Imatge desada",
|
"image_saved_successfully": "Imatge desada",
|
||||||
|
"image_viewer_page_state_provider_download_error": "Error de baixada",
|
||||||
"image_viewer_page_state_provider_download_started": "Baixada començada",
|
"image_viewer_page_state_provider_download_started": "Baixada començada",
|
||||||
"image_viewer_page_state_provider_download_success": "Baixada amb èxit",
|
"image_viewer_page_state_provider_download_success": "Baixada amb èxit",
|
||||||
"image_viewer_page_state_provider_share_error": "Error en compartir",
|
"image_viewer_page_state_provider_share_error": "Error en compartir",
|
||||||
|
"image_viewer_quality_subtitle": "Ajusta la qualitat del visor de detalls d'imatges",
|
||||||
|
"image_viewer_quality_title": "Qualitat del visor d'imatges",
|
||||||
"immich_logo": "Logotip d'Immich",
|
"immich_logo": "Logotip d'Immich",
|
||||||
"immich_web_interface": "Interfície web Immich",
|
"immich_web_interface": "Interfície web Immich",
|
||||||
"import_from_json": "Importar des de JSON",
|
"import_from_json": "Importar des de JSON",
|
||||||
@@ -1089,17 +1197,29 @@
|
|||||||
"last_seen": "Vist per últim cop",
|
"last_seen": "Vist per últim cop",
|
||||||
"latest_version": "Última versió",
|
"latest_version": "Última versió",
|
||||||
"latitude": "Latitud",
|
"latitude": "Latitud",
|
||||||
|
"layout_settings_dynamic_layout_title": "Disseny dinàmic",
|
||||||
|
"layout_settings_group_automatically": "Automàtic",
|
||||||
|
"layout_settings_group_by": "Agrupa elements per",
|
||||||
|
"layout_settings_group_by_month": "Mes",
|
||||||
|
"layout_settings_group_by_month_day": "Mes + dia",
|
||||||
|
"layout_sub_title": "Disseny",
|
||||||
"leave": "Marxar",
|
"leave": "Marxar",
|
||||||
"lens_model": "Model de lents",
|
"lens_model": "Model de lents",
|
||||||
"let_others_respond": "Deixa que els altres responguin",
|
"let_others_respond": "Deixa que els altres responguin",
|
||||||
"level": "Nivell",
|
"level": "Nivell",
|
||||||
"library": "Bibilioteca",
|
"library": "Bibilioteca",
|
||||||
"library_options": "Opcions de biblioteca",
|
"library_options": "Opcions de biblioteca",
|
||||||
|
"library_page_albums": "Àlbums",
|
||||||
|
"library_page_archive": "Arxiu",
|
||||||
"library_page_device_albums": "Àlbums al Dispositiu",
|
"library_page_device_albums": "Àlbums al Dispositiu",
|
||||||
|
"library_page_favorites": "Preferits",
|
||||||
"library_page_new_album": "Nou àlbum",
|
"library_page_new_album": "Nou àlbum",
|
||||||
|
"library_page_sharing": "Compartint",
|
||||||
"library_page_sort_asset_count": "Nombre d'elements",
|
"library_page_sort_asset_count": "Nombre d'elements",
|
||||||
"library_page_sort_created": "Creat més recentment",
|
"library_page_sort_created": "Creat més recentment",
|
||||||
"library_page_sort_last_modified": "Darrera modificació",
|
"library_page_sort_last_modified": "Darrera modificació",
|
||||||
|
"library_page_sort_most_oldest_photo": "Foto més antiga",
|
||||||
|
"library_page_sort_most_recent_photo": "Foto més recent",
|
||||||
"library_page_sort_title": "Títol de l'àlbum",
|
"library_page_sort_title": "Títol de l'àlbum",
|
||||||
"light": "Llum",
|
"light": "Llum",
|
||||||
"like_deleted": "M'agrada suprimit",
|
"like_deleted": "M'agrada suprimit",
|
||||||
@@ -1115,8 +1235,10 @@
|
|||||||
"location_permission": "Permís d'ubicació",
|
"location_permission": "Permís d'ubicació",
|
||||||
"location_permission_content": "Per utilitzar la funció de canvi automàtic, Immich necessita un permís de ubicació precisa perquè pugui llegir el nom de la xarxa WiFi actual",
|
"location_permission_content": "Per utilitzar la funció de canvi automàtic, Immich necessita un permís de ubicació precisa perquè pugui llegir el nom de la xarxa WiFi actual",
|
||||||
"location_picker_choose_on_map": "Escollir en el mapa",
|
"location_picker_choose_on_map": "Escollir en el mapa",
|
||||||
|
"location_picker_latitude": "Latitud",
|
||||||
"location_picker_latitude_error": "Introdueix una latitud vàlida",
|
"location_picker_latitude_error": "Introdueix una latitud vàlida",
|
||||||
"location_picker_latitude_hint": "Introdueix aquí la latitud",
|
"location_picker_latitude_hint": "Introdueix aquí la latitud",
|
||||||
|
"location_picker_longitude": "Longitud",
|
||||||
"location_picker_longitude_error": "Introdueix una longitud vàlida",
|
"location_picker_longitude_error": "Introdueix una longitud vàlida",
|
||||||
"location_picker_longitude_hint": "Introdueix aquí la longitud",
|
"location_picker_longitude_hint": "Introdueix aquí la longitud",
|
||||||
"log_out": "Tanca la sessió",
|
"log_out": "Tanca la sessió",
|
||||||
@@ -1127,6 +1249,7 @@
|
|||||||
"login_disabled": "S'ha desactivat l'inici de sessió",
|
"login_disabled": "S'ha desactivat l'inici de sessió",
|
||||||
"login_form_api_exception": "Excepció de l'API. Comproveu l'URL del servidor i torneu-ho a provar.",
|
"login_form_api_exception": "Excepció de l'API. Comproveu l'URL del servidor i torneu-ho a provar.",
|
||||||
"login_form_back_button_text": "Enrere",
|
"login_form_back_button_text": "Enrere",
|
||||||
|
"login_form_button_text": "Entra",
|
||||||
"login_form_email_hint": "elteu@correu.cat",
|
"login_form_email_hint": "elteu@correu.cat",
|
||||||
"login_form_endpoint_hint": "http://ip-del-servidor:port",
|
"login_form_endpoint_hint": "http://ip-del-servidor:port",
|
||||||
"login_form_endpoint_url": "URL del servidor",
|
"login_form_endpoint_url": "URL del servidor",
|
||||||
@@ -1139,6 +1262,9 @@
|
|||||||
"login_form_failed_get_oauth_server_disable": "La funcionalitat OAuth no està disponible en aquest servidor",
|
"login_form_failed_get_oauth_server_disable": "La funcionalitat OAuth no està disponible en aquest servidor",
|
||||||
"login_form_failed_login": "Error en iniciar sessió, comprova l'URL del servidor, el correu electrònic i la contrasenya.",
|
"login_form_failed_login": "Error en iniciar sessió, comprova l'URL del servidor, el correu electrònic i la contrasenya.",
|
||||||
"login_form_handshake_exception": "S'ha produït una excepció de handshake amb el servidor. Activa el suport per certificats autofirmats a la configuració si estàs fent servir un certificat autofirmat.",
|
"login_form_handshake_exception": "S'ha produït una excepció de handshake amb el servidor. Activa el suport per certificats autofirmats a la configuració si estàs fent servir un certificat autofirmat.",
|
||||||
|
"login_form_label_email": "Correu electrònic",
|
||||||
|
"login_form_label_password": "Contrasenya",
|
||||||
|
"login_form_next_button": "Següent",
|
||||||
"login_form_password_hint": "contrasenya",
|
"login_form_password_hint": "contrasenya",
|
||||||
"login_form_save_login": "Mantingues identificat",
|
"login_form_save_login": "Mantingues identificat",
|
||||||
"login_form_server_empty": "Introdueix l'URL del servidor.",
|
"login_form_server_empty": "Introdueix l'URL del servidor.",
|
||||||
@@ -1153,7 +1279,6 @@
|
|||||||
"loop_videos": "Vídeos en bucle",
|
"loop_videos": "Vídeos en bucle",
|
||||||
"loop_videos_description": "Habilita la reproducció en bucle del vídeo en els detalls.",
|
"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 usant una versió de desenvolupaent. Recomanem fer servir una versió publicada!",
|
||||||
"main_menu": "Menú principal",
|
|
||||||
"make": "Fabricant",
|
"make": "Fabricant",
|
||||||
"manage_shared_links": "Administrar enllaços compartits",
|
"manage_shared_links": "Administrar enllaços compartits",
|
||||||
"manage_sharing_with_partners": "Gestiona la compartició amb els companys",
|
"manage_sharing_with_partners": "Gestiona la compartició amb els companys",
|
||||||
@@ -1166,6 +1291,7 @@
|
|||||||
"map_assets_in_bound": "{} foto",
|
"map_assets_in_bound": "{} foto",
|
||||||
"map_assets_in_bounds": "{} fotos",
|
"map_assets_in_bounds": "{} fotos",
|
||||||
"map_cannot_get_user_location": "No es pot obtenir la ubicació de l'usuari",
|
"map_cannot_get_user_location": "No es pot obtenir la ubicació de l'usuari",
|
||||||
|
"map_location_dialog_cancel": "Cancel·la",
|
||||||
"map_location_dialog_yes": "Sí",
|
"map_location_dialog_yes": "Sí",
|
||||||
"map_location_picker_page_use_location": "Utilitzar aquesta ubicació",
|
"map_location_picker_page_use_location": "Utilitzar aquesta ubicació",
|
||||||
"map_location_service_disabled_content": "El servei de localització s'ha d'activar per mostrar els elements de la teva ubicació actual. Vols activar-lo ara?",
|
"map_location_service_disabled_content": "El servei de localització s'ha d'activar per mostrar els elements de la teva ubicació actual. Vols activar-lo ara?",
|
||||||
@@ -1177,13 +1303,17 @@
|
|||||||
"map_no_location_permission_title": "Permís de localització denegat",
|
"map_no_location_permission_title": "Permís de localització denegat",
|
||||||
"map_settings": "Paràmetres de mapa",
|
"map_settings": "Paràmetres de mapa",
|
||||||
"map_settings_dark_mode": "Mode fosc",
|
"map_settings_dark_mode": "Mode fosc",
|
||||||
|
"map_settings_date_range_option_all": "Tot",
|
||||||
"map_settings_date_range_option_day": "Últimes 24 hores",
|
"map_settings_date_range_option_day": "Últimes 24 hores",
|
||||||
"map_settings_date_range_option_days": "Darrers {} dies",
|
"map_settings_date_range_option_days": "Darrers {} dies",
|
||||||
"map_settings_date_range_option_year": "Any passat",
|
"map_settings_date_range_option_year": "Any passat",
|
||||||
"map_settings_date_range_option_years": "Darrers {} anys",
|
"map_settings_date_range_option_years": "Darrers {} anys",
|
||||||
|
"map_settings_dialog_cancel": "Cancel·la",
|
||||||
|
"map_settings_dialog_save": "Desa",
|
||||||
"map_settings_dialog_title": "Configuració del mapa",
|
"map_settings_dialog_title": "Configuració del mapa",
|
||||||
"map_settings_include_show_archived": "Incloure arxivats",
|
"map_settings_include_show_archived": "Incloure arxivats",
|
||||||
"map_settings_include_show_partners": "Incloure companys",
|
"map_settings_include_show_partners": "Incloure companys",
|
||||||
|
"map_settings_only_relative_range": "Rang de dates",
|
||||||
"map_settings_only_show_favorites": "Mostra només preferits",
|
"map_settings_only_show_favorites": "Mostra només preferits",
|
||||||
"map_settings_theme_settings": "Tema del Mapa",
|
"map_settings_theme_settings": "Tema del Mapa",
|
||||||
"map_zoom_to_see_photos": "Allunya per veure fotos",
|
"map_zoom_to_see_photos": "Allunya per veure fotos",
|
||||||
@@ -1213,6 +1343,8 @@
|
|||||||
"month": "Mes",
|
"month": "Mes",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
"more": "Més",
|
"more": "Més",
|
||||||
|
"motion_photos": "Fotografies animades",
|
||||||
|
"motion_photos_page_title": "Fotos en moviment",
|
||||||
"moved_to_trash": "S'ha mogut a la paperera",
|
"moved_to_trash": "S'ha mogut a la paperera",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "No es pot canviar la data del fitxer(s) de només lectura, ometent",
|
"multiselect_grid_edit_date_time_err_read_only": "No es pot canviar la data del fitxer(s) de només lectura, ometent",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "No es pot canviar la localització de fitxers de només lectura. Saltant.",
|
"multiselect_grid_edit_gps_err_read_only": "No es pot canviar la localització de fitxers de només lectura. Saltant.",
|
||||||
@@ -1245,15 +1377,20 @@
|
|||||||
"no_favorites_message": "Afegiu preferits per trobar les millors fotos i vídeos a l'instant",
|
"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_libraries_message": "Creeu una llibreria externa per veure les vostres fotos i vídeos",
|
||||||
"no_name": "Sense nom",
|
"no_name": "Sense nom",
|
||||||
|
"no_objects": "No hi ha informació d'objectes disponibles",
|
||||||
"no_places": "No hi ha llocs",
|
"no_places": "No hi ha llocs",
|
||||||
"no_results": "Sense resultats",
|
"no_results": "Sense resultats",
|
||||||
"no_results_description": "Proveu un sinònim o una paraula clau més general",
|
"no_results_description": "Proveu un sinònim o una paraula clau més general",
|
||||||
"no_shared_albums_message": "Creeu un àlbum per compartir fotos i vídeos amb persones a la vostra xarxa",
|
"no_shared_albums_message": "Creeu un àlbum per compartir fotos i vídeos amb persones a la vostra xarxa",
|
||||||
|
"none_selected": "Cap seleccionat",
|
||||||
"not_in_any_album": "En cap àlbum",
|
"not_in_any_album": "En cap àlbum",
|
||||||
"not_selected": "No seleccionat",
|
"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",
|
"note_apply_storage_label_to_previously_uploaded assets": "Nota: per aplicar l'etiqueta d'emmagatzematge als actius penjats anteriorment, executeu el",
|
||||||
|
"note_unlimited_quota": "Nota: Intruduïu 0 per a quota il·limitada",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
|
"notification_permission_dialog_cancel": "Cancel·la",
|
||||||
"notification_permission_dialog_content": "Per activar les notificacions, aneu a Configuració i seleccioneu permet.",
|
"notification_permission_dialog_content": "Per activar les notificacions, aneu a Configuració i seleccioneu permet.",
|
||||||
|
"notification_permission_dialog_settings": "Configuració",
|
||||||
"notification_permission_list_tile_content": "Atorga permís per a activar les notificacions.",
|
"notification_permission_list_tile_content": "Atorga permís per a activar les notificacions.",
|
||||||
"notification_permission_list_tile_enable_button": "Activa les notificacions",
|
"notification_permission_list_tile_enable_button": "Activa les notificacions",
|
||||||
"notification_permission_list_tile_title": "Permís de notificacions",
|
"notification_permission_list_tile_title": "Permís de notificacions",
|
||||||
@@ -1287,18 +1424,22 @@
|
|||||||
"other_variables": "Altres variables",
|
"other_variables": "Altres variables",
|
||||||
"owned": "Propi",
|
"owned": "Propi",
|
||||||
"owner": "Propietari",
|
"owner": "Propietari",
|
||||||
|
"page_share_add_users": "Afegeix usuaris",
|
||||||
"partner": "Company/a",
|
"partner": "Company/a",
|
||||||
"partner_can_access": "{partner} hi té accés",
|
"partner_can_access": "{partner} hi té accés",
|
||||||
"partner_can_access_assets": "Totes les vostres fotos i vídeos excepte les arxivades i eliminades",
|
"partner_can_access_assets": "Totes les vostres fotos i vídeos excepte les arxivades i eliminades",
|
||||||
"partner_can_access_location": "Ubicació en què s'han fet les fotos",
|
"partner_can_access_location": "Ubicació en què s'han fet les fotos",
|
||||||
"partner_list_user_photos": "fotos de {user}",
|
"partner_list_user_photos": "fotos de {user}",
|
||||||
"partner_list_view_all": "Veure tot",
|
"partner_list_view_all": "Veure tot",
|
||||||
|
"partner_page_add_partner": "Afegeix company",
|
||||||
"partner_page_empty_message": "Les teves fotos encara no estan compartides amb cap company.",
|
"partner_page_empty_message": "Les teves fotos encara no estan compartides amb cap company.",
|
||||||
"partner_page_no_more_users": "No hi ha més usuaris a afegir",
|
"partner_page_no_more_users": "No hi ha més usuaris a afegir",
|
||||||
"partner_page_partner_add_failed": "No s'ha pogut afegir el company",
|
"partner_page_partner_add_failed": "No s'ha pogut afegir el company",
|
||||||
"partner_page_select_partner": "Escull company",
|
"partner_page_select_partner": "Escull company",
|
||||||
"partner_page_shared_to_title": "Compartit amb",
|
"partner_page_shared_to_title": "Compartit amb",
|
||||||
"partner_page_stop_sharing_content": "{} ja no podrà accedir a les teves fotos.",
|
"partner_page_stop_sharing_content": "{} ja no podrà accedir a les teves fotos.",
|
||||||
|
"partner_page_stop_sharing_title": "Vols deixar de compartir les teves fotos?",
|
||||||
|
"partner_page_title": "Company",
|
||||||
"partner_sharing": "Compartició amb companys",
|
"partner_sharing": "Compartició amb companys",
|
||||||
"partners": "Companys",
|
"partners": "Companys",
|
||||||
"password": "Contrasenya",
|
"password": "Contrasenya",
|
||||||
@@ -1331,12 +1472,20 @@
|
|||||||
"permission_onboarding_continue_anyway": "Continua de totes maneres",
|
"permission_onboarding_continue_anyway": "Continua de totes maneres",
|
||||||
"permission_onboarding_get_started": "Comença",
|
"permission_onboarding_get_started": "Comença",
|
||||||
"permission_onboarding_go_to_settings": "Ves a la configuració",
|
"permission_onboarding_go_to_settings": "Ves a la configuració",
|
||||||
|
"permission_onboarding_grant_permission": "Dona permisos",
|
||||||
|
"permission_onboarding_log_out": "Tanca la sessió",
|
||||||
"permission_onboarding_permission_denied": "S'ha denegat el permís. Per utilitzar Immich, concediu permisos de fotos i vídeos a Configuració.",
|
"permission_onboarding_permission_denied": "S'ha denegat el permís. Per utilitzar Immich, concediu permisos de fotos i vídeos a Configuració.",
|
||||||
"permission_onboarding_permission_granted": "Permís concedit! Tot a punt.",
|
"permission_onboarding_permission_granted": "Permís concedit! Tot a punt.",
|
||||||
"permission_onboarding_permission_limited": "Permís limitat. Per a permetre que Immich faci còpies de seguretat i gestioni tota la col·lecció de la galeria, concediu permisos de fotos i vídeos a Configuració.",
|
"permission_onboarding_permission_limited": "Permís limitat. Per a permetre que Immich faci còpies de seguretat i gestioni tota la col·lecció de la galeria, concediu permisos de fotos i vídeos a Configuració.",
|
||||||
"permission_onboarding_request": "Immich requereix permís per veure les vostres fotos i vídeos.",
|
"permission_onboarding_request": "Immich requereix permís per veure les vostres fotos i vídeos.",
|
||||||
"person": "Persona",
|
"person": "Persona",
|
||||||
"person_birthdate": "Nascut a {date}",
|
"person_add_name_dialog_cancel": "Cancel·la",
|
||||||
|
"person_add_name_dialog_hint": "Nom",
|
||||||
|
"person_add_name_dialog_save": "Desa",
|
||||||
|
"person_add_name_dialog_title": "Afegeix un nom",
|
||||||
|
"person_add_name_subtitle": "Troba'ls ràpid buscant per nom",
|
||||||
|
"person_add_name_title": "Afegeix un nom",
|
||||||
|
"person_edit_name": "Edita el nom",
|
||||||
"person_hidden": "{name}{hidden, select, true { (ocultat)} other {}}",
|
"person_hidden": "{name}{hidden, select, true { (ocultat)} other {}}",
|
||||||
"photo_shared_all_users": "Sembla que has compartit les teves fotos amb tots els usuaris o no tens cap usuari amb qui compartir-les.",
|
"photo_shared_all_users": "Sembla que has compartit les teves fotos amb tots els usuaris o no tens cap usuari amb qui compartir-les.",
|
||||||
"photos": "Fotos",
|
"photos": "Fotos",
|
||||||
@@ -1360,14 +1509,20 @@
|
|||||||
"previous_memory": "Memòria anterior",
|
"previous_memory": "Memòria anterior",
|
||||||
"previous_or_next_photo": "Foto anterior o següent",
|
"previous_or_next_photo": "Foto anterior o següent",
|
||||||
"primary": "Primària",
|
"primary": "Primària",
|
||||||
|
"primary_color_subtitle": "Trieu un color per a les accions i els accents principals.",
|
||||||
|
"primary_color_title": "Color primari",
|
||||||
"privacy": "Privacitat",
|
"privacy": "Privacitat",
|
||||||
"profile_drawer_app_logs": "Registres",
|
"profile_drawer_app_logs": "Registres",
|
||||||
"profile_drawer_client_out_of_date_major": "L'aplicació mòbil està desactualitzada. Si us plau, actualitzeu a l'última versió major.",
|
"profile_drawer_client_out_of_date_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_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_client_server_up_to_date": "El Client i el Servidor estan actualitzats",
|
||||||
|
"profile_drawer_documentation": "Documentació",
|
||||||
"profile_drawer_github": "GitHub",
|
"profile_drawer_github": "GitHub",
|
||||||
"profile_drawer_server_out_of_date_major": "L'aplicació mòbil està desactualitzada. Si us plau, actualitzeu a l'última versió major.",
|
"profile_drawer_server_out_of_date_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_server_out_of_date_minor": "L'aplicació mòbil està desactualitzada. Si us plau, actualitzeu a l'última versió menor.",
|
||||||
|
"profile_drawer_settings": "Configuració",
|
||||||
|
"profile_drawer_sign_out": "Tanca la sessió",
|
||||||
|
"profile_drawer_trash": "Paperera",
|
||||||
"profile_image_of_user": "Imatge de perfil de {user}",
|
"profile_image_of_user": "Imatge de perfil de {user}",
|
||||||
"profile_picture_set": "Imatge de perfil configurada.",
|
"profile_picture_set": "Imatge de perfil configurada.",
|
||||||
"public_album": "Àlbum públic",
|
"public_album": "Àlbum públic",
|
||||||
@@ -1430,6 +1585,8 @@
|
|||||||
"refreshing_faces": "Refrescant cares",
|
"refreshing_faces": "Refrescant cares",
|
||||||
"refreshing_metadata": "Actualitzant les metadades",
|
"refreshing_metadata": "Actualitzant les metadades",
|
||||||
"regenerating_thumbnails": "Regenerant les miniatures",
|
"regenerating_thumbnails": "Regenerant les miniatures",
|
||||||
|
"remainder": "Restant",
|
||||||
|
"remainder_sub": "Fotografies i vídeos restants per copiar de la selecció",
|
||||||
"remove": "Eliminar",
|
"remove": "Eliminar",
|
||||||
"remove_assets_album_confirmation": "Confirmes que vols eliminar {count, plural, one {# recurs} other {# recursos}} de l'àlbum?",
|
"remove_assets_album_confirmation": "Confirmes que vols eliminar {count, plural, one {# recurs} other {# recursos}} de l'àlbum?",
|
||||||
"remove_assets_shared_link_confirmation": "Esteu segur que voleu eliminar {count, plural, one {# recurs} other {# recursos}} d'aquest enllaç compartit?",
|
"remove_assets_shared_link_confirmation": "Esteu segur que voleu eliminar {count, plural, one {# recurs} other {# recursos}} d'aquest enllaç compartit?",
|
||||||
@@ -1439,24 +1596,22 @@
|
|||||||
"remove_from_album": "Treu de l'àlbum",
|
"remove_from_album": "Treu de l'àlbum",
|
||||||
"remove_from_favorites": "Eliminar dels preferits",
|
"remove_from_favorites": "Eliminar dels preferits",
|
||||||
"remove_from_shared_link": "Eliminar de l'enllaç compartit",
|
"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_url": "Eliminar URL",
|
"remove_url": "Eliminar URL",
|
||||||
"remove_user": "Eliminar l'usuari",
|
"remove_user": "Eliminar l'usuari",
|
||||||
"removed_api_key": "Eliminada la clau d'API: {name}",
|
"removed_api_key": "Eliminada la clau d'API: {name}",
|
||||||
"removed_from_archive": "Eliminat de l'arxiu",
|
"removed_from_archive": "Eliminat de l'arxiu",
|
||||||
"removed_from_favorites": "Eliminat dels preferits",
|
"removed_from_favorites": "Eliminat dels preferits",
|
||||||
"removed_from_favorites_count": "{count, plural, other {# eliminats}} dels preferits",
|
"removed_from_favorites_count": "{count, plural, other {# eliminats}} dels preferits",
|
||||||
"removed_memory": "Memòria esborrada",
|
"removed_memory": "Eliminat memòria",
|
||||||
"removed_photo_from_memory": "Eliminat foto de la memòria",
|
"removed_photo_from_memory": "Eliminat foto de memòria",
|
||||||
"removed_tagged_assets": "Etiqueta eliminada de {count, plural, one {# actiu} other {# actius}}",
|
"removed_tagged_assets": "Etiqueta eliminada de {count, plural, one {# actiu} other {# actius}}",
|
||||||
"rename": "Canviar nom",
|
"rename": "Canviar nom",
|
||||||
"repair": "Reparació",
|
"repair": "Reparació",
|
||||||
"repair_no_results_message": "Els fitxers sense seguiment o que falten es mostraran aquí",
|
"repair_no_results_message": "Els fitxers sense seguiment i que falten es mostraran aquí",
|
||||||
"replace_with_upload": "Substitueix amb una pujada",
|
"replace_with_upload": "Substituir amb una pujada",
|
||||||
"repository": "Repositori",
|
"repository": "Repositori",
|
||||||
"require_password": "Requereix contrasenya",
|
"require_password": "Requereix contrasenya",
|
||||||
"require_user_to_change_password_on_first_login": "Demanar que l'usuari canviï la contrasenya en el primer inici de sessió",
|
"require_user_to_change_password_on_first_login": "Requerir que l'usuari canviï la contrasenya en el primer inici de sessió",
|
||||||
"rescan": "Tornar a escanejar",
|
"rescan": "Tornar a escanejar",
|
||||||
"reset": "Restablir",
|
"reset": "Restablir",
|
||||||
"reset_password": "Restablir contrasenya",
|
"reset_password": "Restablir contrasenya",
|
||||||
@@ -1473,7 +1628,7 @@
|
|||||||
"review_duplicates": "Revisar duplicats",
|
"review_duplicates": "Revisar duplicats",
|
||||||
"role": "Rol",
|
"role": "Rol",
|
||||||
"role_editor": "Editor",
|
"role_editor": "Editor",
|
||||||
"role_viewer": "Visor",
|
"role_viewer": "Visualitzador",
|
||||||
"save": "Desa",
|
"save": "Desa",
|
||||||
"save_to_gallery": "Desa a galeria",
|
"save_to_gallery": "Desa a galeria",
|
||||||
"saved_api_key": "Clau d'API guardada",
|
"saved_api_key": "Clau d'API guardada",
|
||||||
@@ -1485,8 +1640,10 @@
|
|||||||
"scan_library": "Escaneja",
|
"scan_library": "Escaneja",
|
||||||
"scan_settings": "Configuració d'escaneig",
|
"scan_settings": "Configuració d'escaneig",
|
||||||
"scanning_for_album": "S'està buscant l'àlbum...",
|
"scanning_for_album": "S'està buscant l'àlbum...",
|
||||||
|
"screenshots": "Captures de pantalla",
|
||||||
"search": "Cerca",
|
"search": "Cerca",
|
||||||
"search_albums": "Buscar àlbums",
|
"search_albums": "Buscar àlbums",
|
||||||
|
"search_bar_hint": "Cerca les teves fotos",
|
||||||
"search_by_context": "Buscar per context",
|
"search_by_context": "Buscar per context",
|
||||||
"search_by_description": "Cercar per descripció",
|
"search_by_description": "Cercar per descripció",
|
||||||
"search_by_description_example": "Jornada de senderisme a Sapa",
|
"search_by_description_example": "Jornada de senderisme a Sapa",
|
||||||
@@ -1497,17 +1654,33 @@
|
|||||||
"search_city": "Buscar per ciutat...",
|
"search_city": "Buscar per ciutat...",
|
||||||
"search_country": "Buscar per país...",
|
"search_country": "Buscar per país...",
|
||||||
"search_filter_apply": "Aplicar filtre",
|
"search_filter_apply": "Aplicar filtre",
|
||||||
|
"search_filter_camera": "Càmera",
|
||||||
|
"search_filter_camera_make": "Marca",
|
||||||
|
"search_filter_camera_model": "Model",
|
||||||
"search_filter_camera_title": "Selecciona el tipus de càmera",
|
"search_filter_camera_title": "Selecciona el tipus de càmera",
|
||||||
|
"search_filter_contextual": "Cerca per contexte",
|
||||||
"search_filter_date": "Data",
|
"search_filter_date": "Data",
|
||||||
"search_filter_date_interval": "{start} a {end}",
|
"search_filter_date_interval": "{start} a {end}",
|
||||||
"search_filter_date_title": "Selecciona un rang de dates",
|
"search_filter_date_title": "Selecciona un rang de dates",
|
||||||
|
"search_filter_description": "Cerca per descripció",
|
||||||
|
"search_filter_display_option_archive": "Arxivat",
|
||||||
|
"search_filter_display_option_favorite": "Preferit",
|
||||||
"search_filter_display_option_not_in_album": "No en àlbum",
|
"search_filter_display_option_not_in_album": "No en àlbum",
|
||||||
"search_filter_display_options": "Opcions de Visualització",
|
"search_filter_display_options": "Opcions de Visualització",
|
||||||
|
"search_filter_display_options_title": "Opcions de visualització",
|
||||||
"search_filter_filename": "Cerca pel nom del fitxer",
|
"search_filter_filename": "Cerca pel nom del fitxer",
|
||||||
"search_filter_location": "Ubicació",
|
"search_filter_location": "Ubicació",
|
||||||
|
"search_filter_location_city": "Ciutat",
|
||||||
|
"search_filter_location_country": "País",
|
||||||
|
"search_filter_location_state": "Estat",
|
||||||
"search_filter_location_title": "Selecciona l'ubicació",
|
"search_filter_location_title": "Selecciona l'ubicació",
|
||||||
"search_filter_media_type": "Tipus de multimèdia",
|
"search_filter_media_type": "Tipus de multimèdia",
|
||||||
|
"search_filter_media_type_all": "Tot",
|
||||||
|
"search_filter_media_type_image": "Imatge",
|
||||||
"search_filter_media_type_title": "Selecciona tipus de multimèdia",
|
"search_filter_media_type_title": "Selecciona tipus de multimèdia",
|
||||||
|
"search_filter_media_type_video": "Vídeo",
|
||||||
|
"search_filter_people": "Persones",
|
||||||
|
"search_filter_people_hint": "Filtra persones",
|
||||||
"search_filter_people_title": "Selecciona persones",
|
"search_filter_people_title": "Selecciona persones",
|
||||||
"search_for": "Cercar",
|
"search_for": "Cercar",
|
||||||
"search_for_existing_person": "Busca una persona existent",
|
"search_for_existing_person": "Busca una persona existent",
|
||||||
@@ -1517,17 +1690,30 @@
|
|||||||
"search_no_result": "No s'han trobat resultats, proveu un terme de cerca o una combinació diferents",
|
"search_no_result": "No s'han trobat resultats, proveu un terme de cerca o una combinació diferents",
|
||||||
"search_options": "Opcions de cerca",
|
"search_options": "Opcions de cerca",
|
||||||
"search_page_categories": "Categories",
|
"search_page_categories": "Categories",
|
||||||
|
"search_page_favorites": "Preferides",
|
||||||
"search_page_motion_photos": "Fotografies animades",
|
"search_page_motion_photos": "Fotografies animades",
|
||||||
"search_page_no_objects": "No hi ha informació d'objectes disponibles",
|
"search_page_no_objects": "No hi ha informació d'objectes disponibles",
|
||||||
"search_page_no_places": "No hi ha informació de llocs disponibles",
|
"search_page_no_places": "No hi ha informació de llocs disponibles",
|
||||||
|
"search_page_people": "Persones",
|
||||||
|
"search_page_person_add_name_dialog_cancel": "Cancel·la",
|
||||||
|
"search_page_person_add_name_dialog_hint": "Nom",
|
||||||
|
"search_page_person_add_name_dialog_save": "Desa",
|
||||||
|
"search_page_person_add_name_dialog_title": "Afegeix un nom",
|
||||||
|
"search_page_person_add_name_subtitle": "Troba'ls ràpid buscant per nom",
|
||||||
|
"search_page_person_add_name_title": "Afegeix un nom",
|
||||||
|
"search_page_person_edit_name": "Edita el nom",
|
||||||
|
"search_page_places": "Llocs",
|
||||||
|
"search_page_recently_added": "Afegit recentment",
|
||||||
"search_page_screenshots": "Captures de pantalla",
|
"search_page_screenshots": "Captures de pantalla",
|
||||||
"search_page_search_photos_videos": "Cerca les teves fotos i vídeos",
|
"search_page_search_photos_videos": "Cerca les teves fotos i vídeos",
|
||||||
"search_page_selfies": "Autofotos",
|
"search_page_selfies": "Autofotos",
|
||||||
"search_page_things": "Coses",
|
"search_page_things": "Coses",
|
||||||
|
"search_page_videos": "Vídeos",
|
||||||
"search_page_view_all_button": "Veure tot",
|
"search_page_view_all_button": "Veure tot",
|
||||||
"search_page_your_activity": "La teva activitat",
|
"search_page_your_activity": "La teva activitat",
|
||||||
"search_page_your_map": "El teu mapa",
|
"search_page_your_map": "El teu mapa",
|
||||||
"search_people": "Buscar persones",
|
"search_people": "Buscar persones",
|
||||||
|
"search_photos_videos": "Cerca les teves fotos i vídeos",
|
||||||
"search_places": "Buscar llocs",
|
"search_places": "Buscar llocs",
|
||||||
"search_rating": "Buscar per qualificació...",
|
"search_rating": "Buscar per qualificació...",
|
||||||
"search_result_page_new_search_hint": "Cerca nova",
|
"search_result_page_new_search_hint": "Cerca nova",
|
||||||
@@ -1543,6 +1729,7 @@
|
|||||||
"second": "Segon",
|
"second": "Segon",
|
||||||
"see_all_people": "Veure totes les persones",
|
"see_all_people": "Veure totes les persones",
|
||||||
"select": "Selecciona",
|
"select": "Selecciona",
|
||||||
|
"select_additional_user_for_sharing_page_suggestions": "Suggeriments",
|
||||||
"select_album_cover": "Seleccionar la portada de l'àlbum",
|
"select_album_cover": "Seleccionar la portada de l'àlbum",
|
||||||
"select_all": "Selecciona-ho tot",
|
"select_all": "Selecciona-ho tot",
|
||||||
"select_all_duplicates": "Seleccioneu tots els duplicats",
|
"select_all_duplicates": "Seleccioneu tots els duplicats",
|
||||||
@@ -1556,16 +1743,21 @@
|
|||||||
"select_photos": "Tria fotografies",
|
"select_photos": "Tria fotografies",
|
||||||
"select_trash_all": "Envia la selecció a la paperera",
|
"select_trash_all": "Envia la selecció a la paperera",
|
||||||
"select_user_for_sharing_page_err_album": "Error al crear l'àlbum",
|
"select_user_for_sharing_page_err_album": "Error al crear l'àlbum",
|
||||||
|
"select_user_for_sharing_page_share_suggestions": "Suggeriments",
|
||||||
"selected": "Seleccionat",
|
"selected": "Seleccionat",
|
||||||
"selected_count": "{count, plural, one {# seleccionat} other {# seleccionats}}",
|
"selected_count": "{count, plural, one {# seleccionat} other {# seleccionats}}",
|
||||||
|
"selfies": "Autofotos",
|
||||||
"send_message": "Envia missatge",
|
"send_message": "Envia missatge",
|
||||||
"send_welcome_email": "Envia correu de benvinguda",
|
"send_welcome_email": "Envia correu de benvinguda",
|
||||||
"server_endpoint": "Endpoint de Servidor",
|
"server_endpoint": "Endpoint de Servidor",
|
||||||
"server_info_box_app_version": "Versió de l'aplicació",
|
"server_info_box_app_version": "Versió de l'aplicació",
|
||||||
|
"server_info_box_latest_release": "Última versió",
|
||||||
"server_info_box_server_url": "URL del servidor",
|
"server_info_box_server_url": "URL del servidor",
|
||||||
|
"server_info_box_server_version": "Versió del servidor",
|
||||||
"server_offline": "Servidor fora de línia",
|
"server_offline": "Servidor fora de línia",
|
||||||
"server_online": "Servidor en línia",
|
"server_online": "Servidor en línia",
|
||||||
"server_stats": "Estadístiques del servidor",
|
"server_stats": "Estadístiques del servidor",
|
||||||
|
"server_storage": "Emmagatzematge del servidor",
|
||||||
"server_version": "Versió del servidor",
|
"server_version": "Versió del servidor",
|
||||||
"set": "Establir",
|
"set": "Establir",
|
||||||
"set_as_album_cover": "Establir com a portada de l'àlbum",
|
"set_as_album_cover": "Establir com a portada de l'àlbum",
|
||||||
@@ -1592,25 +1784,42 @@
|
|||||||
"setting_notifications_single_progress_subtitle": "Informació detallada del progrés de la pujada de cada fitxer",
|
"setting_notifications_single_progress_subtitle": "Informació detallada del progrés de la pujada de cada fitxer",
|
||||||
"setting_notifications_single_progress_title": "Mostra el progrés detallat de la còpia de seguretat en segon pla",
|
"setting_notifications_single_progress_title": "Mostra el progrés detallat de la còpia de seguretat en segon pla",
|
||||||
"setting_notifications_subtitle": "Ajusta les preferències de notificació",
|
"setting_notifications_subtitle": "Ajusta les preferències de notificació",
|
||||||
|
"setting_notifications_title": "Notificacions",
|
||||||
"setting_notifications_total_progress_subtitle": "Progrés general de la pujada (elements completats/total)",
|
"setting_notifications_total_progress_subtitle": "Progrés general de la pujada (elements completats/total)",
|
||||||
"setting_notifications_total_progress_title": "Mostra el progrés total de la còpia de seguretat en segon pla",
|
"setting_notifications_total_progress_title": "Mostra el progrés total de la còpia de seguretat en segon pla",
|
||||||
|
"setting_pages_app_bar_settings": "Configuració",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Habilita per reproduir automàticament un vídeo al visualitzador de detalls.",
|
||||||
"setting_video_viewer_looping_title": "Bucle",
|
"setting_video_viewer_looping_title": "Bucle",
|
||||||
"setting_video_viewer_original_video_subtitle": "Quan reproduïu un vídeo des del servidor, reproduïu l'original encara que hi hagi una transcodificació disponible. Pot conduir a l'amortització. Els vídeos disponibles localment es reprodueixen en qualitat original independentment d'aquesta configuració.",
|
"setting_video_viewer_original_video_subtitle": "Quan reproduïu un vídeo des del servidor, reproduïu l'original encara que hi hagi una transcodificació disponible. Pot conduir a l'amortització. Els vídeos disponibles localment es reprodueixen en qualitat original independentment d'aquesta configuració.",
|
||||||
"setting_video_viewer_original_video_title": "Força el vídeo original",
|
"setting_video_viewer_original_video_title": "Força el vídeo original",
|
||||||
|
"setting_video_viewer_title": "Vídeos",
|
||||||
"settings": "Configuració",
|
"settings": "Configuració",
|
||||||
"settings_require_restart": "Si us plau, reinicieu Immich per a aplicar aquest canvi",
|
"settings_require_restart": "Si us plau, reinicieu Immich per a aplicar aquest canvi",
|
||||||
"settings_saved": "Configuració desada",
|
"settings_saved": "Configuració desada",
|
||||||
|
"settings_subtitle": "Configuració del disseny de la graella de fotos",
|
||||||
|
"settings_title": "Graella de fotos",
|
||||||
"share": "Comparteix",
|
"share": "Comparteix",
|
||||||
|
"share_add": "Afegeix",
|
||||||
"share_add_photos": "Afegeix fotografies",
|
"share_add_photos": "Afegeix fotografies",
|
||||||
|
"share_add_title": "Afegeix un títol",
|
||||||
"share_assets_selected": "{} seleccionats",
|
"share_assets_selected": "{} seleccionats",
|
||||||
|
"share_create_album": "Crea un àlbum",
|
||||||
"share_dialog_preparing": "S'està preparant...",
|
"share_dialog_preparing": "S'està preparant...",
|
||||||
|
"share_done": "Fet",
|
||||||
|
"share_invite": "Convida a l'àlbum",
|
||||||
|
"share_link": "Comparteix l'enllaç",
|
||||||
|
"share_to": "Comparteix a",
|
||||||
"shared": "Compartit",
|
"shared": "Compartit",
|
||||||
"shared_album_activities_input_disable": "Els comentaris estan desactivats",
|
"shared_album_activities_input_disable": "Els comentaris estan desactivats",
|
||||||
|
"shared_album_activities_input_hint": "Digues alguna cosa",
|
||||||
"shared_album_activity_remove_content": "Voleu eliminar aquesta activitat?",
|
"shared_album_activity_remove_content": "Voleu eliminar aquesta activitat?",
|
||||||
"shared_album_activity_remove_title": "Elimina l'activitat",
|
"shared_album_activity_remove_title": "Elimina l'activitat",
|
||||||
|
"shared_album_activity_setting_subtitle": "Permet que altres responguin",
|
||||||
|
"shared_album_activity_setting_title": "Comentaris i m'agrada",
|
||||||
"shared_album_section_people_action_error": "S'ha produït un error en retirar-se/eliminar l'àlbum",
|
"shared_album_section_people_action_error": "S'ha produït un error en retirar-se/eliminar l'àlbum",
|
||||||
"shared_album_section_people_action_leave": "Elimina l'usuari de l'àlbum",
|
"shared_album_section_people_action_leave": "Elimina l'usuari de l'àlbum",
|
||||||
"shared_album_section_people_action_remove_user": "Elimina l'usuari de l'àlbum",
|
"shared_album_section_people_action_remove_user": "Elimina l'usuari de l'àlbum",
|
||||||
|
"shared_album_section_people_owner_label": "Amo",
|
||||||
"shared_album_section_people_title": "PERSONES",
|
"shared_album_section_people_title": "PERSONES",
|
||||||
"shared_by": "Compartit per",
|
"shared_by": "Compartit per",
|
||||||
"shared_by_user": "Compartit per {user}",
|
"shared_by_user": "Compartit per {user}",
|
||||||
@@ -1620,8 +1829,17 @@
|
|||||||
"shared_link_app_bar_title": "Enllaços compartits",
|
"shared_link_app_bar_title": "Enllaços compartits",
|
||||||
"shared_link_clipboard_copied_massage": "S'ha copiat al porta-retalls",
|
"shared_link_clipboard_copied_massage": "S'ha copiat al porta-retalls",
|
||||||
"shared_link_clipboard_text": "Enllaç: {}\nContrasenya: {}",
|
"shared_link_clipboard_text": "Enllaç: {}\nContrasenya: {}",
|
||||||
|
"shared_link_create_app_bar_title": "Crea un enllaç per compartir",
|
||||||
"shared_link_create_error": "S'ha produït un error en crear l'enllaç compartit",
|
"shared_link_create_error": "S'ha produït un error en crear l'enllaç compartit",
|
||||||
|
"shared_link_create_info": "Permet que qualsevol persona amb l'enllaç vegi les fotografies seleccionades",
|
||||||
|
"shared_link_create_submit_button": "Crea enllaç",
|
||||||
|
"shared_link_edit_allow_download": "Permet que usuaris públics descarreguin",
|
||||||
|
"shared_link_edit_allow_upload": "Permet que usuaris públics publiquin",
|
||||||
|
"shared_link_edit_app_bar_title": "Edita l'enllaç",
|
||||||
|
"shared_link_edit_change_expiry": "Canvia la data de caducitat",
|
||||||
|
"shared_link_edit_description": "Descripció",
|
||||||
"shared_link_edit_description_hint": "Introduïu la descripció de compartició",
|
"shared_link_edit_description_hint": "Introduïu la descripció de compartició",
|
||||||
|
"shared_link_edit_expire_after": "Caduca després de",
|
||||||
"shared_link_edit_expire_after_option_day": "1 dia",
|
"shared_link_edit_expire_after_option_day": "1 dia",
|
||||||
"shared_link_edit_expire_after_option_days": "{} dies",
|
"shared_link_edit_expire_after_option_days": "{} dies",
|
||||||
"shared_link_edit_expire_after_option_hour": "1 hora",
|
"shared_link_edit_expire_after_option_hour": "1 hora",
|
||||||
@@ -1629,10 +1847,15 @@
|
|||||||
"shared_link_edit_expire_after_option_minute": "1 minut",
|
"shared_link_edit_expire_after_option_minute": "1 minut",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minuts",
|
"shared_link_edit_expire_after_option_minutes": "{} minuts",
|
||||||
"shared_link_edit_expire_after_option_months": "{} mesos",
|
"shared_link_edit_expire_after_option_months": "{} mesos",
|
||||||
|
"shared_link_edit_expire_after_option_never": "Mai",
|
||||||
"shared_link_edit_expire_after_option_year": "any {}",
|
"shared_link_edit_expire_after_option_year": "any {}",
|
||||||
|
"shared_link_edit_password": "Contrasenya",
|
||||||
"shared_link_edit_password_hint": "Introduïu la contrasenya de compartició",
|
"shared_link_edit_password_hint": "Introduïu la contrasenya de compartició",
|
||||||
|
"shared_link_edit_show_meta": "Mostra metadades",
|
||||||
"shared_link_edit_submit_button": "Actualitza l'enllaç",
|
"shared_link_edit_submit_button": "Actualitza l'enllaç",
|
||||||
|
"shared_link_empty": "No tens cap enllaç compartit",
|
||||||
"shared_link_error_server_url_fetch": "No s'ha pogut obtenir l'URL del servidor",
|
"shared_link_error_server_url_fetch": "No s'ha pogut obtenir l'URL del servidor",
|
||||||
|
"shared_link_expired": "Caducat",
|
||||||
"shared_link_expires_day": "Caduca d'aquí a {} dia",
|
"shared_link_expires_day": "Caduca d'aquí a {} dia",
|
||||||
"shared_link_expires_days": "Caduca d'aquí a {} dies",
|
"shared_link_expires_days": "Caduca d'aquí a {} dies",
|
||||||
"shared_link_expires_hour": "Caduca d'aquí a {} hora",
|
"shared_link_expires_hour": "Caduca d'aquí a {} hora",
|
||||||
@@ -1643,9 +1866,12 @@
|
|||||||
"shared_link_expires_second": "Caduca d'aquí a {} segon",
|
"shared_link_expires_second": "Caduca d'aquí a {} segon",
|
||||||
"shared_link_expires_seconds": "Caduca d'aquí a {} segons",
|
"shared_link_expires_seconds": "Caduca d'aquí a {} segons",
|
||||||
"shared_link_individual_shared": "Individual compartit",
|
"shared_link_individual_shared": "Individual compartit",
|
||||||
|
"shared_link_info_chip_download": "Baixa",
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
|
"shared_link_info_chip_upload": "Puja",
|
||||||
"shared_link_manage_links": "Gestiona els enllaços compartits",
|
"shared_link_manage_links": "Gestiona els enllaços compartits",
|
||||||
"shared_link_options": "Opcions d'enllaços compartits",
|
"shared_link_options": "Opcions d'enllaços compartits",
|
||||||
|
"shared_link_public_album": "Àlbum públic",
|
||||||
"shared_links": "Enllaços compartits",
|
"shared_links": "Enllaços compartits",
|
||||||
"shared_links_description": "Comparteix fotos i vídeos amb un enllaç",
|
"shared_links_description": "Comparteix fotos i vídeos amb un enllaç",
|
||||||
"shared_photos_and_videos_count": "{assetCount, plural, other {# fotos i vídeos compartits.}}",
|
"shared_photos_and_videos_count": "{assetCount, plural, other {# fotos i vídeos compartits.}}",
|
||||||
@@ -1659,6 +1885,7 @@
|
|||||||
"sharing_sidebar_description": "Mostra un enllaç a Compartit a la barra lateral",
|
"sharing_sidebar_description": "Mostra un enllaç a Compartit a la barra lateral",
|
||||||
"sharing_silver_appbar_create_shared_album": "Crea àlbum compartit",
|
"sharing_silver_appbar_create_shared_album": "Crea àlbum compartit",
|
||||||
"sharing_silver_appbar_share_partner": "Comparteix amb un company",
|
"sharing_silver_appbar_share_partner": "Comparteix amb un company",
|
||||||
|
"sharing_silver_appbar_shared_links": "Enllaços compartits",
|
||||||
"shift_to_permanent_delete": "premeu ⇧ per suprimir el recurs permanentment",
|
"shift_to_permanent_delete": "premeu ⇧ per suprimir el recurs permanentment",
|
||||||
"show_album_options": "Mostra les opcions d'àlbum",
|
"show_album_options": "Mostra les opcions d'àlbum",
|
||||||
"show_albums": "Mostrar àlbums",
|
"show_albums": "Mostrar àlbums",
|
||||||
@@ -1707,17 +1934,28 @@
|
|||||||
"stacked_assets_count": "Apilats {count, plural, one {# element} other {# elements}}",
|
"stacked_assets_count": "Apilats {count, plural, one {# element} other {# elements}}",
|
||||||
"stacktrace": "Traça de pila",
|
"stacktrace": "Traça de pila",
|
||||||
"start": "Inicia",
|
"start": "Inicia",
|
||||||
|
"start_backup": "Inicia la còpia",
|
||||||
"start_date": "Data inicial",
|
"start_date": "Data inicial",
|
||||||
"state": "Regió",
|
"state": "Regió",
|
||||||
|
"statistics_album": "Miniatures de la biblioteca",
|
||||||
|
"statistics_assets": "{} elements ({})",
|
||||||
|
"statistics_full": "Imatges completes",
|
||||||
|
"statistics_shared": "Miniatures d'àlbums compartits",
|
||||||
|
"statistics_thumbnail": "Miniatures",
|
||||||
|
"statistics_title": "Ús de memòria cau",
|
||||||
"status": "Estat",
|
"status": "Estat",
|
||||||
|
"status_off": "La copia de seguretat està desactivada",
|
||||||
|
"status_on": "La copia de seguretat està activada",
|
||||||
"stop_motion_photo": "Atura foto en moviment",
|
"stop_motion_photo": "Atura foto en moviment",
|
||||||
"stop_photo_sharing": "Deixar de compartir les teves fotos?",
|
"stop_photo_sharing": "Deixar de compartir les teves fotos?",
|
||||||
"stop_photo_sharing_description": "{partner} no podrà tornar a accedir a les vostres fotos.",
|
"stop_photo_sharing_description": "{partner} no podrà tornar a accedir a les vostres fotos.",
|
||||||
"stop_sharing_photos_with_user": "Deixa de compartir les fotos amb aquest usuari",
|
"stop_sharing_photos_with_user": "Deixa de compartir les fotos amb aquest usuari",
|
||||||
"storage": "Emmagatzematge",
|
"storage": "Emmagatzematge",
|
||||||
|
"storage_format": "{} de {} utilitzats",
|
||||||
"storage_label": "Etiquetatge d'emmagatzematge",
|
"storage_label": "Etiquetatge d'emmagatzematge",
|
||||||
"storage_usage": "{used} de {available} en ús",
|
"storage_usage": "{used} de {available} en ús",
|
||||||
"submit": "Envia",
|
"submit": "Envia",
|
||||||
|
"subtitle": "Controla el comportament de la memòria cau de l'aplicació mòbil Immich",
|
||||||
"suggestions": "Suggeriments",
|
"suggestions": "Suggeriments",
|
||||||
"sunrise_on_the_beach": "Albada a la platja",
|
"sunrise_on_the_beach": "Albada a la platja",
|
||||||
"support": "Suport",
|
"support": "Suport",
|
||||||
@@ -1728,6 +1966,12 @@
|
|||||||
"sync_albums": "Sincronitzar àlbums",
|
"sync_albums": "Sincronitzar àlbums",
|
||||||
"sync_albums_manual_subtitle": "Sincronitza tots els vídeos i fotos penjats amb els àlbums de còpia de seguretat seleccionats",
|
"sync_albums_manual_subtitle": "Sincronitza tots els vídeos i fotos penjats amb els àlbums de còpia de seguretat seleccionats",
|
||||||
"sync_upload_album_setting_subtitle": "Creeu i pugeu les seves fotos i vídeos als àlbums seleccionats a Immich",
|
"sync_upload_album_setting_subtitle": "Creeu i pugeu les seves fotos i vídeos als àlbums seleccionats a Immich",
|
||||||
|
"system_primary_color_title": "Utilitza color de sistema",
|
||||||
|
"system_theme_switch": "Automàtic (Segueix la configuració del sistema)",
|
||||||
|
"tab_controller_nav_library": "Llibreria",
|
||||||
|
"tab_controller_nav_photos": "Fotografies",
|
||||||
|
"tab_controller_nav_search": "Cerca",
|
||||||
|
"tab_controller_nav_sharing": "Compartint",
|
||||||
"tag": "Etiqueta",
|
"tag": "Etiqueta",
|
||||||
"tag_assets": "Etiquetar actius",
|
"tag_assets": "Etiquetar actius",
|
||||||
"tag_created": "Etiqueta creada: {tag}",
|
"tag_created": "Etiqueta creada: {tag}",
|
||||||
@@ -1745,6 +1989,7 @@
|
|||||||
"theme_setting_asset_list_tiles_per_row_title": "Nombre d'elements per fila ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Nombre d'elements per fila ({})",
|
||||||
"theme_setting_colorful_interface_subtitle": "Apliqueu color primari a les superfícies de fons.",
|
"theme_setting_colorful_interface_subtitle": "Apliqueu color primari a les superfícies de fons.",
|
||||||
"theme_setting_colorful_interface_title": "Interfície colorida",
|
"theme_setting_colorful_interface_title": "Interfície colorida",
|
||||||
|
"theme_setting_dark_mode_switch": "Tema fosc",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "Ajusta la qualitat del visor de detalls d'imatges",
|
"theme_setting_image_viewer_quality_subtitle": "Ajusta la qualitat del visor de detalls d'imatges",
|
||||||
"theme_setting_image_viewer_quality_title": "Qualitat del visor d'imatges",
|
"theme_setting_image_viewer_quality_title": "Qualitat del visor d'imatges",
|
||||||
"theme_setting_primary_color_subtitle": "Trieu un color per a les accions i els accents principals.",
|
"theme_setting_primary_color_subtitle": "Trieu un color per a les accions i els accents principals.",
|
||||||
@@ -1752,14 +1997,25 @@
|
|||||||
"theme_setting_system_primary_color_title": "Utilitza color de sistema",
|
"theme_setting_system_primary_color_title": "Utilitza color de sistema",
|
||||||
"theme_setting_system_theme_switch": "Automàtic (Segueix la configuració del sistema)",
|
"theme_setting_system_theme_switch": "Automàtic (Segueix la configuració del sistema)",
|
||||||
"theme_setting_theme_subtitle": "Trieu la configuració del tema de l'aplicació",
|
"theme_setting_theme_subtitle": "Trieu la configuració del tema de l'aplicació",
|
||||||
|
"theme_setting_theme_title": "Tema",
|
||||||
"theme_setting_three_stage_loading_subtitle": "La càrrega en tres etapes podria augmentar el rendiment de càrrega, però causa un consum de xarxa significativament més alt",
|
"theme_setting_three_stage_loading_subtitle": "La càrrega en tres etapes podria augmentar el rendiment de càrrega, però causa un consum de xarxa significativament més alt",
|
||||||
"theme_setting_three_stage_loading_title": "Activa la càrrega en tres etapes",
|
"theme_setting_three_stage_loading_title": "Activa la càrrega en tres etapes",
|
||||||
|
"theme_subtitle": "Trieu la configuració del tema de l'aplicació",
|
||||||
|
"theme_title": "Tema",
|
||||||
"they_will_be_merged_together": "Es combinaran",
|
"they_will_be_merged_together": "Es combinaran",
|
||||||
|
"things": "Coses",
|
||||||
"third_party_resources": "Recursos de tercers",
|
"third_party_resources": "Recursos de tercers",
|
||||||
|
"three_stage_loading_subtitle": "La càrrega en tres etapes podria augmentar el rendiment de càrrega, però causa un consum de xarxa significativament més alt",
|
||||||
|
"three_stage_loading_title": "Activa la càrrega en tres etapes",
|
||||||
|
"thumbnail_size": "Mida de la memòria cau de les miniatures ({} elements)",
|
||||||
|
"tile_subtitle": "Controla el comportament de l'emmagatzematge local",
|
||||||
|
"tile_title": "Emmagatzematge local",
|
||||||
"time_based_memories": "Records basats en el temps",
|
"time_based_memories": "Records basats en el temps",
|
||||||
"timeline": "Cronologia",
|
"timeline": "Cronologia",
|
||||||
"timezone": "Fus horari",
|
"timezone": "Fus horari",
|
||||||
|
"title": "Configuració de la memòria cau",
|
||||||
"to_archive": "Arxivar",
|
"to_archive": "Arxivar",
|
||||||
|
"to_backup": "Àlbums a copiar",
|
||||||
"to_change_password": "Canviar la contrasenya",
|
"to_change_password": "Canviar la contrasenya",
|
||||||
"to_favorite": "Prefereix",
|
"to_favorite": "Prefereix",
|
||||||
"to_login": "Iniciar sessió",
|
"to_login": "Iniciar sessió",
|
||||||
@@ -1768,21 +2024,31 @@
|
|||||||
"toggle_settings": "Canvia configuració",
|
"toggle_settings": "Canvia configuració",
|
||||||
"toggle_theme": "Alternar tema",
|
"toggle_theme": "Alternar tema",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"total_sub": "Totes les fotografies i vídeos dels àlbums seleccionats",
|
||||||
"total_usage": "Ús total",
|
"total_usage": "Ús total",
|
||||||
|
"translated_text_options": "Opcions",
|
||||||
"trash": "Paperera",
|
"trash": "Paperera",
|
||||||
"trash_all": "Envia-ho tot a la paperera",
|
"trash_all": "Envia-ho tot a la paperera",
|
||||||
"trash_count": "Paperera {count, number}",
|
"trash_count": "Paperera {count, number}",
|
||||||
"trash_delete_asset": "Esborra/Elimina element",
|
"trash_delete_asset": "Esborra/Elimina element",
|
||||||
"trash_emptied": "Paperera buidada",
|
"trash_emptied": "Paperera buidada",
|
||||||
|
"trash_from_immich": "Mou a paperera",
|
||||||
"trash_no_results_message": "Les imatges i vídeos que s'enviïn a la paperera es mostraran aquí.",
|
"trash_no_results_message": "Les imatges i vídeos que s'enviïn a la paperera es mostraran aquí.",
|
||||||
|
"trash_page_delete": "Elimina",
|
||||||
"trash_page_delete_all": "Eliminar-ho tot",
|
"trash_page_delete_all": "Eliminar-ho tot",
|
||||||
|
"trash_page_empty_trash_btn": "Buida la paperera",
|
||||||
"trash_page_empty_trash_dialog_content": "Segur que voleu eliminar els elements? Aquests elements seran eliminats permanentment de Immich",
|
"trash_page_empty_trash_dialog_content": "Segur que voleu eliminar els elements? Aquests elements seran eliminats permanentment de Immich",
|
||||||
|
"trash_page_empty_trash_dialog_ok": "Sí",
|
||||||
"trash_page_info": "Els elements que s'enviïn a la paperera s'eliminaran permanentment després de {} dies",
|
"trash_page_info": "Els elements que s'enviïn a la paperera s'eliminaran permanentment després de {} dies",
|
||||||
"trash_page_no_assets": "No hi ha elements a la paperera",
|
"trash_page_no_assets": "No hi ha elements a la paperera",
|
||||||
|
"trash_page_restore": "Restaura",
|
||||||
"trash_page_restore_all": "Restaura-ho tot",
|
"trash_page_restore_all": "Restaura-ho tot",
|
||||||
"trash_page_select_assets_btn": "Selecciona elements",
|
"trash_page_select_assets_btn": "Selecciona elements",
|
||||||
|
"trash_page_select_btn": "Selecciona",
|
||||||
"trash_page_title": "Paperera ({})",
|
"trash_page_title": "Paperera ({})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Els elements que s'enviïn a la paperera s'eliminaran permanentment després de {days, plural, one {# dia} other {# dies}}.",
|
"trashed_items_will_be_permanently_deleted_after": "Els elements que s'enviïn a la paperera s'eliminaran permanentment després de {days, plural, one {# dia} other {# dies}}.",
|
||||||
|
"turn_off": "Desactiva la còpia de seguretat",
|
||||||
|
"turn_on": "Activa la còpia de seguretat",
|
||||||
"type": "Tipus",
|
"type": "Tipus",
|
||||||
"unarchive": "Desarxivar",
|
"unarchive": "Desarxivar",
|
||||||
"unarchived_count": "{count, plural, other {# elements desarxivats}}",
|
"unarchived_count": "{count, plural, other {# elements desarxivats}}",
|
||||||
@@ -1807,10 +2073,13 @@
|
|||||||
"untracked_files": "Fitxers no monitoritzats",
|
"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",
|
"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",
|
"up_next": "Pròxim",
|
||||||
|
"update": "Actualitzar",
|
||||||
"updated_password": "Contrasenya actualitzada",
|
"updated_password": "Contrasenya actualitzada",
|
||||||
"upload": "Pujar",
|
"upload": "Pujar",
|
||||||
"upload_concurrency": "Concurrència de pujades",
|
"upload_concurrency": "Concurrència de pujades",
|
||||||
|
"upload_dialog_cancel": "Cancel·la",
|
||||||
"upload_dialog_info": "Vols fer còpia de seguretat dels elements seleccionats al servidor?",
|
"upload_dialog_info": "Vols fer còpia de seguretat dels elements seleccionats al servidor?",
|
||||||
|
"upload_dialog_ok": "Puja",
|
||||||
"upload_dialog_title": "Puja elements",
|
"upload_dialog_title": "Puja elements",
|
||||||
"upload_errors": "Càrrega completada amb {count, plural, one {# error} other {# errors}}, actualitzeu la pàgina per veure els nous elements carregats.",
|
"upload_errors": "Càrrega completada amb {count, plural, one {# error} other {# errors}}, actualitzeu la pàgina per veure els nous elements carregats.",
|
||||||
"upload_progress": "Restant {remaining, number} - Processat {processed, number}/{total, number}",
|
"upload_progress": "Restant {remaining, number} - Processat {processed, number}/{total, number}",
|
||||||
@@ -1821,6 +2090,7 @@
|
|||||||
"upload_success": "Pujada correcta, actualitza la pàgina per veure nous recursos de pujada.",
|
"upload_success": "Pujada correcta, actualitza la pàgina per veure nous recursos de pujada.",
|
||||||
"upload_to_immich": "Puja a Immich ({})",
|
"upload_to_immich": "Puja a Immich ({})",
|
||||||
"uploading": "Pujant",
|
"uploading": "Pujant",
|
||||||
|
"uploading_file_info": "S'està pujant la informació del fitxer",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Ús",
|
"usage": "Ús",
|
||||||
"use_current_connection": "utilitzar la connexió actual",
|
"use_current_connection": "utilitzar la connexió actual",
|
||||||
@@ -1843,6 +2113,7 @@
|
|||||||
"version": "Versió",
|
"version": "Versió",
|
||||||
"version_announcement_closing": "El teu amic Alex",
|
"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 <link>notes de llançament</link> 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_message": "Hola! Hi ha una nova versió d'Immich, si us plau, preneu-vos una estona per llegir les <link>notes de llançament</link> 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_ack": "D'acord",
|
||||||
"version_announcement_overlay_release_notes": "notes de llançament",
|
"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_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_2": "si us plau, pren-te una estona per visitar les ",
|
||||||
@@ -1858,6 +2129,7 @@
|
|||||||
"view": "Veure",
|
"view": "Veure",
|
||||||
"view_album": "Veure l'àlbum",
|
"view_album": "Veure l'àlbum",
|
||||||
"view_all": "Veure tot",
|
"view_all": "Veure tot",
|
||||||
|
"view_all_button": "Veure tot",
|
||||||
"view_all_users": "Mostra tot els usuaris",
|
"view_all_users": "Mostra tot els usuaris",
|
||||||
"view_in_timeline": "Mostrar a la línia de temps",
|
"view_in_timeline": "Mostrar a la línia de temps",
|
||||||
"view_link": "Veure enllaç",
|
"view_link": "Veure enllaç",
|
||||||
@@ -1865,7 +2137,6 @@
|
|||||||
"view_name": "Veure",
|
"view_name": "Veure",
|
||||||
"view_next_asset": "Mostra el següent element",
|
"view_next_asset": "Mostra el següent element",
|
||||||
"view_previous_asset": "Mostra l'element anterior",
|
"view_previous_asset": "Mostra l'element anterior",
|
||||||
"view_qr_code": "Veure codi QR",
|
|
||||||
"view_stack": "Veure la pila",
|
"view_stack": "Veure la pila",
|
||||||
"viewer_remove_from_stack": "Elimina de la pila",
|
"viewer_remove_from_stack": "Elimina de la pila",
|
||||||
"viewer_stack_use_as_main_asset": "Fes servir com a element principal",
|
"viewer_stack_use_as_main_asset": "Fes servir com a element principal",
|
||||||
@@ -1881,6 +2152,8 @@
|
|||||||
"years_ago": "Fa {years, plural, one {# any} other {# anys}}",
|
"years_ago": "Fa {years, plural, one {# any} other {# anys}}",
|
||||||
"yes": "Sí",
|
"yes": "Sí",
|
||||||
"you_dont_have_any_shared_links": "No tens cap enllaç compartit",
|
"you_dont_have_any_shared_links": "No tens cap enllaç compartit",
|
||||||
|
"your_activity": "La teva activitat",
|
||||||
|
"your_map": "El teu mapa",
|
||||||
"your_wifi_name": "El teu nom WiFi",
|
"your_wifi_name": "El teu nom WiFi",
|
||||||
"zoom_image": "Ampliar Imatge"
|
"zoom_image": "Ampliar Imatge"
|
||||||
}
|
}
|
||||||
351
i18n/cs.json
351
i18n/cs.json
File diff suppressed because it is too large
Load Diff
295
i18n/da.json
295
i18n/da.json
@@ -4,6 +4,12 @@
|
|||||||
"account_settings": "Kontoindstillinger",
|
"account_settings": "Kontoindstillinger",
|
||||||
"acknowledge": "Godkend",
|
"acknowledge": "Godkend",
|
||||||
"action": "Handling",
|
"action": "Handling",
|
||||||
|
"action_common_back": "Tilbage",
|
||||||
|
"action_common_cancel": "Annuller",
|
||||||
|
"action_common_clear": "Ryd",
|
||||||
|
"action_common_confirm": "Bekræft",
|
||||||
|
"action_common_save": "Save",
|
||||||
|
"action_common_select": "Select",
|
||||||
"action_common_update": "Opdater",
|
"action_common_update": "Opdater",
|
||||||
"actions": "Handlinger",
|
"actions": "Handlinger",
|
||||||
"active": "Aktive",
|
"active": "Aktive",
|
||||||
@@ -70,13 +76,8 @@
|
|||||||
"forcing_refresh_library_files": "Tvinger genopfriskning af alle biblioteksfiler",
|
"forcing_refresh_library_files": "Tvinger genopfriskning af alle biblioteksfiler",
|
||||||
"image_format": "Format",
|
"image_format": "Format",
|
||||||
"image_format_description": "WebP producerer mindre filer end JPEG, men er langsommere at komprimere.",
|
"image_format_description": "WebP producerer mindre filer end JPEG, men er langsommere at komprimere.",
|
||||||
"image_fullsize_description": "Fuld størrelses billede uden metadata, brugt når zoomet ind",
|
|
||||||
"image_fullsize_enabled": "Aktiver fuld størrelses billede generering",
|
|
||||||
"image_fullsize_enabled_description": "Generer fuld-størrelses billede for ikke-web-venlige formater. Når \"Foretræk indlejret forhåndsvisning\" er slået til, bliver indlejrede forhåndsvisninger brugt direkte uden konvertering. Påvirker ikke web-venlige formater såsom JPEG.",
|
|
||||||
"image_fullsize_quality_description": "Fuld-størrelses billede kvalitet fra 1-100. Højere er bedre, men producerer større filer.",
|
|
||||||
"image_fullsize_title": "Full-størrelses billede indstillinger",
|
|
||||||
"image_prefer_embedded_preview": "Foretræk indlejret forhåndsvisning",
|
"image_prefer_embedded_preview": "Foretræk indlejret forhåndsvisning",
|
||||||
"image_prefer_embedded_preview_setting_description": "Brug indlejrede forhåndsvisninger i RAW fotos som input til billedbehandling og når det er tilgængeligt. Dette kan give mere nøjagtige farver for nogle billeder, men kvaliteten af forhåndsvisningen er kameraafhængig, og billedet kan have flere komprimeringsartefakter.",
|
"image_prefer_embedded_preview_setting_description": "Brug indlejrede forhåndsvisninger i RAW fotos som input til billedbehandling, når det er tilgængeligt. Dette kan give mere nøjagtige farver for nogle billeder, men kvaliteten af forhåndsvisningen er kameraafhængig, og billedet kan have flere komprimeringsartefakter.",
|
||||||
"image_prefer_wide_gamut": "Foretrækker bred farveskala",
|
"image_prefer_wide_gamut": "Foretrækker bred farveskala",
|
||||||
"image_prefer_wide_gamut_setting_description": "Brug Display P3 til miniaturebilleder. Dette bevarer billeder med brede farveskalaers dynamik bedre, men billeder kan komme til at se anderledes ud på gamle enheder med en gammel browserversion. sRGB-billeder bliver beholdt som sRGB for at undgå farveskift.",
|
"image_prefer_wide_gamut_setting_description": "Brug Display P3 til miniaturebilleder. Dette bevarer billeder med brede farveskalaers dynamik bedre, men billeder kan komme til at se anderledes ud på gamle enheder med en gammel browserversion. sRGB-billeder bliver beholdt som sRGB for at undgå farveskift.",
|
||||||
"image_preview_description": "Mellemstørrelse billede med fjernet metadata, der bruges, når du ser en enkelt mediefil og til machine learning",
|
"image_preview_description": "Mellemstørrelse billede med fjernet metadata, der bruges, når du ser en enkelt mediefil og til machine learning",
|
||||||
@@ -171,6 +172,7 @@
|
|||||||
"no_pattern_added": "Intet mønster tilføjet",
|
"no_pattern_added": "Intet mønster tilføjet",
|
||||||
"note_apply_storage_label_previous_assets": "Bemærk: For at anvende Lagringsmærkatet på tidligere uploadede mediefiler, kør",
|
"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!",
|
"note_cannot_be_changed_later": "BEMÆRK: Dette kan ikke ændres senere!",
|
||||||
|
"note_unlimited_quota": "Bemærk: Indsæt 0 for uendelig kvote",
|
||||||
"notification_email_from_address": "Fra adressse",
|
"notification_email_from_address": "Fra adressse",
|
||||||
"notification_email_from_address_description": "Afsenderemailadresse, for eksempel: \"Immich Billedserver <noreply@example.com>\"",
|
"notification_email_from_address_description": "Afsenderemailadresse, for eksempel: \"Immich Billedserver <noreply@example.com>\"",
|
||||||
"notification_email_host_description": "Host af emailserver (fx smtp.immich.app)",
|
"notification_email_host_description": "Host af emailserver (fx smtp.immich.app)",
|
||||||
@@ -192,13 +194,20 @@
|
|||||||
"oauth_auto_register": "Autoregistrér",
|
"oauth_auto_register": "Autoregistrér",
|
||||||
"oauth_auto_register_description": "Registrér automatisk nye brugere efter at have logget ind med OAuth",
|
"oauth_auto_register_description": "Registrér automatisk nye brugere efter at have logget ind med OAuth",
|
||||||
"oauth_button_text": "Knaptekst",
|
"oauth_button_text": "Knaptekst",
|
||||||
|
"oauth_client_id": "Kunde-ID",
|
||||||
|
"oauth_client_secret": "Kundehemmelighed",
|
||||||
"oauth_enable_description": "Log ind med OAuth",
|
"oauth_enable_description": "Log ind med OAuth",
|
||||||
|
"oauth_issuer_url": "Udsteder-URL",
|
||||||
"oauth_mobile_redirect_uri": "Mobilomdiregerings-URL",
|
"oauth_mobile_redirect_uri": "Mobilomdiregerings-URL",
|
||||||
"oauth_mobile_redirect_uri_override": "Tilsidesættelse af mobil omdiregerings-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_profile_signing_algorithm": "Log-ind-algoritme",
|
||||||
|
"oauth_profile_signing_algorithm_description": "Algoritme til signering af brugerprofilen.",
|
||||||
|
"oauth_scope": "Omfang",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "Administrer OAuth login-indstillinger",
|
"oauth_settings_description": "Administrer OAuth login-indstillinger",
|
||||||
"oauth_settings_more_details": "Læs flere detaljer om funktionen i <link>dokumentationen</link>.",
|
"oauth_settings_more_details": "Læs flere detaljer om funktionen i <link>dokumentationen</link>.",
|
||||||
|
"oauth_signing_algorithm": "Signeringsalgoritme",
|
||||||
"oauth_storage_label_claim": "Lagringsmærkat fordring",
|
"oauth_storage_label_claim": "Lagringsmærkat fordring",
|
||||||
"oauth_storage_label_claim_description": "Sæt automatisk brugerens lagringsmærkat til denne fordrings værdi.",
|
"oauth_storage_label_claim_description": "Sæt automatisk brugerens lagringsmærkat til denne fordrings værdi.",
|
||||||
"oauth_storage_quota_claim": "Lagringskvotefordring",
|
"oauth_storage_quota_claim": "Lagringskvotefordring",
|
||||||
@@ -364,7 +373,6 @@
|
|||||||
"admin_password": "Administratoradgangskode",
|
"admin_password": "Administratoradgangskode",
|
||||||
"administration": "Administration",
|
"administration": "Administration",
|
||||||
"advanced": "Avanceret",
|
"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_log_level_title": "Logniveau: {}",
|
"advanced_settings_log_level_title": "Logniveau: {}",
|
||||||
"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_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_prefer_remote_title": "Foretræk elementer på serveren",
|
||||||
@@ -373,6 +381,7 @@
|
|||||||
"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_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_self_signed_ssl_title": "Tillad selvsignerede certifikater",
|
||||||
"advanced_settings_tile_subtitle": "Avancerede brugerindstillinger",
|
"advanced_settings_tile_subtitle": "Avancerede brugerindstillinger",
|
||||||
|
"advanced_settings_tile_title": "Arkivér",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Slå ekstra funktioner for fejlsøgning til",
|
"advanced_settings_troubleshooting_subtitle": "Slå ekstra funktioner for fejlsøgning til",
|
||||||
"advanced_settings_troubleshooting_title": "Fejlsøgning",
|
"advanced_settings_troubleshooting_title": "Fejlsøgning",
|
||||||
"age_months": "Alder {months, plural, one {# måned} other {# måneder}}",
|
"age_months": "Alder {months, plural, one {# måned} other {# måneder}}",
|
||||||
@@ -383,8 +392,10 @@
|
|||||||
"album_cover_updated": "Albumcover opdateret",
|
"album_cover_updated": "Albumcover opdateret",
|
||||||
"album_delete_confirmation": "Er du sikker på at du vil slette albummet {album}?",
|
"album_delete_confirmation": "Er du sikker på at du vil slette albummet {album}?",
|
||||||
"album_delete_confirmation_description": "Hvis dette album er delt, vil andre brugere ikke længere kunne få adgang til det.",
|
"album_delete_confirmation_description": "Hvis dette album er delt, vil andre brugere ikke længere kunne få adgang til det.",
|
||||||
|
"album_info": "{} genstande",
|
||||||
"album_info_card_backup_album_excluded": "EKSKLUDERET",
|
"album_info_card_backup_album_excluded": "EKSKLUDERET",
|
||||||
"album_info_card_backup_album_included": "INKLUDERET",
|
"album_info_card_backup_album_included": "INKLUDERET",
|
||||||
|
"album_info_shared": "{} genstande • Delt",
|
||||||
"album_info_updated": "Albuminfo opdateret",
|
"album_info_updated": "Albuminfo opdateret",
|
||||||
"album_leave": "Forlad albummet?",
|
"album_leave": "Forlad albummet?",
|
||||||
"album_leave_confirmation": "Er du sikker på at du vil forlade {album}?",
|
"album_leave_confirmation": "Er du sikker på at du vil forlade {album}?",
|
||||||
@@ -396,17 +407,21 @@
|
|||||||
"album_thumbnail_card_item": "1 genstand",
|
"album_thumbnail_card_item": "1 genstand",
|
||||||
"album_thumbnail_card_items": "{} genstande",
|
"album_thumbnail_card_items": "{} genstande",
|
||||||
"album_thumbnail_card_shared": ". Delt",
|
"album_thumbnail_card_shared": ". Delt",
|
||||||
|
"album_thumbnail_owned": "Ejet",
|
||||||
"album_thumbnail_shared_by": "Delt af {}",
|
"album_thumbnail_shared_by": "Delt af {}",
|
||||||
|
"album_thumbnails": "Biblioteksminiaturebilleder ({} elementer)",
|
||||||
"album_updated": "Album opdateret",
|
"album_updated": "Album opdateret",
|
||||||
"album_updated_setting_description": "Modtag en emailnotifikation når et delt album får nye mediefiler",
|
"album_updated_setting_description": "Modtag en emailnotifikation når et delt album får nye mediefiler",
|
||||||
"album_user_left": "Forlod {album}",
|
"album_user_left": "Forlod {album}",
|
||||||
"album_user_removed": "Fjernede {user}",
|
"album_user_removed": "Fjernede {user}",
|
||||||
"album_viewer_appbar_delete_confirm": "Er du sikker på, du vil slette dette album fra din bruger?",
|
"album_viewer_appbar_delete_confirm": "Er du sikker på, du vil slette dette album fra din bruger?",
|
||||||
|
"album_viewer_appbar_share_delete": "Slet album",
|
||||||
"album_viewer_appbar_share_err_delete": "Fejlede sletning af album",
|
"album_viewer_appbar_share_err_delete": "Fejlede sletning af album",
|
||||||
"album_viewer_appbar_share_err_leave": "Fejlede i at forlade album",
|
"album_viewer_appbar_share_err_leave": "Fejlede i at forlade album",
|
||||||
"album_viewer_appbar_share_err_remove": "Der er problemer med at fjerne elementer fra album",
|
"album_viewer_appbar_share_err_remove": "Der er problemer med at fjerne elementer fra album",
|
||||||
"album_viewer_appbar_share_err_title": "Fejlede i at ændre albumtitel",
|
"album_viewer_appbar_share_err_title": "Fejlede i at ændre albumtitel",
|
||||||
"album_viewer_appbar_share_leave": "Forlad album",
|
"album_viewer_appbar_share_leave": "Forlad album",
|
||||||
|
"album_viewer_appbar_share_remove": "Fjern fra album",
|
||||||
"album_viewer_appbar_share_to": "Del til",
|
"album_viewer_appbar_share_to": "Del til",
|
||||||
"album_viewer_page_share_add_users": "Tilføj brugere",
|
"album_viewer_page_share_add_users": "Tilføj brugere",
|
||||||
"album_with_link_access": "Lad alle med linket se billeder og personer i dette album.",
|
"album_with_link_access": "Lad alle med linket se billeder og personer i dette album.",
|
||||||
@@ -415,7 +430,9 @@
|
|||||||
"all": "Alt",
|
"all": "Alt",
|
||||||
"all_albums": "Alle albummer",
|
"all_albums": "Alle albummer",
|
||||||
"all_people": "Alle personer",
|
"all_people": "Alle personer",
|
||||||
|
"all_people_page_title": "Personer",
|
||||||
"all_videos": "Alle videoer",
|
"all_videos": "Alle videoer",
|
||||||
|
"all_videos_page_title": "Videoer",
|
||||||
"allow_dark_mode": "Tillad mørk tilstand",
|
"allow_dark_mode": "Tillad mørk tilstand",
|
||||||
"allow_edits": "Tillad redigeringer",
|
"allow_edits": "Tillad redigeringer",
|
||||||
"allow_public_user_to_download": "Tillad offentlige brugere til at hente",
|
"allow_public_user_to_download": "Tillad offentlige brugere til at hente",
|
||||||
@@ -430,6 +447,15 @@
|
|||||||
"app_bar_signout_dialog_ok": "Ja",
|
"app_bar_signout_dialog_ok": "Ja",
|
||||||
"app_bar_signout_dialog_title": "Log ud",
|
"app_bar_signout_dialog_title": "Log ud",
|
||||||
"app_settings": "Appindstillinger",
|
"app_settings": "Appindstillinger",
|
||||||
|
"appbar_delete_confirm": "Er du sikker på, du vil slette dette album fra din bruger?",
|
||||||
|
"appbar_share_delete": "Slet album",
|
||||||
|
"appbar_share_err_delete": "Fejlede sletning af album",
|
||||||
|
"appbar_share_err_leave": "Fejlede i at forlade album",
|
||||||
|
"appbar_share_err_remove": "Der er problemer med at fjerne elementer fra album",
|
||||||
|
"appbar_share_err_title": "Fejlede i at ændre albumtitel",
|
||||||
|
"appbar_share_leave": "Forlad album",
|
||||||
|
"appbar_share_remove": "Fjern fra album",
|
||||||
|
"appbar_share_to": "Del til",
|
||||||
"appears_in": "Optræder i",
|
"appears_in": "Optræder i",
|
||||||
"archive": "Arkiv",
|
"archive": "Arkiv",
|
||||||
"archive_or_unarchive_photo": "Arkivér eller dearkivér billede",
|
"archive_or_unarchive_photo": "Arkivér eller dearkivér billede",
|
||||||
@@ -453,10 +479,13 @@
|
|||||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamisk layout",
|
"asset_list_layout_settings_dynamic_layout_title": "Dynamisk layout",
|
||||||
"asset_list_layout_settings_group_automatically": "Automatisk",
|
"asset_list_layout_settings_group_automatically": "Automatisk",
|
||||||
"asset_list_layout_settings_group_by": "Gruppér elementer pr. ",
|
"asset_list_layout_settings_group_by": "Gruppér elementer pr. ",
|
||||||
|
"asset_list_layout_settings_group_by_month": "Måned",
|
||||||
"asset_list_layout_settings_group_by_month_day": "Måned + dag",
|
"asset_list_layout_settings_group_by_month_day": "Måned + dag",
|
||||||
"asset_list_layout_sub_title": "Layout",
|
"asset_list_layout_sub_title": "Layout",
|
||||||
"asset_list_settings_subtitle": "Indstillinger for billedgitterlayout",
|
"asset_list_settings_subtitle": "Indstillinger for billedgitterlayout",
|
||||||
"asset_list_settings_title": "Billedgitter",
|
"asset_list_settings_title": "Billedgitter",
|
||||||
|
"asset_list_storage_indicator_title": "Vis opbevaringsindikator på filer",
|
||||||
|
"asset_list_tiles_per_row_title": "Antal elementer per række ({})",
|
||||||
"asset_offline": "Mediefil offline",
|
"asset_offline": "Mediefil offline",
|
||||||
"asset_offline_description": "Denne eksterne mediefil kan ikke længere findes på drevet. Kontakt venligst din Immich-administrator for hjælp.",
|
"asset_offline_description": "Denne eksterne mediefil kan ikke længere findes på drevet. Kontakt venligst din Immich-administrator for hjælp.",
|
||||||
"asset_restored_successfully": "Elementet blev gendannet succesfuldt",
|
"asset_restored_successfully": "Elementet blev gendannet succesfuldt",
|
||||||
@@ -489,8 +518,27 @@
|
|||||||
"automatic_endpoint_switching_title": "Automatisk skift af URL",
|
"automatic_endpoint_switching_title": "Automatisk skift af URL",
|
||||||
"back": "Tilbage",
|
"back": "Tilbage",
|
||||||
"back_close_deselect": "Tilbage, luk eller fravælg",
|
"back_close_deselect": "Tilbage, luk eller fravælg",
|
||||||
|
"background_app_refresh_disabled_content": "Slå baggrundsopdatering af applikationen til i Indstillinger > Generelt > Baggrundsopdatering af applikationer, for at bruge sikkerhedskopi i baggrunden.",
|
||||||
|
"background_app_refresh_disabled_title": "Baggrundsopdatering af app er slået fra",
|
||||||
|
"background_app_refresh_enable_button_text": "Gå til indstillinger",
|
||||||
|
"background_battery_info_link": "Vis mig hvordan",
|
||||||
|
"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.",
|
||||||
|
"background_battery_info_ok": "OK",
|
||||||
|
"background_battery_info_title": "Batterioptimering",
|
||||||
|
"background_charging": "Kun under opladning",
|
||||||
|
"background_configure_error": "Fejlede konfigureringen af sikkerhedskopiering i baggrunden",
|
||||||
|
"background_delay": "Udskyd sikkerhedskopi af nye elementer: {}",
|
||||||
|
"background_description": "Slå sikkerhedskopiering i baggrunden til, for automatisk at tage sikkerhedskopi af nye elementer, uden at skulle åbne appen",
|
||||||
|
"background_is_off": "Automatisk sikkerhedskopiering i baggrunden er slået fra",
|
||||||
|
"background_is_on": "Automatisk sikkerhedskopiering i baggrunden er slået til",
|
||||||
"background_location_permission": "Tilladelse til baggrundsplacering",
|
"background_location_permission": "Tilladelse til baggrundsplacering",
|
||||||
"background_location_permission_content": "For at skifte netværk, når appen kører i baggrunden, skal Immich *altid* have præcis placeringsadgang, så appen kan læse WiFi-netværkets navn",
|
"background_location_permission_content": "For at skifte netværk, når appen kører i baggrunden, skal Immich *altid* have præcis placeringsadgang, så appen kan læse WiFi-netværkets navn",
|
||||||
|
"background_turn_off": "Slå sikkerhedskopiering i baggrunden fra",
|
||||||
|
"background_turn_on": "Slå sikkerhedskopiering i baggrunden til",
|
||||||
|
"background_wifi": "Kun med WiFi",
|
||||||
|
"backup": "Sikkerhedskopier",
|
||||||
|
"backup_album_excluded": "EKSKLUDERET",
|
||||||
|
"backup_album_included": "INKLUDERET",
|
||||||
"backup_album_selection_page_albums_device": "Albummer på enhed ({})",
|
"backup_album_selection_page_albums_device": "Albummer på enhed ({})",
|
||||||
"backup_album_selection_page_albums_tap": "Tryk en gang for at inkludere, tryk to gange for at ekskludere",
|
"backup_album_selection_page_albums_tap": "Tryk en gang for at inkludere, tryk to gange for at ekskludere",
|
||||||
"backup_album_selection_page_assets_scatter": "Elementer kan være spredt på tværs af flere albummer. Albummer kan således inkluderes eller udelukkes under sikkerhedskopieringsprocessen.",
|
"backup_album_selection_page_assets_scatter": "Elementer kan være spredt på tværs af flere albummer. Albummer kan således inkluderes eller udelukkes under sikkerhedskopieringsprocessen.",
|
||||||
@@ -525,6 +573,7 @@
|
|||||||
"backup_controller_page_backup": "Sikkerhedskopier",
|
"backup_controller_page_backup": "Sikkerhedskopier",
|
||||||
"backup_controller_page_backup_selected": "Valgte: ",
|
"backup_controller_page_backup_selected": "Valgte: ",
|
||||||
"backup_controller_page_backup_sub": "Sikkerhedskopierede billeder og videoer",
|
"backup_controller_page_backup_sub": "Sikkerhedskopierede billeder og videoer",
|
||||||
|
"backup_controller_page_cancel": "Annuller",
|
||||||
"backup_controller_page_created": "Oprettet den: {}",
|
"backup_controller_page_created": "Oprettet den: {}",
|
||||||
"backup_controller_page_desc_backup": "Slå sikkerhedskopiering til automatisk at uploade nye elementer til serveren.",
|
"backup_controller_page_desc_backup": "Slå sikkerhedskopiering til automatisk at uploade nye elementer til serveren.",
|
||||||
"backup_controller_page_excluded": "Ekskluderet: ",
|
"backup_controller_page_excluded": "Ekskluderet: ",
|
||||||
@@ -535,12 +584,14 @@
|
|||||||
"backup_controller_page_none_selected": "Ingen valgte",
|
"backup_controller_page_none_selected": "Ingen valgte",
|
||||||
"backup_controller_page_remainder": "Tilbageværende",
|
"backup_controller_page_remainder": "Tilbageværende",
|
||||||
"backup_controller_page_remainder_sub": "Tilbageværende billeder og albummer, at sikkerhedskopiere, fra valgte",
|
"backup_controller_page_remainder_sub": "Tilbageværende billeder og albummer, at sikkerhedskopiere, fra valgte",
|
||||||
|
"backup_controller_page_select": "Vælg",
|
||||||
"backup_controller_page_server_storage": "Serverlager",
|
"backup_controller_page_server_storage": "Serverlager",
|
||||||
"backup_controller_page_start_backup": "Start sikkerhedskopiering",
|
"backup_controller_page_start_backup": "Start sikkerhedskopiering",
|
||||||
"backup_controller_page_status_off": "Sikkerhedskopiering er slået fra",
|
"backup_controller_page_status_off": "Sikkerhedskopiering er slået fra",
|
||||||
"backup_controller_page_status_on": "Sikkerhedskopiering er slået til",
|
"backup_controller_page_status_on": "Sikkerhedskopiering er slået til",
|
||||||
"backup_controller_page_storage_format": "{} af {} brugt",
|
"backup_controller_page_storage_format": "{} af {} brugt",
|
||||||
"backup_controller_page_to_backup": "Albummer at sikkerhedskopiere",
|
"backup_controller_page_to_backup": "Albummer at sikkerhedskopiere",
|
||||||
|
"backup_controller_page_total": "I alt",
|
||||||
"backup_controller_page_total_sub": "Alle unikke billeder og videoer fra valgte albummer",
|
"backup_controller_page_total_sub": "Alle unikke billeder og videoer fra valgte albummer",
|
||||||
"backup_controller_page_turn_off": "Slå sikkerhedskopiering fra",
|
"backup_controller_page_turn_off": "Slå sikkerhedskopiering fra",
|
||||||
"backup_controller_page_turn_on": "Slå sikkerhedskopiering til",
|
"backup_controller_page_turn_on": "Slå sikkerhedskopiering til",
|
||||||
@@ -548,11 +599,14 @@
|
|||||||
"backup_err_only_album": "Kan ikke slette det eneste album",
|
"backup_err_only_album": "Kan ikke slette det eneste album",
|
||||||
"backup_info_card_assets": "elementer",
|
"backup_info_card_assets": "elementer",
|
||||||
"backup_manual_cancelled": "Annulleret",
|
"backup_manual_cancelled": "Annulleret",
|
||||||
|
"backup_manual_failed": "Mislykkedes",
|
||||||
"backup_manual_in_progress": "Upload er allerede undervejs. Prøv igen efter noget tid",
|
"backup_manual_in_progress": "Upload er allerede undervejs. Prøv igen efter noget tid",
|
||||||
"backup_manual_success": "Succes",
|
"backup_manual_success": "Succes",
|
||||||
"backup_manual_title": "Uploadstatus",
|
"backup_manual_title": "Uploadstatus",
|
||||||
"backup_options_page_title": "Backupindstillinger",
|
"backup_options_page_title": "Backupindstillinger",
|
||||||
|
"backup_selected": "Valgte: ",
|
||||||
"backup_setting_subtitle": "Administrer indstillnger for upload i forgrund og baggrund",
|
"backup_setting_subtitle": "Administrer indstillnger for upload i forgrund og baggrund",
|
||||||
|
"backup_sub": "Sikkerhedskopierede billeder og videoer",
|
||||||
"backward": "Baglæns",
|
"backward": "Baglæns",
|
||||||
"birthdate_saved": "Fødselsdatoen blev gemt",
|
"birthdate_saved": "Fødselsdatoen blev gemt",
|
||||||
"birthdate_set_description": "Fødselsdato bruges til at beregne alderen på denne person på tidspunktet for et billede.",
|
"birthdate_set_description": "Fødselsdato bruges til at beregne alderen på denne person på tidspunktet for et billede.",
|
||||||
@@ -591,6 +645,7 @@
|
|||||||
"cannot_merge_people": "Kan ikke sammenflette personer",
|
"cannot_merge_people": "Kan ikke sammenflette personer",
|
||||||
"cannot_undo_this_action": "Du kan ikke fortryde denne handling!",
|
"cannot_undo_this_action": "Du kan ikke fortryde denne handling!",
|
||||||
"cannot_update_the_description": "Kan ikke opdatere beskrivelsen",
|
"cannot_update_the_description": "Kan ikke opdatere beskrivelsen",
|
||||||
|
"categories": "Kategorier",
|
||||||
"change_date": "Ændr dato",
|
"change_date": "Ændr dato",
|
||||||
"change_display_order": "Ændrer visningsrækkefølge",
|
"change_display_order": "Ændrer visningsrækkefølge",
|
||||||
"change_expiration_time": "Ændr udløbstidspunkt",
|
"change_expiration_time": "Ændr udløbstidspunkt",
|
||||||
@@ -616,6 +671,8 @@
|
|||||||
"clear": "Ryd",
|
"clear": "Ryd",
|
||||||
"clear_all": "Ryd alle",
|
"clear_all": "Ryd alle",
|
||||||
"clear_all_recent_searches": "Ryd alle seneste søgninger",
|
"clear_all_recent_searches": "Ryd alle seneste søgninger",
|
||||||
|
"clear_cache_button": "Fjern cache",
|
||||||
|
"clear_cache_button_title": "Fjern appens cache. Dette vil i stor grad påvirke appens ydeevne indtil cachen er genopbygget.",
|
||||||
"clear_message": "Ryd bedsked",
|
"clear_message": "Ryd bedsked",
|
||||||
"clear_value": "Ryd værdi",
|
"clear_value": "Ryd værdi",
|
||||||
"client_cert_dialog_msg_confirm": "OK",
|
"client_cert_dialog_msg_confirm": "OK",
|
||||||
@@ -623,6 +680,7 @@
|
|||||||
"client_cert_import": "Import",
|
"client_cert_import": "Import",
|
||||||
"client_cert_import_success_msg": "Client certificate is imported",
|
"client_cert_import_success_msg": "Client certificate is imported",
|
||||||
"client_cert_invalid_msg": "Invalid certificate file or wrong password",
|
"client_cert_invalid_msg": "Invalid certificate file or wrong password",
|
||||||
|
"client_cert_remove": "Remove",
|
||||||
"client_cert_remove_msg": "Client certificate is removed",
|
"client_cert_remove_msg": "Client certificate is removed",
|
||||||
"client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login",
|
"client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login",
|
||||||
"client_cert_title": "SSL Client Certificate",
|
"client_cert_title": "SSL Client Certificate",
|
||||||
@@ -632,12 +690,17 @@
|
|||||||
"collapse_all": "Klap alle sammen",
|
"collapse_all": "Klap alle sammen",
|
||||||
"color": "Farve",
|
"color": "Farve",
|
||||||
"color_theme": "Farvetema",
|
"color_theme": "Farvetema",
|
||||||
|
"colorful_interface_subtitle": "Tilføj primær farve til baggrundsoverflader.",
|
||||||
|
"colorful_interface_title": "Farverig grænseflade",
|
||||||
"comment_deleted": "Kommentar slettet",
|
"comment_deleted": "Kommentar slettet",
|
||||||
"comment_options": "Kommentarindstillinger",
|
"comment_options": "Kommentarindstillinger",
|
||||||
"comments_and_likes": "Kommentarer og likes",
|
"comments_and_likes": "Kommentarer og likes",
|
||||||
"comments_are_disabled": "Kommentarer er slået fra",
|
"comments_are_disabled": "Kommentarer er slået fra",
|
||||||
|
"common_add_to_album": "Tilføj til album",
|
||||||
|
"common_change_password": "Skift kodeord",
|
||||||
"common_create_new_album": "Opret et nyt album",
|
"common_create_new_album": "Opret et nyt album",
|
||||||
"common_server_error": "Tjek din internetforbindelse, sørg for at serveren er tilgængelig og at app- og serversioner er kompatible.",
|
"common_server_error": "Tjek din internetforbindelse, sørg for at serveren er tilgængelig og at app- og serversioner er kompatible.",
|
||||||
|
"common_shared": "Delt",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
"confirm": "Bekræft",
|
"confirm": "Bekræft",
|
||||||
"confirm_admin_password": "Bekræft administratoradgangskode",
|
"confirm_admin_password": "Bekræft administratoradgangskode",
|
||||||
@@ -647,16 +710,29 @@
|
|||||||
"confirm_password": "Bekræft adgangskode",
|
"confirm_password": "Bekræft adgangskode",
|
||||||
"contain": "Inddæm",
|
"contain": "Inddæm",
|
||||||
"context": "Kontekst",
|
"context": "Kontekst",
|
||||||
|
"contextual_search": "Sunrise on the beach",
|
||||||
"continue": "Fortsæt",
|
"continue": "Fortsæt",
|
||||||
|
"control_bottom_app_bar_add_to_album": "Tilføj til album",
|
||||||
|
"control_bottom_app_bar_album_info": "{} genstande",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} genstande • Delt",
|
"control_bottom_app_bar_album_info_shared": "{} genstande • Delt",
|
||||||
|
"control_bottom_app_bar_archive": "Arkiv",
|
||||||
"control_bottom_app_bar_create_new_album": "Opret nyt album",
|
"control_bottom_app_bar_create_new_album": "Opret nyt album",
|
||||||
|
"control_bottom_app_bar_delete": "Slet",
|
||||||
"control_bottom_app_bar_delete_from_immich": "Slet fra Immich",
|
"control_bottom_app_bar_delete_from_immich": "Slet fra Immich",
|
||||||
"control_bottom_app_bar_delete_from_local": "Slet fra enhed",
|
"control_bottom_app_bar_delete_from_local": "Slet fra enhed",
|
||||||
|
"control_bottom_app_bar_download": "Hent",
|
||||||
|
"control_bottom_app_bar_edit": "Rediger",
|
||||||
"control_bottom_app_bar_edit_location": "Rediger placering",
|
"control_bottom_app_bar_edit_location": "Rediger placering",
|
||||||
"control_bottom_app_bar_edit_time": "Rediger tid og dato",
|
"control_bottom_app_bar_edit_time": "Rediger tid og dato",
|
||||||
|
"control_bottom_app_bar_favorite": "Favorit",
|
||||||
|
"control_bottom_app_bar_share": "Del",
|
||||||
"control_bottom_app_bar_share_link": "Share Link",
|
"control_bottom_app_bar_share_link": "Share Link",
|
||||||
"control_bottom_app_bar_share_to": "Del til",
|
"control_bottom_app_bar_share_to": "Del til",
|
||||||
|
"control_bottom_app_bar_stack": "Stak",
|
||||||
"control_bottom_app_bar_trash_from_immich": "Flyt til papirkurv",
|
"control_bottom_app_bar_trash_from_immich": "Flyt til papirkurv",
|
||||||
|
"control_bottom_app_bar_unarchive": "Afakivér",
|
||||||
|
"control_bottom_app_bar_unfavorite": "Fjern favorit",
|
||||||
|
"control_bottom_app_bar_upload": "Upload",
|
||||||
"copied_image_to_clipboard": "Kopierede billede til clipboard.",
|
"copied_image_to_clipboard": "Kopierede billede til clipboard.",
|
||||||
"copied_to_clipboard": "Kopieret til udklipsholder!",
|
"copied_to_clipboard": "Kopieret til udklipsholder!",
|
||||||
"copy_error": "Kopifejl",
|
"copy_error": "Kopifejl",
|
||||||
@@ -677,9 +753,12 @@
|
|||||||
"create_link_to_share": "Opret link for at dele",
|
"create_link_to_share": "Opret link for at dele",
|
||||||
"create_link_to_share_description": "Tillad alle med linket at se de(t) valgte billede(r)",
|
"create_link_to_share_description": "Tillad alle med linket at se de(t) valgte billede(r)",
|
||||||
"create_new": "OPRET NY",
|
"create_new": "OPRET NY",
|
||||||
|
"create_new_album": "Opret nyt album",
|
||||||
"create_new_person": "Opret ny person",
|
"create_new_person": "Opret ny person",
|
||||||
"create_new_person_hint": "Tildel valgte aktiver til en ny person",
|
"create_new_person_hint": "Tildel valgte aktiver til en ny person",
|
||||||
"create_new_user": "Opret ny bruger",
|
"create_new_user": "Opret ny bruger",
|
||||||
|
"create_shared_album_page_create": "Opret",
|
||||||
|
"create_shared_album_page_share": "Del",
|
||||||
"create_shared_album_page_share_add_assets": "TILFØJ ELEMENT",
|
"create_shared_album_page_share_add_assets": "TILFØJ ELEMENT",
|
||||||
"create_shared_album_page_share_select_photos": "Vælg billeder",
|
"create_shared_album_page_share_select_photos": "Vælg billeder",
|
||||||
"create_tag": "Opret tag",
|
"create_tag": "Opret tag",
|
||||||
@@ -687,6 +766,7 @@
|
|||||||
"create_user": "Opret bruger",
|
"create_user": "Opret bruger",
|
||||||
"created": "Oprettet",
|
"created": "Oprettet",
|
||||||
"crop": "Beskær",
|
"crop": "Beskær",
|
||||||
|
"curated_location_page_title": "Steder",
|
||||||
"curated_object_page_title": "Ting",
|
"curated_object_page_title": "Ting",
|
||||||
"current_device": "Nuværende enhed",
|
"current_device": "Nuværende enhed",
|
||||||
"current_server_address": "Nuværende serveraddresse",
|
"current_server_address": "Nuværende serveraddresse",
|
||||||
@@ -695,6 +775,7 @@
|
|||||||
"daily_title_text_date": "E, dd MMM",
|
"daily_title_text_date": "E, dd MMM",
|
||||||
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
||||||
"dark": "Mørk",
|
"dark": "Mørk",
|
||||||
|
"dark_mode_switch": "Mørk tilstand",
|
||||||
"date_after": "Dato efter",
|
"date_after": "Dato efter",
|
||||||
"date_and_time": "Dato og klokkeslæt",
|
"date_and_time": "Dato og klokkeslæt",
|
||||||
"date_before": "Dato før",
|
"date_before": "Dato før",
|
||||||
@@ -716,10 +797,14 @@
|
|||||||
"delete_dialog_alert_local": "Disse elementer slettes permanent fra din enhed, men vil stadig være tilgængelige på serveren",
|
"delete_dialog_alert_local": "Disse elementer slettes permanent fra din enhed, men vil stadig være tilgængelige på serveren",
|
||||||
"delete_dialog_alert_local_non_backed_up": "Nogle af elementerne har ingen backup på serveren og vil blive slettet permanent fra din enhed",
|
"delete_dialog_alert_local_non_backed_up": "Nogle af elementerne har ingen backup på serveren og vil blive slettet permanent fra din enhed",
|
||||||
"delete_dialog_alert_remote": "Disse elementer slettes permanent fra serveren",
|
"delete_dialog_alert_remote": "Disse elementer slettes permanent fra serveren",
|
||||||
|
"delete_dialog_cancel": "Annuller",
|
||||||
|
"delete_dialog_ok": "Slet",
|
||||||
"delete_dialog_ok_force": "Slet alligevel",
|
"delete_dialog_ok_force": "Slet alligevel",
|
||||||
"delete_dialog_title": "Slet permanent",
|
"delete_dialog_title": "Slet permanent",
|
||||||
"delete_duplicates_confirmation": "Er du sikker på, at du vil slette disse dubletter permanent?",
|
"delete_duplicates_confirmation": "Er du sikker på, at du vil slette disse dubletter permanent?",
|
||||||
"delete_face": "Slet ansigt",
|
"delete_face": "Slet ansigt",
|
||||||
|
"delete_from_immich": "Slet fra Immich",
|
||||||
|
"delete_from_local": "Slet fra enhed",
|
||||||
"delete_key": "Slet nøgle",
|
"delete_key": "Slet nøgle",
|
||||||
"delete_library": "Slet bibliotek",
|
"delete_library": "Slet bibliotek",
|
||||||
"delete_link": "Slet link",
|
"delete_link": "Slet link",
|
||||||
@@ -727,15 +812,18 @@
|
|||||||
"delete_local_dialog_ok_force": "Slet alligevel",
|
"delete_local_dialog_ok_force": "Slet alligevel",
|
||||||
"delete_others": "Slet andre",
|
"delete_others": "Slet andre",
|
||||||
"delete_shared_link": "Slet delt link",
|
"delete_shared_link": "Slet delt link",
|
||||||
|
"delete_shared_link_dialog_content": "Er du sikker på, du vil slette dette delte link?",
|
||||||
"delete_shared_link_dialog_title": "Slet delt link",
|
"delete_shared_link_dialog_title": "Slet delt link",
|
||||||
"delete_tag": "Slet tag",
|
"delete_tag": "Slet tag",
|
||||||
"delete_tag_confirmation_prompt": "Er du sikker på, at du vil slette {tagName}-tagget?",
|
"delete_tag_confirmation_prompt": "Er du sikker på, at du vil slette {tagName}-tagget?",
|
||||||
"delete_user": "Slet bruger",
|
"delete_user": "Slet bruger",
|
||||||
"deleted_shared_link": "Slettede delt link",
|
"deleted_shared_link": "Slettede delt link",
|
||||||
"deletes_missing_assets": "Sletter aktiver, der mangler fra disken",
|
"deletes_missing_assets": "Sletter aktiver, der mangler fra disken",
|
||||||
|
"desc_backup": "Slå sikkerhedskopiering til automatisk at uploade nye elementer til serveren.",
|
||||||
"description": "Beskrivelse",
|
"description": "Beskrivelse",
|
||||||
"description_input_hint_text": "Tilføj en beskrivelse...",
|
"description_input_hint_text": "Tilføj en beskrivelse...",
|
||||||
"description_input_submit_error": "Fejl med at opdatere beskrivelsen. Tjek loggen for flere detaljer",
|
"description_input_submit_error": "Fejl med at opdatere beskrivelsen. Tjek loggen for flere detaljer",
|
||||||
|
"description_search": "Hiking day in Sapa",
|
||||||
"details": "DETALJER",
|
"details": "DETALJER",
|
||||||
"direction": "Retning",
|
"direction": "Retning",
|
||||||
"disabled": "Deaktiveret",
|
"disabled": "Deaktiveret",
|
||||||
@@ -773,6 +861,9 @@
|
|||||||
"downloading_asset_filename": "Downloader mediefil {filename}",
|
"downloading_asset_filename": "Downloader mediefil {filename}",
|
||||||
"downloading_media": "Download medier",
|
"downloading_media": "Download medier",
|
||||||
"drop_files_to_upload": "Slip filer hvor som helst for at uploade dem",
|
"drop_files_to_upload": "Slip filer hvor som helst for at uploade dem",
|
||||||
|
"duplicated_assets_clear_button": "RYD",
|
||||||
|
"duplicated_assets_subtitle": "Billeder og videoer der er sortlistet af appen",
|
||||||
|
"duplicated_assets_title": "Dublikerede elementer ({})",
|
||||||
"duplicates": "Duplikater",
|
"duplicates": "Duplikater",
|
||||||
"duplicates_description": "Løs hver gruppe ved at angive, hvilke, hvis nogen, er dubletter",
|
"duplicates_description": "Løs hver gruppe ved at angive, hvilke, hvis nogen, er dubletter",
|
||||||
"duration": "Varighed",
|
"duration": "Varighed",
|
||||||
@@ -781,8 +872,12 @@
|
|||||||
"edit_avatar": "Redigér avatar",
|
"edit_avatar": "Redigér avatar",
|
||||||
"edit_date": "Redigér dato",
|
"edit_date": "Redigér dato",
|
||||||
"edit_date_and_time": "Redigér dato og tid",
|
"edit_date_and_time": "Redigér dato og tid",
|
||||||
|
"edit_date_time_dialog_date_time": "Dato og klokkeslæt",
|
||||||
|
"edit_date_time_dialog_search_timezone": "Search timezone...",
|
||||||
|
"edit_date_time_dialog_timezone": "Tidszone",
|
||||||
"edit_exclusion_pattern": "Redigér udelukkelsesmønster",
|
"edit_exclusion_pattern": "Redigér udelukkelsesmønster",
|
||||||
"edit_faces": "Redigér ansigter",
|
"edit_faces": "Redigér ansigter",
|
||||||
|
"edit_image_title": "Rediger",
|
||||||
"edit_import_path": "Redigér import-sti",
|
"edit_import_path": "Redigér import-sti",
|
||||||
"edit_import_paths": "Redigér import-stier",
|
"edit_import_paths": "Redigér import-stier",
|
||||||
"edit_key": "Redigér nøgle",
|
"edit_key": "Redigér nøgle",
|
||||||
@@ -792,6 +887,7 @@
|
|||||||
"edit_name": "Rediger navn",
|
"edit_name": "Rediger navn",
|
||||||
"edit_people": "Redigér personer",
|
"edit_people": "Redigér personer",
|
||||||
"edit_tag": "Rediger tag",
|
"edit_tag": "Rediger tag",
|
||||||
|
"edit_time": "Rediger tid og dato",
|
||||||
"edit_title": "Redigér titel",
|
"edit_title": "Redigér titel",
|
||||||
"edit_user": "Redigér bruger",
|
"edit_user": "Redigér bruger",
|
||||||
"edited": "Redigeret",
|
"edited": "Redigeret",
|
||||||
@@ -941,10 +1037,12 @@
|
|||||||
"unable_to_update_user": "Ikke i stand til at opdatere bruger",
|
"unable_to_update_user": "Ikke i stand til at opdatere bruger",
|
||||||
"unable_to_upload_file": "Filen kunne ikke uploades"
|
"unable_to_upload_file": "Filen kunne ikke uploades"
|
||||||
},
|
},
|
||||||
|
"excluded": "Ekskluderet: ",
|
||||||
"exif": "Exif",
|
"exif": "Exif",
|
||||||
"exif_bottom_sheet_description": "Tilføj beskrivelse...",
|
"exif_bottom_sheet_description": "Tilføj beskrivelse...",
|
||||||
"exif_bottom_sheet_details": "DETALJER",
|
"exif_bottom_sheet_details": "DETALJER",
|
||||||
"exif_bottom_sheet_location": "LOKATION",
|
"exif_bottom_sheet_location": "LOKATION",
|
||||||
|
"exif_bottom_sheet_location_add": "Tilføj en placering",
|
||||||
"exif_bottom_sheet_people": "PERSONER",
|
"exif_bottom_sheet_people": "PERSONER",
|
||||||
"exif_bottom_sheet_person_add_person": "Tilføj navn",
|
"exif_bottom_sheet_person_add_person": "Tilføj navn",
|
||||||
"exif_bottom_sheet_person_age": "Age {}",
|
"exif_bottom_sheet_person_age": "Age {}",
|
||||||
@@ -977,12 +1075,14 @@
|
|||||||
"favorite_or_unfavorite_photo": "Tilføj eller fjern fra yndlingsbilleder",
|
"favorite_or_unfavorite_photo": "Tilføj eller fjern fra yndlingsbilleder",
|
||||||
"favorites": "Favoritter",
|
"favorites": "Favoritter",
|
||||||
"favorites_page_no_favorites": "Ingen favoritter blev fundet",
|
"favorites_page_no_favorites": "Ingen favoritter blev fundet",
|
||||||
|
"favorites_page_title": "Favoritter",
|
||||||
"feature_photo_updated": "Forsidebillede uploadet",
|
"feature_photo_updated": "Forsidebillede uploadet",
|
||||||
"features": "Funktioner",
|
"features": "Funktioner",
|
||||||
"features_setting_description": "Administrer app-funktioner",
|
"features_setting_description": "Administrer app-funktioner",
|
||||||
"file_name": "Filnavn",
|
"file_name": "Filnavn",
|
||||||
"file_name_or_extension": "Filnavn eller filtype",
|
"file_name_or_extension": "Filnavn eller filtype",
|
||||||
"filename": "Filnavn",
|
"filename": "Filnavn",
|
||||||
|
"filename_search": "File name or extension",
|
||||||
"filetype": "Filtype",
|
"filetype": "Filtype",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"filter_people": "Filtrér personer",
|
"filter_people": "Filtrér personer",
|
||||||
@@ -1002,6 +1102,7 @@
|
|||||||
"go_to_search": "Gå til søgning",
|
"go_to_search": "Gå til søgning",
|
||||||
"grant_permission": "Giv tilladelse",
|
"grant_permission": "Giv tilladelse",
|
||||||
"group_albums_by": "Gruppér albummer efter...",
|
"group_albums_by": "Gruppér albummer efter...",
|
||||||
|
"group_by_sub_title": "Gruppér efter",
|
||||||
"group_country": "Gruppér efter land",
|
"group_country": "Gruppér efter land",
|
||||||
"group_no": "Ingen gruppering",
|
"group_no": "Ingen gruppering",
|
||||||
"group_owner": "Grupper efter ejer",
|
"group_owner": "Grupper efter ejer",
|
||||||
@@ -1014,6 +1115,7 @@
|
|||||||
"header_settings_field_validator_msg": "Value cannot be empty",
|
"header_settings_field_validator_msg": "Value cannot be empty",
|
||||||
"header_settings_header_name_input": "Header name",
|
"header_settings_header_name_input": "Header name",
|
||||||
"header_settings_header_value_input": "Header value",
|
"header_settings_header_value_input": "Header value",
|
||||||
|
"header_settings_page_title": "Proxy Headers",
|
||||||
"headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request",
|
"headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request",
|
||||||
"headers_settings_tile_title": "Custom proxy headers",
|
"headers_settings_tile_title": "Custom proxy headers",
|
||||||
"hi_user": "Hej {name} ({email})",
|
"hi_user": "Hej {name} ({email})",
|
||||||
@@ -1039,6 +1141,7 @@
|
|||||||
"home_page_upload_err_limit": "Det er kun muligt at lave sikkerhedskopi af 30 elementer ad gangen. Springer over",
|
"home_page_upload_err_limit": "Det er kun muligt at lave sikkerhedskopi af 30 elementer ad gangen. Springer over",
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"hour": "Time",
|
"hour": "Time",
|
||||||
|
"id": "ID: {}",
|
||||||
"ignore_icloud_photos": "Ignorer iCloud-billeder",
|
"ignore_icloud_photos": "Ignorer iCloud-billeder",
|
||||||
"ignore_icloud_photos_description": "Billeder der er gemt på iCloud vil ikke blive uploadet til Immich-serveren",
|
"ignore_icloud_photos_description": "Billeder der er gemt på iCloud vil ikke blive uploadet til Immich-serveren",
|
||||||
"image": "Billede",
|
"image": "Billede",
|
||||||
@@ -1052,10 +1155,14 @@
|
|||||||
"image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} taget i {city}, {country} med {person1} og {person2} den {date}",
|
"image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} taget i {city}, {country} med {person1} og {person2} den {date}",
|
||||||
"image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} taget i {city}, {country} med {person1}, {person2}, og {person3} den {date}",
|
"image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} taget i {city}, {country} med {person1}, {person2}, og {person3} den {date}",
|
||||||
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} taget i {city}, {country} med {person1}, {person2}, og {additionalCount, number} andre den {date}",
|
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} taget i {city}, {country} med {person1}, {person2}, og {additionalCount, number} andre den {date}",
|
||||||
|
"image_cache_size": "Størrelse af billedecache ({} elementer)",
|
||||||
"image_saved_successfully": "Billede gemt",
|
"image_saved_successfully": "Billede gemt",
|
||||||
|
"image_viewer_page_state_provider_download_error": "Fejl ved download",
|
||||||
"image_viewer_page_state_provider_download_started": "Download startet",
|
"image_viewer_page_state_provider_download_started": "Download startet",
|
||||||
"image_viewer_page_state_provider_download_success": "Download succesfuld",
|
"image_viewer_page_state_provider_download_success": "Download succesfuld",
|
||||||
"image_viewer_page_state_provider_share_error": "Delingsfejl",
|
"image_viewer_page_state_provider_share_error": "Delingsfejl",
|
||||||
|
"image_viewer_quality_subtitle": "Juster kvaliteten i billedfremviseren",
|
||||||
|
"image_viewer_quality_title": "Billedfremviserkvalitet",
|
||||||
"immich_logo": "Immich logo",
|
"immich_logo": "Immich logo",
|
||||||
"immich_web_interface": "Immich webinterface",
|
"immich_web_interface": "Immich webinterface",
|
||||||
"import_from_json": "Importér fra JSON",
|
"import_from_json": "Importér fra JSON",
|
||||||
@@ -1090,17 +1197,29 @@
|
|||||||
"last_seen": "Sidst set",
|
"last_seen": "Sidst set",
|
||||||
"latest_version": "Seneste version",
|
"latest_version": "Seneste version",
|
||||||
"latitude": "Breddegrad",
|
"latitude": "Breddegrad",
|
||||||
|
"layout_settings_dynamic_layout_title": "Dynamisk layout",
|
||||||
|
"layout_settings_group_automatically": "Automatisk",
|
||||||
|
"layout_settings_group_by": "Gruppér elementer pr. ",
|
||||||
|
"layout_settings_group_by_month": "Måned",
|
||||||
|
"layout_settings_group_by_month_day": "Måned + dag",
|
||||||
|
"layout_sub_title": "Layout",
|
||||||
"leave": "Forlad",
|
"leave": "Forlad",
|
||||||
"lens_model": "Objektivmodel",
|
"lens_model": "Objektivmodel",
|
||||||
"let_others_respond": "Lad andre svare",
|
"let_others_respond": "Lad andre svare",
|
||||||
"level": "Niveau",
|
"level": "Niveau",
|
||||||
"library": "Bibliotek",
|
"library": "Bibliotek",
|
||||||
"library_options": "Biblioteksindstillinger",
|
"library_options": "Biblioteksindstillinger",
|
||||||
|
"library_page_albums": "Albummer",
|
||||||
|
"library_page_archive": "Arkiv",
|
||||||
"library_page_device_albums": "Albummer på enhed",
|
"library_page_device_albums": "Albummer på enhed",
|
||||||
|
"library_page_favorites": "Favoritter",
|
||||||
"library_page_new_album": "Nyt album",
|
"library_page_new_album": "Nyt album",
|
||||||
|
"library_page_sharing": "Delte",
|
||||||
"library_page_sort_asset_count": "Antal af elementer\n",
|
"library_page_sort_asset_count": "Antal af elementer\n",
|
||||||
"library_page_sort_created": "Senest oprettet",
|
"library_page_sort_created": "Senest oprettet",
|
||||||
"library_page_sort_last_modified": "Sidst redigeret",
|
"library_page_sort_last_modified": "Sidst redigeret",
|
||||||
|
"library_page_sort_most_oldest_photo": "Ældste billede",
|
||||||
|
"library_page_sort_most_recent_photo": "Seneste billede",
|
||||||
"library_page_sort_title": "Albumtitel",
|
"library_page_sort_title": "Albumtitel",
|
||||||
"light": "Lys",
|
"light": "Lys",
|
||||||
"like_deleted": "Ligesom slettet",
|
"like_deleted": "Ligesom slettet",
|
||||||
@@ -1116,8 +1235,10 @@
|
|||||||
"location_permission": "Tilladelse til placering",
|
"location_permission": "Tilladelse til placering",
|
||||||
"location_permission_content": "For automatisk at skifte netværk, skal Immich *altid* have præcis placeringsadgang, så appen kan læse WiFi-netværkets navn",
|
"location_permission_content": "For automatisk at skifte netværk, skal Immich *altid* have præcis placeringsadgang, så appen kan læse WiFi-netværkets navn",
|
||||||
"location_picker_choose_on_map": "Vælg på kort",
|
"location_picker_choose_on_map": "Vælg på kort",
|
||||||
|
"location_picker_latitude": "Breddegrad",
|
||||||
"location_picker_latitude_error": "Indtast en gyldig breddegrad",
|
"location_picker_latitude_error": "Indtast en gyldig breddegrad",
|
||||||
"location_picker_latitude_hint": "Indtast din breddegrad her",
|
"location_picker_latitude_hint": "Indtast din breddegrad her",
|
||||||
|
"location_picker_longitude": "Længdegrad",
|
||||||
"location_picker_longitude_error": "Indtast en gyldig længdegrad",
|
"location_picker_longitude_error": "Indtast en gyldig længdegrad",
|
||||||
"location_picker_longitude_hint": "Indtast din længdegrad her",
|
"location_picker_longitude_hint": "Indtast din længdegrad her",
|
||||||
"log_out": "Log ud",
|
"log_out": "Log ud",
|
||||||
@@ -1128,6 +1249,7 @@
|
|||||||
"login_disabled": "Login er blevet deaktiveret",
|
"login_disabled": "Login er blevet deaktiveret",
|
||||||
"login_form_api_exception": "API-undtagelse. Tjek serverens URL og prøv igen. ",
|
"login_form_api_exception": "API-undtagelse. Tjek serverens URL og prøv igen. ",
|
||||||
"login_form_back_button_text": "Tilbage",
|
"login_form_back_button_text": "Tilbage",
|
||||||
|
"login_form_button_text": "Log ind",
|
||||||
"login_form_email_hint": "din-e-mail@e-mail.com",
|
"login_form_email_hint": "din-e-mail@e-mail.com",
|
||||||
"login_form_endpoint_hint": "http://din-server-ip:port",
|
"login_form_endpoint_hint": "http://din-server-ip:port",
|
||||||
"login_form_endpoint_url": "Server Endpoint URL",
|
"login_form_endpoint_url": "Server Endpoint URL",
|
||||||
@@ -1140,6 +1262,9 @@
|
|||||||
"login_form_failed_get_oauth_server_disable": "OAuth er ikke tilgængelig på denne server",
|
"login_form_failed_get_oauth_server_disable": "OAuth er ikke tilgængelig på denne server",
|
||||||
"login_form_failed_login": "Der opstod en vejl ved at logge ind. Tjek server webadressen, e-mailen og kodeordet",
|
"login_form_failed_login": "Der opstod en vejl ved at logge ind. Tjek server webadressen, e-mailen og kodeordet",
|
||||||
"login_form_handshake_exception": "Der opstod en fejl med at oprette forbindelse til serveren. Aktiver selvsignerede certifikater i indstillingerne, hvis du bruger et selv signeret certifikat.",
|
"login_form_handshake_exception": "Der opstod en fejl med at oprette forbindelse til serveren. Aktiver selvsignerede certifikater i indstillingerne, hvis du bruger et selv signeret certifikat.",
|
||||||
|
"login_form_label_email": "E-mail",
|
||||||
|
"login_form_label_password": "Kodeord",
|
||||||
|
"login_form_next_button": "Næste",
|
||||||
"login_form_password_hint": "kodeord",
|
"login_form_password_hint": "kodeord",
|
||||||
"login_form_save_login": "Forbliv logget ind",
|
"login_form_save_login": "Forbliv logget ind",
|
||||||
"login_form_server_empty": "Indtast server-URL.",
|
"login_form_server_empty": "Indtast server-URL.",
|
||||||
@@ -1166,6 +1291,7 @@
|
|||||||
"map_assets_in_bound": "{} billede",
|
"map_assets_in_bound": "{} billede",
|
||||||
"map_assets_in_bounds": "{} billeder",
|
"map_assets_in_bounds": "{} billeder",
|
||||||
"map_cannot_get_user_location": "Kan ikke finde brugerens placering",
|
"map_cannot_get_user_location": "Kan ikke finde brugerens placering",
|
||||||
|
"map_location_dialog_cancel": "Annuller",
|
||||||
"map_location_dialog_yes": "Ja",
|
"map_location_dialog_yes": "Ja",
|
||||||
"map_location_picker_page_use_location": "Brug denne placering",
|
"map_location_picker_page_use_location": "Brug denne placering",
|
||||||
"map_location_service_disabled_content": "Placeringstjenesten skal aktiveres for at vise elementer fra din nuværende placering. Vil du aktivere den nu?",
|
"map_location_service_disabled_content": "Placeringstjenesten skal aktiveres for at vise elementer fra din nuværende placering. Vil du aktivere den nu?",
|
||||||
@@ -1177,13 +1303,17 @@
|
|||||||
"map_no_location_permission_title": "Placeringstilladelse blev afvist",
|
"map_no_location_permission_title": "Placeringstilladelse blev afvist",
|
||||||
"map_settings": "Kortindstillinger",
|
"map_settings": "Kortindstillinger",
|
||||||
"map_settings_dark_mode": "Mørk tilstand",
|
"map_settings_dark_mode": "Mørk tilstand",
|
||||||
|
"map_settings_date_range_option_all": "Alle",
|
||||||
"map_settings_date_range_option_day": "Sidste 24 timer",
|
"map_settings_date_range_option_day": "Sidste 24 timer",
|
||||||
"map_settings_date_range_option_days": "Sidste {} dage",
|
"map_settings_date_range_option_days": "Sidste {} dage",
|
||||||
"map_settings_date_range_option_year": "Sidste år",
|
"map_settings_date_range_option_year": "Sidste år",
|
||||||
"map_settings_date_range_option_years": "Sidste {} år",
|
"map_settings_date_range_option_years": "Sidste {} år",
|
||||||
|
"map_settings_dialog_cancel": "Annuller",
|
||||||
|
"map_settings_dialog_save": "Gem",
|
||||||
"map_settings_dialog_title": "Kortindstillinger",
|
"map_settings_dialog_title": "Kortindstillinger",
|
||||||
"map_settings_include_show_archived": "Inkluder arkiveret",
|
"map_settings_include_show_archived": "Inkluder arkiveret",
|
||||||
"map_settings_include_show_partners": "Inkluder partnere",
|
"map_settings_include_show_partners": "Inkluder partnere",
|
||||||
|
"map_settings_only_relative_range": "Datointerval",
|
||||||
"map_settings_only_show_favorites": "Vis kun favoritter",
|
"map_settings_only_show_favorites": "Vis kun favoritter",
|
||||||
"map_settings_theme_settings": "Korttema",
|
"map_settings_theme_settings": "Korttema",
|
||||||
"map_zoom_to_see_photos": "Zoom ud for at vise billeder",
|
"map_zoom_to_see_photos": "Zoom ud for at vise billeder",
|
||||||
@@ -1213,6 +1343,8 @@
|
|||||||
"month": "Måned",
|
"month": "Måned",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
"more": "Mere",
|
"more": "Mere",
|
||||||
|
"motion_photos": "Bevægelsesbilleder",
|
||||||
|
"motion_photos_page_title": "Bevægelsesbilleder",
|
||||||
"moved_to_trash": "Flyttet til skraldespand",
|
"moved_to_trash": "Flyttet til skraldespand",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Kan ikke redigere datoen på kun læselige elementer. Springer over",
|
"multiselect_grid_edit_date_time_err_read_only": "Kan ikke redigere datoen på kun læselige elementer. Springer over",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Kan ikke redigere lokation af kun læselige elementer. Springer over",
|
"multiselect_grid_edit_gps_err_read_only": "Kan ikke redigere lokation af kun læselige elementer. Springer over",
|
||||||
@@ -1245,15 +1377,20 @@
|
|||||||
"no_favorites_message": "Tilføj favoritter for hurtigt at finde dine bedst billeder og videoer",
|
"no_favorites_message": "Tilføj favoritter for hurtigt at finde dine bedst billeder og videoer",
|
||||||
"no_libraries_message": "Opret et eksternt bibliotek for at se dine billeder og videoer",
|
"no_libraries_message": "Opret et eksternt bibliotek for at se dine billeder og videoer",
|
||||||
"no_name": "Intet navn",
|
"no_name": "Intet navn",
|
||||||
|
"no_objects": "Ingen elementer er tilgængelige",
|
||||||
"no_places": "Ingen steder",
|
"no_places": "Ingen steder",
|
||||||
"no_results": "Ingen resultater",
|
"no_results": "Ingen resultater",
|
||||||
"no_results_description": "Prøv et synonym eller et mere generelt søgeord",
|
"no_results_description": "Prøv et synonym eller et mere generelt søgeord",
|
||||||
"no_shared_albums_message": "Opret et album for at dele billeder og videoer med personer i dit netværk",
|
"no_shared_albums_message": "Opret et album for at dele billeder og videoer med personer i dit netværk",
|
||||||
|
"none_selected": "Ingen valgte",
|
||||||
"not_in_any_album": "Ikke i noget album",
|
"not_in_any_album": "Ikke i noget album",
|
||||||
"not_selected": "Not selected",
|
"not_selected": "Not selected",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "Bemærk: For at anvende Lagringsmærkat på tidligere uploadede medier, kør",
|
"note_apply_storage_label_to_previously_uploaded assets": "Bemærk: For at anvende Lagringsmærkat på tidligere uploadede medier, kør",
|
||||||
|
"note_unlimited_quota": "Bemærk: Indsæt 0 for ubegrænset kvote",
|
||||||
"notes": "Noter",
|
"notes": "Noter",
|
||||||
|
"notification_permission_dialog_cancel": "Annuller",
|
||||||
"notification_permission_dialog_content": "Gå til indstillinger for at slå notifikationer til.",
|
"notification_permission_dialog_content": "Gå til indstillinger for at slå notifikationer til.",
|
||||||
|
"notification_permission_dialog_settings": "Indstillinger",
|
||||||
"notification_permission_list_tile_content": "Tillad at bruge notifikationer.",
|
"notification_permission_list_tile_content": "Tillad at bruge notifikationer.",
|
||||||
"notification_permission_list_tile_enable_button": "Slå notifikationer til",
|
"notification_permission_list_tile_enable_button": "Slå notifikationer til",
|
||||||
"notification_permission_list_tile_title": "Notifikationstilladelser",
|
"notification_permission_list_tile_title": "Notifikationstilladelser",
|
||||||
@@ -1287,18 +1424,22 @@
|
|||||||
"other_variables": "Andre variable",
|
"other_variables": "Andre variable",
|
||||||
"owned": "Egne",
|
"owned": "Egne",
|
||||||
"owner": "Ejer",
|
"owner": "Ejer",
|
||||||
|
"page_share_add_users": "Tilføj brugere",
|
||||||
"partner": "Partner",
|
"partner": "Partner",
|
||||||
"partner_can_access": "{partner} kan tilgå",
|
"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_assets": "Alle dine billeder og videoer, bortset fra dem i Arkivet og Slettet",
|
||||||
"partner_can_access_location": "Stedet, hvor dine billeder blev taget",
|
"partner_can_access_location": "Stedet, hvor dine billeder blev taget",
|
||||||
"partner_list_user_photos": "{user}s billeder",
|
"partner_list_user_photos": "{user}s billeder",
|
||||||
"partner_list_view_all": "Se alle",
|
"partner_list_view_all": "Se alle",
|
||||||
|
"partner_page_add_partner": "Tilføj partner",
|
||||||
"partner_page_empty_message": "Dine billeder er endnu ikke delt med en partner.",
|
"partner_page_empty_message": "Dine billeder er endnu ikke delt med en partner.",
|
||||||
"partner_page_no_more_users": "Der er ikke flere brugere at tilføje",
|
"partner_page_no_more_users": "Der er ikke flere brugere at tilføje",
|
||||||
"partner_page_partner_add_failed": "Kunne ikke tilføje en partner",
|
"partner_page_partner_add_failed": "Kunne ikke tilføje en partner",
|
||||||
"partner_page_select_partner": "Vælg partner",
|
"partner_page_select_partner": "Vælg partner",
|
||||||
"partner_page_shared_to_title": "Delt til",
|
"partner_page_shared_to_title": "Delt til",
|
||||||
"partner_page_stop_sharing_content": "{} vil ikke længere have adgang til dine billeder.",
|
"partner_page_stop_sharing_content": "{} vil ikke længere have adgang til dine billeder.",
|
||||||
|
"partner_page_stop_sharing_title": "Stop med at dele dine billeder?",
|
||||||
|
"partner_page_title": "Partner",
|
||||||
"partner_sharing": "Partnerdeling",
|
"partner_sharing": "Partnerdeling",
|
||||||
"partners": "Partnere",
|
"partners": "Partnere",
|
||||||
"password": "Kodeord",
|
"password": "Kodeord",
|
||||||
@@ -1331,12 +1472,21 @@
|
|||||||
"permission_onboarding_continue_anyway": "Fortsæt alligevel",
|
"permission_onboarding_continue_anyway": "Fortsæt alligevel",
|
||||||
"permission_onboarding_get_started": "Kom i gang",
|
"permission_onboarding_get_started": "Kom i gang",
|
||||||
"permission_onboarding_go_to_settings": "Gå til indstillinger",
|
"permission_onboarding_go_to_settings": "Gå til indstillinger",
|
||||||
|
"permission_onboarding_grant_permission": "Giv tilladelse",
|
||||||
|
"permission_onboarding_log_out": "Log ud",
|
||||||
"permission_onboarding_permission_denied": "Tilladelse afvist. For at bruge Immich, skal der gives tilladelse til at se billeder og videoer i indstillinger.",
|
"permission_onboarding_permission_denied": "Tilladelse afvist. For at bruge Immich, skal der gives tilladelse til at se billeder og videoer i indstillinger.",
|
||||||
"permission_onboarding_permission_granted": "Tilladelse givet! Du er nu klar.",
|
"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_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.",
|
"permission_onboarding_request": "Immich kræver tilliadelse til at se dine billeder og videoer.",
|
||||||
"person": "Person",
|
"person": "Person",
|
||||||
|
"person_add_name_dialog_cancel": "Annuller",
|
||||||
|
"person_add_name_dialog_hint": "Navn",
|
||||||
|
"person_add_name_dialog_save": "Gem",
|
||||||
|
"person_add_name_dialog_title": "Tilføj et navn",
|
||||||
|
"person_add_name_subtitle": "Find dem hurtigt ved navn med en søgning",
|
||||||
|
"person_add_name_title": "Tilføj et navn",
|
||||||
"person_birthdate": "Født den {date}",
|
"person_birthdate": "Født den {date}",
|
||||||
|
"person_edit_name": "Rediger navn",
|
||||||
"person_hidden": "{name}{hidden, select, true { (skjult)} other {}}",
|
"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.",
|
"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.",
|
||||||
"photos": "Billeder",
|
"photos": "Billeder",
|
||||||
@@ -1360,14 +1510,20 @@
|
|||||||
"previous_memory": "Forrige minde",
|
"previous_memory": "Forrige minde",
|
||||||
"previous_or_next_photo": "Forrige eller næste billede",
|
"previous_or_next_photo": "Forrige eller næste billede",
|
||||||
"primary": "Primære",
|
"primary": "Primære",
|
||||||
|
"primary_color_subtitle": "Vælg en farve til primære handlinger og accenter.",
|
||||||
|
"primary_color_title": "Primær farve",
|
||||||
"privacy": "Privatliv",
|
"privacy": "Privatliv",
|
||||||
"profile_drawer_app_logs": "Log",
|
"profile_drawer_app_logs": "Log",
|
||||||
"profile_drawer_client_out_of_date_major": "Mobilapp er forældet. Opdater venligst til den nyeste større version",
|
"profile_drawer_client_out_of_date_major": "Mobilapp er forældet. Opdater venligst til den nyeste større version",
|
||||||
"profile_drawer_client_out_of_date_minor": "Mobilapp er forældet. Opdater venligst til den nyeste mindre version",
|
"profile_drawer_client_out_of_date_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_client_server_up_to_date": "Klient og server er ajour",
|
||||||
|
"profile_drawer_documentation": "Dokumentation",
|
||||||
"profile_drawer_github": "GitHub",
|
"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_major": "Server er forældet. Opdater venligst til den nyeste større version",
|
||||||
"profile_drawer_server_out_of_date_minor": "Server er forældet. Opdater venligst til den nyeste mindre version",
|
"profile_drawer_server_out_of_date_minor": "Server er forældet. Opdater venligst til den nyeste mindre version",
|
||||||
|
"profile_drawer_settings": "Indstillinger",
|
||||||
|
"profile_drawer_sign_out": "Log ud",
|
||||||
|
"profile_drawer_trash": "Papirkurv",
|
||||||
"profile_image_of_user": "Profilbillede af {user}",
|
"profile_image_of_user": "Profilbillede af {user}",
|
||||||
"profile_picture_set": "Profilbillede indstillet.",
|
"profile_picture_set": "Profilbillede indstillet.",
|
||||||
"public_album": "Offentligt album",
|
"public_album": "Offentligt album",
|
||||||
@@ -1417,7 +1573,7 @@
|
|||||||
"recent": "For nylig",
|
"recent": "For nylig",
|
||||||
"recent-albums": "Seneste albums",
|
"recent-albums": "Seneste albums",
|
||||||
"recent_searches": "Seneste søgninger",
|
"recent_searches": "Seneste søgninger",
|
||||||
"recently_added": "Senest tilføjet",
|
"recently_added": "Nyligt tilføjet",
|
||||||
"recently_added_page_title": "Nyligt tilføjet",
|
"recently_added_page_title": "Nyligt tilføjet",
|
||||||
"refresh": "Opdatér",
|
"refresh": "Opdatér",
|
||||||
"refresh_encoded_videos": "Opdater kodede videoer",
|
"refresh_encoded_videos": "Opdater kodede videoer",
|
||||||
@@ -1430,6 +1586,8 @@
|
|||||||
"refreshing_faces": "Opdaterer ansigter",
|
"refreshing_faces": "Opdaterer ansigter",
|
||||||
"refreshing_metadata": "Opdaterer metadata",
|
"refreshing_metadata": "Opdaterer metadata",
|
||||||
"regenerating_thumbnails": "Regenererer forhåndsvisninger",
|
"regenerating_thumbnails": "Regenererer forhåndsvisninger",
|
||||||
|
"remainder": "Tilbageværende",
|
||||||
|
"remainder_sub": "Tilbageværende billeder og albummer, at sikkerhedskopiere, fra valgte",
|
||||||
"remove": "Fjern",
|
"remove": "Fjern",
|
||||||
"remove_assets_album_confirmation": "Er du sikker på, at du vil fjerne {count, plural, one {# aktiv} other {# aktiver}} fra albummet?",
|
"remove_assets_album_confirmation": "Er du sikker på, at du vil fjerne {count, plural, one {# aktiv} other {# aktiver}} fra albummet?",
|
||||||
"remove_assets_shared_link_confirmation": "Er du sikker på, at du vil fjerne {count, plural, one {# aktiv} other {# aktiver}} fra dette delte link?",
|
"remove_assets_shared_link_confirmation": "Er du sikker på, at du vil fjerne {count, plural, one {# aktiv} other {# aktiver}} fra dette delte link?",
|
||||||
@@ -1485,8 +1643,10 @@
|
|||||||
"scan_library": "Skan",
|
"scan_library": "Skan",
|
||||||
"scan_settings": "Skanningsindstillinger",
|
"scan_settings": "Skanningsindstillinger",
|
||||||
"scanning_for_album": "Skanner efter albummer...",
|
"scanning_for_album": "Skanner efter albummer...",
|
||||||
|
"screenshots": "Skærmbilleder",
|
||||||
"search": "Søg",
|
"search": "Søg",
|
||||||
"search_albums": "Søg i albummer",
|
"search_albums": "Søg i albummer",
|
||||||
|
"search_bar_hint": "Søg i dine billeder",
|
||||||
"search_by_context": "Søg efter kontekst",
|
"search_by_context": "Søg efter kontekst",
|
||||||
"search_by_description": "Søg efter beskrivelse",
|
"search_by_description": "Søg efter beskrivelse",
|
||||||
"search_by_description_example": "Vandredag i Paris",
|
"search_by_description_example": "Vandredag i Paris",
|
||||||
@@ -1497,17 +1657,33 @@
|
|||||||
"search_city": "Søg efter by...",
|
"search_city": "Søg efter by...",
|
||||||
"search_country": "Søg efter land...",
|
"search_country": "Søg efter land...",
|
||||||
"search_filter_apply": "Tilføj filter",
|
"search_filter_apply": "Tilføj filter",
|
||||||
|
"search_filter_camera": "Kamera",
|
||||||
|
"search_filter_camera_make": "Producent",
|
||||||
|
"search_filter_camera_model": "Model",
|
||||||
"search_filter_camera_title": "Vælg type af kamera",
|
"search_filter_camera_title": "Vælg type af kamera",
|
||||||
|
"search_filter_contextual": "Search by context",
|
||||||
"search_filter_date": "Dato",
|
"search_filter_date": "Dato",
|
||||||
"search_filter_date_interval": "{start} til { slut}",
|
"search_filter_date_interval": "{start} til { slut}",
|
||||||
"search_filter_date_title": "Vælg et datointerval",
|
"search_filter_date_title": "Vælg et datointerval",
|
||||||
|
"search_filter_description": "Search by description",
|
||||||
|
"search_filter_display_option_archive": "Arkiv",
|
||||||
|
"search_filter_display_option_favorite": "Favorit",
|
||||||
"search_filter_display_option_not_in_album": "Ikke i album",
|
"search_filter_display_option_not_in_album": "Ikke i album",
|
||||||
"search_filter_display_options": "Visningsindstillinger",
|
"search_filter_display_options": "Visningsindstillinger",
|
||||||
|
"search_filter_display_options_title": "Visningsindstillinger",
|
||||||
"search_filter_filename": "Search by file name",
|
"search_filter_filename": "Search by file name",
|
||||||
"search_filter_location": "Lokation",
|
"search_filter_location": "Lokation",
|
||||||
|
"search_filter_location_city": "By",
|
||||||
|
"search_filter_location_country": "Land",
|
||||||
|
"search_filter_location_state": "Stat",
|
||||||
"search_filter_location_title": "Vælg lokation",
|
"search_filter_location_title": "Vælg lokation",
|
||||||
"search_filter_media_type": "Medietype",
|
"search_filter_media_type": "Medietype",
|
||||||
|
"search_filter_media_type_all": "Alle",
|
||||||
|
"search_filter_media_type_image": "Billede",
|
||||||
"search_filter_media_type_title": "Vælg medietype",
|
"search_filter_media_type_title": "Vælg medietype",
|
||||||
|
"search_filter_media_type_video": "Video",
|
||||||
|
"search_filter_people": "Personer",
|
||||||
|
"search_filter_people_hint": "Filter people",
|
||||||
"search_filter_people_title": "Vælg personer",
|
"search_filter_people_title": "Vælg personer",
|
||||||
"search_for": "Søg efter",
|
"search_for": "Søg efter",
|
||||||
"search_for_existing_person": "Søg efter eksisterende person",
|
"search_for_existing_person": "Søg efter eksisterende person",
|
||||||
@@ -1517,17 +1693,30 @@
|
|||||||
"search_no_result": "No results found, try a different search term or combination",
|
"search_no_result": "No results found, try a different search term or combination",
|
||||||
"search_options": "Søgemuligheder",
|
"search_options": "Søgemuligheder",
|
||||||
"search_page_categories": "Kategorier",
|
"search_page_categories": "Kategorier",
|
||||||
|
"search_page_favorites": "Favoritter",
|
||||||
"search_page_motion_photos": "Bevægelsesbilleder",
|
"search_page_motion_photos": "Bevægelsesbilleder",
|
||||||
"search_page_no_objects": "Ingen elementer er tilgængelige",
|
"search_page_no_objects": "Ingen elementer er tilgængelige",
|
||||||
"search_page_no_places": "Ingen placeringsinformation er tilgængelig",
|
"search_page_no_places": "Ingen placeringsinformation er tilgængelig",
|
||||||
|
"search_page_people": "Personer",
|
||||||
|
"search_page_person_add_name_dialog_cancel": "Annuller",
|
||||||
|
"search_page_person_add_name_dialog_hint": "Navn",
|
||||||
|
"search_page_person_add_name_dialog_save": "Gem",
|
||||||
|
"search_page_person_add_name_dialog_title": "Tilføj et navn",
|
||||||
|
"search_page_person_add_name_subtitle": "Find dem hurtigt ved navn med en søgning",
|
||||||
|
"search_page_person_add_name_title": "Tilføj et navn",
|
||||||
|
"search_page_person_edit_name": "Rediger navn",
|
||||||
|
"search_page_places": "Steder",
|
||||||
|
"search_page_recently_added": "Nyligt tilføjet",
|
||||||
"search_page_screenshots": "Skærmbilleder",
|
"search_page_screenshots": "Skærmbilleder",
|
||||||
"search_page_search_photos_videos": "Søg i dine billeder og videoer",
|
"search_page_search_photos_videos": "Søg i dine billeder og videoer",
|
||||||
"search_page_selfies": "Selfier",
|
"search_page_selfies": "Selfier",
|
||||||
"search_page_things": "Ting",
|
"search_page_things": "Ting",
|
||||||
|
"search_page_videos": "Videoer",
|
||||||
"search_page_view_all_button": "Vis alt",
|
"search_page_view_all_button": "Vis alt",
|
||||||
"search_page_your_activity": "Din aktivitet",
|
"search_page_your_activity": "Din aktivitet",
|
||||||
"search_page_your_map": "Dit kort",
|
"search_page_your_map": "Dit kort",
|
||||||
"search_people": "Søg i personer",
|
"search_people": "Søg i personer",
|
||||||
|
"search_photos_videos": "Søg i dine billeder og videoer",
|
||||||
"search_places": "Søg i steder",
|
"search_places": "Søg i steder",
|
||||||
"search_rating": "Søg efter vurdering...",
|
"search_rating": "Søg efter vurdering...",
|
||||||
"search_result_page_new_search_hint": "Ny søgning",
|
"search_result_page_new_search_hint": "Ny søgning",
|
||||||
@@ -1543,6 +1732,7 @@
|
|||||||
"second": "Sekund",
|
"second": "Sekund",
|
||||||
"see_all_people": "Se alle personer",
|
"see_all_people": "Se alle personer",
|
||||||
"select": "Vælg",
|
"select": "Vælg",
|
||||||
|
"select_additional_user_for_sharing_page_suggestions": "Anbefalinger",
|
||||||
"select_album_cover": "Vælg albumcover",
|
"select_album_cover": "Vælg albumcover",
|
||||||
"select_all": "Vælg alle",
|
"select_all": "Vælg alle",
|
||||||
"select_all_duplicates": "Vælg alle dubletter",
|
"select_all_duplicates": "Vælg alle dubletter",
|
||||||
@@ -1556,16 +1746,21 @@
|
|||||||
"select_photos": "Vælg billeder",
|
"select_photos": "Vælg billeder",
|
||||||
"select_trash_all": "Vælg smid alle ud",
|
"select_trash_all": "Vælg smid alle ud",
|
||||||
"select_user_for_sharing_page_err_album": "Fejlede i at oprette et nyt album",
|
"select_user_for_sharing_page_err_album": "Fejlede i at oprette et nyt album",
|
||||||
|
"select_user_for_sharing_page_share_suggestions": "Anbefalinger",
|
||||||
"selected": "Valgt",
|
"selected": "Valgt",
|
||||||
"selected_count": "{count, plural, one {# valgt} other {# valgte}}",
|
"selected_count": "{count, plural, one {# valgt} other {# valgte}}",
|
||||||
|
"selfies": "Selfier",
|
||||||
"send_message": "Send besked",
|
"send_message": "Send besked",
|
||||||
"send_welcome_email": "Send velkomstemail",
|
"send_welcome_email": "Send velkomstemail",
|
||||||
"server_endpoint": "Server endepunkt",
|
"server_endpoint": "Server endepunkt",
|
||||||
"server_info_box_app_version": "Applikationsversion",
|
"server_info_box_app_version": "Applikationsversion",
|
||||||
|
"server_info_box_latest_release": "Seneste version",
|
||||||
"server_info_box_server_url": "Server URL",
|
"server_info_box_server_url": "Server URL",
|
||||||
|
"server_info_box_server_version": "Serverversion ",
|
||||||
"server_offline": "Server Offline",
|
"server_offline": "Server Offline",
|
||||||
"server_online": "Server Online",
|
"server_online": "Server Online",
|
||||||
"server_stats": "Serverstatus",
|
"server_stats": "Serverstatus",
|
||||||
|
"server_storage": "Serverlager",
|
||||||
"server_version": "Server Version",
|
"server_version": "Server Version",
|
||||||
"set": "Indstil",
|
"set": "Indstil",
|
||||||
"set_as_album_cover": "Indstil som albumcover",
|
"set_as_album_cover": "Indstil som albumcover",
|
||||||
@@ -1592,25 +1787,42 @@
|
|||||||
"setting_notifications_single_progress_subtitle": "Detaljeret uploadstatus pr. element",
|
"setting_notifications_single_progress_subtitle": "Detaljeret uploadstatus pr. element",
|
||||||
"setting_notifications_single_progress_title": "Vis detaljeret baggrundsuploadstatus",
|
"setting_notifications_single_progress_title": "Vis detaljeret baggrundsuploadstatus",
|
||||||
"setting_notifications_subtitle": "Tilpas dine notifikationspræferencer",
|
"setting_notifications_subtitle": "Tilpas dine notifikationspræferencer",
|
||||||
|
"setting_notifications_title": "Notifikationer",
|
||||||
"setting_notifications_total_progress_subtitle": "Samlet uploadstatus (færdige/samlet antal elementer)",
|
"setting_notifications_total_progress_subtitle": "Samlet uploadstatus (færdige/samlet antal elementer)",
|
||||||
"setting_notifications_total_progress_title": "Vis samlet baggrundsuploadstatus",
|
"setting_notifications_total_progress_title": "Vis samlet baggrundsuploadstatus",
|
||||||
|
"setting_pages_app_bar_settings": "Indstillinger",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
"setting_video_viewer_looping_title": "Looping",
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
"setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.",
|
"setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.",
|
||||||
"setting_video_viewer_original_video_title": "Force original video",
|
"setting_video_viewer_original_video_title": "Force original video",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"settings": "Indstillinger",
|
"settings": "Indstillinger",
|
||||||
"settings_require_restart": "Genstart venligst Immich for at anvende denne ændring",
|
"settings_require_restart": "Genstart venligst Immich for at anvende denne ændring",
|
||||||
"settings_saved": "Indstillinger er gemt",
|
"settings_saved": "Indstillinger er gemt",
|
||||||
|
"settings_subtitle": "Indstillinger for billedgitterlayout",
|
||||||
|
"settings_title": "Billedgitter",
|
||||||
"share": "Del",
|
"share": "Del",
|
||||||
|
"share_add": "Tilføj",
|
||||||
"share_add_photos": "Tilføj billeder",
|
"share_add_photos": "Tilføj billeder",
|
||||||
|
"share_add_title": "Tilføj en titel",
|
||||||
"share_assets_selected": "{} valgt",
|
"share_assets_selected": "{} valgt",
|
||||||
|
"share_create_album": "Opret album",
|
||||||
"share_dialog_preparing": "Forbereder...",
|
"share_dialog_preparing": "Forbereder...",
|
||||||
|
"share_done": "Færdig",
|
||||||
|
"share_invite": "Inviter til album",
|
||||||
|
"share_link": "Share Link",
|
||||||
|
"share_to": "Del til",
|
||||||
"shared": "Delt",
|
"shared": "Delt",
|
||||||
"shared_album_activities_input_disable": "Kommentarer er deaktiveret",
|
"shared_album_activities_input_disable": "Kommentarer er deaktiveret",
|
||||||
|
"shared_album_activities_input_hint": "Skriv noget",
|
||||||
"shared_album_activity_remove_content": "Vil du slette denne aktivitet?",
|
"shared_album_activity_remove_content": "Vil du slette denne aktivitet?",
|
||||||
"shared_album_activity_remove_title": "Slet aktivitet",
|
"shared_album_activity_remove_title": "Slet aktivitet",
|
||||||
|
"shared_album_activity_setting_subtitle": "Lad andre svare",
|
||||||
|
"shared_album_activity_setting_title": "Kommentarer & 'synes godt om'",
|
||||||
"shared_album_section_people_action_error": "Der opstod en fejl i fjernelsen fra albummet",
|
"shared_album_section_people_action_error": "Der opstod en fejl i fjernelsen fra albummet",
|
||||||
"shared_album_section_people_action_leave": "Fjern brugere fra albummet",
|
"shared_album_section_people_action_leave": "Fjern brugere fra albummet",
|
||||||
"shared_album_section_people_action_remove_user": "Fjern brugere fra albummet",
|
"shared_album_section_people_action_remove_user": "Fjern brugere fra albummet",
|
||||||
|
"shared_album_section_people_owner_label": "Ejer",
|
||||||
"shared_album_section_people_title": "PERSONER",
|
"shared_album_section_people_title": "PERSONER",
|
||||||
"shared_by": "Delt af",
|
"shared_by": "Delt af",
|
||||||
"shared_by_user": "Delt af {user}",
|
"shared_by_user": "Delt af {user}",
|
||||||
@@ -1620,8 +1832,17 @@
|
|||||||
"shared_link_app_bar_title": "Delte links",
|
"shared_link_app_bar_title": "Delte links",
|
||||||
"shared_link_clipboard_copied_massage": "Kopieret til udklipsholderen",
|
"shared_link_clipboard_copied_massage": "Kopieret til udklipsholderen",
|
||||||
"shared_link_clipboard_text": "Link: {}\nkodeord: {}",
|
"shared_link_clipboard_text": "Link: {}\nkodeord: {}",
|
||||||
|
"shared_link_create_app_bar_title": "Opret link for at dele",
|
||||||
"shared_link_create_error": "Der opstod en fejl i oprettelsen af et delt link",
|
"shared_link_create_error": "Der opstod en fejl i oprettelsen af et delt link",
|
||||||
|
"shared_link_create_info": "Lad enhver med linket se de(t) valgte billede(r)",
|
||||||
|
"shared_link_create_submit_button": "Oprat link",
|
||||||
|
"shared_link_edit_allow_download": "Tillad at en offenlig bruger kan downloade",
|
||||||
|
"shared_link_edit_allow_upload": "Tillad at en offentlig bruger kan uploade",
|
||||||
|
"shared_link_edit_app_bar_title": "Rediger link",
|
||||||
|
"shared_link_edit_change_expiry": "Ændrer udløbstidspunkt",
|
||||||
|
"shared_link_edit_description": "Beskrivelse",
|
||||||
"shared_link_edit_description_hint": "Indtast beskrivelse",
|
"shared_link_edit_description_hint": "Indtast beskrivelse",
|
||||||
|
"shared_link_edit_expire_after": "Udløb efter",
|
||||||
"shared_link_edit_expire_after_option_day": "1 dag",
|
"shared_link_edit_expire_after_option_day": "1 dag",
|
||||||
"shared_link_edit_expire_after_option_days": "{} dage",
|
"shared_link_edit_expire_after_option_days": "{} dage",
|
||||||
"shared_link_edit_expire_after_option_hour": "1 time",
|
"shared_link_edit_expire_after_option_hour": "1 time",
|
||||||
@@ -1629,10 +1850,15 @@
|
|||||||
"shared_link_edit_expire_after_option_minute": "1 minut",
|
"shared_link_edit_expire_after_option_minute": "1 minut",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minutter",
|
"shared_link_edit_expire_after_option_minutes": "{} minutter",
|
||||||
"shared_link_edit_expire_after_option_months": "{} måneder",
|
"shared_link_edit_expire_after_option_months": "{} måneder",
|
||||||
|
"shared_link_edit_expire_after_option_never": "Aldrig",
|
||||||
"shared_link_edit_expire_after_option_year": "{} år",
|
"shared_link_edit_expire_after_option_year": "{} år",
|
||||||
|
"shared_link_edit_password": "Kodeord",
|
||||||
"shared_link_edit_password_hint": "Indtast kodeordet",
|
"shared_link_edit_password_hint": "Indtast kodeordet",
|
||||||
|
"shared_link_edit_show_meta": "Vis metadata",
|
||||||
"shared_link_edit_submit_button": "Opdater link",
|
"shared_link_edit_submit_button": "Opdater link",
|
||||||
|
"shared_link_empty": "Du har endnu ingen delte links",
|
||||||
"shared_link_error_server_url_fetch": "Kan ikke finde server URL",
|
"shared_link_error_server_url_fetch": "Kan ikke finde server URL",
|
||||||
|
"shared_link_expired": "Udløbet",
|
||||||
"shared_link_expires_day": "Udløber om {} dag",
|
"shared_link_expires_day": "Udløber om {} dag",
|
||||||
"shared_link_expires_days": "Udløber om {} dage",
|
"shared_link_expires_days": "Udløber om {} dage",
|
||||||
"shared_link_expires_hour": "Udløber om {} time",
|
"shared_link_expires_hour": "Udløber om {} time",
|
||||||
@@ -1643,9 +1869,12 @@
|
|||||||
"shared_link_expires_second": "Udløber om {} sekund",
|
"shared_link_expires_second": "Udløber om {} sekund",
|
||||||
"shared_link_expires_seconds": "Udløber om {} sekunder",
|
"shared_link_expires_seconds": "Udløber om {} sekunder",
|
||||||
"shared_link_individual_shared": "Individuelt delt",
|
"shared_link_individual_shared": "Individuelt delt",
|
||||||
|
"shared_link_info_chip_download": "Hent",
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
|
"shared_link_info_chip_upload": "Upload",
|
||||||
"shared_link_manage_links": "Håndter delte links",
|
"shared_link_manage_links": "Håndter delte links",
|
||||||
"shared_link_options": "Muligheder for delt link",
|
"shared_link_options": "Muligheder for delt link",
|
||||||
|
"shared_link_public_album": "Offentligt album",
|
||||||
"shared_links": "Delte links",
|
"shared_links": "Delte links",
|
||||||
"shared_links_description": "Del billeder og videoer med et link",
|
"shared_links_description": "Del billeder og videoer med et link",
|
||||||
"shared_photos_and_videos_count": "{assetCount, plural, other {# delte billeder & videoer.}}",
|
"shared_photos_and_videos_count": "{assetCount, plural, other {# delte billeder & videoer.}}",
|
||||||
@@ -1659,6 +1888,7 @@
|
|||||||
"sharing_sidebar_description": "Vis et link til deling i sidemenuen",
|
"sharing_sidebar_description": "Vis et link til deling i sidemenuen",
|
||||||
"sharing_silver_appbar_create_shared_album": "Opret delt album",
|
"sharing_silver_appbar_create_shared_album": "Opret delt album",
|
||||||
"sharing_silver_appbar_share_partner": "Del med partner",
|
"sharing_silver_appbar_share_partner": "Del med partner",
|
||||||
|
"sharing_silver_appbar_shared_links": "Delte links",
|
||||||
"shift_to_permanent_delete": "tryk på ⇧ for at slette aktiv permanent",
|
"shift_to_permanent_delete": "tryk på ⇧ for at slette aktiv permanent",
|
||||||
"show_album_options": "Vis albumindstillinger",
|
"show_album_options": "Vis albumindstillinger",
|
||||||
"show_albums": "Vis albummer",
|
"show_albums": "Vis albummer",
|
||||||
@@ -1707,17 +1937,28 @@
|
|||||||
"stacked_assets_count": "Stablet {count, plural, one {# aktiv} other {# aktiver}}",
|
"stacked_assets_count": "Stablet {count, plural, one {# aktiv} other {# aktiver}}",
|
||||||
"stacktrace": "Stacktrace",
|
"stacktrace": "Stacktrace",
|
||||||
"start": "Start",
|
"start": "Start",
|
||||||
|
"start_backup": "Start sikkerhedskopiering",
|
||||||
"start_date": "Startdato",
|
"start_date": "Startdato",
|
||||||
"state": "Stat",
|
"state": "Stat",
|
||||||
|
"statistics_album": "Biblioteksminiaturer",
|
||||||
|
"statistics_assets": "{} elementer ({})",
|
||||||
|
"statistics_full": "Fulde billeder",
|
||||||
|
"statistics_shared": "Miniaturebilleder til delte albummer",
|
||||||
|
"statistics_thumbnail": "Miniaturebilleder",
|
||||||
|
"statistics_title": "Cacheforbrug",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
|
"status_off": "Sikkerhedskopiering er slået fra",
|
||||||
|
"status_on": "Sikkerhedskopiering er slået til",
|
||||||
"stop_motion_photo": "Stopmotionbillede",
|
"stop_motion_photo": "Stopmotionbillede",
|
||||||
"stop_photo_sharing": "Stop med at dele dine billeder?",
|
"stop_photo_sharing": "Stop med at dele dine billeder?",
|
||||||
"stop_photo_sharing_description": "{partner} vil ikke længere kunne tilgå dine billeder.",
|
"stop_photo_sharing_description": "{partner} vil ikke længere kunne tilgå dine billeder.",
|
||||||
"stop_sharing_photos_with_user": "Afslut deling af dine fotos med denne bruger",
|
"stop_sharing_photos_with_user": "Afslut deling af dine fotos med denne bruger",
|
||||||
"storage": "Lagringsplads",
|
"storage": "Lagringsplads",
|
||||||
|
"storage_format": "{} af {} brugt",
|
||||||
"storage_label": "Lagringsmærkat",
|
"storage_label": "Lagringsmærkat",
|
||||||
"storage_usage": "{used} ud af {available} brugt",
|
"storage_usage": "{used} ud af {available} brugt",
|
||||||
"submit": "Indsend",
|
"submit": "Indsend",
|
||||||
|
"subtitle": "Håndter cache-adfærden for Immich-appen.",
|
||||||
"suggestions": "Anbefalinger",
|
"suggestions": "Anbefalinger",
|
||||||
"sunrise_on_the_beach": "Solopgang på stranden",
|
"sunrise_on_the_beach": "Solopgang på stranden",
|
||||||
"support": "Support",
|
"support": "Support",
|
||||||
@@ -1728,6 +1969,12 @@
|
|||||||
"sync_albums": "Synkroniser albummer",
|
"sync_albums": "Synkroniser albummer",
|
||||||
"sync_albums_manual_subtitle": "Synkroniser alle uploadet billeder og videoer til de valgte backupalbummer",
|
"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",
|
"sync_upload_album_setting_subtitle": "Opret og upload dine billeder og videoer til de valgte albummer i Immich",
|
||||||
|
"system_primary_color_title": "Brug systemfarver",
|
||||||
|
"system_theme_switch": "Automatisk (Følg systemindstillinger)",
|
||||||
|
"tab_controller_nav_library": "Bibliotek",
|
||||||
|
"tab_controller_nav_photos": "Billeder",
|
||||||
|
"tab_controller_nav_search": "Søg",
|
||||||
|
"tab_controller_nav_sharing": "Deling",
|
||||||
"tag": "Tag",
|
"tag": "Tag",
|
||||||
"tag_assets": "Tag mediefiler",
|
"tag_assets": "Tag mediefiler",
|
||||||
"tag_created": "Oprettet tag: {tag}",
|
"tag_created": "Oprettet tag: {tag}",
|
||||||
@@ -1745,6 +1992,7 @@
|
|||||||
"theme_setting_asset_list_tiles_per_row_title": "Antal elementer per række ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Antal elementer per række ({})",
|
||||||
"theme_setting_colorful_interface_subtitle": "Tilføj primær farve til baggrundsoverflader.",
|
"theme_setting_colorful_interface_subtitle": "Tilføj primær farve til baggrundsoverflader.",
|
||||||
"theme_setting_colorful_interface_title": "Farverig grænseflade",
|
"theme_setting_colorful_interface_title": "Farverig grænseflade",
|
||||||
|
"theme_setting_dark_mode_switch": "Mørk tilstand",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "Juster kvaliteten i billedfremviseren",
|
"theme_setting_image_viewer_quality_subtitle": "Juster kvaliteten i billedfremviseren",
|
||||||
"theme_setting_image_viewer_quality_title": "Billedfremviserkvalitet",
|
"theme_setting_image_viewer_quality_title": "Billedfremviserkvalitet",
|
||||||
"theme_setting_primary_color_subtitle": "Vælg en farve til primære handlinger og accenter.",
|
"theme_setting_primary_color_subtitle": "Vælg en farve til primære handlinger og accenter.",
|
||||||
@@ -1752,14 +2000,25 @@
|
|||||||
"theme_setting_system_primary_color_title": "Brug systemfarver",
|
"theme_setting_system_primary_color_title": "Brug systemfarver",
|
||||||
"theme_setting_system_theme_switch": "Automatisk (Følg systemindstillinger)",
|
"theme_setting_system_theme_switch": "Automatisk (Følg systemindstillinger)",
|
||||||
"theme_setting_theme_subtitle": "Vælg appens temaindstilling",
|
"theme_setting_theme_subtitle": "Vælg appens temaindstilling",
|
||||||
|
"theme_setting_theme_title": "Tema",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Tre-trins indlæsning kan øge ydeevnen, men kan ligeledes føre til højere netværksbelastning",
|
"theme_setting_three_stage_loading_subtitle": "Tre-trins indlæsning kan øge ydeevnen, men kan ligeledes føre til højere netværksbelastning",
|
||||||
"theme_setting_three_stage_loading_title": "Slå tre-trins indlæsning til",
|
"theme_setting_three_stage_loading_title": "Slå tre-trins indlæsning til",
|
||||||
|
"theme_subtitle": "Vælg appens temaindstilling",
|
||||||
|
"theme_title": "Tema",
|
||||||
"they_will_be_merged_together": "De vil blive slået sammen",
|
"they_will_be_merged_together": "De vil blive slået sammen",
|
||||||
|
"things": "Ting",
|
||||||
"third_party_resources": "Tredjepartsressourcer",
|
"third_party_resources": "Tredjepartsressourcer",
|
||||||
|
"three_stage_loading_subtitle": "Tre-trins indlæsning kan øge ydeevnen, men kan ligeledes føre til højere netværksbelastning",
|
||||||
|
"three_stage_loading_title": "Slå tre-trins indlæsning til",
|
||||||
|
"thumbnail_size": "Størrelse af miniaturebillede cache ({} elementer)",
|
||||||
|
"tile_subtitle": "Kontroller den lokale lagerplads",
|
||||||
|
"tile_title": "Lokal lagerplads",
|
||||||
"time_based_memories": "Tidsbaserede minder",
|
"time_based_memories": "Tidsbaserede minder",
|
||||||
"timeline": "Tidslinje",
|
"timeline": "Tidslinje",
|
||||||
"timezone": "Tidszone",
|
"timezone": "Tidszone",
|
||||||
|
"title": "Cache-indstillinger",
|
||||||
"to_archive": "Arkivér",
|
"to_archive": "Arkivér",
|
||||||
|
"to_backup": "Albummer at sikkerhedskopiere",
|
||||||
"to_change_password": "Skift adgangskode",
|
"to_change_password": "Skift adgangskode",
|
||||||
"to_favorite": "Gør til favorit",
|
"to_favorite": "Gør til favorit",
|
||||||
"to_login": "Login",
|
"to_login": "Login",
|
||||||
@@ -1768,21 +2027,31 @@
|
|||||||
"toggle_settings": "Slå indstillinger til eller fra",
|
"toggle_settings": "Slå indstillinger til eller fra",
|
||||||
"toggle_theme": "Slå mørkt tema til eller fra",
|
"toggle_theme": "Slå mørkt tema til eller fra",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"total_sub": "Alle unikke billeder og videoer fra valgte albummer",
|
||||||
"total_usage": "Samlet forbrug",
|
"total_usage": "Samlet forbrug",
|
||||||
|
"translated_text_options": "Handlinger",
|
||||||
"trash": "Papirkurv",
|
"trash": "Papirkurv",
|
||||||
"trash_all": "Smid alle ud",
|
"trash_all": "Smid alle ud",
|
||||||
"trash_count": "Slet {count, number}",
|
"trash_count": "Slet {count, number}",
|
||||||
"trash_delete_asset": "Flyt mediefil til Papirkurv",
|
"trash_delete_asset": "Flyt mediefil til Papirkurv",
|
||||||
"trash_emptied": "Tømte papirkurven",
|
"trash_emptied": "Tømte papirkurven",
|
||||||
|
"trash_from_immich": "Flyt til papirkurv",
|
||||||
"trash_no_results_message": "Billeder og videoer markeret til sletning vil blive vist her.",
|
"trash_no_results_message": "Billeder og videoer markeret til sletning vil blive vist her.",
|
||||||
|
"trash_page_delete": "Slet",
|
||||||
"trash_page_delete_all": "Slet alt",
|
"trash_page_delete_all": "Slet alt",
|
||||||
|
"trash_page_empty_trash_btn": "Tøm papirkurv",
|
||||||
"trash_page_empty_trash_dialog_content": "Vil du tømme papirkurven? Disse elementer vil blive permanent fjernet fra Immich",
|
"trash_page_empty_trash_dialog_content": "Vil du tømme papirkurven? Disse elementer vil blive permanent fjernet fra Immich",
|
||||||
|
"trash_page_empty_trash_dialog_ok": "Ok",
|
||||||
"trash_page_info": "Slettede elementer vil blive slettet permanent efter {} dage",
|
"trash_page_info": "Slettede elementer vil blive slettet permanent efter {} dage",
|
||||||
"trash_page_no_assets": "Ingen slettede elementer",
|
"trash_page_no_assets": "Ingen slettede elementer",
|
||||||
|
"trash_page_restore": "Gendan",
|
||||||
"trash_page_restore_all": "Gendan alt",
|
"trash_page_restore_all": "Gendan alt",
|
||||||
"trash_page_select_assets_btn": "Vælg elementer",
|
"trash_page_select_assets_btn": "Vælg elementer",
|
||||||
|
"trash_page_select_btn": "Vælg",
|
||||||
"trash_page_title": "Papirkurv ({})",
|
"trash_page_title": "Papirkurv ({})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Mediefiler i skraldespanden vil blive slettet permanent efter {days, plural, one {# dag} other {# dage}}.",
|
"trashed_items_will_be_permanently_deleted_after": "Mediefiler i skraldespanden vil blive slettet permanent efter {days, plural, one {# dag} other {# dage}}.",
|
||||||
|
"turn_off": "Slå sikkerhedskopiering fra",
|
||||||
|
"turn_on": "Slå sikkerhedskopiering til",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"unarchive": "Afakivér",
|
"unarchive": "Afakivér",
|
||||||
"unarchived_count": "{count, plural, other {Uarkiveret #}}",
|
"unarchived_count": "{count, plural, other {Uarkiveret #}}",
|
||||||
@@ -1807,10 +2076,13 @@
|
|||||||
"untracked_files": "Ikke overvågede filer",
|
"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",
|
"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",
|
"up_next": "Næste",
|
||||||
|
"update": "Opdater",
|
||||||
"updated_password": "Opdaterede adgangskode",
|
"updated_password": "Opdaterede adgangskode",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"upload_concurrency": "Upload samtidighed",
|
"upload_concurrency": "Upload samtidighed",
|
||||||
|
"upload_dialog_cancel": "Annuller",
|
||||||
"upload_dialog_info": "Vil du sikkerhedskopiere de(t) valgte element(er) til serveren?",
|
"upload_dialog_info": "Vil du sikkerhedskopiere de(t) valgte element(er) til serveren?",
|
||||||
|
"upload_dialog_ok": "Upload",
|
||||||
"upload_dialog_title": "Upload element",
|
"upload_dialog_title": "Upload element",
|
||||||
"upload_errors": "Upload afsluttet med {count, plural, one {# fejl} other {# fejl}}. Opdater siden for at se nye uploadaktiver.",
|
"upload_errors": "Upload afsluttet med {count, plural, one {# fejl} other {# fejl}}. Opdater siden for at se nye uploadaktiver.",
|
||||||
"upload_progress": "Resterende {remaining, number} - Behandlet {processed, number}/{total, number}",
|
"upload_progress": "Resterende {remaining, number} - Behandlet {processed, number}/{total, number}",
|
||||||
@@ -1821,6 +2093,7 @@
|
|||||||
"upload_success": "Upload gennemført. Opdater siden for at se nye uploadaktiver.",
|
"upload_success": "Upload gennemført. Opdater siden for at se nye uploadaktiver.",
|
||||||
"upload_to_immich": "Upload to Immich ({})",
|
"upload_to_immich": "Upload to Immich ({})",
|
||||||
"uploading": "Uploading",
|
"uploading": "Uploading",
|
||||||
|
"uploading_file_info": "Uploader filinformation",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Forbrug",
|
"usage": "Forbrug",
|
||||||
"use_current_connection": "brug nuværende forbindelse",
|
"use_current_connection": "brug nuværende forbindelse",
|
||||||
@@ -1843,6 +2116,7 @@
|
|||||||
"version": "Version",
|
"version": "Version",
|
||||||
"version_announcement_closing": "Din ven, Alex",
|
"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 <link>udgivelsesbemærkningerne</link> 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_message": "Hej! En ny version af Immich er tilgængelig. Brug venligst lidt tid på at læse <link>udgivelsesbemærkningerne</link> 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_ack": "Accepter",
|
||||||
"version_announcement_overlay_release_notes": "udgivelsesnoterne",
|
"version_announcement_overlay_release_notes": "udgivelsesnoterne",
|
||||||
"version_announcement_overlay_text_1": "Hej ven, der er en ny version af",
|
"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_2": ". Besøg venligst ",
|
||||||
@@ -1858,6 +2132,7 @@
|
|||||||
"view": "Se",
|
"view": "Se",
|
||||||
"view_album": "Se album",
|
"view_album": "Se album",
|
||||||
"view_all": "Se alle",
|
"view_all": "Se alle",
|
||||||
|
"view_all_button": "Vis alt",
|
||||||
"view_all_users": "Se alle brugere",
|
"view_all_users": "Se alle brugere",
|
||||||
"view_in_timeline": "Se på tidslinjen",
|
"view_in_timeline": "Se på tidslinjen",
|
||||||
"view_link": "Vis Link",
|
"view_link": "Vis Link",
|
||||||
@@ -1880,6 +2155,8 @@
|
|||||||
"years_ago": "{years, plural, one {# år} other {# år}} siden",
|
"years_ago": "{years, plural, one {# år} other {# år}} siden",
|
||||||
"yes": "Ja",
|
"yes": "Ja",
|
||||||
"you_dont_have_any_shared_links": "Du har ikke nogen delte links",
|
"you_dont_have_any_shared_links": "Du har ikke nogen delte links",
|
||||||
|
"your_activity": "Din aktivitet",
|
||||||
|
"your_map": "Dit kort",
|
||||||
"your_wifi_name": "Dit WiFi-navn",
|
"your_wifi_name": "Dit WiFi-navn",
|
||||||
"zoom_image": "Zoom billede"
|
"zoom_image": "Zoom billede"
|
||||||
}
|
}
|
||||||
515
i18n/de.json
515
i18n/de.json
File diff suppressed because it is too large
Load Diff
488
i18n/el.json
488
i18n/el.json
File diff suppressed because it is too large
Load Diff
465
i18n/en.json
465
i18n/en.json
File diff suppressed because it is too large
Load Diff
576
i18n/es.json
576
i18n/es.json
File diff suppressed because it is too large
Load Diff
338
i18n/et.json
338
i18n/et.json
@@ -4,7 +4,6 @@
|
|||||||
"account_settings": "Konto seaded",
|
"account_settings": "Konto seaded",
|
||||||
"acknowledge": "Sain aru",
|
"acknowledge": "Sain aru",
|
||||||
"action": "Tegevus",
|
"action": "Tegevus",
|
||||||
"action_common_update": "Uuenda",
|
|
||||||
"actions": "Tegevused",
|
"actions": "Tegevused",
|
||||||
"active": "Aktiivne",
|
"active": "Aktiivne",
|
||||||
"activity": "Aktiivsus",
|
"activity": "Aktiivsus",
|
||||||
@@ -14,7 +13,6 @@
|
|||||||
"add_a_location": "Lisa asukoht",
|
"add_a_location": "Lisa asukoht",
|
||||||
"add_a_name": "Lisa nimi",
|
"add_a_name": "Lisa nimi",
|
||||||
"add_a_title": "Lisa pealkiri",
|
"add_a_title": "Lisa pealkiri",
|
||||||
"add_endpoint": "Lisa lõpp-punkt",
|
|
||||||
"add_exclusion_pattern": "Lisa välistamismuster",
|
"add_exclusion_pattern": "Lisa välistamismuster",
|
||||||
"add_import_path": "Lisa imporditee",
|
"add_import_path": "Lisa imporditee",
|
||||||
"add_location": "Lisa asukoht",
|
"add_location": "Lisa asukoht",
|
||||||
@@ -24,8 +22,6 @@
|
|||||||
"add_photos": "Lisa fotosid",
|
"add_photos": "Lisa fotosid",
|
||||||
"add_to": "Lisa kohta…",
|
"add_to": "Lisa kohta…",
|
||||||
"add_to_album": "Lisa albumisse",
|
"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_shared_album": "Lisa jagatud albumisse",
|
"add_to_shared_album": "Lisa jagatud albumisse",
|
||||||
"add_url": "Lisa URL",
|
"add_url": "Lisa URL",
|
||||||
"added_to_archive": "Lisatud arhiivi",
|
"added_to_archive": "Lisatud arhiivi",
|
||||||
@@ -39,11 +35,11 @@
|
|||||||
"authentication_settings_disable_all": "Kas oled kindel, et soovid kõik sisselogimismeetodid välja lülitada? Sisselogimine lülitatakse täielikult välja.",
|
"authentication_settings_disable_all": "Kas oled kindel, et soovid kõik sisselogimismeetodid välja lülitada? Sisselogimine lülitatakse täielikult välja.",
|
||||||
"authentication_settings_reenable": "Et taas lubada, kasuta <link>serveri käsku</link>.",
|
"authentication_settings_reenable": "Et taas lubada, kasuta <link>serveri käsku</link>.",
|
||||||
"background_task_job": "Tausttegumid",
|
"background_task_job": "Tausttegumid",
|
||||||
"backup_database": "Loo andmebaasi tõmmis",
|
"backup_database": "Varunda andmebaas",
|
||||||
"backup_database_enable_description": "Luba andmebaasi tõmmised",
|
"backup_database_enable_description": "Luba andmebaasi varundamine",
|
||||||
"backup_keep_last_amount": "Eelmiste tõmmiste arv, mida alles hoida",
|
"backup_keep_last_amount": "Varukoopiate arv, mida alles hoida",
|
||||||
"backup_settings": "Andmebaasi tõmmiste seaded",
|
"backup_settings": "Varundamise seaded",
|
||||||
"backup_settings_description": "Halda andmebaasi tõmmiste seadeid. Märkus: Neid tööteid ei jälgita ning ebaõnnestumisest ei hoiatata.",
|
"backup_settings_description": "Halda andmebaasi varundamise seadeid",
|
||||||
"check_all": "Märgi kõik",
|
"check_all": "Märgi kõik",
|
||||||
"cleanup": "Koristus",
|
"cleanup": "Koristus",
|
||||||
"cleared_jobs": "Tööted eemaldatud: {job}",
|
"cleared_jobs": "Tööted eemaldatud: {job}",
|
||||||
@@ -171,6 +167,7 @@
|
|||||||
"no_pattern_added": "Mustreid ei ole",
|
"no_pattern_added": "Mustreid ei ole",
|
||||||
"note_apply_storage_label_previous_assets": "Märkus: Et rakendada talletussilt varem üleslaaditud üksustele, käivita",
|
"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!",
|
"note_cannot_be_changed_later": "MÄRKUS: Seda ei saa hiljem muuta!",
|
||||||
|
"note_unlimited_quota": "Märkus: Piiramatu kvoodi jaoks sisesta 0",
|
||||||
"notification_email_from_address": "Saatja aadress",
|
"notification_email_from_address": "Saatja aadress",
|
||||||
"notification_email_from_address_description": "Saatja e-posti aadress, näiteks: \"Immich Photo Server <noreply@example.com>\"",
|
"notification_email_from_address_description": "Saatja e-posti aadress, näiteks: \"Immich Photo Server <noreply@example.com>\"",
|
||||||
"notification_email_host_description": "E-posti serveri host (nt. smtp.immich.app)",
|
"notification_email_host_description": "E-posti serveri host (nt. smtp.immich.app)",
|
||||||
@@ -192,22 +189,26 @@
|
|||||||
"oauth_auto_register": "Automaatne registreerimine",
|
"oauth_auto_register": "Automaatne registreerimine",
|
||||||
"oauth_auto_register_description": "Registreeri uued kasutajad automaatselt OAuth abil sisselogimisel",
|
"oauth_auto_register_description": "Registreeri uued kasutajad automaatselt OAuth abil sisselogimisel",
|
||||||
"oauth_button_text": "Nupu tekst",
|
"oauth_button_text": "Nupu tekst",
|
||||||
"oauth_client_secret_description": "Nõutud, kui PKCE (Proof Key for Code Exchange) ei ole OAuth pakkuja poolt toetatud",
|
"oauth_client_id": "Kliendi ID",
|
||||||
|
"oauth_client_secret": "Kliendi saladus",
|
||||||
"oauth_enable_description": "Sisene OAuth abil",
|
"oauth_enable_description": "Sisene OAuth abil",
|
||||||
|
"oauth_issuer_url": "Väljastaja URL",
|
||||||
"oauth_mobile_redirect_uri": "Mobiilne ümbersuunamise URI",
|
"oauth_mobile_redirect_uri": "Mobiilne ümbersuunamise URI",
|
||||||
"oauth_mobile_redirect_uri_override": "Mobiilse ümbersuunamise URI ülekirjutamine",
|
"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_profile_signing_algorithm": "Profiili allkirjastamise algoritm",
|
||||||
|
"oauth_profile_signing_algorithm_description": "Algoritm, mida kasutatakse kasutajaprofiili allkirjastamiseks.",
|
||||||
|
"oauth_scope": "Skoop",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "Halda OAuth sisselogimise seadeid",
|
"oauth_settings_description": "Halda OAuth sisselogimise seadeid",
|
||||||
"oauth_settings_more_details": "Selle funktsiooni kohta rohkem teada saamiseks loe <link>dokumentatsiooni</link>.",
|
"oauth_settings_more_details": "Selle funktsiooni kohta rohkem teada saamiseks loe <link>dokumentatsiooni</link>.",
|
||||||
|
"oauth_signing_algorithm": "Allkirjastamise algoritm",
|
||||||
"oauth_storage_label_claim": "Talletussildi väide",
|
"oauth_storage_label_claim": "Talletussildi väide",
|
||||||
"oauth_storage_label_claim_description": "Sea kasutaja talletussildiks automaatselt selle väite väärtus.",
|
"oauth_storage_label_claim_description": "Sea kasutaja talletussildiks automaatselt selle väite väärtus.",
|
||||||
"oauth_storage_quota_claim": "Talletuskvoodi väide",
|
"oauth_storage_quota_claim": "Talletuskvoodi väide",
|
||||||
"oauth_storage_quota_claim_description": "Sea kasutaja talletuskvoodiks automaatselt selle väite väärtus.",
|
"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": "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 (piiramatu kvoodi jaoks sisesta 0).",
|
||||||
"oauth_timeout": "Päringu ajalõpp",
|
|
||||||
"oauth_timeout_description": "Päringute ajalõpp millisekundites",
|
|
||||||
"offline_paths": "Ühenduseta failiteed",
|
"offline_paths": "Ühenduseta failiteed",
|
||||||
"offline_paths_description": "Need tulemused võivad olla põhjustatud manuaalselt kustutatud failidest, mis ei ole osa välisest kogust.",
|
"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_enable_description": "Logi sisse e-posti aadressi ja parooliga",
|
||||||
@@ -367,19 +368,6 @@
|
|||||||
"admin_password": "Administraatori parool",
|
"admin_password": "Administraatori parool",
|
||||||
"administration": "Administratsioon",
|
"administration": "Administratsioon",
|
||||||
"advanced": "Täpsemad valikud",
|
"advanced": "Täpsemad valikud",
|
||||||
"advanced_settings_enable_alternate_media_filter_subtitle": "Kasuta seda valikut, et filtreerida sünkroonimise ajal üksuseid alternatiivsete kriteeriumite alusel. Proovi seda ainult siis, kui rakendusel on probleeme kõigi albumite tuvastamisega.",
|
|
||||||
"advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAALNE] Kasuta alternatiivset seadme albumi sünkroonimise filtrit",
|
|
||||||
"advanced_settings_log_level_title": "Logimistase: {}",
|
|
||||||
"advanced_settings_prefer_remote_subtitle": "Mõned seadmed laadivad seadmes olevate üksuste pisipilte piinavalt aeglaselt. Aktiveeri see seadistus, et laadida selle asemel kaugpilte.",
|
|
||||||
"advanced_settings_prefer_remote_title": "Eelista kaugpilte",
|
|
||||||
"advanced_settings_proxy_headers_subtitle": "Määra vaheserveri päised, mida Immich peaks iga päringuga saatma",
|
|
||||||
"advanced_settings_proxy_headers_title": "Vaheserveri päised",
|
|
||||||
"advanced_settings_self_signed_ssl_subtitle": "Jätab serveri lõpp-punkti SSL-sertifikaadi kontrolli vahele. Nõutud endasigneeritud sertifikaatide jaoks.",
|
|
||||||
"advanced_settings_self_signed_ssl_title": "Luba endasigneeritud SSL-sertifikaadid",
|
|
||||||
"advanced_settings_sync_remote_deletions_subtitle": "Kustuta või taasta üksus selles seadmes automaatself, kui sama tegevus toimub veebis",
|
|
||||||
"advanced_settings_sync_remote_deletions_title": "Sünkrooni kaugkustutamised [EKSPERIMENTAALNE]",
|
|
||||||
"advanced_settings_troubleshooting_subtitle": "Luba lisafunktsioonid tõrkeotsinguks",
|
|
||||||
"advanced_settings_troubleshooting_title": "Tõrkeotsing",
|
|
||||||
"age_months": "Vanus {months, plural, one {# kuu} other {# kuud}}",
|
"age_months": "Vanus {months, plural, one {# kuu} other {# kuud}}",
|
||||||
"age_year_months": "Vanus 1 aasta, {months, plural, one {# kuu} other {# kuud}}",
|
"age_year_months": "Vanus 1 aasta, {months, plural, one {# kuu} other {# kuud}}",
|
||||||
"age_years": "{years, plural, other {Vanus #}}",
|
"age_years": "{years, plural, other {Vanus #}}",
|
||||||
@@ -388,8 +376,6 @@
|
|||||||
"album_cover_updated": "Albumi kaanepilt muudetud",
|
"album_cover_updated": "Albumi kaanepilt muudetud",
|
||||||
"album_delete_confirmation": "Kas oled kindel, et soovid albumi {album} kustutada?",
|
"album_delete_confirmation": "Kas oled kindel, et soovid albumi {album} kustutada?",
|
||||||
"album_delete_confirmation_description": "Kui see album on jagatud, ei pääse teised kasutajad sellele enam ligi.",
|
"album_delete_confirmation_description": "Kui see album on jagatud, ei pääse teised kasutajad sellele enam ligi.",
|
||||||
"album_info_card_backup_album_excluded": "VÄLJA JÄETUD",
|
|
||||||
"album_info_card_backup_album_included": "LISATUD",
|
|
||||||
"album_info_updated": "Albumi info muudetud",
|
"album_info_updated": "Albumi info muudetud",
|
||||||
"album_leave": "Lahku albumist?",
|
"album_leave": "Lahku albumist?",
|
||||||
"album_leave_confirmation": "Kas oled kindel, et soovid albumist {album} lahkuda?",
|
"album_leave_confirmation": "Kas oled kindel, et soovid albumist {album} lahkuda?",
|
||||||
@@ -398,22 +384,10 @@
|
|||||||
"album_remove_user": "Eemalda kasutaja?",
|
"album_remove_user": "Eemalda kasutaja?",
|
||||||
"album_remove_user_confirmation": "Kas oled kindel, et soovid kasutaja {user} eemaldada?",
|
"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_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": "{} üksust",
|
|
||||||
"album_thumbnail_card_shared": " · Jagatud",
|
|
||||||
"album_thumbnail_shared_by": "Jagas {}",
|
|
||||||
"album_updated": "Album muudetud",
|
"album_updated": "Album muudetud",
|
||||||
"album_updated_setting_description": "Saa teavitus e-posti teel, kui jagatud albumis on uusi üksuseid",
|
"album_updated_setting_description": "Saa teavitus e-posti teel, kui jagatud albumis on uusi üksuseid",
|
||||||
"album_user_left": "Lahkutud albumist {album}",
|
"album_user_left": "Lahkutud albumist {album}",
|
||||||
"album_user_removed": "Kasutaja {user} eemaldatud",
|
"album_user_removed": "Kasutaja {user} eemaldatud",
|
||||||
"album_viewer_appbar_delete_confirm": "Kas oled kindel, et soovid selle albumi oma kontolt kustutada?",
|
|
||||||
"album_viewer_appbar_share_err_delete": "Albumi kustutamine ebaõnnestus",
|
|
||||||
"album_viewer_appbar_share_err_leave": "Albumist lahkumine ebaõnnestus",
|
|
||||||
"album_viewer_appbar_share_err_remove": "Üksuste albumist eemaldamisel tekkis probleeme",
|
|
||||||
"album_viewer_appbar_share_err_title": "Albumi pealkirja muutmine ebaõnnestus",
|
|
||||||
"album_viewer_appbar_share_leave": "Lahku albumist",
|
|
||||||
"album_viewer_appbar_share_to": "Jaga",
|
|
||||||
"album_viewer_page_share_add_users": "Lisa kasutajaid",
|
|
||||||
"album_with_link_access": "Luba kõigil, kellel on link, näha selle albumi fotosid ja isikuid.",
|
"album_with_link_access": "Luba kõigil, kellel on link, näha selle albumi fotosid ja isikuid.",
|
||||||
"albums": "Albumid",
|
"albums": "Albumid",
|
||||||
"albums_count": "{count, plural, one {{count, number} album} other {{count, number} albumit}}",
|
"albums_count": "{count, plural, one {{count, number} album} other {{count, number} albumit}}",
|
||||||
@@ -431,94 +405,42 @@
|
|||||||
"api_key_description": "Seda väärtust kuvatakse ainult üks kord. Kopeeri see enne akna sulgemist.",
|
"api_key_description": "Seda väärtust kuvatakse ainult üks kord. Kopeeri see enne akna sulgemist.",
|
||||||
"api_key_empty": "Su API võtme nimi ei tohiks olla tühi",
|
"api_key_empty": "Su API võtme nimi ei tohiks olla tühi",
|
||||||
"api_keys": "API võtmed",
|
"api_keys": "API võtmed",
|
||||||
"app_bar_signout_dialog_content": "Kas oled kindel, et soovid välja logida?",
|
|
||||||
"app_bar_signout_dialog_ok": "Jah",
|
|
||||||
"app_bar_signout_dialog_title": "Logi välja",
|
|
||||||
"app_settings": "Rakenduse seaded",
|
"app_settings": "Rakenduse seaded",
|
||||||
"appears_in": "Albumid",
|
"appears_in": "Albumid",
|
||||||
"archive": "Arhiiv",
|
"archive": "Arhiiv",
|
||||||
"archive_or_unarchive_photo": "Arhiveeri või taasta foto",
|
"archive_or_unarchive_photo": "Arhiveeri või taasta foto",
|
||||||
"archive_page_no_archived_assets": "Arhiveeritud üksuseid ei leitud",
|
|
||||||
"archive_page_title": "Arhiveeri ({})",
|
|
||||||
"archive_size": "Arhiivi suurus",
|
"archive_size": "Arhiivi suurus",
|
||||||
"archive_size_description": "Seadista arhiivi suurus allalaadimiseks (GiB)",
|
"archive_size_description": "Seadista arhiivi suurus allalaadimiseks (GiB)",
|
||||||
"archived": "Arhiveeritud",
|
|
||||||
"archived_count": "{count, plural, other {# arhiveeritud}}",
|
"archived_count": "{count, plural, other {# arhiveeritud}}",
|
||||||
"are_these_the_same_person": "Kas need on sama isik?",
|
"are_these_the_same_person": "Kas need on sama isik?",
|
||||||
"are_you_sure_to_do_this": "Kas oled kindel, et soovid seda teha?",
|
"are_you_sure_to_do_this": "Kas oled kindel, et soovid seda teha?",
|
||||||
"asset_action_delete_err_read_only": "Kirjutuskaitstud üksuseid ei saa kustutada, jätan vahele",
|
|
||||||
"asset_added_to_album": "Lisatud albumisse",
|
"asset_added_to_album": "Lisatud albumisse",
|
||||||
"asset_adding_to_album": "Albumisse lisamine…",
|
"asset_adding_to_album": "Albumisse lisamine…",
|
||||||
"asset_description_updated": "Üksuse kirjeldus on muudetud",
|
"asset_description_updated": "Üksuse kirjeldus on muudetud",
|
||||||
"asset_filename_is_offline": "Üksus {filename} ei ole kättesaadav",
|
"asset_filename_is_offline": "Üksus {filename} ei ole kättesaadav",
|
||||||
"asset_has_unassigned_faces": "Üksusel on seostamata nägusid",
|
"asset_has_unassigned_faces": "Üksusel on seostamata nägusid",
|
||||||
"asset_hashing": "Räsimine…",
|
"asset_hashing": "Räsimine…",
|
||||||
"asset_list_group_by_sub_title": "Grupeeri",
|
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "Dünaamiline asetus",
|
|
||||||
"asset_list_layout_settings_group_automatically": "Automaatne",
|
|
||||||
"asset_list_layout_settings_group_by": "Grupeeri üksused",
|
|
||||||
"asset_list_layout_settings_group_by_month_day": "Kuu + päev",
|
|
||||||
"asset_list_layout_sub_title": "Asetus",
|
|
||||||
"asset_list_settings_subtitle": "Fotoruudustiku paigutuse sätted",
|
|
||||||
"asset_list_settings_title": "Fotoruudustik",
|
|
||||||
"asset_offline": "Üksus pole kättesaadav",
|
"asset_offline": "Üksus pole kättesaadav",
|
||||||
"asset_offline_description": "Seda välise kogu üksust ei leitud kettalt. Abi saamiseks palun võta ühendust oma Immich'i administraatoriga.",
|
"asset_offline_description": "Seda välise kogu üksust ei leitud kettalt. Abi saamiseks palun võta ühendust oma Immich'i administraatoriga.",
|
||||||
"asset_restored_successfully": "Üksus edukalt taastatud",
|
|
||||||
"asset_skipped": "Vahele jäetud",
|
"asset_skipped": "Vahele jäetud",
|
||||||
"asset_skipped_in_trash": "Prügikastis",
|
"asset_skipped_in_trash": "Prügikastis",
|
||||||
"asset_uploaded": "Üleslaaditud",
|
"asset_uploaded": "Üleslaaditud",
|
||||||
"asset_uploading": "Üleslaadimine…",
|
"asset_uploading": "Üleslaadimine…",
|
||||||
"asset_viewer_settings_subtitle": "Halda galeriivaaturi seadeid",
|
|
||||||
"asset_viewer_settings_title": "Üksuste vaatur",
|
|
||||||
"assets": "Üksused",
|
"assets": "Üksused",
|
||||||
"assets_added_count": "{count, plural, one {# üksus} other {# üksust}} lisatud",
|
"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_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 <b>{name}</b>} other {uude albumisse}}",
|
"assets_added_to_name_count": "{count, plural, one {# üksus} other {# üksust}} lisatud {hasName, select, true {albumisse <b>{name}</b>} other {uude albumisse}}",
|
||||||
"assets_count": "{count, plural, one {# üksus} other {# üksust}}",
|
"assets_count": "{count, plural, one {# üksus} other {# üksust}}",
|
||||||
"assets_deleted_permanently": "{} üksus(t) jäädavalt kustutatud",
|
|
||||||
"assets_deleted_permanently_from_server": "{} üksus(t) Immich'i serverist jäädavalt kustutatud",
|
|
||||||
"assets_moved_to_trash_count": "{count, plural, one {# üksus} other {# üksust}} liigutatud prügikasti",
|
"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_permanently_deleted_count": "{count, plural, one {# üksus} other {# üksust}} jäädavalt kustutatud",
|
||||||
"assets_removed_count": "{count, plural, one {# üksus} other {# üksust}} eemaldatud",
|
"assets_removed_count": "{count, plural, one {# üksus} other {# üksust}} eemaldatud",
|
||||||
"assets_removed_permanently_from_device": "{} üksus(t) seadmest jäädavalt eemaldatud",
|
|
||||||
"assets_restore_confirmation": "Kas oled kindel, et soovid oma prügikasti liigutatud üksused taastada? Seda ei saa tagasi võtta! Pane tähele, et sel meetodil ei saa taastada ühenduseta üksuseid.",
|
"assets_restore_confirmation": "Kas oled kindel, et soovid oma prügikasti liigutatud üksused taastada? Seda ei saa tagasi võtta! Pane tähele, et sel meetodil ei saa taastada ühenduseta üksuseid.",
|
||||||
"assets_restored_count": "{count, plural, one {# üksus} other {# üksust}} taastatud",
|
"assets_restored_count": "{count, plural, one {# üksus} other {# üksust}} taastatud",
|
||||||
"assets_restored_successfully": "{} üksus(t) edukalt taastatud",
|
|
||||||
"assets_trashed": "{} üksus(t) liigutatud prügikasti",
|
|
||||||
"assets_trashed_count": "{count, plural, one {# üksus} other {# üksust}} liigutatud prügikasti",
|
"assets_trashed_count": "{count, plural, one {# üksus} other {# üksust}} liigutatud prügikasti",
|
||||||
"assets_trashed_from_server": "{} üksus(t) liigutatud Immich'i serveris prügikasti",
|
|
||||||
"assets_were_part_of_album_count": "{count, plural, one {Üksus oli} other {Üksused olid}} juba osa albumist",
|
"assets_were_part_of_album_count": "{count, plural, one {Üksus oli} other {Üksused olid}} juba osa albumist",
|
||||||
"authorized_devices": "Autoriseeritud seadmed",
|
"authorized_devices": "Autoriseeritud seadmed",
|
||||||
"automatic_endpoint_switching_subtitle": "Ühendu lokaalselt üle valitud WiFi-võrgu, kui see on saadaval, ja kasuta mujal alternatiivseid ühendusi",
|
|
||||||
"automatic_endpoint_switching_title": "Automaatne URL-i ümberlülitamine",
|
|
||||||
"back": "Tagasi",
|
"back": "Tagasi",
|
||||||
"back_close_deselect": "Tagasi, sulge või tühista valik",
|
"back_close_deselect": "Tagasi, sulge või tühista valik",
|
||||||
"backup_album_selection_page_select_albums": "Vali albumid",
|
|
||||||
"backup_album_selection_page_selection_info": "Valiku info",
|
|
||||||
"backup_album_selection_page_total_assets": "Unikaalseid üksuseid kokku",
|
|
||||||
"backup_all": "Kõik",
|
|
||||||
"backup_background_service_default_notification": "Uute üksuste kontrollimine…",
|
|
||||||
"backup_background_service_error_title": "Varundamise viga",
|
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Taustal varundamise kasutamiseks luba rakenduse taustal värskendamine: Seaded > Üldine > Rakenduse taustal värskendamine.",
|
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "Rakenduse taustal värskendamine keelatud",
|
|
||||||
"backup_controller_page_background_battery_info_link": "Näita mulle, kuidas",
|
|
||||||
"backup_controller_page_background_battery_info_ok": "OK",
|
|
||||||
"backup_controller_page_background_configure_error": "Taustateenuse seadistamine ebaõnnestus",
|
|
||||||
"backup_controller_page_background_description": "Lülita taustateenus sisse, et uusi üksuseid automaatselt varundada, ilma et peaks rakendust avama",
|
|
||||||
"backup_controller_page_background_is_off": "Automaatne varundamine on välja lülitatud",
|
|
||||||
"backup_controller_page_background_is_on": "Automaatne varundamine on sisse lülitatud",
|
|
||||||
"backup_controller_page_background_turn_off": "Lülita taustateenus välja",
|
|
||||||
"backup_controller_page_background_turn_on": "Lülita taustateenus sisse",
|
|
||||||
"backup_controller_page_background_wifi": "Ainult WiFi-võrgus",
|
|
||||||
"backup_controller_page_backup_sub": "Varundatud fotod ja videod",
|
|
||||||
"backup_controller_page_desc_backup": "Lülita sisse esiplaanil varundamine, et rakenduse avamisel uued üksused automaatselt serverisse üles laadida.",
|
|
||||||
"backup_controller_page_to_backup": "Albumid, mida varundada",
|
|
||||||
"backup_controller_page_total_sub": "Kõik unikaalsed fotod ja videod valitud albumitest",
|
|
||||||
"backup_err_only_album": "Ei saa ainsat albumit eemaldada",
|
|
||||||
"backup_info_card_assets": "üksused",
|
|
||||||
"backup_manual_cancelled": "Tühistatud",
|
|
||||||
"backup_manual_title": "Üleslaadimise staatus",
|
|
||||||
"backup_setting_subtitle": "Halda taustal ja esiplaanil üleslaadimise seadeid",
|
|
||||||
"backward": "Tagasi",
|
"backward": "Tagasi",
|
||||||
"birthdate_saved": "Sünnikuupäev salvestatud",
|
"birthdate_saved": "Sünnikuupäev salvestatud",
|
||||||
"birthdate_set_description": "Sünnikuupäeva kasutatakse isiku vanuse arvutamiseks foto tegemise hetkel.",
|
"birthdate_set_description": "Sünnikuupäeva kasutatakse isiku vanuse arvutamiseks foto tegemise hetkel.",
|
||||||
@@ -530,19 +452,11 @@
|
|||||||
"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_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.",
|
"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",
|
"buy": "Osta Immich",
|
||||||
"cache_settings_clear_cache_button": "Tühjenda puhver",
|
|
||||||
"cache_settings_statistics_album": "Kogu pisipildid",
|
|
||||||
"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_thumbnail_size": "Pisipiltide puhvri suurus ({} üksust)",
|
|
||||||
"camera": "Kaamera",
|
"camera": "Kaamera",
|
||||||
"camera_brand": "Kaamera mark",
|
"camera_brand": "Kaamera mark",
|
||||||
"camera_model": "Kaamera mudel",
|
"camera_model": "Kaamera mudel",
|
||||||
"cancel": "Katkesta",
|
"cancel": "Katkesta",
|
||||||
"cancel_search": "Katkesta otsing",
|
"cancel_search": "Katkesta otsing",
|
||||||
"canceled": "Tühistatud",
|
|
||||||
"cannot_merge_people": "Ei saa isikuid ühendada",
|
"cannot_merge_people": "Ei saa isikuid ühendada",
|
||||||
"cannot_undo_this_action": "Sa ei saa seda tagasi võtta!",
|
"cannot_undo_this_action": "Sa ei saa seda tagasi võtta!",
|
||||||
"cannot_update_the_description": "Kirjelduse muutmine ebaõnnestus",
|
"cannot_update_the_description": "Kirjelduse muutmine ebaõnnestus",
|
||||||
@@ -553,10 +467,6 @@
|
|||||||
"change_name_successfully": "Nimi edukalt muudetud",
|
"change_name_successfully": "Nimi edukalt muudetud",
|
||||||
"change_password": "Parooli muutmine",
|
"change_password": "Parooli muutmine",
|
||||||
"change_password_description": "See on su esimene kord süsteemi siseneda, või on tehtud taotlus parooli muutmiseks. Palun sisesta allpool uus parool.",
|
"change_password_description": "See on su esimene kord süsteemi siseneda, või on tehtud taotlus parooli muutmiseks. Palun sisesta allpool uus parool.",
|
||||||
"change_password_form_confirm_password": "Kinnita parool",
|
|
||||||
"change_password_form_new_password": "Uus parool",
|
|
||||||
"change_password_form_password_mismatch": "Paroolid ei klapi",
|
|
||||||
"change_password_form_reenter_new_password": "Korda uut parooli",
|
|
||||||
"change_your_password": "Muuda oma parooli",
|
"change_your_password": "Muuda oma parooli",
|
||||||
"changed_visibility_successfully": "Nähtavus muudetud",
|
"changed_visibility_successfully": "Nähtavus muudetud",
|
||||||
"check_all": "Märgi kõik",
|
"check_all": "Märgi kõik",
|
||||||
@@ -568,14 +478,6 @@
|
|||||||
"clear_all_recent_searches": "Tühjenda hiljutised otsingud",
|
"clear_all_recent_searches": "Tühjenda hiljutised otsingud",
|
||||||
"clear_message": "Tühjenda sõnum",
|
"clear_message": "Tühjenda sõnum",
|
||||||
"clear_value": "Tühjenda väärtus",
|
"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",
|
|
||||||
"client_cert_invalid_msg": "Vigane sertifikaadi fail või vale parool",
|
|
||||||
"client_cert_remove_msg": "Klientsertifikaat on eemaldatud",
|
|
||||||
"client_cert_subtitle": "Toetab ainult PKCS12 (.p12, .pfx) formaati. Sertifikaadi importimine/eemaldamine on saadaval ainult enne sisselogimist",
|
|
||||||
"client_cert_title": "SSL klientsertifikaat",
|
|
||||||
"clockwise": "Päripäeva",
|
"clockwise": "Päripäeva",
|
||||||
"close": "Sulge",
|
"close": "Sulge",
|
||||||
"collapse": "Peida",
|
"collapse": "Peida",
|
||||||
@@ -586,8 +488,6 @@
|
|||||||
"comment_options": "Kommentaari valikud",
|
"comment_options": "Kommentaari valikud",
|
||||||
"comments_and_likes": "Kommentaarid ja meeldimised",
|
"comments_and_likes": "Kommentaarid ja meeldimised",
|
||||||
"comments_are_disabled": "Kommentaarid on keelatud",
|
"comments_are_disabled": "Kommentaarid on keelatud",
|
||||||
"common_create_new_album": "Lisa uus album",
|
|
||||||
"completed": "Lõpetatud",
|
|
||||||
"confirm": "Kinnita",
|
"confirm": "Kinnita",
|
||||||
"confirm_admin_password": "Kinnita administraatori parool",
|
"confirm_admin_password": "Kinnita administraatori parool",
|
||||||
"confirm_delete_face": "Kas oled kindel, et soovid isiku {name} näo üksuselt kustutada?",
|
"confirm_delete_face": "Kas oled kindel, et soovid isiku {name} näo üksuselt kustutada?",
|
||||||
@@ -597,10 +497,6 @@
|
|||||||
"contain": "Mahuta ära",
|
"contain": "Mahuta ära",
|
||||||
"context": "Kontekst",
|
"context": "Kontekst",
|
||||||
"continue": "Jätka",
|
"continue": "Jätka",
|
||||||
"control_bottom_app_bar_create_new_album": "Lisa uus album",
|
|
||||||
"control_bottom_app_bar_delete_from_local": "Kustuta seadmest",
|
|
||||||
"control_bottom_app_bar_edit_location": "Muuda asukohta",
|
|
||||||
"control_bottom_app_bar_edit_time": "Muuda kuupäeva ja aega",
|
|
||||||
"copied_image_to_clipboard": "Pilt kopeeritud lõikelauale.",
|
"copied_image_to_clipboard": "Pilt kopeeritud lõikelauale.",
|
||||||
"copied_to_clipboard": "Kopeeritud lõikelauale!",
|
"copied_to_clipboard": "Kopeeritud lõikelauale!",
|
||||||
"copy_error": "Kopeeri viga",
|
"copy_error": "Kopeeri viga",
|
||||||
@@ -622,7 +518,6 @@
|
|||||||
"create_new_person": "Lisa uus isik",
|
"create_new_person": "Lisa uus isik",
|
||||||
"create_new_person_hint": "Seosta valitud üksused uue isikuga",
|
"create_new_person_hint": "Seosta valitud üksused uue isikuga",
|
||||||
"create_new_user": "Lisa uus kasutaja",
|
"create_new_user": "Lisa uus kasutaja",
|
||||||
"create_shared_album_page_share_select_photos": "Vali fotod",
|
|
||||||
"create_tag": "Lisa silt",
|
"create_tag": "Lisa silt",
|
||||||
"create_tag_description": "Lisa uus silt. Pesastatud siltide jaoks sisesta täielik tee koos kaldkriipsudega.",
|
"create_tag_description": "Lisa uus silt. Pesastatud siltide jaoks sisesta täielik tee koos kaldkriipsudega.",
|
||||||
"create_user": "Lisa kasutaja",
|
"create_user": "Lisa kasutaja",
|
||||||
@@ -647,23 +542,19 @@
|
|||||||
"delete": "Kustuta",
|
"delete": "Kustuta",
|
||||||
"delete_album": "Kustuta album",
|
"delete_album": "Kustuta album",
|
||||||
"delete_api_key_prompt": "Kas oled kindel, et soovid selle API võtme kustutada?",
|
"delete_api_key_prompt": "Kas oled kindel, et soovid selle API võtme kustutada?",
|
||||||
"delete_dialog_title": "Kustuta jäädavalt",
|
|
||||||
"delete_duplicates_confirmation": "Kas oled kindel, et soovid need duplikaadid jäädavalt kustutada?",
|
"delete_duplicates_confirmation": "Kas oled kindel, et soovid need duplikaadid jäädavalt kustutada?",
|
||||||
"delete_face": "Kustuta nägu",
|
"delete_face": "Kustuta nägu",
|
||||||
"delete_key": "Kustuta võti",
|
"delete_key": "Kustuta võti",
|
||||||
"delete_library": "Kustuta kogu",
|
"delete_library": "Kustuta kogu",
|
||||||
"delete_link": "Kustuta link",
|
"delete_link": "Kustuta link",
|
||||||
"delete_local_dialog_ok_backed_up_only": "Kustuta ainult varundatud",
|
|
||||||
"delete_others": "Kustuta teised",
|
"delete_others": "Kustuta teised",
|
||||||
"delete_shared_link": "Kustuta jagatud link",
|
"delete_shared_link": "Kustuta jagatud link",
|
||||||
"delete_shared_link_dialog_title": "Kustuta jagatud link",
|
|
||||||
"delete_tag": "Kustuta silt",
|
"delete_tag": "Kustuta silt",
|
||||||
"delete_tag_confirmation_prompt": "Kas oled kindel, et soovid sildi {tagName} kustutada?",
|
"delete_tag_confirmation_prompt": "Kas oled kindel, et soovid sildi {tagName} kustutada?",
|
||||||
"delete_user": "Kustuta kasutaja",
|
"delete_user": "Kustuta kasutaja",
|
||||||
"deleted_shared_link": "Jagatud link kustutatud",
|
"deleted_shared_link": "Jagatud link kustutatud",
|
||||||
"deletes_missing_assets": "Kustutab üksused, mis on kettalt puudu",
|
"deletes_missing_assets": "Kustutab üksused, mis on kettalt puudu",
|
||||||
"description": "Kirjeldus",
|
"description": "Kirjeldus",
|
||||||
"description_input_hint_text": "Lisa kirjeldus...",
|
|
||||||
"details": "Üksikasjad",
|
"details": "Üksikasjad",
|
||||||
"direction": "Suund",
|
"direction": "Suund",
|
||||||
"disabled": "Välja lülitatud",
|
"disabled": "Välja lülitatud",
|
||||||
@@ -680,20 +571,10 @@
|
|||||||
"documentation": "Dokumentatsioon",
|
"documentation": "Dokumentatsioon",
|
||||||
"done": "Tehtud",
|
"done": "Tehtud",
|
||||||
"download": "Laadi alla",
|
"download": "Laadi alla",
|
||||||
"download_canceled": "Allalaadimine katkestatud",
|
|
||||||
"download_complete": "Allalaadimine lõpetatud",
|
|
||||||
"download_enqueue": "Allalaadimine ootel",
|
|
||||||
"download_error": "Allalaadimise viga",
|
|
||||||
"download_failed": "Allalaadimine ebaõnnestus",
|
|
||||||
"download_finished": "Allalaadimine lõpetatud",
|
|
||||||
"download_include_embedded_motion_videos": "Manustatud videod",
|
"download_include_embedded_motion_videos": "Manustatud videod",
|
||||||
"download_include_embedded_motion_videos_description": "Lisa liikuvatesse fotodesse manustatud videod eraldi failidena",
|
"download_include_embedded_motion_videos_description": "Lisa liikuvatesse fotodesse manustatud videod eraldi failidena",
|
||||||
"download_paused": "Allalaadimine peatatud",
|
|
||||||
"download_settings": "Allalaadimine",
|
"download_settings": "Allalaadimine",
|
||||||
"download_settings_description": "Halda üksuste allalaadimise seadeid",
|
"download_settings_description": "Halda üksuste allalaadimise seadeid",
|
||||||
"download_started": "Allalaadimine alustatud",
|
|
||||||
"download_sucess": "Allalaadimine õnnestus",
|
|
||||||
"download_sucess_android": "Meediumid laaditi alla kataloogi DCIM/Immich",
|
|
||||||
"downloading": "Allalaadimine",
|
"downloading": "Allalaadimine",
|
||||||
"downloading_asset_filename": "Üksuse {filename} allalaadimine",
|
"downloading_asset_filename": "Üksuse {filename} allalaadimine",
|
||||||
"drop_files_to_upload": "Failide üleslaadimiseks sikuta need ükskõik kuhu",
|
"drop_files_to_upload": "Failide üleslaadimiseks sikuta need ükskõik kuhu",
|
||||||
@@ -712,7 +593,6 @@
|
|||||||
"edit_key": "Muuda võtit",
|
"edit_key": "Muuda võtit",
|
||||||
"edit_link": "Muuda linki",
|
"edit_link": "Muuda linki",
|
||||||
"edit_location": "Muuda asukohta",
|
"edit_location": "Muuda asukohta",
|
||||||
"edit_location_dialog_title": "Asukoht",
|
|
||||||
"edit_name": "Muuda nime",
|
"edit_name": "Muuda nime",
|
||||||
"edit_people": "Muuda isikuid",
|
"edit_people": "Muuda isikuid",
|
||||||
"edit_tag": "Muuda silti",
|
"edit_tag": "Muuda silti",
|
||||||
@@ -725,15 +605,12 @@
|
|||||||
"editor_crop_tool_h2_aspect_ratios": "Kuvasuhted",
|
"editor_crop_tool_h2_aspect_ratios": "Kuvasuhted",
|
||||||
"editor_crop_tool_h2_rotation": "Pööre",
|
"editor_crop_tool_h2_rotation": "Pööre",
|
||||||
"email": "E-post",
|
"email": "E-post",
|
||||||
"empty_folder": "See kaust on tühi",
|
|
||||||
"empty_trash": "Tühjenda prügikast",
|
"empty_trash": "Tühjenda prügikast",
|
||||||
"empty_trash_confirmation": "Kas oled kindel, et soovid prügikasti tühjendada? See eemaldab kõik seal olevad üksused Immich'ist jäädavalt.\nSeda tegevust ei saa tagasi võtta!",
|
"empty_trash_confirmation": "Kas oled kindel, et soovid prügikasti tühjendada? See eemaldab kõik seal olevad üksused Immich'ist jäädavalt.\nSeda tegevust ei saa tagasi võtta!",
|
||||||
"enable": "Luba",
|
"enable": "Luba",
|
||||||
"enabled": "Lubatud",
|
"enabled": "Lubatud",
|
||||||
"end_date": "Lõppkuupäev",
|
"end_date": "Lõppkuupäev",
|
||||||
"enter_wifi_name": "Sisesta WiFi-võrgu nimi",
|
|
||||||
"error": "Viga",
|
"error": "Viga",
|
||||||
"error_change_sort_album": "Albumi sorteerimisjärjestuse muutmine ebaõnnestus",
|
|
||||||
"error_delete_face": "Viga näo kustutamisel",
|
"error_delete_face": "Viga näo kustutamisel",
|
||||||
"error_loading_image": "Viga pildi laadimisel",
|
"error_loading_image": "Viga pildi laadimisel",
|
||||||
"error_title": "Viga - midagi läks valesti",
|
"error_title": "Viga - midagi läks valesti",
|
||||||
@@ -765,12 +642,10 @@
|
|||||||
"failed_to_keep_this_delete_others": "Selle üksuse säilitamine ja ülejäänute kustutamine ebaõnnestus",
|
"failed_to_keep_this_delete_others": "Selle üksuse säilitamine ja ülejäänute kustutamine ebaõnnestus",
|
||||||
"failed_to_load_asset": "Üksuse laadimine ebaõnnestus",
|
"failed_to_load_asset": "Üksuse laadimine ebaõnnestus",
|
||||||
"failed_to_load_assets": "Üksuste laadimine ebaõnnestus",
|
"failed_to_load_assets": "Üksuste laadimine ebaõnnestus",
|
||||||
"failed_to_load_notifications": "Teavituste laadimine ebaõnnestus",
|
|
||||||
"failed_to_load_people": "Isikute laadimine ebaõnnestus",
|
"failed_to_load_people": "Isikute laadimine ebaõnnestus",
|
||||||
"failed_to_remove_product_key": "Tootevõtme eemaldamine ebaõnnestus",
|
"failed_to_remove_product_key": "Tootevõtme eemaldamine ebaõnnestus",
|
||||||
"failed_to_stack_assets": "Üksuste virnastamine ebaõnnestus",
|
"failed_to_stack_assets": "Üksuste virnastamine ebaõnnestus",
|
||||||
"failed_to_unstack_assets": "Üksuste eraldamine ebaõnnestus",
|
"failed_to_unstack_assets": "Üksuste eraldamine ebaõnnestus",
|
||||||
"failed_to_update_notification_status": "Teavituste seisundi uuendamine ebaõnnestus",
|
|
||||||
"import_path_already_exists": "See imporditee on juba olemas.",
|
"import_path_already_exists": "See imporditee on juba olemas.",
|
||||||
"incorrect_email_or_password": "Vale e-posti aadress või parool",
|
"incorrect_email_or_password": "Vale e-posti aadress või parool",
|
||||||
"paths_validation_failed": "{paths, plural, one {# tee} other {# teed}} ei valideerunud",
|
"paths_validation_failed": "{paths, plural, one {# tee} other {# teed}} ei valideerunud",
|
||||||
@@ -866,14 +741,8 @@
|
|||||||
"unable_to_upload_file": "Faili üleslaadimine ebaõnnestus"
|
"unable_to_upload_file": "Faili üleslaadimine ebaõnnestus"
|
||||||
},
|
},
|
||||||
"exif": "Exif",
|
"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",
|
|
||||||
"exit_slideshow": "Sulge slaidiesitlus",
|
"exit_slideshow": "Sulge slaidiesitlus",
|
||||||
"expand_all": "Näita kõik",
|
"expand_all": "Näita kõik",
|
||||||
"experimental_settings_title": "Eksperimentaalne",
|
|
||||||
"expire_after": "Aegub",
|
"expire_after": "Aegub",
|
||||||
"expired": "Aegunud",
|
"expired": "Aegunud",
|
||||||
"expires_date": "Aegub {date}",
|
"expires_date": "Aegub {date}",
|
||||||
@@ -884,7 +753,6 @@
|
|||||||
"extension": "Laiend",
|
"extension": "Laiend",
|
||||||
"external": "Väline",
|
"external": "Väline",
|
||||||
"external_libraries": "Välised kogud",
|
"external_libraries": "Välised kogud",
|
||||||
"external_network_sheet_info": "Kui seade ei ole eelistatud WiFi-võrgus, ühendub rakendus serveriga allolevatest URL-idest esimese kättesaadava kaudu, alustades ülevalt",
|
|
||||||
"face_unassigned": "Seostamata",
|
"face_unassigned": "Seostamata",
|
||||||
"failed_to_load_assets": "Üksuste laadimine ebaõnnestus",
|
"failed_to_load_assets": "Üksuste laadimine ebaõnnestus",
|
||||||
"favorite": "Lemmik",
|
"favorite": "Lemmik",
|
||||||
@@ -900,8 +768,6 @@
|
|||||||
"filter_people": "Filtreeri isikuid",
|
"filter_people": "Filtreeri isikuid",
|
||||||
"find_them_fast": "Leia teda kiiresti nime järgi otsides",
|
"find_them_fast": "Leia teda kiiresti nime järgi otsides",
|
||||||
"fix_incorrect_match": "Paranda ebaõige vaste",
|
"fix_incorrect_match": "Paranda ebaõige vaste",
|
||||||
"folder": "Kaust",
|
|
||||||
"folder_not_found": "Kausta ei leitud",
|
|
||||||
"folders": "Kaustad",
|
"folders": "Kaustad",
|
||||||
"folders_feature_description": "Kaustavaate abil failisüsteemis olevate fotode ja videote sirvimine",
|
"folders_feature_description": "Kaustavaate abil failisüsteemis olevate fotode ja videote sirvimine",
|
||||||
"forward": "Edasi",
|
"forward": "Edasi",
|
||||||
@@ -917,15 +783,7 @@
|
|||||||
"group_owner": "Grupeeri omaniku kaupa",
|
"group_owner": "Grupeeri omaniku kaupa",
|
||||||
"group_places_by": "Grupeeri kohad...",
|
"group_places_by": "Grupeeri kohad...",
|
||||||
"group_year": "Grupeeri aasta kaupa",
|
"group_year": "Grupeeri aasta kaupa",
|
||||||
"haptic_feedback_switch": "Luba haptiline tagasiside",
|
|
||||||
"haptic_feedback_title": "Haptiline tagasiside",
|
|
||||||
"has_quota": "On kvoot",
|
"has_quota": "On kvoot",
|
||||||
"header_settings_add_header_tip": "Lisa päis",
|
|
||||||
"header_settings_field_validator_msg": "Väärtus ei saa olla tühi",
|
|
||||||
"header_settings_header_name_input": "Päise nimi",
|
|
||||||
"header_settings_header_value_input": "Päise väärtus",
|
|
||||||
"headers_settings_tile_subtitle": "Määra vaheserveri päised, mida rakendus peaks iga päringuga saatma",
|
|
||||||
"headers_settings_tile_title": "Kohandatud vaheserveri päised",
|
|
||||||
"hi_user": "Tere {name} ({email})",
|
"hi_user": "Tere {name} ({email})",
|
||||||
"hide_all_people": "Peida kõik isikud",
|
"hide_all_people": "Peida kõik isikud",
|
||||||
"hide_gallery": "Peida galerii",
|
"hide_gallery": "Peida galerii",
|
||||||
@@ -933,20 +791,8 @@
|
|||||||
"hide_password": "Peida parool",
|
"hide_password": "Peida parool",
|
||||||
"hide_person": "Peida isik",
|
"hide_person": "Peida isik",
|
||||||
"hide_unnamed_people": "Peida nimetud isikud",
|
"hide_unnamed_people": "Peida nimetud isikud",
|
||||||
"home_page_add_to_album_conflicts": "{added} üksust lisati albumisse {album}. {failed} üksust oli juba albumis.",
|
|
||||||
"home_page_add_to_album_err_local": "Lokaalseid üksuseid ei saa veel albumisse lisada, jätan vahele",
|
|
||||||
"home_page_add_to_album_success": "{added} üksust lisati albumisse {album}.",
|
|
||||||
"home_page_album_err_partner": "Partneri üksuseid ei saa veel albumisse lisada, jätan vahele",
|
|
||||||
"home_page_archive_err_local": "Lokaalseid üksuseid ei saa veel arhiveerida, jätan vahele",
|
|
||||||
"home_page_archive_err_partner": "Partneri üksuseid ei saa arhiveerida, jätan vahele",
|
|
||||||
"home_page_building_timeline": "Ajajoone koostamine",
|
|
||||||
"home_page_delete_err_partner": "Partneri üksuseid ei saa kustutada, jätan vahele",
|
|
||||||
"home_page_favorite_err_local": "Lokaalseid üksuseid ei saa lemmikuks märkida, jätan vahele",
|
|
||||||
"home_page_favorite_err_partner": "Partneri üksuseid ei saa lemmikuks märkida, jätan vahele",
|
|
||||||
"home_page_share_err_local": "Lokaalseid üksuseid ei saa lingiga jagada, jätan vahele",
|
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"hour": "Tund",
|
"hour": "Tund",
|
||||||
"ignore_icloud_photos": "Ignoreeri iCloud fotosid",
|
|
||||||
"image": "Pilt",
|
"image": "Pilt",
|
||||||
"image_alt_text_date": "{isVideo, select, true {Video} other {Pilt}} tehtud {date}",
|
"image_alt_text_date": "{isVideo, select, true {Video} other {Pilt}} tehtud {date}",
|
||||||
"image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} koos isikuga {person1}",
|
"image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} koos isikuga {person1}",
|
||||||
@@ -958,8 +804,6 @@
|
|||||||
"image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos isikutega {person1} ja {person2}",
|
"image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos isikutega {person1} ja {person2}",
|
||||||
"image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos isikutega {person1}, {person2} ja {person3}",
|
"image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos isikutega {person1}, {person2} ja {person3}",
|
||||||
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos {person1}, {person2} ja veel {additionalCount, number} isikuga",
|
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos {person1}, {person2} ja veel {additionalCount, number} isikuga",
|
||||||
"image_viewer_page_state_provider_download_started": "Allalaadimine alustatud",
|
|
||||||
"image_viewer_page_state_provider_download_success": "Allalaadimine õnnestus",
|
|
||||||
"immich_logo": "Immich'i logo",
|
"immich_logo": "Immich'i logo",
|
||||||
"immich_web_interface": "Immich'i veebiliides",
|
"immich_web_interface": "Immich'i veebiliides",
|
||||||
"import_from_json": "Impordi JSON-formaadist",
|
"import_from_json": "Impordi JSON-formaadist",
|
||||||
@@ -978,8 +822,6 @@
|
|||||||
"night_at_midnight": "Iga päev keskööl",
|
"night_at_midnight": "Iga päev keskööl",
|
||||||
"night_at_twoam": "Iga öö kell 2"
|
"night_at_twoam": "Iga öö kell 2"
|
||||||
},
|
},
|
||||||
"invalid_date": "Vigane kuupäev",
|
|
||||||
"invalid_date_format": "Vigane kuupäevaformaat",
|
|
||||||
"invite_people": "Kutsu inimesi",
|
"invite_people": "Kutsu inimesi",
|
||||||
"invite_to_album": "Kutsu albumisse",
|
"invite_to_album": "Kutsu albumisse",
|
||||||
"items_count": "{count, plural, one {# üksus} other {# üksust}}",
|
"items_count": "{count, plural, one {# üksus} other {# üksust}}",
|
||||||
@@ -1000,9 +842,6 @@
|
|||||||
"level": "Tase",
|
"level": "Tase",
|
||||||
"library": "Kogu",
|
"library": "Kogu",
|
||||||
"library_options": "Kogu seaded",
|
"library_options": "Kogu seaded",
|
||||||
"library_page_new_album": "Uus album",
|
|
||||||
"library_page_sort_asset_count": "Üksuste arv",
|
|
||||||
"library_page_sort_title": "Albumi pealkiri",
|
|
||||||
"light": "Hele",
|
"light": "Hele",
|
||||||
"like_deleted": "Meeldimine kustutatud",
|
"like_deleted": "Meeldimine kustutatud",
|
||||||
"link_motion_video": "Lingi liikuv video",
|
"link_motion_video": "Lingi liikuv video",
|
||||||
@@ -1012,29 +851,12 @@
|
|||||||
"list": "Loend",
|
"list": "Loend",
|
||||||
"loading": "Laadimine",
|
"loading": "Laadimine",
|
||||||
"loading_search_results_failed": "Otsitulemuste laadimine ebaõnnestus",
|
"loading_search_results_failed": "Otsitulemuste laadimine ebaõnnestus",
|
||||||
"local_network_sheet_info": "Rakendus ühendub valitud Wi-Fi võrgus olles serveriga selle URL-i kaudu",
|
|
||||||
"location_permission_content": "Automaatseks ümberlülitumiseks vajab Immich täpse asukoha luba, et saaks lugeda aktiivse WiFi-võrgu nime",
|
|
||||||
"location_picker_choose_on_map": "Vali kaardil",
|
|
||||||
"log_out": "Logi välja",
|
"log_out": "Logi välja",
|
||||||
"log_out_all_devices": "Logi kõigist seadmetest välja",
|
"log_out_all_devices": "Logi kõigist seadmetest välja",
|
||||||
"logged_out_all_devices": "Kõigist seadmetest välja logitud",
|
"logged_out_all_devices": "Kõigist seadmetest välja logitud",
|
||||||
"logged_out_device": "Seadmest välja logitud",
|
"logged_out_device": "Seadmest välja logitud",
|
||||||
"login": "Logi sisse",
|
"login": "Logi sisse",
|
||||||
"login_form_back_button_text": "Tagasi",
|
|
||||||
"login_form_email_hint": "sinunimi@email.com",
|
|
||||||
"login_form_endpoint_hint": "http://serveri-ip:port",
|
|
||||||
"login_form_endpoint_url": "Serveri lõpp-punkti URL",
|
|
||||||
"login_form_err_http": "Palun täpsusta http:// või https://",
|
|
||||||
"login_form_err_invalid_email": "Vigane e-posti aadress",
|
|
||||||
"login_form_err_invalid_url": "Vigane URL",
|
|
||||||
"login_form_err_leading_whitespace": "Eelnevad tühikud",
|
|
||||||
"login_form_err_trailing_whitespace": "Järgnevad tühikud",
|
|
||||||
"login_form_password_hint": "parool",
|
|
||||||
"login_form_save_login": "Jää sisselogituks",
|
|
||||||
"login_form_server_empty": "Sisesta serveri URL.",
|
|
||||||
"login_form_server_error": "Serveriga ühendumine ebaõnnestus.",
|
|
||||||
"login_has_been_disabled": "Sisselogimine on keelatud.",
|
"login_has_been_disabled": "Sisselogimine on keelatud.",
|
||||||
"login_password_changed_success": "Parool edukalt uuendatud",
|
|
||||||
"logout_all_device_confirmation": "Kas oled kindel, et soovid kõigist seadmetest välja logida?",
|
"logout_all_device_confirmation": "Kas oled kindel, et soovid kõigist seadmetest välja logida?",
|
||||||
"logout_this_device_confirmation": "Kas oled kindel, et soovid sellest seadmest välja logida?",
|
"logout_this_device_confirmation": "Kas oled kindel, et soovid sellest seadmest välja logida?",
|
||||||
"longitude": "Pikkuskraad",
|
"longitude": "Pikkuskraad",
|
||||||
@@ -1052,27 +874,13 @@
|
|||||||
"manage_your_devices": "Halda oma autenditud seadmeid",
|
"manage_your_devices": "Halda oma autenditud seadmeid",
|
||||||
"manage_your_oauth_connection": "Halda oma OAuth ühendust",
|
"manage_your_oauth_connection": "Halda oma OAuth ühendust",
|
||||||
"map": "Kaart",
|
"map": "Kaart",
|
||||||
"map_assets_in_bound": "{} foto",
|
|
||||||
"map_assets_in_bounds": "{} fotot",
|
|
||||||
"map_location_dialog_yes": "Jah",
|
|
||||||
"map_location_picker_page_use_location": "Kasuta seda asukohta",
|
|
||||||
"map_marker_for_images": "Kaardimarker kohas {city}, {country} tehtud piltide jaoks",
|
"map_marker_for_images": "Kaardimarker kohas {city}, {country} tehtud piltide jaoks",
|
||||||
"map_marker_with_image": "Kaardimarker pildiga",
|
"map_marker_with_image": "Kaardimarker pildiga",
|
||||||
"map_settings": "Kaardi seaded",
|
"map_settings": "Kaardi seaded",
|
||||||
"map_settings_date_range_option_day": "Viimased 24 tundi",
|
|
||||||
"map_settings_date_range_option_days": "Viimased {} päeva",
|
|
||||||
"map_settings_date_range_option_year": "Viimane aasta",
|
|
||||||
"map_settings_date_range_option_years": "Viimased {} aastat",
|
|
||||||
"map_settings_dialog_title": "Kaardi seaded",
|
|
||||||
"mark_all_as_read": "Märgi kõik loetuks",
|
|
||||||
"mark_as_read": "Märgi loetuks",
|
|
||||||
"marked_all_as_read": "Kõik märgiti loetuks",
|
|
||||||
"matches": "Ühtivad failid",
|
"matches": "Ühtivad failid",
|
||||||
"media_type": "Meediumi tüüp",
|
"media_type": "Meedia tüüp",
|
||||||
"memories": "Mälestused",
|
"memories": "Mälestused",
|
||||||
"memories_setting_description": "Halda, mida sa oma mälestustes näed",
|
"memories_setting_description": "Halda, mida sa oma mälestustes näed",
|
||||||
"memories_year_ago": "Aasta tagasi",
|
|
||||||
"memories_years_ago": "{} aastat tagasi",
|
|
||||||
"memory": "Mälestus",
|
"memory": "Mälestus",
|
||||||
"memory_lane_title": "Mälestus {title}",
|
"memory_lane_title": "Mälestus {title}",
|
||||||
"menu": "Menüü",
|
"menu": "Menüü",
|
||||||
@@ -1087,19 +895,12 @@
|
|||||||
"missing": "Puuduvad",
|
"missing": "Puuduvad",
|
||||||
"model": "Mudel",
|
"model": "Mudel",
|
||||||
"month": "Kuu",
|
"month": "Kuu",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
|
||||||
"more": "Rohkem",
|
"more": "Rohkem",
|
||||||
"moved_to_archive": "{count, plural, one {# üksus} other {# üksust}} liigutatud arhiivi",
|
|
||||||
"moved_to_library": "{count, plural, one {# üksus} other {# üksust}} liigutatud kogusse",
|
|
||||||
"moved_to_trash": "Liigutatud prügikasti",
|
"moved_to_trash": "Liigutatud prügikasti",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Kirjutuskaitsega üksus(t)e kuupäeva ei saa muuta, jätan vahele",
|
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Kirjutuskaitsega üksus(t)e asukohta ei saa muuta, jätan vahele",
|
|
||||||
"mute_memories": "Vaigista mälestused",
|
"mute_memories": "Vaigista mälestused",
|
||||||
"my_albums": "Minu albumid",
|
"my_albums": "Minu albumid",
|
||||||
"name": "Nimi",
|
"name": "Nimi",
|
||||||
"name_or_nickname": "Nimi või hüüdnimi",
|
"name_or_nickname": "Nimi või hüüdnimi",
|
||||||
"networking_settings": "Võrguühendus",
|
|
||||||
"networking_subtitle": "Halda serveri lõpp-punkti seadeid",
|
|
||||||
"never": "Mitte kunagi",
|
"never": "Mitte kunagi",
|
||||||
"new_album": "Uus album",
|
"new_album": "Uus album",
|
||||||
"new_api_key": "Uus API võti",
|
"new_api_key": "Uus API võti",
|
||||||
@@ -1122,17 +923,14 @@
|
|||||||
"no_favorites_message": "Lisa lemmikud, et oma parimaid fotosid ja videosid kiiresti leida",
|
"no_favorites_message": "Lisa lemmikud, et oma parimaid fotosid ja videosid kiiresti leida",
|
||||||
"no_libraries_message": "Lisa väline kogu oma fotode ja videote vaatamiseks",
|
"no_libraries_message": "Lisa väline kogu oma fotode ja videote vaatamiseks",
|
||||||
"no_name": "Nimetu",
|
"no_name": "Nimetu",
|
||||||
"no_notifications": "Teavitusi pole",
|
|
||||||
"no_places": "Kohti ei ole",
|
"no_places": "Kohti ei ole",
|
||||||
"no_results": "Vasteid pole",
|
"no_results": "Vasteid pole",
|
||||||
"no_results_description": "Proovi sünonüümi või üldisemat märksõna",
|
"no_results_description": "Proovi sünonüümi või üldisemat märksõna",
|
||||||
"no_shared_albums_message": "Lisa album, et fotosid ja videosid teistega jagada",
|
"no_shared_albums_message": "Lisa album, et fotosid ja videosid teistega jagada",
|
||||||
"not_in_any_album": "Pole üheski albumis",
|
"not_in_any_album": "Pole üheski albumis",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "Märkus: Et rakendada talletussilt varem üleslaaditud üksustele, käivita",
|
"note_apply_storage_label_to_previously_uploaded assets": "Märkus: Et rakendada talletussilt varem üleslaaditud üksustele, käivita",
|
||||||
|
"note_unlimited_quota": "Märkus: Piiramatu kvoodi jaoks sisesta 0",
|
||||||
"notes": "Märkused",
|
"notes": "Märkused",
|
||||||
"notification_permission_list_tile_content": "Anna luba teavituste saatmiseks.",
|
|
||||||
"notification_permission_list_tile_enable_button": "Luba teavitused",
|
|
||||||
"notification_permission_list_tile_title": "Teavituste luba",
|
|
||||||
"notification_toggle_setting_description": "Luba e-posti teel teavitused",
|
"notification_toggle_setting_description": "Luba e-posti teel teavitused",
|
||||||
"notifications": "Teavitused",
|
"notifications": "Teavitused",
|
||||||
"notifications_setting_description": "Halda teavitusi",
|
"notifications_setting_description": "Halda teavitusi",
|
||||||
@@ -1143,7 +941,6 @@
|
|||||||
"offline_paths_description": "Need tulemused võivad olla põhjustatud manuaalselt kustutatud failidest, mis ei ole osa välisest kogust.",
|
"offline_paths_description": "Need tulemused võivad olla põhjustatud manuaalselt kustutatud failidest, mis ei ole osa välisest kogust.",
|
||||||
"ok": "Ok",
|
"ok": "Ok",
|
||||||
"oldest_first": "Vanemad eespool",
|
"oldest_first": "Vanemad eespool",
|
||||||
"on_this_device": "Sellel seadmel",
|
|
||||||
"onboarding": "Kasutuselevõtt",
|
"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_privacy_description": "Järgnevad (valikulised) funktsioonid sõltuvad välistest teenustest ning neid saab igal ajal administraatori seadetes välja lülitada.",
|
||||||
"onboarding_theme_description": "Vali oma serverile värviteema. Saad seda hiljem seadetes muuta.",
|
"onboarding_theme_description": "Vali oma serverile värviteema. Saad seda hiljem seadetes muuta.",
|
||||||
@@ -1151,7 +948,6 @@
|
|||||||
"onboarding_welcome_user": "Tere tulemast, {user}",
|
"onboarding_welcome_user": "Tere tulemast, {user}",
|
||||||
"online": "Ühendatud",
|
"online": "Ühendatud",
|
||||||
"only_favorites": "Ainult lemmikud",
|
"only_favorites": "Ainult lemmikud",
|
||||||
"open": "Ava",
|
|
||||||
"open_in_map_view": "Ava kaardi vaates",
|
"open_in_map_view": "Ava kaardi vaates",
|
||||||
"open_in_openstreetmap": "Ava OpenStreetMap",
|
"open_in_openstreetmap": "Ava OpenStreetMap",
|
||||||
"open_the_search_filters": "Ava otsingufiltrid",
|
"open_the_search_filters": "Ava otsingufiltrid",
|
||||||
@@ -1168,10 +964,6 @@
|
|||||||
"partner_can_access": "{partner} pääseb ligi",
|
"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_assets": "Kõik su fotod ja videod, välja arvatud arhiveeritud ja kustutatud",
|
||||||
"partner_can_access_location": "Asukohad, kus su fotod tehti",
|
"partner_can_access_location": "Asukohad, kus su fotod tehti",
|
||||||
"partner_list_user_photos": "Kasutaja {user} fotod",
|
|
||||||
"partner_list_view_all": "Vaata kõiki",
|
|
||||||
"partner_page_partner_add_failed": "Partneri lisamine ebaõnnestus",
|
|
||||||
"partner_page_select_partner": "Vali partner",
|
|
||||||
"partner_sharing": "Partneriga jagamine",
|
"partner_sharing": "Partneriga jagamine",
|
||||||
"partners": "Partnerid",
|
"partners": "Partnerid",
|
||||||
"password": "Parool",
|
"password": "Parool",
|
||||||
@@ -1200,8 +992,6 @@
|
|||||||
"permanently_delete_assets_prompt": "Kas oled kindel, et soovid {count, plural, one {selle üksuse} other {need <b>#</b> üksust}} jäädavalt kustutada? Sellega eemaldatakse {count, plural, one {see} other {need}} ka oma albumi(te)st.",
|
"permanently_delete_assets_prompt": "Kas oled kindel, et soovid {count, plural, one {selle üksuse} other {need <b>#</b> ü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_asset": "Üksus jäädavalt kustutatud",
|
||||||
"permanently_deleted_assets_count": "{count, plural, one {# üksus} other {# üksust}} jäädavalt kustutatud",
|
"permanently_deleted_assets_count": "{count, plural, one {# üksus} other {# üksust}} jäädavalt kustutatud",
|
||||||
"permission_onboarding_back": "Tagasi",
|
|
||||||
"permission_onboarding_continue_anyway": "Jätka sellegipoolest",
|
|
||||||
"person": "Isik",
|
"person": "Isik",
|
||||||
"person_birthdate": "Sündinud {date}",
|
"person_birthdate": "Sündinud {date}",
|
||||||
"person_hidden": "{name}{hidden, select, true { (peidetud)} other {}}",
|
"person_hidden": "{name}{hidden, select, true { (peidetud)} other {}}",
|
||||||
@@ -1219,8 +1009,6 @@
|
|||||||
"play_motion_photo": "Esita liikuv foto",
|
"play_motion_photo": "Esita liikuv foto",
|
||||||
"play_or_pause_video": "Esita või peata video",
|
"play_or_pause_video": "Esita või peata video",
|
||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preferences_settings_subtitle": "Halda rakenduse eelistusi",
|
|
||||||
"preferences_settings_title": "Eelistused",
|
|
||||||
"preset": "Eelseadistus",
|
"preset": "Eelseadistus",
|
||||||
"preview": "Eelvaade",
|
"preview": "Eelvaade",
|
||||||
"previous": "Eelmine",
|
"previous": "Eelmine",
|
||||||
@@ -1228,8 +1016,6 @@
|
|||||||
"previous_or_next_photo": "Eelmine või järgmine foto",
|
"previous_or_next_photo": "Eelmine või järgmine foto",
|
||||||
"primary": "Peamine",
|
"primary": "Peamine",
|
||||||
"privacy": "Privaatsus",
|
"privacy": "Privaatsus",
|
||||||
"profile_drawer_app_logs": "Logid",
|
|
||||||
"profile_drawer_github": "GitHub",
|
|
||||||
"profile_image_of_user": "Kasutaja {user} profiilipilt",
|
"profile_image_of_user": "Kasutaja {user} profiilipilt",
|
||||||
"profile_picture_set": "Profiilipilt määratud.",
|
"profile_picture_set": "Profiilipilt määratud.",
|
||||||
"public_album": "Avalik album",
|
"public_album": "Avalik album",
|
||||||
@@ -1279,8 +1065,6 @@
|
|||||||
"recent": "Hiljutine",
|
"recent": "Hiljutine",
|
||||||
"recent-albums": "Hiljutised albumid",
|
"recent-albums": "Hiljutised albumid",
|
||||||
"recent_searches": "Hiljutised otsingud",
|
"recent_searches": "Hiljutised otsingud",
|
||||||
"recently_added": "Hiljuti lisatud",
|
|
||||||
"recently_added_page_title": "Hiljuti lisatud",
|
|
||||||
"refresh": "Värskenda",
|
"refresh": "Värskenda",
|
||||||
"refresh_encoded_videos": "Värskenda kodeeritud videod",
|
"refresh_encoded_videos": "Värskenda kodeeritud videod",
|
||||||
"refresh_faces": "Värskenda näod",
|
"refresh_faces": "Värskenda näod",
|
||||||
@@ -1337,7 +1121,6 @@
|
|||||||
"role_editor": "Muutja",
|
"role_editor": "Muutja",
|
||||||
"role_viewer": "Vaataja",
|
"role_viewer": "Vaataja",
|
||||||
"save": "Salvesta",
|
"save": "Salvesta",
|
||||||
"save_to_gallery": "Salvesta galeriisse",
|
|
||||||
"saved_api_key": "API võti salvestatud",
|
"saved_api_key": "API võti salvestatud",
|
||||||
"saved_profile": "Profiil salvestatud",
|
"saved_profile": "Profiil salvestatud",
|
||||||
"saved_settings": "Seaded salvestatud",
|
"saved_settings": "Seaded salvestatud",
|
||||||
@@ -1357,33 +1140,14 @@
|
|||||||
"search_camera_model": "Otsi kaamera mudelit...",
|
"search_camera_model": "Otsi kaamera mudelit...",
|
||||||
"search_city": "Otsi linna...",
|
"search_city": "Otsi linna...",
|
||||||
"search_country": "Otsi riiki...",
|
"search_country": "Otsi riiki...",
|
||||||
"search_filter_apply": "Rakenda filter",
|
|
||||||
"search_filter_camera_title": "Vali kaamera tüüp",
|
|
||||||
"search_filter_date": "Kuupäev",
|
|
||||||
"search_filter_date_interval": "{start} kuni {end}",
|
|
||||||
"search_filter_date_title": "Vali kuupäevavahemik",
|
|
||||||
"search_filter_display_options": "Kuva valikud",
|
|
||||||
"search_filter_filename": "Otsi failinime alusel",
|
|
||||||
"search_filter_location": "Asukoht",
|
|
||||||
"search_filter_location_title": "Vali asukoht",
|
|
||||||
"search_filter_media_type": "Meediumi tüüp",
|
|
||||||
"search_filter_media_type_title": "Vali meediumi tüüp",
|
|
||||||
"search_filter_people_title": "Vali isikud",
|
|
||||||
"search_for": "Otsi",
|
"search_for": "Otsi",
|
||||||
"search_for_existing_person": "Otsi olemasolevat isikut",
|
"search_for_existing_person": "Otsi olemasolevat isikut",
|
||||||
"search_no_people": "Isikuid ei ole",
|
"search_no_people": "Isikuid ei ole",
|
||||||
"search_no_people_named": "Ei ole isikuid nimega \"{name}\"",
|
"search_no_people_named": "Ei ole isikuid nimega \"{name}\"",
|
||||||
"search_options": "Otsingu valikud",
|
"search_options": "Otsingu valikud",
|
||||||
"search_page_categories": "Kategooriad",
|
|
||||||
"search_page_screenshots": "Ekraanipildid",
|
|
||||||
"search_page_search_photos_videos": "Otsi oma fotosid ja videosid",
|
|
||||||
"search_page_selfies": "Selfid",
|
|
||||||
"search_page_things": "Asjad",
|
|
||||||
"search_page_view_all_button": "Vaata kõiki",
|
|
||||||
"search_people": "Otsi inimesi",
|
"search_people": "Otsi inimesi",
|
||||||
"search_places": "Otsi kohti",
|
"search_places": "Otsi kohti",
|
||||||
"search_rating": "Otsi hinnangu järgi...",
|
"search_rating": "Otsi hinnangu järgi...",
|
||||||
"search_result_page_new_search_hint": "Uus otsing",
|
|
||||||
"search_settings": "Otsi seadeid",
|
"search_settings": "Otsi seadeid",
|
||||||
"search_state": "Otsi osariiki...",
|
"search_state": "Otsi osariiki...",
|
||||||
"search_tags": "Otsi silte...",
|
"search_tags": "Otsi silte...",
|
||||||
@@ -1404,17 +1168,12 @@
|
|||||||
"select_keep_all": "Vali jäta kõik alles",
|
"select_keep_all": "Vali jäta kõik alles",
|
||||||
"select_library_owner": "Vali kogu omanik",
|
"select_library_owner": "Vali kogu omanik",
|
||||||
"select_new_face": "Vali uus nägu",
|
"select_new_face": "Vali uus nägu",
|
||||||
"select_person_to_tag": "Vali sildistamiseks isik",
|
|
||||||
"select_photos": "Vali fotod",
|
"select_photos": "Vali fotod",
|
||||||
"select_trash_all": "Vali kõik prügikasti",
|
"select_trash_all": "Vali kõik prügikasti",
|
||||||
"select_user_for_sharing_page_err_album": "Albumi lisamine ebaõnnestus",
|
|
||||||
"selected": "Valitud",
|
"selected": "Valitud",
|
||||||
"selected_count": "{count, plural, other {# valitud}}",
|
"selected_count": "{count, plural, other {# valitud}}",
|
||||||
"send_message": "Saada sõnum",
|
"send_message": "Saada sõnum",
|
||||||
"send_welcome_email": "Saada tervituskiri",
|
"send_welcome_email": "Saada tervituskiri",
|
||||||
"server_endpoint": "Serveri lõpp-punkt",
|
|
||||||
"server_info_box_app_version": "Rakenduse versioon",
|
|
||||||
"server_info_box_server_url": "Serveri URL",
|
|
||||||
"server_offline": "Serveriga ühendus puudub",
|
"server_offline": "Serveriga ühendus puudub",
|
||||||
"server_online": "Server ühendatud",
|
"server_online": "Server ühendatud",
|
||||||
"server_stats": "Serveri statistika",
|
"server_stats": "Serveri statistika",
|
||||||
@@ -1426,70 +1185,22 @@
|
|||||||
"set_date_of_birth": "Määra sünnikuupäev",
|
"set_date_of_birth": "Määra sünnikuupäev",
|
||||||
"set_profile_picture": "Sea profiilipilt",
|
"set_profile_picture": "Sea profiilipilt",
|
||||||
"set_slideshow_to_fullscreen": "Kuva slaidiesitlus täisekraanil",
|
"set_slideshow_to_fullscreen": "Kuva slaidiesitlus täisekraanil",
|
||||||
"setting_image_viewer_help": "Detailivaatur laadib kõigepealt väikese pisipildi, seejärel keskmises mõõdus eelvaate (kui lubatud) ja lõpuks originaalpildi (kui lubatud).",
|
|
||||||
"setting_image_viewer_preview_subtitle": "Luba keskmise resolutsiooniga pildi laadimine. Keela, et laadida kohe originaalpilt või kasutada ainult pisipilti.",
|
|
||||||
"setting_image_viewer_preview_title": "Laadi pildi eelvaade",
|
|
||||||
"setting_image_viewer_title": "Pildid",
|
|
||||||
"setting_languages_apply": "Rakenda",
|
|
||||||
"setting_languages_subtitle": "Muuda rakenduse keelt",
|
|
||||||
"setting_languages_title": "Keeled",
|
|
||||||
"setting_notifications_notify_hours": "{} tundi",
|
|
||||||
"setting_notifications_notify_immediately": "kohe",
|
|
||||||
"setting_notifications_notify_minutes": "{} minutit",
|
|
||||||
"setting_notifications_notify_never": "mitte kunagi",
|
|
||||||
"setting_notifications_notify_seconds": "{} sekundit",
|
|
||||||
"setting_notifications_single_progress_title": "Kuva taustal varundamise detailset edenemist",
|
|
||||||
"setting_notifications_subtitle": "Halda oma teavituste eelistusi",
|
|
||||||
"setting_notifications_total_progress_title": "Kuva taustal varundamise üldist edenemist",
|
|
||||||
"settings": "Seaded",
|
"settings": "Seaded",
|
||||||
"settings_saved": "Seaded salvestatud",
|
"settings_saved": "Seaded salvestatud",
|
||||||
"share": "Jaga",
|
"share": "Jaga",
|
||||||
"share_add_photos": "Lisa fotosid",
|
|
||||||
"share_assets_selected": "{} valitud",
|
|
||||||
"shared": "Jagatud",
|
"shared": "Jagatud",
|
||||||
"shared_album_section_people_action_error": "Viga albumist eemaldamisel/lahkumisel",
|
|
||||||
"shared_album_section_people_action_leave": "Eemalda kasutaja albumist",
|
|
||||||
"shared_album_section_people_action_remove_user": "Eemalda kasutaja albumist",
|
|
||||||
"shared_album_section_people_title": "ISIKUD",
|
|
||||||
"shared_by": "Jagas",
|
"shared_by": "Jagas",
|
||||||
"shared_by_user": "Jagas {user}",
|
"shared_by_user": "Jagas {user}",
|
||||||
"shared_by_you": "Jagasid sina",
|
"shared_by_you": "Jagasid sina",
|
||||||
"shared_from_partner": "Fotod partnerilt {partner}",
|
"shared_from_partner": "Fotod partnerilt {partner}",
|
||||||
"shared_link_app_bar_title": "Jagatud lingid",
|
|
||||||
"shared_link_clipboard_copied_massage": "Kopeeritud lõikelauale",
|
|
||||||
"shared_link_clipboard_text": "Link: {}\nParool: {}",
|
|
||||||
"shared_link_create_error": "Viga jagatud lingi loomisel",
|
|
||||||
"shared_link_edit_expire_after_option_day": "1 päev",
|
|
||||||
"shared_link_edit_expire_after_option_days": "{} päeva",
|
|
||||||
"shared_link_edit_expire_after_option_hour": "1 tund",
|
|
||||||
"shared_link_edit_expire_after_option_hours": "{} tundi",
|
|
||||||
"shared_link_edit_expire_after_option_minute": "1 minut",
|
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minutit",
|
|
||||||
"shared_link_edit_expire_after_option_months": "{} kuud",
|
|
||||||
"shared_link_edit_expire_after_option_year": "{} aasta",
|
|
||||||
"shared_link_expires_day": "Aegub {} päeva pärast",
|
|
||||||
"shared_link_expires_days": "Aegub {} päeva pärast",
|
|
||||||
"shared_link_expires_hour": "Aegub {} tunni pärast",
|
|
||||||
"shared_link_expires_hours": "Aegub {} tunni pärast",
|
|
||||||
"shared_link_expires_minute": "Aegub {} minuti pärast",
|
|
||||||
"shared_link_expires_minutes": "Aegub {} minuti pärast",
|
|
||||||
"shared_link_expires_never": "Ei aegu",
|
|
||||||
"shared_link_expires_second": "Aegub {} sekundi pärast",
|
|
||||||
"shared_link_expires_seconds": "Aegub {} sekundi pärast",
|
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
|
||||||
"shared_link_manage_links": "Halda jagatud linke",
|
|
||||||
"shared_link_options": "Jagatud lingi valikud",
|
"shared_link_options": "Jagatud lingi valikud",
|
||||||
"shared_links": "Jagatud lingid",
|
"shared_links": "Jagatud lingid",
|
||||||
"shared_links_description": "Jaga fotosid ja videosid lingiga",
|
"shared_links_description": "Jaga fotosid ja videosid lingiga",
|
||||||
"shared_photos_and_videos_count": "{assetCount, plural, other {# jagatud fotot ja videot.}}",
|
"shared_photos_and_videos_count": "{assetCount, plural, other {# jagatud fotot ja videot.}}",
|
||||||
"shared_with_me": "Minuga jagatud",
|
|
||||||
"shared_with_partner": "Jagatud partneriga {partner}",
|
"shared_with_partner": "Jagatud partneriga {partner}",
|
||||||
"sharing": "Jagamine",
|
"sharing": "Jagamine",
|
||||||
"sharing_enter_password": "Palun sisesta selle lehe vaatamiseks salasõna.",
|
"sharing_enter_password": "Palun sisesta selle lehe vaatamiseks salasõna.",
|
||||||
"sharing_page_album": "Jagatud albumid",
|
|
||||||
"sharing_sidebar_description": "Kuva külgmenüüs Jagamise linki",
|
"sharing_sidebar_description": "Kuva külgmenüüs Jagamise linki",
|
||||||
"sharing_silver_appbar_create_shared_album": "Uus jagatud album",
|
|
||||||
"sharing_silver_appbar_share_partner": "Jaga partneriga",
|
|
||||||
"shift_to_permanent_delete": "vajuta ⇧, et üksus jäädavalt kustutada",
|
"shift_to_permanent_delete": "vajuta ⇧, et üksus jäädavalt kustutada",
|
||||||
"show_album_options": "Näita albumi valikuid",
|
"show_album_options": "Näita albumi valikuid",
|
||||||
"show_albums": "Näita albumeid",
|
"show_albums": "Näita albumeid",
|
||||||
@@ -1556,7 +1267,6 @@
|
|||||||
"support_third_party_description": "Sinu Immich'i install on kolmanda osapoole pakendatud. Probleemid, mida täheldad, võivad olla põhjustatud selle pakendamise poolt, seega võta esmajärjekorras nendega ühendust, kasutades allolevaid linke.",
|
"support_third_party_description": "Sinu Immich'i install on kolmanda osapoole pakendatud. Probleemid, mida täheldad, võivad olla põhjustatud selle pakendamise poolt, seega võta esmajärjekorras nendega ühendust, kasutades allolevaid linke.",
|
||||||
"swap_merge_direction": "Muuda ühendamise suunda",
|
"swap_merge_direction": "Muuda ühendamise suunda",
|
||||||
"sync": "Sünkrooni",
|
"sync": "Sünkrooni",
|
||||||
"sync_albums": "Sünkrooni albumid",
|
|
||||||
"tag": "Silt",
|
"tag": "Silt",
|
||||||
"tag_assets": "Sildista üksuseid",
|
"tag_assets": "Sildista üksuseid",
|
||||||
"tag_created": "Lisatud silt: {tag}",
|
"tag_created": "Lisatud silt: {tag}",
|
||||||
@@ -1570,13 +1280,6 @@
|
|||||||
"theme": "Teema",
|
"theme": "Teema",
|
||||||
"theme_selection": "Teema valik",
|
"theme_selection": "Teema valik",
|
||||||
"theme_selection_description": "Sea automaatselt hele või tume teema vastavalt veebilehitseja eelistustele",
|
"theme_selection_description": "Sea automaatselt hele või tume teema vastavalt veebilehitseja eelistustele",
|
||||||
"theme_setting_colorful_interface_subtitle": "Rakenda taustapindadele primaarne värv.",
|
|
||||||
"theme_setting_colorful_interface_title": "Värviline kasutajaliides",
|
|
||||||
"theme_setting_image_viewer_quality_subtitle": "Kohanda detailvaaturi kvaliteeti",
|
|
||||||
"theme_setting_image_viewer_quality_title": "Pildivaaturi kvaliteet",
|
|
||||||
"theme_setting_primary_color_title": "Põhivärv",
|
|
||||||
"theme_setting_system_primary_color_title": "Kasuta süsteemset värvi",
|
|
||||||
"theme_setting_system_theme_switch": "Automaatne (järgi süsteemi seadet)",
|
|
||||||
"they_will_be_merged_together": "Nad ühendatakse kokku",
|
"they_will_be_merged_together": "Nad ühendatakse kokku",
|
||||||
"third_party_resources": "Kolmanda osapoole ressursid",
|
"third_party_resources": "Kolmanda osapoole ressursid",
|
||||||
"time_based_memories": "Ajapõhised mälestused",
|
"time_based_memories": "Ajapõhised mälestused",
|
||||||
@@ -1596,11 +1299,7 @@
|
|||||||
"trash_all": "Kõik prügikasti",
|
"trash_all": "Kõik prügikasti",
|
||||||
"trash_count": "Liiguta {count, number} prügikasti",
|
"trash_count": "Liiguta {count, number} prügikasti",
|
||||||
"trash_delete_asset": "Kustuta üksus",
|
"trash_delete_asset": "Kustuta üksus",
|
||||||
"trash_emptied": "Prügikast tühjendatud",
|
|
||||||
"trash_no_results_message": "Siia ilmuvad prügikasti liigutatud fotod ja videod.",
|
"trash_no_results_message": "Siia ilmuvad prügikasti liigutatud fotod ja videod.",
|
||||||
"trash_page_delete_all": "Kustuta kõik",
|
|
||||||
"trash_page_restore_all": "Taasta kõik",
|
|
||||||
"trash_page_select_assets_btn": "Vali üksused",
|
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Prügikasti tõstetud üksused kustutatakse jäädavalt {days, plural, one {# päeva} other {# päeva}} pärast.",
|
"trashed_items_will_be_permanently_deleted_after": "Prügikasti tõstetud üksused kustutatakse jäädavalt {days, plural, one {# päeva} other {# päeva}} pärast.",
|
||||||
"type": "Tüüp",
|
"type": "Tüüp",
|
||||||
"unarchive": "Taasta arhiivist",
|
"unarchive": "Taasta arhiivist",
|
||||||
@@ -1636,7 +1335,6 @@
|
|||||||
"upload_status_errors": "Vead",
|
"upload_status_errors": "Vead",
|
||||||
"upload_status_uploaded": "Üleslaaditud",
|
"upload_status_uploaded": "Üleslaaditud",
|
||||||
"upload_success": "Üleslaadimine õnnestus, uute üksuste nägemiseks värskenda lehte.",
|
"upload_success": "Üleslaadimine õnnestus, uute üksuste nägemiseks värskenda lehte.",
|
||||||
"uploading": "Üleslaadimine",
|
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Kasutus",
|
"usage": "Kasutus",
|
||||||
"use_custom_date_range": "Kasuta kohandatud kuupäevavahemikku",
|
"use_custom_date_range": "Kasuta kohandatud kuupäevavahemikku",
|
||||||
@@ -1657,7 +1355,6 @@
|
|||||||
"version": "Versioon",
|
"version": "Versioon",
|
||||||
"version_announcement_closing": "Sinu sõber, Alex",
|
"version_announcement_closing": "Sinu sõber, Alex",
|
||||||
"version_announcement_message": "Hei! Saadaval on uus Immich'i versioon. Palun võta aega, et lugeda <link>väljalasketeadet</link> ning veendu, et su seadistus on ajakohane, et vältida konfiguratsiooniprobleeme, eriti kui kasutad WatchTower'it või muud mehhanismi, mis Immich'it automaatselt uuendab.",
|
"version_announcement_message": "Hei! Saadaval on uus Immich'i versioon. Palun võta aega, et lugeda <link>väljalasketeadet</link> ning veendu, et su seadistus on ajakohane, et vältida konfiguratsiooniprobleeme, eriti kui kasutad WatchTower'it või muud mehhanismi, mis Immich'it automaatselt uuendab.",
|
||||||
"version_announcement_overlay_title": "Uus serveri versioon saadaval 🎉",
|
|
||||||
"version_history": "Versiooniajalugu",
|
"version_history": "Versiooniajalugu",
|
||||||
"version_history_item": "Versioon {version} paigaldatud {date}",
|
"version_history_item": "Versioon {version} paigaldatud {date}",
|
||||||
"video": "Video",
|
"video": "Video",
|
||||||
@@ -1675,21 +1372,16 @@
|
|||||||
"view_name": "Vaade",
|
"view_name": "Vaade",
|
||||||
"view_next_asset": "Vaata järgmist üksust",
|
"view_next_asset": "Vaata järgmist üksust",
|
||||||
"view_previous_asset": "Vaata eelmist üksust",
|
"view_previous_asset": "Vaata eelmist üksust",
|
||||||
"view_qr_code": "Vaata QR-koodi",
|
|
||||||
"view_stack": "Vaata virna",
|
"view_stack": "Vaata virna",
|
||||||
"viewer_remove_from_stack": "Eemalda virnast",
|
|
||||||
"viewer_unstack": "Eralda",
|
|
||||||
"visibility_changed": "{count, plural, one {# isiku} other {# isiku}} nähtavus muudetud",
|
"visibility_changed": "{count, plural, one {# isiku} other {# isiku}} nähtavus muudetud",
|
||||||
"waiting": "Ootel",
|
"waiting": "Ootel",
|
||||||
"warning": "Hoiatus",
|
"warning": "Hoiatus",
|
||||||
"week": "Nädal",
|
"week": "Nädal",
|
||||||
"welcome": "Tere tulemast",
|
"welcome": "Tere tulemast",
|
||||||
"welcome_to_immich": "Tere tulemast Immich'isse",
|
"welcome_to_immich": "Tere tulemast Immich'isse",
|
||||||
"wifi_name": "WiFi-võrgu nimi",
|
|
||||||
"year": "Aasta",
|
"year": "Aasta",
|
||||||
"years_ago": "{years, plural, one {# aasta} other {# aastat}} tagasi",
|
"years_ago": "{years, plural, one {# aasta} other {# aastat}} tagasi",
|
||||||
"yes": "Jah",
|
"yes": "Jah",
|
||||||
"you_dont_have_any_shared_links": "Sul pole ühtegi jagatud linki",
|
"you_dont_have_any_shared_links": "Sul pole ühtegi jagatud linki",
|
||||||
"your_wifi_name": "Sinu WiFi-võrgu nimi",
|
|
||||||
"zoom_image": "Suumi pilti"
|
"zoom_image": "Suumi pilti"
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user