Compare commits
248 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f5d9bebd84 | |||
| f16bdb2a01 | |||
| adb17c4d58 | |||
| 56156b97e7 | |||
| c411c1472a | |||
| 0bbe70e6a3 | |||
| a65c905621 | |||
| 61d784f4e7 | |||
| b63d6cdcd6 | |||
| fa45a26cff | |||
| 8f045bc602 | |||
| 5353658114 | |||
| 21880aec14 | |||
| 48d746d9d5 | |||
| 8ab5040351 | |||
| 1219fd82a0 | |||
| 28d8357cc5 | |||
| a9e7d0388b | |||
| 86d64f3483 | |||
| c1150fe7e3 | |||
| ecb66fdb2c | |||
| c046651f23 | |||
| 6117329057 | |||
| 585997d46f | |||
| 7146ec99b1 | |||
| b7b0b9b6d8 | |||
| 4935f3e0bb | |||
| 709a7b70aa | |||
| 6a4d21205f | |||
| 3a0ddfb92d | |||
| cd03d0c0f2 | |||
| 117b263887 | |||
| f357f3324f | |||
| 7d95bad5cb | |||
| 77b0505006 | |||
| fac1beb7d8 | |||
| 3944f5d73b | |||
| 4445288758 | |||
| 4efc41d5d9 | |||
| c9d45eee86 | |||
| b3b774cfe5 | |||
| 15e894b9b5 | |||
| ca06d0aa83 | |||
| 0cd51ae9c5 | |||
| 68f6111b77 | |||
| 668288ca20 | |||
| 3fdc1df89c | |||
| 989d9dbe51 | |||
| 48112d84a3 | |||
| 80dfe7a5e9 | |||
| ce90a2ec1a | |||
| dccbe0b3ed | |||
| c0ad12f279 | |||
| 9c484b23a9 | |||
| eed014482d | |||
| d271e6a3ae | |||
| 60c43081ed | |||
| 81d959a27e | |||
| bb775110ef | |||
| 7280331b76 | |||
| 93ee6ee0a5 | |||
| 7544a678ec | |||
| 3066c8198c | |||
| eb8dfa283e | |||
| 41a127e2ab | |||
| feb475561e | |||
| 4c4c67f0d2 | |||
| 381b66bf70 | |||
| a89f3ad97c | |||
| c473511133 | |||
| 0d66a6b51f | |||
| 66400b2e8e | |||
| 87cdf0ebd9 | |||
| 3f719bd8d7 | |||
| 55af925ab3 | |||
| ff63b0fa8f | |||
| f21fe8716c | |||
| 6a69dafd31 | |||
| 47b1938f17 | |||
| 2ffcfe06f3 | |||
| 89551edee5 | |||
| cb6c541ae1 | |||
| b1e1362246 | |||
| ccc2b191dd | |||
| bb7010b2bb | |||
| 8db666bc38 | |||
| eace0f716d | |||
| 96743b6c33 | |||
| ff181cf346 | |||
| 0cd5960007 | |||
| 698592c1b0 | |||
| f75d853e9a | |||
| 3a1e3e82e7 | |||
| 0beb3ac4c1 | |||
| 894545aeed | |||
| 5250269fa4 | |||
| a169fb6a79 | |||
| 09ced9a171 | |||
| a6e5e4f625 | |||
| bbd8de177b | |||
| 867f6e64f9 | |||
| ec6379b0b2 | |||
| 2a80251dc3 | |||
| d33ce13561 | |||
| 016d7a6ceb | |||
| 8ff25a4f7a | |||
| 61a3eba1bd | |||
| 7072e48cbe | |||
| ece977d9ca | |||
| 2af8095880 | |||
| 30822fcd10 | |||
| c578273e7a | |||
| 118a3fc9db | |||
| 1138f6dcce | |||
| 33f3751b72 | |||
| b8509e6411 | |||
| bd43edbcd7 | |||
| c8b4a7e1f1 | |||
| f34f83e164 | |||
| df2cf5d106 | |||
| 52975eadb3 | |||
| 12610e4a9f | |||
| 2b3efa02d8 | |||
| a21a997f21 | |||
| 7d61ed7ce4 | |||
| 8f7baf8336 | |||
| 44923acfd6 | |||
| ab95881ebb | |||
| 8801ae5821 | |||
| ea9f11bf39 | |||
| 62fc5b3c7d | |||
| 15d431ba6a | |||
| 5d21ba3166 | |||
| da7a81b752 | |||
| becdc3dcf5 | |||
| 84b51e3cbb | |||
| b845184c80 | |||
| 1fde02ee1e | |||
| 526c02297c | |||
| 732b06eec8 | |||
| 436cff72b5 | |||
| be5cc2cdf5 | |||
| 094a41ac9a | |||
| ebad6a008f | |||
| 0c261ffbe2 | |||
| 6df6103c67 | |||
| 8c5116bc1d | |||
| e3812a0e36 | |||
| 4b1ced439b | |||
| 2e8a286540 | |||
| 038a82c4f1 | |||
| 2c2dd01bf0 | |||
| ac73e163df | |||
| d89e88bb3f | |||
| 3ce353393a | |||
| 0e4cf9ac57 | |||
| 07290580a6 | |||
| d9ce74b896 | |||
| 4c0f79b162 | |||
| 9851d24628 | |||
| fe6cbd93b1 | |||
| df20788088 | |||
| 3d042cc7f1 | |||
| 85446c5862 | |||
| fb52ac0f5b | |||
| 48bcbee6ed | |||
| f621f8ef2c | |||
| 7f69abbf0d | |||
| 895b2bf5cd | |||
| f64e6f5dc3 | |||
| 64e738f79d | |||
| a17390a422 | |||
| 1b5fc9c665 | |||
| 23717ce981 | |||
| 2fd05e8447 | |||
| c664d99a34 | |||
| 21c7d70336 | |||
| ad272333db | |||
| 460d594791 | |||
| e6c575c33e | |||
| 85ac0512a6 | |||
| 205260d31c | |||
| 3858973be5 | |||
| 02994883fe | |||
| a1f8150c30 | |||
| d85ef19bfc | |||
| d0014bdf94 | |||
| e822e3eca9 | |||
| 644defa4a1 | |||
| 1fe3c7b9b3 | |||
| 0d60be3d87 | |||
| 765da7b182 | |||
| b037158028 | |||
| a03902f174 | |||
| 1d610ad9cb | |||
| dab4870fed | |||
| 37f5e6e2cb | |||
| 57d622bc43 | |||
| c167e46ec7 | |||
| 6ce8a1deeb | |||
| f659ef4b7a | |||
| bb6cdc99ab | |||
| 830b4dadcb | |||
| d2f2f8d672 | |||
| be1062474b | |||
| 64000d9d76 | |||
| 59fa8fbd0e | |||
| 19746a8685 | |||
| 987e5ab76c | |||
| 1b5e981a45 | |||
| b7a0cf2470 | |||
| 13d6bd67b1 | |||
| 1de2eae12d | |||
| bc5875ba8d | |||
| 0426b574fe | |||
| 2c3658e642 | |||
| a493dab294 | |||
| 699fdd0d1b | |||
| a774153f67 | |||
| ca12aff3a4 | |||
| 550c1c0a10 | |||
| 92ac1193e6 | |||
| 2a95eccf6a | |||
| ee017803bf | |||
| 0986a71ce3 | |||
| af36eaa61b | |||
| fda68f972f | |||
| a8eec92da7 | |||
| ad8511c978 | |||
| fe8c5e8107 | |||
| c70140e707 | |||
| 010b144754 | |||
| b71039e83c | |||
| 56a4aa9ffe | |||
| 488dc4efbd | |||
| f0ff8581da | |||
| c49fd2065b | |||
| 21a6eb30ff | |||
| 9e063c993c | |||
| dd1fcd5be5 | |||
| 52ae06c119 | |||
| 854ea13d6a | |||
| 504930947d | |||
| 0e6ac87645 | |||
| bd2deda50c | |||
| 160bb492a2 | |||
| 6474a78b8b | |||
| e275f2d8b3 |
+1
-1
@@ -1 +1 @@
|
|||||||
22.14.0
|
22.15.0
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
name: 'Single arch image build'
|
||||||
|
description: 'Build single-arch image on platform appropriate runner'
|
||||||
|
inputs:
|
||||||
|
image:
|
||||||
|
description: 'Name of the image to build'
|
||||||
|
required: true
|
||||||
|
ghcr-token:
|
||||||
|
description: 'GitHub Container Registry token'
|
||||||
|
required: true
|
||||||
|
platform:
|
||||||
|
description: 'Platform to build for'
|
||||||
|
required: true
|
||||||
|
artifact-key-base:
|
||||||
|
description: 'Base key for artifact name'
|
||||||
|
required: true
|
||||||
|
context:
|
||||||
|
description: 'Path to build context'
|
||||||
|
required: true
|
||||||
|
dockerfile:
|
||||||
|
description: 'Path to Dockerfile'
|
||||||
|
required: true
|
||||||
|
build-args:
|
||||||
|
description: 'Docker build arguments'
|
||||||
|
required: false
|
||||||
|
runs:
|
||||||
|
using: 'composite'
|
||||||
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
id: prepare
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
PLATFORM: ${{ inputs.platform }}
|
||||||
|
run: |
|
||||||
|
echo "platform-pair=${PLATFORM//\//-}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ inputs.ghcr-token }}
|
||||||
|
|
||||||
|
- name: Generate cache key suffix
|
||||||
|
id: cache-key-suffix
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
REF: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||||
|
echo "cache-key-suffix=pr-${{ github.event.number }}" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
SUFFIX=$(echo "${REF}" | sed 's/[^a-zA-Z0-9]/-/g')
|
||||||
|
echo "suffix=${SUFFIX}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate cache target
|
||||||
|
id: cache-target
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
BUILD_ARGS: ${{ inputs.build-args }}
|
||||||
|
IMAGE: ${{ inputs.image }}
|
||||||
|
SUFFIX: ${{ steps.cache-key-suffix.outputs.suffix }}
|
||||||
|
PLATFORM_PAIR: ${{ steps.prepare.outputs.platform-pair }}
|
||||||
|
run: |
|
||||||
|
HASH=$(sha256sum <<< "${BUILD_ARGS}" | cut -d' ' -f1)
|
||||||
|
CACHE_KEY="${PLATFORM_PAIR}-${HASH}"
|
||||||
|
echo "cache-key-base=${CACHE_KEY}" >> $GITHUB_OUTPUT
|
||||||
|
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||||
|
# Essentially just ignore the cache output (forks can't write to registry cache)
|
||||||
|
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "cache-to=type=registry,ref=${IMAGE}-build-cache:${CACHE_KEY}-${SUFFIX},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate docker image tags
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||||
|
env:
|
||||||
|
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
id: build
|
||||||
|
uses: docker/build-push-action@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
|
||||||
@@ -7,6 +7,15 @@ 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]
|
||||||
@@ -15,16 +24,23 @@ 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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
mobile:
|
mobile:
|
||||||
@@ -38,31 +54,26 @@ 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
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
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
|
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.get-ref.outputs.ref }}
|
ref: ${{ inputs.ref || github.sha }}
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
|
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
cache: 'gradle'
|
cache: 'gradle'
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -93,7 +104,7 @@ jobs:
|
|||||||
flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64
|
flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64
|
||||||
|
|
||||||
- name: Publish Android Artifact
|
- name: Publish Android Artifact
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: release-apk-signed
|
name: release-apk-signed
|
||||||
path: mobile/build/app/outputs/flutter-apk/*.apk
|
path: mobile/build/app/outputs/flutter-apk/*.apk
|
||||||
|
|||||||
@@ -8,31 +8,38 @@ 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.2.2
|
||||||
|
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 $BRANCH -L 100 | cut -f 1 )
|
cacheKeysForPR=$(gh actions-cache list -R $REPO -B ${REF} -L 100 | cut -f 1 )
|
||||||
|
|
||||||
## Setting this to not fail the workflow while deleting cache keys.
|
## 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 $BRANCH --confirm
|
gh actions-cache delete $cacheKey -R "$REPO" -B "${REF}" --confirm
|
||||||
done
|
done
|
||||||
echo "Done"
|
echo "Done"
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|||||||
@@ -16,21 +16,25 @@ 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.2.2
|
||||||
|
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@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
@@ -48,11 +52,16 @@ 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.2.2
|
||||||
|
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
|
||||||
@@ -61,7 +70,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@@ -76,7 +85,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate docker image tags
|
- name: Generate docker image tags
|
||||||
id: metadata
|
id: metadata
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||||
with:
|
with:
|
||||||
flavor: |
|
flavor: |
|
||||||
latest=false
|
latest=false
|
||||||
@@ -87,7 +96,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@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
||||||
with:
|
with:
|
||||||
file: cli/Dockerfile
|
file: cli/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ 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
|
||||||
@@ -42,11 +44,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # v3
|
uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
||||||
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.
|
||||||
@@ -59,7 +63,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@45775bd8235c68ba998cffa5171334d58593da47 # v3
|
uses: github/codeql-action/autobuild@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
||||||
|
|
||||||
# ℹ️ 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
|
||||||
@@ -72,6 +76,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@45775bd8235c68ba998cffa5171334d58593da47 # v3
|
uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
||||||
with:
|
with:
|
||||||
category: '/language:${{matrix.language}}'
|
category: '/language:${{matrix.language}}'
|
||||||
|
|||||||
+85
-403
@@ -12,20 +12,23 @@ 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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
server:
|
server:
|
||||||
@@ -37,6 +40,8 @@ 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
|
||||||
@@ -45,6 +50,9 @@ 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:
|
||||||
@@ -52,24 +60,28 @@ jobs:
|
|||||||
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
||||||
steps:
|
steps:
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
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: |
|
||||||
REGISTRY_NAME="ghcr.io"
|
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_PR}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
||||||
REPOSITORY=${{ github.repository_owner }}/immich-machine-learning
|
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_COMMIT}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
||||||
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:
|
||||||
@@ -77,424 +89,91 @@ jobs:
|
|||||||
suffix: ['']
|
suffix: ['']
|
||||||
steps:
|
steps:
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
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: |
|
||||||
REGISTRY_NAME="ghcr.io"
|
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_PR}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
||||||
REPOSITORY=${{ github.repository_owner }}/immich-server
|
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_COMMIT}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
||||||
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
|
|
||||||
|
|
||||||
build_and_push_ml:
|
machine-learning:
|
||||||
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:
|
strategy:
|
||||||
# Prevent a failure in one image from stopping the other builds
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
device: cpu
|
|
||||||
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
device: cpu
|
|
||||||
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
device: cuda
|
|
||||||
suffix: -cuda
|
|
||||||
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: mich
|
|
||||||
device: rocm
|
|
||||||
suffix: -rocm
|
|
||||||
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
device: openvino
|
|
||||||
suffix: -openvino
|
|
||||||
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
device: armnn
|
|
||||||
suffix: -armnn
|
|
||||||
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
device: rknn
|
|
||||||
suffix: -rknn
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Generate cache key suffix
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
|
||||||
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "CACHE_KEY_SUFFIX=$(echo ${{ github.ref_name }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate cache target
|
|
||||||
id: cache-target
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
|
||||||
# Essentially just ignore the cache output (forks can't write to registry cache)
|
|
||||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "cache-to=type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
id: build
|
|
||||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
|
||||||
with:
|
|
||||||
context: ${{ env.context }}
|
|
||||||
file: ${{ env.file }}
|
|
||||||
platforms: ${{ matrix.platforms }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
|
||||||
cache-from: |
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }}
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-main
|
|
||||||
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
build-args: |
|
|
||||||
DEVICE=${{ matrix.device }}
|
|
||||||
BUILD_ID=${{ github.run_id }}
|
|
||||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
|
||||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
|
||||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
run: |
|
|
||||||
mkdir -p ${{ runner.temp }}/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
||||||
with:
|
|
||||||
name: ml-digests-${{ matrix.device }}-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: ${{ runner.temp }}/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
merge_ml:
|
|
||||||
name: Merge & Push ML
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' && !github.event.pull_request.head.repo.fork }}
|
|
||||||
env:
|
|
||||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning
|
|
||||||
DOCKER_REPO: altran1502/immich-machine-learning
|
|
||||||
strategy:
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- device: cpu
|
- device: cpu
|
||||||
|
tag-suffix: ''
|
||||||
- device: cuda
|
- device: cuda
|
||||||
suffix: -cuda
|
tag-suffix: '-cuda'
|
||||||
- device: rocm
|
platforms: linux/amd64
|
||||||
suffix: -rocm
|
|
||||||
- device: openvino
|
- device: openvino
|
||||||
suffix: -openvino
|
tag-suffix: '-openvino'
|
||||||
|
platforms: linux/amd64
|
||||||
- device: armnn
|
- device: armnn
|
||||||
suffix: -armnn
|
tag-suffix: '-armnn'
|
||||||
|
platforms: linux/arm64
|
||||||
- device: rknn
|
- device: rknn
|
||||||
suffix: -rknn
|
tag-suffix: '-rknn'
|
||||||
needs:
|
platforms: linux/arm64
|
||||||
- build_and_push_ml
|
- device: rocm
|
||||||
steps:
|
tag-suffix: '-rocm'
|
||||||
- name: Download digests
|
platforms: linux/amd64
|
||||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
runner-mapping: '{"linux/amd64": "mich"}'
|
||||||
with:
|
uses: ./.github/workflows/multi-runner-build.yml
|
||||||
path: ${{ runner.temp }}/digests
|
permissions:
|
||||||
pattern: ml-digests-${{ matrix.device }}-*
|
contents: read
|
||||||
merge-multiple: true
|
actions: read
|
||||||
|
packages: write
|
||||||
|
secrets:
|
||||||
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
image: immich-machine-learning
|
||||||
|
context: machine-learning
|
||||||
|
dockerfile: machine-learning/Dockerfile
|
||||||
|
platforms: ${{ matrix.platforms }}
|
||||||
|
runner-mapping: ${{ matrix.runner-mapping }}
|
||||||
|
tag-suffix: ${{ matrix.tag-suffix }}
|
||||||
|
dockerhub-push: ${{ github.event_name == 'release' }}
|
||||||
|
build-args: |
|
||||||
|
DEVICE=${{ matrix.device }}
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
server:
|
||||||
if: ${{ github.event_name == 'release' }}
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Login to GHCR
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
with:
|
|
||||||
flavor: |
|
|
||||||
# Disable latest tag
|
|
||||||
latest=false
|
|
||||||
suffix=${{ matrix.suffix }}
|
|
||||||
images: |
|
|
||||||
name=${{ env.GHCR_REPO }}
|
|
||||||
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
|
||||||
tags: |
|
|
||||||
# Tag with branch name
|
|
||||||
type=ref,event=branch
|
|
||||||
# Tag with pr-number
|
|
||||||
type=ref,event=pr
|
|
||||||
# Tag with long commit sha hash
|
|
||||||
type=sha,format=long,prefix=commit-
|
|
||||||
# Tag with git tag on release
|
|
||||||
type=ref,event=tag
|
|
||||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: ${{ runner.temp }}/digests
|
|
||||||
run: |
|
|
||||||
# Process annotations
|
|
||||||
declare -a ANNOTATIONS=()
|
|
||||||
if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then
|
|
||||||
while IFS= read -r annotation; do
|
|
||||||
# Extract key and value by removing the manifest: prefix
|
|
||||||
if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then
|
|
||||||
key="${BASH_REMATCH[1]}"
|
|
||||||
value="${BASH_REMATCH[2]}"
|
|
||||||
# Use array to properly handle arguments with spaces
|
|
||||||
ANNOTATIONS+=(--annotation "index:$key=$value")
|
|
||||||
fi
|
|
||||||
done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
|
||||||
fi
|
|
||||||
|
|
||||||
TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
SOURCE_ARGS=$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *)
|
|
||||||
|
|
||||||
echo "docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS"
|
|
||||||
|
|
||||||
docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS
|
|
||||||
|
|
||||||
build_and_push_server:
|
|
||||||
name: Build and Push Server
|
name: Build and Push Server
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||||
env:
|
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-server
|
image: immich-server
|
||||||
context: .
|
context: .
|
||||||
file: server/Dockerfile
|
dockerfile: server/Dockerfile
|
||||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
dockerhub-push: ${{ github.event_name == 'release' }}
|
||||||
strategy:
|
build-args: |
|
||||||
fail-fast: false
|
DEVICE=cpu
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
steps:
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Generate cache key suffix
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
|
||||||
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "CACHE_KEY_SUFFIX=$(echo ${{ github.ref_name }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate cache target
|
|
||||||
id: cache-target
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
|
||||||
# Essentially just ignore the cache output (forks can't write to registry cache)
|
|
||||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "cache-to=type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ env.CACHE_KEY_SUFFIX }},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
id: build
|
|
||||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
|
||||||
with:
|
|
||||||
context: ${{ env.context }}
|
|
||||||
file: ${{ env.file }}
|
|
||||||
platforms: ${{ matrix.platform }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
|
||||||
cache-from: |
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ env.CACHE_KEY_SUFFIX }}
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-main
|
|
||||||
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
build-args: |
|
|
||||||
DEVICE=cpu
|
|
||||||
BUILD_ID=${{ github.run_id }}
|
|
||||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
|
||||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
|
||||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
run: |
|
|
||||||
mkdir -p ${{ runner.temp }}/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
||||||
with:
|
|
||||||
name: server-digests-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: ${{ runner.temp }}/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
merge_server:
|
|
||||||
name: Merge & Push Server
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' && !github.event.pull_request.head.repo.fork }}
|
|
||||||
env:
|
|
||||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
|
||||||
DOCKER_REPO: altran1502/immich-server
|
|
||||||
needs:
|
|
||||||
- build_and_push_server
|
|
||||||
steps:
|
|
||||||
- name: Download digests
|
|
||||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
|
||||||
with:
|
|
||||||
path: ${{ runner.temp }}/digests
|
|
||||||
pattern: server-digests-*
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
if: ${{ github.event_name == 'release' }}
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Login to GHCR
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
with:
|
|
||||||
flavor: |
|
|
||||||
# Disable latest tag
|
|
||||||
latest=false
|
|
||||||
suffix=${{ matrix.suffix }}
|
|
||||||
images: |
|
|
||||||
name=${{ env.GHCR_REPO }}
|
|
||||||
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
|
||||||
tags: |
|
|
||||||
# Tag with branch name
|
|
||||||
type=ref,event=branch
|
|
||||||
# Tag with pr-number
|
|
||||||
type=ref,event=pr
|
|
||||||
# Tag with long commit sha hash
|
|
||||||
type=sha,format=long,prefix=commit-
|
|
||||||
# Tag with git tag on release
|
|
||||||
type=ref,event=tag
|
|
||||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: ${{ runner.temp }}/digests
|
|
||||||
run: |
|
|
||||||
# Process annotations
|
|
||||||
declare -a ANNOTATIONS=()
|
|
||||||
if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then
|
|
||||||
while IFS= read -r annotation; do
|
|
||||||
# Extract key and value by removing the manifest: prefix
|
|
||||||
if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then
|
|
||||||
key="${BASH_REMATCH[1]}"
|
|
||||||
value="${BASH_REMATCH[2]}"
|
|
||||||
# Use array to properly handle arguments with spaces
|
|
||||||
ANNOTATIONS+=(--annotation "index:$key=$value")
|
|
||||||
fi
|
|
||||||
done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
|
||||||
fi
|
|
||||||
|
|
||||||
TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
SOURCE_ARGS=$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *)
|
|
||||||
|
|
||||||
echo "docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS"
|
|
||||||
|
|
||||||
docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS
|
|
||||||
|
|
||||||
success-check-server:
|
success-check-server:
|
||||||
name: Docker Build & Push Server Success
|
name: Docker Build & Push Server Success
|
||||||
needs: [merge_server, retag_server]
|
needs: [server, retag_server]
|
||||||
|
permissions: {}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -503,11 +182,13 @@ 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: [merge_ml, retag_ml]
|
needs: [machine-learning, retag_ml]
|
||||||
|
permissions: {}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -516,4 +197,5 @@ 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) }}"
|
||||||
|
|||||||
@@ -10,16 +10,22 @@ 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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
docs:
|
docs:
|
||||||
@@ -33,6 +39,8 @@ 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:
|
||||||
@@ -41,10 +49,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './docs/.nvmrc'
|
node-version-file: './docs/.nvmrc'
|
||||||
|
|
||||||
@@ -58,8 +68,9 @@ jobs:
|
|||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Upload build output
|
- name: Upload build output
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
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
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: Docs deploy
|
name: Docs deploy
|
||||||
on:
|
on:
|
||||||
workflow_run:
|
workflow_run: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
||||||
workflows: ['Docs build']
|
workflows: ['Docs build']
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
@@ -9,6 +9,9 @@ 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 }}
|
||||||
@@ -17,7 +20,7 @@ jobs:
|
|||||||
run: echo 'The triggering workflow did not succeed' && exit 1
|
run: echo 'The triggering workflow did not succeed' && exit 1
|
||||||
- name: Get artifact
|
- name: Get artifact
|
||||||
id: get-artifact
|
id: get-artifact
|
||||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||||
@@ -35,7 +38,9 @@ jobs:
|
|||||||
return { found: true, id: matchArtifact.id };
|
return { found: true, id: matchArtifact.id };
|
||||||
- name: Determine deploy parameters
|
- name: Determine deploy parameters
|
||||||
id: parameters
|
id: parameters
|
||||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
|
env:
|
||||||
|
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const eventType = context.payload.workflow_run.event;
|
const eventType = context.payload.workflow_run.event;
|
||||||
@@ -57,7 +62,8 @@ 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 response = await github.rest.search.issuesAndPullRequests({q: 'repo:${{ github.repository }} is:pr sha:${{ github.event.workflow_run.head_sha }}',per_page: 1,})
|
const {HEAD_SHA} = process.env;
|
||||||
|
const response = await github.rest.search.issuesAndPullRequests({q: `repo:${{ github.repository }} is:pr sha:${HEAD_SHA}`,per_page: 1,})
|
||||||
const items = response.data.items
|
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")
|
||||||
@@ -95,30 +101,36 @@ 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.2.2
|
||||||
|
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.0.1
|
||||||
|
env:
|
||||||
|
PARAM_JSON: ${{ needs.checks.outputs.parameters }}
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const json = `${{ needs.checks.outputs.parameters }}`;
|
const parameters = JSON.parse(process.env.PARAM_JSON);
|
||||||
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.0.1
|
||||||
|
env:
|
||||||
|
ARTIFACT_JSON: ${{ needs.checks.outputs.artifact }}
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
let artifact = ${{ needs.checks.outputs.artifact }};
|
let artifact = JSON.parse(process.env.ARTIFACT_JSON);
|
||||||
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,
|
||||||
@@ -138,7 +150,7 @@ jobs:
|
|||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2.1.5
|
||||||
with:
|
with:
|
||||||
tg_version: '0.58.12'
|
tg_version: '0.58.12'
|
||||||
tofu_version: '1.7.1'
|
tofu_version: '1.7.1'
|
||||||
@@ -153,7 +165,7 @@ jobs:
|
|||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2.1.5
|
||||||
with:
|
with:
|
||||||
tg_version: '0.58.12'
|
tg_version: '0.58.12'
|
||||||
tofu_version: '1.7.1'
|
tofu_version: '1.7.1'
|
||||||
@@ -162,12 +174,15 @@ jobs:
|
|||||||
|
|
||||||
- name: Output Cleaning
|
- name: Output Cleaning
|
||||||
id: clean
|
id: clean
|
||||||
|
env:
|
||||||
|
TG_OUTPUT: ${{ steps.docs-output.outputs.tg_action_output }}
|
||||||
run: |
|
run: |
|
||||||
TG_OUT=$(echo '${{ steps.docs-output.outputs.tg_action_output }}' | sed 's|%0A|\n|g ; s|%3C|<|g' | jq -c .)
|
CLEANED=$(echo "$TG_OUTPUT" | sed 's|%0A|\n|g ; s|%3C|<|g' | jq -c .)
|
||||||
echo "output=$TG_OUT" >> $GITHUB_OUTPUT
|
echo "output=$CLEANED" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Publish to Cloudflare Pages
|
- name: Publish to Cloudflare Pages
|
||||||
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1
|
# TODO: Action is deprecated
|
||||||
|
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1.5.0
|
||||||
with:
|
with:
|
||||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
|
||||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
@@ -184,7 +199,7 @@ jobs:
|
|||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2.1.5
|
||||||
with:
|
with:
|
||||||
tg_version: '0.58.12'
|
tg_version: '0.58.12'
|
||||||
tofu_version: '1.7.1'
|
tofu_version: '1.7.1'
|
||||||
@@ -192,7 +207,7 @@ jobs:
|
|||||||
tg_command: 'apply'
|
tg_command: 'apply'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3
|
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
|
||||||
if: ${{ steps.parameters.outputs.event == 'pr' }}
|
if: ${{ steps.parameters.outputs.event == 'pr' }}
|
||||||
with:
|
with:
|
||||||
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
|
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
name: Docs destroy
|
name: Docs destroy
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
||||||
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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Destroy Docs Subdomain
|
- name: Destroy Docs Subdomain
|
||||||
env:
|
env:
|
||||||
@@ -18,7 +25,7 @@ jobs:
|
|||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2.1.5
|
||||||
with:
|
with:
|
||||||
tg_version: '0.58.12'
|
tg_version: '0.58.12'
|
||||||
tofu_version: '1.7.1'
|
tofu_version: '1.7.1'
|
||||||
@@ -26,7 +33,7 @@ jobs:
|
|||||||
tg_command: 'destroy -refresh=false'
|
tg_command: 'destroy -refresh=false'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3
|
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
|
||||||
with:
|
with:
|
||||||
number: ${{ github.event.number }}
|
number: ${{ github.event.number }}
|
||||||
delete: true
|
delete: true
|
||||||
|
|||||||
@@ -4,28 +4,32 @@ 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@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2
|
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||||
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 }}
|
||||||
|
|
||||||
- name: 'Checkout'
|
- name: 'Checkout'
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
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@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -33,13 +37,13 @@ jobs:
|
|||||||
run: make install-all && make format-all
|
run: make install-all && make format-all
|
||||||
|
|
||||||
- name: Commit and push
|
- name: Commit and push
|
||||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9
|
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
||||||
with:
|
with:
|
||||||
default_author: github_actions
|
default_author: github_actions
|
||||||
message: 'chore: fix formatting'
|
message: 'chore: fix formatting'
|
||||||
|
|
||||||
- name: Remove label
|
- name: Remove label
|
||||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
@@ -0,0 +1,185 @@
|
|||||||
|
name: 'Multi-runner container image build'
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
image:
|
||||||
|
description: 'Name of the image'
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
context:
|
||||||
|
description: 'Path to build context'
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
dockerfile:
|
||||||
|
description: 'Path to Dockerfile'
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
tag-suffix:
|
||||||
|
description: 'Suffix to append to the image tag'
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
dockerhub-push:
|
||||||
|
description: 'Push to Docker Hub'
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
build-args:
|
||||||
|
description: 'Docker build arguments'
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
platforms:
|
||||||
|
description: 'Platforms to build for'
|
||||||
|
type: string
|
||||||
|
runner-mapping:
|
||||||
|
description: 'Mapping from platforms to runners'
|
||||||
|
type: string
|
||||||
|
secrets:
|
||||||
|
DOCKERHUB_USERNAME:
|
||||||
|
required: false
|
||||||
|
DOCKERHUB_TOKEN:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ inputs.image }}
|
||||||
|
DOCKERHUB_IMAGE: altran1502/${{ inputs.image }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
matrix:
|
||||||
|
name: 'Generate matrix'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.matrix.outputs.matrix }}
|
||||||
|
key: ${{ steps.artifact-key.outputs.base }}
|
||||||
|
steps:
|
||||||
|
- name: Generate build matrix
|
||||||
|
id: matrix
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
PLATFORMS: ${{ inputs.platforms || 'linux/amd64,linux/arm64' }}
|
||||||
|
RUNNER_MAPPING: ${{ inputs.runner-mapping || '{"linux/amd64":"ubuntu-latest","linux/arm64":"ubuntu-24.04-arm"}' }}
|
||||||
|
run: |
|
||||||
|
matrix=$(jq -R -c \
|
||||||
|
--argjson runner_mapping "${RUNNER_MAPPING}" \
|
||||||
|
'split(",") | map({platform: ., runner: $runner_mapping[.]})' \
|
||||||
|
<<< "${PLATFORMS}")
|
||||||
|
echo "${matrix}"
|
||||||
|
echo "matrix=${matrix}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Determine artifact key
|
||||||
|
id: artifact-key
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
IMAGE: ${{ inputs.image }}
|
||||||
|
SUFFIX: ${{ inputs.tag-suffix }}
|
||||||
|
run: |
|
||||||
|
if [[ -n "${SUFFIX}" ]]; then
|
||||||
|
base="${IMAGE}${SUFFIX}-digests"
|
||||||
|
else
|
||||||
|
base="${IMAGE}-digests"
|
||||||
|
fi
|
||||||
|
echo "${base}"
|
||||||
|
echo "base=${base}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: matrix
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include: ${{ fromJson(needs.matrix.outputs.matrix) }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- uses: ./.github/actions/image-build
|
||||||
|
with:
|
||||||
|
context: ${{ inputs.context }}
|
||||||
|
dockerfile: ${{ inputs.dockerfile }}
|
||||||
|
image: ${{ env.GHCR_IMAGE }}
|
||||||
|
ghcr-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
platform: ${{ matrix.platform }}
|
||||||
|
artifact-key-base: ${{ needs.matrix.outputs.key }}
|
||||||
|
build-args: ${{ inputs.build-args }}
|
||||||
|
|
||||||
|
merge:
|
||||||
|
needs: [matrix, build]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Download digests
|
||||||
|
uses: actions/download-artifact@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
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
name: PR Label Validation
|
name: PR Label Validation
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
||||||
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
|
||||||
@@ -12,7 +14,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Require PR to have a changelog label
|
- name: Require PR to have a changelog label
|
||||||
uses: mheap/github-action-required-labels@388fd6af37b34cdfe5a23b37060e763217e58b03 # v5
|
uses: mheap/github-action-required-labels@388fd6af37b34cdfe5a23b37060e763217e58b03 # v5.5.0
|
||||||
with:
|
with:
|
||||||
mode: exactly
|
mode: exactly
|
||||||
count: 1
|
count: 1
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
name: 'Pull Request Labeler'
|
name: 'Pull Request Labeler'
|
||||||
on:
|
on:
|
||||||
- pull_request_target
|
- pull_request_target # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
@@ -9,4 +11,4 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5
|
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ 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
|
||||||
|
|||||||
@@ -21,35 +21,40 @@ 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@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2
|
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||||
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 }}
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
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@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
|
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||||
|
|
||||||
- name: Bump version
|
- name: Bump version
|
||||||
run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}"
|
env:
|
||||||
|
SERVER_BUMP: ${{ inputs.serverBump }}
|
||||||
|
MOBILE_BUMP: ${{ inputs.mobileBump }}
|
||||||
|
run: misc/release/pump-version.sh -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}"
|
||||||
|
|
||||||
- name: Commit and tag
|
- name: Commit and tag
|
||||||
id: push-tag
|
id: push-tag
|
||||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9
|
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
||||||
with:
|
with:
|
||||||
default_author: github_actions
|
default_author: github_actions
|
||||||
message: 'chore: version ${{ env.IMMICH_VERSION }}'
|
message: 'chore: version ${{ env.IMMICH_VERSION }}'
|
||||||
@@ -59,37 +64,47 @@ jobs:
|
|||||||
build_mobile:
|
build_mobile:
|
||||||
uses: ./.github/workflows/build-mobile.yml
|
uses: ./.github/workflows/build-mobile.yml
|
||||||
needs: bump_version
|
needs: bump_version
|
||||||
secrets: inherit
|
permissions:
|
||||||
|
contents: read
|
||||||
|
secrets:
|
||||||
|
KEY_JKS: ${{ secrets.KEY_JKS }}
|
||||||
|
ALIAS: ${{ secrets.ALIAS }}
|
||||||
|
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||||
|
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
|
||||||
with:
|
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@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2
|
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||||
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 }}
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
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@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||||
with:
|
with:
|
||||||
name: release-apk-signed
|
name: release-apk-signed
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Create draft release
|
||||||
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2
|
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
||||||
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: |
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ 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
|
||||||
@@ -11,7 +13,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2
|
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2
|
||||||
with:
|
with:
|
||||||
message-id: 'preview-status'
|
message-id: 'preview-status'
|
||||||
message: 'Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.cloud/'
|
message: 'Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.cloud/'
|
||||||
@@ -22,7 +24,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
github.rest.issues.removeLabel({
|
github.rest.issues.removeLabel({
|
||||||
|
|||||||
@@ -4,20 +4,24 @@ 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.2.2
|
||||||
|
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@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
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'
|
||||||
|
|||||||
@@ -9,16 +9,22 @@ 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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
mobile:
|
mobile:
|
||||||
@@ -33,15 +39,17 @@ 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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -59,7 +67,7 @@ jobs:
|
|||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
id: verify-changed-files
|
id: verify-changed-files
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
@@ -69,9 +77,11 @@ 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: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
echo "Changed files: ${CHANGED_FILES}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Run dart analyze
|
- name: Run dart analyze
|
||||||
@@ -85,3 +95,30 @@ 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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Install the latest version of uv
|
||||||
|
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||||
|
|
||||||
|
- name: Run zizmor 🌈
|
||||||
|
run: uvx zizmor --format=sarif . > results.sarif
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Upload SARIF file
|
||||||
|
uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
||||||
|
with:
|
||||||
|
sarif_file: results.sarif
|
||||||
|
category: zizmor
|
||||||
|
|||||||
+210
-47
@@ -9,10 +9,15 @@ 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_i18n: ${{ steps.found_paths.outputs.i18n == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
should_run_web: ${{ steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_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' }}
|
||||||
should_run_cli: ${{ steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run_cli: ${{ steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
@@ -24,11 +29,16 @@ jobs:
|
|||||||
should_run_.github: ${{ steps.found_paths.outputs['.github'] == 'true' || steps.should_force.outputs.should_force == 'true' }} # redundant to have should_force but if someone changes the trigger then this won't have to be changed
|
should_run_.github: ${{ steps.found_paths.outputs['.github'] == 'true' || steps.should_force.outputs.should_force == 'true' }} # redundant to have should_force but if someone changes the trigger then this won't have to be changed
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
|
i18n:
|
||||||
|
- 'i18n/**'
|
||||||
web:
|
web:
|
||||||
- 'web/**'
|
- 'web/**'
|
||||||
- 'i18n/**'
|
- 'i18n/**'
|
||||||
@@ -58,16 +68,20 @@ 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
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -95,16 +109,20 @@ 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
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
|
||||||
@@ -136,16 +154,20 @@ 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
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
|
||||||
@@ -165,21 +187,25 @@ jobs:
|
|||||||
run: npm run test:cov
|
run: npm run test:cov
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
web-unit-tests:
|
web-lint:
|
||||||
name: Test & Lint Web
|
name: 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: ubuntu-latest
|
runs-on: mich
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./web
|
working-directory: ./web
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
|
|
||||||
@@ -191,7 +217,7 @@ jobs:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: npm run lint
|
run: npm run lint:p
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
@@ -202,6 +228,35 @@ 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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
|
with:
|
||||||
|
node-version-file: './web/.nvmrc'
|
||||||
|
|
||||||
|
- name: Run setup typescript-sdk
|
||||||
|
run: npm ci && npm run build
|
||||||
|
working-directory: ./open-api/typescript-sdk
|
||||||
|
|
||||||
|
- name: Run npm install
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: npm run check:typescript
|
run: npm run check:typescript
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
@@ -210,21 +265,65 @@ jobs:
|
|||||||
run: npm run test:cov
|
run: npm run test:cov
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
|
i18n-tests:
|
||||||
|
name: Test i18n
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_i18n == 'true' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
|
with:
|
||||||
|
node-version-file: './web/.nvmrc'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm --prefix=web ci
|
||||||
|
|
||||||
|
- name: Format
|
||||||
|
run: npm --prefix=web run format:i18n
|
||||||
|
|
||||||
|
- name: Find file changes
|
||||||
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
|
id: verify-changed-files
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
i18n/**
|
||||||
|
|
||||||
|
- name: Verify files have not changed
|
||||||
|
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||||
|
env:
|
||||||
|
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
|
||||||
|
run: |
|
||||||
|
echo "ERROR: i18n files not up to date!"
|
||||||
|
echo "Changed files: ${CHANGED_FILES}"
|
||||||
|
exit 1
|
||||||
|
|
||||||
e2e-tests-lint:
|
e2e-tests-lint:
|
||||||
name: End-to-End Lint
|
name: End-to-End Lint
|
||||||
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
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -254,16 +353,20 @@ 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
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -278,19 +381,25 @@ 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: mich
|
runs-on: ${{ matrix.runner }}
|
||||||
|
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.2.2
|
||||||
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@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -320,19 +429,25 @@ 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: mich
|
runs-on: ${{ matrix.runner }}
|
||||||
|
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.2.2
|
||||||
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@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
@@ -357,15 +472,35 @@ 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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -378,14 +513,19 @@ 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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
|
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
# 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
|
||||||
@@ -411,16 +551,20 @@ 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
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './.github/.nvmrc'
|
node-version-file: './.github/.nvmrc'
|
||||||
|
|
||||||
@@ -434,25 +578,34 @@ 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.2.2
|
||||||
|
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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -466,7 +619,7 @@ jobs:
|
|||||||
run: make open-api
|
run: make open-api
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
id: verify-changed-files
|
id: verify-changed-files
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
@@ -476,14 +629,18 @@ 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: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
echo "Changed files: ${CHANGED_FILES}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
generated-typeorm-migrations-up-to-date:
|
sql-schema-up-to-date:
|
||||||
name: TypeORM Checks
|
name: SQL Schema Checks
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
@@ -504,10 +661,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
|
||||||
@@ -521,23 +680,25 @@ 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 typeorm:schema:reset
|
run: npm run schema:reset
|
||||||
|
|
||||||
- name: Generate new migrations
|
- name: Generate new migrations
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: npm run migrations:generate TestMigration
|
run: npm run migrations:generate src/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.0.4
|
||||||
id: verify-changed-files
|
id: verify-changed-files
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
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: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
echo "Changed files: ${CHANGED_FILES}"
|
||||||
cat ./src/*-TestMigration.ts
|
cat ./src/*-TestMigration.ts
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
@@ -547,7 +708,7 @@ jobs:
|
|||||||
DB_URL: postgres://postgres:postgres@localhost:5432/immich
|
DB_URL: postgres://postgres:postgres@localhost:5432/immich
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
id: verify-changed-sql-files
|
id: verify-changed-sql-files
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
@@ -555,9 +716,11 @@ 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: ${{ steps.verify-changed-sql-files.outputs.changed_files }}"
|
echo "Changed files: ${CHANGED_FILES}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
# mobile-integration-tests:
|
# mobile-integration-tests:
|
||||||
|
|||||||
@@ -4,30 +4,32 @@ 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.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- id: found_paths
|
- id: found_paths
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
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
|
||||||
@@ -36,7 +38,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Find Pull Request
|
- name: Find Pull Request
|
||||||
uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1
|
uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1.9.0
|
||||||
id: find-pr
|
id: find-pr
|
||||||
with:
|
with:
|
||||||
branch: chore/translations
|
branch: chore/translations
|
||||||
@@ -47,6 +49,7 @@ 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?
|
||||||
@@ -54,4 +57,5 @@ 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) }}"
|
||||||
|
|||||||
Vendored
+48
-30
@@ -1,45 +1,63 @@
|
|||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"[javascript]": {
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
||||||
"editor.tabSize": 2,
|
|
||||||
"editor.formatOnSave": true
|
|
||||||
},
|
|
||||||
"[typescript]": {
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
||||||
"editor.tabSize": 2,
|
|
||||||
"editor.formatOnSave": true
|
|
||||||
},
|
|
||||||
"[css]": {
|
"[css]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.tabSize": 2,
|
"editor.formatOnSave": true,
|
||||||
"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"
|
|
||||||
},
|
},
|
||||||
"cSpell.words": [
|
"[javascript]": {
|
||||||
"immich"
|
"editor.codeActionsOnSave": {
|
||||||
],
|
"source.organizeImports": "explicit",
|
||||||
|
"source.removeUnusedImports": "explicit"
|
||||||
|
},
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"[json]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"[jsonc]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"[svelte]": {
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": "explicit",
|
||||||
|
"source.removeUnusedImports": "explicit"
|
||||||
|
},
|
||||||
|
"editor.defaultFormatter": "svelte.svelte-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": "explicit",
|
||||||
|
"source.removeUnusedImports": "explicit"
|
||||||
|
},
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"cSpell.words": ["immich"],
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"eslint.validate": ["javascript", "svelte"],
|
||||||
"explorer.fileNesting.enabled": true,
|
"explorer.fileNesting.enabled": true,
|
||||||
"explorer.fileNesting.patterns": {
|
"explorer.fileNesting.patterns": {
|
||||||
"*.ts": "${capture}.spec.ts,${capture}.mock.ts",
|
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
|
||||||
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart"
|
"*.ts": "${capture}.spec.ts,${capture}.mock.ts"
|
||||||
}
|
},
|
||||||
|
"svelte.enable-ts-plugin": true,
|
||||||
|
"typescript.preferences.importModuleSpecifier": "non-relative"
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,9 @@ 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
-1
@@ -1 +1 @@
|
|||||||
22.14.0
|
22.15.0
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
FROM node:22.14.0-alpine3.20@sha256:40be979442621049f40b1d51a26b55e281246b5de4e5f51a18da7beb6e17e3f9 AS core
|
FROM node:22.15.0-alpine3.20@sha256:686b8892b69879ef5bfd6047589666933508f9a5451c67320df3070ba0e9807b 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 ./
|
||||||
|
|||||||
Generated
+1180
-158
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.61",
|
"version": "2.2.65",
|
||||||
"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.0",
|
"@types/node": "^22.15.16",
|
||||||
"@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",
|
||||||
@@ -69,6 +69,6 @@
|
|||||||
"micromatch": "^4.0.8"
|
"micromatch": "^4.0.8"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.14.0"
|
"node": "22.15.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1
|
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1
|
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
@@ -90,7 +90,7 @@ services:
|
|||||||
container_name: immich_prometheus
|
container_name: immich_prometheus
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- 9090:9090
|
||||||
image: prom/prometheus@sha256:502ad90314c7485892ce696cb14a99fceab9fc27af29f4b427f41bd39701a199
|
image: prom/prometheus@sha256:e2b8aa62b64855956e3ec1e18b4f9387fb6203174a4471936f4662f437f04405
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
@@ -102,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.0-ubuntu@sha256:fd8fa48213c624e1a95122f1d93abbf1cf1cbe85fc73212c1e599dbd76c63ff8
|
image: grafana/grafana:11.6.1-ubuntu@sha256:6fc273288470ef499dd3c6b36aeade093170d4f608f864c5dd3a7fabeae77b50
|
||||||
volumes:
|
volumes:
|
||||||
- grafana-data:/var/lib/grafana
|
- grafana-data:/var/lib/grafana
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1
|
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
22.14.0
|
22.15.0
|
||||||
|
|||||||
@@ -23,23 +23,32 @@ Refer to the official [postgres documentation](https://www.postgresql.org/docs/c
|
|||||||
It is not recommended to directly backup the `DB_DATA_LOCATION` folder. Doing so while the database is running can lead to a corrupted backup that cannot be restored.
|
It is not recommended to directly backup the `DB_DATA_LOCATION` folder. Doing so while the database is running can lead to a corrupted backup that cannot be restored.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Automatic Database Backups
|
### Automatic Database Dumps
|
||||||
|
|
||||||
For convenience, Immich will automatically create database backups by default. The backups are stored in `UPLOAD_LOCATION/backups`.
|
:::warning
|
||||||
As mentioned above, you should make your own backup of these together with the asset folders as noted below.
|
The automatic database dumps can be used to restore the database in the event of damage to the Postgres database files.
|
||||||
You can adjust the schedule and amount of kept backups in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
|
There is no monitoring for these dumps and you will not be notified if they are unsuccessful.
|
||||||
By default, Immich will keep the last 14 backups and create a new backup every day at 2:00 AM.
|
:::
|
||||||
|
|
||||||
#### Trigger Backup
|
:::caution
|
||||||
|
The database dumps do **NOT** contain any pictures or videos, only metadata. They are only usable with a copy of the other files in `UPLOAD_LOCATION` as outlined below.
|
||||||
|
:::
|
||||||
|
|
||||||
You are able to trigger a backup in the [admin job status page](http://my.immich.app/admin/jobs-status).
|
For disaster-recovery purposes, Immich will automatically create database dumps. The dumps are stored in `UPLOAD_LOCATION/backups`.
|
||||||
Visit the page, open the "Create job" modal from the top right, select "Backup Database" and click "Confirm".
|
Please be sure to make your own, independent backup of the database together with the asset folders as noted below.
|
||||||
A job will run and trigger a backup, you can verify this worked correctly by checking the logs or the backup folder.
|
You can adjust the schedule and amount of kept database dumps in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
|
||||||
This backup will count towards the last X backups that will be kept based on your settings.
|
By default, Immich will keep the last 14 database dumps and create a new dump every day at 2:00 AM.
|
||||||
|
|
||||||
|
#### Trigger Dump
|
||||||
|
|
||||||
|
You are able to trigger a database dump in the [admin job status page](http://my.immich.app/admin/jobs-status).
|
||||||
|
Visit the page, open the "Create job" modal from the top right, select "Create Database Dump" and click "Confirm".
|
||||||
|
A job will run and trigger a dump, you can verify this worked correctly by checking the logs or the `backups/` folder.
|
||||||
|
This dumps will count towards the last `X` dumps that will be kept based on your settings.
|
||||||
|
|
||||||
#### Restoring
|
#### Restoring
|
||||||
|
|
||||||
We hope to make restoring simpler in future versions, for now you can find the backups in the `UPLOAD_LOCATION/backups` folder on your host.
|
We hope to make restoring simpler in future versions, for now you can find the database dumps in the `UPLOAD_LOCATION/backups` folder on your host.
|
||||||
Then please follow the steps in the following section for restoring the database.
|
Then please follow the steps in the following section for restoring the database.
|
||||||
|
|
||||||
### Manual Backup and Restore
|
### Manual Backup and Restore
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ server {
|
|||||||
client_max_body_size 50000M;
|
client_max_body_size 50000M;
|
||||||
|
|
||||||
# Set headers
|
# Set headers
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-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/entities`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration.
|
After making any changes in the `server/src/schema`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration.
|
||||||
|
|
||||||
1. Run the command
|
1. Run the command
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run typeorm:migrations:generate <migration-name>
|
npm run 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/migrations` in your code editor.
|
3. Move the migration file to folder `./server/src/schema/migrations` in your code editor.
|
||||||
|
|
||||||
The server will automatically detect `*.ts` file changes and restart. Part of the server start-up process includes running any new migrations, so it will be applied immediately.
|
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.
|
||||||
|
|||||||
@@ -121,6 +121,6 @@ Once this is done, you can continue to step 3 of "Basic Setup".
|
|||||||
|
|
||||||
[hw-file]: https://github.com/immich-app/immich/releases/latest/download/hwaccel.transcoding.yml
|
[hw-file]: https://github.com/immich-app/immich/releases/latest/download/hwaccel.transcoding.yml
|
||||||
[nvct]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
|
[nvct]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
|
||||||
[jellyfin-lp]: https://jellyfin.org/docs/general/administration/hardware-acceleration/intel/#configure-and-verify-lp-mode-on-linux
|
[jellyfin-lp]: https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#low-power-encoding
|
||||||
[jellyfin-kernel-bug]: https://jellyfin.org/docs/general/administration/hardware-acceleration/intel/#known-issues-and-limitations
|
[jellyfin-kernel-bug]: https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#known-issues-and-limitations-on-linux
|
||||||
[libmali-rockchip]: https://github.com/tsukumijima/libmali-rockchip/releases
|
[libmali-rockchip]: https://github.com/tsukumijima/libmali-rockchip/releases
|
||||||
|
|||||||
@@ -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 managment page.
|
There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page.
|
||||||
|
|
||||||
## Usage
|
## 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 >= 535 (it must support CUDA 12.2).
|
- The installed driver must be >= 545 (it must support CUDA 12.3).
|
||||||
- On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed.
|
- On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed.
|
||||||
|
|
||||||
#### ROCm
|
#### ROCm
|
||||||
|
|||||||
@@ -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 afer performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors.
|
**Memory (MiB)**: The peak RSS usage of the process after performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors.
|
||||||
|
|
||||||
**Recall (%)**: Evaluated on Crossmodal-3600, the average of the recall@1, recall@5 and recall@10 results for zeroshot image retrieval. Chinese (Simplified), English, French, German, Italian, Japanese, Korean, Polish, Russian, Spanish and Turkish are additionally tested on XTD-10. Chinese (Simplified) and English are additionally tested on Flickr30k. The recall metrics are the average across all tested datasets.
|
**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,53 +2,13 @@
|
|||||||
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.
|
||||||
|
|
||||||
## Step 1 - Download the required files
|
import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx';
|
||||||
|
|
||||||
Create a directory of your choice (e.g. `./immich-app`) to hold the `docker-compose.yml` and `.env` files.
|
<DockerComposeSteps />
|
||||||
|
|
||||||
```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.
|
||||||
@@ -70,6 +30,3 @@ 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
|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ Information on the current workers can be found [here](/docs/administration/jobs
|
|||||||
| `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 [`pgvector`, `pgvecto.rs`]) | `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 |
|
||||||
|
|
||||||
|
|||||||
@@ -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](./docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue.
|
Follow [Step 2 in Docker Compose](/docs/install/docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue.
|
||||||
|
|
||||||
## Step 3 - Create a new project in Container Manager
|
## Step 3 - Create a new project in Container Manager
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
sidebar_position: 80
|
sidebar_position: 80
|
||||||
---
|
---
|
||||||
|
|
||||||
# TrueNAS SCALE [Community]
|
# TrueNAS [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 SCALE via the **Community** train application.
|
Immich can easily be installed on TrueNAS Community Edition via the **Community** train application.
|
||||||
Consider reviewing the TrueNAS [Apps tutorial](https://www.truenas.com/docs/scale/scaletutorials/apps/) if you have not previously configured applications on your system.
|
Consider reviewing the TrueNAS [Apps resources](https://apps.truenas.com/getting-started/) if you have not previously configured applications on your system.
|
||||||
|
|
||||||
TrueNAS SCALE makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries.
|
TrueNAS Community Edition makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries.
|
||||||
|
|
||||||
## First Steps
|
## First Steps
|
||||||
|
|
||||||
The Immich app in TrueNAS SCALE installs, completes the initial configuration, then starts the Immich web portal.
|
The Immich app in TrueNAS Community Edition installs, completes the initial configuration, then starts the Immich web portal.
|
||||||
When updates become available, SCALE alerts and provides easy updates.
|
When updates become available, TrueNAS alerts and provides easy updates.
|
||||||
|
|
||||||
Before installing the Immich app in SCALE, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation.
|
Before installing the Immich app in TrueNAS, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation.
|
||||||
You may also configure environment variables at any time after deploying the application.
|
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 SCALE versions below TrueNAS Scale 24.10 Electric Eel.
|
Only numbers within the range 9000-65535 may be used on TrueNAS versions below TrueNAS Community Edition 24.10 Electric Eel.
|
||||||
|
|
||||||
Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/references/defaultports/).
|
Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/solutions/optimizations/security/#truenas-default-ports).
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Storage Configuration
|
### 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 SCALE server where your external library is located.
|
The **Host Path** is the location on the TrueNAS Community Edition server where your external library is located.
|
||||||
|
|
||||||
<!-- 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).
|
||||||
|
|
||||||
Accept the default **Memory** limit of `4096` MB or specify the number of MB of RAM. If you're using Machine Learning you should probably set this above 8000 MB.
|
Specify the **Memory** limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB.
|
||||||
|
|
||||||
:::info Older SCALE Versions
|
:::info Older TrueNAS Versions
|
||||||
Before TrueNAS SCALE version 24.10 Electric Eel:
|
Before TrueNAS Community Edition version 24.10 Electric Eel:
|
||||||
|
|
||||||
The **CPU** value was specified in a different format with a default of `4000m` which is 4 threads.
|
The **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://www.truenas.com/docs/truenasapps/#gpu-passthrough)
|
Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough)
|
||||||
|
|
||||||
### Install
|
### Install
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ className="border rounded-xl"
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
Some Environment Variables are not available for the TrueNAS SCALE app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings).
|
Some Environment Variables are not available for the TrueNAS Community Edition app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings).
|
||||||
|
|
||||||
Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`.
|
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, SCALE alerts and provides easy updates.
|
When updates become available, TrueNAS 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: 2
|
sidebar_position: 3
|
||||||
---
|
---
|
||||||
|
|
||||||
# Comparison
|
# Comparison
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 233 KiB |
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
sidebar_position: 3
|
sidebar_position: 2
|
||||||
---
|
---
|
||||||
|
|
||||||
# Quick start
|
# Quick start
|
||||||
@@ -10,11 +10,20 @@ to install and use it.
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Check the [requirements page](/docs/install/requirements) to get started.
|
- A system with at least 4GB of RAM and 2 CPU cores.
|
||||||
|
- [Docker](https://docs.docker.com/engine/install/)
|
||||||
|
|
||||||
|
> For a more detailed list of requirements, see the [requirements page](/docs/install/requirements).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Set up the server
|
## Set up the server
|
||||||
|
|
||||||
Follow the [Docker Compose (Recommended)](/docs/install/docker-compose) instructions to install the server.
|
import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx';
|
||||||
|
|
||||||
|
<DockerComposeSteps />
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Try the web app
|
## Try the web app
|
||||||
|
|
||||||
@@ -26,6 +35,8 @@ 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
|
||||||
@@ -56,6 +67,8 @@ 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
|
||||||
@@ -65,6 +78,8 @@ Immich has built-in database backups. You can refer to the
|
|||||||
The database only contains metadata and user information. You must setup manual backups of the images and videos stored in `UPLOAD_LOCATION`.
|
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.
|
||||||
|
|||||||
@@ -2,9 +2,13 @@
|
|||||||
sidebar_position: 1
|
sidebar_position: 1
|
||||||
---
|
---
|
||||||
|
|
||||||
# Introduction
|
# Welcome to Immich
|
||||||
|
|
||||||
<img src={require('./img/feature-panel.webp').default} alt="Immich - Self-hosted photos and videos backup tool" />
|
<img
|
||||||
|
src={require('./img/social-preview-light.webp').default}
|
||||||
|
alt="Immich - Self-hosted photos and videos backup tool"
|
||||||
|
data-theme="light"
|
||||||
|
/>
|
||||||
|
|
||||||
## Welcome!
|
## Welcome!
|
||||||
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import CodeBlock from '@theme/CodeBlock';
|
||||||
|
import ExampleEnv from '!!raw-loader!../../../docker/example.env';
|
||||||
|
|
||||||
|
### Step 1 - Download the required files
|
||||||
|
|
||||||
|
Create a directory of your choice (e.g. `./immich-app`) to hold the `docker-compose.yml` and `.env` files.
|
||||||
|
|
||||||
|
```bash title="Move to the directory you created"
|
||||||
|
mkdir ./immich-app
|
||||||
|
cd ./immich-app
|
||||||
|
```
|
||||||
|
|
||||||
|
Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) by running the following commands:
|
||||||
|
|
||||||
|
```bash title="Get docker-compose.yml file"
|
||||||
|
wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash title="Get .env file"
|
||||||
|
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||||
|
```
|
||||||
|
|
||||||
|
You can alternatively download these two files from your browser and move them to the directory that you created, in which case ensure that you rename `example.env` to `.env`.
|
||||||
|
|
||||||
|
### Step 2 - Populate the .env file with custom values
|
||||||
|
|
||||||
|
<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/introduction',
|
to: '/docs/overview/welcome',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
label: 'Docs',
|
label: 'Docs',
|
||||||
},
|
},
|
||||||
@@ -124,6 +124,12 @@ 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: {
|
||||||
@@ -134,7 +140,7 @@ const config = {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'Welcome',
|
label: 'Welcome',
|
||||||
to: '/docs/overview/introduction',
|
to: '/docs/overview/welcome',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Installation',
|
label: 'Installation',
|
||||||
|
|||||||
+1
-1
@@ -57,6 +57,6 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.14.0"
|
"node": "22.15.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,8 +40,9 @@ const projects: CommunityProjectProps[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Lightroom Immich Plugin: lrc-immich-plugin',
|
title: 'Lightroom Immich Plugin: lrc-immich-plugin',
|
||||||
description: 'Another Lightroom plugin to publish or export photos from Lightroom to Immich.',
|
description:
|
||||||
url: 'https://github.com/bmachek/lrc-immich-plugin',
|
'Lightroom plugin to publish, export photos from Lightroom to Immich. Import from Immich to Lightroom is also supported.',
|
||||||
|
url: 'https://blog.fokuspunk.de/lrc-immich-plugin/',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Immich Duplicate Finder',
|
title: 'Immich Duplicate Finder',
|
||||||
|
|||||||
+26
-10
@@ -7,14 +7,22 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
@font-face {
|
||||||
|
font-family: 'Overpass';
|
||||||
body {
|
src: url('/fonts/overpass/Overpass.ttf') format('truetype-variations');
|
||||||
font-family: 'Be Vietnam Pro', serif;
|
font-weight: 1 999;
|
||||||
font-optical-sizing: auto;
|
font-style: normal;
|
||||||
/* font-size: 1.125rem;
|
|
||||||
ascent-override: 106.25%;
|
ascent-override: 106.25%;
|
||||||
size-adjust: 106.25%; */
|
size-adjust: 106.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Overpass Mono';
|
||||||
|
src: url('/fonts/overpass/OverpassMono.ttf') format('truetype-variations');
|
||||||
|
font-weight: 1 999;
|
||||||
|
font-style: normal;
|
||||||
|
ascent-override: 106.25%;
|
||||||
|
size-adjust: 106.25%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumbs__link {
|
.breadcrumbs__link {
|
||||||
@@ -29,6 +37,7 @@ 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;
|
||||||
@@ -59,14 +68,12 @@ div[class^='announcementBar_'] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu__link {
|
.menu__link {
|
||||||
padding: 10px;
|
padding: 10px 10px 10px 16px;
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -83,3 +90,12 @@ 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,6 +2,7 @@ import {
|
|||||||
mdiBug,
|
mdiBug,
|
||||||
mdiCalendarToday,
|
mdiCalendarToday,
|
||||||
mdiCrosshairsOff,
|
mdiCrosshairsOff,
|
||||||
|
mdiCrop,
|
||||||
mdiDatabase,
|
mdiDatabase,
|
||||||
mdiLeadPencil,
|
mdiLeadPencil,
|
||||||
mdiLockOff,
|
mdiLockOff,
|
||||||
@@ -22,6 +23,18 @@ 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',
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Errors
|
||||||
|
|
||||||
|
## TypeORM Upgrade
|
||||||
|
|
||||||
|
The upgrade to Immich `v2.x.x` has a required upgrade path to `v1.132.0+`. This means it is required to start up the application at least once on version `1.132.0` (or later). Doing so will complete database schema upgrades that are required for `v2.0.0`. After Immich has successfully booted on this version, shut the system down and try the `v2.x.x` upgrade again.
|
||||||
+25
-20
@@ -4,6 +4,7 @@ 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>
|
||||||
@@ -12,11 +13,14 @@ 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">
|
||||||
<ThemedImage
|
<a href="https://futo.org" target="_blank" rel="noopener noreferrer">
|
||||||
sources={{ dark: 'img/logomark-dark.svg', light: 'img/logomark-light.svg' }}
|
<ThemedImage
|
||||||
className="h-[115px] w-[115px] mb-2 antialiased rounded-none"
|
sources={{ dark: 'img/logomark-dark-with-futo.svg', light: 'img/logomark-light-with-futo.svg' }}
|
||||||
alt="Immich logo"
|
className="h-[125px] w-[125px] mb-2 antialiased rounded-none"
|
||||||
/>
|
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{' '}
|
||||||
@@ -27,7 +31,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">
|
<p className="max-w-1/4 m-auto mt-4 px-4 text-lg text-gray-700 dark:text-gray-100">
|
||||||
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.
|
||||||
@@ -35,27 +39,21 @@ 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 uppercase"
|
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"
|
||||||
to="docs/overview/introduction"
|
to="docs/overview/quick-start"
|
||||||
>
|
>
|
||||||
Get started
|
Get Started
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<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"
|
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"
|
||||||
to="https://demo.immich.app/"
|
to="https://demo.immich.app/"
|
||||||
>
|
>
|
||||||
Demo
|
Open 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. */
|
||||||
@@ -88,11 +86,18 @@ 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' }}
|
||||||
@@ -111,7 +116,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-xs">Privacy should not be a luxury</p>
|
<p className="text-sm">Privacy should not be a luxury</p>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
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,6 +76,7 @@ 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';
|
||||||
@@ -83,6 +84,8 @@ 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),
|
||||||
@@ -242,6 +245,13 @@ 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',
|
||||||
@@ -249,6 +259,14 @@ 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',
|
||||||
@@ -266,8 +284,8 @@ const milestones: Item[] = [
|
|||||||
withRelease({
|
withRelease({
|
||||||
icon: mdiDatabaseOutline,
|
icon: mdiDatabaseOutline,
|
||||||
iconColor: 'brown',
|
iconColor: 'brown',
|
||||||
title: 'Automatic database backups',
|
title: 'Automatic database dumps',
|
||||||
description: 'Database backups are now integrated into the Immich server',
|
description: 'Database dumps are now integrated into the Immich server',
|
||||||
release: 'v1.120.0',
|
release: 'v1.120.0',
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@@ -300,7 +318,7 @@ const milestones: Item[] = [
|
|||||||
withRelease({
|
withRelease({
|
||||||
icon: mdiFolderMultiple,
|
icon: mdiFolderMultiple,
|
||||||
iconColor: 'brown',
|
iconColor: 'brown',
|
||||||
title: 'Folders',
|
title: 'Folders view',
|
||||||
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',
|
||||||
}),
|
}),
|
||||||
|
|||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
Policy: https://github.com/immich-app/immich/blob/main/SECURITY.md
|
||||||
|
Contact: mailto:security@immich.app
|
||||||
|
Preferred-Languages: en
|
||||||
|
Expires: 2026-05-01T23:59:00.000Z
|
||||||
|
Canonical: https://immich.app/.well-known/security.txt
|
||||||
Vendored
+1
@@ -30,3 +30,4 @@
|
|||||||
/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
|
||||||
Vendored
+4
-152
@@ -1,36 +1,16 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"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"
|
||||||
@@ -47,46 +27,14 @@
|
|||||||
"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"
|
||||||
@@ -95,18 +43,6 @@
|
|||||||
"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"
|
||||||
@@ -115,34 +51,14 @@
|
|||||||
"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"
|
||||||
@@ -151,14 +67,6 @@
|
|||||||
"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"
|
||||||
@@ -171,18 +79,10 @@
|
|||||||
"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"
|
||||||
@@ -195,14 +95,6 @@
|
|||||||
"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"
|
||||||
@@ -211,38 +103,14 @@
|
|||||||
"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"
|
||||||
@@ -251,26 +119,10 @@
|
|||||||
"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
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+13
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 14 KiB |
+22
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 75 KiB |
+21
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 75 KiB |
+1
-1
@@ -1 +1 @@
|
|||||||
22.14.0
|
22.15.0
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ services:
|
|||||||
- 2285:2285
|
- 2285:2285
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
||||||
|
|
||||||
database:
|
database:
|
||||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||||
|
|||||||
Generated
+1009
-380
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.131.3",
|
"version": "1.132.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -25,9 +25,9 @@
|
|||||||
"@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.0",
|
"@types/node": "^22.15.16",
|
||||||
"@types/oidc-provider": "^8.5.1",
|
"@types/oidc-provider": "^8.5.1",
|
||||||
"@types/pg": "^8.11.0",
|
"@types/pg": "^8.15.1",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
@@ -52,6 +52,6 @@
|
|||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.14.0"
|
"node": "22.15.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,38 +46,6 @@ 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')
|
||||||
@@ -192,30 +160,6 @@ 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')
|
||||||
@@ -330,20 +274,6 @@ 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, uuidDto } from 'src/fixtures';
|
import { createUserDto } 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,28 +128,6 @@ 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`)
|
||||||
@@ -323,12 +301,6 @@ 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`)
|
||||||
@@ -421,12 +393,6 @@ 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')
|
||||||
@@ -438,12 +404,6 @@ 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')
|
||||||
@@ -471,12 +431,6 @@ 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)
|
||||||
@@ -526,14 +480,6 @@ 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',
|
||||||
@@ -576,15 +522,6 @@ 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`)
|
||||||
@@ -679,13 +616,6 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).put(`/albums/${user1Albums[0].id}/users`).send({ sharedUserIds: [] });
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to add user to own album', async () => {
|
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, uuidDto } from 'src/fixtures';
|
import { createUserDto } 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,12 +24,6 @@ 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' });
|
||||||
@@ -99,12 +93,6 @@ 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([]);
|
||||||
@@ -125,12 +113,6 @@ 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)
|
||||||
@@ -140,14 +122,6 @@ 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)
|
||||||
@@ -165,12 +139,6 @@ 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)
|
||||||
@@ -181,15 +149,6 @@ 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)
|
||||||
@@ -208,12 +167,6 @@ 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)
|
||||||
@@ -223,14 +176,6 @@ 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,6 +3,7 @@ import {
|
|||||||
AssetMediaStatus,
|
AssetMediaStatus,
|
||||||
AssetResponseDto,
|
AssetResponseDto,
|
||||||
AssetTypeEnum,
|
AssetTypeEnum,
|
||||||
|
AssetVisibility,
|
||||||
getAssetInfo,
|
getAssetInfo,
|
||||||
getMyUser,
|
getMyUser,
|
||||||
LoginResponseDto,
|
LoginResponseDto,
|
||||||
@@ -22,27 +23,9 @@ 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 facesAssetFilepath = `${testAssetDir}/metadata/faces/portrait.jpg`;
|
const facesAssetDir = `${testAssetDir}/metadata/faces`;
|
||||||
|
|
||||||
const readTags = async (bytes: Buffer, filename: string) => {
|
const readTags = async (bytes: Buffer, filename: string) => {
|
||||||
const filepath = join(tempDir, filename);
|
const filepath = join(tempDir, filename);
|
||||||
@@ -137,9 +120,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, { isArchived: true }),
|
utils.createAsset(statsUser.accessToken, { visibility: AssetVisibility.Archive }),
|
||||||
utils.createAsset(statsUser.accessToken, {
|
utils.createAsset(statsUser.accessToken, {
|
||||||
isArchived: true,
|
visibility: AssetVisibility.Archive,
|
||||||
isFavorite: true,
|
isFavorite: true,
|
||||||
assetData: { filename: 'example.mp4' },
|
assetData: { filename: 'example.mp4' },
|
||||||
}),
|
}),
|
||||||
@@ -160,13 +143,6 @@ 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`)
|
||||||
@@ -178,20 +154,6 @@ 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}`)
|
||||||
@@ -224,31 +186,22 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get the asset faces', async () => {
|
describe('faces', () => {
|
||||||
const config = await utils.getSystemConfig(admin.accessToken);
|
const metadataFaceTests = [
|
||||||
config.metadata.faces.import = true;
|
{
|
||||||
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
description: 'without orientation',
|
||||||
|
|
||||||
// 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',
|
||||||
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: facesAsset.id });
|
filename: 'portrait-orientation-6.jpg',
|
||||||
|
},
|
||||||
const { status, body } = await request(app)
|
];
|
||||||
.get(`/assets/${facesAsset.id}`)
|
// should produce same resulting face region coordinates for any orientation
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
const expectedFaces = [
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body.id).toEqual(facesAsset.id);
|
|
||||||
expect(body.people).toMatchObject([
|
|
||||||
{
|
{
|
||||||
name: 'Marie Curie',
|
name: 'Marie Curie',
|
||||||
birthDate: null,
|
birthDate: null,
|
||||||
thumbnailPath: '',
|
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
faces: [
|
faces: [
|
||||||
{
|
{
|
||||||
@@ -265,7 +218,6 @@ describe('/asset', () => {
|
|||||||
{
|
{
|
||||||
name: 'Pierre Curie',
|
name: 'Pierre Curie',
|
||||||
birthDate: null,
|
birthDate: null,
|
||||||
thumbnailPath: '',
|
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
faces: [
|
faces: [
|
||||||
{
|
{
|
||||||
@@ -279,7 +231,30 @@ describe('/asset', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
it.each(metadataFaceTests)('should get the asset faces from $filename $description', async ({ filename }) => {
|
||||||
|
const config = await utils.getSystemConfig(admin.accessToken);
|
||||||
|
config.metadata.faces.import = true;
|
||||||
|
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
const facesAsset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: await readFile(`${facesAssetDir}/${filename}`),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: facesAsset.id });
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get(`/assets/${facesAsset.id}`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.id).toEqual(facesAsset.id);
|
||||||
|
expect(body.people).toMatchObject(expectedFaces);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with a shared link', async () => {
|
it('should work with a shared link', async () => {
|
||||||
@@ -333,7 +308,7 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('disallows viewing archived assets', async () => {
|
it('disallows viewing archived assets', async () => {
|
||||||
const asset = await utils.createAsset(user1.accessToken, { isArchived: true });
|
const asset = await utils.createAsset(user1.accessToken, { visibility: AssetVisibility.Archive });
|
||||||
|
|
||||||
const { status } = await request(app)
|
const { status } = await request(app)
|
||||||
.get(`/assets/${asset.id}`)
|
.get(`/assets/${asset.id}`)
|
||||||
@@ -354,13 +329,6 @@ 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')
|
||||||
@@ -384,7 +352,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({ isArchived: true });
|
.query({ visibility: AssetVisibility.Archive });
|
||||||
|
|
||||||
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 });
|
||||||
@@ -394,7 +362,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, isArchived: true });
|
.query({ isFavorite: true, visibility: AssetVisibility.Archive });
|
||||||
|
|
||||||
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 });
|
||||||
@@ -404,7 +372,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, isArchived: false });
|
.query({ isFavorite: false, visibility: AssetVisibility.Timeline });
|
||||||
|
|
||||||
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 });
|
||||||
@@ -425,13 +393,6 @@ 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')
|
||||||
@@ -467,31 +428,9 @@ 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}`)
|
||||||
@@ -519,7 +458,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({ isArchived: true });
|
.send({ visibility: AssetVisibility.Archive });
|
||||||
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);
|
||||||
});
|
});
|
||||||
@@ -619,28 +558,6 @@ 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}`)
|
||||||
@@ -712,17 +629,6 @@ 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}`)
|
||||||
@@ -746,25 +652,6 @@ 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`)
|
||||||
@@ -877,13 +764,6 @@ 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',
|
||||||
@@ -919,13 +799,6 @@ 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`)
|
||||||
@@ -946,43 +819,9 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /assets', () => {
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).put('/assets');
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('POST /assets', () => {
|
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',
|
||||||
@@ -1244,31 +1083,21 @@ describe('/asset', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
it(`should upload and generate a thumbnail for different file types`, async () => {
|
it.each(tests)(`should upload and generate a thumbnail for different file types`, async ({ input, expected }) => {
|
||||||
// upload in parallel
|
const filepath = join(testAssetDir, input);
|
||||||
const assets = await Promise.all(
|
const response = await utils.createAsset(admin.accessToken, {
|
||||||
tests.map(async ({ input }) => {
|
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
||||||
const filepath = join(testAssetDir, input);
|
});
|
||||||
return utils.createAsset(admin.accessToken, {
|
|
||||||
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const { id, status } of assets) {
|
expect(response.status).toBe(AssetMediaStatus.Created);
|
||||||
expect(status).toBe(AssetMediaStatus.Created);
|
const id = response.id;
|
||||||
// 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 });
|
||||||
}
|
|
||||||
|
|
||||||
for (const [i, { id }] of assets.entries()) {
|
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
const { expected } = tests[i];
|
expect(asset.exifInfo).toBeDefined();
|
||||||
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
expect(asset.exifInfo).toMatchObject(expected.exifInfo);
|
||||||
|
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 () => {
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
import { deleteAssets, getAuditFiles, updateAsset, type LoginResponseDto } from '@immich/sdk';
|
|
||||||
import { asBearerAuth, utils } from 'src/utils';
|
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
|
||||||
|
|
||||||
describe('/audits', () => {
|
|
||||||
let admin: LoginResponseDto;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await utils.resetDatabase();
|
|
||||||
await utils.resetFilesystem();
|
|
||||||
|
|
||||||
admin = await utils.adminSetup();
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: Enable these tests again once #7436 is resolved as these were flaky
|
|
||||||
describe.skip('GET :/file-report', () => {
|
|
||||||
it('excludes assets without issues from report', async () => {
|
|
||||||
const [trashedAsset, archivedAsset] = await Promise.all([
|
|
||||||
utils.createAsset(admin.accessToken),
|
|
||||||
utils.createAsset(admin.accessToken),
|
|
||||||
utils.createAsset(admin.accessToken),
|
|
||||||
]);
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
deleteAssets({ assetBulkDeleteDto: { ids: [trashedAsset.id] } }, { headers: asBearerAuth(admin.accessToken) }),
|
|
||||||
updateAsset(
|
|
||||||
{
|
|
||||||
id: archivedAsset.id,
|
|
||||||
updateAssetDto: { isArchived: true },
|
|
||||||
},
|
|
||||||
{ headers: asBearerAuth(admin.accessToken) },
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const body = await getAuditFiles({
|
|
||||||
headers: asBearerAuth(admin.accessToken),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(body.orphans).toHaveLength(0);
|
|
||||||
expect(body.extras).toHaveLength(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -19,17 +19,6 @@ describe(`/auth/admin-sign-up`, () => {
|
|||||||
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 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 });
|
||||||
|
|
||||||
@@ -57,22 +46,6 @@ 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);
|
||||||
@@ -127,14 +100,6 @@ 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,6 +1,5 @@
|
|||||||
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';
|
||||||
@@ -17,15 +16,6 @@ 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')
|
||||||
@@ -42,15 +32,6 @@ 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 { LoginResponseDto } from '@immich/sdk';
|
import { AssetVisibility, 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({ isArchived: false })
|
.query({ visibility: AssetVisibility.Timeline })
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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';
|
||||||
@@ -21,18 +22,30 @@ 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 { url } = await startOAuth({ oAuthConfigDto: { redirectUri: redirectUri ?? `${baseUrl}/auth/login` } });
|
const state = randomBytes(16).toString('base64url');
|
||||||
|
const codeVerifier = randomBytes(64).toString('base64url');
|
||||||
|
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
||||||
|
|
||||||
|
const { url } = await startOAuth({
|
||||||
|
oAuthConfigDto: { redirectUri: redirectUri ?? `${baseUrl}/auth/login`, state, codeChallenge },
|
||||||
|
});
|
||||||
|
|
||||||
// login
|
// 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' });
|
||||||
@@ -40,7 +53,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' });
|
||||||
@@ -51,9 +64,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')).toBeDefined();
|
expect(params.get('state')).toBe(state);
|
||||||
|
|
||||||
return redirectUrl;
|
return { url: redirectUrl, state, codeVerifier };
|
||||||
};
|
};
|
||||||
|
|
||||||
const setupOAuth = async (token: string, dto: Partial<SystemConfigOAuthDto>) => {
|
const setupOAuth = async (token: string, dto: Partial<SystemConfigOAuthDto>) => {
|
||||||
@@ -119,9 +132,42 @@ describe(`/oauth`, () => {
|
|||||||
expect(body).toEqual(errorDto.badRequest(['url should not be empty']));
|
expect(body).toEqual(errorDto.badRequest(['url should not be empty']));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should auto register the user by default', async () => {
|
it(`should throw an error if the state is not provided`, async () => {
|
||||||
const url = await loginWithOAuth('oauth-auto-register');
|
const { url } = await loginWithOAuth('oauth-auto-register');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest('OAuth state is missing'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should throw an error if the state mismatches`, async () => {
|
||||||
|
const callbackParams = await loginWithOAuth('oauth-auto-register');
|
||||||
|
const { state } = await loginWithOAuth('oauth-auto-register');
|
||||||
|
const { status } = await request(app)
|
||||||
|
.post('/oauth/callback')
|
||||||
|
.send({ ...callbackParams, state });
|
||||||
|
expect(status).toBeGreaterThanOrEqual(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should throw an error if the codeVerifier is not provided`, async () => {
|
||||||
|
const { url, state } = await loginWithOAuth('oauth-auto-register');
|
||||||
|
const { status, body } = await request(app).post('/oauth/callback').send({ url, state });
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.badRequest('OAuth code verifier is missing'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should throw an error if the codeVerifier doesn't match the challenge`, async () => {
|
||||||
|
const callbackParams = await loginWithOAuth('oauth-auto-register');
|
||||||
|
const { codeVerifier } = await loginWithOAuth('oauth-auto-register');
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/oauth/callback')
|
||||||
|
.send({ ...callbackParams, codeVerifier });
|
||||||
|
console.log(body);
|
||||||
|
expect(status).toBeGreaterThanOrEqual(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should auto register the user by default', async () => {
|
||||||
|
const callbackParams = await loginWithOAuth('oauth-auto-register');
|
||||||
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
@@ -132,16 +178,30 @@ describe(`/oauth`, () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow passing state and codeVerifier via cookies', async () => {
|
||||||
|
const { url, state, codeVerifier } = await loginWithOAuth('oauth-auto-register');
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/oauth/callback')
|
||||||
|
.set('Cookie', [`immich_oauth_state=${state}`, `immich_oauth_code_verifier=${codeVerifier}`])
|
||||||
|
.send({ url });
|
||||||
|
expect(status).toBe(201);
|
||||||
|
expect(body).toMatchObject({
|
||||||
|
accessToken: expect.any(String),
|
||||||
|
userId: expect.any(String),
|
||||||
|
userEmail: 'oauth-auto-register@immich.app',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle a user without an email', async () => {
|
it('should handle a user without an email', async () => {
|
||||||
const url = await loginWithOAuth(OAuthUser.NO_EMAIL);
|
const callbackParams = await loginWithOAuth(OAuthUser.NO_EMAIL);
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
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 url = await loginWithOAuth(OAuthUser.WITH_QUOTA);
|
const callbackParams = await loginWithOAuth(OAuthUser.WITH_QUOTA);
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
@@ -154,8 +214,8 @@ describe(`/oauth`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should set the storage label from a claim', async () => {
|
it('should set the storage label from a claim', async () => {
|
||||||
const url = await loginWithOAuth(OAuthUser.WITH_USERNAME);
|
const callbackParams = await loginWithOAuth(OAuthUser.WITH_USERNAME);
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
@@ -176,8 +236,8 @@ describe(`/oauth`, () => {
|
|||||||
buttonText: 'Login with Immich',
|
buttonText: 'Login with Immich',
|
||||||
signingAlgorithm: 'RS256',
|
signingAlgorithm: 'RS256',
|
||||||
});
|
});
|
||||||
const url = await loginWithOAuth('oauth-RS256-token');
|
const callbackParams = await loginWithOAuth('oauth-RS256-token');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
@@ -196,8 +256,8 @@ describe(`/oauth`, () => {
|
|||||||
buttonText: 'Login with Immich',
|
buttonText: 'Login with Immich',
|
||||||
profileSigningAlgorithm: 'RS256',
|
profileSigningAlgorithm: 'RS256',
|
||||||
});
|
});
|
||||||
const url = await loginWithOAuth('oauth-signed-profile');
|
const callbackParams = await loginWithOAuth('oauth-signed-profile');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
userId: expect.any(String),
|
userId: expect.any(String),
|
||||||
@@ -213,8 +273,8 @@ describe(`/oauth`, () => {
|
|||||||
buttonText: 'Login with Immich',
|
buttonText: 'Login with Immich',
|
||||||
signingAlgorithm: 'something-that-does-not-work',
|
signingAlgorithm: 'something-that-does-not-work',
|
||||||
});
|
});
|
||||||
const url = await loginWithOAuth('oauth-signed-bad');
|
const callbackParams = await loginWithOAuth('oauth-signed-bad');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
expect(status).toBe(500);
|
expect(status).toBe(500);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
error: 'Internal Server Error',
|
error: 'Internal Server Error',
|
||||||
@@ -235,8 +295,8 @@ describe(`/oauth`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not auto register the user', async () => {
|
it('should not auto register the user', async () => {
|
||||||
const url = await loginWithOAuth('oauth-no-auto-register');
|
const callbackParams = await loginWithOAuth('oauth-no-auto-register');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
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.'));
|
||||||
});
|
});
|
||||||
@@ -247,8 +307,8 @@ describe(`/oauth`, () => {
|
|||||||
email: 'oauth-user3@immich.app',
|
email: 'oauth-user3@immich.app',
|
||||||
password: 'password',
|
password: 'password',
|
||||||
});
|
});
|
||||||
const url = await loginWithOAuth('oauth-user3');
|
const callbackParams = await loginWithOAuth('oauth-user3');
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
userId,
|
userId,
|
||||||
@@ -286,13 +346,15 @@ describe(`/oauth`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should auto register the user by default', async () => {
|
it('should auto register the user by default', async () => {
|
||||||
const url = await loginWithOAuth('oauth-mobile-override', 'app.immich:///oauth-callback');
|
const callbackParams = await loginWithOAuth('oauth-mobile-override', 'app.immich:///oauth-callback');
|
||||||
expect(url).toEqual(expect.stringContaining(mobileOverrideRedirectUri));
|
expect(callbackParams.url).toEqual(expect.stringContaining(mobileOverrideRedirectUri));
|
||||||
|
|
||||||
// simulate redirecting back to mobile app
|
// simulate redirecting back to mobile app
|
||||||
const redirectUri = url.replace(mobileOverrideRedirectUri, 'app.immich:///oauth-callback');
|
const url = callbackParams.url.replace(mobileOverrideRedirectUri, 'app.immich:///oauth-callback');
|
||||||
|
|
||||||
const { status, body } = await request(app).post('/oauth/callback').send({ url: redirectUri });
|
const { status, body } = await request(app)
|
||||||
|
.post('/oauth/callback')
|
||||||
|
.send({ ...callbackParams, url });
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
|
|||||||
@@ -5,22 +5,6 @@ import { app, asBearerAuth, utils } from 'src/utils';
|
|||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
const invalidBirthday = [
|
|
||||||
{
|
|
||||||
birthDate: 'false',
|
|
||||||
response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
birthDate: '123567',
|
|
||||||
response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
birthDate: 123_567,
|
|
||||||
response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'],
|
|
||||||
},
|
|
||||||
{ birthDate: '9999-01-01', response: ['Birth date cannot be in the future'] },
|
|
||||||
];
|
|
||||||
|
|
||||||
describe('/people', () => {
|
describe('/people', () => {
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
let visiblePerson: PersonResponseDto;
|
let visiblePerson: PersonResponseDto;
|
||||||
@@ -58,14 +42,6 @@ describe('/people', () => {
|
|||||||
|
|
||||||
describe('GET /people', () => {
|
describe('GET /people', () => {
|
||||||
beforeEach(async () => {});
|
beforeEach(async () => {});
|
||||||
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/people');
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return all people (including hidden)', async () => {
|
it('should return all people (including hidden)', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/people')
|
.get('/people')
|
||||||
@@ -117,13 +93,6 @@ describe('/people', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /people/:id', () => {
|
describe('GET /people/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get(`/people/${uuidDto.notFound}`);
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw error if person with id does not exist', async () => {
|
it('should throw error if person with id does not exist', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/people/${uuidDto.notFound}`)
|
.get(`/people/${uuidDto.notFound}`)
|
||||||
@@ -144,13 +113,6 @@ describe('/people', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /people/:id/statistics', () => {
|
describe('GET /people/:id/statistics', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get(`/people/${multipleAssetsPerson.id}/statistics`);
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw error if person with id does not exist', async () => {
|
it('should throw error if person with id does not exist', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/people/${uuidDto.notFound}/statistics`)
|
.get(`/people/${uuidDto.notFound}/statistics`)
|
||||||
@@ -171,23 +133,6 @@ describe('/people', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /people', () => {
|
describe('POST /people', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post(`/people`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const { birthDate, response } of invalidBirthday) {
|
|
||||||
it(`should not accept an invalid birth date [${birthDate}]`, async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post(`/people`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send({ birthDate });
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(response));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should create a person', async () => {
|
it('should create a person', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post(`/people`)
|
.post(`/people`)
|
||||||
@@ -223,39 +168,6 @@ describe('/people', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /people/:id', () => {
|
describe('PUT /people/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).put(`/people/${uuidDto.notFound}`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const { key, type } of [
|
|
||||||
{ key: 'name', type: 'string' },
|
|
||||||
{ key: 'featureFaceAssetId', type: 'string' },
|
|
||||||
{ key: 'isHidden', type: 'boolean value' },
|
|
||||||
{ key: 'isFavorite', type: 'boolean value' },
|
|
||||||
]) {
|
|
||||||
it(`should not allow null ${key}`, async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/people/${visiblePerson.id}`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send({ [key]: null });
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest([`${key} must be a ${type}`]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const { birthDate, response } of invalidBirthday) {
|
|
||||||
it(`should not accept an invalid birth date [${birthDate}]`, async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/people/${visiblePerson.id}`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send({ birthDate });
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(response));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should update a date of birth', async () => {
|
it('should update a date of birth', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/people/${visiblePerson.id}`)
|
.put(`/people/${visiblePerson.id}`)
|
||||||
@@ -312,12 +224,6 @@ describe('/people', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /people/:id/merge', () => {
|
describe('POST /people/:id/merge', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post(`/people/${uuidDto.notFound}/merge`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not supporting merging a person into themselves', async () => {
|
it('should not supporting merging a person into themselves', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.post(`/people/${visiblePerson.id}/merge`)
|
.post(`/people/${visiblePerson.id}/merge`)
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
import { AssetMediaResponseDto, AssetResponseDto, deleteAssets, LoginResponseDto, updateAsset } from '@immich/sdk';
|
import {
|
||||||
|
AssetMediaResponseDto,
|
||||||
|
AssetResponseDto,
|
||||||
|
AssetVisibility,
|
||||||
|
deleteAssets,
|
||||||
|
LoginResponseDto,
|
||||||
|
updateAsset,
|
||||||
|
} from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { 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';
|
||||||
@@ -50,7 +56,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: { isArchived: true } },
|
{ filename: '/metadata/gps-position/thompson-springs.jpg', dto: { visibility: AssetVisibility.Archive } },
|
||||||
|
|
||||||
// used for search suggestions
|
// used for search suggestions
|
||||||
{ filename: '/formats/png/density_plot.png' },
|
{ filename: '/formats/png/density_plot.png' },
|
||||||
@@ -141,65 +147,6 @@ 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',
|
||||||
@@ -231,12 +178,12 @@ describe('/search', () => {
|
|||||||
deferred: () => ({ dto: { size: 1, isFavorite: false }, assets: [assetLast] }),
|
deferred: () => ({ dto: { size: 1, isFavorite: false }, assets: [assetLast] }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
should: 'should search by isArchived (true)',
|
should: 'should search by visibility (AssetVisibility.Archive)',
|
||||||
deferred: () => ({ dto: { isArchived: true }, assets: [assetSprings] }),
|
deferred: () => ({ dto: { visibility: AssetVisibility.Archive }, assets: [assetSprings] }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
should: 'should search by isArchived (false)',
|
should: 'should search by visibility (AssetVisibility.Timeline)',
|
||||||
deferred: () => ({ dto: { size: 1, isArchived: false }, assets: [assetLast] }),
|
deferred: () => ({ dto: { size: 1, visibility: AssetVisibility.Timeline }, assets: [assetLast] }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
should: 'should search by type (image)',
|
should: 'should search by type (image)',
|
||||||
@@ -245,7 +192,7 @@ describe('/search', () => {
|
|||||||
{
|
{
|
||||||
should: 'should search by type (video)',
|
should: 'should search by type (video)',
|
||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: { type: 'VIDEO' },
|
dto: { type: 'VIDEO', visibility: AssetVisibility.Hidden },
|
||||||
assets: [
|
assets: [
|
||||||
// the three live motion photos
|
// the three live motion photos
|
||||||
{ id: expect.any(String) },
|
{ id: expect.any(String) },
|
||||||
@@ -289,13 +236,6 @@ 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: () => ({
|
||||||
@@ -325,7 +265,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
city: '',
|
city: '',
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -336,7 +276,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
city: null,
|
city: null,
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -357,7 +297,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
state: '',
|
state: '',
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
withExif: true,
|
withExif: true,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
@@ -369,7 +309,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
state: null,
|
state: null,
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast, assetNotocactus],
|
assets: [assetLast, assetNotocactus],
|
||||||
@@ -390,7 +330,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
country: '',
|
country: '',
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -401,7 +341,7 @@ describe('/search', () => {
|
|||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
dto: {
|
dto: {
|
||||||
country: null,
|
country: null,
|
||||||
isVisible: true,
|
visibility: AssetVisibility.Timeline,
|
||||||
includeNull: true,
|
includeNull: true,
|
||||||
},
|
},
|
||||||
assets: [assetLast],
|
assets: [assetLast],
|
||||||
@@ -454,14 +394,6 @@ describe('/search', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /search/smart', () => {
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post('/search/smart');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('POST /search/random', () => {
|
describe('POST /search/random', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@@ -476,13 +408,6 @@ 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')
|
||||||
@@ -512,12 +437,6 @@ 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')
|
||||||
@@ -528,12 +447,6 @@ 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';
|
||||||
|
|
||||||
@@ -552,12 +465,6 @@ 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')
|
||||||
@@ -576,12 +483,6 @@ 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, LoginResponseDto, SharedLinkType, TimeBucketSize } from '@immich/sdk';
|
import { AssetMediaResponseDto, AssetVisibility, 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, isArchived: true });
|
.query({ size: TimeBucketSize.Month, withPartners: true, visibility: AssetVisibility.Archive });
|
||||||
|
|
||||||
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, isArchived: undefined });
|
.query({ size: TimeBucketSize.Month, withPartners: true, visibility: undefined });
|
||||||
|
|
||||||
expect(req2.status).toBe(400);
|
expect(req2.status).toBe(400);
|
||||||
expect(req2.body).toEqual(errorDto.badRequest());
|
expect(req2.body).toEqual(errorDto.badRequest());
|
||||||
|
|||||||
@@ -215,6 +215,19 @@ 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', () => {
|
||||||
@@ -240,19 +253,6 @@ 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`)
|
||||||
|
|||||||
@@ -139,6 +139,19 @@ 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', () => {
|
||||||
@@ -158,19 +171,6 @@ 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`)
|
||||||
|
|||||||
+5
-1
@@ -3,6 +3,7 @@ import {
|
|||||||
AssetMediaCreateDto,
|
AssetMediaCreateDto,
|
||||||
AssetMediaResponseDto,
|
AssetMediaResponseDto,
|
||||||
AssetResponseDto,
|
AssetResponseDto,
|
||||||
|
AssetVisibility,
|
||||||
CheckExistingAssetsDto,
|
CheckExistingAssetsDto,
|
||||||
CreateAlbumDto,
|
CreateAlbumDto,
|
||||||
CreateLibraryDto,
|
CreateLibraryDto,
|
||||||
@@ -429,7 +430,10 @@ export const utils = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
archiveAssets: (accessToken: string, ids: string[]) =>
|
archiveAssets: (accessToken: string, ids: string[]) =>
|
||||||
updateAssets({ assetBulkUpdateDto: { ids, isArchived: true } }, { headers: asBearerAuth(accessToken) }),
|
updateAssets(
|
||||||
|
{ assetBulkUpdateDto: { ids, visibility: AssetVisibility.Archive } },
|
||||||
|
{ headers: asBearerAuth(accessToken) },
|
||||||
|
),
|
||||||
|
|
||||||
deleteAssets: (accessToken: string, ids: string[]) =>
|
deleteAssets: (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');
|
await page.goto('/auth/login?autoLaunch=0');
|
||||||
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');
|
await page.goto('/auth/login?autoLaunch=0');
|
||||||
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');
|
await expect(page).toHaveURL('/auth/login?autoLaunch=0');
|
||||||
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,23 +21,9 @@ 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,16 +47,13 @@ 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 page.getByRole('button', { name: 'Download' }).click();
|
await Promise.all([page.waitForEvent('download'), 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 page.getByRole('button', { name: 'Download' }).click();
|
await Promise.all([page.waitForEvent('download'), 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 }) => {
|
||||||
|
|||||||
+1
-1
Submodule e2e/test-assets updated: 9e3b964b08...8885d6d01c
+13
-8
@@ -187,20 +187,13 @@
|
|||||||
"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": "المطالبة بحصة التخزين",
|
||||||
@@ -605,6 +598,7 @@
|
|||||||
"change_password_form_new_password": "كلمة المرور الجديدة",
|
"change_password_form_new_password": "كلمة المرور الجديدة",
|
||||||
"change_password_form_password_mismatch": "كلمة المرور غير مطابقة",
|
"change_password_form_password_mismatch": "كلمة المرور غير مطابقة",
|
||||||
"change_password_form_reenter_new_password": "أعد إدخال كلمة مرور جديدة",
|
"change_password_form_reenter_new_password": "أعد إدخال كلمة مرور جديدة",
|
||||||
|
"change_pin_code": "تغيير الرقم السري",
|
||||||
"change_your_password": "غير كلمة المرور الخاصة بك",
|
"change_your_password": "غير كلمة المرور الخاصة بك",
|
||||||
"changed_visibility_successfully": "تم تغيير الرؤية بنجاح",
|
"changed_visibility_successfully": "تم تغيير الرؤية بنجاح",
|
||||||
"check_all": "تحقق من الكل",
|
"check_all": "تحقق من الكل",
|
||||||
@@ -645,6 +639,7 @@
|
|||||||
"confirm_delete_face": "هل أنت متأكد من حذف وجه {name} من الأصول؟",
|
"confirm_delete_face": "هل أنت متأكد من حذف وجه {name} من الأصول؟",
|
||||||
"confirm_delete_shared_link": "هل أنت متأكد أنك تريد حذف هذا الرابط المشترك؟",
|
"confirm_delete_shared_link": "هل أنت متأكد أنك تريد حذف هذا الرابط المشترك؟",
|
||||||
"confirm_keep_this_delete_others": "سيتم حذف جميع الأصول الأخرى في المجموعة باستثناء هذا الأصل. هل أنت متأكد من أنك تريد المتابعة؟",
|
"confirm_keep_this_delete_others": "سيتم حذف جميع الأصول الأخرى في المجموعة باستثناء هذا الأصل. هل أنت متأكد من أنك تريد المتابعة؟",
|
||||||
|
"confirm_new_pin_code": "ثبت الرقم السري الجديد",
|
||||||
"confirm_password": "تأكيد كلمة المرور",
|
"confirm_password": "تأكيد كلمة المرور",
|
||||||
"contain": "محتواة",
|
"contain": "محتواة",
|
||||||
"context": "السياق",
|
"context": "السياق",
|
||||||
@@ -690,6 +685,7 @@
|
|||||||
"crop": "Crop",
|
"crop": "Crop",
|
||||||
"curated_object_page_title": "أشياء",
|
"curated_object_page_title": "أشياء",
|
||||||
"current_device": "الجهاز الحالي",
|
"current_device": "الجهاز الحالي",
|
||||||
|
"current_pin_code": "الرقم السري الحالي",
|
||||||
"current_server_address": "Current server address",
|
"current_server_address": "Current server address",
|
||||||
"custom_locale": "لغة مخصصة",
|
"custom_locale": "لغة مخصصة",
|
||||||
"custom_locale_description": "تنسيق التواريخ والأرقام بناءً على اللغة والمنطقة",
|
"custom_locale_description": "تنسيق التواريخ والأرقام بناءً على اللغة والمنطقة",
|
||||||
@@ -1228,6 +1224,7 @@
|
|||||||
"new_api_key": "مفتاح API جديد",
|
"new_api_key": "مفتاح API جديد",
|
||||||
"new_password": "كلمة المرور الجديدة",
|
"new_password": "كلمة المرور الجديدة",
|
||||||
"new_person": "شخص جديد",
|
"new_person": "شخص جديد",
|
||||||
|
"new_pin_code": "الرقم السري الجديد",
|
||||||
"new_user_created": "تم إنشاء مستخدم جديد",
|
"new_user_created": "تم إنشاء مستخدم جديد",
|
||||||
"new_version_available": "إصدار جديد متاح",
|
"new_version_available": "إصدار جديد متاح",
|
||||||
"newest_first": "الأحدث أولاً",
|
"newest_first": "الأحدث أولاً",
|
||||||
@@ -1345,6 +1342,9 @@
|
|||||||
"photos_count": "{count, plural, one {{count, number} صورة} other {{count, number} صور}}",
|
"photos_count": "{count, plural, one {{count, number} صورة} other {{count, number} صور}}",
|
||||||
"photos_from_previous_years": "صور من السنوات السابقة",
|
"photos_from_previous_years": "صور من السنوات السابقة",
|
||||||
"pick_a_location": "اختر موقعًا",
|
"pick_a_location": "اختر موقعًا",
|
||||||
|
"pin_code_changed_successfully": "تم تغير الرقم السري",
|
||||||
|
"pin_code_reset_successfully": "تم اعادة تعيين الرقم السري",
|
||||||
|
"pin_code_setup_successfully": "تم انشاء رقم سري",
|
||||||
"place": "مكان",
|
"place": "مكان",
|
||||||
"places": "الأماكن",
|
"places": "الأماكن",
|
||||||
"places_count": "{count, plural, one {{count, number} مكان} other {{count, number} أماكن}}",
|
"places_count": "{count, plural, one {{count, number} مكان} other {{count, number} أماكن}}",
|
||||||
@@ -1375,7 +1375,7 @@
|
|||||||
"public_share": "مشاركة عامة",
|
"public_share": "مشاركة عامة",
|
||||||
"purchase_account_info": "داعم",
|
"purchase_account_info": "داعم",
|
||||||
"purchase_activated_subtitle": "شكرًا لك على دعمك لـ Immich والبرمجيات مفتوحة المصدر",
|
"purchase_activated_subtitle": "شكرًا لك على دعمك لـ Immich والبرمجيات مفتوحة المصدر",
|
||||||
"purchase_activated_time": "تم التفعيل في {date, date}",
|
"purchase_activated_time": "تم التفعيل في {date}",
|
||||||
"purchase_activated_title": "لقد تم تفعيل مفتاحك بنجاح",
|
"purchase_activated_title": "لقد تم تفعيل مفتاحك بنجاح",
|
||||||
"purchase_button_activate": "تنشيط",
|
"purchase_button_activate": "تنشيط",
|
||||||
"purchase_button_buy": "شراء",
|
"purchase_button_buy": "شراء",
|
||||||
@@ -1601,6 +1601,7 @@
|
|||||||
"settings": "الإعدادات",
|
"settings": "الإعدادات",
|
||||||
"settings_require_restart": "يرجى إعادة تشغيل لتطبيق هذا الإعداد",
|
"settings_require_restart": "يرجى إعادة تشغيل لتطبيق هذا الإعداد",
|
||||||
"settings_saved": "تم حفظ الإعدادات",
|
"settings_saved": "تم حفظ الإعدادات",
|
||||||
|
"setup_pin_code": "تحديد رقم سري",
|
||||||
"share": "مشاركة",
|
"share": "مشاركة",
|
||||||
"share_add_photos": "إضافة الصور",
|
"share_add_photos": "إضافة الصور",
|
||||||
"share_assets_selected": "{} selected",
|
"share_assets_selected": "{} selected",
|
||||||
@@ -1785,6 +1786,8 @@
|
|||||||
"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 {# أيام }}.",
|
||||||
"type": "النوع",
|
"type": "النوع",
|
||||||
|
"unable_to_change_pin_code": "تفيير الرقم السري غير ممكن",
|
||||||
|
"unable_to_setup_pin_code": "انشاء الرقم السري غير ممكن",
|
||||||
"unarchive": "أخرج من الأرشيف",
|
"unarchive": "أخرج من الأرشيف",
|
||||||
"unarchived_count": "{count, plural, other {غير مؤرشفة #}}",
|
"unarchived_count": "{count, plural, other {غير مؤرشفة #}}",
|
||||||
"unfavorite": "أزل التفضيل",
|
"unfavorite": "أزل التفضيل",
|
||||||
@@ -1829,6 +1832,8 @@
|
|||||||
"user": "مستخدم",
|
"user": "مستخدم",
|
||||||
"user_id": "معرف المستخدم",
|
"user_id": "معرف المستخدم",
|
||||||
"user_liked": "قام {user} بالإعجاب {type, select, photo {بهذه الصورة} video {بهذا الفيديو} asset {بهذا المحتوى} other {بها}}",
|
"user_liked": "قام {user} بالإعجاب {type, select, photo {بهذه الصورة} video {بهذا الفيديو} asset {بهذا المحتوى} other {بها}}",
|
||||||
|
"user_pin_code_settings": "الرقم السري",
|
||||||
|
"user_pin_code_settings_description": "تغير الرقم السري",
|
||||||
"user_purchase_settings": "الشراء",
|
"user_purchase_settings": "الشراء",
|
||||||
"user_purchase_settings_description": "إدارة عملية الشراء الخاصة بك",
|
"user_purchase_settings_description": "إدارة عملية الشراء الخاصة بك",
|
||||||
"user_role_set": "قم بتعيين {user} كـ {role}",
|
"user_role_set": "قم بتعيين {user} كـ {role}",
|
||||||
|
|||||||
+17
-2
@@ -4,6 +4,7 @@
|
|||||||
"account_settings": "Налады ўліковага запісу",
|
"account_settings": "Налады ўліковага запісу",
|
||||||
"acknowledge": "Пацвердзіць",
|
"acknowledge": "Пацвердзіць",
|
||||||
"action": "Дзеянне",
|
"action": "Дзеянне",
|
||||||
|
"action_common_update": "Абнавіць",
|
||||||
"actions": "Дзеянні",
|
"actions": "Дзеянні",
|
||||||
"active": "Актыўны",
|
"active": "Актыўны",
|
||||||
"activity": "Актыўнасць",
|
"activity": "Актыўнасць",
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
"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": "Дадайце месца",
|
||||||
@@ -20,8 +22,10 @@
|
|||||||
"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": "Дададзена ў архіў",
|
||||||
@@ -39,8 +43,9 @@
|
|||||||
"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} бібліятэку?",
|
||||||
@@ -58,8 +63,18 @@
|
|||||||
"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": "Раздзяляльнасць",
|
||||||
|
|||||||
+1
-8
@@ -183,20 +183,13 @@
|
|||||||
"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": "Заявка за квота за съхранение",
|
||||||
@@ -1013,7 +1006,7 @@
|
|||||||
"public_share": "Публично споделяне",
|
"public_share": "Публично споделяне",
|
||||||
"purchase_account_info": "Поддръжник",
|
"purchase_account_info": "Поддръжник",
|
||||||
"purchase_activated_subtitle": "Благодарим ви, че подкрепяте Immich и софтуера с отворен код",
|
"purchase_activated_subtitle": "Благодарим ви, че подкрепяте Immich и софтуера с отворен код",
|
||||||
"purchase_activated_time": "Активиран на {date, date}",
|
"purchase_activated_time": "Активиран на {date}",
|
||||||
"purchase_activated_title": "Вашият ключ беше успешно активиран",
|
"purchase_activated_title": "Вашият ключ беше успешно активиран",
|
||||||
"purchase_button_activate": "Активирай",
|
"purchase_button_activate": "Активирай",
|
||||||
"purchase_button_buy": "Купи",
|
"purchase_button_buy": "Купи",
|
||||||
|
|||||||
@@ -138,17 +138,12 @@
|
|||||||
"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": "",
|
||||||
|
|||||||
+17
-1
@@ -1 +1,17 @@
|
|||||||
{}
|
{
|
||||||
|
"about": "সম্পর্কে",
|
||||||
|
"account": "অ্যাকাউন্ট",
|
||||||
|
"account_settings": "অ্যাকাউন্ট সেটিংস",
|
||||||
|
"acknowledge": "স্বীকৃতি",
|
||||||
|
"action": "কার্য",
|
||||||
|
"action_common_update": "আপডেট",
|
||||||
|
"actions": "কর্ম",
|
||||||
|
"active": "সচল",
|
||||||
|
"activity": "কার্যকলাপ",
|
||||||
|
"add": "যোগ করুন",
|
||||||
|
"add_a_description": "একটি বিবরণ যোগ করুন",
|
||||||
|
"add_a_location": "একটি অবস্থান যোগ করুন",
|
||||||
|
"add_a_name": "একটি নাম যোগ করুন",
|
||||||
|
"add_a_title": "একটি শিরোনাম যোগ করুন",
|
||||||
|
"add_endpoint": "এন্ডপয়েন্ট যোগ করুন"
|
||||||
|
}
|
||||||
|
|||||||
+128
-91
@@ -39,11 +39,11 @@
|
|||||||
"authentication_settings_disable_all": "Estàs segur que vols desactivar tots els mètodes d'inici de sessió? L'inici de sessió quedarà completament desactivat.",
|
"authentication_settings_disable_all": "Estàs segur que vols desactivar tots els mètodes d'inici de sessió? L'inici de sessió quedarà completament desactivat.",
|
||||||
"authentication_settings_reenable": "Per a tornar a habilitar, empra una <link>Comanda de Servidor</link>.",
|
"authentication_settings_reenable": "Per a tornar a habilitar, empra una <link>Comanda de Servidor</link>.",
|
||||||
"background_task_job": "Tasques en segon pla",
|
"background_task_job": "Tasques en segon pla",
|
||||||
"backup_database": "Còpia de la base de dades",
|
"backup_database": "Fer un bolcat de la base de dades",
|
||||||
"backup_database_enable_description": "Habilitar còpies de la base de dades",
|
"backup_database_enable_description": "Habilitar bolcat de la base de dades",
|
||||||
"backup_keep_last_amount": "Quantitat de còpies de seguretat anteriors per conservar",
|
"backup_keep_last_amount": "Quantitat de bolcats anteriors per conservar",
|
||||||
"backup_settings": "Ajustes de les còpies de seguretat",
|
"backup_settings": "Configuració dels bolcats",
|
||||||
"backup_settings_description": "Gestionar la configuració de la còpia de seguretat de la base de dades",
|
"backup_settings_description": "Gestionar la configuració bolcats de la base de dades. Nota: els treballs no es monitoritzen ni es notifiquen les fallades.",
|
||||||
"check_all": "Marca-ho tot",
|
"check_all": "Marca-ho tot",
|
||||||
"cleanup": "Neteja",
|
"cleanup": "Neteja",
|
||||||
"cleared_jobs": "Tasques esborrades per a: {job}",
|
"cleared_jobs": "Tasques esborrades per a: {job}",
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
"confirm_email_below": "Per a confirmar, escriviu \"{email}\" a sota",
|
"confirm_email_below": "Per a confirmar, escriviu \"{email}\" a sota",
|
||||||
"confirm_reprocess_all_faces": "Esteu segur que voleu reprocessar totes les cares? Això també esborrarà la gent que heu anomenat.",
|
"confirm_reprocess_all_faces": "Esteu segur que voleu reprocessar totes les cares? Això també esborrarà la gent que heu anomenat.",
|
||||||
"confirm_user_password_reset": "Esteu segur que voleu reinicialitzar la contrasenya de l'usuari {user}?",
|
"confirm_user_password_reset": "Esteu segur que voleu reinicialitzar la contrasenya de l'usuari {user}?",
|
||||||
|
"confirm_user_pin_code_reset": "Esteu segur que voleu restablir el codi PIN de {user}?",
|
||||||
"create_job": "Crear tasca",
|
"create_job": "Crear tasca",
|
||||||
"cron_expression": "Expressió Cron",
|
"cron_expression": "Expressió Cron",
|
||||||
"cron_expression_description": "Estableix l'interval d'escaneig amb el format cron. Per obtenir més informació, consulteu, p.e <link>Crontab Guru</link>",
|
"cron_expression_description": "Estableix l'interval d'escaneig amb el format cron. Per obtenir més informació, consulteu, p.e <link>Crontab Guru</link>",
|
||||||
@@ -192,26 +193,22 @@
|
|||||||
"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_description": "Requerit si PKCE (Proof Key for Code Exchange) no està suportat pel proveïdor OAuth",
|
||||||
"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",
|
||||||
"oauth_storage_quota_claim_description": "Estableix automàticament la quota d'emmagatzematge de l'usuari al valor d'aquest paràmetre.",
|
"oauth_storage_quota_claim_description": "Estableix automàticament la quota d'emmagatzematge de l'usuari al valor d'aquest paràmetre.",
|
||||||
"oauth_storage_quota_default": "Quota d'emmagatzematge predeterminada (GiB)",
|
"oauth_storage_quota_default": "Quota d'emmagatzematge predeterminada (GiB)",
|
||||||
"oauth_storage_quota_default_description": "Quota disponible en GB quan no s'estableixi cap valor (Entreu 0 per a quota il·limitada).",
|
"oauth_storage_quota_default_description": "Quota disponible en GB quan no s'estableixi cap valor (Entreu 0 per a quota il·limitada).",
|
||||||
|
"oauth_timeout": "Solicitud caducada",
|
||||||
|
"oauth_timeout_description": "Timeout per a sol·licituds en mil·lisegons",
|
||||||
"offline_paths": "Rutes sense connexió",
|
"offline_paths": "Rutes sense connexió",
|
||||||
"offline_paths_description": "Aquests resultats poden ser deguts a l'eliminació manual de fitxers que no formen part d'una llibreria externa.",
|
"offline_paths_description": "Aquests resultats poden ser deguts a l'eliminació manual de fitxers que no formen part d'una llibreria externa.",
|
||||||
"password_enable_description": "Inicia sessió amb correu electrònic i contrasenya",
|
"password_enable_description": "Inicia sessió amb correu electrònic i contrasenya",
|
||||||
@@ -352,6 +349,7 @@
|
|||||||
"user_delete_delay_settings_description": "Nombre de dies després de la supressió per eliminar permanentment el compte i els elements d'un usuari. El treball de supressió d'usuaris s'executa a mitjanit per comprovar si hi ha usuaris preparats per eliminar. Els canvis en aquesta configuració s'avaluaran en la propera execució.",
|
"user_delete_delay_settings_description": "Nombre de dies després de la supressió per eliminar permanentment el compte i els elements d'un usuari. El treball de supressió d'usuaris s'executa a mitjanit per comprovar si hi ha usuaris preparats per eliminar. Els canvis en aquesta configuració s'avaluaran en la propera execució.",
|
||||||
"user_delete_immediately": "El compte i els recursos de <b>{user}</b> es posaran a la cua per suprimir-los permanentment <b>immediatament</b>.",
|
"user_delete_immediately": "El compte i els recursos de <b>{user}</b> es posaran a la cua per suprimir-los permanentment <b>immediatament</b>.",
|
||||||
"user_delete_immediately_checkbox": "Posa en cua l'usuari i els recursos per suprimir-los immediatament",
|
"user_delete_immediately_checkbox": "Posa en cua l'usuari i els recursos per suprimir-los immediatament",
|
||||||
|
"user_details": "Detalls d'usuari",
|
||||||
"user_management": "Gestió d'usuaris",
|
"user_management": "Gestió d'usuaris",
|
||||||
"user_password_has_been_reset": "La contrasenya de l'usuari ha estat restablida:",
|
"user_password_has_been_reset": "La contrasenya de l'usuari ha estat restablida:",
|
||||||
"user_password_reset_description": "Si us plau, proporcioneu la contrasenya temporal a l'usuari i informeu-los que haurà de canviar la contrasenya en el proper inici de sessió.",
|
"user_password_reset_description": "Si us plau, proporcioneu la contrasenya temporal a l'usuari i informeu-los que haurà de canviar la contrasenya en el proper inici de sessió.",
|
||||||
@@ -371,13 +369,17 @@
|
|||||||
"admin_password": "Contrasenya de l'administrador",
|
"admin_password": "Contrasenya de l'administrador",
|
||||||
"administration": "Administrador",
|
"administration": "Administrador",
|
||||||
"advanced": "Avançat",
|
"advanced": "Avançat",
|
||||||
"advanced_settings_log_level_title": "Nivell de registre: {}",
|
"advanced_settings_enable_alternate_media_filter_subtitle": "Feu servir aquesta opció per filtrar els continguts multimèdia durant la sincronització segons criteris alternatius. Només proveu-ho si teniu problemes amb l'aplicació per detectar tots els àlbums.",
|
||||||
|
"advanced_settings_enable_alternate_media_filter_title": "Utilitza el filtre de sincronització d'àlbums de dispositius alternatius",
|
||||||
|
"advanced_settings_log_level_title": "Nivell de registre: {level}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Alguns dispositius són molt lents en carregar miniatures dels elements del dispositiu. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.",
|
"advanced_settings_prefer_remote_subtitle": "Alguns dispositius són molt lents en carregar miniatures dels elements del dispositiu. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.",
|
||||||
"advanced_settings_prefer_remote_title": "Prefereix imatges remotes",
|
"advanced_settings_prefer_remote_title": "Prefereix imatges remotes",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Definiu les capçaleres de proxy que Immich per enviar amb cada sol·licitud de xarxa",
|
"advanced_settings_proxy_headers_subtitle": "Definiu les capçaleres de proxy que Immich per enviar amb cada sol·licitud de xarxa",
|
||||||
"advanced_settings_proxy_headers_title": "Capçaleres de proxy",
|
"advanced_settings_proxy_headers_title": "Capçaleres de proxy",
|
||||||
"advanced_settings_self_signed_ssl_subtitle": "Omet la verificació del certificat SSL del servidor. Requerit per a certificats autosignats.",
|
"advanced_settings_self_signed_ssl_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_sync_remote_deletions_subtitle": "Suprimeix o restaura automàticament un actiu en aquest dispositiu quan es realitzi aquesta acció al web",
|
||||||
|
"advanced_settings_sync_remote_deletions_title": "Sincronitza les eliminacions remotes",
|
||||||
"advanced_settings_tile_subtitle": "Configuració avançada de l'usuari",
|
"advanced_settings_tile_subtitle": "Configuració avançada de l'usuari",
|
||||||
"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",
|
||||||
@@ -400,9 +402,9 @@
|
|||||||
"album_remove_user_confirmation": "Esteu segurs que voleu eliminar {user}?",
|
"album_remove_user_confirmation": "Esteu segurs que voleu eliminar {user}?",
|
||||||
"album_share_no_users": "Sembla que has compartit aquest àlbum amb tots els usuaris o no tens cap usuari amb qui compartir-ho.",
|
"album_share_no_users": "Sembla que has compartit aquest àlbum amb tots els usuaris o no tens cap usuari amb qui compartir-ho.",
|
||||||
"album_thumbnail_card_item": "1 element",
|
"album_thumbnail_card_item": "1 element",
|
||||||
"album_thumbnail_card_items": "{} elements",
|
"album_thumbnail_card_items": "{count} elements",
|
||||||
"album_thumbnail_card_shared": " · Compartit",
|
"album_thumbnail_card_shared": " · Compartit",
|
||||||
"album_thumbnail_shared_by": "Compartit per {}",
|
"album_thumbnail_shared_by": "Compartit per {user}",
|
||||||
"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}",
|
||||||
@@ -440,7 +442,7 @@
|
|||||||
"archive": "Arxiu",
|
"archive": "Arxiu",
|
||||||
"archive_or_unarchive_photo": "Arxivar o desarxivar fotografia",
|
"archive_or_unarchive_photo": "Arxivar o desarxivar fotografia",
|
||||||
"archive_page_no_archived_assets": "No s'ha trobat res arxivat",
|
"archive_page_no_archived_assets": "No s'ha trobat res arxivat",
|
||||||
"archive_page_title": "Arxiu({})",
|
"archive_page_title": "Arxiu({count})",
|
||||||
"archive_size": "Mida de l'arxiu",
|
"archive_size": "Mida de l'arxiu",
|
||||||
"archive_size_description": "Configureu la mida de l'arxiu de les descàrregues (en GiB)",
|
"archive_size_description": "Configureu la mida de l'arxiu de les descàrregues (en GiB)",
|
||||||
"archived": "Arxivat",
|
"archived": "Arxivat",
|
||||||
@@ -477,18 +479,18 @@
|
|||||||
"assets_added_to_album_count": "{count, plural, one {Afegit un element} other {Afegits # elements}} a l'àlbum",
|
"assets_added_to_album_count": "{count, plural, one {Afegit un element} other {Afegits # elements}} a l'àlbum",
|
||||||
"assets_added_to_name_count": "{count, plural, one {S'ha afegit # recurs} other {S'han afegit # recursos}} a {hasName, select, true {<b>{name}</b>} other {new album}}",
|
"assets_added_to_name_count": "{count, plural, one {S'ha afegit # recurs} other {S'han afegit # recursos}} a {hasName, select, true {<b>{name}</b>} other {new album}}",
|
||||||
"assets_count": "{count, plural, one {# recurs} other {# recursos}}",
|
"assets_count": "{count, plural, one {# recurs} other {# recursos}}",
|
||||||
"assets_deleted_permanently": "{} element(s) esborrats permanentment",
|
"assets_deleted_permanently": "{count} element(s) esborrats permanentment",
|
||||||
"assets_deleted_permanently_from_server": "{} element(s) esborrats permanentment del servidor d'Immich",
|
"assets_deleted_permanently_from_server": "{count} element(s) esborrats permanentment del servidor d'Immich",
|
||||||
"assets_moved_to_trash_count": "{count, plural, one {# recurs mogut} other {# recursos moguts}} a la paperera",
|
"assets_moved_to_trash_count": "{count, plural, one {# recurs mogut} other {# recursos moguts}} a la paperera",
|
||||||
"assets_permanently_deleted_count": "{count, plural, one {# recurs esborrat} other {# recursos esborrats}} permanentment",
|
"assets_permanently_deleted_count": "{count, plural, one {# recurs esborrat} other {# recursos esborrats}} permanentment",
|
||||||
"assets_removed_count": "{count, plural, one {# element eliminat} other {# elements eliminats}}",
|
"assets_removed_count": "{count, plural, one {# element eliminat} other {# elements eliminats}}",
|
||||||
"assets_removed_permanently_from_device": "{} element(s) esborrat permanentment del dispositiu",
|
"assets_removed_permanently_from_device": "{count} element(s) esborrat permanentment del dispositiu",
|
||||||
"assets_restore_confirmation": "Esteu segurs que voleu restaurar tots els teus actius? Aquesta acció no es pot desfer! Tingueu en compte que els recursos fora de línia no es poden restaurar d'aquesta manera.",
|
"assets_restore_confirmation": "Esteu segurs que voleu restaurar tots els teus actius? Aquesta acció no es pot desfer! Tingueu en compte que els recursos fora de línia no es poden restaurar d'aquesta manera.",
|
||||||
"assets_restored_count": "{count, plural, one {# element restaurat} other {# elements restaurats}}",
|
"assets_restored_count": "{count, plural, one {# element restaurat} other {# elements restaurats}}",
|
||||||
"assets_restored_successfully": "{} element(s) recuperats correctament",
|
"assets_restored_successfully": "{count} element(s) recuperats correctament",
|
||||||
"assets_trashed": "{} element(s) enviat a la paperera",
|
"assets_trashed": "{count} element(s) enviat a la paperera",
|
||||||
"assets_trashed_count": "{count, plural, one {# element enviat} other {# elements enviats}} a la paperera",
|
"assets_trashed_count": "{count, plural, one {# element enviat} other {# elements enviats}} a la paperera",
|
||||||
"assets_trashed_from_server": "{} element(s) enviat a la paperera del servidor d'Immich",
|
"assets_trashed_from_server": "{count} element(s) enviat a la paperera del servidor d'Immich",
|
||||||
"assets_were_part_of_album_count": "{count, plural, one {L'element ja és} other {Els elements ja són}} part de l'àlbum",
|
"assets_were_part_of_album_count": "{count, plural, one {L'element ja és} other {Els elements ja són}} part de l'àlbum",
|
||||||
"authorized_devices": "Dispositius autoritzats",
|
"authorized_devices": "Dispositius autoritzats",
|
||||||
"automatic_endpoint_switching_subtitle": "Connecteu-vos localment a través de la Wi-Fi designada quan estigui disponible i utilitzeu connexions alternatives en altres llocs",
|
"automatic_endpoint_switching_subtitle": "Connecteu-vos localment a través de la Wi-Fi designada quan estigui disponible i utilitzeu connexions alternatives en altres llocs",
|
||||||
@@ -497,7 +499,7 @@
|
|||||||
"back_close_deselect": "Tornar, tancar o anul·lar la selecció",
|
"back_close_deselect": "Tornar, tancar o anul·lar la selecció",
|
||||||
"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",
|
||||||
"backup_album_selection_page_albums_device": "Àlbums al dispositiu ({})",
|
"backup_album_selection_page_albums_device": "Àlbums al dispositiu ({count})",
|
||||||
"backup_album_selection_page_albums_tap": "Un toc per incloure, doble toc per excloure",
|
"backup_album_selection_page_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.",
|
||||||
"backup_album_selection_page_select_albums": "Selecciona àlbums",
|
"backup_album_selection_page_select_albums": "Selecciona àlbums",
|
||||||
@@ -506,37 +508,37 @@
|
|||||||
"backup_all": "Tots",
|
"backup_all": "Tots",
|
||||||
"backup_background_service_backup_failed_message": "No s'ha pogut copiar els elements. Tornant a intentar…",
|
"backup_background_service_backup_failed_message": "No s'ha pogut copiar els elements. Tornant a intentar…",
|
||||||
"backup_background_service_connection_failed_message": "No s'ha pogut connectar al servidor. Tornant a intentar…",
|
"backup_background_service_connection_failed_message": "No s'ha pogut connectar al servidor. Tornant a intentar…",
|
||||||
"backup_background_service_current_upload_notification": "Pujant {}",
|
"backup_background_service_current_upload_notification": "Pujant {filename}",
|
||||||
"backup_background_service_default_notification": "Cercant nous elements...",
|
"backup_background_service_default_notification": "Cercant nous elements…",
|
||||||
"backup_background_service_error_title": "Error copiant",
|
"backup_background_service_error_title": "Error copiant",
|
||||||
"backup_background_service_in_progress_notification": "Copiant els teus elements",
|
"backup_background_service_in_progress_notification": "Copiant els teus elements…",
|
||||||
"backup_background_service_upload_failure_notification": "Error al pujar {}",
|
"backup_background_service_upload_failure_notification": "Error en pujar {filename}",
|
||||||
"backup_controller_page_albums": "Copia els àlbums",
|
"backup_controller_page_albums": "Copia els àlbums",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Activa l'actualització en segon pla de l'aplicació a Configuració > General > Actualització en segon pla per utilitzar la copia de seguretat en segon pla.",
|
"backup_controller_page_background_app_refresh_disabled_content": "Activa l'actualització en segon pla de l'aplicació a Configuració > General > Actualització en segon pla per utilitzar la copia de seguretat en segon pla.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "Actualització en segon pla desactivada",
|
"backup_controller_page_background_app_refresh_disabled_title": "Actualització en segon pla desactivada",
|
||||||
"backup_controller_page_background_app_refresh_enable_button_text": "Vés a configuració",
|
"backup_controller_page_background_app_refresh_enable_button_text": "Vés a configuració",
|
||||||
"backup_controller_page_background_battery_info_link": "Mostra'm com",
|
"backup_controller_page_background_battery_info_link": "Mostra'm com",
|
||||||
"backup_controller_page_background_battery_info_message": "Per obtenir la millor experiència de copia de seguretat en segon pla, desactiveu qualsevol optimització de bateria que restringeixi l'activitat en segon pla per a Immich.\n\nAtès que això és específic del dispositiu, busqueu la informació necessària per al fabricant del vostre dispositiu",
|
"backup_controller_page_background_battery_info_message": "Per obtenir la millor experiència de còpia de seguretat en segon pla, desactiveu qualsevol optimització de bateria que restringeixi l'activitat en segon pla per a Immich.\n\nAtès que això és específic del dispositiu, busqueu la informació necessària per al fabricant del vostre dispositiu.",
|
||||||
"backup_controller_page_background_battery_info_ok": "D'acord",
|
"backup_controller_page_background_battery_info_ok": "D'acord",
|
||||||
"backup_controller_page_background_battery_info_title": "Optimitzacions de bateria",
|
"backup_controller_page_background_battery_info_title": "Optimitzacions de bateria",
|
||||||
"backup_controller_page_background_charging": "Només mentre es carrega",
|
"backup_controller_page_background_charging": "Només mentre es carrega",
|
||||||
"backup_controller_page_background_configure_error": "No s'ha pogut configurar el servei en segon pla",
|
"backup_controller_page_background_configure_error": "No s'ha pogut configurar el servei en segon pla",
|
||||||
"backup_controller_page_background_delay": "Retard en la copia de seguretat de nous elements: {}",
|
"backup_controller_page_background_delay": "Retard en la còpia de seguretat de nous elements: {duration}",
|
||||||
"backup_controller_page_background_description": "Activeu el servei en segon pla per copiar automàticament tots els nous elements sense haver d'obrir l'aplicació.",
|
"backup_controller_page_background_description": "Activeu el servei en segon pla per copiar automàticament tots els nous elements sense haver d'obrir l'aplicació",
|
||||||
"backup_controller_page_background_is_off": "La còpia automàtica en segon pla està desactivada",
|
"backup_controller_page_background_is_off": "La còpia automàtica en segon pla està desactivada",
|
||||||
"backup_controller_page_background_is_on": "La còpia automàtica en segon pla està activada",
|
"backup_controller_page_background_is_on": "La còpia automàtica en segon pla està activada",
|
||||||
"backup_controller_page_background_turn_off": "Desactiva el servei en segon pla",
|
"backup_controller_page_background_turn_off": "Desactiva el servei en segon pla",
|
||||||
"backup_controller_page_background_turn_on": "Activa el servei en segon pla",
|
"backup_controller_page_background_turn_on": "Activa el servei en segon pla",
|
||||||
"backup_controller_page_background_wifi": "Només amb WiFi",
|
"backup_controller_page_background_wifi": "Només amb Wi-Fi",
|
||||||
"backup_controller_page_backup": "Còpia",
|
"backup_controller_page_backup": "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_created": "Creat el: {}",
|
"backup_controller_page_created": "Creat el: {date}",
|
||||||
"backup_controller_page_desc_backup": "Activeu la còpia de seguretat per pujar automàticament els nous elements al servidor en obrir l'aplicació.",
|
"backup_controller_page_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 ({count})",
|
||||||
"backup_controller_page_filename": "Nom de l'arxiu: {} [{}]",
|
"backup_controller_page_filename": "Nom de l'arxiu: {filename} [{size}]",
|
||||||
"backup_controller_page_id": "ID: {}",
|
"backup_controller_page_id": "ID: {id}",
|
||||||
"backup_controller_page_info": "Informació de la còpia",
|
"backup_controller_page_info": "Informació de la còpia",
|
||||||
"backup_controller_page_none_selected": "Cap seleccionat",
|
"backup_controller_page_none_selected": "Cap seleccionat",
|
||||||
"backup_controller_page_remainder": "Restant",
|
"backup_controller_page_remainder": "Restant",
|
||||||
@@ -545,7 +547,7 @@
|
|||||||
"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": "{used} de {total} utilitzats",
|
||||||
"backup_controller_page_to_backup": "Àlbums a copiar",
|
"backup_controller_page_to_backup": "Àlbums a copiar",
|
||||||
"backup_controller_page_total_sub": "Totes les fotografies i vídeos dels àlbums seleccionats",
|
"backup_controller_page_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",
|
||||||
@@ -570,21 +572,21 @@
|
|||||||
"bulk_keep_duplicates_confirmation": "Esteu segur que voleu mantenir {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? Això resoldrà tots els grups duplicats sense eliminar res.",
|
"bulk_keep_duplicates_confirmation": "Esteu segur que voleu mantenir {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? Això resoldrà tots els grups duplicats sense eliminar res.",
|
||||||
"bulk_trash_duplicates_confirmation": "Esteu segur que voleu enviar a les escombraries {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? Això mantindrà el recurs més gran de cada grup i eliminarà la resta de duplicats.",
|
"bulk_trash_duplicates_confirmation": "Esteu segur que voleu enviar a les escombraries {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? Això mantindrà el recurs més gran de cada grup i eliminarà la resta de duplicats.",
|
||||||
"buy": "Comprar Immich",
|
"buy": "Comprar Immich",
|
||||||
"cache_settings_album_thumbnails": "Miniatures de la pàgina de la biblioteca ({} elements)",
|
"cache_settings_album_thumbnails": "Miniatures de la pàgina de la biblioteca ({count} elements)",
|
||||||
"cache_settings_clear_cache_button": "Neteja la memòria cau",
|
"cache_settings_clear_cache_button": "Neteja la memòria cau",
|
||||||
"cache_settings_clear_cache_button_title": "Neteja la memòria cau de l'aplicació. Això impactarà significativament el rendiment fins que la memòria cau es torni a reconstruir.",
|
"cache_settings_clear_cache_button_title": "Neteja la memòria cau de l'aplicació. Això impactarà significativament el rendiment fins que la memòria cau es torni a reconstruir.",
|
||||||
"cache_settings_duplicated_assets_clear_button": "NETEJA",
|
"cache_settings_duplicated_assets_clear_button": "NETEJA",
|
||||||
"cache_settings_duplicated_assets_subtitle": "Fotos i vídeos que estan a la llista negra de l'aplicació.",
|
"cache_settings_duplicated_assets_subtitle": "Fotos i vídeos que estan a la llista negra de l'aplicació",
|
||||||
"cache_settings_duplicated_assets_title": "Elements duplicats ({})",
|
"cache_settings_duplicated_assets_title": "Elements duplicats ({count})",
|
||||||
"cache_settings_image_cache_size": "Mida de la memòria cau de imatges ({} elements)",
|
"cache_settings_image_cache_size": "Mida de la memòria cau d'imatges ({count} elements)",
|
||||||
"cache_settings_statistics_album": "Miniatures de la biblioteca",
|
"cache_settings_statistics_album": "Miniatures de la biblioteca",
|
||||||
"cache_settings_statistics_assets": "{} elements ({})",
|
"cache_settings_statistics_assets": "{count} elements ({size})",
|
||||||
"cache_settings_statistics_full": "Imatges completes",
|
"cache_settings_statistics_full": "Imatges completes",
|
||||||
"cache_settings_statistics_shared": "Miniatures d'àlbums compartits",
|
"cache_settings_statistics_shared": "Miniatures d'àlbums compartits",
|
||||||
"cache_settings_statistics_thumbnail": "Miniatures",
|
"cache_settings_statistics_thumbnail": "Miniatures",
|
||||||
"cache_settings_statistics_title": "Ús de memòria cau",
|
"cache_settings_statistics_title": "Ús de memòria cau",
|
||||||
"cache_settings_subtitle": "Controla el comportament de la memòria cau de l'aplicació mòbil Immich",
|
"cache_settings_subtitle": "Controla el comportament de la memòria cau de l'aplicació mòbil Immich",
|
||||||
"cache_settings_thumbnail_size": "Mida de la memòria cau de les miniatures ({} elements)",
|
"cache_settings_thumbnail_size": "Mida de la memòria cau de les miniatures ({count} elements)",
|
||||||
"cache_settings_tile_subtitle": "Controla el comportament de l'emmagatzematge local",
|
"cache_settings_tile_subtitle": "Controla el comportament de l'emmagatzematge local",
|
||||||
"cache_settings_tile_title": "Emmagatzematge local",
|
"cache_settings_tile_title": "Emmagatzematge local",
|
||||||
"cache_settings_title": "Configuració de la memòria cau",
|
"cache_settings_title": "Configuració de la memòria cau",
|
||||||
@@ -610,6 +612,7 @@
|
|||||||
"change_password_form_new_password": "Nova contrasenya",
|
"change_password_form_new_password": "Nova contrasenya",
|
||||||
"change_password_form_password_mismatch": "Les contrasenyes no coincideixen",
|
"change_password_form_password_mismatch": "Les contrasenyes no coincideixen",
|
||||||
"change_password_form_reenter_new_password": "Torna a introduir la nova contrasenya",
|
"change_password_form_reenter_new_password": "Torna a introduir la nova contrasenya",
|
||||||
|
"change_pin_code": "Canviar el codi PIN",
|
||||||
"change_your_password": "Canvia la teva contrasenya",
|
"change_your_password": "Canvia la teva contrasenya",
|
||||||
"changed_visibility_successfully": "Visibilitat canviada amb èxit",
|
"changed_visibility_successfully": "Visibilitat canviada amb èxit",
|
||||||
"check_all": "Marqueu-ho tot",
|
"check_all": "Marqueu-ho tot",
|
||||||
@@ -650,11 +653,12 @@
|
|||||||
"confirm_delete_face": "Estàs segur que vols eliminar la cara de {name} de les cares reconegudes?",
|
"confirm_delete_face": "Estàs segur que vols eliminar la cara de {name} de les cares reconegudes?",
|
||||||
"confirm_delete_shared_link": "Esteu segurs que voleu eliminar aquest enllaç compartit?",
|
"confirm_delete_shared_link": "Esteu segurs que voleu eliminar aquest enllaç compartit?",
|
||||||
"confirm_keep_this_delete_others": "Excepte aquest element, tots els altres de la pila se suprimiran. Esteu segur que voleu continuar?",
|
"confirm_keep_this_delete_others": "Excepte aquest element, tots els altres de la pila se suprimiran. Esteu segur que voleu continuar?",
|
||||||
|
"confirm_new_pin_code": "Confirma el nou codi PIN",
|
||||||
"confirm_password": "Confirmació de contrasenya",
|
"confirm_password": "Confirmació de contrasenya",
|
||||||
"contain": "Contingut",
|
"contain": "Contingut",
|
||||||
"context": "Context",
|
"context": "Context",
|
||||||
"continue": "Continuar",
|
"continue": "Continuar",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} elements - Compartits",
|
"control_bottom_app_bar_album_info_shared": "{count} elements - Compartits",
|
||||||
"control_bottom_app_bar_create_new_album": "Crea un àlbum nou",
|
"control_bottom_app_bar_create_new_album": "Crea un àlbum nou",
|
||||||
"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",
|
||||||
@@ -692,9 +696,11 @@
|
|||||||
"create_tag_description": "Crear una nova etiqueta. Per les etiquetes aniuades, escriu la ruta comperta de l'etiqueta, incloses les barres diagonals.",
|
"create_tag_description": "Crear una nova etiqueta. Per les etiquetes aniuades, escriu la ruta comperta de l'etiqueta, incloses les barres diagonals.",
|
||||||
"create_user": "Crea un usuari",
|
"create_user": "Crea un usuari",
|
||||||
"created": "Creat",
|
"created": "Creat",
|
||||||
|
"created_at": "Creat",
|
||||||
"crop": "Retalla",
|
"crop": "Retalla",
|
||||||
"curated_object_page_title": "Coses",
|
"curated_object_page_title": "Coses",
|
||||||
"current_device": "Dispositiu actual",
|
"current_device": "Dispositiu actual",
|
||||||
|
"current_pin_code": "Codi PIN actual",
|
||||||
"current_server_address": "Adreça actual del servidor",
|
"current_server_address": "Adreça actual del servidor",
|
||||||
"custom_locale": "Localització personalitzada",
|
"custom_locale": "Localització personalitzada",
|
||||||
"custom_locale_description": "Format de dates i números segons la llengua i regió",
|
"custom_locale_description": "Format de dates i números segons la llengua i regió",
|
||||||
@@ -718,7 +724,7 @@
|
|||||||
"delete": "Esborra",
|
"delete": "Esborra",
|
||||||
"delete_album": "Esborra l'àlbum",
|
"delete_album": "Esborra l'àlbum",
|
||||||
"delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?",
|
"delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?",
|
||||||
"delete_dialog_alert": "Aquests elements seran eliminats de manera permanent d'Immich i del vostre dispositiu.",
|
"delete_dialog_alert": "Aquests elements seran eliminats de manera permanent d'Immich i del vostre dispositiu",
|
||||||
"delete_dialog_alert_local": "Aquests elements s'eliminaran permanentment del vostre dispositiu, però encara estaran disponibles al servidor Immich",
|
"delete_dialog_alert_local": "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",
|
||||||
@@ -763,7 +769,7 @@
|
|||||||
"download_enqueue": "Descàrrega en cua",
|
"download_enqueue": "Descàrrega en cua",
|
||||||
"download_error": "Error de descàrrega",
|
"download_error": "Error de descàrrega",
|
||||||
"download_failed": "Descàrrega ha fallat",
|
"download_failed": "Descàrrega ha fallat",
|
||||||
"download_filename": "arxiu: {}",
|
"download_filename": "arxiu: {filename}",
|
||||||
"download_finished": "Descàrrega acabada",
|
"download_finished": "Descàrrega acabada",
|
||||||
"download_include_embedded_motion_videos": "Vídeos incrustats",
|
"download_include_embedded_motion_videos": "Vídeos incrustats",
|
||||||
"download_include_embedded_motion_videos_description": "Incloure vídeos incrustats en fotografies en moviment com un arxiu separat",
|
"download_include_embedded_motion_videos_description": "Incloure vídeos incrustats en fotografies en moviment com un arxiu separat",
|
||||||
@@ -807,6 +813,7 @@
|
|||||||
"editor_crop_tool_h2_aspect_ratios": "Relació d'aspecte",
|
"editor_crop_tool_h2_aspect_ratios": "Relació d'aspecte",
|
||||||
"editor_crop_tool_h2_rotation": "Rotació",
|
"editor_crop_tool_h2_rotation": "Rotació",
|
||||||
"email": "Correu electrònic",
|
"email": "Correu electrònic",
|
||||||
|
"email_notifications": "Correu electrònic de notificacions",
|
||||||
"empty_folder": "Aquesta carpeta és buida",
|
"empty_folder": "Aquesta carpeta és buida",
|
||||||
"empty_trash": "Buidar la paperera",
|
"empty_trash": "Buidar la paperera",
|
||||||
"empty_trash_confirmation": "Esteu segur que voleu buidar la paperera? Això eliminarà tots els recursos a la paperera permanentment d'Immich.\nNo podeu desfer aquesta acció!",
|
"empty_trash_confirmation": "Esteu segur que voleu buidar la paperera? Això eliminarà tots els recursos a la paperera permanentment d'Immich.\nNo podeu desfer aquesta acció!",
|
||||||
@@ -814,12 +821,12 @@
|
|||||||
"enabled": "Activat",
|
"enabled": "Activat",
|
||||||
"end_date": "Data final",
|
"end_date": "Data final",
|
||||||
"enqueued": "En cua",
|
"enqueued": "En cua",
|
||||||
"enter_wifi_name": "Introdueix el nom de WiFi",
|
"enter_wifi_name": "Introdueix el nom de Wi-Fi",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"error_change_sort_album": "No s'ha pogut canviar l'ordre d'ordenació dels àlbums",
|
"error_change_sort_album": "No s'ha pogut canviar l'ordre d'ordenació dels àlbums",
|
||||||
"error_delete_face": "Error esborrant cara de les cares reconegudes",
|
"error_delete_face": "Error esborrant cara de les cares reconegudes",
|
||||||
"error_loading_image": "Error carregant la imatge",
|
"error_loading_image": "Error carregant la imatge",
|
||||||
"error_saving_image": "Error: {}",
|
"error_saving_image": "Error: {error}",
|
||||||
"error_title": "Error - Quelcom ha anat malament",
|
"error_title": "Error - Quelcom ha anat malament",
|
||||||
"errors": {
|
"errors": {
|
||||||
"cannot_navigate_next_asset": "No es pot navegar a l'element següent",
|
"cannot_navigate_next_asset": "No es pot navegar a l'element següent",
|
||||||
@@ -849,10 +856,12 @@
|
|||||||
"failed_to_keep_this_delete_others": "No s'ha pogut conservar aquest element i suprimir els altres",
|
"failed_to_keep_this_delete_others": "No s'ha pogut conservar aquest element i suprimir els altres",
|
||||||
"failed_to_load_asset": "No s'ha pogut carregar l'element",
|
"failed_to_load_asset": "No s'ha pogut carregar l'element",
|
||||||
"failed_to_load_assets": "No s'han pogut carregar els elements",
|
"failed_to_load_assets": "No s'han pogut carregar els elements",
|
||||||
|
"failed_to_load_notifications": "Error en carregar les notificacions",
|
||||||
"failed_to_load_people": "No s'han pogut carregar les persones",
|
"failed_to_load_people": "No s'han pogut carregar les persones",
|
||||||
"failed_to_remove_product_key": "No s'ha pogut eliminar la clau del producte",
|
"failed_to_remove_product_key": "No s'ha pogut eliminar la clau del producte",
|
||||||
"failed_to_stack_assets": "No s'han pogut apilar els elements",
|
"failed_to_stack_assets": "No s'han pogut apilar els elements",
|
||||||
"failed_to_unstack_assets": "No s'han pogut desapilar els elements",
|
"failed_to_unstack_assets": "No s'han pogut desapilar els elements",
|
||||||
|
"failed_to_update_notification_status": "Error en actualitzar l'estat de les notificacions",
|
||||||
"import_path_already_exists": "Aquesta ruta d'importació ja existeix.",
|
"import_path_already_exists": "Aquesta ruta d'importació ja existeix.",
|
||||||
"incorrect_email_or_password": "Correu electrònic o contrasenya incorrectes",
|
"incorrect_email_or_password": "Correu electrònic o contrasenya incorrectes",
|
||||||
"paths_validation_failed": "{paths, plural, one {# ruta} other {# rutes}} no ha pogut validar",
|
"paths_validation_failed": "{paths, plural, one {# ruta} other {# rutes}} no ha pogut validar",
|
||||||
@@ -920,6 +929,7 @@
|
|||||||
"unable_to_remove_reaction": "No es pot eliminar la reacció",
|
"unable_to_remove_reaction": "No es pot eliminar la reacció",
|
||||||
"unable_to_repair_items": "No es poden reparar els elements",
|
"unable_to_repair_items": "No es poden reparar els elements",
|
||||||
"unable_to_reset_password": "No es pot restablir la contrasenya",
|
"unable_to_reset_password": "No es pot restablir la contrasenya",
|
||||||
|
"unable_to_reset_pin_code": "No es pot restablir el codi PIN",
|
||||||
"unable_to_resolve_duplicate": "No es pot resoldre el duplicat",
|
"unable_to_resolve_duplicate": "No es pot resoldre el duplicat",
|
||||||
"unable_to_restore_assets": "No es poden restaurar els recursos",
|
"unable_to_restore_assets": "No es poden restaurar els recursos",
|
||||||
"unable_to_restore_trash": "No es pot restaurar la paperera",
|
"unable_to_restore_trash": "No es pot restaurar la paperera",
|
||||||
@@ -948,15 +958,15 @@
|
|||||||
"unable_to_upload_file": "No es pot carregar el fitxer"
|
"unable_to_upload_file": "No es pot carregar el fitxer"
|
||||||
},
|
},
|
||||||
"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_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 {age}",
|
||||||
"exif_bottom_sheet_person_age_months": "Edat {} mesos",
|
"exif_bottom_sheet_person_age_months": "Edat {months} mesos",
|
||||||
"exif_bottom_sheet_person_age_year_months": "Edat 1 any, {} mesos",
|
"exif_bottom_sheet_person_age_year_months": "Edat 1 any, {months} mesos",
|
||||||
"exif_bottom_sheet_person_age_years": "Edat {}",
|
"exif_bottom_sheet_person_age_years": "Edat {years}",
|
||||||
"exit_slideshow": "Surt de la presentació de diapositives",
|
"exit_slideshow": "Surt de la presentació de diapositives",
|
||||||
"expand_all": "Ampliar-ho tot",
|
"expand_all": "Ampliar-ho tot",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Treball en curs",
|
"experimental_settings_new_asset_list_subtitle": "Treball en curs",
|
||||||
@@ -974,7 +984,7 @@
|
|||||||
"external": "Extern",
|
"external": "Extern",
|
||||||
"external_libraries": "Llibreries externes",
|
"external_libraries": "Llibreries externes",
|
||||||
"external_network": "Xarxa externa",
|
"external_network": "Xarxa externa",
|
||||||
"external_network_sheet_info": "Quan no estigui a la xarxa WiFi preferida, l'aplicació es connectarà al servidor mitjançant el primer dels URL següents a què pot arribar, començant de dalt a baix.",
|
"external_network_sheet_info": "Quan no estigui a la xarxa Wi-Fi preferida, l'aplicació es connectarà al servidor mitjançant el primer dels URL següents a què pot arribar, començant de dalt a baix",
|
||||||
"face_unassigned": "Sense assignar",
|
"face_unassigned": "Sense assignar",
|
||||||
"failed": "Fallat",
|
"failed": "Fallat",
|
||||||
"failed_to_load_assets": "Error carregant recursos",
|
"failed_to_load_assets": "Error carregant recursos",
|
||||||
@@ -992,6 +1002,7 @@
|
|||||||
"filetype": "Tipus d'arxiu",
|
"filetype": "Tipus d'arxiu",
|
||||||
"filter": "Filtrar",
|
"filter": "Filtrar",
|
||||||
"filter_people": "Filtra persones",
|
"filter_people": "Filtra persones",
|
||||||
|
"filter_places": "Filtrar per llocs",
|
||||||
"find_them_fast": "Trobeu-los ràpidament pel nom amb la cerca",
|
"find_them_fast": "Trobeu-los ràpidament pel nom amb la cerca",
|
||||||
"fix_incorrect_match": "Corregiu la coincidència incorrecta",
|
"fix_incorrect_match": "Corregiu la coincidència incorrecta",
|
||||||
"folder": "Carpeta",
|
"folder": "Carpeta",
|
||||||
@@ -1040,11 +1051,12 @@
|
|||||||
"home_page_delete_remote_err_local": "Elements locals a la selecció d'eliminació remota, ometent",
|
"home_page_delete_remote_err_local": "Elements locals a la selecció d'eliminació remota, ometent",
|
||||||
"home_page_favorite_err_local": "Encara no es pot afegir a preferits elements locals, ometent",
|
"home_page_favorite_err_local": "Encara no es pot afegir a preferits elements locals, ometent",
|
||||||
"home_page_favorite_err_partner": "Encara no es pot afegir a preferits elements de companys, ometent",
|
"home_page_favorite_err_partner": "Encara no es pot afegir a preferits elements de companys, ometent",
|
||||||
"home_page_first_time_notice": "Si és la primera vegada que utilitzes l'app, si us plau, assegura't d'escollir un àlbum de còpia de seguretat perquè la línia de temps pugui carregar fotos i vídeos als àlbums.",
|
"home_page_first_time_notice": "Si és la primera vegada que utilitzes l'app, si us plau, assegura't d'escollir un àlbum de còpia de seguretat perquè la línia de temps pugui carregar fotos i vídeos als àlbums",
|
||||||
"home_page_share_err_local": "No es poden compartir els elements locals a través d'un enllaç, ometent",
|
"home_page_share_err_local": "No es poden compartir els elements locals a través d'un enllaç, ometent",
|
||||||
"home_page_upload_err_limit": "Només es poden pujar un màxim de 30 elements alhora, ometent",
|
"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",
|
||||||
@@ -1120,7 +1132,7 @@
|
|||||||
"local_network": "Xarxa local",
|
"local_network": "Xarxa local",
|
||||||
"local_network_sheet_info": "L'aplicació es connectarà al servidor mitjançant aquest URL quan utilitzeu la xarxa Wi-Fi especificada",
|
"local_network_sheet_info": "L'aplicació es connectarà al servidor mitjançant aquest URL quan utilitzeu la xarxa Wi-Fi especificada",
|
||||||
"location_permission": "Permís d'ubicació",
|
"location_permission": "Permís d'ubicació",
|
||||||
"location_permission_content": "Per utilitzar la funció de canvi automàtic, Immich necessita un permís de ubicació precisa perquè pugui llegir el nom de la xarxa WiFi actual",
|
"location_permission_content": "Per utilitzar la funció de canvi automàtic, Immich necessita un permís d'ubicació precisa perquè pugui llegir el nom de la xarxa Wi-Fi actual",
|
||||||
"location_picker_choose_on_map": "Escollir en el mapa",
|
"location_picker_choose_on_map": "Escollir en el mapa",
|
||||||
"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",
|
||||||
@@ -1144,7 +1156,7 @@
|
|||||||
"login_form_err_trailing_whitespace": "Espai en blanc al final",
|
"login_form_err_trailing_whitespace": "Espai en blanc al final",
|
||||||
"login_form_failed_get_oauth_server_config": "Error en iniciar sessió amb OAuth, comprova l'URL del servidor",
|
"login_form_failed_get_oauth_server_config": "Error en iniciar sessió amb OAuth, comprova l'URL del servidor",
|
||||||
"login_form_failed_get_oauth_server_disable": "La funcionalitat OAuth no està disponible en aquest servidor",
|
"login_form_failed_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_password_hint": "contrasenya",
|
"login_form_password_hint": "contrasenya",
|
||||||
"login_form_save_login": "Mantingues identificat",
|
"login_form_save_login": "Mantingues identificat",
|
||||||
@@ -1170,8 +1182,8 @@
|
|||||||
"manage_your_devices": "Gestioneu els vostres dispositius connectats",
|
"manage_your_devices": "Gestioneu els vostres dispositius connectats",
|
||||||
"manage_your_oauth_connection": "Gestioneu la vostra connexió OAuth",
|
"manage_your_oauth_connection": "Gestioneu la vostra connexió OAuth",
|
||||||
"map": "Mapa",
|
"map": "Mapa",
|
||||||
"map_assets_in_bound": "{} foto",
|
"map_assets_in_bound": "{count} foto",
|
||||||
"map_assets_in_bounds": "{} fotos",
|
"map_assets_in_bounds": "{count} 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_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ó",
|
||||||
@@ -1185,15 +1197,18 @@
|
|||||||
"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_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 {days} 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 {years} anys",
|
||||||
"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_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",
|
||||||
|
"mark_all_as_read": "Marcar-ho tot com a llegit",
|
||||||
|
"mark_as_read": "Marcar com ha llegit",
|
||||||
|
"marked_all_as_read": "Marcat tot com a llegit",
|
||||||
"matches": "Coincidències",
|
"matches": "Coincidències",
|
||||||
"media_type": "Tipus de mitjà",
|
"media_type": "Tipus de mitjà",
|
||||||
"memories": "Records",
|
"memories": "Records",
|
||||||
@@ -1203,7 +1218,7 @@
|
|||||||
"memories_start_over": "Torna a començar",
|
"memories_start_over": "Torna a començar",
|
||||||
"memories_swipe_to_close": "Llisca per tancar",
|
"memories_swipe_to_close": "Llisca per tancar",
|
||||||
"memories_year_ago": "Fa un any",
|
"memories_year_ago": "Fa un any",
|
||||||
"memories_years_ago": "Fa {} anys",
|
"memories_years_ago": "Fa {years, plural, other {# years}} anys",
|
||||||
"memory": "Record",
|
"memory": "Record",
|
||||||
"memory_lane_title": "Línia de records {title}",
|
"memory_lane_title": "Línia de records {title}",
|
||||||
"menu": "Menú",
|
"menu": "Menú",
|
||||||
@@ -1220,9 +1235,11 @@
|
|||||||
"month": "Mes",
|
"month": "Mes",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
"more": "Més",
|
"more": "Més",
|
||||||
|
"moved_to_archive": "S'han mogut {count, plural, one {# asset} other {# assets}} a l'arxiu",
|
||||||
|
"moved_to_library": "S'ha mogut {count, plural, one {# asset} other {# assets}} a la llibreria",
|
||||||
"moved_to_trash": "S'ha mogut a la paperera",
|
"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",
|
||||||
"mute_memories": "Silenciar records",
|
"mute_memories": "Silenciar records",
|
||||||
"my_albums": "Els meus àlbums",
|
"my_albums": "Els meus àlbums",
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
@@ -1234,6 +1251,7 @@
|
|||||||
"new_api_key": "Nova clau de l'API",
|
"new_api_key": "Nova clau de l'API",
|
||||||
"new_password": "Nova contrasenya",
|
"new_password": "Nova contrasenya",
|
||||||
"new_person": "Persona nova",
|
"new_person": "Persona nova",
|
||||||
|
"new_pin_code": "Nou codi PIN",
|
||||||
"new_user_created": "Nou usuari creat",
|
"new_user_created": "Nou usuari creat",
|
||||||
"new_version_available": "NOVA VERSIÓ DISPONIBLE",
|
"new_version_available": "NOVA VERSIÓ DISPONIBLE",
|
||||||
"newest_first": "El més nou primer",
|
"newest_first": "El més nou primer",
|
||||||
@@ -1252,6 +1270,8 @@
|
|||||||
"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_notifications": "No hi ha notificacions",
|
||||||
|
"no_people_found": "No s'han trobat coincidències de persones",
|
||||||
"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",
|
||||||
@@ -1282,6 +1302,7 @@
|
|||||||
"onboarding_welcome_user": "Benvingut, {user}",
|
"onboarding_welcome_user": "Benvingut, {user}",
|
||||||
"online": "En línia",
|
"online": "En línia",
|
||||||
"only_favorites": "Només preferits",
|
"only_favorites": "Només preferits",
|
||||||
|
"open": "Obrir",
|
||||||
"open_in_map_view": "Obrir a la vista del mapa",
|
"open_in_map_view": "Obrir a la vista del mapa",
|
||||||
"open_in_openstreetmap": "Obre a OpenStreetMap",
|
"open_in_openstreetmap": "Obre a OpenStreetMap",
|
||||||
"open_the_search_filters": "Obriu els filtres de cerca",
|
"open_the_search_filters": "Obriu els filtres de cerca",
|
||||||
@@ -1305,7 +1326,7 @@
|
|||||||
"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": "{partner} ja no podrà accedir a les teves fotos.",
|
||||||
"partner_sharing": "Compartició amb companys",
|
"partner_sharing": "Compartició amb companys",
|
||||||
"partners": "Companys",
|
"partners": "Companys",
|
||||||
"password": "Contrasenya",
|
"password": "Contrasenya",
|
||||||
@@ -1351,6 +1372,9 @@
|
|||||||
"photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}",
|
"photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}",
|
||||||
"photos_from_previous_years": "Fotos d'anys anteriors",
|
"photos_from_previous_years": "Fotos d'anys anteriors",
|
||||||
"pick_a_location": "Triar una ubicació",
|
"pick_a_location": "Triar una ubicació",
|
||||||
|
"pin_code_changed_successfully": "Codi PIN canviat correctament",
|
||||||
|
"pin_code_reset_successfully": "S'ha restablert correctament el codi PIN",
|
||||||
|
"pin_code_setup_successfully": "S'ha configurat correctament un codi PIN",
|
||||||
"place": "Lloc",
|
"place": "Lloc",
|
||||||
"places": "Llocs",
|
"places": "Llocs",
|
||||||
"places_count": "{count, plural, one {{count, number} Lloc} other {{count, number} Llocs}}",
|
"places_count": "{count, plural, one {{count, number} Lloc} other {{count, number} Llocs}}",
|
||||||
@@ -1368,6 +1392,7 @@
|
|||||||
"previous_or_next_photo": "Foto anterior o següent",
|
"previous_or_next_photo": "Foto anterior o següent",
|
||||||
"primary": "Primària",
|
"primary": "Primària",
|
||||||
"privacy": "Privacitat",
|
"privacy": "Privacitat",
|
||||||
|
"profile": "Perfil",
|
||||||
"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.",
|
||||||
@@ -1381,7 +1406,7 @@
|
|||||||
"public_share": "Compartit públicament",
|
"public_share": "Compartit públicament",
|
||||||
"purchase_account_info": "Contribuent",
|
"purchase_account_info": "Contribuent",
|
||||||
"purchase_activated_subtitle": "Gràcies per donar suport a Immich i al programari de codi obert",
|
"purchase_activated_subtitle": "Gràcies per donar suport a Immich i al programari de codi obert",
|
||||||
"purchase_activated_time": "Activat el {date, date}",
|
"purchase_activated_time": "Activat el {date}",
|
||||||
"purchase_activated_title": "La teva clau s'ha activat correctament",
|
"purchase_activated_title": "La teva clau s'ha activat correctament",
|
||||||
"purchase_button_activate": "Activar",
|
"purchase_button_activate": "Activar",
|
||||||
"purchase_button_buy": "Comprar",
|
"purchase_button_buy": "Comprar",
|
||||||
@@ -1426,6 +1451,8 @@
|
|||||||
"recent_searches": "Cerques recents",
|
"recent_searches": "Cerques recents",
|
||||||
"recently_added": "Afegit recentment",
|
"recently_added": "Afegit recentment",
|
||||||
"recently_added_page_title": "Afegit recentment",
|
"recently_added_page_title": "Afegit recentment",
|
||||||
|
"recently_taken": "Fet recentment",
|
||||||
|
"recently_taken_page_title": "Fet recentment",
|
||||||
"refresh": "Actualitzar",
|
"refresh": "Actualitzar",
|
||||||
"refresh_encoded_videos": "Actualitza vídeos codificats",
|
"refresh_encoded_videos": "Actualitza vídeos codificats",
|
||||||
"refresh_faces": "Actualitzar cares",
|
"refresh_faces": "Actualitzar cares",
|
||||||
@@ -1468,6 +1495,7 @@
|
|||||||
"reset": "Restablir",
|
"reset": "Restablir",
|
||||||
"reset_password": "Restablir contrasenya",
|
"reset_password": "Restablir contrasenya",
|
||||||
"reset_people_visibility": "Restablir la visibilitat de les persones",
|
"reset_people_visibility": "Restablir la visibilitat de les persones",
|
||||||
|
"reset_pin_code": "Restablir el codi PIN",
|
||||||
"reset_to_default": "Restableix els valors predeterminats",
|
"reset_to_default": "Restableix els valors predeterminats",
|
||||||
"resolve_duplicates": "Resoldre duplicats",
|
"resolve_duplicates": "Resoldre duplicats",
|
||||||
"resolved_all_duplicates": "Tots els duplicats resolts",
|
"resolved_all_duplicates": "Tots els duplicats resolts",
|
||||||
@@ -1560,6 +1588,7 @@
|
|||||||
"select_keep_all": "Mantén tota la selecció",
|
"select_keep_all": "Mantén tota la selecció",
|
||||||
"select_library_owner": "Selecciona el propietari de la bilbioteca",
|
"select_library_owner": "Selecciona el propietari de la bilbioteca",
|
||||||
"select_new_face": "Selecciona nova cara",
|
"select_new_face": "Selecciona nova cara",
|
||||||
|
"select_person_to_tag": "Selecciona una persona per etiquetar",
|
||||||
"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",
|
||||||
@@ -1590,12 +1619,12 @@
|
|||||||
"setting_languages_apply": "Aplicar",
|
"setting_languages_apply": "Aplicar",
|
||||||
"setting_languages_subtitle": "Canvia el llenguatge de l'aplicació",
|
"setting_languages_subtitle": "Canvia el llenguatge de l'aplicació",
|
||||||
"setting_languages_title": "Idiomes",
|
"setting_languages_title": "Idiomes",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notifica les fallades de la còpia de seguretat en segon pla: {}",
|
"setting_notifications_notify_failures_grace_period": "Notifica les fallades de la còpia de seguretat en segon pla: {duration}",
|
||||||
"setting_notifications_notify_hours": "{} hores",
|
"setting_notifications_notify_hours": "{count} hores",
|
||||||
"setting_notifications_notify_immediately": "immediatament",
|
"setting_notifications_notify_immediately": "immediatament",
|
||||||
"setting_notifications_notify_minutes": "{} minuts",
|
"setting_notifications_notify_minutes": "{count} minuts",
|
||||||
"setting_notifications_notify_never": "mai",
|
"setting_notifications_notify_never": "mai",
|
||||||
"setting_notifications_notify_seconds": "{} segons",
|
"setting_notifications_notify_seconds": "{count} segons",
|
||||||
"setting_notifications_single_progress_subtitle": "Informació detallada del progrés de la pujada de cada fitxer",
|
"setting_notifications_single_progress_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ó",
|
||||||
@@ -1607,9 +1636,10 @@
|
|||||||
"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",
|
||||||
|
"setup_pin_code": "Configurar un codi PIN",
|
||||||
"share": "Comparteix",
|
"share": "Comparteix",
|
||||||
"share_add_photos": "Afegeix fotografies",
|
"share_add_photos": "Afegeix fotografies",
|
||||||
"share_assets_selected": "{} seleccionats",
|
"share_assets_selected": "{count} seleccionats",
|
||||||
"share_dialog_preparing": "S'està preparant...",
|
"share_dialog_preparing": "S'està preparant...",
|
||||||
"shared": "Compartit",
|
"shared": "Compartit",
|
||||||
"shared_album_activities_input_disable": "Els comentaris estan desactivats",
|
"shared_album_activities_input_disable": "Els comentaris estan desactivats",
|
||||||
@@ -1623,32 +1653,32 @@
|
|||||||
"shared_by_user": "Compartit per {user}",
|
"shared_by_user": "Compartit per {user}",
|
||||||
"shared_by_you": "Compartit per tu",
|
"shared_by_you": "Compartit per tu",
|
||||||
"shared_from_partner": "Fotos de {partner}",
|
"shared_from_partner": "Fotos de {partner}",
|
||||||
"shared_intent_upload_button_progress_text": "{} / {} Pujat",
|
"shared_intent_upload_button_progress_text": "{current} / {total} Pujat",
|
||||||
"shared_link_app_bar_title": "Enllaços compartits",
|
"shared_link_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ç: {link}\nContrasenya: {password}",
|
||||||
"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_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_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": "{count} dies",
|
||||||
"shared_link_edit_expire_after_option_hour": "1 hora",
|
"shared_link_edit_expire_after_option_hour": "1 hora",
|
||||||
"shared_link_edit_expire_after_option_hours": "{} hores",
|
"shared_link_edit_expire_after_option_hours": "{count} hores",
|
||||||
"shared_link_edit_expire_after_option_minute": "1 minut",
|
"shared_link_edit_expire_after_option_minute": "1 minut",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minuts",
|
"shared_link_edit_expire_after_option_minutes": "{count} minuts",
|
||||||
"shared_link_edit_expire_after_option_months": "{} mesos",
|
"shared_link_edit_expire_after_option_months": "{count} mesos",
|
||||||
"shared_link_edit_expire_after_option_year": "any {}",
|
"shared_link_edit_expire_after_option_year": "any {count}",
|
||||||
"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_submit_button": "Actualitza l'enllaç",
|
"shared_link_edit_submit_button": "Actualitza l'enllaç",
|
||||||
"shared_link_error_server_url_fetch": "No s'ha pogut obtenir l'URL del servidor",
|
"shared_link_error_server_url_fetch": "No s'ha pogut obtenir l'URL del servidor",
|
||||||
"shared_link_expires_day": "Caduca d'aquí a {} dia",
|
"shared_link_expires_day": "Caduca d'aquí a {count} dia",
|
||||||
"shared_link_expires_days": "Caduca d'aquí a {} dies",
|
"shared_link_expires_days": "Caduca d'aquí a {count} dies",
|
||||||
"shared_link_expires_hour": "Caduca d'aquí a {} hora",
|
"shared_link_expires_hour": "Caduca d'aquí a {count} hora",
|
||||||
"shared_link_expires_hours": "Caduca d'aquí a {} hores",
|
"shared_link_expires_hours": "Caduca d'aquí a {count} hores",
|
||||||
"shared_link_expires_minute": "Caduca d'aquí a {} minut",
|
"shared_link_expires_minute": "Caduca d'aquí a {count} minut",
|
||||||
"shared_link_expires_minutes": "Caduca d'aquí a {} minuts",
|
"shared_link_expires_minutes": "Caduca d'aquí a {count} minuts",
|
||||||
"shared_link_expires_never": "Caduca ∞",
|
"shared_link_expires_never": "Caduca ∞",
|
||||||
"shared_link_expires_second": "Caduca d'aquí a {} segon",
|
"shared_link_expires_second": "Caduca d'aquí a {count} segon",
|
||||||
"shared_link_expires_seconds": "Caduca d'aquí a {} segons",
|
"shared_link_expires_seconds": "Caduca d'aquí a {count} segons",
|
||||||
"shared_link_individual_shared": "Individual compartit",
|
"shared_link_individual_shared": "Individual compartit",
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
"shared_link_manage_links": "Gestiona els enllaços compartits",
|
"shared_link_manage_links": "Gestiona els enllaços compartits",
|
||||||
@@ -1723,6 +1753,7 @@
|
|||||||
"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_label": "Etiquetatge d'emmagatzematge",
|
"storage_label": "Etiquetatge d'emmagatzematge",
|
||||||
|
"storage_quota": "Quota d'emmagatzematge",
|
||||||
"storage_usage": "{used} de {available} en ús",
|
"storage_usage": "{used} de {available} en ús",
|
||||||
"submit": "Envia",
|
"submit": "Envia",
|
||||||
"suggestions": "Suggeriments",
|
"suggestions": "Suggeriments",
|
||||||
@@ -1749,7 +1780,7 @@
|
|||||||
"theme_selection": "Selecció de tema",
|
"theme_selection": "Selecció de tema",
|
||||||
"theme_selection_description": "Activa automàticament el tema fosc o clar en funció de les preferències del sistema del navegador",
|
"theme_selection_description": "Activa automàticament el tema fosc o clar en funció de les preferències del sistema del navegador",
|
||||||
"theme_setting_asset_list_storage_indicator_title": "Mostra l'indicador d'emmagatzematge als títols dels elements",
|
"theme_setting_asset_list_storage_indicator_title": "Mostra l'indicador d'emmagatzematge als títols dels elements",
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "Nombre d'elements per fila ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Nombre d'elements per fila ({count})",
|
||||||
"theme_setting_colorful_interface_subtitle": "Apliqueu color primari a les superfícies de fons.",
|
"theme_setting_colorful_interface_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_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",
|
||||||
@@ -1784,13 +1815,15 @@
|
|||||||
"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_all": "Eliminar-ho tot",
|
"trash_page_delete_all": "Eliminar-ho tot",
|
||||||
"trash_page_empty_trash_dialog_content": "Segur que voleu eliminar els elements? Aquests elements seran eliminats permanentment de Immich",
|
"trash_page_empty_trash_dialog_content": "Segur que voleu eliminar els elements? Aquests elements seran eliminats permanentment de Immich",
|
||||||
"trash_page_info": "Els elements que s'enviïn a la paperera s'eliminaran permanentment després de {} dies",
|
"trash_page_info": "Els elements que s'enviïn a la paperera s'eliminaran permanentment després de {days} dies",
|
||||||
"trash_page_no_assets": "No hi ha elements a la paperera",
|
"trash_page_no_assets": "No hi ha elements a la paperera",
|
||||||
"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_title": "Paperera ({})",
|
"trash_page_title": "Paperera ({count})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Els elements que s'enviïn a la paperera s'eliminaran permanentment després de {days, plural, one {# dia} other {# dies}}.",
|
"trashed_items_will_be_permanently_deleted_after": "Els elements que s'enviïn a la paperera s'eliminaran permanentment després de {days, plural, one {# dia} other {# dies}}.",
|
||||||
"type": "Tipus",
|
"type": "Tipus",
|
||||||
|
"unable_to_change_pin_code": "No es pot canviar el codi PIN",
|
||||||
|
"unable_to_setup_pin_code": "No s'ha pogut configurar el codi PIN",
|
||||||
"unarchive": "Desarxivar",
|
"unarchive": "Desarxivar",
|
||||||
"unarchived_count": "{count, plural, other {# elements desarxivats}}",
|
"unarchived_count": "{count, plural, other {# elements desarxivats}}",
|
||||||
"unfavorite": "Reverteix preferit",
|
"unfavorite": "Reverteix preferit",
|
||||||
@@ -1814,6 +1847,7 @@
|
|||||||
"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",
|
||||||
|
"updated_at": "Actualitzat",
|
||||||
"updated_password": "Contrasenya actualitzada",
|
"updated_password": "Contrasenya actualitzada",
|
||||||
"upload": "Pujar",
|
"upload": "Pujar",
|
||||||
"upload_concurrency": "Concurrència de pujades",
|
"upload_concurrency": "Concurrència de pujades",
|
||||||
@@ -1826,15 +1860,18 @@
|
|||||||
"upload_status_errors": "Errors",
|
"upload_status_errors": "Errors",
|
||||||
"upload_status_uploaded": "Carregat",
|
"upload_status_uploaded": "Carregat",
|
||||||
"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 ({count})",
|
||||||
"uploading": "Pujant",
|
"uploading": "Pujant",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Ús",
|
"usage": "Ús",
|
||||||
"use_current_connection": "utilitzar la connexió actual",
|
"use_current_connection": "utilitzar la connexió actual",
|
||||||
"use_custom_date_range": "Fes servir un rang de dates personalitzat",
|
"use_custom_date_range": "Fes servir un rang de dates personalitzat",
|
||||||
"user": "Usuari",
|
"user": "Usuari",
|
||||||
|
"user_has_been_deleted": "Aquest usuari ha sigut eliminat.",
|
||||||
"user_id": "ID d'usuari",
|
"user_id": "ID d'usuari",
|
||||||
"user_liked": "A {user} li ha agradat {type, select, photo {aquesta foto} video {aquest vídeo} asset {aquest recurs} other {}}",
|
"user_liked": "A {user} li ha agradat {type, select, photo {aquesta foto} video {aquest vídeo} asset {aquest recurs} other {}}",
|
||||||
|
"user_pin_code_settings": "Codi PIN",
|
||||||
|
"user_pin_code_settings_description": "Gestiona el teu codi PIN",
|
||||||
"user_purchase_settings": "Compra",
|
"user_purchase_settings": "Compra",
|
||||||
"user_purchase_settings_description": "Gestiona la teva compra",
|
"user_purchase_settings_description": "Gestiona la teva compra",
|
||||||
"user_role_set": "Establir {user} com a {role}",
|
"user_role_set": "Establir {user} com a {role}",
|
||||||
@@ -1883,11 +1920,11 @@
|
|||||||
"week": "Setmana",
|
"week": "Setmana",
|
||||||
"welcome": "Benvingut",
|
"welcome": "Benvingut",
|
||||||
"welcome_to_immich": "Benvingut a immich",
|
"welcome_to_immich": "Benvingut a immich",
|
||||||
"wifi_name": "Nom WiFi",
|
"wifi_name": "Nom Wi-Fi",
|
||||||
"year": "Any",
|
"year": "Any",
|
||||||
"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_wifi_name": "El teu nom WiFi",
|
"your_wifi_name": "Nom del teu Wi-Fi",
|
||||||
"zoom_image": "Ampliar Imatge"
|
"zoom_image": "Ampliar Imatge"
|
||||||
}
|
}
|
||||||
|
|||||||
+127
-90
@@ -39,11 +39,11 @@
|
|||||||
"authentication_settings_disable_all": "Opravdu chcete zakázat všechny metody přihlášení? Přihlašování bude úplně zakázáno.",
|
"authentication_settings_disable_all": "Opravdu chcete zakázat všechny metody přihlášení? Přihlašování bude úplně zakázáno.",
|
||||||
"authentication_settings_reenable": "Pro opětovné povolení použijte příkaz <link>Příkaz serveru</link>.",
|
"authentication_settings_reenable": "Pro opětovné povolení použijte příkaz <link>Příkaz serveru</link>.",
|
||||||
"background_task_job": "Úkoly na pozadí",
|
"background_task_job": "Úkoly na pozadí",
|
||||||
"backup_database": "Zálohování databáze",
|
"backup_database": "Vytvořit výpis databáze",
|
||||||
"backup_database_enable_description": "Povolit zálohování databáze",
|
"backup_database_enable_description": "Povolit výpisy z databáze",
|
||||||
"backup_keep_last_amount": "Počet předchozích záloh k uchování",
|
"backup_keep_last_amount": "Počet předchozích výpisů, které se mají ponechat",
|
||||||
"backup_settings": "Nastavení zálohování",
|
"backup_settings": "Nastavení výpisu databáze",
|
||||||
"backup_settings_description": "Správa nastavení zálohování databáze",
|
"backup_settings_description": "Správa nastavení výpisu databáze. Poznámka: Tyto úlohy nejsou monitorovány a nebudete upozorněni na jejich selhání.",
|
||||||
"check_all": "Vše zkontrolovat",
|
"check_all": "Vše zkontrolovat",
|
||||||
"cleanup": "Vyčištění",
|
"cleanup": "Vyčištění",
|
||||||
"cleared_jobs": "Hotové úlohy pro: {job}",
|
"cleared_jobs": "Hotové úlohy pro: {job}",
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
"confirm_email_below": "Pro potvrzení zadejte níže \"{email}\"",
|
"confirm_email_below": "Pro potvrzení zadejte níže \"{email}\"",
|
||||||
"confirm_reprocess_all_faces": "Opravdu chcete znovu zpracovat všechny obličeje? Tím se vymažou i pojmenované osoby.",
|
"confirm_reprocess_all_faces": "Opravdu chcete znovu zpracovat všechny obličeje? Tím se vymažou i pojmenované osoby.",
|
||||||
"confirm_user_password_reset": "Opravdu chcete obnovit heslo uživatele {user}?",
|
"confirm_user_password_reset": "Opravdu chcete obnovit heslo uživatele {user}?",
|
||||||
|
"confirm_user_pin_code_reset": "Opravdu chcete resetovat PIN kód uživatele {user}?",
|
||||||
"create_job": "Vytvořit úlohu",
|
"create_job": "Vytvořit úlohu",
|
||||||
"cron_expression": "Výraz cron",
|
"cron_expression": "Výraz cron",
|
||||||
"cron_expression_description": "Nastavte interval prohledávání pomocí cron formátu. Další informace naleznete např. v <link>Crontab Guru</link>",
|
"cron_expression_description": "Nastavte interval prohledávání pomocí cron formátu. Další informace naleznete např. v <link>Crontab Guru</link>",
|
||||||
@@ -192,26 +193,22 @@
|
|||||||
"oauth_auto_register": "Automatická registrace",
|
"oauth_auto_register": "Automatická registrace",
|
||||||
"oauth_auto_register_description": "Automaticky registrovat nové uživatele po přihlášení pomocí OAuth",
|
"oauth_auto_register_description": "Automaticky registrovat nové uživatele po přihlášení pomocí OAuth",
|
||||||
"oauth_button_text": "Text tlačítka",
|
"oauth_button_text": "Text tlačítka",
|
||||||
"oauth_client_id": "Client ID",
|
"oauth_client_secret_description": "Vyžaduje se, pokud poskytovatel OAuth nepodporuje PKCE (Proof Key for Code Exchange)",
|
||||||
"oauth_client_secret": "Client Secret",
|
|
||||||
"oauth_enable_description": "Přihlásit pomocí OAuth",
|
"oauth_enable_description": "Přihlásit pomocí OAuth",
|
||||||
"oauth_issuer_url": "URL vydavatele",
|
|
||||||
"oauth_mobile_redirect_uri": "Mobilní přesměrování URI",
|
"oauth_mobile_redirect_uri": "Mobilní přesměrování URI",
|
||||||
"oauth_mobile_redirect_uri_override": "Přepsat mobilní přesměrování URI",
|
"oauth_mobile_redirect_uri_override": "Přepsat mobilní přesměrování URI",
|
||||||
"oauth_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například '{callback}'",
|
"oauth_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například '{callback}'",
|
||||||
"oauth_profile_signing_algorithm": "Algoritmus podepisování profilu",
|
|
||||||
"oauth_profile_signing_algorithm_description": "Algoritmus použitý k podepsání profilu uživatele.",
|
|
||||||
"oauth_scope": "Rozsah",
|
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "Správa nastavení OAuth přihlášení",
|
"oauth_settings_description": "Správa nastavení OAuth přihlášení",
|
||||||
"oauth_settings_more_details": "Další podrobnosti o této funkci naleznete v <link>dokumentaci</link>.",
|
"oauth_settings_more_details": "Další podrobnosti o této funkci naleznete v <link>dokumentaci</link>.",
|
||||||
"oauth_signing_algorithm": "Algoritmus podepisování",
|
|
||||||
"oauth_storage_label_claim": "Deklarace štítku úložiště",
|
"oauth_storage_label_claim": "Deklarace štítku úložiště",
|
||||||
"oauth_storage_label_claim_description": "Automaticky nastavit štítek úložiště uživatele na hodnotu této deklarace.",
|
"oauth_storage_label_claim_description": "Automaticky nastavit štítek úložiště uživatele na hodnotu této deklarace.",
|
||||||
"oauth_storage_quota_claim": "Deklarace kvóty úložiště",
|
"oauth_storage_quota_claim": "Deklarace kvóty úložiště",
|
||||||
"oauth_storage_quota_claim_description": "Automaticky nastavit kvótu úložiště uživatele na hodnotu této deklarace.",
|
"oauth_storage_quota_claim_description": "Automaticky nastavit kvótu úložiště uživatele na hodnotu této deklarace.",
|
||||||
"oauth_storage_quota_default": "Výchozí kvóta úložiště (GiB)",
|
"oauth_storage_quota_default": "Výchozí kvóta úložiště (GiB)",
|
||||||
"oauth_storage_quota_default_description": "Kvóta v GiB, která se použije, pokud není poskytnuta žádná deklarace (pro neomezenou kvótu zadejte 0).",
|
"oauth_storage_quota_default_description": "Kvóta v GiB, která se použije, pokud není poskytnuta žádná deklarace (pro neomezenou kvótu zadejte 0).",
|
||||||
|
"oauth_timeout": "Časový limit požadavku",
|
||||||
|
"oauth_timeout_description": "Časový limit pro požadavky v milisekundách",
|
||||||
"offline_paths": "Cesty offline",
|
"offline_paths": "Cesty offline",
|
||||||
"offline_paths_description": "Tyto výsledky mohou být způsobeny ručním odstraněním souborů, které nejsou součástí externí knihovny.",
|
"offline_paths_description": "Tyto výsledky mohou být způsobeny ručním odstraněním souborů, které nejsou součástí externí knihovny.",
|
||||||
"password_enable_description": "Přihlášení pomocí e-mailu a hesla",
|
"password_enable_description": "Přihlášení pomocí e-mailu a hesla",
|
||||||
@@ -352,6 +349,7 @@
|
|||||||
"user_delete_delay_settings_description": "Počet dní po odstranění, po kterých bude odstraněn účet a položky uživatele. Úloha odstraňování uživatelů se spouští o půlnoci a kontroluje uživatele, kteří jsou připraveni k odstranění. Změny tohoto nastavení se vyhodnotí při dalším spuštění.",
|
"user_delete_delay_settings_description": "Počet dní po odstranění, po kterých bude odstraněn účet a položky uživatele. Úloha odstraňování uživatelů se spouští o půlnoci a kontroluje uživatele, kteří jsou připraveni k odstranění. Změny tohoto nastavení se vyhodnotí při dalším spuštění.",
|
||||||
"user_delete_immediately": "Účet a položky uživatele <b>{user}</b> budou zařazeny do fronty k trvalému smazání <b>okamžitě</b>.",
|
"user_delete_immediately": "Účet a položky uživatele <b>{user}</b> budou zařazeny do fronty k trvalému smazání <b>okamžitě</b>.",
|
||||||
"user_delete_immediately_checkbox": "Uživatele a položky zařadit do fronty k okamžitému smazání",
|
"user_delete_immediately_checkbox": "Uživatele a položky zařadit do fronty k okamžitému smazání",
|
||||||
|
"user_details": "Podrobnosti o uživateli",
|
||||||
"user_management": "Správa uživatelů",
|
"user_management": "Správa uživatelů",
|
||||||
"user_password_has_been_reset": "Heslo uživatele bylo obnoveno:",
|
"user_password_has_been_reset": "Heslo uživatele bylo obnoveno:",
|
||||||
"user_password_reset_description": "Poskytněte uživateli dočasné heslo a informujte ho, že si ho bude muset při příštím přihlášení změnit.",
|
"user_password_reset_description": "Poskytněte uživateli dočasné heslo a informujte ho, že si ho bude muset při příštím přihlášení změnit.",
|
||||||
@@ -371,18 +369,22 @@
|
|||||||
"admin_password": "Heslo správce",
|
"admin_password": "Heslo správce",
|
||||||
"administration": "Administrace",
|
"administration": "Administrace",
|
||||||
"advanced": "Pokročilé",
|
"advanced": "Pokročilé",
|
||||||
"advanced_settings_log_level_title": "Úroveň protokolování: {}",
|
"advanced_settings_enable_alternate_media_filter_subtitle": "Tuto možnost použijte k filtrování médií během synchronizace na základě alternativních kritérií. Tuto možnost vyzkoušejte pouze v případě, že máte problémy s detekcí všech alb v aplikaci.",
|
||||||
|
"advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] Použít alternativní filtr pro synchronizaci alb zařízení",
|
||||||
|
"advanced_settings_log_level_title": "Úroveň protokolování: {level}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z prostředků v zařízení velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.",
|
"advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z prostředků v zařízení velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.",
|
||||||
"advanced_settings_prefer_remote_title": "Preferovat vzdálené obrázky",
|
"advanced_settings_prefer_remote_title": "Preferovat vzdálené obrázky",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, které by měl Immich odesílat s každým síťovým požadavkem",
|
"advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, které by měl Immich odesílat s každým síťovým požadavkem",
|
||||||
"advanced_settings_proxy_headers_title": "Proxy hlavičky",
|
"advanced_settings_proxy_headers_title": "Proxy hlavičky",
|
||||||
"advanced_settings_self_signed_ssl_subtitle": "Vynechá ověření SSL certifikátu serveru. Vyžadováno pro self-signed certifikáty.",
|
"advanced_settings_self_signed_ssl_subtitle": "Vynechá ověření SSL certifikátu serveru. Vyžadováno pro self-signed certifikáty.",
|
||||||
"advanced_settings_self_signed_ssl_title": "Povolit self-signed SSL certifikáty",
|
"advanced_settings_self_signed_ssl_title": "Povolit self-signed SSL certifikáty",
|
||||||
|
"advanced_settings_sync_remote_deletions_subtitle": "Automaticky odstranit nebo obnovit položku v tomto zařízení, když je tato akce provedena na webu",
|
||||||
|
"advanced_settings_sync_remote_deletions_title": "Synchronizace vzdáleného mazání [EXPERIMENTÁLNÍ]",
|
||||||
"advanced_settings_tile_subtitle": "Pokročilé uživatelské nastavení",
|
"advanced_settings_tile_subtitle": "Pokročilé uživatelské nastavení",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Zobrazit dodatečné vlastnosti pro řešení problémů",
|
"advanced_settings_troubleshooting_subtitle": "Zobrazit dodatečné vlastnosti pro řešení problémů",
|
||||||
"advanced_settings_troubleshooting_title": "Řešení problémů",
|
"advanced_settings_troubleshooting_title": "Řešení problémů",
|
||||||
"age_months": "{months, plural, one {# měsíc} few {# měsíce} other {# měsíců}}",
|
"age_months": "Věk {months, plural, one {# měsíc} few {# měsíce} other {# měsíců}}",
|
||||||
"age_year_months": "1 rok a {months, plural, one {# měsíc} few {# měsíce} other {# měsíců}}",
|
"age_year_months": "Věk 1 rok, {months, plural, one {# měsíc} other {# měsíce}}",
|
||||||
"age_years": "{years, plural, one {# rok} few {# roky} other {# let}}",
|
"age_years": "{years, plural, one {# rok} few {# roky} other {# let}}",
|
||||||
"album_added": "Přidáno album",
|
"album_added": "Přidáno album",
|
||||||
"album_added_notification_setting_description": "Dostávat e-mailové oznámení, když jste přidáni do sdíleného alba",
|
"album_added_notification_setting_description": "Dostávat e-mailové oznámení, když jste přidáni do sdíleného alba",
|
||||||
@@ -400,9 +402,9 @@
|
|||||||
"album_remove_user_confirmation": "Opravdu chcete odebrat uživatele {user}?",
|
"album_remove_user_confirmation": "Opravdu chcete odebrat uživatele {user}?",
|
||||||
"album_share_no_users": "Zřejmě jste toto album sdíleli se všemi uživateli, nebo nemáte žádného uživatele, se kterým byste ho mohli sdílet.",
|
"album_share_no_users": "Zřejmě jste toto album sdíleli se všemi uživateli, nebo nemáte žádného uživatele, se kterým byste ho mohli sdílet.",
|
||||||
"album_thumbnail_card_item": "1 položka",
|
"album_thumbnail_card_item": "1 položka",
|
||||||
"album_thumbnail_card_items": "{} položek",
|
"album_thumbnail_card_items": "{count} položek",
|
||||||
"album_thumbnail_card_shared": " · Sdíleno",
|
"album_thumbnail_card_shared": " · Sdíleno",
|
||||||
"album_thumbnail_shared_by": "Sdílel(a) {}",
|
"album_thumbnail_shared_by": "Sdílel(a) {user}",
|
||||||
"album_updated": "Album aktualizováno",
|
"album_updated": "Album aktualizováno",
|
||||||
"album_updated_setting_description": "Dostávat e-mailová oznámení o nových položkách sdíleného alba",
|
"album_updated_setting_description": "Dostávat e-mailová oznámení o nových položkách sdíleného alba",
|
||||||
"album_user_left": "Opustil {album}",
|
"album_user_left": "Opustil {album}",
|
||||||
@@ -410,7 +412,7 @@
|
|||||||
"album_viewer_appbar_delete_confirm": "Opravdu chcete toto album odstranit ze svého účtu?",
|
"album_viewer_appbar_delete_confirm": "Opravdu chcete toto album odstranit ze svého účtu?",
|
||||||
"album_viewer_appbar_share_err_delete": "Nepodařilo se smazat album",
|
"album_viewer_appbar_share_err_delete": "Nepodařilo se smazat album",
|
||||||
"album_viewer_appbar_share_err_leave": "Nepodařilo se opustit album",
|
"album_viewer_appbar_share_err_leave": "Nepodařilo se opustit album",
|
||||||
"album_viewer_appbar_share_err_remove": "Při odstraňování položek z alba se vyskytly problémy.",
|
"album_viewer_appbar_share_err_remove": "Při odstraňování položek z alba se vyskytly problémy",
|
||||||
"album_viewer_appbar_share_err_title": "Nepodařilo se změnit název alba",
|
"album_viewer_appbar_share_err_title": "Nepodařilo se změnit název alba",
|
||||||
"album_viewer_appbar_share_leave": "Opustit album",
|
"album_viewer_appbar_share_leave": "Opustit album",
|
||||||
"album_viewer_appbar_share_to": "Sdílet na",
|
"album_viewer_appbar_share_to": "Sdílet na",
|
||||||
@@ -440,7 +442,7 @@
|
|||||||
"archive": "Archiv",
|
"archive": "Archiv",
|
||||||
"archive_or_unarchive_photo": "Archivovat nebo odarchivovat fotku",
|
"archive_or_unarchive_photo": "Archivovat nebo odarchivovat fotku",
|
||||||
"archive_page_no_archived_assets": "Nebyla nalezena žádná archivovaná média",
|
"archive_page_no_archived_assets": "Nebyla nalezena žádná archivovaná média",
|
||||||
"archive_page_title": "Archiv ({})",
|
"archive_page_title": "Archiv ({count})",
|
||||||
"archive_size": "Velikost archivu",
|
"archive_size": "Velikost archivu",
|
||||||
"archive_size_description": "Nastavte velikost archivu pro stahování (v GiB)",
|
"archive_size_description": "Nastavte velikost archivu pro stahování (v GiB)",
|
||||||
"archived": "Archiv",
|
"archived": "Archiv",
|
||||||
@@ -477,18 +479,18 @@
|
|||||||
"assets_added_to_album_count": "Do alba {count, plural, one {byla přidána # položka} few {byly přidány # položky} other {bylo přidáno # položek}}",
|
"assets_added_to_album_count": "Do alba {count, plural, one {byla přidána # položka} few {byly přidány # položky} other {bylo přidáno # položek}}",
|
||||||
"assets_added_to_name_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}} do {hasName, select, true {alba <b>{name}</b>} other {nového alba}}",
|
"assets_added_to_name_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}} do {hasName, select, true {alba <b>{name}</b>} other {nového alba}}",
|
||||||
"assets_count": "{count, plural, one {# položka} few {# položky} other {# položek}}",
|
"assets_count": "{count, plural, one {# položka} few {# položky} other {# položek}}",
|
||||||
"assets_deleted_permanently": "{} položek trvale odstraněno",
|
"assets_deleted_permanently": "{count} položek trvale odstraněno",
|
||||||
"assets_deleted_permanently_from_server": "{} položek trvale odstraněno z Immich serveru",
|
"assets_deleted_permanently_from_server": "{count} položek trvale odstraněno z Immich serveru",
|
||||||
"assets_moved_to_trash_count": "Do koše {count, plural, one {přesunuta # položka} few {přesunuty # položky} other {přesunuto # položek}}",
|
"assets_moved_to_trash_count": "Do koše {count, plural, one {přesunuta # položka} few {přesunuty # položky} other {přesunuto # položek}}",
|
||||||
"assets_permanently_deleted_count": "Trvale {count, plural, one {smazána # položka} few {smazány # položky} other {smazáno # položek}}",
|
"assets_permanently_deleted_count": "Trvale {count, plural, one {smazána # položka} few {smazány # položky} other {smazáno # položek}}",
|
||||||
"assets_removed_count": "{count, plural, one {Odstraněna # položka} few {Odstraněny # položky} other {Odstraněno # položek}}",
|
"assets_removed_count": "{count, plural, one {Odstraněna # položka} few {Odstraněny # položky} other {Odstraněno # položek}}",
|
||||||
"assets_removed_permanently_from_device": "{} položek trvale odstraněno z vašeho zařízení",
|
"assets_removed_permanently_from_device": "{count} položek trvale odstraněno z vašeho zařízení",
|
||||||
"assets_restore_confirmation": "Opravdu chcete obnovit všechny vyhozené položky? Tuto akci nelze vrátit zpět! Upozorňujeme, že tímto způsobem nelze obnovit žádné offline položky.",
|
"assets_restore_confirmation": "Opravdu chcete obnovit všechny vyhozené položky? Tuto akci nelze vrátit zpět! Upozorňujeme, že tímto způsobem nelze obnovit žádné offline položky.",
|
||||||
"assets_restored_count": "{count, plural, one {Obnovena # položka} few {Obnoveny # položky} other {Obnoveno # položek}}",
|
"assets_restored_count": "{count, plural, one {Obnovena # položka} few {Obnoveny # položky} other {Obnoveno # položek}}",
|
||||||
"assets_restored_successfully": "{} položek úspěšně obnoveno",
|
"assets_restored_successfully": "{count} položek úspěšně obnoveno",
|
||||||
"assets_trashed": "{} položek vyhozeno do koše",
|
"assets_trashed": "{count} položek vyhozeno do koše",
|
||||||
"assets_trashed_count": "{count, plural, one {Vyhozena # položka} few {Vyhozeny # položky} other {Vyhozeno # položek}}",
|
"assets_trashed_count": "{count, plural, one {Vyhozena # položka} few {Vyhozeny # položky} other {Vyhozeno # položek}}",
|
||||||
"assets_trashed_from_server": "{} položek vyhozeno do koše na Immich serveru",
|
"assets_trashed_from_server": "{count} položek vyhozeno do koše na Immich serveru",
|
||||||
"assets_were_part_of_album_count": "{count, plural, one {Položka byla} other {Položky byly}} součástí alba",
|
"assets_were_part_of_album_count": "{count, plural, one {Položka byla} other {Položky byly}} součástí alba",
|
||||||
"authorized_devices": "Autorizovaná zařízení",
|
"authorized_devices": "Autorizovaná zařízení",
|
||||||
"automatic_endpoint_switching_subtitle": "Připojit se místně přes určenou Wi-Fi, pokud je k dispozici, a používat alternativní připojení jinde",
|
"automatic_endpoint_switching_subtitle": "Připojit se místně přes určenou Wi-Fi, pokud je k dispozici, a používat alternativní připojení jinde",
|
||||||
@@ -497,23 +499,23 @@
|
|||||||
"back_close_deselect": "Zpět, zavřít nebo zrušit výběr",
|
"back_close_deselect": "Zpět, zavřít nebo zrušit výběr",
|
||||||
"background_location_permission": "Povolení polohy na pozadí",
|
"background_location_permission": "Povolení polohy na pozadí",
|
||||||
"background_location_permission_content": "Aby bylo možné přepínat sítě při běhu na pozadí, musí mít Immich *vždy* přístup k přesné poloze, aby mohl zjistit název Wi-Fi sítě",
|
"background_location_permission_content": "Aby bylo možné přepínat sítě při běhu na pozadí, musí mít Immich *vždy* přístup k přesné poloze, aby mohl zjistit název Wi-Fi sítě",
|
||||||
"backup_album_selection_page_albums_device": "Alba v zařízení ({})",
|
"backup_album_selection_page_albums_device": "Alba v zařízení ({count})",
|
||||||
"backup_album_selection_page_albums_tap": "Klepnutím na položku ji zahrnete, opětovným klepnutím ji vyloučíte",
|
"backup_album_selection_page_albums_tap": "Klepnutím na položku ji zahrnete, opětovným klepnutím ji vyloučíte",
|
||||||
"backup_album_selection_page_assets_scatter": "Položky mohou být roztroušeny ve více albech. To umožňuje zahrnout nebo vyloučit alba během procesu zálohování.",
|
"backup_album_selection_page_assets_scatter": "Položky mohou být roztroušeny ve více albech. To umožňuje zahrnout nebo vyloučit alba během procesu zálohování.",
|
||||||
"backup_album_selection_page_select_albums": "Vybraná alba",
|
"backup_album_selection_page_select_albums": "Vybraná alba",
|
||||||
"backup_album_selection_page_selection_info": "Informace o výběru",
|
"backup_album_selection_page_selection_info": "Informace o výběru",
|
||||||
"backup_album_selection_page_total_assets": "Celkový počet jedinečných položek",
|
"backup_album_selection_page_total_assets": "Celkový počet jedinečných položek",
|
||||||
"backup_all": "Vše",
|
"backup_all": "Vše",
|
||||||
"backup_background_service_backup_failed_message": "Zálohování médií selhalo. Zkouším to znovu...",
|
"backup_background_service_backup_failed_message": "Zálohování médií selhalo. Zkouším to znovu…",
|
||||||
"backup_background_service_connection_failed_message": "Nepodařilo se připojit k serveru. Zkouším to znovu...",
|
"backup_background_service_connection_failed_message": "Nepodařilo se připojit k serveru. Zkouším to znovu…",
|
||||||
"backup_background_service_current_upload_notification": "Nahrávání {}",
|
"backup_background_service_current_upload_notification": "Nahrávání {filename}",
|
||||||
"backup_background_service_default_notification": "Kontrola nových médií…",
|
"backup_background_service_default_notification": "Kontrola nových médií…",
|
||||||
"backup_background_service_error_title": "Chyba zálohování",
|
"backup_background_service_error_title": "Chyba zálohování",
|
||||||
"backup_background_service_in_progress_notification": "Zálohování vašich médií...",
|
"backup_background_service_in_progress_notification": "Zálohování vašich médií…",
|
||||||
"backup_background_service_upload_failure_notification": "Nepodařilo se nahrát {}",
|
"backup_background_service_upload_failure_notification": "Nepodařilo se nahrát {filename}",
|
||||||
"backup_controller_page_albums": "Zálohovaná alba",
|
"backup_controller_page_albums": "Zálohovaná alba",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Povolte obnovení aplikace na pozadí v Nastavení > Obecné > Obnovení aplikace na pozadí, abyste mohli používat zálohování na pozadí.",
|
"backup_controller_page_background_app_refresh_disabled_content": "Povolte obnovení aplikace na pozadí v Nastavení > Obecné > Obnovení aplikace na pozadí, abyste mohli používat zálohování na pozadí.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": " Obnovování aplikací na pozadí je vypnuté",
|
"backup_controller_page_background_app_refresh_disabled_title": "Obnovování aplikací na pozadí je vypnuté",
|
||||||
"backup_controller_page_background_app_refresh_enable_button_text": "Přejít do nastavení",
|
"backup_controller_page_background_app_refresh_enable_button_text": "Přejít do nastavení",
|
||||||
"backup_controller_page_background_battery_info_link": "Ukaž mi jak",
|
"backup_controller_page_background_battery_info_link": "Ukaž mi jak",
|
||||||
"backup_controller_page_background_battery_info_message": "Chcete-li dosáhnout nejlepších výsledků při zálohování na pozadí, vypněte všechny optimalizace baterie, které omezují aktivitu na pozadí pro Immich ve vašem zařízení. \n\nJelikož je to závislé na typu zařízení, vyhledejte požadované informace pro výrobce vašeho zařízení.",
|
"backup_controller_page_background_battery_info_message": "Chcete-li dosáhnout nejlepších výsledků při zálohování na pozadí, vypněte všechny optimalizace baterie, které omezují aktivitu na pozadí pro Immich ve vašem zařízení. \n\nJelikož je to závislé na typu zařízení, vyhledejte požadované informace pro výrobce vašeho zařízení.",
|
||||||
@@ -521,7 +523,7 @@
|
|||||||
"backup_controller_page_background_battery_info_title": "Optimalizace baterie",
|
"backup_controller_page_background_battery_info_title": "Optimalizace baterie",
|
||||||
"backup_controller_page_background_charging": "Pouze během nabíjení",
|
"backup_controller_page_background_charging": "Pouze během nabíjení",
|
||||||
"backup_controller_page_background_configure_error": "Nepodařilo se nakonfigurovat službu na pozadí",
|
"backup_controller_page_background_configure_error": "Nepodařilo se nakonfigurovat službu na pozadí",
|
||||||
"backup_controller_page_background_delay": "Zpoždění zálohování nových médií: {}",
|
"backup_controller_page_background_delay": "Zpoždění zálohování nových médií: {duration}",
|
||||||
"backup_controller_page_background_description": "Povolte službu na pozadí pro automatické zálohování všech nových položek bez nutnosti otevření aplikace",
|
"backup_controller_page_background_description": "Povolte službu na pozadí pro automatické zálohování všech nových položek bez nutnosti otevření aplikace",
|
||||||
"backup_controller_page_background_is_off": "Automatické zálohování na pozadí je vypnuto",
|
"backup_controller_page_background_is_off": "Automatické zálohování na pozadí je vypnuto",
|
||||||
"backup_controller_page_background_is_on": "Automatické zálohování na pozadí je zapnuto",
|
"backup_controller_page_background_is_on": "Automatické zálohování na pozadí je zapnuto",
|
||||||
@@ -531,12 +533,12 @@
|
|||||||
"backup_controller_page_backup": "Zálohování",
|
"backup_controller_page_backup": "Zálohování",
|
||||||
"backup_controller_page_backup_selected": "Vybrané: ",
|
"backup_controller_page_backup_selected": "Vybrané: ",
|
||||||
"backup_controller_page_backup_sub": "Zálohované fotografie a videa",
|
"backup_controller_page_backup_sub": "Zálohované fotografie a videa",
|
||||||
"backup_controller_page_created": "Vytvořeno: {}",
|
"backup_controller_page_created": "Vytvořeno: {date}",
|
||||||
"backup_controller_page_desc_backup": "Zapněte zálohování na popředí, aby se nové položky automaticky nahrávaly na server při otevření aplikace.",
|
"backup_controller_page_desc_backup": "Zapněte zálohování na popředí, aby se nové položky automaticky nahrávaly na server při otevření aplikace.",
|
||||||
"backup_controller_page_excluded": "Vyloučeno: ",
|
"backup_controller_page_excluded": "Vyloučeno: ",
|
||||||
"backup_controller_page_failed": "Nepodařilo se ({})",
|
"backup_controller_page_failed": "Nepodařilo se ({count})",
|
||||||
"backup_controller_page_filename": "Název souboru: {} [{}]",
|
"backup_controller_page_filename": "Název souboru: {filename} [{size}]",
|
||||||
"backup_controller_page_id": "ID: {}",
|
"backup_controller_page_id": "ID: {id}",
|
||||||
"backup_controller_page_info": "Informace o zálohování",
|
"backup_controller_page_info": "Informace o zálohování",
|
||||||
"backup_controller_page_none_selected": "Žádné vybrané",
|
"backup_controller_page_none_selected": "Žádné vybrané",
|
||||||
"backup_controller_page_remainder": "Zbývá",
|
"backup_controller_page_remainder": "Zbývá",
|
||||||
@@ -545,7 +547,7 @@
|
|||||||
"backup_controller_page_start_backup": "Spustit zálohování",
|
"backup_controller_page_start_backup": "Spustit zálohování",
|
||||||
"backup_controller_page_status_off": "Automatické zálohování na popředí je vypnuto",
|
"backup_controller_page_status_off": "Automatické zálohování na popředí je vypnuto",
|
||||||
"backup_controller_page_status_on": "Automatické zálohování na popředí je zapnuto",
|
"backup_controller_page_status_on": "Automatické zálohování na popředí je zapnuto",
|
||||||
"backup_controller_page_storage_format": "{} z {} použitých",
|
"backup_controller_page_storage_format": "{used} z {total} použitých",
|
||||||
"backup_controller_page_to_backup": "Alba, která mají být zálohována",
|
"backup_controller_page_to_backup": "Alba, která mají být zálohována",
|
||||||
"backup_controller_page_total_sub": "Všechny jedinečné fotografie a videa z vybraných alb",
|
"backup_controller_page_total_sub": "Všechny jedinečné fotografie a videa z vybraných alb",
|
||||||
"backup_controller_page_turn_off": "Vypnout zálohování na popředí",
|
"backup_controller_page_turn_off": "Vypnout zálohování na popředí",
|
||||||
@@ -570,21 +572,21 @@
|
|||||||
"bulk_keep_duplicates_confirmation": "Opravdu si chcete ponechat {count, plural, one {# duplicitní položku} few {# duplicitní položky} other {# duplicitních položek}}? Tím se vyřeší všechny duplicitní skupiny, aniž by se cokoli odstranilo.",
|
"bulk_keep_duplicates_confirmation": "Opravdu si chcete ponechat {count, plural, one {# duplicitní položku} few {# duplicitní položky} other {# duplicitních položek}}? Tím se vyřeší všechny duplicitní skupiny, aniž by se cokoli odstranilo.",
|
||||||
"bulk_trash_duplicates_confirmation": "Opravdu chcete hromadně vyhodit {count, plural, one {# duplicitní položku} few {# duplicitní položky} other {# duplicitních položek}}? Tím se zachová největší položka z každé skupiny a všechny ostatní duplikáty se vyhodí.",
|
"bulk_trash_duplicates_confirmation": "Opravdu chcete hromadně vyhodit {count, plural, one {# duplicitní položku} few {# duplicitní položky} other {# duplicitních položek}}? Tím se zachová největší položka z každé skupiny a všechny ostatní duplikáty se vyhodí.",
|
||||||
"buy": "Zakoupit Immich",
|
"buy": "Zakoupit Immich",
|
||||||
"cache_settings_album_thumbnails": "Náhledy stránek knihovny (položek {})",
|
"cache_settings_album_thumbnails": "Náhledy stránek knihovny ({count} položek)",
|
||||||
"cache_settings_clear_cache_button": "Vymazat vyrovnávací paměť",
|
"cache_settings_clear_cache_button": "Vymazat vyrovnávací paměť",
|
||||||
"cache_settings_clear_cache_button_title": "Vymaže vyrovnávací paměť aplikace. To výrazně ovlivní výkon aplikace, dokud se vyrovnávací paměť neobnoví.",
|
"cache_settings_clear_cache_button_title": "Vymaže vyrovnávací paměť aplikace. To výrazně ovlivní výkon aplikace, dokud se vyrovnávací paměť neobnoví.",
|
||||||
"cache_settings_duplicated_assets_clear_button": "VYMAZAT",
|
"cache_settings_duplicated_assets_clear_button": "VYMAZAT",
|
||||||
"cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace zařadila na černou listinu",
|
"cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace zařadila na černou listinu",
|
||||||
"cache_settings_duplicated_assets_title": "Duplicitní položky ({})",
|
"cache_settings_duplicated_assets_title": "Duplicitní položky ({count})",
|
||||||
"cache_settings_image_cache_size": "Velikost vyrovnávací paměti (položek {})",
|
"cache_settings_image_cache_size": "Velikost vyrovnávací paměti ({count} položek)",
|
||||||
"cache_settings_statistics_album": "Knihovna náhledů",
|
"cache_settings_statistics_album": "Knihovna náhledů",
|
||||||
"cache_settings_statistics_assets": "{} položky ({})",
|
"cache_settings_statistics_assets": "{count, plural, one {# položka} few {# položky} other {# položek}} ({size})",
|
||||||
"cache_settings_statistics_full": "Kompletní fotografie",
|
"cache_settings_statistics_full": "Kompletní fotografie",
|
||||||
"cache_settings_statistics_shared": "Sdílené náhledy alb",
|
"cache_settings_statistics_shared": "Sdílené náhledy alb",
|
||||||
"cache_settings_statistics_thumbnail": "Náhledy",
|
"cache_settings_statistics_thumbnail": "Náhledy",
|
||||||
"cache_settings_statistics_title": "Použití vyrovnávací paměti",
|
"cache_settings_statistics_title": "Použití vyrovnávací paměti",
|
||||||
"cache_settings_subtitle": "Ovládání chování mobilní aplikace Immich v mezipaměti",
|
"cache_settings_subtitle": "Ovládání chování mobilní aplikace Immich v mezipaměti",
|
||||||
"cache_settings_thumbnail_size": "Velikost vyrovnávací paměti náhledů (položek {})",
|
"cache_settings_thumbnail_size": "Velikost vyrovnávací paměti náhledů ({count, plural, one {# položka} few {# položky} other {# položek}})",
|
||||||
"cache_settings_tile_subtitle": "Ovládání chování místního úložiště",
|
"cache_settings_tile_subtitle": "Ovládání chování místního úložiště",
|
||||||
"cache_settings_tile_title": "Místní úložiště",
|
"cache_settings_tile_title": "Místní úložiště",
|
||||||
"cache_settings_title": "Nastavení vyrovnávací paměti",
|
"cache_settings_title": "Nastavení vyrovnávací paměti",
|
||||||
@@ -610,6 +612,7 @@
|
|||||||
"change_password_form_new_password": "Nové heslo",
|
"change_password_form_new_password": "Nové heslo",
|
||||||
"change_password_form_password_mismatch": "Hesla se neshodují",
|
"change_password_form_password_mismatch": "Hesla se neshodují",
|
||||||
"change_password_form_reenter_new_password": "Znovu zadejte nové heslo",
|
"change_password_form_reenter_new_password": "Znovu zadejte nové heslo",
|
||||||
|
"change_pin_code": "Změnit PIN kód",
|
||||||
"change_your_password": "Změna vašeho hesla",
|
"change_your_password": "Změna vašeho hesla",
|
||||||
"changed_visibility_successfully": "Změna viditelnosti proběhla úspěšně",
|
"changed_visibility_successfully": "Změna viditelnosti proběhla úspěšně",
|
||||||
"check_all": "Zkontrolovat vše",
|
"check_all": "Zkontrolovat vše",
|
||||||
@@ -650,11 +653,12 @@
|
|||||||
"confirm_delete_face": "Opravdu chcete z položky odstranit obličej osoby {name}?",
|
"confirm_delete_face": "Opravdu chcete z položky odstranit obličej osoby {name}?",
|
||||||
"confirm_delete_shared_link": "Opravdu chcete odstranit tento sdílený odkaz?",
|
"confirm_delete_shared_link": "Opravdu chcete odstranit tento sdílený odkaz?",
|
||||||
"confirm_keep_this_delete_others": "Všechny ostatní položky v tomto uskupení mimo této budou odstraněny. Opravdu chcete pokračovat?",
|
"confirm_keep_this_delete_others": "Všechny ostatní položky v tomto uskupení mimo této budou odstraněny. Opravdu chcete pokračovat?",
|
||||||
|
"confirm_new_pin_code": "Potvrzení nového PIN kódu",
|
||||||
"confirm_password": "Potvrzení hesla",
|
"confirm_password": "Potvrzení hesla",
|
||||||
"contain": "Obsah",
|
"contain": "Obsah",
|
||||||
"context": "Kontext",
|
"context": "Kontext",
|
||||||
"continue": "Pokračovat",
|
"continue": "Pokračovat",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} položky – sdílené",
|
"control_bottom_app_bar_album_info_shared": "{count, plural, one {# položka – sdílená} few {# položky – sdílené} other {# položek – sdílených}}",
|
||||||
"control_bottom_app_bar_create_new_album": "Vytvořit nové album",
|
"control_bottom_app_bar_create_new_album": "Vytvořit nové album",
|
||||||
"control_bottom_app_bar_delete_from_immich": "Smazat ze serveru Immich",
|
"control_bottom_app_bar_delete_from_immich": "Smazat ze serveru Immich",
|
||||||
"control_bottom_app_bar_delete_from_local": "Smazat ze zařízení",
|
"control_bottom_app_bar_delete_from_local": "Smazat ze zařízení",
|
||||||
@@ -692,9 +696,11 @@
|
|||||||
"create_tag_description": "Vytvoření nové značky. U vnořených značek zadejte celou cestu ke značce včetně dopředných lomítek.",
|
"create_tag_description": "Vytvoření nové značky. U vnořených značek zadejte celou cestu ke značce včetně dopředných lomítek.",
|
||||||
"create_user": "Vytvořit uživatele",
|
"create_user": "Vytvořit uživatele",
|
||||||
"created": "Vytvořeno",
|
"created": "Vytvořeno",
|
||||||
|
"created_at": "Vytvořeno",
|
||||||
"crop": "Oříznout",
|
"crop": "Oříznout",
|
||||||
"curated_object_page_title": "Věci",
|
"curated_object_page_title": "Věci",
|
||||||
"current_device": "Současné zařízení",
|
"current_device": "Současné zařízení",
|
||||||
|
"current_pin_code": "Aktuální PIN kód",
|
||||||
"current_server_address": "Aktuální adresa serveru",
|
"current_server_address": "Aktuální adresa serveru",
|
||||||
"custom_locale": "Vlastní lokalizace",
|
"custom_locale": "Vlastní lokalizace",
|
||||||
"custom_locale_description": "Formátovat datumy a čísla podle jazyka a oblasti",
|
"custom_locale_description": "Formátovat datumy a čísla podle jazyka a oblasti",
|
||||||
@@ -721,7 +727,7 @@
|
|||||||
"delete_dialog_alert": "Tyto položky budou trvale smazány z aplikace Immich i z vašeho zařízení",
|
"delete_dialog_alert": "Tyto položky budou trvale smazány z aplikace Immich i z vašeho zařízení",
|
||||||
"delete_dialog_alert_local": "Tyto položky budou z vašeho zařízení trvale smazány, ale budou stále k dispozici na Immich serveru",
|
"delete_dialog_alert_local": "Tyto položky budou z vašeho zařízení trvale smazány, ale budou stále k dispozici na Immich serveru",
|
||||||
"delete_dialog_alert_local_non_backed_up": "Některé položky nejsou zálohovány na Immich server a budou ze zařízení trvale smazány",
|
"delete_dialog_alert_local_non_backed_up": "Některé položky nejsou zálohovány na Immich server a budou ze zařízení trvale smazány",
|
||||||
"delete_dialog_alert_remote": "Tyto položky budou trvale smazány z Immich serveru ",
|
"delete_dialog_alert_remote": "Tyto položky budou trvale smazány z Immich serveru",
|
||||||
"delete_dialog_ok_force": "Přesto smazat",
|
"delete_dialog_ok_force": "Přesto smazat",
|
||||||
"delete_dialog_title": "Smazat trvale",
|
"delete_dialog_title": "Smazat trvale",
|
||||||
"delete_duplicates_confirmation": "Opravdu chcete tyto duplicity trvale odstranit?",
|
"delete_duplicates_confirmation": "Opravdu chcete tyto duplicity trvale odstranit?",
|
||||||
@@ -763,7 +769,7 @@
|
|||||||
"download_enqueue": "Stahování ve frontě",
|
"download_enqueue": "Stahování ve frontě",
|
||||||
"download_error": "Chyba při stahování",
|
"download_error": "Chyba při stahování",
|
||||||
"download_failed": "Stahování selhalo",
|
"download_failed": "Stahování selhalo",
|
||||||
"download_filename": "soubor: {}",
|
"download_filename": "soubor: {filename}",
|
||||||
"download_finished": "Stahování dokončeno",
|
"download_finished": "Stahování dokončeno",
|
||||||
"download_include_embedded_motion_videos": "Vložená videa",
|
"download_include_embedded_motion_videos": "Vložená videa",
|
||||||
"download_include_embedded_motion_videos_description": "Zahrnout videa vložená do pohyblivých fotografií jako samostatný soubor",
|
"download_include_embedded_motion_videos_description": "Zahrnout videa vložená do pohyblivých fotografií jako samostatný soubor",
|
||||||
@@ -807,6 +813,7 @@
|
|||||||
"editor_crop_tool_h2_aspect_ratios": "Poměr stran",
|
"editor_crop_tool_h2_aspect_ratios": "Poměr stran",
|
||||||
"editor_crop_tool_h2_rotation": "Otočení",
|
"editor_crop_tool_h2_rotation": "Otočení",
|
||||||
"email": "E-mail",
|
"email": "E-mail",
|
||||||
|
"email_notifications": "E-mailová oznámení",
|
||||||
"empty_folder": "Tato složka je prázdná",
|
"empty_folder": "Tato složka je prázdná",
|
||||||
"empty_trash": "Vyprázdnit koš",
|
"empty_trash": "Vyprázdnit koš",
|
||||||
"empty_trash_confirmation": "Opravdu chcete vysypat koš? Tím se z Immiche trvale odstraní všechny položky v koši.\nTuto akci nelze vrátit zpět!",
|
"empty_trash_confirmation": "Opravdu chcete vysypat koš? Tím se z Immiche trvale odstraní všechny položky v koši.\nTuto akci nelze vrátit zpět!",
|
||||||
@@ -814,12 +821,12 @@
|
|||||||
"enabled": "Povoleno",
|
"enabled": "Povoleno",
|
||||||
"end_date": "Konečné datum",
|
"end_date": "Konečné datum",
|
||||||
"enqueued": "Ve frontě",
|
"enqueued": "Ve frontě",
|
||||||
"enter_wifi_name": "Zadejte název WiFi",
|
"enter_wifi_name": "Zadejte název Wi-Fi",
|
||||||
"error": "Chyba",
|
"error": "Chyba",
|
||||||
"error_change_sort_album": "Nepodařilo se změnit pořadí alba",
|
"error_change_sort_album": "Nepodařilo se změnit pořadí alba",
|
||||||
"error_delete_face": "Chyba při odstraňování obličeje z položky",
|
"error_delete_face": "Chyba při odstraňování obličeje z položky",
|
||||||
"error_loading_image": "Chyba při načítání obrázku",
|
"error_loading_image": "Chyba při načítání obrázku",
|
||||||
"error_saving_image": "Chyba: {}",
|
"error_saving_image": "Chyba: {error}",
|
||||||
"error_title": "Chyba - Něco se pokazilo",
|
"error_title": "Chyba - Něco se pokazilo",
|
||||||
"errors": {
|
"errors": {
|
||||||
"cannot_navigate_next_asset": "Nelze přejít na další položku",
|
"cannot_navigate_next_asset": "Nelze přejít na další položku",
|
||||||
@@ -849,10 +856,12 @@
|
|||||||
"failed_to_keep_this_delete_others": "Nepodařilo se zachovat tuto položku a odstranit ostatní položky",
|
"failed_to_keep_this_delete_others": "Nepodařilo se zachovat tuto položku a odstranit ostatní položky",
|
||||||
"failed_to_load_asset": "Nepodařilo se načíst položku",
|
"failed_to_load_asset": "Nepodařilo se načíst položku",
|
||||||
"failed_to_load_assets": "Nepodařilo se načíst položky",
|
"failed_to_load_assets": "Nepodařilo se načíst položky",
|
||||||
|
"failed_to_load_notifications": "Nepodařilo se načíst oznámení",
|
||||||
"failed_to_load_people": "Chyba načítání osob",
|
"failed_to_load_people": "Chyba načítání osob",
|
||||||
"failed_to_remove_product_key": "Nepodařilo se odebrat klíč produktu",
|
"failed_to_remove_product_key": "Nepodařilo se odebrat klíč produktu",
|
||||||
"failed_to_stack_assets": "Nepodařilo se seskupit položky",
|
"failed_to_stack_assets": "Nepodařilo se seskupit položky",
|
||||||
"failed_to_unstack_assets": "Nepodařilo se rozložit položky",
|
"failed_to_unstack_assets": "Nepodařilo se rozložit položky",
|
||||||
|
"failed_to_update_notification_status": "Nepodařilo se aktualizovat stav oznámení",
|
||||||
"import_path_already_exists": "Tato cesta importu již existuje.",
|
"import_path_already_exists": "Tato cesta importu již existuje.",
|
||||||
"incorrect_email_or_password": "Nesprávný e-mail nebo heslo",
|
"incorrect_email_or_password": "Nesprávný e-mail nebo heslo",
|
||||||
"paths_validation_failed": "{paths, plural, one {# cesta neprošla} few {# cesty neprošly} other {# cest neprošlo}} kontrolou",
|
"paths_validation_failed": "{paths, plural, one {# cesta neprošla} few {# cesty neprošly} other {# cest neprošlo}} kontrolou",
|
||||||
@@ -920,6 +929,7 @@
|
|||||||
"unable_to_remove_reaction": "Nelze odstranit reakci",
|
"unable_to_remove_reaction": "Nelze odstranit reakci",
|
||||||
"unable_to_repair_items": "Nelze opravit položky",
|
"unable_to_repair_items": "Nelze opravit položky",
|
||||||
"unable_to_reset_password": "Nelze obnovit heslo",
|
"unable_to_reset_password": "Nelze obnovit heslo",
|
||||||
|
"unable_to_reset_pin_code": "Nelze resetovat PIN kód",
|
||||||
"unable_to_resolve_duplicate": "Nelze vyřešit duplicitu",
|
"unable_to_resolve_duplicate": "Nelze vyřešit duplicitu",
|
||||||
"unable_to_restore_assets": "Nelze obnovit položky",
|
"unable_to_restore_assets": "Nelze obnovit položky",
|
||||||
"unable_to_restore_trash": "Nelze obnovit koš",
|
"unable_to_restore_trash": "Nelze obnovit koš",
|
||||||
@@ -953,10 +963,10 @@
|
|||||||
"exif_bottom_sheet_location": "POLOHA",
|
"exif_bottom_sheet_location": "POLOHA",
|
||||||
"exif_bottom_sheet_people": "LIDÉ",
|
"exif_bottom_sheet_people": "LIDÉ",
|
||||||
"exif_bottom_sheet_person_add_person": "Přidat jméno",
|
"exif_bottom_sheet_person_add_person": "Přidat jméno",
|
||||||
"exif_bottom_sheet_person_age": "{} let",
|
"exif_bottom_sheet_person_age": "Věk {age, plural, one {# rok} few {# roky} other {# let}}",
|
||||||
"exif_bottom_sheet_person_age_months": "{} měsíců",
|
"exif_bottom_sheet_person_age_months": "Věk {months, plural, one {# měsíc} few {# měsíce} other {# měsíců}}",
|
||||||
"exif_bottom_sheet_person_age_year_months": "1 rok a {} měsíců",
|
"exif_bottom_sheet_person_age_year_months": "Věk 1 rok, {months, plural, one {# měsíc} other {# měsíce}}",
|
||||||
"exif_bottom_sheet_person_age_years": "{} let",
|
"exif_bottom_sheet_person_age_years": "Věk {years, plural, one {# rok} few {# roky} other {# let}}",
|
||||||
"exit_slideshow": "Ukončit prezentaci",
|
"exit_slideshow": "Ukončit prezentaci",
|
||||||
"expand_all": "Rozbalit vše",
|
"expand_all": "Rozbalit vše",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Zpracovávám",
|
"experimental_settings_new_asset_list_subtitle": "Zpracovávám",
|
||||||
@@ -974,7 +984,7 @@
|
|||||||
"external": "Externí",
|
"external": "Externí",
|
||||||
"external_libraries": "Externí knihovny",
|
"external_libraries": "Externí knihovny",
|
||||||
"external_network": "Externí síť",
|
"external_network": "Externí síť",
|
||||||
"external_network_sheet_info": "Pokud nejste v preferované síti WiFi, aplikace se připojí k serveru prostřednictvím první z níže uvedených adres URL, které může dosáhnout, počínaje shora dolů",
|
"external_network_sheet_info": "Pokud nejste v preferované síti Wi-Fi, aplikace se připojí k serveru prostřednictvím první z níže uvedených adres URL, které může dosáhnout, počínaje shora dolů",
|
||||||
"face_unassigned": "Nepřiřazena",
|
"face_unassigned": "Nepřiřazena",
|
||||||
"failed": "Selhalo",
|
"failed": "Selhalo",
|
||||||
"failed_to_load_assets": "Nepodařilo se načíst položky",
|
"failed_to_load_assets": "Nepodařilo se načíst položky",
|
||||||
@@ -992,6 +1002,7 @@
|
|||||||
"filetype": "Typ souboru",
|
"filetype": "Typ souboru",
|
||||||
"filter": "Filtr",
|
"filter": "Filtr",
|
||||||
"filter_people": "Filtrovat lidi",
|
"filter_people": "Filtrovat lidi",
|
||||||
|
"filter_places": "Filtrovat místa",
|
||||||
"find_them_fast": "Najděte je rychle vyhledáním jejich jména",
|
"find_them_fast": "Najděte je rychle vyhledáním jejich jména",
|
||||||
"fix_incorrect_match": "Opravit nesprávnou shodu",
|
"fix_incorrect_match": "Opravit nesprávnou shodu",
|
||||||
"folder": "Složka",
|
"folder": "Složka",
|
||||||
@@ -1040,11 +1051,12 @@
|
|||||||
"home_page_delete_remote_err_local": "Místní položky ve vzdáleném výběru pro smazání, přeskakuji",
|
"home_page_delete_remote_err_local": "Místní položky ve vzdáleném výběru pro smazání, přeskakuji",
|
||||||
"home_page_favorite_err_local": "Zatím není možné zařadit lokální média mezi oblíbená, přeskakuji",
|
"home_page_favorite_err_local": "Zatím není možné zařadit lokální média mezi oblíbená, přeskakuji",
|
||||||
"home_page_favorite_err_partner": "Položky partnera nelze označit jako oblíbené, přeskakuji",
|
"home_page_favorite_err_partner": "Položky partnera nelze označit jako oblíbené, přeskakuji",
|
||||||
"home_page_first_time_notice": "Pokud aplikaci používáte poprvé, nezapomeňte si vybrat zálohovaná alba, aby se na časové ose mohly nacházet fotografie a videa z vybraných alb.",
|
"home_page_first_time_notice": "Pokud aplikaci používáte poprvé, nezapomeňte si vybrat zálohovaná alba, aby se na časové ose mohly nacházet fotografie a videa z vybraných alb",
|
||||||
"home_page_share_err_local": "Nelze sdílet místní položky prostřednictvím odkazu, přeskakuji",
|
"home_page_share_err_local": "Nelze sdílet místní položky prostřednictvím odkazu, přeskakuji",
|
||||||
"home_page_upload_err_limit": "Lze nahrát nejvýše 30 položek najednou, přeskakuji",
|
"home_page_upload_err_limit": "Lze nahrát nejvýše 30 položek najednou, přeskakuji",
|
||||||
"host": "Hostitel",
|
"host": "Hostitel",
|
||||||
"hour": "Hodina",
|
"hour": "Hodina",
|
||||||
|
"id": "ID",
|
||||||
"ignore_icloud_photos": "Ignorovat fotografie na iCloudu",
|
"ignore_icloud_photos": "Ignorovat fotografie na iCloudu",
|
||||||
"ignore_icloud_photos_description": "Fotografie uložené na iCloudu se nebudou nahrávat na Immich server",
|
"ignore_icloud_photos_description": "Fotografie uložené na iCloudu se nebudou nahrávat na Immich server",
|
||||||
"image": "Obrázek",
|
"image": "Obrázek",
|
||||||
@@ -1120,7 +1132,7 @@
|
|||||||
"local_network": "Místní síť",
|
"local_network": "Místní síť",
|
||||||
"local_network_sheet_info": "Aplikace se při použití zadané sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL",
|
"local_network_sheet_info": "Aplikace se při použití zadané sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL",
|
||||||
"location_permission": "Oprávnění polohy",
|
"location_permission": "Oprávnění polohy",
|
||||||
"location_permission_content": "Aby bylo možné používat funkci automatického přepínání, potřebuje Immich oprávnění k přesné poloze, aby mohl přečíst název aktuální WiFi sítě",
|
"location_permission_content": "Aby bylo možné používat funkci automatického přepínání, potřebuje Immich oprávnění k přesné poloze, aby mohl přečíst název aktuální sítě Wi-Fi",
|
||||||
"location_picker_choose_on_map": "Vyberte na mapě",
|
"location_picker_choose_on_map": "Vyberte na mapě",
|
||||||
"location_picker_latitude_error": "Zadejte platnou zeměpisnou šířku",
|
"location_picker_latitude_error": "Zadejte platnou zeměpisnou šířku",
|
||||||
"location_picker_latitude_hint": "Zadejte vlastní zeměpisnou šířku",
|
"location_picker_latitude_hint": "Zadejte vlastní zeměpisnou šířku",
|
||||||
@@ -1144,7 +1156,7 @@
|
|||||||
"login_form_err_trailing_whitespace": "Koncová mezera",
|
"login_form_err_trailing_whitespace": "Koncová mezera",
|
||||||
"login_form_failed_get_oauth_server_config": "Chyba přihlášení pomocí OAuth, zkontrolujte adresu URL serveru",
|
"login_form_failed_get_oauth_server_config": "Chyba přihlášení pomocí OAuth, zkontrolujte adresu URL serveru",
|
||||||
"login_form_failed_get_oauth_server_disable": "Funkce OAuth není na tomto serveru dostupná",
|
"login_form_failed_get_oauth_server_disable": "Funkce OAuth není na tomto serveru dostupná",
|
||||||
"login_form_failed_login": "Chyba přihlášení, zkontrolujte URL adresu serveru, e-mail a heslo.",
|
"login_form_failed_login": "Chyba přihlášení, zkontrolujte URL adresu serveru, e-mail a heslo",
|
||||||
"login_form_handshake_exception": "Došlo k výjimce Handshake se serverem. Pokud používáte self-signed certifikát, povolte v nastavení podporu self-signed certifikátu.",
|
"login_form_handshake_exception": "Došlo k výjimce Handshake se serverem. Pokud používáte self-signed certifikát, povolte v nastavení podporu self-signed certifikátu.",
|
||||||
"login_form_password_hint": "heslo",
|
"login_form_password_hint": "heslo",
|
||||||
"login_form_save_login": "Zůstat přihlášen",
|
"login_form_save_login": "Zůstat přihlášen",
|
||||||
@@ -1170,8 +1182,8 @@
|
|||||||
"manage_your_devices": "Správa přihlášených zařízení",
|
"manage_your_devices": "Správa přihlášených zařízení",
|
||||||
"manage_your_oauth_connection": "Správa OAuth propojení",
|
"manage_your_oauth_connection": "Správa OAuth propojení",
|
||||||
"map": "Mapa",
|
"map": "Mapa",
|
||||||
"map_assets_in_bound": "{} fotka",
|
"map_assets_in_bound": "{count, plural, one {# fotka} few {# fotky} other {# fotek}}",
|
||||||
"map_assets_in_bounds": "{} fotek",
|
"map_assets_in_bounds": "{count, plural, one {# fotka} few {# fotky} other {# fotek}}",
|
||||||
"map_cannot_get_user_location": "Nelze zjistit polohu uživatele",
|
"map_cannot_get_user_location": "Nelze zjistit polohu uživatele",
|
||||||
"map_location_dialog_yes": "Ano",
|
"map_location_dialog_yes": "Ano",
|
||||||
"map_location_picker_page_use_location": "Použít tuto polohu",
|
"map_location_picker_page_use_location": "Použít tuto polohu",
|
||||||
@@ -1185,15 +1197,18 @@
|
|||||||
"map_settings": "Nastavení mapy",
|
"map_settings": "Nastavení mapy",
|
||||||
"map_settings_dark_mode": "Tmavý režim",
|
"map_settings_dark_mode": "Tmavý režim",
|
||||||
"map_settings_date_range_option_day": "Posledních 24 hodin",
|
"map_settings_date_range_option_day": "Posledních 24 hodin",
|
||||||
"map_settings_date_range_option_days": "Posledních {} dní",
|
"map_settings_date_range_option_days": "Posledních {days, plural, one {# den} few {# dny} other {# dní}}",
|
||||||
"map_settings_date_range_option_year": "Poslední rok",
|
"map_settings_date_range_option_year": "Poslední rok",
|
||||||
"map_settings_date_range_option_years": "Poslední {} roky",
|
"map_settings_date_range_option_years": "Poslední {years, plural, one {# rok} few {# roky} other {# roky}}",
|
||||||
"map_settings_dialog_title": "Nastavení map",
|
"map_settings_dialog_title": "Nastavení map",
|
||||||
"map_settings_include_show_archived": "Zahrnout archivované",
|
"map_settings_include_show_archived": "Zahrnout archivované",
|
||||||
"map_settings_include_show_partners": "Včetně partnerů",
|
"map_settings_include_show_partners": "Včetně partnerů",
|
||||||
"map_settings_only_show_favorites": "Zobrazit pouze oblíbené",
|
"map_settings_only_show_favorites": "Zobrazit pouze oblíbené",
|
||||||
"map_settings_theme_settings": "Motiv mapy",
|
"map_settings_theme_settings": "Motiv mapy",
|
||||||
"map_zoom_to_see_photos": "Oddálit pro zobrazení fotografií",
|
"map_zoom_to_see_photos": "Oddálit pro zobrazení fotografií",
|
||||||
|
"mark_all_as_read": "Označit vše jako přečtené",
|
||||||
|
"mark_as_read": "Označit jako přečtené",
|
||||||
|
"marked_all_as_read": "Vše označeno jako přečtené",
|
||||||
"matches": "Shody",
|
"matches": "Shody",
|
||||||
"media_type": "Typ média",
|
"media_type": "Typ média",
|
||||||
"memories": "Vzpomínky",
|
"memories": "Vzpomínky",
|
||||||
@@ -1203,7 +1218,7 @@
|
|||||||
"memories_start_over": "Začít znovu",
|
"memories_start_over": "Začít znovu",
|
||||||
"memories_swipe_to_close": "Přejetím nahoru zavřete",
|
"memories_swipe_to_close": "Přejetím nahoru zavřete",
|
||||||
"memories_year_ago": "Před rokem",
|
"memories_year_ago": "Před rokem",
|
||||||
"memories_years_ago": "Před {} lety",
|
"memories_years_ago": "Před {years, plural, one {# rokem} few {# roky} other {# lety}}",
|
||||||
"memory": "Vzpomínka",
|
"memory": "Vzpomínka",
|
||||||
"memory_lane_title": "Řada vzpomínek {title}",
|
"memory_lane_title": "Řada vzpomínek {title}",
|
||||||
"menu": "Nabídka",
|
"menu": "Nabídka",
|
||||||
@@ -1220,6 +1235,8 @@
|
|||||||
"month": "Měsíc",
|
"month": "Měsíc",
|
||||||
"monthly_title_text_date_format": "LLLL y",
|
"monthly_title_text_date_format": "LLLL y",
|
||||||
"more": "Více",
|
"more": "Více",
|
||||||
|
"moved_to_archive": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do archivu",
|
||||||
|
"moved_to_library": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do knihovny",
|
||||||
"moved_to_trash": "Přesunuto do koše",
|
"moved_to_trash": "Přesunuto do koše",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Nelze upravit datum položek pouze pro čtení, přeskakuji",
|
"multiselect_grid_edit_date_time_err_read_only": "Nelze upravit datum položek pouze pro čtení, přeskakuji",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Nelze upravit polohu položek pouze pro čtení, přeskakuji",
|
"multiselect_grid_edit_gps_err_read_only": "Nelze upravit polohu položek pouze pro čtení, přeskakuji",
|
||||||
@@ -1234,6 +1251,7 @@
|
|||||||
"new_api_key": "Nový API klíč",
|
"new_api_key": "Nový API klíč",
|
||||||
"new_password": "Nové heslo",
|
"new_password": "Nové heslo",
|
||||||
"new_person": "Nová osoba",
|
"new_person": "Nová osoba",
|
||||||
|
"new_pin_code": "Nový PIN kód",
|
||||||
"new_user_created": "Vytvořen nový uživatel",
|
"new_user_created": "Vytvořen nový uživatel",
|
||||||
"new_version_available": "NOVÁ VERZE K DISPOZICI",
|
"new_version_available": "NOVÁ VERZE K DISPOZICI",
|
||||||
"newest_first": "Nejnovější první",
|
"newest_first": "Nejnovější první",
|
||||||
@@ -1252,6 +1270,8 @@
|
|||||||
"no_favorites_message": "Přidejte si oblíbené položky a rychle najděte své nejlepší obrázky a videa",
|
"no_favorites_message": "Přidejte si oblíbené položky a rychle najděte své nejlepší obrázky a videa",
|
||||||
"no_libraries_message": "Vytvořte si externí knihovnu pro zobrazení fotografií a videí",
|
"no_libraries_message": "Vytvořte si externí knihovnu pro zobrazení fotografií a videí",
|
||||||
"no_name": "Bez jména",
|
"no_name": "Bez jména",
|
||||||
|
"no_notifications": "Žádná oznámení",
|
||||||
|
"no_people_found": "Nebyli nalezeni žádní odpovídající lidé",
|
||||||
"no_places": "Žádná místa",
|
"no_places": "Žádná místa",
|
||||||
"no_results": "Žádné výsledky",
|
"no_results": "Žádné výsledky",
|
||||||
"no_results_description": "Zkuste použít synonymum nebo obecnější klíčové slovo",
|
"no_results_description": "Zkuste použít synonymum nebo obecnější klíčové slovo",
|
||||||
@@ -1282,6 +1302,7 @@
|
|||||||
"onboarding_welcome_user": "Vítej, {user}",
|
"onboarding_welcome_user": "Vítej, {user}",
|
||||||
"online": "Online",
|
"online": "Online",
|
||||||
"only_favorites": "Pouze oblíbené",
|
"only_favorites": "Pouze oblíbené",
|
||||||
|
"open": "Otevřít",
|
||||||
"open_in_map_view": "Otevřít v zobrazení mapy",
|
"open_in_map_view": "Otevřít v zobrazení mapy",
|
||||||
"open_in_openstreetmap": "Otevřít v OpenStreetMap",
|
"open_in_openstreetmap": "Otevřít v OpenStreetMap",
|
||||||
"open_the_search_filters": "Otevřít vyhledávací filtry",
|
"open_the_search_filters": "Otevřít vyhledávací filtry",
|
||||||
@@ -1305,7 +1326,7 @@
|
|||||||
"partner_page_partner_add_failed": "Nepodařilo se přidat partnera",
|
"partner_page_partner_add_failed": "Nepodařilo se přidat partnera",
|
||||||
"partner_page_select_partner": "Vyberte partnera",
|
"partner_page_select_partner": "Vyberte partnera",
|
||||||
"partner_page_shared_to_title": "Sdíleno",
|
"partner_page_shared_to_title": "Sdíleno",
|
||||||
"partner_page_stop_sharing_content": "{} již nebude mít přístup k vašim fotografiím.",
|
"partner_page_stop_sharing_content": "{partner} již nebude mít přístup k vašim fotografiím.",
|
||||||
"partner_sharing": "Sdílení mezi partnery",
|
"partner_sharing": "Sdílení mezi partnery",
|
||||||
"partners": "Partneři",
|
"partners": "Partneři",
|
||||||
"password": "Heslo",
|
"password": "Heslo",
|
||||||
@@ -1351,6 +1372,9 @@
|
|||||||
"photos_count": "{count, plural, one {{count, number} fotka} few {{count, number} fotky} other {{count, number} fotek}}",
|
"photos_count": "{count, plural, one {{count, number} fotka} few {{count, number} fotky} other {{count, number} fotek}}",
|
||||||
"photos_from_previous_years": "Fotky z předchozích let",
|
"photos_from_previous_years": "Fotky z předchozích let",
|
||||||
"pick_a_location": "Vyberte polohu",
|
"pick_a_location": "Vyberte polohu",
|
||||||
|
"pin_code_changed_successfully": "PIN kód byl úspěšně změněn",
|
||||||
|
"pin_code_reset_successfully": "PIN kód úspěšně resetován",
|
||||||
|
"pin_code_setup_successfully": "PIN kód úspěšně nastaven",
|
||||||
"place": "Místo",
|
"place": "Místo",
|
||||||
"places": "Místa",
|
"places": "Místa",
|
||||||
"places_count": "{count, plural, one {{count, number} místo} few {{count, number} místa} other {{count, number} míst}}",
|
"places_count": "{count, plural, one {{count, number} místo} few {{count, number} místa} other {{count, number} míst}}",
|
||||||
@@ -1368,6 +1392,7 @@
|
|||||||
"previous_or_next_photo": "Předchozí nebo další fotka",
|
"previous_or_next_photo": "Předchozí nebo další fotka",
|
||||||
"primary": "Primární",
|
"primary": "Primární",
|
||||||
"privacy": "Soukromí",
|
"privacy": "Soukromí",
|
||||||
|
"profile": "Profil",
|
||||||
"profile_drawer_app_logs": "Logy",
|
"profile_drawer_app_logs": "Logy",
|
||||||
"profile_drawer_client_out_of_date_major": "Mobilní aplikace je zastaralá. Aktualizujte ji na nejnovější hlavní verzi.",
|
"profile_drawer_client_out_of_date_major": "Mobilní aplikace je zastaralá. Aktualizujte ji na nejnovější hlavní verzi.",
|
||||||
"profile_drawer_client_out_of_date_minor": "Mobilní aplikace je zastaralá. Aktualizujte ji na nejnovější verzi.",
|
"profile_drawer_client_out_of_date_minor": "Mobilní aplikace je zastaralá. Aktualizujte ji na nejnovější verzi.",
|
||||||
@@ -1381,7 +1406,7 @@
|
|||||||
"public_share": "Veřejné sdílení",
|
"public_share": "Veřejné sdílení",
|
||||||
"purchase_account_info": "Podporovatel",
|
"purchase_account_info": "Podporovatel",
|
||||||
"purchase_activated_subtitle": "Děkujeme vám za podporu aplikace Immich a softwaru s otevřeným zdrojovým kódem",
|
"purchase_activated_subtitle": "Děkujeme vám za podporu aplikace Immich a softwaru s otevřeným zdrojovým kódem",
|
||||||
"purchase_activated_time": "Aktivováno dne {date, date}",
|
"purchase_activated_time": "Aktivováno dne {date}",
|
||||||
"purchase_activated_title": "Váš klíč byl úspěšně aktivován",
|
"purchase_activated_title": "Váš klíč byl úspěšně aktivován",
|
||||||
"purchase_button_activate": "Aktivovat",
|
"purchase_button_activate": "Aktivovat",
|
||||||
"purchase_button_buy": "Koupit",
|
"purchase_button_buy": "Koupit",
|
||||||
@@ -1426,6 +1451,8 @@
|
|||||||
"recent_searches": "Nedávná vyhledávání",
|
"recent_searches": "Nedávná vyhledávání",
|
||||||
"recently_added": "Nedávno přidané",
|
"recently_added": "Nedávno přidané",
|
||||||
"recently_added_page_title": "Nedávno přidané",
|
"recently_added_page_title": "Nedávno přidané",
|
||||||
|
"recently_taken": "Nedávno pořízené",
|
||||||
|
"recently_taken_page_title": "Nedávno pořízené",
|
||||||
"refresh": "Obnovit",
|
"refresh": "Obnovit",
|
||||||
"refresh_encoded_videos": "Obnovit kódovaná videa",
|
"refresh_encoded_videos": "Obnovit kódovaná videa",
|
||||||
"refresh_faces": "Obnovit obličeje",
|
"refresh_faces": "Obnovit obličeje",
|
||||||
@@ -1468,6 +1495,7 @@
|
|||||||
"reset": "Výchozí",
|
"reset": "Výchozí",
|
||||||
"reset_password": "Obnovit heslo",
|
"reset_password": "Obnovit heslo",
|
||||||
"reset_people_visibility": "Obnovit viditelnost lidí",
|
"reset_people_visibility": "Obnovit viditelnost lidí",
|
||||||
|
"reset_pin_code": "Resetovat PIN kód",
|
||||||
"reset_to_default": "Obnovit výchozí nastavení",
|
"reset_to_default": "Obnovit výchozí nastavení",
|
||||||
"resolve_duplicates": "Vyřešit duplicity",
|
"resolve_duplicates": "Vyřešit duplicity",
|
||||||
"resolved_all_duplicates": "Vyřešeny všechny duplicity",
|
"resolved_all_duplicates": "Vyřešeny všechny duplicity",
|
||||||
@@ -1560,6 +1588,7 @@
|
|||||||
"select_keep_all": "Vybrat ponechat vše",
|
"select_keep_all": "Vybrat ponechat vše",
|
||||||
"select_library_owner": "Vyberte vlastníka knihovny",
|
"select_library_owner": "Vyberte vlastníka knihovny",
|
||||||
"select_new_face": "Výběr nového obličeje",
|
"select_new_face": "Výběr nového obličeje",
|
||||||
|
"select_person_to_tag": "Vyberte osobu, kterou chcete označit",
|
||||||
"select_photos": "Vybrat fotky",
|
"select_photos": "Vybrat fotky",
|
||||||
"select_trash_all": "Vybrat vyhodit vše",
|
"select_trash_all": "Vybrat vyhodit vše",
|
||||||
"select_user_for_sharing_page_err_album": "Nepodařilo se vytvořit album",
|
"select_user_for_sharing_page_err_album": "Nepodařilo se vytvořit album",
|
||||||
@@ -1590,12 +1619,12 @@
|
|||||||
"setting_languages_apply": "Použít",
|
"setting_languages_apply": "Použít",
|
||||||
"setting_languages_subtitle": "Změna jazyka aplikace",
|
"setting_languages_subtitle": "Změna jazyka aplikace",
|
||||||
"setting_languages_title": "Jazyk",
|
"setting_languages_title": "Jazyk",
|
||||||
"setting_notifications_notify_failures_grace_period": "Oznámení o selhání zálohování na pozadí: {}",
|
"setting_notifications_notify_failures_grace_period": "Oznámení o selhání zálohování na pozadí: {duration}",
|
||||||
"setting_notifications_notify_hours": "{} hodin",
|
"setting_notifications_notify_hours": "{count, plural, one {# hodina} few {# hodiny} other {# hodin}}",
|
||||||
"setting_notifications_notify_immediately": "okamžitě",
|
"setting_notifications_notify_immediately": "okamžitě",
|
||||||
"setting_notifications_notify_minutes": "{} minut",
|
"setting_notifications_notify_minutes": "{count, plural, one {# minuta} few {# minuty} other {# minut}}",
|
||||||
"setting_notifications_notify_never": "nikdy",
|
"setting_notifications_notify_never": "nikdy",
|
||||||
"setting_notifications_notify_seconds": "{} sekundy",
|
"setting_notifications_notify_seconds": "{count, plural, one {# sekunda} few {# sekundy} other {# sekund}}",
|
||||||
"setting_notifications_single_progress_subtitle": "Podrobné informace o průběhu nahrávání položky",
|
"setting_notifications_single_progress_subtitle": "Podrobné informace o průběhu nahrávání položky",
|
||||||
"setting_notifications_single_progress_title": "Zobrazit průběh detailů zálohování na pozadí",
|
"setting_notifications_single_progress_title": "Zobrazit průběh detailů zálohování na pozadí",
|
||||||
"setting_notifications_subtitle": "Přizpůsobení předvoleb oznámení",
|
"setting_notifications_subtitle": "Přizpůsobení předvoleb oznámení",
|
||||||
@@ -1607,9 +1636,10 @@
|
|||||||
"settings": "Nastavení",
|
"settings": "Nastavení",
|
||||||
"settings_require_restart": "Pro použití tohoto nastavení restartujte Immich",
|
"settings_require_restart": "Pro použití tohoto nastavení restartujte Immich",
|
||||||
"settings_saved": "Nastavení uloženo",
|
"settings_saved": "Nastavení uloženo",
|
||||||
|
"setup_pin_code": "Nastavení PIN kódu",
|
||||||
"share": "Sdílet",
|
"share": "Sdílet",
|
||||||
"share_add_photos": "Přidat fotografie",
|
"share_add_photos": "Přidat fotografie",
|
||||||
"share_assets_selected": "{} vybráno",
|
"share_assets_selected": "{count} vybráno",
|
||||||
"share_dialog_preparing": "Připravuji...",
|
"share_dialog_preparing": "Připravuji...",
|
||||||
"shared": "Sdílené",
|
"shared": "Sdílené",
|
||||||
"shared_album_activities_input_disable": "Komentář je vypnutý",
|
"shared_album_activities_input_disable": "Komentář je vypnutý",
|
||||||
@@ -1623,32 +1653,32 @@
|
|||||||
"shared_by_user": "Sdílel(a) {user}",
|
"shared_by_user": "Sdílel(a) {user}",
|
||||||
"shared_by_you": "Sdíleli jste",
|
"shared_by_you": "Sdíleli jste",
|
||||||
"shared_from_partner": "Fotky od {partner}",
|
"shared_from_partner": "Fotky od {partner}",
|
||||||
"shared_intent_upload_button_progress_text": "{} / {} nahráno",
|
"shared_intent_upload_button_progress_text": "{current} / {total} nahráno",
|
||||||
"shared_link_app_bar_title": "Sdílené odkazy",
|
"shared_link_app_bar_title": "Sdílené odkazy",
|
||||||
"shared_link_clipboard_copied_massage": "Zkopírováno do schránky",
|
"shared_link_clipboard_copied_massage": "Zkopírováno do schránky",
|
||||||
"shared_link_clipboard_text": "Odkaz: {}\nHeslo: {}",
|
"shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}",
|
||||||
"shared_link_create_error": "Chyba při vytváření sdíleného odkazu",
|
"shared_link_create_error": "Chyba při vytváření sdíleného odkazu",
|
||||||
"shared_link_edit_description_hint": "Zadejte popis sdílení",
|
"shared_link_edit_description_hint": "Zadejte popis sdílení",
|
||||||
"shared_link_edit_expire_after_option_day": "1 den",
|
"shared_link_edit_expire_after_option_day": "1 den",
|
||||||
"shared_link_edit_expire_after_option_days": "{} dní",
|
"shared_link_edit_expire_after_option_days": "{count, plural, one {# den} few {# dny} other {# dní}}",
|
||||||
"shared_link_edit_expire_after_option_hour": "1 hodina",
|
"shared_link_edit_expire_after_option_hour": "1 hodina",
|
||||||
"shared_link_edit_expire_after_option_hours": "{} hodin",
|
"shared_link_edit_expire_after_option_hours": "{count, plural, one {# hodina} few {# hodiny} other {# hodin}}",
|
||||||
"shared_link_edit_expire_after_option_minute": "1 minuta",
|
"shared_link_edit_expire_after_option_minute": "1 minuta",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minut",
|
"shared_link_edit_expire_after_option_minutes": "{count, plural, one {# minuta} few {# minuty} other {# minut}}",
|
||||||
"shared_link_edit_expire_after_option_months": "{} měsíce",
|
"shared_link_edit_expire_after_option_months": "{count, plural, one {# měsíc} few {# měsíce} other {# měsíců}}",
|
||||||
"shared_link_edit_expire_after_option_year": "{} rok",
|
"shared_link_edit_expire_after_option_year": "{count, plural, one {# rok} few {# roky} other {# let}}",
|
||||||
"shared_link_edit_password_hint": "Zadejte heslo pro sdílení",
|
"shared_link_edit_password_hint": "Zadejte heslo pro sdílení",
|
||||||
"shared_link_edit_submit_button": "Aktualizovat odkaz",
|
"shared_link_edit_submit_button": "Aktualizovat odkaz",
|
||||||
"shared_link_error_server_url_fetch": "Nelze načíst url serveru",
|
"shared_link_error_server_url_fetch": "Nelze načíst url serveru",
|
||||||
"shared_link_expires_day": "Vyprší za {} den",
|
"shared_link_expires_day": "Vyprší za {count, plural, one {# den} few {# dny} other {# dní}}",
|
||||||
"shared_link_expires_days": "Vyprší za {} dní",
|
"shared_link_expires_days": "Vyprší za {count, plural, one {# den} few {# dny} other {# dní}}",
|
||||||
"shared_link_expires_hour": "Vyprší za {} hodinu",
|
"shared_link_expires_hour": "Vyprší za {count, plural, one {# hodina} few {# hodiny} other {# hodin}}",
|
||||||
"shared_link_expires_hours": "Vyprší za {} hodin",
|
"shared_link_expires_hours": "Vyprší za {count, plural, one {# hodina} few {# hodiny} other {# hodin}}",
|
||||||
"shared_link_expires_minute": "Vyprší za {} minutu",
|
"shared_link_expires_minute": "Vyprší za {count, plural, one {# minuta} few {# minuty} other {# minut}}",
|
||||||
"shared_link_expires_minutes": "Vyprší za {} minut",
|
"shared_link_expires_minutes": "Vyprší za {count, plural, one {# minuta} few {# minuty} other {# minut}}",
|
||||||
"shared_link_expires_never": "Platnost ∞",
|
"shared_link_expires_never": "Platnost ∞",
|
||||||
"shared_link_expires_second": "Vyprší za {} sekundu",
|
"shared_link_expires_second": "Vyprší za {count, plural, one {# sekunda} few {# sekundy} other {# sekund}}",
|
||||||
"shared_link_expires_seconds": "Vyprší za {} sekund",
|
"shared_link_expires_seconds": "Vyprší za {count, plural, one {# sekunda} few {# sekundy} other {# sekund}}",
|
||||||
"shared_link_individual_shared": "Individuální sdílení",
|
"shared_link_individual_shared": "Individuální sdílení",
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
"shared_link_manage_links": "Spravovat sdílené odkazy",
|
"shared_link_manage_links": "Spravovat sdílené odkazy",
|
||||||
@@ -1723,6 +1753,7 @@
|
|||||||
"stop_sharing_photos_with_user": "Přestat sdílet své fotky s tímto uživatelem",
|
"stop_sharing_photos_with_user": "Přestat sdílet své fotky s tímto uživatelem",
|
||||||
"storage": "Velikost úložiště",
|
"storage": "Velikost úložiště",
|
||||||
"storage_label": "Štítek úložiště",
|
"storage_label": "Štítek úložiště",
|
||||||
|
"storage_quota": "Kvóta úložiště",
|
||||||
"storage_usage": "Využito {used} z {available}",
|
"storage_usage": "Využito {used} z {available}",
|
||||||
"submit": "Odeslat",
|
"submit": "Odeslat",
|
||||||
"suggestions": "Návrhy",
|
"suggestions": "Návrhy",
|
||||||
@@ -1749,7 +1780,7 @@
|
|||||||
"theme_selection": "Výběr motivu",
|
"theme_selection": "Výběr motivu",
|
||||||
"theme_selection_description": "Automatické nastavení světlého nebo tmavého motivu podle systémových preferencí prohlížeče",
|
"theme_selection_description": "Automatické nastavení světlého nebo tmavého motivu podle systémových preferencí prohlížeče",
|
||||||
"theme_setting_asset_list_storage_indicator_title": "Zobrazit indikátor úložiště na dlaždicích položek",
|
"theme_setting_asset_list_storage_indicator_title": "Zobrazit indikátor úložiště na dlaždicích položek",
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "Počet položek na řádek ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Počet položek na řádek ({count})",
|
||||||
"theme_setting_colorful_interface_subtitle": "Použít hlavní barvu na povrchy pozadí.",
|
"theme_setting_colorful_interface_subtitle": "Použít hlavní barvu na povrchy pozadí.",
|
||||||
"theme_setting_colorful_interface_title": "Barevné rozhraní",
|
"theme_setting_colorful_interface_title": "Barevné rozhraní",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "Přizpůsobení kvality detailů prohlížeče obrázků",
|
"theme_setting_image_viewer_quality_subtitle": "Přizpůsobení kvality detailů prohlížeče obrázků",
|
||||||
@@ -1759,7 +1790,7 @@
|
|||||||
"theme_setting_system_primary_color_title": "Použití systémové barvy",
|
"theme_setting_system_primary_color_title": "Použití systémové barvy",
|
||||||
"theme_setting_system_theme_switch": "Automaticky (podle systemového nastavení)",
|
"theme_setting_system_theme_switch": "Automaticky (podle systemového nastavení)",
|
||||||
"theme_setting_theme_subtitle": "Vyberte nastavení tématu aplikace",
|
"theme_setting_theme_subtitle": "Vyberte nastavení tématu aplikace",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Třístupňové načítání může zvýšit výkonnost načítání, ale vede k výrazně vyššímu zatížení sítě.",
|
"theme_setting_three_stage_loading_subtitle": "Třístupňové načítání může zvýšit výkonnost načítání, ale vede k výrazně vyššímu zatížení sítě",
|
||||||
"theme_setting_three_stage_loading_title": "Povolení třístupňového načítání",
|
"theme_setting_three_stage_loading_title": "Povolení třístupňového načítání",
|
||||||
"they_will_be_merged_together": "Budou sloučeny dohromady",
|
"they_will_be_merged_together": "Budou sloučeny dohromady",
|
||||||
"third_party_resources": "Zdroje třetích stran",
|
"third_party_resources": "Zdroje třetích stran",
|
||||||
@@ -1784,13 +1815,15 @@
|
|||||||
"trash_no_results_message": "Zde se zobrazí odstraněné fotky a videa.",
|
"trash_no_results_message": "Zde se zobrazí odstraněné fotky a videa.",
|
||||||
"trash_page_delete_all": "Smazat všechny",
|
"trash_page_delete_all": "Smazat všechny",
|
||||||
"trash_page_empty_trash_dialog_content": "Chcete vyprázdnit svoje vyhozené položky? Tyto položky budou trvale odstraněny z aplikace",
|
"trash_page_empty_trash_dialog_content": "Chcete vyprázdnit svoje vyhozené položky? Tyto položky budou trvale odstraněny z aplikace",
|
||||||
"trash_page_info": "Vyhozené položky budou trvale smazány po {} dnech",
|
"trash_page_info": "Vyhozené položky budou trvale smazány po {count, plural, one {# dni} other {# dnech}}",
|
||||||
"trash_page_no_assets": "Žádné vyhozené položky",
|
"trash_page_no_assets": "Žádné vyhozené položky",
|
||||||
"trash_page_restore_all": "Obnovit všechny",
|
"trash_page_restore_all": "Obnovit všechny",
|
||||||
"trash_page_select_assets_btn": "Vybrat položky",
|
"trash_page_select_assets_btn": "Vybrat položky",
|
||||||
"trash_page_title": "Koš ({})",
|
"trash_page_title": "Koš ({count})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Smazané položky budou trvale odstraněny po {days, plural, one {# dni} other {# dnech}}.",
|
"trashed_items_will_be_permanently_deleted_after": "Smazané položky budou trvale odstraněny po {days, plural, one {# dni} other {# dnech}}.",
|
||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
|
"unable_to_change_pin_code": "Nelze změnit PIN kód",
|
||||||
|
"unable_to_setup_pin_code": "Nelze nastavit PIN kód",
|
||||||
"unarchive": "Odarchivovat",
|
"unarchive": "Odarchivovat",
|
||||||
"unarchived_count": "{count, plural, one {Odarchivována #} few {Odarchivovány #} other {Odarchivováno #}}",
|
"unarchived_count": "{count, plural, one {Odarchivována #} few {Odarchivovány #} other {Odarchivováno #}}",
|
||||||
"unfavorite": "Zrušit oblíbení",
|
"unfavorite": "Zrušit oblíbení",
|
||||||
@@ -1814,6 +1847,7 @@
|
|||||||
"untracked_files": "Nesledované soubory",
|
"untracked_files": "Nesledované soubory",
|
||||||
"untracked_files_decription": "Tyto soubory nejsou aplikaci známy. Mohou být výsledkem neúspěšných přesunů, přerušeného nahrávání nebo mohou zůstat pozadu kvůli chybě",
|
"untracked_files_decription": "Tyto soubory nejsou aplikaci známy. Mohou být výsledkem neúspěšných přesunů, přerušeného nahrávání nebo mohou zůstat pozadu kvůli chybě",
|
||||||
"up_next": "To je prozatím vše",
|
"up_next": "To je prozatím vše",
|
||||||
|
"updated_at": "Aktualizováno",
|
||||||
"updated_password": "Heslo aktualizováno",
|
"updated_password": "Heslo aktualizováno",
|
||||||
"upload": "Nahrát",
|
"upload": "Nahrát",
|
||||||
"upload_concurrency": "Souběžnost nahrávání",
|
"upload_concurrency": "Souběžnost nahrávání",
|
||||||
@@ -1826,15 +1860,18 @@
|
|||||||
"upload_status_errors": "Chyby",
|
"upload_status_errors": "Chyby",
|
||||||
"upload_status_uploaded": "Nahráno",
|
"upload_status_uploaded": "Nahráno",
|
||||||
"upload_success": "Nahrání proběhlo úspěšně, obnovením stránky se zobrazí nově nahrané položky.",
|
"upload_success": "Nahrání proběhlo úspěšně, obnovením stránky se zobrazí nově nahrané položky.",
|
||||||
"upload_to_immich": "Nahrát do Immiche ({})",
|
"upload_to_immich": "Nahrát do Immich ({count})",
|
||||||
"uploading": "Nahrávání",
|
"uploading": "Nahrávání",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Využití",
|
"usage": "Využití",
|
||||||
"use_current_connection": "použít aktuální připojení",
|
"use_current_connection": "použít aktuální připojení",
|
||||||
"use_custom_date_range": "Použít vlastní rozsah dat",
|
"use_custom_date_range": "Použít vlastní rozsah dat",
|
||||||
"user": "Uživatel",
|
"user": "Uživatel",
|
||||||
|
"user_has_been_deleted": "Tento uživatel byl smazán.",
|
||||||
"user_id": "ID uživatele",
|
"user_id": "ID uživatele",
|
||||||
"user_liked": "Uživateli {user} se {type, select, photo {líbila tato fotka} video {líbilo toto video} asset {líbila tato položka} other {to líbilo}}",
|
"user_liked": "Uživateli {user} se {type, select, photo {líbila tato fotka} video {líbilo toto video} asset {líbila tato položka} other {to líbilo}}",
|
||||||
|
"user_pin_code_settings": "PIN kód",
|
||||||
|
"user_pin_code_settings_description": "Správa vašeho PIN kódu",
|
||||||
"user_purchase_settings": "Nákup",
|
"user_purchase_settings": "Nákup",
|
||||||
"user_purchase_settings_description": "Správa vašeho nákupu",
|
"user_purchase_settings_description": "Správa vašeho nákupu",
|
||||||
"user_role_set": "Uživatel {user} nastaven jako {role}",
|
"user_role_set": "Uživatel {user} nastaven jako {role}",
|
||||||
@@ -1883,11 +1920,11 @@
|
|||||||
"week": "Týden",
|
"week": "Týden",
|
||||||
"welcome": "Vítejte",
|
"welcome": "Vítejte",
|
||||||
"welcome_to_immich": "Vítejte v Immichi",
|
"welcome_to_immich": "Vítejte v Immichi",
|
||||||
"wifi_name": "Název WiFi",
|
"wifi_name": "Název Wi-Fi",
|
||||||
"year": "Rok",
|
"year": "Rok",
|
||||||
"years_ago": "Před {years, plural, one {rokem} other {# lety}}",
|
"years_ago": "Před {years, plural, one {rokem} other {# lety}}",
|
||||||
"yes": "Ano",
|
"yes": "Ano",
|
||||||
"you_dont_have_any_shared_links": "Nemáte žádné sdílené odkazy",
|
"you_dont_have_any_shared_links": "Nemáte žádné sdílené odkazy",
|
||||||
"your_wifi_name": "Váš název WiFi",
|
"your_wifi_name": "Název vaší Wi-Fi",
|
||||||
"zoom_image": "Zvětšit obrázek"
|
"zoom_image": "Zvětšit obrázek"
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-9
@@ -70,8 +70,13 @@
|
|||||||
"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, når det er tilgængeligt. Dette kan give mere nøjagtige farver for nogle billeder, men kvaliteten af forhåndsvisningen er kameraafhængig, og billedet kan have flere komprimeringsartefakter.",
|
"image_prefer_embedded_preview_setting_description": "Brug indlejrede forhåndsvisninger i RAW fotos som input til billedbehandling og når det er tilgængeligt. Dette kan give mere nøjagtige farver for nogle billeder, men kvaliteten af forhåndsvisningen er kameraafhængig, og billedet kan have flere komprimeringsartefakter.",
|
||||||
"image_prefer_wide_gamut": "Foretrækker bred farveskala",
|
"image_prefer_wide_gamut": "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",
|
||||||
@@ -187,20 +192,13 @@
|
|||||||
"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",
|
||||||
@@ -366,6 +364,7 @@
|
|||||||
"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",
|
||||||
@@ -1375,7 +1374,7 @@
|
|||||||
"public_share": "Offentlig deling",
|
"public_share": "Offentlig deling",
|
||||||
"purchase_account_info": "Supporter",
|
"purchase_account_info": "Supporter",
|
||||||
"purchase_activated_subtitle": "Tak fordi du støtter Immich og open source-software",
|
"purchase_activated_subtitle": "Tak fordi du støtter Immich og open source-software",
|
||||||
"purchase_activated_time": "Aktiveret den {date, date}",
|
"purchase_activated_time": "Aktiveret den {date}",
|
||||||
"purchase_activated_title": "Din nøgle er blevet aktiveret",
|
"purchase_activated_title": "Din nøgle er blevet aktiveret",
|
||||||
"purchase_button_activate": "Aktiver",
|
"purchase_button_activate": "Aktiver",
|
||||||
"purchase_button_buy": "Køb",
|
"purchase_button_buy": "Køb",
|
||||||
|
|||||||
+179
-142
@@ -39,11 +39,11 @@
|
|||||||
"authentication_settings_disable_all": "Bist du sicher, dass du alle Anmeldemethoden deaktivieren willst? Die Anmeldung wird vollständig deaktiviert.",
|
"authentication_settings_disable_all": "Bist du sicher, dass du alle Anmeldemethoden deaktivieren willst? Die Anmeldung wird vollständig deaktiviert.",
|
||||||
"authentication_settings_reenable": "Nutze einen <link>Server-Befehl</link> zur Reaktivierung.",
|
"authentication_settings_reenable": "Nutze einen <link>Server-Befehl</link> zur Reaktivierung.",
|
||||||
"background_task_job": "Hintergrundaufgaben",
|
"background_task_job": "Hintergrundaufgaben",
|
||||||
"backup_database": "Datenbank sichern",
|
"backup_database": "Datenbankabbild erstellen",
|
||||||
"backup_database_enable_description": "Sicherung der Datenbank aktivieren",
|
"backup_database_enable_description": "Erstellen von Datenbankabbildern aktivieren",
|
||||||
"backup_keep_last_amount": "Anzahl der aufzubewahrenden früheren Sicherungen",
|
"backup_keep_last_amount": "Anzahl der aufzubewahrenden früheren Abbilder",
|
||||||
"backup_settings": "Datensicherungs-Einstellungen",
|
"backup_settings": "Datenbankabbild-Einstellungen",
|
||||||
"backup_settings_description": "Datensicherungs-Einstellungen verwalten",
|
"backup_settings_description": "Einstellungen zum Datenbankabbild verwalten. Hinweis: Diese Jobs werden nicht überwacht und du wirst nicht über Fehlschläge informiert.",
|
||||||
"check_all": "Alle überprüfen",
|
"check_all": "Alle überprüfen",
|
||||||
"cleanup": "Aufräumen",
|
"cleanup": "Aufräumen",
|
||||||
"cleared_jobs": "Folgende Aufgaben zurückgesetzt: {job}",
|
"cleared_jobs": "Folgende Aufgaben zurückgesetzt: {job}",
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
"confirm_email_below": "Bestätige, indem du unten \"{email}\" eingibst",
|
"confirm_email_below": "Bestätige, indem du unten \"{email}\" eingibst",
|
||||||
"confirm_reprocess_all_faces": "Bist du sicher, dass du alle Gesichter erneut verarbeiten möchtest? Dies löscht auch alle bereits benannten Personen.",
|
"confirm_reprocess_all_faces": "Bist du sicher, dass du alle Gesichter erneut verarbeiten möchtest? Dies löscht auch alle bereits benannten Personen.",
|
||||||
"confirm_user_password_reset": "Bist du sicher, dass du das Passwort für {user} zurücksetzen möchtest?",
|
"confirm_user_password_reset": "Bist du sicher, dass du das Passwort für {user} zurücksetzen möchtest?",
|
||||||
|
"confirm_user_pin_code_reset": "Bist du sicher, dass du den PIN Code von {user} zurücksetzen möchtest?",
|
||||||
"create_job": "Aufgabe erstellen",
|
"create_job": "Aufgabe erstellen",
|
||||||
"cron_expression": "Cron-Ausdruck",
|
"cron_expression": "Cron-Ausdruck",
|
||||||
"cron_expression_description": "Stellen Sie das Scanintervall im Cron-Format ein. Weitere Informationen finden Sie beispielsweise unter <link>Crontab Guru</link>",
|
"cron_expression_description": "Stellen Sie das Scanintervall im Cron-Format ein. Weitere Informationen finden Sie beispielsweise unter <link>Crontab Guru</link>",
|
||||||
@@ -106,7 +107,7 @@
|
|||||||
"library_scanning_enable_description": "Regelmäßiges Scannen der Bibliothek aktivieren",
|
"library_scanning_enable_description": "Regelmäßiges Scannen der Bibliothek aktivieren",
|
||||||
"library_settings": "Externe Bibliothek",
|
"library_settings": "Externe Bibliothek",
|
||||||
"library_settings_description": "Einstellungen externer Bibliotheken verwalten",
|
"library_settings_description": "Einstellungen externer Bibliotheken verwalten",
|
||||||
"library_tasks_description": "Überprüfe externe Bibliotheken auf neue oder veränderte Medien",
|
"library_tasks_description": "Überprüfe externe Bibliotheken auf neue und/oder veränderte Medien",
|
||||||
"library_watching_enable_description": "Überwache externe Bibliotheken auf Dateiänderungen",
|
"library_watching_enable_description": "Überwache externe Bibliotheken auf Dateiänderungen",
|
||||||
"library_watching_settings": "Bibliotheksüberwachung (EXPERIMENTELL)",
|
"library_watching_settings": "Bibliotheksüberwachung (EXPERIMENTELL)",
|
||||||
"library_watching_settings_description": "Automatisch auf geänderte Dateien prüfen",
|
"library_watching_settings_description": "Automatisch auf geänderte Dateien prüfen",
|
||||||
@@ -192,32 +193,28 @@
|
|||||||
"oauth_auto_register": "Automatische Registrierung",
|
"oauth_auto_register": "Automatische Registrierung",
|
||||||
"oauth_auto_register_description": "Automatische Registrierung neuer Benutzer nach der OAuth-Anmeldung",
|
"oauth_auto_register_description": "Automatische Registrierung neuer Benutzer nach der OAuth-Anmeldung",
|
||||||
"oauth_button_text": "Button-Text",
|
"oauth_button_text": "Button-Text",
|
||||||
"oauth_client_id": "Client-ID",
|
"oauth_client_secret_description": "Erforderlich wenn PKCE (Proof Key for Code Exchange) nicht vom OAuth- Anbieter unterstützt wird",
|
||||||
"oauth_client_secret": "Client-Geheimnis",
|
|
||||||
"oauth_enable_description": "Anmeldung mit OAuth",
|
"oauth_enable_description": "Anmeldung mit OAuth",
|
||||||
"oauth_issuer_url": "Aussteller-URL",
|
|
||||||
"oauth_mobile_redirect_uri": "Mobile Umleitungs-URI",
|
"oauth_mobile_redirect_uri": "Mobile Umleitungs-URI",
|
||||||
"oauth_mobile_redirect_uri_override": "Mobile Umleitungs-URI überschreiben",
|
"oauth_mobile_redirect_uri_override": "Mobile Umleitungs-URI überschreiben",
|
||||||
"oauth_mobile_redirect_uri_override_description": "Einschalten, wenn der OAuth-Anbieter keine mobile URI wie '{callback}' erlaubt",
|
"oauth_mobile_redirect_uri_override_description": "Einschalten, wenn der OAuth-Anbieter keine mobile URI wie '{callback}' erlaubt",
|
||||||
"oauth_profile_signing_algorithm": "Algorithmus zur Profilsignierung",
|
|
||||||
"oauth_profile_signing_algorithm_description": "Dieser Algorithmus wird für die Signatur des Benutzerprofils verwendet.",
|
|
||||||
"oauth_scope": "Umfang",
|
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "OAuth-Anmeldeeinstellungen verwalten",
|
"oauth_settings_description": "OAuth-Anmeldeeinstellungen verwalten",
|
||||||
"oauth_settings_more_details": "Weitere Informationen zu dieser Funktion findest du in der <link>Dokumentation</link>.",
|
"oauth_settings_more_details": "Weitere Informationen zu dieser Funktion findest du in der <link>Dokumentation</link>.",
|
||||||
"oauth_signing_algorithm": "Signier-Algorithmus",
|
|
||||||
"oauth_storage_label_claim": "Speicherpfadbezeichnung",
|
"oauth_storage_label_claim": "Speicherpfadbezeichnung",
|
||||||
"oauth_storage_label_claim_description": "Die Speicherpfadbezeichnung des Benutzers automatisch auf den Wert dieser Eingabe setzen.",
|
"oauth_storage_label_claim_description": "Die Speicherpfadbezeichnung des Benutzers automatisch auf den Wert dieser Eingabe setzen.",
|
||||||
"oauth_storage_quota_claim": "Speicherkontingentangabe",
|
"oauth_storage_quota_claim": "Speicherkontingentangabe",
|
||||||
"oauth_storage_quota_claim_description": "Setzen Sie das Speicherkontingent des Benutzers automatisch auf den angegebenen Wert.",
|
"oauth_storage_quota_claim_description": "Setzen Sie das Speicherkontingent des Benutzers automatisch auf den angegebenen Wert.",
|
||||||
"oauth_storage_quota_default": "Standard-Speicherplatzkontingent (GiB)",
|
"oauth_storage_quota_default": "Standard-Speicherplatzkontingent (GiB)",
|
||||||
"oauth_storage_quota_default_description": "Kontingent in GiB, das verwendet werden soll, wenn keines übermittelt wird (gib 0 für ein unbegrenztes Kontingent ein).",
|
"oauth_storage_quota_default_description": "Kontingent in GiB, das verwendet werden soll, wenn keines übermittelt wird (gib 0 für ein unbegrenztes Kontingent ein).",
|
||||||
|
"oauth_timeout": "Zeitüberschreitung bei Anfrage",
|
||||||
|
"oauth_timeout_description": "Zeitüberschreitung für Anfragen in Millisekunden",
|
||||||
"offline_paths": "Offline-Pfade",
|
"offline_paths": "Offline-Pfade",
|
||||||
"offline_paths_description": "Die Ergebnisse könnten durch manuelles Löschen von Dateien, die nicht Teil einer externen Bibliothek sind, verursacht sein.",
|
"offline_paths_description": "Dies könnte durch manuelles Löschen von Dateien, die nicht Teil einer externen Bibliothek sind, verursacht sein.",
|
||||||
"password_enable_description": "Login mit E-Mail und Passwort",
|
"password_enable_description": "Mit E-Mail und Passwort anmelden",
|
||||||
"password_settings": "Passwort-Login",
|
"password_settings": "Passwort-Anmeldung",
|
||||||
"password_settings_description": "Passwort-Anmeldeeinstellungen verwalten",
|
"password_settings_description": "Passwort-Anmeldeeinstellungen verwalten",
|
||||||
"paths_validated_successfully": "Alle Pfade wurden erfolgreich validiert",
|
"paths_validated_successfully": "Alle Pfade erfolgreich überprüft",
|
||||||
"person_cleanup_job": "Personen aufräumen",
|
"person_cleanup_job": "Personen aufräumen",
|
||||||
"quota_size_gib": "Kontingent (GiB)",
|
"quota_size_gib": "Kontingent (GiB)",
|
||||||
"refreshing_all_libraries": "Alle Bibliotheken aktualisieren",
|
"refreshing_all_libraries": "Alle Bibliotheken aktualisieren",
|
||||||
@@ -251,7 +248,7 @@
|
|||||||
"storage_template_hash_verification_enabled_description": "Aktiviert die Hash-Verifizierung. Deaktiviere diese Option nur, wenn du dir über die damit verbundenen Auswirkungen im Klaren bist",
|
"storage_template_hash_verification_enabled_description": "Aktiviert die Hash-Verifizierung. Deaktiviere diese Option nur, wenn du dir über die damit verbundenen Auswirkungen im Klaren bist",
|
||||||
"storage_template_migration": "Migration von Speichervorlagen",
|
"storage_template_migration": "Migration von Speichervorlagen",
|
||||||
"storage_template_migration_description": "Diese Aufgabe wendet die aktuelle <link>{template}</link> auf zuvor hochgeladene Dateien an",
|
"storage_template_migration_description": "Diese Aufgabe wendet die aktuelle <link>{template}</link> auf zuvor hochgeladene Dateien an",
|
||||||
"storage_template_migration_info": "Die Vorlage wird alle Dateierweiterungen in Kleinbuchstaben umwandeln. Vorlagenänderungen gelten nur für neue Dateien. Um die Vorlage rückwirkend auf bereits hochgeladene Assets anzuwenden, führe den <link>{job}</link> aus.",
|
"storage_template_migration_info": "Die Speichervorlage wird alle Dateierweiterungen in Kleinbuchstaben umwandeln. Vorlagenänderungen gelten nur für neue Dateien. Um die Vorlage rückwirkend auf bereits hochgeladene Assets anzuwenden, führe den <link>{job}</link> aus.",
|
||||||
"storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe",
|
"storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe",
|
||||||
"storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter <template-link>Speichervorlage</template-link> und dessen <implications-link>Implikationen</implications-link>",
|
"storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter <template-link>Speichervorlage</template-link> und dessen <implications-link>Implikationen</implications-link>",
|
||||||
"storage_template_onboarding_description": "Wenn aktiviert, sortiert diese Funktion Dateien automatisch basierend auf einer benutzerdefinierten Vorlage. Aufgrund von Stabilitätsproblemen ist die Funktion standardmäßig deaktiviert. Weitere Informationen findest du in der <link>Dokumentation</link>.",
|
"storage_template_onboarding_description": "Wenn aktiviert, sortiert diese Funktion Dateien automatisch basierend auf einer benutzerdefinierten Vorlage. Aufgrund von Stabilitätsproblemen ist die Funktion standardmäßig deaktiviert. Weitere Informationen findest du in der <link>Dokumentation</link>.",
|
||||||
@@ -352,6 +349,7 @@
|
|||||||
"user_delete_delay_settings_description": "Gibt die Anzahl der Tage bis zur endgültigen Löschung eines Kontos und seiner Dateien an. Der Benutzerlöschauftrag wird täglich um Mitternacht ausgeführt, um zu überprüfen, ob Nutzer zur Löschung bereit sind. Änderungen an dieser Einstellung werden erst bei der nächsten Ausführung berücksichtigt.",
|
"user_delete_delay_settings_description": "Gibt die Anzahl der Tage bis zur endgültigen Löschung eines Kontos und seiner Dateien an. Der Benutzerlöschauftrag wird täglich um Mitternacht ausgeführt, um zu überprüfen, ob Nutzer zur Löschung bereit sind. Änderungen an dieser Einstellung werden erst bei der nächsten Ausführung berücksichtigt.",
|
||||||
"user_delete_immediately": "Das Konto und die Dateien von <b>{user}</b> werden <b>sofort</b> für eine permanente Löschung in die Warteschlange gestellt.",
|
"user_delete_immediately": "Das Konto und die Dateien von <b>{user}</b> werden <b>sofort</b> für eine permanente Löschung in die Warteschlange gestellt.",
|
||||||
"user_delete_immediately_checkbox": "Benutzer und Dateien zur sofortigen Löschung in die Warteschlange stellen",
|
"user_delete_immediately_checkbox": "Benutzer und Dateien zur sofortigen Löschung in die Warteschlange stellen",
|
||||||
|
"user_details": "Benutzerdetails",
|
||||||
"user_management": "Benutzerverwaltung",
|
"user_management": "Benutzerverwaltung",
|
||||||
"user_password_has_been_reset": "Das Passwort des Benutzers wurde zurückgesetzt:",
|
"user_password_has_been_reset": "Das Passwort des Benutzers wurde zurückgesetzt:",
|
||||||
"user_password_reset_description": "Bitte gib dem Benutzer das temporäre Passwort und informiere ihn, dass das Passwort beim nächsten Login geändert werden muss.",
|
"user_password_reset_description": "Bitte gib dem Benutzer das temporäre Passwort und informiere ihn, dass das Passwort beim nächsten Login geändert werden muss.",
|
||||||
@@ -371,13 +369,17 @@
|
|||||||
"admin_password": "Administrator Passwort",
|
"admin_password": "Administrator Passwort",
|
||||||
"administration": "Verwaltung",
|
"administration": "Verwaltung",
|
||||||
"advanced": "Erweitert",
|
"advanced": "Erweitert",
|
||||||
"advanced_settings_log_level_title": "Log-Level: {}",
|
"advanced_settings_enable_alternate_media_filter_subtitle": "Verwende diese Option, um Medien während der Synchronisierung nach anderen Kriterien zu filtern. Versuchen dies nur, wenn Probleme mit der Erkennung aller Alben durch die App auftreten.",
|
||||||
|
"advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTELL] Benutze alternativen Filter für Synchronisierung der Gerätealben",
|
||||||
|
"advanced_settings_log_level_title": "Log-Level: {level}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von Miniaturbildern direkt aus dem Gerät. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.",
|
"advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von Miniaturbildern direkt aus dem Gerät. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.",
|
||||||
"advanced_settings_prefer_remote_title": "Server-Bilder bevorzugen",
|
"advanced_settings_prefer_remote_title": "Server-Bilder bevorzugen",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Definiere einen Proxy-Header, den Immich bei jeder Netzwerkanfrage mitschicken soll",
|
"advanced_settings_proxy_headers_subtitle": "Definiere einen Proxy-Header, den Immich bei jeder Netzwerkanfrage mitschicken soll",
|
||||||
"advanced_settings_proxy_headers_title": "Proxy-Headers",
|
"advanced_settings_proxy_headers_title": "Proxy-Headers",
|
||||||
"advanced_settings_self_signed_ssl_subtitle": "Verifizierung von SSL-Zertifikaten vom Server überspringen. Notwendig bei selbstsignierten Zertifikaten.",
|
"advanced_settings_self_signed_ssl_subtitle": "Verifizierung von SSL-Zertifikaten vom Server überspringen. Notwendig bei selbstsignierten Zertifikaten.",
|
||||||
"advanced_settings_self_signed_ssl_title": "Selbstsignierte SSL-Zertifikate erlauben",
|
"advanced_settings_self_signed_ssl_title": "Selbstsignierte SSL-Zertifikate erlauben",
|
||||||
|
"advanced_settings_sync_remote_deletions_subtitle": "Automatisches Löschen oder Wiederherstellen einer Datei auf diesem Gerät, wenn diese Aktion im Web durchgeführt wird",
|
||||||
|
"advanced_settings_sync_remote_deletions_title": "Synchrone Remote-Löschungen [Experimentell]",
|
||||||
"advanced_settings_tile_subtitle": "Erweiterte Benutzereinstellungen",
|
"advanced_settings_tile_subtitle": "Erweiterte Benutzereinstellungen",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Erweiterte Funktionen zur Fehlersuche aktivieren",
|
"advanced_settings_troubleshooting_subtitle": "Erweiterte Funktionen zur Fehlersuche aktivieren",
|
||||||
"advanced_settings_troubleshooting_title": "Fehlersuche",
|
"advanced_settings_troubleshooting_title": "Fehlersuche",
|
||||||
@@ -400,9 +402,9 @@
|
|||||||
"album_remove_user_confirmation": "Bist du sicher, dass du {user} entfernen willst?",
|
"album_remove_user_confirmation": "Bist du sicher, dass du {user} entfernen willst?",
|
||||||
"album_share_no_users": "Es sieht so aus, als hättest du dieses Album mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.",
|
"album_share_no_users": "Es sieht so aus, als hättest du dieses Album mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.",
|
||||||
"album_thumbnail_card_item": "1 Element",
|
"album_thumbnail_card_item": "1 Element",
|
||||||
"album_thumbnail_card_items": "{} Elemente",
|
"album_thumbnail_card_items": "{count} Elemente",
|
||||||
"album_thumbnail_card_shared": " · Geteilt",
|
"album_thumbnail_card_shared": " · Geteilt",
|
||||||
"album_thumbnail_shared_by": "Geteilt von {}",
|
"album_thumbnail_shared_by": "Geteilt von {user}",
|
||||||
"album_updated": "Album aktualisiert",
|
"album_updated": "Album aktualisiert",
|
||||||
"album_updated_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn ein freigegebenes Album neue Dateien enthält",
|
"album_updated_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn ein freigegebenes Album neue Dateien enthält",
|
||||||
"album_user_left": "{album} verlassen",
|
"album_user_left": "{album} verlassen",
|
||||||
@@ -440,15 +442,15 @@
|
|||||||
"archive": "Archiv",
|
"archive": "Archiv",
|
||||||
"archive_or_unarchive_photo": "Foto archivieren bzw. Archivierung aufheben",
|
"archive_or_unarchive_photo": "Foto archivieren bzw. Archivierung aufheben",
|
||||||
"archive_page_no_archived_assets": "Keine archivierten Inhalte gefunden",
|
"archive_page_no_archived_assets": "Keine archivierten Inhalte gefunden",
|
||||||
"archive_page_title": "Archiv ({})",
|
"archive_page_title": "Archiv ({count})",
|
||||||
"archive_size": "Archivgröße",
|
"archive_size": "Archivgröße",
|
||||||
"archive_size_description": "Archivgröße für Downloads konfigurieren (in GiB)",
|
"archive_size_description": "Archivgröße für Downloads konfigurieren (in GiB)",
|
||||||
"archived": "Archiviert",
|
"archived": "Archiviert",
|
||||||
"archived_count": "{count, plural, other {# archiviert}}",
|
"archived_count": "{count, plural, other {# archiviert}}",
|
||||||
"are_these_the_same_person": "Ist das dieselbe Person?",
|
"are_these_the_same_person": "Ist das dieselbe Person?",
|
||||||
"are_you_sure_to_do_this": "Bist du sicher, dass du das tun willst?",
|
"are_you_sure_to_do_this": "Bist du sicher, dass du das tun willst?",
|
||||||
"asset_action_delete_err_read_only": "Schreibgeschützte Inhalte können nicht gelöscht werden, überspringen...",
|
"asset_action_delete_err_read_only": "Schreibgeschützte Inhalte können nicht gelöscht werden, überspringen",
|
||||||
"asset_action_share_err_offline": "Die Offline-Inhalte konnten nicht gelesen werden, überspringen...",
|
"asset_action_share_err_offline": "Die Offline-Inhalte konnten nicht gelesen werden, überspringen",
|
||||||
"asset_added_to_album": "Zum Album hinzugefügt",
|
"asset_added_to_album": "Zum Album hinzugefügt",
|
||||||
"asset_adding_to_album": "Hinzufügen zum Album…",
|
"asset_adding_to_album": "Hinzufügen zum Album…",
|
||||||
"asset_description_updated": "Die Beschreibung der Datei wurde aktualisiert",
|
"asset_description_updated": "Die Beschreibung der Datei wurde aktualisiert",
|
||||||
@@ -477,43 +479,43 @@
|
|||||||
"assets_added_to_album_count": "{count, plural, one {# Datei} other {# Dateien}} zum Album hinzugefügt",
|
"assets_added_to_album_count": "{count, plural, one {# Datei} other {# Dateien}} zum Album hinzugefügt",
|
||||||
"assets_added_to_name_count": "{count, plural, one {# Element} other {# Elemente}} zu {hasName, select, true {<b>{name}</b>} other {neuem Album}} hinzugefügt",
|
"assets_added_to_name_count": "{count, plural, one {# Element} other {# Elemente}} zu {hasName, select, true {<b>{name}</b>} other {neuem Album}} hinzugefügt",
|
||||||
"assets_count": "{count, plural, one {# Datei} other {# Dateien}}",
|
"assets_count": "{count, plural, one {# Datei} other {# Dateien}}",
|
||||||
"assets_deleted_permanently": "{} Datei/en permanent gelöscht",
|
"assets_deleted_permanently": "{count} Element(e) permanent gelöscht",
|
||||||
"assets_deleted_permanently_from_server": "{} Datei/en wurden permanent vom Immich Server gelöscht",
|
"assets_deleted_permanently_from_server": "{count} Element(e) permanent vom Immich-Server gelöscht",
|
||||||
"assets_moved_to_trash_count": "{count, plural, one {# Datei} other {# Dateien}} in den Papierkorb verschoben",
|
"assets_moved_to_trash_count": "{count, plural, one {# Datei} other {# Dateien}} in den Papierkorb verschoben",
|
||||||
"assets_permanently_deleted_count": "{count, plural, one {# Datei} other {# Dateien}} endgültig gelöscht",
|
"assets_permanently_deleted_count": "{count, plural, one {# Datei} other {# Dateien}} endgültig gelöscht",
|
||||||
"assets_removed_count": "{count, plural, one {# Datei} other {# Dateien}} entfernt",
|
"assets_removed_count": "{count, plural, one {# Datei} other {# Dateien}} entfernt",
|
||||||
"assets_removed_permanently_from_device": "{} Datei/en wurden permanent vom Gerät gelöscht",
|
"assets_removed_permanently_from_device": "{count} Element(e) permanent von Ihrem Gerät gelöscht",
|
||||||
"assets_restore_confirmation": "Bist du sicher, dass du alle Dateien aus dem Papierkorb wiederherstellen willst? Diese Aktion kann nicht rückgängig gemacht werden! Beachte, dass Offline-Dateien auf diese Weise nicht wiederhergestellt werden können.",
|
"assets_restore_confirmation": "Bist du sicher, dass du alle Dateien aus dem Papierkorb wiederherstellen willst? Diese Aktion kann nicht rückgängig gemacht werden! Beachte, dass Offline-Dateien auf diese Weise nicht wiederhergestellt werden können.",
|
||||||
"assets_restored_count": "{count, plural, one {# Datei} other {# Dateien}} wiederhergestellt",
|
"assets_restored_count": "{count, plural, one {# Datei} other {# Dateien}} wiederhergestellt",
|
||||||
"assets_restored_successfully": "{} Datei/en erfolgreich wiederhergestellt",
|
"assets_restored_successfully": "{count} Element(e) erfolgreich wiederhergestellt",
|
||||||
"assets_trashed": "{} Datei/en gelöscht",
|
"assets_trashed": "{count} Element(e) gelöscht",
|
||||||
"assets_trashed_count": "{count, plural, one {# Datei} other {# Dateien}} in den Papierkorb verschoben",
|
"assets_trashed_count": "{count, plural, one {# Datei} other {# Dateien}} in den Papierkorb verschoben",
|
||||||
"assets_trashed_from_server": "{} Datei/en vom Immich-Server gelöscht",
|
"assets_trashed_from_server": "{count} Element(e) vom Immich-Server gelöscht",
|
||||||
"assets_were_part_of_album_count": "{count, plural, one {# Datei ist} other {# Dateien sind}} bereits im Album vorhanden",
|
"assets_were_part_of_album_count": "{count, plural, one {# Datei ist} other {# Dateien sind}} bereits im Album vorhanden",
|
||||||
"authorized_devices": "Verwendete Geräte",
|
"authorized_devices": "Verwendete Geräte",
|
||||||
"automatic_endpoint_switching_subtitle": "Verbinden Sie sich lokal über ein bestimmtes WLAN, wenn es verfügbar ist, und verwenden Sie andere Verbindungsmöglichkeiten anderswo.",
|
"automatic_endpoint_switching_subtitle": "Verbinden Sie sich lokal über ein bestimmtes WLAN, wenn es verfügbar ist, und verwenden Sie andere Verbindungsmöglichkeiten anderswo",
|
||||||
"automatic_endpoint_switching_title": "Automatische URL-Umschaltung",
|
"automatic_endpoint_switching_title": "Automatische URL-Umschaltung",
|
||||||
"back": "Zurück",
|
"back": "Zurück",
|
||||||
"back_close_deselect": "Zurück, Schließen oder Abwählen",
|
"back_close_deselect": "Zurück, Schließen oder Abwählen",
|
||||||
"background_location_permission": "Hintergrund Standortfreigabe",
|
"background_location_permission": "Hintergrund Standortfreigabe",
|
||||||
"background_location_permission_content": "Um im Hintergrund zwischen den Netzwerken wechseln zu können, muss Immich *immer* Zugriff auf den genauen Standort haben, damit die App den Namen des WLAN-Netzwerks ermitteln kann",
|
"background_location_permission_content": "Um im Hintergrund zwischen den Netzwerken wechseln zu können, muss Immich *immer* Zugriff auf den genauen Standort haben, damit die App den Namen des WLAN-Netzwerks ermitteln kann",
|
||||||
"backup_album_selection_page_albums_device": "Alben auf dem Gerät ({})",
|
"backup_album_selection_page_albums_device": "Alben auf dem Gerät ({count})",
|
||||||
"backup_album_selection_page_albums_tap": "Einmalig das Album antippen um es zu sichern, doppelt antippen um es nicht mehr zu sichern.",
|
"backup_album_selection_page_albums_tap": "Einmalig das Album antippen um es zu sichern, doppelt antippen um es nicht mehr zu sichern",
|
||||||
"backup_album_selection_page_assets_scatter": "Elemente (Fotos / Videos) können sich über mehrere Alben verteilen. Daher können diese vor der Sicherung eingeschlossen oder ausgeschlossen werden.",
|
"backup_album_selection_page_assets_scatter": "Elemente (Fotos / Videos) können sich über mehrere Alben verteilen. Daher können diese vor der Sicherung eingeschlossen oder ausgeschlossen werden.",
|
||||||
"backup_album_selection_page_select_albums": "Alben auswählen",
|
"backup_album_selection_page_select_albums": "Alben auswählen",
|
||||||
"backup_album_selection_page_selection_info": "Information",
|
"backup_album_selection_page_selection_info": "Information",
|
||||||
"backup_album_selection_page_total_assets": "Elemente",
|
"backup_album_selection_page_total_assets": "Elemente",
|
||||||
"backup_all": "Alle",
|
"backup_all": "Alle",
|
||||||
"backup_background_service_backup_failed_message": "Es trat ein Fehler bei der Sicherung auf. Erneuter Versuch...",
|
"backup_background_service_backup_failed_message": "Es trat ein Fehler bei der Sicherung auf. Erneuter Versuch…",
|
||||||
"backup_background_service_connection_failed_message": "Es konnte keine Verbindung zum Server hergestellt werden. Erneuter Versuch...",
|
"backup_background_service_connection_failed_message": "Es konnte keine Verbindung zum Server hergestellt werden. Erneuter Versuch…",
|
||||||
"backup_background_service_current_upload_notification": "Lädt {} hoch",
|
"backup_background_service_current_upload_notification": "Lädt {filename} hoch",
|
||||||
"backup_background_service_default_notification": "Suche nach neuen Elementen…",
|
"backup_background_service_default_notification": "Suche nach neuen Elementen…",
|
||||||
"backup_background_service_error_title": "Fehler bei der Sicherung",
|
"backup_background_service_error_title": "Fehler bei der Sicherung",
|
||||||
"backup_background_service_in_progress_notification": "Elemente werden gesichert...",
|
"backup_background_service_in_progress_notification": "Elemente werden gesichert…",
|
||||||
"backup_background_service_upload_failure_notification": "Konnte {} nicht hochladen",
|
"backup_background_service_upload_failure_notification": "Konnte {filename} nicht hochladen",
|
||||||
"backup_controller_page_albums": "Gesicherte Alben",
|
"backup_controller_page_albums": "Gesicherte Alben",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Aktiviere Hintergrundaktualisierungen in Einstellungen -> Allgemein -> Hintergrundaktualisierungen um Sicherungen im Hintergrund zu ermöglichen. ",
|
"backup_controller_page_background_app_refresh_disabled_content": "Aktiviere Hintergrundaktualisierungen in Einstellungen -> Allgemein -> Hintergrundaktualisierungen um Sicherungen im Hintergrund zu ermöglichen.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "Hintergrundaktualisierungen sind deaktiviert.",
|
"backup_controller_page_background_app_refresh_disabled_title": "Hintergrundaktualisierungen sind deaktiviert",
|
||||||
"backup_controller_page_background_app_refresh_enable_button_text": "Gehe zu Einstellungen",
|
"backup_controller_page_background_app_refresh_enable_button_text": "Gehe zu Einstellungen",
|
||||||
"backup_controller_page_background_battery_info_link": "Zeige mir wie",
|
"backup_controller_page_background_battery_info_link": "Zeige mir wie",
|
||||||
"backup_controller_page_background_battery_info_message": "Für die besten Ergebnisse für Sicherungen im Hintergrund, deaktiviere alle Batterieoptimierungen und Einschränkungen für die Hintergrundaktivitäten von Immich.\n\nDa dies gerätespezifisch ist, schlage diese Informationen für deinen Gerätehersteller nach.",
|
"backup_controller_page_background_battery_info_message": "Für die besten Ergebnisse für Sicherungen im Hintergrund, deaktiviere alle Batterieoptimierungen und Einschränkungen für die Hintergrundaktivitäten von Immich.\n\nDa dies gerätespezifisch ist, schlage diese Informationen für deinen Gerätehersteller nach.",
|
||||||
@@ -521,7 +523,7 @@
|
|||||||
"backup_controller_page_background_battery_info_title": "Batterieoptimierungen",
|
"backup_controller_page_background_battery_info_title": "Batterieoptimierungen",
|
||||||
"backup_controller_page_background_charging": "Nur während des Ladens",
|
"backup_controller_page_background_charging": "Nur während des Ladens",
|
||||||
"backup_controller_page_background_configure_error": "Konnte Hintergrundservice nicht konfigurieren",
|
"backup_controller_page_background_configure_error": "Konnte Hintergrundservice nicht konfigurieren",
|
||||||
"backup_controller_page_background_delay": "Sicherung neuer Elemente verzögern um: {}",
|
"backup_controller_page_background_delay": "Sicherung neuer Elemente verzögern um: {duration}",
|
||||||
"backup_controller_page_background_description": "Schalte den Hintergrundservice ein, um neue Elemente automatisch im Hintergrund zu sichern ohne die App zu öffnen",
|
"backup_controller_page_background_description": "Schalte den Hintergrundservice ein, um neue Elemente automatisch im Hintergrund zu sichern ohne die App zu öffnen",
|
||||||
"backup_controller_page_background_is_off": "Automatische Sicherung im Hintergrund ist deaktiviert",
|
"backup_controller_page_background_is_off": "Automatische Sicherung im Hintergrund ist deaktiviert",
|
||||||
"backup_controller_page_background_is_on": "Automatische Sicherung im Hintergrund ist aktiviert",
|
"backup_controller_page_background_is_on": "Automatische Sicherung im Hintergrund ist aktiviert",
|
||||||
@@ -531,12 +533,12 @@
|
|||||||
"backup_controller_page_backup": "Sicherung",
|
"backup_controller_page_backup": "Sicherung",
|
||||||
"backup_controller_page_backup_selected": "Ausgewählt: ",
|
"backup_controller_page_backup_selected": "Ausgewählt: ",
|
||||||
"backup_controller_page_backup_sub": "Gesicherte Fotos und Videos",
|
"backup_controller_page_backup_sub": "Gesicherte Fotos und Videos",
|
||||||
"backup_controller_page_created": "Erstellt: {}",
|
"backup_controller_page_created": "Erstellt am: {date}",
|
||||||
"backup_controller_page_desc_backup": "Aktiviere die Sicherung, um Elemente immer automatisch auf den Server zu laden, während du die App benutzt.",
|
"backup_controller_page_desc_backup": "Aktiviere die Sicherung, um Elemente immer automatisch auf den Server zu laden, während du die App benutzt.",
|
||||||
"backup_controller_page_excluded": "Ausgeschlossen: ",
|
"backup_controller_page_excluded": "Ausgeschlossen: ",
|
||||||
"backup_controller_page_failed": "Fehlgeschlagen ({})",
|
"backup_controller_page_failed": "Fehlgeschlagen ({count})",
|
||||||
"backup_controller_page_filename": "Dateiname: {} [{}]",
|
"backup_controller_page_filename": "Dateiname: {filename} [{size}]",
|
||||||
"backup_controller_page_id": "ID: {}",
|
"backup_controller_page_id": "ID: {id}",
|
||||||
"backup_controller_page_info": "Informationen zur Sicherung",
|
"backup_controller_page_info": "Informationen zur Sicherung",
|
||||||
"backup_controller_page_none_selected": "Keine ausgewählt",
|
"backup_controller_page_none_selected": "Keine ausgewählt",
|
||||||
"backup_controller_page_remainder": "Verbleibend",
|
"backup_controller_page_remainder": "Verbleibend",
|
||||||
@@ -545,7 +547,7 @@
|
|||||||
"backup_controller_page_start_backup": "Sicherung starten",
|
"backup_controller_page_start_backup": "Sicherung starten",
|
||||||
"backup_controller_page_status_off": "Sicherung im Vordergrund ist inaktiv",
|
"backup_controller_page_status_off": "Sicherung im Vordergrund ist inaktiv",
|
||||||
"backup_controller_page_status_on": "Sicherung im Vordergrund ist aktiv",
|
"backup_controller_page_status_on": "Sicherung im Vordergrund ist aktiv",
|
||||||
"backup_controller_page_storage_format": "{} von {} genutzt",
|
"backup_controller_page_storage_format": "{used} von {total} genutzt",
|
||||||
"backup_controller_page_to_backup": "Zu sichernde Alben",
|
"backup_controller_page_to_backup": "Zu sichernde Alben",
|
||||||
"backup_controller_page_total_sub": "Alle Fotos und Videos",
|
"backup_controller_page_total_sub": "Alle Fotos und Videos",
|
||||||
"backup_controller_page_turn_off": "Sicherung im Vordergrund ausschalten",
|
"backup_controller_page_turn_off": "Sicherung im Vordergrund ausschalten",
|
||||||
@@ -554,7 +556,7 @@
|
|||||||
"backup_err_only_album": "Das einzige Album kann nicht entfernt werden",
|
"backup_err_only_album": "Das einzige Album kann nicht entfernt werden",
|
||||||
"backup_info_card_assets": "Elemente",
|
"backup_info_card_assets": "Elemente",
|
||||||
"backup_manual_cancelled": "Abgebrochen",
|
"backup_manual_cancelled": "Abgebrochen",
|
||||||
"backup_manual_in_progress": "Sicherung läuft bereits. Bitte versuche es später erneut.",
|
"backup_manual_in_progress": "Sicherung läuft bereits. Bitte versuche es später erneut",
|
||||||
"backup_manual_success": "Erfolgreich",
|
"backup_manual_success": "Erfolgreich",
|
||||||
"backup_manual_title": "Sicherungsstatus",
|
"backup_manual_title": "Sicherungsstatus",
|
||||||
"backup_options_page_title": "Sicherungsoptionen",
|
"backup_options_page_title": "Sicherungsoptionen",
|
||||||
@@ -570,21 +572,21 @@
|
|||||||
"bulk_keep_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien}} behalten möchtest? Dies wird alle Duplikat-Gruppen auflösen ohne etwas zu löschen.",
|
"bulk_keep_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien}} behalten möchtest? Dies wird alle Duplikat-Gruppen auflösen ohne etwas zu löschen.",
|
||||||
"bulk_trash_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien gemeinsam}} in den Papierkorb verschieben möchtest? Dies wird die größte Datei jeder Gruppe behalten und alle anderen Duplikate in den Papierkorb verschieben.",
|
"bulk_trash_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien gemeinsam}} in den Papierkorb verschieben möchtest? Dies wird die größte Datei jeder Gruppe behalten und alle anderen Duplikate in den Papierkorb verschieben.",
|
||||||
"buy": "Immich erwerben",
|
"buy": "Immich erwerben",
|
||||||
"cache_settings_album_thumbnails": "Vorschaubilder der Bibliothek ({} Elemente)",
|
"cache_settings_album_thumbnails": "Vorschaubilder der Bibliothek ({count} Elemente)",
|
||||||
"cache_settings_clear_cache_button": "Zwischenspeicher löschen",
|
"cache_settings_clear_cache_button": "Zwischenspeicher löschen",
|
||||||
"cache_settings_clear_cache_button_title": "Löscht den Zwischenspeicher der App. Dies wird die Leistungsfähigkeit der App deutlich einschränken, bis der Zwischenspeicher wieder aufgebaut wurde.",
|
"cache_settings_clear_cache_button_title": "Löscht den Zwischenspeicher der App. Dies wird die Leistungsfähigkeit der App deutlich einschränken, bis der Zwischenspeicher wieder aufgebaut wurde.",
|
||||||
"cache_settings_duplicated_assets_clear_button": "LEEREN",
|
"cache_settings_duplicated_assets_clear_button": "LEEREN",
|
||||||
"cache_settings_duplicated_assets_subtitle": "Fotos und Videos, die von der App blockiert werden",
|
"cache_settings_duplicated_assets_subtitle": "Fotos und Videos, die von der App blockiert werden",
|
||||||
"cache_settings_duplicated_assets_title": "Duplikate ({})",
|
"cache_settings_duplicated_assets_title": "Duplikate ({count})",
|
||||||
"cache_settings_image_cache_size": "{} Bilder im Zwischenspeicher",
|
"cache_settings_image_cache_size": "Bilder im Zwischenspeicher ({count} Bilder)",
|
||||||
"cache_settings_statistics_album": "Vorschaubilder der Bibliothek",
|
"cache_settings_statistics_album": "Vorschaubilder der Bibliothek",
|
||||||
"cache_settings_statistics_assets": "{} Elemente ({})",
|
"cache_settings_statistics_assets": "{count} Elemente ({size})",
|
||||||
"cache_settings_statistics_full": "Originalbilder",
|
"cache_settings_statistics_full": "Originalbilder",
|
||||||
"cache_settings_statistics_shared": "Vorschaubilder geteilter Alben",
|
"cache_settings_statistics_shared": "Vorschaubilder geteilter Alben",
|
||||||
"cache_settings_statistics_thumbnail": "Vorschaubilder",
|
"cache_settings_statistics_thumbnail": "Vorschaubilder",
|
||||||
"cache_settings_statistics_title": "Zwischenspeicher-Nutzung",
|
"cache_settings_statistics_title": "Zwischenspeicher-Nutzung",
|
||||||
"cache_settings_subtitle": "Kontrollieren, wie Immich den Zwischenspeicher nutzt",
|
"cache_settings_subtitle": "Kontrollieren, wie Immich den Zwischenspeicher nutzt",
|
||||||
"cache_settings_thumbnail_size": "{} Vorschaubilder im Zwischenspeicher",
|
"cache_settings_thumbnail_size": "Vorschaubilder im Zwischenspeicher ({count} Bilder)",
|
||||||
"cache_settings_tile_subtitle": "Lokalen Speicher verwalten",
|
"cache_settings_tile_subtitle": "Lokalen Speicher verwalten",
|
||||||
"cache_settings_tile_title": "Lokaler Speicher",
|
"cache_settings_tile_title": "Lokaler Speicher",
|
||||||
"cache_settings_title": "Zwischenspeicher Einstellungen",
|
"cache_settings_title": "Zwischenspeicher Einstellungen",
|
||||||
@@ -610,6 +612,7 @@
|
|||||||
"change_password_form_new_password": "Neues Passwort",
|
"change_password_form_new_password": "Neues Passwort",
|
||||||
"change_password_form_password_mismatch": "Passwörter stimmen nicht überein",
|
"change_password_form_password_mismatch": "Passwörter stimmen nicht überein",
|
||||||
"change_password_form_reenter_new_password": "Passwort erneut eingeben",
|
"change_password_form_reenter_new_password": "Passwort erneut eingeben",
|
||||||
|
"change_pin_code": "PIN Code ändern",
|
||||||
"change_your_password": "Ändere dein Passwort",
|
"change_your_password": "Ändere dein Passwort",
|
||||||
"changed_visibility_successfully": "Die Sichtbarkeit wurde erfolgreich geändert",
|
"changed_visibility_successfully": "Die Sichtbarkeit wurde erfolgreich geändert",
|
||||||
"check_all": "Alle prüfen",
|
"check_all": "Alle prüfen",
|
||||||
@@ -630,8 +633,8 @@
|
|||||||
"client_cert_import_success_msg": "Client Zertifikat wurde importiert",
|
"client_cert_import_success_msg": "Client Zertifikat wurde importiert",
|
||||||
"client_cert_invalid_msg": "Ungültige Zertifikatsdatei oder falsches Passwort",
|
"client_cert_invalid_msg": "Ungültige Zertifikatsdatei oder falsches Passwort",
|
||||||
"client_cert_remove_msg": "Client Zertifikat wurde entfernt",
|
"client_cert_remove_msg": "Client Zertifikat wurde entfernt",
|
||||||
"client_cert_subtitle": "Unterstützt nur das PKCS12 (.p12, .pfx) Format. Zertifikatsimporte oder -entfernungen sind nur vor dem Login möglich.",
|
"client_cert_subtitle": "Unterstützt nur das PKCS12 (.p12, .pfx) Format. Zertifikatsimporte oder -entfernungen sind nur vor dem Login möglich",
|
||||||
"client_cert_title": "SSL-Client-Zertifikat ",
|
"client_cert_title": "SSL-Client-Zertifikat",
|
||||||
"clockwise": "Im Uhrzeigersinn",
|
"clockwise": "Im Uhrzeigersinn",
|
||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
"collapse": "Zusammenklappen",
|
"collapse": "Zusammenklappen",
|
||||||
@@ -643,18 +646,19 @@
|
|||||||
"comments_and_likes": "Kommentare & Likes",
|
"comments_and_likes": "Kommentare & Likes",
|
||||||
"comments_are_disabled": "Kommentare sind deaktiviert",
|
"comments_are_disabled": "Kommentare sind deaktiviert",
|
||||||
"common_create_new_album": "Neues Album erstellen",
|
"common_create_new_album": "Neues Album erstellen",
|
||||||
"common_server_error": "Bitte überprüfe Deine Netzwerkverbindung und stelle sicher, dass die App und Server Versionen kompatibel sind.",
|
"common_server_error": "Bitte überprüfe deine Netzwerkverbindung und stelle sicher, dass die App und Server Versionen kompatibel sind.",
|
||||||
"completed": "Fertig\n",
|
"completed": "Abgeschlossen",
|
||||||
"confirm": "Bestätigen",
|
"confirm": "Bestätigen",
|
||||||
"confirm_admin_password": "Administrator Passwort bestätigen",
|
"confirm_admin_password": "Administrator Passwort bestätigen",
|
||||||
"confirm_delete_face": "Bist du sicher dass du das Gesicht von {name} aus der Datei entfernen willst?",
|
"confirm_delete_face": "Bist du sicher dass du das Gesicht von {name} aus der Datei entfernen willst?",
|
||||||
"confirm_delete_shared_link": "Bist du sicher, dass du diesen geteilten Link löschen willst?",
|
"confirm_delete_shared_link": "Bist du sicher, dass du diesen geteilten Link löschen willst?",
|
||||||
"confirm_keep_this_delete_others": "Alle anderen Dateien im Stapel bis auf diese werden gelöscht. Bist du sicher, dass du fortfahren möchten?",
|
"confirm_keep_this_delete_others": "Alle anderen Dateien im Stapel bis auf diese werden gelöscht. Bist du sicher, dass du fortfahren möchten?",
|
||||||
|
"confirm_new_pin_code": "Neuen PIN Code bestätigen",
|
||||||
"confirm_password": "Passwort bestätigen",
|
"confirm_password": "Passwort bestätigen",
|
||||||
"contain": "Vollständig",
|
"contain": "Vollständig",
|
||||||
"context": "Kontext",
|
"context": "Kontext",
|
||||||
"continue": "Fortsetzen",
|
"continue": "Fortsetzen",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} Elemente · Geteilt",
|
"control_bottom_app_bar_album_info_shared": "{count} Elemente · Geteilt",
|
||||||
"control_bottom_app_bar_create_new_album": "Neues Album erstellen",
|
"control_bottom_app_bar_create_new_album": "Neues Album erstellen",
|
||||||
"control_bottom_app_bar_delete_from_immich": "Aus Immich löschen",
|
"control_bottom_app_bar_delete_from_immich": "Aus Immich löschen",
|
||||||
"control_bottom_app_bar_delete_from_local": "Vom Gerät löschen",
|
"control_bottom_app_bar_delete_from_local": "Vom Gerät löschen",
|
||||||
@@ -692,9 +696,11 @@
|
|||||||
"create_tag_description": "Erstelle einen neuen Tag. Für verschachtelte Tags, gib den gesamten Pfad inklusive Schrägstrich an.",
|
"create_tag_description": "Erstelle einen neuen Tag. Für verschachtelte Tags, gib den gesamten Pfad inklusive Schrägstrich an.",
|
||||||
"create_user": "Nutzer erstellen",
|
"create_user": "Nutzer erstellen",
|
||||||
"created": "Erstellt",
|
"created": "Erstellt",
|
||||||
|
"created_at": "Erstellt",
|
||||||
"crop": "Zuschneiden",
|
"crop": "Zuschneiden",
|
||||||
"curated_object_page_title": "Dinge",
|
"curated_object_page_title": "Dinge",
|
||||||
"current_device": "Aktuelles Gerät",
|
"current_device": "Aktuelles Gerät",
|
||||||
|
"current_pin_code": "Aktueller PIN Code",
|
||||||
"current_server_address": "Aktuelle Serveradresse",
|
"current_server_address": "Aktuelle Serveradresse",
|
||||||
"custom_locale": "Benutzerdefinierte Sprache",
|
"custom_locale": "Benutzerdefinierte Sprache",
|
||||||
"custom_locale_description": "Datumsangaben und Zahlen je nach Sprache und Land formatieren",
|
"custom_locale_description": "Datumsangaben und Zahlen je nach Sprache und Land formatieren",
|
||||||
@@ -719,9 +725,9 @@
|
|||||||
"delete_album": "Album löschen",
|
"delete_album": "Album löschen",
|
||||||
"delete_api_key_prompt": "Bist du sicher, dass du diesen API-Schlüssel löschen willst?",
|
"delete_api_key_prompt": "Bist du sicher, dass du diesen API-Schlüssel löschen willst?",
|
||||||
"delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt",
|
"delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt",
|
||||||
"delete_dialog_alert_local": "Diese Inhalte werden vom Gerät gelöscht, bleiben aber auf dem Immich-Server.",
|
"delete_dialog_alert_local": "Diese Inhalte werden vom Gerät gelöscht, bleiben aber auf dem Immich-Server",
|
||||||
"delete_dialog_alert_local_non_backed_up": "Einige Inhalte sind nicht in Immich gesichert und werden dauerhaft vom Gerät gelöscht.",
|
"delete_dialog_alert_local_non_backed_up": "Einige Inhalte sind nicht in Immich gesichert und werden dauerhaft vom Gerät gelöscht",
|
||||||
"delete_dialog_alert_remote": "Diese Inhalte werden dauerhaft vom Immich-Server gelöscht.",
|
"delete_dialog_alert_remote": "Diese Inhalte werden dauerhaft vom Immich-Server gelöscht",
|
||||||
"delete_dialog_ok_force": "Trotzdem löschen",
|
"delete_dialog_ok_force": "Trotzdem löschen",
|
||||||
"delete_dialog_title": "Endgültig löschen",
|
"delete_dialog_title": "Endgültig löschen",
|
||||||
"delete_duplicates_confirmation": "Bist du sicher, dass du diese Duplikate endgültig löschen willst?",
|
"delete_duplicates_confirmation": "Bist du sicher, dass du diese Duplikate endgültig löschen willst?",
|
||||||
@@ -741,7 +747,7 @@
|
|||||||
"deletes_missing_assets": "Löscht Dateien, die auf der Festplatte fehlen",
|
"deletes_missing_assets": "Löscht Dateien, die auf der Festplatte fehlen",
|
||||||
"description": "Beschreibung",
|
"description": "Beschreibung",
|
||||||
"description_input_hint_text": "Beschreibung hinzufügen...",
|
"description_input_hint_text": "Beschreibung hinzufügen...",
|
||||||
"description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log für mehr Details nachsehen.",
|
"description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log für mehr Details nachsehen",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
"direction": "Richtung",
|
"direction": "Richtung",
|
||||||
"disabled": "Deaktiviert",
|
"disabled": "Deaktiviert",
|
||||||
@@ -758,23 +764,23 @@
|
|||||||
"documentation": "Dokumentation",
|
"documentation": "Dokumentation",
|
||||||
"done": "Fertig",
|
"done": "Fertig",
|
||||||
"download": "Herunterladen",
|
"download": "Herunterladen",
|
||||||
"download_canceled": "Download abgebrochen!",
|
"download_canceled": "Download abgebrochen",
|
||||||
"download_complete": "Download vollständig!",
|
"download_complete": "Download vollständig",
|
||||||
"download_enqueue": "Download in die Warteschlange gesetzt!",
|
"download_enqueue": "Download in die Warteschlange gesetzt",
|
||||||
"download_error": "Download fehlerhaft",
|
"download_error": "Download fehlerhaft",
|
||||||
"download_failed": "Download fehlerhaft!",
|
"download_failed": "Download fehlerhaft",
|
||||||
"download_filename": "Datei: {}",
|
"download_filename": "Datei: {filename}",
|
||||||
"download_finished": "Download abgeschlossen",
|
"download_finished": "Download abgeschlossen",
|
||||||
"download_include_embedded_motion_videos": "Eingebettete Videos",
|
"download_include_embedded_motion_videos": "Eingebettete Videos",
|
||||||
"download_include_embedded_motion_videos_description": "Videos, die in Bewegungsfotos eingebettet sind, als separate Datei einfügen",
|
"download_include_embedded_motion_videos_description": "Videos, die in Bewegungsfotos eingebettet sind, als separate Datei einfügen",
|
||||||
"download_notfound": "Download nicht gefunden!",
|
"download_notfound": "Download nicht gefunden",
|
||||||
"download_paused": "Download pausiert!",
|
"download_paused": "Download pausiert",
|
||||||
"download_settings": "Download",
|
"download_settings": "Download",
|
||||||
"download_settings_description": "Einstellungen für das Herunterladen von Dateien verwalten",
|
"download_settings_description": "Einstellungen für das Herunterladen von Dateien verwalten",
|
||||||
"download_started": "Download gestartet",
|
"download_started": "Download gestartet",
|
||||||
"download_sucess": "Download erfolgreich",
|
"download_sucess": "Download erfolgreich",
|
||||||
"download_sucess_android": "Die Datei wurde nach DCIM/Immich heruntergeladen",
|
"download_sucess_android": "Die Datei wurde nach DCIM/Immich heruntergeladen",
|
||||||
"download_waiting_to_retry": "Warte auf erneuten Versuch...",
|
"download_waiting_to_retry": "Warte auf erneuten Versuch",
|
||||||
"downloading": "Herunterladen",
|
"downloading": "Herunterladen",
|
||||||
"downloading_asset_filename": "Datei {filename} wird heruntergeladen",
|
"downloading_asset_filename": "Datei {filename} wird heruntergeladen",
|
||||||
"downloading_media": "Medien werden heruntergeladen",
|
"downloading_media": "Medien werden heruntergeladen",
|
||||||
@@ -807,6 +813,7 @@
|
|||||||
"editor_crop_tool_h2_aspect_ratios": "Seitenverhältnisse",
|
"editor_crop_tool_h2_aspect_ratios": "Seitenverhältnisse",
|
||||||
"editor_crop_tool_h2_rotation": "Drehung",
|
"editor_crop_tool_h2_rotation": "Drehung",
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
|
"email_notifications": "E-Mail Benachrichtigungen",
|
||||||
"empty_folder": "Dieser Ordner ist leer",
|
"empty_folder": "Dieser Ordner ist leer",
|
||||||
"empty_trash": "Papierkorb leeren",
|
"empty_trash": "Papierkorb leeren",
|
||||||
"empty_trash_confirmation": "Bist du sicher, dass du den Papierkorb leeren willst?\nDies entfernt alle Dateien im Papierkorb endgültig aus Immich und kann nicht rückgängig gemacht werden!",
|
"empty_trash_confirmation": "Bist du sicher, dass du den Papierkorb leeren willst?\nDies entfernt alle Dateien im Papierkorb endgültig aus Immich und kann nicht rückgängig gemacht werden!",
|
||||||
@@ -819,7 +826,7 @@
|
|||||||
"error_change_sort_album": "Ändern der Anzeigereihenfolge fehlgeschlagen",
|
"error_change_sort_album": "Ändern der Anzeigereihenfolge fehlgeschlagen",
|
||||||
"error_delete_face": "Fehler beim Löschen des Gesichts",
|
"error_delete_face": "Fehler beim Löschen des Gesichts",
|
||||||
"error_loading_image": "Fehler beim Laden des Bildes",
|
"error_loading_image": "Fehler beim Laden des Bildes",
|
||||||
"error_saving_image": "Fehler: {}",
|
"error_saving_image": "Fehler: {error}",
|
||||||
"error_title": "Fehler - Etwas ist schief gelaufen",
|
"error_title": "Fehler - Etwas ist schief gelaufen",
|
||||||
"errors": {
|
"errors": {
|
||||||
"cannot_navigate_next_asset": "Kann nicht zur nächsten Datei navigieren",
|
"cannot_navigate_next_asset": "Kann nicht zur nächsten Datei navigieren",
|
||||||
@@ -849,10 +856,12 @@
|
|||||||
"failed_to_keep_this_delete_others": "Fehler beim Löschen der anderen Dateien",
|
"failed_to_keep_this_delete_others": "Fehler beim Löschen der anderen Dateien",
|
||||||
"failed_to_load_asset": "Fehler beim Laden der Datei",
|
"failed_to_load_asset": "Fehler beim Laden der Datei",
|
||||||
"failed_to_load_assets": "Fehler beim Laden der Dateien",
|
"failed_to_load_assets": "Fehler beim Laden der Dateien",
|
||||||
|
"failed_to_load_notifications": "Fehler beim Laden der Benachrichtigungen",
|
||||||
"failed_to_load_people": "Fehler beim Laden von Personen",
|
"failed_to_load_people": "Fehler beim Laden von Personen",
|
||||||
"failed_to_remove_product_key": "Fehler beim Entfernen des Produktschlüssels",
|
"failed_to_remove_product_key": "Fehler beim Entfernen des Produktschlüssels",
|
||||||
"failed_to_stack_assets": "Dateien konnten nicht gestapelt werden",
|
"failed_to_stack_assets": "Dateien konnten nicht gestapelt werden",
|
||||||
"failed_to_unstack_assets": "Dateien konnten nicht entstapelt werden",
|
"failed_to_unstack_assets": "Dateien konnten nicht entstapelt werden",
|
||||||
|
"failed_to_update_notification_status": "Benachrichtigungsstatus aktualisieren fehlgeschlagen",
|
||||||
"import_path_already_exists": "Dieser Importpfad existiert bereits.",
|
"import_path_already_exists": "Dieser Importpfad existiert bereits.",
|
||||||
"incorrect_email_or_password": "Ungültige E-Mail oder Passwort",
|
"incorrect_email_or_password": "Ungültige E-Mail oder Passwort",
|
||||||
"paths_validation_failed": "{paths, plural, one {# Pfad konnte} other {# Pfade konnten}} nicht validiert werden",
|
"paths_validation_failed": "{paths, plural, one {# Pfad konnte} other {# Pfade konnten}} nicht validiert werden",
|
||||||
@@ -920,6 +929,7 @@
|
|||||||
"unable_to_remove_reaction": "Reaktion kann nicht entfernt werden",
|
"unable_to_remove_reaction": "Reaktion kann nicht entfernt werden",
|
||||||
"unable_to_repair_items": "Objekte können nicht repariert werden",
|
"unable_to_repair_items": "Objekte können nicht repariert werden",
|
||||||
"unable_to_reset_password": "Passwort kann nicht zurückgesetzt werden",
|
"unable_to_reset_password": "Passwort kann nicht zurückgesetzt werden",
|
||||||
|
"unable_to_reset_pin_code": "Zurücksetzen des PIN Code nicht möglich",
|
||||||
"unable_to_resolve_duplicate": "Duplikate können nicht aufgelöst werden",
|
"unable_to_resolve_duplicate": "Duplikate können nicht aufgelöst werden",
|
||||||
"unable_to_restore_assets": "Dateien konnten nicht wiederhergestellt werden",
|
"unable_to_restore_assets": "Dateien konnten nicht wiederhergestellt werden",
|
||||||
"unable_to_restore_trash": "Papierkorb kann nicht wiederhergestellt werden",
|
"unable_to_restore_trash": "Papierkorb kann nicht wiederhergestellt werden",
|
||||||
@@ -953,10 +963,10 @@
|
|||||||
"exif_bottom_sheet_location": "STANDORT",
|
"exif_bottom_sheet_location": "STANDORT",
|
||||||
"exif_bottom_sheet_people": "PERSONEN",
|
"exif_bottom_sheet_people": "PERSONEN",
|
||||||
"exif_bottom_sheet_person_add_person": "Namen hinzufügen",
|
"exif_bottom_sheet_person_add_person": "Namen hinzufügen",
|
||||||
"exif_bottom_sheet_person_age": "Alter {}",
|
"exif_bottom_sheet_person_age": "Alter {age}",
|
||||||
"exif_bottom_sheet_person_age_months": "Age {} months",
|
"exif_bottom_sheet_person_age_months": "{months} Monate alt",
|
||||||
"exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months",
|
"exif_bottom_sheet_person_age_year_months": "1 Jahr, {months} Monate alt",
|
||||||
"exif_bottom_sheet_person_age_years": "Age {}",
|
"exif_bottom_sheet_person_age_years": "Alter {years}",
|
||||||
"exit_slideshow": "Diashow beenden",
|
"exit_slideshow": "Diashow beenden",
|
||||||
"expand_all": "Alle aufklappen",
|
"expand_all": "Alle aufklappen",
|
||||||
"experimental_settings_new_asset_list_subtitle": "In Arbeit",
|
"experimental_settings_new_asset_list_subtitle": "In Arbeit",
|
||||||
@@ -965,7 +975,7 @@
|
|||||||
"experimental_settings_title": "Experimentell",
|
"experimental_settings_title": "Experimentell",
|
||||||
"expire_after": "Verfällt nach",
|
"expire_after": "Verfällt nach",
|
||||||
"expired": "Verfallen",
|
"expired": "Verfallen",
|
||||||
"expires_date": "Läuft {date} ab",
|
"expires_date": "Läuft am {date} ab",
|
||||||
"explore": "Erkunden",
|
"explore": "Erkunden",
|
||||||
"explorer": "Datei-Explorer",
|
"explorer": "Datei-Explorer",
|
||||||
"export": "Exportieren",
|
"export": "Exportieren",
|
||||||
@@ -992,6 +1002,7 @@
|
|||||||
"filetype": "Dateityp",
|
"filetype": "Dateityp",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"filter_people": "Personen filtern",
|
"filter_people": "Personen filtern",
|
||||||
|
"filter_places": "Orte filtern",
|
||||||
"find_them_fast": "Finde sie schneller mit der Suche nach Namen",
|
"find_them_fast": "Finde sie schneller mit der Suche nach Namen",
|
||||||
"fix_incorrect_match": "Fehlerhafte Übereinstimmung beheben",
|
"fix_incorrect_match": "Fehlerhafte Übereinstimmung beheben",
|
||||||
"folder": "Ordner",
|
"folder": "Ordner",
|
||||||
@@ -1001,7 +1012,7 @@
|
|||||||
"forward": "Vorwärts",
|
"forward": "Vorwärts",
|
||||||
"general": "Allgemein",
|
"general": "Allgemein",
|
||||||
"get_help": "Hilfe erhalten",
|
"get_help": "Hilfe erhalten",
|
||||||
"get_wifiname_error": "WLAN-Name konnte nicht ermittelt werden. Vergewissere dich, dass die erforderlichen Berechtigungen erteilt wurden und du mit einem WLAN-Netzwerk verbunden bist.\n",
|
"get_wifiname_error": "WLAN-Name konnte nicht ermittelt werden. Vergewissere dich, dass die erforderlichen Berechtigungen erteilt wurden und du mit einem WLAN-Netzwerk verbunden bist",
|
||||||
"getting_started": "Erste Schritte",
|
"getting_started": "Erste Schritte",
|
||||||
"go_back": "Zurück",
|
"go_back": "Zurück",
|
||||||
"go_to_folder": "Gehe zu Ordner",
|
"go_to_folder": "Gehe zu Ordner",
|
||||||
@@ -1030,23 +1041,24 @@
|
|||||||
"hide_person": "Person verbergen",
|
"hide_person": "Person verbergen",
|
||||||
"hide_unnamed_people": "Unbenannte Personen verbergen",
|
"hide_unnamed_people": "Unbenannte Personen verbergen",
|
||||||
"home_page_add_to_album_conflicts": "{added} Elemente zu {album} hinzugefügt. {failed} Elemente sind bereits vorhanden.",
|
"home_page_add_to_album_conflicts": "{added} Elemente zu {album} hinzugefügt. {failed} Elemente sind bereits vorhanden.",
|
||||||
"home_page_add_to_album_err_local": "Es können lokale Elemente noch nicht zu Alben hinzugefügt werden, überspringen...",
|
"home_page_add_to_album_err_local": "Es können lokale Elemente noch nicht zu Alben hinzugefügt werden, überspringen",
|
||||||
"home_page_add_to_album_success": "{added} Elemente zu {album} hinzugefügt.",
|
"home_page_add_to_album_success": "{added} Elemente zu {album} hinzugefügt.",
|
||||||
"home_page_album_err_partner": "Inhalte von Partnern können derzeit nicht zu Alben hinzugefügt werden!",
|
"home_page_album_err_partner": "Inhalte von Partnern können derzeit nicht zu Alben hinzugefügt werden",
|
||||||
"home_page_archive_err_local": "Kann lokale Elemente nicht archvieren, überspringen...",
|
"home_page_archive_err_local": "Kann lokale Elemente nicht archvieren, überspringen",
|
||||||
"home_page_archive_err_partner": "Inhalte von Partnern können nicht archiviert werden!",
|
"home_page_archive_err_partner": "Inhalte von Partnern können nicht archiviert werden",
|
||||||
"home_page_building_timeline": "Zeitachse wird erstellt.",
|
"home_page_building_timeline": "Zeitachse wird erstellt",
|
||||||
"home_page_delete_err_partner": "Inhalte von Partnern können nicht gelöscht werden!",
|
"home_page_delete_err_partner": "Inhalte von Partnern können nicht gelöscht werden, überspringe",
|
||||||
"home_page_delete_remote_err_local": "Lokale Inhalte in der Auswahl, überspringen...",
|
"home_page_delete_remote_err_local": "Lokale Inhalte in der Auswahl, überspringen",
|
||||||
"home_page_favorite_err_local": "Kann lokale Elemente noch nicht favorisieren, überspringen...",
|
"home_page_favorite_err_local": "Kann lokale Elemente noch nicht favorisieren, überspringen",
|
||||||
"home_page_favorite_err_partner": "Inhalte von Partnern können nicht favorisiert werden!",
|
"home_page_favorite_err_partner": "Inhalte von Partnern können nicht favorisiert werden, überspringe",
|
||||||
"home_page_first_time_notice": "Wenn dies das erste Mal ist dass Du Immich nutzt, stelle bitte sicher, dass mindestens ein Album zur Sicherung ausgewählt ist, sodass die Zeitachse mit Fotos und Videos gefüllt werden kann.",
|
"home_page_first_time_notice": "Wenn dies das erste Mal ist dass Du Immich nutzt, stelle bitte sicher, dass mindestens ein Album zur Sicherung ausgewählt ist, sodass die Zeitachse mit Fotos und Videos gefüllt werden kann",
|
||||||
"home_page_share_err_local": "Lokale Inhalte können nicht per Link geteilt werden, überspringe",
|
"home_page_share_err_local": "Lokale Inhalte können nicht per Link geteilt werden, überspringe",
|
||||||
"home_page_upload_err_limit": "Es können max. 30 Elemente gleichzeitig hochgeladen werden, überspringen...",
|
"home_page_upload_err_limit": "Es können max. 30 Elemente gleichzeitig hochgeladen werden, überspringen",
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"hour": "Stunde",
|
"hour": "Stunde",
|
||||||
|
"id": "ID",
|
||||||
"ignore_icloud_photos": "iCloud Fotos ignorieren",
|
"ignore_icloud_photos": "iCloud Fotos ignorieren",
|
||||||
"ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen",
|
"ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen",
|
||||||
"image": "Bild",
|
"image": "Bild",
|
||||||
"image_alt_text_date": "{isVideo, select, true {Video} other {Bild}} aufgenommen am {date}",
|
"image_alt_text_date": "{isVideo, select, true {Video} other {Bild}} aufgenommen am {date}",
|
||||||
"image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Bild}} aufgenommen mit {person1} am {date}",
|
"image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Bild}} aufgenommen mit {person1} am {date}",
|
||||||
@@ -1080,7 +1092,7 @@
|
|||||||
"night_at_midnight": "Täglich um Mitternacht",
|
"night_at_midnight": "Täglich um Mitternacht",
|
||||||
"night_at_twoam": "Täglich nachts um 2:00 Uhr"
|
"night_at_twoam": "Täglich nachts um 2:00 Uhr"
|
||||||
},
|
},
|
||||||
"invalid_date": "Ungültiges Datum ",
|
"invalid_date": "Ungültiges Datum",
|
||||||
"invalid_date_format": "Ungültiges Datumsformat",
|
"invalid_date_format": "Ungültiges Datumsformat",
|
||||||
"invite_people": "Personen einladen",
|
"invite_people": "Personen einladen",
|
||||||
"invite_to_album": "Zum Album einladen",
|
"invite_to_album": "Zum Album einladen",
|
||||||
@@ -1120,7 +1132,7 @@
|
|||||||
"local_network": "Lokales Netzwerk",
|
"local_network": "Lokales Netzwerk",
|
||||||
"local_network_sheet_info": "Die App stellt über diese URL eine Verbindung zum Server her, wenn sie das angegebene WLAN-Netzwerk verwendet",
|
"local_network_sheet_info": "Die App stellt über diese URL eine Verbindung zum Server her, wenn sie das angegebene WLAN-Netzwerk verwendet",
|
||||||
"location_permission": "Standort Genehmigung",
|
"location_permission": "Standort Genehmigung",
|
||||||
"location_permission_content": "Um die automatische Umschaltfunktion nutzen zu können, benötigt Immich eine genaue Standortberechtigung, damit es den Namen des aktuellen WLAN-Netzwerks ermitteln kann",
|
"location_permission_content": "Um die automatische Umschaltfunktion nutzen zu können, benötigt Immich genaue Standortberechtigung, damit es den Namen des aktuellen WLAN-Netzwerks ermitteln kann",
|
||||||
"location_picker_choose_on_map": "Auf der Karte auswählen",
|
"location_picker_choose_on_map": "Auf der Karte auswählen",
|
||||||
"location_picker_latitude_error": "Gültigen Breitengrad eingeben",
|
"location_picker_latitude_error": "Gültigen Breitengrad eingeben",
|
||||||
"location_picker_latitude_hint": "Breitengrad eingeben",
|
"location_picker_latitude_hint": "Breitengrad eingeben",
|
||||||
@@ -1143,7 +1155,7 @@
|
|||||||
"login_form_err_leading_whitespace": "Leerzeichen am Anfang",
|
"login_form_err_leading_whitespace": "Leerzeichen am Anfang",
|
||||||
"login_form_err_trailing_whitespace": "Leerzeichen am Ende",
|
"login_form_err_trailing_whitespace": "Leerzeichen am Ende",
|
||||||
"login_form_failed_get_oauth_server_config": "Fehler beim Login per OAuth, bitte Server-URL überprüfen",
|
"login_form_failed_get_oauth_server_config": "Fehler beim Login per OAuth, bitte Server-URL überprüfen",
|
||||||
"login_form_failed_get_oauth_server_disable": "Die OAuth-Funktion ist auf diesem Server nicht verfügbar.",
|
"login_form_failed_get_oauth_server_disable": "Die OAuth-Funktion ist auf diesem Server nicht verfügbar",
|
||||||
"login_form_failed_login": "Fehler beim Login, bitte überprüfe die Server-URL, deine E-Mail oder das Passwort",
|
"login_form_failed_login": "Fehler beim Login, bitte überprüfe die Server-URL, deine E-Mail oder das Passwort",
|
||||||
"login_form_handshake_exception": "Fehler beim Verbindungsaufbau mit dem Server. Falls du ein selbstsigniertes Zertifikat verwendest, aktiviere die Unterstützung dafür in den Einstellungen.",
|
"login_form_handshake_exception": "Fehler beim Verbindungsaufbau mit dem Server. Falls du ein selbstsigniertes Zertifikat verwendest, aktiviere die Unterstützung dafür in den Einstellungen.",
|
||||||
"login_form_password_hint": "Passwort",
|
"login_form_password_hint": "Passwort",
|
||||||
@@ -1151,8 +1163,8 @@
|
|||||||
"login_form_server_empty": "Serveradresse eingeben.",
|
"login_form_server_empty": "Serveradresse eingeben.",
|
||||||
"login_form_server_error": "Es Konnte sich nicht mit dem Server verbunden werden.",
|
"login_form_server_error": "Es Konnte sich nicht mit dem Server verbunden werden.",
|
||||||
"login_has_been_disabled": "Die Anmeldung wurde deaktiviert.",
|
"login_has_been_disabled": "Die Anmeldung wurde deaktiviert.",
|
||||||
"login_password_changed_error": "Fehler beim Passwort ändern!",
|
"login_password_changed_error": "Fehler beim Ändern deines Passwort",
|
||||||
"login_password_changed_success": "Passwort erfolgreich geändert.",
|
"login_password_changed_success": "Passwort erfolgreich geändert",
|
||||||
"logout_all_device_confirmation": "Bist du sicher, dass du alle Geräte abmelden willst?",
|
"logout_all_device_confirmation": "Bist du sicher, dass du alle Geräte abmelden willst?",
|
||||||
"logout_this_device_confirmation": "Bist du sicher, dass du dieses Gerät abmelden willst?",
|
"logout_this_device_confirmation": "Bist du sicher, dass du dieses Gerät abmelden willst?",
|
||||||
"longitude": "Längengrad",
|
"longitude": "Längengrad",
|
||||||
@@ -1170,9 +1182,9 @@
|
|||||||
"manage_your_devices": "Deine eingeloggten Geräte verwalten",
|
"manage_your_devices": "Deine eingeloggten Geräte verwalten",
|
||||||
"manage_your_oauth_connection": "Deine OAuth-Verknüpfung verwalten",
|
"manage_your_oauth_connection": "Deine OAuth-Verknüpfung verwalten",
|
||||||
"map": "Karte",
|
"map": "Karte",
|
||||||
"map_assets_in_bound": "{} Foto",
|
"map_assets_in_bound": "{count} Foto",
|
||||||
"map_assets_in_bounds": "{} Fotos",
|
"map_assets_in_bounds": "{count} Fotos",
|
||||||
"map_cannot_get_user_location": "Standort konnte nicht ermittelt werden!",
|
"map_cannot_get_user_location": "Standort konnte nicht ermittelt werden",
|
||||||
"map_location_dialog_yes": "Ja",
|
"map_location_dialog_yes": "Ja",
|
||||||
"map_location_picker_page_use_location": "Aufnahmeort verwenden",
|
"map_location_picker_page_use_location": "Aufnahmeort verwenden",
|
||||||
"map_location_service_disabled_content": "Ortungsdienste müssen aktiviert sein, um Inhalte am aktuellen Standort anzuzeigen. Willst du die Ortungsdienste jetzt aktivieren?",
|
"map_location_service_disabled_content": "Ortungsdienste müssen aktiviert sein, um Inhalte am aktuellen Standort anzuzeigen. Willst du die Ortungsdienste jetzt aktivieren?",
|
||||||
@@ -1181,29 +1193,32 @@
|
|||||||
"map_marker_with_image": "Kartenmarkierung mit Bild",
|
"map_marker_with_image": "Kartenmarkierung mit Bild",
|
||||||
"map_no_assets_in_bounds": "Keine Fotos in dieser Gegend",
|
"map_no_assets_in_bounds": "Keine Fotos in dieser Gegend",
|
||||||
"map_no_location_permission_content": "Ortungsdienste müssen aktiviert sein, um Inhalte am aktuellen Standort anzuzeigen. Willst du die Ortungsdienste jetzt aktivieren?",
|
"map_no_location_permission_content": "Ortungsdienste müssen aktiviert sein, um Inhalte am aktuellen Standort anzuzeigen. Willst du die Ortungsdienste jetzt aktivieren?",
|
||||||
"map_no_location_permission_title": "Kein Zugriff auf den Standort!",
|
"map_no_location_permission_title": "Kein Zugriff auf den Standort",
|
||||||
"map_settings": "Karteneinstellungen",
|
"map_settings": "Karteneinstellungen",
|
||||||
"map_settings_dark_mode": "Dunkler Modus",
|
"map_settings_dark_mode": "Dunkler Modus",
|
||||||
"map_settings_date_range_option_day": "Letzte 24 Stunden",
|
"map_settings_date_range_option_day": "Letzte 24 Stunden",
|
||||||
"map_settings_date_range_option_days": "Letzte {} Tage",
|
"map_settings_date_range_option_days": "Letzten {days} Tage",
|
||||||
"map_settings_date_range_option_year": "Letztes Jahr",
|
"map_settings_date_range_option_year": "Letztes Jahr",
|
||||||
"map_settings_date_range_option_years": "Letzte {} Jahre",
|
"map_settings_date_range_option_years": "Letzten {years} Jahre",
|
||||||
"map_settings_dialog_title": "Karteneinstellungen",
|
"map_settings_dialog_title": "Karteneinstellungen",
|
||||||
"map_settings_include_show_archived": "Archivierte anzeigen",
|
"map_settings_include_show_archived": "Archivierte anzeigen",
|
||||||
"map_settings_include_show_partners": "Partner einbeziehen",
|
"map_settings_include_show_partners": "Partner einbeziehen",
|
||||||
"map_settings_only_show_favorites": "Nur Favoriten anzeigen",
|
"map_settings_only_show_favorites": "Nur Favoriten anzeigen",
|
||||||
"map_settings_theme_settings": "Karten Design",
|
"map_settings_theme_settings": "Karten Design",
|
||||||
"map_zoom_to_see_photos": "Ansicht verkleinern um Fotos zu sehen",
|
"map_zoom_to_see_photos": "Ansicht verkleinern um Fotos zu sehen",
|
||||||
|
"mark_all_as_read": "Alle als gelesen markieren",
|
||||||
|
"mark_as_read": "Als gelesen markieren",
|
||||||
|
"marked_all_as_read": "Alle als gelesen markiert",
|
||||||
"matches": "Treffer",
|
"matches": "Treffer",
|
||||||
"media_type": "Medientyp",
|
"media_type": "Medientyp",
|
||||||
"memories": "Erinnerungen",
|
"memories": "Erinnerungen",
|
||||||
"memories_all_caught_up": "Alles aufgeholt",
|
"memories_all_caught_up": "Alles aufgeholt",
|
||||||
"memories_check_back_tomorrow": "Schau morgen wieder vorbei für weitere Erinnerungen!",
|
"memories_check_back_tomorrow": "Schau morgen wieder vorbei für weitere Erinnerungen",
|
||||||
"memories_setting_description": "Verwalte, was du in deinen Erinnerungen siehst",
|
"memories_setting_description": "Verwalte, was du in deinen Erinnerungen siehst",
|
||||||
"memories_start_over": "Erneut beginnen",
|
"memories_start_over": "Erneut beginnen",
|
||||||
"memories_swipe_to_close": "Nach oben Wischen zum schließen",
|
"memories_swipe_to_close": "Nach oben Wischen zum schließen",
|
||||||
"memories_year_ago": "ein Jahr her",
|
"memories_year_ago": "ein Jahr her",
|
||||||
"memories_years_ago": "{} Jahre her",
|
"memories_years_ago": "Vor {years} Jahren",
|
||||||
"memory": "Erinnerung",
|
"memory": "Erinnerung",
|
||||||
"memory_lane_title": "Foto-Erinnerungen {title}",
|
"memory_lane_title": "Foto-Erinnerungen {title}",
|
||||||
"menu": "Menü",
|
"menu": "Menü",
|
||||||
@@ -1220,9 +1235,11 @@
|
|||||||
"month": "Monat",
|
"month": "Monat",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
"more": "Mehr",
|
"more": "Mehr",
|
||||||
|
"moved_to_archive": "{count, plural, one {# Datei} other {# Dateien}} archiviert",
|
||||||
|
"moved_to_library": "{count, plural, one {# Datei} other {# Dateien}} in die Bibliothek verschoben",
|
||||||
"moved_to_trash": "In den Papierkorb verschoben",
|
"moved_to_trash": "In den Papierkorb verschoben",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Das Datum und die Uhrzeit von schreibgeschützten Inhalten kann nicht verändert werden, überspringen...",
|
"multiselect_grid_edit_date_time_err_read_only": "Das Datum und die Uhrzeit von schreibgeschützten Inhalten kann nicht verändert werden, überspringen",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Der Aufnahmeort von schreibgeschützten Inhalten kann nicht verändert werden, überspringen...",
|
"multiselect_grid_edit_gps_err_read_only": "Der Aufnahmeort von schreibgeschützten Inhalten kann nicht verändert werden, überspringen",
|
||||||
"mute_memories": "Erinnerungen stumm schalten",
|
"mute_memories": "Erinnerungen stumm schalten",
|
||||||
"my_albums": "Meine Alben",
|
"my_albums": "Meine Alben",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
@@ -1234,6 +1251,7 @@
|
|||||||
"new_api_key": "Neuer API-Schlüssel",
|
"new_api_key": "Neuer API-Schlüssel",
|
||||||
"new_password": "Neues Passwort",
|
"new_password": "Neues Passwort",
|
||||||
"new_person": "Neue Person",
|
"new_person": "Neue Person",
|
||||||
|
"new_pin_code": "Neuer PIN Code",
|
||||||
"new_user_created": "Neuer Benutzer wurde erstellt",
|
"new_user_created": "Neuer Benutzer wurde erstellt",
|
||||||
"new_version_available": "NEUE VERSION VERFÜGBAR",
|
"new_version_available": "NEUE VERSION VERFÜGBAR",
|
||||||
"newest_first": "Neueste zuerst",
|
"newest_first": "Neueste zuerst",
|
||||||
@@ -1252,6 +1270,8 @@
|
|||||||
"no_favorites_message": "Füge Favoriten hinzu, um deine besten Bilder und Videos schnell zu finden",
|
"no_favorites_message": "Füge Favoriten hinzu, um deine besten Bilder und Videos schnell zu finden",
|
||||||
"no_libraries_message": "Eine externe Bibliothek erstellen, um deine Fotos und Videos anzusehen",
|
"no_libraries_message": "Eine externe Bibliothek erstellen, um deine Fotos und Videos anzusehen",
|
||||||
"no_name": "Kein Name",
|
"no_name": "Kein Name",
|
||||||
|
"no_notifications": "Keine Benachrichtigungen",
|
||||||
|
"no_people_found": "Keine passenden Personen gefunden",
|
||||||
"no_places": "Keine Orte",
|
"no_places": "Keine Orte",
|
||||||
"no_results": "Keine Ergebnisse",
|
"no_results": "Keine Ergebnisse",
|
||||||
"no_results_description": "Versuche es mit einem Synonym oder einem allgemeineren Stichwort",
|
"no_results_description": "Versuche es mit einem Synonym oder einem allgemeineren Stichwort",
|
||||||
@@ -1260,8 +1280,8 @@
|
|||||||
"not_selected": "Nicht ausgewählt",
|
"not_selected": "Nicht ausgewählt",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den",
|
"note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den",
|
||||||
"notes": "Notizen",
|
"notes": "Notizen",
|
||||||
"notification_permission_dialog_content": "Um Benachrichtigungen zu aktivieren, navigiere zu Einstellungen und klicke \"Erlauben\"",
|
"notification_permission_dialog_content": "Um Benachrichtigungen zu aktivieren, navigiere zu Einstellungen und klicke \"Erlauben\".",
|
||||||
"notification_permission_list_tile_content": "Erlaube Berechtigung für Benachrichtigungen",
|
"notification_permission_list_tile_content": "Erlaube Berechtigung für Benachrichtigungen.",
|
||||||
"notification_permission_list_tile_enable_button": "Aktiviere Benachrichtigungen",
|
"notification_permission_list_tile_enable_button": "Aktiviere Benachrichtigungen",
|
||||||
"notification_permission_list_tile_title": "Benachrichtigungs-Berechtigung",
|
"notification_permission_list_tile_title": "Benachrichtigungs-Berechtigung",
|
||||||
"notification_toggle_setting_description": "E-Mail-Benachrichtigungen aktivieren",
|
"notification_toggle_setting_description": "E-Mail-Benachrichtigungen aktivieren",
|
||||||
@@ -1282,6 +1302,7 @@
|
|||||||
"onboarding_welcome_user": "Willkommen, {user}",
|
"onboarding_welcome_user": "Willkommen, {user}",
|
||||||
"online": "Online",
|
"online": "Online",
|
||||||
"only_favorites": "Nur Favoriten",
|
"only_favorites": "Nur Favoriten",
|
||||||
|
"open": "Öffnen",
|
||||||
"open_in_map_view": "In Kartenansicht öffnen",
|
"open_in_map_view": "In Kartenansicht öffnen",
|
||||||
"open_in_openstreetmap": "In OpenStreetMap öffnen",
|
"open_in_openstreetmap": "In OpenStreetMap öffnen",
|
||||||
"open_the_search_filters": "Die Suchfilter öffnen",
|
"open_the_search_filters": "Die Suchfilter öffnen",
|
||||||
@@ -1305,7 +1326,7 @@
|
|||||||
"partner_page_partner_add_failed": "Fehler beim Partner hinzufügen",
|
"partner_page_partner_add_failed": "Fehler beim Partner hinzufügen",
|
||||||
"partner_page_select_partner": "Partner auswählen",
|
"partner_page_select_partner": "Partner auswählen",
|
||||||
"partner_page_shared_to_title": "Geteilt mit",
|
"partner_page_shared_to_title": "Geteilt mit",
|
||||||
"partner_page_stop_sharing_content": "{} wird nicht mehr auf deine Fotos zugreifen können.",
|
"partner_page_stop_sharing_content": "{partner} wird nicht mehr auf deine Fotos zugreifen können.",
|
||||||
"partner_sharing": "Partner-Sharing",
|
"partner_sharing": "Partner-Sharing",
|
||||||
"partners": "Partner",
|
"partners": "Partner",
|
||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
@@ -1351,6 +1372,9 @@
|
|||||||
"photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}",
|
"photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}",
|
||||||
"photos_from_previous_years": "Fotos von vorherigen Jahren",
|
"photos_from_previous_years": "Fotos von vorherigen Jahren",
|
||||||
"pick_a_location": "Wähle einen Ort",
|
"pick_a_location": "Wähle einen Ort",
|
||||||
|
"pin_code_changed_successfully": "PIN Code erfolgreich geändert",
|
||||||
|
"pin_code_reset_successfully": "PIN Code erfolgreich zurückgesetzt",
|
||||||
|
"pin_code_setup_successfully": "PIN Code erfolgreich festgelegt",
|
||||||
"place": "Ort",
|
"place": "Ort",
|
||||||
"places": "Orte",
|
"places": "Orte",
|
||||||
"places_count": "{count, plural, one {{count, number} Ort} other {{count, number} Orte}}",
|
"places_count": "{count, plural, one {{count, number} Ort} other {{count, number} Orte}}",
|
||||||
@@ -1368,10 +1392,11 @@
|
|||||||
"previous_or_next_photo": "Vorheriges oder nächstes Foto",
|
"previous_or_next_photo": "Vorheriges oder nächstes Foto",
|
||||||
"primary": "Primär",
|
"primary": "Primär",
|
||||||
"privacy": "Privatsphäre",
|
"privacy": "Privatsphäre",
|
||||||
|
"profile": "Profil",
|
||||||
"profile_drawer_app_logs": "Logs",
|
"profile_drawer_app_logs": "Logs",
|
||||||
"profile_drawer_client_out_of_date_major": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Major-Version.",
|
"profile_drawer_client_out_of_date_major": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Major-Version.",
|
||||||
"profile_drawer_client_out_of_date_minor": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.",
|
"profile_drawer_client_out_of_date_minor": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.",
|
||||||
"profile_drawer_client_server_up_to_date": "Die App-Version / Server-Version sind aktuell.",
|
"profile_drawer_client_server_up_to_date": "Die App- und Server-Versionen sind aktuell",
|
||||||
"profile_drawer_github": "GitHub",
|
"profile_drawer_github": "GitHub",
|
||||||
"profile_drawer_server_out_of_date_major": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Major-Version.",
|
"profile_drawer_server_out_of_date_major": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Major-Version.",
|
||||||
"profile_drawer_server_out_of_date_minor": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.",
|
"profile_drawer_server_out_of_date_minor": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.",
|
||||||
@@ -1381,7 +1406,7 @@
|
|||||||
"public_share": "Öffentliche Freigabe",
|
"public_share": "Öffentliche Freigabe",
|
||||||
"purchase_account_info": "Unterstützer",
|
"purchase_account_info": "Unterstützer",
|
||||||
"purchase_activated_subtitle": "Danke für die Unterstützung von Immich und Open-Source Software",
|
"purchase_activated_subtitle": "Danke für die Unterstützung von Immich und Open-Source Software",
|
||||||
"purchase_activated_time": "Aktiviert am {date, date}",
|
"purchase_activated_time": "Aktiviert am {date}",
|
||||||
"purchase_activated_title": "Dein Schlüssel wurde erfolgreich aktiviert",
|
"purchase_activated_title": "Dein Schlüssel wurde erfolgreich aktiviert",
|
||||||
"purchase_button_activate": "Aktivieren",
|
"purchase_button_activate": "Aktivieren",
|
||||||
"purchase_button_buy": "Kaufen",
|
"purchase_button_buy": "Kaufen",
|
||||||
@@ -1426,6 +1451,8 @@
|
|||||||
"recent_searches": "Letzte Suchen",
|
"recent_searches": "Letzte Suchen",
|
||||||
"recently_added": "Kürzlich hinzugefügt",
|
"recently_added": "Kürzlich hinzugefügt",
|
||||||
"recently_added_page_title": "Zuletzt hinzugefügt",
|
"recently_added_page_title": "Zuletzt hinzugefügt",
|
||||||
|
"recently_taken": "Kürzlich aufgenommen",
|
||||||
|
"recently_taken_page_title": "Kürzlich aufgenommen",
|
||||||
"refresh": "Aktualisieren",
|
"refresh": "Aktualisieren",
|
||||||
"refresh_encoded_videos": "Kodierte Videos aktualisieren",
|
"refresh_encoded_videos": "Kodierte Videos aktualisieren",
|
||||||
"refresh_faces": "Gesichter aktualisieren",
|
"refresh_faces": "Gesichter aktualisieren",
|
||||||
@@ -1468,6 +1495,7 @@
|
|||||||
"reset": "Zurücksetzen",
|
"reset": "Zurücksetzen",
|
||||||
"reset_password": "Passwort zurücksetzen",
|
"reset_password": "Passwort zurücksetzen",
|
||||||
"reset_people_visibility": "Sichtbarkeit von Personen zurücksetzen",
|
"reset_people_visibility": "Sichtbarkeit von Personen zurücksetzen",
|
||||||
|
"reset_pin_code": "PIN Code zurücksetzen",
|
||||||
"reset_to_default": "Auf Standard zurücksetzen",
|
"reset_to_default": "Auf Standard zurücksetzen",
|
||||||
"resolve_duplicates": "Duplikate entfernen",
|
"resolve_duplicates": "Duplikate entfernen",
|
||||||
"resolved_all_duplicates": "Alle Duplikate aufgelöst",
|
"resolved_all_duplicates": "Alle Duplikate aufgelöst",
|
||||||
@@ -1504,7 +1532,7 @@
|
|||||||
"search_city": "Suche nach Stadt...",
|
"search_city": "Suche nach Stadt...",
|
||||||
"search_country": "Suche nach Land...",
|
"search_country": "Suche nach Land...",
|
||||||
"search_filter_apply": "Filter anwenden",
|
"search_filter_apply": "Filter anwenden",
|
||||||
"search_filter_camera_title": "Kameratyp auswählen ",
|
"search_filter_camera_title": "Kameratyp auswählen",
|
||||||
"search_filter_date": "Datum",
|
"search_filter_date": "Datum",
|
||||||
"search_filter_date_interval": "{start} bis {end}",
|
"search_filter_date_interval": "{start} bis {end}",
|
||||||
"search_filter_date_title": "Wähle einen Zeitraum",
|
"search_filter_date_title": "Wähle einen Zeitraum",
|
||||||
@@ -1512,10 +1540,10 @@
|
|||||||
"search_filter_display_options": "Anzeigeeinstellungen",
|
"search_filter_display_options": "Anzeigeeinstellungen",
|
||||||
"search_filter_filename": "Suche nach Dateiname",
|
"search_filter_filename": "Suche nach Dateiname",
|
||||||
"search_filter_location": "Ort",
|
"search_filter_location": "Ort",
|
||||||
"search_filter_location_title": "Ort auswählen ",
|
"search_filter_location_title": "Ort auswählen",
|
||||||
"search_filter_media_type": "Medientyp",
|
"search_filter_media_type": "Medientyp",
|
||||||
"search_filter_media_type_title": "Medientyp auswählen ",
|
"search_filter_media_type_title": "Medientyp auswählen",
|
||||||
"search_filter_people_title": "Personen auswählen ",
|
"search_filter_people_title": "Personen auswählen",
|
||||||
"search_for": "Suche nach",
|
"search_for": "Suche nach",
|
||||||
"search_for_existing_person": "Suche nach vorhandener Person",
|
"search_for_existing_person": "Suche nach vorhandener Person",
|
||||||
"search_no_more_result": "Keine weiteren Ergebnisse",
|
"search_no_more_result": "Keine weiteren Ergebnisse",
|
||||||
@@ -1560,6 +1588,7 @@
|
|||||||
"select_keep_all": "Alle behalten",
|
"select_keep_all": "Alle behalten",
|
||||||
"select_library_owner": "Bibliotheksbesitzer auswählen",
|
"select_library_owner": "Bibliotheksbesitzer auswählen",
|
||||||
"select_new_face": "Neues Gesicht auswählen",
|
"select_new_face": "Neues Gesicht auswählen",
|
||||||
|
"select_person_to_tag": "Wählen Sie eine Person zum Markieren aus",
|
||||||
"select_photos": "Fotos auswählen",
|
"select_photos": "Fotos auswählen",
|
||||||
"select_trash_all": "Alle löschen",
|
"select_trash_all": "Alle löschen",
|
||||||
"select_user_for_sharing_page_err_album": "Album konnte nicht erstellt werden",
|
"select_user_for_sharing_page_err_album": "Album konnte nicht erstellt werden",
|
||||||
@@ -1590,13 +1619,13 @@
|
|||||||
"setting_languages_apply": "Anwenden",
|
"setting_languages_apply": "Anwenden",
|
||||||
"setting_languages_subtitle": "App-Sprache ändern",
|
"setting_languages_subtitle": "App-Sprache ändern",
|
||||||
"setting_languages_title": "Sprachen",
|
"setting_languages_title": "Sprachen",
|
||||||
"setting_notifications_notify_failures_grace_period": "Benachrichtigung bei Fehler/n in der Hintergrundsicherung: {}",
|
"setting_notifications_notify_failures_grace_period": "Benachrichtigung bei Fehler(n) in der Hintergrundsicherung: {duration}",
|
||||||
"setting_notifications_notify_hours": "{} Stunden",
|
"setting_notifications_notify_hours": "{count} Stunden",
|
||||||
"setting_notifications_notify_immediately": "sofort",
|
"setting_notifications_notify_immediately": "sofort",
|
||||||
"setting_notifications_notify_minutes": "{} Minuten",
|
"setting_notifications_notify_minutes": "{count} Minuten",
|
||||||
"setting_notifications_notify_never": "niemals",
|
"setting_notifications_notify_never": "niemals",
|
||||||
"setting_notifications_notify_seconds": "{} Sekunden",
|
"setting_notifications_notify_seconds": "{count} Sekunden",
|
||||||
"setting_notifications_single_progress_subtitle": "Detaillierter Upload-Fortschritt für jedes Element.",
|
"setting_notifications_single_progress_subtitle": "Detaillierter Upload-Fortschritt für jedes Element",
|
||||||
"setting_notifications_single_progress_title": "Zeige den detaillierten Fortschritt der Hintergrundsicherung",
|
"setting_notifications_single_progress_title": "Zeige den detaillierten Fortschritt der Hintergrundsicherung",
|
||||||
"setting_notifications_subtitle": "Benachrichtigungen anpassen",
|
"setting_notifications_subtitle": "Benachrichtigungen anpassen",
|
||||||
"setting_notifications_total_progress_subtitle": "Gesamter Upload-Fortschritt (abgeschlossen/Anzahl Elemente)",
|
"setting_notifications_total_progress_subtitle": "Gesamter Upload-Fortschritt (abgeschlossen/Anzahl Elemente)",
|
||||||
@@ -1605,14 +1634,15 @@
|
|||||||
"setting_video_viewer_original_video_subtitle": "Beim Streaming eines Videos vom Server wird das Original abgespielt, auch wenn eine Transkodierung verfügbar ist. Kann zu Pufferung führen. Lokal verfügbare Videos werden unabhängig von dieser Einstellung in Originalqualität wiedergegeben.",
|
"setting_video_viewer_original_video_subtitle": "Beim Streaming eines Videos vom Server wird das Original abgespielt, auch wenn eine Transkodierung verfügbar ist. Kann zu Pufferung führen. Lokal verfügbare Videos werden unabhängig von dieser Einstellung in Originalqualität wiedergegeben.",
|
||||||
"setting_video_viewer_original_video_title": "Originalvideo erzwingen",
|
"setting_video_viewer_original_video_title": "Originalvideo erzwingen",
|
||||||
"settings": "Einstellungen",
|
"settings": "Einstellungen",
|
||||||
"settings_require_restart": "Bitte starte Immich neu, um diese Einstellung anzuwenden.",
|
"settings_require_restart": "Bitte starte Immich neu, um diese Einstellung anzuwenden",
|
||||||
"settings_saved": "Einstellungen gespeichert",
|
"settings_saved": "Einstellungen gespeichert",
|
||||||
|
"setup_pin_code": "Einen PIN Code festlegen",
|
||||||
"share": "Teilen",
|
"share": "Teilen",
|
||||||
"share_add_photos": "Fotos hinzufügen",
|
"share_add_photos": "Fotos hinzufügen",
|
||||||
"share_assets_selected": "{} ausgewählt",
|
"share_assets_selected": "{count} ausgewählt",
|
||||||
"share_dialog_preparing": "Vorbereiten...",
|
"share_dialog_preparing": "Vorbereiten...",
|
||||||
"shared": "Geteilt",
|
"shared": "Geteilt",
|
||||||
"shared_album_activities_input_disable": "Kommentare sind deaktiviert.",
|
"shared_album_activities_input_disable": "Kommentare sind deaktiviert",
|
||||||
"shared_album_activity_remove_content": "Möchtest du diese Aktivität entfernen?",
|
"shared_album_activity_remove_content": "Möchtest du diese Aktivität entfernen?",
|
||||||
"shared_album_activity_remove_title": "Aktivität entfernen",
|
"shared_album_activity_remove_title": "Aktivität entfernen",
|
||||||
"shared_album_section_people_action_error": "Fehler beim Verlassen oder Entfernen aus dem Album",
|
"shared_album_section_people_action_error": "Fehler beim Verlassen oder Entfernen aus dem Album",
|
||||||
@@ -1623,32 +1653,32 @@
|
|||||||
"shared_by_user": "Von {user} geteilt",
|
"shared_by_user": "Von {user} geteilt",
|
||||||
"shared_by_you": "Von dir geteilt",
|
"shared_by_you": "Von dir geteilt",
|
||||||
"shared_from_partner": "Fotos von {partner}",
|
"shared_from_partner": "Fotos von {partner}",
|
||||||
"shared_intent_upload_button_progress_text": "{} / {} hochgeladen",
|
"shared_intent_upload_button_progress_text": "{current} / {total} hochgeladen",
|
||||||
"shared_link_app_bar_title": "Geteilte Links",
|
"shared_link_app_bar_title": "Geteilte Links",
|
||||||
"shared_link_clipboard_copied_massage": "Link kopiert",
|
"shared_link_clipboard_copied_massage": "Link kopiert",
|
||||||
"shared_link_clipboard_text": "Link: {}\nPasswort: {}",
|
"shared_link_clipboard_text": "Link: {link}\nPasswort: {password}",
|
||||||
"shared_link_create_error": "Fehler beim Erstellen der Linkfreigabe",
|
"shared_link_create_error": "Fehler beim Erstellen der Linkfreigabe",
|
||||||
"shared_link_edit_description_hint": "Beschreibung eingeben",
|
"shared_link_edit_description_hint": "Beschreibung eingeben",
|
||||||
"shared_link_edit_expire_after_option_day": "1 Tag",
|
"shared_link_edit_expire_after_option_day": "1 Tag",
|
||||||
"shared_link_edit_expire_after_option_days": "{} Tage",
|
"shared_link_edit_expire_after_option_days": "{count} Tagen",
|
||||||
"shared_link_edit_expire_after_option_hour": "1 Stunde",
|
"shared_link_edit_expire_after_option_hour": "1 Stunde",
|
||||||
"shared_link_edit_expire_after_option_hours": "{} Stunden",
|
"shared_link_edit_expire_after_option_hours": "{count} Stunden",
|
||||||
"shared_link_edit_expire_after_option_minute": "1 Minute",
|
"shared_link_edit_expire_after_option_minute": "1 Minute",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} Minuten",
|
"shared_link_edit_expire_after_option_minutes": "{count} Minuten",
|
||||||
"shared_link_edit_expire_after_option_months": "{} Monat/en",
|
"shared_link_edit_expire_after_option_months": "{count} Monaten",
|
||||||
"shared_link_edit_expire_after_option_year": "{} Jahr/en",
|
"shared_link_edit_expire_after_option_year": "{count} Jahr",
|
||||||
"shared_link_edit_password_hint": "Passwort eingeben",
|
"shared_link_edit_password_hint": "Passwort eingeben",
|
||||||
"shared_link_edit_submit_button": "Link aktualisieren",
|
"shared_link_edit_submit_button": "Link aktualisieren",
|
||||||
"shared_link_error_server_url_fetch": "Fehler beim Ermitteln der Server-URL",
|
"shared_link_error_server_url_fetch": "Fehler beim Ermitteln der Server-URL",
|
||||||
"shared_link_expires_day": "Verfällt in {} Tag",
|
"shared_link_expires_day": "Läuft ab in {count} Tag",
|
||||||
"shared_link_expires_days": "Verfällt in {} Tag/en",
|
"shared_link_expires_days": "Läuft ab in {count} Tagen",
|
||||||
"shared_link_expires_hour": "Verfällt in {} Stunde",
|
"shared_link_expires_hour": "Läuft ab in {count} Stunde",
|
||||||
"shared_link_expires_hours": "Verfällt in {} Stunde/n",
|
"shared_link_expires_hours": "Läuft ab in {count} Stunden",
|
||||||
"shared_link_expires_minute": "Verfällt in {} Minute",
|
"shared_link_expires_minute": "Läuft ab in {count} Minute",
|
||||||
"shared_link_expires_minutes": "Verfällt in {} Minute/n",
|
"shared_link_expires_minutes": "Läuft ab in {count} Minuten",
|
||||||
"shared_link_expires_never": "Läuft nie ab",
|
"shared_link_expires_never": "Läuft nie ab",
|
||||||
"shared_link_expires_second": "Verfällt in {} Sekunde",
|
"shared_link_expires_second": "Läuft ab in {count} Sekunde",
|
||||||
"shared_link_expires_seconds": "Verfällt in {} Sekunde/n",
|
"shared_link_expires_seconds": "Läuft ab in {count} Sekunden",
|
||||||
"shared_link_individual_shared": "Individuell geteilt",
|
"shared_link_individual_shared": "Individuell geteilt",
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
"shared_link_manage_links": "Geteilte Links verwalten",
|
"shared_link_manage_links": "Geteilte Links verwalten",
|
||||||
@@ -1723,6 +1753,7 @@
|
|||||||
"stop_sharing_photos_with_user": "Aufhören Fotos mit diesem Benutzer zu teilen",
|
"stop_sharing_photos_with_user": "Aufhören Fotos mit diesem Benutzer zu teilen",
|
||||||
"storage": "Speicherplatz",
|
"storage": "Speicherplatz",
|
||||||
"storage_label": "Speicherpfad",
|
"storage_label": "Speicherpfad",
|
||||||
|
"storage_quota": "Speicherplatz-Kontingent",
|
||||||
"storage_usage": "{used} von {available} verwendet",
|
"storage_usage": "{used} von {available} verwendet",
|
||||||
"submit": "Bestätigen",
|
"submit": "Bestätigen",
|
||||||
"suggestions": "Vorschläge",
|
"suggestions": "Vorschläge",
|
||||||
@@ -1739,7 +1770,7 @@
|
|||||||
"tag_assets": "Dateien taggen",
|
"tag_assets": "Dateien taggen",
|
||||||
"tag_created": "Tag erstellt: {tag}",
|
"tag_created": "Tag erstellt: {tag}",
|
||||||
"tag_feature_description": "Durchsuchen von Fotos und Videos, gruppiert nach logischen Tag-Themen",
|
"tag_feature_description": "Durchsuchen von Fotos und Videos, gruppiert nach logischen Tag-Themen",
|
||||||
"tag_not_found_question": "Kein Tag zu finden? <link>Erstelle einen neuen Tag.</link>",
|
"tag_not_found_question": "Kein Tag vorhanden? <link>Erstelle einen neuen Tag.</link>",
|
||||||
"tag_people": "Personen taggen",
|
"tag_people": "Personen taggen",
|
||||||
"tag_updated": "Tag aktualisiert: {tag}",
|
"tag_updated": "Tag aktualisiert: {tag}",
|
||||||
"tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged",
|
"tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged",
|
||||||
@@ -1749,12 +1780,12 @@
|
|||||||
"theme_selection": "Themenauswahl",
|
"theme_selection": "Themenauswahl",
|
||||||
"theme_selection_description": "Automatische Einstellung des Themes auf Hell oder Dunkel, je nach Systemeinstellung des Browsers",
|
"theme_selection_description": "Automatische Einstellung des Themes auf Hell oder Dunkel, je nach Systemeinstellung des Browsers",
|
||||||
"theme_setting_asset_list_storage_indicator_title": "Forschrittsbalken der Sicherung auf dem Vorschaubild",
|
"theme_setting_asset_list_storage_indicator_title": "Forschrittsbalken der Sicherung auf dem Vorschaubild",
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "Anzahl der Elemente pro Reihe ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Anzahl der Elemente pro Reihe ({count})",
|
||||||
"theme_setting_colorful_interface_subtitle": "Primärfarbe auf App-Hintergrund anwenden",
|
"theme_setting_colorful_interface_subtitle": "Primärfarbe auf App-Hintergrund anwenden.",
|
||||||
"theme_setting_colorful_interface_title": "Farbige UI-Oberfläche",
|
"theme_setting_colorful_interface_title": "Farbige UI-Oberfläche",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "Einstellen der Qualität des Detailbildbetrachters",
|
"theme_setting_image_viewer_quality_subtitle": "Einstellen der Qualität des Detailbildbetrachters",
|
||||||
"theme_setting_image_viewer_quality_title": "Qualität des Bildbetrachters",
|
"theme_setting_image_viewer_quality_title": "Qualität des Bildbetrachters",
|
||||||
"theme_setting_primary_color_subtitle": "Farbauswahl für primäre Aktionen und Akzente",
|
"theme_setting_primary_color_subtitle": "Farbauswahl für primäre Aktionen und Akzente.",
|
||||||
"theme_setting_primary_color_title": "Primärfarbe",
|
"theme_setting_primary_color_title": "Primärfarbe",
|
||||||
"theme_setting_system_primary_color_title": "Systemfarbe verwenden",
|
"theme_setting_system_primary_color_title": "Systemfarbe verwenden",
|
||||||
"theme_setting_system_theme_switch": "Automatisch (Systemeinstellung)",
|
"theme_setting_system_theme_switch": "Automatisch (Systemeinstellung)",
|
||||||
@@ -1784,13 +1815,15 @@
|
|||||||
"trash_no_results_message": "Gelöschte Fotos und Videos werden hier angezeigt.",
|
"trash_no_results_message": "Gelöschte Fotos und Videos werden hier angezeigt.",
|
||||||
"trash_page_delete_all": "Alle löschen",
|
"trash_page_delete_all": "Alle löschen",
|
||||||
"trash_page_empty_trash_dialog_content": "Elemente im Papierkorb löschen? Diese Elemente werden dauerhaft aus Immich entfernt",
|
"trash_page_empty_trash_dialog_content": "Elemente im Papierkorb löschen? Diese Elemente werden dauerhaft aus Immich entfernt",
|
||||||
"trash_page_info": "Elemente im Papierkorb werden nach {} Tagen endgültig gelöscht.",
|
"trash_page_info": "Elemente im Papierkorb werden nach {days} Tagen endgültig gelöscht",
|
||||||
"trash_page_no_assets": "Es gibt keine Daten im Papierkorb",
|
"trash_page_no_assets": "Es gibt keine Daten im Papierkorb",
|
||||||
"trash_page_restore_all": "Alle wiederherstellen",
|
"trash_page_restore_all": "Alle wiederherstellen",
|
||||||
"trash_page_select_assets_btn": "Elemente auswählen",
|
"trash_page_select_assets_btn": "Elemente auswählen",
|
||||||
"trash_page_title": "Papierkorb ({})",
|
"trash_page_title": "Papierkorb ({count})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Gelöschte Objekte werden nach {days, plural, one {# Tag} other {# Tagen}} endgültig gelöscht.",
|
"trashed_items_will_be_permanently_deleted_after": "Gelöschte Objekte werden nach {days, plural, one {# Tag} other {# Tagen}} endgültig gelöscht.",
|
||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
|
"unable_to_change_pin_code": "PIN Code konnte nicht geändert werden",
|
||||||
|
"unable_to_setup_pin_code": "PIN Code konnte nicht festgelegt werden",
|
||||||
"unarchive": "Entarchivieren",
|
"unarchive": "Entarchivieren",
|
||||||
"unarchived_count": "{count, plural, other {# entarchiviert}}",
|
"unarchived_count": "{count, plural, other {# entarchiviert}}",
|
||||||
"unfavorite": "Entfavorisieren",
|
"unfavorite": "Entfavorisieren",
|
||||||
@@ -1814,6 +1847,7 @@
|
|||||||
"untracked_files": "Unverfolgte Dateien",
|
"untracked_files": "Unverfolgte Dateien",
|
||||||
"untracked_files_decription": "Diese Dateien werden nicht von der Application getrackt. Sie können das Ergebnis fehlgeschlagener Verschiebungen, unterbrochener Uploads oder aufgrund eines Fehlers sein",
|
"untracked_files_decription": "Diese Dateien werden nicht von der Application getrackt. Sie können das Ergebnis fehlgeschlagener Verschiebungen, unterbrochener Uploads oder aufgrund eines Fehlers sein",
|
||||||
"up_next": "Weiter",
|
"up_next": "Weiter",
|
||||||
|
"updated_at": "Aktualisiert",
|
||||||
"updated_password": "Passwort aktualisiert",
|
"updated_password": "Passwort aktualisiert",
|
||||||
"upload": "Hochladen",
|
"upload": "Hochladen",
|
||||||
"upload_concurrency": "Parallelität beim Hochladen",
|
"upload_concurrency": "Parallelität beim Hochladen",
|
||||||
@@ -1826,15 +1860,18 @@
|
|||||||
"upload_status_errors": "Fehler",
|
"upload_status_errors": "Fehler",
|
||||||
"upload_status_uploaded": "Hochgeladen",
|
"upload_status_uploaded": "Hochgeladen",
|
||||||
"upload_success": "Hochladen erfolgreich. Aktualisiere die Seite, um neue hochgeladene Dateien zu sehen.",
|
"upload_success": "Hochladen erfolgreich. Aktualisiere die Seite, um neue hochgeladene Dateien zu sehen.",
|
||||||
"upload_to_immich": "Zu Immich hochladen ({})",
|
"upload_to_immich": "Auf Immich hochladen ({count})",
|
||||||
"uploading": "Wird hochgeladen",
|
"uploading": "Wird hochgeladen",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Verwendung",
|
"usage": "Verwendung",
|
||||||
"use_current_connection": "aktuelle Verbindung verwenden",
|
"use_current_connection": "aktuelle Verbindung verwenden",
|
||||||
"use_custom_date_range": "Stattdessen einen benutzerdefinierten Datumsbereich verwenden",
|
"use_custom_date_range": "Stattdessen einen benutzerdefinierten Datumsbereich verwenden",
|
||||||
"user": "Nutzer",
|
"user": "Nutzer",
|
||||||
|
"user_has_been_deleted": "Dieser Benutzer wurde gelöscht.",
|
||||||
"user_id": "Nutzer-ID",
|
"user_id": "Nutzer-ID",
|
||||||
"user_liked": "{type, select, photo {Dieses Foto} video {Dieses Video} asset {Diese Datei} other {Dies}} gefällt {user}",
|
"user_liked": "{type, select, photo {Dieses Foto} video {Dieses Video} asset {Diese Datei} other {Dies}} gefällt {user}",
|
||||||
|
"user_pin_code_settings": "PIN Code",
|
||||||
|
"user_pin_code_settings_description": "Verwalte deinen PIN Code",
|
||||||
"user_purchase_settings": "Kauf",
|
"user_purchase_settings": "Kauf",
|
||||||
"user_purchase_settings_description": "Kauf verwalten",
|
"user_purchase_settings_description": "Kauf verwalten",
|
||||||
"user_role_set": "{user} als {role} festlegen",
|
"user_role_set": "{user} als {role} festlegen",
|
||||||
|
|||||||
+108
-100
@@ -14,7 +14,7 @@
|
|||||||
"add_a_location": "Προσθήκη μίας τοποθεσίας",
|
"add_a_location": "Προσθήκη μίας τοποθεσίας",
|
||||||
"add_a_name": "Προσθήκη ονόματος",
|
"add_a_name": "Προσθήκη ονόματος",
|
||||||
"add_a_title": "Προσθήκη τίτλου",
|
"add_a_title": "Προσθήκη τίτλου",
|
||||||
"add_endpoint": "Add endpoint",
|
"add_endpoint": "Προσθήκη τελικού σημείου",
|
||||||
"add_exclusion_pattern": "Προσθήκη μοτίβου αποκλεισμού",
|
"add_exclusion_pattern": "Προσθήκη μοτίβου αποκλεισμού",
|
||||||
"add_import_path": "Προσθήκη μονοπατιού εισαγωγής",
|
"add_import_path": "Προσθήκη μονοπατιού εισαγωγής",
|
||||||
"add_location": "Προσθήκη τοποθεσίας",
|
"add_location": "Προσθήκη τοποθεσίας",
|
||||||
@@ -39,11 +39,11 @@
|
|||||||
"authentication_settings_disable_all": "Είστε βέβαιοι ότι θέλετε να απενεργοποιήσετε όλες τις μεθόδους σύνδεσης; Η σύνδεση θα απενεργοποιηθεί πλήρως.",
|
"authentication_settings_disable_all": "Είστε βέβαιοι ότι θέλετε να απενεργοποιήσετε όλες τις μεθόδους σύνδεσης; Η σύνδεση θα απενεργοποιηθεί πλήρως.",
|
||||||
"authentication_settings_reenable": "Για επανενεργοποίηση, χρησιμοποιήστε μία <link>Εντολή Διακομιστή</link>.",
|
"authentication_settings_reenable": "Για επανενεργοποίηση, χρησιμοποιήστε μία <link>Εντολή Διακομιστή</link>.",
|
||||||
"background_task_job": "Εργασίες Παρασκηνίου",
|
"background_task_job": "Εργασίες Παρασκηνίου",
|
||||||
"backup_database": "Δημιουργία Αντιγράφου Ασφαλείας της Βάσης Δεδομένων",
|
"backup_database": "Δημιουργία Dump βάσης δεδομένων",
|
||||||
"backup_database_enable_description": "Ενεργοποίηση αντιγράφων ασφαλείας της βάσης δεδομένων",
|
"backup_database_enable_description": "Ενεργοποίηση dumps βάσης δεδομένων",
|
||||||
"backup_keep_last_amount": "Αριθμός προηγούμενων αντιγράφων ασφαλείας για διατήρηση",
|
"backup_keep_last_amount": "Ποσότητα προηγούμενων dumps που πρέπει να διατηρηθούν",
|
||||||
"backup_settings": "Ρυθμίσεις Αντιγράφων Ασφαλείας",
|
"backup_settings": "Ρυθμίσεις dump βάσης δεδομένων",
|
||||||
"backup_settings_description": "Διαχείρηση ρυθμίσεων των αντιγράφων ασφαλείας της βάσης δεδομένων",
|
"backup_settings_description": "Διαχείριση ρυθμίσεων dump της βάσης δεδομένων. Σημείωση: Αυτές οι εργασίες δεν παρακολουθούνται και δεν θα ειδοποιηθείτε για αποτυχία.",
|
||||||
"check_all": "Έλεγχος Όλων",
|
"check_all": "Έλεγχος Όλων",
|
||||||
"cleanup": "Εκκαθάριση",
|
"cleanup": "Εκκαθάριση",
|
||||||
"cleared_jobs": "Εκκαθαρίστηκαν οι εργασίες για: {job}",
|
"cleared_jobs": "Εκκαθαρίστηκαν οι εργασίες για: {job}",
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
"confirm_email_below": "Για επιβεβαίωση, πληκτρολογήστε \"{email}\" παρακάτω",
|
"confirm_email_below": "Για επιβεβαίωση, πληκτρολογήστε \"{email}\" παρακάτω",
|
||||||
"confirm_reprocess_all_faces": "Είστε βέβαιοι ότι θέλετε να επεξεργαστείτε ξανά όλα τα πρόσωπα; Αυτό θα εκκαθαρίσει ακόμα και τα άτομα στα οποία έχετε ήδη ορίσει το όνομα.",
|
"confirm_reprocess_all_faces": "Είστε βέβαιοι ότι θέλετε να επεξεργαστείτε ξανά όλα τα πρόσωπα; Αυτό θα εκκαθαρίσει ακόμα και τα άτομα στα οποία έχετε ήδη ορίσει το όνομα.",
|
||||||
"confirm_user_password_reset": "Είστε βέβαιοι ότι θέλετε να επαναφέρετε τον κωδικό πρόσβασης του χρήστη {user};",
|
"confirm_user_password_reset": "Είστε βέβαιοι ότι θέλετε να επαναφέρετε τον κωδικό πρόσβασης του χρήστη {user};",
|
||||||
|
"confirm_user_pin_code_reset": "Είστε βέβαιοι ότι θέλετε να επαναφέρετε τον κωδικό PIN του χρήστη {user};",
|
||||||
"create_job": "Δημιουργία εργασίας",
|
"create_job": "Δημιουργία εργασίας",
|
||||||
"cron_expression": "Σύνταξη Cron",
|
"cron_expression": "Σύνταξη Cron",
|
||||||
"cron_expression_description": "Ορίστε το διάστημα σάρωσης χρησιμοποιώντας τη μορφή cron. Για περισσότερες πληροφορίες, ανατρέξτε π.χ. στο <link>Crontab Guru</link>",
|
"cron_expression_description": "Ορίστε το διάστημα σάρωσης χρησιμοποιώντας τη μορφή cron. Για περισσότερες πληροφορίες, ανατρέξτε π.χ. στο <link>Crontab Guru</link>",
|
||||||
@@ -192,20 +193,13 @@
|
|||||||
"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": "Ταυτότητα πελάτη (Client)",
|
|
||||||
"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": "Δήλωση ποσοστού αποθήκευσης",
|
||||||
@@ -352,6 +346,7 @@
|
|||||||
"user_delete_delay_settings_description": "Αριθμός ημερών μετά την αφαίρεση, για την οριστική διαγραφή του λογαριασμού και των αρχείων ενός χρήστη. Η εργασία διαγραφής χρηστών εκτελείται τα μεσάνυχτα, για να ελέγξει ποιοι χρήστες είναι έτοιμοι για διαγραφή. Οι αλλαγές σε αυτή τη ρύθμιση θα αξιολογηθούν κατά την επόμενη εκτέλεση.",
|
"user_delete_delay_settings_description": "Αριθμός ημερών μετά την αφαίρεση, για την οριστική διαγραφή του λογαριασμού και των αρχείων ενός χρήστη. Η εργασία διαγραφής χρηστών εκτελείται τα μεσάνυχτα, για να ελέγξει ποιοι χρήστες είναι έτοιμοι για διαγραφή. Οι αλλαγές σε αυτή τη ρύθμιση θα αξιολογηθούν κατά την επόμενη εκτέλεση.",
|
||||||
"user_delete_immediately": "Ο λογαριασμός και τα αρχεία του/της <b>{user}</b> θα μπουν στην ουρά για οριστική διαγραφή, <b>άμεσα</b>.",
|
"user_delete_immediately": "Ο λογαριασμός και τα αρχεία του/της <b>{user}</b> θα μπουν στην ουρά για οριστική διαγραφή, <b>άμεσα</b>.",
|
||||||
"user_delete_immediately_checkbox": "Βάλε τον χρήστη και τα αρχεία του στην ουρά για άμεση διαγραφή",
|
"user_delete_immediately_checkbox": "Βάλε τον χρήστη και τα αρχεία του στην ουρά για άμεση διαγραφή",
|
||||||
|
"user_details": "Λεπτομέρειες χρήστη",
|
||||||
"user_management": "Διαχείριση χρηστών",
|
"user_management": "Διαχείριση χρηστών",
|
||||||
"user_password_has_been_reset": "Ο κωδικός πρόσβασης του χρήστη έχει επαναρυθμιστεί:",
|
"user_password_has_been_reset": "Ο κωδικός πρόσβασης του χρήστη έχει επαναρυθμιστεί:",
|
||||||
"user_password_reset_description": "Παρακαλώ παρέχετε τον προσωρινό κωδικό πρόσβασης στον χρήστη και ενημερώστε τον ότι θα πρέπει να τον αλλάξει, κατά την επόμενη σύνδεσή του.",
|
"user_password_reset_description": "Παρακαλώ παρέχετε τον προσωρινό κωδικό πρόσβασης στον χρήστη και ενημερώστε τον ότι θα πρέπει να τον αλλάξει, κατά την επόμενη σύνδεσή του.",
|
||||||
@@ -371,13 +366,17 @@
|
|||||||
"admin_password": "Κωδικός πρόσβασης Διαχειριστή",
|
"admin_password": "Κωδικός πρόσβασης Διαχειριστή",
|
||||||
"administration": "Διαχείριση",
|
"administration": "Διαχείριση",
|
||||||
"advanced": "Για προχωρημένους",
|
"advanced": "Για προχωρημένους",
|
||||||
"advanced_settings_log_level_title": "Επίπεδο καταγραφής: {}",
|
"advanced_settings_enable_alternate_media_filter_subtitle": "Χρησιμοποιήστε αυτήν την επιλογή για να φιλτράρετε τα μέσα ενημέρωσης κατά τον συγχρονισμό με βάση εναλλακτικά κριτήρια. Δοκιμάστε αυτή τη δυνατότητα μόνο αν έχετε προβλήματα με την εφαρμογή που εντοπίζει όλα τα άλμπουμ.",
|
||||||
|
"advanced_settings_enable_alternate_media_filter_title": "[ΠΕΙΡΑΜΑΤΙΚΟ] Χρήση εναλλακτικού φίλτρου συγχρονισμού άλμπουμ συσκευής",
|
||||||
|
"advanced_settings_log_level_title": "Επίπεδο σύνδεσης: {level}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Μερικές συσκευές αργούν πολύ να φορτώσουν μικρογραφίες από αρχεία στη συσκευή. Ενεργοποιήστε αυτήν τη ρύθμιση για να φορτώνονται αντί αυτού απομακρυσμένες εικόνες.",
|
"advanced_settings_prefer_remote_subtitle": "Μερικές συσκευές αργούν πολύ να φορτώσουν μικρογραφίες από αρχεία στη συσκευή. Ενεργοποιήστε αυτήν τη ρύθμιση για να φορτώνονται αντί αυτού απομακρυσμένες εικόνες.",
|
||||||
"advanced_settings_prefer_remote_title": "Προτίμηση απομακρυσμένων εικόνων.",
|
"advanced_settings_prefer_remote_title": "Προτίμηση απομακρυσμένων εικόνων",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Καθορισμός κεφαλίδων διακομιστή μεσολάβησης που το Immich πρέπει να στέλνει με κάθε αίτημα δικτύου",
|
"advanced_settings_proxy_headers_subtitle": "Καθορισμός κεφαλίδων διακομιστή μεσολάβησης που το Immich πρέπει να στέλνει με κάθε αίτημα δικτύου",
|
||||||
"advanced_settings_proxy_headers_title": "Κεφαλίδες διακομιστή μεσολάβησης",
|
"advanced_settings_proxy_headers_title": "Κεφαλίδες διακομιστή μεσολάβησης",
|
||||||
"advanced_settings_self_signed_ssl_subtitle": "Παρακάμπτει τον έλεγχο πιστοποιητικού SSL του διακομιστή. Απαραίτητο για αυτο-υπογεγραμμένα πιστοποιητικά.",
|
"advanced_settings_self_signed_ssl_subtitle": "Παρακάμπτει τον έλεγχο πιστοποιητικού SSL του διακομιστή. Απαραίτητο για αυτο-υπογεγραμμένα πιστοποιητικά.",
|
||||||
"advanced_settings_self_signed_ssl_title": "Να επιτρέπονται αυτο-υπογεγραμμένα πιστοποιητικά SSL",
|
"advanced_settings_self_signed_ssl_title": "Να επιτρέπονται αυτο-υπογεγραμμένα πιστοποιητικά SSL",
|
||||||
|
"advanced_settings_sync_remote_deletions_subtitle": "Αυτόματη διαγραφή ή επαναφορά ενός περιουσιακού στοιχείου σε αυτή τη συσκευή, όταν η ενέργεια αυτή πραγματοποιείται στο διαδίκτυο",
|
||||||
|
"advanced_settings_sync_remote_deletions_title": "Συγχρονισμός απομακρυσμένων διαγραφών [ΠΕΙΡΑΜΑΤΙΚΟ]",
|
||||||
"advanced_settings_tile_subtitle": "Ρυθμίσεις προχωρημένου χρήστη",
|
"advanced_settings_tile_subtitle": "Ρυθμίσεις προχωρημένου χρήστη",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Ενεργοποίηση πρόσθετων χαρακτηριστικών για αντιμετώπιση προβλημάτων",
|
"advanced_settings_troubleshooting_subtitle": "Ενεργοποίηση πρόσθετων χαρακτηριστικών για αντιμετώπιση προβλημάτων",
|
||||||
"advanced_settings_troubleshooting_title": "Αντιμετώπιση προβλημάτων",
|
"advanced_settings_troubleshooting_title": "Αντιμετώπιση προβλημάτων",
|
||||||
@@ -400,9 +399,9 @@
|
|||||||
"album_remove_user_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε τον/την {user};",
|
"album_remove_user_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε τον/την {user};",
|
||||||
"album_share_no_users": "Φαίνεται ότι έχετε κοινοποιήσει αυτό το άλμπουμ σε όλους τους χρήστες ή δεν έχετε χρήστες για να το κοινοποιήσετε.",
|
"album_share_no_users": "Φαίνεται ότι έχετε κοινοποιήσει αυτό το άλμπουμ σε όλους τους χρήστες ή δεν έχετε χρήστες για να το κοινοποιήσετε.",
|
||||||
"album_thumbnail_card_item": "1 αντικείμενο",
|
"album_thumbnail_card_item": "1 αντικείμενο",
|
||||||
"album_thumbnail_card_items": "{} αντικείμενα",
|
"album_thumbnail_card_items": "{count} αντικείμενα",
|
||||||
"album_thumbnail_card_shared": "· Κοινόχρηστο",
|
"album_thumbnail_card_shared": " Κοινόχρηστο",
|
||||||
"album_thumbnail_shared_by": "Κοινοποιημένο από {}",
|
"album_thumbnail_shared_by": "Κοινοποιημένο από {user}",
|
||||||
"album_updated": "Το άλμπουμ, ενημερώθηκε",
|
"album_updated": "Το άλμπουμ, ενημερώθηκε",
|
||||||
"album_updated_setting_description": "Λάβετε ειδοποίηση μέσω email όταν ένα κοινόχρηστο άλμπουμ έχει νέα αρχεία",
|
"album_updated_setting_description": "Λάβετε ειδοποίηση μέσω email όταν ένα κοινόχρηστο άλμπουμ έχει νέα αρχεία",
|
||||||
"album_user_left": "Αποχωρήσατε από το {album}",
|
"album_user_left": "Αποχωρήσατε από το {album}",
|
||||||
@@ -440,7 +439,7 @@
|
|||||||
"archive": "Αρχείο",
|
"archive": "Αρχείο",
|
||||||
"archive_or_unarchive_photo": "Αρχειοθέτηση ή αποαρχειοθέτηση φωτογραφίας",
|
"archive_or_unarchive_photo": "Αρχειοθέτηση ή αποαρχειοθέτηση φωτογραφίας",
|
||||||
"archive_page_no_archived_assets": "Δε βρέθηκαν αρχειοθετημένα στοιχεία",
|
"archive_page_no_archived_assets": "Δε βρέθηκαν αρχειοθετημένα στοιχεία",
|
||||||
"archive_page_title": "Αρχείο ({})",
|
"archive_page_title": "Αρχείο ({count})",
|
||||||
"archive_size": "Μέγεθος Αρχείου",
|
"archive_size": "Μέγεθος Αρχείου",
|
||||||
"archive_size_description": "Ρυθμίστε το μέγεθος του αρχείου για λήψεις (σε GiB)",
|
"archive_size_description": "Ρυθμίστε το μέγεθος του αρχείου για λήψεις (σε GiB)",
|
||||||
"archived": "Αρχείο",
|
"archived": "Αρχείο",
|
||||||
@@ -470,46 +469,46 @@
|
|||||||
"asset_skipped_in_trash": "Στον κάδο απορριμμάτων",
|
"asset_skipped_in_trash": "Στον κάδο απορριμμάτων",
|
||||||
"asset_uploaded": "Ανεβάστηκε",
|
"asset_uploaded": "Ανεβάστηκε",
|
||||||
"asset_uploading": "Ανεβάζεται…",
|
"asset_uploading": "Ανεβάζεται…",
|
||||||
"asset_viewer_settings_subtitle": "Manage your gallery viewer settings",
|
"asset_viewer_settings_subtitle": "Διαχείριση ρυθμίσεων προβολής συλλογής",
|
||||||
"asset_viewer_settings_title": "Προβολή Στοιχείων",
|
"asset_viewer_settings_title": "Προβολή Στοιχείων",
|
||||||
"assets": "Αντικείμενα",
|
"assets": "Αντικείμενα",
|
||||||
"assets_added_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}}",
|
"assets_added_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}}",
|
||||||
"assets_added_to_album_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο άλμπουμ",
|
"assets_added_to_album_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο άλμπουμ",
|
||||||
"assets_added_to_name_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο {hasName, select, true {<b>{name}</b>} other {νέο άλμπουμ}}",
|
"assets_added_to_name_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο {hasName, select, true {<b>{name}</b>} other {νέο άλμπουμ}}",
|
||||||
"assets_count": "{count, plural, one {# αρχείο} other {# αρχεία}}",
|
"assets_count": "{count, plural, one {# αρχείο} other {# αρχεία}}",
|
||||||
"assets_deleted_permanently": "{} στοιχείο(α) διαγράφηκαν οριστικά",
|
"assets_deleted_permanently": "{count} τα στοιχεία διαγράφηκαν οριστικά",
|
||||||
"assets_deleted_permanently_from_server": "{} στοιχείο(α) διαγράφηκαν οριστικά από τον διακομιστή Immich",
|
"assets_deleted_permanently_from_server": "{count} στοιχεία διαγράφηκαν οριστικά από το διακομιστή Immich",
|
||||||
"assets_moved_to_trash_count": "Μετακινήθηκε/καν {count, plural, one {# αρχείο} other {# αρχεία}} στον κάδο απορριμμάτων",
|
"assets_moved_to_trash_count": "Μετακινήθηκαν {count, plural, one {# αρχείο} other {# αρχεία}} στον κάδο απορριμμάτων",
|
||||||
"assets_permanently_deleted_count": "Διαγράφηκε/καν μόνιμα {count, plural, one {# αρχείο} other {# αρχεία}}",
|
"assets_permanently_deleted_count": "Διαγράφηκαν μόνιμα {count, plural, one {# αρχείο} other {# αρχεία}}",
|
||||||
"assets_removed_count": "Αφαιρέθηκαν {count, plural, one {# αρχείο} other {# αρχεία}}",
|
"assets_removed_count": "Αφαιρέθηκαν {count, plural, one {# αρχείο} other {# αρχεία}}",
|
||||||
"assets_removed_permanently_from_device": "{} στοιχεία καταργήθηκαν οριστικά από τη συσκευή σας",
|
"assets_removed_permanently_from_device": "{count} στοιχεία καταργήθηκαν οριστικά από τη συσκευή σας",
|
||||||
"assets_restore_confirmation": "Είστε βέβαιοι ότι θέλετε να επαναφέρετε όλα τα στοιχεία που βρίσκονται στον κάδο απορριμμάτων; Αυτή η ενέργεια δεν μπορεί να αναιρεθεί! Λάβετε υπόψη ότι δεν θα είναι δυνατή η επαναφορά στοιχείων εκτός σύνδεσης.",
|
"assets_restore_confirmation": "Είστε βέβαιοι ότι θέλετε να επαναφέρετε όλα τα στοιχεία που βρίσκονται στον κάδο απορριμμάτων; Αυτή η ενέργεια δεν μπορεί να αναιρεθεί! Λάβετε υπόψη ότι δεν θα είναι δυνατή η επαναφορά στοιχείων εκτός σύνδεσης.",
|
||||||
"assets_restored_count": "Έγινε επαναφορά {count, plural, one {# στοιχείου} other {# στοιχείων}}",
|
"assets_restored_count": "Έγινε επαναφορά {count, plural, one {# στοιχείου} other {# στοιχείων}}",
|
||||||
"assets_restored_successfully": "{} στοιχεία αποκαταστάθηκαν με επιτυχία",
|
"assets_restored_successfully": "{count} στοιχεία αποκαταστάθηκαν με επιτυχία",
|
||||||
"assets_trashed": "{} στοιχεία μεταφέρθηκαν στον κάδο απορριμμάτων",
|
"assets_trashed": "{} στοιχεία μεταφέρθηκαν στον κάδο απορριμμάτων",
|
||||||
"assets_trashed_count": "Μετακιν. στον κάδο απορριμάτων {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
"assets_trashed_count": "Μετακιν. στον κάδο απορριμάτων {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
||||||
"assets_trashed_from_server": "{} στοιχεία μεταφέρθηκαν στον κάδο απορριμμάτων από τον διακομιστή Immich",
|
"assets_trashed_from_server": "{} στοιχεία μεταφέρθηκαν στον κάδο απορριμμάτων από το διακομιστή Immich",
|
||||||
"assets_were_part_of_album_count": "{count, plural, one {Το στοιχείο ανήκει} other {Τα στοιχεία ανήκουν}} ήδη στο άλμπουμ",
|
"assets_were_part_of_album_count": "{count, plural, one {Το στοιχείο ανήκει} other {Τα στοιχεία ανήκουν}} ήδη στο άλμπουμ",
|
||||||
"authorized_devices": "Εξουσιοδοτημένες Συσκευές",
|
"authorized_devices": "Εξουσιοδοτημένες Συσκευές",
|
||||||
"automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere",
|
"automatic_endpoint_switching_subtitle": "Σύνδεση τοπικά μέσω του καθορισμένου Wi-Fi όταν είναι διαθέσιμο και χρήση εναλλακτικών συνδέσεων αλλού",
|
||||||
"automatic_endpoint_switching_title": "Automatic URL switching",
|
"automatic_endpoint_switching_title": "Αυτόματη εναλλαγή URL",
|
||||||
"back": "Πίσω",
|
"back": "Πίσω",
|
||||||
"back_close_deselect": "Πίσω, κλείσιμο ή αποεπιλογή",
|
"back_close_deselect": "Πίσω, κλείσιμο ή αποεπιλογή",
|
||||||
"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": "Το Immich για να μπορεί να αλλάζει δίκτυα όταν τρέχει στο παρασκήνιο, πρέπει *πάντα* να έχει πρόσβαση στην ακριβή τοποθεσία ώστε η εφαρμογή να μπορεί να διαβάζει το όνομα του δικτύου Wi-Fi",
|
||||||
"backup_album_selection_page_albums_device": "Άλμπουμ στη συσκευή ({})",
|
"backup_album_selection_page_albums_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": "Τα στοιχεία μπορεί να διασκορπιστούν σε πολλά άλμπουμ. Έτσι, τα άλμπουμ μπορούν να περιληφθούν ή να εξαιρεθούν κατά τη διαδικασία δημιουργίας αντιγράφων ασφαλείας.",
|
||||||
"backup_album_selection_page_select_albums": "Επιλογή άλμπουμ",
|
"backup_album_selection_page_select_albums": "Επιλογή άλμπουμ",
|
||||||
"backup_album_selection_page_selection_info": "Πληροφορίες επιλογής",
|
"backup_album_selection_page_selection_info": "Πληροφορίες επιλογής",
|
||||||
"backup_album_selection_page_total_assets": "Συνολικά μοναδικά στοιχεία",
|
"backup_album_selection_page_total_assets": "Συνολικά μοναδικά στοιχεία",
|
||||||
"backup_all": "Όλα",
|
"backup_all": "Όλα",
|
||||||
"backup_background_service_backup_failed_message": "Αποτυχία δημιουργίας αντιγράφων ασφαλείας. Επανάληψη...",
|
"backup_background_service_backup_failed_message": "Αποτυχία δημιουργίας αντιγράφων ασφαλείας. Επανάληψη…",
|
||||||
"backup_background_service_connection_failed_message": "Αποτυχία σύνδεσης με το διακομιστή. Επανάληψη...",
|
"backup_background_service_connection_failed_message": "Αποτυχία σύνδεσης με το διακομιστή. Επανάληψη…",
|
||||||
"backup_background_service_current_upload_notification": "Μεταφόρτωση {}",
|
"backup_background_service_current_upload_notification": "Μεταφόρτωση {}",
|
||||||
"backup_background_service_default_notification": "Έλεγχος για νέα στοιχεία...",
|
"backup_background_service_default_notification": "Έλεγχος για νέα στοιχεία…",
|
||||||
"backup_background_service_error_title": "Σφάλμα δημιουργίας αντιγράφων ασφαλείας",
|
"backup_background_service_error_title": "Σφάλμα δημιουργίας αντιγράφων ασφαλείας",
|
||||||
"backup_background_service_in_progress_notification": "Δημιουργία αντιγράφων ασφαλείας των στοιχείων σας...",
|
"backup_background_service_in_progress_notification": "Δημιουργία αντιγράφων ασφαλείας των στοιχείων σας…",
|
||||||
"backup_background_service_upload_failure_notification": "Αποτυχία μεταφόρτωσης {}",
|
"backup_background_service_upload_failure_notification": "Αποτυχία μεταφόρτωσης {}",
|
||||||
"backup_controller_page_albums": "Δημιουργία αντιγράφων ασφαλείας άλμπουμ",
|
"backup_controller_page_albums": "Δημιουργία αντιγράφων ασφαλείας άλμπουμ",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Ενεργοποιήστε την ανανέωση εφαρμογής στο παρασκήνιο στις Ρυθμίσεις > Γενικά > Ανανέωση Εφαρμογής στο Παρασκήνιο για να χρησιμοποιήσετε την δημιουργία αντιγράφων ασφαλείας στο παρασκήνιο.",
|
"backup_controller_page_background_app_refresh_disabled_content": "Ενεργοποιήστε την ανανέωση εφαρμογής στο παρασκήνιο στις Ρυθμίσεις > Γενικά > Ανανέωση Εφαρμογής στο Παρασκήνιο για να χρησιμοποιήσετε την δημιουργία αντιγράφων ασφαλείας στο παρασκήνιο.",
|
||||||
@@ -527,13 +526,13 @@
|
|||||||
"backup_controller_page_background_is_on": "Η αυτόματη δημιουργία αντιγράφων ασφαλείας στο παρασκήνιο είναι ενεργοποιημένη",
|
"backup_controller_page_background_is_on": "Η αυτόματη δημιουργία αντιγράφων ασφαλείας στο παρασκήνιο είναι ενεργοποιημένη",
|
||||||
"backup_controller_page_background_turn_off": "Απενεργοποίηση υπηρεσίας παρασκηνίου",
|
"backup_controller_page_background_turn_off": "Απενεργοποίηση υπηρεσίας παρασκηνίου",
|
||||||
"backup_controller_page_background_turn_on": "Ενεργοποίηση υπηρεσίας παρασκηνίου",
|
"backup_controller_page_background_turn_on": "Ενεργοποίηση υπηρεσίας παρασκηνίου",
|
||||||
"backup_controller_page_background_wifi": "Μόνο σε σύνδεση WiFi",
|
"backup_controller_page_background_wifi": "Μόνο σε σύνδεση Wi-Fi",
|
||||||
"backup_controller_page_backup": "Αντίγραφα ασφαλείας",
|
"backup_controller_page_backup": "Αντίγραφα ασφαλείας",
|
||||||
"backup_controller_page_backup_selected": "Επιλεγμένα:",
|
"backup_controller_page_backup_selected": "Επιλεγμένα: ",
|
||||||
"backup_controller_page_backup_sub": "Φωτογραφίες και βίντεο για τα οποία έχουν δημιουργηθεί αντίγραφα ασφαλείας",
|
"backup_controller_page_backup_sub": "Φωτογραφίες και βίντεο για τα οποία έχουν δημιουργηθεί αντίγραφα ασφαλείας",
|
||||||
"backup_controller_page_created": "Δημιουργήθηκε στις: {}",
|
"backup_controller_page_created": "Δημιουργήθηκε στις: {}",
|
||||||
"backup_controller_page_desc_backup": "Ενεργοποιήστε την δημιουργία αντιγράφων ασφαλείας στο προσκήνιο για αυτόματη μεταφόρτωση νέων στοιχείων στον διακομιστή όταν ανοίγετε την εφαρμογή.",
|
"backup_controller_page_desc_backup": "Ενεργοποιήστε την δημιουργία αντιγράφων ασφαλείας στο προσκήνιο για αυτόματη μεταφόρτωση νέων στοιχείων στον διακομιστή όταν ανοίγετε την εφαρμογή.",
|
||||||
"backup_controller_page_excluded": "Εξαιρούμενα:",
|
"backup_controller_page_excluded": "Εξαιρούμενα: ",
|
||||||
"backup_controller_page_failed": "Αποτυχημένα ({})",
|
"backup_controller_page_failed": "Αποτυχημένα ({})",
|
||||||
"backup_controller_page_filename": "Όνομα αρχείου: {} [{}]",
|
"backup_controller_page_filename": "Όνομα αρχείου: {} [{}]",
|
||||||
"backup_controller_page_id": "ID: {}",
|
"backup_controller_page_id": "ID: {}",
|
||||||
@@ -543,13 +542,13 @@
|
|||||||
"backup_controller_page_remainder_sub": "Υπόλοιπες φωτογραφίες και βίντεο για αντιγραφή ασφαλείας από την επιλογή",
|
"backup_controller_page_remainder_sub": "Υπόλοιπες φωτογραφίες και βίντεο για αντιγραφή ασφαλείας από την επιλογή",
|
||||||
"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": "Η αυτόματη δημιουργία αντιγράφου ασφαλείας στο προσκήνιο είναι απενεργοποιημένη\n",
|
"backup_controller_page_status_off": "Η αυτόματη δημιουργία αντιγράφου ασφαλείας στο προσκήνιο, είναι απενεργοποιημένη",
|
||||||
"backup_controller_page_status_on": "Η αυτόματη δημιουργία αντιγράφου ασφαλείας στο προσκήνιο είναι ενεργοποιημένη",
|
"backup_controller_page_status_on": "Η αυτόματη δημιουργία αντιγράφου ασφαλείας στο προσκήνιο είναι ενεργοποιημένη",
|
||||||
"backup_controller_page_storage_format": "{} από {} σε χρήση",
|
"backup_controller_page_storage_format": "{} από {} σε χρήση",
|
||||||
"backup_controller_page_to_backup": "Άλμπουμ για δημιουργία αντιγράφου ασφαλείας",
|
"backup_controller_page_to_backup": "Άλμπουμ για δημιουργία αντιγράφου ασφαλείας",
|
||||||
"backup_controller_page_total_sub": "Όλες οι μοναδικές φωτογραφίες και βίντεο από τα επιλεγμένα άλμπουμ\n",
|
"backup_controller_page_total_sub": "Όλες οι μοναδικές φωτογραφίες και βίντεο από τα επιλεγμένα άλμπουμ",
|
||||||
"backup_controller_page_turn_off": "Απενεργοποίηση δημιουργίας αντιγράφου ασφαλείας στο προσκήνιο\n",
|
"backup_controller_page_turn_off": "Απενεργοποίηση δημιουργίας αντιγράφου ασφαλείας στο προσκήνιο",
|
||||||
"backup_controller_page_turn_on": "Ενεργοποίηση δημιουργίας αντιγράφου ασφαλείας στο προσκήνιο\n",
|
"backup_controller_page_turn_on": "Ενεργοποίηση δημιουργίας αντιγράφου ασφαλείας στο προσκήνιο",
|
||||||
"backup_controller_page_uploading_file_info": "Μεταφόρτωση πληροφοριών αρχείου",
|
"backup_controller_page_uploading_file_info": "Μεταφόρτωση πληροφοριών αρχείου",
|
||||||
"backup_err_only_album": "Δεν είναι δυνατή η αφαίρεση του μοναδικού άλμπουμ",
|
"backup_err_only_album": "Δεν είναι δυνατή η αφαίρεση του μοναδικού άλμπουμ",
|
||||||
"backup_info_card_assets": "στοιχεία",
|
"backup_info_card_assets": "στοιχεία",
|
||||||
@@ -558,7 +557,7 @@
|
|||||||
"backup_manual_success": "Επιτυχία",
|
"backup_manual_success": "Επιτυχία",
|
||||||
"backup_manual_title": "Κατάσταση μεταφόρτωσης",
|
"backup_manual_title": "Κατάσταση μεταφόρτωσης",
|
||||||
"backup_options_page_title": "Επιλογές αντιγράφων ασφαλείας",
|
"backup_options_page_title": "Επιλογές αντιγράφων ασφαλείας",
|
||||||
"backup_setting_subtitle": "Manage background and foreground upload settings",
|
"backup_setting_subtitle": "Διαχείριση ρυθμίσεων μεταφόρτωσης στο παρασκήνιο και στο προσκήνιο",
|
||||||
"backward": "Προς τα πίσω",
|
"backward": "Προς τα πίσω",
|
||||||
"birthdate_saved": "Η ημερομηνία γέννησης αποθηκεύτηκε επιτυχώς",
|
"birthdate_saved": "Η ημερομηνία γέννησης αποθηκεύτηκε επιτυχώς",
|
||||||
"birthdate_set_description": "Η ημερομηνία γέννησης χρησιμοποιείται για τον υπολογισμό της ηλικίας αυτού του ατόμου, τη χρονική στιγμή μιας φωτογραφίας.",
|
"birthdate_set_description": "Η ημερομηνία γέννησης χρησιμοποιείται για τον υπολογισμό της ηλικίας αυτού του ατόμου, τη χρονική στιγμή μιας φωτογραφίας.",
|
||||||
@@ -575,7 +574,7 @@
|
|||||||
"cache_settings_clear_cache_button_title": "Καθαρίζει τη προσωρινή μνήμη της εφαρμογής. Αυτό θα επηρεάσει σημαντικά την απόδοση της εφαρμογής μέχρι να αναδημιουργηθεί η προσωρινή μνήμη.",
|
"cache_settings_clear_cache_button_title": "Καθαρίζει τη προσωρινή μνήμη της εφαρμογής. Αυτό θα επηρεάσει σημαντικά την απόδοση της εφαρμογής μέχρι να αναδημιουργηθεί η προσωρινή μνήμη.",
|
||||||
"cache_settings_duplicated_assets_clear_button": "ΕΚΚΑΘΑΡΙΣΗ",
|
"cache_settings_duplicated_assets_clear_button": "ΕΚΚΑΘΑΡΙΣΗ",
|
||||||
"cache_settings_duplicated_assets_subtitle": "Φωτογραφίες και βίντεο που έχουν μπει στη μαύρη λίστα από την εφαρμογή",
|
"cache_settings_duplicated_assets_subtitle": "Φωτογραφίες και βίντεο που έχουν μπει στη μαύρη λίστα από την εφαρμογή",
|
||||||
"cache_settings_duplicated_assets_title": "Διπλά στοιχεία ({})",
|
"cache_settings_duplicated_assets_title": "Διπλότυπα στοιχεία ({})",
|
||||||
"cache_settings_image_cache_size": "Μέγεθος προσωρινής μνήμης εικόνων ({} στοιχεία)",
|
"cache_settings_image_cache_size": "Μέγεθος προσωρινής μνήμης εικόνων ({} στοιχεία)",
|
||||||
"cache_settings_statistics_album": "Μικρογραφίες βιβλιοθήκης",
|
"cache_settings_statistics_album": "Μικρογραφίες βιβλιοθήκης",
|
||||||
"cache_settings_statistics_assets": "{} στοιχεία ({})",
|
"cache_settings_statistics_assets": "{} στοιχεία ({})",
|
||||||
@@ -593,12 +592,12 @@
|
|||||||
"camera_model": "Μοντέλο κάμερας",
|
"camera_model": "Μοντέλο κάμερας",
|
||||||
"cancel": "Ακύρωση",
|
"cancel": "Ακύρωση",
|
||||||
"cancel_search": "Ακύρωση αναζήτησης",
|
"cancel_search": "Ακύρωση αναζήτησης",
|
||||||
"canceled": "Canceled",
|
"canceled": "Ακυρωμένο",
|
||||||
"cannot_merge_people": "Αδύνατη η συγχώνευση ατόμων",
|
"cannot_merge_people": "Αδύνατη η συγχώνευση ατόμων",
|
||||||
"cannot_undo_this_action": "Δεν μπορείτε να αναιρέσετε αυτήν την ενέργεια!",
|
"cannot_undo_this_action": "Δεν μπορείτε να αναιρέσετε αυτήν την ενέργεια!",
|
||||||
"cannot_update_the_description": "Αδύνατη η ενημέρωση της περιγραφής",
|
"cannot_update_the_description": "Αδύνατη η ενημέρωση της περιγραφής",
|
||||||
"change_date": "Αλλαγή ημερομηνίας",
|
"change_date": "Αλλαγή ημερομηνίας",
|
||||||
"change_display_order": "Change display order",
|
"change_display_order": "Αλλαγή σειράς εμφάνισης",
|
||||||
"change_expiration_time": "Αλλαγή χρόνου λήξης",
|
"change_expiration_time": "Αλλαγή χρόνου λήξης",
|
||||||
"change_location": "Αλλαγή τοποθεσίας",
|
"change_location": "Αλλαγή τοποθεσίας",
|
||||||
"change_name": "Αλλαγή ονομασίας",
|
"change_name": "Αλλαγή ονομασίας",
|
||||||
@@ -613,9 +612,9 @@
|
|||||||
"change_your_password": "Αλλάξτε τον κωδικό σας",
|
"change_your_password": "Αλλάξτε τον κωδικό σας",
|
||||||
"changed_visibility_successfully": "Η προβολή, άλλαξε με επιτυχία",
|
"changed_visibility_successfully": "Η προβολή, άλλαξε με επιτυχία",
|
||||||
"check_all": "Επιλογή Όλων",
|
"check_all": "Επιλογή Όλων",
|
||||||
"check_corrupt_asset_backup": "Check for corrupt asset backups",
|
"check_corrupt_asset_backup": "Έλεγχος για κατεστραμμένα αντίγραφα ασφαλείας στοιχείων",
|
||||||
"check_corrupt_asset_backup_button": "Perform check",
|
"check_corrupt_asset_backup_button": "Εκτέλεση ελέγχου",
|
||||||
"check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.",
|
"check_corrupt_asset_backup_description": "Εκτέλεσε αυτόν τον έλεγχο μόνο μέσω Wi-Fi και αφού έχουν αποθηκευτεί όλα τα αντίγραφα ασφαλείας των στοιχείων. Η διαδικασία μπορεί να διαρκέσει μερικά λεπτά.",
|
||||||
"check_logs": "Ελέγξτε τα αρχεία καταγραφής",
|
"check_logs": "Ελέγξτε τα αρχεία καταγραφής",
|
||||||
"choose_matching_people_to_merge": "Επιλέξτε τα αντίστοιχα άτομα για συγχώνευση",
|
"choose_matching_people_to_merge": "Επιλέξτε τα αντίστοιχα άτομα για συγχώνευση",
|
||||||
"city": "Πόλη",
|
"city": "Πόλη",
|
||||||
@@ -644,7 +643,7 @@
|
|||||||
"comments_are_disabled": "Τα σχόλια είναι απενεργοποιημένα",
|
"comments_are_disabled": "Τα σχόλια είναι απενεργοποιημένα",
|
||||||
"common_create_new_album": "Δημιουργία νέου άλμπουμ",
|
"common_create_new_album": "Δημιουργία νέου άλμπουμ",
|
||||||
"common_server_error": "Ελέγξτε τη σύνδεσή σας, βεβαιωθείτε ότι ο διακομιστής είναι προσβάσιμος και ότι οι εκδόσεις της εφαρμογής/διακομιστή είναι συμβατές.",
|
"common_server_error": "Ελέγξτε τη σύνδεσή σας, βεβαιωθείτε ότι ο διακομιστής είναι προσβάσιμος και ότι οι εκδόσεις της εφαρμογής/διακομιστή είναι συμβατές.",
|
||||||
"completed": "Completed",
|
"completed": "Ολοκληρώθηκε",
|
||||||
"confirm": "Επιβεβαίωση",
|
"confirm": "Επιβεβαίωση",
|
||||||
"confirm_admin_password": "Επιβεβαίωση κωδικού Διαχειριστή",
|
"confirm_admin_password": "Επιβεβαίωση κωδικού Διαχειριστή",
|
||||||
"confirm_delete_face": "Είστε σίγουροι ότι θέλετε να διαγράψετε το πρόσωπο του/της {name} από το στοιχείο;",
|
"confirm_delete_face": "Είστε σίγουροι ότι θέλετε να διαγράψετε το πρόσωπο του/της {name} από το στοιχείο;",
|
||||||
@@ -660,7 +659,7 @@
|
|||||||
"control_bottom_app_bar_delete_from_local": "Διαγραφή από τη συσκευή",
|
"control_bottom_app_bar_delete_from_local": "Διαγραφή από τη συσκευή",
|
||||||
"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_share_link": "Share Link",
|
"control_bottom_app_bar_share_link": "Κοινοποιήστε το σύνδεσμο",
|
||||||
"control_bottom_app_bar_share_to": "Κοινοποίηση Σε",
|
"control_bottom_app_bar_share_to": "Κοινοποίηση Σε",
|
||||||
"control_bottom_app_bar_trash_from_immich": "Μετακίνηση στα Απορρίμματα",
|
"control_bottom_app_bar_trash_from_immich": "Μετακίνηση στα Απορρίμματα",
|
||||||
"copied_image_to_clipboard": "Η εικόνα αντιγράφηκε στο πρόχειρο.",
|
"copied_image_to_clipboard": "Η εικόνα αντιγράφηκε στο πρόχειρο.",
|
||||||
@@ -695,7 +694,7 @@
|
|||||||
"crop": "Αποκοπή",
|
"crop": "Αποκοπή",
|
||||||
"curated_object_page_title": "Πράγματα",
|
"curated_object_page_title": "Πράγματα",
|
||||||
"current_device": "Τρέχουσα συσκευή",
|
"current_device": "Τρέχουσα συσκευή",
|
||||||
"current_server_address": "Current server address",
|
"current_server_address": "Τρέχουσα διεύθυνση διακομιστή",
|
||||||
"custom_locale": "Προσαρμοσμένη Τοπική Ρύθμιση",
|
"custom_locale": "Προσαρμοσμένη Τοπική Ρύθμιση",
|
||||||
"custom_locale_description": "Μορφοποιήστε τις ημερομηνίες και τους αριθμούς, σύμφωνα με τη γλώσσα και την περιοχή",
|
"custom_locale_description": "Μορφοποιήστε τις ημερομηνίες και τους αριθμούς, σύμφωνα με τη γλώσσα και την περιοχή",
|
||||||
"daily_title_text_date": "Ε, MMM dd",
|
"daily_title_text_date": "Ε, MMM dd",
|
||||||
@@ -746,7 +745,7 @@
|
|||||||
"direction": "Κατεύθυνση",
|
"direction": "Κατεύθυνση",
|
||||||
"disabled": "Απενεργοποιημένο",
|
"disabled": "Απενεργοποιημένο",
|
||||||
"disallow_edits": "Απαγόρευση επεξεργασιών",
|
"disallow_edits": "Απαγόρευση επεξεργασιών",
|
||||||
"discord": "Discord",
|
"discord": "Πλατφόρμα Discord",
|
||||||
"discover": "Ανίχνευση",
|
"discover": "Ανίχνευση",
|
||||||
"dismiss_all_errors": "Παράβλεψη όλων των σφαλμάτων",
|
"dismiss_all_errors": "Παράβλεψη όλων των σφαλμάτων",
|
||||||
"dismiss_error": "Παράβλεψη σφάλματος",
|
"dismiss_error": "Παράβλεψη σφάλματος",
|
||||||
@@ -807,16 +806,16 @@
|
|||||||
"editor_crop_tool_h2_aspect_ratios": "Αναλογίες διαστάσεων",
|
"editor_crop_tool_h2_aspect_ratios": "Αναλογίες διαστάσεων",
|
||||||
"editor_crop_tool_h2_rotation": "Περιστροφή",
|
"editor_crop_tool_h2_rotation": "Περιστροφή",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"empty_folder": "This folder is empty",
|
"empty_folder": "Αυτός ο φάκελος είναι κενός",
|
||||||
"empty_trash": "Άδειασμα κάδου απορριμμάτων",
|
"empty_trash": "Άδειασμα κάδου απορριμμάτων",
|
||||||
"empty_trash_confirmation": "Είστε σίγουροι οτι θέλετε να αδειάσετε τον κάδο απορριμμάτων; Αυτό θα αφαιρέσει μόνιμα όλα τα στοιχεία του κάδου απορριμμάτων του Immich. \nΑυτή η ενέργεια δεν μπορεί να αναιρεθεί!",
|
"empty_trash_confirmation": "Είστε σίγουροι οτι θέλετε να αδειάσετε τον κάδο απορριμμάτων; Αυτό θα αφαιρέσει μόνιμα όλα τα στοιχεία του κάδου απορριμμάτων του Immich. \nΑυτή η ενέργεια δεν μπορεί να αναιρεθεί!",
|
||||||
"enable": "Ενεργοποίηση",
|
"enable": "Ενεργοποίηση",
|
||||||
"enabled": "Ενεργοποιημένο",
|
"enabled": "Ενεργοποιημένο",
|
||||||
"end_date": "Τελική ημερομηνία",
|
"end_date": "Τελική ημερομηνία",
|
||||||
"enqueued": "Enqueued",
|
"enqueued": "Τοποθετήθηκε στη λίστα αναμονής",
|
||||||
"enter_wifi_name": "Enter WiFi name",
|
"enter_wifi_name": "Εισαγωγή ονόματος Wi-Fi",
|
||||||
"error": "Σφάλμα",
|
"error": "Σφάλμα",
|
||||||
"error_change_sort_album": "Failed to change album sort order",
|
"error_change_sort_album": "Απέτυχε η αλλαγή σειράς του άλμπουμ",
|
||||||
"error_delete_face": "Σφάλμα διαγραφής προσώπου από το στοιχείο",
|
"error_delete_face": "Σφάλμα διαγραφής προσώπου από το στοιχείο",
|
||||||
"error_loading_image": "Σφάλμα κατά τη φόρτωση της εικόνας",
|
"error_loading_image": "Σφάλμα κατά τη φόρτωση της εικόνας",
|
||||||
"error_saving_image": "Σφάλμα: {}",
|
"error_saving_image": "Σφάλμα: {}",
|
||||||
@@ -853,6 +852,7 @@
|
|||||||
"failed_to_remove_product_key": "Αποτυχία αφαίρεσης κλειδιού προϊόντος",
|
"failed_to_remove_product_key": "Αποτυχία αφαίρεσης κλειδιού προϊόντος",
|
||||||
"failed_to_stack_assets": "Αποτυχία στην συμπίεση των στοιχείων",
|
"failed_to_stack_assets": "Αποτυχία στην συμπίεση των στοιχείων",
|
||||||
"failed_to_unstack_assets": "Αποτυχία στην αποσυμπίεση των στοιχείων",
|
"failed_to_unstack_assets": "Αποτυχία στην αποσυμπίεση των στοιχείων",
|
||||||
|
"failed_to_update_notification_status": "Αποτυχία ενημέρωσης της κατάστασης ειδοποίησης",
|
||||||
"import_path_already_exists": "Αυτή η διαδρομή εισαγωγής υπάρχει ήδη.",
|
"import_path_already_exists": "Αυτή η διαδρομή εισαγωγής υπάρχει ήδη.",
|
||||||
"incorrect_email_or_password": "Λανθασμένο email ή κωδικός πρόσβασης",
|
"incorrect_email_or_password": "Λανθασμένο email ή κωδικός πρόσβασης",
|
||||||
"paths_validation_failed": "{paths, plural, one {# διαδρομή} other {# διαδρομές}} απέτυχαν κατά την επικύρωση",
|
"paths_validation_failed": "{paths, plural, one {# διαδρομή} other {# διαδρομές}} απέτυχαν κατά την επικύρωση",
|
||||||
@@ -951,12 +951,12 @@
|
|||||||
"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_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": "Ηλικία {}",
|
||||||
"exif_bottom_sheet_person_age_months": "Age {} months",
|
"exif_bottom_sheet_person_age_months": "Ηλικία {} μήνες",
|
||||||
"exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months",
|
"exif_bottom_sheet_person_age_year_months": "Ηλικία 1 έτους, {} μηνών",
|
||||||
"exif_bottom_sheet_person_age_years": "Age {}",
|
"exif_bottom_sheet_person_age_years": "Ηλικία {}",
|
||||||
"exit_slideshow": "Έξοδος από την παρουσίαση",
|
"exit_slideshow": "Έξοδος από την παρουσίαση",
|
||||||
"expand_all": "Ανάπτυξη όλων",
|
"expand_all": "Ανάπτυξη όλων",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Σε εξέλιξη",
|
"experimental_settings_new_asset_list_subtitle": "Σε εξέλιξη",
|
||||||
@@ -973,12 +973,12 @@
|
|||||||
"extension": "Επέκταση",
|
"extension": "Επέκταση",
|
||||||
"external": "Εξωτερικός",
|
"external": "Εξωτερικός",
|
||||||
"external_libraries": "Εξωτερικές βιβλιοθήκες",
|
"external_libraries": "Εξωτερικές βιβλιοθήκες",
|
||||||
"external_network": "External network",
|
"external_network": "Εξωτερικό δίκτυο",
|
||||||
"external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom",
|
"external_network_sheet_info": "Όταν δεν είστε συνδεδεμένοι στο προτιμώμενο δίκτυο Wi-Fi, η εφαρμογή θα συνδεθεί με τον διακομιστή μέσω του πρώτου από τα παρακάτω URLs που μπορεί να βρει διαθέσιμο, ξεκινώντας από το πάνω προς το κάτω",
|
||||||
"face_unassigned": "Μη ανατεθειμένο",
|
"face_unassigned": "Μη ανατεθειμένο",
|
||||||
"failed": "Failed",
|
"failed": "Απέτυχε",
|
||||||
"failed_to_load_assets": "Αποτυχία φόρτωσης στοιχείων",
|
"failed_to_load_assets": "Αποτυχία φόρτωσης στοιχείων",
|
||||||
"failed_to_load_folder": "Failed to load folder",
|
"failed_to_load_folder": "Αποτυχία φόρτωσης φακέλου",
|
||||||
"favorite": "Αγαπημένο",
|
"favorite": "Αγαπημένο",
|
||||||
"favorite_or_unfavorite_photo": "Ορίστε μία φωτογραφία ως αγαπημένη ή αφαιρέστε την από τα αγαπημένα",
|
"favorite_or_unfavorite_photo": "Ορίστε μία φωτογραφία ως αγαπημένη ή αφαιρέστε την από τα αγαπημένα",
|
||||||
"favorites": "Αγαπημένα",
|
"favorites": "Αγαπημένα",
|
||||||
@@ -992,21 +992,22 @@
|
|||||||
"filetype": "Τύπος αρχείου",
|
"filetype": "Τύπος αρχείου",
|
||||||
"filter": "Φίλτρο",
|
"filter": "Φίλτρο",
|
||||||
"filter_people": "Φιλτράρισμα ατόμων",
|
"filter_people": "Φιλτράρισμα ατόμων",
|
||||||
|
"filter_places": "Φιλτράρισμα τοποθεσιών",
|
||||||
"find_them_fast": "Βρείτε τους γρήγορα με αναζήτηση κατά όνομα",
|
"find_them_fast": "Βρείτε τους γρήγορα με αναζήτηση κατά όνομα",
|
||||||
"fix_incorrect_match": "Διόρθωση λανθασμένης αντιστοίχισης",
|
"fix_incorrect_match": "Διόρθωση λανθασμένης αντιστοίχισης",
|
||||||
"folder": "Folder",
|
"folder": "Φάκελος",
|
||||||
"folder_not_found": "Folder not found",
|
"folder_not_found": "Ο φάκελος δεν βρέθηκε",
|
||||||
"folders": "Φάκελοι",
|
"folders": "Φάκελοι",
|
||||||
"folders_feature_description": "Περιήγηση στην προβολή φακέλου για τις φωτογραφίες και τα βίντεο στο σύστημα αρχείων",
|
"folders_feature_description": "Περιήγηση στην προβολή φακέλου για τις φωτογραφίες και τα βίντεο στο σύστημα αρχείων",
|
||||||
"forward": "Προς τα εμπρός",
|
"forward": "Προς τα εμπρός",
|
||||||
"general": "Γενικά",
|
"general": "Γενικά",
|
||||||
"get_help": "Ζητήστε βοήθεια",
|
"get_help": "Ζητήστε βοήθεια",
|
||||||
"get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network",
|
"get_wifiname_error": "Δεν ήταν δυνατή η λήψη του ονόματος Wi-Fi. Βεβαιωθείτε ότι έχετε δώσει τις απαραίτητες άδειες και ότι είστε συνδεδεμένοι σε δίκτυο Wi-Fi",
|
||||||
"getting_started": "Ξεκινώντας",
|
"getting_started": "Ξεκινώντας",
|
||||||
"go_back": "Πηγαίνετε πίσω",
|
"go_back": "Πηγαίνετε πίσω",
|
||||||
"go_to_folder": "Μετάβαση στο φάκελο",
|
"go_to_folder": "Μετάβαση στο φάκελο",
|
||||||
"go_to_search": "Πηγαίνετε στην αναζήτηση",
|
"go_to_search": "Πηγαίνετε στην αναζήτηση",
|
||||||
"grant_permission": "Grant permission",
|
"grant_permission": "Επιτρέψτε την άδεια",
|
||||||
"group_albums_by": "Ομαδοποίηση άλμπουμ κατά...",
|
"group_albums_by": "Ομαδοποίηση άλμπουμ κατά...",
|
||||||
"group_country": "Ομαδοποίηση κατά χώρα",
|
"group_country": "Ομαδοποίηση κατά χώρα",
|
||||||
"group_no": "Καμία ομοδοποίηση",
|
"group_no": "Καμία ομοδοποίηση",
|
||||||
@@ -1040,7 +1041,7 @@
|
|||||||
"home_page_delete_remote_err_local": "Τοπικά στοιχεία στη διαγραφή απομακρυσμένης επιλογής, παραλείπεται",
|
"home_page_delete_remote_err_local": "Τοπικά στοιχεία στη διαγραφή απομακρυσμένης επιλογής, παραλείπεται",
|
||||||
"home_page_favorite_err_local": "Δεν μπορώ ακόμα να αγαπήσω τα τοπικά στοιχεία, παραλείπεται",
|
"home_page_favorite_err_local": "Δεν μπορώ ακόμα να αγαπήσω τα τοπικά στοιχεία, παραλείπεται",
|
||||||
"home_page_favorite_err_partner": "Δεν είναι ακόμα δυνατή η πρόσθεση στοιχείων συντρόφου στα αγαπημένα, παραλείπεται",
|
"home_page_favorite_err_partner": "Δεν είναι ακόμα δυνατή η πρόσθεση στοιχείων συντρόφου στα αγαπημένα, παραλείπεται",
|
||||||
"home_page_first_time_notice": "Εάν αυτή είναι η πρώτη φορά που χρησιμοποιείτε την εφαρμογή, βεβαιωθείτε ότι έχετε επιλέξει ένα άλμπουμ αντίγραφου ασφαλείας, ώστε το χρονοδιάγραμμα να μπορεί να συμπληρώσει φωτογραφίες και βίντεο στα άλμπουμ.",
|
"home_page_first_time_notice": "Εάν αυτή είναι η πρώτη φορά που χρησιμοποιείτε την εφαρμογή, βεβαιωθείτε ότι έχετε επιλέξει ένα άλμπουμ αντίγραφου ασφαλείας, ώστε το χρονοδιάγραμμα να μπορεί να συμπληρώσει φωτογραφίες και βίντεο στα άλμπουμ",
|
||||||
"home_page_share_err_local": "Δεν είναι δυνατή η κοινή χρήση τοπικών στοιχείων μέσω συνδέσμου, παραλείπεται",
|
"home_page_share_err_local": "Δεν είναι δυνατή η κοινή χρήση τοπικών στοιχείων μέσω συνδέσμου, παραλείπεται",
|
||||||
"home_page_upload_err_limit": "Μπορείτε να ανεβάσετε μόνο 30 στοιχεία κάθε φορά, παραλείπεται",
|
"home_page_upload_err_limit": "Μπορείτε να ανεβάσετε μόνο 30 στοιχεία κάθε φορά, παραλείπεται",
|
||||||
"host": "Φιλοξενία",
|
"host": "Φιλοξενία",
|
||||||
@@ -1118,9 +1119,9 @@
|
|||||||
"loading": "Φόρτωση",
|
"loading": "Φόρτωση",
|
||||||
"loading_search_results_failed": "Η φόρτωση αποτελεσμάτων αναζήτησης απέτυχε",
|
"loading_search_results_failed": "Η φόρτωση αποτελεσμάτων αναζήτησης απέτυχε",
|
||||||
"local_network": "Local network",
|
"local_network": "Local network",
|
||||||
"local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network",
|
"local_network_sheet_info": "Η εφαρμογή θα συνδεθεί με τον διακομιστή μέσω αυτού του URL όταν χρησιμοποιείται το καθορισμένο δίκτυο Wi-Fi",
|
||||||
"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": "Για να χρησιμοποιηθεί η λειτουργία αυτόματης εναλλαγής, το Immich χρειάζεται άδεια για την ακριβή τοποθεσία της συσκευής ώστε να μπορεί να διαβάζει το όνομα του τρέχοντος δικτύου Wi-Fi",
|
||||||
"location_picker_choose_on_map": "Επιλέξτε στο χάρτη",
|
"location_picker_choose_on_map": "Επιλέξτε στο χάρτη",
|
||||||
"location_picker_latitude_error": "Εισαγάγετε ένα έγκυρο γεωγραφικό πλάτος",
|
"location_picker_latitude_error": "Εισαγάγετε ένα έγκυρο γεωγραφικό πλάτος",
|
||||||
"location_picker_latitude_hint": "Εισαγάγετε το γεωγραφικό πλάτος σας εδώ",
|
"location_picker_latitude_hint": "Εισαγάγετε το γεωγραφικό πλάτος σας εδώ",
|
||||||
@@ -1194,6 +1195,9 @@
|
|||||||
"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": "Σμικρύνετε για να δείτε φωτογραφίες",
|
||||||
|
"mark_all_as_read": "Επισήμανση όλων ως αναγνωσμένα",
|
||||||
|
"mark_as_read": "Επισήμανση ως αναγνωσμένο",
|
||||||
|
"marked_all_as_read": "Όλα επισημάνθηκαν ως αναγνωσμένα",
|
||||||
"matches": "Αντιστοιχίες",
|
"matches": "Αντιστοιχίες",
|
||||||
"media_type": "Τύπος πολυμέσου",
|
"media_type": "Τύπος πολυμέσου",
|
||||||
"memories": "Αναμνήσεις",
|
"memories": "Αναμνήσεις",
|
||||||
@@ -1203,7 +1207,7 @@
|
|||||||
"memories_start_over": "Ξεκινήστε από την αρχή",
|
"memories_start_over": "Ξεκινήστε από την αρχή",
|
||||||
"memories_swipe_to_close": "Σύρετε προς τα πάνω για να κλείσετε",
|
"memories_swipe_to_close": "Σύρετε προς τα πάνω για να κλείσετε",
|
||||||
"memories_year_ago": "Πριν ένα χρόνο",
|
"memories_year_ago": "Πριν ένα χρόνο",
|
||||||
"memories_years_ago": "Πριν από {} χρόνια",
|
"memories_years_ago": "Πριν από {} έτη",
|
||||||
"memory": "Ανάμνηση",
|
"memory": "Ανάμνηση",
|
||||||
"memory_lane_title": "Διαδρομή Αναμνήσεων {title}",
|
"memory_lane_title": "Διαδρομή Αναμνήσεων {title}",
|
||||||
"menu": "Μενού",
|
"menu": "Μενού",
|
||||||
@@ -1227,8 +1231,8 @@
|
|||||||
"my_albums": "Τα άλμπουμ μου",
|
"my_albums": "Τα άλμπουμ μου",
|
||||||
"name": "Όνομα",
|
"name": "Όνομα",
|
||||||
"name_or_nickname": "Όνομα ή ψευδώνυμο",
|
"name_or_nickname": "Όνομα ή ψευδώνυμο",
|
||||||
"networking_settings": "Networking",
|
"networking_settings": "Δικτύωση",
|
||||||
"networking_subtitle": "Manage the server endpoint settings",
|
"networking_subtitle": "Διαχείριση ρυθμίσεων τελικών σημείων διακομιστή",
|
||||||
"never": "Ποτέ",
|
"never": "Ποτέ",
|
||||||
"new_album": "Νέο Άλμπουμ",
|
"new_album": "Νέο Άλμπουμ",
|
||||||
"new_api_key": "Νέο API Key",
|
"new_api_key": "Νέο API Key",
|
||||||
@@ -1252,12 +1256,13 @@
|
|||||||
"no_favorites_message": "Προσθέστε αγαπημένα για να βρείτε γρήγορα τις καλύτερες φωτογραφίες και τα βίντεό σας",
|
"no_favorites_message": "Προσθέστε αγαπημένα για να βρείτε γρήγορα τις καλύτερες φωτογραφίες και τα βίντεό σας",
|
||||||
"no_libraries_message": "Δημιουργήστε μια εξωτερική βιβλιοθήκη για να προβάλετε τις φωτογραφίες και τα βίντεό σας",
|
"no_libraries_message": "Δημιουργήστε μια εξωτερική βιβλιοθήκη για να προβάλετε τις φωτογραφίες και τα βίντεό σας",
|
||||||
"no_name": "Χωρίς Όνομα",
|
"no_name": "Χωρίς Όνομα",
|
||||||
|
"no_notifications": "Καμία ειδοποίηση",
|
||||||
"no_places": "Καμία τοποθεσία",
|
"no_places": "Καμία τοποθεσία",
|
||||||
"no_results": "Κανένα αποτέλεσμα",
|
"no_results": "Κανένα αποτέλεσμα",
|
||||||
"no_results_description": "Δοκιμάστε ένα συνώνυμο ή πιο γενική λέξη-κλειδί",
|
"no_results_description": "Δοκιμάστε ένα συνώνυμο ή πιο γενική λέξη-κλειδί",
|
||||||
"no_shared_albums_message": "Δημιουργήστε ένα άλμπουμ για να μοιράζεστε φωτογραφίες και βίντεο με άτομα στο δίκτυό σας",
|
"no_shared_albums_message": "Δημιουργήστε ένα άλμπουμ για να μοιράζεστε φωτογραφίες και βίντεο με άτομα στο δίκτυό σας",
|
||||||
"not_in_any_album": "Σε κανένα άλμπουμ",
|
"not_in_any_album": "Σε κανένα άλμπουμ",
|
||||||
"not_selected": "Not selected",
|
"not_selected": "Δεν επιλέχθηκε",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "Σημείωση: Για να εφαρμόσετε την Ετικέτα Αποθήκευσης σε στοιχεία που έχουν μεταφορτωθεί προηγουμένως, εκτελέστε το",
|
"note_apply_storage_label_to_previously_uploaded assets": "Σημείωση: Για να εφαρμόσετε την Ετικέτα Αποθήκευσης σε στοιχεία που έχουν μεταφορτωθεί προηγουμένως, εκτελέστε το",
|
||||||
"notes": "Σημειώσεις",
|
"notes": "Σημειώσεις",
|
||||||
"notification_permission_dialog_content": "Για να ενεργοποιήσετε τις ειδοποιήσεις, μεταβείτε στις Ρυθμίσεις και επιλέξτε να επιτρέπεται.",
|
"notification_permission_dialog_content": "Για να ενεργοποιήσετε τις ειδοποιήσεις, μεταβείτε στις Ρυθμίσεις και επιλέξτε να επιτρέπεται.",
|
||||||
@@ -1282,6 +1287,7 @@
|
|||||||
"onboarding_welcome_user": "Καλωσόρισες, {user}",
|
"onboarding_welcome_user": "Καλωσόρισες, {user}",
|
||||||
"online": "Σε σύνδεση",
|
"online": "Σε σύνδεση",
|
||||||
"only_favorites": "Μόνο αγαπημένα",
|
"only_favorites": "Μόνο αγαπημένα",
|
||||||
|
"open": "Άνοιγμα",
|
||||||
"open_in_map_view": "Άνοιγμα σε προβολή χάρτη",
|
"open_in_map_view": "Άνοιγμα σε προβολή χάρτη",
|
||||||
"open_in_openstreetmap": "Άνοιγμα στο OpenStreetMap",
|
"open_in_openstreetmap": "Άνοιγμα στο OpenStreetMap",
|
||||||
"open_the_search_filters": "Ανοίξτε τα φίλτρα αναζήτησης",
|
"open_the_search_filters": "Ανοίξτε τα φίλτρα αναζήτησης",
|
||||||
@@ -1359,7 +1365,7 @@
|
|||||||
"play_motion_photo": "Αναπαραγωγή Κινούμενης Φωτογραφίας",
|
"play_motion_photo": "Αναπαραγωγή Κινούμενης Φωτογραφίας",
|
||||||
"play_or_pause_video": "Αναπαραγωγή ή παύση βίντεο",
|
"play_or_pause_video": "Αναπαραγωγή ή παύση βίντεο",
|
||||||
"port": "Θύρα",
|
"port": "Θύρα",
|
||||||
"preferences_settings_subtitle": "Manage the app's preferences",
|
"preferences_settings_subtitle": "Διαχειριστείτε τις προτιμήσεις της εφαρμογής",
|
||||||
"preferences_settings_title": "Προτιμήσεις",
|
"preferences_settings_title": "Προτιμήσεις",
|
||||||
"preset": "Προκαθορισμένη ρύθμιση",
|
"preset": "Προκαθορισμένη ρύθμιση",
|
||||||
"preview": "Προεπισκόπηση",
|
"preview": "Προεπισκόπηση",
|
||||||
@@ -1381,7 +1387,7 @@
|
|||||||
"public_share": "Δημόσια Κοινή Χρήση",
|
"public_share": "Δημόσια Κοινή Χρήση",
|
||||||
"purchase_account_info": "Υποστηρικτής",
|
"purchase_account_info": "Υποστηρικτής",
|
||||||
"purchase_activated_subtitle": "Σας ευχαριστούμε για την υποστήριξη του Immich και λογισμικών ανοιχτού κώδικα",
|
"purchase_activated_subtitle": "Σας ευχαριστούμε για την υποστήριξη του Immich και λογισμικών ανοιχτού κώδικα",
|
||||||
"purchase_activated_time": "Ενεργοποιήθηκε στις {date, date}",
|
"purchase_activated_time": "Ενεργοποιήθηκε στις {date}",
|
||||||
"purchase_activated_title": "Το κλειδί σας ενεργοποιήθηκε με επιτυχία",
|
"purchase_activated_title": "Το κλειδί σας ενεργοποιήθηκε με επιτυχία",
|
||||||
"purchase_button_activate": "Ενεργοποίηση",
|
"purchase_button_activate": "Ενεργοποίηση",
|
||||||
"purchase_button_buy": "Αγορά",
|
"purchase_button_buy": "Αγορά",
|
||||||
@@ -1426,6 +1432,8 @@
|
|||||||
"recent_searches": "Πρόσφατες αναζητήσεις",
|
"recent_searches": "Πρόσφατες αναζητήσεις",
|
||||||
"recently_added": "Προστέθηκαν πρόσφατα",
|
"recently_added": "Προστέθηκαν πρόσφατα",
|
||||||
"recently_added_page_title": "Προστέθηκαν Πρόσφατα",
|
"recently_added_page_title": "Προστέθηκαν Πρόσφατα",
|
||||||
|
"recently_taken": "Λήφθηκαν πρόσφατα",
|
||||||
|
"recently_taken_page_title": "Λήφθηκαν Πρόσφατα",
|
||||||
"refresh": "Ανανέωση",
|
"refresh": "Ανανέωση",
|
||||||
"refresh_encoded_videos": "Ανανέωση κωδικοποιημένων βίντεο",
|
"refresh_encoded_videos": "Ανανέωση κωδικοποιημένων βίντεο",
|
||||||
"refresh_faces": "Ανανέωση προσώπων",
|
"refresh_faces": "Ανανέωση προσώπων",
|
||||||
@@ -1510,7 +1518,7 @@
|
|||||||
"search_filter_date_title": "Επιλέξτε εύρος ημερομηνιών",
|
"search_filter_date_title": "Επιλέξτε εύρος ημερομηνιών",
|
||||||
"search_filter_display_option_not_in_album": "Όχι στο άλμπουμ",
|
"search_filter_display_option_not_in_album": "Όχι στο άλμπουμ",
|
||||||
"search_filter_display_options": "Επιλογές εμφάνισης",
|
"search_filter_display_options": "Επιλογές εμφάνισης",
|
||||||
"search_filter_filename": "Search by file name",
|
"search_filter_filename": "Αναζήτηση με όνομα αρχείου",
|
||||||
"search_filter_location": "Τοποθεσία",
|
"search_filter_location": "Τοποθεσία",
|
||||||
"search_filter_location_title": "Επιλέξτε τοποθεσία",
|
"search_filter_location_title": "Επιλέξτε τοποθεσία",
|
||||||
"search_filter_media_type": "Τύπος Μέσου",
|
"search_filter_media_type": "Τύπος Μέσου",
|
||||||
@@ -1518,17 +1526,17 @@
|
|||||||
"search_filter_people_title": "Επιλέξτε άτομα",
|
"search_filter_people_title": "Επιλέξτε άτομα",
|
||||||
"search_for": "Αναζήτηση για",
|
"search_for": "Αναζήτηση για",
|
||||||
"search_for_existing_person": "Αναζήτηση υπάρχοντος ατόμου",
|
"search_for_existing_person": "Αναζήτηση υπάρχοντος ατόμου",
|
||||||
"search_no_more_result": "No more results",
|
"search_no_more_result": "Δεν υπάρχουν άλλα αποτελέσματα",
|
||||||
"search_no_people": "Κανένα άτομο",
|
"search_no_people": "Κανένα άτομο",
|
||||||
"search_no_people_named": "Κανένα άτομο με όνομα \"{name}\"",
|
"search_no_people_named": "Κανένα άτομο με όνομα \"{name}\"",
|
||||||
"search_no_result": "No results found, try a different search term or combination",
|
"search_no_result": "Δεν βρέθηκαν αποτελέσματα, προσπαθήστε με διαφορετικές ορολογίες αναζήτησης ή συνδυασμούς",
|
||||||
"search_options": "Επιλογές αναζήτησης",
|
"search_options": "Επιλογές αναζήτησης",
|
||||||
"search_page_categories": "Κατηγορίες",
|
"search_page_categories": "Κατηγορίες",
|
||||||
"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_screenshots": "Στιγμιότυπα οθόνης",
|
"search_page_screenshots": "Στιγμιότυπα οθόνης",
|
||||||
"search_page_search_photos_videos": "Search for your photos and videos",
|
"search_page_search_photos_videos": "Αναζήτηση για τις φωτογραφίες και τα βίντεό σας",
|
||||||
"search_page_selfies": "Σέλφι",
|
"search_page_selfies": "Σέλφι",
|
||||||
"search_page_things": "Πράγματα",
|
"search_page_things": "Πράγματα",
|
||||||
"search_page_view_all_button": "Προβολή όλων",
|
"search_page_view_all_button": "Προβολή όλων",
|
||||||
@@ -1540,7 +1548,7 @@
|
|||||||
"search_result_page_new_search_hint": "Νέα Αναζήτηση",
|
"search_result_page_new_search_hint": "Νέα Αναζήτηση",
|
||||||
"search_settings": "Ρυθμίσεις αναζήτησης",
|
"search_settings": "Ρυθμίσεις αναζήτησης",
|
||||||
"search_state": "Αναζήτηση νομού...",
|
"search_state": "Αναζήτηση νομού...",
|
||||||
"search_suggestion_list_smart_search_hint_1": "Η έξυπνη αναζήτηση είναι ενεργοποιημένη από προεπιλογή, για αναζήτηση μεταδεδομένων χρησιμοποιήστε το συντακτικό",
|
"search_suggestion_list_smart_search_hint_1": "Η έξυπνη αναζήτηση είναι ενεργοποιημένη από προεπιλογή, για αναζήτηση μεταδεδομένων χρησιμοποιήστε το συντακτικό ",
|
||||||
"search_suggestion_list_smart_search_hint_2": "m:όρος-αναζήτησης",
|
"search_suggestion_list_smart_search_hint_2": "m:όρος-αναζήτησης",
|
||||||
"search_tags": "Αναζήτηση ετικετών...",
|
"search_tags": "Αναζήτηση ετικετών...",
|
||||||
"search_timezone": "Αναζήτηση ζώνης ώρας...",
|
"search_timezone": "Αναζήτηση ζώνης ώρας...",
|
||||||
@@ -1567,7 +1575,7 @@
|
|||||||
"selected_count": "{count, plural, other {# επιλεγμένοι}}",
|
"selected_count": "{count, plural, other {# επιλεγμένοι}}",
|
||||||
"send_message": "Αποστολή μηνύματος",
|
"send_message": "Αποστολή μηνύματος",
|
||||||
"send_welcome_email": "Αποστολή email καλωσορίσματος",
|
"send_welcome_email": "Αποστολή email καλωσορίσματος",
|
||||||
"server_endpoint": "Server Endpoint",
|
"server_endpoint": "Τελικό σημείο Διακομιστή",
|
||||||
"server_info_box_app_version": "Έκδοση εφαρμογής",
|
"server_info_box_app_version": "Έκδοση εφαρμογής",
|
||||||
"server_info_box_server_url": "URL διακομιστή",
|
"server_info_box_server_url": "URL διακομιστή",
|
||||||
"server_offline": "Διακομιστής Εκτός Σύνδεσης",
|
"server_offline": "Διακομιστής Εκτός Σύνδεσης",
|
||||||
@@ -1588,7 +1596,7 @@
|
|||||||
"setting_image_viewer_preview_title": "Φόρτωση εικόνας προεπισκόπησης",
|
"setting_image_viewer_preview_title": "Φόρτωση εικόνας προεπισκόπησης",
|
||||||
"setting_image_viewer_title": "Εικόνες",
|
"setting_image_viewer_title": "Εικόνες",
|
||||||
"setting_languages_apply": "Εφαρμογή",
|
"setting_languages_apply": "Εφαρμογή",
|
||||||
"setting_languages_subtitle": "Change the app's language",
|
"setting_languages_subtitle": "Αλλάξτε τη γλώσσα της εφαρμογής",
|
||||||
"setting_languages_title": "Γλώσσες",
|
"setting_languages_title": "Γλώσσες",
|
||||||
"setting_notifications_notify_failures_grace_period": "Ειδοποίηση αποτυχιών δημιουργίας αντιγράφων ασφαλείας στο παρασκήνιο: {}",
|
"setting_notifications_notify_failures_grace_period": "Ειδοποίηση αποτυχιών δημιουργίας αντιγράφων ασφαλείας στο παρασκήνιο: {}",
|
||||||
"setting_notifications_notify_hours": "{} ώρες",
|
"setting_notifications_notify_hours": "{} ώρες",
|
||||||
@@ -1602,8 +1610,8 @@
|
|||||||
"setting_notifications_total_progress_subtitle": "Συνολική πρόοδος μεταφόρτωσης (ολοκληρώθηκε/σύνολο στοιχείων)",
|
"setting_notifications_total_progress_subtitle": "Συνολική πρόοδος μεταφόρτωσης (ολοκληρώθηκε/σύνολο στοιχείων)",
|
||||||
"setting_notifications_total_progress_title": "Εμφάνιση συνολικής προόδου δημιουργίας αντιγράφων ασφαλείας παρασκηνίου",
|
"setting_notifications_total_progress_title": "Εμφάνιση συνολικής προόδου δημιουργίας αντιγράφων ασφαλείας παρασκηνίου",
|
||||||
"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": "Όταν μεταδίδετε ένα βίντεο από τον διακομιστή, αναπαράγετε το αυθεντικό ακόμη και όταν υπάρχει διαθέσιμο με διαφορετική κωδικοποίηση. Μπορεί να προκαλέσει καθυστέρηση φόρτωσης. Τα βίντεο που είναι διαθέσιμα τοπικά, αναπαράγονται στην αυθεντική ποιότητα, ανεξαρτήτως αυτής της ρύθμισης.",
|
||||||
"setting_video_viewer_original_video_title": "Force original video",
|
"setting_video_viewer_original_video_title": "Αναγκαστική αναπαραγωγή αυθεντικού βίντεο",
|
||||||
"settings": "Ρυθμίσεις",
|
"settings": "Ρυθμίσεις",
|
||||||
"settings_require_restart": "Επανεκκινήστε το Immich για να εφαρμόσετε αυτήν τη ρύθμιση",
|
"settings_require_restart": "Επανεκκινήστε το Immich για να εφαρμόσετε αυτήν τη ρύθμιση",
|
||||||
"settings_saved": "Οι ρυθμίσεις αποθηκεύτηκαν",
|
"settings_saved": "Οι ρυθμίσεις αποθηκεύτηκαν",
|
||||||
@@ -1618,12 +1626,12 @@
|
|||||||
"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_title": "ΑΝΘΡΩΠΟΙ",
|
"shared_album_section_people_title": "ΑΤΟΜΑ",
|
||||||
"shared_by": "Σε κοινή χρήση από",
|
"shared_by": "Σε κοινή χρήση από",
|
||||||
"shared_by_user": "Σε κοινή χρήση από {user}",
|
"shared_by_user": "Σε κοινή χρήση από {user}",
|
||||||
"shared_by_you": "Σε κοινή χρήση από εσάς",
|
"shared_by_you": "Σε κοινή χρήση από εσάς",
|
||||||
"shared_from_partner": "Φωτογραφίες από {partner}",
|
"shared_from_partner": "Φωτογραφίες από {partner}",
|
||||||
"shared_intent_upload_button_progress_text": "{} / {} Uploaded",
|
"shared_intent_upload_button_progress_text": "{} / {} Μεταφορτωμένα",
|
||||||
"shared_link_app_bar_title": "Κοινόχρηστοι Σύνδεσμοι",
|
"shared_link_app_bar_title": "Κοινόχρηστοι Σύνδεσμοι",
|
||||||
"shared_link_clipboard_copied_massage": "Αντιγράφηκε στο πρόχειρο",
|
"shared_link_clipboard_copied_massage": "Αντιγράφηκε στο πρόχειρο",
|
||||||
"shared_link_clipboard_text": "Σύνδεσμος: {}\nΚωδικός πρόσβασης: {}",
|
"shared_link_clipboard_text": "Σύνδεσμος: {}\nΚωδικός πρόσβασης: {}",
|
||||||
@@ -1802,7 +1810,7 @@
|
|||||||
"unlink_motion_video": "Αποσυνδέστε το βίντεο κίνησης",
|
"unlink_motion_video": "Αποσυνδέστε το βίντεο κίνησης",
|
||||||
"unlink_oauth": "Αποσύνδεση OAuth",
|
"unlink_oauth": "Αποσύνδεση OAuth",
|
||||||
"unlinked_oauth_account": "Ο λογαριασμός OAuth αποσυνδέθηκε",
|
"unlinked_oauth_account": "Ο λογαριασμός OAuth αποσυνδέθηκε",
|
||||||
"unmute_memories": "Αποσυνδέστε τις αναμνήσεις",
|
"unmute_memories": "Ενεργοποίηση Αναμνήσεων",
|
||||||
"unnamed_album": "Ανώνυμο Άλμπουμ",
|
"unnamed_album": "Ανώνυμο Άλμπουμ",
|
||||||
"unnamed_album_delete_confirmation": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το άλμπουμ;",
|
"unnamed_album_delete_confirmation": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το άλμπουμ;",
|
||||||
"unnamed_share": "Ανώνυμη Κοινή Χρήση",
|
"unnamed_share": "Ανώνυμη Κοινή Χρήση",
|
||||||
@@ -1826,11 +1834,11 @@
|
|||||||
"upload_status_errors": "Σφάλματα",
|
"upload_status_errors": "Σφάλματα",
|
||||||
"upload_status_uploaded": "Μεταφορτώθηκαν",
|
"upload_status_uploaded": "Μεταφορτώθηκαν",
|
||||||
"upload_success": "Η μεταφόρτωση ολοκληρώθηκε, ανανεώστε τη σελίδα για να δείτε τα νέα αντικείμενα.",
|
"upload_success": "Η μεταφόρτωση ολοκληρώθηκε, ανανεώστε τη σελίδα για να δείτε τα νέα αντικείμενα.",
|
||||||
"upload_to_immich": "Upload to Immich ({})",
|
"upload_to_immich": "Μεταφόρτωση στο Immich ({})",
|
||||||
"uploading": "Uploading",
|
"uploading": "Μεταφορτώνεται",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Χρήση",
|
"usage": "Χρήση",
|
||||||
"use_current_connection": "use current connection",
|
"use_current_connection": "χρήση τρέχουσας σύνδεσης",
|
||||||
"use_custom_date_range": "Χρήση προσαρμοσμένου εύρους ημερομηνιών",
|
"use_custom_date_range": "Χρήση προσαρμοσμένου εύρους ημερομηνιών",
|
||||||
"user": "Χρήστης",
|
"user": "Χρήστης",
|
||||||
"user_id": "ID Χρήστη",
|
"user_id": "ID Χρήστη",
|
||||||
@@ -1845,14 +1853,14 @@
|
|||||||
"users": "Χρήστες",
|
"users": "Χρήστες",
|
||||||
"utilities": "Βοηθητικά προγράμματα",
|
"utilities": "Βοηθητικά προγράμματα",
|
||||||
"validate": "Επικύρωση",
|
"validate": "Επικύρωση",
|
||||||
"validate_endpoint_error": "Please enter a valid URL",
|
"validate_endpoint_error": "Παρακαλώ εισάγετε ένα έγκυρο URL",
|
||||||
"variables": "Μεταβλητές",
|
"variables": "Μεταβλητές",
|
||||||
"version": "Έκδοση",
|
"version": "Έκδοση",
|
||||||
"version_announcement_closing": "Ο φίλος σου, Alex",
|
"version_announcement_closing": "Ο φίλος σου, Alex",
|
||||||
"version_announcement_message": "Γειά σας! Μια νέα έκδοση του Immich είναι διαθέσιμη. Παρακαλούμε αφιερώστε λίγο χρόνο για να διαβάσετε τις <link>σημειώσεις έκδοσης</link> ώστε να βεβαιωθείτε ότι η ρύθμιση σας είναι ενημερωμένη και να αποφύγετε τυχόν σφάλματα, ειδικά αν χρησιμοποιείτε το WatchTower ή οποιοδήποτε μηχανισμό που διαχειρίζεται αυτόματα την ενημέρωση της εγκατάστασης του Immich σας.",
|
"version_announcement_message": "Γειά σας! Μια νέα έκδοση του Immich είναι διαθέσιμη. Παρακαλούμε αφιερώστε λίγο χρόνο για να διαβάσετε τις <link>σημειώσεις έκδοσης</link> ώστε να βεβαιωθείτε ότι η ρύθμιση σας είναι ενημερωμένη και να αποφύγετε τυχόν σφάλματα, ειδικά αν χρησιμοποιείτε το WatchTower ή οποιοδήποτε μηχανισμό που διαχειρίζεται αυτόματα την ενημέρωση της εγκατάστασης του Immich σας.",
|
||||||
"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": "παρακαλώ αφιερώστε χρόνο να επισκεφθείτε το ",
|
||||||
"version_announcement_overlay_text_3": " και βεβαιωθείτε ότι το docker-compose και το .env σας είναι ενημερωμένη για την αποφυγή τυχόν εσφαλμένων διαμορφώσεων, ειδικά εάν χρησιμοποιείτε το WatchTower ή οποιονδήποτε μηχανισμό που χειρίζεται την αυτόματη ενημέρωση του διακομιστή σας.",
|
"version_announcement_overlay_text_3": " και βεβαιωθείτε ότι το docker-compose και το .env σας είναι ενημερωμένη για την αποφυγή τυχόν εσφαλμένων διαμορφώσεων, ειδικά εάν χρησιμοποιείτε το WatchTower ή οποιονδήποτε μηχανισμό που χειρίζεται την αυτόματη ενημέρωση του διακομιστή σας.",
|
||||||
"version_announcement_overlay_title": "Διαθέσιμη νέα έκδοση διακομιστή 🎉",
|
"version_announcement_overlay_title": "Διαθέσιμη νέα έκδοση διακομιστή 🎉",
|
||||||
"version_history": "Ιστορικό Εκδόσεων",
|
"version_history": "Ιστορικό Εκδόσεων",
|
||||||
@@ -1883,11 +1891,11 @@
|
|||||||
"week": "Εβδομάδα",
|
"week": "Εβδομάδα",
|
||||||
"welcome": "Καλωσορίσατε",
|
"welcome": "Καλωσορίσατε",
|
||||||
"welcome_to_immich": "Καλωσορίσατε στο Ιmmich",
|
"welcome_to_immich": "Καλωσορίσατε στο Ιmmich",
|
||||||
"wifi_name": "WiFi Name",
|
"wifi_name": "Όνομα Wi-Fi",
|
||||||
"year": "Έτος",
|
"year": "Έτος",
|
||||||
"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_wifi_name": "Your WiFi name",
|
"your_wifi_name": "Το όνομα του Wi-Fi σας",
|
||||||
"zoom_image": "Ζουμ Εικόνας"
|
"zoom_image": "Ζουμ Εικόνας"
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user