mirror of
https://github.com/immich-app/immich.git
synced 2026-06-04 05:05:22 -04:00
Compare commits
297 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a555972d8 | |||
| 95c1f0efeb | |||
| fbe631fe91 | |||
| 2143a0c935 | |||
| 136bd1e2eb | |||
| 564065a3ed | |||
| 9bcce59719 | |||
| cd86a83c33 | |||
| f29c06799f | |||
| 6fcf651d76 | |||
| 196307bca5 | |||
| 776b9cbad5 | |||
| 960be0c27a | |||
| 123119ca0d | |||
| 1772f720bf | |||
| bcc29903de | |||
| 767caf9bfe | |||
| 649d14822a | |||
| 207672c481 | |||
| 4fcd9c2e0d | |||
| a2687d674e | |||
| fb1bc7f9e2 | |||
| 18e8d30b1c | |||
| 95ef60628c | |||
| a19b7148e5 | |||
| 8e414e42f3 | |||
| db0f86c749 | |||
| adb6b39eec | |||
| c8ae99e7d7 | |||
| 37823bcd51 | |||
| b465f2b58f | |||
| 2166f07b1f | |||
| c9e251c78c | |||
| da4b88fc14 | |||
| d1e2e8ab4e | |||
| 2a619d3c10 | |||
| c29493e3a0 | |||
| 4ef777d145 | |||
| 0b40f4fd76 | |||
| ecba4e2a62 | |||
| 4eb531197e | |||
| 505a07a825 | |||
| 548dbe8ad6 | |||
| 0c184940f4 | |||
| be180fd9da | |||
| 859f58174e | |||
| a6c7e76008 | |||
| 0ff94213e6 | |||
| 6b1dd6f680 | |||
| 7d4286bbc5 | |||
| 18201a26d9 | |||
| a2e3635ac9 | |||
| ce346bf956 | |||
| a1a2939868 | |||
| e8309585d6 | |||
| 17d4941089 | |||
| b09ebb11e9 | |||
| 181b028b09 | |||
| eb20b715e4 | |||
| a277c6311f | |||
| 5889c42eb6 | |||
| 14cce0cba3 | |||
| 9b80ffd9c6 | |||
| 306a3b8c7f | |||
| be0fc403d8 | |||
| c13fd9e4b5 | |||
| 8724848fce | |||
| 2d950db940 | |||
| 4b9ebc2cff | |||
| e2d26ebdea | |||
| 8c6adf7157 | |||
| 48fdd39d30 | |||
| 22bf7c2005 | |||
| 47b45453c8 | |||
| 448c069fb6 | |||
| 958f270f0d | |||
| 9f699fdfc3 | |||
| 00da7b88a1 | |||
| 144a57ddff | |||
| 1bd2d474d7 | |||
| b33874ef12 | |||
| dbaf4b548b | |||
| 7d58d5be12 | |||
| 42fe86d24c | |||
| eeb55c279b | |||
| 5c159d70a7 | |||
| 44ae0fa7ed | |||
| f782782662 | |||
| 4436cab827 | |||
| 74789ad1c4 | |||
| 7877097b3f | |||
| fb84c1cf61 | |||
| 940a1d4ab8 | |||
| fae25dbe65 | |||
| 8dd0d7f34c | |||
| 9b78f2c0ba | |||
| 67cedfef17 | |||
| c9c2322b9d | |||
| 389356149a | |||
| 4812a2e2d8 | |||
| 8f01d06927 | |||
| a2ff075e9a | |||
| d8b39906f9 | |||
| b36911a16b | |||
| b074ee202e | |||
| 78bb6cf926 | |||
| c980f5fc19 | |||
| a26d9e05ba | |||
| c862163204 | |||
| 5fb8f9bf1a | |||
| b9b5dba037 | |||
| 8bfa75087c | |||
| 95280edd6c | |||
| a9666d2cef | |||
| 4af9edc20b | |||
| c975fe5bc7 | |||
| 12a4d8e2ee | |||
| ce9b32a61a | |||
| 4ddc288cd1 | |||
| 94b15b8678 | |||
| ff9ae24219 | |||
| b456f78771 | |||
| 1506776891 | |||
| 0e93aa74cf | |||
| e95ad9d2eb | |||
| b98a227bbd | |||
| 2dd785e3e2 | |||
| 7e754125cd | |||
| e2eb03d3a4 | |||
| bf065a834f | |||
| db79173b5b | |||
| 33666ccd21 | |||
| be93b9040c | |||
| 00dae6ac38 | |||
| 5a8fd40dc5 | |||
| 813d684aaa | |||
| 644f705be1 | |||
| f3e4bcc733 | |||
| 9a0c17fdb8 | |||
| b7c4497dfd | |||
| 9c227aeaf5 | |||
| e939fde6f1 | |||
| 019beaed0b | |||
| 0e4d6d4eac | |||
| 79f978ddeb | |||
| f2445ecab1 | |||
| 86311e3cfe | |||
| 29000461c2 | |||
| b30373b24f | |||
| bc2439883a | |||
| 044257531e | |||
| f413f5c692 | |||
| 52307ed09f | |||
| 77020e742a | |||
| 38b135ff36 | |||
| cda4a2a5fc | |||
| 88002cf7fe | |||
| 694ea151f5 | |||
| b092c8b601 | |||
| 48e6e17829 | |||
| 0519833d75 | |||
| 34caed3b2b | |||
| 677cb660f5 | |||
| 9b0b2bfcf2 | |||
| ac6938a629 | |||
| 16749ff8ba | |||
| bba4a00eb1 | |||
| 9dafc8e8e9 | |||
| 4e44fb9cf7 | |||
| 82db581cc5 | |||
| b66c97b785 | |||
| ff936f901d | |||
| 48fe111daa | |||
| 0581b49750 | |||
| 2c6d4f3fe1 | |||
| 55513cd59f | |||
| 10fa928abe | |||
| e322d44f95 | |||
| c2a279e49e | |||
| 226b9390db | |||
| 754f072ef9 | |||
| c91d8745b4 | |||
| f3b7cd6198 | |||
| 990aff441b | |||
| 001d7d083f | |||
| 3fd24e2083 | |||
| 6bb8f4fcc4 | |||
| d4605b21d9 | |||
| 3bd37ebbfb | |||
| 5c3777ab46 | |||
| 6c531e0a5a | |||
| 471c27cd33 | |||
| 4773788a88 | |||
| d49d995611 | |||
| 0ac3d6a83a | |||
| 9996ee12d0 | |||
| 0a79dd1228 | |||
| e45308b949 | |||
| c403e03a42 | |||
| e7db3b220d | |||
| 28d5c169c0 | |||
| 0f2fe656db | |||
| 34ce68095d | |||
| 8764a1894b | |||
| 27f69b39b2 | |||
| 9fc6fbc373 | |||
| 9fc32b6f7a | |||
| 4571940a4e | |||
| 1ceb6d2e21 | |||
| 1a4c5d73ac | |||
| 22b43bf4d9 | |||
| 45eff1c663 | |||
| 56b8e1b8a9 | |||
| f79c8cf1c1 | |||
| 8e50d25f45 | |||
| 8222781d1f | |||
| 08c4594cde | |||
| d325231df2 | |||
| f2726606e0 | |||
| 0edbca24e4 | |||
| 4791d9c0c3 | |||
| a47b232235 | |||
| df0c86920d | |||
| 422111d26e | |||
| 7a83baaf27 | |||
| aaf34fa7d4 | |||
| 4a384bca86 | |||
| dd72ec2621 | |||
| e73686bd76 | |||
| 6e9a425592 | |||
| 6012d22d98 | |||
| abfcffb423 | |||
| ec7246b86f | |||
| 9597f8c37f | |||
| 7b0deb1fd3 | |||
| 5ab05e57fa | |||
| ba3f114625 | |||
| 9b642633c1 | |||
| a05c8c6087 | |||
| 35a521c6ec | |||
| 09fabb36b6 | |||
| c259fee309 | |||
| 78ba9cbc63 | |||
| 33d75462c9 | |||
| e9451f10d6 | |||
| 480b7e8d65 | |||
| 228ac63ab9 | |||
| 7e9da945f6 | |||
| dd03c9c0a9 | |||
| 16e4a2b92a | |||
| 5caa7e1902 | |||
| 8279e1078a | |||
| 011ecbb43d | |||
| 2725c96cb1 | |||
| 3c476b1987 | |||
| 5989c9b4aa | |||
| 13c4260a1f | |||
| 54bc9ddd69 | |||
| f94e0fbc39 | |||
| 5532f669eb | |||
| e4c24bdec8 | |||
| 56f14162f6 | |||
| 8abbbc49cf | |||
| 4eb08eee18 | |||
| 0560f98c2d | |||
| 49ad411d50 | |||
| 2478cc40f4 | |||
| 44eeb1e088 | |||
| a868ae3ad0 | |||
| acac0d4f37 | |||
| 8c40a28fef | |||
| b2081eda1e | |||
| 9670c853c6 | |||
| cc2dacb308 | |||
| 15fc6b18f3 | |||
| a284e38890 | |||
| 05010c3a84 | |||
| 4da3d68a67 | |||
| 20c639e52a | |||
| 6deb97d5bc | |||
| b282d83e95 | |||
| 5bc08f8654 | |||
| f54924d46a | |||
| dffe4d1d5c | |||
| 7f47cdd645 | |||
| 625b30c50a | |||
| 8619d14eca | |||
| 062546c168 | |||
| ea668d6b22 | |||
| f06af2c600 | |||
| 9dd2633e0c | |||
| 13a514c189 | |||
| b0c9120bb6 | |||
| bc4265416d | |||
| d4434f2276 | |||
| f4e156494f | |||
| 84abad564e |
+1
-1
@@ -1 +1 @@
|
|||||||
24.13.1
|
24.14.1
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"format": "prettier --check .",
|
"format": "prettier --cache --check .",
|
||||||
"format:fix": "prettier --write ."
|
"format:fix": "prettier --cache --write --list-different ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^3.7.4"
|
"prettier": "^3.7.4"
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
name: Auto-close PRs
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target: # zizmor: ignore[dangerous-triggers]
|
||||||
|
types: [opened, edited, labeled]
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
parse_template:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event.action != 'labeled' && github.event.pull_request.head.repo.fork == true }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
outputs:
|
||||||
|
uses_template: ${{ steps.check.outputs.uses_template }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
sparse-checkout: .github/pull_request_template.md
|
||||||
|
sparse-checkout-cone-mode: false
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Check required sections
|
||||||
|
id: check
|
||||||
|
env:
|
||||||
|
BODY: ${{ github.event.pull_request.body }}
|
||||||
|
run: |
|
||||||
|
OK=true
|
||||||
|
while IFS= read -r header; do
|
||||||
|
printf '%s\n' "$BODY" | grep -qF "$header" || OK=false
|
||||||
|
done < <(sed '/<!--/,/-->/d' .github/pull_request_template.md | grep "^## ")
|
||||||
|
echo "uses_template=$OK" | tee --append "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
close_template:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: parse_template
|
||||||
|
if: >-
|
||||||
|
${{
|
||||||
|
needs.parse_template.outputs.uses_template == 'false'
|
||||||
|
&& github.event.pull_request.state != 'closed'
|
||||||
|
&& !contains(github.event.pull_request.labels.*.name, 'auto-closed:template')
|
||||||
|
}}
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: Comment and close
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
NODE_ID: ${{ github.event.pull_request.node_id }}
|
||||||
|
run: |
|
||||||
|
gh api graphql \
|
||||||
|
-f prId="$NODE_ID" \
|
||||||
|
-f body="This PR has been automatically closed as the description doesn't follow our template. After you edit it to match the template, the PR will automatically be reopened." \
|
||||||
|
-f query='
|
||||||
|
mutation CommentAndClosePR($prId: ID!, $body: String!) {
|
||||||
|
addComment(input: {
|
||||||
|
subjectId: $prId,
|
||||||
|
body: $body
|
||||||
|
}) {
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
closePullRequest(input: {
|
||||||
|
pullRequestId: $prId
|
||||||
|
}) {
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
|
||||||
|
- name: Add label
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
run: gh pr edit "$PR_NUMBER" --repo "${{ github.repository }}" --add-label "auto-closed:template"
|
||||||
|
|
||||||
|
close_llm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event.action == 'labeled' && github.event.label.name == 'auto-closed:llm' }}
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: Comment and close
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
NODE_ID: ${{ github.event.pull_request.node_id }}
|
||||||
|
run: |
|
||||||
|
gh api graphql \
|
||||||
|
-f prId="$NODE_ID" \
|
||||||
|
-f body="Thank you for your interest in contributing to Immich! Unfortunately this PR looks like it was generated using an LLM. As noted in our [CONTRIBUTING.md](https://github.com/immich-app/immich/blob/main/CONTRIBUTING.md#use-of-generative-ai), we request that you don't use LLMs to generate PRs as those are not a good use of maintainer time." \
|
||||||
|
-f query='
|
||||||
|
mutation CommentAndClosePR($prId: ID!, $body: String!) {
|
||||||
|
addComment(input: {
|
||||||
|
subjectId: $prId,
|
||||||
|
body: $body
|
||||||
|
}) {
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
closePullRequest(input: {
|
||||||
|
pullRequestId: $prId
|
||||||
|
}) {
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
|
||||||
|
reopen:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: parse_template
|
||||||
|
if: >-
|
||||||
|
${{
|
||||||
|
needs.parse_template.outputs.uses_template == 'true'
|
||||||
|
&& github.event.pull_request.state == 'closed'
|
||||||
|
&& contains(github.event.pull_request.labels.*.name, 'auto-closed:template')
|
||||||
|
}}
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: Remove template label
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
run: gh pr edit "$PR_NUMBER" --repo "${{ github.repository }}" --remove-label "auto-closed:template" || true
|
||||||
|
|
||||||
|
- name: Check for remaining auto-closed labels
|
||||||
|
id: check_labels
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
run: |
|
||||||
|
REMAINING=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json labels \
|
||||||
|
--jq '[.labels[].name | select(startswith("auto-closed:"))] | length')
|
||||||
|
echo "remaining=$REMAINING" | tee --append "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Reopen PR
|
||||||
|
if: ${{ steps.check_labels.outputs.remaining == '0' }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
NODE_ID: ${{ github.event.pull_request.node_id }}
|
||||||
|
run: |
|
||||||
|
gh api graphql \
|
||||||
|
-f prId="$NODE_ID" \
|
||||||
|
-f query='
|
||||||
|
mutation ReopenPR($prId: ID!) {
|
||||||
|
reopenPullRequest(input: {
|
||||||
|
pullRequestId: $prId
|
||||||
|
}) {
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}'
|
||||||
@@ -51,14 +51,14 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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: Check what should run
|
- name: Check what should run
|
||||||
id: check
|
id: check
|
||||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
filters: |
|
filters: |
|
||||||
@@ -79,7 +79,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -103,7 +103,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Restore Gradle Cache
|
- name: Restore Gradle Cache
|
||||||
id: cache-gradle-restore
|
id: cache-gradle-restore
|
||||||
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
@@ -114,7 +114,7 @@ jobs:
|
|||||||
key: build-mobile-gradle-${{ runner.os }}-main
|
key: build-mobile-gradle-${{ runner.os }}-main
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0
|
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -153,14 +153,14 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Publish Android Artifact
|
- name: Publish Android Artifact
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
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
|
||||||
|
|
||||||
- name: Save Gradle Cache
|
- name: Save Gradle Cache
|
||||||
id: cache-gradle-save
|
id: cache-gradle-save
|
||||||
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/main'
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
@@ -185,13 +185,13 @@ jobs:
|
|||||||
run: sudo xcode-select -s /Applications/Xcode_26.2.app/Contents/Developer
|
run: sudo xcode-select -s /Applications/Xcode_26.2.app/Contents/Developer
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.ref || github.sha }}
|
ref: ${{ inputs.ref || github.sha }}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2
|
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -210,7 +210,7 @@ jobs:
|
|||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
- name: Setup Ruby
|
- name: Setup Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@c515ec17f69368147deb311832da000dd229d338 # v1.297.0
|
||||||
with:
|
with:
|
||||||
ruby-version: '3.3'
|
ruby-version: '3.3'
|
||||||
bundler-cache: true
|
bundler-cache: true
|
||||||
@@ -291,7 +291,7 @@ jobs:
|
|||||||
security delete-keychain build.keychain || true
|
security delete-keychain build.keychain || true
|
||||||
|
|
||||||
- name: Upload IPA artifact
|
- name: Upload IPA artifact
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: ios-release-ipa
|
name: ios-release-ipa
|
||||||
path: mobile/ios/Runner.ipa
|
path: mobile/ios/Runner.ipa
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
actions: write
|
actions: write
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Check for breaking API changes
|
- name: Check for breaking API changes
|
||||||
# sha is pinning to a commit instead of a tag since the action does not tag versions
|
uses: oasdiff/oasdiff-action/breaking@1f38ea5ea0b4a2e4e49901c3bcdf4386a05e9ea1 # v0.0.37
|
||||||
uses: oasdiff/oasdiff-action/breaking@ccb863950ce437a50f8f1a40d2a1112117e06ce4
|
|
||||||
with:
|
with:
|
||||||
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
|
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
|
||||||
revision: open-api/immich-openapi-specs.json
|
revision: open-api/immich-openapi-specs.json
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
working-directory: ./cli
|
working-directory: ./cli
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -42,10 +42,10 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.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'
|
||||||
@@ -71,7 +71,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -83,13 +83,13 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.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
|
||||||
@@ -104,7 +104,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate docker image tags
|
- name: Generate docker image tags
|
||||||
id: metadata
|
id: metadata
|
||||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
|
||||||
with:
|
with:
|
||||||
flavor: |
|
flavor: |
|
||||||
latest=false
|
latest=false
|
||||||
@@ -115,7 +115,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@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
|
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||||
with:
|
with:
|
||||||
file: cli/Dockerfile
|
file: cli/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
needs: [get_body, should_run]
|
needs: [get_body, should_run]
|
||||||
if: ${{ needs.should_run.outputs.should_run == 'true' }}
|
if: ${{ needs.should_run.outputs.should_run == 'true' }}
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/immich-app/mdq:main@sha256:4f9860d04c88f7f87861f8ee84bfeedaec15ed7ca5ca87bc7db44b036f81645f
|
image: ghcr.io/immich-app/mdq:main@sha256:df7188ba88abb0800d73cc97d3633280f0c0c3d4c441d678225067bf154150fb
|
||||||
outputs:
|
outputs:
|
||||||
checked: ${{ steps.get_checkbox.outputs.checked }}
|
checked: ${{ steps.get_checkbox.outputs.checked }}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
name: Close LLM-generated PRs
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [labeled]
|
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
comment_and_close:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.event.label.name == 'llm-generated' }}
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
- name: Comment and close
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
NODE_ID: ${{ github.event.pull_request.node_id }}
|
|
||||||
run: |
|
|
||||||
gh api graphql \
|
|
||||||
-f prId="$NODE_ID" \
|
|
||||||
-f body="Thank you for your interest in contributing to Immich! Unfortunately this PR looks like it was generated using an LLM. As noted in our [CONTRIBUTING.md](https://github.com/immich-app/immich/blob/main/CONTRIBUTING.md#use-of-generative-ai), we request that you don't use LLMs to generate PRs as those are not a good use of maintainer time." \
|
|
||||||
-f query='
|
|
||||||
mutation CommentAndClosePR($prId: ID!, $body: String!) {
|
|
||||||
addComment(input: {
|
|
||||||
subjectId: $prId,
|
|
||||||
body: $body
|
|
||||||
}) {
|
|
||||||
__typename
|
|
||||||
}
|
|
||||||
|
|
||||||
closePullRequest(input: {
|
|
||||||
pullRequestId: $prId
|
|
||||||
}) {
|
|
||||||
__typename
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
|
uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||||
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.
|
||||||
@@ -70,7 +70,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@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
|
uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||||
|
|
||||||
# ℹ️ 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
|
||||||
@@ -83,6 +83,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@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
|
uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||||
with:
|
with:
|
||||||
category: '/language:${{matrix.language}}'
|
category: '/language:${{matrix.language}}'
|
||||||
|
|||||||
@@ -23,14 +23,14 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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: Check what should run
|
- name: Check what should run
|
||||||
id: check
|
id: check
|
||||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
filters: |
|
filters: |
|
||||||
@@ -60,7 +60,7 @@ 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@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
@@ -90,7 +90,7 @@ jobs:
|
|||||||
suffix: ['']
|
suffix: ['']
|
||||||
steps:
|
steps:
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
@@ -131,8 +131,8 @@ jobs:
|
|||||||
- device: rocm
|
- device: rocm
|
||||||
suffixes: '-rocm'
|
suffixes: '-rocm'
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
runner-mapping: '{"linux/amd64": "pokedex-giant"}'
|
runner-mapping: '{"linux/amd64": "pokedex-large"}'
|
||||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@bd49ed7a5a6022149f79b6564df48177476a822b # multi-runner-build-workflow-v2.2.1
|
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@61a0fc2b41524edcc7c9fffb8bb178e6b0ccf21d # multi-runner-build-workflow-v2.3.0
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
actions: read
|
actions: read
|
||||||
@@ -155,7 +155,7 @@ jobs:
|
|||||||
name: Build and Push Server
|
name: Build and Push Server
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }}
|
if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }}
|
||||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@bd49ed7a5a6022149f79b6564df48177476a822b # multi-runner-build-workflow-v2.2.1
|
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@61a0fc2b41524edcc7c9fffb8bb178e6b0ccf21d # multi-runner-build-workflow-v2.3.0
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
actions: read
|
actions: read
|
||||||
@@ -178,7 +178,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
- uses: immich-app/devtools/actions/success-check@53bb77345ee9f953f93bd6fd9980f07a2f24965e # success-check-action-v0.0.5
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|
||||||
@@ -189,6 +189,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
- uses: immich-app/devtools/actions/success-check@53bb77345ee9f953f93bd6fd9980f07a2f24965e # success-check-action-v0.0.5
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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: Check what should run
|
- name: Check what should run
|
||||||
id: check
|
id: check
|
||||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
filters: |
|
filters: |
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -67,10 +67,10 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './docs/.nvmrc'
|
node-version-file: './docs/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -86,7 +86,7 @@ jobs:
|
|||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
- name: Upload build output
|
- name: Upload build output
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: docs-build-output
|
name: docs-build-output
|
||||||
path: docs/build/
|
path: docs/build/
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
artifact: ${{ steps.get-artifact.outputs.result }}
|
artifact: ${{ steps.get-artifact.outputs.result }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -119,7 +119,7 @@ jobs:
|
|||||||
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:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -131,7 +131,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@dab18118da6476e8237ac94080fd937983fecd42 # use-mise-action-v1.1.2
|
uses: immich-app/devtools/actions/use-mise@035e80a7d4355d5f087ffb95db9e4a0944c04e56 # use-mise-action-v1.1.3
|
||||||
|
|
||||||
- name: Load parameters
|
- name: Load parameters
|
||||||
id: parameters
|
id: parameters
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@dab18118da6476e8237ac94080fd937983fecd42 # use-mise-action-v1.1.2
|
uses: immich-app/devtools/actions/use-mise@035e80a7d4355d5f087ffb95db9e4a0944c04e56 # use-mise-action-v1.1.3
|
||||||
|
|
||||||
- name: Destroy Docs Subdomain
|
- name: Destroy Docs Subdomain
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
|
||||||
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 }}
|
||||||
@@ -29,10 +29,10 @@ jobs:
|
|||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate_token
|
id: generate_token
|
||||||
if: ${{ inputs.skip != true }}
|
if: ${{ inputs.skip != true }}
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
|
||||||
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 }}
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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: Require PR to have a changelog label
|
- name: Require PR to have a changelog label
|
||||||
uses: mheap/github-action-required-labels@8afbe8ae6ab7647d0c9f0cfa7c2f939650d22509 # v5.5.1
|
uses: mheap/github-action-required-labels@0ac283b4e65c1fb28ce6079dea5546ceca98ccbe # v5.5.2
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
mode: exactly
|
mode: exactly
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
|
||||||
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 }}
|
||||||
@@ -63,13 +63,13 @@ jobs:
|
|||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
|
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -124,7 +124,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
|
||||||
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 }}
|
||||||
@@ -136,13 +136,13 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Download APK
|
- name: Download APK
|
||||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: release-apk-signed
|
name: release-apk-signed
|
||||||
github-token: ${{ steps.generate-token.outputs.token }}
|
github-token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Create draft release
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
tag_name: ${{ needs.bump_version.outputs.version }}
|
tag_name: ${{ needs.bump_version.outputs.version }}
|
||||||
@@ -151,6 +151,7 @@ jobs:
|
|||||||
body_path: misc/release/notes.tmpl
|
body_path: misc/release/notes.tmpl
|
||||||
files: |
|
files: |
|
||||||
docker/docker-compose.yml
|
docker/docker-compose.yml
|
||||||
|
docker/docker-compose.rootless.yml
|
||||||
docker/example.env
|
docker/example.env
|
||||||
docker/hwaccel.ml.yml
|
docker/hwaccel.ml.yml
|
||||||
docker/hwaccel.transcoding.yml
|
docker/hwaccel.transcoding.yml
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2
|
- uses: mshick/add-pr-comment@ffd016c7e151d97d69d21a843022fd4cd5b96fe5 # v3.9.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
message-id: 'preview-status'
|
message-id: 'preview-status'
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -48,14 +48,14 @@ jobs:
|
|||||||
name: 'preview'
|
name: 'preview'
|
||||||
})
|
})
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2
|
- uses: mshick/add-pr-comment@ffd016c7e151d97d69d21a843022fd4cd5b96fe5 # v3.9.0
|
||||||
if: ${{ github.event.pull_request.head.repo.fork }}
|
if: ${{ github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
message-id: 'preview-status'
|
message-id: 'preview-status'
|
||||||
message: 'PRs from forks cannot have preview environments.'
|
message: 'PRs from forks cannot have preview environments.'
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2
|
- uses: mshick/add-pr-comment@ffd016c7e151d97d69d21a843022fd4cd5b96fe5 # v3.9.0
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
name: Manage release PR
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
bump:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Generate a token
|
|
||||||
id: generate-token
|
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
|
||||||
with:
|
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
with:
|
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
|
||||||
persist-credentials: true
|
|
||||||
ref: main
|
|
||||||
|
|
||||||
- name: Install uv
|
|
||||||
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
|
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
|
||||||
with:
|
|
||||||
node-version-file: './server/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
|
|
||||||
- name: Determine release type
|
|
||||||
id: bump-type
|
|
||||||
uses: ietf-tools/semver-action@c90370b2958652d71c06a3484129a4d423a6d8a8 # v1.11.0
|
|
||||||
with:
|
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
|
||||||
|
|
||||||
- name: Bump versions
|
|
||||||
env:
|
|
||||||
TYPE: ${{ steps.bump-type.outputs.bump }}
|
|
||||||
run: |
|
|
||||||
if [ "$TYPE" == "none" ]; then
|
|
||||||
exit 1 # TODO: Is there a cleaner way to abort the workflow?
|
|
||||||
fi
|
|
||||||
misc/release/pump-version.sh -s $TYPE -m true
|
|
||||||
|
|
||||||
- name: Manage Outline release document
|
|
||||||
id: outline
|
|
||||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
|
||||||
env:
|
|
||||||
OUTLINE_API_KEY: ${{ secrets.OUTLINE_API_KEY }}
|
|
||||||
NEXT_VERSION: ${{ steps.bump-type.outputs.next }}
|
|
||||||
with:
|
|
||||||
github-token: ${{ steps.generate-token.outputs.token }}
|
|
||||||
script: |
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
const outlineKey = process.env.OUTLINE_API_KEY;
|
|
||||||
const parentDocumentId = 'da856355-0844-43df-bd71-f8edce5382d9'
|
|
||||||
const collectionId = 'e2910656-714c-4871-8721-447d9353bd73';
|
|
||||||
const baseUrl = 'https://outline.immich.cloud';
|
|
||||||
|
|
||||||
const listResponse = await fetch(`${baseUrl}/api/documents.list`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Authorization': `Bearer ${outlineKey}`,
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ parentDocumentId })
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!listResponse.ok) {
|
|
||||||
throw new Error(`Outline list failed: ${listResponse.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const listData = await listResponse.json();
|
|
||||||
const allDocuments = listData.data || [];
|
|
||||||
|
|
||||||
const document = allDocuments.find(doc => doc.title === 'next');
|
|
||||||
|
|
||||||
let documentId;
|
|
||||||
let documentUrl;
|
|
||||||
let documentText;
|
|
||||||
|
|
||||||
if (!document) {
|
|
||||||
// Create new document
|
|
||||||
console.log('No existing document found. Creating new one...');
|
|
||||||
const notesTmpl = fs.readFileSync('misc/release/notes.tmpl', 'utf8');
|
|
||||||
const createResponse = await fetch(`${baseUrl}/api/documents.create`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Authorization': `Bearer ${outlineKey}`,
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
title: 'next',
|
|
||||||
text: notesTmpl,
|
|
||||||
collectionId: collectionId,
|
|
||||||
parentDocumentId: parentDocumentId,
|
|
||||||
publish: true
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!createResponse.ok) {
|
|
||||||
throw new Error(`Failed to create document: ${createResponse.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const createData = await createResponse.json();
|
|
||||||
documentId = createData.data.id;
|
|
||||||
const urlId = createData.data.urlId;
|
|
||||||
documentUrl = `${baseUrl}/doc/next-${urlId}`;
|
|
||||||
documentText = createData.data.text || '';
|
|
||||||
console.log(`Created new document: ${documentUrl}`);
|
|
||||||
} else {
|
|
||||||
documentId = document.id;
|
|
||||||
const docPath = document.url;
|
|
||||||
documentUrl = `${baseUrl}${docPath}`;
|
|
||||||
documentText = document.text || '';
|
|
||||||
console.log(`Found existing document: ${documentUrl}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate GitHub release notes
|
|
||||||
console.log('Generating GitHub release notes...');
|
|
||||||
const releaseNotesResponse = await github.rest.repos.generateReleaseNotes({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
tag_name: `${process.env.NEXT_VERSION}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Combine the content
|
|
||||||
const changelog = `
|
|
||||||
# ${process.env.NEXT_VERSION}
|
|
||||||
|
|
||||||
${documentText}
|
|
||||||
|
|
||||||
${releaseNotesResponse.data.body}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
const existingChangelog = fs.existsSync('CHANGELOG.md') ? fs.readFileSync('CHANGELOG.md', 'utf8') : '';
|
|
||||||
fs.writeFileSync('CHANGELOG.md', changelog + existingChangelog, 'utf8');
|
|
||||||
|
|
||||||
core.setOutput('document_url', documentUrl);
|
|
||||||
|
|
||||||
- name: Create PR
|
|
||||||
id: create-pr
|
|
||||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
|
||||||
with:
|
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
|
||||||
commit-message: 'chore: release ${{ steps.bump-type.outputs.next }}'
|
|
||||||
title: 'chore: release ${{ steps.bump-type.outputs.next }}'
|
|
||||||
body: 'Release notes: ${{ steps.outline.outputs.document_url }}'
|
|
||||||
labels: 'changelog:skip'
|
|
||||||
branch: 'release/next'
|
|
||||||
draft: true
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
name: release.yml
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [closed]
|
|
||||||
paths:
|
|
||||||
- CHANGELOG.md
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Maybe double check PR source branch?
|
|
||||||
|
|
||||||
merge_translations:
|
|
||||||
uses: ./.github/workflows/merge-translations.yml
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
secrets:
|
|
||||||
PUSH_O_MATIC_APP_ID: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
|
||||||
PUSH_O_MATIC_APP_KEY: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
|
||||||
WEBLATE_TOKEN: ${{ secrets.WEBLATE_TOKEN }}
|
|
||||||
|
|
||||||
build_mobile:
|
|
||||||
uses: ./.github/workflows/build-mobile.yml
|
|
||||||
needs: merge_translations
|
|
||||||
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 }}
|
|
||||||
# iOS secrets
|
|
||||||
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
|
|
||||||
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
|
|
||||||
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
|
|
||||||
IOS_CERTIFICATE_P12: ${{ secrets.IOS_CERTIFICATE_P12 }}
|
|
||||||
IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
|
|
||||||
IOS_PROVISIONING_PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE }}
|
|
||||||
IOS_PROVISIONING_PROFILE_SHARE_EXTENSION: ${{ secrets.IOS_PROVISIONING_PROFILE_SHARE_EXTENSION }}misc/release/notes.tmpl
|
|
||||||
IOS_PROVISIONING_PROFILE_WIDGET_EXTENSION: ${{ secrets.IOS_PROVISIONING_PROFILE_WIDGET_EXTENSION }}
|
|
||||||
IOS_DEVELOPMENT_PROVISIONING_PROFILE: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE }}
|
|
||||||
IOS_DEVELOPMENT_PROVISIONING_PROFILE_SHARE_EXTENSION: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE_SHARE_EXTENSION }}
|
|
||||||
IOS_DEVELOPMENT_PROVISIONING_PROFILE_WIDGET_EXTENSION: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE_WIDGET_EXTENSION }}
|
|
||||||
FASTLANE_TEAM_ID: ${{ secrets.FASTLANE_TEAM_ID }}
|
|
||||||
with:
|
|
||||||
ref: main
|
|
||||||
environment: production
|
|
||||||
|
|
||||||
prepare_release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build_mobile
|
|
||||||
permissions:
|
|
||||||
actions: read # To download the app artifact
|
|
||||||
steps:
|
|
||||||
- name: Generate a token
|
|
||||||
id: generate-token
|
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
|
||||||
with:
|
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
with:
|
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
|
||||||
persist-credentials: false
|
|
||||||
ref: main
|
|
||||||
|
|
||||||
- name: Extract changelog
|
|
||||||
id: changelog
|
|
||||||
run: |
|
|
||||||
CHANGELOG_PATH=$RUNNER_TEMP/changelog.md
|
|
||||||
sed -n '1,/^---$/p' CHANGELOG.md | head -n -1 > $CHANGELOG_PATH
|
|
||||||
echo "path=$CHANGELOG_PATH" >> $GITHUB_OUTPUT
|
|
||||||
VERSION=$(sed -n 's/^# //p' $CHANGELOG_PATH)
|
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Download APK
|
|
||||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
|
||||||
with:
|
|
||||||
name: release-apk-signed
|
|
||||||
github-token: ${{ steps.generate-token.outputs.token }}
|
|
||||||
|
|
||||||
- name: Create draft release
|
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
|
||||||
with:
|
|
||||||
tag_name: ${{ steps.version.outputs.result }}
|
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
|
||||||
body_path: ${{ steps.changelog.outputs.path }}
|
|
||||||
draft: true
|
|
||||||
files: |
|
|
||||||
docker/docker-compose.yml
|
|
||||||
docker/docker-compose.rootless.yml
|
|
||||||
docker/example.env
|
|
||||||
docker/hwaccel.ml.yml
|
|
||||||
docker/hwaccel.transcoding.yml
|
|
||||||
docker/prometheus.yml
|
|
||||||
*.apk
|
|
||||||
|
|
||||||
- name: Rename Outline document
|
|
||||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
|
||||||
continue-on-error: true
|
|
||||||
env:
|
|
||||||
OUTLINE_API_KEY: ${{ secrets.OUTLINE_API_KEY }}
|
|
||||||
VERSION: ${{ steps.changelog.outputs.version }}
|
|
||||||
with:
|
|
||||||
github-token: ${{ steps.generate-token.outputs.token }}
|
|
||||||
script: |
|
|
||||||
const outlineKey = process.env.OUTLINE_API_KEY;
|
|
||||||
const version = process.env.VERSION;
|
|
||||||
const parentDocumentId = 'da856355-0844-43df-bd71-f8edce5382d9';
|
|
||||||
const baseUrl = 'https://outline.immich.cloud';
|
|
||||||
|
|
||||||
const listResponse = await fetch(`${baseUrl}/api/documents.list`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Authorization': `Bearer ${outlineKey}`,
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ parentDocumentId })
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!listResponse.ok) {
|
|
||||||
throw new Error(`Outline list failed: ${listResponse.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const listData = await listResponse.json();
|
|
||||||
const allDocuments = listData.data || [];
|
|
||||||
const document = allDocuments.find(doc => doc.title === 'next');
|
|
||||||
|
|
||||||
if (document) {
|
|
||||||
console.log(`Found document 'next', renaming to '${version}'...`);
|
|
||||||
|
|
||||||
const updateResponse = await fetch(`${baseUrl}/api/documents.update`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Authorization': `Bearer ${outlineKey}`,
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
id: document.id,
|
|
||||||
title: version
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!updateResponse.ok) {
|
|
||||||
throw new Error(`Failed to rename document: ${updateResponse.statusText}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log('No document titled "next" found to rename');
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
working-directory: ./open-api/typescript-sdk
|
working-directory: ./open-api/typescript-sdk
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -30,10 +30,10 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
|
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.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'
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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: Check what should run
|
- name: Check what should run
|
||||||
id: check
|
id: check
|
||||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
filters: |
|
filters: |
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0
|
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
|
|||||||
+52
-52
@@ -17,14 +17,14 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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: Check what should run
|
- name: Check what should run
|
||||||
id: check
|
id: check
|
||||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
filters: |
|
filters: |
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
working-directory: ./server
|
working-directory: ./server
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -75,9 +75,9 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -108,7 +108,7 @@ jobs:
|
|||||||
working-directory: ./cli
|
working-directory: ./cli
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -119,9 +119,9 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -155,7 +155,7 @@ jobs:
|
|||||||
working-directory: ./cli
|
working-directory: ./cli
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -166,9 +166,9 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -197,7 +197,7 @@ jobs:
|
|||||||
working-directory: ./web
|
working-directory: ./web
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -208,9 +208,9 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -241,7 +241,7 @@ jobs:
|
|||||||
working-directory: ./web
|
working-directory: ./web
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -252,9 +252,9 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -279,7 +279,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -290,9 +290,9 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -327,7 +327,7 @@ jobs:
|
|||||||
working-directory: ./e2e
|
working-directory: ./e2e
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -338,9 +338,9 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -373,7 +373,7 @@ jobs:
|
|||||||
working-directory: ./server
|
working-directory: ./server
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -385,9 +385,9 @@ jobs:
|
|||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -412,7 +412,7 @@ jobs:
|
|||||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -424,9 +424,9 @@ jobs:
|
|||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -464,7 +464,7 @@ jobs:
|
|||||||
run: docker compose logs --no-color > docker-compose-logs.txt
|
run: docker compose logs --no-color > docker-compose-logs.txt
|
||||||
working-directory: ./e2e
|
working-directory: ./e2e
|
||||||
- name: Archive Docker logs
|
- name: Archive Docker logs
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: e2e-server-docker-logs-${{ matrix.runner }}
|
name: e2e-server-docker-logs-${{ matrix.runner }}
|
||||||
@@ -484,7 +484,7 @@ jobs:
|
|||||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -496,9 +496,9 @@ jobs:
|
|||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -522,7 +522,7 @@ jobs:
|
|||||||
run: pnpm test:web
|
run: pnpm test:web
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Archive e2e test (web) results
|
- name: Archive e2e test (web) results
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
if: success() || failure()
|
if: success() || failure()
|
||||||
with:
|
with:
|
||||||
name: e2e-web-test-results-${{ matrix.runner }}
|
name: e2e-web-test-results-${{ matrix.runner }}
|
||||||
@@ -533,7 +533,7 @@ jobs:
|
|||||||
run: pnpm test:web:ui
|
run: pnpm test:web:ui
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Archive ui test (web) results
|
- name: Archive ui test (web) results
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
if: success() || failure()
|
if: success() || failure()
|
||||||
with:
|
with:
|
||||||
name: e2e-ui-test-results-${{ matrix.runner }}
|
name: e2e-ui-test-results-${{ matrix.runner }}
|
||||||
@@ -544,7 +544,7 @@ jobs:
|
|||||||
run: pnpm test:web:maintenance
|
run: pnpm test:web:maintenance
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Archive maintenance tests (web) results
|
- name: Archive maintenance tests (web) results
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
if: success() || failure()
|
if: success() || failure()
|
||||||
with:
|
with:
|
||||||
name: e2e-maintenance-isolated-test-results-${{ matrix.runner }}
|
name: e2e-maintenance-isolated-test-results-${{ matrix.runner }}
|
||||||
@@ -554,7 +554,7 @@ jobs:
|
|||||||
run: docker compose logs --no-color > docker-compose-logs.txt
|
run: docker compose logs --no-color > docker-compose-logs.txt
|
||||||
working-directory: ./e2e
|
working-directory: ./e2e
|
||||||
- name: Archive Docker logs
|
- name: Archive Docker logs
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: e2e-web-docker-logs-${{ matrix.runner }}
|
name: e2e-web-docker-logs-${{ matrix.runner }}
|
||||||
@@ -566,7 +566,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
- uses: immich-app/devtools/actions/success-check@53bb77345ee9f953f93bd6fd9980f07a2f24965e # success-check-action-v0.0.5
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
mobile-unit-tests:
|
mobile-unit-tests:
|
||||||
@@ -578,7 +578,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -588,7 +588,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0
|
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -610,7 +610,7 @@ jobs:
|
|||||||
working-directory: ./machine-learning
|
working-directory: ./machine-learning
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -620,7 +620,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
|
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.11
|
python-version: 3.11
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
@@ -650,7 +650,7 @@ jobs:
|
|||||||
working-directory: ./.github
|
working-directory: ./.github
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -661,9 +661,9 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './.github/.nvmrc'
|
node-version-file: './.github/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -680,7 +680,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -701,7 +701,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -712,9 +712,9 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -763,7 +763,7 @@ jobs:
|
|||||||
working-directory: ./server
|
working-directory: ./server
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -774,9 +774,9 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ jobs:
|
|||||||
should_run: ${{ steps.check.outputs.should_run }}
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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: Check what should run
|
- name: Check what should run
|
||||||
id: check
|
id: check
|
||||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
github-token: ${{ steps.token.outputs.token }}
|
||||||
filters: |
|
filters: |
|
||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }}
|
if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }}
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
|
||||||
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 }}
|
||||||
@@ -68,6 +68,6 @@ jobs:
|
|||||||
permissions: {}
|
permissions: {}
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
- uses: immich-app/devtools/actions/success-check@53bb77345ee9f953f93bd6fd9980f07a2f24965e # success-check-action-v0.0.5
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|||||||
Vendored
+8
-1
@@ -5,6 +5,13 @@
|
|||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"dart-code.flutter",
|
"dart-code.flutter",
|
||||||
"dart-code.dart-code",
|
"dart-code.dart-code",
|
||||||
"dcmdev.dcm-vscode-extension"
|
"dcmdev.dcm-vscode-extension",
|
||||||
|
"bradlc.vscode-tailwindcss",
|
||||||
|
"ms-playwright.playwright",
|
||||||
|
"vitest.explorer",
|
||||||
|
"editorconfig.editorconfig",
|
||||||
|
"foxundermoon.shell-format",
|
||||||
|
"timonwong.shellcheck",
|
||||||
|
"bluebrown.yamlfmt"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+35
-13
@@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"[css]": {
|
"[css]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true
|
||||||
"editor.tabSize": 2
|
|
||||||
},
|
},
|
||||||
"[dart]": {
|
"[dart]": {
|
||||||
"editor.defaultFormatter": "Dart-Code.dart-code",
|
"editor.defaultFormatter": "Dart-Code.dart-code",
|
||||||
@@ -19,18 +18,15 @@
|
|||||||
"source.removeUnusedImports": "explicit"
|
"source.removeUnusedImports": "explicit"
|
||||||
},
|
},
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true
|
||||||
"editor.tabSize": 2
|
|
||||||
},
|
},
|
||||||
"[json]": {
|
"[json]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true
|
||||||
"editor.tabSize": 2
|
|
||||||
},
|
},
|
||||||
"[jsonc]": {
|
"[jsonc]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true
|
||||||
"editor.tabSize": 2
|
|
||||||
},
|
},
|
||||||
"[svelte]": {
|
"[svelte]": {
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
@@ -38,8 +34,7 @@
|
|||||||
"source.removeUnusedImports": "explicit"
|
"source.removeUnusedImports": "explicit"
|
||||||
},
|
},
|
||||||
"editor.defaultFormatter": "svelte.svelte-vscode",
|
"editor.defaultFormatter": "svelte.svelte-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true
|
||||||
"editor.tabSize": 2
|
|
||||||
},
|
},
|
||||||
"[typescript]": {
|
"[typescript]": {
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
@@ -47,18 +42,45 @@
|
|||||||
"source.removeUnusedImports": "explicit"
|
"source.removeUnusedImports": "explicit"
|
||||||
},
|
},
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true
|
||||||
"editor.tabSize": 2
|
|
||||||
},
|
},
|
||||||
"cSpell.words": ["immich"],
|
"cSpell.words": ["immich"],
|
||||||
|
"css.lint.unknownAtRules": "ignore",
|
||||||
|
"editor.bracketPairColorization.enabled": true,
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
|
"eslint.useFlatConfig": true,
|
||||||
"eslint.validate": ["javascript", "typescript", "svelte"],
|
"eslint.validate": ["javascript", "typescript", "svelte"],
|
||||||
|
"eslint.workingDirectories": [
|
||||||
|
{ "directory": "cli", "changeProcessCWD": true },
|
||||||
|
{ "directory": "e2e", "changeProcessCWD": true },
|
||||||
|
{ "directory": "server", "changeProcessCWD": true },
|
||||||
|
{ "directory": "web", "changeProcessCWD": true }
|
||||||
|
],
|
||||||
|
"files.watcherExclude": {
|
||||||
|
"**/.jj/**": true,
|
||||||
|
"**/.git/**": true,
|
||||||
|
"**/node_modules/**": true,
|
||||||
|
"**/build/**": true,
|
||||||
|
"**/dist/**": true,
|
||||||
|
"**/.svelte-kit/**": true
|
||||||
|
},
|
||||||
"explorer.fileNesting.enabled": true,
|
"explorer.fileNesting.enabled": true,
|
||||||
"explorer.fileNesting.patterns": {
|
"explorer.fileNesting.patterns": {
|
||||||
"*.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",
|
"*.ts": "${capture}.spec.ts,${capture}.mock.ts",
|
||||||
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock, pnpm-workspace.yaml, .pnpmfile.cjs"
|
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock, pnpm-workspace.yaml, .pnpmfile.cjs"
|
||||||
},
|
},
|
||||||
|
"search.exclude": {
|
||||||
|
"**/node_modules": true,
|
||||||
|
"**/build": true,
|
||||||
|
"**/dist": true,
|
||||||
|
"**/.svelte-kit": true,
|
||||||
|
"**/open-api/typescript-sdk/src": true
|
||||||
|
},
|
||||||
"svelte.enable-ts-plugin": true,
|
"svelte.enable-ts-plugin": true,
|
||||||
"typescript.preferences.importModuleSpecifier": "non-relative"
|
"tailwindCSS.experimental.configFile": {
|
||||||
|
"web/src/app.css": "web/src/**"
|
||||||
|
},
|
||||||
|
"js/ts.preferences.importModuleSpecifier": "non-relative",
|
||||||
|
"vitest.maximumConfigs": 10
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ Please try to keep pull requests as focused as possible. A PR should do exactly
|
|||||||
|
|
||||||
If you are looking for something to work on, there are discussions and issues with a `good-first-issue` label on them. These are always a good starting point. If none of them sound interesting or fit your skill set, feel free to reach out on our Discord. We're happy to help you find something to work on!
|
If you are looking for something to work on, there are discussions and issues with a `good-first-issue` label on them. These are always a good starting point. If none of them sound interesting or fit your skill set, feel free to reach out on our Discord. We're happy to help you find something to work on!
|
||||||
|
|
||||||
|
We usually do not assign issues to new contributors, since it happens often that a PR is never even opened. Again, reach out on Discord if you fear putting a lot of time into fixing an issue, but ending up with a duplicate PR.
|
||||||
|
|
||||||
## Use of generative AI
|
## Use of generative AI
|
||||||
|
|
||||||
We ask you not to open PRs generated with an LLM. We find that code generated like this tends to need a large amount of back-and-forth, which is a very inefficient use of our time. If we want LLM-generated code, it's much faster for us to use an LLM ourselves than to go through an intermediary via a pull request.
|
We ask you not to open PRs generated with an LLM. We find that code generated like this tends to need a large amount of back-and-forth, which is a very inefficient use of our time. If we want LLM-generated code, it's much faster for us to use an LLM ourselves than to go through an intermediary via a pull request.
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
24.13.1
|
24.14.1
|
||||||
|
|||||||
+8
-9
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.5.6",
|
"version": "2.6.3",
|
||||||
"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",
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
"@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": "^24.10.13",
|
"@types/node": "^24.12.0",
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^4.0.0",
|
||||||
"byte-size": "^9.0.0",
|
"byte-size": "^9.0.0",
|
||||||
"cli-progress": "^3.12.0",
|
"cli-progress": "^3.12.0",
|
||||||
"commander": "^12.0.0",
|
"commander": "^12.0.0",
|
||||||
@@ -35,9 +35,8 @@
|
|||||||
"prettier-plugin-organize-imports": "^4.0.0",
|
"prettier-plugin-organize-imports": "^4.0.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"typescript-eslint": "^8.28.0",
|
"typescript-eslint": "^8.28.0",
|
||||||
"vite": "^7.0.0",
|
"vite": "^8.0.0",
|
||||||
"vite-tsconfig-paths": "^6.0.0",
|
"vitest": "^4.0.0",
|
||||||
"vitest": "^3.0.0",
|
|
||||||
"vitest-fetch-mock": "^0.4.0",
|
"vitest-fetch-mock": "^0.4.0",
|
||||||
"yaml": "^2.3.1"
|
"yaml": "^2.3.1"
|
||||||
},
|
},
|
||||||
@@ -49,8 +48,8 @@
|
|||||||
"prepack": "pnpm run build",
|
"prepack": "pnpm run build",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:cov": "vitest --coverage",
|
"test:cov": "vitest --coverage",
|
||||||
"format": "prettier --check .",
|
"format": "prettier --cache --check .",
|
||||||
"format:fix": "prettier --write .",
|
"format:fix": "prettier --cache --write --list-different .",
|
||||||
"check": "tsc --noEmit"
|
"check": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -69,6 +68,6 @@
|
|||||||
"micromatch": "^4.0.8"
|
"micromatch": "^4.0.8"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "24.13.1"
|
"node": "24.14.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import * as os from 'node:os';
|
import os from 'node:os';
|
||||||
import * as path from 'node:path';
|
import path from 'node:path';
|
||||||
import { setTimeout as sleep } from 'node:timers/promises';
|
import { setTimeout as sleep } from 'node:timers/promises';
|
||||||
import { describe, expect, it, MockedFunction, vi } from 'vitest';
|
import { describe, expect, it, MockedFunction, vi } from 'vitest';
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ describe('uploadFiles', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('returns new assets when upload file is successful', async () => {
|
it('returns new assets when upload file is successful', async () => {
|
||||||
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), () => {
|
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), function () {
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
body: JSON.stringify({ id: 'fc5621b1-86f6-44a1-9905-403e607df9f5', status: 'created' }),
|
body: JSON.stringify({ id: 'fc5621b1-86f6-44a1-9905-403e607df9f5', status: 'created' }),
|
||||||
@@ -75,7 +75,7 @@ describe('uploadFiles', () => {
|
|||||||
|
|
||||||
it('returns new assets when upload file retry is successful', async () => {
|
it('returns new assets when upload file retry is successful', async () => {
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), () => {
|
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), function () {
|
||||||
counter++;
|
counter++;
|
||||||
if (counter < retry) {
|
if (counter < retry) {
|
||||||
throw new Error('Network error');
|
throw new Error('Network error');
|
||||||
@@ -96,7 +96,7 @@ describe('uploadFiles', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('returns new assets when upload file retry is failed', async () => {
|
it('returns new assets when upload file retry is failed', async () => {
|
||||||
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), () => {
|
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), function () {
|
||||||
throw new Error('Network error');
|
throw new Error('Network error');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -236,16 +236,19 @@ describe('startWatch', () => {
|
|||||||
await sleep(100); // to debounce the watcher from considering the test file as a existing file
|
await sleep(100); // to debounce the watcher from considering the test file as a existing file
|
||||||
await fs.promises.writeFile(testFilePath, 'testjpg');
|
await fs.promises.writeFile(testFilePath, 'testjpg');
|
||||||
|
|
||||||
await vi.waitUntil(() => checkBulkUploadMocked.mock.calls.length > 0, 3000);
|
await vi.waitFor(
|
||||||
expect(checkBulkUpload).toHaveBeenCalledWith({
|
() =>
|
||||||
assetBulkUploadCheckDto: {
|
expect(checkBulkUpload).toHaveBeenCalledWith({
|
||||||
assets: [
|
assetBulkUploadCheckDto: {
|
||||||
expect.objectContaining({
|
assets: [
|
||||||
id: testFilePath,
|
expect.objectContaining({
|
||||||
}),
|
id: testFilePath,
|
||||||
],
|
}),
|
||||||
},
|
],
|
||||||
});
|
},
|
||||||
|
}),
|
||||||
|
{ timeout: 5000 },
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should filter out unsupported files', async () => {
|
it('should filter out unsupported files', async () => {
|
||||||
@@ -257,16 +260,19 @@ describe('startWatch', () => {
|
|||||||
await fs.promises.writeFile(testFilePath, 'testjpg');
|
await fs.promises.writeFile(testFilePath, 'testjpg');
|
||||||
await fs.promises.writeFile(unsupportedFilePath, 'testtxt');
|
await fs.promises.writeFile(unsupportedFilePath, 'testtxt');
|
||||||
|
|
||||||
await vi.waitUntil(() => checkBulkUploadMocked.mock.calls.length > 0, 3000);
|
await vi.waitFor(
|
||||||
expect(checkBulkUpload).toHaveBeenCalledWith({
|
() =>
|
||||||
assetBulkUploadCheckDto: {
|
expect(checkBulkUpload).toHaveBeenCalledWith({
|
||||||
assets: expect.arrayContaining([
|
assetBulkUploadCheckDto: {
|
||||||
expect.objectContaining({
|
assets: expect.arrayContaining([
|
||||||
id: testFilePath,
|
expect.objectContaining({
|
||||||
}),
|
id: testFilePath,
|
||||||
]),
|
}),
|
||||||
},
|
]),
|
||||||
});
|
},
|
||||||
|
}),
|
||||||
|
{ timeout: 5000 },
|
||||||
|
);
|
||||||
|
|
||||||
expect(checkBulkUpload).not.toHaveBeenCalledWith({
|
expect(checkBulkUpload).not.toHaveBeenCalledWith({
|
||||||
assetBulkUploadCheckDto: {
|
assetBulkUploadCheckDto: {
|
||||||
@@ -291,16 +297,19 @@ describe('startWatch', () => {
|
|||||||
await fs.promises.writeFile(testFilePath, 'testjpg');
|
await fs.promises.writeFile(testFilePath, 'testjpg');
|
||||||
await fs.promises.writeFile(ignoredFilePath, 'ignoredjpg');
|
await fs.promises.writeFile(ignoredFilePath, 'ignoredjpg');
|
||||||
|
|
||||||
await vi.waitUntil(() => checkBulkUploadMocked.mock.calls.length > 0, 3000);
|
await vi.waitFor(
|
||||||
expect(checkBulkUpload).toHaveBeenCalledWith({
|
() =>
|
||||||
assetBulkUploadCheckDto: {
|
expect(checkBulkUpload).toHaveBeenCalledWith({
|
||||||
assets: expect.arrayContaining([
|
assetBulkUploadCheckDto: {
|
||||||
expect.objectContaining({
|
assets: expect.arrayContaining([
|
||||||
id: testFilePath,
|
expect.objectContaining({
|
||||||
}),
|
id: testFilePath,
|
||||||
]),
|
}),
|
||||||
},
|
]),
|
||||||
});
|
},
|
||||||
|
}),
|
||||||
|
{ timeout: 5000 },
|
||||||
|
);
|
||||||
|
|
||||||
expect(checkBulkUpload).not.toHaveBeenCalledWith({
|
expect(checkBulkUpload).not.toHaveBeenCalledWith({
|
||||||
assetBulkUploadCheckDto: {
|
assetBulkUploadCheckDto: {
|
||||||
|
|||||||
+1
-1
@@ -81,7 +81,7 @@ export const connect = async (url: string, key: string) => {
|
|||||||
|
|
||||||
const [error] = await withError(getMyUser());
|
const [error] = await withError(getMyUser());
|
||||||
if (isHttpError(error)) {
|
if (isHttpError(error)) {
|
||||||
logError(error, 'Failed to connect to server');
|
logError(error, `Failed to connect to server ${url}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-6
@@ -1,10 +1,12 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig, UserConfig } from 'vite';
|
||||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
resolve: { alias: { src: '/src' } },
|
resolve: {
|
||||||
|
alias: { src: '/src' },
|
||||||
|
tsconfigPaths: true,
|
||||||
|
},
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rolldownOptions: {
|
||||||
input: 'src/index.ts',
|
input: 'src/index.ts',
|
||||||
output: {
|
output: {
|
||||||
dir: 'dist',
|
dir: 'dist',
|
||||||
@@ -16,5 +18,8 @@ export default defineConfig({
|
|||||||
// bundle everything except for Node built-ins
|
// bundle everything except for Node built-ins
|
||||||
noExternal: /^(?!node:).*$/,
|
noExternal: /^(?!node:).*$/,
|
||||||
},
|
},
|
||||||
plugins: [tsconfigPaths()],
|
test: {
|
||||||
});
|
name: 'cli:unit',
|
||||||
|
globals: true,
|
||||||
|
},
|
||||||
|
} as UserConfig);
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { defineConfig } from 'vitest/config';
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
test: {
|
|
||||||
globals: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[tools]
|
[tools]
|
||||||
terragrunt = "0.98.0"
|
terragrunt = "0.99.4"
|
||||||
opentofu = "1.11.4"
|
opentofu = "1.11.5"
|
||||||
|
|
||||||
[tasks."tg:fmt"]
|
[tasks."tg:fmt"]
|
||||||
run = "terragrunt hclfmt"
|
run = "terragrunt hclfmt"
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ services:
|
|||||||
IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues
|
IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues
|
||||||
IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://docs.immich.app
|
IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://docs.immich.app
|
||||||
IMMICH_THIRD_PARTY_SUPPORT_URL: https://docs.immich.app/community-guides
|
IMMICH_THIRD_PARTY_SUPPORT_URL: https://docs.immich.app/community-guides
|
||||||
|
IMMICH_HELMET_FILE: 'true'
|
||||||
ports:
|
ports:
|
||||||
- 9230:9230
|
- 9230:9230
|
||||||
- 9231:9231
|
- 9231:9231
|
||||||
@@ -155,7 +156,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:930b41430fb727f533c5982fe509b6f04233e26d0f7354e04de4b0d5c706e44e
|
image: docker.io/valkey/valkey:9@sha256:3eeb09785cd61ec8e3be35f8804c8892080f3ca21934d628abc24ee4ed1698f6
|
||||||
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:9@sha256:930b41430fb727f533c5982fe509b6f04233e26d0f7354e04de4b0d5c706e44e
|
image: docker.io/valkey/valkey:9@sha256:3eeb09785cd61ec8e3be35f8804c8892080f3ca21934d628abc24ee4ed1698f6
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
@@ -85,7 +85,7 @@ services:
|
|||||||
container_name: immich_prometheus
|
container_name: immich_prometheus
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- 9090:9090
|
||||||
image: prom/prometheus@sha256:1f0f50f06acaceb0f5670d2c8a658a599affe7b0d8e78b898c1035653849a702
|
image: prom/prometheus@sha256:4a61322ac1103a0e3aea2a61ef1718422a48fa046441f299d71e660a3bc71ae9
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
@@ -97,7 +97,7 @@ services:
|
|||||||
command: ['./run.sh', '-disable-reporting']
|
command: ['./run.sh', '-disable-reporting']
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
image: grafana/grafana:12.3.2-ubuntu@sha256:6cca4b429a1dc0d37d401dee54825c12d40056c3c6f3f56e3f0d6318ce77749b
|
image: grafana/grafana:12.4.2-ubuntu@sha256:78839fe49e1425c02416fa8072591533a72bd9598e563b54a07d78f9e27fb5d3
|
||||||
volumes:
|
volumes:
|
||||||
- grafana-data:/var/lib/grafana
|
- grafana-data:/var/lib/grafana
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:930b41430fb727f533c5982fe509b6f04233e26d0f7354e04de4b0d5c706e44e
|
image: docker.io/valkey/valkey:9@sha256:3eeb09785cd61ec8e3be35f8804c8892080f3ca21934d628abc24ee4ed1698f6
|
||||||
user: '1000:1000'
|
user: '1000:1000'
|
||||||
security_opt:
|
security_opt:
|
||||||
- no-new-privileges:true
|
- no-new-privileges:true
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:930b41430fb727f533c5982fe509b6f04233e26d0f7354e04de4b0d5c706e44e
|
image: docker.io/valkey/valkey:9@sha256:3eeb09785cd61ec8e3be35f8804c8892080f3ca21934d628abc24ee4ed1698f6
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
24.13.1
|
24.14.1
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -67,7 +67,8 @@ graph TD
|
|||||||
C --> D["Thumbnail Generation (Large, small, blurred and person)"]
|
C --> D["Thumbnail Generation (Large, small, blurred and person)"]
|
||||||
D --> E[Smart Search]
|
D --> E[Smart Search]
|
||||||
D --> F[Face Detection]
|
D --> F[Face Detection]
|
||||||
D --> G[Video Transcoding]
|
D --> G[OCR]
|
||||||
E --> H[Duplicate Detection]
|
D --> H[Video Transcoding]
|
||||||
F --> I[Facial Recognition]
|
E --> I[Duplicate Detection]
|
||||||
|
F --> J[Facial Recognition]
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Immich supports 3rd party authentication via [OpenID Connect][oidc] (OIDC), an i
|
|||||||
- [Authelia](https://www.authelia.com/integration/openid-connect/immich/)
|
- [Authelia](https://www.authelia.com/integration/openid-connect/immich/)
|
||||||
- [Okta](https://www.okta.com/openid-connect/)
|
- [Okta](https://www.okta.com/openid-connect/)
|
||||||
- [Google](https://developers.google.com/identity/openid-connect/openid-connect)
|
- [Google](https://developers.google.com/identity/openid-connect/openid-connect)
|
||||||
|
- [Keycloak](https://www.keycloak.org)
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@@ -253,4 +254,40 @@ Configuration of OAuth in Immich System Settings
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Keycloak Example</summary>
|
||||||
|
|
||||||
|
### Keycloak Example
|
||||||
|
|
||||||
|
Here's an example of OAuth configured for Keycloak:
|
||||||
|
|
||||||
|
Create your immich client on your Keycloak Realm.
|
||||||
|
|
||||||
|
<img src={require('./img/keycloak-general-settings.webp').default} width='100%' title="Keycloak Client general Settings" />
|
||||||
|
<img src={require('./img/keycloak-access-settings.webp').default} width='100%' title="Keycloak Client Access Settings" />
|
||||||
|
<img src={require('./img/keycloak-capability-config.webp').default} width='100%' title="Keycloak Client Capability Configuration" />
|
||||||
|
|
||||||
|
Configuration of OAuth in Immich System Settings
|
||||||
|
|
||||||
|
| Setting | Value |
|
||||||
|
| ---------------------------- | ----------------------------------------------------- |
|
||||||
|
| Issuer URL | `https://<KEYCLOAK_DOMAIN>/realms/<YOUR_REALM>` |
|
||||||
|
| Client ID | immich |
|
||||||
|
| Client Secret | can be optained from Clients -> immich -> Credentials |
|
||||||
|
| Scope | openid email profile |
|
||||||
|
| Signing Algorithm | RS256 |
|
||||||
|
| Storage Label Claim | preferred_username |
|
||||||
|
| Role Claim | immich_role |
|
||||||
|
| Storage Quota Claim | immich_quota |
|
||||||
|
| Default Storage Quota (GiB) | 0 (empty for unlimited quota) |
|
||||||
|
| Button Text | Sign in with Keycloak (recommended) |
|
||||||
|
| Auto Register | Enabled (optional) |
|
||||||
|
| Auto Launch | Enabled (optional) |
|
||||||
|
| Mobile Redirect URI Override | Disabled |
|
||||||
|
| Mobile Redirect URI | |
|
||||||
|
|
||||||
|
Role Claim can be managed via Client Role. Remember to create a mapper with claim name `immich_role`.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
[oidc]: https://openid.net/connect/
|
[oidc]: https://openid.net/connect/
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ The default value is `ultrafast`.
|
|||||||
|
|
||||||
### Audio codec (`ffmpeg.targetAudioCodec`) {#ffmpeg.targetAudioCodec}
|
### Audio codec (`ffmpeg.targetAudioCodec`) {#ffmpeg.targetAudioCodec}
|
||||||
|
|
||||||
Which audio codec to use when the audio stream is being transcoded. Can be one of `mp3`, `aac`, `libopus`.
|
Which audio codec to use when the audio stream is being transcoded. Can be one of `mp3`, `aac`, `opus`.
|
||||||
|
|
||||||
The default value is `aac`.
|
The default value is `aac`.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Duplicates Utility
|
||||||
|
|
||||||
|
Immich comes with a duplicates utility to help you detect assets that look visually similar. The duplicate detection feature relies on machine learning and is enabled by default. For more information about when the duplicate detection job runs, see [Jobs and Workers](/administration/jobs-workers). Once an asset has been processed and added to a duplicate group, it becomes available to review in the "Review duplicates" utility, which can be found [here](https://my.immich.app/utilities/duplicates).
|
||||||
|
|
||||||
|
## Reviewing duplicates
|
||||||
|
|
||||||
|
The review duplicates page allows the user to individually select which assets should be kept and which ones should be trashed. When more than one asset is kept, there is an option to automatically put the kept assets into a stack.
|
||||||
|
|
||||||
|
### Automatic preselection
|
||||||
|
|
||||||
|
When using "Deduplicate All" or viewing suggestions, Immich automatically preselects which assets to keep based on:
|
||||||
|
|
||||||
|
1. **Image size in bytes** — larger files are preferred as they typically have higher quality.
|
||||||
|
2. **Count of EXIF data** — assets with more metadata are preferred.
|
||||||
|
|
||||||
|
### Synchronizing metadata
|
||||||
|
|
||||||
|
When resolving duplicates, metadata from trashed assets is automatically synchronized to the kept assets. The following metadata is synchronized:
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| Album | The kept assets will be added to _every_ album that the other assets in the group belong to. |
|
||||||
|
| Favorite | If any of the assets in the group have been added to favorites, every kept asset will also be added to favorites. |
|
||||||
|
| Rating | If one or more assets in the duplicate group have a rating, the highest rating is selected and synchronized to the kept assets. |
|
||||||
|
| Description | Descriptions from each asset are combined together and synchronized to all the kept assets. |
|
||||||
|
| Visibility | The most restrictive visibility is applied to the kept assets. |
|
||||||
|
| Location | Latitude and longitude are copied if all assets with geolocation data in the group share the same coordinates. |
|
||||||
|
| Tag | Tags from all assets in the group are merged and applied to every kept asset. |
|
||||||
@@ -28,17 +28,17 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
|
|||||||
|
|
||||||
## Video formats
|
## Video formats
|
||||||
|
|
||||||
| Format | Extension(s) | Supported? | Notes |
|
| Format | Extension(s) | Supported? | Notes |
|
||||||
| :---------- | :-------------------- | :----------------: | :---- |
|
| :---------- | :-------------------------- | :----------------: | :---- |
|
||||||
| `3GPP` | `.3gp` `.3gpp` | :white_check_mark: | |
|
| `3GPP` | `.3gp` `.3gpp` | :white_check_mark: | |
|
||||||
| `AVI` | `.avi` | :white_check_mark: | |
|
| `AVI` | `.avi` | :white_check_mark: | |
|
||||||
| `FLV` | `.flv` | :white_check_mark: | |
|
| `FLV` | `.flv` | :white_check_mark: | |
|
||||||
| `M4V` | `.m4v` | :white_check_mark: | |
|
| `M4V` | `.m4v` | :white_check_mark: | |
|
||||||
| `MATROSKA` | `.mkv` | :white_check_mark: | |
|
| `MATROSKA` | `.mkv` | :white_check_mark: | |
|
||||||
| `MP2T` | `.mts` `.m2ts` `.m2t` | :white_check_mark: | |
|
| `MP2T` | `.mts` `.m2ts` `.m2t` `.ts` | :white_check_mark: | |
|
||||||
| `MP4` | `.mp4` `.insv` | :white_check_mark: | |
|
| `MP4` | `.mp4` `.insv` | :white_check_mark: | |
|
||||||
| `MPEG` | `.mpg` `.mpe` `.mpeg` | :white_check_mark: | |
|
| `MPEG` | `.mpg` `.mpe` `.mpeg` | :white_check_mark: | |
|
||||||
| `MXF` | `.mxf` | :white_check_mark: | |
|
| `MXF` | `.mxf` | :white_check_mark: | |
|
||||||
| `QUICKTIME` | `.mov` | :white_check_mark: | |
|
| `QUICKTIME` | `.mov` | :white_check_mark: | |
|
||||||
| `WEBM` | `.webm` | :white_check_mark: | |
|
| `WEBM` | `.webm` | :white_check_mark: | |
|
||||||
| `WMV` | `.wmv` | :white_check_mark: | |
|
| `WMV` | `.wmv` | :white_check_mark: | |
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
You may decide that you'd like to modify the style document which is used to
|
You may decide that you'd like to modify the style document which is used to
|
||||||
draw the maps in Immich. In addition to visual customization, this also allows
|
draw the maps in Immich. In addition to visual customization, this also allows
|
||||||
you to pick your own map tile provider instead of the default one. The default
|
you to pick your own map tile provider instead of the default one. The default
|
||||||
`style.json` for [light theme](https://github.com/immich-app/immich/tree/main/server/resources/style-light.json)
|
`style.json` for [light theme](https://tiles.immich.cloud/v1/style/light.json)
|
||||||
and [dark theme](https://github.com/immich-app/immich/blob/main/server/resources/style-dark.json)
|
and [dark theme](https://tiles.immich.cloud/v1/style/dark.json)
|
||||||
can be used as a basis for creating your own style.
|
can be used as a basis for creating your own style.
|
||||||
|
|
||||||
There are several sources for already-made `style.json` map themes, as well as
|
There are several sources for already-made `style.json` map themes, as well as
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ The default configuration looks like this:
|
|||||||
"ffmpeg": {
|
"ffmpeg": {
|
||||||
"accel": "disabled",
|
"accel": "disabled",
|
||||||
"accelDecode": false,
|
"accelDecode": false,
|
||||||
"acceptedAudioCodecs": ["aac", "mp3", "libopus"],
|
"acceptedAudioCodecs": ["aac", "mp3", "opus"],
|
||||||
"acceptedContainers": ["mov", "ogg", "webm"],
|
"acceptedContainers": ["mov", "ogg", "webm"],
|
||||||
"acceptedVideoCodecs": ["h264"],
|
"acceptedVideoCodecs": ["h264"],
|
||||||
"bframes": -1,
|
"bframes": -1,
|
||||||
|
|||||||
@@ -29,22 +29,23 @@ These environment variables are used by the `docker-compose.yml` file and do **N
|
|||||||
|
|
||||||
## General
|
## General
|
||||||
|
|
||||||
| Variable | Description | Default | Containers | Workers |
|
| Variable | Description | Default | Containers | Workers |
|
||||||
| :---------------------------------- | :---------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
|
| :---------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
|
||||||
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
|
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
|
||||||
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
|
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
|
||||||
| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
|
| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
|
||||||
| `IMMICH_LOG_FORMAT` | Log output format (`console`, `json`) | `console` | server | api, microservices |
|
| `IMMICH_LOG_FORMAT` | Log output format (`console`, `json`) | `console` | server | api, microservices |
|
||||||
| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `/data` | server | api, microservices |
|
| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `/data` | server | api, microservices |
|
||||||
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
|
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
|
||||||
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
|
| `IMMICH_HELMET_FILE` | Path to a json file with [helmet](https://www.npmjs.com/package/helmet) options. Set to `false` to disable. Set to `true` to use `server/helmet.json`. | `false` | server | api, microservices |
|
||||||
| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | |
|
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
|
||||||
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
|
| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | |
|
||||||
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
|
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
|
||||||
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
|
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
|
||||||
| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api |
|
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
|
||||||
| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/administration/system-integrity) | | server | api, microservices |
|
| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api |
|
||||||
| `IMMICH_ALLOW_SETUP` | When `false` disables the `/auth/admin-sign-up` endpoint | `true` | server | api |
|
| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/administration/system-integrity) | | server | api, microservices |
|
||||||
|
| `IMMICH_ALLOW_SETUP` | When `false` disables the `/auth/admin-sign-up` endpoint | `true` | server | api |
|
||||||
|
|
||||||
\*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
|
\*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
|
||||||
`TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution.
|
`TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution.
|
||||||
@@ -166,6 +167,8 @@ Redis (Sentinel) URL example JSON before encoding:
|
|||||||
| `MACHINE_LEARNING_PRELOAD__CLIP__VISUAL` | Comma-separated list of (visual) CLIP model(s) to preload and cache | | machine learning |
|
| `MACHINE_LEARNING_PRELOAD__CLIP__VISUAL` | Comma-separated list of (visual) CLIP model(s) to preload and cache | | machine learning |
|
||||||
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION` | Comma-separated list of (recognition) facial recognition model(s) to preload and cache | | machine learning |
|
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION` | Comma-separated list of (recognition) facial recognition model(s) to preload and cache | | machine learning |
|
||||||
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION` | Comma-separated list of (detection) facial recognition model(s) to preload and cache | | machine learning |
|
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION` | Comma-separated list of (detection) facial recognition model(s) to preload and cache | | machine learning |
|
||||||
|
| `MACHINE_LEARNING_PRELOAD__OCR__RECOGNITION` | Comma-separated list of (recognition) OCR model(s) to preload and cache | | machine learning |
|
||||||
|
| `MACHINE_LEARNING_PRELOAD__OCR__DETECTION` | Comma-separated list of (detection) OCR model(s) to preload and cache | | machine learning |
|
||||||
| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning |
|
| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning |
|
||||||
| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning |
|
| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning |
|
||||||
| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning |
|
| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning |
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Hardware and software requirements for Immich:
|
|||||||
|
|
||||||
## Hardware
|
## Hardware
|
||||||
|
|
||||||
- **OS**: Recommended Linux or \*nix operating system (Ubuntu, Debian, etc).
|
- **OS**: Recommended Linux or \*nix 64-bit operating system (Ubuntu, Debian, etc).
|
||||||
- Non-Linux OSes tend to provide a poor Docker experience and are strongly discouraged.
|
- Non-Linux OSes tend to provide a poor Docker experience and are strongly discouraged.
|
||||||
Our ability to assist with setup or troubleshooting on non-Linux OSes will be severely reduced.
|
Our ability to assist with setup or troubleshooting on non-Linux OSes will be severely reduced.
|
||||||
If you still want to try to use a non-Linux OS, you can set it up as follows:
|
If you still want to try to use a non-Linux OS, you can set it up as follows:
|
||||||
@@ -19,6 +19,10 @@ Hardware and software requirements for Immich:
|
|||||||
If you have issues, we recommend that you switch to a supported VM deployment.
|
If you have issues, we recommend that you switch to a supported VM deployment.
|
||||||
- **RAM**: Minimum 6GB, recommended 8GB.
|
- **RAM**: Minimum 6GB, recommended 8GB.
|
||||||
- **CPU**: Minimum 2 cores, recommended 4 cores.
|
- **CPU**: Minimum 2 cores, recommended 4 cores.
|
||||||
|
- Immich runs on the `amd64` and `arm64` platforms.
|
||||||
|
Since `v2.6`, the machine learning container on `amd64` requires the `>= x86-64-v2` [microarchitecture level](https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels).
|
||||||
|
Most CPUs released since ~2012 support this microarchitecture.
|
||||||
|
If you are using a virtual machine, ensure you have selected a [supported microarchitecture](https://pve.proxmox.com/pve-docs/chapter-qm.html#_qemu_cpu_types).
|
||||||
- **Storage**: Recommended Unix-compatible filesystem (EXT4, ZFS, APFS, etc.) with support for user/group ownership and permissions.
|
- **Storage**: Recommended Unix-compatible filesystem (EXT4, ZFS, APFS, etc.) with support for user/group ownership and permissions.
|
||||||
- The generation of thumbnails and transcoded video can increase the size of the photo library by 10-20% on average.
|
- The generation of thumbnails and transcoded video can increase the size of the photo library by 10-20% on average.
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -4,8 +4,8 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docusaurus": "docusaurus",
|
"docusaurus": "docusaurus",
|
||||||
"format": "prettier --check .",
|
"format": "prettier --cache --check .",
|
||||||
"format:fix": "prettier --write .",
|
"format:fix": "prettier --cache --write --list-different .",
|
||||||
"start": "docusaurus start --port 3005",
|
"start": "docusaurus start --port 3005",
|
||||||
"copy:openapi": "jq -c < ../open-api/immich-openapi-specs.json > ./static/openapi.json || exit 0",
|
"copy:openapi": "jq -c < ../open-api/immich-openapi-specs.json > ./static/openapi.json || exit 0",
|
||||||
"build": "pnpm run copy:openapi && docusaurus build",
|
"build": "pnpm run copy:openapi && docusaurus build",
|
||||||
@@ -58,6 +58,6 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "24.13.1"
|
"node": "24.14.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+4
@@ -1,4 +1,8 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"label": "v2.6.3",
|
||||||
|
"url": "https://docs.v2.6.3.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v2.5.6",
|
"label": "v2.5.6",
|
||||||
"url": "https://docs.v2.5.6.archive.immich.app"
|
"url": "https://docs.v2.5.6.archive.immich.app"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export enum OAuthClient {
|
|||||||
export enum OAuthUser {
|
export enum OAuthUser {
|
||||||
NO_EMAIL = 'no-email',
|
NO_EMAIL = 'no-email',
|
||||||
NO_NAME = 'no-name',
|
NO_NAME = 'no-name',
|
||||||
|
ID_TOKEN_CLAIMS = 'id-token-claims',
|
||||||
WITH_QUOTA = 'with-quota',
|
WITH_QUOTA = 'with-quota',
|
||||||
WITH_USERNAME = 'with-username',
|
WITH_USERNAME = 'with-username',
|
||||||
WITH_ROLE = 'with-role',
|
WITH_ROLE = 'with-role',
|
||||||
@@ -52,12 +53,25 @@ const withDefaultClaims = (sub: string) => ({
|
|||||||
email_verified: true,
|
email_verified: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const getClaims = (sub: string) => claims.find((user) => user.sub === sub) || withDefaultClaims(sub);
|
const getClaims = (sub: string, use?: string) => {
|
||||||
|
if (sub === OAuthUser.ID_TOKEN_CLAIMS) {
|
||||||
|
return {
|
||||||
|
sub,
|
||||||
|
email: `oauth-${sub}@immich.app`,
|
||||||
|
email_verified: true,
|
||||||
|
name: use === 'id_token' ? 'ID Token User' : 'Userinfo User',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return claims.find((user) => user.sub === sub) || withDefaultClaims(sub);
|
||||||
|
};
|
||||||
|
|
||||||
const setup = async () => {
|
const setup = async () => {
|
||||||
const { privateKey, publicKey } = await generateKeyPair('RS256');
|
const { privateKey, publicKey } = await generateKeyPair('RS256');
|
||||||
|
|
||||||
const redirectUris = ['http://127.0.0.1:2285/auth/login', 'https://photos.immich.app/oauth/mobile-redirect'];
|
const redirectUris = [
|
||||||
|
'http://127.0.0.1:2285/auth/login',
|
||||||
|
'https://photos.immich.app/oauth/mobile-redirect',
|
||||||
|
];
|
||||||
const port = 2286;
|
const port = 2286;
|
||||||
const host = '0.0.0.0';
|
const host = '0.0.0.0';
|
||||||
const oidc = new Provider(`http://${host}:${port}`, {
|
const oidc = new Provider(`http://${host}:${port}`, {
|
||||||
@@ -66,7 +80,10 @@ const setup = async () => {
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
ctx.body = 'Internal Server Error';
|
ctx.body = 'Internal Server Error';
|
||||||
},
|
},
|
||||||
findAccount: (ctx, sub) => ({ accountId: sub, claims: () => getClaims(sub) }),
|
findAccount: (ctx, sub) => ({
|
||||||
|
accountId: sub,
|
||||||
|
claims: (use) => getClaims(sub, use),
|
||||||
|
}),
|
||||||
scopes: ['openid', 'email', 'profile'],
|
scopes: ['openid', 'email', 'profile'],
|
||||||
claims: {
|
claims: {
|
||||||
openid: ['sub'],
|
openid: ['sub'],
|
||||||
@@ -94,6 +111,7 @@ const setup = async () => {
|
|||||||
state: 'oidc.state',
|
state: 'oidc.state',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
conformIdTokenClaims: false,
|
||||||
pkce: {
|
pkce: {
|
||||||
required: () => false,
|
required: () => false,
|
||||||
},
|
},
|
||||||
@@ -125,7 +143,10 @@ const setup = async () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const onStart = () => console.log(`[e2e-auth-server] http://${host}:${port}/.well-known/openid-configuration`);
|
const onStart = () =>
|
||||||
|
console.log(
|
||||||
|
`[e2e-auth-server] http://${host}:${port}/.well-known/openid-configuration`,
|
||||||
|
);
|
||||||
const app = oidc.listen(port, host, onStart);
|
const app = oidc.listen(port, host, onStart);
|
||||||
return () => app.close();
|
return () => app.close();
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
24.13.1
|
24.14.1
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich-e2e-redis
|
container_name: immich-e2e-redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:930b41430fb727f533c5982fe509b6f04233e26d0f7354e04de4b0d5c706e44e
|
image: docker.io/valkey/valkey:9@sha256:3eeb09785cd61ec8e3be35f8804c8892080f3ca21934d628abc24ee4ed1698f6
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
|
|||||||
+8
-7
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "2.5.6",
|
"version": "2.6.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
"start:web": "pnpm exec playwright test --ui --project=web",
|
"start:web": "pnpm exec playwright test --ui --project=web",
|
||||||
"start:web:maintenance": "pnpm exec playwright test --ui --project=maintenance",
|
"start:web:maintenance": "pnpm exec playwright test --ui --project=maintenance",
|
||||||
"start:web:ui": "pnpm exec playwright test --ui --project=ui",
|
"start:web:ui": "pnpm exec playwright test --ui --project=ui",
|
||||||
"format": "prettier --check .",
|
"format": "prettier --cache --check .",
|
||||||
"format:fix": "prettier --write .",
|
"format:fix": "prettier --cache --write --list-different .",
|
||||||
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
|
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
|
||||||
"lint:fix": "pnpm run lint --fix",
|
"lint:fix": "pnpm run lint --fix",
|
||||||
"check": "tsc --noEmit"
|
"check": "tsc --noEmit"
|
||||||
@@ -27,12 +27,12 @@
|
|||||||
"@eslint/js": "^10.0.0",
|
"@eslint/js": "^10.0.0",
|
||||||
"@faker-js/faker": "^10.1.0",
|
"@faker-js/faker": "^10.1.0",
|
||||||
"@immich/cli": "workspace:*",
|
"@immich/cli": "workspace:*",
|
||||||
"@immich/e2e-auth-server": "workspace:*",
|
"@immich/e2e-auth-server": "workspace:*",
|
||||||
"@immich/sdk": "workspace:*",
|
"@immich/sdk": "workspace:*",
|
||||||
"@playwright/test": "^1.44.1",
|
"@playwright/test": "^1.44.1",
|
||||||
"@socket.io/component-emitter": "^3.1.2",
|
"@socket.io/component-emitter": "^3.1.2",
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^24.10.13",
|
"@types/node": "^24.12.0",
|
||||||
"@types/pg": "^8.15.1",
|
"@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",
|
||||||
@@ -54,9 +54,10 @@
|
|||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"typescript-eslint": "^8.28.0",
|
"typescript-eslint": "^8.28.0",
|
||||||
"utimes": "^5.2.1",
|
"utimes": "^5.2.1",
|
||||||
"vitest": "^3.0.0"
|
"vite-tsconfig-paths": "^6.1.1",
|
||||||
|
"vitest": "^4.0.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "24.13.1"
|
"node": "24.14.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,651 @@
|
|||||||
|
import { LoginResponseDto } from '@immich/sdk';
|
||||||
|
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||||
|
import { errorDto } from 'src/responses';
|
||||||
|
import { app, utils } from 'src/utils';
|
||||||
|
import request from 'supertest';
|
||||||
|
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
describe('/duplicates', () => {
|
||||||
|
let admin: LoginResponseDto;
|
||||||
|
let user1: LoginResponseDto;
|
||||||
|
let user2: LoginResponseDto;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await utils.resetDatabase();
|
||||||
|
|
||||||
|
admin = await utils.adminSetup();
|
||||||
|
|
||||||
|
[user1, user2] = await Promise.all([
|
||||||
|
utils.userSetup(admin.accessToken, createUserDto.user1),
|
||||||
|
utils.userSetup(admin.accessToken, createUserDto.user2),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
// Reset assets, albums, tags, and stacks between tests to ensure clean state for repeated test runs
|
||||||
|
// Note: We don't reset users since they're set up once in beforeAll
|
||||||
|
// Stack must be reset before asset due to foreign key constraint
|
||||||
|
await utils.resetDatabase(['stack', 'asset', 'album', 'tag']);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /duplicates', () => {
|
||||||
|
it('should return empty array when no duplicates', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/duplicates')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return duplicate groups with suggestedKeepAssetIds', async () => {
|
||||||
|
// Create assets with different file sizes for duplicate detection
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Manually set duplicateId on both assets to create a duplicate group
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000001';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/duplicates')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual([
|
||||||
|
{
|
||||||
|
duplicateId,
|
||||||
|
assets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: asset1.id }),
|
||||||
|
expect.objectContaining({ id: asset2.id }),
|
||||||
|
]),
|
||||||
|
suggestedKeepAssetIds: expect.any(Array),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(body[0].suggestedKeepAssetIds.length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('POST /duplicates/resolve', () => {
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId: uuidDto.dummy, keepAssetIds: [], trashAssetIds: [] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return failure for non-existent duplicate group', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId: uuidDto.dummy, keepAssetIds: [], trashAssetIds: [] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual({
|
||||||
|
status: 'COMPLETED',
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
duplicateId: uuidDto.dummy,
|
||||||
|
status: 'FAILED',
|
||||||
|
reason: expect.stringContaining('not found or access denied'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve duplicate group with keepers', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000002';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual({
|
||||||
|
status: 'COMPLETED',
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
duplicateId,
|
||||||
|
status: 'SUCCESS',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify side effects: duplicateId cleared on kept asset
|
||||||
|
const keptAsset = await utils.getAssetInfo(user1.accessToken, asset1.id);
|
||||||
|
expect(keptAsset.duplicateId).toBeNull();
|
||||||
|
|
||||||
|
// Verify side effects: trashed asset is trashed and duplicateId cleared
|
||||||
|
const trashedAsset = await utils.getAssetInfo(user1.accessToken, asset2.id);
|
||||||
|
expect(trashedAsset.isTrashed).toBe(true);
|
||||||
|
expect(trashedAsset.duplicateId).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject when keepAssetIds and trashAssetIds overlap', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000003';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset1.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('FAILED');
|
||||||
|
expect(body.results[0].reason).toContain('disjoint');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require keepAssetIds when partially trashing', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000004';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [], trashAssetIds: [asset1.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('FAILED');
|
||||||
|
expect(body.results[0].reason).toContain('must cover all assets');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject partial resolution (not all assets covered)', async () => {
|
||||||
|
const [asset1, asset2, asset3] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000010';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset3.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('FAILED');
|
||||||
|
expect(body.results[0].reason).toContain('must cover all assets');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject asset not in duplicate group', async () => {
|
||||||
|
const [asset1, asset2, outsideAsset] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000011';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [outsideAsset.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('FAILED');
|
||||||
|
expect(body.results[0].reason).toContain('not a member of duplicate group');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow trash-all without keepers', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000012';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [], trashAssetIds: [asset1.id, asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual({
|
||||||
|
status: 'COMPLETED',
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
duplicateId,
|
||||||
|
status: 'SUCCESS',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify both assets are trashed
|
||||||
|
const [asset1Info, asset2Info] = await Promise.all([
|
||||||
|
utils.getAssetInfo(user1.accessToken, asset1.id),
|
||||||
|
utils.getAssetInfo(user1.accessToken, asset2.id),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(asset1Info.isTrashed).toBe(true);
|
||||||
|
expect(asset1Info.duplicateId).toBeNull();
|
||||||
|
expect(asset2Info.isTrashed).toBe(true);
|
||||||
|
expect(asset2Info.duplicateId).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject cross-user duplicate group access', async () => {
|
||||||
|
const asset1 = await utils.createAsset(user1.accessToken);
|
||||||
|
const asset2 = await utils.createAsset(user2.accessToken);
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000013';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user2.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
// User1 tries to resolve a group containing user2's asset
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('FAILED');
|
||||||
|
expect(body.results[0].reason).toContain('not a member of duplicate group');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should synchronize favorites when enabled', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Mark one asset as favorite
|
||||||
|
await request(app)
|
||||||
|
.put('/assets')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({ ids: [asset2.id], isFavorite: true });
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000020';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('SUCCESS');
|
||||||
|
|
||||||
|
// Verify favorite was synchronized to keeper
|
||||||
|
const keptAsset = await utils.getAssetInfo(user1.accessToken, asset1.id);
|
||||||
|
expect(keptAsset.isFavorite).toBe(true);
|
||||||
|
expect(keptAsset.duplicateId).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should synchronize visibility when enabled', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Archive one asset
|
||||||
|
await utils.archiveAssets(user1.accessToken, [asset2.id]);
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000021';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('SUCCESS');
|
||||||
|
|
||||||
|
// Verify visibility was synchronized to keeper
|
||||||
|
const keptAsset = await utils.getAssetInfo(user1.accessToken, asset1.id);
|
||||||
|
expect(keptAsset.visibility).toBe('archive');
|
||||||
|
expect(keptAsset.duplicateId).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should synchronize rating when enabled', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Set rating on one asset
|
||||||
|
await request(app)
|
||||||
|
.put('/assets')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({ ids: [asset2.id], rating: 5 });
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000022';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('SUCCESS');
|
||||||
|
|
||||||
|
// Verify rating was synchronized to keeper
|
||||||
|
const keptAsset = await utils.getAssetInfo(user1.accessToken, asset1.id);
|
||||||
|
expect(keptAsset.exifInfo?.rating).toBe(5);
|
||||||
|
expect(keptAsset.duplicateId).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should synchronize description when enabled', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Set description on one asset
|
||||||
|
await request(app)
|
||||||
|
.put('/assets')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({ ids: [asset2.id], description: 'Test description for duplicate' });
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000023';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('SUCCESS');
|
||||||
|
|
||||||
|
// Verify description was synchronized to keeper
|
||||||
|
const keptAsset = await utils.getAssetInfo(user1.accessToken, asset1.id);
|
||||||
|
expect(keptAsset.exifInfo?.description).toBe('Test description for duplicate');
|
||||||
|
expect(keptAsset.duplicateId).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should synchronize location when enabled', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Set location on one asset
|
||||||
|
await request(app)
|
||||||
|
.put('/assets')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({ ids: [asset2.id], latitude: 40.7128, longitude: -74.006 });
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000024';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('SUCCESS');
|
||||||
|
|
||||||
|
// Verify location was synchronized to keeper
|
||||||
|
const keptAsset = await utils.getAssetInfo(user1.accessToken, asset1.id);
|
||||||
|
expect(keptAsset.exifInfo?.latitude).toBe(40.7128);
|
||||||
|
expect(keptAsset.exifInfo?.longitude).toBe(-74.006);
|
||||||
|
expect(keptAsset.duplicateId).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should synchronize albums when enabled', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create albums and add assets to different albums
|
||||||
|
const album1 = await utils.createAlbum(user1.accessToken, {
|
||||||
|
albumName: 'Album 1',
|
||||||
|
assetIds: [asset1.id],
|
||||||
|
});
|
||||||
|
const album2 = await utils.createAlbum(user1.accessToken, {
|
||||||
|
albumName: 'Album 2',
|
||||||
|
assetIds: [asset2.id],
|
||||||
|
});
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000025';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('SUCCESS');
|
||||||
|
|
||||||
|
// Verify keeper is now in both albums
|
||||||
|
const keptAsset = await utils.getAssetInfo(user1.accessToken, asset1.id);
|
||||||
|
expect(keptAsset.duplicateId).toBeNull();
|
||||||
|
|
||||||
|
// Check albums directly
|
||||||
|
const { status: album1Status, body: album1Body } = await request(app)
|
||||||
|
.get(`/albums/${album1.id}`)
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
const { status: album2Status, body: album2Body } = await request(app)
|
||||||
|
.get(`/albums/${album2.id}`)
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
|
||||||
|
expect(album1Status).toBe(200);
|
||||||
|
expect(album2Status).toBe(200);
|
||||||
|
expect(album1Body.assets.map((a: any) => a.id)).toContain(asset1.id);
|
||||||
|
expect(album2Body.assets.map((a: any) => a.id)).toContain(asset1.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should synchronize tags when enabled', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Wait for metadata extraction to complete before adding tags
|
||||||
|
// Otherwise, metadata jobs will race and overwrite our tags
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||||
|
|
||||||
|
// Create tags and tag assets differently
|
||||||
|
const tags = await utils.upsertTags(user1.accessToken, ['tag1', 'tag2']);
|
||||||
|
await utils.tagAssets(user1.accessToken, tags[0].id, [asset1.id]);
|
||||||
|
await utils.tagAssets(user1.accessToken, tags[1].id, [asset2.id]);
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000026';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('SUCCESS');
|
||||||
|
|
||||||
|
// Verify keeper has both tags
|
||||||
|
const keptAsset = await utils.getAssetInfo(user1.accessToken, asset1.id);
|
||||||
|
expect(keptAsset.duplicateId).toBeNull();
|
||||||
|
expect(keptAsset.tags).toBeDefined();
|
||||||
|
const tagIds = keptAsset.tags?.map((t) => t.id) || [];
|
||||||
|
expect(tagIds).toContain(tags[0].id);
|
||||||
|
expect(tagIds).toContain(tags[1].id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle batch resolve with mixed success and failure', async () => {
|
||||||
|
// Create first group that will succeed
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
const duplicateId1 = '00000000-0000-4000-8000-000000000027';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId1);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId1);
|
||||||
|
|
||||||
|
// Create second group with non-existent duplicate ID (will fail)
|
||||||
|
const fakeId = '00000000-0000-4000-8000-000000000099';
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [
|
||||||
|
{ duplicateId: duplicateId1, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] },
|
||||||
|
{ duplicateId: fakeId, keepAssetIds: [], trashAssetIds: [] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.status).toBe('COMPLETED');
|
||||||
|
expect(body.results).toHaveLength(2);
|
||||||
|
|
||||||
|
// First group should succeed
|
||||||
|
expect(body.results[0].duplicateId).toBe(duplicateId1);
|
||||||
|
expect(body.results[0].status).toBe('SUCCESS');
|
||||||
|
|
||||||
|
// Second group should fail
|
||||||
|
expect(body.results[1].duplicateId).toBe(fakeId);
|
||||||
|
expect(body.results[1].status).toBe('FAILED');
|
||||||
|
expect(body.results[1].reason).toContain('not found or access denied');
|
||||||
|
|
||||||
|
// Verify first group was actually resolved despite second failure
|
||||||
|
const asset1Info = await utils.getAssetInfo(user1.accessToken, asset1.id);
|
||||||
|
expect(asset1Info.duplicateId).toBeNull();
|
||||||
|
const asset2Info = await utils.getAssetInfo(user1.accessToken, asset2.id);
|
||||||
|
expect(asset2Info.isTrashed).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trash assets when trash is enabled', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000028';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
// Ensure trash is enabled (default)
|
||||||
|
const config = await utils.getSystemConfig(admin.accessToken);
|
||||||
|
expect(config.trash.enabled).toBe(true);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('SUCCESS');
|
||||||
|
|
||||||
|
// Verify asset is trashed (not deleted)
|
||||||
|
const trashedAsset = await utils.getAssetInfo(user1.accessToken, asset2.id);
|
||||||
|
expect(trashedAsset.isTrashed).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete assets when trash is disabled', async () => {
|
||||||
|
const [asset1, asset2] = await Promise.all([
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
utils.createAsset(user1.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const duplicateId = '00000000-0000-4000-8000-000000000029';
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset1.id, duplicateId);
|
||||||
|
await utils.setAssetDuplicateId(user1.accessToken, asset2.id, duplicateId);
|
||||||
|
|
||||||
|
// Disable trash
|
||||||
|
await request(app)
|
||||||
|
.put('/system-config')
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send({
|
||||||
|
trash: { enabled: false, days: 30 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/duplicates/resolve')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({
|
||||||
|
groups: [{ duplicateId, keepAssetIds: [asset1.id], trashAssetIds: [asset2.id] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.results[0].status).toBe('SUCCESS');
|
||||||
|
|
||||||
|
// Asset should be marked as deleted (force delete)
|
||||||
|
const { status: getStatus } = await request(app)
|
||||||
|
.get(`/assets/${asset2.id}`)
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
|
||||||
|
// Asset should still be accessible (soft deleted) but marked as deleted
|
||||||
|
expect(getStatus).toBe(200);
|
||||||
|
|
||||||
|
// Re-enable trash for other tests
|
||||||
|
await utils.resetAdminConfig(admin.accessToken);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,6 +2,8 @@ export const uuidDto = {
|
|||||||
invalid: 'invalid-uuid',
|
invalid: 'invalid-uuid',
|
||||||
// valid uuid v4
|
// valid uuid v4
|
||||||
notFound: '00000000-0000-4000-a000-000000000000',
|
notFound: '00000000-0000-4000-a000-000000000000',
|
||||||
|
dummy: '00000000-0000-4000-a000-000000000001',
|
||||||
|
dummy2: '00000000-0000-4000-a000-000000000002',
|
||||||
};
|
};
|
||||||
|
|
||||||
const adminLoginDto = {
|
const adminLoginDto = {
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ describe('/admin/database-backups', () => {
|
|||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await utils.resetDatabase();
|
await utils.resetDatabase();
|
||||||
admin = await utils.adminSetup();
|
admin = await utils.adminSetup({
|
||||||
|
onboarding: false,
|
||||||
|
});
|
||||||
await utils.resetBackups(admin.accessToken);
|
await utils.resetBackups(admin.accessToken);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -94,7 +96,9 @@ describe('/admin/database-backups', () => {
|
|||||||
({ status, body }) => status === 200 && !body.maintenanceMode,
|
({ status, body }) => status === 200 && !body.maintenanceMode,
|
||||||
);
|
);
|
||||||
|
|
||||||
admin = await utils.adminSetup();
|
admin = await utils.adminSetup({
|
||||||
|
onboarding: false,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.sequential('should not work when the server is configured', async () => {
|
it.sequential('should not work when the server is configured', async () => {
|
||||||
|
|||||||
@@ -524,14 +524,19 @@ describe('/albums', () => {
|
|||||||
expect(body).toEqual(errorDto.badRequest('Not found or no album.update access'));
|
expect(body).toEqual(errorDto.badRequest('Not found or no album.update access'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not be able to update as an editor', async () => {
|
it('should be able to update as an editor', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.patch(`/albums/${user1Albums[0].id}`)
|
.patch(`/albums/${user1Albums[0].id}`)
|
||||||
.set('Authorization', `Bearer ${user2.accessToken}`)
|
.set('Authorization', `Bearer ${user2.accessToken}`)
|
||||||
.send({ albumName: 'New album name' });
|
.send({ albumName: 'New album name' });
|
||||||
|
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual(errorDto.badRequest('Not found or no album.update access'));
|
expect(body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: user1Albums[0].id,
|
||||||
|
albumName: 'New album name',
|
||||||
|
}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -380,4 +380,23 @@ describe(`/oauth`, () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('idTokenClaims', () => {
|
||||||
|
it('should use claims from the ID token if IDP includes them', async () => {
|
||||||
|
await setupOAuth(admin.accessToken, {
|
||||||
|
enabled: true,
|
||||||
|
clientId: OAuthClient.DEFAULT,
|
||||||
|
clientSecret: OAuthClient.DEFAULT,
|
||||||
|
});
|
||||||
|
const callbackParams = await loginWithOAuth(OAuthUser.ID_TOKEN_CLAIMS);
|
||||||
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
|
expect(status).toBe(201);
|
||||||
|
expect(body).toMatchObject({
|
||||||
|
accessToken: expect.any(String),
|
||||||
|
name: 'ID Token User',
|
||||||
|
userEmail: 'oauth-id-token-claims@immich.app',
|
||||||
|
userId: expect.any(String),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -438,6 +438,16 @@ describe('/shared-links', () => {
|
|||||||
expect(body).toEqual(errorDto.badRequest('Invalid shared link type'));
|
expect(body).toEqual(errorDto.badRequest('Invalid shared link type'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should reject guests removing assets from an individual shared link', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.delete(`/shared-links/${linkWithAssets.id}/assets`)
|
||||||
|
.query({ key: linkWithAssets.key })
|
||||||
|
.send({ assetIds: [asset1.id] });
|
||||||
|
|
||||||
|
expect(status).toBe(403);
|
||||||
|
expect(body).toEqual(errorDto.forbidden);
|
||||||
|
});
|
||||||
|
|
||||||
it('should remove assets from a shared link (individual)', async () => {
|
it('should remove assets from a shared link (individual)', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/shared-links/${linkWithAssets.id}/assets`)
|
.delete(`/shared-links/${linkWithAssets.id}/assets`)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { LoginResponseDto } from '@immich/sdk';
|
import { LoginResponseDto } from '@immich/sdk';
|
||||||
import { test } from '@playwright/test';
|
import { expect, test } from '@playwright/test';
|
||||||
import { utils } from 'src/utils';
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { testAssetDir, utils } from 'src/utils';
|
||||||
|
|
||||||
test.describe('Album', () => {
|
test.describe('Album', () => {
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
@@ -22,4 +23,41 @@ test.describe('Album', () => {
|
|||||||
await page.reload();
|
await page.reload();
|
||||||
await page.getByRole('button', { name: 'Select photos' }).waitFor();
|
await page.getByRole('button', { name: 'Select photos' }).waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should keep map view open after viewing an asset from the map and going back', async ({ context, page }) => {
|
||||||
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
|
||||||
|
const imagePath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
||||||
|
const mapAsset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
bytes: readFileSync(imagePath),
|
||||||
|
filename: 'thompson-springs.jpg',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||||
|
|
||||||
|
const mapAlbum = await utils.createAlbum(admin.accessToken, {
|
||||||
|
albumName: 'Map Test Album',
|
||||||
|
assetIds: [mapAsset.id],
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.goto(`/albums/${mapAlbum.id}`);
|
||||||
|
const mapButton = page.getByRole('button', { name: 'Map' });
|
||||||
|
await expect(mapButton).toBeVisible();
|
||||||
|
await mapButton.click();
|
||||||
|
|
||||||
|
const mapModal = page.getByRole('dialog');
|
||||||
|
await expect(mapModal).toBeVisible();
|
||||||
|
|
||||||
|
const mapMarker = mapModal.getByRole('img', { name: /Map marker/i }).first();
|
||||||
|
await expect(mapMarker).toBeVisible();
|
||||||
|
await mapMarker.click();
|
||||||
|
|
||||||
|
await page.waitForSelector('#immich-asset-viewer');
|
||||||
|
await page.getByRole('button', { name: 'Go back' }).click();
|
||||||
|
|
||||||
|
await expect(page.locator('#immich-asset-viewer')).not.toBeVisible();
|
||||||
|
await expect(mapModal).toBeVisible();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
|
|
||||||
import { expect, Page, test } from '@playwright/test';
|
|
||||||
import { utils } from 'src/utils';
|
|
||||||
|
|
||||||
async function ensureDetailPanelVisible(page: Page) {
|
|
||||||
await page.waitForSelector('#immich-asset-viewer');
|
|
||||||
|
|
||||||
const isVisible = await page.locator('#detail-panel').isVisible();
|
|
||||||
if (!isVisible) {
|
|
||||||
await page.keyboard.press('i');
|
|
||||||
await page.waitForSelector('#detail-panel');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test.describe('Asset Viewer stack', () => {
|
|
||||||
let admin: LoginResponseDto;
|
|
||||||
let assetOne: AssetMediaResponseDto;
|
|
||||||
let assetTwo: AssetMediaResponseDto;
|
|
||||||
|
|
||||||
test.beforeAll(async () => {
|
|
||||||
utils.initSdk();
|
|
||||||
await utils.resetDatabase();
|
|
||||||
admin = await utils.adminSetup();
|
|
||||||
await utils.updateMyPreferences(admin.accessToken, { tags: { enabled: true } });
|
|
||||||
|
|
||||||
assetOne = await utils.createAsset(admin.accessToken);
|
|
||||||
assetTwo = await utils.createAsset(admin.accessToken);
|
|
||||||
await utils.createStack(admin.accessToken, [assetOne.id, assetTwo.id]);
|
|
||||||
|
|
||||||
const tags = await utils.upsertTags(admin.accessToken, ['test/1', 'test/2']);
|
|
||||||
const tagOne = tags.find((tag) => tag.value === 'test/1')!;
|
|
||||||
const tagTwo = tags.find((tag) => tag.value === 'test/2')!;
|
|
||||||
await utils.tagAssets(admin.accessToken, tagOne.id, [assetOne.id]);
|
|
||||||
await utils.tagAssets(admin.accessToken, tagTwo.id, [assetTwo.id]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('stack slideshow is visible', async ({ page, context }) => {
|
|
||||||
await utils.setAuthCookies(context, admin.accessToken);
|
|
||||||
await page.goto(`/photos/${assetOne.id}`);
|
|
||||||
|
|
||||||
const stackAssets = page.locator('#stack-slideshow [data-asset]');
|
|
||||||
await expect(stackAssets.first()).toBeVisible();
|
|
||||||
await expect(stackAssets.nth(1)).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('tags of primary asset are visible', async ({ page, context }) => {
|
|
||||||
await utils.setAuthCookies(context, admin.accessToken);
|
|
||||||
await page.goto(`/photos/${assetOne.id}`);
|
|
||||||
await ensureDetailPanelVisible(page);
|
|
||||||
|
|
||||||
const tags = page.getByTestId('detail-panel-tags').getByRole('link');
|
|
||||||
await expect(tags.first()).toHaveText('test/1');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('tags of second asset are visible', async ({ page, context }) => {
|
|
||||||
await utils.setAuthCookies(context, admin.accessToken);
|
|
||||||
await page.goto(`/photos/${assetOne.id}`);
|
|
||||||
await ensureDetailPanelVisible(page);
|
|
||||||
|
|
||||||
const stackAssets = page.locator('#stack-slideshow [data-asset]');
|
|
||||||
await stackAssets.nth(1).click();
|
|
||||||
|
|
||||||
const tags = page.getByTestId('detail-panel-tags').getByRole('link');
|
|
||||||
await expect(tags.first()).toHaveText('test/2');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { AssetMediaResponseDto, LoginResponseDto, updateAssets } from '@immich/sdk';
|
||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
import crypto from 'node:crypto';
|
||||||
|
import { asBearerAuth, utils } from 'src/utils';
|
||||||
|
|
||||||
|
test.describe('Duplicates Utility', () => {
|
||||||
|
let admin: LoginResponseDto;
|
||||||
|
let firstAsset: AssetMediaResponseDto;
|
||||||
|
let secondAsset: AssetMediaResponseDto;
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
utils.initSdk();
|
||||||
|
await utils.resetDatabase();
|
||||||
|
admin = await utils.adminSetup();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
[firstAsset, secondAsset] = await Promise.all([
|
||||||
|
utils.createAsset(admin.accessToken, { deviceAssetId: 'duplicate-a' }),
|
||||||
|
utils.createAsset(admin.accessToken, { deviceAssetId: 'duplicate-b' }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await updateAssets(
|
||||||
|
{
|
||||||
|
assetBulkUpdateDto: {
|
||||||
|
ids: [firstAsset.id, secondAsset.id],
|
||||||
|
duplicateId: crypto.randomUUID(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ headers: asBearerAuth(admin.accessToken) },
|
||||||
|
);
|
||||||
|
|
||||||
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('navigates with arrow keys between duplicate preview assets', async ({ page }) => {
|
||||||
|
await page.goto('/utilities/duplicates');
|
||||||
|
await page.getByRole('button', { name: 'View' }).first().click();
|
||||||
|
await page.waitForSelector('#immich-asset-viewer');
|
||||||
|
|
||||||
|
const getViewedAssetId = () => new URL(page.url()).pathname.split('/').at(-1) ?? '';
|
||||||
|
const initialAssetId = getViewedAssetId();
|
||||||
|
expect([firstAsset.id, secondAsset.id]).toContain(initialAssetId);
|
||||||
|
|
||||||
|
await page.keyboard.press('ArrowRight');
|
||||||
|
await expect.poll(getViewedAssetId).not.toBe(initialAssetId);
|
||||||
|
|
||||||
|
await page.keyboard.press('ArrowLeft');
|
||||||
|
await expect.poll(getViewedAssetId).toBe(initialAssetId);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
|
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
|
||||||
import { Page, expect, test } from '@playwright/test';
|
import { expect, test } from '@playwright/test';
|
||||||
|
import type { Socket } from 'socket.io-client';
|
||||||
import { utils } from 'src/utils';
|
import { utils } from 'src/utils';
|
||||||
|
|
||||||
function imageLocator(page: Page) {
|
|
||||||
return page.getByAltText('Image taken').locator('visible=true');
|
|
||||||
}
|
|
||||||
test.describe('Photo Viewer', () => {
|
test.describe('Photo Viewer', () => {
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
let asset: AssetMediaResponseDto;
|
let asset: AssetMediaResponseDto;
|
||||||
let rawAsset: AssetMediaResponseDto;
|
let rawAsset: AssetMediaResponseDto;
|
||||||
|
let websocket: Socket;
|
||||||
|
|
||||||
test.beforeAll(async () => {
|
test.beforeAll(async () => {
|
||||||
utils.initSdk();
|
utils.initSdk();
|
||||||
@@ -16,6 +15,11 @@ test.describe('Photo Viewer', () => {
|
|||||||
admin = await utils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
asset = await utils.createAsset(admin.accessToken);
|
asset = await utils.createAsset(admin.accessToken);
|
||||||
rawAsset = await utils.createAsset(admin.accessToken, { assetData: { filename: 'test.arw' } });
|
rawAsset = await utils.createAsset(admin.accessToken, { assetData: { filename: 'test.arw' } });
|
||||||
|
websocket = await utils.connectWebsocket(admin.accessToken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterAll(() => {
|
||||||
|
utils.disconnectWebsocket(websocket);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }) => {
|
test.beforeEach(async ({ context, page }) => {
|
||||||
@@ -26,31 +30,65 @@ test.describe('Photo Viewer', () => {
|
|||||||
|
|
||||||
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');
|
|
||||||
const box = await imageLocator(page).boundingBox();
|
const preview = page.getByTestId('preview').filter({ visible: true });
|
||||||
expect(box).toBeTruthy();
|
await expect(preview).toHaveAttribute('src', /.+/);
|
||||||
const { x, y, width, height } = box!;
|
|
||||||
await page.mouse.move(x + width / 2, y + height / 2);
|
const originalResponse = page.waitForResponse((response) => response.url().includes('/original'));
|
||||||
|
|
||||||
|
const { width, height } = page.viewportSize()!;
|
||||||
|
await page.mouse.move(width / 2, height / 2);
|
||||||
await page.mouse.wheel(0, -1);
|
await page.mouse.wheel(0, -1);
|
||||||
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('original');
|
|
||||||
|
await originalResponse;
|
||||||
|
|
||||||
|
const original = page.getByTestId('original').filter({ visible: true });
|
||||||
|
await expect(original).toHaveAttribute('src', /original/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('loads fullsize image when zoomed and original is web-incompatible', async ({ page }) => {
|
test('loads fullsize image when zoomed and original is web-incompatible', async ({ page }) => {
|
||||||
await page.goto(`/photos/${rawAsset.id}`);
|
await page.goto(`/photos/${rawAsset.id}`);
|
||||||
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail');
|
|
||||||
const box = await imageLocator(page).boundingBox();
|
const preview = page.getByTestId('preview').filter({ visible: true });
|
||||||
expect(box).toBeTruthy();
|
await expect(preview).toHaveAttribute('src', /.+/);
|
||||||
const { x, y, width, height } = box!;
|
|
||||||
await page.mouse.move(x + width / 2, y + height / 2);
|
const fullsizeResponse = page.waitForResponse((response) => response.url().includes('fullsize'));
|
||||||
|
|
||||||
|
const { width, height } = page.viewportSize()!;
|
||||||
|
await page.mouse.move(width / 2, height / 2);
|
||||||
await page.mouse.wheel(0, -1);
|
await page.mouse.wheel(0, -1);
|
||||||
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('fullsize');
|
|
||||||
|
await fullsizeResponse;
|
||||||
|
|
||||||
|
const original = page.getByTestId('original').filter({ visible: true });
|
||||||
|
await expect(original).toHaveAttribute('src', /fullsize/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('right-click targets the img element', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${asset.id}`);
|
||||||
|
|
||||||
|
const preview = page.getByTestId('preview').filter({ visible: true });
|
||||||
|
await expect(preview).toHaveAttribute('src', /.+/);
|
||||||
|
|
||||||
|
const box = await preview.boundingBox();
|
||||||
|
const tagAtCenter = await page.evaluate(({ x, y }) => document.elementFromPoint(x, y)?.tagName, {
|
||||||
|
x: box!.x + box!.width / 2,
|
||||||
|
y: box!.y + box!.height / 2,
|
||||||
|
});
|
||||||
|
expect(tagAtCenter).toBe('IMG');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('reloads photo when checksum changes', async ({ page }) => {
|
test('reloads photo when checksum changes', 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');
|
|
||||||
const initialSrc = await imageLocator(page).getAttribute('src');
|
const preview = page.getByTestId('preview').filter({ visible: true });
|
||||||
|
await expect(preview).toHaveAttribute('src', /.+/);
|
||||||
|
const initialSrc = await preview.getAttribute('src');
|
||||||
|
|
||||||
|
const websocketEvent = utils.waitForWebsocketEvent({ event: 'assetUpdate', id: asset.id });
|
||||||
await utils.replaceAsset(admin.accessToken, asset.id);
|
await utils.replaceAsset(admin.accessToken, asset.id);
|
||||||
await expect.poll(async () => await imageLocator(page).getAttribute('src')).not.toBe(initialSrc);
|
await websocketEvent;
|
||||||
|
|
||||||
|
await expect(preview).not.toHaveAttribute('src', initialSrc!);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,15 +12,18 @@ import { asBearerAuth, utils } from 'src/utils';
|
|||||||
test.describe('Shared Links', () => {
|
test.describe('Shared Links', () => {
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
let asset: AssetMediaResponseDto;
|
let asset: AssetMediaResponseDto;
|
||||||
|
let asset2: AssetMediaResponseDto;
|
||||||
let album: AlbumResponseDto;
|
let album: AlbumResponseDto;
|
||||||
let sharedLink: SharedLinkResponseDto;
|
let sharedLink: SharedLinkResponseDto;
|
||||||
let sharedLinkPassword: SharedLinkResponseDto;
|
let sharedLinkPassword: SharedLinkResponseDto;
|
||||||
|
let individualSharedLink: SharedLinkResponseDto;
|
||||||
|
|
||||||
test.beforeAll(async () => {
|
test.beforeAll(async () => {
|
||||||
utils.initSdk();
|
utils.initSdk();
|
||||||
await utils.resetDatabase();
|
await utils.resetDatabase();
|
||||||
admin = await utils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
asset = await utils.createAsset(admin.accessToken);
|
asset = await utils.createAsset(admin.accessToken);
|
||||||
|
asset2 = await utils.createAsset(admin.accessToken);
|
||||||
album = await createAlbum(
|
album = await createAlbum(
|
||||||
{
|
{
|
||||||
createAlbumDto: {
|
createAlbumDto: {
|
||||||
@@ -39,6 +42,10 @@ test.describe('Shared Links', () => {
|
|||||||
albumId: album.id,
|
albumId: album.id,
|
||||||
password: 'test-password',
|
password: 'test-password',
|
||||||
});
|
});
|
||||||
|
individualSharedLink = await utils.createSharedLink(admin.accessToken, {
|
||||||
|
type: SharedLinkType.Individual,
|
||||||
|
assetIds: [asset.id, asset2.id],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('download from a shared link', async ({ page }) => {
|
test('download from a shared link', async ({ page }) => {
|
||||||
@@ -109,4 +116,21 @@ test.describe('Shared Links', () => {
|
|||||||
await page.waitForURL('/photos');
|
await page.waitForURL('/photos');
|
||||||
await page.locator(`[data-asset-id="${asset.id}"]`).waitFor();
|
await page.locator(`[data-asset-id="${asset.id}"]`).waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('owner can remove assets from an individual shared link', async ({ context, page }) => {
|
||||||
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
|
||||||
|
await page.goto(`/share/${individualSharedLink.key}`);
|
||||||
|
await page.locator(`[data-asset="${asset.id}"]`).waitFor();
|
||||||
|
await expect(page.locator(`[data-asset]`)).toHaveCount(2);
|
||||||
|
|
||||||
|
await page.locator(`[data-asset="${asset.id}"]`).hover();
|
||||||
|
await page.locator(`[data-asset="${asset.id}"] [role="checkbox"]`).click();
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Remove from shared link' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Remove', exact: true }).click();
|
||||||
|
|
||||||
|
await expect(page.locator(`[data-asset="${asset.id}"]`)).toHaveCount(0);
|
||||||
|
await expect(page.locator(`[data-asset="${asset2.id}"]`)).toHaveCount(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ export const setupBaseMockApiRoutes = async (context: BrowserContext, adminUserI
|
|||||||
'.mpeg',
|
'.mpeg',
|
||||||
'.mpg',
|
'.mpg',
|
||||||
'.mts',
|
'.mts',
|
||||||
|
'.ts',
|
||||||
'.vob',
|
'.vob',
|
||||||
'.webm',
|
'.webm',
|
||||||
'.wmv',
|
'.wmv',
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
import { AssetTypeEnum, AssetVisibility, type AssetResponseDto, type StackResponseDto } from '@immich/sdk';
|
||||||
|
import { BrowserContext } from '@playwright/test';
|
||||||
|
import { randomPreview, randomThumbnail } from 'src/ui/generators/timeline';
|
||||||
|
|
||||||
|
export type MockStack = {
|
||||||
|
id: string;
|
||||||
|
primaryAssetId: string;
|
||||||
|
assets: AssetResponseDto[];
|
||||||
|
brokenAssetIds: Set<string>;
|
||||||
|
assetMap: Map<string, AssetResponseDto>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createMockStackAsset = (ownerId: string): AssetResponseDto => {
|
||||||
|
const assetId = faker.string.uuid();
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
return {
|
||||||
|
id: assetId,
|
||||||
|
deviceAssetId: `device-${assetId}`,
|
||||||
|
ownerId,
|
||||||
|
owner: {
|
||||||
|
id: ownerId,
|
||||||
|
email: 'admin@immich.cloud',
|
||||||
|
name: 'Admin',
|
||||||
|
profileImagePath: '',
|
||||||
|
profileChangedAt: now,
|
||||||
|
avatarColor: 'blue' as never,
|
||||||
|
},
|
||||||
|
libraryId: `library-${ownerId}`,
|
||||||
|
deviceId: `device-${ownerId}`,
|
||||||
|
type: AssetTypeEnum.Image,
|
||||||
|
originalPath: `/original/${assetId}.jpg`,
|
||||||
|
originalFileName: `${assetId}.jpg`,
|
||||||
|
originalMimeType: 'image/jpeg',
|
||||||
|
thumbhash: null,
|
||||||
|
fileCreatedAt: now,
|
||||||
|
fileModifiedAt: now,
|
||||||
|
localDateTime: now,
|
||||||
|
updatedAt: now,
|
||||||
|
createdAt: now,
|
||||||
|
isFavorite: false,
|
||||||
|
isArchived: false,
|
||||||
|
isTrashed: false,
|
||||||
|
visibility: AssetVisibility.Timeline,
|
||||||
|
duration: '0:00:00.00000',
|
||||||
|
exifInfo: {
|
||||||
|
make: null,
|
||||||
|
model: null,
|
||||||
|
exifImageWidth: 3000,
|
||||||
|
exifImageHeight: 4000,
|
||||||
|
fileSizeInByte: null,
|
||||||
|
orientation: null,
|
||||||
|
dateTimeOriginal: now,
|
||||||
|
modifyDate: null,
|
||||||
|
timeZone: null,
|
||||||
|
lensModel: null,
|
||||||
|
fNumber: null,
|
||||||
|
focalLength: null,
|
||||||
|
iso: null,
|
||||||
|
exposureTime: null,
|
||||||
|
latitude: null,
|
||||||
|
longitude: null,
|
||||||
|
city: null,
|
||||||
|
country: null,
|
||||||
|
state: null,
|
||||||
|
description: null,
|
||||||
|
},
|
||||||
|
livePhotoVideoId: null,
|
||||||
|
tags: [],
|
||||||
|
people: [],
|
||||||
|
unassignedFaces: [],
|
||||||
|
stack: null,
|
||||||
|
isOffline: false,
|
||||||
|
hasMetadata: true,
|
||||||
|
duplicateId: null,
|
||||||
|
resized: true,
|
||||||
|
checksum: faker.string.alphanumeric({ length: 28 }),
|
||||||
|
width: 3000,
|
||||||
|
height: 4000,
|
||||||
|
isEdited: false,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createMockStack = (
|
||||||
|
primaryAssetDto: AssetResponseDto,
|
||||||
|
additionalAssets: AssetResponseDto[],
|
||||||
|
brokenAssetIds?: Set<string>,
|
||||||
|
): MockStack => {
|
||||||
|
const stackId = faker.string.uuid();
|
||||||
|
const allAssets = [primaryAssetDto, ...additionalAssets];
|
||||||
|
const resolvedBrokenIds = brokenAssetIds ?? new Set(additionalAssets.map((a) => a.id));
|
||||||
|
const assetMap = new Map(allAssets.map((a) => [a.id, a]));
|
||||||
|
|
||||||
|
primaryAssetDto.stack = {
|
||||||
|
id: stackId,
|
||||||
|
assetCount: allAssets.length,
|
||||||
|
primaryAssetId: primaryAssetDto.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: stackId,
|
||||||
|
primaryAssetId: primaryAssetDto.id,
|
||||||
|
assets: allAssets,
|
||||||
|
brokenAssetIds: resolvedBrokenIds,
|
||||||
|
assetMap,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setupBrokenAssetMockApiRoutes = async (context: BrowserContext, mockStack: MockStack) => {
|
||||||
|
await context.route('**/api/stacks/*', async (route, request) => {
|
||||||
|
if (request.method() !== 'GET') {
|
||||||
|
return route.fallback();
|
||||||
|
}
|
||||||
|
const stackResponse: StackResponseDto = {
|
||||||
|
id: mockStack.id,
|
||||||
|
primaryAssetId: mockStack.primaryAssetId,
|
||||||
|
assets: mockStack.assets,
|
||||||
|
};
|
||||||
|
return route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
json: stackResponse,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await context.route('**/api/assets/*', async (route, request) => {
|
||||||
|
if (request.method() !== 'GET') {
|
||||||
|
return route.fallback();
|
||||||
|
}
|
||||||
|
const url = new URL(request.url());
|
||||||
|
const segments = url.pathname.split('/');
|
||||||
|
const assetId = segments.at(-1);
|
||||||
|
if (assetId && mockStack.assetMap.has(assetId)) {
|
||||||
|
return route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
json: mockStack.assetMap.get(assetId),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return route.fallback();
|
||||||
|
});
|
||||||
|
|
||||||
|
await context.route('**/api/assets/*/thumbnail?size=*', async (route, request) => {
|
||||||
|
if (!route.request().serviceWorker()) {
|
||||||
|
return route.continue();
|
||||||
|
}
|
||||||
|
const pattern = /\/api\/assets\/(?<assetId>[^/]+)\/thumbnail\?size=(?<size>preview|thumbnail)/;
|
||||||
|
const match = request.url().match(pattern);
|
||||||
|
if (!match?.groups || !mockStack.assetMap.has(match.groups.assetId)) {
|
||||||
|
return route.fallback();
|
||||||
|
}
|
||||||
|
if (mockStack.brokenAssetIds.has(match.groups.assetId)) {
|
||||||
|
return route.fulfill({ status: 404 });
|
||||||
|
}
|
||||||
|
const asset = mockStack.assetMap.get(match.groups.assetId)!;
|
||||||
|
const ratio = (asset.exifInfo?.exifImageWidth ?? 3000) / (asset.exifInfo?.exifImageHeight ?? 4000);
|
||||||
|
const body =
|
||||||
|
match.groups.size === 'preview'
|
||||||
|
? await randomPreview(match.groups.assetId, ratio)
|
||||||
|
: await randomThumbnail(match.groups.assetId, ratio);
|
||||||
|
return route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
headers: { 'content-type': 'image/jpeg' },
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
import { BrowserContext } from '@playwright/test';
|
||||||
|
import { randomThumbnail } from 'src/ui/generators/timeline';
|
||||||
|
|
||||||
|
// Minimal valid H.264 MP4 (8x8px, 1 frame) that browsers can decode to get videoWidth/videoHeight
|
||||||
|
const MINIMAL_MP4_BASE64 =
|
||||||
|
'AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAAAr9tZGF0AAACoAYF//+c' +
|
||||||
|
'3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDEyNSAtIEguMjY0L01QRUctNCBBVkMgY29kZWMg' +
|
||||||
|
'LSBDb3B5bGVmdCAyMDAzLTIwMTIgLSBodHRwOi8vd3d3LnZpZGVvbGFuLm9yZy94MjY0Lmh0bWwg' +
|
||||||
|
'LSBvcHRpb25zOiBjYWJhYz0xIHJlZj0zIGRlYmxvY2s9MTowOjAgYW5hbHlzZT0weDM6MHgxMTMg' +
|
||||||
|
'bWU9aGV4IHN1Ym1lPTcgcHN5PTEgcHN5X3JkPTEuMDA6MC4wMCBtaXhlZF9yZWY9MSBtZV9yYW5n' +
|
||||||
|
'ZT0xNiBjaHJvbWFfbWU9MSB0cmVsbGlzPTEgOHg4ZGN0PTEgY3FtPTAgZGVhZHpvbmU9MjEsMTEg' +
|
||||||
|
'ZmFzdF9wc2tpcD0xIGNocm9tYV9xcF9vZmZzZXQ9LTIgdGhyZWFkcz02IGxvb2thaGVhZF90aHJl' +
|
||||||
|
'YWRzPTEgc2xpY2VkX3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNlZD0wIGJsdXJh' +
|
||||||
|
'eV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0wIGJmcmFtZXM9MyBiX3B5cmFtaWQ9MiBiX2Fk' +
|
||||||
|
'YXB0PTEgYl9iaWFzPTAgZGlyZWN0PTEgd2VpZ2h0Yj0xIG9wZW5fZ29wPTAgd2VpZ2h0cD0yIGtl' +
|
||||||
|
'eWludD0yNTAga2V5aW50X21pbj0yNCBzY2VuZWN1dD00MCBpbnRyYV9yZWZyZXNoPTAgcmNfbG9v' +
|
||||||
|
'a2FoZWFkPTQwIHJjPWNyZiBtYnRyZWU9MSBjcmY9MjMuMCBxY29tcD0wLjYwIHFwbWluPTAgcXBt' +
|
||||||
|
'YXg9NjkgcXBzdGVwPTQgaXBfcmF0aW89MS40MCBhcT0xOjEuMDAAgAAAAA9liIQAV/0TAAYdeBTX' +
|
||||||
|
'zg8AAALvbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAACoAAQAAAQAAAAAAAAAAAAAAAAEAAAAA' +
|
||||||
|
'AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA' +
|
||||||
|
'Ahl0cmFrAAAAXHRraGQAAAAPAAAAAAAAAAAAAAABAAAAAAAAACoAAAAAAAAAAAAAAAAAAAAAAAEAAAAA' +
|
||||||
|
'AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAgAAAAIAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAA' +
|
||||||
|
'AAEAAAAqAAAAAAABAAAAAAGRbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAAwAAAAAgBVxAAAAAAA' +
|
||||||
|
'LWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAABPG1pbmYAAAAUdm1oZAAA' +
|
||||||
|
'AAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAPxzdGJsAAAAmHN0' +
|
||||||
|
'c2QAAAAAAAAAAQAAAIhhdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAgACABIAAAASAAAAAAAAAAB' +
|
||||||
|
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGP//AAAAMmF2Y0MBZAAK/+EAGWdkAAqs' +
|
||||||
|
'2V+WXAWyAAADAAIAAAMAYB4kSywBAAZo6+PLIsAAAAAYc3R0cwAAAAAAAAABAAAAAQAAAgAAAAAcc3Rz' +
|
||||||
|
'YwAAAAAAAAABAAAAAQAAAAEAAAABAAAAFHN0c3oAAAAAAAACtwAAAAEAAAAUc3RjbwAAAAAAAAABAAAA' +
|
||||||
|
'MAAAAGJ1ZHRhAAAAWm1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAALWls' +
|
||||||
|
'c3QAAAAlqXRvbwAAAB1kYXRhAAAAAQAAAABMYXZmNTQuNjMuMTA0';
|
||||||
|
|
||||||
|
export const MINIMAL_MP4_BUFFER = Buffer.from(MINIMAL_MP4_BASE64, 'base64');
|
||||||
|
|
||||||
|
export type MockPerson = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
birthDate: string | null;
|
||||||
|
isHidden: boolean;
|
||||||
|
thumbnailPath: string;
|
||||||
|
updatedAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createMockPeople = (count: number): MockPerson[] => {
|
||||||
|
const names = [
|
||||||
|
'Alice Johnson',
|
||||||
|
'Bob Smith',
|
||||||
|
'Charlie Brown',
|
||||||
|
'Diana Prince',
|
||||||
|
'Eve Adams',
|
||||||
|
'Frank Castle',
|
||||||
|
'Grace Lee',
|
||||||
|
'Hank Pym',
|
||||||
|
'Iris West',
|
||||||
|
'Jack Ryan',
|
||||||
|
];
|
||||||
|
return Array.from({ length: count }, (_, index) => ({
|
||||||
|
id: `person-${index}`,
|
||||||
|
name: names[index % names.length],
|
||||||
|
birthDate: null,
|
||||||
|
isHidden: false,
|
||||||
|
thumbnailPath: `/upload/thumbs/person-${index}.jpeg`,
|
||||||
|
updatedAt: '2025-01-01T00:00:00.000Z',
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FaceCreateCapture = {
|
||||||
|
requests: Array<{
|
||||||
|
assetId: string;
|
||||||
|
personId: string;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
imageWidth: number;
|
||||||
|
imageHeight: number;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setupFaceEditorMockApiRoutes = async (
|
||||||
|
context: BrowserContext,
|
||||||
|
mockPeople: MockPerson[],
|
||||||
|
faceCreateCapture: FaceCreateCapture,
|
||||||
|
) => {
|
||||||
|
await context.route('**/api/people?*', async (route, request) => {
|
||||||
|
if (request.method() !== 'GET') {
|
||||||
|
return route.fallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
json: {
|
||||||
|
hasNextPage: false,
|
||||||
|
hidden: 0,
|
||||||
|
people: mockPeople,
|
||||||
|
total: mockPeople.length,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await context.route('**/api/faces', async (route, request) => {
|
||||||
|
if (request.method() !== 'POST') {
|
||||||
|
return route.fallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = request.postDataJSON();
|
||||||
|
faceCreateCapture.requests.push(body);
|
||||||
|
|
||||||
|
return route.fulfill({
|
||||||
|
status: 201,
|
||||||
|
contentType: 'text/plain',
|
||||||
|
body: 'OK',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await context.route('**/api/people/*/thumbnail', async (route) => {
|
||||||
|
if (!route.request().serviceWorker()) {
|
||||||
|
return route.continue();
|
||||||
|
}
|
||||||
|
return route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
headers: { 'content-type': 'image/jpeg' },
|
||||||
|
body: await randomThumbnail('person-thumb', 1),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
import type { AssetOcrResponseDto } from '@immich/sdk';
|
||||||
|
import { BrowserContext } from '@playwright/test';
|
||||||
|
|
||||||
|
export type MockOcrBox = {
|
||||||
|
text: string;
|
||||||
|
x1: number;
|
||||||
|
y1: number;
|
||||||
|
x2: number;
|
||||||
|
y2: number;
|
||||||
|
x3: number;
|
||||||
|
y3: number;
|
||||||
|
x4: number;
|
||||||
|
y4: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createMockOcrData = (assetId: string, boxes: MockOcrBox[]): AssetOcrResponseDto[] => {
|
||||||
|
return boxes.map((box) => ({
|
||||||
|
id: faker.string.uuid(),
|
||||||
|
assetId,
|
||||||
|
x1: box.x1,
|
||||||
|
y1: box.y1,
|
||||||
|
x2: box.x2,
|
||||||
|
y2: box.y2,
|
||||||
|
x3: box.x3,
|
||||||
|
y3: box.y3,
|
||||||
|
x4: box.x4,
|
||||||
|
y4: box.y4,
|
||||||
|
boxScore: 0.95,
|
||||||
|
textScore: 0.9,
|
||||||
|
text: box.text,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setupOcrMockApiRoutes = async (
|
||||||
|
context: BrowserContext,
|
||||||
|
ocrDataByAssetId: Map<string, AssetOcrResponseDto[]>,
|
||||||
|
) => {
|
||||||
|
await context.route('**/assets/*/ocr', async (route, request) => {
|
||||||
|
if (request.method() !== 'GET') {
|
||||||
|
return route.fallback();
|
||||||
|
}
|
||||||
|
const url = new URL(request.url());
|
||||||
|
const segments = url.pathname.split('/');
|
||||||
|
const assetIdIndex = segments.indexOf('assets') + 1;
|
||||||
|
const assetId = segments[assetIdIndex];
|
||||||
|
|
||||||
|
const ocrData = ocrDataByAssetId.get(assetId) ?? [];
|
||||||
|
return route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
json: ocrData,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
TimelineData,
|
TimelineData,
|
||||||
} from 'src/ui/generators/timeline';
|
} from 'src/ui/generators/timeline';
|
||||||
import { sleep } from 'src/ui/specs/timeline/utils';
|
import { sleep } from 'src/ui/specs/timeline/utils';
|
||||||
|
import { MINIMAL_MP4_BUFFER } from './face-editor-network';
|
||||||
|
|
||||||
export class TimelineTestContext {
|
export class TimelineTestContext {
|
||||||
slowBucket = false;
|
slowBucket = false;
|
||||||
@@ -135,6 +136,14 @@ export const setupTimelineMockApiRoutes = async (
|
|||||||
return route.continue();
|
return route.continue();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await context.route('**/api/assets/*/video/playback*', async (route) => {
|
||||||
|
return route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
headers: { 'content-type': 'video/mp4' },
|
||||||
|
body: MINIMAL_MP4_BUFFER,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
await context.route('**/api/albums/**', async (route, request) => {
|
await context.route('**/api/albums/**', async (route, request) => {
|
||||||
const albumsMatch = request.url().match(/\/api\/albums\/(?<albumId>[^/?]+)/);
|
const albumsMatch = request.url().match(/\/api\/albums\/(?<albumId>[^/?]+)/);
|
||||||
if (albumsMatch) {
|
if (albumsMatch) {
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
import { toAssetResponseDto } from 'src/ui/generators/timeline';
|
||||||
|
import {
|
||||||
|
createMockStack,
|
||||||
|
createMockStackAsset,
|
||||||
|
MockStack,
|
||||||
|
setupBrokenAssetMockApiRoutes,
|
||||||
|
} from 'src/ui/mock-network/broken-asset-network';
|
||||||
|
import { assetViewerUtils } from '../timeline/utils';
|
||||||
|
import { setupAssetViewerFixture } from './utils';
|
||||||
|
|
||||||
|
test.describe.configure({ mode: 'parallel' });
|
||||||
|
test.describe('broken-asset responsiveness', () => {
|
||||||
|
const fixture = setupAssetViewerFixture(889);
|
||||||
|
let mockStack: MockStack;
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
const primaryAssetDto = toAssetResponseDto(fixture.primaryAsset);
|
||||||
|
|
||||||
|
const brokenAssets = [
|
||||||
|
createMockStackAsset(fixture.adminUserId),
|
||||||
|
createMockStackAsset(fixture.adminUserId),
|
||||||
|
createMockStackAsset(fixture.adminUserId),
|
||||||
|
];
|
||||||
|
|
||||||
|
mockStack = createMockStack(primaryAssetDto, brokenAssets);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
await setupBrokenAssetMockApiRoutes(context, mockStack);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('broken asset in stack strip hides icon at small size', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, fixture.primaryAsset);
|
||||||
|
|
||||||
|
const stackSlideshow = page.locator('#stack-slideshow');
|
||||||
|
await expect(stackSlideshow).toBeVisible();
|
||||||
|
|
||||||
|
const brokenAssets = stackSlideshow.locator('[data-broken-asset]');
|
||||||
|
await expect(brokenAssets.first()).toBeVisible();
|
||||||
|
await expect(brokenAssets).toHaveCount(mockStack.brokenAssetIds.size);
|
||||||
|
|
||||||
|
for (const brokenAsset of await brokenAssets.all()) {
|
||||||
|
await expect(brokenAsset.locator('svg')).not.toBeVisible();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('broken asset in stack strip uses text-xs class', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, fixture.primaryAsset);
|
||||||
|
|
||||||
|
const stackSlideshow = page.locator('#stack-slideshow');
|
||||||
|
await expect(stackSlideshow).toBeVisible();
|
||||||
|
|
||||||
|
const brokenAssets = stackSlideshow.locator('[data-broken-asset]');
|
||||||
|
await expect(brokenAssets.first()).toBeVisible();
|
||||||
|
|
||||||
|
for (const brokenAsset of await brokenAssets.all()) {
|
||||||
|
const messageSpan = brokenAsset.locator('span');
|
||||||
|
await expect(messageSpan).toHaveClass(/text-xs/);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('broken asset in main viewer shows icon and uses text-base', async ({ context, page }) => {
|
||||||
|
await context.route(
|
||||||
|
(url) =>
|
||||||
|
url.pathname.includes(`/api/assets/${fixture.primaryAsset.id}/thumbnail`) ||
|
||||||
|
url.pathname.includes(`/api/assets/${fixture.primaryAsset.id}/original`),
|
||||||
|
async (route) => {
|
||||||
|
return route.fulfill({ status: 404 });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await page.waitForSelector('#immich-asset-viewer');
|
||||||
|
|
||||||
|
const viewerBrokenAsset = page.locator('[data-viewer-content] [data-broken-asset]').first();
|
||||||
|
await expect(viewerBrokenAsset).toBeVisible();
|
||||||
|
|
||||||
|
await expect(viewerBrokenAsset.locator('svg')).toBeVisible();
|
||||||
|
|
||||||
|
const messageSpan = viewerBrokenAsset.locator('span');
|
||||||
|
await expect(messageSpan).toHaveClass(/text-base/);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
import { expect, Page, test } from '@playwright/test';
|
||||||
|
import { SeededRandom, selectRandom, TimelineAssetConfig } from 'src/ui/generators/timeline';
|
||||||
|
import {
|
||||||
|
createMockPeople,
|
||||||
|
FaceCreateCapture,
|
||||||
|
MockPerson,
|
||||||
|
setupFaceEditorMockApiRoutes,
|
||||||
|
} from 'src/ui/mock-network/face-editor-network';
|
||||||
|
import { assetViewerUtils } from '../timeline/utils';
|
||||||
|
import { setupAssetViewerFixture } from './utils';
|
||||||
|
|
||||||
|
const waitForSelectorTransition = async (page: Page) => {
|
||||||
|
await page.waitForFunction(
|
||||||
|
() => {
|
||||||
|
const selector = document.querySelector('#face-selector') as HTMLElement | null;
|
||||||
|
if (!selector) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return selector.getAnimations({ subtree: false }).every((animation) => animation.playState === 'finished');
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
{ timeout: 1000, polling: 50 },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openFaceEditor = async (page: Page, asset: TimelineAssetConfig) => {
|
||||||
|
await page.goto(`/photos/${asset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, asset);
|
||||||
|
await page.keyboard.press('i');
|
||||||
|
await page.locator('#detail-panel').waitFor({ state: 'visible' });
|
||||||
|
await page.getByLabel('Tag people').click();
|
||||||
|
await page.locator('#face-selector').waitFor({ state: 'visible' });
|
||||||
|
await waitForSelectorTransition(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
test.describe.configure({ mode: 'parallel' });
|
||||||
|
test.describe('face-editor', () => {
|
||||||
|
const fixture = setupAssetViewerFixture(777);
|
||||||
|
const rng = new SeededRandom(777);
|
||||||
|
let mockPeople: MockPerson[];
|
||||||
|
let faceCreateCapture: FaceCreateCapture;
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
mockPeople = createMockPeople(8);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
faceCreateCapture = { requests: [] };
|
||||||
|
await setupFaceEditorMockApiRoutes(context, mockPeople, faceCreateCapture);
|
||||||
|
});
|
||||||
|
|
||||||
|
type ScreenRect = { top: number; left: number; width: number; height: number };
|
||||||
|
|
||||||
|
const getFaceBoxRect = async (page: Page): Promise<ScreenRect> => {
|
||||||
|
const dataEl = page.locator('#face-editor-data');
|
||||||
|
await expect(dataEl).toHaveAttribute('data-face-left', /^-?\d+/);
|
||||||
|
await expect(dataEl).toHaveAttribute('data-face-top', /^-?\d+/);
|
||||||
|
await expect(dataEl).toHaveAttribute('data-face-width', /^[1-9]/);
|
||||||
|
await expect(dataEl).toHaveAttribute('data-face-height', /^[1-9]/);
|
||||||
|
const canvasBox = await page.locator('#face-editor').boundingBox();
|
||||||
|
if (!canvasBox) {
|
||||||
|
throw new Error('Canvas element not found');
|
||||||
|
}
|
||||||
|
const left = Number(await dataEl.getAttribute('data-face-left'));
|
||||||
|
const top = Number(await dataEl.getAttribute('data-face-top'));
|
||||||
|
const width = Number(await dataEl.getAttribute('data-face-width'));
|
||||||
|
const height = Number(await dataEl.getAttribute('data-face-height'));
|
||||||
|
return {
|
||||||
|
top: canvasBox.y + top,
|
||||||
|
left: canvasBox.x + left,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectorRect = async (page: Page): Promise<ScreenRect> => {
|
||||||
|
const box = await page.locator('#face-selector').boundingBox();
|
||||||
|
if (!box) {
|
||||||
|
throw new Error('Face selector element not found');
|
||||||
|
}
|
||||||
|
return { top: box.y, left: box.x, width: box.width, height: box.height };
|
||||||
|
};
|
||||||
|
|
||||||
|
const computeOverlapArea = (a: ScreenRect, b: ScreenRect): number => {
|
||||||
|
const overlapX = Math.max(0, Math.min(a.left + a.width, b.left + b.width) - Math.max(a.left, b.left));
|
||||||
|
const overlapY = Math.max(0, Math.min(a.top + a.height, b.top + b.height) - Math.max(a.top, b.top));
|
||||||
|
return overlapX * overlapY;
|
||||||
|
};
|
||||||
|
|
||||||
|
const dragFaceBox = async (page: Page, deltaX: number, deltaY: number) => {
|
||||||
|
const faceBox = await getFaceBoxRect(page);
|
||||||
|
const centerX = faceBox.left + faceBox.width / 2;
|
||||||
|
const centerY = faceBox.top + faceBox.height / 2;
|
||||||
|
await page.mouse.move(centerX, centerY);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.move(centerX + deltaX, centerY + deltaY, { steps: 5 });
|
||||||
|
await page.mouse.up();
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
};
|
||||||
|
|
||||||
|
test('Face editor opens with person list', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
await expect(page.locator('#face-selector')).toBeVisible();
|
||||||
|
await expect(page.locator('#face-editor')).toBeVisible();
|
||||||
|
|
||||||
|
for (const person of mockPeople) {
|
||||||
|
await expect(page.locator('#face-selector').getByText(person.name)).toBeVisible();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Search filters people by name', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
const searchInput = page.locator('#face-selector input');
|
||||||
|
await searchInput.fill('Alice');
|
||||||
|
|
||||||
|
await expect(page.locator('#face-selector').getByText('Alice Johnson')).toBeVisible();
|
||||||
|
await expect(page.locator('#face-selector').getByText('Bob Smith')).toBeHidden();
|
||||||
|
|
||||||
|
await searchInput.clear();
|
||||||
|
|
||||||
|
for (const person of mockPeople) {
|
||||||
|
await expect(page.locator('#face-selector').getByText(person.name)).toBeVisible();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Search with no results shows empty message', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
const searchInput = page.locator('#face-selector input');
|
||||||
|
await searchInput.fill('Nonexistent Person XYZ');
|
||||||
|
|
||||||
|
for (const person of mockPeople) {
|
||||||
|
await expect(page.locator('#face-selector').getByText(person.name)).toBeHidden();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Selecting a person shows confirmation dialog', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
const personToTag = mockPeople[0];
|
||||||
|
await page.locator('#face-selector').getByText(personToTag.name).click();
|
||||||
|
|
||||||
|
await expect(page.getByRole('dialog')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Confirming tag calls createFace API and closes editor', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
const personToTag = mockPeople[0];
|
||||||
|
await page.locator('#face-selector').getByText(personToTag.name).click();
|
||||||
|
|
||||||
|
await expect(page.getByRole('dialog')).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: /confirm/i }).click();
|
||||||
|
|
||||||
|
await expect(page.locator('#face-selector')).toBeHidden();
|
||||||
|
await expect(page.locator('#face-editor')).toBeHidden();
|
||||||
|
|
||||||
|
expect(faceCreateCapture.requests).toHaveLength(1);
|
||||||
|
expect(faceCreateCapture.requests[0].assetId).toBe(asset.id);
|
||||||
|
expect(faceCreateCapture.requests[0].personId).toBe(personToTag.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Cancel button closes face editor', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
await expect(page.locator('#face-selector')).toBeVisible();
|
||||||
|
await expect(page.locator('#face-editor')).toBeVisible();
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: /cancel/i }).click();
|
||||||
|
|
||||||
|
await expect(page.locator('#face-selector')).toBeHidden();
|
||||||
|
await expect(page.locator('#face-editor')).toBeHidden();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Selector does not overlap face box on initial open', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
const faceBox = await getFaceBoxRect(page);
|
||||||
|
const selectorBox = await getSelectorRect(page);
|
||||||
|
const overlap = computeOverlapArea(faceBox, selectorBox);
|
||||||
|
|
||||||
|
expect(overlap).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Selector repositions without overlap after dragging face box down', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
await dragFaceBox(page, 0, 150);
|
||||||
|
|
||||||
|
const faceBox = await getFaceBoxRect(page);
|
||||||
|
const selectorBox = await getSelectorRect(page);
|
||||||
|
const overlap = computeOverlapArea(faceBox, selectorBox);
|
||||||
|
|
||||||
|
expect(overlap).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Selector repositions without overlap after dragging face box right', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
await dragFaceBox(page, 200, 0);
|
||||||
|
|
||||||
|
const faceBox = await getFaceBoxRect(page);
|
||||||
|
const selectorBox = await getSelectorRect(page);
|
||||||
|
const overlap = computeOverlapArea(faceBox, selectorBox);
|
||||||
|
|
||||||
|
expect(overlap).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Selector repositions without overlap after dragging face box to top-left corner', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
await dragFaceBox(page, -300, -300);
|
||||||
|
|
||||||
|
const faceBox = await getFaceBoxRect(page);
|
||||||
|
const selectorBox = await getSelectorRect(page);
|
||||||
|
const overlap = computeOverlapArea(faceBox, selectorBox);
|
||||||
|
|
||||||
|
expect(overlap).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Selector repositions without overlap after dragging face box to bottom-right', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
await dragFaceBox(page, 300, 300);
|
||||||
|
|
||||||
|
const faceBox = await getFaceBoxRect(page);
|
||||||
|
const selectorBox = await getSelectorRect(page);
|
||||||
|
const overlap = computeOverlapArea(faceBox, selectorBox);
|
||||||
|
|
||||||
|
expect(overlap).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Selector stays within viewport bounds', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
const viewportSize = page.viewportSize()!;
|
||||||
|
const selectorBox = await getSelectorRect(page);
|
||||||
|
|
||||||
|
expect(selectorBox.top).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(selectorBox.left).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(selectorBox.top + selectorBox.height).toBeLessThanOrEqual(viewportSize.height);
|
||||||
|
expect(selectorBox.left + selectorBox.width).toBeLessThanOrEqual(viewportSize.width);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Selector stays within viewport after dragging to edge', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
await dragFaceBox(page, -400, -400);
|
||||||
|
|
||||||
|
const viewportSize = page.viewportSize()!;
|
||||||
|
const selectorBox = await getSelectorRect(page);
|
||||||
|
|
||||||
|
expect(selectorBox.top).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(selectorBox.left).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(selectorBox.top + selectorBox.height).toBeLessThanOrEqual(viewportSize.height);
|
||||||
|
expect(selectorBox.left + selectorBox.width).toBeLessThanOrEqual(viewportSize.width);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Face box is draggable on the canvas', async ({ page }) => {
|
||||||
|
const asset = selectRandom(fixture.assets, rng);
|
||||||
|
await openFaceEditor(page, asset);
|
||||||
|
|
||||||
|
const beforeDrag = await getFaceBoxRect(page);
|
||||||
|
await dragFaceBox(page, 100, 50);
|
||||||
|
const afterDrag = await getFaceBoxRect(page);
|
||||||
|
|
||||||
|
expect(afterDrag.left).toBeGreaterThan(beforeDrag.left + 50);
|
||||||
|
expect(afterDrag.top).toBeGreaterThan(beforeDrag.top + 20);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,300 @@
|
|||||||
|
import type { AssetOcrResponseDto, AssetResponseDto } from '@immich/sdk';
|
||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
import { toAssetResponseDto } from 'src/ui/generators/timeline';
|
||||||
|
import {
|
||||||
|
createMockStack,
|
||||||
|
createMockStackAsset,
|
||||||
|
MockStack,
|
||||||
|
setupBrokenAssetMockApiRoutes,
|
||||||
|
} from 'src/ui/mock-network/broken-asset-network';
|
||||||
|
import { createMockOcrData, setupOcrMockApiRoutes } from 'src/ui/mock-network/ocr-network';
|
||||||
|
import { assetViewerUtils } from '../timeline/utils';
|
||||||
|
import { setupAssetViewerFixture } from './utils';
|
||||||
|
|
||||||
|
test.describe.configure({ mode: 'parallel' });
|
||||||
|
|
||||||
|
const PRIMARY_OCR_BOXES = [
|
||||||
|
{ text: 'Hello World', x1: 0.1, y1: 0.1, x2: 0.4, y2: 0.1, x3: 0.4, y3: 0.15, x4: 0.1, y4: 0.15 },
|
||||||
|
{ text: 'Immich Photo', x1: 0.2, y1: 0.3, x2: 0.6, y2: 0.3, x3: 0.6, y3: 0.36, x4: 0.2, y4: 0.36 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const SECONDARY_OCR_BOXES = [
|
||||||
|
{ text: 'Second Asset Text', x1: 0.15, y1: 0.2, x2: 0.55, y2: 0.2, x3: 0.55, y3: 0.26, x4: 0.15, y4: 0.26 },
|
||||||
|
];
|
||||||
|
|
||||||
|
test.describe('OCR bounding boxes', () => {
|
||||||
|
const fixture = setupAssetViewerFixture(920);
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
const primaryAssetDto = toAssetResponseDto(fixture.primaryAsset);
|
||||||
|
const ocrDataByAssetId = new Map<string, AssetOcrResponseDto[]>([
|
||||||
|
[primaryAssetDto.id, createMockOcrData(primaryAssetDto.id, PRIMARY_OCR_BOXES)],
|
||||||
|
]);
|
||||||
|
|
||||||
|
await setupOcrMockApiRoutes(context, ocrDataByAssetId);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('OCR bounding boxes appear when clicking OCR button', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, fixture.primaryAsset);
|
||||||
|
|
||||||
|
const ocrButton = page.getByLabel('Text recognition');
|
||||||
|
await expect(ocrButton).toBeVisible();
|
||||||
|
await ocrButton.click();
|
||||||
|
|
||||||
|
const ocrBoxes = page.locator('[data-viewer-content] [data-testid="ocr-box"]');
|
||||||
|
await expect(ocrBoxes).toHaveCount(2);
|
||||||
|
|
||||||
|
await expect(ocrBoxes.nth(0)).toContainText('Hello World');
|
||||||
|
await expect(ocrBoxes.nth(1)).toContainText('Immich Photo');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('OCR bounding boxes toggle off on second click', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, fixture.primaryAsset);
|
||||||
|
|
||||||
|
const ocrButton = page.getByLabel('Text recognition');
|
||||||
|
await ocrButton.click();
|
||||||
|
await expect(page.locator('[data-viewer-content] [data-testid="ocr-box"]').first()).toBeVisible();
|
||||||
|
|
||||||
|
await ocrButton.click();
|
||||||
|
await expect(page.locator('[data-viewer-content] [data-testid="ocr-box"]')).toHaveCount(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('OCR with stacked assets', () => {
|
||||||
|
const fixture = setupAssetViewerFixture(921);
|
||||||
|
let mockStack: MockStack;
|
||||||
|
let primaryAssetDto: AssetResponseDto;
|
||||||
|
let secondAssetDto: AssetResponseDto;
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
primaryAssetDto = toAssetResponseDto(fixture.primaryAsset);
|
||||||
|
secondAssetDto = createMockStackAsset(fixture.adminUserId);
|
||||||
|
secondAssetDto.originalFileName = 'second-ocr-asset.jpg';
|
||||||
|
mockStack = createMockStack(primaryAssetDto, [secondAssetDto], new Set());
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
await setupBrokenAssetMockApiRoutes(context, mockStack);
|
||||||
|
|
||||||
|
const ocrDataByAssetId = new Map<string, AssetOcrResponseDto[]>([
|
||||||
|
[primaryAssetDto.id, createMockOcrData(primaryAssetDto.id, PRIMARY_OCR_BOXES)],
|
||||||
|
[secondAssetDto.id, createMockOcrData(secondAssetDto.id, SECONDARY_OCR_BOXES)],
|
||||||
|
]);
|
||||||
|
|
||||||
|
await setupOcrMockApiRoutes(context, ocrDataByAssetId);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('different OCR boxes shown for different stacked assets', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, fixture.primaryAsset);
|
||||||
|
|
||||||
|
const ocrButton = page.getByLabel('Text recognition');
|
||||||
|
await expect(ocrButton).toBeVisible();
|
||||||
|
await ocrButton.click();
|
||||||
|
|
||||||
|
const ocrBoxes = page.locator('[data-viewer-content] [data-testid="ocr-box"]');
|
||||||
|
await expect(ocrBoxes).toHaveCount(2);
|
||||||
|
await expect(ocrBoxes.nth(0)).toContainText('Hello World');
|
||||||
|
|
||||||
|
const stackThumbnails = page.locator('#stack-slideshow [data-asset]');
|
||||||
|
await expect(stackThumbnails).toHaveCount(2);
|
||||||
|
await stackThumbnails.nth(1).click();
|
||||||
|
|
||||||
|
// refreshOcr() clears showOverlay when switching assets, so re-enable it
|
||||||
|
await expect(ocrBoxes).toHaveCount(0);
|
||||||
|
await expect(ocrButton).toBeVisible();
|
||||||
|
await ocrButton.click();
|
||||||
|
|
||||||
|
await expect(ocrBoxes).toHaveCount(1);
|
||||||
|
await expect(ocrBoxes.first()).toContainText('Second Asset Text');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('OCR boxes and zoom', () => {
|
||||||
|
const fixture = setupAssetViewerFixture(922);
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
const primaryAssetDto = toAssetResponseDto(fixture.primaryAsset);
|
||||||
|
const ocrDataByAssetId = new Map<string, AssetOcrResponseDto[]>([
|
||||||
|
[primaryAssetDto.id, createMockOcrData(primaryAssetDto.id, PRIMARY_OCR_BOXES)],
|
||||||
|
]);
|
||||||
|
|
||||||
|
await setupOcrMockApiRoutes(context, ocrDataByAssetId);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('OCR boxes scale with zoom', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, fixture.primaryAsset);
|
||||||
|
|
||||||
|
const ocrButton = page.getByLabel('Text recognition');
|
||||||
|
await expect(ocrButton).toBeVisible();
|
||||||
|
await ocrButton.click();
|
||||||
|
|
||||||
|
const ocrBox = page.locator('[data-viewer-content] [data-testid="ocr-box"]').first();
|
||||||
|
await expect(ocrBox).toBeVisible();
|
||||||
|
|
||||||
|
const initialBox = await ocrBox.boundingBox();
|
||||||
|
expect(initialBox).toBeTruthy();
|
||||||
|
|
||||||
|
const { width, height } = page.viewportSize()!;
|
||||||
|
await page.mouse.move(width / 2, height / 2);
|
||||||
|
await page.mouse.wheel(0, -3);
|
||||||
|
|
||||||
|
await expect(async () => {
|
||||||
|
const zoomedBox = await ocrBox.boundingBox();
|
||||||
|
expect(zoomedBox).toBeTruthy();
|
||||||
|
expect(zoomedBox!.width).toBeGreaterThan(initialBox!.width);
|
||||||
|
expect(zoomedBox!.height).toBeGreaterThan(initialBox!.height);
|
||||||
|
}).toPass({ timeout: 2000 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('OCR text interaction', () => {
|
||||||
|
const fixture = setupAssetViewerFixture(923);
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
const primaryAssetDto = toAssetResponseDto(fixture.primaryAsset);
|
||||||
|
const ocrDataByAssetId = new Map<string, AssetOcrResponseDto[]>([
|
||||||
|
[primaryAssetDto.id, createMockOcrData(primaryAssetDto.id, PRIMARY_OCR_BOXES)],
|
||||||
|
]);
|
||||||
|
|
||||||
|
await setupOcrMockApiRoutes(context, ocrDataByAssetId);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('OCR text box has data-overlay-interactive attribute', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, fixture.primaryAsset);
|
||||||
|
|
||||||
|
await page.getByLabel('Text recognition').click();
|
||||||
|
|
||||||
|
const ocrBox = page.locator('[data-viewer-content] [data-testid="ocr-box"]').first();
|
||||||
|
await expect(ocrBox).toBeVisible();
|
||||||
|
await expect(ocrBox).toHaveAttribute('data-overlay-interactive');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('OCR text box receives focus on click', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, fixture.primaryAsset);
|
||||||
|
|
||||||
|
await page.getByLabel('Text recognition').click();
|
||||||
|
|
||||||
|
const ocrBox = page.locator('[data-viewer-content] [data-testid="ocr-box"]').first();
|
||||||
|
await expect(ocrBox).toBeVisible();
|
||||||
|
|
||||||
|
await ocrBox.click();
|
||||||
|
await expect(ocrBox).toBeFocused();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dragging on OCR text box does not trigger image pan', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, fixture.primaryAsset);
|
||||||
|
|
||||||
|
await page.getByLabel('Text recognition').click();
|
||||||
|
|
||||||
|
const ocrBox = page.locator('[data-viewer-content] [data-testid="ocr-box"]').first();
|
||||||
|
await expect(ocrBox).toBeVisible();
|
||||||
|
|
||||||
|
const imgLocator = page.locator('[data-viewer-content] img[draggable="false"]');
|
||||||
|
const initialTransform = await imgLocator.evaluate((element) => {
|
||||||
|
return getComputedStyle(element.closest('[style*="transform"]') ?? element).transform;
|
||||||
|
});
|
||||||
|
|
||||||
|
const box = await ocrBox.boundingBox();
|
||||||
|
expect(box).toBeTruthy();
|
||||||
|
const centerX = box!.x + box!.width / 2;
|
||||||
|
const centerY = box!.y + box!.height / 2;
|
||||||
|
|
||||||
|
await page.mouse.move(centerX, centerY);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.move(centerX + 50, centerY + 30, { steps: 5 });
|
||||||
|
await page.mouse.up();
|
||||||
|
|
||||||
|
const afterTransform = await imgLocator.evaluate((element) => {
|
||||||
|
return getComputedStyle(element.closest('[style*="transform"]') ?? element).transform;
|
||||||
|
});
|
||||||
|
expect(afterTransform).toBe(initialTransform);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('split touch gesture across zoom container does not trigger zoom', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, fixture.primaryAsset);
|
||||||
|
|
||||||
|
await page.getByLabel('Text recognition').click();
|
||||||
|
const ocrBox = page.locator('[data-viewer-content] [data-testid="ocr-box"]').first();
|
||||||
|
await expect(ocrBox).toBeVisible();
|
||||||
|
|
||||||
|
const imgLocator = page.locator('[data-viewer-content] img[draggable="false"]');
|
||||||
|
const initialTransform = await imgLocator.evaluate((element) => {
|
||||||
|
return getComputedStyle(element.closest('[style*="transform"]') ?? element).transform;
|
||||||
|
});
|
||||||
|
|
||||||
|
const viewerContent = page.locator('[data-viewer-content]');
|
||||||
|
const viewerBox = await viewerContent.boundingBox();
|
||||||
|
expect(viewerBox).toBeTruthy();
|
||||||
|
|
||||||
|
// Dispatch a synthetic split gesture: one touch inside the viewer, one outside
|
||||||
|
await page.evaluate(
|
||||||
|
({ viewerCenterX, viewerCenterY, outsideY }) => {
|
||||||
|
const viewer = document.querySelector('[data-viewer-content]');
|
||||||
|
if (!viewer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTouch = (id: number, x: number, y: number) => {
|
||||||
|
return new Touch({
|
||||||
|
identifier: id,
|
||||||
|
target: viewer,
|
||||||
|
clientX: x,
|
||||||
|
clientY: y,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const insideTouch = createTouch(0, viewerCenterX, viewerCenterY);
|
||||||
|
const outsideTouch = createTouch(1, viewerCenterX, outsideY);
|
||||||
|
|
||||||
|
const touchStartEvent = new TouchEvent('touchstart', {
|
||||||
|
touches: [insideTouch, outsideTouch],
|
||||||
|
targetTouches: [insideTouch],
|
||||||
|
changedTouches: [insideTouch, outsideTouch],
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const touchMoveEvent = new TouchEvent('touchmove', {
|
||||||
|
touches: [createTouch(0, viewerCenterX, viewerCenterY - 30), createTouch(1, viewerCenterX, outsideY + 30)],
|
||||||
|
targetTouches: [createTouch(0, viewerCenterX, viewerCenterY - 30)],
|
||||||
|
changedTouches: [
|
||||||
|
createTouch(0, viewerCenterX, viewerCenterY - 30),
|
||||||
|
createTouch(1, viewerCenterX, outsideY + 30),
|
||||||
|
],
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const touchEndEvent = new TouchEvent('touchend', {
|
||||||
|
touches: [],
|
||||||
|
targetTouches: [],
|
||||||
|
changedTouches: [insideTouch, outsideTouch],
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.dispatchEvent(touchStartEvent);
|
||||||
|
viewer.dispatchEvent(touchMoveEvent);
|
||||||
|
viewer.dispatchEvent(touchEndEvent);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
viewerCenterX: viewerBox!.x + viewerBox!.width / 2,
|
||||||
|
viewerCenterY: viewerBox!.y + viewerBox!.height / 2,
|
||||||
|
outsideY: 10, // near the top of the page, outside the viewer
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const afterTransform = await imgLocator.evaluate((element) => {
|
||||||
|
return getComputedStyle(element.closest('[style*="transform"]') ?? element).transform;
|
||||||
|
});
|
||||||
|
expect(afterTransform).toBe(initialTransform);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
import type { AssetResponseDto } from '@immich/sdk';
|
||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
import { toAssetResponseDto } from 'src/ui/generators/timeline';
|
||||||
|
import {
|
||||||
|
createMockStack,
|
||||||
|
createMockStackAsset,
|
||||||
|
MockStack,
|
||||||
|
setupBrokenAssetMockApiRoutes,
|
||||||
|
} from 'src/ui/mock-network/broken-asset-network';
|
||||||
|
import { assetViewerUtils } from '../timeline/utils';
|
||||||
|
import { enableTagsPreference, ensureDetailPanelVisible, setupAssetViewerFixture } from './utils';
|
||||||
|
|
||||||
|
test.describe.configure({ mode: 'parallel' });
|
||||||
|
test.describe('asset-viewer stack', () => {
|
||||||
|
const fixture = setupAssetViewerFixture(888);
|
||||||
|
let mockStack: MockStack;
|
||||||
|
let primaryAssetDto: AssetResponseDto;
|
||||||
|
let secondAssetDto: AssetResponseDto;
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
primaryAssetDto = toAssetResponseDto(fixture.primaryAsset);
|
||||||
|
primaryAssetDto.tags = [
|
||||||
|
{
|
||||||
|
id: faker.string.uuid(),
|
||||||
|
name: '1',
|
||||||
|
value: 'test/1',
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
secondAssetDto = createMockStackAsset(fixture.adminUserId);
|
||||||
|
secondAssetDto.tags = [
|
||||||
|
{
|
||||||
|
id: faker.string.uuid(),
|
||||||
|
name: '2',
|
||||||
|
value: 'test/2',
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
mockStack = createMockStack(primaryAssetDto, [secondAssetDto], new Set());
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
await setupBrokenAssetMockApiRoutes(context, mockStack);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('stack slideshow is visible', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await assetViewerUtils.waitForViewerLoad(page, fixture.primaryAsset);
|
||||||
|
|
||||||
|
const stackSlideshow = page.locator('#stack-slideshow');
|
||||||
|
await expect(stackSlideshow).toBeVisible();
|
||||||
|
|
||||||
|
const stackAssets = stackSlideshow.locator('[data-asset]');
|
||||||
|
await expect(stackAssets).toHaveCount(mockStack.assets.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tags of primary asset are visible', async ({ context, page }) => {
|
||||||
|
await enableTagsPreference(context);
|
||||||
|
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await ensureDetailPanelVisible(page);
|
||||||
|
|
||||||
|
const tags = page.getByTestId('detail-panel-tags').getByRole('link');
|
||||||
|
await expect(tags.first()).toHaveText('test/1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tags of second asset are visible', async ({ context, page }) => {
|
||||||
|
await enableTagsPreference(context);
|
||||||
|
|
||||||
|
await page.goto(`/photos/${fixture.primaryAsset.id}`);
|
||||||
|
await ensureDetailPanelVisible(page);
|
||||||
|
|
||||||
|
const stackAssets = page.locator('#stack-slideshow [data-asset]');
|
||||||
|
await stackAssets.nth(1).click();
|
||||||
|
|
||||||
|
const tags = page.getByTestId('detail-panel-tags').getByRole('link');
|
||||||
|
await expect(tags.first()).toHaveText('test/2');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
import type { AssetResponseDto } from '@immich/sdk';
|
||||||
|
import { BrowserContext, Page, test } from '@playwright/test';
|
||||||
|
import {
|
||||||
|
Changes,
|
||||||
|
createDefaultTimelineConfig,
|
||||||
|
generateTimelineData,
|
||||||
|
SeededRandom,
|
||||||
|
selectRandom,
|
||||||
|
TimelineAssetConfig,
|
||||||
|
TimelineData,
|
||||||
|
toAssetResponseDto,
|
||||||
|
} from 'src/ui/generators/timeline';
|
||||||
|
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network';
|
||||||
|
import { setupTimelineMockApiRoutes, TimelineTestContext } from 'src/ui/mock-network/timeline-network';
|
||||||
|
import { utils } from 'src/utils';
|
||||||
|
|
||||||
|
export type AssetViewerTestFixture = {
|
||||||
|
adminUserId: string;
|
||||||
|
timelineRestData: TimelineData;
|
||||||
|
assets: TimelineAssetConfig[];
|
||||||
|
testContext: TimelineTestContext;
|
||||||
|
changes: Changes;
|
||||||
|
primaryAsset: TimelineAssetConfig;
|
||||||
|
primaryAssetDto: AssetResponseDto;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function setupAssetViewerFixture(seed: number): AssetViewerTestFixture {
|
||||||
|
const rng = new SeededRandom(seed);
|
||||||
|
const testContext = new TimelineTestContext();
|
||||||
|
|
||||||
|
const fixture: AssetViewerTestFixture = {
|
||||||
|
adminUserId: undefined!,
|
||||||
|
timelineRestData: undefined!,
|
||||||
|
assets: [],
|
||||||
|
testContext,
|
||||||
|
changes: {
|
||||||
|
albumAdditions: [],
|
||||||
|
assetDeletions: [],
|
||||||
|
assetArchivals: [],
|
||||||
|
assetFavorites: [],
|
||||||
|
},
|
||||||
|
primaryAsset: undefined!,
|
||||||
|
primaryAssetDto: undefined!,
|
||||||
|
};
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
test.fail(
|
||||||
|
process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS !== '1',
|
||||||
|
'This test requires env var: PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1',
|
||||||
|
);
|
||||||
|
utils.initSdk();
|
||||||
|
fixture.adminUserId = faker.string.uuid();
|
||||||
|
testContext.adminId = fixture.adminUserId;
|
||||||
|
fixture.timelineRestData = generateTimelineData({
|
||||||
|
...createDefaultTimelineConfig(),
|
||||||
|
ownerId: fixture.adminUserId,
|
||||||
|
});
|
||||||
|
for (const timeBucket of fixture.timelineRestData.buckets.values()) {
|
||||||
|
fixture.assets.push(...timeBucket);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixture.primaryAsset = selectRandom(
|
||||||
|
fixture.assets.filter((a) => a.isImage),
|
||||||
|
rng,
|
||||||
|
);
|
||||||
|
fixture.primaryAssetDto = toAssetResponseDto(fixture.primaryAsset);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
await setupBaseMockApiRoutes(context, fixture.adminUserId);
|
||||||
|
await setupTimelineMockApiRoutes(context, fixture.timelineRestData, fixture.changes, fixture.testContext);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterEach(() => {
|
||||||
|
fixture.testContext.slowBucket = false;
|
||||||
|
fixture.changes.albumAdditions = [];
|
||||||
|
fixture.changes.assetDeletions = [];
|
||||||
|
fixture.changes.assetArchivals = [];
|
||||||
|
fixture.changes.assetFavorites = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
return fixture;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function ensureDetailPanelVisible(page: Page) {
|
||||||
|
await page.waitForSelector('#immich-asset-viewer');
|
||||||
|
|
||||||
|
const isVisible = await page.locator('#detail-panel').isVisible();
|
||||||
|
if (!isVisible) {
|
||||||
|
await page.keyboard.press('i');
|
||||||
|
await page.waitForSelector('#detail-panel');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function enableTagsPreference(context: BrowserContext) {
|
||||||
|
await context.route('**/users/me/preferences', async (route) => {
|
||||||
|
return route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
json: {
|
||||||
|
albums: { defaultAssetOrder: 'desc' },
|
||||||
|
folders: { enabled: false, sidebarWeb: false },
|
||||||
|
memories: { enabled: true, duration: 5 },
|
||||||
|
people: { enabled: true, sidebarWeb: false },
|
||||||
|
sharedLinks: { enabled: true, sidebarWeb: false },
|
||||||
|
ratings: { enabled: false },
|
||||||
|
tags: { enabled: true, sidebarWeb: false },
|
||||||
|
emailNotifications: { enabled: true, albumInvite: true, albumUpdate: true },
|
||||||
|
download: { archiveSize: 4_294_967_296, includeEmbeddedVideos: false },
|
||||||
|
purchase: { showSupportBadge: true, hideBuyButtonUntil: '2100-02-12T00:00:00.000Z' },
|
||||||
|
cast: { gCastEnabled: false },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
generateTimelineData,
|
generateTimelineData,
|
||||||
TimelineAssetConfig,
|
TimelineAssetConfig,
|
||||||
TimelineData,
|
TimelineData,
|
||||||
|
toAssetResponseDto,
|
||||||
} from 'src/ui/generators/timeline';
|
} from 'src/ui/generators/timeline';
|
||||||
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network';
|
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network';
|
||||||
import { setupTimelineMockApiRoutes, TimelineTestContext } from 'src/ui/mock-network/timeline-network';
|
import { setupTimelineMockApiRoutes, TimelineTestContext } from 'src/ui/mock-network/timeline-network';
|
||||||
@@ -30,6 +31,10 @@ test.describe('search gallery-viewer', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test.beforeAll(async () => {
|
test.beforeAll(async () => {
|
||||||
|
test.fail(
|
||||||
|
process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS !== '1',
|
||||||
|
'This test requires env var: PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1',
|
||||||
|
);
|
||||||
adminUserId = faker.string.uuid();
|
adminUserId = faker.string.uuid();
|
||||||
testContext.adminId = adminUserId;
|
testContext.adminId = adminUserId;
|
||||||
timelineRestData = generateTimelineData({ ...createDefaultTimelineConfig(), ownerId: adminUserId });
|
timelineRestData = generateTimelineData({ ...createDefaultTimelineConfig(), ownerId: adminUserId });
|
||||||
@@ -44,7 +49,10 @@ test.describe('search gallery-viewer', () => {
|
|||||||
|
|
||||||
await context.route('**/api/search/metadata', async (route, request) => {
|
await context.route('**/api/search/metadata', async (route, request) => {
|
||||||
if (request.method() === 'POST') {
|
if (request.method() === 'POST') {
|
||||||
const searchAssets = assets.slice(0, 5).filter((asset) => !changes.assetDeletions.includes(asset.id));
|
const searchAssets = assets
|
||||||
|
.slice(0, 5)
|
||||||
|
.filter((asset) => !changes.assetDeletions.includes(asset.id))
|
||||||
|
.map((asset) => toAssetResponseDto(asset));
|
||||||
return route.fulfill({
|
return route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export const thumbnailUtils = {
|
|||||||
return page.locator(`[data-thumbnail-focus-container][data-asset="${assetId}"]`);
|
return page.locator(`[data-thumbnail-focus-container][data-asset="${assetId}"]`);
|
||||||
},
|
},
|
||||||
selectButton(page: Page, assetId: string) {
|
selectButton(page: Page, assetId: string) {
|
||||||
return page.locator(`[data-thumbnail-focus-container][data-asset="${assetId}"] button`);
|
return page.locator(`[data-thumbnail-focus-container][data-asset="${assetId}"] button[role="checkbox"]`);
|
||||||
},
|
},
|
||||||
selectedAsset(page: Page) {
|
selectedAsset(page: Page) {
|
||||||
return page.locator('[data-thumbnail-focus-container][data-selected]');
|
return page.locator('[data-thumbnail-focus-container][data-selected]');
|
||||||
@@ -215,8 +215,9 @@ export const pageUtils = {
|
|||||||
await page.getByText('Confirm').click();
|
await page.getByText('Confirm').click();
|
||||||
},
|
},
|
||||||
async selectDay(page: Page, day: string) {
|
async selectDay(page: Page, day: string) {
|
||||||
await page.getByTitle(day).hover();
|
const section = page.getByTitle(day).locator('xpath=ancestor::section[@data-group]');
|
||||||
await page.locator('[data-group] .w-8').click();
|
await section.hover();
|
||||||
|
await section.locator('.w-8').click();
|
||||||
},
|
},
|
||||||
async pauseTestDebug() {
|
async pauseTestDebug() {
|
||||||
console.log('NOTE: pausing test indefinately for debug');
|
console.log('NOTE: pausing test indefinately for debug');
|
||||||
|
|||||||
+43
-29
@@ -177,40 +177,51 @@ export const utils = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
resetDatabase: async (tables?: string[]) => {
|
resetDatabase: async (tables?: string[]) => {
|
||||||
try {
|
client = await utils.connectDatabase();
|
||||||
client = await utils.connectDatabase();
|
|
||||||
|
|
||||||
tables = tables || [
|
tables = tables || [
|
||||||
// TODO e2e test for deleting a stack, since it is quite complex
|
// TODO e2e test for deleting a stack, since it is quite complex
|
||||||
'stack',
|
'stack',
|
||||||
'library',
|
'library',
|
||||||
'shared_link',
|
'shared_link',
|
||||||
'person',
|
'person',
|
||||||
'album',
|
'album',
|
||||||
'asset',
|
'asset',
|
||||||
'asset_face',
|
'asset_face',
|
||||||
'activity',
|
'activity',
|
||||||
'api_key',
|
'api_key',
|
||||||
'session',
|
'session',
|
||||||
'user',
|
'user',
|
||||||
'system_metadata',
|
'system_metadata',
|
||||||
'tag',
|
'tag',
|
||||||
];
|
];
|
||||||
|
|
||||||
const sql: string[] = [];
|
const truncateTables = tables.filter((table) => table !== 'system_metadata');
|
||||||
|
const sql: string[] = [];
|
||||||
|
|
||||||
for (const table of tables) {
|
if (truncateTables.length > 0) {
|
||||||
if (table === 'system_metadata') {
|
sql.push(`TRUNCATE "${truncateTables.join('", "')}" CASCADE;`);
|
||||||
sql.push(`DELETE FROM "system_metadata" where "key" NOT IN ('reverse-geocoding-state', 'system-flags');`);
|
}
|
||||||
} else {
|
|
||||||
sql.push(`DELETE FROM "${table}" CASCADE;`);
|
if (tables.includes('system_metadata')) {
|
||||||
|
sql.push(`DELETE FROM "system_metadata" where "key" NOT IN ('reverse-geocoding-state', 'system-flags');`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = sql.join('\n');
|
||||||
|
const maxRetries = 3;
|
||||||
|
|
||||||
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||||
|
try {
|
||||||
|
await client.query(query);
|
||||||
|
return;
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error?.code === '40P01' && attempt < maxRetries) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 250 * attempt));
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
console.error('Failed to reset database', error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.query(sql.join('\n'));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to reset database', error);
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -499,6 +510,9 @@ export const utils = {
|
|||||||
createStack: (accessToken: string, assetIds: string[]) =>
|
createStack: (accessToken: string, assetIds: string[]) =>
|
||||||
createStack({ stackCreateDto: { assetIds } }, { headers: asBearerAuth(accessToken) }),
|
createStack({ stackCreateDto: { assetIds } }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
|
setAssetDuplicateId: (accessToken: string, assetId: string, duplicateId: string | null) =>
|
||||||
|
updateAssets({ assetBulkUpdateDto: { ids: [assetId], duplicateId } }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
upsertTags: (accessToken: string, tags: string[]) =>
|
upsertTags: (accessToken: string, tags: string[]) =>
|
||||||
upsertTags({ tagUpsertDto: { tags } }, { headers: asBearerAuth(accessToken) }),
|
upsertTags({ tagUpsertDto: { tags } }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -17,6 +17,6 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"baseUrl": "./"
|
"baseUrl": "./"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts"],
|
"include": ["src/**/*.ts", "vitest*.config.ts"],
|
||||||
"exclude": ["dist", "node_modules"]
|
"exclude": ["dist", "node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
import { defineConfig } from 'vitest/config';
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
const skipDockerSetup = process.env.VITEST_DISABLE_DOCKER_SETUP === 'true';
|
const skipDockerSetup = process.env.VITEST_DISABLE_DOCKER_SETUP === 'true';
|
||||||
@@ -14,15 +15,14 @@ if (!skipDockerSetup) {
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
test: {
|
test: {
|
||||||
|
name: 'e2e:server',
|
||||||
retry: process.env.CI ? 4 : 0,
|
retry: process.env.CI ? 4 : 0,
|
||||||
include: ['src/specs/server/**/*.e2e-spec.ts'],
|
include: ['src/specs/server/**/*.e2e-spec.ts'],
|
||||||
globalSetup,
|
globalSetup,
|
||||||
testTimeout: 15_000,
|
testTimeout: 15_000,
|
||||||
pool: 'threads',
|
pool: 'threads',
|
||||||
poolOptions: {
|
maxWorkers: 1,
|
||||||
threads: {
|
isolate: false,
|
||||||
singleThread: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
plugins: [tsconfigPaths()],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
import { defineConfig } from 'vitest/config';
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
const skipDockerSetup = process.env.VITEST_DISABLE_DOCKER_SETUP === 'true';
|
const skipDockerSetup = process.env.VITEST_DISABLE_DOCKER_SETUP === 'true';
|
||||||
@@ -14,15 +15,14 @@ if (!skipDockerSetup) {
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
test: {
|
test: {
|
||||||
|
name: 'e2e:maintenance',
|
||||||
retry: process.env.CI ? 4 : 0,
|
retry: process.env.CI ? 4 : 0,
|
||||||
include: ['src/specs/maintenance/server/**/*.e2e-spec.ts'],
|
include: ['src/specs/maintenance/server/**/*.e2e-spec.ts'],
|
||||||
globalSetup,
|
globalSetup,
|
||||||
testTimeout: 15_000,
|
testTimeout: 15_000,
|
||||||
pool: 'threads',
|
pool: 'threads',
|
||||||
poolOptions: {
|
maxWorkers: 1,
|
||||||
threads: {
|
isolate: false,
|
||||||
singleThread: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
plugins: [tsconfigPaths()],
|
||||||
});
|
});
|
||||||
|
|||||||
+224
-125
@@ -2,147 +2,147 @@
|
|||||||
"about": "Oor",
|
"about": "Oor",
|
||||||
"account": "Rekening",
|
"account": "Rekening",
|
||||||
"account_settings": "Rekeninginstellings",
|
"account_settings": "Rekeninginstellings",
|
||||||
"acknowledge": "Erken",
|
"acknowledge": "Neem kennis",
|
||||||
"action": "Aksie",
|
"action": "Aksie",
|
||||||
"action_common_update": "Opdateur",
|
"action_common_update": "Werk by",
|
||||||
"actions": "Aksies",
|
"actions": "Aksies",
|
||||||
"active": "Aktief",
|
"active": "Aktief",
|
||||||
"activity": "Aktiwiteite",
|
"activity": "Aktiwiteite",
|
||||||
"activity_changed": "Aktiwiteit is {enabled, select, true {aangeskakel} other {afgeskakel}}",
|
"activity_changed": "Aktiwiteit is {enabled, select, true {geaktiveer} other {gedeaktiveer}}",
|
||||||
"add": "Voegby",
|
"add": "Voeg toe",
|
||||||
"add_a_description": "Voeg 'n beskrywing by",
|
"add_a_description": "Voeg ’n beskrywing toe",
|
||||||
"add_a_location": "Voeg 'n ligging by",
|
"add_a_location": "Voeg ’n ligging toe",
|
||||||
"add_a_name": "Voeg 'n naam by",
|
"add_a_name": "Voeg ’n naam toe",
|
||||||
"add_a_title": "Voeg 'n titel by",
|
"add_a_title": "Voeg ’n titel toe",
|
||||||
"add_birthday": "Voeg 'n verjaarsdag by",
|
"add_birthday": "Voeg ’n verjaarsdag toe",
|
||||||
"add_endpoint": "Voeg Koppelvlakpunt by",
|
"add_endpoint": "Voeg eindpunt toe",
|
||||||
"add_exclusion_pattern": "Voeg uitsgluitingspatrone by",
|
"add_exclusion_pattern": "Voeg uitsluitingspatroon toe",
|
||||||
"add_location": "Voeg ligging by",
|
"add_location": "Voeg ligging toe",
|
||||||
"add_more_users": "Voeg meer gebruikers by",
|
"add_more_users": "Voeg meer gebruikers toe",
|
||||||
"add_partner": "Voeg vennoot by",
|
"add_partner": "Voeg vennoot toe",
|
||||||
"add_path": "Voeg pad by",
|
"add_path": "Voeg pad toe",
|
||||||
"add_photos": "Voeg foto's by",
|
"add_photos": "Voeg foto’s toe",
|
||||||
"add_tag": "Voeg tag by",
|
"add_tag": "Voeg etiket toe",
|
||||||
"add_to": "Voeg by…",
|
"add_to": "Voeg toe tot…",
|
||||||
"add_to_album": "Voeg na album",
|
"add_to_album": "Voeg toe tot album",
|
||||||
"add_to_album_bottom_sheet_added": "By {album} bygevoeg",
|
"add_to_album_bottom_sheet_added": "Tot {album} toegevoeg",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Reeds in {album}",
|
"add_to_album_bottom_sheet_already_exists": "Reeds in {album}",
|
||||||
"add_to_albums": "Voeg by albums",
|
"add_to_albums": "Voeg toe tot albums",
|
||||||
"add_to_albums_count": "Voeg by ({count}) albums",
|
"add_to_albums_count": "Voeg toe tot albums ({count})",
|
||||||
"add_to_shared_album": "Voeg toe aan gedeelde album",
|
"add_to_shared_album": "Voeg toe tot gedeelde album",
|
||||||
"add_url": "Voeg URL by",
|
"add_url": "Voeg bronadres toe",
|
||||||
"added_to_archive": "By argief toegevoegd",
|
"added_to_archive": "Tot argief toegevoeg",
|
||||||
"added_to_favorites": "By gunstelinge toegevoegd",
|
"added_to_favorites": "Tot gunstelinge toegevoeg",
|
||||||
"added_to_favorites_count": "Het {count, number} by gunstelinge toegevoegd",
|
"added_to_favorites_count": "{count, number} tot gunstelinge toegevoeg",
|
||||||
"admin": {
|
"admin": {
|
||||||
"add_exclusion_pattern_description": "Voeg uitsluitingspatrone by. Globbing met *, ** en ? word ondersteun. Om alle lêers in enige lêergids genaamd \"Raw\" te ignoreer, gebruik \"**/Raw/**\". Om alle lêers wat op \".tif\" eindig, te ignoreer, gebruik \"**/*.tif\". Om 'n absolute pad te ignoreer, gebruik \"/path/to/ignore/**\".",
|
"add_exclusion_pattern_description": "Voeg uitsluitingspatrone toe. Plekhouers met *, ** en ? word ondersteun. Om alle lêers in enige vouer genaamd “Raw” te ignoreer, gebruik “**/Raw/**”. Om alle lêers wat op “.tif” eindig, te ignoreer, gebruik “**/*.tif”. Om ’n absolute pad te ignoreer, gebruik “/path/to/ignore/**”.",
|
||||||
"admin_user": "Admin gebruiker",
|
"admin_user": "Admingebruiker",
|
||||||
"asset_offline_description": "Hierdie eksterne biblioteekbate word nie meer op skyf gevind nie en is na die asblik geskuif. As die lêer binne die biblioteek geskuif is, gaan jou tydlyn na vir die nuwe ooreenstemmende bate. Om hierdie bate te herstel, maak asseblief seker dat die lêerpad hieronder deur Immich verkry kan word en skandeer die biblioteek.",
|
"asset_offline_description": "Hierdie eksterne biblioteekitem word nie meer op skyf gevind nie en is na die asblik geskuif. As die lêer binne die biblioteek geskuif is, gaan u tydlyn na vir die nuwe ooreenstemmende item. Om hierdie item te herstel, maak asseblief seker dat die lêerpad hieronder deur Immich verkry kan word en skandeer die biblioteek.",
|
||||||
"authentication_settings": "Verifikasie instellings",
|
"authentication_settings": "Waarmerkinstellings",
|
||||||
"authentication_settings_description": "Bestuur wagwoord, OAuth en ander verifikasie instellings",
|
"authentication_settings_description": "Bestuur wagwoord, OAuth en ander waarmerkinstellings",
|
||||||
"authentication_settings_disable_all": "Is jy seker jy wil alle aanmeldmetodes deaktiveer? Aanmelding sal heeltemal gedeaktiveer word.",
|
"authentication_settings_disable_all": "Is u seker u wil alle aantekenmetodes deaktiveer? Aantekening sal heeltemal gedeaktiveer word.",
|
||||||
"authentication_settings_reenable": "Om te heraktiveer, gebruik 'n <link>Server Command</link>.",
|
"authentication_settings_reenable": "Gebruik ’n <link>bedienerbevel</link> om te heraktiveer.",
|
||||||
"background_task_job": "Agtergrondtake",
|
"background_task_job": "Agtergrondtake",
|
||||||
"backup_database": "Skep Datastortlêer",
|
"backup_database": "Skep Databasisstortlêer",
|
||||||
"backup_database_enable_description": "Aktiveer databasisrugsteun",
|
"backup_database_enable_description": "Aktiveer databasisstortlêers",
|
||||||
"backup_keep_last_amount": "Aantal vorige rugsteune om te hou",
|
"backup_keep_last_amount": "Aantal vorige stortlêers om te hou",
|
||||||
"backup_onboarding_3_description": "totale kopieë van jou data, insluitende die oorspronklikke lêers. Dit sluit in 1 kopie op 'n ander perseel en 2 kopieë om die huidige rekenaar.",
|
"backup_onboarding_3_description": "totale kopieë van u data, insluitend die oorspronklike lêers. Dit sluit 1 kopie op ’n ander perseel en 2 lokale kopieë in.",
|
||||||
"backup_onboarding_description": "'N <backblaze-link>3-2-1 rugsteun strategie</backblaze-link> word sterk aanbeveel om jou data veilig te hou. Hou kopieë van jou fotos/videos so wel as die Immich databasis vir 'n volledige rugsteun oplossing.",
|
"backup_onboarding_description": "’n <backblaze-link>3-2-1-rugsteunstrategie</backblaze-link> word sterk aanbeveel om u data veilig te hou. Hou kopieë van u foto’s/video’s sowel as die Immich-databasis vir ’n volledige rugsteunoplossing.",
|
||||||
"backup_onboarding_footer": "Vir meer inligting oor hoe om 'n rugsteun kopie van Immich te maak, gaan lees asseblief hierdie <link>dokument</link>.",
|
"backup_onboarding_footer": "Lees hierdie <link>dokument</link> vir meer inligting oor hoe om ’n rugsteunkopie van Immich te maak.",
|
||||||
"backup_onboarding_parts_title": "'N 3-2-1 rugsteun sluit in:",
|
"backup_onboarding_parts_title": "’n 3-2-1-rugsteun sluit in:",
|
||||||
"backup_onboarding_title": "Rugsteun kopieë",
|
"backup_onboarding_title": "Rugsteunkopieë",
|
||||||
"backup_settings": "Rugsteun instellings",
|
"backup_settings": "Databasisstortinstellings",
|
||||||
"backup_settings_description": "Bestuur databasis rugsteun instellings.",
|
"backup_settings_description": "Bestuur databasisrugsteuninstellings.",
|
||||||
"cleared_jobs": "Poste gevee vir: {job}",
|
"cleared_jobs": "Take gewis vir: {job}",
|
||||||
"config_set_by_file": "Config word tans deur 'n konfigurasielêer gestel",
|
"config_set_by_file": "Config word tans deur ’n konfigurasielêer gestel",
|
||||||
"confirm_delete_library": "Is jy seker jy wil {library}-biblioteek uitvee?",
|
"confirm_delete_library": "Is u seker u wil {library}-biblioteek skrap?",
|
||||||
"confirm_delete_library_assets": "Is jy seker jy wil hierdie biblioteek uitvee? Dit sal {count, plural, one {# bevatte base} other {# bevatte bates}} uit Immich uitvee en kan nie ongedaan gemaak word nie. Lêers sal op skyf bly.",
|
"confirm_delete_library_assets": "Is u seker u wil hierdie biblioteek skrap? Dit sal {count, plural, one {# bevatte item} other {# bevatte items}} uit Immich skrap en kan nie ongedaan gemaak word nie. Lêers sal op skyf bly.",
|
||||||
"confirm_email_below": "Om te bevestig, tik \"{email}\" hieronder",
|
"confirm_email_below": "Tik “{email}” hieronder ter bevestiging",
|
||||||
"confirm_reprocess_all_faces": "Is jy seker jy wil alle gesigte herverwerk? Dit sal ook genoemde mense skoonmaak.",
|
"confirm_reprocess_all_faces": "Is u seker u wil alle gesigte herverwerk? Dit sal ook genoemde mense skoonmaak.",
|
||||||
"confirm_user_password_reset": "Is jy seker jy wil {user} se wagwoord terugstel?",
|
"confirm_user_password_reset": "Is u seker u wil {user} se wagwoord terugstel?",
|
||||||
"confirm_user_pin_code_reset": "Is jy seker jy wil {user} se PIN kode herstel?",
|
"confirm_user_pin_code_reset": "Is u seker u wil {user} se PIN-kode herstel?",
|
||||||
"create_job": "Skep werk",
|
"create_job": "Skep taak",
|
||||||
"cron_expression": "Cron uitdrukking",
|
"cron_expression": "Cron-uitdrukking",
|
||||||
"cron_expression_description": "Stel die skanderingsinterval in met die cron-formaat. Vir meer inligting verwys asseblief na bv. <link>Crontab Guru</link>",
|
"cron_expression_description": "Stel die skanderingsinterval in met die cron-formaat. Kyk gerus na bv. <link>Crontab Guru</link> vir meer inligting",
|
||||||
"cron_expression_presets": "Cron uitdrukking voorafinstellings",
|
"cron_expression_presets": "Cron-uitdrukking voorafinstellings",
|
||||||
"disable_login": "Deaktiveer aanmelding",
|
"disable_login": "Deaktiveer aantekening",
|
||||||
"duplicate_detection_job_description": "Begin masjienleer op bates om soortgelyke beelde op te spoor. Maak staat op Smart Search",
|
"duplicate_detection_job_description": "Begin masjienleer op items om soortgelyke beelde op te spoor. Maak staat op Slimsoek",
|
||||||
"exclusion_pattern_description": "Met uitsluitingspatrone kan jy lêers en vouers ignoreer wanneer jy jou biblioteek skandeer. Dit is nuttig as jy vouers het wat lêers bevat wat jy nie wil invoer nie, soos RAW-lêers.",
|
"exclusion_pattern_description": "Met uitsluitingspatrone kan u lêers en vouers ignoreer wanneer u u biblioteek skandeer. Dit is nuttig as u vouers het wat lêers bevat wat u nie wil invoer nie, soos RAW-lêers.",
|
||||||
"face_detection": "Gesig herkenning",
|
"face_detection": "Gesigherkenning",
|
||||||
"face_detection_description": "Identifiseer die gesigte in media deur middel van masjienleer. Vir videos word slegs die duimnaelskets oorweeg. “Herlaai” (ver)werk al die media weer. “Stel terug” verwyder alle huidige gesigdata. “Onverwerk” plaas bates in die tou wat nog nie verwerk is nie. Geidentifiseerde gesigte sal ná voltooiing van Gesigidentifikasie vir Gesigherkenning in die tou geplaas word, om hulle in bestaande of nuwe persone te groepeer.",
|
"face_detection_description": "Identifiseer die gesigte in media d.m.v. masjienleer. Vir video’s word slegs die duimnael oorweeg. “Herlaai” (ver)werk al die media weer. “Stel terug” verwyder alle huidige gesigdata. “Onverwerk” plaas items in die ry wat nog nie verwerk is nie. Geïdentifiseerde gesigte sal ná voltooiing van Gesigidentifikasie vir Gesigherkenning in die ry geplaas word om hulle in bestaande of nuwe persone te groepeer.",
|
||||||
"facial_recognition_job_description": "Groepeer gesigte in mense in. Die stap is vinniger nadat Gesig Deteksie klaar is. \"Herstel\" (her-)groepeer alle gesigte. \"Vermiste\" plaas gesigte in ry wat nie 'n persoon gekoppel het nie.",
|
"facial_recognition_job_description": "Groepeer gesigte in mense. Die stap is vinniger nadat Gesigherkenning klaar is. “Herstel” (her-)groepeer alle gesigte. “Vermiste” plaas gesigte in ry wat nie ’n persoon gekoppel het nie.",
|
||||||
"failed_job_command": "Opdrag {command} het misluk vir werk: {job}",
|
"failed_job_command": "Bevel {command} het misluk vir taak: {job}",
|
||||||
"force_delete_user_warning": "WAARSKUWING: Dit sal onmiddellik die gebruiker en alle bates verwyder. Dit kan nie ontdoen word nie en die lêers kan nie herstel word nie.",
|
"force_delete_user_warning": "WAARSKUWING: Dit sal onmiddellik die gebruiker en alle items verwyder. Dit kan nie ontdaan word nie en die lêers kan nie herstel word nie.",
|
||||||
"image_format": "Formaat",
|
"image_format": "Formaat",
|
||||||
"image_format_description": "WebP produseer kleiner lêers as JPEG, maar is stadiger om te enkodeer.",
|
"image_format_description": "WebP lewer kleiner lêers as JPEG, maar is stadiger om te enkodeer.",
|
||||||
"image_fullsize_description": "Vol grote prent met geen metadata, gebruik wanner ingezoem",
|
"image_fullsize_description": "Volgrootte prent met geen metadata, gebruik wanner ingezoem",
|
||||||
"image_fullsize_enabled": "Skakel aan vol grote prent generasie",
|
"image_fullsize_enabled": "Aktiveer spek van volgrootte prent",
|
||||||
"image_prefer_embedded_preview": "Verkies ingebedde voorskou",
|
"image_prefer_embedded_preview": "Verkies ingebedde voorskou",
|
||||||
"image_prefer_wide_gamut": "Verkies wide gamut",
|
"image_prefer_wide_gamut": "Verkies breëspektrum",
|
||||||
"image_prefer_wide_gamut_setting_description": "Gebruik Display P3 vir kleinkiekies. Dit behou die lewendheid van beelde met wye kleurruimtes beter, maar beelde kan anders verskyn op ou apparate met 'n ou blaaierweergawe. sRGB-beelde gebruik steeds sRGB om kleurverskuiwings te voorkom.",
|
"image_prefer_wide_gamut_setting_description": "Gebruik Display P3 vir duimnaels. Dit behou die lewendheid van beelde met wye kleurruimtes beter, maar beelde kan anders verskyn op ou toestelle met ’n ou blaaierweergawe. sRGB-beelde gebruik steeds sRGB om kleurverskuiwings te voorkom.",
|
||||||
"image_preview_description": "Mediumgrootte prent met gestroopte metadata, wat gebruik word wanneer 'n enkele bate bekyk word en vir masjienleer",
|
"image_preview_description": "Mediumgrootte prent met gestroopte metadata, wat gebruik word wanneer ’n enkele item bekyk word en vir masjienleer",
|
||||||
"image_preview_quality_description": "Voorskou kwaliteit van 1-100. Hoër is beter, maar produseer groter lêers en kan app-reaksie verminder. Die stel van 'n lae waarde kan masjienleerkwaliteit beïnvloed.",
|
"image_preview_quality_description": "Voorskoukwaliteit van 1-100. Hoër is beter, maar lewer groter lêers en kan die toep vertraag. Die stel van ’n lae waarde kan masjienleerkwaliteit beïnvloed.",
|
||||||
"image_preview_title": "Voorskou Instellings",
|
"image_preview_title": "Voorskou-instellings",
|
||||||
"image_quality": "Kwaliteit",
|
"image_quality": "Kwaliteit",
|
||||||
"image_resolution": "Resolusie",
|
"image_resolution": "Resolusie",
|
||||||
"image_resolution_description": "Hoër resolusies kan meer detail bewaar, maar neem langer om te enkodeer, het groter lêergroottes en kan app-reaksie verminder.",
|
"image_resolution_description": "Hoër resolusies kan meer detail bewaar, maar neem langer om te enkodeer, het groter lêergroottes en kan die toep vertraag.",
|
||||||
"image_settings": "Prent Instellings",
|
"image_settings": "Prentinstellings",
|
||||||
"image_settings_description": "Bestuur die kwaliteit en resolusie van gegenereerde beelde",
|
"image_settings_description": "Bestuur die kwaliteit en resolusie van gegenereerde beelde",
|
||||||
"image_thumbnail_description": "Klein kleinkiekies sonder metadata, gebruik om groepe foto's soos die tydlyn te bekyk",
|
"image_thumbnail_description": "Klein duimnaels sonder metadata, gebruik om groepe foto’s soos die tydlyn te bekyk",
|
||||||
"image_thumbnail_quality_description": "Kleinkiekiekwaliteit van 1-100. Hoër is beter, maar produseer groter lêers en kan die toepassing vertraag.",
|
"image_thumbnail_quality_description": "Duinmaelkwaliteit van 1-100. Hoër is beter, maar lewer groter lêers en kan die toep vertraag.",
|
||||||
"image_thumbnail_title": "Kleinkiekie-instellings",
|
"image_thumbnail_title": "Duimnaelinstellings",
|
||||||
"job_concurrency": "{job} gelyktydigheid",
|
"job_concurrency": "{job} gelyktydigheid",
|
||||||
"job_created": "Taak gemaak",
|
"job_created": "Taak geskep",
|
||||||
"job_not_concurrency_safe": "Hierdie taak kan nie gelyktydig uitgevoer word nie.",
|
"job_not_concurrency_safe": "Hierdie taak kan nie gelyktydig uitgevoer word nie.",
|
||||||
"job_settings": "Agtergrondtaakinstellings",
|
"job_settings": "Taakinstellings",
|
||||||
"job_settings_description": "Bestuur werkgelyktydigheid",
|
"job_settings_description": "Bestuur taakgelyktydigheid",
|
||||||
"library_created": "Biblioteek geskep: {library}",
|
"library_created": "Biblioteek geskep: {library}",
|
||||||
"library_deleted": "Biblioteek verwyder",
|
"library_deleted": "Biblioteek geskrap",
|
||||||
"library_scanning": "Periodieke Soek",
|
"library_scanning": "Periodieke skandering",
|
||||||
"library_scanning_description": "Stel periodieke deursoek van biblioteek in",
|
"library_scanning_description": "Stel periodieke skandering van biblioteek in",
|
||||||
"library_scanning_enable_description": "Aktiveer periodieke biblioteekskandering",
|
"library_scanning_enable_description": "Aktiveer periodieke biblioteekskandering",
|
||||||
"library_settings": "Eksterne Biblioteek",
|
"library_settings": "Eksterne biblioteek",
|
||||||
"library_settings_description": "Eksterne biblioteek verstellings",
|
"library_settings_description": "Eksternebiblioteekinstellings",
|
||||||
"library_tasks_description": "Deursoek eksterne biblioteke vir nuwe of veranderde bates",
|
"library_tasks_description": "Skandeer eksterne biblioteke vir nuwe of veranderde items",
|
||||||
"library_watching_enable_description": "Hou eksterne biblioteke dop vir leer veranderinge",
|
"library_watching_enable_description": "Hou eksterne biblioteke dop vir lêerveranderinge",
|
||||||
"library_watching_settings": "Biblioteek dop hou (EKSPERIMENTEEL)",
|
"library_watching_settings": "Biblioteekdophou [EKSPERIMENTEEL]",
|
||||||
"library_watching_settings_description": "Hou automaties dop vir veranderinge",
|
"library_watching_settings_description": "Hou automaties dop vir veranderinge",
|
||||||
"logging_enable_description": "Aktifeer \"logging\"",
|
"logging_enable_description": "Aktiveer logboekbyhouding",
|
||||||
"logging_level_description": "Wanneer aktief, watter vlak van \"logs\" om te skep.",
|
"logging_level_description": "Wanneer aktief, welke logboekvlak om te gebruik.",
|
||||||
"logging_settings": "\"Logs\"",
|
"logging_settings": "Logboek",
|
||||||
"machine_learning_clip_model": "CLIP model",
|
"machine_learning_clip_model": "CLIP-model",
|
||||||
"machine_learning_duplicate_detection": "Duplikaat herkenning",
|
"machine_learning_duplicate_detection": "Duplikaatbespeuring",
|
||||||
"machine_learning_duplicate_detection_enabled": "Aktifeer duplikaat herkenning",
|
"machine_learning_duplicate_detection_enabled": "Aktiveer duplikaatbespeuring",
|
||||||
"machine_learning_enabled": "Aktifeer masjienleer",
|
"machine_learning_enabled": "Aktiveer masjienleer",
|
||||||
"machine_learning_facial_recognition": "Gesigsherkenning",
|
"machine_learning_facial_recognition": "Gesigherkenning",
|
||||||
"machine_learning_facial_recognition_description": "Herken, identifiseer en groepeer gesigte in fotos",
|
"machine_learning_facial_recognition_description": "Bespeur, identifiseer en groepeer gesigte in foto’s",
|
||||||
"machine_learning_facial_recognition_model": "Gesigsherkennings model",
|
"machine_learning_facial_recognition_model": "Gesigherkenningsmodel",
|
||||||
"machine_learning_facial_recognition_setting": "Aktifeer gesigsherkenning",
|
"machine_learning_facial_recognition_setting": "Aktiveer gesigherkenning",
|
||||||
"machine_learning_max_detection_distance": "Maksimum herkennings afstand",
|
"machine_learning_max_detection_distance": "Maksimum herkenningsafstand",
|
||||||
"map_settings": "Kaart",
|
"map_settings": "Kaart",
|
||||||
"migration_job": "Migrasie",
|
"migration_job": "Migrasie",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"transcoding_acceleration_vaapi": "VAAPI",
|
"transcoding_acceleration_vaapi": "VAAPI",
|
||||||
"transcoding_preferred_hardware_device": "Verkiesde hardeware"
|
"transcoding_preferred_hardware_device": "Voorkeurapparatuur"
|
||||||
},
|
},
|
||||||
"administration": "Administrasie",
|
"administration": "Administrasie",
|
||||||
"advanced": "Gevorderde",
|
"advanced": "Gevorderd",
|
||||||
"albums": "Albums",
|
"albums": "Albums",
|
||||||
"all": "Alle",
|
"all": "Alle",
|
||||||
"anti_clockwise": "Anti-kloksgewys",
|
"anti_clockwise": "Linksom",
|
||||||
"archive": "Argief",
|
"archive": "Argief",
|
||||||
"asset_skipped": "Oorgeslaan",
|
"asset_skipped": "Oorgeslaan",
|
||||||
"asset_uploaded": "Opgelaai",
|
"asset_uploaded": "Opgelaai",
|
||||||
"asset_uploading": "Oplaai…",
|
"asset_uploading": "Laai tans op…",
|
||||||
"assets": "Bates",
|
"assets": "Items",
|
||||||
"back": "Terug",
|
"back": "Terug",
|
||||||
"backward": "Agteruit",
|
"backward": "Agteruit",
|
||||||
"build": "Bou",
|
"build": "Bou",
|
||||||
"camera": "Kamera",
|
"camera": "Kamera",
|
||||||
"cancel": "Kanselleer",
|
"cancel": "Kanselleer",
|
||||||
"city": "Stad",
|
"city": "Stad",
|
||||||
"clockwise": "Kloksgewys",
|
"clockwise": "Regsom",
|
||||||
"close": "Maak toe",
|
"close": "Sluit",
|
||||||
"color": "Kleur",
|
"color": "Kleur",
|
||||||
"confirm": "Bevestig",
|
"confirm": "Bevestig",
|
||||||
"contain": "Bevat",
|
"contain": "Bevat",
|
||||||
@@ -154,54 +154,153 @@
|
|||||||
"created": "Geskep",
|
"created": "Geskep",
|
||||||
"dark": "Donker",
|
"dark": "Donker",
|
||||||
"day": "Dag",
|
"day": "Dag",
|
||||||
"delete": "Verwyder",
|
"delete": "Skrap",
|
||||||
"description": "Beskrywing",
|
"description": "Beskrywing",
|
||||||
"details": "Besonderhede",
|
"details": "Besonderhede",
|
||||||
"direction": "Rigting",
|
"direction": "Rigting",
|
||||||
"discover": "Ontdek",
|
"discover": "Ontdek",
|
||||||
"documentation": "Dokumentasie",
|
"documentation": "Dokumentasie",
|
||||||
"done": "Klaar",
|
"done": "Gereed",
|
||||||
"download": "Aflaai",
|
"download": "Laai af",
|
||||||
"download_settings": "Aflaai",
|
"download_settings": "Laai af",
|
||||||
"duplicates": "Duplikate",
|
"duplicates": "Duplikate",
|
||||||
"duration": "Duur",
|
"duration": "Duur",
|
||||||
"edit": "Wysig",
|
"edit": "Wysig",
|
||||||
"search_by_description": "Soek by beskrywing",
|
"search_by_description": "Soek op beskrywing",
|
||||||
"search_by_description_example": "Stapdag in Sapa",
|
"search_by_description_example": "Stapdag in Sapa",
|
||||||
|
"stacktrace": "Stapelnasporing",
|
||||||
|
"start": "Begin",
|
||||||
|
"start_date": "Begindatum",
|
||||||
|
"start_date_before_end_date": "Begindatum moet voor einddatum wees",
|
||||||
|
"state": "Staat",
|
||||||
|
"status": "Status",
|
||||||
|
"stop_casting": "Stop sending",
|
||||||
|
"stop_motion_photo": "Stop bewegingsfoto",
|
||||||
|
"stop_photo_sharing": "Staak die deel van u foto’s?",
|
||||||
|
"stop_photo_sharing_description": "{partner} sal nie meer toegang tot u foto’s hê nie.",
|
||||||
|
"unnamed_share": "Naamlose deelskakel",
|
||||||
|
"unsaved_change": "Onbewaarde verandering",
|
||||||
|
"unselect_all": "Ontkies alles",
|
||||||
|
"unselect_all_duplicates": "Ontkies alle duplikate",
|
||||||
|
"unselect_all_in": "Ontkies alles in {group}",
|
||||||
|
"unstack": "Ontstapel",
|
||||||
|
"unstack_action_prompt": "{count} ongestapel",
|
||||||
|
"unstacked_assets_count": "{count, plural, one {# item} other {# items}} ontstapel",
|
||||||
|
"unsupported_field_type": "Onondersteunde veldtipe",
|
||||||
|
"unsupported_file_type": "Lêer {file} kan nie opgelaai word nie omdat die lêertipe {type} nie ondersteun word nie.",
|
||||||
|
"untagged": "Sonder etiket",
|
||||||
|
"untitled_workflow": "Naamlose werkvloei",
|
||||||
|
"up_next": "Volgende",
|
||||||
|
"update_location_action_prompt": "Werk die ligging van {count} gekose items by met:",
|
||||||
|
"updated_at": "Bygewerk",
|
||||||
|
"updated_password": "Wagwoord bygewerk",
|
||||||
|
"upload": "Laai op",
|
||||||
|
"upload_concurrency": "Aantal gelyktydige oplaaie",
|
||||||
|
"upload_details": "Oplaaidetails",
|
||||||
|
"upload_dialog_info": "Wil u ’n rugsteun maak van die gekose item(s) op die bediener?",
|
||||||
|
"upload_dialog_title": "Laai item op",
|
||||||
|
"upload_error_with_count": "Oplaaifout vir {count, plural, one {# item} other {# items}}",
|
||||||
|
"upload_errors": "Oplaai voltooi met {count, plural, one {# fout} other {# foute}}, verfris die blad om die nuwe items te sien.",
|
||||||
|
"upload_finished": "Klaar opgelaai",
|
||||||
|
"upload_progress": "Oorblywend {remaining, number} - Verwerk {processed, number}/{total, number}",
|
||||||
|
"upload_skipped_duplicates": "{count, plural, one {# duplikaat item} other {# duplikaat items}} oorgeslaan",
|
||||||
|
"upload_status_duplicates": "Duplikate",
|
||||||
|
"upload_status_errors": "Foute",
|
||||||
|
"upload_status_uploaded": "Opgelaai",
|
||||||
|
"upload_success": "Oplaai suksesvol, verfris die blad om nuut opgelaaide items te sien.",
|
||||||
|
"upload_to_immich": "Laai op na Immich ({count})",
|
||||||
|
"uploading": "Word opgelaai",
|
||||||
|
"uploading_media": "Media word opgelaai",
|
||||||
|
"url": "URL",
|
||||||
|
"usage": "Gebruik",
|
||||||
|
"use_biometric": "Gebruik biometrie",
|
||||||
|
"use_browser_locale": "Gebruik blaaier se landinstelling",
|
||||||
|
"use_browser_locale_description": "Formatteer datums, tye en getalle gebaseer op u blaaier se landinstelling",
|
||||||
|
"use_current_connection": "Gebruik huidige verbinding",
|
||||||
|
"use_custom_date_range": "Gebruik eerder pasgemaakte datumomvang",
|
||||||
|
"user": "Gebruiker",
|
||||||
|
"user_has_been_deleted": "Hierdie gebruiker is geskrap.",
|
||||||
|
"user_id": "Gebruiker ID",
|
||||||
|
"user_liked": "{user} het van {type, select, photo {hierdie foto} video {hierdie video} asset {} other {hierdie item}} gehou",
|
||||||
|
"user_pin_code_settings": "PIN-kode",
|
||||||
|
"user_pin_code_settings_description": "Bestuur u PIN-kode",
|
||||||
|
"user_privacy": "Gebruikersprivaatheid",
|
||||||
|
"user_purchase_settings": "Koop",
|
||||||
|
"user_purchase_settings_description": "Bestuur u aankoop",
|
||||||
|
"user_role_set": "Stel {user} in as {role}",
|
||||||
|
"user_usage_detail": "Gedetailleerde gebruik van gebruikers",
|
||||||
|
"user_usage_stats": "Statistieke vir rekeninggebruik",
|
||||||
|
"user_usage_stats_description": "Bekyk statistieke van rekeninggebruik",
|
||||||
|
"username": "Gebruikersnaam",
|
||||||
|
"users": "Gebruikers",
|
||||||
|
"users_added_to_album_count": "{count, plural, one {# Gebruiker} other {# Gebruikers}} tot album toegevoeg",
|
||||||
|
"utilities": "Gereedskap",
|
||||||
|
"validate": "Valideer",
|
||||||
|
"validate_endpoint_error": "Voer asb. ’n geldige bronadres in",
|
||||||
|
"validation_error": "Valideerfout",
|
||||||
|
"variables": "Veranderlikes",
|
||||||
"version": "Weergawe",
|
"version": "Weergawe",
|
||||||
"version_announcement_closing": "Jou friend, Alex",
|
"version_announcement_closing": "Jou friend, Alex",
|
||||||
|
"version_announcement_message": "Hallo! Daar is ’n nuwe weergawe van Immich beskikbaar. Neem gerus bietjie tyd om die <link>vrystellingsnotas</link> te lees en maak seker u opstelling is op datum om wanopstellings te voorkom, veral as u WatchTower of ’n ander bywerkmeganisme gebruik.",
|
||||||
"version_history": "Weergawegeskiedenis",
|
"version_history": "Weergawegeskiedenis",
|
||||||
"version_history_item": "{version} geinstaleerd op {date}",
|
"version_history_item": "{version} geïnstaleer op {date}",
|
||||||
"video": "Video",
|
"video": "Video",
|
||||||
"videos": "Video's",
|
"video_hover_setting": "Speel videoduimnael by muishang",
|
||||||
|
"video_hover_setting_description": "Speel videoduimnael wanneer muis oor item hang. Selfs indien gedeaktiveer kan afspeel begin deur oor die afspeelknop te hang.",
|
||||||
|
"videos": "Video’s",
|
||||||
|
"videos_count": "{count, plural, one {# video} other {# video’s}}",
|
||||||
|
"videos_only": "Slegs video’s",
|
||||||
"view": "Bekyk",
|
"view": "Bekyk",
|
||||||
"view_album": "Bekyk Album",
|
"view_album": "Bekyk album",
|
||||||
"view_all": "Bekyk alle",
|
"view_all": "Bekyk alle",
|
||||||
"view_all_users": "Bekyk alle gebruikers",
|
"view_all_users": "Bekyk alle gebruikers",
|
||||||
|
"view_asset_owners": "Bekyk itemeienaars",
|
||||||
|
"view_details": "Bekyk detail",
|
||||||
"view_in_timeline": "Bekyk in tydlyn",
|
"view_in_timeline": "Bekyk in tydlyn",
|
||||||
"view_link": "Bekyk skakel",
|
"view_link": "Bekyk skakel",
|
||||||
"view_links": "Bekyk skakels",
|
"view_links": "Bekyk skakels",
|
||||||
"view_name": "Bekyk",
|
"view_name": "Bekyk",
|
||||||
"view_next_asset": "Bekyk volgende bate",
|
"view_next_asset": "Bekyk volgende item",
|
||||||
"view_previous_asset": "Bekyk vorige bate",
|
"view_previous_asset": "Bekyk vorige item",
|
||||||
"view_qr_code": "Bekyk QR-kode",
|
"view_qr_code": "Bekyk QR-kode",
|
||||||
|
"view_similar_photos": "Bekyk soortgelyke foto’s",
|
||||||
"view_stack": "Bekyk stapel",
|
"view_stack": "Bekyk stapel",
|
||||||
"view_user": "Bekyk gebruiker",
|
"view_user": "Bekyk gebruiker",
|
||||||
"viewer_remove_from_stack": "Verwyder van stapel",
|
"viewer_remove_from_stack": "Verwyder van stapel",
|
||||||
"viewer_stack_use_as_main_asset": "Gebruik as hoofbate",
|
"viewer_stack_use_as_main_asset": "Gebruik as hoofitem",
|
||||||
"viewer_unstack": "Ontstapel",
|
"viewer_unstack": "Ontstapel",
|
||||||
"visibility_changed": "Sigbaarheid verander voor {count, plural, one {# person} other {# people}}",
|
"visibility": "Sigbaarheid",
|
||||||
|
"visibility_changed": "Sigbaarheid verander vir {count, plural, one {# mens} other {# mense}}",
|
||||||
|
"visual": "Visueel",
|
||||||
|
"visual_builder": "Visuele bouer",
|
||||||
"waiting": "Wag",
|
"waiting": "Wag",
|
||||||
"warning": "Waaskuwing",
|
"waiting_count": "Wagtend: {count}",
|
||||||
|
"warning": "Waarskuwing",
|
||||||
"week": "Week",
|
"week": "Week",
|
||||||
"welcome": "Welkom",
|
"welcome": "Welkom",
|
||||||
"welcome_to_immich": "Welkom by Immich",
|
"welcome_to_immich": "Welkom by Immich",
|
||||||
"wifi_name": "Wi-Fi Naam",
|
"width": "Breedte",
|
||||||
|
"wifi_name": "Wi-Fi-naam",
|
||||||
|
"workflow_delete_prompt": "Is u seker u wil hierdie werkvloei skrap?",
|
||||||
|
"workflow_deleted": "Werkvloei geskrap",
|
||||||
|
"workflow_description": "Werkvloeibeskrywing",
|
||||||
|
"workflow_info": "Werkvloei-inligting",
|
||||||
|
"workflow_json": "Werkvloei-JSON",
|
||||||
|
"workflow_json_help": "Wysig die werkvloei-opstelling in JSON-formaat. Veranderinge sal na die visuele bouer sinchroniseer.",
|
||||||
|
"workflow_name": "Werkvloeinaam",
|
||||||
|
"workflow_navigation_prompt": "Is u seker u wil verlaat sonder om u veranderinge te bewaar?",
|
||||||
|
"workflow_summary": "Werkvloei-opsomming",
|
||||||
|
"workflow_update_success": "Werkvloei suksesvol bygewerk",
|
||||||
|
"workflow_updated": "Werkvloei bygewerk",
|
||||||
|
"workflows": "Werkvloeie",
|
||||||
|
"workflows_help_text": "Werkvloeie outomatiseer aksies op u items gebaseer op snellers en filters",
|
||||||
"wrong_pin_code": "Verkeerde PIN-kode",
|
"wrong_pin_code": "Verkeerde PIN-kode",
|
||||||
"year": "Jaar",
|
"year": "Jaar",
|
||||||
"years_ago": "{years, plural, one {# year} other {# years}} gelede",
|
"years_ago": "{years, plural, one {# jaar} other {# jaar}} gelede",
|
||||||
"yes": "Ja",
|
"yes": "Ja",
|
||||||
"you_dont_have_any_shared_links": "Jy het geen gedeelde skakels",
|
"you_dont_have_any_shared_links": "U het geen gedeelde skakels nie",
|
||||||
"your_wifi_name": "Jou Wi-Fi naam",
|
"your_wifi_name": "U Wi-Fi-naam",
|
||||||
"zoom_image": "Vergroot Prent"
|
"zero_to_clear_rating": "druk 0 om itemgradering te wis",
|
||||||
|
"zoom_image": "Zoem in",
|
||||||
|
"zoom_to_bounds": "Zoem na rande"
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-15
@@ -311,7 +311,7 @@
|
|||||||
"search_jobs": "البحث عن وظائف…",
|
"search_jobs": "البحث عن وظائف…",
|
||||||
"send_welcome_email": "إرسال بريد ترحيبي",
|
"send_welcome_email": "إرسال بريد ترحيبي",
|
||||||
"server_external_domain_settings": "إسم النطاق الخارجي",
|
"server_external_domain_settings": "إسم النطاق الخارجي",
|
||||||
"server_external_domain_settings_description": "إسم النطاق لروابط المشاركة العامة، بما في ذلك http(s)://",
|
"server_external_domain_settings_description": "النطاق مستخدم لروابط خارجية",
|
||||||
"server_public_users": "المستخدمون العامون",
|
"server_public_users": "المستخدمون العامون",
|
||||||
"server_public_users_description": "يتم إدراج جميع المستخدمين (الاسم والبريد الإلكتروني) عند إضافة مستخدم إلى الألبومات المشتركة. عند تعطيل هذه الميزة، ستكون قائمة المستخدمين متاحة فقط لمستخدمي الإدارة.",
|
"server_public_users_description": "يتم إدراج جميع المستخدمين (الاسم والبريد الإلكتروني) عند إضافة مستخدم إلى الألبومات المشتركة. عند تعطيل هذه الميزة، ستكون قائمة المستخدمين متاحة فقط لمستخدمي الإدارة.",
|
||||||
"server_settings": "إعدادات الخادم",
|
"server_settings": "إعدادات الخادم",
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
"transcoding_tone_mapping": "رسم الخرائط النغمية",
|
"transcoding_tone_mapping": "رسم الخرائط النغمية",
|
||||||
"transcoding_tone_mapping_description": "تحاول الحفاظ على مظهر مقاطع الفيديو HDR عند تحويلها إلى SDR. يقدم كل خوارزمية تنازلات مختلفة بين اللون والتفاصيل والسطوع. Hable تحافظ على التفاصيل، Mobius تحافظ على الألوان، و Reinhard تحافظ على السطوع.",
|
"transcoding_tone_mapping_description": "تحاول الحفاظ على مظهر مقاطع الفيديو HDR عند تحويلها إلى SDR. يقدم كل خوارزمية تنازلات مختلفة بين اللون والتفاصيل والسطوع. Hable تحافظ على التفاصيل، Mobius تحافظ على الألوان، و Reinhard تحافظ على السطوع.",
|
||||||
"transcoding_transcode_policy": "سياسة الترميز",
|
"transcoding_transcode_policy": "سياسة الترميز",
|
||||||
"transcoding_transcode_policy_description": "سياسة تحديد متى يجب ترميز الفيديو. سيتم دائمًا ترميز مقاطع الفيديو HDR (ما لم يتم تعطيل الترميز).",
|
"transcoding_transcode_policy_description": "سياسة تحديد متى يجب ترميز الفيديو. سيتم دائمًا ترميز مقاطع الفيديو HDR و مقاطع الفديو اللتي تستدخم تنسيق غير YUV 4:2:0. (ما لم يتم تعطيل الترميز).",
|
||||||
"transcoding_two_pass_encoding": "الترميز بمرورين",
|
"transcoding_two_pass_encoding": "الترميز بمرورين",
|
||||||
"transcoding_two_pass_encoding_setting_description": "ترميز بمرورين لإنتاج مقاطع فيديو بترميز أفضل. عند تمكين الحد الأقصى لمعدل البت (مطلوب لكي يعمل مع H.264 و HEVC)، يستخدم هذا الوضع نطاق معدل البت استنادًا إلى الحد الأقصى لمعدل البت ويتجاهل CRF. بالنسبة لـ VP9، يمكن استخدام CRF إذا تم تعطيل الحد الأقصى لمعدل البت.",
|
"transcoding_two_pass_encoding_setting_description": "ترميز بمرورين لإنتاج مقاطع فيديو بترميز أفضل. عند تمكين الحد الأقصى لمعدل البت (مطلوب لكي يعمل مع H.264 و HEVC)، يستخدم هذا الوضع نطاق معدل البت استنادًا إلى الحد الأقصى لمعدل البت ويتجاهل CRF. بالنسبة لـ VP9، يمكن استخدام CRF إذا تم تعطيل الحد الأقصى لمعدل البت.",
|
||||||
"transcoding_video_codec": "ترميز الفيديو",
|
"transcoding_video_codec": "ترميز الفيديو",
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
"user_successfully_removed": "المستخدم {email} تمت ازالته بنجاح.",
|
"user_successfully_removed": "المستخدم {email} تمت ازالته بنجاح.",
|
||||||
"users_page_description": "صفحة ادارة المستخدمين",
|
"users_page_description": "صفحة ادارة المستخدمين",
|
||||||
"version_check_enabled_description": "تفعيل التحقق من الإصدارات الجديدة",
|
"version_check_enabled_description": "تفعيل التحقق من الإصدارات الجديدة",
|
||||||
"version_check_implications": "تعتمد ميزة التحقق من الإصدار على التواصل الدوري مع github.com",
|
"version_check_implications": "تعتمد ميزة التحقق من الإصدار على التواصل الدوري مع {server}",
|
||||||
"version_check_settings": "التحقق من الإصدار",
|
"version_check_settings": "التحقق من الإصدار",
|
||||||
"version_check_settings_description": "تفعيل/تعطيل الإشعار لإصدار جديد",
|
"version_check_settings_description": "تفعيل/تعطيل الإشعار لإصدار جديد",
|
||||||
"video_conversion_job": "تحويل أشرطة الفيديو",
|
"video_conversion_job": "تحويل أشرطة الفيديو",
|
||||||
@@ -794,6 +794,11 @@
|
|||||||
"color": "اللون",
|
"color": "اللون",
|
||||||
"color_theme": "نمط الألوان",
|
"color_theme": "نمط الألوان",
|
||||||
"command": "امر",
|
"command": "امر",
|
||||||
|
"command_palette_prompt": "اعثر بسرعة على الصفحات أو الإجراءات أو الأوامر",
|
||||||
|
"command_palette_to_close": "للاغلاق",
|
||||||
|
"command_palette_to_navigate": "للدخول",
|
||||||
|
"command_palette_to_select": "للاختيار",
|
||||||
|
"command_palette_to_show_all": "لعرض الكل",
|
||||||
"comment_deleted": "تم حذف التعليق",
|
"comment_deleted": "تم حذف التعليق",
|
||||||
"comment_options": "خيارات التعليق",
|
"comment_options": "خيارات التعليق",
|
||||||
"comments_and_likes": "التعليقات والإعجابات",
|
"comments_and_likes": "التعليقات والإعجابات",
|
||||||
@@ -861,13 +866,14 @@
|
|||||||
"crop_aspect_ratio_fixed": "تم الاصلاح",
|
"crop_aspect_ratio_fixed": "تم الاصلاح",
|
||||||
"crop_aspect_ratio_free": "حر",
|
"crop_aspect_ratio_free": "حر",
|
||||||
"crop_aspect_ratio_original": "اصلي",
|
"crop_aspect_ratio_original": "اصلي",
|
||||||
|
"crop_aspect_ratio_square": "مربع",
|
||||||
"curated_object_page_title": "أشياء",
|
"curated_object_page_title": "أشياء",
|
||||||
"current_device": "الجهاز الحالي",
|
"current_device": "الجهاز الحالي",
|
||||||
"current_pin_code": "رمز PIN الحالي",
|
"current_pin_code": "رمز PIN الحالي",
|
||||||
"current_server_address": "عنوان الخادم الحالي",
|
"current_server_address": "عنوان الخادم الحالي",
|
||||||
"custom_date": "تاريخ مخصص",
|
"custom_date": "تاريخ مخصص",
|
||||||
"custom_locale": "لغة مخصصة",
|
"custom_locale": "لغة مخصصة",
|
||||||
"custom_locale_description": "تنسيق التواريخ والأرقام بناءً على اللغة والمنطقة",
|
"custom_locale_description": "تنسيق التواريخ, الأوقات والأرقام بناءً على اللغة والمنطقة المختاره",
|
||||||
"custom_url": "رابط مخصص",
|
"custom_url": "رابط مخصص",
|
||||||
"cutoff_date_description": "احتفظ بالصور من آخر…",
|
"cutoff_date_description": "احتفظ بالصور من آخر…",
|
||||||
"cutoff_day": "{count, plural, one {يوم} other {ايام}}",
|
"cutoff_day": "{count, plural, one {يوم} other {ايام}}",
|
||||||
@@ -875,7 +881,7 @@
|
|||||||
"daily_title_text_date": "E ، MMM DD",
|
"daily_title_text_date": "E ، MMM DD",
|
||||||
"daily_title_text_date_year": "E ، MMM DD ، yyyy",
|
"daily_title_text_date_year": "E ، MMM DD ، yyyy",
|
||||||
"dark": "معتم",
|
"dark": "معتم",
|
||||||
"dark_theme": "تبديل المظهر الداكن",
|
"dark_theme": "تبديل المظهر إلى الداكن",
|
||||||
"date": "تاريخ",
|
"date": "تاريخ",
|
||||||
"date_after": "التارخ بعد",
|
"date_after": "التارخ بعد",
|
||||||
"date_and_time": "التاريخ و الوقت",
|
"date_and_time": "التاريخ و الوقت",
|
||||||
@@ -886,12 +892,6 @@
|
|||||||
"day": "يوم",
|
"day": "يوم",
|
||||||
"days": "ايام",
|
"days": "ايام",
|
||||||
"deduplicate_all": "إلغاء تكرار الكل",
|
"deduplicate_all": "إلغاء تكرار الكل",
|
||||||
"deduplication_criteria_1": "حجم الصورة بوحدات البايت",
|
|
||||||
"deduplication_criteria_2": "عدد بيانات EXIF",
|
|
||||||
"deduplication_info": "معلومات إلغاء البيانات المكررة",
|
|
||||||
"deduplication_info_description": "لتحديد الأصول مسبقا تلقائيا وإزالة التكرارات بكميات كبيرة، ننظر إلى:",
|
|
||||||
"default_locale": "اللغة الافتراضية",
|
|
||||||
"default_locale_description": "تنسيق التواريخ والأرقام بناءً على لغة المتصفح الخاص بك",
|
|
||||||
"delete": "حذف",
|
"delete": "حذف",
|
||||||
"delete_action_confirmation_message": "هل انت متأكد من حذف هذا الملف؟ هذا سؤدي الى نقل الملف الى سلة مهملات الخادم وسيتم اشعارك ان كنت تريد حذفه على الجهاز",
|
"delete_action_confirmation_message": "هل انت متأكد من حذف هذا الملف؟ هذا سؤدي الى نقل الملف الى سلة مهملات الخادم وسيتم اشعارك ان كنت تريد حذفه على الجهاز",
|
||||||
"delete_action_prompt": "تم حذف {count}",
|
"delete_action_prompt": "تم حذف {count}",
|
||||||
@@ -967,7 +967,7 @@
|
|||||||
"downloading_media": "تنزيل الوسائط",
|
"downloading_media": "تنزيل الوسائط",
|
||||||
"drop_files_to_upload": "قم بإسقاط الملفات في أي مكان لرفعها",
|
"drop_files_to_upload": "قم بإسقاط الملفات في أي مكان لرفعها",
|
||||||
"duplicates": "التكرارات",
|
"duplicates": "التكرارات",
|
||||||
"duplicates_description": "قم بحل كل مجموعة من خلال الإشارة إلى التكرارات، إن وجدت",
|
"duplicates_description": "قم بحل كل مجموعة من خلال الإشارة إلى التكرارات، إن وجدت.",
|
||||||
"duration": "المدة",
|
"duration": "المدة",
|
||||||
"edit": "تعديل",
|
"edit": "تعديل",
|
||||||
"edit_album": "تعديل الألبوم",
|
"edit_album": "تعديل الألبوم",
|
||||||
@@ -1004,6 +1004,8 @@
|
|||||||
"editor_edits_applied_success": "تم تطبيق التعديلات بنجاح",
|
"editor_edits_applied_success": "تم تطبيق التعديلات بنجاح",
|
||||||
"editor_flip_horizontal": "اقلب أفقيًا",
|
"editor_flip_horizontal": "اقلب أفقيًا",
|
||||||
"editor_flip_vertical": "اقلب عموديًا",
|
"editor_flip_vertical": "اقلب عموديًا",
|
||||||
|
"editor_handle_corner": "{corner, select, top_left {أعلى اليسار} top_right {أعلى اليمين} bottom_left {أسفل اليسار} bottom_right {أسفل اليمين} other {أخري}} corner handle",
|
||||||
|
"editor_handle_edge": "{edge, select, top {أعلي} bottom {أسفل} left {يسار} right {يمين} other {أخري}} edge handle",
|
||||||
"editor_orientation": "اتجاه",
|
"editor_orientation": "اتجاه",
|
||||||
"editor_reset_all_changes": "اعادة ظبط التغييرات",
|
"editor_reset_all_changes": "اعادة ظبط التغييرات",
|
||||||
"editor_rotate_left": "أدر 90° عكس اتجاه عقارب الساعة",
|
"editor_rotate_left": "أدر 90° عكس اتجاه عقارب الساعة",
|
||||||
@@ -1069,6 +1071,7 @@
|
|||||||
"failed_to_update_notification_status": "فشل في تحديث حالة الإشعار",
|
"failed_to_update_notification_status": "فشل في تحديث حالة الإشعار",
|
||||||
"incorrect_email_or_password": "بريد أو كلمة مرور غير صحيحة",
|
"incorrect_email_or_password": "بريد أو كلمة مرور غير صحيحة",
|
||||||
"library_folder_already_exists": "مسار الاستيراد موجود بالفعل.",
|
"library_folder_already_exists": "مسار الاستيراد موجود بالفعل.",
|
||||||
|
"page_not_found": "الصفحة غير موجودة",
|
||||||
"paths_validation_failed": "فشل في التحقق من {paths, plural, one {# مسار} other {# مسارات}}",
|
"paths_validation_failed": "فشل في التحقق من {paths, plural, one {# مسار} other {# مسارات}}",
|
||||||
"profile_picture_transparent_pixels": "لا يمكن أن تحتوي صور الملف الشخصي على أجزاء/بكسلات شفافة. يرجى التكبير و/أو تحريك الصورة.",
|
"profile_picture_transparent_pixels": "لا يمكن أن تحتوي صور الملف الشخصي على أجزاء/بكسلات شفافة. يرجى التكبير و/أو تحريك الصورة.",
|
||||||
"quota_higher_than_disk_size": "لقد قمت بتعيين حصة نسبية أعلى من حجم القرص",
|
"quota_higher_than_disk_size": "لقد قمت بتعيين حصة نسبية أعلى من حجم القرص",
|
||||||
@@ -1168,6 +1171,7 @@
|
|||||||
"exif_bottom_sheet_people": "الناس",
|
"exif_bottom_sheet_people": "الناس",
|
||||||
"exif_bottom_sheet_person_add_person": "اضف اسما",
|
"exif_bottom_sheet_person_add_person": "اضف اسما",
|
||||||
"exit_slideshow": "خروج من العرض التقديمي",
|
"exit_slideshow": "خروج من العرض التقديمي",
|
||||||
|
"expand": "توسعة",
|
||||||
"expand_all": "توسيع الكل",
|
"expand_all": "توسيع الكل",
|
||||||
"experimental_settings_new_asset_list_subtitle": "أعمال جارية",
|
"experimental_settings_new_asset_list_subtitle": "أعمال جارية",
|
||||||
"experimental_settings_new_asset_list_title": "تمكين شبكة الصور التجريبية",
|
"experimental_settings_new_asset_list_title": "تمكين شبكة الصور التجريبية",
|
||||||
@@ -1212,6 +1216,7 @@
|
|||||||
"filter_description": "شروط تصفية الأصول المستهدفة",
|
"filter_description": "شروط تصفية الأصول المستهدفة",
|
||||||
"filter_people": "تصفية الاشخاص",
|
"filter_people": "تصفية الاشخاص",
|
||||||
"filter_places": "تصفية الاماكن",
|
"filter_places": "تصفية الاماكن",
|
||||||
|
"filter_tags": "تصفية العلامات",
|
||||||
"filters": "التصفيات",
|
"filters": "التصفيات",
|
||||||
"find_them_fast": "يمكنك العثور عليها بسرعة بالاسم من خلال البحث",
|
"find_them_fast": "يمكنك العثور عليها بسرعة بالاسم من خلال البحث",
|
||||||
"first": "الاول",
|
"first": "الاول",
|
||||||
@@ -1642,6 +1647,8 @@
|
|||||||
"online": "متصل",
|
"online": "متصل",
|
||||||
"only_favorites": "المفضلة فقط",
|
"only_favorites": "المفضلة فقط",
|
||||||
"open": "فتح",
|
"open": "فتح",
|
||||||
|
"open_calendar": "افتح الرزنامة",
|
||||||
|
"open_in_browser": "فتح في متصفح",
|
||||||
"open_in_map_view": "فتح في عرض الخريطة",
|
"open_in_map_view": "فتح في عرض الخريطة",
|
||||||
"open_in_openstreetmap": "فتح في OpenStreetMap",
|
"open_in_openstreetmap": "فتح في OpenStreetMap",
|
||||||
"open_the_search_filters": "افتح مرشحات البحث",
|
"open_the_search_filters": "افتح مرشحات البحث",
|
||||||
@@ -1801,9 +1808,8 @@
|
|||||||
"rate_asset": "تقييم الاصل",
|
"rate_asset": "تقييم الاصل",
|
||||||
"rating": "تقييم نجمي",
|
"rating": "تقييم نجمي",
|
||||||
"rating_clear": "مسح التقييم",
|
"rating_clear": "مسح التقييم",
|
||||||
"rating_count": "{count, plural, one {# نجمة} other {# نجوم}}",
|
"rating_count": "{count, plural, =0 {Unrated} one {# نجمة} other {# نجوم}}",
|
||||||
"rating_description": "اعرض تقييم EXIF في لوحة المعلومات",
|
"rating_description": "اعرض تقييم EXIF في لوحة المعلومات",
|
||||||
"rating_set": "تم تحديد التصنيف {rating, plural, one {# نجمة} other {# نجوم}}",
|
|
||||||
"reaction_options": "خيارات رد الفعل",
|
"reaction_options": "خيارات رد الفعل",
|
||||||
"read_changelog": "قراءة سجل التغيير",
|
"read_changelog": "قراءة سجل التغيير",
|
||||||
"readonly_mode_disabled": "تم تعطيل وضع القراءة فقط",
|
"readonly_mode_disabled": "تم تعطيل وضع القراءة فقط",
|
||||||
@@ -1875,7 +1881,10 @@
|
|||||||
"reset_pin_code_success": "تم اعادة تعيين رمز الPIN بنجاح",
|
"reset_pin_code_success": "تم اعادة تعيين رمز الPIN بنجاح",
|
||||||
"reset_pin_code_with_password": "يمكنك دائما اعادة تعيين رمز الPIN الخاص بك عن طريق كلمة المرور الخاصة بك",
|
"reset_pin_code_with_password": "يمكنك دائما اعادة تعيين رمز الPIN الخاص بك عن طريق كلمة المرور الخاصة بك",
|
||||||
"reset_sqlite": "إعادة تعيين قاعدة بيانات SQLite",
|
"reset_sqlite": "إعادة تعيين قاعدة بيانات SQLite",
|
||||||
"reset_sqlite_confirmation": "هل أنت متأكد من رغبتك في إعادة ضبط قاعدة بيانات SQLite؟ ستحتاج إلى تسجيل الخروج ثم تسجيل الدخول مرة أخرى لإعادة مزامنة البيانات",
|
"reset_sqlite_clear_app_data": "مسح البيانات",
|
||||||
|
"reset_sqlite_confirmation": "هل أنت متأكد من رغبتك في حذف ضبط بيانات التطبيق؟ سيؤدي هذا إلى إزالة جميع الإعدادات وتسجيل خروجك.",
|
||||||
|
"reset_sqlite_confirmation_note": "ملاحظة: سيتعين عليك إعادة تشغيل التطبيق بعد المسح.",
|
||||||
|
"reset_sqlite_done": "تم مسح بيانات التطبيق. يرجى إعادة تشغيل تطبيق Immich وتسجيل الدخول مرة أخرى.",
|
||||||
"reset_sqlite_success": "تم إعادة تعيين قاعدة بيانات SQLite بنجاح",
|
"reset_sqlite_success": "تم إعادة تعيين قاعدة بيانات SQLite بنجاح",
|
||||||
"reset_to_default": "إعادة التعيين إلى الافتراضي",
|
"reset_to_default": "إعادة التعيين إلى الافتراضي",
|
||||||
"resolution": "دقة",
|
"resolution": "دقة",
|
||||||
@@ -1903,6 +1912,7 @@
|
|||||||
"saved_settings": "تم حفظ الإعدادات",
|
"saved_settings": "تم حفظ الإعدادات",
|
||||||
"say_something": "قل شيئًا",
|
"say_something": "قل شيئًا",
|
||||||
"scaffold_body_error_occurred": "حدث خطأ",
|
"scaffold_body_error_occurred": "حدث خطأ",
|
||||||
|
"scaffold_body_error_unrecoverable": "حدث خطأ لا يمكن إصلاحه. يرجى مشاركة تفاصيل الخطأ وتسلسل الأخطاء على Discord أو GitHub حتى نتمكن من مساعدتك. إذا طُلب منك ذلك، يمكنك مسح بيانات التطبيق أدناه.",
|
||||||
"scan": "بحث",
|
"scan": "بحث",
|
||||||
"scan_all_libraries": "فحص كل المكتبات",
|
"scan_all_libraries": "فحص كل المكتبات",
|
||||||
"scan_library": "مسح",
|
"scan_library": "مسح",
|
||||||
@@ -1938,6 +1948,7 @@
|
|||||||
"search_filter_ocr": "البحث عن طريق التعرف البصري على الحروف",
|
"search_filter_ocr": "البحث عن طريق التعرف البصري على الحروف",
|
||||||
"search_filter_people_title": "اختر الاشخاص",
|
"search_filter_people_title": "اختر الاشخاص",
|
||||||
"search_filter_star_rating": "تقييم النجوم",
|
"search_filter_star_rating": "تقييم النجوم",
|
||||||
|
"search_filter_tags_title": "تحديد العلامات",
|
||||||
"search_for": "البحث عن",
|
"search_for": "البحث عن",
|
||||||
"search_for_existing_person": "البحث عن شخص موجود",
|
"search_for_existing_person": "البحث عن شخص موجود",
|
||||||
"search_no_more_result": "لا توجد نتائج اضافية",
|
"search_no_more_result": "لا توجد نتائج اضافية",
|
||||||
@@ -2017,6 +2028,9 @@
|
|||||||
"set_profile_picture": "تحديد صورة الملف الشخصي",
|
"set_profile_picture": "تحديد صورة الملف الشخصي",
|
||||||
"set_slideshow_to_fullscreen": "تحديد عرض الشرائح على وضع ملء الشاشة",
|
"set_slideshow_to_fullscreen": "تحديد عرض الشرائح على وضع ملء الشاشة",
|
||||||
"set_stack_primary_asset": "تعيين كأصل اساسي",
|
"set_stack_primary_asset": "تعيين كأصل اساسي",
|
||||||
|
"setting_image_navigation_enable_subtitle": "في حال تم التفعيل، يمكنك الانتقال إلى الصورة السابقة أو التالية عن طريق النقر على الربع الأيسر أو الربع الأيمن من الشاشة.",
|
||||||
|
"setting_image_navigation_enable_title": "النقر للتنقل",
|
||||||
|
"setting_image_navigation_title": "التنقل بين الصور",
|
||||||
"setting_image_viewer_help": "يقوم عارض التفاصيل بتحميل الصورة المصغرة الصغيرة أولاً ، ثم يقوم بتحميل المعاينة متوسطة الحجم (إذا تم تمكينها) ، ويقوم أخيرًا بتحميل الأصل (إذا تم تمكينه).",
|
"setting_image_viewer_help": "يقوم عارض التفاصيل بتحميل الصورة المصغرة الصغيرة أولاً ، ثم يقوم بتحميل المعاينة متوسطة الحجم (إذا تم تمكينها) ، ويقوم أخيرًا بتحميل الأصل (إذا تم تمكينه).",
|
||||||
"setting_image_viewer_original_subtitle": "تمكين تحميل الصورة الكاملة الدقة الأصلية (كبيرة!).تعطيل لتقليل استخدام البيانات (كل من الشبكة وعلى ذاكرة التخزين المؤقت للجهاز).",
|
"setting_image_viewer_original_subtitle": "تمكين تحميل الصورة الكاملة الدقة الأصلية (كبيرة!).تعطيل لتقليل استخدام البيانات (كل من الشبكة وعلى ذاكرة التخزين المؤقت للجهاز).",
|
||||||
"setting_image_viewer_original_title": "تحميل الصورة الأصلية",
|
"setting_image_viewer_original_title": "تحميل الصورة الأصلية",
|
||||||
@@ -2183,6 +2197,7 @@
|
|||||||
"support": "الدعم",
|
"support": "الدعم",
|
||||||
"support_and_feedback": "الدعم والتعليقات",
|
"support_and_feedback": "الدعم والتعليقات",
|
||||||
"support_third_party_description": "تم حزم تثبيت immich الخاص بك بواسطة جهة خارجية. قد تكون المشكلات التي تواجهها ناجمة عن هذه الحزمة، لذا يرجى طرح المشكلات معهم في المقام الأول باستخدام الروابط أدناه.",
|
"support_third_party_description": "تم حزم تثبيت immich الخاص بك بواسطة جهة خارجية. قد تكون المشكلات التي تواجهها ناجمة عن هذه الحزمة، لذا يرجى طرح المشكلات معهم في المقام الأول باستخدام الروابط أدناه.",
|
||||||
|
"supporter": "داعم",
|
||||||
"swap_merge_direction": "تبديل اتجاه الدمج",
|
"swap_merge_direction": "تبديل اتجاه الدمج",
|
||||||
"sync": "مزامنة",
|
"sync": "مزامنة",
|
||||||
"sync_albums": "مزامنة الالبومات",
|
"sync_albums": "مزامنة الالبومات",
|
||||||
@@ -2294,6 +2309,7 @@
|
|||||||
"unstack_action_prompt": "تم ازالة تكديس {count}",
|
"unstack_action_prompt": "تم ازالة تكديس {count}",
|
||||||
"unstacked_assets_count": "تم إخراج {count, plural, one {# الأصل} other {# الأصول}} من التكديس",
|
"unstacked_assets_count": "تم إخراج {count, plural, one {# الأصل} other {# الأصول}} من التكديس",
|
||||||
"unsupported_field_type": "نوع حقل غير مدعوم",
|
"unsupported_field_type": "نوع حقل غير مدعوم",
|
||||||
|
"unsupported_file_type": "لا يمكن رفع الملف {file} لأن نوع الملف {type} غير مدعوم.",
|
||||||
"untagged": "غير مُعَلَّم",
|
"untagged": "غير مُعَلَّم",
|
||||||
"untitled_workflow": "خطة سير عمل بدون عنوان",
|
"untitled_workflow": "خطة سير عمل بدون عنوان",
|
||||||
"up_next": "التالي",
|
"up_next": "التالي",
|
||||||
@@ -2320,6 +2336,8 @@
|
|||||||
"url": "عنوان URL",
|
"url": "عنوان URL",
|
||||||
"usage": "الاستخدام",
|
"usage": "الاستخدام",
|
||||||
"use_biometric": "استخدم البايومتري",
|
"use_biometric": "استخدم البايومتري",
|
||||||
|
"use_browser_locale": "استخدم لغه للمتصفح",
|
||||||
|
"use_browser_locale_description": "تنسيق التواريخ والأوقات والأرقام وفقًا لإعدادات اللغة في متصفحك",
|
||||||
"use_current_connection": "استخدم الاتصال الحالي",
|
"use_current_connection": "استخدم الاتصال الحالي",
|
||||||
"use_custom_date_range": "استخدم النطاق الزمني المخصص بدلاً من ذلك",
|
"use_custom_date_range": "استخدم النطاق الزمني المخصص بدلاً من ذلك",
|
||||||
"user": "مستخدم",
|
"user": "مستخدم",
|
||||||
|
|||||||
+24
-1
@@ -104,6 +104,8 @@
|
|||||||
"image_preview_description": "Відарыс сярэдняга памеру з выдаленымі метаданымі, выкарыстоўваецца пры праглядзе асобнага рэсурсу і для машыннага навучання",
|
"image_preview_description": "Відарыс сярэдняга памеру з выдаленымі метаданымі, выкарыстоўваецца пры праглядзе асобнага рэсурсу і для машыннага навучання",
|
||||||
"image_preview_quality_description": "Якасць праявы ад 1 да 100. Чым вышэй, тым лепш, але пры гэтым ствараюцца файлы большага памеру і можа знізіцца хуткасць водгуку прыкладання. Ўстаноўка нізкага значэння можа паўплываць на якасць машыннага навучання.",
|
"image_preview_quality_description": "Якасць праявы ад 1 да 100. Чым вышэй, тым лепш, але пры гэтым ствараюцца файлы большага памеру і можа знізіцца хуткасць водгуку прыкладання. Ўстаноўка нізкага значэння можа паўплываць на якасць машыннага навучання.",
|
||||||
"image_preview_title": "Налады папярэдняга прагляду",
|
"image_preview_title": "Налады папярэдняга прагляду",
|
||||||
|
"image_progressive": "Прагрэсіўны",
|
||||||
|
"image_progressive_description": "Выявы з прагрэсіўным кодаваннем загружаюцца хутчэй, паступова паляпшаецца якасць. Налада не ўплывае на выяву ў фармаце WebP.",
|
||||||
"image_quality": "Якасць",
|
"image_quality": "Якасць",
|
||||||
"image_resolution": "Раздзяляльнасць",
|
"image_resolution": "Раздзяляльнасць",
|
||||||
"image_resolution_description": "Больш высокая раздзяляльнасць дазваляе захаваць больш дэталяў, але патрабуе больш часу для кадавання, прыводзіць да павялічвання памеру файлаў і можа знізіць хуткасць водгуку дадатку.",
|
"image_resolution_description": "Больш высокая раздзяляльнасць дазваляе захаваць больш дэталяў, але патрабуе больш часу для кадавання, прыводзіць да павялічвання памеру файлаў і можа знізіць хуткасць водгуку дадатку.",
|
||||||
@@ -120,6 +122,7 @@
|
|||||||
"job_settings_description": "Кіраваць наладамі паралельнага выканання заданняў",
|
"job_settings_description": "Кіраваць наладамі паралельнага выканання заданняў",
|
||||||
"jobs_delayed": "{jobCount, plural, other {# адкладзена}}",
|
"jobs_delayed": "{jobCount, plural, other {# адкладзена}}",
|
||||||
"jobs_failed": "{jobCount, plural, other {# не выканалася}}",
|
"jobs_failed": "{jobCount, plural, other {# не выканалася}}",
|
||||||
|
"jobs_over_time": "Графік апрацоўкі",
|
||||||
"library_created": "Створана бібліятэка: {library}",
|
"library_created": "Створана бібліятэка: {library}",
|
||||||
"library_deleted": "Бібліятэка выдалена",
|
"library_deleted": "Бібліятэка выдалена",
|
||||||
"library_details": "Параметры бібліятэкі",
|
"library_details": "Параметры бібліятэкі",
|
||||||
@@ -160,8 +163,27 @@
|
|||||||
"machine_learning_facial_recognition_model_description": "Мадэлі пералічаны ў парадку ўбывання іх памеру. Большыя мадэлі павольней і выкарыстоўваюць больш памяці, але даюць лепшыя вынікі. Звярніце увагу, што пасля змены мадэлі трэба зноў запусціць заданне распазнавання твараў для ўсіх відарысаў.",
|
"machine_learning_facial_recognition_model_description": "Мадэлі пералічаны ў парадку ўбывання іх памеру. Большыя мадэлі павольней і выкарыстоўваюць больш памяці, але даюць лепшыя вынікі. Звярніце увагу, што пасля змены мадэлі трэба зноў запусціць заданне распазнавання твараў для ўсіх відарысаў.",
|
||||||
"machine_learning_facial_recognition_setting": "Уключыць распазнаванне твараў",
|
"machine_learning_facial_recognition_setting": "Уключыць распазнаванне твараў",
|
||||||
"machine_learning_facial_recognition_setting_description": "Калі адключана, відарысы не будуць кадавацца для распазнавання твараў, і не будзе запаўняцца раздзел \"Людзі\" на старонцы \"Агляд\".",
|
"machine_learning_facial_recognition_setting_description": "Калі адключана, відарысы не будуць кадавацца для распазнавання твараў, і не будзе запаўняцца раздзел \"Людзі\" на старонцы \"Агляд\".",
|
||||||
|
"machine_learning_max_detection_distance": "Максімальная адлегласць выяўлення",
|
||||||
|
"machine_learning_max_detection_distance_description": "Максімальная розніца паміж двума выявамі, якія лічацца дублікатамі, складае ад 0,001 да 0,1. Больш высокія значэнні дазволяць выявіць больш дублікатаў, але могуць прывесці да няправільных выяўленняў.",
|
||||||
|
"machine_learning_max_recognition_distance": "Парог разпазнавання",
|
||||||
|
"machine_learning_max_recognition_distance_description": "Максімальнае адрозненне паміж двума асобамі, якія можна лічыць адным чалавекам (у дыяпазоне ад 0 да 2).Зніжэнне гэтага параметру можа прадухіліць распазнанне двух людзей як аднаго і таго ж чалавека, а павышэнне - як двух розных людзей. Майце на ўвазе, што прасцей аб'яднаць двух людзей, чым падзяліць аднаго чалавека на дваіх, таму па магчымасці выбірайце меншы парог.",
|
||||||
|
"machine_learning_min_detection_score": "Мінімальны парог разпазнавання",
|
||||||
|
"machine_learning_min_detection_score_description": "Мінімальны парог для выяўлення асобы (ад 0 да 1). Ніжэйшае значэнне дазволіць знаходзіць больш асоб, але можа прывесці да ілжывых спрацоўванняў.",
|
||||||
|
"machine_learning_min_recognized_faces": "Мінімум разпазнаных твараў",
|
||||||
|
"machine_learning_min_recognized_faces_description": "Мінімальная колькасць распазнаных твараў для стварэння асобы. Павялічэнне гэтага параметра робіць распазнанне асоб больш дакладным, але пры гэтым павялічваецца верагоднасць таго, што твар не будзе прысвоены асобе.",
|
||||||
|
"machine_learning_ocr": "Разпазнаванне тэксту (OCR)",
|
||||||
|
"machine_learning_ocr_description": "Выкарыстоўвайце машыннае навучанне для распазнавання тэксту на малюнках",
|
||||||
|
"machine_learning_ocr_enabled": "Дадаць OCR",
|
||||||
|
"machine_learning_ocr_enabled_description": "Калі адключана, выявы не будуць распазнавацца з выкарыстаннем тэксту.",
|
||||||
"machine_learning_ocr_max_resolution": "Максімальная раздзяляльнасць",
|
"machine_learning_ocr_max_resolution": "Максімальная раздзяляльнасць",
|
||||||
"machine_learning_ocr_max_resolution_description": "Відарысы з раздзяляльнасцю больш гэтай будуць паменшаны з захаваннем суадносіны бакоў. Больш высокія значэнні павышаюць дакладнасць распазнавання, але патрабуюць больш часу на апрацоўку і выкарыстоўваюць больш памяці.",
|
"machine_learning_ocr_max_resolution_description": "Відарысы з раздзяляльнасцю больш гэтай будуць паменшаны з захаваннем суадносіны бакоў. Больш высокія значэнні павышаюць дакладнасць распазнавання, але патрабуюць больш часу на апрацоўку і выкарыстоўваюць больш памяці.",
|
||||||
|
"machine_learning_ocr_min_detection_score": "Мінімальны бал выяўлення",
|
||||||
|
"machine_learning_ocr_min_detection_score_description": "Мінімальны бал даверу для выяўлення тэксту складае ад 0 да 1. Больш нізкія значэнні дазволяць выявіць больш тэксту, але могуць прывесці да хібных спрацоўванняў.",
|
||||||
|
"machine_learning_ocr_min_recognition_score": "Мінімальны бал распазнавання",
|
||||||
|
"machine_learning_ocr_min_score_recognition_description": "Мінімальны бал даверу для распазнавання выяўленага тэксту складае ад 0 да 1. Больш нізкія значэнні распазнаюць больш тэксту, але могуць прывесці да хібных спрацоўванняў.",
|
||||||
|
"machine_learning_ocr_model": "Мадэль машыннага навучання (OCR)",
|
||||||
|
"machine_learning_ocr_model_description": "Серверныя мадэлі больш дакладныя, чым мабільныя, але апрацоўваюць дадзеныя даўжэй і выкарыстоўваюць больш памяці.",
|
||||||
|
"machine_learning_settings": "Налады машыннага навучання",
|
||||||
"map_dark_style": "Цёмны стыль",
|
"map_dark_style": "Цёмны стыль",
|
||||||
"map_enable_description": "Уключыць функцыі карты",
|
"map_enable_description": "Уключыць функцыі карты",
|
||||||
"map_gps_settings": "Налады карты і GPS",
|
"map_gps_settings": "Налады карты і GPS",
|
||||||
@@ -171,6 +193,7 @@
|
|||||||
"map_style_description": "URL-адрас style.json тэмы карты",
|
"map_style_description": "URL-адрас style.json тэмы карты",
|
||||||
"metadata_extraction_job_description": "Выняць метаданыя з файлаў, такія як месцазнаходжанне, твары і раздзяляльнасць",
|
"metadata_extraction_job_description": "Выняць метаданыя з файлаў, такія як месцазнаходжанне, твары і раздзяляльнасць",
|
||||||
"metadata_settings": "Налады метаданых",
|
"metadata_settings": "Налады метаданых",
|
||||||
|
"notification_email_port_description": "Порт паштовага сервера (напрыклад, 25, 465 або 587)",
|
||||||
"oauth_button_text": "Тэкст кнопкі",
|
"oauth_button_text": "Тэкст кнопкі",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"refreshing_all_libraries": "Абнаўленне ўсіх бібліятэк",
|
"refreshing_all_libraries": "Абнаўленне ўсіх бібліятэк",
|
||||||
@@ -216,7 +239,7 @@
|
|||||||
"user_settings": "Налады карыстальніка",
|
"user_settings": "Налады карыстальніка",
|
||||||
"user_settings_description": "Кіраванне наладамі карыстальніка",
|
"user_settings_description": "Кіраванне наладамі карыстальніка",
|
||||||
"version_check_enabled_description": "Уключыць праверку версіі",
|
"version_check_enabled_description": "Уключыць праверку версіі",
|
||||||
"version_check_implications": "Функцыя праверкі версіі перыядычна звяртаецца да github.com",
|
"version_check_implications": "Функцыя праверкі версіі перыядычна звяртаецца да {server}",
|
||||||
"version_check_settings": "Праверка версіі",
|
"version_check_settings": "Праверка версіі",
|
||||||
"version_check_settings_description": "Уключыць/адключыць апавяшчэнні аб новай версіі"
|
"version_check_settings_description": "Уключыць/адключыць апавяшчэнні аб новай версіі"
|
||||||
},
|
},
|
||||||
|
|||||||
+48
-25
@@ -61,7 +61,7 @@
|
|||||||
"backup_onboarding_1_description": "копие на облака или друго физическо място.",
|
"backup_onboarding_1_description": "копие на облака или друго физическо място.",
|
||||||
"backup_onboarding_2_description": "локални копия на различни устройства. Това включва основните файлове и локални архиви на тези файлове.",
|
"backup_onboarding_2_description": "локални копия на различни устройства. Това включва основните файлове и локални архиви на тези файлове.",
|
||||||
"backup_onboarding_3_description": "общо копия на вашите данни, включитено оригиналните файлове. Това включва 1 копие извън системата и 2 локални копия.",
|
"backup_onboarding_3_description": "общо копия на вашите данни, включитено оригиналните файлове. Това включва 1 копие извън системата и 2 локални копия.",
|
||||||
"backup_onboarding_description": "За надеждна защита препоръчваме стратегията <backblaze-link>3-2-1</backblaze-link>. Правете архивни копия както на качените снимки/видеа, така и на базата данни на Immich.",
|
"backup_onboarding_description": "За надеждна защита препоръчваме <backblaze-link>стратегията 3-2-1</backblaze-link>. Правете архивни копия както на качените снимки/видеа, така и на базата данни на Immich.",
|
||||||
"backup_onboarding_footer": "За подробна информация относно архивирането в Immich, моля вижте в <link>документацията</link>.",
|
"backup_onboarding_footer": "За подробна информация относно архивирането в Immich, моля вижте в <link>документацията</link>.",
|
||||||
"backup_onboarding_parts_title": "Стратегията 3-2-1 включва:",
|
"backup_onboarding_parts_title": "Стратегията 3-2-1 включва:",
|
||||||
"backup_onboarding_title": "Резервни копия",
|
"backup_onboarding_title": "Резервни копия",
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
"image_preview_description": "Среден размер на изображението с премахнати метаданни, използвано при преглед на един елемент и за машинно обучение",
|
"image_preview_description": "Среден размер на изображението с премахнати метаданни, използвано при преглед на един елемент и за машинно обучение",
|
||||||
"image_preview_quality_description": "Качество на предварителния преглед от 1 до 100. По-високата стойност е по-добра, но води до по-големи файлове и може да намали бързодействието на приложението. Задаването на ниска стойност може да повлияе на качеството на машинното обучение.",
|
"image_preview_quality_description": "Качество на предварителния преглед от 1 до 100. По-високата стойност е по-добра, но води до по-големи файлове и може да намали бързодействието на приложението. Задаването на ниска стойност може да повлияе на качеството на машинното обучение.",
|
||||||
"image_preview_title": "Настройки на прегледа",
|
"image_preview_title": "Настройки на прегледа",
|
||||||
"image_progressive": "Прогресивен JPEG",
|
"image_progressive": "Прогресивно",
|
||||||
"image_progressive_description": "Изображенията, кодирани в прогресивен JPEG формат, се зареждат по-бързо, с постепенно подобряващо се качество. Това няма влияние на кодираните като WebP изображения.",
|
"image_progressive_description": "Изображенията, кодирани в прогресивен JPEG формат, се зареждат по-бързо, с постепенно подобряващо се качество. Това няма влияние на кодираните като WebP изображения.",
|
||||||
"image_quality": "Качество",
|
"image_quality": "Качество",
|
||||||
"image_resolution": "Резолюция",
|
"image_resolution": "Резолюция",
|
||||||
@@ -311,7 +311,7 @@
|
|||||||
"search_jobs": "Търсене на задачи…",
|
"search_jobs": "Търсене на задачи…",
|
||||||
"send_welcome_email": "Изпращане на имейл за добре дошли",
|
"send_welcome_email": "Изпращане на имейл за добре дошли",
|
||||||
"server_external_domain_settings": "Външен домейн",
|
"server_external_domain_settings": "Външен домейн",
|
||||||
"server_external_domain_settings_description": "Домейн за публични споделени връзки, включително http(s)://",
|
"server_external_domain_settings_description": "Домейн за външни връзки",
|
||||||
"server_public_users": "Публични потребители",
|
"server_public_users": "Публични потребители",
|
||||||
"server_public_users_description": "Всички потребители (име и имейл) са изброени при добавяне на потребител в споделени албуми. Когато е деактивирано, списъкът с потребители ще бъде достъпен само за администраторите.",
|
"server_public_users_description": "Всички потребители (име и имейл) са изброени при добавяне на потребител в споделени албуми. Когато е деактивирано, списъкът с потребители ще бъде достъпен само за администраторите.",
|
||||||
"server_settings": "Настройки на сървъра",
|
"server_settings": "Настройки на сървъра",
|
||||||
@@ -333,7 +333,7 @@
|
|||||||
"storage_template_migration_description": "Прилагане на текущия <link>{template}</link> към предишно качените файлове",
|
"storage_template_migration_description": "Прилагане на текущия <link>{template}</link> към предишно качените файлове",
|
||||||
"storage_template_migration_info": "Шаблона ще преобразува всички разширения на имената на файловете в долен регистър. Промените в шаблоните ще се прилагат само за нови елементи. За да приложите принудително шаблона към вече качени елементи, изпълнете <link>{job}</link>.",
|
"storage_template_migration_info": "Шаблона ще преобразува всички разширения на имената на файловете в долен регистър. Промените в шаблоните ще се прилагат само за нови елементи. За да приложите принудително шаблона към вече качени елементи, изпълнете <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "Задача за миграция на шаблона за съхранение",
|
"storage_template_migration_job": "Задача за миграция на шаблона за съхранение",
|
||||||
"storage_template_more_details": "За повече подробности относно тази функция се обърнете към шаблона <template-link>Storage Template</template-link> и неговите <implications-link> последствия </implications-link>",
|
"storage_template_more_details": "За повече подробности относно тази функция се обърнете към шаблона <template-link>Storage Template</template-link> и неговите <implications-link>последствия</implications-link>",
|
||||||
"storage_template_onboarding_description_v2": "Когато е разрешена, тази функция ще организира автоматично файловете, според шаблон, дефиниран от потребителя. За допълнителна информация, моля вижте <link>документацията</link>.",
|
"storage_template_onboarding_description_v2": "Когато е разрешена, тази функция ще организира автоматично файловете, според шаблон, дефиниран от потребителя. За допълнителна информация, моля вижте <link>документацията</link>.",
|
||||||
"storage_template_path_length": "Ограничение на дължината на пътя: <b>{length, number}</b>/{limit, number}",
|
"storage_template_path_length": "Ограничение на дължината на пътя: <b>{length, number}</b>/{limit, number}",
|
||||||
"storage_template_settings": "Шаблон за съхранение",
|
"storage_template_settings": "Шаблон за съхранение",
|
||||||
@@ -372,7 +372,7 @@
|
|||||||
"transcoding_audio_codec": "Аудио кодек",
|
"transcoding_audio_codec": "Аудио кодек",
|
||||||
"transcoding_audio_codec_description": "Opus е опцията с най-високо качество, но има по-ниска съвместимост със стари устройства или софтуер.",
|
"transcoding_audio_codec_description": "Opus е опцията с най-високо качество, но има по-ниска съвместимост със стари устройства или софтуер.",
|
||||||
"transcoding_bitrate_description": "Видеоклипове с по-висок от максималния битрейт или не в приет формат",
|
"transcoding_bitrate_description": "Видеоклипове с по-висок от максималния битрейт или не в приет формат",
|
||||||
"transcoding_codecs_learn_more": "За да научите повече за използваната терминология, вижте документацията на FFmpeg за <h264-link>кодек H.264</h264-link>, <hevc-link>кодек HEVC</hevc-link> и <vp9-link>VP9 кодек</vp9-link>.",
|
"transcoding_codecs_learn_more": "За да научите повече за използваната терминология, вижте документацията на FFmpeg за <h264-link>кодек H.264</h264-link>, <hevc-link>кодек HEVC</hevc-link> и <vp9-link>кодек VP9</vp9-link>.",
|
||||||
"transcoding_constant_quality_mode": "Режим на постоянно качество",
|
"transcoding_constant_quality_mode": "Режим на постоянно качество",
|
||||||
"transcoding_constant_quality_mode_description": "ICQ е по-добър от CQP, но някои устройства за хардуерно ускоряване не поддържат този режим. С задаването на тази опция ще предпочете посочения режим при използване на базирано на качество кодиране. Игнорирано от NVENC, тъй като не поддържа ICQ.",
|
"transcoding_constant_quality_mode_description": "ICQ е по-добър от CQP, но някои устройства за хардуерно ускоряване не поддържат този режим. С задаването на тази опция ще предпочете посочения режим при използване на базирано на качество кодиране. Игнорирано от NVENC, тъй като не поддържа ICQ.",
|
||||||
"transcoding_constant_rate_factor": "Коефициент на постоянна скорост (-crf)",
|
"transcoding_constant_rate_factor": "Коефициент на постоянна скорост (-crf)",
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
"transcoding_tone_mapping": "Тонално картографиране",
|
"transcoding_tone_mapping": "Тонално картографиране",
|
||||||
"transcoding_tone_mapping_description": "Опитва се да запази външния вид на HDR видеоклипове, когато се преобразува в SDR. Всеки алгоритъм прави различни компромиси за цвят, детайлност и яркост. Hable запазва детайлите, Mobius запазва цвета, а Reinhard запазва яркостта.",
|
"transcoding_tone_mapping_description": "Опитва се да запази външния вид на HDR видеоклипове, когато се преобразува в SDR. Всеки алгоритъм прави различни компромиси за цвят, детайлност и яркост. Hable запазва детайлите, Mobius запазва цвета, а Reinhard запазва яркостта.",
|
||||||
"transcoding_transcode_policy": "Правила за транскодиране",
|
"transcoding_transcode_policy": "Правила за транскодиране",
|
||||||
"transcoding_transcode_policy_description": "Правила за това кога видеоклипът трябва да бъде транскодиран. HDR видеоклиповете винаги ще бъдат транскодирани (освен ако транскодирането е деактивирано).",
|
"transcoding_transcode_policy_description": "Правила за това кога видеоклипът трябва да бъде транскодиран. HDR видеоклиповете и тези с формат, различен от YUV 4:2:0, ще бъдат винаги транскодирани (освен ако транскодирането е деактивирано).",
|
||||||
"transcoding_two_pass_encoding": "Кодиране с двойно минаване",
|
"transcoding_two_pass_encoding": "Кодиране с двойно минаване",
|
||||||
"transcoding_two_pass_encoding_setting_description": "Транскодирането с две минавания създава по-добре кодиране видеа. Когато максималния битрейт е включен (задължително е да се работи с H.264 и HEVC), тази опция използва диапазон на битрейта базиран на максималния битрейт и игнорира CRF. За VP9, CRF може да се използва ако максималният битрейт е изключен.",
|
"transcoding_two_pass_encoding_setting_description": "Транскодирането с две минавания създава по-добре кодиране видеа. Когато максималния битрейт е включен (задължително е да се работи с H.264 и HEVC), тази опция използва диапазон на битрейта базиран на максималния битрейт и игнорира CRF. За VP9, CRF може да се използва ако максималният битрейт е изключен.",
|
||||||
"transcoding_video_codec": "Видеокодек",
|
"transcoding_video_codec": "Видеокодек",
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
"user_successfully_removed": "Потребител {email} е успешно премахнат.",
|
"user_successfully_removed": "Потребител {email} е успешно премахнат.",
|
||||||
"users_page_description": "Страница за администриране на потребители",
|
"users_page_description": "Страница за администриране на потребители",
|
||||||
"version_check_enabled_description": "Активирай проверка на версията",
|
"version_check_enabled_description": "Активирай проверка на версията",
|
||||||
"version_check_implications": "Функцията за проверка на версията разчита на периодична комуникация с github.com",
|
"version_check_implications": "Функцията за проверка на версията разчита на периодична комуникация с {server}",
|
||||||
"version_check_settings": "Проверка на версията",
|
"version_check_settings": "Проверка на версията",
|
||||||
"version_check_settings_description": "Активирайте/деактивирайте известието за нова версия",
|
"version_check_settings_description": "Активирайте/деактивирайте известието за нова версия",
|
||||||
"video_conversion_job": "Транскодиране на видеоклиповете",
|
"video_conversion_job": "Транскодиране на видеоклиповете",
|
||||||
@@ -794,6 +794,11 @@
|
|||||||
"color": "Цвят",
|
"color": "Цвят",
|
||||||
"color_theme": "Цветова тема",
|
"color_theme": "Цветова тема",
|
||||||
"command": "Команда",
|
"command": "Команда",
|
||||||
|
"command_palette_prompt": "Бързо намиране на страници, действия или команди",
|
||||||
|
"command_palette_to_close": "затвори",
|
||||||
|
"command_palette_to_navigate": "влез",
|
||||||
|
"command_palette_to_select": "избери",
|
||||||
|
"command_palette_to_show_all": "покажи всичко",
|
||||||
"comment_deleted": "Коментарът е изтрит",
|
"comment_deleted": "Коментарът е изтрит",
|
||||||
"comment_options": "Опции за коментар",
|
"comment_options": "Опции за коментар",
|
||||||
"comments_and_likes": "Коментари и харесвания",
|
"comments_and_likes": "Коментари и харесвания",
|
||||||
@@ -861,13 +866,14 @@
|
|||||||
"crop_aspect_ratio_fixed": "Фиксиран",
|
"crop_aspect_ratio_fixed": "Фиксиран",
|
||||||
"crop_aspect_ratio_free": "Свободен",
|
"crop_aspect_ratio_free": "Свободен",
|
||||||
"crop_aspect_ratio_original": "Оригинален",
|
"crop_aspect_ratio_original": "Оригинален",
|
||||||
|
"crop_aspect_ratio_square": "Квадрат",
|
||||||
"curated_object_page_title": "Неща",
|
"curated_object_page_title": "Неща",
|
||||||
"current_device": "Текущо устройство",
|
"current_device": "Текущо устройство",
|
||||||
"current_pin_code": "Сегашен PIN код",
|
"current_pin_code": "Сегашен PIN код",
|
||||||
"current_server_address": "Настоящ адрес на сървъра",
|
"current_server_address": "Настоящ адрес на сървъра",
|
||||||
"custom_date": "Персонализирана дата",
|
"custom_date": "Персонализирана дата",
|
||||||
"custom_locale": "Персонализиран локал",
|
"custom_locale": "Персонализирани езикови настройки",
|
||||||
"custom_locale_description": "Форматиране на дати и числа в зависимост от езика и региона",
|
"custom_locale_description": "Форматиране на дата, време и числа в зависимост от избрания език и регион",
|
||||||
"custom_url": "Персонализиран URL адрес",
|
"custom_url": "Персонализиран URL адрес",
|
||||||
"cutoff_date_description": "Запазване на снимки от последните…",
|
"cutoff_date_description": "Запазване на снимки от последните…",
|
||||||
"cutoff_day": "{count, plural, one {ден} other {дни}}",
|
"cutoff_day": "{count, plural, one {ден} other {дни}}",
|
||||||
@@ -875,7 +881,7 @@
|
|||||||
"daily_title_text_date": "E, dd MMM",
|
"daily_title_text_date": "E, dd MMM",
|
||||||
"daily_title_text_date_year": "E, dd MMM yyyy",
|
"daily_title_text_date_year": "E, dd MMM yyyy",
|
||||||
"dark": "Тъмен",
|
"dark": "Тъмен",
|
||||||
"dark_theme": "Тъмна тема",
|
"dark_theme": "Премини към тъмна тема",
|
||||||
"date": "Дата",
|
"date": "Дата",
|
||||||
"date_after": "Дата след",
|
"date_after": "Дата след",
|
||||||
"date_and_time": "Дата и час",
|
"date_and_time": "Дата и час",
|
||||||
@@ -886,12 +892,8 @@
|
|||||||
"day": "Ден",
|
"day": "Ден",
|
||||||
"days": "Дни",
|
"days": "Дни",
|
||||||
"deduplicate_all": "Дедупликиране на всички",
|
"deduplicate_all": "Дедупликиране на всички",
|
||||||
"deduplication_criteria_1": "Размер на снимката в байтове",
|
"default_locale": "Език по подразбиране",
|
||||||
"deduplication_criteria_2": "Брой EXIF данни",
|
"default_locale_description": "Формат на дата и числа според езиковата настройка на браузъра",
|
||||||
"deduplication_info": "Информация за дедупликацията",
|
|
||||||
"deduplication_info_description": "За автоматично предварително избиране на ресурси и премахване на дубликати на едро, разглеждаме:",
|
|
||||||
"default_locale": "Локализация по подразбиране",
|
|
||||||
"default_locale_description": "Форматиране на дати и числа в зависимост от езиковата настройка на браузъра",
|
|
||||||
"delete": "Изтрий",
|
"delete": "Изтрий",
|
||||||
"delete_action_confirmation_message": "Сигурни ли сте, че искате да изтриете този обект? Следва преместване на обекта в коша за отпадъци на сървъра и ще получите предложение обекта да бъде изтрит локално",
|
"delete_action_confirmation_message": "Сигурни ли сте, че искате да изтриете този обект? Следва преместване на обекта в коша за отпадъци на сървъра и ще получите предложение обекта да бъде изтрит локално",
|
||||||
"delete_action_prompt": "{count} са изтрити",
|
"delete_action_prompt": "{count} са изтрити",
|
||||||
@@ -967,7 +969,7 @@
|
|||||||
"downloading_media": "Изтегляне на медия",
|
"downloading_media": "Изтегляне на медия",
|
||||||
"drop_files_to_upload": "Пуснете файловете, за да ги качите",
|
"drop_files_to_upload": "Пуснете файловете, за да ги качите",
|
||||||
"duplicates": "Дубликати",
|
"duplicates": "Дубликати",
|
||||||
"duplicates_description": "Изберете всяка група, като посочите кои, ако има такива, са дубликати",
|
"duplicates_description": "Изберете всяка група, като посочите кои, ако има такива, са дубликати.",
|
||||||
"duration": "Продължителност",
|
"duration": "Продължителност",
|
||||||
"edit": "Редактиране",
|
"edit": "Редактиране",
|
||||||
"edit_album": "Редактиране на албум",
|
"edit_album": "Редактиране на албум",
|
||||||
@@ -1004,6 +1006,8 @@
|
|||||||
"editor_edits_applied_success": "Успешно прилагане на промените",
|
"editor_edits_applied_success": "Успешно прилагане на промените",
|
||||||
"editor_flip_horizontal": "Обърни хоризонтално",
|
"editor_flip_horizontal": "Обърни хоризонтално",
|
||||||
"editor_flip_vertical": "Обърни вертикално",
|
"editor_flip_vertical": "Обърни вертикално",
|
||||||
|
"editor_handle_corner": "Манипулатор {corner, select, top_left {горен ляв} top_right {горен десен} bottom_left {долен ляв} bottom_right {долен десен} other {в}} ъгъл",
|
||||||
|
"editor_handle_edge": "Манипулатор {edge, select, top {горен} bottom {долен} left {ляв} right {десен} other {по}} ръб",
|
||||||
"editor_orientation": "Ориентация",
|
"editor_orientation": "Ориентация",
|
||||||
"editor_reset_all_changes": "Възстанови всички промени",
|
"editor_reset_all_changes": "Възстанови всички промени",
|
||||||
"editor_rotate_left": "Завърти 90° обратно на часовниковата стрелка",
|
"editor_rotate_left": "Завърти 90° обратно на часовниковата стрелка",
|
||||||
@@ -1069,6 +1073,7 @@
|
|||||||
"failed_to_update_notification_status": "Неуспешно обновяване на състоянието на известията",
|
"failed_to_update_notification_status": "Неуспешно обновяване на състоянието на известията",
|
||||||
"incorrect_email_or_password": "Неправилен имейл или парола",
|
"incorrect_email_or_password": "Неправилен имейл или парола",
|
||||||
"library_folder_already_exists": "Тази папка вече съществува.",
|
"library_folder_already_exists": "Тази папка вече съществува.",
|
||||||
|
"page_not_found": "Страницата не е намерена",
|
||||||
"paths_validation_failed": "{paths, plural, one {# път} other {# пътища}} не преминаха валидация",
|
"paths_validation_failed": "{paths, plural, one {# път} other {# пътища}} не преминаха валидация",
|
||||||
"profile_picture_transparent_pixels": "Профилните снимки не могат да имат прозрачни пиксели. Моля, увеличете и/или преместете изображението.",
|
"profile_picture_transparent_pixels": "Профилните снимки не могат да имат прозрачни пиксели. Моля, увеличете и/или преместете изображението.",
|
||||||
"quota_higher_than_disk_size": "Зададена е квота, по-голяма от размера на диска",
|
"quota_higher_than_disk_size": "Зададена е квота, по-голяма от размера на диска",
|
||||||
@@ -1168,6 +1173,7 @@
|
|||||||
"exif_bottom_sheet_people": "ХОРА",
|
"exif_bottom_sheet_people": "ХОРА",
|
||||||
"exif_bottom_sheet_person_add_person": "Добави име",
|
"exif_bottom_sheet_person_add_person": "Добави име",
|
||||||
"exit_slideshow": "Изход от слайдшоуто",
|
"exit_slideshow": "Изход от слайдшоуто",
|
||||||
|
"expand": "Разгъни",
|
||||||
"expand_all": "Разшири всички",
|
"expand_all": "Разшири всички",
|
||||||
"experimental_settings_new_asset_list_subtitle": "В развитие",
|
"experimental_settings_new_asset_list_subtitle": "В развитие",
|
||||||
"experimental_settings_new_asset_list_title": "Включи експериментална подредба на снимки",
|
"experimental_settings_new_asset_list_title": "Включи експериментална подредба на снимки",
|
||||||
@@ -1212,6 +1218,7 @@
|
|||||||
"filter_description": "Условия за филтриране на обекти",
|
"filter_description": "Условия за филтриране на обекти",
|
||||||
"filter_people": "Филтриране на хора",
|
"filter_people": "Филтриране на хора",
|
||||||
"filter_places": "Филтър по място",
|
"filter_places": "Филтър по място",
|
||||||
|
"filter_tags": "Филтриране по етикети",
|
||||||
"filters": "Филтри",
|
"filters": "Филтри",
|
||||||
"find_them_fast": "Намерете ги бързо по име с търсене",
|
"find_them_fast": "Намерете ги бързо по име с търсене",
|
||||||
"first": "Първи",
|
"first": "Първи",
|
||||||
@@ -1311,7 +1318,7 @@
|
|||||||
"import_path": "Път за импортиране",
|
"import_path": "Път за импортиране",
|
||||||
"in_albums": "В {count, plural, one {# албум} other {# албума}}",
|
"in_albums": "В {count, plural, one {# албум} other {# албума}}",
|
||||||
"in_archive": "В архив",
|
"in_archive": "В архив",
|
||||||
"in_year": "{year} г.",
|
"in_year": "През {year}",
|
||||||
"in_year_selector": "През",
|
"in_year_selector": "През",
|
||||||
"include_archived": "Включване на архивирани",
|
"include_archived": "Включване на архивирани",
|
||||||
"include_shared_albums": "Включване на споделени албуми",
|
"include_shared_albums": "Включване на споделени албуми",
|
||||||
@@ -1379,9 +1386,11 @@
|
|||||||
"library_page_sort_title": "Заглавие на албума",
|
"library_page_sort_title": "Заглавие на албума",
|
||||||
"licenses": "Лицензи",
|
"licenses": "Лицензи",
|
||||||
"light": "Светло",
|
"light": "Светло",
|
||||||
|
"light_theme": "Премини към светла тема",
|
||||||
"like": "Харесайте",
|
"like": "Харесайте",
|
||||||
"like_deleted": "Като изтрит",
|
"like_deleted": "Като изтрит",
|
||||||
"link_motion_video": "Линк към видео",
|
"link_motion_video": "Линк към видео",
|
||||||
|
"link_to_docs": "За повече информация вижте <link>документацията</link>.",
|
||||||
"link_to_oauth": "Линк към OAuth",
|
"link_to_oauth": "Линк към OAuth",
|
||||||
"linked_oauth_account": "Свързан OAuth акаунт",
|
"linked_oauth_account": "Свързан OAuth акаунт",
|
||||||
"list": "Лист",
|
"list": "Лист",
|
||||||
@@ -1642,13 +1651,15 @@
|
|||||||
"online": "Онлайн",
|
"online": "Онлайн",
|
||||||
"only_favorites": "Само любими",
|
"only_favorites": "Само любими",
|
||||||
"open": "Отвори",
|
"open": "Отвори",
|
||||||
|
"open_calendar": "Отвори календар",
|
||||||
|
"open_in_browser": "Отвори в браузър",
|
||||||
"open_in_map_view": "Отвори изглед на карта",
|
"open_in_map_view": "Отвори изглед на карта",
|
||||||
"open_in_openstreetmap": "Отвори в OpenStreetMap",
|
"open_in_openstreetmap": "Отвори в OpenStreetMap",
|
||||||
"open_the_search_filters": "Отвари филтрите за търсене",
|
"open_the_search_filters": "Отвари филтрите за търсене",
|
||||||
"options": "Настройки",
|
"options": "Настройки",
|
||||||
"or": "или",
|
"or": "или",
|
||||||
"organize_into_albums": "Organitzar per àlbums",
|
"organize_into_albums": "Подредете в албуми",
|
||||||
"organize_into_albums_description": "Posar les fotos existents dins dels àlbums fent servir la configuració de sincronització",
|
"organize_into_albums_description": "Добавете наличните снимки в албуми, като използвате текущите настройки за синхронизиране",
|
||||||
"organize_your_library": "Организиране на вашата библиотека",
|
"organize_your_library": "Организиране на вашата библиотека",
|
||||||
"original": "оригинал",
|
"original": "оригинал",
|
||||||
"other": "Други",
|
"other": "Други",
|
||||||
@@ -1796,14 +1807,13 @@
|
|||||||
"purchase_server_description_2": "Статус на поддръжник",
|
"purchase_server_description_2": "Статус на поддръжник",
|
||||||
"purchase_server_title": "Сървър",
|
"purchase_server_title": "Сървър",
|
||||||
"purchase_settings_server_activated": "Продуктовият ключ на сървъра се управлява от администратора",
|
"purchase_settings_server_activated": "Продуктовият ключ на сървъра се управлява от администратора",
|
||||||
"query_asset_id": "Buscar item per ID",
|
"query_asset_id": "Търсене на елемент по ID",
|
||||||
"queue_status": "В опашка {count} от {total}",
|
"queue_status": "В опашка {count} от {total}",
|
||||||
"rate_asset": "Задаване на рейтинг",
|
"rate_asset": "Задаване на рейтинг",
|
||||||
"rating": "Оценка със звезди",
|
"rating": "Оценка със звезди",
|
||||||
"rating_clear": "Изчисти оценката",
|
"rating_clear": "Изчисти оценката",
|
||||||
"rating_count": "{count, plural, one {# звезда} other {# звезди}}",
|
"rating_count": "{count, plural, =0 {Без рейтинг} one {# звезда} other {# звезди}}",
|
||||||
"rating_description": "Покажи EXIF оценката в панела с информация",
|
"rating_description": "Покажи EXIF оценката в панела с информация",
|
||||||
"rating_set": "Зададен е рейтинг {rating, plural, one {# звезда} other {# звезди}}",
|
|
||||||
"reaction_options": "Избор на реакция",
|
"reaction_options": "Избор на реакция",
|
||||||
"read_changelog": "Прочети промените",
|
"read_changelog": "Прочети промените",
|
||||||
"readonly_mode_disabled": "Режима само за четене е деактивиран",
|
"readonly_mode_disabled": "Режима само за четене е деактивиран",
|
||||||
@@ -1875,7 +1885,10 @@
|
|||||||
"reset_pin_code_success": "Успешно нулиран ПИН код",
|
"reset_pin_code_success": "Успешно нулиран ПИН код",
|
||||||
"reset_pin_code_with_password": "С вашата парола можете винаги да нулирате своя ПИН код",
|
"reset_pin_code_with_password": "С вашата парола можете винаги да нулирате своя ПИН код",
|
||||||
"reset_sqlite": "Нулиране на базата данни SQLite",
|
"reset_sqlite": "Нулиране на базата данни SQLite",
|
||||||
"reset_sqlite_confirmation": "Наистина ли искате да нулирате базата данни SQLite? Ще трябва да излезете от системата и да се впишете отново за нова синхронизация на данните",
|
"reset_sqlite_clear_app_data": "Премахни данните",
|
||||||
|
"reset_sqlite_confirmation": "Наистина ли искате да нулирате данните на приложението? Това ще премахни всички настройки и ще Ви отпише от системата.",
|
||||||
|
"reset_sqlite_confirmation_note": "Бележка: След премахване на данните ще трябва да рестартирате приложението.",
|
||||||
|
"reset_sqlite_done": "Данните на приложението са премахнати. Моля, рестартирайте Immich и се впишете отново.",
|
||||||
"reset_sqlite_success": "Успешно нулиране на базата данни SQLite",
|
"reset_sqlite_success": "Успешно нулиране на базата данни SQLite",
|
||||||
"reset_to_default": "Връщане на фабрични настройки",
|
"reset_to_default": "Връщане на фабрични настройки",
|
||||||
"resolution": "Резолюция",
|
"resolution": "Резолюция",
|
||||||
@@ -1903,6 +1916,7 @@
|
|||||||
"saved_settings": "Запазени настройки",
|
"saved_settings": "Запазени настройки",
|
||||||
"say_something": "Кажи нещо",
|
"say_something": "Кажи нещо",
|
||||||
"scaffold_body_error_occurred": "Възникна грешка",
|
"scaffold_body_error_occurred": "Възникна грешка",
|
||||||
|
"scaffold_body_error_unrecoverable": "Възникна непоправима грешка. Моля, споделете грешката и трасирането на стека в Discord или GitHub, за да можем да Ви помогнем. Ако бъдете посъветвани, може да изчистите данните на приложението.",
|
||||||
"scan": "Сканиранe",
|
"scan": "Сканиранe",
|
||||||
"scan_all_libraries": "Сканирай всички библиотеки",
|
"scan_all_libraries": "Сканирай всички библиотеки",
|
||||||
"scan_library": "Сканирай",
|
"scan_library": "Сканирай",
|
||||||
@@ -1938,6 +1952,7 @@
|
|||||||
"search_filter_ocr": "Търсене нa текст",
|
"search_filter_ocr": "Търсене нa текст",
|
||||||
"search_filter_people_title": "Избери хора",
|
"search_filter_people_title": "Избери хора",
|
||||||
"search_filter_star_rating": "Класация със звезди",
|
"search_filter_star_rating": "Класация със звезди",
|
||||||
|
"search_filter_tags_title": "Изберете етикети",
|
||||||
"search_for": "Търси за",
|
"search_for": "Търси за",
|
||||||
"search_for_existing_person": "Търси съществуващ човек",
|
"search_for_existing_person": "Търси съществуващ човек",
|
||||||
"search_no_more_result": "Няма други резултати",
|
"search_no_more_result": "Няма други резултати",
|
||||||
@@ -2017,6 +2032,9 @@
|
|||||||
"set_profile_picture": "Задайте профилна снимка",
|
"set_profile_picture": "Задайте профилна снимка",
|
||||||
"set_slideshow_to_fullscreen": "Задайте Слайдшоу на цял екран",
|
"set_slideshow_to_fullscreen": "Задайте Слайдшоу на цял екран",
|
||||||
"set_stack_primary_asset": "Задай като основни обекти",
|
"set_stack_primary_asset": "Задай като основни обекти",
|
||||||
|
"setting_image_navigation_enable_subtitle": "Ако е избрано, можете да навигирате към предишна/следваща снимка като натиснете върху лявата/дясната страна на екрана.",
|
||||||
|
"setting_image_navigation_enable_title": "Натисни за навигиране",
|
||||||
|
"setting_image_navigation_title": "Навигиране на снимка",
|
||||||
"setting_image_viewer_help": "При показване на обект първо се зарежда миниатюра, после изображение със средно качество (ако е разрешено) и накрая оригинала (ако е разрешено).",
|
"setting_image_viewer_help": "При показване на обект първо се зарежда миниатюра, после изображение със средно качество (ако е разрешено) и накрая оригинала (ако е разрешено).",
|
||||||
"setting_image_viewer_original_subtitle": "Разреши за да се зарежда оригиналното изображение в пълен размер (голям!). Забрани за да се намали обема на данните (по мрежата и в кеша на устройството).",
|
"setting_image_viewer_original_subtitle": "Разреши за да се зарежда оригиналното изображение в пълен размер (голям!). Забрани за да се намали обема на данните (по мрежата и в кеша на устройството).",
|
||||||
"setting_image_viewer_original_title": "Зареждане на оригинално изображение",
|
"setting_image_viewer_original_title": "Зареждане на оригинално изображение",
|
||||||
@@ -2183,6 +2201,7 @@
|
|||||||
"support": "Поддръжка",
|
"support": "Поддръжка",
|
||||||
"support_and_feedback": "Поддръжка и обратна връзка",
|
"support_and_feedback": "Поддръжка и обратна връзка",
|
||||||
"support_third_party_description": "Вашата инсталация на Immich е пакетирана от трета страна. Проблемите, които изпитвате, може да са причинени от този пакет, затова моля, първо подавайте проблемите си към тях чрез линковете по-долу.",
|
"support_third_party_description": "Вашата инсталация на Immich е пакетирана от трета страна. Проблемите, които изпитвате, може да са причинени от този пакет, затова моля, първо подавайте проблемите си към тях чрез линковете по-долу.",
|
||||||
|
"supporter": "Поддръжник",
|
||||||
"swap_merge_direction": "Размяна посоката на сливане",
|
"swap_merge_direction": "Размяна посоката на сливане",
|
||||||
"sync": "Синхронизиране",
|
"sync": "Синхронизиране",
|
||||||
"sync_albums": "Синхронизиране на албуми",
|
"sync_albums": "Синхронизиране на албуми",
|
||||||
@@ -2196,7 +2215,7 @@
|
|||||||
"tag_assets": "Тагни елементи",
|
"tag_assets": "Тагни елементи",
|
||||||
"tag_created": "Създаден етикет: {tag}",
|
"tag_created": "Създаден етикет: {tag}",
|
||||||
"tag_feature_description": "Разглеждане на снимки и видеоклипове, групирани по теми с логически тагове",
|
"tag_feature_description": "Разглеждане на снимки и видеоклипове, групирани по теми с логически тагове",
|
||||||
"tag_not_found_question": "Не можете да намерите етикет? Създайте такъв <link>тук</link>",
|
"tag_not_found_question": "Не можете да намерите етикет? <link>Създайте нов етикет.</link>",
|
||||||
"tag_people": "Отбележи Хора",
|
"tag_people": "Отбележи Хора",
|
||||||
"tag_updated": "Обновен етикет: {tag}",
|
"tag_updated": "Обновен етикет: {tag}",
|
||||||
"tagged_assets": "Тагнати {count, plural, one {# елемент} other {# елементи}}",
|
"tagged_assets": "Тагнати {count, plural, one {# елемент} other {# елементи}}",
|
||||||
@@ -2294,6 +2313,7 @@
|
|||||||
"unstack_action_prompt": "{count} са разгрупирани",
|
"unstack_action_prompt": "{count} са разгрупирани",
|
||||||
"unstacked_assets_count": "Разкачени {count, plural, one {# елемент} other {# елементи}}",
|
"unstacked_assets_count": "Разкачени {count, plural, one {# елемент} other {# елементи}}",
|
||||||
"unsupported_field_type": "Типа на полето не се поддържа",
|
"unsupported_field_type": "Типа на полето не се поддържа",
|
||||||
|
"unsupported_file_type": "Файлът {file} не може да бъде зареден, защото неговият тип {type} не се поддържа.",
|
||||||
"untagged": "Немаркирани",
|
"untagged": "Немаркирани",
|
||||||
"untitled_workflow": "Работен процес без име",
|
"untitled_workflow": "Работен процес без име",
|
||||||
"up_next": "Следващ",
|
"up_next": "Следващ",
|
||||||
@@ -2320,6 +2340,8 @@
|
|||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Потребление",
|
"usage": "Потребление",
|
||||||
"use_biometric": "Използвай биометрия",
|
"use_biometric": "Използвай биометрия",
|
||||||
|
"use_browser_locale": "Използвай езиковите настройки на браузъра",
|
||||||
|
"use_browser_locale_description": "Формат на дата, време и числа според езиковата настройка на браузъра",
|
||||||
"use_current_connection": "Използвай текущата връзка",
|
"use_current_connection": "Използвай текущата връзка",
|
||||||
"use_custom_date_range": "Използвайте собствен диапазон от дати вместо това",
|
"use_custom_date_range": "Използвайте собствен диапазон от дати вместо това",
|
||||||
"user": "Потребител",
|
"user": "Потребител",
|
||||||
@@ -2373,6 +2395,7 @@
|
|||||||
"viewer_remove_from_stack": "Премахване от опашката",
|
"viewer_remove_from_stack": "Премахване от опашката",
|
||||||
"viewer_stack_use_as_main_asset": "Използвай като основен",
|
"viewer_stack_use_as_main_asset": "Използвай като основен",
|
||||||
"viewer_unstack": "Премахни от опашката",
|
"viewer_unstack": "Премахни от опашката",
|
||||||
|
"visibility": "Видимост",
|
||||||
"visibility_changed": "Видимостта е променена за {count, plural, one {# човек} other {# човека}}",
|
"visibility_changed": "Видимостта е променена за {count, plural, one {# човек} other {# човека}}",
|
||||||
"visual": "Визуален",
|
"visual": "Визуален",
|
||||||
"visual_builder": "Визуален конструктор",
|
"visual_builder": "Визуален конструктор",
|
||||||
|
|||||||
+333
-28
@@ -70,23 +70,23 @@
|
|||||||
"cleared_jobs": "{job} এর জন্য jobs খালি করা হয়েছে",
|
"cleared_jobs": "{job} এর জন্য jobs খালি করা হয়েছে",
|
||||||
"config_set_by_file": "কনফিগ বর্তমানে একটি কনফিগ ফাইল দ্বারা সেট করা আছে",
|
"config_set_by_file": "কনফিগ বর্তমানে একটি কনফিগ ফাইল দ্বারা সেট করা আছে",
|
||||||
"confirm_delete_library": "আপনি কি নিশ্চিত যে আপনি {library} লাইব্রেরি মুছে ফেলতে চান?",
|
"confirm_delete_library": "আপনি কি নিশ্চিত যে আপনি {library} লাইব্রেরি মুছে ফেলতে চান?",
|
||||||
"confirm_delete_library_assets": "আপনি কি নিশ্চিত যে আপনি এই লাইব্রেরিটি মুছে ফেলতে চান? এটি Immich থেকে {count, plural, one {# contained asset} other {all # contained asset}} মুছে ফেলবে এবং পূর্বাবস্থায় ফেরানো যাবে না। ফাইলগুলি ডিস্কে থাকবে।",
|
"confirm_delete_library_assets": "আপনি কি নিশ্চিতভাবে এই লাইব্রেরিটি মুছে ফেলতে চান? এতে Immich থেকে {count, plural, one {#টি অ্যাসেট} other {#টি অ্যাসেট}} মুছে যাবে এবং এই কাজটি পরে আর পূর্বাবস্থায় ফেরানো যাবে না। তবে ফাইলগুলো ডিস্কে থেকে যাবে।",
|
||||||
"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": "আপনি কি নিশ্চিত যে আপনি {user} এর পিন কোড রিসেট করতে চান?",
|
"confirm_user_pin_code_reset": "আপনি কি নিশ্চিত যে আপনি {user}-এর পিন কোড রিসেট করতে চান?",
|
||||||
"copy_config_to_clipboard_description": "বর্তমান সিস্টেম কনফিগারেশন একটি JSON অবজেক্ট হিসেবে ক্লিপবোর্ডে কপি করুন",
|
"copy_config_to_clipboard_description": "বর্তমান সিস্টেম কনফিগারেশনটিকে একটি JSON অবজেক্ট হিসেবে ক্লিপবোর্ডে কপি করুন",
|
||||||
"create_job": "job তৈরি করুন",
|
"create_job": "Job তৈরি করুন",
|
||||||
"cron_expression": "ক্রোন এক্সপ্রেশন",
|
"cron_expression": "Cron এক্সপ্রেশন",
|
||||||
"cron_expression_description": "ক্রোন ফর্ম্যাট ব্যবহার করে স্ক্যানিং ব্যবধান সেট করুন। আরও তথ্যের জন্য দয়া করে দেখুন যেমন <link>Crontab Guru</link>",
|
"cron_expression_description": "Cron ফরম্যাট ব্যবহার করে স্ক্যানিং ইন্টারভ্যাল নির্ধারণ করুন। আরও তথ্যের জন্য দয়া করে <link>Crontab Guru</link> দেখুন",
|
||||||
"cron_expression_presets": "ক্রোন এক্সপ্রেশন প্রিসেট",
|
"cron_expression_presets": "Cron এক্সপ্রেশন প্রিসেট",
|
||||||
"disable_login": "লগইন অক্ষম করুন",
|
"disable_login": "লগইন অক্ষম করুন",
|
||||||
"duplicate_detection_job_description": "অনুরূপ ছবি সনাক্ত করতে সম্পদগুলিতে মেশিন লার্নিং চালান। স্মার্ট অনুসন্ধানের উপর নির্ভর করে",
|
"duplicate_detection_job_description": "সদৃশ ছবি শনাক্ত করতে অ্যাসেটগুলোর উপর মেশিন লার্নিং চালান। এটি Smart Search-এর উপর নির্ভর করে",
|
||||||
"exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন ব্যবহার করে আপনি আপনার লাইব্রেরি স্ক্যান করার সময় ফাইল এবং ফোল্ডারগুলিকে উপেক্ষা করতে পারবেন। যদি আপনার এমন ফোল্ডার থাকে যেখানে এমন ফাইল থাকে যা আপনি আমদানি করতে চান না, যেমন RAW ফাইল।",
|
"exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন ব্যবহার করে লাইব্রেরি স্ক্যান করার সময় নির্দিষ্ট ফাইল ও ফোল্ডার উপেক্ষা করা যায়। এটি তখনই উপকারী যখন কিছু ফোল্ডারে এমন ফাইল থাকে যা আপনি ইমপোর্ট করতে চান না, যেমন RAW ফাইল।",
|
||||||
"export_config_as_json_description": "বর্তমান সিস্টেম কনফিগারেশন একটি JSON ফাইল হিসেবে ডাউনলোড করুন",
|
"export_config_as_json_description": "বর্তমান সিস্টেম কনফিগারেশনটিকে একটি JSON ফাইল হিসেবে ডাউনলোড করুন",
|
||||||
"external_libraries_page_description": "অ্যাডমিন external লাইব্রেরি পেজ",
|
"external_libraries_page_description": "অ্যাডমিন এক্সটার্নাল লাইব্রেরি পেজ",
|
||||||
"face_detection": "মুখ সনাক্তকরণ",
|
"face_detection": "মুখ শনাক্তকরণ",
|
||||||
"face_detection_description": "মেশিন লার্নিং ব্যবহার করে অ্যাসেটে থাকা মুখ/চেহারা গুলি সনাক্ত করুন। ভিডিও গুলির জন্য, শুধুমাত্র থাম্বনেইল বিবেচনা করা হয়। \"রিফ্রেশ\" (পুনরায়) সমস্ত অ্যাসেট প্রক্রিয়া করে। \"রিসেট\" করার মাধ্যমে অতিরিক্তভাবে সমস্ত বর্তমান মুখের ডেটা সাফ করে। \"অনুপস্থিত\" অ্যাসেটগুলিকে সারিবদ্ধ করে যা এখনও প্রক্রিয়া করা হয়নি। সনাক্ত করা মুখগুলিকে ফেসিয়াল রিকগনিশনের জন্য সারিবদ্ধ করা হবে, ফেসিয়াল ডিটেকশন সম্পূর্ণ হওয়ার পরে, বিদ্যমান বা নতুন ব্যক্তিদের মধ্যে গোষ্ঠীবদ্ধ করে।",
|
"face_detection_description": "মেশিন লার্নিং ব্যবহার করে অ্যাসেটে থাকা মুখ/চেহারা শনাক্ত করুন। ভিডিওর ক্ষেত্রে শুধুমাত্র থাম্বনেইল বিবেচনা করা হয়। \"রিফ্রেশ\" সব অ্যাসেট পুনরায় প্রক্রিয়া করে। \"রিসেট\" করলে বিদ্যমান সব মুখের ডেটা মুছে যায়। \"মিসিং\" ওই অ্যাসেটগুলোকে সারিতে যোগ করে যাদেরকে এখনো প্রক্রিয়া করা হয়নি। ফেস ডিটেকশন সম্পন্ন হলে শনাক্ত হওয়া মুখগুলো ফেসিয়াল রিকগনিশনের জন্য সারিতে যোগ করা হবে এবং সেগুলোকে বিদ্যমান বা নতুন ব্যক্তিদের সাথে গ্রুপ করা হবে।",
|
||||||
"facial_recognition_job_description": "শনাক্ত করা মুখগুলিকে মানুষের মধ্যে গোষ্ঠীভুক্ত/গ্রুপ করুন। মুখ সনাক্তকরণ সম্পূর্ণ হওয়ার পরে এই ধাপটি চলে। \"রিসেট\" (পুনরায়) সমস্ত মুখকে ক্লাস্টার করে। \"অনুপস্থিত/মিসিং\" মুখগুলিকে সারিতে রাখে যেগুলো কোনও ব্যক্তিকে এসাইন/বরাদ্দ করা হয়নি।",
|
"facial_recognition_job_description": "শনাক্ত করা মুখগুলিকে মানুষের মধ্যে গোষ্ঠীভুক্ত/গ্রুপ করুন। মুখ সনাক্তকরণ সম্পূর্ণ হওয়ার পরে এই ধাপটি চলে। \"রিসেট\" (পুনরায়) সমস্ত মুখকে ক্লাস্টার করে। \"অনুপস্থিত/মিসিং\" মুখগুলিকে সারিতে রাখে যেগুলো কোনও ব্যক্তিকে এসাইন/বরাদ্দ করা হয়নি।",
|
||||||
"failed_job_command": "কমান্ড {command} কাজের জন্য ব্যর্থ হয়েছে: {job}",
|
"failed_job_command": "কমান্ড {command} কাজের জন্য ব্যর্থ হয়েছে: {job}",
|
||||||
"force_delete_user_warning": "সতর্কতা: এটি ব্যবহারকারী এবং সমস্ত সম্পদ অবিলম্বে সরিয়ে ফেলবে। এটি পূর্বাবস্থায় ফেরানো যাবে না এবং ফাইলগুলি পুনরুদ্ধার করা যাবে না।",
|
"force_delete_user_warning": "সতর্কতা: এটি ব্যবহারকারী এবং সমস্ত সম্পদ অবিলম্বে সরিয়ে ফেলবে। এটি পূর্বাবস্থায় ফেরানো যাবে না এবং ফাইলগুলি পুনরুদ্ধার করা যাবে না।",
|
||||||
@@ -98,9 +98,9 @@
|
|||||||
"image_fullsize_quality_description": "পূর্ণ-আকারের ছবির মান ১-১০০। উচ্চতর হলে ভালো, কিন্তু আরও বড় ফাইল তৈরি হয়।",
|
"image_fullsize_quality_description": "পূর্ণ-আকারের ছবির মান ১-১০০। উচ্চতর হলে ভালো, কিন্তু আরও বড় ফাইল তৈরি হয়।",
|
||||||
"image_fullsize_title": "পূর্ণ-আকারের চিত্র সেটিংস",
|
"image_fullsize_title": "পূর্ণ-আকারের চিত্র সেটিংস",
|
||||||
"image_prefer_embedded_preview": "এম্বেড করা প্রিভিউ পছন্দ করুন",
|
"image_prefer_embedded_preview": "এম্বেড করা প্রিভিউ পছন্দ করুন",
|
||||||
"image_prefer_embedded_preview_setting_description": "যদি পাওয়া যায়, RAW ছবির ভেতরে থাকা প্রিভিউ ব্যবহার করুন। এতে কিছু ছবির রঙ আরও সঠিক দেখা যেতে পারে, তবে মান ক্যামেরার ওপর নির্ভর করে এবং ছবিতে বাড়তি কমপ্রেশন আর্টিফ্যাক্ট দেখা যেতে পারে।",
|
"image_prefer_embedded_preview_setting_description": "RAW ছবিতে থাকা এমবেডেড প্রিভিউগুলোকে ইমেজ প্রসেসিংয়ের ইনপুট হিসেবে ব্যবহার করুন যদি তা উপলভ্য থাকে। এতে কিছু ছবির রঙ আরও সঠিকভাবে পাওয়া যেতে পারে, তবে প্রিভিউয়ের মান ক্যামেরার উপর নির্ভর করে এবং ছবিতে বেশি কমপ্রেশন আর্টিফ্যাক্ট থাকতে পারে।",
|
||||||
"image_prefer_wide_gamut": "প্রশস্ত পরিসর পছন্দ করুন",
|
"image_prefer_wide_gamut": "প্রশস্ত পরিসর পছন্দ করুন",
|
||||||
"image_prefer_wide_gamut_setting_description": "থাম্বনেইলের জন্য Display P3 ব্যবহার করুন। এটি ওয়াইড কালারস্পেস ছবির উজ্জ্বলতা ও প্রাণবন্ত রঙ ভালোভাবে ধরে রাখে, তবে পুরনো ডিভাইস বা ব্রাউজারে ছবিগুলো ভিন্নভাবে দেখা যেতে পারে। sRGB ছবিগুলো রঙের পরিবর্তন এড়াতে sRGB হিসেবেই রাখা হবে।",
|
"image_prefer_wide_gamut_setting_description": "থাম্বনেইলের জন্য Display P3 ব্যবহার করুন। এতে বিস্তীর্ণ কালারস্পেসের ছবিতে ছবির উজ্জ্বলতা ও প্রাণবন্ততা আরও ভালোভাবে বজায় থাকে। তবে পুরোনো ডিভাইস বা পুরোনো ব্রাউজারের দোষে ছবিগুলো কিছুটা ভিন্নভাবে দেখা দিতে পারে। sRGB ছবিগুলোতে রঙের পরিবর্তন এড়াতে sRGB হিসেবেই রাখা হয়।",
|
||||||
"image_preview_description": "স্ট্রিপড মেটাডেটা সহ মাঝারি আকারের ছবি, একটি একক সম্পদ দেখার সময় এবং মেশিন লার্নিংয়ের জন্য ব্যবহৃত হয়",
|
"image_preview_description": "স্ট্রিপড মেটাডেটা সহ মাঝারি আকারের ছবি, একটি একক সম্পদ দেখার সময় এবং মেশিন লার্নিংয়ের জন্য ব্যবহৃত হয়",
|
||||||
"image_preview_quality_description": "১-১০০ এর মধ্যে প্রিভিউ কোয়ালিটি। বেশি হলে ভালো, কিন্তু বড় ফাইল তৈরি হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে। কম মান সেট করলে মেশিন লার্নিং কোয়ালিটির উপর প্রভাব পড়তে পারে।",
|
"image_preview_quality_description": "১-১০০ এর মধ্যে প্রিভিউ কোয়ালিটি। বেশি হলে ভালো, কিন্তু বড় ফাইল তৈরি হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে। কম মান সেট করলে মেশিন লার্নিং কোয়ালিটির উপর প্রভাব পড়তে পারে।",
|
||||||
"image_preview_title": "প্রিভিউ সেটিংস",
|
"image_preview_title": "প্রিভিউ সেটিংস",
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
"import_config_from_json_description": "একটি JSON কনফিগ ফাইল আপলোড করে সিস্টেম কনফিগারেশন ইমপোর্ট করুন।",
|
"import_config_from_json_description": "একটি JSON কনফিগ ফাইল আপলোড করে সিস্টেম কনফিগারেশন ইমপোর্ট করুন।",
|
||||||
"job_concurrency": "{job} কনকারেন্সি",
|
"job_concurrency": "{job} কনকারেন্সি",
|
||||||
"job_created": "Job তৈরি হয়েছে",
|
"job_created": "Job তৈরি হয়েছে",
|
||||||
"job_not_concurrency_safe": "এই কাজটি সমান্তরালভাবে চালানো নিরাপদ নয়",
|
"job_not_concurrency_safe": "এই কাজটি সমান্তরালভাবে চালানো নিরাপদ নয়।",
|
||||||
"job_settings": "কাজের সেটিংস",
|
"job_settings": "কাজের সেটিংস",
|
||||||
"job_settings_description": "কাজের সমান্তরালতা পরিচালনা করুন",
|
"job_settings_description": "কাজের সমান্তরালতা পরিচালনা করুন",
|
||||||
"jobs_delayed": "{jobCount, plural, other {# বিলম্বিত}}",
|
"jobs_delayed": "{jobCount, plural, other {# বিলম্বিত}}",
|
||||||
@@ -137,20 +137,20 @@
|
|||||||
"library_tasks_description": "নতুন এবং/অথবা পরিবর্তিত সম্পদের জন্য বহিরাগত লাইব্রেরি স্ক্যান করুন",
|
"library_tasks_description": "নতুন এবং/অথবা পরিবর্তিত সম্পদের জন্য বহিরাগত লাইব্রেরি স্ক্যান করুন",
|
||||||
"library_updated": "আপডেটকৃত লাইব্রেরি।",
|
"library_updated": "আপডেটকৃত লাইব্রেরি।",
|
||||||
"library_watching_enable_description": "ফাইল পরিবর্তনের জন্য বহিরাগত লাইব্রেরিগুলি দেখুন",
|
"library_watching_enable_description": "ফাইল পরিবর্তনের জন্য বহিরাগত লাইব্রেরিগুলি দেখুন",
|
||||||
"library_watching_settings": "লাইব্রেরি দেখা (পরীক্ষামূলক)",
|
"library_watching_settings": "লাইব্রেরি পর্যবেক্ষণ [পরীক্ষামূলক]",
|
||||||
"library_watching_settings_description": "পরিবর্তিত ফাইলগুলির জন্য স্বয়ংক্রিয়ভাবে নজর রাখুন",
|
"library_watching_settings_description": "পরিবর্তিত ফাইলগুলির জন্য স্বয়ংক্রিয়ভাবে নজর রাখুন",
|
||||||
"logging_enable_description": "লগিং এনাবল/সক্ষম করুন",
|
"logging_enable_description": "লগিং এনাবল/সক্ষম করুন",
|
||||||
"logging_level_description": "সক্রিয় থাকাকালীন, কোন লগ স্তর ব্যবহার করতে হবে।",
|
"logging_level_description": "সক্রিয় থাকাকালীন, কোন লগ স্তর ব্যবহার করতে হবে।",
|
||||||
"logging_settings": "লগিং",
|
"logging_settings": "লগিং",
|
||||||
"machine_learning_availability_checks": "প্রাপ্যতা পরীক্ষা",
|
"machine_learning_availability_checks": "প্রাপ্যতা পরীক্ষা",
|
||||||
"machine_learning_availability_checks_description": "স্বয়ংক্রিয়ভাবে উপলব্ধ মেশিন লার্নিং সার্ভারগুলি সনাক্ত করুন এবং পছন্দ করুন",
|
"machine_learning_availability_checks_description": "উপলভ্য মেশিন লার্নিং সার্ভারগুলো স্বয়ংক্রিয়ভাবে শনাক্ত করে সেগুলোকে অগ্রাধিকার দিন",
|
||||||
"machine_learning_availability_checks_enabled": "প্রাপ্যতা পরীক্ষা সক্ষম করুন",
|
"machine_learning_availability_checks_enabled": "প্রাপ্যতা পরীক্ষা সক্ষম করুন",
|
||||||
"machine_learning_availability_checks_interval": "চেক ব্যবধান",
|
"machine_learning_availability_checks_interval": "চেক ব্যবধান",
|
||||||
"machine_learning_availability_checks_interval_description": "প্রাপ্যতা পরীক্ষাগুলির মধ্যে ব্যবধান মিলিসেকেন্ডে",
|
"machine_learning_availability_checks_interval_description": "প্রাপ্যতা পরীক্ষাগুলির মধ্যে ব্যবধান মিলিসেকেন্ডে",
|
||||||
"machine_learning_availability_checks_timeout": "অনুরোধের সময়সীমা শেষ",
|
"machine_learning_availability_checks_timeout": "অনুরোধের সময়সীমা শেষ",
|
||||||
"machine_learning_availability_checks_timeout_description": "প্রাপ্যতার পরীক্ষার জন্য মিলিসেকেন্ডে সময়সীমা।",
|
"machine_learning_availability_checks_timeout_description": "প্রাপ্যতার পরীক্ষার জন্য মিলিসেকেন্ডে সময়সীমা।",
|
||||||
"machine_learning_clip_model": "CLIP মডেল",
|
"machine_learning_clip_model": "CLIP মডেল",
|
||||||
"machine_learning_clip_model_description": "<link>এখানে</link> তালিকাভুক্ত একটি CLIP মডেলের নাম। মনে রাখবেন, মডেল পরিবর্তনের পর সব ছবির জন্য অবশ্যই ‘Smart Search’ কাজটি আবার চালাতে হবে।",
|
"machine_learning_clip_model_description": "<link>এখানে</link> তালিকাভুক্ত একটি CLIP মডেলের নাম। মনে রাখবেন, মডেল পরিবর্তন করলে সব ছবির জন্য 'Smart Search' জবটি পুনরায় চালাতে হবে।",
|
||||||
"machine_learning_duplicate_detection": "পুনরাবৃত্তি সনাক্তকরণ",
|
"machine_learning_duplicate_detection": "পুনরাবৃত্তি সনাক্তকরণ",
|
||||||
"machine_learning_duplicate_detection_enabled": "পুনরাবৃত্তি শনাক্তকরণ চালু করুন",
|
"machine_learning_duplicate_detection_enabled": "পুনরাবৃত্তি শনাক্তকরণ চালু করুন",
|
||||||
"machine_learning_duplicate_detection_enabled_description": "নিষ্ক্রিয় থাকলেও হুবহু একই সম্পদগুলোর ডুপ্লিকেট সরিয়ে ফেলা হবে।",
|
"machine_learning_duplicate_detection_enabled_description": "নিষ্ক্রিয় থাকলেও হুবহু একই সম্পদগুলোর ডুপ্লিকেট সরিয়ে ফেলা হবে।",
|
||||||
@@ -192,7 +192,7 @@
|
|||||||
"machine_learning_url_description": "মেশিন লার্নিং সার্ভারের URL। যদি একের বেশি URL প্রদান করা হয়, তবে একটি সফলভাবে সাড়া না দেওয়া পর্যন্ত প্রতিটি সার্ভারে এক এক করে চেষ্টা করা হবে (প্রথম থেকে শেষ ক্রমানুসারে)। যে সার্ভারগুলো সাড়া দেবে না, সেগুলো পুনরায় সচল হওয়া পর্যন্ত সাময়িকভাবে উপেক্ষা করা হবে।",
|
"machine_learning_url_description": "মেশিন লার্নিং সার্ভারের URL। যদি একের বেশি URL প্রদান করা হয়, তবে একটি সফলভাবে সাড়া না দেওয়া পর্যন্ত প্রতিটি সার্ভারে এক এক করে চেষ্টা করা হবে (প্রথম থেকে শেষ ক্রমানুসারে)। যে সার্ভারগুলো সাড়া দেবে না, সেগুলো পুনরায় সচল হওয়া পর্যন্ত সাময়িকভাবে উপেক্ষা করা হবে।",
|
||||||
"maintenance_delete_backup": "ব্যাকআপ (Backup)মুছুন",
|
"maintenance_delete_backup": "ব্যাকআপ (Backup)মুছুন",
|
||||||
"maintenance_delete_backup_description": "এই ফাইলটি চিরতরে মুছে ফেলা হবে।",
|
"maintenance_delete_backup_description": "এই ফাইলটি চিরতরে মুছে ফেলা হবে।",
|
||||||
"maintenance_delete_error": "ব্যাকআপ মুছতে ব্যর্থ হয়েছে।",
|
"maintenance_delete_error": "ব্যাকআপ মুছে ফেলতে ব্যর্থ হয়েছে।",
|
||||||
"maintenance_restore_backup": "ব্যাকআপ পুনরুদ্ধার(Restore) করুন",
|
"maintenance_restore_backup": "ব্যাকআপ পুনরুদ্ধার(Restore) করুন",
|
||||||
"maintenance_restore_backup_description": "Immich মুছে ফেলা হবে এবং নির্বাচিত ব্যাকআপ থেকে পুনরুদ্ধার করা হবে। কার্যক্রম চালিয়ে যাওয়ার আগে একটি ব্যাকআপ তৈরি করা হবে।",
|
"maintenance_restore_backup_description": "Immich মুছে ফেলা হবে এবং নির্বাচিত ব্যাকআপ থেকে পুনরুদ্ধার করা হবে। কার্যক্রম চালিয়ে যাওয়ার আগে একটি ব্যাকআপ তৈরি করা হবে।",
|
||||||
"maintenance_restore_backup_different_version": "এই ব্যাকআপটি Immich-এর একটি ভিন্ন সংস্করণের মাধ্যমে তৈরি করা হয়েছিল!",
|
"maintenance_restore_backup_different_version": "এই ব্যাকআপটি Immich-এর একটি ভিন্ন সংস্করণের মাধ্যমে তৈরি করা হয়েছিল!",
|
||||||
@@ -220,7 +220,7 @@
|
|||||||
"map_reverse_geocoding_settings": "রিভার্স জিওকোডিং সেটিংস (Reverse Geocoding Settings)",
|
"map_reverse_geocoding_settings": "রিভার্স জিওকোডিং সেটিংস (Reverse Geocoding Settings)",
|
||||||
"map_settings": "মানচিত্র (Map)",
|
"map_settings": "মানচিত্র (Map)",
|
||||||
"map_settings_description": "মানচিত্রের সেটিংস পরিচালনা করুন (Manage map settings)",
|
"map_settings_description": "মানচিত্রের সেটিংস পরিচালনা করুন (Manage map settings)",
|
||||||
"map_style_description": "একটি style.json ম্যাপ থিমের URL (URL to a style.json map theme)",
|
"map_style_description": "style.json ম্যাপ থিমের URL ঠিকানা",
|
||||||
"memory_cleanup_job": "মেমরি ক্লিনআপ (Memory cleanup)",
|
"memory_cleanup_job": "মেমরি ক্লিনআপ (Memory cleanup)",
|
||||||
"memory_generate_job": "স্মৃতি তৈরি করা(Memory generation)",
|
"memory_generate_job": "স্মৃতি তৈরি করা(Memory generation)",
|
||||||
"metadata_extraction_job": "মেটাডেটা এক্সট্র্যাক্ট করুন (Extract metadata)",
|
"metadata_extraction_job": "মেটাডেটা এক্সট্র্যাক্ট করুন (Extract metadata)",
|
||||||
@@ -231,6 +231,8 @@
|
|||||||
"metadata_settings_description": "মেটাডেটা সেটিংস পরিচালনা করুন (Manage metadata settings)",
|
"metadata_settings_description": "মেটাডেটা সেটিংস পরিচালনা করুন (Manage metadata settings)",
|
||||||
"migration_job": "মাইগ্রেশন (Migration)",
|
"migration_job": "মাইগ্রেশন (Migration)",
|
||||||
"migration_job_description": "অ্যাসেট এবং ফেস থাম্বনেইলগুলোকে সর্বশেষ ফোল্ডার স্ট্রাকচারে মাইগ্রেট করুন। (Migrate thumbnails for assets and faces to the latest folder structure)",
|
"migration_job_description": "অ্যাসেট এবং ফেস থাম্বনেইলগুলোকে সর্বশেষ ফোল্ডার স্ট্রাকচারে মাইগ্রেট করুন। (Migrate thumbnails for assets and faces to the latest folder structure)",
|
||||||
|
"nightly_tasks_cluster_faces_setting_description": "নতুন শনাক্ত হওয়া মুখগুলিতে ফেসিয়াল রিকগনিশন চালান",
|
||||||
|
"nightly_tasks_cluster_new_faces_setting": "নতুন মুখগুলোর গুচ্ছ",
|
||||||
"nightly_tasks_database_cleanup_setting": "ডেটাবেস ক্লিনআপ টাস্কসমূহ (Database cleanup tasks)",
|
"nightly_tasks_database_cleanup_setting": "ডেটাবেস ক্লিনআপ টাস্কসমূহ (Database cleanup tasks)",
|
||||||
"nightly_tasks_database_cleanup_setting_description": "ডেটাবেস থেকে পুরোনো এবং মেয়াদোত্তীর্ণ ডেটা মুছে ফেলুন",
|
"nightly_tasks_database_cleanup_setting_description": "ডেটাবেস থেকে পুরোনো এবং মেয়াদোত্তীর্ণ ডেটা মুছে ফেলুন",
|
||||||
"nightly_tasks_generate_memories_setting": "মেমোরিজ তৈরি করুন (Generate memories)",
|
"nightly_tasks_generate_memories_setting": "মেমোরিজ তৈরি করুন (Generate memories)",
|
||||||
@@ -257,6 +259,20 @@
|
|||||||
"notification_email_secure": "SMTPS (স্মার্ট মেইল ট্রান্সফার প্রোটোকল সিকিউর)",
|
"notification_email_secure": "SMTPS (স্মার্ট মেইল ট্রান্সফার প্রোটোকল সিকিউর)",
|
||||||
"notification_email_secure_description": "SMTPS (SMTP over TLS) ব্যবহার করুন",
|
"notification_email_secure_description": "SMTPS (SMTP over TLS) ব্যবহার করুন",
|
||||||
"notification_email_sent_test_email_button": "টেস্ট ইমেল পাঠান এবং সেভ করুন",
|
"notification_email_sent_test_email_button": "টেস্ট ইমেল পাঠান এবং সেভ করুন",
|
||||||
|
"notification_email_setting_description": "ইমেল নোটিফিকেশন পাঠানোর সেটিংস",
|
||||||
|
"notification_email_test_email": "পরীক্ষামূলক ইমেইল পাঠান",
|
||||||
|
"notification_email_test_email_failed": "পরীক্ষামূলক ইমেল পাঠানো সম্ভব হয়নি, আপনার সেটিংস যাচাই করুন",
|
||||||
|
"notification_email_test_email_sent": "{email}-এ একটি পরীক্ষামূলক ইমেল পাঠানো হয়েছে। অনুগ্রহ করে আপনার ইনবক্স দেখুন।",
|
||||||
|
"notification_email_username_description": "ইমেল সার্ভারে ভেরিফিকেসনের জন্য ব্যবহৃত ইউজারনেম",
|
||||||
|
"notification_enable_email_notifications": "ইমেল নোটিফিকেসন সক্রিয় করুন",
|
||||||
|
"notification_settings": "নোটিফিকেসন সেটিংস",
|
||||||
|
"notification_settings_description": "ইমেইল সহ নোটিফিকেশন সেটিংস পরিচালনা করুন",
|
||||||
|
"oauth_auto_launch": "অটো লঞ্চ",
|
||||||
|
"oauth_auto_launch_description": "লগইন পেজে প্রবেশ করার সাথে সাথে OAuth লগইন প্রক্রিয়াটি স্বয়ংক্রিয়ভাবে শুরু করুন",
|
||||||
|
"oauth_auto_register": "সয়ংক্রিয়ভাবে রেজিস্টার করুন",
|
||||||
|
"oauth_auto_register_description": "OAuth দিয়ে সাইন ইন করার পর নতুন ব্যবহারকারীদের স্বয়ংক্রিয়ভাবে নিবন্ধন করুন",
|
||||||
|
"oauth_button_text": "বাটন টেক্সট",
|
||||||
|
"oauth_client_secret_description": "গোপনীয় ক্লায়েন্টের জন্য প্রয়োজন, অথবা যদি পাবলিক ক্লায়েন্টের জন্য PKCE (Proof Key for Code Exchange) সমর্থিত না হয়।",
|
||||||
"oauth_enable_description": "OAuth-এর মাধ্যমে লগইন করুন",
|
"oauth_enable_description": "OAuth-এর মাধ্যমে লগইন করুন",
|
||||||
"oauth_mobile_redirect_uri": "মোবাইল রিডাইরেক্ট ইউআরআই (URI)",
|
"oauth_mobile_redirect_uri": "মোবাইল রিডাইরেক্ট ইউআরআই (URI)",
|
||||||
"oauth_mobile_redirect_uri_override": "মোবাইল রিডাইরেক্ট ইউআরআই (URI) ওভাররাইড",
|
"oauth_mobile_redirect_uri_override": "মোবাইল রিডাইরেক্ট ইউআরআই (URI) ওভাররাইড",
|
||||||
@@ -295,7 +311,7 @@
|
|||||||
"search_jobs": "জব সার্চ করুন…",
|
"search_jobs": "জব সার্চ করুন…",
|
||||||
"send_welcome_email": "স্বাগত ইমেল পাঠান",
|
"send_welcome_email": "স্বাগত ইমেল পাঠান",
|
||||||
"server_external_domain_settings": "এক্সটার্নাল ডোমেইন (External Domain)",
|
"server_external_domain_settings": "এক্সটার্নাল ডোমেইন (External Domain)",
|
||||||
"server_external_domain_settings_description": "পাবলিক শেয়ারিং লিঙ্কের জন্য ডোমেইন (http(s):// সহ)",
|
"server_external_domain_settings_description": "বাইরের লিঙ্কের জন্য ব্যবহৃত ডোমেইন",
|
||||||
"server_public_users": "পাবলিক ইউজার (Public Users)",
|
"server_public_users": "পাবলিক ইউজার (Public Users)",
|
||||||
"server_public_users_description": "শেয়ার করা অ্যালবামে কোনো ব্যবহারকারীকে যোগ করার সময় সমস্ত ব্যবহারকারীর (নাম এবং ইমেল) তালিকা দেখানো হয়। এটি নিষ্ক্রিয় (Disabled) করা হলে, ব্যবহারকারীর তালিকা শুধুমাত্র অ্যাডমিনদের জন্য উপলব্ধ হবে।",
|
"server_public_users_description": "শেয়ার করা অ্যালবামে কোনো ব্যবহারকারীকে যোগ করার সময় সমস্ত ব্যবহারকারীর (নাম এবং ইমেল) তালিকা দেখানো হয়। এটি নিষ্ক্রিয় (Disabled) করা হলে, ব্যবহারকারীর তালিকা শুধুমাত্র অ্যাডমিনদের জন্য উপলব্ধ হবে।",
|
||||||
"server_settings": "সার্ভার সেটিংস (Server Settings)",
|
"server_settings": "সার্ভার সেটিংস (Server Settings)",
|
||||||
@@ -317,12 +333,26 @@
|
|||||||
"storage_template_migration_description": "পূর্বে আপলোড করা অ্যাসেটগুলোতে বর্তমান <link>{template}</link> প্রয়োগ করুন",
|
"storage_template_migration_description": "পূর্বে আপলোড করা অ্যাসেটগুলোতে বর্তমান <link>{template}</link> প্রয়োগ করুন",
|
||||||
"storage_template_migration_info": "স্টোরেজ টেমপ্লেটটি সমস্ত এক্সটেনশনকে ছোট হাতের অক্ষরে (lowercase) রূপান্তর করবে। টেমপ্লেটের পরিবর্তনগুলো কেবল নতুন অ্যাসেটগুলোর ক্ষেত্রে প্রযোজ্য হবে। পূর্বে আপলোড করা অ্যাসেটগুলোতে এই টেমপ্লেটটি ভূতাপেক্ষভাবে (retroactively) প্রয়োগ করতে <link>{job}</link> রান করুন।",
|
"storage_template_migration_info": "স্টোরেজ টেমপ্লেটটি সমস্ত এক্সটেনশনকে ছোট হাতের অক্ষরে (lowercase) রূপান্তর করবে। টেমপ্লেটের পরিবর্তনগুলো কেবল নতুন অ্যাসেটগুলোর ক্ষেত্রে প্রযোজ্য হবে। পূর্বে আপলোড করা অ্যাসেটগুলোতে এই টেমপ্লেটটি ভূতাপেক্ষভাবে (retroactively) প্রয়োগ করতে <link>{job}</link> রান করুন।",
|
||||||
"storage_template_migration_job": "স্টোরেজ টেমপ্লেট মাইগ্রেশন জব",
|
"storage_template_migration_job": "স্টোরেজ টেমপ্লেট মাইগ্রেশন জব",
|
||||||
"storage_template_more_details": "এই ফিচারটি সম্পর্কে আরও বিস্তারিত জানতে, <template-link>Storage Template</template-link> এবং এর <implications-link>প্রভাবগুলো (implications)</implications-link> দেখুন।",
|
"storage_template_more_details": "এই ফিচার সম্পর্কে আরও বিস্তারিতভাবে জানতে <template-link>Storage Template</template-link> এবং এর <implications-link>প্রভাব</implications-link> দেখুন",
|
||||||
"storage_template_onboarding_description_v2": "এটি সক্রিয় থাকলে, ফিচারটি ব্যবহারকারীর নির্ধারিত টেমপ্লেট অনুযায়ী ফাইলগুলোকে স্বয়ংক্রিয়ভাবে অর্গানাইজ (Auto-organize) করবে। আরও তথ্যের জন্য অনুগ্রহ করে <link>ডকুমেন্টেশন</link> দেখুন।",
|
"storage_template_onboarding_description_v2": "এটি সক্রিয় থাকলে, ফিচারটি ব্যবহারকারীর নির্ধারিত টেমপ্লেট অনুযায়ী ফাইলগুলোকে স্বয়ংক্রিয়ভাবে অর্গানাইজ (Auto-organize) করবে। আরও তথ্যের জন্য অনুগ্রহ করে <link>ডকুমেন্টেশন</link> দেখুন।",
|
||||||
"storage_template_path_length": "আনুমানিক পাথ লেন্থ লিমিট (Path length limit): <b>{length, number}</b>/{limit, number}",
|
"storage_template_path_length": "আনুমানিকভাবে পথের দৈর্ঘ্যের সীমা: <b>{length, number}</b>/{limit, number}",
|
||||||
"storage_template_settings": "স্টোরেজ টেমপ্লেট (Storage Template)",
|
"storage_template_settings": "স্টোরেজ টেমপ্লেট (Storage Template)",
|
||||||
"storage_template_settings_description": "আপলোড করা অ্যাসেটের ফোল্ডার স্ট্রাকচার এবং ফাইল নেম ম্যানেজ করুন",
|
"storage_template_settings_description": "আপলোড করা অ্যাসেটের ফোল্ডার স্ট্রাকচার এবং ফাইল নেম ম্যানেজ করুন",
|
||||||
"storage_template_user_label": "<code>{label}</code> হলো ব্যবহারকারীর স্টোরেজ লেবেল (Storage Label)",
|
"storage_template_user_label": "<code>{label}</code> হলো ব্যবহারকারীর স্টোরেজ লেবেল (Storage Label)",
|
||||||
|
"system_settings": "সিস্টেম সেটিংস",
|
||||||
|
"tag_cleanup_job": "ট্যাগ মুছে ফেলা",
|
||||||
|
"template_email_available_tags": "আপনি আপনার টেমপ্লেটে নিম্নলিখিত ভেরিয়েবলগুলো ব্যবহার করতে পারেন: {tags}",
|
||||||
|
"template_email_if_empty": "টেমপ্লেটটি খালি থাকলে ডিফল্ট ইমেল ব্যবহার করা হবে।",
|
||||||
|
"template_email_invite_album": "ইনভাইট অ্যালবাম টেমপ্লেট",
|
||||||
|
"template_email_preview": "প্রিভিউ",
|
||||||
|
"template_email_settings": "ইমেইল টেমপ্লেট",
|
||||||
|
"template_email_update_album": "অ্যালবাম টেমপ্লেট আপডেট করুন",
|
||||||
|
"template_email_welcome": "স্বাগতম ইমেইল টেমপ্লেট",
|
||||||
|
"template_settings": "নোটিফিকেশন টেমপ্লেট",
|
||||||
|
"template_settings_description": "নোটিফিকেশনের জন্য কাস্টম টেমপ্লেট পরিচালনা করুন",
|
||||||
|
"theme_custom_css_settings": "কাস্টম CSS",
|
||||||
|
"theme_custom_css_settings_description": "ক্যাসকেডিং স্টাইল শীট ব্যবহার করে Immich এর ডিজাইন কাস্টমাইজ করা যায়।",
|
||||||
|
"theme_settings": "থীম সেটিংস",
|
||||||
"theme_settings_description": "ইমিচ (Immich) ওয়েব ইন্টারফেসের কাস্টমাইজেশন ম্যানেজ করুন",
|
"theme_settings_description": "ইমিচ (Immich) ওয়েব ইন্টারফেসের কাস্টমাইজেশন ম্যানেজ করুন",
|
||||||
"thumbnail_generation_job": "থাম্বনেইল তৈরি করুন (Generate Thumbnails)",
|
"thumbnail_generation_job": "থাম্বনেইল তৈরি করুন (Generate Thumbnails)",
|
||||||
"thumbnail_generation_job_description": "প্রতিটি অ্যাসেটের জন্য বড়, ছোট এবং ব্লার (অস্পষ্ট) থাম্বনেইল তৈরি করুন, সেই সাথে প্রতিটি ব্যক্তির জন্যও থাম্বনেইল তৈরি করুন।",
|
"thumbnail_generation_job_description": "প্রতিটি অ্যাসেটের জন্য বড়, ছোট এবং ব্লার (অস্পষ্ট) থাম্বনেইল তৈরি করুন, সেই সাথে প্রতিটি ব্যক্তির জন্যও থাম্বনেইল তৈরি করুন।",
|
||||||
@@ -334,8 +364,283 @@
|
|||||||
"transcoding_acceleration_vaapi": "VA-API (ভিডিও অ্যাক্সিলারেশন এপিআই)",
|
"transcoding_acceleration_vaapi": "VA-API (ভিডিও অ্যাক্সিলারেশন এপিআই)",
|
||||||
"transcoding_accepted_audio_codecs": "গ্রহণযোগ্য অডিও কোডেকসমূহ (Accepted audio codecs)",
|
"transcoding_accepted_audio_codecs": "গ্রহণযোগ্য অডিও কোডেকসমূহ (Accepted audio codecs)",
|
||||||
"transcoding_accepted_audio_codecs_description": "কোন অডিও কোডেকগুলো ট্রানসকোড করার প্রয়োজন নেই তা নির্বাচন করুন। এটি শুধুমাত্র নির্দিষ্ট ট্রানসকোড পলিসির (transcode policies) জন্য ব্যবহৃত হয়।",
|
"transcoding_accepted_audio_codecs_description": "কোন অডিও কোডেকগুলো ট্রানসকোড করার প্রয়োজন নেই তা নির্বাচন করুন। এটি শুধুমাত্র নির্দিষ্ট ট্রানসকোড পলিসির (transcode policies) জন্য ব্যবহৃত হয়।",
|
||||||
"transcoding_accepted_containers": "গ্রহণযোগ্য কন্টেইনারসমূহ (Accepted containers)"
|
"transcoding_accepted_containers": "গ্রহণযোগ্য কন্টেইনারসমূহ (Accepted containers)",
|
||||||
|
"transcoding_accepted_containers_description": "কোন কন্টেইনার ফরম্যাটগুলোকে MP4-এ রিমুক্স করার প্রয়োজন নেই তা নির্বাচন করুন। শুধুমাত্র নির্দিষ্ট ট্রান্সকোড পলিসির জন্য ব্যবহৃত হয়।",
|
||||||
|
"transcoding_accepted_video_codecs": "সমর্থিত ভিডিও কোডেকগুলো",
|
||||||
|
"transcoding_accepted_video_codecs_description": "কোন ভিডিও কোডেকগুলো ট্রান্সকোড করার প্রয়োজন নেই তা নির্বাচন করুন। শুধুমাত্র নির্দিষ্ট ট্রান্সকোড নীতির জন্য ব্যবহৃত হয়।",
|
||||||
|
"transcoding_advanced_options_description": "বেশিরভাগ ব্যবহারকারীর পরিবর্তন করার প্রয়োজন নেই এমন অপশনসমূহ",
|
||||||
|
"transcoding_audio_codec": "অডিও কোডেক",
|
||||||
|
"transcoding_audio_codec_description": "Opus সর্বোচ্চ মানের অপশন, তবে পুরোনো ডিভাইস বা সফটওয়্যারের সাথে এর সামঞ্জস্য কম।",
|
||||||
|
"transcoding_bitrate_description": "সর্বোচ্চ বিটরেটের চেয়ে বেশি বা সমর্থিত ফরম্যাটে নয় এমন ভিডিও",
|
||||||
|
"transcoding_codecs_learn_more": "এখানে ব্যবহৃত পরিভাষা সম্পর্কে আরও জানতে FFmpeg ডকুমেন্টেশন দেখুন, <h264-link>H.264 কোডেক</h264-link>, <hevc-link>HEVC কোডেক</hevc-link> এবং <vp9-link>VP9 কোডেক</vp9-link>।",
|
||||||
|
"transcoding_constant_quality_mode": "নির্দিষ্ট মান মোড",
|
||||||
|
"transcoding_constant_quality_mode_description": "ICQ, CQP-এর চেয়ে ভালো মান দেয়, কিন্তু সব হার্ডওয়্যার অ্যাক্সেলারেশন ডিভাইসে কাজ করে না। এই অপশন চালু থাকলে কোয়ালিটি-ভিত্তিক এনকোডিংয়ে এটি প্রাধান্য পাবে। NVENC এটি সমর্থন করে না, তাই এটি উপেক্ষা করা হবে।",
|
||||||
|
"transcoding_constant_rate_factor": "নির্দিষ্ট রেট ফ্যাক্টর (-crf)",
|
||||||
|
"transcoding_constant_rate_factor_description": "ভিডিওর গুণমানের স্তর। সাধারণ মানগুলো হলো H.264-এর জন্য ২৩, HEVC-এর জন্য ২৮, VP9-এর জন্য ৩১ এবং AV1-এর জন্য ৩৫। মান যত কম হবে, ভিডিওর গুণমান তত উন্নত হবে, তবে ফাইলের আকার তত বড় হবে।",
|
||||||
|
"transcoding_disabled_description": "কোনো ভিডিও ট্রান্সকোড করবেন না, এতে কিছু ক্লায়েন্টে প্লেব্যাক নষ্ট হতে পারে",
|
||||||
|
"transcoding_encoding_options": "এনকোডিং এর অপশনগুলি",
|
||||||
|
"transcoding_encoding_options_description": "এনকোড করা ভিডিওগুলির জন্য কোডেক, রেজোলিউশন, কোয়ালিটি এবং অন্যান্য অপশন সেট করুন",
|
||||||
|
"transcoding_hardware_acceleration": "হার্ডওয়্যার এক্সিলারেসন (Acceleration)",
|
||||||
|
"transcoding_hardware_acceleration_description": "পরীক্ষামূলক: দ্রুততর ট্রান্সকোডিং, কিন্তু একই বিটরেটে গুণমান হ্রাস পেতে পারে",
|
||||||
|
"transcoding_hardware_decoding": "হার্ডওয়্যার ডিকোডিং",
|
||||||
|
"transcoding_hardware_decoding_setting_description": "শুধু এনকোডিং অ্যাক্সিলারেশন করার পরিবর্তে এটি এন্ড-টু-এন্ড অ্যাক্সিলারেশন সক্ষম করে। সব ভিডিওতে কাজ নাও করতে পারে।",
|
||||||
|
"transcoding_max_b_frames": "সর্বোচ্চ বি-ফ্রেম (B-frames)",
|
||||||
|
"transcoding_max_b_frames_description": "মান যত বেশি হবে, কমপ্রেশন তত ভালো হবে কিন্তু এনকোডিং ধীরে চলবে। পুরোনো ডিভাইসে হার্ডওয়্যার অ্যাক্সেলারেশন কাজ নাও করতে পারে। ০ দিলে B-frames বন্ধ থাকবে, -১ দিলে এটি নিজে থেকেই ঠিক হবে।",
|
||||||
|
"transcoding_max_bitrate": "সর্বোচ্চ বিটরেট",
|
||||||
|
"transcoding_max_bitrate_description": "সর্বোচ্চ বিটরেট নির্ধারণ করলে ফাইলের আকার আরও অনুমানযোগ্য হতে পারে, তবে এর ফলে কোয়ালিটির কিছুটা অবনতি ঘটে। 720p-তে, VP9 বা HEVC-এর জন্য সাধারণ মান হলো 2600 kbit/s, অথবা H.264-এর জন্য 4500 kbit/s।এর মান 0 সেট করা হলে এটি বন্ধ থাকে। যখন কোনো একক নির্দিষ্ট করা থাকে না, তখন k (kbit/s-এর জন্য) ধরে নেওয়া হয়; তাই 5000, 5000k, এবং 5M (Mbit/s-এর জন্য) সমতুল্য।",
|
||||||
|
"transcoding_max_keyframe_interval": "সর্বোচ্চ কীফ্রেম ব্যবধান",
|
||||||
|
"transcoding_max_keyframe_interval_description": "কীফ্রেমের মধ্যে সর্বোচ্চ ফ্রেম দূরত্ব নির্ধারণ করে। মান কম হলে কমপ্রেশন দক্ষতা কমে, তবে ভিডিওতে খুঁজে বের করা দ্রুত হয় এবং দ্রুত চলমান দৃশ্যে মানও কিছুটা ভালো হতে পারে। ০ দিলে এই মান স্বয়ংক্রিয়ভাবে নির্ধারিত হয়।",
|
||||||
|
"transcoding_optimal_description": "নির্দিষ্ট রেজোলিউশনের চেয়ে বড় বা সমর্থিত ফরম্যাটে নয় এমন ভিডিও",
|
||||||
|
"transcoding_policy": "ট্রান্সকোড নীতি",
|
||||||
|
"transcoding_policy_description": "ভিডিও কখন ট্রান্সকোড করা হবে তা সেট করুন",
|
||||||
|
"transcoding_preferred_hardware_device": "পছন্দের হার্ডওয়্যার ডিভাইস",
|
||||||
|
"transcoding_preferred_hardware_device_description": "শুধুমাত্র VAAPI এবং QSV-এর ক্ষেত্রে প্রযোজ্য। হার্ডওয়্যার ট্রান্সকোডিংয়ের জন্য ব্যবহৃত dri নোড নির্ধারণ করে।",
|
||||||
|
"transcoding_preset_preset": "প্রিসেট (-preset)",
|
||||||
|
"transcoding_preset_preset_description": "কম্প্রেশন স্পিড। ধীরগতির প্রিসেটগুলো ছোট ফাইল তৈরি করে এবং একটি নির্দিষ্ট বিটরেট লক্ষ্য করার সময় গুণমান বৃদ্ধি করে। VP9 'faster'-এর চেয়ে বেশি গতি উপেক্ষা করে।",
|
||||||
|
"transcoding_reference_frames": "রেফারেন্স ফ্রেম",
|
||||||
|
"transcoding_reference_frames_description": "একটি ফ্রেম কম্প্রেস করার সময় কতটি ফ্রেমকে রেফারেন্স হিসেবে নেওয়া হবে। মান যত বেশি হবে, কমপ্রেশন দক্ষতা তত ভালো হবে, তবে এনকোডিং ধীর হবে। ০ দিলে এই মান স্বয়ংক্রিয়ভাবে নির্ধারিত হবে।",
|
||||||
|
"transcoding_required_description": "শুধুমাত্র অনুমোদিত ফরম্যাটে নেই এমন ভিডিও",
|
||||||
|
"transcoding_settings": "ভিডিও ট্রান্সকোডিং সেটিংস",
|
||||||
|
"transcoding_settings_description": "নির্ধারণ করুন কোন ভিডিওগুলোকে ট্রান্সকোড করতে হবে এবং কিভাবে প্রক্রিয়া করতে হবে",
|
||||||
|
"transcoding_target_resolution": "টার্গেট রেজোলিউশন",
|
||||||
|
"transcoding_target_resolution_description": "উচ্চ রেজোলিউশন বেশি বিস্তারিত রাখে, কিন্তু এনকোডিং ধীরে হয়, ফাইল বড় হয়, এবং অ্যাপ ধীর প্রতিক্রিয়া করতে পারে।",
|
||||||
|
"transcoding_temporal_aq": "টেম্পোরাল AQ",
|
||||||
|
"transcoding_temporal_aq_description": "শুধুমাত্র NVENC-এর ক্ষেত্রে প্রযোজ্য। টেম্পোরাল অ্যাডাপটিভ কোয়ান্টাইজেশন (Adaptive Quantization) উচ্চ-বিস্তারিত ও স্বল্প-গতির দৃশ্যের মান বৃদ্ধি করে। পুরোনো ডিভাইসগুলোর সাথে সামঞ্জস্যপূর্ণ নাও হতে পারে।",
|
||||||
|
"transcoding_threads": "থ্রেড",
|
||||||
|
"transcoding_threads_description": "উচ্চ মানে এনকোডিং দ্রুত হয়, কিন্তু সার্ভার কম কাজ করতে পারে। CPU কোরের বেশি মান দেওয়া উচিত নয়। ০ দিলে সর্বাধিক ব্যবহার হবে।",
|
||||||
|
"transcoding_tone_mapping": "টোন-ম্যাপিং",
|
||||||
|
"transcoding_tone_mapping_description": "এইচডিআর (HDR) ভিডিওকে এসডিআর (SDR)-এ রূপান্তর করার সময় এর বাহ্যিক রূপ অক্ষুণ্ণ রাখার চেষ্টা করা হয়। প্রতিটি অ্যালগরিদম রঙ, ডিটেইল এবং উজ্জ্বলতার জন্য ভিন্ন ভিন্ন সমন্বয় করে। হেবল ডিটেইল, মোবিয়াস রঙ এবং রাইনহার্ড উজ্জ্বলতা অক্ষুণ্ণ রাখে।",
|
||||||
|
"transcoding_transcode_policy": "ট্রান্সকোড নীতি",
|
||||||
|
"transcoding_transcode_policy_description": "কখন একটি ভিডিও ট্রান্সকোড করা হবে তার নীতিমালা। HDR ভিডিও এবং YUV 4:2:0 ব্যতীত অন্য পিক্সেল ফরম্যাটের ভিডিও সর্বদা ট্রান্সকোড করা হবে (যদি না ট্রান্সকোডিং বন্ধ করা থাকে)।",
|
||||||
|
"transcoding_two_pass_encoding": "টু-পাস এনকোডিং",
|
||||||
|
"transcoding_two_pass_encoding_setting_description": "আরও উন্নত মানের এনকোডেড ভিডিও তৈরি করতে দুই ধাপে ট্রান্সকোড করুন। যখন সর্বোচ্চ বিটরেট সক্রিয় করা হয় (যা H.264 এবং HEVC-এর সাথে কাজ করার জন্য আবশ্যক), তখন এই মোডটি সর্বোচ্চ বিটরেটের উপর ভিত্তি করে একটি বিটরেট রেঞ্জ ব্যবহার করে এবং CRF উপেক্ষা করে। VP9-এর ক্ষেত্রে, সর্বোচ্চ বিটরেট নিষ্ক্রিয় থাকলেও CRF ব্যবহার করা যেতে পারে।",
|
||||||
|
"transcoding_video_codec": "ভিডিও কোডেক",
|
||||||
|
"transcoding_video_codec_description": "VP9 উচ্চ কর্মদক্ষতা সম্পন্ন এবং ওয়েবের সাথে সামঞ্জস্যপূর্ণ, কিন্তু ট্রান্সকোড করতে বেশি সময় লাগে। HEVC-এর কর্মক্ষমতাও প্রায় একই রকম, কিন্তু এর ওয়েব সামঞ্জস্যতা কম। H.264 ব্যাপকভাবে সামঞ্জস্যপূর্ণ এবং দ্রুত ট্রান্সকোড করা যায়, কিন্তু এটি অনেক বড় ফাইল তৈরি করে। AV1 সবচেয়ে কর্মদক্ষ কোডেক, কিন্তু পুরোনো ডিভাইসগুলোতে এর সমর্থন নেই।",
|
||||||
|
"trash_enabled_description": "ট্র্যাশ ফিচার চালু করুন",
|
||||||
|
"trash_number_of_days": "দিনের সংখ্যা",
|
||||||
|
"trash_number_of_days_description": "ট্র্যাশে থাকা অ্যাসেটগুলো স্থায়ীভাবে মুছে ফেলার আগে রাখার দিন সংখ্যা",
|
||||||
|
"trash_settings": "ট্র্যাশ সেটিংস",
|
||||||
|
"trash_settings_description": "ট্র্যাশ সেটিংস পরিচালনা করুন",
|
||||||
|
"unlink_all_oauth_accounts": "সকল OAuth অ্যাকাউন্ট আনলিঙ্ক করুন",
|
||||||
|
"unlink_all_oauth_accounts_description": "নতুন প্রোভাইডারে মাইগ্রেট করার আগে সব OAuth অ্যাকাউন্ট আনলিঙ্ক করুন।",
|
||||||
|
"unlink_all_oauth_accounts_prompt": "আপনি কি সব OAuth অ্যাকাউন্ট আনলিঙ্ক করতে নিশ্চিত? এটি প্রতিটি ব্যবহারকারীর OAuth আইডি রিসেট করে দেবে এবং এটি আর পূর্বাবস্থায় ফেরানো যাবে না।",
|
||||||
|
"user_cleanup_job": "ইউজার ক্লিনআপ",
|
||||||
|
"user_delete_delay": "<b>{user}</b>-এর অ্যাকাউন্ট এবং অ্যাসেট {delay, plural, one {# day} other {# days}} পর স্থায়ীভাবে মুছে ফেলার জন্য নির্ধারিত হবে।",
|
||||||
|
"user_delete_delay_settings": "মুছে ফেলার সময় বিলম্ব",
|
||||||
|
"user_delete_delay_settings_description": "অ্যাকাউন্ট এবং অ্যাসেট মুছে ফেলার পর কত দিনের মধ্যে স্থায়ীভাবে মুছে ফেলা হবে। ব্যবহারকারী মুছে ফেলার কাজ মধ্যরাতে চালানো হয় এবং দেখা হয় কোন ব্যবহারকারী স্থায়ীভাবে মুছে ফেলার জন্য প্রস্তুত। এই সেটিং পরিবর্তন করলে পরবর্তী এক্সিকিউশনের সময় তা প্রযোজ্য হবে।",
|
||||||
|
"user_delete_immediately": "<b>{user}</b>-এর অ্যাকাউন্ট এবং অ্যাসেট স্থায়ীভাবে মুছে ফেলার জন্য <b>immediately</b> কিউতে অন্তর্ভুক্ত করা হবে।",
|
||||||
|
"user_delete_immediately_checkbox": "ব্যবহারকারী ও অ্যাসেট তৎক্ষণাৎ মুছে ফেলার জন্য কিউ",
|
||||||
|
"user_details": "ব্যবহারকারী তথ্য",
|
||||||
|
"user_management": "ব্যবহারকারী ম্যানেজমেন্ট",
|
||||||
|
"user_password_has_been_reset": "ব্যবহারকারীর পাসওয়ার্ড রিসেট করা হয়েছে:",
|
||||||
|
"user_password_reset_description": "দয়া করে ব্যবহারকারীর জন্য সাময়িক পাসওয়ার্ড দিন এবং জানিয়ে দিন যে তারা পরবর্তী লগইনে পাসওয়ার্ড পরিবর্তন করবেন।",
|
||||||
|
"user_restore_description": "<b>{user}</b> এর অ্যাকাউন্ট পুনরুদ্ধার করা হবে।",
|
||||||
|
"user_restore_scheduled_removal": "ব্যবহারকারী পুনরুদ্ধার করুন - মুছে ফেলার জন্য নির্ধারিত তারিখ:{date, date, long}",
|
||||||
|
"user_settings": "ব্যবহারকারী সেটিংস",
|
||||||
|
"user_settings_description": "ব্যবহারকারী সেটিংস ম্যানেজ করুন",
|
||||||
|
"user_successfully_removed": "সফলভাবে ইউজার {email}-কে সরিয়ে দেওয়া হয়েছে।",
|
||||||
|
"version_check_enabled_description": "ভার্সন যাচাই চালু করুন",
|
||||||
|
"version_check_implications": "ভার্সন চেক ফিচারটি github.com-এর সঙ্গে নিয়মিত সংযোগের ওপর নির্ভরশীল",
|
||||||
|
"version_check_settings": "ভার্সন যাচাই",
|
||||||
|
"version_check_settings_description": "নতুন ভার্সনের নোটিফিকেশন চালু/বন্ধ করুন",
|
||||||
|
"video_conversion_job": "ভিডিও ট্রান্সকোড করুন",
|
||||||
|
"video_conversion_job_description": "ব্রাউজার এবং ডিভাইসে আরও ভালোভাবে চলার জন্য ভিডিও ট্রান্সকোড করুন"
|
||||||
},
|
},
|
||||||
|
"admin_email": "অ্যাডমিনের ইমেইল",
|
||||||
|
"admin_password": "অ্যাডমিনের পাসওয়ার্ড",
|
||||||
|
"administration": "অ্যাডমিন",
|
||||||
|
"advanced": "অ্যাডভান্সড",
|
||||||
|
"age_months": "বয়স {months, plural, one {# month} other {# months}}",
|
||||||
|
"age_year_months": "বয়স ১ বছর, {months, plural, one {# month} other {# months}}",
|
||||||
|
"album_added": "অ্যালবাম যুক্ত করা হয়েছে",
|
||||||
|
"album_added_notification_setting_description": "শেয়ার করা অ্যালবামে যুক্ত হলে ইমেইল নোটিফিকেশন পান",
|
||||||
|
"album_cover_updated": "অ্যালবামের কভার আপডেট হয়েছে",
|
||||||
|
"album_delete_confirmation": "আপনি কি সত্যিই অ্যালবাম {album} মুছে ফেলতে চান?",
|
||||||
|
"album_delete_confirmation_description": "অ্যালবামটি শেয়ার করা থাকলেও অন্য ব্যবহারকারীরা আর এটি অ্যাক্সেস করতে পারবেন না।",
|
||||||
|
"album_info_updated": "অ্যালবামের তথ্য আপডেট করা হয়েছে",
|
||||||
|
"album_leave": "অ্যালবাম থেকে বেরিয়ে যেতে চান ?",
|
||||||
|
"album_leave_confirmation": "আপনি কি নিশ্চিত যে আপনি {album} ছেড়ে যেতে চান?",
|
||||||
|
"album_name": "অ্যালবামের নাম",
|
||||||
|
"album_options": "অ্যালবামের অপশনসমূহ",
|
||||||
|
"album_remove_user": "ব্যবহারকারী সরাতে চান?",
|
||||||
|
"album_remove_user_confirmation": "আপনি কি নিশ্চিত যে আপনি {user}-কে সরাতে চান?",
|
||||||
|
"album_share_no_users": "এই অ্যালবামটি সব ব্যবহারকারীর সঙ্গে শেয়ার করা হয়েছে, বা শেয়ার করার জন্য কোনো ব্যবহারকারী নেই।",
|
||||||
|
"album_updated": "অ্যালবাম আপডেট করা হয়েছে",
|
||||||
|
"album_updated_setting_description": "নতুন অ্যাসেট যুক্ত হলে শেয়ার করা অ্যালবামের জন্য ইমেইল নোটিফিকেশন পান",
|
||||||
|
"album_user_left": "বাম {album}",
|
||||||
|
"album_user_removed": "{user} কে সরানো হয়েছে",
|
||||||
|
"album_with_link_access": "লিঙ্ক থাকা যে কেউ এই অ্যালবামের ছবি ও মানুষজনকে দেখতে পারবে।",
|
||||||
|
"albums": "অ্যালবামসমূহ",
|
||||||
|
"all": "সব",
|
||||||
|
"all_albums": "সকল অ্যালবামসমূহ",
|
||||||
|
"all_people": "সব ব্যবহারকারী",
|
||||||
|
"all_videos": "সব ভিডিও",
|
||||||
|
"allow_dark_mode": "ডার্ক মোড চালু করুন",
|
||||||
|
"allow_edits": "এডিটের অনুমতি দিন",
|
||||||
|
"allow_public_user_to_download": "সাধারণ ব্যবহারকারী ডাউনলোড করতে পারবে",
|
||||||
|
"allow_public_user_to_upload": "সাধারণ ব্যবহারকারী আপলোড করতে পারবে",
|
||||||
|
"anti_clockwise": "বিপরীত দিক",
|
||||||
|
"api_key": "API কী",
|
||||||
|
"api_key_description": "এই মান একবারই দেখানো হবে। উইন্ডো বন্ধ করার আগে অবশ্যই এটি কপি করুন।",
|
||||||
|
"api_key_empty": "API কী-এর নাম খালি রাখা যাবে না",
|
||||||
|
"api_keys": "API কী সমূহ",
|
||||||
|
"app_settings": "অ্যাপ সেটিংস",
|
||||||
|
"appears_in": "v1.106.4 থেকে, অ্যাসেট সাইডবারে ব্যবহার হয় ‘[albums]-এ উপস্থিত’ বোঝাতে",
|
||||||
|
"archive": "আর্কাইভ",
|
||||||
|
"archive_or_unarchive_photo": "ফটো আর্কাইভ অথবা আনআর্কাইভ করুন",
|
||||||
|
"archive_size": "আর্কাইভ সাইজ",
|
||||||
|
"archive_size_description": "ডাউনলোডের আর্কাইভ সাইজ নির্ধারণ করুন (GiB)",
|
||||||
|
"are_these_the_same_person": "এরা কি একই ব্যক্তি?",
|
||||||
|
"are_you_sure_to_do_this": "আপনি কি নিশ্চিত যে আপনি এটি করতে চান?",
|
||||||
|
"asset_added_to_album": "অ্যালবামে যুক্ত করা হয়েছে",
|
||||||
|
"asset_adding_to_album": "অ্যালবামে যুক্ত করা হচ্ছে…",
|
||||||
|
"asset_description_updated": "অ্যাসেটের বিবরণ আপডেট করা হয়েছে",
|
||||||
|
"asset_filename_is_offline": "{filename} অ্যাসেটটি বর্তমানে অফলাইন",
|
||||||
|
"asset_has_unassigned_faces": "অ্যাসেটটির কিছু মুখ অনির্ধারিত ফেস রয়েছে",
|
||||||
|
"asset_hashing": "হ্যাশিং চলছে…",
|
||||||
|
"asset_offline": "অ্যাসেট বর্তমানে অফলাইন",
|
||||||
|
"asset_offline_description": "এই এক্সটার্নাল অ্যাসেটটি এখন ডিস্কে নেই। সহায়তার জন্য Immich অ্যাডমিনিস্ট্রেটরের সাথে যোগাযোগ করুন।",
|
||||||
|
"asset_skipped": "এড়ানো হয়েছে",
|
||||||
|
"asset_skipped_in_trash": "ট্র্যাশে",
|
||||||
|
"asset_uploaded": "আপলোড সম্পন্ন",
|
||||||
|
"asset_uploading": "আপলোড চলছে…",
|
||||||
|
"assets": "অ্যাসেটসমূহ",
|
||||||
|
"assets_added_to_album_count": "অ্যালবামে {count, plural, one {# asset} other {# assets}} যুক্ত করা হয়েছে",
|
||||||
|
"assets_moved_to_trash_count": "{count, plural, one {# asset} other {# assets}} ট্র্যাশে সরানো হয়েছে",
|
||||||
|
"assets_permanently_deleted_count": "{count, plural, one {# asset} other {# assets}} স্থায়ীভাবে মুছে ফেলা হয়েছে",
|
||||||
|
"assets_removed_count": "{count, plural, one {# asset} other {# assets}} সরানো হয়েছে",
|
||||||
|
"assets_restore_confirmation": "আপনি কি সত্যিই আপনার সব ট্র্যাশ করা অ্যাসেট পুনরুদ্ধার করতে চান? এটি পূর্বাবস্থায় ফিরানো যাবে না। তবে অফলাইন অ্যাসেট এইভাবে পুনরুদ্ধার হবে না।",
|
||||||
|
"assets_restored_count": "{count, plural, one {# asset} other {# assets}} পুনরুদ্ধার করা হয়েছে",
|
||||||
|
"assets_trashed_count": "{count, plural, one {# asset} other {# assets}} ট্র্যাশে পাঠানো হয়েছে",
|
||||||
|
"assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} আগেই অ্যালবামে যুক্ত ছিল",
|
||||||
|
"authorized_devices": "অনুমোদিত ডিভাইস",
|
||||||
|
"back": "ফিরে যান",
|
||||||
|
"back_close_deselect": "ফিরে যান, বন্ধ করুন বা নির্বাচন বাতিল করুন",
|
||||||
|
"backward": "পিছনে",
|
||||||
|
"birthdate_saved": "জন্ম তারিখ সংরক্ষণ সম্পন্ন",
|
||||||
|
"birthdate_set_description": "একটি ছবির সময়ে ব্যক্তির বয়স গণনার জন্য জন্ম তারিখ ব্যবহার করা হয়।",
|
||||||
|
"blurred_background": "ব্লারড ব্যাকগ্রাউন্ড",
|
||||||
|
"bugs_and_feature_requests": "বাগ ও ফিচার রিকোয়েস্ট",
|
||||||
|
"build": "বিল্ড",
|
||||||
|
"build_image": "বিল্ড ইমেজ",
|
||||||
|
"bulk_delete_duplicates_confirmation": "আপনি কি সত্যিই {count, plural, one {# duplicate asset} other {# duplicate assets}} একসাথে মুছে ফেলতে চান? প্রতিটি গ্রুপের সবচেয়ে বড় অ্যাসেট রাখা হবে, বাকিগুলো স্থায়ীভাবে মুছে যাবে। এটি পূর্বাবস্থায় ফিরানো যাবে না!",
|
||||||
|
"bulk_keep_duplicates_confirmation": "আপনি কি সত্যিই {count, plural, one {# duplicate asset} other {# duplicate assets}} রাখতে চান? সব ডুপ্লিকেট গ্রুপ ঠিক করা হবে, কোনো কিছু মুছে ফেলা হবে না।",
|
||||||
|
"bulk_trash_duplicates_confirmation": "আপনি কি সত্যিই {count, plural, one {# duplicate asset} other {# duplicate assets}} একসাথে ট্র্যাশ করতে চান? প্রতিটি গ্রুপের সবচেয়ে বড় অ্যাসেট রাখা হবে, বাকিগুলো ট্র্যাশে যাবে।",
|
||||||
|
"buy": "Immich ক্রয় করুন",
|
||||||
|
"camera": "ক্যামেরা",
|
||||||
|
"camera_brand": "ক্যামেরা ব্র্যান্ড",
|
||||||
|
"camera_model": "ক্যামেরা মডেল",
|
||||||
|
"cancel": "বাতিল",
|
||||||
|
"cancel_search": "সার্চ বন্ধ করুন",
|
||||||
|
"cannot_merge_people": "ব্যক্তিদের একত্র করা সম্ভব নয়",
|
||||||
|
"cannot_undo_this_action": "এই কাজ পূর্বাবস্থায় ফেরানো যাবে না!",
|
||||||
|
"cannot_update_the_description": "বিবরণ পরিবর্তন সম্ভব নয়",
|
||||||
|
"change_date": "তারিখ পরিবর্তন",
|
||||||
|
"change_expiration_time": "মেয়াদ শেষের সময় পরিবর্তন",
|
||||||
|
"change_location": "লোকেশন পরিবর্তন",
|
||||||
|
"change_name": "নাম পরিবর্তন করুন",
|
||||||
|
"change_name_successfully": "নাম সফলভাবে পরিবর্তন হয়েছে",
|
||||||
|
"change_password": "পাসওয়ার্ড পরিবর্তন করুন",
|
||||||
|
"change_password_description": "আপনি হয়তো প্রথমবার লগইন করছেন বা পাসওয়ার্ড পরিবর্তনের অনুরোধ করেছেন। নিচে নতুন পাসওয়ার্ড দিন।",
|
||||||
|
"change_your_password": "আপনার পাসওয়ার্ড পরিবর্তন করুন",
|
||||||
|
"changed_visibility_successfully": "ভিসিবিলিটি সফলভাবে পরিবর্তন হয়েছে",
|
||||||
|
"check_logs": "লগ দেখুন",
|
||||||
|
"choose_matching_people_to_merge": "একত্র করার জন্য মিল থাকা ব্যক্তিদের নির্বাচন করুন",
|
||||||
|
"city": "শহর",
|
||||||
|
"clear": "মুছুন",
|
||||||
|
"clear_all": "সব মুছুন",
|
||||||
|
"clear_all_recent_searches": "সাম্প্রতিক সব অনুসন্ধান পরিষ্কার করুন",
|
||||||
|
"clear_message": "মেসেজ পরিষ্কার করুন",
|
||||||
|
"clear_value": "ভ্যালু মুছুন",
|
||||||
|
"clockwise": "ঘড়ির কাঁটার দিকে",
|
||||||
|
"close": "বন্ধ",
|
||||||
|
"collapse": "সংকুচিত করুন",
|
||||||
|
"collapse_all": "সব সংকুচিত",
|
||||||
|
"color": "রং",
|
||||||
|
"color_theme": "কালার থিম",
|
||||||
|
"comment_deleted": "মন্তব্য মুছে ফেলা হয়েছে",
|
||||||
|
"comment_options": "মন্তব্য অপশন",
|
||||||
|
"comments_and_likes": "মন্তব্য ও লাইক",
|
||||||
|
"comments_are_disabled": "মন্তব্য বন্ধ করা হয়েছে",
|
||||||
|
"confirm": "নিশ্চিত",
|
||||||
|
"confirm_admin_password": "অ্যাডমিন পাসওয়ার্ড পুনরায় লিখুন",
|
||||||
|
"confirm_delete_shared_link": "আপনি কি নিশ্চিত যে আপনি এই শেয়ার করা লিঙ্কটি মুছে ফেলতে চান?",
|
||||||
|
"confirm_keep_this_delete_others": "স্ট্যাকের এই অ্যাসেট ছাড়া সব অন্যান্য অ্যাসেট মুছে যাবে। আপনি কি নিশ্চিত যে আপনি চালিয়ে যেতে চান?",
|
||||||
|
"confirm_password": "পাসওয়ার্ড পুনরায় লিখুন",
|
||||||
|
"contain": "মাপমত",
|
||||||
|
"context": "প্রসঙ্গ",
|
||||||
|
"continue": "এগিয়ে যান",
|
||||||
|
"copied_image_to_clipboard": "ছবি ক্লিপবোর্ডে কপি হয়েছে।",
|
||||||
|
"copied_to_clipboard": "ক্লিপবোর্ডে কপি হয়েছে!",
|
||||||
|
"copy_error": "Error-টি কপি করুন",
|
||||||
|
"copy_file_path": "ফাইল পাথ কপি",
|
||||||
|
"copy_image": "ছবি কপি",
|
||||||
|
"copy_link": "লিঙ্ক কপি",
|
||||||
|
"copy_link_to_clipboard": "ক্লিপবোর্ডে লিঙ্ক কপি করুন",
|
||||||
|
"copy_password": "পাসওয়ার্ড কপি করুন",
|
||||||
|
"copy_to_clipboard": "ক্লিপবোর্ডে কপি করুন",
|
||||||
|
"country": "দেশ",
|
||||||
|
"cover": "সম্পূর্ণভাবে",
|
||||||
|
"covers": "কভারস",
|
||||||
|
"create": "তৈরি করুন",
|
||||||
|
"create_album": "অ্যালবাম তৈরি",
|
||||||
|
"create_library": "লাইব্রেরি তৈরি",
|
||||||
|
"create_link": "লিঙ্ক তৈরি",
|
||||||
|
"create_link_to_share": "শেয়ার লিঙ্ক তৈরি",
|
||||||
|
"create_link_to_share_description": "লিঙ্কের মাধ্যমে সবাই নির্বাচিত ছবি দেখতে পারবে",
|
||||||
|
"create_new_person": "নতুন ব্যক্তি যোগ করুন",
|
||||||
|
"create_new_person_hint": "নির্বাচিত অ্যাসেট নতুন ব্যক্তির সঙ্গে যুক্ত করুন",
|
||||||
|
"create_new_user": "নতুন ব্যবহারকারী যোগ করুন",
|
||||||
|
"create_tag": "ট্যাগ তৈরি",
|
||||||
|
"create_tag_description": "নতুন ট্যাগ তৈরি করুন। নেস্টেড ট্যাগের ক্ষেত্রে সম্পূর্ণ পাথ - ফরওয়ার্ড স্ল্যাশসহ দিন।",
|
||||||
|
"create_user": "ব্যবহারকারী যোগ করুন",
|
||||||
|
"created": "যোগ করা হয়েছে",
|
||||||
|
"current_device": "চলতি ডিভাইস",
|
||||||
|
"custom_locale": "কাস্টম লোকেল",
|
||||||
|
"custom_locale_description": "নির্বাচিত ভাষা এবং অঞ্চলের ভিত্তিতে তারিখ, সময় এবং সংখ্যা ফরম্যাট করুন",
|
||||||
|
"dark": "ডার্ক",
|
||||||
|
"date_after": "এর পরের তারিখ",
|
||||||
|
"date_and_time": "তারিখ এবং সময়",
|
||||||
|
"date_before": "এর আগের তারিখ",
|
||||||
|
"date_of_birth_saved": "জন্ম তারিখ সফলভাবে সংরক্ষণ করা হয়েছে",
|
||||||
|
"delete": "মুছুন",
|
||||||
|
"delete_album": "অ্যালবাম মুছুন",
|
||||||
|
"delete_api_key_prompt": "আপনি কি সত্যিই এই API key মুছে ফেলতে চান?",
|
||||||
|
"delete_duplicates_confirmation": "আপনি কি সত্যিই এই ডুপ্লিকেটগুলো স্থায়ীভাবে মুছতে চান?",
|
||||||
|
"delete_key": "key মুছুন",
|
||||||
|
"delete_library": "লাইব্রেরি মুছুন",
|
||||||
|
"delete_link": "লিঙ্ক মুছুন",
|
||||||
|
"delete_others": "বাকিগুলো মুছুন",
|
||||||
|
"delete_shared_link": "শেয়ার করা লিঙ্ক মুছুন",
|
||||||
|
"delete_tag": "ট্যাগ মুছুন",
|
||||||
|
"delete_tag_confirmation_prompt": "আপনি কি নিশ্চিতভাবে {tagName} ট্যাগটি মুছতে চান?",
|
||||||
|
"delete_user": "ইউজার মুছুন",
|
||||||
|
"deleted_shared_link": "শেয়ার করা লিঙ্কটি মুছুন",
|
||||||
|
"deletes_missing_assets": "ডিস্ক থেকে হারানো অ্যাসেটগুলো মুছে",
|
||||||
|
"description": "বিবরন",
|
||||||
|
"details": "বিস্তারিত",
|
||||||
|
"direction": "দিকনির্দেশনা",
|
||||||
|
"disabled": "নিষ্ক্রিয়",
|
||||||
|
"disallow_edits": "সম্পাদনা করার অনুমতি দেবেন না",
|
||||||
|
"discord": "ডিসকর্ড",
|
||||||
|
"discover": "ডিসকভার",
|
||||||
|
"dismiss_all_errors": "সব ত্রুটি বাতিল করুন",
|
||||||
|
"dismiss_error": "ত্রুটি বাতিল করুন",
|
||||||
|
"display_options": "ডিসপ্লে অপশন",
|
||||||
|
"display_order": "ডিসপ্লে অর্ডার",
|
||||||
|
"display_original_photos": "অরিজিনাল ছবি দেখান",
|
||||||
|
"display_original_photos_setting_description": "অরিজিনাল অ্যাসেটটি ওয়েব-সামঞ্জস্যপূর্ণ (web-compatible) হলে অ্যাসেট দেখার সময় থাম্বনেইলের পরিবর্তে মূল ফটোটি প্রদর্শন করতে অগ্রাধিকার দিন। এর ফলে ফটো প্রদর্শনের গতি কিছুটা ধীর হতে পারে।",
|
||||||
|
"do_not_show_again": "এই মেসেজটি আর দেখাবেন না",
|
||||||
|
"documentation": "সহায়ক নির্দেশিকা",
|
||||||
|
"done": "সম্পন্ন",
|
||||||
|
"download": "ডাউনলোড",
|
||||||
|
"download_include_embedded_motion_videos": "এমবেডেড ভিডিও",
|
||||||
|
"download_include_embedded_motion_videos_description": "মোশন ফটোর (motion photos) মধ্যে থাকা ভিডিওগুলোকে আলাদা ফাইল হিসেবে অন্তর্ভুক্ত করুন",
|
||||||
|
"download_settings": "ডাউনলোড",
|
||||||
|
"download_settings_description": "অ্যাসেট ডাউনলোডের সেটিংস পরিচালনা করুন",
|
||||||
|
"open_in_browser": "ব্রাউজারে ওপেন করুন",
|
||||||
|
"user_usage_stats": "অ্যাকাউন্ট ব্যবহারের পরিসংখ্যান",
|
||||||
|
"user_usage_stats_description": "অ্যাকাউন্ট ব্যবহারের পরিসংখ্যান দেখুন",
|
||||||
"yes": "হ্যাঁ",
|
"yes": "হ্যাঁ",
|
||||||
"you_dont_have_any_shared_links": "আপনার কোনো শেয়ার করা লিঙ্ক নেই (You don't have any shared links)",
|
"you_dont_have_any_shared_links": "আপনার কোনো শেয়ার করা লিঙ্ক নেই (You don't have any shared links)",
|
||||||
"your_wifi_name": "আপনার ওয়াই-ফাই এর নাম (Your Wi-Fi name)",
|
"your_wifi_name": "আপনার ওয়াই-ফাই এর নাম (Your Wi-Fi name)",
|
||||||
|
|||||||
+38
-23
@@ -372,7 +372,7 @@
|
|||||||
"transcoding_audio_codec": "Còdec d'àudio",
|
"transcoding_audio_codec": "Còdec d'àudio",
|
||||||
"transcoding_audio_codec_description": "Opus és l'opció de màxima qualitat, però té menor compatibilitat amb dispositius o programari antics.",
|
"transcoding_audio_codec_description": "Opus és l'opció de màxima qualitat, però té menor compatibilitat amb dispositius o programari antics.",
|
||||||
"transcoding_bitrate_description": "Vídeos superiors a la taxa de bits màxima o que no tenen un format acceptat",
|
"transcoding_bitrate_description": "Vídeos superiors a la taxa de bits màxima o que no tenen un format acceptat",
|
||||||
"transcoding_codecs_learn_more": "Per obtenir més informació sobre la terminologia utilitzada, consulteu la documentació de FFmpeg per al <h264-link> còdec H.264</h264-link>, <hevc-link> còdec HEVC</hevc-link> i <vp9-link> còdec VP9</vp9-link>.",
|
"transcoding_codecs_learn_more": "Per obtenir més informació sobre la terminologia utilitzada, consulteu la documentació de FFmpeg per al <h264-link>còdec H.264</h264-link>, <hevc-link>còdec HEVC</hevc-link> i <vp9-link>còdec VP9</vp9-link>.",
|
||||||
"transcoding_constant_quality_mode": "Mode de qualitat constant",
|
"transcoding_constant_quality_mode": "Mode de qualitat constant",
|
||||||
"transcoding_constant_quality_mode_description": "ICQ és millor que CQP, però alguns dispositius d'acceleració de maquinari no admeten aquest mode. Establir aquesta opció preferirà el mode especificat quan utilitzeu la codificació basada en la qualitat. Ignorat per NVENC perquè no és compatible amb ICQ.",
|
"transcoding_constant_quality_mode_description": "ICQ és millor que CQP, però alguns dispositius d'acceleració de maquinari no admeten aquest mode. Establir aquesta opció preferirà el mode especificat quan utilitzeu la codificació basada en la qualitat. Ignorat per NVENC perquè no és compatible amb ICQ.",
|
||||||
"transcoding_constant_rate_factor": "Factor de taxa constant (-crf)",
|
"transcoding_constant_rate_factor": "Factor de taxa constant (-crf)",
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
"transcoding_tone_mapping": "Mapeig de to",
|
"transcoding_tone_mapping": "Mapeig de to",
|
||||||
"transcoding_tone_mapping_description": "Intenta preservar l'aspecte dels vídeos HDR quan es converteixen a SDR. Cada algorisme fa diferents compensacions pel color, el detall i la brillantor. Hable conserva els detalls, Mobius conserva el color i Reinhard conserva la brillantor.",
|
"transcoding_tone_mapping_description": "Intenta preservar l'aspecte dels vídeos HDR quan es converteixen a SDR. Cada algorisme fa diferents compensacions pel color, el detall i la brillantor. Hable conserva els detalls, Mobius conserva el color i Reinhard conserva la brillantor.",
|
||||||
"transcoding_transcode_policy": "Política de transcodificació",
|
"transcoding_transcode_policy": "Política de transcodificació",
|
||||||
"transcoding_transcode_policy_description": "Política sobre quan s'ha de transcodificar un vídeo. Els vídeos HDR sempre es transcodificaran (excepte si la transcodificació està desactivada).",
|
"transcoding_transcode_policy_description": "Política sobre quan s'ha de transcodificar un vídeo. Els vídeos HDR i els vídeos amb un format de píxel diferent a YUV 4:2:0 sempre es transcodificaran (excepte si la transcodificació està desactivada).",
|
||||||
"transcoding_two_pass_encoding": "Codificació de dues passades",
|
"transcoding_two_pass_encoding": "Codificació de dues passades",
|
||||||
"transcoding_two_pass_encoding_setting_description": "Transcodifica en dos passos per produir vídeos millor codificats. Quan la taxa de bits màxima està habilitada (necessari perquè funcioni amb H.264 i HEVC), aquest mode utilitza un interval de velocitat de bits basat en la taxa de bits màxima i ignora CRF. Per a VP9, es pot utilitzar CRF si la taxa de bits màxima està desactivada.",
|
"transcoding_two_pass_encoding_setting_description": "Transcodifica en dos passos per produir vídeos millor codificats. Quan la taxa de bits màxima està habilitada (necessari perquè funcioni amb H.264 i HEVC), aquest mode utilitza un interval de velocitat de bits basat en la taxa de bits màxima i ignora CRF. Per a VP9, es pot utilitzar CRF si la taxa de bits màxima està desactivada.",
|
||||||
"transcoding_video_codec": "Còdec de video",
|
"transcoding_video_codec": "Còdec de video",
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
"user_successfully_removed": "L'usuari {email} s'ha eliminat correctament.",
|
"user_successfully_removed": "L'usuari {email} s'ha eliminat correctament.",
|
||||||
"users_page_description": "Pàgina d'usuaris de l'administrador",
|
"users_page_description": "Pàgina d'usuaris de l'administrador",
|
||||||
"version_check_enabled_description": "Activa la comprovació de la versió",
|
"version_check_enabled_description": "Activa la comprovació de la versió",
|
||||||
"version_check_implications": "La funció de comprovació de versions depèn de comunicacions periòdiques amb github.com",
|
"version_check_implications": "La funció de comprovació de versions depèn de comunicacions periòdiques amb {server}",
|
||||||
"version_check_settings": "Comprovació de versió",
|
"version_check_settings": "Comprovació de versió",
|
||||||
"version_check_settings_description": "Activa/desactiva la notificació de nova versió",
|
"version_check_settings_description": "Activa/desactiva la notificació de nova versió",
|
||||||
"video_conversion_job": "Transcodificació de vídeos",
|
"video_conversion_job": "Transcodificació de vídeos",
|
||||||
@@ -866,13 +866,14 @@
|
|||||||
"crop_aspect_ratio_fixed": "Fixat",
|
"crop_aspect_ratio_fixed": "Fixat",
|
||||||
"crop_aspect_ratio_free": "Lliure",
|
"crop_aspect_ratio_free": "Lliure",
|
||||||
"crop_aspect_ratio_original": "Original",
|
"crop_aspect_ratio_original": "Original",
|
||||||
|
"crop_aspect_ratio_square": "Quadrat",
|
||||||
"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_pin_code": "Codi PIN actual",
|
||||||
"current_server_address": "Adreça actual del servidor",
|
"current_server_address": "Adreça actual del servidor",
|
||||||
"custom_date": "Data personalitzada",
|
"custom_date": "Data personalitzada",
|
||||||
"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ó seleccionades",
|
||||||
"custom_url": "URL personalitzada",
|
"custom_url": "URL personalitzada",
|
||||||
"cutoff_date_description": "Manté fotos des de l'últim…",
|
"cutoff_date_description": "Manté fotos des de l'últim…",
|
||||||
"cutoff_day": "{count, plural, one {dia} other {dies}}",
|
"cutoff_day": "{count, plural, one {dia} other {dies}}",
|
||||||
@@ -880,7 +881,7 @@
|
|||||||
"daily_title_text_date": "E, dd MMM",
|
"daily_title_text_date": "E, dd MMM",
|
||||||
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
||||||
"dark": "Fosc",
|
"dark": "Fosc",
|
||||||
"dark_theme": "Canviar a tema fosc",
|
"dark_theme": "Canvia a tema fosc",
|
||||||
"date": "Data",
|
"date": "Data",
|
||||||
"date_after": "Data posterior a",
|
"date_after": "Data posterior a",
|
||||||
"date_and_time": "Data i hora",
|
"date_and_time": "Data i hora",
|
||||||
@@ -891,12 +892,8 @@
|
|||||||
"day": "Dia",
|
"day": "Dia",
|
||||||
"days": "Dies",
|
"days": "Dies",
|
||||||
"deduplicate_all": "Desduplica-ho tot",
|
"deduplicate_all": "Desduplica-ho tot",
|
||||||
"deduplication_criteria_1": "Mida d'imatge en bytes",
|
"default_locale": "Configuració regional predeterminada",
|
||||||
"deduplication_criteria_2": "Quantitat de dades EXIF",
|
"default_locale_description": "Format de dades i números en funció de la configuració local",
|
||||||
"deduplication_info": "Informació de deduplicació",
|
|
||||||
"deduplication_info_description": "Per preseleccionar recursos automàticament i eliminar els duplicats de manera massiva, ens fixem en:",
|
|
||||||
"default_locale": "Localització predeterminada",
|
|
||||||
"default_locale_description": "Format de dates i números segons la configuració del navegador",
|
|
||||||
"delete": "Esborrar",
|
"delete": "Esborrar",
|
||||||
"delete_action_confirmation_message": "Segur que vols eliminar aquest recurs? Aquesta acció el mourà a la paperera del servidor, i et preguntarà si el vols eliminar localment",
|
"delete_action_confirmation_message": "Segur que vols eliminar aquest recurs? Aquesta acció el mourà a la paperera del servidor, i et preguntarà si el vols eliminar localment",
|
||||||
"delete_action_prompt": "{count} eliminats",
|
"delete_action_prompt": "{count} eliminats",
|
||||||
@@ -972,7 +969,7 @@
|
|||||||
"downloading_media": "Descàrrega multimèdia",
|
"downloading_media": "Descàrrega multimèdia",
|
||||||
"drop_files_to_upload": "Deixeu els fitxers a qualsevol lloc per pujar-los",
|
"drop_files_to_upload": "Deixeu els fitxers a qualsevol lloc per pujar-los",
|
||||||
"duplicates": "Duplicats",
|
"duplicates": "Duplicats",
|
||||||
"duplicates_description": "Resol cada grup indicant, si n'hi ha, quins són duplicats",
|
"duplicates_description": "Resol cada grup indicant, si n'hi ha, quins són duplicats.",
|
||||||
"duration": "Durada",
|
"duration": "Durada",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
"edit_album": "Edita l'àlbum",
|
"edit_album": "Edita l'àlbum",
|
||||||
@@ -994,7 +991,7 @@
|
|||||||
"edit_location_dialog_title": "Ubicació",
|
"edit_location_dialog_title": "Ubicació",
|
||||||
"edit_name": "Edita el nom",
|
"edit_name": "Edita el nom",
|
||||||
"edit_people": "Edita la gent",
|
"edit_people": "Edita la gent",
|
||||||
"edit_tag": "Editar etiqueta",
|
"edit_tag": "Edita etiqueta",
|
||||||
"edit_title": "Edita títol",
|
"edit_title": "Edita títol",
|
||||||
"edit_user": "Edita l'usuari",
|
"edit_user": "Edita l'usuari",
|
||||||
"edit_workflow": "Edita el flux de treball",
|
"edit_workflow": "Edita el flux de treball",
|
||||||
@@ -1009,6 +1006,8 @@
|
|||||||
"editor_edits_applied_success": "Les modificacions s'han aplicat correctament",
|
"editor_edits_applied_success": "Les modificacions s'han aplicat correctament",
|
||||||
"editor_flip_horizontal": "Capgira horitzontalment",
|
"editor_flip_horizontal": "Capgira horitzontalment",
|
||||||
"editor_flip_vertical": "Capgira verticalment",
|
"editor_flip_vertical": "Capgira verticalment",
|
||||||
|
"editor_handle_corner": "{corner, select, top_left {Top-left} top_right {Top-right} bottom_left {Bottom-left} bottom_right {Bottom-right} other {A}} cantó per agafar",
|
||||||
|
"editor_handle_edge": "{edge, select, top {Top} bottom {Bottom} left {Left} right {Right} other {An}} cantó per agafar",
|
||||||
"editor_orientation": "Orientació",
|
"editor_orientation": "Orientació",
|
||||||
"editor_reset_all_changes": "Reiniciar canvis",
|
"editor_reset_all_changes": "Reiniciar canvis",
|
||||||
"editor_rotate_left": "Rota 90º al contrari de les agulles",
|
"editor_rotate_left": "Rota 90º al contrari de les agulles",
|
||||||
@@ -1074,6 +1073,7 @@
|
|||||||
"failed_to_update_notification_status": "Error en actualitzar l'estat de les notificacions",
|
"failed_to_update_notification_status": "Error en actualitzar l'estat de les notificacions",
|
||||||
"incorrect_email_or_password": "Correu electrònic o contrasenya incorrectes",
|
"incorrect_email_or_password": "Correu electrònic o contrasenya incorrectes",
|
||||||
"library_folder_already_exists": "Aquesta ruta d'importació ja existeix.",
|
"library_folder_already_exists": "Aquesta ruta d'importació ja existeix.",
|
||||||
|
"page_not_found": "Pàgina no trobada",
|
||||||
"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",
|
||||||
"profile_picture_transparent_pixels": "Les fotos de perfil no poden tenir píxels transparents. Per favor, feu zoom in, mogueu la imatge o ambdues.",
|
"profile_picture_transparent_pixels": "Les fotos de perfil no poden tenir píxels transparents. Per favor, feu zoom in, mogueu la imatge o ambdues.",
|
||||||
"quota_higher_than_disk_size": "Heu establert una quota més gran que la mida de disc",
|
"quota_higher_than_disk_size": "Heu establert una quota més gran que la mida de disc",
|
||||||
@@ -1169,7 +1169,7 @@
|
|||||||
"exif_bottom_sheet_description_error": "No s'ha pogut actualitzar la descripció",
|
"exif_bottom_sheet_description_error": "No s'ha pogut actualitzar la descripció",
|
||||||
"exif_bottom_sheet_details": "DETALLS",
|
"exif_bottom_sheet_details": "DETALLS",
|
||||||
"exif_bottom_sheet_location": "UBICACIÓ",
|
"exif_bottom_sheet_location": "UBICACIÓ",
|
||||||
"exif_bottom_sheet_no_description": "Sense descrioció",
|
"exif_bottom_sheet_no_description": "Sense descripció",
|
||||||
"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",
|
||||||
"exit_slideshow": "Surt de la presentació de diapositives",
|
"exit_slideshow": "Surt de la presentació de diapositives",
|
||||||
@@ -1218,6 +1218,7 @@
|
|||||||
"filter_description": "Condicions per filtrar els actius de destinació",
|
"filter_description": "Condicions per filtrar els actius de destinació",
|
||||||
"filter_people": "Filtra persones",
|
"filter_people": "Filtra persones",
|
||||||
"filter_places": "Filtrar per llocs",
|
"filter_places": "Filtrar per llocs",
|
||||||
|
"filter_tags": "Filtrar etiquetes",
|
||||||
"filters": "Filtres",
|
"filters": "Filtres",
|
||||||
"find_them_fast": "Trobeu-los ràpidament pel nom amb la cerca",
|
"find_them_fast": "Trobeu-los ràpidament pel nom amb la cerca",
|
||||||
"first": "Primer",
|
"first": "Primer",
|
||||||
@@ -1385,9 +1386,11 @@
|
|||||||
"library_page_sort_title": "Títol de l'àlbum",
|
"library_page_sort_title": "Títol de l'àlbum",
|
||||||
"licenses": "Llicències",
|
"licenses": "Llicències",
|
||||||
"light": "Llum",
|
"light": "Llum",
|
||||||
|
"light_theme": "Canviar a tema clar",
|
||||||
"like": "M'agrada",
|
"like": "M'agrada",
|
||||||
"like_deleted": "M'agrada suprimit",
|
"like_deleted": "M'agrada suprimit",
|
||||||
"link_motion_video": "Enllaçar vídeo en moviment",
|
"link_motion_video": "Enllaçar vídeo en moviment",
|
||||||
|
"link_to_docs": "Per més informació, mirar la <link>documentation</link>.",
|
||||||
"link_to_oauth": "Enllaç a OAuth",
|
"link_to_oauth": "Enllaç a OAuth",
|
||||||
"linked_oauth_account": "Compte OAuth enllaçat",
|
"linked_oauth_account": "Compte OAuth enllaçat",
|
||||||
"list": "Llista",
|
"list": "Llista",
|
||||||
@@ -1649,6 +1652,7 @@
|
|||||||
"only_favorites": "Només preferits",
|
"only_favorites": "Només preferits",
|
||||||
"open": "Obrir",
|
"open": "Obrir",
|
||||||
"open_calendar": "Obrir el calendari",
|
"open_calendar": "Obrir el calendari",
|
||||||
|
"open_in_browser": "Obre al navegador",
|
||||||
"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",
|
||||||
@@ -1808,9 +1812,8 @@
|
|||||||
"rate_asset": "Valorar Recurs",
|
"rate_asset": "Valorar Recurs",
|
||||||
"rating": "Valoració",
|
"rating": "Valoració",
|
||||||
"rating_clear": "Esborrar valoració",
|
"rating_clear": "Esborrar valoració",
|
||||||
"rating_count": "{count, plural, one {# estrella} other {# estrelles}}",
|
"rating_count": "{count, plural, =0 {Unrated} one {# estrella} other {# estrelles}}",
|
||||||
"rating_description": "Mostrar la valoració EXIF al panell d'informació",
|
"rating_description": "Mostrar la valoració EXIF al panell d'informació",
|
||||||
"rating_set": "Valoració establerta a {rating, plural, one {# estrella} other {# estrelles}}",
|
|
||||||
"reaction_options": "Opcions de reacció",
|
"reaction_options": "Opcions de reacció",
|
||||||
"read_changelog": "Llegeix el registre de canvis",
|
"read_changelog": "Llegeix el registre de canvis",
|
||||||
"readonly_mode_disabled": "Mode de només lectura desactivat",
|
"readonly_mode_disabled": "Mode de només lectura desactivat",
|
||||||
@@ -1882,7 +1885,10 @@
|
|||||||
"reset_pin_code_success": "Codi PIN reiniciat correctament",
|
"reset_pin_code_success": "Codi PIN reiniciat correctament",
|
||||||
"reset_pin_code_with_password": "Sempre pots reiniciar el codi PIN amb la teva contrasenya",
|
"reset_pin_code_with_password": "Sempre pots reiniciar el codi PIN amb la teva contrasenya",
|
||||||
"reset_sqlite": "Reiniciar base de dades SQLite",
|
"reset_sqlite": "Reiniciar base de dades SQLite",
|
||||||
"reset_sqlite_confirmation": "Segur que vols reiniciar la base de dades SQLite? Hauràs de tancar la sessió i tornar a accedir per a resincronitzar les dades",
|
"reset_sqlite_clear_app_data": "Netejar dada",
|
||||||
|
"reset_sqlite_confirmation": "Segur que vols esborrar les dades de l'aplicació? Això eliminarà tota la configuració i tancarà la sessió.",
|
||||||
|
"reset_sqlite_confirmation_note": "Nota: Hauràs de reiniciar l'app després d'eliminar.",
|
||||||
|
"reset_sqlite_done": "Les dades de l'app s'han netejat. Si us plau, reinicia l'app Immich i inicia sessió de nou.",
|
||||||
"reset_sqlite_success": "S'ha reiniciat la base de dades correctament",
|
"reset_sqlite_success": "S'ha reiniciat la base de dades correctament",
|
||||||
"reset_to_default": "Restableix els valors predeterminats",
|
"reset_to_default": "Restableix els valors predeterminats",
|
||||||
"resolution": "Resolució",
|
"resolution": "Resolució",
|
||||||
@@ -1910,6 +1916,7 @@
|
|||||||
"saved_settings": "Configuració guardada",
|
"saved_settings": "Configuració guardada",
|
||||||
"say_something": "Digues quelcom",
|
"say_something": "Digues quelcom",
|
||||||
"scaffold_body_error_occurred": "S'ha produït un error",
|
"scaffold_body_error_occurred": "S'ha produït un error",
|
||||||
|
"scaffold_body_error_unrecoverable": "S'ha produït un error irrecuperable. Comparteix l'error i el rastre de la pila a Discord o GitHub perquè puguem ajudar-te. Si us ho aconsella, podeu esborrar les dades de l'aplicació a continuació.",
|
||||||
"scan": "Escaneja",
|
"scan": "Escaneja",
|
||||||
"scan_all_libraries": "Escanejar totes les llibreries",
|
"scan_all_libraries": "Escanejar totes les llibreries",
|
||||||
"scan_library": "Escaneja",
|
"scan_library": "Escaneja",
|
||||||
@@ -1945,6 +1952,7 @@
|
|||||||
"search_filter_ocr": "Buscar per OCR",
|
"search_filter_ocr": "Buscar per OCR",
|
||||||
"search_filter_people_title": "Selecciona persones",
|
"search_filter_people_title": "Selecciona persones",
|
||||||
"search_filter_star_rating": "Classificació per estrelles",
|
"search_filter_star_rating": "Classificació per estrelles",
|
||||||
|
"search_filter_tags_title": "Seleccionar etiquetes",
|
||||||
"search_for": "Cercar",
|
"search_for": "Cercar",
|
||||||
"search_for_existing_person": "Busca una persona existent",
|
"search_for_existing_person": "Busca una persona existent",
|
||||||
"search_no_more_result": "No més resultats",
|
"search_no_more_result": "No més resultats",
|
||||||
@@ -2024,6 +2032,9 @@
|
|||||||
"set_profile_picture": "Establir imatge de perfil",
|
"set_profile_picture": "Establir imatge de perfil",
|
||||||
"set_slideshow_to_fullscreen": "Mostra Diapositives en pantalla completa",
|
"set_slideshow_to_fullscreen": "Mostra Diapositives en pantalla completa",
|
||||||
"set_stack_primary_asset": "Estableix com a actiu principal",
|
"set_stack_primary_asset": "Estableix com a actiu principal",
|
||||||
|
"setting_image_navigation_enable_subtitle": "Si està activat, pots navegar a la imatge anterior/següent tocant la quarta part més esquerra/dreta de la pantalla.",
|
||||||
|
"setting_image_navigation_enable_title": "Toca per navegar",
|
||||||
|
"setting_image_navigation_title": "Navegació d'imatges",
|
||||||
"setting_image_viewer_help": "El visor de detalls carrega primer la miniatura petita, després carrega la vista prèvia de mida mitjana (si està habilitada), finalment carrega l'original (si està habilitada).",
|
"setting_image_viewer_help": "El visor de detalls carrega primer la miniatura petita, després carrega la vista prèvia de mida mitjana (si està habilitada), finalment carrega l'original (si està habilitada).",
|
||||||
"setting_image_viewer_original_subtitle": "Activa per carregar la imatge en resolució original (molt gran!). Desactiva per reduir el consum de dades (tant de xarxa com de memòria cau).",
|
"setting_image_viewer_original_subtitle": "Activa per carregar la imatge en resolució original (molt gran!). Desactiva per reduir el consum de dades (tant de xarxa com de memòria cau).",
|
||||||
"setting_image_viewer_original_title": "Carrega la imatge original",
|
"setting_image_viewer_original_title": "Carrega la imatge original",
|
||||||
@@ -2191,20 +2202,20 @@
|
|||||||
"support_and_feedback": "Suport i comentaris",
|
"support_and_feedback": "Suport i comentaris",
|
||||||
"support_third_party_description": "La vostra instal·lació immich la va empaquetar un tercer. Els problemes que experimenteu poden ser causats per aquest paquet així que, si us plau, plantegeu els poblemes amb ells en primer lloc mitjançant els enllaços següents.",
|
"support_third_party_description": "La vostra instal·lació immich la va empaquetar un tercer. Els problemes que experimenteu poden ser causats per aquest paquet així que, si us plau, plantegeu els poblemes amb ells en primer lloc mitjançant els enllaços següents.",
|
||||||
"supporter": "Contribuïdor",
|
"supporter": "Contribuïdor",
|
||||||
"swap_merge_direction": "Canvia la direcció d'unió",
|
"swap_merge_direction": "Intercanvia la direcció d'unió",
|
||||||
"sync": "Sincronitza",
|
"sync": "Sincronitza",
|
||||||
"sync_albums": "Sincronitzar àlbums",
|
"sync_albums": "Sincronitza àlbums",
|
||||||
"sync_albums_manual_subtitle": "Sincronitza tots els vídeos i fotos penjats amb els àlbums de còpia de seguretat seleccionats",
|
"sync_albums_manual_subtitle": "Sincronitza tots els vídeos i fotos penjats amb els àlbums de còpia de seguretat seleccionats",
|
||||||
"sync_local": "Sincronitza Local",
|
"sync_local": "Sincronitza localment",
|
||||||
"sync_remote": "Sincronitza Remot",
|
"sync_remote": "Sincronitza remotament",
|
||||||
"sync_status": "Estat de sincronització",
|
"sync_status": "Estat de la incronització",
|
||||||
"sync_status_subtitle": "Observa i administra el sistema de sincronització",
|
"sync_status_subtitle": "Observa i administra el sistema de sincronització",
|
||||||
"sync_upload_album_setting_subtitle": "Creeu i pugeu les seves fotos i vídeos als àlbums seleccionats a Immich",
|
"sync_upload_album_setting_subtitle": "Creeu i pugeu les seves fotos i vídeos als àlbums seleccionats a Immich",
|
||||||
"tag": "Etiqueta",
|
"tag": "Etiqueta",
|
||||||
"tag_assets": "Etiquetar actius",
|
"tag_assets": "Etiquetar actius",
|
||||||
"tag_created": "Etiqueta creada: {tag}",
|
"tag_created": "Etiqueta creada: {tag}",
|
||||||
"tag_feature_description": "Exploreu fotos i vídeos agrupats per temes d'etiquetes lògiques",
|
"tag_feature_description": "Exploreu fotos i vídeos agrupats per temes d'etiquetes lògiques",
|
||||||
"tag_not_found_question": "No trobeu una etiqueta? <link>Crear una nova etiqueta</link>",
|
"tag_not_found_question": "No trobeu una etiqueta? <link>Crear una nova etiqueta.</link>",
|
||||||
"tag_people": "Etiquetar personas",
|
"tag_people": "Etiquetar personas",
|
||||||
"tag_updated": "Etiqueta actualizada: {tag}",
|
"tag_updated": "Etiqueta actualizada: {tag}",
|
||||||
"tagged_assets": "{count, plural, one {#Etiquetat} other {#Etiquetats}} {count, plural, one {# actiu} other {# actius}}",
|
"tagged_assets": "{count, plural, one {#Etiquetat} other {#Etiquetats}} {count, plural, one {# actiu} other {# actius}}",
|
||||||
@@ -2302,6 +2313,7 @@
|
|||||||
"unstack_action_prompt": "{count} sense apilar",
|
"unstack_action_prompt": "{count} sense apilar",
|
||||||
"unstacked_assets_count": "No apilat {count, plural, one {# recurs} other {# recursos}}",
|
"unstacked_assets_count": "No apilat {count, plural, one {# recurs} other {# recursos}}",
|
||||||
"unsupported_field_type": "Tipus de camp no suportat",
|
"unsupported_field_type": "Tipus de camp no suportat",
|
||||||
|
"unsupported_file_type": "No es pot carregar el fitxer {file} perquè el seu tipus de fitxer {type} no és compatible.",
|
||||||
"untagged": "Sense etiqueta",
|
"untagged": "Sense etiqueta",
|
||||||
"untitled_workflow": "Automatització sense títol",
|
"untitled_workflow": "Automatització sense títol",
|
||||||
"up_next": "Pròxim",
|
"up_next": "Pròxim",
|
||||||
@@ -2328,6 +2340,8 @@
|
|||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Ús",
|
"usage": "Ús",
|
||||||
"use_biometric": "Empra biometria",
|
"use_biometric": "Empra biometria",
|
||||||
|
"use_browser_locale": "Fer servir la localització del navegador",
|
||||||
|
"use_browser_locale_description": "Formatejar dates, hores i números segons la llengua i regió del navegador",
|
||||||
"use_current_connection": "Utilitza la connexió actual",
|
"use_current_connection": "Utilitza 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",
|
||||||
@@ -2381,6 +2395,7 @@
|
|||||||
"viewer_remove_from_stack": "Elimina de la pila",
|
"viewer_remove_from_stack": "Elimina de la pila",
|
||||||
"viewer_stack_use_as_main_asset": "Fes servir com a element principal",
|
"viewer_stack_use_as_main_asset": "Fes servir com a element principal",
|
||||||
"viewer_unstack": "Desapila",
|
"viewer_unstack": "Desapila",
|
||||||
|
"visibility": "Visibilitat",
|
||||||
"visibility_changed": "La visibilitat ha canviat per {count, plural, one {# persona} other {# persones}}",
|
"visibility_changed": "La visibilitat ha canviat per {count, plural, one {# persona} other {# persones}}",
|
||||||
"visual": "Visual",
|
"visual": "Visual",
|
||||||
"visual_builder": "Constructor visual",
|
"visual_builder": "Constructor visual",
|
||||||
|
|||||||
+37
-18
@@ -40,7 +40,7 @@
|
|||||||
"add_to_albums_count": "Přidat do alb ({count})",
|
"add_to_albums_count": "Přidat do alb ({count})",
|
||||||
"add_to_bottom_bar": "Přidat do",
|
"add_to_bottom_bar": "Přidat do",
|
||||||
"add_to_shared_album": "Přidat do sdíleného alba",
|
"add_to_shared_album": "Přidat do sdíleného alba",
|
||||||
"add_upload_to_stack": "Přidat nahrané do zásobníku",
|
"add_upload_to_stack": "Přidat nahrané do seskupení",
|
||||||
"add_url": "Přidat URL",
|
"add_url": "Přidat URL",
|
||||||
"add_workflow_step": "Přidat krok pracovního postupu",
|
"add_workflow_step": "Přidat krok pracovního postupu",
|
||||||
"added_to_archive": "Přidáno do archivu",
|
"added_to_archive": "Přidáno do archivu",
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
"transcoding_tone_mapping": "Mapování tónů",
|
"transcoding_tone_mapping": "Mapování tónů",
|
||||||
"transcoding_tone_mapping_description": "Snaží se zachovat vzhled videí HDR při převodu na SDR. Každý algoritmus dělá různé kompromisy v oblasti barev, detailů a jasu. Hable zachovává detaily, Mobius zachovává barvy a Reinhard zachovává jas.",
|
"transcoding_tone_mapping_description": "Snaží se zachovat vzhled videí HDR při převodu na SDR. Každý algoritmus dělá různé kompromisy v oblasti barev, detailů a jasu. Hable zachovává detaily, Mobius zachovává barvy a Reinhard zachovává jas.",
|
||||||
"transcoding_transcode_policy": "Zásady překódování",
|
"transcoding_transcode_policy": "Zásady překódování",
|
||||||
"transcoding_transcode_policy_description": "Zásady, kdy má být video překódováno. Videa HDR budou překódována vždy (kromě případů, kdy je překódování zakázáno).",
|
"transcoding_transcode_policy_description": "Zásady, kdy má být video překódováno. HDR videa a videa s jiným formátem pixelů než YUV 4:2:0 budou překódována vždy (kromě případů, kdy je překódování zakázáno).",
|
||||||
"transcoding_two_pass_encoding": "Dvouprůchodové kódování",
|
"transcoding_two_pass_encoding": "Dvouprůchodové kódování",
|
||||||
"transcoding_two_pass_encoding_setting_description": "Překódováním ve dvou průchodech získáte lépe zakódovaná videa. Pokud je povolen maximální datový tok (nutný pro práci s H.264 a HEVC), tento režim používá rozsah datového toku založený na maximálním datovém toku a ignoruje CRF. U VP9 lze CRF použít, pokud je max. datový tok zakázán.",
|
"transcoding_two_pass_encoding_setting_description": "Překódováním ve dvou průchodech získáte lépe zakódovaná videa. Pokud je povolen maximální datový tok (nutný pro práci s H.264 a HEVC), tento režim používá rozsah datového toku založený na maximálním datovém toku a ignoruje CRF. U VP9 lze CRF použít, pokud je max. datový tok zakázán.",
|
||||||
"transcoding_video_codec": "Video kodek",
|
"transcoding_video_codec": "Video kodek",
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
"user_successfully_removed": "Uživatel {email} byl úspěšně odstraněn.",
|
"user_successfully_removed": "Uživatel {email} byl úspěšně odstraněn.",
|
||||||
"users_page_description": "Stránka správců",
|
"users_page_description": "Stránka správců",
|
||||||
"version_check_enabled_description": "Povolit kontrolu verzí",
|
"version_check_enabled_description": "Povolit kontrolu verzí",
|
||||||
"version_check_implications": "Kontrola verze je založena na pravidelné komunikaci s github.com",
|
"version_check_implications": "Kontrola verze je založena na pravidelné komunikaci s {server}",
|
||||||
"version_check_settings": "Kontrola verze",
|
"version_check_settings": "Kontrola verze",
|
||||||
"version_check_settings_description": "Povolení/zakázání oznámení o nové verzi",
|
"version_check_settings_description": "Povolení/zakázání oznámení o nové verzi",
|
||||||
"video_conversion_job": "Překódování videí",
|
"video_conversion_job": "Překódování videí",
|
||||||
@@ -849,9 +849,12 @@
|
|||||||
"create_link_to_share": "Vytvořit odkaz pro sdílení",
|
"create_link_to_share": "Vytvořit odkaz pro sdílení",
|
||||||
"create_link_to_share_description": "Umožnit každému, kdo má odkaz, zobrazit vybrané fotografie",
|
"create_link_to_share_description": "Umožnit každému, kdo má odkaz, zobrazit vybrané fotografie",
|
||||||
"create_new": "VYTVOŘIT NOVÉ",
|
"create_new": "VYTVOŘIT NOVÉ",
|
||||||
|
"create_new_face": "Vytvořit nový obličej",
|
||||||
"create_new_person": "Vytvořit novou osobu",
|
"create_new_person": "Vytvořit novou osobu",
|
||||||
"create_new_person_hint": "Přiřadit vybrané položky nové osobě",
|
"create_new_person_hint": "Přiřadit vybrané položky nové osobě",
|
||||||
"create_new_user": "Vytvořit nového uživatele",
|
"create_new_user": "Vytvořit nového uživatele",
|
||||||
|
"create_person": "Vytvořit osobu",
|
||||||
|
"create_person_subtitle": "Přidejte jméno ke zvolenému obličeji pro vytvoření a označení nové osoby",
|
||||||
"create_shared_album_page_share_add_assets": "PŘIDAT POLOŽKY",
|
"create_shared_album_page_share_add_assets": "PŘIDAT POLOŽKY",
|
||||||
"create_shared_album_page_share_select_photos": "Vybrat fotografie",
|
"create_shared_album_page_share_select_photos": "Vybrat fotografie",
|
||||||
"create_shared_link": "Vytvořit sdílený odkaz",
|
"create_shared_link": "Vytvořit sdílený odkaz",
|
||||||
@@ -866,13 +869,14 @@
|
|||||||
"crop_aspect_ratio_fixed": "Pevný",
|
"crop_aspect_ratio_fixed": "Pevný",
|
||||||
"crop_aspect_ratio_free": "Volný",
|
"crop_aspect_ratio_free": "Volný",
|
||||||
"crop_aspect_ratio_original": "Původní",
|
"crop_aspect_ratio_original": "Původní",
|
||||||
|
"crop_aspect_ratio_square": "Čtverec",
|
||||||
"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_pin_code": "Aktuální PIN kód",
|
||||||
"current_server_address": "Aktuální adresa serveru",
|
"current_server_address": "Aktuální adresa serveru",
|
||||||
"custom_date": "Vlastní datum",
|
"custom_date": "Vlastní datum",
|
||||||
"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, časy a čísla podle vybraného jazyka a oblasti",
|
||||||
"custom_url": "Vlastní URL",
|
"custom_url": "Vlastní URL",
|
||||||
"cutoff_date_description": "Zanechat fotografie a videa z posledních…",
|
"cutoff_date_description": "Zanechat fotografie a videa z posledních…",
|
||||||
"cutoff_day": "{count, plural, one {den} few {dny} other {dnů}}",
|
"cutoff_day": "{count, plural, one {den} few {dny} other {dnů}}",
|
||||||
@@ -880,7 +884,7 @@
|
|||||||
"daily_title_text_date": "EEEE, d. MMMM",
|
"daily_title_text_date": "EEEE, d. MMMM",
|
||||||
"daily_title_text_date_year": "EEEE, d. MMMM y",
|
"daily_title_text_date_year": "EEEE, d. MMMM y",
|
||||||
"dark": "Tmavý",
|
"dark": "Tmavý",
|
||||||
"dark_theme": "Přepnout tmavý motiv",
|
"dark_theme": "Přepnout na tmavý motiv",
|
||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"date_after": "Datum po",
|
"date_after": "Datum po",
|
||||||
"date_and_time": "Datum a čas",
|
"date_and_time": "Datum a čas",
|
||||||
@@ -891,12 +895,8 @@
|
|||||||
"day": "Den",
|
"day": "Den",
|
||||||
"days": "Dnů",
|
"days": "Dnů",
|
||||||
"deduplicate_all": "Odstranit všechny duplicity",
|
"deduplicate_all": "Odstranit všechny duplicity",
|
||||||
"deduplication_criteria_1": "Velikost obrázku v bajtech",
|
"default_locale": "Výchozí národní prostředí",
|
||||||
"deduplication_criteria_2": "Počet EXIF dat",
|
"default_locale_description": "Formátování datumu a čísel podle místního nastavení prohlížeče",
|
||||||
"deduplication_info": "Informace o deduplikaci",
|
|
||||||
"deduplication_info_description": "Pro automatický předvýběr položek a hromadné odstranění duplicit se zohledňuje:",
|
|
||||||
"default_locale": "Výchozí jazyk",
|
|
||||||
"default_locale_description": "Formátovat datumy a čísla podle místního prostředí prohlížeče",
|
|
||||||
"delete": "Smazat",
|
"delete": "Smazat",
|
||||||
"delete_action_confirmation_message": "Opravdu chcete odstranit tuto položku? Tato akce přesune položku do serverového koše a zeptá se vás, zda ji chcete odstranit lokálně",
|
"delete_action_confirmation_message": "Opravdu chcete odstranit tuto položku? Tato akce přesune položku do serverového koše a zeptá se vás, zda ji chcete odstranit lokálně",
|
||||||
"delete_action_prompt": "{count} smazáno",
|
"delete_action_prompt": "{count} smazáno",
|
||||||
@@ -972,7 +972,7 @@
|
|||||||
"downloading_media": "Stahování média",
|
"downloading_media": "Stahování média",
|
||||||
"drop_files_to_upload": "Pro nahrání sem přetáhněte soubory",
|
"drop_files_to_upload": "Pro nahrání sem přetáhněte soubory",
|
||||||
"duplicates": "Duplicity",
|
"duplicates": "Duplicity",
|
||||||
"duplicates_description": "Vyřešte každou skupinu tak, že uvedete, které skupiny jsou duplicitní",
|
"duplicates_description": "Vyřešte každou skupinu tak, že uvedete, které skupiny jsou duplicitní.",
|
||||||
"duration": "Doba trvání",
|
"duration": "Doba trvání",
|
||||||
"edit": "Upravit",
|
"edit": "Upravit",
|
||||||
"edit_album": "Upravit album",
|
"edit_album": "Upravit album",
|
||||||
@@ -1003,12 +1003,14 @@
|
|||||||
"editor_close_without_save_title": "Zavřít editor?",
|
"editor_close_without_save_title": "Zavřít editor?",
|
||||||
"editor_confirm_reset_all_changes": "Opravdu chcete zrušit všechny změny?",
|
"editor_confirm_reset_all_changes": "Opravdu chcete zrušit všechny změny?",
|
||||||
"editor_discard_edits_confirm": "Zrušit úpravy",
|
"editor_discard_edits_confirm": "Zrušit úpravy",
|
||||||
"editor_discard_edits_prompt": "Máte neuložené úpravy. Opravdu je chcete smazat?",
|
"editor_discard_edits_prompt": "Máte neuložené úpravy. Opravdu je chcete zahodit?",
|
||||||
"editor_discard_edits_title": "Zrušit úpravy?",
|
"editor_discard_edits_title": "Zrušit úpravy?",
|
||||||
"editor_edits_applied_error": "Nepodařilo se použít úpravy",
|
"editor_edits_applied_error": "Nepodařilo se použít úpravy",
|
||||||
"editor_edits_applied_success": "Úpravy byly úspěšně provedeny",
|
"editor_edits_applied_success": "Úpravy byly úspěšně provedeny",
|
||||||
"editor_flip_horizontal": "Otočit vodorovně",
|
"editor_flip_horizontal": "Otočit vodorovně",
|
||||||
"editor_flip_vertical": "Otočit svisle",
|
"editor_flip_vertical": "Otočit svisle",
|
||||||
|
"editor_handle_corner": "{corner, select, top_left {Vlevo nahoře} top_right {Vpravo nahoře} bottom_left {Vlevo dole} bottom_right {Vpravo dole} other {A}} rohová úchytka",
|
||||||
|
"editor_handle_edge": "{edge, select, top {Nahoře} bottom {Dole} left {Vlevo} right {Vpravo} other {An}}úchyt hrany",
|
||||||
"editor_orientation": "Orientace",
|
"editor_orientation": "Orientace",
|
||||||
"editor_reset_all_changes": "Zrušit změny",
|
"editor_reset_all_changes": "Zrušit změny",
|
||||||
"editor_rotate_left": "Otočit o 90° doleva",
|
"editor_rotate_left": "Otočit o 90° doleva",
|
||||||
@@ -1074,6 +1076,7 @@
|
|||||||
"failed_to_update_notification_status": "Nepodařilo se aktualizovat stav oznámení",
|
"failed_to_update_notification_status": "Nepodařilo se aktualizovat stav oznámení",
|
||||||
"incorrect_email_or_password": "Nesprávný e-mail nebo heslo",
|
"incorrect_email_or_password": "Nesprávný e-mail nebo heslo",
|
||||||
"library_folder_already_exists": "Tato importní cesta již existuje.",
|
"library_folder_already_exists": "Tato importní cesta již existuje.",
|
||||||
|
"page_not_found": "Stránka nebyla nalezena",
|
||||||
"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",
|
||||||
"profile_picture_transparent_pixels": "Profilové obrázky nemohou mít průhledné pixely. Obrázek si prosím zvětšete nebo posuňte.",
|
"profile_picture_transparent_pixels": "Profilové obrázky nemohou mít průhledné pixely. Obrázek si prosím zvětšete nebo posuňte.",
|
||||||
"quota_higher_than_disk_size": "Nastavili jste kvótu vyšší, než je velikost disku",
|
"quota_higher_than_disk_size": "Nastavili jste kvótu vyšší, než je velikost disku",
|
||||||
@@ -1218,6 +1221,7 @@
|
|||||||
"filter_description": "Podmínky pro filtrování cílových položek",
|
"filter_description": "Podmínky pro filtrování cílových položek",
|
||||||
"filter_people": "Filtrovat lidi",
|
"filter_people": "Filtrovat lidi",
|
||||||
"filter_places": "Filtrovat místa",
|
"filter_places": "Filtrovat místa",
|
||||||
|
"filter_tags": "Filtrovat značky",
|
||||||
"filters": "Filtry",
|
"filters": "Filtry",
|
||||||
"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",
|
||||||
"first": "První",
|
"first": "První",
|
||||||
@@ -1385,9 +1389,11 @@
|
|||||||
"library_page_sort_title": "Podle názvu alba",
|
"library_page_sort_title": "Podle názvu alba",
|
||||||
"licenses": "Licence",
|
"licenses": "Licence",
|
||||||
"light": "Světlý",
|
"light": "Světlý",
|
||||||
|
"light_theme": "Přepnout na světlý motiv",
|
||||||
"like": "Líbí se mi",
|
"like": "Líbí se mi",
|
||||||
"like_deleted": "Oblíbení smazáno",
|
"like_deleted": "Oblíbení smazáno",
|
||||||
"link_motion_video": "Připojit pohyblivé video",
|
"link_motion_video": "Připojit pohyblivé video",
|
||||||
|
"link_to_docs": "Další informace najdete v <link>dokumentaci</link>.",
|
||||||
"link_to_oauth": "Propojit s OAuth",
|
"link_to_oauth": "Propojit s OAuth",
|
||||||
"linked_oauth_account": "Propojený OAuth účet",
|
"linked_oauth_account": "Propojený OAuth účet",
|
||||||
"list": "Seznam",
|
"list": "Seznam",
|
||||||
@@ -1649,6 +1655,7 @@
|
|||||||
"only_favorites": "Pouze oblíbené",
|
"only_favorites": "Pouze oblíbené",
|
||||||
"open": "Otevřít",
|
"open": "Otevřít",
|
||||||
"open_calendar": "Otevřít kalendář",
|
"open_calendar": "Otevřít kalendář",
|
||||||
|
"open_in_browser": "Otevřít v prohlížeči",
|
||||||
"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",
|
||||||
@@ -1808,9 +1815,8 @@
|
|||||||
"rate_asset": "Hodnotit položku",
|
"rate_asset": "Hodnotit položku",
|
||||||
"rating": "Hodnocení hvězdičkami",
|
"rating": "Hodnocení hvězdičkami",
|
||||||
"rating_clear": "Vyčistit hodnocení",
|
"rating_clear": "Vyčistit hodnocení",
|
||||||
"rating_count": "{count, plural, one {# hvězdička} few {# hvězdičky} other {# hvězdček}}",
|
"rating_count": "{count, plural, =0 {Nehodnoceno} one {# hvězdička} few {# hvězdičky} other {# hvězdček}}",
|
||||||
"rating_description": "Zobrazit EXIF hodnocení v informačním panelu",
|
"rating_description": "Zobrazit EXIF hodnocení v informačním panelu",
|
||||||
"rating_set": "Hodnocení nastaveno na {rating, plural, one {# hvězdičku} few {# hvězdičky} other {# hvězdiček}}",
|
|
||||||
"reaction_options": "Možnosti reakce",
|
"reaction_options": "Možnosti reakce",
|
||||||
"read_changelog": "Přečtěte si seznam změn",
|
"read_changelog": "Přečtěte si seznam změn",
|
||||||
"readonly_mode_disabled": "Režim pouze pro čtení je deaktivován",
|
"readonly_mode_disabled": "Režim pouze pro čtení je deaktivován",
|
||||||
@@ -1882,7 +1888,10 @@
|
|||||||
"reset_pin_code_success": "PIN kód úspěšně resetován",
|
"reset_pin_code_success": "PIN kód úspěšně resetován",
|
||||||
"reset_pin_code_with_password": "Svůj PIN kód můžete vždy resetovat pomocí hesla",
|
"reset_pin_code_with_password": "Svůj PIN kód můžete vždy resetovat pomocí hesla",
|
||||||
"reset_sqlite": "Obnovit databázi SQLite",
|
"reset_sqlite": "Obnovit databázi SQLite",
|
||||||
"reset_sqlite_confirmation": "Jste si jisti, že chcete obnovit databázi SQLite? Pro opětovnou synchronizaci dat se budete muset odhlásit a znovu přihlásit",
|
"reset_sqlite_clear_app_data": "Vymazat data",
|
||||||
|
"reset_sqlite_confirmation": "Opravdu chcete vymazat data aplikace? Tím se odstraní všechna nastavení a odhlásíte se.",
|
||||||
|
"reset_sqlite_confirmation_note": "Poznámka: Po vymazání budete muset aplikaci restartovat.",
|
||||||
|
"reset_sqlite_done": "Data aplikace byla vymazána. Restartujte Immich a znovu se přihlaste.",
|
||||||
"reset_sqlite_success": "Obnovení SQLite databáze proběhlo úspěšně",
|
"reset_sqlite_success": "Obnovení SQLite databáze proběhlo úspěšně",
|
||||||
"reset_to_default": "Obnovit výchozí nastavení",
|
"reset_to_default": "Obnovit výchozí nastavení",
|
||||||
"resolution": "Rozlišení",
|
"resolution": "Rozlišení",
|
||||||
@@ -1910,6 +1919,7 @@
|
|||||||
"saved_settings": "Nastavení uloženo",
|
"saved_settings": "Nastavení uloženo",
|
||||||
"say_something": "Napište něco",
|
"say_something": "Napište něco",
|
||||||
"scaffold_body_error_occurred": "Došlo k chybě",
|
"scaffold_body_error_occurred": "Došlo k chybě",
|
||||||
|
"scaffold_body_error_unrecoverable": "Došlo k neopravitelné chybě. Abychom vám mohli pomoci, sdělte nám prosím chybu a výpis zásobníku na Discordu nebo GitHubu. Pokud vám bylo doporučeno, můžete vymazat data aplikace níže.",
|
||||||
"scan": "Prohledat",
|
"scan": "Prohledat",
|
||||||
"scan_all_libraries": "Prohledat všechny knihovny",
|
"scan_all_libraries": "Prohledat všechny knihovny",
|
||||||
"scan_library": "Prohledat",
|
"scan_library": "Prohledat",
|
||||||
@@ -1945,6 +1955,7 @@
|
|||||||
"search_filter_ocr": "Hledat pomocí OCR",
|
"search_filter_ocr": "Hledat pomocí OCR",
|
||||||
"search_filter_people_title": "Výběr lidí",
|
"search_filter_people_title": "Výběr lidí",
|
||||||
"search_filter_star_rating": "Hodnocení hvězdičkami",
|
"search_filter_star_rating": "Hodnocení hvězdičkami",
|
||||||
|
"search_filter_tags_title": "Vybrat značky",
|
||||||
"search_for": "Vyhledat",
|
"search_for": "Vyhledat",
|
||||||
"search_for_existing_person": "Vyhledat existující osobu",
|
"search_for_existing_person": "Vyhledat existující osobu",
|
||||||
"search_no_more_result": "Žádné další výsledky",
|
"search_no_more_result": "Žádné další výsledky",
|
||||||
@@ -2024,6 +2035,9 @@
|
|||||||
"set_profile_picture": "Nastavit profilový obrázek",
|
"set_profile_picture": "Nastavit profilový obrázek",
|
||||||
"set_slideshow_to_fullscreen": "Nastavit prezentaci na celou obrazovku",
|
"set_slideshow_to_fullscreen": "Nastavit prezentaci na celou obrazovku",
|
||||||
"set_stack_primary_asset": "Nastavit jako hlavní položku",
|
"set_stack_primary_asset": "Nastavit jako hlavní položku",
|
||||||
|
"setting_image_navigation_enable_subtitle": "Pokud je zapnuto, budete moci přejít na předchozí/další obrázek klepnutím do levé/pravé čtvrtiny obrazovky.",
|
||||||
|
"setting_image_navigation_enable_title": "Klepněte pro navigaci",
|
||||||
|
"setting_image_navigation_title": "Navigace mezi obrázky",
|
||||||
"setting_image_viewer_help": "V prohlížeči detailů se nejprve načte malá miniatura, poté se načte náhled střední velikosti (je-li povolen) a nakonec se načte originál (je-li povolen).",
|
"setting_image_viewer_help": "V prohlížeči detailů se nejprve načte malá miniatura, poté se načte náhled střední velikosti (je-li povolen) a nakonec se načte originál (je-li povolen).",
|
||||||
"setting_image_viewer_original_subtitle": "Umožňuje načíst původní obrázek v plném rozlišení (velký!). Zakažte pro snížení využití dat (v síti i v mezipaměti zařízení).",
|
"setting_image_viewer_original_subtitle": "Umožňuje načíst původní obrázek v plném rozlišení (velký!). Zakažte pro snížení využití dat (v síti i v mezipaměti zařízení).",
|
||||||
"setting_image_viewer_original_title": "Načíst původní obrázek",
|
"setting_image_viewer_original_title": "Načíst původní obrázek",
|
||||||
@@ -2203,6 +2217,7 @@
|
|||||||
"tag": "Značka",
|
"tag": "Značka",
|
||||||
"tag_assets": "Přiřadit značku",
|
"tag_assets": "Přiřadit značku",
|
||||||
"tag_created": "Vytvořena značka: {tag}",
|
"tag_created": "Vytvořena značka: {tag}",
|
||||||
|
"tag_face": "Označit obličej",
|
||||||
"tag_feature_description": "Procházení fotografií a videí seskupených podle témat logických značek",
|
"tag_feature_description": "Procházení fotografií a videí seskupených podle témat logických značek",
|
||||||
"tag_not_found_question": "Nemůžete najít značku? <link>Vytvořte novou.</link>",
|
"tag_not_found_question": "Nemůžete najít značku? <link>Vytvořte novou.</link>",
|
||||||
"tag_people": "Označit lidi",
|
"tag_people": "Označit lidi",
|
||||||
@@ -2302,6 +2317,7 @@
|
|||||||
"unstack_action_prompt": "{count} seskupených zrušeno",
|
"unstack_action_prompt": "{count} seskupených zrušeno",
|
||||||
"unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položek}}",
|
"unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položek}}",
|
||||||
"unsupported_field_type": "Nepodporovaný typ pole",
|
"unsupported_field_type": "Nepodporovaný typ pole",
|
||||||
|
"unsupported_file_type": "Soubor {file} nelze nahrát, protože jeho typ {type} není podporován.",
|
||||||
"untagged": "Neoznačeno",
|
"untagged": "Neoznačeno",
|
||||||
"untitled_workflow": "Pracovní postup bez názvu",
|
"untitled_workflow": "Pracovní postup bez názvu",
|
||||||
"up_next": "To je prozatím vše",
|
"up_next": "To je prozatím vše",
|
||||||
@@ -2328,6 +2344,8 @@
|
|||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Využití",
|
"usage": "Využití",
|
||||||
"use_biometric": "Použít biometrické údaje",
|
"use_biometric": "Použít biometrické údaje",
|
||||||
|
"use_browser_locale": "Použít jazyk prohlížeče",
|
||||||
|
"use_browser_locale_description": "Formátujte data, časy a čísla podle nastavení místního formátu vašeho prohlížeče",
|
||||||
"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",
|
||||||
@@ -2378,9 +2396,10 @@
|
|||||||
"view_similar_photos": "Zobrazit podobné fotky",
|
"view_similar_photos": "Zobrazit podobné fotky",
|
||||||
"view_stack": "Zobrazit seskupení",
|
"view_stack": "Zobrazit seskupení",
|
||||||
"view_user": "Zobrazit uživatele",
|
"view_user": "Zobrazit uživatele",
|
||||||
"viewer_remove_from_stack": "Odstranit ze zásobníku",
|
"viewer_remove_from_stack": "Odstranit ze seskupení",
|
||||||
"viewer_stack_use_as_main_asset": "Použít jako hlavní položku",
|
"viewer_stack_use_as_main_asset": "Použít jako hlavní položku",
|
||||||
"viewer_unstack": "Zrušit zásobník",
|
"viewer_unstack": "Zrušit seskupení",
|
||||||
|
"visibility": "Viditelnost",
|
||||||
"visibility_changed": "Viditelnost změněna u {count, plural, one {# osoby} few {# osob} other {# lidí}}",
|
"visibility_changed": "Viditelnost změněna u {count, plural, one {# osoby} few {# osob} other {# lidí}}",
|
||||||
"visual": "Vizuální",
|
"visual": "Vizuální",
|
||||||
"visual_builder": "Vizuální návrhář",
|
"visual_builder": "Vizuální návrhář",
|
||||||
|
|||||||
+40
-18
@@ -311,7 +311,7 @@
|
|||||||
"search_jobs": "Søg opgaver…",
|
"search_jobs": "Søg opgaver…",
|
||||||
"send_welcome_email": "Send velkomst-email",
|
"send_welcome_email": "Send velkomst-email",
|
||||||
"server_external_domain_settings": "Eksternt domæne",
|
"server_external_domain_settings": "Eksternt domæne",
|
||||||
"server_external_domain_settings_description": "Domæne til offentligt delte links, inklusiv http(s)://",
|
"server_external_domain_settings_description": "Domæne brugt til eksterne links",
|
||||||
"server_public_users": "Offentlige brugere",
|
"server_public_users": "Offentlige brugere",
|
||||||
"server_public_users_description": "Alle brugere (navn og e-mail) vises, når en bruger tilføjes til delte album. Når den er deaktiveret, vil brugerlisten kun være tilgængelig for administratorbrugere.",
|
"server_public_users_description": "Alle brugere (navn og e-mail) vises, når en bruger tilføjes til delte album. Når den er deaktiveret, vil brugerlisten kun være tilgængelig for administratorbrugere.",
|
||||||
"server_settings": "Serverindstillinger",
|
"server_settings": "Serverindstillinger",
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
"transcoding_tone_mapping": "Tone-kortlægning",
|
"transcoding_tone_mapping": "Tone-kortlægning",
|
||||||
"transcoding_tone_mapping_description": "Forsøger at bevare HDR-videoers udseende når konverteret til SDR. Hver algoritme har forskellige afvejninger af farve, detalje og lysstyrke. Hable bevarer farve og Reinhard bevarer lysstyrke.",
|
"transcoding_tone_mapping_description": "Forsøger at bevare HDR-videoers udseende når konverteret til SDR. Hver algoritme har forskellige afvejninger af farve, detalje og lysstyrke. Hable bevarer farve og Reinhard bevarer lysstyrke.",
|
||||||
"transcoding_transcode_policy": "Transkodningspolitik",
|
"transcoding_transcode_policy": "Transkodningspolitik",
|
||||||
"transcoding_transcode_policy_description": "Politik for hvornår en video skal transkodes. HDR videoer vil altid blive transkodet (bortset fra, hvis transkodning er slået fra).",
|
"transcoding_transcode_policy_description": "Politik for hvornår en video skal transkodes. HDR videoer og videoer med en anden pixelformat end YUV 4:2:0 vil altid blive transkodet (bortset fra, hvis transkodning er slået fra).",
|
||||||
"transcoding_two_pass_encoding": "To-omgangsindkodning",
|
"transcoding_two_pass_encoding": "To-omgangsindkodning",
|
||||||
"transcoding_two_pass_encoding_setting_description": "Transkoder af to omgange for at producere bedre indkodede videoer. Når den maksimale bitrate er slået til (som det kræver for at det fungerer med H.264 og HEVC), bruger denne tilstand en bitrateinterval baseret på den maksimale birate og ignorerer CRF. For VP9, kan CRF bruges hvis den maksimale bitrate er slået fra.",
|
"transcoding_two_pass_encoding_setting_description": "Transkoder af to omgange for at producere bedre indkodede videoer. Når den maksimale bitrate er slået til (som det kræver for at det fungerer med H.264 og HEVC), bruger denne tilstand en bitrateinterval baseret på den maksimale birate og ignorerer CRF. For VP9, kan CRF bruges hvis den maksimale bitrate er slået fra.",
|
||||||
"transcoding_video_codec": "Videocodec",
|
"transcoding_video_codec": "Videocodec",
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
"user_successfully_removed": "Bruger {email} er blevet fjernet med succes.",
|
"user_successfully_removed": "Bruger {email} er blevet fjernet med succes.",
|
||||||
"users_page_description": "Admin-brugere side",
|
"users_page_description": "Admin-brugere side",
|
||||||
"version_check_enabled_description": "Aktivér versionstjek",
|
"version_check_enabled_description": "Aktivér versionstjek",
|
||||||
"version_check_implications": "Funktionen til versionstjek er afhængig af periodisk kommunikation med github.com",
|
"version_check_implications": "Funktionen til versionstjek er afhængig af periodisk kommunikation med {server}",
|
||||||
"version_check_settings": "Versionstjek",
|
"version_check_settings": "Versionstjek",
|
||||||
"version_check_settings_description": "Aktiver/deaktiverer notifikation for den nye version",
|
"version_check_settings_description": "Aktiver/deaktiverer notifikation for den nye version",
|
||||||
"video_conversion_job": "Transkod videoer",
|
"video_conversion_job": "Transkod videoer",
|
||||||
@@ -794,6 +794,11 @@
|
|||||||
"color": "Farve",
|
"color": "Farve",
|
||||||
"color_theme": "Farvetema",
|
"color_theme": "Farvetema",
|
||||||
"command": "Kommando",
|
"command": "Kommando",
|
||||||
|
"command_palette_prompt": "Find hurtigt sider, handlinger eller kommandoer",
|
||||||
|
"command_palette_to_close": "for at lukke",
|
||||||
|
"command_palette_to_navigate": "for at indtaste",
|
||||||
|
"command_palette_to_select": "for at vælge",
|
||||||
|
"command_palette_to_show_all": "for at vise alle",
|
||||||
"comment_deleted": "Kommentar slettet",
|
"comment_deleted": "Kommentar slettet",
|
||||||
"comment_options": "Kommentarindstillinger",
|
"comment_options": "Kommentarindstillinger",
|
||||||
"comments_and_likes": "Kommentarer og likes",
|
"comments_and_likes": "Kommentarer og likes",
|
||||||
@@ -858,16 +863,17 @@
|
|||||||
"created_at": "Oprettet",
|
"created_at": "Oprettet",
|
||||||
"creating_linked_albums": "Opretter sammenkædede albums...",
|
"creating_linked_albums": "Opretter sammenkædede albums...",
|
||||||
"crop": "Beskær",
|
"crop": "Beskær",
|
||||||
"crop_aspect_ratio_fixed": "Fikset",
|
"crop_aspect_ratio_fixed": "Fast",
|
||||||
"crop_aspect_ratio_free": "Gratis",
|
"crop_aspect_ratio_free": "Fri",
|
||||||
"crop_aspect_ratio_original": "Original",
|
"crop_aspect_ratio_original": "Original",
|
||||||
|
"crop_aspect_ratio_square": "Kvadrat",
|
||||||
"curated_object_page_title": "Ting",
|
"curated_object_page_title": "Ting",
|
||||||
"current_device": "Nuværende enhed",
|
"current_device": "Nuværende enhed",
|
||||||
"current_pin_code": "Nuværende PIN kode",
|
"current_pin_code": "Nuværende PIN kode",
|
||||||
"current_server_address": "Nuværende serveraddresse",
|
"current_server_address": "Nuværende serveraddresse",
|
||||||
"custom_date": "Brugerdefineret dato",
|
"custom_date": "Brugerdefineret dato",
|
||||||
"custom_locale": "Brugerdefineret lokale",
|
"custom_locale": "Brugerdefineret lokale",
|
||||||
"custom_locale_description": "Formatér datoer og tal baseret på sproget og regionen",
|
"custom_locale_description": "Formatér datoer, klokkeslæt og tal baseret på det valgte sprog og den valgte region",
|
||||||
"custom_url": "Tilpasset URL",
|
"custom_url": "Tilpasset URL",
|
||||||
"cutoff_date_description": "Behold fotos fra den sidste…",
|
"cutoff_date_description": "Behold fotos fra den sidste…",
|
||||||
"cutoff_day": "{count, plural, one {dag} other {dage}}",
|
"cutoff_day": "{count, plural, one {dag} other {dage}}",
|
||||||
@@ -885,13 +891,8 @@
|
|||||||
"date_range": "Datointerval",
|
"date_range": "Datointerval",
|
||||||
"day": "Dag",
|
"day": "Dag",
|
||||||
"days": "Dage",
|
"days": "Dage",
|
||||||
"deduplicate_all": "Kopier alle",
|
"deduplicate_all": "Dedubliker alle",
|
||||||
"deduplication_criteria_1": "Billedstørrelse i bytes",
|
"default_locale_description": "Formatér datoer og tal baseret på din browsers landestandard",
|
||||||
"deduplication_criteria_2": "Antal EXIF-data",
|
|
||||||
"deduplication_info": "Deduplikerings info",
|
|
||||||
"deduplication_info_description": "For automatisk at forudvælge emner og fjerne dubletter i bulk ser vi på:",
|
|
||||||
"default_locale": "Standardlokalitet",
|
|
||||||
"default_locale_description": "Formatér datoer og tal baseret på din browsers regions indstillinger",
|
|
||||||
"delete": "Slet",
|
"delete": "Slet",
|
||||||
"delete_action_confirmation_message": "Er du sikker på, at du vil slette dette objekt? Denne handling vil flytte objektet til serverens papirkurv, og vil spørge dig, om du vil slette den lokalt",
|
"delete_action_confirmation_message": "Er du sikker på, at du vil slette dette objekt? Denne handling vil flytte objektet til serverens papirkurv, og vil spørge dig, om du vil slette den lokalt",
|
||||||
"delete_action_prompt": "{count} slettet",
|
"delete_action_prompt": "{count} slettet",
|
||||||
@@ -967,7 +968,7 @@
|
|||||||
"downloading_media": "Download medier",
|
"downloading_media": "Download medier",
|
||||||
"drop_files_to_upload": "Slip filer hvor som helst for at uploade dem",
|
"drop_files_to_upload": "Slip filer hvor som helst for at uploade dem",
|
||||||
"duplicates": "Duplikater",
|
"duplicates": "Duplikater",
|
||||||
"duplicates_description": "Løs hver gruppe ved at angive, hvilke, hvis nogen, er dubletter",
|
"duplicates_description": "Løs hver gruppe ved at angive hvilke, hvis nogen, er dubletter",
|
||||||
"duration": "Varighed",
|
"duration": "Varighed",
|
||||||
"edit": "Rediger",
|
"edit": "Rediger",
|
||||||
"edit_album": "Redigér album",
|
"edit_album": "Redigér album",
|
||||||
@@ -1004,6 +1005,8 @@
|
|||||||
"editor_edits_applied_success": "Redigeringer gemt",
|
"editor_edits_applied_success": "Redigeringer gemt",
|
||||||
"editor_flip_horizontal": "Vend horisontalt",
|
"editor_flip_horizontal": "Vend horisontalt",
|
||||||
"editor_flip_vertical": "Flip vertikal",
|
"editor_flip_vertical": "Flip vertikal",
|
||||||
|
"editor_handle_corner": "{corner, select, top_left {Øverst venstre} top_right {Øverst højre} bottom_left {Nederst venstre} bottom_right {Nederst højre} other {A}} hjørnehåndtag",
|
||||||
|
"editor_handle_edge": "{edge, select, top {Øverst} bottom {Nederst} left {Venstre} right {Højre} other {Et}} kanthåndtag",
|
||||||
"editor_orientation": "Orientering",
|
"editor_orientation": "Orientering",
|
||||||
"editor_reset_all_changes": "Nulstil ændringer",
|
"editor_reset_all_changes": "Nulstil ændringer",
|
||||||
"editor_rotate_left": "Rotér 90° mod uret",
|
"editor_rotate_left": "Rotér 90° mod uret",
|
||||||
@@ -1014,7 +1017,7 @@
|
|||||||
"empty_trash": "Tøm papirkurv",
|
"empty_trash": "Tøm papirkurv",
|
||||||
"empty_trash_confirmation": "Er du sikker på, at du vil tømme papirkurven? Dette vil fjerne alle objekter i papirkurven permanent fra Immich.\nDu kan ikke fortryde denne handling!",
|
"empty_trash_confirmation": "Er du sikker på, at du vil tømme papirkurven? Dette vil fjerne alle objekter i papirkurven permanent fra Immich.\nDu kan ikke fortryde denne handling!",
|
||||||
"enable": "Aktivér",
|
"enable": "Aktivér",
|
||||||
"enable_backup": "Aktiver backup",
|
"enable_backup": "Aktivér backup",
|
||||||
"enable_biometric_auth_description": "Indtast din PIN kode for at slå biometrisk adgangskontrol til",
|
"enable_biometric_auth_description": "Indtast din PIN kode for at slå biometrisk adgangskontrol til",
|
||||||
"enabled": "Aktiveret",
|
"enabled": "Aktiveret",
|
||||||
"end_date": "Slutdato",
|
"end_date": "Slutdato",
|
||||||
@@ -1069,6 +1072,7 @@
|
|||||||
"failed_to_update_notification_status": "Kunne ikke uploade notifikations status",
|
"failed_to_update_notification_status": "Kunne ikke uploade notifikations status",
|
||||||
"incorrect_email_or_password": "Forkert email eller kodeord",
|
"incorrect_email_or_password": "Forkert email eller kodeord",
|
||||||
"library_folder_already_exists": "Denne import sti findes allerede.",
|
"library_folder_already_exists": "Denne import sti findes allerede.",
|
||||||
|
"page_not_found": "Siden blev ikke fundet",
|
||||||
"paths_validation_failed": "{paths, plural, one {# sti} other {# stier}} slog fejl ved validering",
|
"paths_validation_failed": "{paths, plural, one {# sti} other {# stier}} slog fejl ved validering",
|
||||||
"profile_picture_transparent_pixels": "Profilbilleder kan ikke have gennemsigtige pixels. Zoom venligst ind og/eller flyt billedet.",
|
"profile_picture_transparent_pixels": "Profilbilleder kan ikke have gennemsigtige pixels. Zoom venligst ind og/eller flyt billedet.",
|
||||||
"quota_higher_than_disk_size": "Du har sat en kvote der er større end disken",
|
"quota_higher_than_disk_size": "Du har sat en kvote der er større end disken",
|
||||||
@@ -1168,6 +1172,7 @@
|
|||||||
"exif_bottom_sheet_people": "PERSONER",
|
"exif_bottom_sheet_people": "PERSONER",
|
||||||
"exif_bottom_sheet_person_add_person": "Tilføj navn",
|
"exif_bottom_sheet_person_add_person": "Tilføj navn",
|
||||||
"exit_slideshow": "Afslut slideshow",
|
"exit_slideshow": "Afslut slideshow",
|
||||||
|
"expand": "Udvid",
|
||||||
"expand_all": "Udvid alle",
|
"expand_all": "Udvid alle",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Under udarbejdelse",
|
"experimental_settings_new_asset_list_subtitle": "Under udarbejdelse",
|
||||||
"experimental_settings_new_asset_list_title": "Aktiver eksperimentelt fotogitter",
|
"experimental_settings_new_asset_list_title": "Aktiver eksperimentelt fotogitter",
|
||||||
@@ -1212,6 +1217,7 @@
|
|||||||
"filter_description": "Betingelser for filtrering af valgte mediefiler",
|
"filter_description": "Betingelser for filtrering af valgte mediefiler",
|
||||||
"filter_people": "Filtrér personer",
|
"filter_people": "Filtrér personer",
|
||||||
"filter_places": "Filtrer steder",
|
"filter_places": "Filtrer steder",
|
||||||
|
"filter_tags": "Filtrer tags",
|
||||||
"filters": "Filtre",
|
"filters": "Filtre",
|
||||||
"find_them_fast": "Find dem hurtigt med søgning via navn",
|
"find_them_fast": "Find dem hurtigt med søgning via navn",
|
||||||
"first": "Første",
|
"first": "Første",
|
||||||
@@ -1379,9 +1385,11 @@
|
|||||||
"library_page_sort_title": "Albumtitel",
|
"library_page_sort_title": "Albumtitel",
|
||||||
"licenses": "Licenser",
|
"licenses": "Licenser",
|
||||||
"light": "Lys",
|
"light": "Lys",
|
||||||
|
"light_theme": "Skift til lyst tema",
|
||||||
"like": "Synes om",
|
"like": "Synes om",
|
||||||
"like_deleted": "Ligesom slettet",
|
"like_deleted": "Ligesom slettet",
|
||||||
"link_motion_video": "Link bevægelsesvideo",
|
"link_motion_video": "Link bevægelsesvideo",
|
||||||
|
"link_to_docs": "For yderligere information, se <link>dokumentationen</link>.",
|
||||||
"link_to_oauth": "Link til OAuth",
|
"link_to_oauth": "Link til OAuth",
|
||||||
"linked_oauth_account": "Tilsluttet OAuth-konto",
|
"linked_oauth_account": "Tilsluttet OAuth-konto",
|
||||||
"list": "Liste",
|
"list": "Liste",
|
||||||
@@ -1642,6 +1650,8 @@
|
|||||||
"online": "Online",
|
"online": "Online",
|
||||||
"only_favorites": "Kun favoritter",
|
"only_favorites": "Kun favoritter",
|
||||||
"open": "Åben",
|
"open": "Åben",
|
||||||
|
"open_calendar": "Åbn kalender",
|
||||||
|
"open_in_browser": "Åbn i browser",
|
||||||
"open_in_map_view": "Åben i kortvisning",
|
"open_in_map_view": "Åben i kortvisning",
|
||||||
"open_in_openstreetmap": "Åben i OpenStreetMap",
|
"open_in_openstreetmap": "Åben i OpenStreetMap",
|
||||||
"open_the_search_filters": "Åbn søgefiltre",
|
"open_the_search_filters": "Åbn søgefiltre",
|
||||||
@@ -1801,9 +1811,8 @@
|
|||||||
"rate_asset": "Vurder filer",
|
"rate_asset": "Vurder filer",
|
||||||
"rating": "Stjernebedømmelse",
|
"rating": "Stjernebedømmelse",
|
||||||
"rating_clear": "Nulstil vurdering",
|
"rating_clear": "Nulstil vurdering",
|
||||||
"rating_count": "{count, plural, one {# stjerne} other {# stjerner}}",
|
"rating_count": "{count, plural, =0 {Unrated} one {# stjerne} other {# stjerner}}",
|
||||||
"rating_description": "Vis EXIF-klassificeringen i infopanelet",
|
"rating_description": "Vis EXIF-klassificeringen i infopanelet",
|
||||||
"rating_set": "Vurdering sat til {rating, plural, one {# stjerne} other {# stjerner}}",
|
|
||||||
"reaction_options": "Reaktionsindstillinger",
|
"reaction_options": "Reaktionsindstillinger",
|
||||||
"read_changelog": "Læs ændringslog",
|
"read_changelog": "Læs ændringslog",
|
||||||
"readonly_mode_disabled": "Skrivebeskyttet tilstand deaktiveret",
|
"readonly_mode_disabled": "Skrivebeskyttet tilstand deaktiveret",
|
||||||
@@ -1875,7 +1884,10 @@
|
|||||||
"reset_pin_code_success": "PIN-koden er Nulstillet",
|
"reset_pin_code_success": "PIN-koden er Nulstillet",
|
||||||
"reset_pin_code_with_password": "Du kan altid nulstille din PIN-kode med dit password",
|
"reset_pin_code_with_password": "Du kan altid nulstille din PIN-kode med dit password",
|
||||||
"reset_sqlite": "Reset SQLite Databasen",
|
"reset_sqlite": "Reset SQLite Databasen",
|
||||||
"reset_sqlite_confirmation": "Er du sikker på, at du vil nulstille SQLite databasen? Du er nødt til at logge ud og ind igen for at gensynkronisere dine data",
|
"reset_sqlite_clear_app_data": "Ryd data",
|
||||||
|
"reset_sqlite_confirmation": "Er du sikker på, at du vil ryde app dataen? Dette vil fjerne alle settings og logge dig ud.",
|
||||||
|
"reset_sqlite_confirmation_note": "Bemærk: Du skal genstarte appen efter rydning.",
|
||||||
|
"reset_sqlite_done": "Appdata er blevet slettet. Genstart Immich og log ind igen.",
|
||||||
"reset_sqlite_success": "Vellykket reset af SQLite databasen",
|
"reset_sqlite_success": "Vellykket reset af SQLite databasen",
|
||||||
"reset_to_default": "Nulstil til standard",
|
"reset_to_default": "Nulstil til standard",
|
||||||
"resolution": "Opløsning",
|
"resolution": "Opløsning",
|
||||||
@@ -1903,6 +1915,7 @@
|
|||||||
"saved_settings": "Gemte indstillinger",
|
"saved_settings": "Gemte indstillinger",
|
||||||
"say_something": "Skriv noget",
|
"say_something": "Skriv noget",
|
||||||
"scaffold_body_error_occurred": "Der opstod en fejl",
|
"scaffold_body_error_occurred": "Der opstod en fejl",
|
||||||
|
"scaffold_body_error_unrecoverable": "Der er opstået en uoprettelig fejl. Del venligst fejlen og stack trace på Discord eller GitHub, så vi kan hjælpe. Hvis du bliver bedt om det, kan du rydde appdataene nedenfor.",
|
||||||
"scan": "Skan",
|
"scan": "Skan",
|
||||||
"scan_all_libraries": "Skan alle biblioteker",
|
"scan_all_libraries": "Skan alle biblioteker",
|
||||||
"scan_library": "Skan",
|
"scan_library": "Skan",
|
||||||
@@ -1938,6 +1951,7 @@
|
|||||||
"search_filter_ocr": "Søg via OCR",
|
"search_filter_ocr": "Søg via OCR",
|
||||||
"search_filter_people_title": "Vælg personer",
|
"search_filter_people_title": "Vælg personer",
|
||||||
"search_filter_star_rating": "Stjerne Vurdering",
|
"search_filter_star_rating": "Stjerne Vurdering",
|
||||||
|
"search_filter_tags_title": "Vælg tags",
|
||||||
"search_for": "Søg efter",
|
"search_for": "Søg efter",
|
||||||
"search_for_existing_person": "Søg efter eksisterende person",
|
"search_for_existing_person": "Søg efter eksisterende person",
|
||||||
"search_no_more_result": "Ikke flere resultater",
|
"search_no_more_result": "Ikke flere resultater",
|
||||||
@@ -2017,6 +2031,9 @@
|
|||||||
"set_profile_picture": "Indstil profilbillede",
|
"set_profile_picture": "Indstil profilbillede",
|
||||||
"set_slideshow_to_fullscreen": "Sæt diasshow til fuldskærmsvisning",
|
"set_slideshow_to_fullscreen": "Sæt diasshow til fuldskærmsvisning",
|
||||||
"set_stack_primary_asset": "Angiv som primært billede",
|
"set_stack_primary_asset": "Angiv som primært billede",
|
||||||
|
"setting_image_navigation_enable_subtitle": "Hvis aktiveret, kan du navigere til det forrige/næste billede ved at trykke på den yderste venstre/højre fjerdedel af skærmen.",
|
||||||
|
"setting_image_navigation_enable_title": "Tryk for at navigere",
|
||||||
|
"setting_image_navigation_title": "Billednavigation",
|
||||||
"setting_image_viewer_help": "Detaljeret visning indlæser miniaturebilleder først. Herefter indlæses mediumstørrelse forhåndsvisning af billedet (hvis dette er slået til), for til sidst at vise originalen (hvis dette er slået til).",
|
"setting_image_viewer_help": "Detaljeret visning indlæser miniaturebilleder først. Herefter indlæses mediumstørrelse forhåndsvisning af billedet (hvis dette er slået til), for til sidst at vise originalen (hvis dette er slået til).",
|
||||||
"setting_image_viewer_original_subtitle": "Slå indlæsning af originalbillede i fuld størrelse til (stort!). Deaktiver for at reducere dataforbruget (både på netværket og for enhedscache).",
|
"setting_image_viewer_original_subtitle": "Slå indlæsning af originalbillede i fuld størrelse til (stort!). Deaktiver for at reducere dataforbruget (både på netværket og for enhedscache).",
|
||||||
"setting_image_viewer_original_title": "Indlæs originalbillede",
|
"setting_image_viewer_original_title": "Indlæs originalbillede",
|
||||||
@@ -2183,6 +2200,7 @@
|
|||||||
"support": "Support",
|
"support": "Support",
|
||||||
"support_and_feedback": "Support og feedback",
|
"support_and_feedback": "Support og feedback",
|
||||||
"support_third_party_description": "Din Immich-installation blev sammensat af en tredjepart. Problemer, du oplever, kan være forårsaget af denne udvikler, så rejs venligst problemer med dem i første omgang ved at bruge nedenstående links.",
|
"support_third_party_description": "Din Immich-installation blev sammensat af en tredjepart. Problemer, du oplever, kan være forårsaget af denne udvikler, så rejs venligst problemer med dem i første omgang ved at bruge nedenstående links.",
|
||||||
|
"supporter": "Supporter",
|
||||||
"swap_merge_direction": "Byt retning for sammenfletning",
|
"swap_merge_direction": "Byt retning for sammenfletning",
|
||||||
"sync": "Synkronisér",
|
"sync": "Synkronisér",
|
||||||
"sync_albums": "Synkroniser albummer",
|
"sync_albums": "Synkroniser albummer",
|
||||||
@@ -2294,6 +2312,7 @@
|
|||||||
"unstack_action_prompt": "{count} ustakket",
|
"unstack_action_prompt": "{count} ustakket",
|
||||||
"unstacked_assets_count": "Ikke-stablet {count, plural, one {# aktiv} other {# aktiver}}",
|
"unstacked_assets_count": "Ikke-stablet {count, plural, one {# aktiv} other {# aktiver}}",
|
||||||
"unsupported_field_type": "Ikke-understøttet felttype",
|
"unsupported_field_type": "Ikke-understøttet felttype",
|
||||||
|
"unsupported_file_type": "Filen {file} kan ikke uploades, fordi filtypen {type} ikke understøttes.",
|
||||||
"untagged": "Umærket",
|
"untagged": "Umærket",
|
||||||
"untitled_workflow": "Unavngivet arbejdsgang",
|
"untitled_workflow": "Unavngivet arbejdsgang",
|
||||||
"up_next": "Næste",
|
"up_next": "Næste",
|
||||||
@@ -2320,6 +2339,8 @@
|
|||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Forbrug",
|
"usage": "Forbrug",
|
||||||
"use_biometric": "Brug biometrisk",
|
"use_biometric": "Brug biometrisk",
|
||||||
|
"use_browser_locale": "Brug browserens lokalitet",
|
||||||
|
"use_browser_locale_description": "Formatér datoer, klokkeslæt og tal baseret på din browsers lokalitet",
|
||||||
"use_current_connection": "Brug nuværende forbindelse",
|
"use_current_connection": "Brug nuværende forbindelse",
|
||||||
"use_custom_date_range": "Brug tilpasset datointerval i stedet",
|
"use_custom_date_range": "Brug tilpasset datointerval i stedet",
|
||||||
"user": "Bruger",
|
"user": "Bruger",
|
||||||
@@ -2373,6 +2394,7 @@
|
|||||||
"viewer_remove_from_stack": "Fjern fra stak",
|
"viewer_remove_from_stack": "Fjern fra stak",
|
||||||
"viewer_stack_use_as_main_asset": "Brug som hovedelement",
|
"viewer_stack_use_as_main_asset": "Brug som hovedelement",
|
||||||
"viewer_unstack": "Fjern fra stak",
|
"viewer_unstack": "Fjern fra stak",
|
||||||
|
"visibility": "Synlighed",
|
||||||
"visibility_changed": "Synlighed ændret for {count, plural, one {# person} other {# personer}}",
|
"visibility_changed": "Synlighed ændret for {count, plural, one {# person} other {# personer}}",
|
||||||
"visual": "Visuel",
|
"visual": "Visuel",
|
||||||
"visual_builder": "Visuel builder",
|
"visual_builder": "Visuel builder",
|
||||||
|
|||||||
+72
-49
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"about": "Über Immich",
|
"about": "Über",
|
||||||
"account": "Konto",
|
"account": "Konto",
|
||||||
"account_settings": "Kontoeinstellungen",
|
"account_settings": "Kontoeinstellungen",
|
||||||
"acknowledge": "Verstanden",
|
"acknowledge": "Verstanden",
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
"action_description": "Eine Reihe von Aktionen, die an den gefilterten Assets ausgeführt werden sollen",
|
"action_description": "Eine Reihe von Aktionen, die an den gefilterten Assets ausgeführt werden sollen",
|
||||||
"actions": "Aktionen",
|
"actions": "Aktionen",
|
||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
"active_count": "Aktive:{count}",
|
"active_count": "Aktive: {count}",
|
||||||
"activity": "Aktivität",
|
"activity": "Aktivität",
|
||||||
"activity_changed": "Aktivität ist {enabled, select, true {aktiviert} other {deaktiviert}}",
|
"activity_changed": "Aktivität ist {enabled, select, true {aktiviert} other {deaktiviert}}",
|
||||||
"add": "Hinzufügen",
|
"add": "Hinzufügen",
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"backup_database_enable_description": "Datenbank regelmäßig sichern",
|
"backup_database_enable_description": "Datenbank regelmäßig sichern",
|
||||||
"backup_keep_last_amount": "Anzahl der aufzubewahrenden früheren Sicherungen",
|
"backup_keep_last_amount": "Anzahl der aufzubewahrenden früheren Sicherungen",
|
||||||
"backup_onboarding_1_description": "Offsite-Kopie in der Cloud oder an einem anderen physischen Ort.",
|
"backup_onboarding_1_description": "Offsite-Kopie in der Cloud oder an einem anderen physischen Ort.",
|
||||||
"backup_onboarding_2_description": "lokale Kopien auf verschiedenen Geräten. Dazu gehören die Hauptdateien und eine lokale Sicherung dieser Dateien.",
|
"backup_onboarding_2_description": "Lokale Kopien auf verschiedenen Geräten. Dazu gehören die Hauptdateien und eine lokale Sicherung dieser Dateien.",
|
||||||
"backup_onboarding_3_description": "Kopien deiner Daten inklusive Originaldateien. Dies umfasst 1 Kopie an einem anderen Ort und 2 lokale Kopien.",
|
"backup_onboarding_3_description": "Kopien deiner Daten inklusive Originaldateien. Dies umfasst 1 Kopie an einem anderen Ort und 2 lokale Kopien.",
|
||||||
"backup_onboarding_description": "Eine <backblaze-link>3-2-1 Sicherungsstrategie</backblaze-link> wird empfohlen, um deine Daten zu schützen. Du solltest sowohl Kopien deiner hochgeladenen Fotos/Videos als auch der Immich-Datenbank aufbewahren, um eine umfassende Sicherungslösung zu haben.",
|
"backup_onboarding_description": "Eine <backblaze-link>3-2-1 Sicherungsstrategie</backblaze-link> wird empfohlen, um deine Daten zu schützen. Du solltest sowohl Kopien deiner hochgeladenen Fotos/Videos als auch der Immich-Datenbank aufbewahren, um eine umfassende Sicherungslösung zu haben.",
|
||||||
"backup_onboarding_footer": "Weitere Informationen zum Sichern von Immich findest du in der <link>Dokumentation</link>.",
|
"backup_onboarding_footer": "Weitere Informationen zum Sichern von Immich findest du in der <link>Dokumentation</link>.",
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
"confirm_user_pin_code_reset": "Bist du sicher, dass du den PIN-Code von {user} zurücksetzen möchtest?",
|
"confirm_user_pin_code_reset": "Bist du sicher, dass du den PIN-Code von {user} zurücksetzen möchtest?",
|
||||||
"copy_config_to_clipboard_description": "Kopieren Sie die aktuelle Systemkonfiguration als JSON-Objekt in die Zwischenablage",
|
"copy_config_to_clipboard_description": "Kopieren Sie die aktuelle Systemkonfiguration als JSON-Objekt in die Zwischenablage",
|
||||||
"create_job": "Aufgabe erstellen",
|
"create_job": "Aufgabe erstellen",
|
||||||
"cron_expression": "Cron-Zeitangabe",
|
"cron_expression": "Cron-Ausdruck",
|
||||||
"cron_expression_description": "Setze das Scanintervall im Cron-Format. Hilfe mit dem Format bietet dir dabei z. B. der <link>Crontab Guru</link>",
|
"cron_expression_description": "Setze das Scanintervall im Cron-Format. Hilfe mit dem Format bietet dir dabei z. B. der <link>Crontab Guru</link>",
|
||||||
"cron_expression_presets": "Vorlagen für Cron-Zeitangabe",
|
"cron_expression_presets": "Vorlagen für Cron-Zeitangabe",
|
||||||
"disable_login": "Login deaktivieren",
|
"disable_login": "Login deaktivieren",
|
||||||
@@ -309,9 +309,9 @@
|
|||||||
"reset_settings_to_recent_saved": "Einstellungen auf die zuletzt gespeicherten Einstellungen zurücksetzen",
|
"reset_settings_to_recent_saved": "Einstellungen auf die zuletzt gespeicherten Einstellungen zurücksetzen",
|
||||||
"scanning_library": "Bibliothek scannen",
|
"scanning_library": "Bibliothek scannen",
|
||||||
"search_jobs": "Suchaufgaben…",
|
"search_jobs": "Suchaufgaben…",
|
||||||
"send_welcome_email": "Begrüssungsmail senden",
|
"send_welcome_email": "Begrüßungsmail senden",
|
||||||
"server_external_domain_settings": "Externe Domain",
|
"server_external_domain_settings": "Externe Domain",
|
||||||
"server_external_domain_settings_description": "Domäne für öffentlich freigegebene Links, einschließlich http(s)://",
|
"server_external_domain_settings_description": "Für externe Links verwendete Domäne",
|
||||||
"server_public_users": "Öffentliche Benutzer",
|
"server_public_users": "Öffentliche Benutzer",
|
||||||
"server_public_users_description": "Beim Hinzufügen eines Benutzers zu freigegebenen Alben werden alle Benutzer (Name und E-Mail) aufgelistet. Wenn diese Option deaktiviert ist, steht die Benutzerliste nur Administratoren zur Verfügung.",
|
"server_public_users_description": "Beim Hinzufügen eines Benutzers zu freigegebenen Alben werden alle Benutzer (Name und E-Mail) aufgelistet. Wenn diese Option deaktiviert ist, steht die Benutzerliste nur Administratoren zur Verfügung.",
|
||||||
"server_settings": "Servereinstellungen",
|
"server_settings": "Servereinstellungen",
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
"transcoding_tone_mapping": "Farbton-Mapping",
|
"transcoding_tone_mapping": "Farbton-Mapping",
|
||||||
"transcoding_tone_mapping_description": "Versucht, das Aussehen von HDR-Videos bei der Konvertierung in SDR beizubehalten. Jeder Algorithmus geht unterschiedliche Kompromisse bei Farbe, Details und Helligkeit ein. Hable bewahrt Details, Mobius bewahrt die Farbe und Reinhard bewahrt die Helligkeit.",
|
"transcoding_tone_mapping_description": "Versucht, das Aussehen von HDR-Videos bei der Konvertierung in SDR beizubehalten. Jeder Algorithmus geht unterschiedliche Kompromisse bei Farbe, Details und Helligkeit ein. Hable bewahrt Details, Mobius bewahrt die Farbe und Reinhard bewahrt die Helligkeit.",
|
||||||
"transcoding_transcode_policy": "Transcodierungsrichtlinie",
|
"transcoding_transcode_policy": "Transcodierungsrichtlinie",
|
||||||
"transcoding_transcode_policy_description": "Richtlinie, wann ein Video transkodiert werden soll. HDR-Videos werden immer transkodiert (außer wenn die Transkodierung deaktiviert ist).",
|
"transcoding_transcode_policy_description": "Richtlinie, wann ein Video transkodiert werden soll. HDR-Videos und Videos ohne das Format YUV 4:2:0 werden immer transkodiert (außer wenn die Transkodierung deaktiviert ist).",
|
||||||
"transcoding_two_pass_encoding": "Two-Pass Codierung",
|
"transcoding_two_pass_encoding": "Two-Pass Codierung",
|
||||||
"transcoding_two_pass_encoding_setting_description": "Führt eine Transkodierung in zwei Durchgängen durch, um besser kodierte Videos zu erzeugen. Wenn die maximale Bitrate aktiviert ist (erforderlich für die Verwendung mit H.264 und HEVC), verwendet dieser Modus einen Bitratenbereich, der auf der maximalen Bitrate basiert, und ignoriert CRF. Für VP9 kann CRF verwendet werden, wenn die maximale Bitrate deaktiviert ist.",
|
"transcoding_two_pass_encoding_setting_description": "Führt eine Transkodierung in zwei Durchgängen durch, um besser kodierte Videos zu erzeugen. Wenn die maximale Bitrate aktiviert ist (erforderlich für die Verwendung mit H.264 und HEVC), verwendet dieser Modus einen Bitratenbereich, der auf der maximalen Bitrate basiert, und ignoriert CRF. Für VP9 kann CRF verwendet werden, wenn die maximale Bitrate deaktiviert ist.",
|
||||||
"transcoding_video_codec": "Video-Codec",
|
"transcoding_video_codec": "Video-Codec",
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
"user_successfully_removed": "Der Benutzer {email} wurde erfolgreich entfernt.",
|
"user_successfully_removed": "Der Benutzer {email} wurde erfolgreich entfernt.",
|
||||||
"users_page_description": "Administrator-Benutzerseite",
|
"users_page_description": "Administrator-Benutzerseite",
|
||||||
"version_check_enabled_description": "Versionsprüfung aktivieren",
|
"version_check_enabled_description": "Versionsprüfung aktivieren",
|
||||||
"version_check_implications": "Die Funktion zur Versionsprüfung basiert auf regelmäßiger Kommunikation mit GitHub.com",
|
"version_check_implications": "Die Funktion zur Versionsprüfung basiert auf regelmäßiger Kommunikation mit {server}",
|
||||||
"version_check_settings": "Versionsprüfung",
|
"version_check_settings": "Versionsprüfung",
|
||||||
"version_check_settings_description": "Aktivieren/Deaktivieren der Benachrichtigung über neue Versionen",
|
"version_check_settings_description": "Aktivieren/Deaktivieren der Benachrichtigung über neue Versionen",
|
||||||
"video_conversion_job": "Videos transkodieren",
|
"video_conversion_job": "Videos transkodieren",
|
||||||
@@ -472,7 +472,7 @@
|
|||||||
"advanced_settings_troubleshooting_title": "Fehlersuche",
|
"advanced_settings_troubleshooting_title": "Fehlersuche",
|
||||||
"age_months": "Alter {months, plural, one {# Monat} other {# Monate}}",
|
"age_months": "Alter {months, plural, one {# Monat} other {# Monate}}",
|
||||||
"age_year_months": "Alter 1 Jahr, {months, plural, one {# Monat} other {# Monate}}",
|
"age_year_months": "Alter 1 Jahr, {months, plural, one {# Monat} other {# Monate}}",
|
||||||
"age_years": "Alter {years, plural, one {# Jahr} other {# Jahre}}",
|
"age_years": "{years, plural, other {Alter #}}",
|
||||||
"album": "Album",
|
"album": "Album",
|
||||||
"album_added": "Album hinzugefügt",
|
"album_added": "Album hinzugefügt",
|
||||||
"album_added_notification_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn du zu einem freigegebenen Album hinzugefügt wurdest",
|
"album_added_notification_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn du zu einem freigegebenen Album hinzugefügt wurdest",
|
||||||
@@ -580,7 +580,7 @@
|
|||||||
"asset_restored_successfully": "Datei erfolgreich wiederhergestellt",
|
"asset_restored_successfully": "Datei erfolgreich wiederhergestellt",
|
||||||
"asset_skipped": "Übersprungen",
|
"asset_skipped": "Übersprungen",
|
||||||
"asset_skipped_in_trash": "Im Papierkorb",
|
"asset_skipped_in_trash": "Im Papierkorb",
|
||||||
"asset_trashed": "Datei Gelöscht",
|
"asset_trashed": "Datei gelöscht",
|
||||||
"asset_troubleshoot": "Datei Fehlerbehebung",
|
"asset_troubleshoot": "Datei Fehlerbehebung",
|
||||||
"asset_uploaded": "Hochgeladen",
|
"asset_uploaded": "Hochgeladen",
|
||||||
"asset_uploading": "Hochladen…",
|
"asset_uploading": "Hochladen…",
|
||||||
@@ -610,14 +610,14 @@
|
|||||||
"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",
|
||||||
"assets_were_part_of_albums_count": "{count, plural, one {Datei war} other {Dateien waren}} bereits in den Alben",
|
"assets_were_part_of_albums_count": "{count, plural, one {Datei war} other {Dateien waren}} bereits in den Alben",
|
||||||
"authorized_devices": "Verwendete Geräte",
|
"authorized_devices": "Verwendete Geräte",
|
||||||
"automatic_endpoint_switching_subtitle": "Verbinden Sie sich lokal über ein bestimmtes WiFi, wenn es verfügbar ist, und verwenden Sie andere Verbindungsmöglichkeiten",
|
"automatic_endpoint_switching_subtitle": "Verbinden Sie sich lokal über ein bestimmtes WLAN-Netz, wenn es verfügbar ist, und verwenden Sie ansonsten andere Verbindungsmöglichkeiten",
|
||||||
"automatic_endpoint_switching_title": "Automatische URL-Umschaltung",
|
"automatic_endpoint_switching_title": "Automatische URL-Umschaltung",
|
||||||
"autoplay_slideshow": "Automatische Diashow",
|
"autoplay_slideshow": "Automatische Diashow",
|
||||||
"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_backup_running_error": "Sicherung läuft im Hintergrund. Manuelle Sicherung kann nicht gestartet werden",
|
"background_backup_running_error": "Sicherung läuft im Hintergrund. Manuelle Sicherung kann nicht gestartet werden",
|
||||||
"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 WiFi-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",
|
||||||
"background_options": "Hintergrund Optionen",
|
"background_options": "Hintergrund Optionen",
|
||||||
"backup": "Sicherung",
|
"backup": "Sicherung",
|
||||||
"backup_album_selection_page_albums_device": "Alben auf dem Gerät ({count})",
|
"backup_album_selection_page_albums_device": "Alben auf dem Gerät ({count})",
|
||||||
@@ -652,7 +652,7 @@
|
|||||||
"backup_controller_page_background_is_on": "Automatische Sicherung im Hintergrund ist aktiviert",
|
"backup_controller_page_background_is_on": "Automatische Sicherung im Hintergrund ist aktiviert",
|
||||||
"backup_controller_page_background_turn_off": "Hintergrundservice ausschalten",
|
"backup_controller_page_background_turn_off": "Hintergrundservice ausschalten",
|
||||||
"backup_controller_page_background_turn_on": "Hintergrundservice einschalten",
|
"backup_controller_page_background_turn_on": "Hintergrundservice einschalten",
|
||||||
"backup_controller_page_background_wifi": "Nur im WiFi",
|
"backup_controller_page_background_wifi": "Nur im WLAN",
|
||||||
"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",
|
||||||
@@ -687,7 +687,7 @@
|
|||||||
"backup_options_page_title": "Sicherungsoptionen",
|
"backup_options_page_title": "Sicherungsoptionen",
|
||||||
"backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund",
|
"backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund",
|
||||||
"backup_settings_subtitle": "Upload-Einstellungen verwalten",
|
"backup_settings_subtitle": "Upload-Einstellungen verwalten",
|
||||||
"backup_upload_details_page_more_details": "Tippen für weitere Details",
|
"backup_upload_details_page_more_details": "Tippe für weitere Details",
|
||||||
"backward": "Rückwärts",
|
"backward": "Rückwärts",
|
||||||
"biometric_auth_enabled": "Biometrische Authentifizierung aktiviert",
|
"biometric_auth_enabled": "Biometrische Authentifizierung aktiviert",
|
||||||
"biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen",
|
"biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen",
|
||||||
@@ -698,7 +698,7 @@
|
|||||||
"blurred_background": "Unscharfer Hintergrund",
|
"blurred_background": "Unscharfer Hintergrund",
|
||||||
"bugs_and_feature_requests": "Fehler & Verbesserungsvorschläge",
|
"bugs_and_feature_requests": "Fehler & Verbesserungsvorschläge",
|
||||||
"build": "Build",
|
"build": "Build",
|
||||||
"build_image": "Build Abbild",
|
"build_image": "Abbildversion",
|
||||||
"bulk_delete_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien gemeinsam}} löschen möchtest? Dabei wird die größte Datei jeder Gruppe behalten und alle anderen Duplikate endgültig gelöscht. Diese Aktion kann nicht rückgängig gemacht werden!",
|
"bulk_delete_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien gemeinsam}} löschen möchtest? Dabei wird die größte Datei jeder Gruppe behalten und alle anderen Duplikate endgültig gelöscht. Diese Aktion kann nicht rückgängig gemacht werden!",
|
||||||
"bulk_keep_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien}} behalten möchtest? Dies wird alle Duplikat-Gruppen auflösen ohne etwas zu löschen.",
|
"bulk_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.",
|
||||||
@@ -728,7 +728,7 @@
|
|||||||
"cannot_undo_this_action": "Diese Aktion kann nicht rückgängig gemacht werden!",
|
"cannot_undo_this_action": "Diese Aktion kann nicht rückgängig gemacht werden!",
|
||||||
"cannot_update_the_description": "Beschreibung kann nicht aktualisiert werden",
|
"cannot_update_the_description": "Beschreibung kann nicht aktualisiert werden",
|
||||||
"cast": "Übertragen",
|
"cast": "Übertragen",
|
||||||
"cast_description": "Konfiguration verfügbarer Ziele",
|
"cast_description": "Verfügbare Cast-Ziele konfigurieren",
|
||||||
"change_date": "Datum ändern",
|
"change_date": "Datum ändern",
|
||||||
"change_description": "Beschreibung anpassen",
|
"change_description": "Beschreibung anpassen",
|
||||||
"change_display_order": "Anzeigereihenfolge ändern",
|
"change_display_order": "Anzeigereihenfolge ändern",
|
||||||
@@ -739,7 +739,7 @@
|
|||||||
"change_password": "Passwort ändern",
|
"change_password": "Passwort ändern",
|
||||||
"change_password_description": "Dies ist entweder das erste Mal, dass du dich im System anmeldest, oder es wurde eine Anfrage zur Änderung deines Passworts gestellt. Bitte gib unten dein neues Passwort ein.",
|
"change_password_description": "Dies ist entweder das erste Mal, dass du dich im System anmeldest, oder es wurde eine Anfrage zur Änderung deines Passworts gestellt. Bitte gib unten dein neues Passwort ein.",
|
||||||
"change_password_form_confirm_password": "Passwort bestätigen",
|
"change_password_form_confirm_password": "Passwort bestätigen",
|
||||||
"change_password_form_description": "Hallo {name}\n\nDas ist entweder das erste Mal dass du dich einloggst oder es wurde eine Anfrage zur Änderung deines Passwortes gestellt. Bitte gib das neue Passwort ein.",
|
"change_password_form_description": "Hallo {name}\n\nDas ist entweder das erste Mal, dass du dich einloggst oder es wurde eine Anfrage zur Änderung deines Passwortes gestellt. Bitte gib das neue Passwort ein.",
|
||||||
"change_password_form_log_out": "Von allen Geräte abmelden",
|
"change_password_form_log_out": "Von allen Geräte abmelden",
|
||||||
"change_password_form_log_out_description": "Es wird empfohlen, alle anderen Geräte abzumelden",
|
"change_password_form_log_out_description": "Es wird empfohlen, alle anderen Geräte abzumelden",
|
||||||
"change_password_form_new_password": "Neues Passwort",
|
"change_password_form_new_password": "Neues Passwort",
|
||||||
@@ -754,7 +754,7 @@
|
|||||||
"charging_requirement_mobile_backup": "Backup im Hintergrund erfordert Aufladen des Geräts",
|
"charging_requirement_mobile_backup": "Backup im Hintergrund erfordert Aufladen des Geräts",
|
||||||
"check_corrupt_asset_backup": "Auf beschädigte Asset-Backups überprüfen",
|
"check_corrupt_asset_backup": "Auf beschädigte Asset-Backups überprüfen",
|
||||||
"check_corrupt_asset_backup_button": "Überprüfung durchführen",
|
"check_corrupt_asset_backup_button": "Überprüfung durchführen",
|
||||||
"check_corrupt_asset_backup_description": "Führe diese Prüfung nur mit aktivierten WiFi durch, nachdem alle Dateien gesichert worden sind. Dieser Vorgang kann ein paar Minuten dauern.",
|
"check_corrupt_asset_backup_description": "Führe diese Prüfung nur mit aktivierten WLAN durch, nachdem alle Dateien gesichert worden sind. Dieser Vorgang kann ein paar Minuten dauern.",
|
||||||
"check_logs": "Logs prüfen",
|
"check_logs": "Logs prüfen",
|
||||||
"checksum": "Prüfsumme",
|
"checksum": "Prüfsumme",
|
||||||
"choose_matching_people_to_merge": "Wähle passende Personen zum Zusammenführen",
|
"choose_matching_people_to_merge": "Wähle passende Personen zum Zusammenführen",
|
||||||
@@ -794,6 +794,11 @@
|
|||||||
"color": "Farbe",
|
"color": "Farbe",
|
||||||
"color_theme": "Farb-Theme",
|
"color_theme": "Farb-Theme",
|
||||||
"command": "Befehl",
|
"command": "Befehl",
|
||||||
|
"command_palette_prompt": "Finde schnell Seiten, Aktionen oder Befehle",
|
||||||
|
"command_palette_to_close": "Schließen",
|
||||||
|
"command_palette_to_navigate": "eingeben",
|
||||||
|
"command_palette_to_select": "Auswählen",
|
||||||
|
"command_palette_to_show_all": "Alle anzeigen",
|
||||||
"comment_deleted": "Kommentar gelöscht",
|
"comment_deleted": "Kommentar gelöscht",
|
||||||
"comment_options": "Kommentaroptionen",
|
"comment_options": "Kommentaroptionen",
|
||||||
"comments_and_likes": "Kommentare & Likes",
|
"comments_and_likes": "Kommentare & Likes",
|
||||||
@@ -802,7 +807,7 @@
|
|||||||
"completed": "Abgeschlossen",
|
"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_new_pin_code": "Neuen PIN-Code bestätigen",
|
||||||
@@ -861,13 +866,14 @@
|
|||||||
"crop_aspect_ratio_fixed": "Fixiert",
|
"crop_aspect_ratio_fixed": "Fixiert",
|
||||||
"crop_aspect_ratio_free": "Frei",
|
"crop_aspect_ratio_free": "Frei",
|
||||||
"crop_aspect_ratio_original": "Original",
|
"crop_aspect_ratio_original": "Original",
|
||||||
|
"crop_aspect_ratio_square": "Quadratisch",
|
||||||
"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_pin_code": "Aktueller PIN-Code",
|
||||||
"current_server_address": "Aktuelle Serveradresse",
|
"current_server_address": "Aktuelle Serveradresse",
|
||||||
"custom_date": "Benutzerdefiniertes Datum",
|
"custom_date": "Benutzerdefiniertes Datum",
|
||||||
"custom_locale": "Benutzerdefinierte Sprache",
|
"custom_locale": "Benutzerdefiniertes Gebietsschema",
|
||||||
"custom_locale_description": "Datumsangaben und Zahlen je nach Sprache und Land formatieren",
|
"custom_locale_description": "Datumsangaben, Uhrzeiten und Zahlen je nach Sprache und Land formatieren",
|
||||||
"custom_url": "Benutzerdefinierte URL",
|
"custom_url": "Benutzerdefinierte URL",
|
||||||
"cutoff_date_description": "Behalte Fotos der letzten…",
|
"cutoff_date_description": "Behalte Fotos der letzten…",
|
||||||
"cutoff_day": "{count, plural, one {Tag} other {Tage}}",
|
"cutoff_day": "{count, plural, one {Tag} other {Tage}}",
|
||||||
@@ -875,7 +881,7 @@
|
|||||||
"daily_title_text_date": "E, dd MMM",
|
"daily_title_text_date": "E, dd MMM",
|
||||||
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
||||||
"dark": "Dunkel",
|
"dark": "Dunkel",
|
||||||
"dark_theme": "Dunkle Ansicht umschalten",
|
"dark_theme": "Auf dunkle Ansicht umschalten",
|
||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"date_after": "Datum nach",
|
"date_after": "Datum nach",
|
||||||
"date_and_time": "Datum und Zeit",
|
"date_and_time": "Datum und Zeit",
|
||||||
@@ -886,12 +892,8 @@
|
|||||||
"day": "Tag",
|
"day": "Tag",
|
||||||
"days": "Tage",
|
"days": "Tage",
|
||||||
"deduplicate_all": "Alle Duplikate entfernen",
|
"deduplicate_all": "Alle Duplikate entfernen",
|
||||||
"deduplication_criteria_1": "Bildgröße in Bytes",
|
"default_locale": "Standardgebietsschema",
|
||||||
"deduplication_criteria_2": "Anzahl der EXIF-Daten",
|
"default_locale_description": "Datumsangaben und Zahlen werden entsprechend Ihrer Browsereinstellungen formatiert",
|
||||||
"deduplication_info": "Deduplizierungsinformationen",
|
|
||||||
"deduplication_info_description": "Für die automatische Datei-Vorauswahl und das Deduplizieren aller Dateien berücksichtigen wir:",
|
|
||||||
"default_locale": "Standard-Sprache",
|
|
||||||
"default_locale_description": "Datumsangaben und Zahlen basierend auf dem Gebietsschema des Browsers formatieren",
|
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"delete_action_confirmation_message": "Bist du sicher, dass du dieses Objekt löschen willst? Diese Aktion wird das Objekt in den Papierkorb des Servers verschieben und fragen, ob du es lokal löschen willst",
|
"delete_action_confirmation_message": "Bist du sicher, dass du dieses Objekt löschen willst? Diese Aktion wird das Objekt in den Papierkorb des Servers verschieben und fragen, ob du es lokal löschen willst",
|
||||||
"delete_action_prompt": "{count} gelöscht",
|
"delete_action_prompt": "{count} gelöscht",
|
||||||
@@ -967,7 +969,7 @@
|
|||||||
"downloading_media": "Medien werden heruntergeladen",
|
"downloading_media": "Medien werden heruntergeladen",
|
||||||
"drop_files_to_upload": "Lade Dateien hoch, indem du sie hierhin ziehst",
|
"drop_files_to_upload": "Lade Dateien hoch, indem du sie hierhin ziehst",
|
||||||
"duplicates": "Duplikate",
|
"duplicates": "Duplikate",
|
||||||
"duplicates_description": "Löse jede Gruppe auf, indem du angibst, welche, wenn überhaupt, Duplikate sind",
|
"duplicates_description": "Löse jede Gruppe auf, indem du angibst, welche, wenn überhaupt, Duplikate sind.",
|
||||||
"duration": "Dauer",
|
"duration": "Dauer",
|
||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
"edit_album": "Album bearbeiten",
|
"edit_album": "Album bearbeiten",
|
||||||
@@ -1004,6 +1006,8 @@
|
|||||||
"editor_edits_applied_success": "Änderungen erfolgreich angewendet",
|
"editor_edits_applied_success": "Änderungen erfolgreich angewendet",
|
||||||
"editor_flip_horizontal": "Horizontal spiegeln",
|
"editor_flip_horizontal": "Horizontal spiegeln",
|
||||||
"editor_flip_vertical": "Vertikal spiegeln",
|
"editor_flip_vertical": "Vertikal spiegeln",
|
||||||
|
"editor_handle_corner": "{corner, select, top_left {Oben links} top_right {Oben rechts} bottom_left {Unten links} bottom_right {Unten rechts} other {A}} Eckgriff",
|
||||||
|
"editor_handle_edge": "{edge, select, top {Oben} bottom {Unten} left {Links} right {Rechts} other {Ein}} Kantenanfasser",
|
||||||
"editor_orientation": "Ausrichtung",
|
"editor_orientation": "Ausrichtung",
|
||||||
"editor_reset_all_changes": "Änderungen zurücksetzen",
|
"editor_reset_all_changes": "Änderungen zurücksetzen",
|
||||||
"editor_rotate_left": "Um 90° gegen den Uhrzeigersinn drehen",
|
"editor_rotate_left": "Um 90° gegen den Uhrzeigersinn drehen",
|
||||||
@@ -1019,7 +1023,7 @@
|
|||||||
"enabled": "Aktiviert",
|
"enabled": "Aktiviert",
|
||||||
"end_date": "Enddatum",
|
"end_date": "Enddatum",
|
||||||
"enqueued": "Eingereiht",
|
"enqueued": "Eingereiht",
|
||||||
"enter_wifi_name": "WiFi-Name eingeben",
|
"enter_wifi_name": "WLAN-Name eingeben",
|
||||||
"enter_your_pin_code": "PIN-Code eingeben",
|
"enter_your_pin_code": "PIN-Code eingeben",
|
||||||
"enter_your_pin_code_subtitle": "Gib deinen PIN-Code ein, um auf den gesperrten Ordner zuzugreifen",
|
"enter_your_pin_code_subtitle": "Gib deinen PIN-Code ein, um auf den gesperrten Ordner zuzugreifen",
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
@@ -1069,15 +1073,16 @@
|
|||||||
"failed_to_update_notification_status": "Benachrichtigungsstatus aktualisieren fehlgeschlagen",
|
"failed_to_update_notification_status": "Benachrichtigungsstatus aktualisieren fehlgeschlagen",
|
||||||
"incorrect_email_or_password": "Ungültige E-Mail oder Passwort",
|
"incorrect_email_or_password": "Ungültige E-Mail oder Passwort",
|
||||||
"library_folder_already_exists": "Dieser Importpfad existiert bereits.",
|
"library_folder_already_exists": "Dieser Importpfad existiert bereits.",
|
||||||
|
"page_not_found": "Seite nicht gefunden",
|
||||||
"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",
|
||||||
"profile_picture_transparent_pixels": "Profilbilder dürfen keine transparenten Pixel haben. Bitte zoome heran und/oder verschiebe das Bild.",
|
"profile_picture_transparent_pixels": "Profilbilder dürfen keine transparenten Pixel haben. Bitte zoome heran und/oder verschiebe das Bild.",
|
||||||
"quota_higher_than_disk_size": "Dein festgelegtes Kontingent ist größer als der verfügbare Speicher",
|
"quota_higher_than_disk_size": "Dein festgelegtes Kontingent ist größer als der verfügbare Speicher",
|
||||||
"something_went_wrong": "Ein Fehler ist eingetreten",
|
"something_went_wrong": "Ein Fehler ist eingetreten",
|
||||||
"unable_to_add_album_users": "Benutzer konnten nicht zum Album hinzugefügt werden",
|
"unable_to_add_album_users": "Benutzer konnten nicht zum Album hinzugefügt werden",
|
||||||
"unable_to_add_assets_to_shared_link": "Datei konnte nicht zum geteilten Link hinzugefügt werden",
|
"unable_to_add_assets_to_shared_link": "Datei konnte nicht zum geteilten Link hinzugefügt werden",
|
||||||
"unable_to_add_comment": "Es kann kein Kommentar hinzufügt werden",
|
"unable_to_add_comment": "Es kann kein Kommentar hinzugefügt werden",
|
||||||
"unable_to_add_exclusion_pattern": "Ausschlussmuster konnte nicht hinzugefügt werden",
|
"unable_to_add_exclusion_pattern": "Ausschlussmuster konnte nicht hinzugefügt werden",
|
||||||
"unable_to_add_partners": "Es können keine Partner hinzufügt werden",
|
"unable_to_add_partners": "Es können keine Partner hinzugefügt werden",
|
||||||
"unable_to_add_remove_archive": "Datei konnte nicht {archived, select, true {aus dem Archiv entfernt} other {zum Archiv hinzugefügt}} werden",
|
"unable_to_add_remove_archive": "Datei konnte nicht {archived, select, true {aus dem Archiv entfernt} other {zum Archiv hinzugefügt}} werden",
|
||||||
"unable_to_add_remove_favorites": "Datei konnte nicht {favorite, select, true {von den Favoriten entfernt} other {zu den Favoriten hinzugefügt}} werden",
|
"unable_to_add_remove_favorites": "Datei konnte nicht {favorite, select, true {von den Favoriten entfernt} other {zu den Favoriten hinzugefügt}} werden",
|
||||||
"unable_to_archive_unarchive": "Konnte nicht {archived, select, true {archivieren} other {entarchivieren}}",
|
"unable_to_archive_unarchive": "Konnte nicht {archived, select, true {archivieren} other {entarchivieren}}",
|
||||||
@@ -1168,6 +1173,7 @@
|
|||||||
"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",
|
||||||
"exit_slideshow": "Diashow beenden",
|
"exit_slideshow": "Diashow beenden",
|
||||||
|
"expand": "Erweitern",
|
||||||
"expand_all": "Alle aufklappen",
|
"expand_all": "Alle aufklappen",
|
||||||
"experimental_settings_new_asset_list_subtitle": "In Arbeit",
|
"experimental_settings_new_asset_list_subtitle": "In Arbeit",
|
||||||
"experimental_settings_new_asset_list_title": "Experimentelles Fotogitter aktivieren",
|
"experimental_settings_new_asset_list_title": "Experimentelles Fotogitter aktivieren",
|
||||||
@@ -1212,6 +1218,7 @@
|
|||||||
"filter_description": "Bedingungen zur Filterung der betreffenden Dateien",
|
"filter_description": "Bedingungen zur Filterung der betreffenden Dateien",
|
||||||
"filter_people": "Personen filtern",
|
"filter_people": "Personen filtern",
|
||||||
"filter_places": "Orte filtern",
|
"filter_places": "Orte filtern",
|
||||||
|
"filter_tags": "Tags filtern",
|
||||||
"filters": "Filter",
|
"filters": "Filter",
|
||||||
"find_them_fast": "Finde sie schneller mit der Suche nach Namen",
|
"find_them_fast": "Finde sie schneller mit der Suche nach Namen",
|
||||||
"first": "Erste",
|
"first": "Erste",
|
||||||
@@ -1232,7 +1239,7 @@
|
|||||||
"geolocation_instruction_location": "Klicke auf eine Datei mit GPS Koordinaten um diesen Standort zu verwenden oder wähle einen Standort direkt auf der Karte",
|
"geolocation_instruction_location": "Klicke auf eine Datei mit GPS Koordinaten um diesen Standort zu verwenden oder wähle einen Standort direkt auf der Karte",
|
||||||
"get_help": "Hilfe erhalten",
|
"get_help": "Hilfe erhalten",
|
||||||
"get_people_error": "Fehler beim Laden der Personen",
|
"get_people_error": "Fehler beim Laden der Personen",
|
||||||
"get_wifiname_error": "WiFi-Name konnte nicht ermittelt werden. Vergewissere dich, dass die erforderlichen Berechtigungen erteilt wurden und du mit einem WiFi-Netzwerk verbunden bist",
|
"get_wifiname_error": "Das WLAN-Netz 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",
|
||||||
@@ -1271,7 +1278,7 @@
|
|||||||
"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 archivieren, ü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, überspringe",
|
"home_page_delete_err_partner": "Inhalte von Partnern können nicht gelöscht werden, überspringe",
|
||||||
@@ -1379,9 +1386,11 @@
|
|||||||
"library_page_sort_title": "Titel des Albums",
|
"library_page_sort_title": "Titel des Albums",
|
||||||
"licenses": "Lizenzen",
|
"licenses": "Lizenzen",
|
||||||
"light": "Hell",
|
"light": "Hell",
|
||||||
|
"light_theme": "Auf helle Ansicht umschalten",
|
||||||
"like": "Gefällt mir",
|
"like": "Gefällt mir",
|
||||||
"like_deleted": "Like gelöscht",
|
"like_deleted": "Like gelöscht",
|
||||||
"link_motion_video": "Bewegungsvideo verknüpfen",
|
"link_motion_video": "Bewegungsvideo verknüpfen",
|
||||||
|
"link_to_docs": "Weitere Informationen finden Sie in der <link>Dokumentation</link>.",
|
||||||
"link_to_oauth": "Mit OAuth verknüpfen",
|
"link_to_oauth": "Mit OAuth verknüpfen",
|
||||||
"linked_oauth_account": "Verknüpftes OAuth-Konto",
|
"linked_oauth_account": "Verknüpftes OAuth-Konto",
|
||||||
"list": "Liste",
|
"list": "Liste",
|
||||||
@@ -1396,7 +1405,7 @@
|
|||||||
"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": "Standort",
|
"location": "Standort",
|
||||||
"location_permission": "Standort Genehmigung",
|
"location_permission": "Standort Genehmigung",
|
||||||
"location_permission_content": "Um die automatische Umschaltfunktion nutzen zu können, benötigt Immich genaue Standortberechtigung, damit es den Namen des aktuellen WiFi-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",
|
||||||
@@ -1554,7 +1563,7 @@
|
|||||||
"name_or_nickname": "Name oder Nickname",
|
"name_or_nickname": "Name oder Nickname",
|
||||||
"name_required": "Name ist erforderlich",
|
"name_required": "Name ist erforderlich",
|
||||||
"navigate": "Navigation",
|
"navigate": "Navigation",
|
||||||
"navigate_to_time": "Navigiere zu Zeit",
|
"navigate_to_time": "Zu Zeitpunkt navigieren",
|
||||||
"network_requirement_photos_upload": "Mobile Daten verwenden, um Fotos zu sichern",
|
"network_requirement_photos_upload": "Mobile Daten verwenden, um Fotos zu sichern",
|
||||||
"network_requirement_videos_upload": "Mobile Daten verwenden, um Videos zu sichern",
|
"network_requirement_videos_upload": "Mobile Daten verwenden, um Videos zu sichern",
|
||||||
"network_requirements": "Anforderungen ans Netzwerk",
|
"network_requirements": "Anforderungen ans Netzwerk",
|
||||||
@@ -1613,7 +1622,7 @@
|
|||||||
"not_available": "N/A",
|
"not_available": "N/A",
|
||||||
"not_in_any_album": "In keinem Album",
|
"not_in_any_album": "In keinem Album",
|
||||||
"not_selected": "Nicht ausgewählt",
|
"not_selected": "Nicht ausgewählt",
|
||||||
"notes": "Notizen",
|
"notes": "Hinweise",
|
||||||
"nothing_here_yet": "Noch nichts hier",
|
"nothing_here_yet": "Noch nichts hier",
|
||||||
"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.",
|
||||||
@@ -1642,6 +1651,8 @@
|
|||||||
"online": "Online",
|
"online": "Online",
|
||||||
"only_favorites": "Nur Favoriten",
|
"only_favorites": "Nur Favoriten",
|
||||||
"open": "Öffnen",
|
"open": "Öffnen",
|
||||||
|
"open_calendar": "Kalender öffnen",
|
||||||
|
"open_in_browser": "Im Browser ö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",
|
||||||
@@ -1655,7 +1666,7 @@
|
|||||||
"other_devices": "Andere Geräte",
|
"other_devices": "Andere Geräte",
|
||||||
"other_entities": "Andere Entitäten",
|
"other_entities": "Andere Entitäten",
|
||||||
"other_variables": "Sonstige Variablen",
|
"other_variables": "Sonstige Variablen",
|
||||||
"owned": "Eigenes",
|
"owned": "Eigene",
|
||||||
"owner": "Besitzer",
|
"owner": "Besitzer",
|
||||||
"page": "Seite",
|
"page": "Seite",
|
||||||
"partner": "Partner",
|
"partner": "Partner",
|
||||||
@@ -1710,8 +1721,8 @@
|
|||||||
"permission_onboarding_permission_limited": "Berechtigungen unzureichend. Um Immich das Sichern von ganzen Sammlungen zu ermöglichen, muss der Zugriff auf alle Fotos und Videos in den Einstellungen erlaubt werden.",
|
"permission_onboarding_permission_limited": "Berechtigungen unzureichend. Um Immich das Sichern von ganzen Sammlungen zu ermöglichen, muss der Zugriff auf alle Fotos und Videos in den Einstellungen erlaubt werden.",
|
||||||
"permission_onboarding_request": "Immich benötigt Berechtigung um auf deine Fotos und Videos zuzugreifen.",
|
"permission_onboarding_request": "Immich benötigt Berechtigung um auf deine Fotos und Videos zuzugreifen.",
|
||||||
"person": "Person",
|
"person": "Person",
|
||||||
"person_age_months": "{months, plural, one {# month} other {# months}} alt",
|
"person_age_months": "{months, plural, one {# Monat} other {# Monate}} alt",
|
||||||
"person_age_year_months": "1 Jahr, {months, plural, one {# month} other {# months}} alt",
|
"person_age_year_months": "1 Jahr, {months, plural, one {# Monat} other {# Monate}} alt",
|
||||||
"person_age_years": "{years, plural, one {# Jahr} other {# Jahre}} alt",
|
"person_age_years": "{years, plural, one {# Jahr} other {# Jahre}} alt",
|
||||||
"person_birthdate": "Geboren am {date}",
|
"person_birthdate": "Geboren am {date}",
|
||||||
"person_hidden": "{name}{hidden, select, true { (verborgen)} other {}}",
|
"person_hidden": "{name}{hidden, select, true { (verborgen)} other {}}",
|
||||||
@@ -1801,9 +1812,8 @@
|
|||||||
"rate_asset": "Datei bewerten",
|
"rate_asset": "Datei bewerten",
|
||||||
"rating": "Bewertung",
|
"rating": "Bewertung",
|
||||||
"rating_clear": "Bewertung löschen",
|
"rating_clear": "Bewertung löschen",
|
||||||
"rating_count": "{count, plural, one {# Stern} other {# Sterne}}",
|
"rating_count": "{count, plural, =0 {Unbewertet} one {# Stern} other {# Sterne}}",
|
||||||
"rating_description": "Stellt die EXIF-Bewertung im Informationsbereich dar",
|
"rating_description": "Stellt die EXIF-Bewertung im Informationsbereich dar",
|
||||||
"rating_set": "Mit {rating, plural, one {# Stern} other {# Sternen}} bewertet",
|
|
||||||
"reaction_options": "Reaktionsmöglichkeiten",
|
"reaction_options": "Reaktionsmöglichkeiten",
|
||||||
"read_changelog": "Changelog lesen",
|
"read_changelog": "Changelog lesen",
|
||||||
"readonly_mode_disabled": "Schreibgeschützter Modus deaktiviert",
|
"readonly_mode_disabled": "Schreibgeschützter Modus deaktiviert",
|
||||||
@@ -1863,7 +1873,7 @@
|
|||||||
"repair": "Reparatur",
|
"repair": "Reparatur",
|
||||||
"repair_no_results_message": "Nicht auffindbare und fehlende Dateien werden hier angezeigt",
|
"repair_no_results_message": "Nicht auffindbare und fehlende Dateien werden hier angezeigt",
|
||||||
"replace_with_upload": "Durch Upload ersetzen",
|
"replace_with_upload": "Durch Upload ersetzen",
|
||||||
"repository": "Repositorium",
|
"repository": "Repository",
|
||||||
"require_password": "Passwort erforderlich",
|
"require_password": "Passwort erforderlich",
|
||||||
"require_user_to_change_password_on_first_login": "Benutzer muss das Passwort beim ersten Login ändern",
|
"require_user_to_change_password_on_first_login": "Benutzer muss das Passwort beim ersten Login ändern",
|
||||||
"rescan": "Erneut scannen",
|
"rescan": "Erneut scannen",
|
||||||
@@ -1875,7 +1885,10 @@
|
|||||||
"reset_pin_code_success": "PIN-Code erfolgreich zurückgesetzt",
|
"reset_pin_code_success": "PIN-Code erfolgreich zurückgesetzt",
|
||||||
"reset_pin_code_with_password": "Mit deinem Passwort kannst du jederzeit deinen PIN-Code zurücksetzen",
|
"reset_pin_code_with_password": "Mit deinem Passwort kannst du jederzeit deinen PIN-Code zurücksetzen",
|
||||||
"reset_sqlite": "SQLite Datenbank zurücksetzen",
|
"reset_sqlite": "SQLite Datenbank zurücksetzen",
|
||||||
"reset_sqlite_confirmation": "Bist du sicher, dass du die SQLite-Datenbank zurücksetzen willst? Du musst dich ab- und wieder anmelden, um die Daten neu zu synchronisieren",
|
"reset_sqlite_clear_app_data": "Daten löschen",
|
||||||
|
"reset_sqlite_confirmation": "Bist du sicher, dass du die SQLite-Datenbank zurücksetzen willst? Es werden alle Einstellungen zurückgesetzt und du wirst abgemeldet.",
|
||||||
|
"reset_sqlite_confirmation_note": "Warnung: Du musst nach dem Zurücksetzten die App neu starten.",
|
||||||
|
"reset_sqlite_done": "Die Anwendungsdaten wurden gelöscht. Bitte starte Immich neu um dich erneut anzumelden.",
|
||||||
"reset_sqlite_success": "SQLite Datenbank erfolgreich zurückgesetzt",
|
"reset_sqlite_success": "SQLite Datenbank erfolgreich zurückgesetzt",
|
||||||
"reset_to_default": "Auf Standard zurücksetzen",
|
"reset_to_default": "Auf Standard zurücksetzen",
|
||||||
"resolution": "Auflösung",
|
"resolution": "Auflösung",
|
||||||
@@ -1903,6 +1916,7 @@
|
|||||||
"saved_settings": "Einstellungen gespeichert",
|
"saved_settings": "Einstellungen gespeichert",
|
||||||
"say_something": "Etwas sagen",
|
"say_something": "Etwas sagen",
|
||||||
"scaffold_body_error_occurred": "Ein Fehler ist aufgetreten",
|
"scaffold_body_error_occurred": "Ein Fehler ist aufgetreten",
|
||||||
|
"scaffold_body_error_unrecoverable": "Es ist ein nicht behebbarer Fehler aufgetreten. Bitte teilen Sie uns den Fehler und den Stacktrace auf Discord oder GitHub mit, damit wir Ihnen helfen können. Falls nötig, können Sie unten die App-Daten löschen.",
|
||||||
"scan": "Scannen",
|
"scan": "Scannen",
|
||||||
"scan_all_libraries": "Alle Bibliotheken scannen",
|
"scan_all_libraries": "Alle Bibliotheken scannen",
|
||||||
"scan_library": "Scannen",
|
"scan_library": "Scannen",
|
||||||
@@ -1938,6 +1952,7 @@
|
|||||||
"search_filter_ocr": "Suche per OCR",
|
"search_filter_ocr": "Suche per OCR",
|
||||||
"search_filter_people_title": "Personen auswählen",
|
"search_filter_people_title": "Personen auswählen",
|
||||||
"search_filter_star_rating": "Sternebewertung",
|
"search_filter_star_rating": "Sternebewertung",
|
||||||
|
"search_filter_tags_title": "Tags 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",
|
||||||
@@ -1997,7 +2012,7 @@
|
|||||||
"selected_count": "{count, plural, other {# ausgewählt}}",
|
"selected_count": "{count, plural, other {# ausgewählt}}",
|
||||||
"selected_gps_coordinates": "Ausgewählte GPS-Koordinaten",
|
"selected_gps_coordinates": "Ausgewählte GPS-Koordinaten",
|
||||||
"send_message": "Nachricht senden",
|
"send_message": "Nachricht senden",
|
||||||
"send_welcome_email": "Begrüssungsmail senden",
|
"send_welcome_email": "Begrüßungsmail senden",
|
||||||
"server_endpoint": "Server-Endpunkt",
|
"server_endpoint": "Server-Endpunkt",
|
||||||
"server_info_box_app_version": "App-Version",
|
"server_info_box_app_version": "App-Version",
|
||||||
"server_info_box_server_url": "Server-URL",
|
"server_info_box_server_url": "Server-URL",
|
||||||
@@ -2017,6 +2032,9 @@
|
|||||||
"set_profile_picture": "Profilbild einstellen",
|
"set_profile_picture": "Profilbild einstellen",
|
||||||
"set_slideshow_to_fullscreen": "Diashow auf Vollbild einstellen",
|
"set_slideshow_to_fullscreen": "Diashow auf Vollbild einstellen",
|
||||||
"set_stack_primary_asset": "Als primäre Datei festlegen",
|
"set_stack_primary_asset": "Als primäre Datei festlegen",
|
||||||
|
"setting_image_navigation_enable_subtitle": "Aktivieren, um durch Tippen auf den linksäußeren/rechtsäußeren Bildschirmrand zum vorherigen/nächsten Bild zu navigieren.",
|
||||||
|
"setting_image_navigation_enable_title": "Tippen zum Navigieren",
|
||||||
|
"setting_image_navigation_title": "Bild Navigation",
|
||||||
"setting_image_viewer_help": "Der Detailbildbetrachter lädt zuerst ein (kleines) Vorschaubild, dann ein Vorschaubild in mittlerer Größe (falls aktiviert) und schließlich das Original (falls aktiviert).",
|
"setting_image_viewer_help": "Der Detailbildbetrachter lädt zuerst ein (kleines) Vorschaubild, dann ein Vorschaubild in mittlerer Größe (falls aktiviert) und schließlich das Original (falls aktiviert).",
|
||||||
"setting_image_viewer_original_subtitle": "Aktivieren, um das Originalbild in voller Auflösung (groß!) zu laden. Deaktivieren, um den Datenverbrauch zu reduzieren (sowohl im Netzwerk als auch im Gerätespeicher).",
|
"setting_image_viewer_original_subtitle": "Aktivieren, um das Originalbild in voller Auflösung (groß!) zu laden. Deaktivieren, um den Datenverbrauch zu reduzieren (sowohl im Netzwerk als auch im Gerätespeicher).",
|
||||||
"setting_image_viewer_original_title": "Original laden",
|
"setting_image_viewer_original_title": "Original laden",
|
||||||
@@ -2154,7 +2172,7 @@
|
|||||||
"sort_people_by_similarity": "Personen nach Ähnlichkeit sortieren",
|
"sort_people_by_similarity": "Personen nach Ähnlichkeit sortieren",
|
||||||
"sort_recent": "Neuestes Foto",
|
"sort_recent": "Neuestes Foto",
|
||||||
"sort_title": "Titel",
|
"sort_title": "Titel",
|
||||||
"source": "Quellcode",
|
"source": "Quelle",
|
||||||
"stack": "Stapel",
|
"stack": "Stapel",
|
||||||
"stack_action_prompt": "{count} gestapelt",
|
"stack_action_prompt": "{count} gestapelt",
|
||||||
"stack_duplicates": "Duplikate stapeln",
|
"stack_duplicates": "Duplikate stapeln",
|
||||||
@@ -2183,6 +2201,7 @@
|
|||||||
"support": "Unterstützung",
|
"support": "Unterstützung",
|
||||||
"support_and_feedback": "Unterstützung & Feedback",
|
"support_and_feedback": "Unterstützung & Feedback",
|
||||||
"support_third_party_description": "Deine Immich-Installation wurde von einem Drittanbieter zusammengestellt. Probleme, die bei dir auftreten, können durch dieses Paket verursacht werden. Bitte wende dich daher in erster Linie an diesen Anbieter, indem du die unten stehenden Links verwendest.",
|
"support_third_party_description": "Deine Immich-Installation wurde von einem Drittanbieter zusammengestellt. Probleme, die bei dir auftreten, können durch dieses Paket verursacht werden. Bitte wende dich daher in erster Linie an diesen Anbieter, indem du die unten stehenden Links verwendest.",
|
||||||
|
"supporter": "Unterstützer",
|
||||||
"swap_merge_direction": "Vertauschen der Zusammenführungsrichtung",
|
"swap_merge_direction": "Vertauschen der Zusammenführungsrichtung",
|
||||||
"sync": "Synchronisieren",
|
"sync": "Synchronisieren",
|
||||||
"sync_albums": "Alben synchronisieren",
|
"sync_albums": "Alben synchronisieren",
|
||||||
@@ -2294,10 +2313,11 @@
|
|||||||
"unstack_action_prompt": "{count} entstapelt",
|
"unstack_action_prompt": "{count} entstapelt",
|
||||||
"unstacked_assets_count": "{count, plural, one {# Datei} other {# Dateien}} entstapelt",
|
"unstacked_assets_count": "{count, plural, one {# Datei} other {# Dateien}} entstapelt",
|
||||||
"unsupported_field_type": "Nicht unterstützter Feldtyp",
|
"unsupported_field_type": "Nicht unterstützter Feldtyp",
|
||||||
|
"unsupported_file_type": "Die Datei {file} kann nicht hochgeladen werden, da der Dateityp {type} nicht unterstützt wird.",
|
||||||
"untagged": "Ohne Tag",
|
"untagged": "Ohne Tag",
|
||||||
"untitled_workflow": "Unbenannter Workflow",
|
"untitled_workflow": "Unbenannter Workflow",
|
||||||
"up_next": "Weiter",
|
"up_next": "Weiter",
|
||||||
"update_location_action_prompt": "Aktualsiere den Ort von {count} ausgewählten Dateien mit:",
|
"update_location_action_prompt": "Aktualisiere den Ort von {count} ausgewählten Dateien mit:",
|
||||||
"updated_at": "Aktualisiert",
|
"updated_at": "Aktualisiert",
|
||||||
"updated_password": "Passwort aktualisiert",
|
"updated_password": "Passwort aktualisiert",
|
||||||
"upload": "Hochladen",
|
"upload": "Hochladen",
|
||||||
@@ -2320,6 +2340,8 @@
|
|||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Verwendung",
|
"usage": "Verwendung",
|
||||||
"use_biometric": "Biometrie verwenden",
|
"use_biometric": "Biometrie verwenden",
|
||||||
|
"use_browser_locale": "Gebietsschema des Browsers verwenden",
|
||||||
|
"use_browser_locale_description": "Datum, Uhrzeit und Zahlen werden entsprechend den Einstellungen Ihres Browsers formatiert",
|
||||||
"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",
|
||||||
@@ -2373,6 +2395,7 @@
|
|||||||
"viewer_remove_from_stack": "Aus Stapel entfernen",
|
"viewer_remove_from_stack": "Aus Stapel entfernen",
|
||||||
"viewer_stack_use_as_main_asset": "An Stapelanfang",
|
"viewer_stack_use_as_main_asset": "An Stapelanfang",
|
||||||
"viewer_unstack": "Stapel aufheben",
|
"viewer_unstack": "Stapel aufheben",
|
||||||
|
"visibility": "Sichtbarkeit",
|
||||||
"visibility_changed": "Sichtbarkeit für {count, plural, one {# Person} other {# Personen}} geändert",
|
"visibility_changed": "Sichtbarkeit für {count, plural, one {# Person} other {# Personen}} geändert",
|
||||||
"visual": "Visuell",
|
"visual": "Visuell",
|
||||||
"visual_builder": "Visueller Editor",
|
"visual_builder": "Visueller Editor",
|
||||||
@@ -2383,7 +2406,7 @@
|
|||||||
"welcome": "Willkommen",
|
"welcome": "Willkommen",
|
||||||
"welcome_to_immich": "Willkommen bei Immich",
|
"welcome_to_immich": "Willkommen bei Immich",
|
||||||
"width": "Breite",
|
"width": "Breite",
|
||||||
"wifi_name": "WiFi-Name",
|
"wifi_name": "WLAN-Netzwerk",
|
||||||
"workflow_delete_prompt": "Bist du sicher, dass du diesen Workflow löschen willst?",
|
"workflow_delete_prompt": "Bist du sicher, dass du diesen Workflow löschen willst?",
|
||||||
"workflow_deleted": "Workflow gelöscht",
|
"workflow_deleted": "Workflow gelöscht",
|
||||||
"workflow_description": "Workflow-Beschreibung",
|
"workflow_description": "Workflow-Beschreibung",
|
||||||
@@ -2402,7 +2425,7 @@
|
|||||||
"years_ago": "Vor {years, plural, one {einem Jahr} other {# Jahren}}",
|
"years_ago": "Vor {years, plural, one {einem Jahr} other {# Jahren}}",
|
||||||
"yes": "Ja",
|
"yes": "Ja",
|
||||||
"you_dont_have_any_shared_links": "Du hast keine geteilten Links",
|
"you_dont_have_any_shared_links": "Du hast keine geteilten Links",
|
||||||
"your_wifi_name": "Dein WiFi-Name",
|
"your_wifi_name": "Dein WLAN-Netzwerk",
|
||||||
"zero_to_clear_rating": "drücke 0 um die Dateibewertung zurückzusetzen",
|
"zero_to_clear_rating": "drücke 0 um die Dateibewertung zurückzusetzen",
|
||||||
"zoom_image": "Bild vergrößern",
|
"zoom_image": "Bild vergrößern",
|
||||||
"zoom_to_bounds": "Auf Grenzen zoomen"
|
"zoom_to_bounds": "Auf Grenzen zoomen"
|
||||||
|
|||||||
+40
-12
@@ -75,30 +75,58 @@
|
|||||||
"confirm_reprocess_all_faces": "Bisch sicher, dass du alli Gsichter neu verarbeite wotsch? Däbii werde au benannti Persone glöscht.",
|
"confirm_reprocess_all_faces": "Bisch sicher, dass du alli Gsichter neu verarbeite wotsch? Däbii werde au benannti Persone glöscht.",
|
||||||
"confirm_user_password_reset": "Bisch sicher, dass du s Passwort für {user} möchtisch zruggsetze?",
|
"confirm_user_password_reset": "Bisch sicher, dass du s Passwort für {user} möchtisch zruggsetze?",
|
||||||
"confirm_user_pin_code_reset": "Bisch sicher, dass du de PIN-Code vo {user} möchtisch zruggsetze?",
|
"confirm_user_pin_code_reset": "Bisch sicher, dass du de PIN-Code vo {user} möchtisch zruggsetze?",
|
||||||
"copy_config_to_clipboard_description": "Kopiere die aktuelle Systemkonfiguration als JSON-Objekt in die Zwischenablage",
|
"copy_config_to_clipboard_description": "Kopier die aktuelli Systemkonfiguration als JSON-Objekt i d'Zwüschenablage",
|
||||||
"create_job": "Uufgabe erstelle",
|
"create_job": "Uufgabe erstelle",
|
||||||
"cron_expression": "Cron-Ziitagabe",
|
"cron_expression": "Cron-Ziitagabe",
|
||||||
"cron_expression_description": "Setz s Scanintervall im Cron-Format. Hilf mit däm Format bütet z. B. der <link>Crontab Guru</link>",
|
"cron_expression_description": "Setz s Scanintervall im Cron-Format. Hilf mit däm Format bütet z. B. der <link>Crontab Guru</link>",
|
||||||
"cron_expression_presets": "Vorlage für Cron-Uusdruck",
|
"cron_expression_presets": "Vorlage für Cron-Uusdruck",
|
||||||
"disable_login": "Login deaktiviere",
|
"disable_login": "Login deaktiviere",
|
||||||
"duplicate_detection_job_description": "Die Uufgab füehrt s maschinelle Lärne für jedi Datei us, zum Duplikat finde. Die Uufgabe berueht uf de intelligente Suechi",
|
"duplicate_detection_job_description": "Die Uufgab füehrt s maschinelle Lärne für jedi Datei us, zum Duplikat finde. Die Uufgabe berueht uf de intelligente Suechi",
|
||||||
"exclusion_pattern_description": "Mit Ausschlussmustern können Dateien und Ordner beim Scannen Ihrer Bibliothek ignoriert werden. Dies ist nützlich, wenn du Ordner hast, die Dateien enthalten, die du nicht importieren möchtest, wie z. B. RAW-Dateien.",
|
"exclusion_pattern_description": "Mit Uusschlussmuster chönnd Dateie und Ordner bim Scanne vo dinere Bibliothek ignoriert wärde. Das isch nützlich, wenn du Ordner häsch, wo Dateien drin händ, wo d nöd wotsch importiere, wie z. B. RAW-Dateie.",
|
||||||
"export_config_as_json_description": "Lade die aktuelle Systemkonfiguration als JSON-Datei herunter",
|
"export_config_as_json_description": "Lad die aktuelli Systemkonfiguration als JSON-Datei abe",
|
||||||
"external_libraries_page_description": "Externe Bibliotheksseite für Administratoren",
|
"external_libraries_page_description": "Externi Bibliothekssiite für Administratore",
|
||||||
"face_detection": "Gsichtserkennig",
|
"face_detection": "Gsichtserkennig",
|
||||||
"face_detection_description": "Diese Aufgabe erfasst Gesichter in Dateien mittels maschinellen Lernens. Bei Videos wird nur die Miniaturansicht verwendet. „Aktualisieren“ verarbeitet alle Dateien neu. „Zurücksetzen“ setzt zusätzlich alle Gesichter zurück. „Fehlende“ stellt nur nicht verarbeitete Dateien in die Warteschlange. Erfasste Gesichter werden zur Gesichtsidentifizierung in die Warteschlange gestellt, um sie in bestehende oder neue Personen zu gruppieren.",
|
"face_detection_description": "Die Uufgab erfasst Gsichter in Dateien dur maschinells Lerne. Bi Video wird nur d'Miniaturasicht brucht. „Aktualisiere“ verarbeitet all Dateie neu. „Zruggsetze“ setzt au no all Gsichter zrugg. „Fehlendi“ stellt nur nöd verarbeiteti Dateie in d'Warteschlange. Erfassti Gsichter wärdet zur Gsichtsidentifizierig in diWarteschlange gstellt, damit sie i bestehendi oder neui Persone z'gruppiere.",
|
||||||
"facial_recognition_job_description": "Diese Aufgabe gruppiert im Anschluss an die Gesichtserfassung die erfassten Gesichter zu Personen. „Zurücksetzen“ gruppiert alle Gesichter neu, während „Fehlende“ Gesichter ohne Zuordnung in die Warteschlange stellt.",
|
"facial_recognition_job_description": "Die Uufgabe gruppiert im Anschluss an d'Gsichtserfassig die erfasste Gsichter zu Persone. „Zruggsetze“ gruppiert alli Gsichter neu und mit „Fehlendi“ werdet Gsichter ohni Zuordnig i d'Warteschlange gstellt.",
|
||||||
"failed_job_command": "Befehl {command} ist für Aufgabe {job} fehlgeschlagen",
|
"failed_job_command": "Befehl {command} hät für d'Uufgabe {job} nöd funktioniert",
|
||||||
"force_delete_user_warning": "WARNUNG: Diese Aktion löscht sofort den Benutzer und all seine Dateien. Dies kann nicht rückgängig gemacht werden und die Dateien können nicht wiederhergestellt werden.",
|
"force_delete_user_warning": "WARNIG: Die Aktion löscht dä Benutzer und all sini Dateie. Das chann nöd rückgängig gmacht wärde und d'Dateie chönnd nöd wiederhergstellt wärde.",
|
||||||
"image_format": "Format",
|
"image_format": "Format",
|
||||||
"image_format_description": "WebP erzeugt kleinere Dateien als JPEG, ist aber etwas langsamer in der Erstellung.",
|
"image_format_description": "WebP erzeugt chlineri Dateie we JPEG, isch aber es bitz langsamer i de Erstellig.",
|
||||||
"image_fullsize_description": "Hochauflösendes Bild mit entfernten Metadaten, das beim Zoomen verwendet wird",
|
"image_fullsize_description": "Hochuflösends Bild mit glöschte Metadate, wo bim Zoome brucht wird",
|
||||||
"image_fullsize_enabled": "Hochauflösende Vorschaubilder aktivieren",
|
"image_fullsize_enabled": "Hochuflösendi Vorschaubilder aktiviere",
|
||||||
"image_fullsize_enabled_description": "Generiere hochauflösende Vorschaubilder in Originalauflösung für nicht web-kompatibel Formate. Wenn \"Eingebettete Vorschau bevorzugen\" aktiviert ist, werden eingebettete Vorschaubilder direkt verwendet. Hat keinen Einfluss auf web-kompatible Formate wie JPEG.",
|
"image_fullsize_enabled_description": "Generiere hochauflösende Vorschaubilder in Originalauflösung für nicht web-kompatibel Formate. Wenn \"Eingebettete Vorschau bevorzugen\" aktiviert ist, werden eingebettete Vorschaubilder direkt verwendet. Hat keinen Einfluss auf web-kompatible Formate wie JPEG.",
|
||||||
"image_fullsize_quality_description": "Qualität der hochauflösenden Vorschaubilder von 1-100. Höher ist besser, erzeugt aber grössere Dateien.",
|
"image_fullsize_quality_description": "Qualität der hochauflösenden Vorschaubilder von 1-100. Höher ist besser, erzeugt aber grössere Dateien.",
|
||||||
"image_fullsize_title": "Hochauflösende Vorschaueinstellungen",
|
"image_fullsize_title": "Hochauflösende Vorschaueinstellungen",
|
||||||
"image_prefer_embedded_preview": "Eingebettete Vorschau bevorzugen",
|
"image_prefer_embedded_preview": "Eingebettete Vorschau bevorzugen",
|
||||||
"image_prefer_embedded_preview_setting_description": "Verwende eingebettete Vorschaubilder in RAW-Fotos als Grundlage für die Bildverarbeitung, sofern diese zur Verfügung stehen. Dies kann bei einigen Bildern genauere Farben erzeugen, allerdings ist die Qualität der Vorschau kameraabhängig und das Bild kann mehr Kompressionsartefakte aufweisen.",
|
"image_prefer_embedded_preview_setting_description": "Verwende eingebettete Vorschaubilder in RAW-Fotos als Grundlage für die Bildverarbeitung, sofern diese zur Verfügung stehen. Dies kann bei einigen Bildern genauere Farben erzeugen, allerdings ist die Qualität der Vorschau kameraabhängig und das Bild kann mehr Kompressionsartefakte aufweisen.",
|
||||||
"image_prefer_wide_gamut": "Breites Spektrum bevorzugen"
|
"image_prefer_wide_gamut": "Breites Spektrum bevorzugen",
|
||||||
|
"image_prefer_wide_gamut_setting_description": "Bruuch Display P3 für Vorschaubildli. Das erhaltet d'Vitalität von Bildli mit grossem Farbruum besser. Uf alte Grät mit alte Browser chann das aber andersch uusgseh. sRGB-Bildli wärdet als sRGB bhalte zum Farbänderige vermiide.",
|
||||||
|
"image_preview_description": "Mittelgrossi Bildli ohni Metadate, bruuchts für Einzelaasichte und fürs maschinelle Lärne",
|
||||||
|
"image_preview_quality_description": "Vorschauqualität vo 1-100. Höcher isch besser, git aber grösseri Dateie und chan d'App Schwuppdizität reduziere. Z tüffi Wert chönnd s maschinelle Lärne beiträchtige.",
|
||||||
|
"image_preview_title": "Vorschauiistellige",
|
||||||
|
"image_progressive": "Fortlaufend",
|
||||||
|
"image_progressive_description": "Codier fortlaufendi JPEG-Bildi: Sie wärdet bim Lade aufbauend aazeiget. Das hät kei Würkig uf WebP-Bildi.",
|
||||||
|
"image_quality": "Qualität",
|
||||||
|
"image_resolution": "Uuflösig",
|
||||||
|
"image_resolution_description": "Höcheri Uuflösig erhaltet meh Detail, gaht aber länger zum codiere, macht grösseri Dateie und chan d'App Schuppdizität reduziere.",
|
||||||
|
"image_settings": "Bild-Iistellige",
|
||||||
|
"image_settings_description": "Qualität und Uuflösig von erstellte Bildli verwalte",
|
||||||
|
"image_thumbnail_description": "Chlini Vorschaubildli ohni Metadate, bruuchts für Aasichte mit Gruppe vo Föteli wie i de Hauptziitachse",
|
||||||
|
"image_thumbnail_quality_description": "Vorschauqualität vo 1-100. Höcher isch besser, git aber grösseri Dateie und chan d'App Schwuppdizität reduziere.",
|
||||||
|
"image_thumbnail_title": "Iistellige für Vorschaubildli",
|
||||||
|
"import_config_from_json_description": "Systemkonfiguration importiere durs Ufelade vonere JSON-Datei",
|
||||||
|
"job_concurrency": "{job} Näbeläufigkeit",
|
||||||
|
"job_created": "Uufgab erstellt",
|
||||||
|
"job_not_concurrency_safe": "Die Uufgabe ist nöd für Paralleluusführig gmacht.",
|
||||||
|
"job_settings": "Uufgabe-Iistellige",
|
||||||
|
"job_settings_description": "Uufgabe-Näbeläufigkeit verwalte",
|
||||||
|
"jobs_over_time": "Uufgabe in ziitliche Verlauf",
|
||||||
|
"library_created": "Bibliothek erstellt: {library}",
|
||||||
|
"library_deleted": "Bibliothek glöscht",
|
||||||
|
"library_details": "Bibliotheks-Details",
|
||||||
|
"library_folder_description": "Gib en Order zum Importiere a. Dä Order mit sine Underordner wird nach Bildli und Videos durchsucht.",
|
||||||
|
"library_remove_exclusion_pattern_prompt": "Bisch sicher, dass das Uuschluss-Muster wotsch lösche?",
|
||||||
|
"library_remove_folder_prompt": "Bisch sicher, dass dä Import-Ordner wotsch lösche?",
|
||||||
|
"library_scanning": "Regelmässigi Überprüefig"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-18
@@ -61,7 +61,7 @@
|
|||||||
"backup_onboarding_1_description": "αντίγραφο ασφαλείας εκτός εγκατάστασης, είτε στο cloud είτε σε άλλη φυσική τοποθεσία.",
|
"backup_onboarding_1_description": "αντίγραφο ασφαλείας εκτός εγκατάστασης, είτε στο cloud είτε σε άλλη φυσική τοποθεσία.",
|
||||||
"backup_onboarding_2_description": "τοπικά αντίγραφα σε διαφορετικές συσκευές. Αυτό περιλαμβάνει τα κύρια αρχεία και ένα τοπικό αντίγραφο ασφαλείας αυτών των αρχείων.",
|
"backup_onboarding_2_description": "τοπικά αντίγραφα σε διαφορετικές συσκευές. Αυτό περιλαμβάνει τα κύρια αρχεία και ένα τοπικό αντίγραφο ασφαλείας αυτών των αρχείων.",
|
||||||
"backup_onboarding_3_description": "συνολικά αντίγραφα των δεδομένων σας, συμπεριλαμβανομένων των αρχικών αρχείων. Αυτό περιλαμβάνει 1 αντίγραφο εκτός εγκατάστασης (offsite) και 2 τοπικά αντίγραφα.",
|
"backup_onboarding_3_description": "συνολικά αντίγραφα των δεδομένων σας, συμπεριλαμβανομένων των αρχικών αρχείων. Αυτό περιλαμβάνει 1 αντίγραφο εκτός εγκατάστασης (offsite) και 2 τοπικά αντίγραφα.",
|
||||||
"backup_onboarding_description": "Συνιστάται η στρατηγική <backblaze-link>αντιγράφων ασφαλείας 3-2-1</backblaze-link> για την προστασία των δεδομένων σας. Θα πρέπει να διατηρείτε αντίγραφα των ανεβασμένων φωτογραφιών/βίντεό σας, καθώς και της βάσης δεδομένων του Immich, για μια ολοκληρωμένη λύση backup.",
|
"backup_onboarding_description": "Συνιστάται η <backblaze-link>3-2-1 στρατηγική αντιγράφων ασφαλείας</backblaze-link> για την προστασία των δεδομένων σας. Θα πρέπει να διατηρείτε αντίγραφα των ανεβασμένων φωτογραφιών/βίντεό σας, καθώς και της βάσης δεδομένων του Immich, για μια ολοκληρωμένη λύση backup.",
|
||||||
"backup_onboarding_footer": "Για περισσότερες πληροφορίες σχετικά με τη δημιουργία αντιγράφων ασφαλείας του Immich, ανατρέξε στον <link>οδηγό τεκμηρίωσης</link>.",
|
"backup_onboarding_footer": "Για περισσότερες πληροφορίες σχετικά με τη δημιουργία αντιγράφων ασφαλείας του Immich, ανατρέξε στον <link>οδηγό τεκμηρίωσης</link>.",
|
||||||
"backup_onboarding_parts_title": "Ένα αντίγραφο ασφαλείας τύπου 3-2-1 περιλαμβάνει:",
|
"backup_onboarding_parts_title": "Ένα αντίγραφο ασφαλείας τύπου 3-2-1 περιλαμβάνει:",
|
||||||
"backup_onboarding_title": "Αντίγραφα ασφαλείας",
|
"backup_onboarding_title": "Αντίγραφα ασφαλείας",
|
||||||
@@ -311,7 +311,7 @@
|
|||||||
"search_jobs": "Αναζήτηση εργασιών…",
|
"search_jobs": "Αναζήτηση εργασιών…",
|
||||||
"send_welcome_email": "Αποστολή email καλωσορίσματος",
|
"send_welcome_email": "Αποστολή email καλωσορίσματος",
|
||||||
"server_external_domain_settings": "Εξωτερική διεύθυνση τομέα",
|
"server_external_domain_settings": "Εξωτερική διεύθυνση τομέα",
|
||||||
"server_external_domain_settings_description": "Διεύθυνση τομέα για δημόσιους κοινούς συνδέσμους, περιλαμβανομένου του http(s)://",
|
"server_external_domain_settings_description": "Η διεύθυνση που χρησιμοποιείται για εξωτερικούς συνδέσμους",
|
||||||
"server_public_users": "Δημόσιοι Χρήστες",
|
"server_public_users": "Δημόσιοι Χρήστες",
|
||||||
"server_public_users_description": "Όλοι οι χρήστες (όνομα και email) εμφανίζονται κατά την προσθήκη ενός χρήστη σε κοινόχρηστα άλμπουμ. Όταν αυτή η επιλογή είναι απενεργοποιημένη, η λίστα χρηστών θα είναι διαθέσιμη μόνο στους διαχειριστές.",
|
"server_public_users_description": "Όλοι οι χρήστες (όνομα και email) εμφανίζονται κατά την προσθήκη ενός χρήστη σε κοινόχρηστα άλμπουμ. Όταν αυτή η επιλογή είναι απενεργοποιημένη, η λίστα χρηστών θα είναι διαθέσιμη μόνο στους διαχειριστές.",
|
||||||
"server_settings": "Ρυθμίσεις διακομιστή",
|
"server_settings": "Ρυθμίσεις διακομιστή",
|
||||||
@@ -372,7 +372,7 @@
|
|||||||
"transcoding_audio_codec": "Κωδικοποιητής ήχου",
|
"transcoding_audio_codec": "Κωδικοποιητής ήχου",
|
||||||
"transcoding_audio_codec_description": "Το Opus είναι η επιλογή για την υψηλότερη ποιότητα, αλλά έχει χαμηλότερη συμβατότητα με παλιές συσκευές ή λογισμικό.",
|
"transcoding_audio_codec_description": "Το Opus είναι η επιλογή για την υψηλότερη ποιότητα, αλλά έχει χαμηλότερη συμβατότητα με παλιές συσκευές ή λογισμικό.",
|
||||||
"transcoding_bitrate_description": "Βίντεο με ρυθμό μετάδοσης μεγαλύτερο από το μέγιστο ή που δεν είναι σε αποδεκτή μορφή",
|
"transcoding_bitrate_description": "Βίντεο με ρυθμό μετάδοσης μεγαλύτερο από το μέγιστο ή που δεν είναι σε αποδεκτή μορφή",
|
||||||
"transcoding_codecs_learn_more": "Για να μάθετε περισσότερα για την ορολογία που χρησιμοποιείται εδώ, ανατρέξτε στην τεκμηρίωση του FFmpeg για τους κωδικοποιητές <h264-link>H.264</h264-link>, <hevc-link>HEVC</hevc-link> και <vp9-link>VP9</vp9-link>.",
|
"transcoding_codecs_learn_more": "Για να μάθετε περισσότερα για την ορολογία που χρησιμοποιείται εδώ, ανατρέξτε στην τεκμηρίωση του FFmpeg για τους κωδικοποιητές <h264-link>H.264 codec</h264-link>, <hevc-link>HEVC codec</hevc-link> και <vp9-link>VP9 codec</vp9-link>.",
|
||||||
"transcoding_constant_quality_mode": "Λειτουργία σταθερής ποιότητας",
|
"transcoding_constant_quality_mode": "Λειτουργία σταθερής ποιότητας",
|
||||||
"transcoding_constant_quality_mode_description": "Το ICQ είναι καλύτερο από το CQP, αλλά ορισμένες συσκευές επιτάχυνσης υλικού δεν υποστηρίζουν αυτήν τη λειτουργία. Η ρύθμιση αυτής της επιλογής θα προτιμήσει την καθορισμένη λειτουργία κατά τη χρήση κωδικοποίησης βάσει ποιότητας. Αγνοείται από το NVENC, καθώς δεν υποστηρίζει το ICQ.",
|
"transcoding_constant_quality_mode_description": "Το ICQ είναι καλύτερο από το CQP, αλλά ορισμένες συσκευές επιτάχυνσης υλικού δεν υποστηρίζουν αυτήν τη λειτουργία. Η ρύθμιση αυτής της επιλογής θα προτιμήσει την καθορισμένη λειτουργία κατά τη χρήση κωδικοποίησης βάσει ποιότητας. Αγνοείται από το NVENC, καθώς δεν υποστηρίζει το ICQ.",
|
||||||
"transcoding_constant_rate_factor": "Σταθερός παράγοντας ρυθμού (-crf)",
|
"transcoding_constant_rate_factor": "Σταθερός παράγοντας ρυθμού (-crf)",
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
"transcoding_tone_mapping": "Χαρτογράφηση χρωματικών τόνων",
|
"transcoding_tone_mapping": "Χαρτογράφηση χρωματικών τόνων",
|
||||||
"transcoding_tone_mapping_description": "Προσπαθεί να διατηρήσει την εμφάνιση των HDR βίντεο όταν μετατρέπονται σε SDR. Κάθε αλγόριθμος κάνει διαφορετικές επιλογές σχετικά με τα χρώματα, τις λεπτομέρειες και τη φωτεινότητα. Ο αλγόριθμος Hable διατηρεί τις λεπτομέρειες, ο Mobius διατηρεί τα χρώματα και ο Reinhard διατηρεί τη φωτεινότητα.",
|
"transcoding_tone_mapping_description": "Προσπαθεί να διατηρήσει την εμφάνιση των HDR βίντεο όταν μετατρέπονται σε SDR. Κάθε αλγόριθμος κάνει διαφορετικές επιλογές σχετικά με τα χρώματα, τις λεπτομέρειες και τη φωτεινότητα. Ο αλγόριθμος Hable διατηρεί τις λεπτομέρειες, ο Mobius διατηρεί τα χρώματα και ο Reinhard διατηρεί τη φωτεινότητα.",
|
||||||
"transcoding_transcode_policy": "Πολιτική μετατροπής (βίντεο / ήχου)",
|
"transcoding_transcode_policy": "Πολιτική μετατροπής (βίντεο / ήχου)",
|
||||||
"transcoding_transcode_policy_description": "Πολιτική για το πότε πρέπει να μετατραπεί ένα βίντεο. Τα βίντεο HDR θα μετατρέπονται πάντα (εκτός αν η μετατροπή είναι απενεργοποιημένη).",
|
"transcoding_transcode_policy_description": "Πολιτική για το πότε πρέπει να γίνει μετατροπή ενός βίντεο. Τα HDR βίντεο και τα βίντεο με pixel format διαφορετικό από YUV 4:2:0 θα μετατρέπονται πάντα (εκτός αν η μετατροπή είναι απενεργοποιημένη).",
|
||||||
"transcoding_two_pass_encoding": "Κωδικοποίηση δύο περασμάτων",
|
"transcoding_two_pass_encoding": "Κωδικοποίηση δύο περασμάτων",
|
||||||
"transcoding_two_pass_encoding_setting_description": "Μετατροπή σε δύο περάσματα για την παραγωγή βίντεο με καλύτερη κωδικοποίηση. Όταν είναι ενεργοποιημένος ο μέγιστος ρυθμός μετάδοσης (απαραίτητος για λειτουργία με H.264 και HEVC), αυτή η λειτουργία χρησιμοποιεί ένα εύρος ρυθμού μετάδοσης βάσει του μέγιστου ρυθμού μετάδοσης και αγνοεί το CRF. Στον κωδικοποιητή VP9, το CRF μπορεί να χρησιμοποιηθεί εάν ο μέγιστος ρυθμός μετάδοσης είναι απενεργοποιημένος.",
|
"transcoding_two_pass_encoding_setting_description": "Μετατροπή σε δύο περάσματα για την παραγωγή βίντεο με καλύτερη κωδικοποίηση. Όταν είναι ενεργοποιημένος ο μέγιστος ρυθμός μετάδοσης (απαραίτητος για λειτουργία με H.264 και HEVC), αυτή η λειτουργία χρησιμοποιεί ένα εύρος ρυθμού μετάδοσης βάσει του μέγιστου ρυθμού μετάδοσης και αγνοεί το CRF. Στον κωδικοποιητή VP9, το CRF μπορεί να χρησιμοποιηθεί εάν ο μέγιστος ρυθμός μετάδοσης είναι απενεργοποιημένος.",
|
||||||
"transcoding_video_codec": "Κωδικοποιητής βίντεο",
|
"transcoding_video_codec": "Κωδικοποιητής βίντεο",
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
"user_successfully_removed": "Ο χρήστης {email} αφαιρέθηκε με επιτυχία.",
|
"user_successfully_removed": "Ο χρήστης {email} αφαιρέθηκε με επιτυχία.",
|
||||||
"users_page_description": "Σελίδα χρηστών διαχειριστή",
|
"users_page_description": "Σελίδα χρηστών διαχειριστή",
|
||||||
"version_check_enabled_description": "Ενεργοποίηση ελέγχου έκδοσης",
|
"version_check_enabled_description": "Ενεργοποίηση ελέγχου έκδοσης",
|
||||||
"version_check_implications": "Η λειτουργία ελέγχου έκδοσης, εξαρτάται από την περιοδική επικοινωνία με το github.com",
|
"version_check_implications": "Η λειτουργία ελέγχου έκδοσης, εξαρτάται από την περιοδική επικοινωνία με το {server}",
|
||||||
"version_check_settings": "Έλεγχος εκδοσης",
|
"version_check_settings": "Έλεγχος εκδοσης",
|
||||||
"version_check_settings_description": "Ενεργοποίηση/απενεργοποίηση της ειδοποίησης για νέα έκδοση",
|
"version_check_settings_description": "Ενεργοποίηση/απενεργοποίηση της ειδοποίησης για νέα έκδοση",
|
||||||
"video_conversion_job": "Μετατροπή βίντεο",
|
"video_conversion_job": "Μετατροπή βίντεο",
|
||||||
@@ -794,6 +794,11 @@
|
|||||||
"color": "Χρώμα",
|
"color": "Χρώμα",
|
||||||
"color_theme": "Χρώμα θέματος",
|
"color_theme": "Χρώμα θέματος",
|
||||||
"command": "Εντολή",
|
"command": "Εντολή",
|
||||||
|
"command_palette_prompt": "Γρήγορη εύρεση σελίδων, ενεργειών ή εντολών",
|
||||||
|
"command_palette_to_close": "για κλείσιμο",
|
||||||
|
"command_palette_to_navigate": "για είσοδο",
|
||||||
|
"command_palette_to_select": "για επιλογή",
|
||||||
|
"command_palette_to_show_all": "για προβολή όλων",
|
||||||
"comment_deleted": "Το σχόλιο διαγράφηκε",
|
"comment_deleted": "Το σχόλιο διαγράφηκε",
|
||||||
"comment_options": "Επιλογές σχολίου",
|
"comment_options": "Επιλογές σχολίου",
|
||||||
"comments_and_likes": "Σχόλια & αντιδράσεις (likes)",
|
"comments_and_likes": "Σχόλια & αντιδράσεις (likes)",
|
||||||
@@ -861,13 +866,14 @@
|
|||||||
"crop_aspect_ratio_fixed": "Διορθώθηκε",
|
"crop_aspect_ratio_fixed": "Διορθώθηκε",
|
||||||
"crop_aspect_ratio_free": "Ελεύθερο",
|
"crop_aspect_ratio_free": "Ελεύθερο",
|
||||||
"crop_aspect_ratio_original": "Αυθεντικό",
|
"crop_aspect_ratio_original": "Αυθεντικό",
|
||||||
|
"crop_aspect_ratio_square": "Τετράγωνο",
|
||||||
"curated_object_page_title": "Πράγματα",
|
"curated_object_page_title": "Πράγματα",
|
||||||
"current_device": "Τρέχουσα συσκευή",
|
"current_device": "Τρέχουσα συσκευή",
|
||||||
"current_pin_code": "Τρέχων κωδικός PIN",
|
"current_pin_code": "Τρέχων κωδικός PIN",
|
||||||
"current_server_address": "Τρέχουσα διεύθυνση διακομιστή",
|
"current_server_address": "Τρέχουσα διεύθυνση διακομιστή",
|
||||||
"custom_date": "Προσαρμοσμένη ημερομηνία",
|
"custom_date": "Προσαρμοσμένη ημερομηνία",
|
||||||
"custom_locale": "Προσαρμοσμένη Τοπική Ρύθμιση",
|
"custom_locale": "Προσαρμοσμένη τοπική ρύθμιση",
|
||||||
"custom_locale_description": "Μορφοποιήστε τις ημερομηνίες και τους αριθμούς, σύμφωνα με τη γλώσσα και την περιοχή",
|
"custom_locale_description": "Μορφοποιήστε τις ημερομηνίες, τους χρόνους και τους αριθμούς, σύμφωνα με την επιλεγμένη γλώσσα και περιοχή",
|
||||||
"custom_url": "Προσαρμοσμένη διεύθυνση URL",
|
"custom_url": "Προσαρμοσμένη διεύθυνση URL",
|
||||||
"cutoff_date_description": "Διατήρηση φωτογραφιών από τις τελευταίες…",
|
"cutoff_date_description": "Διατήρηση φωτογραφιών από τις τελευταίες…",
|
||||||
"cutoff_day": "{count, plural, one {ημέρα} other {ημέρες}}",
|
"cutoff_day": "{count, plural, one {ημέρα} other {ημέρες}}",
|
||||||
@@ -875,7 +881,7 @@
|
|||||||
"daily_title_text_date": "Ε, MMM dd",
|
"daily_title_text_date": "Ε, MMM dd",
|
||||||
"daily_title_text_date_year": "Ε, MMM dd, yyyy",
|
"daily_title_text_date_year": "Ε, MMM dd, yyyy",
|
||||||
"dark": "Σκούρο",
|
"dark": "Σκούρο",
|
||||||
"dark_theme": "Εναλλαγή σκοτεινής εμφάνισης",
|
"dark_theme": "Μετάβαση σε σκοτεινό θέμα",
|
||||||
"date": "Ημερομηνία",
|
"date": "Ημερομηνία",
|
||||||
"date_after": "Ημερομηνία μετά",
|
"date_after": "Ημερομηνία μετά",
|
||||||
"date_and_time": "Ημερομηνία και ώρα",
|
"date_and_time": "Ημερομηνία και ώρα",
|
||||||
@@ -886,12 +892,8 @@
|
|||||||
"day": "Ημέρα",
|
"day": "Ημέρα",
|
||||||
"days": "Ημέρες",
|
"days": "Ημέρες",
|
||||||
"deduplicate_all": "Αφαίρεση όλων των διπλότυπων",
|
"deduplicate_all": "Αφαίρεση όλων των διπλότυπων",
|
||||||
"deduplication_criteria_1": "Μέγεθος εικόνας σε byte",
|
"default_locale": "Προεπιλεγμένη γλώσσα",
|
||||||
"deduplication_criteria_2": "Αριθμός δεδομένων EXIF",
|
"default_locale_description": "Μορφοποίηση ημερομηνιών και αριθμών, βάση της γλώσσας του προγράμματος περιήγησης",
|
||||||
"deduplication_info": "Πληροφορίες Αφαίρεσης Διπλοτύπων",
|
|
||||||
"deduplication_info_description": "Για να προεπιλέξουμε αυτόματα τα αρχεία και να αφαιρέσουμε τα διπλότυπα σε μαζική επεξεργασία, εξετάζουμε σε:",
|
|
||||||
"default_locale": "Προεπιλεγμένη Τοπική Ρύθμιση",
|
|
||||||
"default_locale_description": "Μορφοποιήστε τις ημερομηνίες και τους αριθμούς με βάση την τοπική ρύθμιση του προγράμματος περιήγησής σας",
|
|
||||||
"delete": "Διαγραφή",
|
"delete": "Διαγραφή",
|
||||||
"delete_action_confirmation_message": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το αρχείο; Αυτή η ενέργεια θα το μετακινήσει στον κάδο απορριμμάτων του διακομιστή και θα εμφανιστεί μήνυμα για το αν θέλετε να το διαγράψετε και τοπικά",
|
"delete_action_confirmation_message": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το αρχείο; Αυτή η ενέργεια θα το μετακινήσει στον κάδο απορριμμάτων του διακομιστή και θα εμφανιστεί μήνυμα για το αν θέλετε να το διαγράψετε και τοπικά",
|
||||||
"delete_action_prompt": "{count} διαγράφηκαν",
|
"delete_action_prompt": "{count} διαγράφηκαν",
|
||||||
@@ -967,7 +969,7 @@
|
|||||||
"downloading_media": "Λήψη πολυμέσων",
|
"downloading_media": "Λήψη πολυμέσων",
|
||||||
"drop_files_to_upload": "Σύρετε αρχεία εδώ για να τα ανεβάσετε",
|
"drop_files_to_upload": "Σύρετε αρχεία εδώ για να τα ανεβάσετε",
|
||||||
"duplicates": "Διπλότυπα",
|
"duplicates": "Διπλότυπα",
|
||||||
"duplicates_description": "Επιλύστε κάθε ομάδα υποδεικνύοντας ποιες είναι διπλότυπες, εάν υπάρχουν",
|
"duplicates_description": "Επιλύστε κάθε ομάδα υποδεικνύοντας ποιες, εάν υπάρχουν, είναι διπλότυπες.",
|
||||||
"duration": "Διάρκεια",
|
"duration": "Διάρκεια",
|
||||||
"edit": "Επεξεργασία",
|
"edit": "Επεξεργασία",
|
||||||
"edit_album": "Επεξεργασία άλμπουμ",
|
"edit_album": "Επεξεργασία άλμπουμ",
|
||||||
@@ -1004,6 +1006,8 @@
|
|||||||
"editor_edits_applied_success": "Οι αλλαγές εφαρμόστηκαν με επιτυχία",
|
"editor_edits_applied_success": "Οι αλλαγές εφαρμόστηκαν με επιτυχία",
|
||||||
"editor_flip_horizontal": "Οριζόντια αναστροφή",
|
"editor_flip_horizontal": "Οριζόντια αναστροφή",
|
||||||
"editor_flip_vertical": "Κάθετη αναστροφή",
|
"editor_flip_vertical": "Κάθετη αναστροφή",
|
||||||
|
"editor_handle_corner": "{corner, select, top_left {Πάνω αριστερά} top_right {Πάνω δεξιά} bottom_left {Κάτω αριστερά} bottom_right {Κάτω δεξιά} other {A}} λαβή γωνίας",
|
||||||
|
"editor_handle_edge": "{edge, select, top {Πάνω} bottom {Κάτω} left {Αριστερά} right {Δεξιά} other {Μια}} λαβή πλευράς",
|
||||||
"editor_orientation": "Προσανατολισμός",
|
"editor_orientation": "Προσανατολισμός",
|
||||||
"editor_reset_all_changes": "Επαναφορά αλλαγών",
|
"editor_reset_all_changes": "Επαναφορά αλλαγών",
|
||||||
"editor_rotate_left": "Περιστροφή 90° αριστερόστροφα",
|
"editor_rotate_left": "Περιστροφή 90° αριστερόστροφα",
|
||||||
@@ -1069,6 +1073,7 @@
|
|||||||
"failed_to_update_notification_status": "Αποτυχία ενημέρωσης της κατάστασης ειδοποίησης",
|
"failed_to_update_notification_status": "Αποτυχία ενημέρωσης της κατάστασης ειδοποίησης",
|
||||||
"incorrect_email_or_password": "Λανθασμένο email ή κωδικός πρόσβασης",
|
"incorrect_email_or_password": "Λανθασμένο email ή κωδικός πρόσβασης",
|
||||||
"library_folder_already_exists": "Η διαδρομή εισαγωγής υπάρχει ήδη.",
|
"library_folder_already_exists": "Η διαδρομή εισαγωγής υπάρχει ήδη.",
|
||||||
|
"page_not_found": "Η σελίδα δεν βρέθηκε",
|
||||||
"paths_validation_failed": "{paths, plural, one {# διαδρομή} other {# διαδρομές}} απέτυχαν κατά την επικύρωση",
|
"paths_validation_failed": "{paths, plural, one {# διαδρομή} other {# διαδρομές}} απέτυχαν κατά την επικύρωση",
|
||||||
"profile_picture_transparent_pixels": "Οι εικόνες προφίλ δεν μπορούν να έχουν διαφανή εικονοστοιχεία. Παρακαλώ μεγεθύνετε ή/και μετακινήστε την εικόνα.",
|
"profile_picture_transparent_pixels": "Οι εικόνες προφίλ δεν μπορούν να έχουν διαφανή εικονοστοιχεία. Παρακαλώ μεγεθύνετε ή/και μετακινήστε την εικόνα.",
|
||||||
"quota_higher_than_disk_size": "Έχετε ορίσει ένα όριο, μεγαλύτερο από το μέγεθος του δίσκου",
|
"quota_higher_than_disk_size": "Έχετε ορίσει ένα όριο, μεγαλύτερο από το μέγεθος του δίσκου",
|
||||||
@@ -1168,6 +1173,7 @@
|
|||||||
"exif_bottom_sheet_people": "ΑΤΟΜΑ",
|
"exif_bottom_sheet_people": "ΑΤΟΜΑ",
|
||||||
"exif_bottom_sheet_person_add_person": "Προσθήκη ονόματος",
|
"exif_bottom_sheet_person_add_person": "Προσθήκη ονόματος",
|
||||||
"exit_slideshow": "Έξοδος από την παρουσίαση",
|
"exit_slideshow": "Έξοδος από την παρουσίαση",
|
||||||
|
"expand": "Ανάπτυξη",
|
||||||
"expand_all": "Ανάπτυξη όλων",
|
"expand_all": "Ανάπτυξη όλων",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Σε εξέλιξη",
|
"experimental_settings_new_asset_list_subtitle": "Σε εξέλιξη",
|
||||||
"experimental_settings_new_asset_list_title": "Ενεργοποίηση πειραματικού πλέγματος φωτογραφιών",
|
"experimental_settings_new_asset_list_title": "Ενεργοποίηση πειραματικού πλέγματος φωτογραφιών",
|
||||||
@@ -1212,6 +1218,7 @@
|
|||||||
"filter_description": "Συνθήκες για φιλτράρισμα των στοχευμένων στοιχείων",
|
"filter_description": "Συνθήκες για φιλτράρισμα των στοχευμένων στοιχείων",
|
||||||
"filter_people": "Φιλτράρισμα ατόμων",
|
"filter_people": "Φιλτράρισμα ατόμων",
|
||||||
"filter_places": "Φιλτράρισμα τοποθεσιών",
|
"filter_places": "Φιλτράρισμα τοποθεσιών",
|
||||||
|
"filter_tags": "Φιλτράρισμα ετικετών",
|
||||||
"filters": "Φίλτρα",
|
"filters": "Φίλτρα",
|
||||||
"find_them_fast": "Βρείτε τους γρήγορα με αναζήτηση κατά όνομα",
|
"find_them_fast": "Βρείτε τους γρήγορα με αναζήτηση κατά όνομα",
|
||||||
"first": "Αρχικά",
|
"first": "Αρχικά",
|
||||||
@@ -1379,9 +1386,11 @@
|
|||||||
"library_page_sort_title": "Τίτλος άλμπουμ",
|
"library_page_sort_title": "Τίτλος άλμπουμ",
|
||||||
"licenses": "Άδειες",
|
"licenses": "Άδειες",
|
||||||
"light": "Φωτεινό",
|
"light": "Φωτεινό",
|
||||||
|
"light_theme": "Μετάβαση σε φωτεινό θέμα",
|
||||||
"like": "Μου αρέσει",
|
"like": "Μου αρέσει",
|
||||||
"like_deleted": "Το \"μου αρέσει\" διαγράφηκε",
|
"like_deleted": "Το \"μου αρέσει\" διαγράφηκε",
|
||||||
"link_motion_video": "Σύνδεσε βίντεο κίνησης",
|
"link_motion_video": "Σύνδεσε βίντεο κίνησης",
|
||||||
|
"link_to_docs": "Για περισσότερες πληροφορίες, ανατρέξτε στην <link>τεκμηρίωση</link>.",
|
||||||
"link_to_oauth": "Σύνδεση στον OAuth",
|
"link_to_oauth": "Σύνδεση στον OAuth",
|
||||||
"linked_oauth_account": "Ο OAuth λογαριασμός συνδέθηκε",
|
"linked_oauth_account": "Ο OAuth λογαριασμός συνδέθηκε",
|
||||||
"list": "Λίστα",
|
"list": "Λίστα",
|
||||||
@@ -1642,6 +1651,8 @@
|
|||||||
"online": "Σε σύνδεση",
|
"online": "Σε σύνδεση",
|
||||||
"only_favorites": "Μόνο αγαπημένα",
|
"only_favorites": "Μόνο αγαπημένα",
|
||||||
"open": "Άνοιγμα",
|
"open": "Άνοιγμα",
|
||||||
|
"open_calendar": "Άνοιγμα ημερολογίου",
|
||||||
|
"open_in_browser": "Άνοιγμα στο πρόγραμμα περιήγησης",
|
||||||
"open_in_map_view": "Άνοιγμα σε προβολή χάρτη",
|
"open_in_map_view": "Άνοιγμα σε προβολή χάρτη",
|
||||||
"open_in_openstreetmap": "Άνοιγμα στο OpenStreetMap",
|
"open_in_openstreetmap": "Άνοιγμα στο OpenStreetMap",
|
||||||
"open_the_search_filters": "Ανοίξτε τα φίλτρα αναζήτησης",
|
"open_the_search_filters": "Ανοίξτε τα φίλτρα αναζήτησης",
|
||||||
@@ -1801,9 +1812,8 @@
|
|||||||
"rate_asset": "Βαθμολογήστε το στοιχείο",
|
"rate_asset": "Βαθμολογήστε το στοιχείο",
|
||||||
"rating": "Αξιολόγηση με αστέρια",
|
"rating": "Αξιολόγηση με αστέρια",
|
||||||
"rating_clear": "Εκκαθάριση αξιολόγησης",
|
"rating_clear": "Εκκαθάριση αξιολόγησης",
|
||||||
"rating_count": "{count, plural, one {# αστέρι} other {# αστέρια}}",
|
"rating_count": "{count, plural, =0 {Χωρίς αξιολόγηση} one {# αστέρι} other {# αστέρια}}",
|
||||||
"rating_description": "Εμφάνιση της αξιολόγησης EXIF στον πίνακα πληροφοριών",
|
"rating_description": "Εμφάνιση της αξιολόγησης EXIF στον πίνακα πληροφοριών",
|
||||||
"rating_set": "Η βαθμολογία ορίστηκε σε {rating, plural, one {# αστέρι} other {# αστέρια}}",
|
|
||||||
"reaction_options": "Επιλογές αντίδρασης",
|
"reaction_options": "Επιλογές αντίδρασης",
|
||||||
"read_changelog": "Διαβάστε το Αρχείο Καταγραφής Αλλαγών",
|
"read_changelog": "Διαβάστε το Αρχείο Καταγραφής Αλλαγών",
|
||||||
"readonly_mode_disabled": "Η λειτουργία μόνο-για-ανάγνωση απενεργοποιήθηκε",
|
"readonly_mode_disabled": "Η λειτουργία μόνο-για-ανάγνωση απενεργοποιήθηκε",
|
||||||
@@ -1875,7 +1885,10 @@
|
|||||||
"reset_pin_code_success": "Ο κωδικός PIN επαναφέρθηκε επιτυχώς",
|
"reset_pin_code_success": "Ο κωδικός PIN επαναφέρθηκε επιτυχώς",
|
||||||
"reset_pin_code_with_password": "Μπορείτε πάντα να επαναφέρετε τον κωδικό PIN χρησιμοποιώντας τον κωδικό πρόσβασής σας",
|
"reset_pin_code_with_password": "Μπορείτε πάντα να επαναφέρετε τον κωδικό PIN χρησιμοποιώντας τον κωδικό πρόσβασής σας",
|
||||||
"reset_sqlite": "Επαναφορά SQLite βάσης δεδομένων",
|
"reset_sqlite": "Επαναφορά SQLite βάσης δεδομένων",
|
||||||
"reset_sqlite_confirmation": "Είσαι σίγουρος ότι θέλεις να επαναφέρεις τη βάση δεδομένων SQLite; Θα χρειαστεί να κάνεις αποσύνδεση και επανασύνδεση για να επανασυγχρονίσεις τα δεδομένα",
|
"reset_sqlite_clear_app_data": "Εκκαθάριση δεδομένων",
|
||||||
|
"reset_sqlite_confirmation": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τα δεδομένα της εφαρμογής; Αυτό θα διαγράψει όλες τις ρυθμίσεις και θα σας αποσυνδέσει.",
|
||||||
|
"reset_sqlite_confirmation_note": "Σημείωση: Θα χρειαστεί να επανεκκινήσετε την εφαρμογή μετά την εκκαθάριση.",
|
||||||
|
"reset_sqlite_done": "Τα δεδομένα της εφαρμογής διαγράφηκαν. Παρακαλώ επανεκκινήστε το Immich και συνδεθείτε ξανά.",
|
||||||
"reset_sqlite_success": "Η επαναφορά της SQLite βάσης δεδομένων ολοκληρώθηκε με επιτυχία",
|
"reset_sqlite_success": "Η επαναφορά της SQLite βάσης δεδομένων ολοκληρώθηκε με επιτυχία",
|
||||||
"reset_to_default": "Επαναφορά στις προεπιλογές",
|
"reset_to_default": "Επαναφορά στις προεπιλογές",
|
||||||
"resolution": "Ανάλυση",
|
"resolution": "Ανάλυση",
|
||||||
@@ -1903,6 +1916,7 @@
|
|||||||
"saved_settings": "Αποθηκευμένες ρυθμίσεις",
|
"saved_settings": "Αποθηκευμένες ρυθμίσεις",
|
||||||
"say_something": "Πείτε κάτι",
|
"say_something": "Πείτε κάτι",
|
||||||
"scaffold_body_error_occurred": "Παρουσιάστηκε σφάλμα",
|
"scaffold_body_error_occurred": "Παρουσιάστηκε σφάλμα",
|
||||||
|
"scaffold_body_error_unrecoverable": "Παρουσιάστηκε ένα μη ανακτήσιμο σφάλμα. Παρακαλώ κοινοποιήστε το σφάλμα και το stack trace σε Discord ή GitHub για να μπορέσουμε να βοηθήσουμε. Αν σας ζητηθεί, μπορείτε να διαγράψετε τα δεδομένα της εφαρμογής παρακάτω.",
|
||||||
"scan": "Σάρωση",
|
"scan": "Σάρωση",
|
||||||
"scan_all_libraries": "Σάρωση Όλων των Βιβλιοθηκών",
|
"scan_all_libraries": "Σάρωση Όλων των Βιβλιοθηκών",
|
||||||
"scan_library": "Σάρωση",
|
"scan_library": "Σάρωση",
|
||||||
@@ -1938,6 +1952,7 @@
|
|||||||
"search_filter_ocr": "Αναζήτηση κατά OCR",
|
"search_filter_ocr": "Αναζήτηση κατά OCR",
|
||||||
"search_filter_people_title": "Επιλέξτε άτομα",
|
"search_filter_people_title": "Επιλέξτε άτομα",
|
||||||
"search_filter_star_rating": "Βαθμολογία με αστέρια",
|
"search_filter_star_rating": "Βαθμολογία με αστέρια",
|
||||||
|
"search_filter_tags_title": "Επιλογή ετικετών",
|
||||||
"search_for": "Αναζήτηση για",
|
"search_for": "Αναζήτηση για",
|
||||||
"search_for_existing_person": "Αναζήτηση υπάρχοντος ατόμου",
|
"search_for_existing_person": "Αναζήτηση υπάρχοντος ατόμου",
|
||||||
"search_no_more_result": "Δεν υπάρχουν άλλα αποτελέσματα",
|
"search_no_more_result": "Δεν υπάρχουν άλλα αποτελέσματα",
|
||||||
@@ -2017,6 +2032,9 @@
|
|||||||
"set_profile_picture": "Ορισμός εικόνας προφίλ",
|
"set_profile_picture": "Ορισμός εικόνας προφίλ",
|
||||||
"set_slideshow_to_fullscreen": "Ορίστε την παρουσίαση σε πλήρη οθόνη",
|
"set_slideshow_to_fullscreen": "Ορίστε την παρουσίαση σε πλήρη οθόνη",
|
||||||
"set_stack_primary_asset": "Ορισμός ως κύριο στοιχείο",
|
"set_stack_primary_asset": "Ορισμός ως κύριο στοιχείο",
|
||||||
|
"setting_image_navigation_enable_subtitle": "Αν είναι ενεργοποιημένο, μπορείτε να μεταβείτε στην προηγούμενη/επόμενη εικόνα πατώντας στο αριστερότερο/δεξιότερο τέταρτο της οθόνης.",
|
||||||
|
"setting_image_navigation_enable_title": "Πατήστε για Περιήγηση",
|
||||||
|
"setting_image_navigation_title": "Περιήγηση εικόνων",
|
||||||
"setting_image_viewer_help": "Το πρόγραμμα προβολής λεπτομερειών φορτώνει πρώτα τη μικρογραφία, στη συνέχεια φορτώνει την προεπισκόπηση μεσαίου μεγέθους (αν είναι ενεργοποιημένη), τέλος φορτώνει το πρωτότυπο (αν είναι ενεργοποιημένο).",
|
"setting_image_viewer_help": "Το πρόγραμμα προβολής λεπτομερειών φορτώνει πρώτα τη μικρογραφία, στη συνέχεια φορτώνει την προεπισκόπηση μεσαίου μεγέθους (αν είναι ενεργοποιημένη), τέλος φορτώνει το πρωτότυπο (αν είναι ενεργοποιημένο).",
|
||||||
"setting_image_viewer_original_subtitle": "Ενεργοποιήστε τη φόρτωση της πρωτότυπης εικόνας πλήρους ανάλυσης (μεγάλη!). Απενεργοποιήστε για να μειώσετε τη χρήση δεδομένων (τόσο στο δίκτυο όσο και στην κρυφή μνήμη της συσκευής).",
|
"setting_image_viewer_original_subtitle": "Ενεργοποιήστε τη φόρτωση της πρωτότυπης εικόνας πλήρους ανάλυσης (μεγάλη!). Απενεργοποιήστε για να μειώσετε τη χρήση δεδομένων (τόσο στο δίκτυο όσο και στην κρυφή μνήμη της συσκευής).",
|
||||||
"setting_image_viewer_original_title": "Φόρτωση πρωτότυπης εικόνας",
|
"setting_image_viewer_original_title": "Φόρτωση πρωτότυπης εικόνας",
|
||||||
@@ -2183,6 +2201,7 @@
|
|||||||
"support": "Υποστήριξη",
|
"support": "Υποστήριξη",
|
||||||
"support_and_feedback": "Υποστήριξη & Σχόλια",
|
"support_and_feedback": "Υποστήριξη & Σχόλια",
|
||||||
"support_third_party_description": "Η εγκατάσταση του Immich που χρησιμοποιείτε, έχει πακεταριστεί από τρίτους. Τα προβλήματα που αντιμετωπίζετε μπορεί να οφείλονται σε αυτό το πακέτο, οπότε παρακαλούμε να αναφέρετε τα προβλήματα πρώτα σε εκείνους, χρησιμοποιώντας τους παρακάτω συνδέσμους.",
|
"support_third_party_description": "Η εγκατάσταση του Immich που χρησιμοποιείτε, έχει πακεταριστεί από τρίτους. Τα προβλήματα που αντιμετωπίζετε μπορεί να οφείλονται σε αυτό το πακέτο, οπότε παρακαλούμε να αναφέρετε τα προβλήματα πρώτα σε εκείνους, χρησιμοποιώντας τους παρακάτω συνδέσμους.",
|
||||||
|
"supporter": "Υποστηρικτής",
|
||||||
"swap_merge_direction": "Εναλλαγή κατεύθυνσης συγχώνευσης",
|
"swap_merge_direction": "Εναλλαγή κατεύθυνσης συγχώνευσης",
|
||||||
"sync": "Συγχρονισμός",
|
"sync": "Συγχρονισμός",
|
||||||
"sync_albums": "Συγχρονισμός άλμπουμ",
|
"sync_albums": "Συγχρονισμός άλμπουμ",
|
||||||
@@ -2294,6 +2313,7 @@
|
|||||||
"unstack_action_prompt": "{count} αποσυσσωρεύτηκαν",
|
"unstack_action_prompt": "{count} αποσυσσωρεύτηκαν",
|
||||||
"unstacked_assets_count": "Αποστοιβάξατε {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
"unstacked_assets_count": "Αποστοιβάξατε {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
||||||
"unsupported_field_type": "Μη υποστηριζόμενος τύπος πεδίου",
|
"unsupported_field_type": "Μη υποστηριζόμενος τύπος πεδίου",
|
||||||
|
"unsupported_file_type": "Το αρχείο {file} δεν μπορεί να μεταφορτωθεί επειδή ο τύπος αρχείου {type} δεν υποστηρίζεται.",
|
||||||
"untagged": "Χωρίς ετικέτα",
|
"untagged": "Χωρίς ετικέτα",
|
||||||
"untitled_workflow": "Νέα ροή εργασίας",
|
"untitled_workflow": "Νέα ροή εργασίας",
|
||||||
"up_next": "Ακολουθεί",
|
"up_next": "Ακολουθεί",
|
||||||
@@ -2320,6 +2340,8 @@
|
|||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Χρήση",
|
"usage": "Χρήση",
|
||||||
"use_biometric": "Χρήση βιομετρικών στοιχείων",
|
"use_biometric": "Χρήση βιομετρικών στοιχείων",
|
||||||
|
"use_browser_locale": "Χρήση γλώσσας προγράμματος περιήγησης",
|
||||||
|
"use_browser_locale_description": "Μορφοποιήστε τις ημερομηνίες, τους χρόνους και τους αριθμούς σύμφωνα με τη γλώσσα του προγράμματος περιήγησής σας",
|
||||||
"use_current_connection": "Χρήση τρέχουσας σύνδεσης",
|
"use_current_connection": "Χρήση τρέχουσας σύνδεσης",
|
||||||
"use_custom_date_range": "Χρήση προσαρμοσμένου εύρους ημερομηνιών",
|
"use_custom_date_range": "Χρήση προσαρμοσμένου εύρους ημερομηνιών",
|
||||||
"user": "Χρήστης",
|
"user": "Χρήστης",
|
||||||
@@ -2373,6 +2395,7 @@
|
|||||||
"viewer_remove_from_stack": "Κατάργηση από τη Στοίβα",
|
"viewer_remove_from_stack": "Κατάργηση από τη Στοίβα",
|
||||||
"viewer_stack_use_as_main_asset": "Χρήση ως Κύριο Στοιχείο",
|
"viewer_stack_use_as_main_asset": "Χρήση ως Κύριο Στοιχείο",
|
||||||
"viewer_unstack": "Αποστοίβαξε",
|
"viewer_unstack": "Αποστοίβαξε",
|
||||||
|
"visibility": "Ορατότητα",
|
||||||
"visibility_changed": "Η ορατότητα άλλαξε για {count, plural, one {# άτομο} other {# άτομα}}",
|
"visibility_changed": "Η ορατότητα άλλαξε για {count, plural, one {# άτομο} other {# άτομα}}",
|
||||||
"visual": "Οπτικό",
|
"visual": "Οπτικό",
|
||||||
"visual_builder": "Οπτικός δημιουργός",
|
"visual_builder": "Οπτικός δημιουργός",
|
||||||
|
|||||||
+20
-11
@@ -411,7 +411,7 @@
|
|||||||
"transcoding_tone_mapping": "Tone-mapping",
|
"transcoding_tone_mapping": "Tone-mapping",
|
||||||
"transcoding_tone_mapping_description": "Attempts to preserve the appearance of HDR videos when converted to SDR. Each algorithm makes different tradeoffs for color, detail and brightness. Hable preserves detail, Mobius preserves color, and Reinhard preserves brightness.",
|
"transcoding_tone_mapping_description": "Attempts to preserve the appearance of HDR videos when converted to SDR. Each algorithm makes different tradeoffs for color, detail and brightness. Hable preserves detail, Mobius preserves color, and Reinhard preserves brightness.",
|
||||||
"transcoding_transcode_policy": "Transcode policy",
|
"transcoding_transcode_policy": "Transcode policy",
|
||||||
"transcoding_transcode_policy_description": "Policy for when a video should be transcoded. HDR videos will always be transcoded (except if transcoding is disabled).",
|
"transcoding_transcode_policy_description": "Policy for when a video should be transcoded. HDR videos and videos with a pixel format other than YUV 4:2:0 will always be transcoded (except if transcoding is disabled).",
|
||||||
"transcoding_two_pass_encoding": "Two-pass encoding",
|
"transcoding_two_pass_encoding": "Two-pass encoding",
|
||||||
"transcoding_two_pass_encoding_setting_description": "Transcode in two passes to produce better encoded videos. When max bitrate is enabled (required for it to work with H.264 and HEVC), this mode uses a bitrate range based on the max bitrate and ignores CRF. For VP9, CRF can be used if max bitrate is disabled.",
|
"transcoding_two_pass_encoding_setting_description": "Transcode in two passes to produce better encoded videos. When max bitrate is enabled (required for it to work with H.264 and HEVC), this mode uses a bitrate range based on the max bitrate and ignores CRF. For VP9, CRF can be used if max bitrate is disabled.",
|
||||||
"transcoding_video_codec": "Video codec",
|
"transcoding_video_codec": "Video codec",
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
"user_successfully_removed": "User {email} has been successfully removed.",
|
"user_successfully_removed": "User {email} has been successfully removed.",
|
||||||
"users_page_description": "Admin users page",
|
"users_page_description": "Admin users page",
|
||||||
"version_check_enabled_description": "Enable version check",
|
"version_check_enabled_description": "Enable version check",
|
||||||
"version_check_implications": "The version check feature relies on periodic communication with github.com",
|
"version_check_implications": "The version check feature relies on periodic communication with {server}",
|
||||||
"version_check_settings": "Version Check",
|
"version_check_settings": "Version Check",
|
||||||
"version_check_settings_description": "Enable/disable the new version notification",
|
"version_check_settings_description": "Enable/disable the new version notification",
|
||||||
"video_conversion_job": "Transcode videos",
|
"video_conversion_job": "Transcode videos",
|
||||||
@@ -849,9 +849,12 @@
|
|||||||
"create_link_to_share": "Create link to share",
|
"create_link_to_share": "Create link to share",
|
||||||
"create_link_to_share_description": "Let anyone with the link see the selected photo(s)",
|
"create_link_to_share_description": "Let anyone with the link see the selected photo(s)",
|
||||||
"create_new": "CREATE NEW",
|
"create_new": "CREATE NEW",
|
||||||
|
"create_new_face": "Create new face",
|
||||||
"create_new_person": "Create new person",
|
"create_new_person": "Create new person",
|
||||||
"create_new_person_hint": "Assign selected assets to a new person",
|
"create_new_person_hint": "Assign selected assets to a new person",
|
||||||
"create_new_user": "Create new user",
|
"create_new_user": "Create new user",
|
||||||
|
"create_person": "Create person",
|
||||||
|
"create_person_subtitle": "Add a name to the selected face to create and tag the new person",
|
||||||
"create_shared_album_page_share_add_assets": "ADD ASSETS",
|
"create_shared_album_page_share_add_assets": "ADD ASSETS",
|
||||||
"create_shared_album_page_share_select_photos": "Select Photos",
|
"create_shared_album_page_share_select_photos": "Select Photos",
|
||||||
"create_shared_link": "Create shared link",
|
"create_shared_link": "Create shared link",
|
||||||
@@ -866,13 +869,14 @@
|
|||||||
"crop_aspect_ratio_fixed": "Fixed",
|
"crop_aspect_ratio_fixed": "Fixed",
|
||||||
"crop_aspect_ratio_free": "Free",
|
"crop_aspect_ratio_free": "Free",
|
||||||
"crop_aspect_ratio_original": "Original",
|
"crop_aspect_ratio_original": "Original",
|
||||||
|
"crop_aspect_ratio_square": "Square",
|
||||||
"curated_object_page_title": "Things",
|
"curated_object_page_title": "Things",
|
||||||
"current_device": "Current device",
|
"current_device": "Current device",
|
||||||
"current_pin_code": "Current PIN code",
|
"current_pin_code": "Current PIN code",
|
||||||
"current_server_address": "Current server address",
|
"current_server_address": "Current server address",
|
||||||
"custom_date": "Custom date",
|
"custom_date": "Custom date",
|
||||||
"custom_locale": "Custom Locale",
|
"custom_locale": "Custom locale",
|
||||||
"custom_locale_description": "Format dates and numbers based on the language and the region",
|
"custom_locale_description": "Format dates, times, and numbers based on the selected language and region",
|
||||||
"custom_url": "Custom URL",
|
"custom_url": "Custom URL",
|
||||||
"cutoff_date_description": "Keep photos from the last…",
|
"cutoff_date_description": "Keep photos from the last…",
|
||||||
"cutoff_day": "{count, plural, one {day} other {days}}",
|
"cutoff_day": "{count, plural, one {day} other {days}}",
|
||||||
@@ -880,7 +884,7 @@
|
|||||||
"daily_title_text_date": "E, MMM dd",
|
"daily_title_text_date": "E, MMM dd",
|
||||||
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
||||||
"dark": "Dark",
|
"dark": "Dark",
|
||||||
"dark_theme": "Toggle dark theme",
|
"dark_theme": "Switch to dark theme",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"date_after": "Date after",
|
"date_after": "Date after",
|
||||||
"date_and_time": "Date and Time",
|
"date_and_time": "Date and Time",
|
||||||
@@ -891,10 +895,6 @@
|
|||||||
"day": "Day",
|
"day": "Day",
|
||||||
"days": "Days",
|
"days": "Days",
|
||||||
"deduplicate_all": "Deduplicate All",
|
"deduplicate_all": "Deduplicate All",
|
||||||
"deduplication_criteria_1": "Image size in bytes",
|
|
||||||
"deduplication_criteria_2": "Count of EXIF data",
|
|
||||||
"deduplication_info": "Deduplication Info",
|
|
||||||
"deduplication_info_description": "To automatically preselect assets and remove duplicates in bulk, we look at:",
|
|
||||||
"default_locale": "Default Locale",
|
"default_locale": "Default Locale",
|
||||||
"default_locale_description": "Format dates and numbers based on your browser locale",
|
"default_locale_description": "Format dates and numbers based on your browser locale",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
@@ -972,7 +972,7 @@
|
|||||||
"downloading_media": "Downloading media",
|
"downloading_media": "Downloading media",
|
||||||
"drop_files_to_upload": "Drop files anywhere to upload",
|
"drop_files_to_upload": "Drop files anywhere to upload",
|
||||||
"duplicates": "Duplicates",
|
"duplicates": "Duplicates",
|
||||||
"duplicates_description": "Resolve each group by indicating which, if any, are duplicates",
|
"duplicates_description": "Resolve each group by indicating which, if any, are duplicates.",
|
||||||
"duration": "Duration",
|
"duration": "Duration",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"edit_album": "Edit album",
|
"edit_album": "Edit album",
|
||||||
@@ -1009,6 +1009,8 @@
|
|||||||
"editor_edits_applied_success": "Edits applied successfully",
|
"editor_edits_applied_success": "Edits applied successfully",
|
||||||
"editor_flip_horizontal": "Flip horizontal",
|
"editor_flip_horizontal": "Flip horizontal",
|
||||||
"editor_flip_vertical": "Flip vertical",
|
"editor_flip_vertical": "Flip vertical",
|
||||||
|
"editor_handle_corner": "{corner, select, top_left {Top-left} top_right {Top-right} bottom_left {Bottom-left} bottom_right {Bottom-right} other {A}} corner handle",
|
||||||
|
"editor_handle_edge": "{edge, select, top {Top} bottom {Bottom} left {Left} right {Right} other {An}} edge handle",
|
||||||
"editor_orientation": "Orientation",
|
"editor_orientation": "Orientation",
|
||||||
"editor_reset_all_changes": "Reset changes",
|
"editor_reset_all_changes": "Reset changes",
|
||||||
"editor_rotate_left": "Rotate 90° counterclockwise",
|
"editor_rotate_left": "Rotate 90° counterclockwise",
|
||||||
@@ -1074,7 +1076,7 @@
|
|||||||
"failed_to_update_notification_status": "Failed to update notification status",
|
"failed_to_update_notification_status": "Failed to update notification status",
|
||||||
"incorrect_email_or_password": "Incorrect email or password",
|
"incorrect_email_or_password": "Incorrect email or password",
|
||||||
"library_folder_already_exists": "This import path already exists.",
|
"library_folder_already_exists": "This import path already exists.",
|
||||||
"page_not_found": "Page not found :/",
|
"page_not_found": "Page not found",
|
||||||
"paths_validation_failed": "{paths, plural, one {# path} other {# paths}} failed validation",
|
"paths_validation_failed": "{paths, plural, one {# path} other {# paths}} failed validation",
|
||||||
"profile_picture_transparent_pixels": "Profile pictures cannot have transparent pixels. Please zoom in and/or move the image.",
|
"profile_picture_transparent_pixels": "Profile pictures cannot have transparent pixels. Please zoom in and/or move the image.",
|
||||||
"quota_higher_than_disk_size": "You set a quota higher than the disk size",
|
"quota_higher_than_disk_size": "You set a quota higher than the disk size",
|
||||||
@@ -1387,9 +1389,11 @@
|
|||||||
"library_page_sort_title": "Album title",
|
"library_page_sort_title": "Album title",
|
||||||
"licenses": "Licenses",
|
"licenses": "Licenses",
|
||||||
"light": "Light",
|
"light": "Light",
|
||||||
|
"light_theme": "Switch to light theme",
|
||||||
"like": "Like",
|
"like": "Like",
|
||||||
"like_deleted": "Like deleted",
|
"like_deleted": "Like deleted",
|
||||||
"link_motion_video": "Link motion video",
|
"link_motion_video": "Link motion video",
|
||||||
|
"link_to_docs": "For more information, refer to the <link>documentation</link>.",
|
||||||
"link_to_oauth": "Link to OAuth",
|
"link_to_oauth": "Link to OAuth",
|
||||||
"linked_oauth_account": "Linked OAuth account",
|
"linked_oauth_account": "Linked OAuth account",
|
||||||
"list": "List",
|
"list": "List",
|
||||||
@@ -1651,6 +1655,7 @@
|
|||||||
"only_favorites": "Only favorites",
|
"only_favorites": "Only favorites",
|
||||||
"open": "Open",
|
"open": "Open",
|
||||||
"open_calendar": "Open calendar",
|
"open_calendar": "Open calendar",
|
||||||
|
"open_in_browser": "Open in browser",
|
||||||
"open_in_map_view": "Open in map view",
|
"open_in_map_view": "Open in map view",
|
||||||
"open_in_openstreetmap": "Open in OpenStreetMap",
|
"open_in_openstreetmap": "Open in OpenStreetMap",
|
||||||
"open_the_search_filters": "Open the search filters",
|
"open_the_search_filters": "Open the search filters",
|
||||||
@@ -2212,6 +2217,7 @@
|
|||||||
"tag": "Tag",
|
"tag": "Tag",
|
||||||
"tag_assets": "Tag assets",
|
"tag_assets": "Tag assets",
|
||||||
"tag_created": "Created tag: {tag}",
|
"tag_created": "Created tag: {tag}",
|
||||||
|
"tag_face": "Tag face",
|
||||||
"tag_feature_description": "Browsing photos and videos grouped by logical tag topics",
|
"tag_feature_description": "Browsing photos and videos grouped by logical tag topics",
|
||||||
"tag_not_found_question": "Cannot find a tag? <link>Create a new tag.</link>",
|
"tag_not_found_question": "Cannot find a tag? <link>Create a new tag.</link>",
|
||||||
"tag_people": "Tag People",
|
"tag_people": "Tag People",
|
||||||
@@ -2338,6 +2344,8 @@
|
|||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Usage",
|
"usage": "Usage",
|
||||||
"use_biometric": "Use biometric",
|
"use_biometric": "Use biometric",
|
||||||
|
"use_browser_locale": "Use browser locale",
|
||||||
|
"use_browser_locale_description": "Format dates, times, and numbers based on your browser locale",
|
||||||
"use_current_connection": "Use current connection",
|
"use_current_connection": "Use current connection",
|
||||||
"use_custom_date_range": "Use custom date range instead",
|
"use_custom_date_range": "Use custom date range instead",
|
||||||
"user": "User",
|
"user": "User",
|
||||||
@@ -2391,6 +2399,7 @@
|
|||||||
"viewer_remove_from_stack": "Remove from Stack",
|
"viewer_remove_from_stack": "Remove from Stack",
|
||||||
"viewer_stack_use_as_main_asset": "Use as Main Asset",
|
"viewer_stack_use_as_main_asset": "Use as Main Asset",
|
||||||
"viewer_unstack": "Un-Stack",
|
"viewer_unstack": "Un-Stack",
|
||||||
|
"visibility": "Visibility",
|
||||||
"visibility_changed": "Visibility changed for {count, plural, one {# person} other {# people}}",
|
"visibility_changed": "Visibility changed for {count, plural, one {# person} other {# people}}",
|
||||||
"visual": "Visual",
|
"visual": "Visual",
|
||||||
"visual_builder": "Visual builder",
|
"visual_builder": "Visual builder",
|
||||||
|
|||||||
+698
-25
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"about": "Pri",
|
"about": "Pri",
|
||||||
"account": "Konto",
|
"account": "Konto",
|
||||||
"account_settings": "Agordaĵoj de konto",
|
"account_settings": "Agordoj pri konto",
|
||||||
"acknowledge": "Komprenite",
|
"acknowledge": "Komprenite",
|
||||||
"action": "Ago",
|
"action": "Ago",
|
||||||
"action_common_update": "Ĝisdatigi",
|
"action_common_update": "Ĝisdatigi",
|
||||||
@@ -59,12 +59,12 @@
|
|||||||
"backup_database_enable_description": "Ebligi kreon de kopioj de datumbazo",
|
"backup_database_enable_description": "Ebligi kreon de kopioj de datumbazo",
|
||||||
"backup_keep_last_amount": "Nombro de antaŭaj kopioj konservendaj",
|
"backup_keep_last_amount": "Nombro de antaŭaj kopioj konservendaj",
|
||||||
"backup_onboarding_1_description": "fora kopio, ĉu en nubo ĉu en alia fizika loko.",
|
"backup_onboarding_1_description": "fora kopio, ĉu en nubo ĉu en alia fizika loko.",
|
||||||
"backup_onboarding_2_description": "lokaj kopioj ĉe diversaj aparatoj, inkluzive ĉefajn dosierojn kaj lokan sekurkopion de tiuj dosieroj.",
|
"backup_onboarding_2_description": "lokaj kopioj ĉe diversaj aparatoj, inkluzive ĉefajn dosierojn kaj lokan savkopion de tiuj dosieroj.",
|
||||||
"backup_onboarding_3_description": "suma nombro de kopioj de viaj datumoj, inkluzive la originajn dosierojn, t.e. 1 fora kopio kaj 2 lokaj kopioj.",
|
"backup_onboarding_3_description": "suma nombro de kopioj de viaj datumoj, inkluzive la originajn dosierojn, t.e. 1 fora kopio kaj 2 lokaj kopioj.",
|
||||||
"backup_onboarding_description": "Ni rekomendas <backblaze-link>strategion de 3-2-1</backblaze-link> por protekti viajn datumojn. Vi devus havi sekurkopiojn kaj de viaj fotoj/videoj kaj de la datumbazo de Immich por esti plene sekura.",
|
"backup_onboarding_description": "Ni rekomendas <backblaze-link>strategion de 3-2-1</backblaze-link> por protekti viajn datumojn. Vi devus havi savkopiojn kaj de viaj fotoj/videoj kaj de la datumbazo de Immich por esti plene sekura.",
|
||||||
"backup_onboarding_footer": "Por pli da informoj pri sekurkopioj kun Immich, bonvolu legi la <link>dokumentaron</link>.",
|
"backup_onboarding_footer": "Por pli da informoj pri savkopioj kun Immich, bonvolu legi la <link>dokumentaron</link>.",
|
||||||
"backup_onboarding_parts_title": "Sekur-kopioj laŭ strategio 3-2-1 inkluzivas:",
|
"backup_onboarding_parts_title": "Sekur-kopioj laŭ strategio 3-2-1 inkluzivas:",
|
||||||
"backup_onboarding_title": "Sekurkopioj",
|
"backup_onboarding_title": "Savkopioj",
|
||||||
"backup_settings": "Agordaĵoj de kopiado de datumbazo",
|
"backup_settings": "Agordaĵoj de kopiado de datumbazo",
|
||||||
"backup_settings_description": "Administri agordojn pri datumbazo-nekropsio.",
|
"backup_settings_description": "Administri agordojn pri datumbazo-nekropsio.",
|
||||||
"cleared_jobs": "Taskoj forigitaj por: {job}",
|
"cleared_jobs": "Taskoj forigitaj por: {job}",
|
||||||
@@ -192,19 +192,19 @@
|
|||||||
"machine_learning_url_description": "La URL-o de la maŝin-lerna servilo. Se vi donas pli ol unu URL-o, la sistemo provos ĉiun servilon unu post la alia ĝis kiam unu sukcese respondas, de la unua ĝis la lasta. Serviloj, kiuj ne respondas, estos dumtempe ignoritaj.",
|
"machine_learning_url_description": "La URL-o de la maŝin-lerna servilo. Se vi donas pli ol unu URL-o, la sistemo provos ĉiun servilon unu post la alia ĝis kiam unu sukcese respondas, de la unua ĝis la lasta. Serviloj, kiuj ne respondas, estos dumtempe ignoritaj.",
|
||||||
"maintenance_delete_backup": "Forigi savkopion",
|
"maintenance_delete_backup": "Forigi savkopion",
|
||||||
"maintenance_delete_backup_description": "La dosiero estos por ĉiam forigita.",
|
"maintenance_delete_backup_description": "La dosiero estos por ĉiam forigita.",
|
||||||
"maintenance_delete_error": "Malsukcesis forigi sekurkopion.",
|
"maintenance_delete_error": "Malsukcesis forigi savkopion.",
|
||||||
"maintenance_restore_backup": "Restaŭri savkopion",
|
"maintenance_restore_backup": "Restaŭri savkopion",
|
||||||
"maintenance_restore_backup_description": "Immich estos forigita kaj reinstalita de la elektita sekurkopio. Nova sekurkopio estos kreita antaŭe.",
|
"maintenance_restore_backup_description": "Immich estos forigita kaj reinstalita de la elektita savkopio. Nova savkopio estos kreita antaŭe.",
|
||||||
"maintenance_restore_backup_different_version": "Tiu ĉi sekurkopio estis kreita per alia versio de Immich!",
|
"maintenance_restore_backup_different_version": "Tiu ĉi savkopio estis kreita per alia versio de Immich!",
|
||||||
"maintenance_restore_backup_unknown_version": "Ne eblis ektrovi version de la sekurkopio.",
|
"maintenance_restore_backup_unknown_version": "Ne eblis ektrovi version de la savkopio.",
|
||||||
"maintenance_restore_database_backup": "Restaŭri datumbazon el sekurkopio",
|
"maintenance_restore_database_backup": "Restaŭri datumbazon el savkopio",
|
||||||
"maintenance_restore_database_backup_description": "Reveni al antaŭa stato de datumbazo pere de sekurkopio",
|
"maintenance_restore_database_backup_description": "Reveni al antaŭa stato de datumbazo pere de savkopio",
|
||||||
"maintenance_settings": "Funkcitenado",
|
"maintenance_settings": "Funkcitenado",
|
||||||
"maintenance_settings_description": "Ŝalti la funkcitenadan reĝimon de Immich.",
|
"maintenance_settings_description": "Ŝalti la funkcitenadan reĝimon de Immich.",
|
||||||
"maintenance_start": "Ŝanĝi al funkci-tenada reĝimo",
|
"maintenance_start": "Ŝanĝi al funkci-tenada reĝimo",
|
||||||
"maintenance_start_error": "Malsukcesis ŝalti funkci-tenadan reĝimon.",
|
"maintenance_start_error": "Malsukcesis ŝalti funkci-tenadan reĝimon.",
|
||||||
"maintenance_upload_backup": "Alŝuti dosieron de sekurkopio de datumbazo",
|
"maintenance_upload_backup": "Alŝuti dosieron de savkopio de datumbazo",
|
||||||
"maintenance_upload_backup_error": "Malsukcesis alŝuti sekurkopion, ĉu ĝi havas formaton .sql aŭ .sql.gz?",
|
"maintenance_upload_backup_error": "Malsukcesis alŝuti savkopion, ĉu ĝi havas formaton .sql aŭ .sql.gz?",
|
||||||
"manage_concurrency": "Administri samtempajn taskojn",
|
"manage_concurrency": "Administri samtempajn taskojn",
|
||||||
"manage_concurrency_description": "Vizitu la paĝon Taskoj por agordi la nombron de samtempaj taskoj",
|
"manage_concurrency_description": "Vizitu la paĝon Taskoj por agordi la nombron de samtempaj taskoj",
|
||||||
"manage_log_settings": "Administri agordojn pri protokolado",
|
"manage_log_settings": "Administri agordojn pri protokolado",
|
||||||
@@ -259,14 +259,14 @@
|
|||||||
"notification_email_secure": "SMTPS",
|
"notification_email_secure": "SMTPS",
|
||||||
"notification_email_secure_description": "Uzi SMTPS (SMTP pere de TLS)",
|
"notification_email_secure_description": "Uzi SMTPS (SMTP pere de TLS)",
|
||||||
"notification_email_sent_test_email_button": "Sendi testmesaĝon kaj konservi",
|
"notification_email_sent_test_email_button": "Sendi testmesaĝon kaj konservi",
|
||||||
"notification_email_setting_description": "Agordoj pri atentigoj per retmesaĝoj",
|
"notification_email_setting_description": "Agordoj pri sciigoj per retmesaĝoj",
|
||||||
"notification_email_test_email": "Sendi testmesaĝon",
|
"notification_email_test_email": "Sendi testmesaĝon",
|
||||||
"notification_email_test_email_failed": "Malsukcesis sendi testmesaĝon, kontrolu la agordaĵojn",
|
"notification_email_test_email_failed": "Malsukcesis sendi testmesaĝon, kontrolu la agordaĵojn",
|
||||||
"notification_email_test_email_sent": "Testmesaĝo estas sendita al {email}. Bonvolu kontroli ĉu ĝi bone alvenis.",
|
"notification_email_test_email_sent": "Testmesaĝo estas sendita al {email}. Bonvolu kontroli ĉu ĝi bone alvenis.",
|
||||||
"notification_email_username_description": "Uzantonomo por uzi kun la retmesaĝa servilo",
|
"notification_email_username_description": "Uzantonomo por uzi kun la retmesaĝa servilo",
|
||||||
"notification_enable_email_notifications": "Ŝalti retmesaĝajn atentigilojn",
|
"notification_enable_email_notifications": "Ŝalti sciigojn per retmesaĝo",
|
||||||
"notification_settings": "Agordoj pri atentigiloj",
|
"notification_settings": "Agordoj pri sciigoj",
|
||||||
"notification_settings_description": "Administri agordojn pri atentigiloj, inkluzive tiujn per retmesaĝoj",
|
"notification_settings_description": "Administri agordojn pri sciigoj, inkluzive tiujn per retmesaĝoj",
|
||||||
"oauth_auto_launch": "Startigi aŭtomate",
|
"oauth_auto_launch": "Startigi aŭtomate",
|
||||||
"oauth_auto_launch_description": "Aŭtomate startigi la OAuth-procezon tuj ĉe la ensaluta paĝo",
|
"oauth_auto_launch_description": "Aŭtomate startigi la OAuth-procezon tuj ĉe la ensaluta paĝo",
|
||||||
"oauth_auto_register": "Registri aŭtomate",
|
"oauth_auto_register": "Registri aŭtomate",
|
||||||
@@ -311,7 +311,7 @@
|
|||||||
"search_jobs": "Serĉi taskojn…",
|
"search_jobs": "Serĉi taskojn…",
|
||||||
"send_welcome_email": "Sendi bonvenan retmesaĝon",
|
"send_welcome_email": "Sendi bonvenan retmesaĝon",
|
||||||
"server_external_domain_settings": "Ekstera domajno",
|
"server_external_domain_settings": "Ekstera domajno",
|
||||||
"server_external_domain_settings_description": "Domajno por publike dividitaj ligiloj, inkl. http(s)://",
|
"server_external_domain_settings_description": "Domajno por eksteraj ligiloj",
|
||||||
"server_public_users": "Publikaj uzantoj",
|
"server_public_users": "Publikaj uzantoj",
|
||||||
"server_public_users_description": "Nomo kaj retadreso de ĉiuj uzantoj estas listigitaj kiam oni aldonas uzanton al dividita albumo. Kiam malŝaltita, la listo de uzantoj estos videbla nur por administrantoj.",
|
"server_public_users_description": "Nomo kaj retadreso de ĉiuj uzantoj estas listigitaj kiam oni aldonas uzanton al dividita albumo. Kiam malŝaltita, la listo de uzantoj estos videbla nur por administrantoj.",
|
||||||
"server_settings": "Agordoj de servilo",
|
"server_settings": "Agordoj de servilo",
|
||||||
@@ -348,8 +348,8 @@
|
|||||||
"template_email_settings": "Ŝablonoj de retmesaĝoj",
|
"template_email_settings": "Ŝablonoj de retmesaĝoj",
|
||||||
"template_email_update_album": "Ŝablono por retmesaĝo por ĝisdatigi albumon",
|
"template_email_update_album": "Ŝablono por retmesaĝo por ĝisdatigi albumon",
|
||||||
"template_email_welcome": "Ŝablono de bonvena retmesaĝo",
|
"template_email_welcome": "Ŝablono de bonvena retmesaĝo",
|
||||||
"template_settings": "Ŝablonoj de atentigiloj",
|
"template_settings": "Ŝablonoj de sciigoj",
|
||||||
"template_settings_description": "Administri tajloritajn skemojn por atentigiloj",
|
"template_settings_description": "Administri tajloritajn skemojn por sciigoj",
|
||||||
"theme_custom_css_settings": "Tajlorita CSS",
|
"theme_custom_css_settings": "Tajlorita CSS",
|
||||||
"theme_custom_css_settings_description": "Vi povas ŝanĝi la vidan aspekton de Immich per CSS.",
|
"theme_custom_css_settings_description": "Vi povas ŝanĝi la vidan aspekton de Immich per CSS.",
|
||||||
"theme_settings": "Agordoj de la etoso",
|
"theme_settings": "Agordoj de la etoso",
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
"transcoding_tone_mapping": "Mapado de tonoj",
|
"transcoding_tone_mapping": "Mapado de tonoj",
|
||||||
"transcoding_tone_mapping_description": "Klopodas konservi aspekton de HDR-videoj dum transkodigo al SDR. Ĉiu algoritmo faras proprajn kompromisojn pri koloroj, detaloj kaj heleco. Hable konservas detalojn, Mobius konservas kolorojn, kaj Reinhard konservas helecon.",
|
"transcoding_tone_mapping_description": "Klopodas konservi aspekton de HDR-videoj dum transkodigo al SDR. Ĉiu algoritmo faras proprajn kompromisojn pri koloroj, detaloj kaj heleco. Hable konservas detalojn, Mobius konservas kolorojn, kaj Reinhard konservas helecon.",
|
||||||
"transcoding_transcode_policy": "Politiko de transkodado",
|
"transcoding_transcode_policy": "Politiko de transkodado",
|
||||||
"transcoding_transcode_policy_description": "Politiko pri kiam video estos transkodita. HDR-videoj ĉiam estas transkoditaj (krom se transkodado estas malŝaltita).",
|
"transcoding_transcode_policy_description": "Politiko pri kiam video estos transkodita. HDR-videoj, kune kun videoj kun ciuj bilderaj formatoj krom YUV 4:2:0, ĉiam estas transkoditaj (krom se transkodado estas malŝaltita).",
|
||||||
"transcoding_two_pass_encoding": "Dupasa kodigo",
|
"transcoding_two_pass_encoding": "Dupasa kodigo",
|
||||||
"transcoding_two_pass_encoding_setting_description": "Transkodigo per du pasoj por krei pli bone kodigitajn videojn. Kiam eblas uzi maksimuman bitrapidon (bezonate por funkcii kun H.264 kaj kun HEVC), tiu ĉi modo uzas gamon de bitrapidoj surbaze de tiu maksimumo, kaj ignoras CRF. Por VP9, eblas uzi CRF se maksimuma bitrapido estas malŝaltita.",
|
"transcoding_two_pass_encoding_setting_description": "Transkodigo per du pasoj por krei pli bone kodigitajn videojn. Kiam eblas uzi maksimuman bitrapidon (bezonate por funkcii kun H.264 kaj kun HEVC), tiu ĉi modo uzas gamon de bitrapidoj surbaze de tiu maksimumo, kaj ignoras CRF. Por VP9, eblas uzi CRF se maksimuma bitrapido estas malŝaltita.",
|
||||||
"transcoding_video_codec": "Videa kodeko",
|
"transcoding_video_codec": "Videa kodeko",
|
||||||
@@ -433,36 +433,690 @@
|
|||||||
"user_details": "Detaloj pri uzanto",
|
"user_details": "Detaloj pri uzanto",
|
||||||
"user_management": "Administrado de uzantoj",
|
"user_management": "Administrado de uzantoj",
|
||||||
"user_password_has_been_reset": "Pasvorto de tiu ĉi uzanto estas restarigita:",
|
"user_password_has_been_reset": "Pasvorto de tiu ĉi uzanto estas restarigita:",
|
||||||
"user_settings_description": "Administri agordojn pri uzantoj"
|
"user_password_reset_description": "Bonvolu sendi la dumtempan pasvorton al la uzanto, kaj informu ke ĝi devos esti ŝanĝita je la sekva ensaluto.",
|
||||||
|
"user_restore_description": "La konto de <b>{user}</b> estos restaŭrita.",
|
||||||
|
"user_restore_scheduled_removal": "Restaŭri uzanton - forviŝo planita je {date, date, long}",
|
||||||
|
"user_settings": "Agordoj de uzanto",
|
||||||
|
"user_settings_description": "Administri agordojn pri uzantoj",
|
||||||
|
"user_successfully_removed": "La uzanto {email} estas forigita.",
|
||||||
|
"users_page_description": "Paĝo por administri uzantojn",
|
||||||
|
"version_check_enabled_description": "Ebligi kontrolon de versio",
|
||||||
|
"version_check_implications": "La funkcio de kontrolado de versio bezonas de temp' al tempan komunikadon kun {server}",
|
||||||
|
"version_check_settings": "Kontrolo de versio",
|
||||||
|
"version_check_settings_description": "Ŝalti/malŝalti sciigojn pri novaj versioj",
|
||||||
|
"video_conversion_job": "Transkodado de videoj",
|
||||||
|
"video_conversion_job_description": "Transkodi videojn por pli vasta kongruo kun retumiloj kaj aparatoj"
|
||||||
},
|
},
|
||||||
|
"admin_email": "Retadreso de administranto",
|
||||||
|
"admin_password": "Pasvorto de administranto",
|
||||||
|
"administration": "Administrado",
|
||||||
|
"advanced": "Altnivelaj agordoj",
|
||||||
|
"advanced_settings_clear_image_cache": "Forviŝi kaŝmemoron de bildoj",
|
||||||
|
"advanced_settings_clear_image_cache_error": "Malsukcesis forviŝi kaŝmemoron",
|
||||||
|
"advanced_settings_clear_image_cache_success": "Sukcesis liberigi {size}",
|
||||||
|
"advanced_settings_enable_alternate_media_filter_subtitle": "Uzu tiun ĉi agordon por filtri elementojn dum sinkronigo laŭ alternativaj kriterioj. Uzu tion ĉi nur se vi vidas, ke la apo ne sukcesas trovi ĉiujn albumojn.",
|
||||||
|
"advanced_settings_enable_alternate_media_filter_title": "[TESTATA] Uzi alternativan filtrilon por sinkronigi albumojn",
|
||||||
|
"advanced_settings_log_level_title": "Nivelo de protokolado: {level}",
|
||||||
|
"advanced_settings_prefer_remote_subtitle": "Kelkaj aparatoj ege malrapide ŝargas bildetojn el lokaj elementoj. Ŝaltu tiun ĉi agordon por anstataŭe ŝargi bildetojn de la servilo.",
|
||||||
|
"advanced_settings_prefer_remote_title": "Preferi bildojn el servilo",
|
||||||
|
"advanced_settings_proxy_headers_subtitle": "Aldoni paĝokapoj pri prokurilo, kiujn immich sendu kun ĉiu reta peto",
|
||||||
|
"advanced_settings_proxy_headers_title": "Tajloritaj paĝokapoj pri prokurilo [EKSPERIMENTA]",
|
||||||
|
"advanced_settings_readonly_mode_subtitle": "Ŝaltas nurlegan reĝimon, kie oni povas nur rigardi fotojn. Funkcioj kiel elekti plurajn bildojn, dividi kun aliaj, forigi k.a. estas ĉiuj neeblaj. Vi povas ŝalti/malŝalti tiun reĝimon per la profilbildo de uzanto ĉe la hejmpaĝo",
|
||||||
|
"advanced_settings_readonly_mode_title": "Nurlega reĝimo",
|
||||||
|
"advanced_settings_self_signed_ssl_subtitle": "Ignoras kontrolon de SSL-atestilo ĉe la servilo. Bezonata por memsubskribita atestilo.",
|
||||||
|
"advanced_settings_self_signed_ssl_title": "Permesi memsubskribitajn SSL-atestilojn [EKSPERIMENTA]",
|
||||||
|
"advanced_settings_sync_remote_deletions_subtitle": "Aŭtomate forigi aŭ malforigi elementojn en tiu ĉi aparato kiam oni faras tiun agon per retumilo",
|
||||||
|
"advanced_settings_sync_remote_deletions_title": "Sinkronigi forigojn [EKSPERIMENTA]",
|
||||||
|
"advanced_settings_tile_subtitle": "Altnivelaj agordaĵoj",
|
||||||
|
"advanced_settings_troubleshooting_subtitle": "Ŝalti pliajn funkciojn por problemsolvi",
|
||||||
|
"advanced_settings_troubleshooting_title": "Problemsolvi",
|
||||||
|
"age_months": "Aĝo {months, plural, one {# monato} other {# monatoj}}",
|
||||||
|
"age_year_months": "Aĝo 1 jaro, {months, plural, one {# monato} other {# monatoj}}",
|
||||||
|
"age_years": "Aĝo {years, plural, one {# jaro} other {# jaroj}}",
|
||||||
|
"album": "Albumo",
|
||||||
|
"album_added": "Albumo aldonita",
|
||||||
|
"album_added_notification_setting_description": "Ricevi retmesaĝon kiam iu aldonas vin al dividita albumo",
|
||||||
|
"album_cover_updated": "Kovrilo de albumo ĝisdatigita",
|
||||||
|
"album_delete_confirmation": "Ĉu vi certas, ke vi volas forigi la albumon {album}?",
|
||||||
|
"album_delete_confirmation_description": "Se temas pri dividita albumo, aliaj uzantoj ne plu povos vidi ĝin.",
|
||||||
|
"album_deleted": "Albumo forigita",
|
||||||
|
"album_info_card_backup_album_excluded": "EKSKLUDITA",
|
||||||
|
"album_info_card_backup_album_included": "INKLUZIVITA",
|
||||||
|
"album_info_updated": "Informoj pri albumo ĝisdatigitaj",
|
||||||
|
"album_leave": "Foriri de albumo?",
|
||||||
|
"album_leave_confirmation": "Ĉu vi certas, ke vi volas forlasi la albumon {album}?",
|
||||||
|
"album_name": "Nomo de albumo",
|
||||||
|
"album_options": "Agordoj de albumo",
|
||||||
|
"album_remove_user": "Ĉu forigi uzanton?",
|
||||||
|
"album_remove_user_confirmation": "Ĉu vi certas, ke vi volas forigi la uzanton {user}?",
|
||||||
|
"album_search_not_found": "Neniu albumo kongruas kun via serĉo",
|
||||||
|
"album_selected": "Albumo elektita",
|
||||||
|
"album_share_no_users": "Ŝajne vi jam dividis tiun albumon kun ĉiuj uzantoj AŬ ne ekzistas uzantoj kun kiuj vi povus dividi ĝin.",
|
||||||
|
"album_summary": "Resumo de albumo",
|
||||||
|
"album_updated": "Albumo ĝisdatigita",
|
||||||
|
"album_updated_setting_description": "Ricevi retmesaĝon kiam dividita albumo havas novajn elementojn",
|
||||||
|
"album_upload_assets": "Alŝuti elementojn el via komputilo kaj aldoni ilin al albumo",
|
||||||
|
"album_user_left": "Foriris de {album}",
|
||||||
|
"album_user_removed": "Uzanto {user} forigita",
|
||||||
|
"album_viewer_appbar_delete_confirm": "Ĉu vi certas, ke vi volas forigi tiun ĉi albumon de via konto?",
|
||||||
|
"album_viewer_appbar_share_err_delete": "Malsukcesis forigi albumon",
|
||||||
|
"album_viewer_appbar_share_err_leave": "Malsukcesis foriri de albumo",
|
||||||
|
"album_viewer_appbar_share_err_remove": "Okazis problemo dum forigo de elementoj el albumo",
|
||||||
|
"album_viewer_appbar_share_err_title": "Malsukcesis ŝanĝi titolon de albumo",
|
||||||
|
"album_viewer_appbar_share_leave": "Foriri de albumo",
|
||||||
|
"album_viewer_appbar_share_to": "Dividi al",
|
||||||
|
"album_viewer_page_share_add_users": "Aldoni uzantojn",
|
||||||
|
"album_with_link_access": "Permesi, ke iu ajn kun la ligilo povu vidi la fotojn kaj homojn en la albumo.",
|
||||||
|
"albums": "Albumoj",
|
||||||
|
"albums_count": "{count, plural, one {{count, number} Albumo} other {{count, number} Albumoj}}",
|
||||||
|
"albums_default_sort_order": "Defaŭlta vicordigo en albumoj",
|
||||||
|
"albums_default_sort_order_description": "Metodo por vicordigi elementojn defaŭlte uzata en nova albumo.",
|
||||||
|
"albums_feature_description": "Kolektoj de elementoj, kiujn vi povas dividi kun aliaj uzantoj.",
|
||||||
|
"albums_on_device_count": "Albumoj ĉe la aparato ({count})",
|
||||||
|
"albums_selected": "{count, plural, one {# albumo elektita} other {# albumoj elektitaj}}",
|
||||||
|
"all": "Ĉiuj",
|
||||||
|
"all_albums": "Ĉiuj albumoj",
|
||||||
|
"all_people": "Ĉiuj homoj",
|
||||||
|
"all_photos": "Ĉiuj fotoj",
|
||||||
|
"all_videos": "Ĉiuj videoj",
|
||||||
|
"allow_dark_mode": "Permesi malhelan reĝimon",
|
||||||
|
"allow_edits": "Permesi redaktojn",
|
||||||
|
"allow_public_user_to_download": "Permesu, ke publikano elŝutu el la albumo",
|
||||||
|
"allow_public_user_to_upload": "Permesu, ke publikano alŝutu al la albumo",
|
||||||
|
"allowed": "Permesita",
|
||||||
|
"alt_text_qr_code": "Bildo de QR-kodo",
|
||||||
|
"always_keep": "Ĉiam konservi",
|
||||||
|
"always_keep_photos_hint": "La funkcio 'Liberigi spacon' konservos ĉiujn fotojn en tiu ĉi aparato.",
|
||||||
|
"always_keep_videos_hint": "La funkcio 'Liberigi spacon' konservos ĉiujn videojn en tiu ĉi aparato.",
|
||||||
|
"anti_clockwise": "Kontraŭ-horloĝdirekte",
|
||||||
|
"api_key": "API-ŝlosilo",
|
||||||
|
"api_key_description": "Tio ĉi montriĝos nur unufoje. Certiĝu, ke vi kopiis ĝin antaŭ ol fermi la fenestron.",
|
||||||
|
"api_key_empty": "La nomo de via API-ŝlosilo ne devus esti malplena",
|
||||||
|
"api_keys": "API-ŝlosiloj",
|
||||||
|
"app_architecture_variant": "Varianto (arkitekturo)",
|
||||||
|
"app_bar_signout_dialog_content": "Ĉu vi certas, ke vi volas elsaluti?",
|
||||||
|
"app_bar_signout_dialog_ok": "Jes",
|
||||||
|
"app_bar_signout_dialog_title": "Elsaluti",
|
||||||
|
"app_download_links": "Ligiloj por elŝuti la apon",
|
||||||
|
"app_settings": "Agordoj pri apo",
|
||||||
|
"app_stores": "Ap-vendejoj",
|
||||||
|
"app_update_available": "Ĝisdatigo de apo disponeblas",
|
||||||
|
"appears_in": "Aperas en",
|
||||||
|
"apply_count": "Apliki ({count, number})",
|
||||||
|
"archive": "Arĥivo",
|
||||||
|
"archive_action_prompt": "{count} aldonita(j) al arĥivo",
|
||||||
|
"archive_or_unarchive_photo": "Enarĥivigi aŭ elarĥivigi foton",
|
||||||
|
"archive_page_no_archived_assets": "Neniuj elementoj trovitaj en arĥivo",
|
||||||
|
"archive_page_title": "Arĥivigi ({count})",
|
||||||
|
"archive_size": "Grandeco de arĥivo",
|
||||||
|
"archive_size_description": "Agordu la grandecon de arĥivaj dosieroj por elŝuti (en GiB)",
|
||||||
|
"archived": "Enarĥivigita(j)",
|
||||||
|
"archived_count": "{count, plural, one {# enarĥivigita} other {# enarĥivigitaj}}",
|
||||||
|
"are_these_the_same_person": "Ĉu la sama homo?",
|
||||||
|
"are_you_sure_to_do_this": "Ĉu vi certas, ke vi volas fari tion?",
|
||||||
|
"array_field_not_fully_supported": "Tablaj kampoj postulas permanan redakton de JSON",
|
||||||
|
"asset_action_delete_err_read_only": "Ne eblas forigi nurlegajn elementojn, ili estos lasitaj senŝanĝaj",
|
||||||
|
"asset_action_share_err_offline": "Ne eblis repreni nekonektitajn elementojn, ili estos ignoritaj",
|
||||||
|
"asset_added_to_album": "Aldonita al albumo",
|
||||||
|
"asset_adding_to_album": "Aldonas al albumo…",
|
||||||
|
"asset_created": "Elemento kreita",
|
||||||
|
"asset_description_updated": "Priskribo de elemento ĝisdatigita",
|
||||||
|
"asset_filename_is_offline": "Elemento {filename} estas nedisponebla",
|
||||||
|
"asset_has_unassigned_faces": "Elemento enhavas nekonatajn vizaĝojn",
|
||||||
|
"asset_hashing": "Haketado…",
|
||||||
|
"asset_list_group_by_sub_title": "Grupigi laŭ",
|
||||||
|
"asset_list_layout_settings_dynamic_layout_title": "Dinamika enpaĝigo",
|
||||||
|
"asset_list_layout_settings_group_automatically": "Aŭtomata",
|
||||||
|
"asset_list_layout_settings_group_by": "Ĝrupigi elementojn laŭ",
|
||||||
|
"asset_list_layout_settings_group_by_month_day": "Monato + tago",
|
||||||
|
"asset_list_layout_sub_title": "Enpaĝigo",
|
||||||
|
"asset_list_settings_subtitle": "Agordoj pri enpaĝigo",
|
||||||
|
"asset_list_settings_title": "Krado de fotoj",
|
||||||
|
"asset_not_found_on_device_android": "Elemento ne trovita en aparato",
|
||||||
|
"asset_not_found_on_device_ios": "Elemento ne trovita en aparato. Se vi uzas iCloud, povus esti ke la kopio ĉe iCloud estas rompita",
|
||||||
|
"asset_not_found_on_icloud": "Elemento ne trovita en iCloud. Povus esti, ke la kopio ĉe iCloud estas rompita",
|
||||||
|
"asset_offline": "Elemento nedisponebla",
|
||||||
|
"asset_offline_description": "Ne eblis trovi tiun eksteran elementon. Kontaktu la administranton de via Immich por helpo pri tio.",
|
||||||
|
"asset_restored_successfully": "Elemento sukcese riparita",
|
||||||
|
"asset_skipped": "Preterlasita(j)",
|
||||||
|
"asset_skipped_in_trash": "En rubujo",
|
||||||
|
"asset_trashed": "Elemento enrubujigita",
|
||||||
|
"asset_troubleshoot": "Problemsolvi pri elemento",
|
||||||
|
"asset_uploaded": "Alŝutita(j)",
|
||||||
|
"asset_uploading": "Alŝutado…",
|
||||||
"asset_viewer_settings_subtitle": "Administri agordojn pri vidilo de galerioj",
|
"asset_viewer_settings_subtitle": "Administri agordojn pri vidilo de galerioj",
|
||||||
|
"asset_viewer_settings_title": "Montrilo de elemento",
|
||||||
|
"assets": "Elementoj",
|
||||||
|
"assets_added_count": "Sukcese aldonis {count, plural, one {# elementon} other {# elementojn}}",
|
||||||
|
"assets_added_to_album_count": "Sukcese aldonis {count, plural, one {# elementon} other {# elementojn}} al la albumo",
|
||||||
|
"assets_added_to_albums_count": "Sukcese aldonis {assetTotal, plural, one {# elementon} other {# elementojn}} al {albumTotal, plural, one {# albumo} other {# albumoj}}",
|
||||||
|
"assets_cannot_be_added_to_album_count": "Ne eblis aldoni {count, plural, one {tiun elementon} other {tiujn elementojn}} al la albumo",
|
||||||
|
"assets_cannot_be_added_to_albums": "Ne eblis aldoni {count, plural, one {tiun elementon} other {tiujn elementojn}} al iu ajn el la albumoj",
|
||||||
|
"assets_count": "{count, plural, one {# elemento} other {# elementoj}}",
|
||||||
|
"assets_deleted_permanently": "{count} elemento(j) porĉiam forigita(j)",
|
||||||
|
"assets_deleted_permanently_from_server": "{count} elemento(j) porĉiam forigita(j) de la Immich-servilo",
|
||||||
|
"assets_downloaded_failed": "{count, plural, one {Elŝutis # dosieron - dosiero {error} malsukcesis} other {Elŝutis # dosierojn - dosieroj {error} malsukcesis}}",
|
||||||
|
"assets_downloaded_successfully": "{count, plural, one {Sukcese elŝutis # dosieron} other {Sukcese elŝutis # dosierojn}}",
|
||||||
|
"assets_moved_to_trash_count": "Movis {count, plural, one {# elementon} other {# elementojn}} al la rubujo",
|
||||||
|
"assets_permanently_deleted_count": "Porĉiam forigis {count, plural, one {# elementon} other {# elementojn}}",
|
||||||
|
"assets_removed_count": "Forigis {count, plural, one {# elementon} other {# elementojn}}",
|
||||||
|
"assets_removed_permanently_from_device": "{count} elemento(j) porĉiam forigita(j) de via aparato",
|
||||||
|
"assets_restore_confirmation": "Ĉu vi certas, ke vi volas restaŭri ĉion el la rubujo? Ne eblos poste malfari tion. Notu, ke ne eblas tiel restaŭri eksterretajn elementojn.",
|
||||||
|
"assets_restored_count": "Sukcese restaŭris {count, plural, one {# elementon} other {# elementojn}}",
|
||||||
|
"assets_restored_successfully": "{count} elemento(j) sukcese restaŭrita(j)",
|
||||||
|
"assets_trashed": "{count} elemento(j) enrubujigita(j)",
|
||||||
|
"assets_trashed_count": "Sukcese enrubujigis {count, plural, one {# elementon} other {# elementojn}}",
|
||||||
|
"assets_trashed_from_server": "{count} elemento(j) forigitaj de la immich-servilo",
|
||||||
|
"assets_were_part_of_album_count": "{count, plural, one {# Tiu elemento} other {# Tiuj elementoj}} jam estis en la albumo",
|
||||||
|
"assets_were_part_of_albums_count": "{count, plural, one {# Tiu elemento} other {# Tiuj elementoj}} jam estis en la albumoj",
|
||||||
|
"authorized_devices": "Aprobitaj aparatoj",
|
||||||
|
"automatic_endpoint_switching_subtitle": "Konekti per elektita vifio kiam eblas, kaj alikaze uzi aliajn alirojn",
|
||||||
|
"automatic_endpoint_switching_title": "Aŭtomata ŝanĝo de URL",
|
||||||
|
"autoplay_slideshow": "Aŭtomate vidigi bildserion",
|
||||||
|
"back": "Malantaŭen",
|
||||||
|
"back_close_deselect": "Malantaŭen, fermi, aŭ malelekti",
|
||||||
|
"background_backup_running_error": "Savkopiado jam estas fone okazanta, do ne eblas nun lanĉi alian savkopiadon",
|
||||||
|
"background_location_permission": "Rajtigo fone uzi geografian lokon",
|
||||||
|
"background_location_permission_content": "Por ŝanĝi retaliron dum fona funkciado, Immich devas *ĉiam* havi atingorajton al lokiga informo, por povi legi nomojn de vifiaj retoj",
|
||||||
|
"background_options": "Agordoj pri fonaj funkcioj",
|
||||||
|
"backup": "Savkopio",
|
||||||
|
"backup_album_selection_page_albums_device": "Albumoj en la aparato ({count})",
|
||||||
|
"backup_album_selection_page_albums_tap": "Tuŝeti por inkluzivi, duoble tuŝeti por ekskludi",
|
||||||
|
"backup_album_selection_page_assets_scatter": "Foje elementoj troviĝas disĵetitaj al pluraj albumoj, do albumoj povas esti inkluzivitaj aŭ ekskluzivitaj de la savkopiado.",
|
||||||
|
"backup_album_selection_page_select_albums": "Elekti albumojn",
|
||||||
|
"backup_album_selection_page_selection_info": "Info pri la elektitaĵoj",
|
||||||
|
"backup_album_selection_page_total_assets": "Nombro da unikaj elementoj",
|
||||||
|
"backup_albums_sync": "Sinkronigo de la savkopioj de albumoj",
|
||||||
|
"backup_all": "Ĉiuj",
|
||||||
|
"backup_background_service_backup_failed_message": "Malsukcesis krei savkopion de tiuj elementoj. Reprovante…",
|
||||||
|
"backup_background_service_complete_notification": "Savkopiado finita",
|
||||||
|
"backup_background_service_connection_failed_message": "Malsukcesis konektiĝi al la servilo. Reprovante…",
|
||||||
|
"backup_background_service_current_upload_notification": "Alŝutiĝas {filename}",
|
||||||
|
"backup_background_service_default_notification": "Serĉas novajn elementojn…",
|
||||||
|
"backup_background_service_error_title": "Eraro de savkopiado",
|
||||||
|
"backup_background_service_in_progress_notification": "Kreado de savkopio de viaj elementoj…",
|
||||||
|
"backup_background_service_upload_failure_notification": "Malsukcesis alŝuti {filename}",
|
||||||
|
"backup_controller_page_albums": "Savkopiado de albumoj",
|
||||||
|
"backup_controller_page_background_app_refresh_disabled_content": "Por aktivigi la fonan savkopiadon, ŝaltu fonan aktualigon de la apo en Agordoj > Ĝeneralaj > Fona aktualigo de apo.",
|
||||||
|
"backup_controller_page_background_app_refresh_disabled_title": "Fona aktualigo de apo malŝaltita",
|
||||||
|
"backup_controller_page_background_app_refresh_enable_button_text": "Iri al Agordoj",
|
||||||
|
"backup_controller_page_background_battery_info_link": "Montri al mi",
|
||||||
|
"backup_controller_page_background_battery_info_message": "Por bona funkciado de la fona savkopiado, bonvolu malŝalti optimumigon de bateria uzo por Immich.\n\nTiu funkcio dependas de via aparato, do vi devos mem serĉi helpon pri kiel atingi tion.",
|
||||||
|
"backup_controller_page_background_battery_info_ok": "Okej",
|
||||||
|
"backup_controller_page_background_battery_info_title": "Optimumigo de bateria uzo",
|
||||||
|
"backup_controller_page_background_charging": "Nur dum ŝargado",
|
||||||
|
"backup_controller_page_background_configure_error": "Malsukcesis agordi la fonan servon",
|
||||||
|
"backup_controller_page_background_delay": "Prokrasti savkopiadon de novaj elementoj: {duration}",
|
||||||
|
"backup_controller_page_background_description": "Ŝaltu la fonan servon por aŭtomate krei savkopion de novaj elementoj sen malfermi la apon",
|
||||||
|
"backup_controller_page_background_is_off": "Aŭtomata fona savkopiado estas malŝaltita",
|
||||||
|
"backup_controller_page_background_is_on": "Aŭtomata fona savkopiado estas ŝaltita",
|
||||||
|
"backup_controller_page_background_turn_off": "Malŝalti fonan servon",
|
||||||
|
"backup_controller_page_background_turn_on": "Ŝalti fonan servon",
|
||||||
|
"backup_controller_page_background_wifi": "Nur per vifio",
|
||||||
|
"backup_controller_page_backup": "Savkopiado",
|
||||||
|
"backup_controller_page_backup_selected": "Elektita(j): ",
|
||||||
|
"backup_controller_page_backup_sub": "Fotoj kaj videoj kun jama savkopio",
|
||||||
|
"backup_controller_page_created": "Kreita(j) je: {date}",
|
||||||
|
"backup_controller_page_desc_backup": "Ŝaltu malfonan savkopiadon por aŭtomate alŝuti novajn elementojn al la servilo kiam la apo estas malfermita.",
|
||||||
|
"backup_controller_page_excluded": "Ekskluzivita(j): ",
|
||||||
|
"backup_controller_page_failed": "Malsukcesis ({count})",
|
||||||
|
"backup_controller_page_filename": "Dosiernomo: {filename} [{size}]",
|
||||||
|
"backup_controller_page_id": "ID: {id}",
|
||||||
|
"backup_controller_page_info": "Informoj pri savkopio",
|
||||||
|
"backup_controller_page_none_selected": "Neniuj elektitaj",
|
||||||
|
"backup_controller_page_remainder": "Restas",
|
||||||
|
"backup_controller_page_remainder_sub": "Fotoj kaj videoj ankoraŭ ne savkopiitaj el la elektitaj",
|
||||||
|
"backup_controller_page_server_storage": "Stokado ĉe servilo",
|
||||||
|
"backup_controller_page_start_backup": "Komenci savkopiadon",
|
||||||
|
"backup_controller_page_status_off": "Aŭtomata malfona savkopiado estas malŝaltita",
|
||||||
|
"backup_controller_page_status_on": "Aŭtomata malfona savkopiado estas ŝaltita",
|
||||||
|
"backup_controller_page_storage_format": "{used} el {total} uzita",
|
||||||
|
"backup_controller_page_to_backup": "Albumoj savkopiotaj",
|
||||||
|
"backup_controller_page_total_sub": "Ĉiuj unikaj fotoj kaj videoj el elektitaj albumoj",
|
||||||
|
"backup_controller_page_turn_off": "Malŝalti malfonan savkopiadon",
|
||||||
|
"backup_controller_page_turn_on": "Ŝalti malfonan savkopiadon",
|
||||||
|
"backup_controller_page_uploading_file_info": "Alŝutiĝas informoj pri dosiero",
|
||||||
|
"backup_err_only_album": "Ne eblas forigi la solan albumon",
|
||||||
|
"backup_error_sync_failed": "Sinkronigo malsukcesis.",
|
||||||
|
"backup_info_card_assets": "elementoj",
|
||||||
|
"backup_manual_cancelled": "Nuligita",
|
||||||
|
"backup_manual_in_progress": "Alŝuto jam progresas. Provu poste",
|
||||||
|
"backup_manual_success": "Sukceso",
|
||||||
|
"backup_manual_title": "Statuso de alŝuto",
|
||||||
|
"backup_options": "Agordoj pri savkopioj",
|
||||||
|
"backup_options_page_title": "Agordoj pri savkopioj",
|
||||||
"backup_setting_subtitle": "Administri agordojn pri fona kaj malfona alŝutado",
|
"backup_setting_subtitle": "Administri agordojn pri fona kaj malfona alŝutado",
|
||||||
"backup_settings_subtitle": "Administri agordojn pri alŝutado",
|
"backup_settings_subtitle": "Administri agordojn pri alŝutado",
|
||||||
|
"backup_upload_details_page_more_details": "Tuŝu ĉi tie por pli da detaloj",
|
||||||
|
"backward": "Malantaŭen",
|
||||||
|
"biometric_auth_enabled": "Biometria ensaluto ŝaltita",
|
||||||
|
"biometric_locked_out": "Via biometria ensalutkapablo estas blokita",
|
||||||
|
"biometric_no_options": "Neniuj biometriaj ebloj estas disponeblaj",
|
||||||
|
"biometric_not_available": "Tiu ĉi aparato ne havas funkcion por biometria ensaluto",
|
||||||
|
"birthdate_saved": "Naskiĝdato ŝukcese konservita",
|
||||||
|
"birthdate_set_description": "La naskiĝdato estas uzita por kalkuli la aĝon de la homo je la momento de iu foto.",
|
||||||
|
"blurred_background": "Malklarigita fono",
|
||||||
|
"bugs_and_feature_requests": "Cimoj kaj petoj por novaj funkcioj",
|
||||||
|
"build": "Versio",
|
||||||
|
"build_image": "Bildo de la versio",
|
||||||
|
"bulk_delete_duplicates_confirmation": "Ĉu vi certas, ke vi volas amase forigi {count, plural, one {# duoblaĵon} other {# duoblaĵojn}}? Tiel, vi konservos la plej grandan elementon el ĉiu grupo kaj porĉiame forigos duoblaĵojn. Ne eblas malfari tion!",
|
||||||
|
"bulk_keep_duplicates_confirmation": "Ĉu vi certas, ke vi volas konservi {count, plural, one {# duoblaĵon} other {# duoblaĵojn}}? Tio solvos ĉiujn duoblajn grupojn sen forigi ion ajn.",
|
||||||
|
"bulk_trash_duplicates_confirmation": "Ĉu vi certas, ke vi volas amase forigi {count, plural, one {# duoblaĵon} other {# duoblaĵojn}}? Tiel, vi konservos la plej grandan elementon el ĉiu grupo kaj porĉiame forigos duoblaĵojn.",
|
||||||
|
"buy": "Aĉeti Immich",
|
||||||
|
"cache_settings_clear_cache_button": "Forviŝi kaŝmemoron",
|
||||||
|
"cache_settings_clear_cache_button_title": "Forviŝas la kaŝmemoron de la apo. Tio malrapidigos la apon ĝis kiam ĝi finos rekonstrui la kaŝon.",
|
||||||
|
"cache_settings_duplicated_assets_clear_button": "FORVIŜI",
|
||||||
|
"cache_settings_duplicated_assets_subtitle": "Fotoj kaj videoj ignoritaj de la apo",
|
||||||
|
"cache_settings_duplicated_assets_title": "Duoblaĵoj ({count})",
|
||||||
|
"cache_settings_statistics_album": "Bildetoj de la biblioteko",
|
||||||
|
"cache_settings_statistics_full": "Plenaj bildoj",
|
||||||
|
"cache_settings_statistics_shared": "Bildetoj de dividitaj albumoj",
|
||||||
|
"cache_settings_statistics_thumbnail": "Bildetoj",
|
||||||
|
"cache_settings_statistics_title": "Uzo de kaŝmemoro",
|
||||||
|
"cache_settings_subtitle": "Regas la uzadon de kaŝmemoro fare de la apo",
|
||||||
|
"cache_settings_tile_subtitle": "Regas konduton pri loka stokado",
|
||||||
|
"cache_settings_tile_title": "Loka stokado",
|
||||||
|
"cache_settings_title": "Agordoj pri kaŝmemoro",
|
||||||
|
"camera": "Fotilo",
|
||||||
|
"camera_brand": "Fabrikanto de fotilo",
|
||||||
|
"camera_model": "Modelo de fotilo",
|
||||||
|
"cancel": "Nuligi",
|
||||||
|
"cancel_search": "Nuligi serĉon",
|
||||||
|
"canceled": "Nuligita",
|
||||||
|
"canceling": "Nuligado",
|
||||||
|
"cannot_merge_people": "Ne eblas kunfandi tiujn homojn",
|
||||||
|
"cannot_undo_this_action": "Ne eblas malfari tion!",
|
||||||
|
"cannot_update_the_description": "Ne eblas ĝisdatigi la priskribon",
|
||||||
|
"cast": "Elsendi",
|
||||||
|
"cast_description": "Agordi disponeblajn celojn por elsendoj",
|
||||||
|
"change_date": "Ŝanĝi daton",
|
||||||
|
"change_description": "Ŝanĝi priskribon",
|
||||||
|
"change_display_order": "Ŝanĝi vicordon de vidigo",
|
||||||
|
"change_expiration_time": "Ŝanĝi horon de eksvalidiĝo",
|
||||||
|
"change_location": "Ŝanĝi lokon",
|
||||||
|
"change_name": "Ŝanĝi nomon",
|
||||||
|
"change_name_successfully": "Nomo sukcese ŝanĝita",
|
||||||
|
"change_password": "Ŝanĝi pasvorton",
|
||||||
|
"change_password_description": "Aŭ tio ĉi estas via unua ensaluto, aŭ la sistemo ricevis peton ŝanĝigi vian pasvorton. Bonvolu tajpi novan pasvorton ĉi-sube.",
|
||||||
|
"change_password_form_confirm_password": "Konfirmu pasvorton",
|
||||||
|
"change_password_form_description": "Saluton {name},\n\nAŭ tio ĉi estas via unua ensaluto, aŭ la sistemo ricevis peton ŝanĝigi vian pasvorton. Bonvolu tajpi novan pasvorton ĉi-sube.",
|
||||||
|
"change_password_form_log_out": "Elsalutu ĉe ĉiuj aliaj aparatoj",
|
||||||
|
"change_password_form_log_out_description": "Oni rekomendas elsaluti ĉe ĉiuj aliaj aparatoj",
|
||||||
|
"change_password_form_new_password": "Nova pasvorto",
|
||||||
|
"change_password_form_password_mismatch": "Pasvortoj ne kongruas",
|
||||||
|
"change_password_form_reenter_new_password": "Re-tajpu novan pasvorton",
|
||||||
|
"change_pin_code": "Ŝanĝi PIN-kodon",
|
||||||
|
"change_trigger": "Ŝanĝi ekagilon",
|
||||||
|
"change_trigger_prompt": "Ĉu vi certas, ke vi volas ŝanĝi la ekagilon? Tio forigos ĉiujn ekzistantajn agojn kaj filtrilojn.",
|
||||||
|
"change_your_password": "Ŝanĝi vian pasvorton",
|
||||||
|
"changed_visibility_successfully": "Sukcese ŝanĝis videblecon",
|
||||||
|
"charging": "Ŝargado",
|
||||||
|
"charging_requirement_mobile_backup": "Por fona savkopiado, vi devas konekti la aparaton al ŝargilo",
|
||||||
|
"check_corrupt_asset_backup": "Kontroli por koruptitaj savkopioj de elementoj",
|
||||||
|
"check_corrupt_asset_backup_button": "Kontroli",
|
||||||
|
"check_corrupt_asset_backup_description": "Fari tiun ĉi kontrolon nur per vifio kaj post kiam ĉiuj elementoj havas savkopion. La kontrolo povas daŭri kelkajn minutojn.",
|
||||||
|
"check_logs": "Kontroli protokolojn",
|
||||||
|
"checksum": "Kontrolsumo",
|
||||||
|
"choose_matching_people_to_merge": "Elekti duobligitajn homojn por kunfandi",
|
||||||
|
"city": "Urbo",
|
||||||
|
"cleanup_confirm_description": "Immich trovis savkopion en la servilo de {count} elementoj (kreitajn antaŭ {date}). Ĉu vi volas forigi la kopiojn de el tiu ĉi aparato?",
|
||||||
|
"cleanup_confirm_prompt_title": "Forigi el tiu ĉi aparato?",
|
||||||
|
"cleanup_deleted_assets": "Movis {count} elementojn al la rubujo de la aparato",
|
||||||
|
"cleanup_deleting": "Movado al rubujo...",
|
||||||
|
"cleanup_found_assets": "Trovis {count} elementojn kun savkopio",
|
||||||
|
"cleanup_found_assets_with_size": "Trovis {count} elementojn kun savkopio ({size})",
|
||||||
"cleanup_icloud_shared_albums_excluded": "Dividitaj albumoj ĉe iCloud estas ekskluditaj de la analizado",
|
"cleanup_icloud_shared_albums_excluded": "Dividitaj albumoj ĉe iCloud estas ekskluditaj de la analizado",
|
||||||
"cleanup_step3_description": "Serĉi fotojn kaj videojn kun sekurkopio ĉe la servilo, laŭ la elektita limdato kaj filtriloj.",
|
"cleanup_no_assets_found": "Neniuj elementoj trovitaj per la ĉi-supraj kriterioj. La funkcio 'Liberigi spacon' forigas nur elementojn, kiuj havas savkopion ĉe la servilo",
|
||||||
|
"cleanup_preview_title": "Forigotaj elementoj ({count})",
|
||||||
|
"cleanup_step3_description": "Serĉi fotojn kaj videojn kun savkopio ĉe la servilo, laŭ la elektita limdato kaj filtriloj.",
|
||||||
|
"cleanup_step4_summary": "{count} elementoj (kreitaj antaŭ {date}) forigotaj de via aparato. Fotoj restos disponeblaj (pere de la servilo) en la apo Immich.",
|
||||||
|
"cleanup_trash_hint": "Por povi reuzi la liberigitan spacon, malfermu la 'galeria' apo de via aparato kaj malplenigu la rubujon",
|
||||||
|
"clear": "Forviŝi",
|
||||||
|
"clear_all": "Forviŝi ĉiujn kampojn",
|
||||||
|
"clear_all_recent_searches": "Forviŝi ĉiujn lastatempajn serĉojn",
|
||||||
|
"clear_file_cache": "Forviŝi dosier-kaŝon",
|
||||||
|
"clear_message": "Forviŝi mesaĝon",
|
||||||
|
"clear_value": "Forviŝi valoron",
|
||||||
|
"client_cert_dialog_msg_confirm": "Bone",
|
||||||
|
"client_cert_enter_password": "Tajpu pasvorton",
|
||||||
|
"client_cert_import": "Importi",
|
||||||
|
"client_cert_import_success_msg": "Atestilo sukcese importita",
|
||||||
|
"client_cert_invalid_msg": "Nevalida atestilo-dosiero, aŭ malĝusta pasvorto",
|
||||||
|
"client_cert_password_message": "Tajpu la pasvorton por tiu ĉi atestilo",
|
||||||
|
"client_cert_password_title": "Pasvorto de atestilo",
|
||||||
|
"client_cert_remove_msg": "Klient-atestilo forigita",
|
||||||
|
"client_cert_subtitle": "Nur la formato PKCS12 (.p12, .pfx) estas akceptita. Eblas importi/forigi atestilon nur antaŭ ol ensaluti",
|
||||||
|
"client_cert_title": "Klient-atestilo SSL [EKSPERIMENTA]",
|
||||||
|
"clockwise": "Horloĝdirekte",
|
||||||
|
"close": "Fermi",
|
||||||
|
"collapse": "Maletendi",
|
||||||
|
"collapse_all": "Maletendi ĉiujn",
|
||||||
|
"color": "Koloro",
|
||||||
|
"color_theme": "Kolor-temo",
|
||||||
|
"command": "Komando",
|
||||||
|
"command_palette_prompt": "Rapide trovi paĝojn, agojn aŭ komandojn",
|
||||||
|
"command_palette_to_close": "por fermi",
|
||||||
|
"command_palette_to_navigate": "por eniri",
|
||||||
|
"command_palette_to_select": "por elekti",
|
||||||
|
"command_palette_to_show_all": "por ĉion montri",
|
||||||
|
"comment_deleted": "Komento forigita",
|
||||||
|
"comment_options": "Agoj pri komento",
|
||||||
|
"comments_and_likes": "Komentoj kaj ŝatoj",
|
||||||
|
"comments_are_disabled": "Komentoj estas malebligitaj",
|
||||||
|
"common_create_new_album": "Krei novan albumon",
|
||||||
|
"completed": "Finfarita",
|
||||||
|
"confirm": "Konfirmi",
|
||||||
|
"confirm_admin_password": "Konfirmi administran pasvorton",
|
||||||
|
"confirm_delete_face": "Ĉu vi certas ke vi volas forigi la vizaĝon de {name} de tiu elemento?",
|
||||||
|
"confirm_delete_shared_link": "Ĉu vi certas, ke vi volas forigi tiun ligilon?",
|
||||||
|
"confirm_keep_this_delete_others": "Ĉiuj elementoj en la stako krom tiu ĉi estos forigitaj. Ĉu vi certas, ke vi volas tion?",
|
||||||
|
"confirm_new_pin_code": "Konfirmi novan PIN-kodon",
|
||||||
|
"confirm_password": "Konfirmi pasvorton",
|
||||||
|
"confirm_tag_face": "Ĉu vi volas etikedi tiun ĉi vizaĝon kiel {name}?",
|
||||||
|
"confirm_tag_face_unnamed": "Ĉu vi volas etikedi tiun ĉi vizaĝon?",
|
||||||
|
"connected_device": "Konektita aparato",
|
||||||
|
"connected_to": "Konektita al",
|
||||||
|
"contain": "Alĝustigi",
|
||||||
|
"context": "Kunteksto",
|
||||||
|
"continue": "Daŭrigi",
|
||||||
|
"control_bottom_app_bar_create_new_album": "Krei novan albumon",
|
||||||
|
"control_bottom_app_bar_delete_from_immich": "Forigi el Immich",
|
||||||
|
"control_bottom_app_bar_delete_from_local": "Forigi el aparato",
|
||||||
|
"control_bottom_app_bar_edit_location": "Redakti lokon",
|
||||||
|
"control_bottom_app_bar_edit_time": "Redakti daton kaj horon",
|
||||||
|
"control_bottom_app_bar_share_link": "Dividi ligilon",
|
||||||
|
"control_bottom_app_bar_share_to": "Dividi al",
|
||||||
|
"control_bottom_app_bar_trash_from_immich": "Movi al rubujo",
|
||||||
|
"copied_image_to_clipboard": "Bildo kopiita al tondujo.",
|
||||||
|
"copied_to_clipboard": "Kopiita al tondujo!",
|
||||||
|
"copy_error": "Kopii eraron",
|
||||||
|
"copy_file_path": "Kopii dosiervojon",
|
||||||
|
"copy_image": "Kopii bildon",
|
||||||
|
"copy_link": "Kopii ligilon",
|
||||||
|
"copy_link_to_clipboard": "Kopii ligilon al tondujo",
|
||||||
|
"copy_password": "Kopii pasvorton",
|
||||||
|
"copy_to_clipboard": "Kopii al tondujo",
|
||||||
|
"country": "Lando",
|
||||||
|
"cover": "Kovri",
|
||||||
|
"covers": "Kovriloj",
|
||||||
|
"create": "Krei",
|
||||||
|
"create_album": "Krei albumon",
|
||||||
|
"create_album_page_untitled": "Sen titolo",
|
||||||
|
"create_api_key": "Krei API-ŝlosilon",
|
||||||
|
"create_first_workflow": "Krei unuan laborfluon",
|
||||||
|
"create_library": "Krei bibliotekon",
|
||||||
|
"create_link": "Krei ligilon",
|
||||||
|
"create_link_to_share": "Krei ligilon por dividi",
|
||||||
|
"create_link_to_share_description": "Permesi, ke iu ajn kun la ligilo povu vidi la elektita(j)n foto(j)n",
|
||||||
|
"create_new": "KREI NOVAN",
|
||||||
|
"create_new_person": "Krei novan homon",
|
||||||
|
"create_new_person_hint": "Atribui elektitajn elementojn al nova homo",
|
||||||
|
"create_new_user": "Krei novan uzanton",
|
||||||
|
"create_shared_album_page_share_add_assets": "ALDONI ELEMENTOJN",
|
||||||
|
"create_shared_album_page_share_select_photos": "Elekti fotojn",
|
||||||
|
"create_shared_link": "Krei dividitan ligilon",
|
||||||
|
"create_tag": "Krei etikedon",
|
||||||
|
"create_tag_description": "Krei novan etikedon. Por ingitaj etikedoj, bonvolu tajpi la plenan vojon de la etikedo, inkluzive suprenstrekoj (\"/\").",
|
||||||
|
"create_user": "Krei uzanton",
|
||||||
|
"create_workflow": "Krei laborfluon",
|
||||||
|
"created": "Kreita",
|
||||||
|
"created_at": "Kreita",
|
||||||
|
"creating_linked_albums": "Kreado de ligitaj albumoj...",
|
||||||
|
"crop": "Stuci",
|
||||||
|
"crop_aspect_ratio_fixed": "Fiksita",
|
||||||
|
"crop_aspect_ratio_free": "Libera",
|
||||||
|
"crop_aspect_ratio_original": "Originala",
|
||||||
|
"crop_aspect_ratio_square": "Kvadrata",
|
||||||
|
"curated_object_page_title": "Objektoj",
|
||||||
|
"current_device": "Aktuala aparato",
|
||||||
|
"current_pin_code": "Aktuala PIN-kodo",
|
||||||
|
"current_server_address": "Aktuala adreso de servilo",
|
||||||
|
"custom_date": "Elekti propran daton",
|
||||||
|
"custom_locale": "Propra lokaĵaro",
|
||||||
|
"custom_locale_description": "Prezenti datojn, horojn kaj numerojn laŭ la elektita lingvo kaj regiono",
|
||||||
|
"custom_url": "Propra URL",
|
||||||
|
"cutoff_date_description": "Konservi fotojn el la lastaj…",
|
||||||
|
"cutoff_day": "{count, plural, one {tago} other {tagoj}}",
|
||||||
|
"cutoff_year": "{count, plural, one {jaro} other {jaroj}}",
|
||||||
|
"daily_title_text_date": "E, dd MMM",
|
||||||
|
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
||||||
|
"dark": "Malhela",
|
||||||
|
"dark_theme": "Ŝanĝi al hela reĝimo",
|
||||||
|
"date": "Dato",
|
||||||
|
"date_after": "Dato post",
|
||||||
|
"date_and_time": "Dato kaj horo",
|
||||||
|
"date_before": "Dato antaŭ",
|
||||||
|
"date_format": "E, LLL d, y • h:mm a",
|
||||||
|
"date_of_birth_saved": "Naskiĝdato sukcese registrita",
|
||||||
|
"date_range": "Dato-intervalo",
|
||||||
|
"day": "Tago",
|
||||||
|
"days": "Tagoj",
|
||||||
|
"deduplicate_all": "Senduoblaĵigi ĉion",
|
||||||
|
"default_locale": "Defaŭlta lokaĵaro",
|
||||||
|
"default_locale_description": "Prezenti datojn kaj numerojn laŭ la lokaĵaro de via retumilo",
|
||||||
|
"delete": "Forigi",
|
||||||
|
"delete_action_confirmation_message": "Ĉu vi certas, ke vi volas forigi tiun ĉi elementon? Tiu ago movos ĝin al la rubujo ĉe la servilo, kaj demandos ĉu vi volas forigi ĝin de via aparato",
|
||||||
|
"delete_action_prompt": "{count} forigita(j)",
|
||||||
|
"delete_album": "Forigi albumon",
|
||||||
|
"delete_api_key_prompt": "Ĉu vi certas, ke vi volas forigi tiu ĉi API-ŝlosilon?",
|
||||||
|
"delete_dialog_alert": "Tiuj elementoj estos porĉiame forigitaj de Immich kaj de via aparato",
|
||||||
|
"delete_dialog_alert_local": "Tiuj ĉi elementoj estos forigitaj de via aparato, sed restos disponeblaj ĉe la servilo de Immich",
|
||||||
|
"delete_dialog_alert_local_non_backed_up": "Kelkaj el tiuj elementoj ne havas savkopion ĉe Immich kaj estos porĉiame forigitaj de via aparato",
|
||||||
|
"delete_dialog_alert_remote": "Tiuj elementoj estos porĉiame forigitaj de la Immich-servilo",
|
||||||
|
"delete_dialog_ok_force": "Forigi ĉiuokaze",
|
||||||
|
"delete_dialog_title": "Forigi por ĉiam",
|
||||||
|
"delete_duplicates_confirmation": "Ĉu vi certas, ke vi volas porĉiame forigi tiujn ĉi duoblaĵojn?",
|
||||||
|
"delete_face": "Forigi vizaĝon",
|
||||||
|
"delete_key": "Forigi ŝlosilon",
|
||||||
|
"delete_library": "Forigi bibliotekon",
|
||||||
|
"delete_link": "Forigi ligilon",
|
||||||
|
"delete_local_action_prompt": "{count} loke forigita(j)",
|
||||||
|
"delete_local_dialog_ok_backed_up_only": "Forigi nur elementojn, kiuj havas savkopiojn",
|
||||||
|
"delete_local_dialog_ok_force": "Forigi ĉiuokaze",
|
||||||
|
"delete_others": "Forigi la aliajn",
|
||||||
|
"delete_permanently": "Forigi por ĉiam",
|
||||||
|
"delete_permanently_action_prompt": "{count} forigita(j) por ĉiam",
|
||||||
|
"delete_shared_link": "Forigi dividitan ligilon",
|
||||||
|
"delete_shared_link_dialog_title": "Forigi dividitan ligilon",
|
||||||
|
"delete_tag": "Forigi etikedon",
|
||||||
|
"delete_tag_confirmation_prompt": "Ĉu vi certas, ke vi volas forigi la etikedon {tagName}?",
|
||||||
|
"delete_user": "Forigi uzanton",
|
||||||
|
"deleted_shared_link": "Dividita ligilo nun forigita",
|
||||||
|
"deletes_missing_assets": "Forigas elementojn, kiuj mankas ĉe la disko",
|
||||||
|
"description": "Priskribo",
|
||||||
|
"description_input_hint_text": "Aldoni priskribon...",
|
||||||
|
"description_input_submit_error": "Eraro okazis dum ĝisdatigo de priskribo. Kontrolu protokolon por pli da detaloj",
|
||||||
|
"deselect_all": "Malelekti ĉion",
|
||||||
|
"details": "Detaloj",
|
||||||
|
"direction": "Direkto",
|
||||||
|
"disable": "Malebligi",
|
||||||
|
"disabled": "Malebligita",
|
||||||
|
"disallow_edits": "Malpermesi redaktojn",
|
||||||
|
"discord": "Discord",
|
||||||
|
"discover": "Malkovri",
|
||||||
|
"discovered_devices": "Malkovritaj aparatoj",
|
||||||
|
"dismiss_all_errors": "Ignori ĉiujn erarojn",
|
||||||
|
"dismiss_error": "Ignori eraron",
|
||||||
|
"display_options": "Vidigi tiajn elementojn",
|
||||||
|
"display_order": "Vicordo de vidigo",
|
||||||
|
"display_original_photos": "Montri originalajn fotojn",
|
||||||
|
"display_original_photos_setting_description": "Prefere montri originalan foton anstataŭ bildeton se la originalo havas retumil-kongruan formaton. Tio povas malrapidigi vidigon de elementoj.",
|
||||||
|
"do_not_show_again": "Ne plu montri tiun ĉi mesaĝon",
|
||||||
|
"documentation": "Dokumentaro",
|
||||||
|
"done": "Finite",
|
||||||
|
"download": "Elŝuti",
|
||||||
|
"download_action_prompt": "Elŝutado de {count} elementoj",
|
||||||
|
"download_canceled": "Elŝuto nuligita",
|
||||||
|
"download_complete": "Elŝuto finita",
|
||||||
|
"download_enqueue": "Elŝuto en atendovico",
|
||||||
|
"download_error": "Eraro de elŝuto",
|
||||||
|
"download_failed": "Elŝuto malsukcesis",
|
||||||
|
"download_finished": "Elŝuto finiĝis",
|
||||||
|
"download_include_embedded_motion_videos": "Enkorpigitaj videoj",
|
||||||
|
"download_include_embedded_motion_videos_description": "Inkluzivi videon, enkorpigitan en mov-fotoj, kiel apartan dosieron",
|
||||||
|
"download_notfound": "Elŝuto ne trovita",
|
||||||
|
"download_original": "Elŝuti originalon",
|
||||||
|
"download_paused": "Elŝuto paŭzita",
|
||||||
|
"download_settings": "Elŝutado",
|
||||||
"download_settings_description": "Administri agordojn pri elŝutado de elementoj",
|
"download_settings_description": "Administri agordojn pri elŝutado de elementoj",
|
||||||
|
"download_started": "Elŝuto komenciĝis",
|
||||||
|
"download_sucess": "Elŝuto sukcesis",
|
||||||
|
"download_sucess_android": "La elemento estas elŝutita al DCIM/Immich",
|
||||||
|
"download_waiting_to_retry": "Baldaŭ reprovos elŝuton",
|
||||||
|
"downloading": "Elŝutado",
|
||||||
|
"downloading_asset_filename": "Elŝutado de elemento {filename}",
|
||||||
|
"downloading_from_icloud": "Elŝutado el iCloud",
|
||||||
|
"downloading_media": "Elŝutado de elementoj",
|
||||||
|
"drop_files_to_upload": "Demetu dosierojn ĉi tien por alŝuti",
|
||||||
|
"duplicates": "Duoblaĵoj",
|
||||||
|
"duplicates_description": "Solvu ĉiun grupon indikante tiujn, kiuj estas eventualaj duoblaĵoj.",
|
||||||
|
"duration": "Daŭro",
|
||||||
|
"edit": "Redakti",
|
||||||
|
"edit_album": "Redakti albumon",
|
||||||
|
"edit_avatar": "Redakti profilbildon",
|
||||||
|
"edit_birthday": "Redakti naskiĝtagon",
|
||||||
|
"edit_date": "Redakti daton",
|
||||||
|
"edit_date_and_time": "Redakti daton kaj horon",
|
||||||
|
"edit_date_and_time_action_prompt": "{count} datoj kaj horoj redaktitaj",
|
||||||
|
"edit_date_and_time_by_offset": "Deŝovi daton",
|
||||||
|
"edit_date_and_time_by_offset_interval": "Nova intervalo: de {from} ĝis {to}",
|
||||||
|
"edit_description": "Redakti priskribon",
|
||||||
|
"edit_description_prompt": "Bonvolu elekti novan priskribon:",
|
||||||
"edit_exclusion_pattern": "Redakti skemon de ekskludo",
|
"edit_exclusion_pattern": "Redakti skemon de ekskludo",
|
||||||
|
"edit_faces": "Redakti vizaĝojn",
|
||||||
|
"edit_key": "Redakti ŝlosilon",
|
||||||
|
"edit_link": "Redakti ligilon",
|
||||||
|
"edit_location": "Redakti lokon",
|
||||||
|
"edit_location_action_prompt": "{count} loko(j) redaktita(j)",
|
||||||
|
"edit_location_dialog_title": "Loko",
|
||||||
|
"edit_name": "Redakti nomon",
|
||||||
|
"edit_people": "Redakti homojn",
|
||||||
|
"edit_tag": "Redakti etikedon",
|
||||||
|
"edit_title": "Redakti titolon",
|
||||||
|
"edit_user": "Redakti uzanton",
|
||||||
|
"edit_workflow": "Redakti laborfluon",
|
||||||
|
"editor": "Redaktilo",
|
||||||
|
"editor_close_without_save_prompt": "La ŝanĝoj ne konserviĝos",
|
||||||
|
"editor_close_without_save_title": "Ĉu fermi redaktilon?",
|
||||||
|
"editor_confirm_reset_all_changes": "Ĉu vi certas, ke vi volas forĵeti ĉiujn ŝanĝojn?",
|
||||||
|
"editor_discard_edits_confirm": "Forĵeti ŝanĝojn",
|
||||||
|
"editor_discard_edits_prompt": "Vi havas nekonservitajn ŝanĝojn. Ĉu vi certas, ke vi volas forigi ilin?",
|
||||||
|
"editor_discard_edits_title": "Forĵeti ŝanĝojn?",
|
||||||
|
"editor_edits_applied_error": "Malsukcesis apliki redaktojn",
|
||||||
|
"editor_edits_applied_success": "Redaktoj sukcese aplikiĝis",
|
||||||
|
"editor_flip_horizontal": "Inversigi horizontale",
|
||||||
|
"editor_flip_vertical": "Inversigi vertikale",
|
||||||
|
"editor_handle_corner": "{corner, select, top_left {Supra-maldekstra} top_right {Supra-dekstra} bottom_left {Suba-maldekstra} bottom_right {Suba-dekstra} other {Ajna}} angula tenilo",
|
||||||
|
"editor_handle_edge": "{edge, select, top {Supra} bottom {Suba} left {Maldekstra} right {Dekstra} other {Ajna}} randa tenilo",
|
||||||
|
"editor_orientation": "Orientiĝo",
|
||||||
|
"editor_reset_all_changes": "Forviŝi ŝanĝojn",
|
||||||
|
"editor_rotate_left": "Turni 90º kontraŭ-horloĝdirekte",
|
||||||
|
"editor_rotate_right": "Turni 90º horloĝdirekte",
|
||||||
|
"email": "Retadreso",
|
||||||
|
"email_notifications": "Sciigoj per retmesaĝo",
|
||||||
|
"empty_folder": "Tiu ĉi dosierujo estas malplena",
|
||||||
|
"empty_trash": "Malplenigi rubujon",
|
||||||
|
"empty_trash_confirmation": "Ĉu vi certas, ke vi volas malplenigi la rubujon? Ĉiuj elementoj en la rubujo estas por ĉiam forigitaj de Immich.\nNe eblas malfari tion!",
|
||||||
|
"enable": "Ŝalti",
|
||||||
|
"enable_backup": "Ŝalti savkopiadon",
|
||||||
|
"enable_biometric_auth_description": "Tajpu vian PIN-kodon por ŝalti biometrian ensalutadon",
|
||||||
|
"enabled": "Ŝaltita",
|
||||||
|
"end_date": "Fina dato",
|
||||||
|
"enqueued": "En atendovico",
|
||||||
|
"enter_wifi_name": "Tajpu nomon de vifio",
|
||||||
|
"enter_your_pin_code": "Tajpu vian PIN-kodon",
|
||||||
|
"enter_your_pin_code_subtitle": "Tajpu vian PIN-kodon por atingi la ŝlositan dosierujon",
|
||||||
|
"error": "Eraro",
|
||||||
|
"error_change_sort_album": "Malsukcesis ŝanĝi vicordon de album-elementoj",
|
||||||
|
"error_delete_face": "Eraro dum forigo de vizaĝo el elemento",
|
||||||
|
"error_getting_places": "Eraro dum serĉo de lokoj",
|
||||||
|
"error_loading_albums": "Eraro dum ŝargado de albumoj",
|
||||||
|
"error_loading_image": "Eraro dum ŝargado de bildo",
|
||||||
|
"error_loading_partners": "Eraro dum ŝargado de partneroj: {error}",
|
||||||
|
"error_retrieving_asset_information": "Eraro dum ŝargado de informoj pri elemento",
|
||||||
|
"error_saving_image": "Eraro: {error}",
|
||||||
|
"error_tag_face_bounding_box": "Eraro dum etikedado de vizaĝo - ne eblis trovi koordinatojn de kadro",
|
||||||
|
"error_title": "Eraro - io misis",
|
||||||
|
"error_while_navigating": "Eraro dum navigado al elemento",
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"cannot_navigate_next_asset": "Ne eblis navigi al sekva elemento",
|
||||||
|
"cannot_navigate_previous_asset": "Ne eblas navigi al antaŭa elemento",
|
||||||
|
"cant_apply_changes": "Ne eblas apliki ŝanĝojn",
|
||||||
|
"cant_change_activity": "Ne eblas {enabled, select, true {malŝalti} other {ŝalti}} tiun agon",
|
||||||
|
"cant_change_asset_favorite": "Ne eblas ŝanĝi preferaton por tiu elemento",
|
||||||
|
"cant_change_metadata_assets_count": "Ne eblas ŝanĝi metadatumojn de {count, plural, one {# elemento} other {# elementoj}}",
|
||||||
|
"cant_get_faces": "Ne eblas trovi vizaĝojn",
|
||||||
|
"cant_get_number_of_comments": "Ne eblas trovi nombron da komentoj",
|
||||||
|
"cant_search_people": "Ne eblas serĉi homojn",
|
||||||
|
"cant_search_places": "Ne eblas serĉi lokojn",
|
||||||
|
"error_adding_assets_to_album": "Eraro dum ŝargado de elementoj al albumo",
|
||||||
|
"error_adding_users_to_album": "Eraro dum aldono de uzantoj al albumo",
|
||||||
|
"error_deleting_shared_user": "Eraro dum forigo de dividita uzanto",
|
||||||
|
"error_downloading": "Eraro dum elŝuto de {filename}",
|
||||||
|
"error_hiding_buy_button": "Eraro dum kaŝado de butono 'aĉeti'",
|
||||||
|
"error_removing_assets_from_album": "Eraro dum forigo de elementoj el albumo; kontrolu konzolon por detaloj",
|
||||||
|
"error_selecting_all_assets": "Eraro dum elekto de ĉiuj elementoj",
|
||||||
"exclusion_pattern_already_exists": "Tiu ĉi skemo de ekskludo jam ekzistas.",
|
"exclusion_pattern_already_exists": "Tiu ĉi skemo de ekskludo jam ekzistas.",
|
||||||
|
"failed_to_create_album": "Malsukcesis krei albumon",
|
||||||
|
"failed_to_create_shared_link": "Malsukcesis krei dividitan ligilon",
|
||||||
|
"failed_to_edit_shared_link": "Malsukcesis redakti dividitan ligilon",
|
||||||
|
"failed_to_get_people": "Malsukcesis trovi homojn",
|
||||||
|
"failed_to_keep_this_delete_others": "Malsukcesis konservi tiun ĉi elementon kaj forigi la aliajn",
|
||||||
|
"failed_to_load_asset": "Malsukcesis ŝargi elementon",
|
||||||
|
"failed_to_load_assets": "Malsukcesis ŝargi elementojn",
|
||||||
|
"failed_to_load_notifications": "Malsukcesis ŝargi sciigojn",
|
||||||
|
"failed_to_load_people": "Malsukcesis ŝargi homojn",
|
||||||
|
"failed_to_remove_product_key": "Malsukcesis forigi var-ŝalosilon",
|
||||||
|
"failed_to_reset_pin_code": "Malsukcesis restarigi PIN-kodon",
|
||||||
|
"failed_to_stack_assets": "Malsukcesis staki elementojn",
|
||||||
|
"failed_to_unstack_assets": "Malsukcesis malstaki elementojn",
|
||||||
|
"failed_to_update_notification_status": "Malsukcesis ĝisdatigi statuson de sciigoj",
|
||||||
|
"incorrect_email_or_password": "Neĝusta retadreso aŭ pasvorto",
|
||||||
|
"library_folder_already_exists": "Tiu ĉi import-vojo jam ekzistas.",
|
||||||
|
"page_not_found": "Paĝo ne trovita",
|
||||||
"unable_to_add_exclusion_pattern": "Ne eblas aldoni skemon de ekskludo",
|
"unable_to_add_exclusion_pattern": "Ne eblas aldoni skemon de ekskludo",
|
||||||
|
"unable_to_create": "Ne eblis krei laborfluon",
|
||||||
"unable_to_delete_exclusion_pattern": "Ne eblas forigi skemon de ekskludo",
|
"unable_to_delete_exclusion_pattern": "Ne eblas forigi skemon de ekskludo",
|
||||||
|
"unable_to_delete_workflow": "Ne eblis forigi laborfluon",
|
||||||
"unable_to_edit_exclusion_pattern": "Ne eblas redakti skemon de ekskludo",
|
"unable_to_edit_exclusion_pattern": "Ne eblas redakti skemon de ekskludo",
|
||||||
"unable_to_scan_libraries": "Ne eblas analizi biblitekojn",
|
"unable_to_scan_libraries": "Ne eblas analizi biblitekojn",
|
||||||
"unable_to_scan_library": "Ne eblas analizi biblitekon"
|
"unable_to_scan_library": "Ne eblas analizi biblitekon",
|
||||||
|
"unable_to_update_workflow": "Ne eblis ĝisdatigi laborfluon"
|
||||||
},
|
},
|
||||||
"exclusion_pattern": "Skemo de ekskludo",
|
"exclusion_pattern": "Skemo de ekskludo",
|
||||||
|
"expand": "Etendi",
|
||||||
|
"expand_all": "Etendi ĉiujn",
|
||||||
"explore": "Esplori",
|
"explore": "Esplori",
|
||||||
"explorer": "Foliumilo",
|
"explorer": "Foliumilo",
|
||||||
|
"free_up_space": "Liberigi spacon",
|
||||||
|
"free_up_space_description": "Vi forigos fotojn kaj/aŭ videojn, kiuj havas savkopiojn en la servilo, por liberigi spacon en via aparato. La kopioj en la servilo restos.",
|
||||||
|
"general": "Ĝeneralaj",
|
||||||
"manage_media_access_settings": "Malfermi agordaĵaron",
|
"manage_media_access_settings": "Malfermi agordaĵaron",
|
||||||
"manage_the_app_settings": "Agordi la apon",
|
"manage_the_app_settings": "Agordi la apon",
|
||||||
"missing": "Netraktitaj",
|
"missing": "Netraktitaj",
|
||||||
"networking_subtitle": "Administri agordojn pri finpunktoj de la servilo",
|
"networking_subtitle": "Administri agordojn pri finpunktoj de la servilo",
|
||||||
|
"no_devices": "Neniuj aprobitaj aparatoj",
|
||||||
"no_explore_results_message": "Alŝutu pli da fotoj por esplori vian kolekton.",
|
"no_explore_results_message": "Alŝutu pli da fotoj por esplori vian kolekton.",
|
||||||
|
"no_notifications": "Neniuj sciigoj",
|
||||||
|
"no_results_description": "Provu sinonimon aŭ pli ĝeneralan ŝlosilvorton",
|
||||||
|
"notification_permission_dialog_content": "Por ŝalti sciigojn, iru al Agordoj kaj elektu 'permesi'.",
|
||||||
|
"notification_permission_list_tile_content": "Donu permeson por ŝalti sciigojn.",
|
||||||
|
"notification_permission_list_tile_enable_button": "Ŝalti sciigojn",
|
||||||
|
"notification_permission_list_tile_title": "Permeso pri sciigoj",
|
||||||
|
"notification_toggle_setting_description": "Ŝalti sciigojn per retmesaĝo",
|
||||||
|
"notifications": "Sciigoj",
|
||||||
|
"notifications_setting_description": "Administri sciigojn",
|
||||||
"preferences_settings_subtitle": "Administri agordojn pri la apo",
|
"preferences_settings_subtitle": "Administri agordojn pri la apo",
|
||||||
"purchase_settings_server_activated": "La administranto respondecas pri la ŝlosilo de aŭtentikeco por la servilo",
|
"purchase_settings_server_activated": "La administranto respondecas pri la ŝlosilo de aŭtentikeco por la servilo",
|
||||||
|
"rating_clear": "Forviŝi pritakson",
|
||||||
"refresh": "Denove",
|
"refresh": "Denove",
|
||||||
"rescan": "Reanalizi",
|
"rescan": "Reanalizi",
|
||||||
"reset": "Restartigi",
|
"reset": "Restartigi",
|
||||||
|
"reset_sqlite_clear_app_data": "Forviŝi datumojn",
|
||||||
|
"reset_sqlite_confirmation": "Ĉu vi certas, ke vi volas forviŝi la datumojn de la apo? Tio forigos ĉiujn agordojn kaj elsalutigos vin.",
|
||||||
|
"reset_sqlite_confirmation_note": "Noto: vi devos relanĉi la apon por la forviŝo.",
|
||||||
|
"reset_sqlite_done": "Datumoj de la apo estas forviŝitaj. Bonvolu relanĉi Immich kaj ensalutu denove.",
|
||||||
|
"scaffold_body_error_unrecoverable": "Neriparebla eraro okazis. Bonvolu sendi al ni la eraron kaj la stakspuron per Discord aŭ per Github por ke ni povu helpi. Vi povas forviŝi la ĉi-subajn datumojn de la apo se vi volas.",
|
||||||
"scan": "Analizi",
|
"scan": "Analizi",
|
||||||
"scan_all_libraries": "Analizi ĉiujn bibliotekojn",
|
"scan_all_libraries": "Analizi ĉiujn bibliotekojn",
|
||||||
"scan_library": "Analizi",
|
"scan_library": "Analizi",
|
||||||
@@ -470,12 +1124,31 @@
|
|||||||
"scanning": "Analizado",
|
"scanning": "Analizado",
|
||||||
"scanning_for_album": "Serĉado de albumo...",
|
"scanning_for_album": "Serĉado de albumo...",
|
||||||
"search_suggestion_list_smart_search_hint_1": "Inteligenta serĉado defaŭlte estas ŝaltita. Por serĉi metadatumojn, uzu sintakson tiel ",
|
"search_suggestion_list_smart_search_hint_1": "Inteligenta serĉado defaŭlte estas ŝaltita. Por serĉi metadatumojn, uzu sintakson tiel ",
|
||||||
|
"setting_notifications_subtitle": "Redakti viajn preferojn pri sciigoj",
|
||||||
|
"start_date": "Komenca dato",
|
||||||
|
"start_date_before_end_date": "Komenca dato devas esti antaŭ fina dato",
|
||||||
|
"trigger_description": "Evento, kiu ekfunkciigas la laborfluon",
|
||||||
|
"untitled_workflow": "Sentitola laborfluo",
|
||||||
"upload_concurrency": "Nombro da samtempaj alŝutoj",
|
"upload_concurrency": "Nombro da samtempaj alŝutoj",
|
||||||
"user_pin_code_settings_description": "Administri vian PIN-kodon",
|
"user_pin_code_settings_description": "Administri vian PIN-kodon",
|
||||||
"user_purchase_settings_description": "Administri vian aĉeton",
|
"user_purchase_settings_description": "Administri vian aĉeton",
|
||||||
"view_links": "Vidi ligilojn",
|
"view_links": "Vidi ligilojn",
|
||||||
"week": "Semajno",
|
"week": "Semajno",
|
||||||
"wifi_name": "Nomo de Vifireto",
|
"wifi_name": "Nomo de Vifireto",
|
||||||
|
"workflow_delete_prompt": "Ĉu vi certas, ke vi volas forigi tiun ĉi laborfluon?",
|
||||||
|
"workflow_deleted": "Laborfluo forigita",
|
||||||
|
"workflow_description": "Priskribo de laborfluo",
|
||||||
|
"workflow_info": "Informoj pri laborfluo",
|
||||||
|
"workflow_json": "JSON de laborfluo",
|
||||||
|
"workflow_json_help": "Redakti la agordojn pri la laborfluo per formato JSON. La ŝanĝoj sinkroniĝos al la vidiga konstruilo.",
|
||||||
|
"workflow_name": "Nomo de laborfluo",
|
||||||
|
"workflow_navigation_prompt": "Ĉu vi certas, ke vi volas foriri sen konservi viajn ŝanĝojn?",
|
||||||
|
"workflow_summary": "Resumo de laborfluo",
|
||||||
|
"workflow_update_success": "Laborfluo sukcese ĝisdatigita",
|
||||||
|
"workflow_updated": "Laborfluo ĝisdatigita",
|
||||||
|
"workflows": "Laborfluoj",
|
||||||
|
"workflows_help_text": "Laborfluo aŭtomatigas agojn pri elementoj, laŭ ekigiloj kaj filtriloj",
|
||||||
"year": "Jaro",
|
"year": "Jaro",
|
||||||
"yes": "Jes"
|
"yes": "Jes",
|
||||||
|
"zero_to_clear_rating": "tuŝu 0 por forviŝi la pritakson de la elemento"
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-21
@@ -17,13 +17,13 @@
|
|||||||
"add_a_name": "Añadir un nombre",
|
"add_a_name": "Añadir un nombre",
|
||||||
"add_a_title": "Añadir título",
|
"add_a_title": "Añadir título",
|
||||||
"add_action": "Añadir acción",
|
"add_action": "Añadir acción",
|
||||||
"add_action_description": "Haga clic para añadir una acción a realizar",
|
"add_action_description": "Haz clic para añadir una acción a realizar",
|
||||||
"add_assets": "Añadir recursos",
|
"add_assets": "Añadir recursos",
|
||||||
"add_birthday": "Añadir un cumpleaños",
|
"add_birthday": "Añadir un cumpleaños",
|
||||||
"add_endpoint": "Añadir punto final",
|
"add_endpoint": "Añadir punto final",
|
||||||
"add_exclusion_pattern": "Añadir patrón de exclusión",
|
"add_exclusion_pattern": "Añadir patrón de exclusión",
|
||||||
"add_filter": "Añadir filtro",
|
"add_filter": "Añadir filtro",
|
||||||
"add_filter_description": "Haga clic para añadir una condición de filtro",
|
"add_filter_description": "Haz clic para añadir una condición de filtro",
|
||||||
"add_location": "Añadir ubicación",
|
"add_location": "Añadir ubicación",
|
||||||
"add_more_users": "Añadir más usuarios",
|
"add_more_users": "Añadir más usuarios",
|
||||||
"add_partner": "Añadir miembro",
|
"add_partner": "Añadir miembro",
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"backup_onboarding_1_description": "Copia en un lugar externo, en la nube u otra ubicación física.",
|
"backup_onboarding_1_description": "Copia en un lugar externo, en la nube u otra ubicación física.",
|
||||||
"backup_onboarding_2_description": "copias locales en diferentes dispositivos. Incluye los archivos principales y una copia de seguridad local de dichos archivos.",
|
"backup_onboarding_2_description": "copias locales en diferentes dispositivos. Incluye los archivos principales y una copia de seguridad local de dichos archivos.",
|
||||||
"backup_onboarding_3_description": "copias totales de tu data, incluyendo los archivos originales. Incluye 1 copia fuera de sitio y 2 copias locales.",
|
"backup_onboarding_3_description": "copias totales de tu data, incluyendo los archivos originales. Incluye 1 copia fuera de sitio y 2 copias locales.",
|
||||||
"backup_onboarding_description": "Una estrategia de <backblaze-link>copia de seguridad 3-2-1</backblaze-link> es recomendada para proteger tu data. Deberías mantener tanto copias de tus fotos/videos subidos como de la base de datos de Immich para tener una solución de copia de seguridad integral.",
|
"backup_onboarding_description": "Se recomienda una <backblaze-link>estrategia de copia de seguridad 3-2-1</backblaze-link> para proteger tus datos. Deberías mantener copias de las fotos y vídeos que subas, así como de la base de datos de Immich, para contar con una solución de copia de seguridad completa.",
|
||||||
"backup_onboarding_footer": "Para obtener más información sobre cómo hacer una copia de seguridad de Immich, consulta la <link>documentación</link>.",
|
"backup_onboarding_footer": "Para obtener más información sobre cómo hacer una copia de seguridad de Immich, consulta la <link>documentación</link>.",
|
||||||
"backup_onboarding_parts_title": "Una copia de seguridad 3-2-1 incluye:",
|
"backup_onboarding_parts_title": "Una copia de seguridad 3-2-1 incluye:",
|
||||||
"backup_onboarding_title": "Copias de seguridad",
|
"backup_onboarding_title": "Copias de seguridad",
|
||||||
@@ -351,7 +351,7 @@
|
|||||||
"template_settings": "Plantillas de notificación",
|
"template_settings": "Plantillas de notificación",
|
||||||
"template_settings_description": "Gestione plantillas personalizadas para las notificaciones",
|
"template_settings_description": "Gestione plantillas personalizadas para las notificaciones",
|
||||||
"theme_custom_css_settings": "CSS personalizado",
|
"theme_custom_css_settings": "CSS personalizado",
|
||||||
"theme_custom_css_settings_description": "El CSS permite personalizar el diseño de Immich.",
|
"theme_custom_css_settings_description": "Las Hojas de Estilo permiten personalizar el diseño de Immich.",
|
||||||
"theme_settings": "Ajustes del tema",
|
"theme_settings": "Ajustes del tema",
|
||||||
"theme_settings_description": "Gestionar la personalización de la interfaz web de Immich",
|
"theme_settings_description": "Gestionar la personalización de la interfaz web de Immich",
|
||||||
"thumbnail_generation_job": "Generar miniaturas",
|
"thumbnail_generation_job": "Generar miniaturas",
|
||||||
@@ -372,7 +372,7 @@
|
|||||||
"transcoding_audio_codec": "Codec de audio",
|
"transcoding_audio_codec": "Codec de audio",
|
||||||
"transcoding_audio_codec_description": "Opus es la opción de mayor calidad, pero tiene menor compatibilidad con dispositivos o software antiguos.",
|
"transcoding_audio_codec_description": "Opus es la opción de mayor calidad, pero tiene menor compatibilidad con dispositivos o software antiguos.",
|
||||||
"transcoding_bitrate_description": "Vídeos con una tasa de bits superior a la máxima o que no están en un formato aceptado",
|
"transcoding_bitrate_description": "Vídeos con una tasa de bits superior a la máxima o que no están en un formato aceptado",
|
||||||
"transcoding_codecs_learn_more": "Para obtener más información sobre la terminología utilizada aquí, consulte la documentación de FFmpeg sobre los codecs <h264-link>H.264</h264-link>, <hevc-link>HEVC</hevc-link> y <vp9-link>VP9</vp9-link>.",
|
"transcoding_codecs_learn_more": "Para obtener más información sobre la terminología utilizada aquí, consulta la documentación de FFmpeg sobre <h264-link>el códec H.264</h264-link>, <hevc-link>el códec HEVC</hevc-link> y <vp9-link>el códec VP9</vp9-link>.",
|
||||||
"transcoding_constant_quality_mode": "Modo de calidad constante",
|
"transcoding_constant_quality_mode": "Modo de calidad constante",
|
||||||
"transcoding_constant_quality_mode_description": "ICQ es mejor que CQP, pero algunos dispositivos de aceleración de hardware no admiten este modo. Al configurar esta opción, se preferirá el modo especificado cuando se utilice codificación basada en calidad. NVENC lo ignora porque no es compatible con ICQ.",
|
"transcoding_constant_quality_mode_description": "ICQ es mejor que CQP, pero algunos dispositivos de aceleración de hardware no admiten este modo. Al configurar esta opción, se preferirá el modo especificado cuando se utilice codificación basada en calidad. NVENC lo ignora porque no es compatible con ICQ.",
|
||||||
"transcoding_constant_rate_factor": "Factor de tasa constante (-crf)",
|
"transcoding_constant_rate_factor": "Factor de tasa constante (-crf)",
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
"transcoding_tone_mapping": "Mapeo de tonos",
|
"transcoding_tone_mapping": "Mapeo de tonos",
|
||||||
"transcoding_tone_mapping_description": "Intenta preservar la apariencia de los videos HDR cuando se convierten a SDR. Cada algoritmo realiza diferentes compensaciones en cuanto a color, detalle y brillo. Hable conserva los detalles, Mobius conserva el color y Reinhard conserva el brillo.",
|
"transcoding_tone_mapping_description": "Intenta preservar la apariencia de los videos HDR cuando se convierten a SDR. Cada algoritmo realiza diferentes compensaciones en cuanto a color, detalle y brillo. Hable conserva los detalles, Mobius conserva el color y Reinhard conserva el brillo.",
|
||||||
"transcoding_transcode_policy": "Políticas de transcodificación",
|
"transcoding_transcode_policy": "Políticas de transcodificación",
|
||||||
"transcoding_transcode_policy_description": "Política sobre cuándo se debe transcodificar un vídeo. Los vídeos HDR siempre se transcodificarán (excepto si la transcodificación está desactivada).",
|
"transcoding_transcode_policy_description": "Política sobre cuándo se debe transcodificar un vídeo. Los vídeos HDR y vídeos con un formato de píxel diferente a YUV 4:2:0 siempre se transcodificarán (excepto si la transcodificación está desactivada).",
|
||||||
"transcoding_two_pass_encoding": "Codificación en dos pasadas",
|
"transcoding_two_pass_encoding": "Codificación en dos pasadas",
|
||||||
"transcoding_two_pass_encoding_setting_description": "Transcodifica en dos pasadas para producir vídeos mejor codificados. Cuando la velocidad de bits máxima está habilitada (es necesaria para que funcione con H.264 y HEVC), este modo utiliza un rango de velocidad de bits basado en la velocidad de bits máxima e ignora CRF. Para VP9, se puede utilizar CRF si la tasa de bits máxima está deshabilitada.",
|
"transcoding_two_pass_encoding_setting_description": "Transcodifica en dos pasadas para producir vídeos mejor codificados. Cuando la velocidad de bits máxima está habilitada (es necesaria para que funcione con H.264 y HEVC), este modo utiliza un rango de velocidad de bits basado en la velocidad de bits máxima e ignora CRF. Para VP9, se puede utilizar CRF si la tasa de bits máxima está deshabilitada.",
|
||||||
"transcoding_video_codec": "Códecs de video",
|
"transcoding_video_codec": "Códecs de video",
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
"user_successfully_removed": "El usuario {email} ha sido eliminado con éxito.",
|
"user_successfully_removed": "El usuario {email} ha sido eliminado con éxito.",
|
||||||
"users_page_description": "Página de usuarios administradores",
|
"users_page_description": "Página de usuarios administradores",
|
||||||
"version_check_enabled_description": "Activar la comprobación de la versión",
|
"version_check_enabled_description": "Activar la comprobación de la versión",
|
||||||
"version_check_implications": "La función de comprobación de versiones depende de la comunicación periódica con github.com",
|
"version_check_implications": "La función de comprobación de versiones depende de la comunicación periódica con {server}",
|
||||||
"version_check_settings": "Verificar versión",
|
"version_check_settings": "Verificar versión",
|
||||||
"version_check_settings_description": "Activar/desactivar la notificación de nueva versión",
|
"version_check_settings_description": "Activar/desactivar la notificación de nueva versión",
|
||||||
"video_conversion_job": "Transcodificar vídeos",
|
"video_conversion_job": "Transcodificar vídeos",
|
||||||
@@ -459,7 +459,7 @@
|
|||||||
"advanced_settings_log_level_title": "Nivel de registro: {level}",
|
"advanced_settings_log_level_title": "Nivel de registro: {level}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas desde los recursos locales. Activa esta opción para cargar imágenes remotas en su lugar.",
|
"advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas desde los recursos locales. Activa esta opción para cargar imágenes remotas en su lugar.",
|
||||||
"advanced_settings_prefer_remote_title": "Preferir imágenes remotas",
|
"advanced_settings_prefer_remote_title": "Preferir imágenes remotas",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Configura encabezados HTTP que Immich incluirá en cada petición de red",
|
"advanced_settings_proxy_headers_subtitle": "Configura encabezados de proxy que Immich incluirá en cada petición de red",
|
||||||
"advanced_settings_proxy_headers_title": "Cabeceras proxy personalizadas [EXPERIMENTAL]",
|
"advanced_settings_proxy_headers_title": "Cabeceras proxy personalizadas [EXPERIMENTAL]",
|
||||||
"advanced_settings_readonly_mode_subtitle": "Habilita el modo de solo lectura donde las fotografías sólo pueden ser vistas, funciones como seleccionar múltiples imágenes, compartir, transmitir, eliminar son deshabilitadas. Habilita/Deshabilita solo lectura vía el avatar del usuario en la pantalla principal",
|
"advanced_settings_readonly_mode_subtitle": "Habilita el modo de solo lectura donde las fotografías sólo pueden ser vistas, funciones como seleccionar múltiples imágenes, compartir, transmitir, eliminar son deshabilitadas. Habilita/Deshabilita solo lectura vía el avatar del usuario en la pantalla principal",
|
||||||
"advanced_settings_readonly_mode_title": "Modo solo lectura",
|
"advanced_settings_readonly_mode_title": "Modo solo lectura",
|
||||||
@@ -866,13 +866,14 @@
|
|||||||
"crop_aspect_ratio_fixed": "Fijado",
|
"crop_aspect_ratio_fixed": "Fijado",
|
||||||
"crop_aspect_ratio_free": "Libre",
|
"crop_aspect_ratio_free": "Libre",
|
||||||
"crop_aspect_ratio_original": "Original",
|
"crop_aspect_ratio_original": "Original",
|
||||||
|
"crop_aspect_ratio_square": "Cuadrado",
|
||||||
"curated_object_page_title": "Objetos",
|
"curated_object_page_title": "Objetos",
|
||||||
"current_device": "Dispositivo actual",
|
"current_device": "Dispositivo actual",
|
||||||
"current_pin_code": "PIN actual",
|
"current_pin_code": "PIN actual",
|
||||||
"current_server_address": "Dirección actual del servidor",
|
"current_server_address": "Dirección actual del servidor",
|
||||||
"custom_date": "Fecha personalizada",
|
"custom_date": "Fecha personalizada",
|
||||||
"custom_locale": "Configuración regional personalizada",
|
"custom_locale": "Configuración regional personalizada",
|
||||||
"custom_locale_description": "Formatear fechas y números según el idioma y la región",
|
"custom_locale_description": "Dar formato a fechas, horas y números según el idioma y región seleccionados",
|
||||||
"custom_url": "URL personalizada",
|
"custom_url": "URL personalizada",
|
||||||
"cutoff_date_description": "Conserva fotos del último…",
|
"cutoff_date_description": "Conserva fotos del último…",
|
||||||
"cutoff_day": "{count, plural, one {día} other {días}}",
|
"cutoff_day": "{count, plural, one {día} other {días}}",
|
||||||
@@ -880,7 +881,7 @@
|
|||||||
"daily_title_text_date": "E dd, MMM",
|
"daily_title_text_date": "E dd, MMM",
|
||||||
"daily_title_text_date_year": "E dd de MMM, yyyy",
|
"daily_title_text_date_year": "E dd de MMM, yyyy",
|
||||||
"dark": "Oscuro",
|
"dark": "Oscuro",
|
||||||
"dark_theme": "Alternar tema oscuro",
|
"dark_theme": "Cambiar a tema oscuro",
|
||||||
"date": "Fecha",
|
"date": "Fecha",
|
||||||
"date_after": "Fecha posterior",
|
"date_after": "Fecha posterior",
|
||||||
"date_and_time": "Fecha y hora",
|
"date_and_time": "Fecha y hora",
|
||||||
@@ -891,12 +892,8 @@
|
|||||||
"day": "Día",
|
"day": "Día",
|
||||||
"days": "Días",
|
"days": "Días",
|
||||||
"deduplicate_all": "Deduplicar todo",
|
"deduplicate_all": "Deduplicar todo",
|
||||||
"deduplication_criteria_1": "Tamaño de imagen en bytes",
|
|
||||||
"deduplication_criteria_2": "Conteo de datos EXIF",
|
|
||||||
"deduplication_info": "Información de Deduplicación",
|
|
||||||
"deduplication_info_description": "Para automáticamente preseleccionar recursos y eliminar duplicados en conjunto, nosotros consideramos lo siguiente:",
|
|
||||||
"default_locale": "Configuración regional predeterminada",
|
"default_locale": "Configuración regional predeterminada",
|
||||||
"default_locale_description": "Formatee fechas y números según la configuración regional de su navegador",
|
"default_locale_description": "Formatear fechas y números según la configuración regional del navegador",
|
||||||
"delete": "Eliminar",
|
"delete": "Eliminar",
|
||||||
"delete_action_confirmation_message": "¿Está seguro que desea eliminar este recurso? Esta acción lo moverá a la papelera del servidor y le preguntará si desea eliminarlo localmente",
|
"delete_action_confirmation_message": "¿Está seguro que desea eliminar este recurso? Esta acción lo moverá a la papelera del servidor y le preguntará si desea eliminarlo localmente",
|
||||||
"delete_action_prompt": "{count} eliminados",
|
"delete_action_prompt": "{count} eliminados",
|
||||||
@@ -972,7 +969,7 @@
|
|||||||
"downloading_media": "Descargando medios",
|
"downloading_media": "Descargando medios",
|
||||||
"drop_files_to_upload": "Suelta los archivos en cualquier lugar para subirlos",
|
"drop_files_to_upload": "Suelta los archivos en cualquier lugar para subirlos",
|
||||||
"duplicates": "Duplicados",
|
"duplicates": "Duplicados",
|
||||||
"duplicates_description": "Resuelva cada grupo indicando, en cada caso, cuales están duplicados",
|
"duplicates_description": "Resuelve cada grupo indicando cuáles son duplicados, si los hay.",
|
||||||
"duration": "Duración",
|
"duration": "Duración",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
"edit_album": "Editar álbum",
|
"edit_album": "Editar álbum",
|
||||||
@@ -1009,6 +1006,8 @@
|
|||||||
"editor_edits_applied_success": "Edición aplicada con éxito",
|
"editor_edits_applied_success": "Edición aplicada con éxito",
|
||||||
"editor_flip_horizontal": "Girar horizontalmente",
|
"editor_flip_horizontal": "Girar horizontalmente",
|
||||||
"editor_flip_vertical": "Girar verticalmente",
|
"editor_flip_vertical": "Girar verticalmente",
|
||||||
|
"editor_handle_corner": "{corner, select, top_left {Superior izquierda} top_right {Superior derecha} bottom_left {Inferior izquierda} bottom_right {Inferior derecha} other {Un}} controlador de esquina",
|
||||||
|
"editor_handle_edge": "{edge, select, top {Superior} bottom {Inferior} left {Izquierdo} right {Derecho} other {Un}} controlador de borde",
|
||||||
"editor_orientation": "Orientación",
|
"editor_orientation": "Orientación",
|
||||||
"editor_reset_all_changes": "Restablecer cambios",
|
"editor_reset_all_changes": "Restablecer cambios",
|
||||||
"editor_rotate_left": "Rotar 90º sentido antihorario",
|
"editor_rotate_left": "Rotar 90º sentido antihorario",
|
||||||
@@ -1074,6 +1073,7 @@
|
|||||||
"failed_to_update_notification_status": "Error al actualizar el estado de la notificación",
|
"failed_to_update_notification_status": "Error al actualizar el estado de la notificación",
|
||||||
"incorrect_email_or_password": "Contraseña o email incorrecto",
|
"incorrect_email_or_password": "Contraseña o email incorrecto",
|
||||||
"library_folder_already_exists": "Esta ruta de importación ya existe.",
|
"library_folder_already_exists": "Esta ruta de importación ya existe.",
|
||||||
|
"page_not_found": "Página no encontrada",
|
||||||
"paths_validation_failed": "Falló la validación en {paths, plural, one {# carpeta} other {# carpetas}}",
|
"paths_validation_failed": "Falló la validación en {paths, plural, one {# carpeta} other {# carpetas}}",
|
||||||
"profile_picture_transparent_pixels": "Las imágenes de perfil no pueden tener píxeles transparentes. Por favor amplíe y/o mueva la imagen.",
|
"profile_picture_transparent_pixels": "Las imágenes de perfil no pueden tener píxeles transparentes. Por favor amplíe y/o mueva la imagen.",
|
||||||
"quota_higher_than_disk_size": "Se ha establecido una cuota superior al tamaño del disco",
|
"quota_higher_than_disk_size": "Se ha establecido una cuota superior al tamaño del disco",
|
||||||
@@ -1164,7 +1164,7 @@
|
|||||||
},
|
},
|
||||||
"errors_text": "Errores",
|
"errors_text": "Errores",
|
||||||
"exclusion_pattern": "Patrón de exclusión",
|
"exclusion_pattern": "Patrón de exclusión",
|
||||||
"exif": "EXIF",
|
"exif": "Exif",
|
||||||
"exif_bottom_sheet_description": "Añadir descripción…",
|
"exif_bottom_sheet_description": "Añadir descripción…",
|
||||||
"exif_bottom_sheet_description_error": "Error al actualizar la descripción",
|
"exif_bottom_sheet_description_error": "Error al actualizar la descripción",
|
||||||
"exif_bottom_sheet_details": "DETALLES",
|
"exif_bottom_sheet_details": "DETALLES",
|
||||||
@@ -1218,6 +1218,7 @@
|
|||||||
"filter_description": "Condiciones para filtrar los recursos objetivo",
|
"filter_description": "Condiciones para filtrar los recursos objetivo",
|
||||||
"filter_people": "Filtrar personas",
|
"filter_people": "Filtrar personas",
|
||||||
"filter_places": "Filtrar lugares",
|
"filter_places": "Filtrar lugares",
|
||||||
|
"filter_tags": "Filtrar etiquetas",
|
||||||
"filters": "Filtros",
|
"filters": "Filtros",
|
||||||
"find_them_fast": "Encuéntrelos rápidamente por nombre con la búsqueda",
|
"find_them_fast": "Encuéntrelos rápidamente por nombre con la búsqueda",
|
||||||
"first": "Primero",
|
"first": "Primero",
|
||||||
@@ -1385,9 +1386,11 @@
|
|||||||
"library_page_sort_title": "Título del álbum",
|
"library_page_sort_title": "Título del álbum",
|
||||||
"licenses": "Licencias",
|
"licenses": "Licencias",
|
||||||
"light": "Claro",
|
"light": "Claro",
|
||||||
|
"light_theme": "Cambiar a tema claro",
|
||||||
"like": "Me gusta",
|
"like": "Me gusta",
|
||||||
"like_deleted": "Me gusta eliminado",
|
"like_deleted": "Me gusta eliminado",
|
||||||
"link_motion_video": "Enlazar vídeo en movimiento",
|
"link_motion_video": "Enlazar vídeo en movimiento",
|
||||||
|
"link_to_docs": "Para más información, consulta la <link>documentación</link>.",
|
||||||
"link_to_oauth": "Enlace a OAuth",
|
"link_to_oauth": "Enlace a OAuth",
|
||||||
"linked_oauth_account": "Cuenta OAuth vinculada",
|
"linked_oauth_account": "Cuenta OAuth vinculada",
|
||||||
"list": "Lista",
|
"list": "Lista",
|
||||||
@@ -1649,6 +1652,7 @@
|
|||||||
"only_favorites": "Solo favoritos",
|
"only_favorites": "Solo favoritos",
|
||||||
"open": "Abierto",
|
"open": "Abierto",
|
||||||
"open_calendar": "Abrir calendario",
|
"open_calendar": "Abrir calendario",
|
||||||
|
"open_in_browser": "Abrir en el navegador",
|
||||||
"open_in_map_view": "Abrir en la vista del mapa",
|
"open_in_map_view": "Abrir en la vista del mapa",
|
||||||
"open_in_openstreetmap": "Abrir en OpenStreetMap",
|
"open_in_openstreetmap": "Abrir en OpenStreetMap",
|
||||||
"open_the_search_filters": "Abre los filtros de búsqueda",
|
"open_the_search_filters": "Abre los filtros de búsqueda",
|
||||||
@@ -1808,9 +1812,8 @@
|
|||||||
"rate_asset": "Valorar recurso",
|
"rate_asset": "Valorar recurso",
|
||||||
"rating": "Valoración",
|
"rating": "Valoración",
|
||||||
"rating_clear": "Borrar calificación",
|
"rating_clear": "Borrar calificación",
|
||||||
"rating_count": "{count, plural, one {# estrella} other {# estrellas}}",
|
"rating_count": "{count, plural, =0 {# estrella} one {# estrella} other {# estrellas}}",
|
||||||
"rating_description": "Mostrar la clasificación exif en el panel de información",
|
"rating_description": "Mostrar la clasificación exif en el panel de información",
|
||||||
"rating_set": "Calificación establecida en {rating, plural, one {# estrella} other {# estrellas}}",
|
|
||||||
"reaction_options": "Opciones de reacción",
|
"reaction_options": "Opciones de reacción",
|
||||||
"read_changelog": "Leer registro de cambios",
|
"read_changelog": "Leer registro de cambios",
|
||||||
"readonly_mode_disabled": "Modo solo lectura deshabilitado",
|
"readonly_mode_disabled": "Modo solo lectura deshabilitado",
|
||||||
@@ -1882,7 +1885,10 @@
|
|||||||
"reset_pin_code_success": "Código PIN restablecido correctamente",
|
"reset_pin_code_success": "Código PIN restablecido correctamente",
|
||||||
"reset_pin_code_with_password": "Siempre puedes restablecer tu código PIN usando tu contraseña",
|
"reset_pin_code_with_password": "Siempre puedes restablecer tu código PIN usando tu contraseña",
|
||||||
"reset_sqlite": "Restablecer la base de datos SQLite",
|
"reset_sqlite": "Restablecer la base de datos SQLite",
|
||||||
"reset_sqlite_confirmation": "¿Estás seguro que deseas restablecer la base de datos SQLite? Deberás cerrar sesión y volver a iniciarla para resincronizar los datos",
|
"reset_sqlite_clear_app_data": "Limpiar datos",
|
||||||
|
"reset_sqlite_confirmation": "¿Seguro que quieres borrar los datos de la aplicación? Esto eliminará toda la configuración y cerrará tu sesión.",
|
||||||
|
"reset_sqlite_confirmation_note": "Nota: Deberás reiniciar la aplicación después de borrarla.",
|
||||||
|
"reset_sqlite_done": "Se han borrado los datos de la aplicación. Reinicie Immich y vuelva a iniciar sesión.",
|
||||||
"reset_sqlite_success": "Restablecer exitosamente la base de datos SQLite",
|
"reset_sqlite_success": "Restablecer exitosamente la base de datos SQLite",
|
||||||
"reset_to_default": "Restablecer los valores predeterminados",
|
"reset_to_default": "Restablecer los valores predeterminados",
|
||||||
"resolution": "Resolución",
|
"resolution": "Resolución",
|
||||||
@@ -1910,6 +1916,7 @@
|
|||||||
"saved_settings": "Configuraciones guardadas",
|
"saved_settings": "Configuraciones guardadas",
|
||||||
"say_something": "Comenta algo",
|
"say_something": "Comenta algo",
|
||||||
"scaffold_body_error_occurred": "Ha ocurrido un error",
|
"scaffold_body_error_occurred": "Ha ocurrido un error",
|
||||||
|
"scaffold_body_error_unrecoverable": "Se ha producido un error irrecuperable. Comparte el error y el seguimiento de la pila en Discord o GitHub para que podamos ayudarte. Si se indica, puedes borrar los datos de la aplicación a continuación.",
|
||||||
"scan": "Escanear",
|
"scan": "Escanear",
|
||||||
"scan_all_libraries": "Escanear todas las bibliotecas",
|
"scan_all_libraries": "Escanear todas las bibliotecas",
|
||||||
"scan_library": "Escanear",
|
"scan_library": "Escanear",
|
||||||
@@ -1945,6 +1952,7 @@
|
|||||||
"search_filter_ocr": "Buscar por OCR",
|
"search_filter_ocr": "Buscar por OCR",
|
||||||
"search_filter_people_title": "Seleccionar personas",
|
"search_filter_people_title": "Seleccionar personas",
|
||||||
"search_filter_star_rating": "Clasificación de estrellas",
|
"search_filter_star_rating": "Clasificación de estrellas",
|
||||||
|
"search_filter_tags_title": "Seleccionar etiquetas",
|
||||||
"search_for": "Buscar",
|
"search_for": "Buscar",
|
||||||
"search_for_existing_person": "Buscar persona existente",
|
"search_for_existing_person": "Buscar persona existente",
|
||||||
"search_no_more_result": "No hay más resultados",
|
"search_no_more_result": "No hay más resultados",
|
||||||
@@ -2024,6 +2032,9 @@
|
|||||||
"set_profile_picture": "Establecer foto de perfil",
|
"set_profile_picture": "Establecer foto de perfil",
|
||||||
"set_slideshow_to_fullscreen": "Mostrar diapositivas en pantalla completa",
|
"set_slideshow_to_fullscreen": "Mostrar diapositivas en pantalla completa",
|
||||||
"set_stack_primary_asset": "Establecer como recurso principal",
|
"set_stack_primary_asset": "Establecer como recurso principal",
|
||||||
|
"setting_image_navigation_enable_subtitle": "Si está habilitado, puedes navegar a la imagen anterior/siguiente tocando una cuarta parte más a la izquierda/derecha de la pantalla.",
|
||||||
|
"setting_image_navigation_enable_title": "Toca para navegar",
|
||||||
|
"setting_image_navigation_title": "Navegación de imágenes",
|
||||||
"setting_image_viewer_help": "El visor de detalles carga primero la miniatura pequeña, luego carga la vista previa de tamaño mediano (si está habilitada), finalmente carga la original (si está habilitada).",
|
"setting_image_viewer_help": "El visor de detalles carga primero la miniatura pequeña, luego carga la vista previa de tamaño mediano (si está habilitada), finalmente carga la original (si está habilitada).",
|
||||||
"setting_image_viewer_original_subtitle": "Activar para cargar la imagen en resolución original (¡muy grande!). Deshabilitar para reducir el consumo de datos (de red y caché).",
|
"setting_image_viewer_original_subtitle": "Activar para cargar la imagen en resolución original (¡muy grande!). Deshabilitar para reducir el consumo de datos (de red y caché).",
|
||||||
"setting_image_viewer_original_title": "Cargar imagen original",
|
"setting_image_viewer_original_title": "Cargar imagen original",
|
||||||
@@ -2302,6 +2313,7 @@
|
|||||||
"unstack_action_prompt": "{count} desapilado(s)",
|
"unstack_action_prompt": "{count} desapilado(s)",
|
||||||
"unstacked_assets_count": "Desapilado(s) {count, plural, one {# recurso} other {# recursos}}",
|
"unstacked_assets_count": "Desapilado(s) {count, plural, one {# recurso} other {# recursos}}",
|
||||||
"unsupported_field_type": "Tipo de campo no soportado",
|
"unsupported_field_type": "Tipo de campo no soportado",
|
||||||
|
"unsupported_file_type": "El archivo {file} no puede ser cargado porque su tipo de archivo {type} no es soportado.",
|
||||||
"untagged": "Sin etiqueta",
|
"untagged": "Sin etiqueta",
|
||||||
"untitled_workflow": "Flujo de trabajo sin título",
|
"untitled_workflow": "Flujo de trabajo sin título",
|
||||||
"up_next": "A continuación",
|
"up_next": "A continuación",
|
||||||
@@ -2328,6 +2340,8 @@
|
|||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "Uso",
|
"usage": "Uso",
|
||||||
"use_biometric": "Uso biométrico",
|
"use_biometric": "Uso biométrico",
|
||||||
|
"use_browser_locale": "Utilizar la localización del navegador",
|
||||||
|
"use_browser_locale_description": "Dar formato a fechas, horas y números según la localización de su navegador",
|
||||||
"use_current_connection": "Utilice la conexión actual",
|
"use_current_connection": "Utilice la conexión actual",
|
||||||
"use_custom_date_range": "Usa un intervalo de fechas personalizado",
|
"use_custom_date_range": "Usa un intervalo de fechas personalizado",
|
||||||
"user": "Usuario",
|
"user": "Usuario",
|
||||||
@@ -2353,7 +2367,7 @@
|
|||||||
"variables": "Variables",
|
"variables": "Variables",
|
||||||
"version": "Versión",
|
"version": "Versión",
|
||||||
"version_announcement_closing": "Tu amigo, Alex",
|
"version_announcement_closing": "Tu amigo, Alex",
|
||||||
"version_announcement_message": "¡Hola! Hay una nueva versión de Immich disponible. Tómese un tiempo para leer las <link> notas de la versión </link> para asegurarse de que su configuración esté actualizada y evitar errores de configuración, especialmente si utiliza WatchTower o cualquier mecanismo que se encargue de actualizar su instancia de Immich automáticamente.",
|
"version_announcement_message": "¡Hola! Hay una nueva versión de Immich disponible. Tómese un tiempo para leer las <link>notas de la versión</link> para asegurarse de que su configuración esté actualizada y evitar errores de configuración, especialmente si utiliza WatchTower o cualquier mecanismo que se encargue de actualizar su instancia de Immich automáticamente.",
|
||||||
"version_history": "Historial de versiones",
|
"version_history": "Historial de versiones",
|
||||||
"version_history_item": "Instalada {version} el {date}",
|
"version_history_item": "Instalada {version} el {date}",
|
||||||
"video": "Vídeo",
|
"video": "Vídeo",
|
||||||
@@ -2381,6 +2395,7 @@
|
|||||||
"viewer_remove_from_stack": "Quitar de la pila",
|
"viewer_remove_from_stack": "Quitar de la pila",
|
||||||
"viewer_stack_use_as_main_asset": "Usar como recurso principal",
|
"viewer_stack_use_as_main_asset": "Usar como recurso principal",
|
||||||
"viewer_unstack": "Desapilar",
|
"viewer_unstack": "Desapilar",
|
||||||
|
"visibility": "Visibilidad",
|
||||||
"visibility_changed": "Visibilidad cambiada para {count, plural, one {# persona} other {# personas}}",
|
"visibility_changed": "Visibilidad cambiada para {count, plural, one {# persona} other {# personas}}",
|
||||||
"visual": "Visual",
|
"visual": "Visual",
|
||||||
"visual_builder": "Constructor visual",
|
"visual_builder": "Constructor visual",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user