Compare commits
114 Commits
dev/paymen
...
feat/fast-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e04d25d8f5 | ||
|
|
c642150b85 | ||
|
|
a8a7d29891 | ||
|
|
67e98ed313 | ||
|
|
47ef48e3c2 | ||
|
|
376feadb76 | ||
|
|
3d82005797 | ||
|
|
10aa00af21 | ||
|
|
1f8bdcdce7 | ||
|
|
98ebfc22f8 | ||
|
|
032b99fe93 | ||
|
|
07156135c2 | ||
|
|
9dbf5db72e | ||
|
|
52170423be | ||
|
|
ae095baad3 | ||
|
|
f99f289f74 | ||
|
|
476eea44df | ||
|
|
e84657192c | ||
|
|
5dda5d93f5 | ||
|
|
6260caf649 | ||
|
|
9e5c52b7b7 | ||
|
|
0e1311e3d3 | ||
|
|
216cca4383 | ||
|
|
cdc98de848 | ||
|
|
126cbeabe8 | ||
|
|
2e0c6f6fff | ||
|
|
81790ab166 | ||
|
|
69b948f3d0 | ||
|
|
4b2ed28b1a | ||
|
|
b8e6ae65b1 | ||
|
|
36bdbf93a7 | ||
|
|
3eee6c4dcf | ||
|
|
3a3676bc82 | ||
|
|
34fc572276 | ||
|
|
ef17c257ef | ||
|
|
4c69cb89d7 | ||
|
|
735455508c | ||
|
|
eba166a2f1 | ||
|
|
8cf8a2cb35 | ||
|
|
1767ed2192 | ||
|
|
3c15dae341 | ||
|
|
8568c2e8b9 | ||
|
|
d558ea819a | ||
|
|
60701d131e | ||
|
|
04808f8b5c | ||
|
|
8a866297f7 | ||
|
|
b5991c908e | ||
|
|
321c3ccfc6 | ||
|
|
05874bd84e | ||
|
|
79705dc58d | ||
|
|
71a132b0b8 | ||
|
|
d14f23497c | ||
|
|
a916df56ee | ||
|
|
73dcb9b452 | ||
|
|
f32c02bd25 | ||
|
|
b16c9405d8 | ||
|
|
46df165ef2 | ||
|
|
19e35d8d3f | ||
|
|
c4c070569f | ||
|
|
7651f70c88 | ||
|
|
4698c39855 | ||
|
|
2f2aecfb47 | ||
|
|
20efd82461 | ||
|
|
22a0b4d900 | ||
|
|
2f25a8a437 | ||
|
|
7a0bc0ea87 | ||
|
|
a564c80193 | ||
|
|
f4671617d1 | ||
|
|
d331da0ced | ||
|
|
84da9abcbc | ||
|
|
48eede59b9 | ||
|
|
972c66d467 | ||
|
|
69795a3763 | ||
|
|
9c337223e6 | ||
|
|
4d862525bc | ||
|
|
d8d64ecc45 | ||
|
|
e1e7de4d4c | ||
|
|
df412d60c5 | ||
|
|
dbd9782763 | ||
|
|
2519922dd3 | ||
|
|
78b10bbcc6 | ||
|
|
4ec47b4186 | ||
|
|
3c5ba77e86 | ||
|
|
62f8bd80f4 | ||
|
|
69193598cb | ||
|
|
f92513be7e | ||
|
|
b5e0a1cec8 | ||
|
|
2ffde69caf | ||
|
|
9291c782d5 | ||
|
|
b595881084 | ||
|
|
4b49d3a85d | ||
|
|
def5f59242 | ||
|
|
9ac2ac2fcb | ||
|
|
d5f3d98dfc | ||
|
|
3e118793de | ||
|
|
c8f2d994c6 | ||
|
|
f2148ddf03 | ||
|
|
2b1b43a7e4 | ||
|
|
7a46f80ddc | ||
|
|
c6c480c882 | ||
|
|
4ad97ccded | ||
|
|
ca12f3b15f | ||
|
|
86eb2525d7 | ||
|
|
079864dfbe | ||
|
|
051e6cfc0b | ||
|
|
8f42766979 | ||
|
|
7e2a03a8d9 | ||
|
|
1947316b0b | ||
|
|
0f976edf96 | ||
|
|
ce985ef8f8 | ||
|
|
cf223dc98c | ||
|
|
97ffddee7c | ||
|
|
abf6fc25f7 | ||
|
|
b2761b12d1 |
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
design/
|
design/
|
||||||
docker/
|
docker/
|
||||||
|
!docker/scripts
|
||||||
docs/
|
docs/
|
||||||
e2e/
|
e2e/
|
||||||
fastlane/
|
fastlane/
|
||||||
|
|||||||
23
.github/labeler.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
cli:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: cli/**
|
||||||
|
|
||||||
|
documentation:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: docs/**
|
||||||
|
|
||||||
|
🖥️web:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: web/**
|
||||||
|
|
||||||
|
📱mobile:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: mobile/**
|
||||||
|
|
||||||
|
🗄️server:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: server/**
|
||||||
|
|
||||||
|
🧠machine-learning:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: machine-learning/**
|
||||||
25
.github/workflows/cli.yml
vendored
@@ -1,16 +1,17 @@
|
|||||||
name: CLI Build
|
name: CLI Build
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- "cli/**"
|
- 'cli/**'
|
||||||
- ".github/workflows/cli.yml"
|
- '.github/workflows/cli.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- "cli/**"
|
- 'cli/**'
|
||||||
- ".github/workflows/cli.yml"
|
- '.github/workflows/cli.yml'
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
@@ -32,8 +33,8 @@ jobs:
|
|||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "20.x"
|
node-version: '20.x'
|
||||||
registry-url: "https://registry.npmjs.org"
|
registry-url: 'https://registry.npmjs.org'
|
||||||
- name: Prepare SDK
|
- name: Prepare SDK
|
||||||
run: npm ci --prefix ../open-api/typescript-sdk/
|
run: npm ci --prefix ../open-api/typescript-sdk/
|
||||||
- name: Build SDK
|
- name: Build SDK
|
||||||
@@ -41,7 +42,7 @@ jobs:
|
|||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
- run: npm publish
|
- run: npm publish
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|
||||||
@@ -83,15 +84,15 @@ jobs:
|
|||||||
images: |
|
images: |
|
||||||
name=ghcr.io/${{ github.repository_owner }}/immich-cli
|
name=ghcr.io/${{ github.repository_owner }}/immich-cli
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=${{ steps.package-version.outputs.version }},enable=${{ github.event_name == 'workflow_dispatch' }}
|
type=raw,value=${{ steps.package-version.outputs.version }},enable=${{ github.event_name == 'release' }}
|
||||||
type=raw,value=latest,enable=${{ github.event_name == 'workflow_dispatch' }}
|
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@v5.3.0
|
uses: docker/build-push-action@v5.4.0
|
||||||
with:
|
with:
|
||||||
file: cli/Dockerfile
|
file: cli/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: ${{ github.event_name == 'workflow_dispatch' }}
|
push: ${{ github.event_name == 'release' }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
tags: ${{ steps.metadata.outputs.tags }}
|
tags: ${{ steps.metadata.outputs.tags }}
|
||||||
|
|||||||
2
.github/workflows/docker.yml
vendored
@@ -115,7 +115,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@v5.3.0
|
uses: docker/build-push-action@v5.4.0
|
||||||
with:
|
with:
|
||||||
context: ${{ matrix.context }}
|
context: ${{ matrix.context }}
|
||||||
file: ${{ matrix.file }}
|
file: ${{ matrix.file }}
|
||||||
|
|||||||
12
.github/workflows/pr-labeler.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
name: "Pull Request Labeler"
|
||||||
|
on:
|
||||||
|
- pull_request_target
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
labeler:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/labeler@v5
|
||||||
13
.github/workflows/pr-require-label.yml
vendored
@@ -1,13 +0,0 @@
|
|||||||
name: Enforce PR labels
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [labeled, unlabeled, opened, edited, synchronize]
|
|
||||||
jobs:
|
|
||||||
enforce-label:
|
|
||||||
name: Enforce label
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- if: toJson(github.event.pull_request.labels) == '[]'
|
|
||||||
run: exit 1
|
|
||||||
|
|
||||||
6
Makefile
@@ -10,12 +10,6 @@ dev-update:
|
|||||||
dev-scale:
|
dev-scale:
|
||||||
docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
|
docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
|
||||||
|
|
||||||
stage:
|
|
||||||
docker compose -f ./docker/docker-compose.staging.yml up --build -V --remove-orphans
|
|
||||||
|
|
||||||
pull-stage:
|
|
||||||
docker compose -f ./docker/docker-compose.staging.yml pull
|
|
||||||
|
|
||||||
.PHONY: e2e
|
.PHONY: e2e
|
||||||
e2e:
|
e2e:
|
||||||
docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans
|
docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
<a href="readme_i18n/README_zh_CN.md">中文</a>
|
<a href="readme_i18n/README_zh_CN.md">中文</a>
|
||||||
<a href="readme_i18n/README_ru_RU.md">Русский</a>
|
<a href="readme_i18n/README_ru_RU.md">Русский</a>
|
||||||
<a href="readme_i18n/README_pt_BR.md">Português Brasileiro</a>
|
<a href="readme_i18n/README_pt_BR.md">Português Brasileiro</a>
|
||||||
|
<a href="readme_i18n/README_sv_SE.md">Svenska</a>
|
||||||
<a href="readme_i18n/README_ar_JO.md">العربية</a>
|
<a href="readme_i18n/README_ar_JO.md">العربية</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
8
cli/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.0",
|
"version": "2.2.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.0",
|
"version": "2.2.3",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
@@ -47,14 +47,14 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.105.1",
|
"version": "1.106.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oazapfts/runtime": "^1.0.2"
|
"@oazapfts/runtime": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.12.13",
|
"@types/node": "^20.11.0",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.0",
|
"version": "2.2.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",
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: redis:6.2-alpine@sha256:e31ca60b18f7e9b78b573d156702471d4eda038803c0b8e6f01559f350031e93
|
image: redis:6.2-alpine@sha256:d6c2911ac51b289db208767581a5d154544f2b2fe4914ea5056443f62dc6e900
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: pg_isready --dbname='${DB_DATABASE_NAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT SUM(checksum_failures) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
|
test: pg_isready --dbname='${DB_DATABASE_NAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
|
||||||
interval: 5m
|
interval: 5m
|
||||||
start_interval: 30s
|
start_interval: 30s
|
||||||
start_period: 5m
|
start_period: 5m
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: redis:6.2-alpine@sha256:e31ca60b18f7e9b78b573d156702471d4eda038803c0b8e6f01559f350031e93
|
image: redis:6.2-alpine@sha256:d6c2911ac51b289db208767581a5d154544f2b2fe4914ea5056443f62dc6e900
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
@@ -61,7 +61,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: pg_isready --dbname='${DB_DATABASE_NAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT SUM(checksum_failures) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
|
test: pg_isready --dbname='${DB_DATABASE_NAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
|
||||||
interval: 5m
|
interval: 5m
|
||||||
start_interval: 30s
|
start_interval: 30s
|
||||||
start_period: 5m
|
start_period: 5m
|
||||||
@@ -85,7 +85,7 @@ services:
|
|||||||
command: ['./run.sh', '-disable-reporting']
|
command: ['./run.sh', '-disable-reporting']
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
image: grafana/grafana:11.0.0-ubuntu@sha256:02e99d1ee0b52dc9d3000c7b5314e7a07e0dfd69cc49bb3f8ce323491ed3406b
|
image: grafana/grafana:11.0.0-ubuntu@sha256:dcd3ae78713958a862732c3608d32c03f0c279c35a2032d74b80b12c5cdc47b8
|
||||||
volumes:
|
volumes:
|
||||||
- grafana-data:/var/lib/grafana
|
- grafana-data:/var/lib/grafana
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/redis:6.2-alpine@sha256:e31ca60b18f7e9b78b573d156702471d4eda038803c0b8e6f01559f350031e93
|
image: docker.io/redis:6.2-alpine@sha256:d6c2911ac51b289db208767581a5d154544f2b2fe4914ea5056443f62dc6e900
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
@@ -59,7 +59,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: pg_isready --dbname='${DB_DATABASE_NAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT SUM(checksum_failures) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
|
test: pg_isready --dbname='${DB_DATABASE_NAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
|
||||||
interval: 5m
|
interval: 5m
|
||||||
start_interval: 30s
|
start_interval: 30s
|
||||||
start_period: 5m
|
start_period: 5m
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ global:
|
|||||||
evaluation_interval: 15s
|
evaluation_interval: 15s
|
||||||
|
|
||||||
scrape_configs:
|
scrape_configs:
|
||||||
- job_name: immich_server
|
- job_name: immich_api
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ['immich-server:8081']
|
- targets: ['immich-server:8081']
|
||||||
|
|
||||||
- job_name: immich_microservices
|
- job_name: immich_microservices
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ['immich-microservices:8081']
|
- targets: ['immich-server:8082']
|
||||||
|
|||||||
49
docker/scripts/get-cpus.sh
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
LOG_LEVEL="${IMMICH_LOG_LEVEL:='info'}"
|
||||||
|
|
||||||
|
logDebug() {
|
||||||
|
if [ "$LOG_LEVEL" = "debug" ] || [ "$LOG_LEVEL" = "verbose" ]; then
|
||||||
|
echo "DEBUG: $1" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
|
||||||
|
logDebug "cgroup v2 detected."
|
||||||
|
if [ -f /sys/fs/cgroup/cpu.max ]; then
|
||||||
|
read -r quota period </sys/fs/cgroup/cpu.max
|
||||||
|
if [ "$quota" = "max" ]; then
|
||||||
|
logDebug "No CPU limits set."
|
||||||
|
unset quota period
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
logDebug "/sys/fs/cgroup/cpu.max not found."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
logDebug "cgroup v1 detected."
|
||||||
|
|
||||||
|
if [ -f /sys/fs/cgroup/cpu/cpu.cfs_quota_us ] && [ -f /sys/fs/cgroup/cpu/cpu.cfs_period_us ]; then
|
||||||
|
quota=$(cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us)
|
||||||
|
period=$(cat /sys/fs/cgroup/cpu/cpu.cfs_period_us)
|
||||||
|
|
||||||
|
if [ "$quota" = "-1" ]; then
|
||||||
|
logDebug "No CPU limits set."
|
||||||
|
unset quota period
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
logDebug "/sys/fs/cgroup/cpu/cpu.cfs_quota_us or /sys/fs/cgroup/cpu/cpu.cfs_period_us not found."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${quota:-}" ] && [ -n "${period:-}" ]; then
|
||||||
|
cpus=$((quota / period))
|
||||||
|
if [ "$cpus" -eq 0 ]; then
|
||||||
|
cpus=1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
cpus=$(grep -c ^processor /proc/cpuinfo)
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$cpus"
|
||||||
23
docs/docs/administration/email-notification.mdx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Email Notifications
|
||||||
|
|
||||||
|
Immich supports the option to send notifications via Email for the following events:
|
||||||
|
|
||||||
|
- Creating a new user
|
||||||
|
- Notifying a user when they get added to a shared album
|
||||||
|
- Informing other users about the addition of new assets to a shared album
|
||||||
|
|
||||||
|
## SMTP settings
|
||||||
|
|
||||||
|
You can access the settings panel from the web at `Administration -> Settings -> Notification settings`
|
||||||
|
|
||||||
|
Under Email, enter the following details to connect with SMTP servers.
|
||||||
|
|
||||||
|
You can use the following [guide](/docs/guides/smtp-gmail) to use Gmail's SMTP server.
|
||||||
|
|
||||||
|
<img src={require('./img/email-settings.png').default} width="80%" title="SMTP settings" />
|
||||||
|
|
||||||
|
## User's notifications settings
|
||||||
|
|
||||||
|
Users can manage their email notification settings from their account settings page on the web. They can choose to turn email notifications on or off for the following events:
|
||||||
|
|
||||||
|
<img src={require('./img/user-notifications-settings.png').default} width="80%" title="User notification settings" />
|
||||||
BIN
docs/docs/administration/img/email-settings.png
Normal file
|
After Width: | Height: | Size: 218 KiB |
BIN
docs/docs/administration/img/immich-email-notefaction.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/docs/administration/img/send-user-email-notification.webp
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
docs/docs/administration/img/user-notifications-settings.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
@@ -10,6 +10,59 @@ Viewing and modifying the system settings is restricted to the Administrator.
|
|||||||
You can always return to the default settings by clicking the `Reset to default` button.
|
You can always return to the default settings by clicking the `Reset to default` button.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
## Authentication Settings
|
||||||
|
|
||||||
|
Manage password, OAuth, and other authentication settings
|
||||||
|
|
||||||
|
### OAuth Authentication
|
||||||
|
|
||||||
|
Immich supports OAuth Authentication. Read more about this feature and its configuration [here](/docs/administration/oauth).
|
||||||
|
|
||||||
|
### Password Authentication
|
||||||
|
|
||||||
|
The administrator can choose to disable login with username and password for the entire instance. This means that **no one**, including the system administrator, will be able to log using this method. If [OAuth Authentication](/docs/administration/oauth) is also disabled, no users will be able to login using **any** method. Changing this setting does not affect existing sessions, just new login attempts.
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
You can always use the [Server CLI](/docs/administration/server-commands) to re-enable password login.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Image Settings (thumbnails and previews)
|
||||||
|
|
||||||
|
- Thumbnails - Used in the main timeline.
|
||||||
|
- Previews - Used in the asset viewer.
|
||||||
|
|
||||||
|
By default Immich creates 3 thumbnails for each asset,
|
||||||
|
Blurred (thumbhash) , Small - thumbnails (webp) , and Large - previews (jpeg/webp), using these settings you can change the quality for the thumbnails and previews files that are created.
|
||||||
|
|
||||||
|
**Thumbnail format**
|
||||||
|
Allows you to choose the type of format you want for the Thumbnail images, Webp produces smaller files than jpeg, but is slower to encode.
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
You can read in detail about the advantages and disadvantages of using webp over jpeg on [Adobe's website](https://www.adobe.com/creativecloud/file-types/image/raster/webp-file.html)
|
||||||
|
:::
|
||||||
|
|
||||||
|
**Thumbnail resolution**
|
||||||
|
Used when viewing groups of photos (main timeline, album view, etc.). Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness.
|
||||||
|
|
||||||
|
**Preview format**
|
||||||
|
Allows you to choose the type of format you want for the Preview images, Webp produces smaller files than jpeg, but is slower to encode.
|
||||||
|
|
||||||
|
**Preview resolution**
|
||||||
|
Used when viewing a single photo and for machine learning. Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness.
|
||||||
|
|
||||||
|
**Quality**
|
||||||
|
Image quality from 1-100. Higher is better for quality but produces larger files, this option affects the Preview and Thumbnail images.
|
||||||
|
|
||||||
|
**Prefer wide gamut**
|
||||||
|
Use Display P3 for thumbnails. This better preserves the vibrance of images with wide colorspaces, but images may appear differently on old devices with an old browser version. sRGB images are kept as sRGB to avoid color shifts.
|
||||||
|
|
||||||
|
**Prefer embedded preview**
|
||||||
|
Use embedded previews in RAW photos as the input to image processing when available. This can produce more accurate colors for some images, but the quality of the preview is camera-dependent and the image may have more compression artifacts.
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
The default resolution for Large thumbnails can be lowered from 1440p (default) to 1080p or 720p to save storage space.
|
||||||
|
:::
|
||||||
|
|
||||||
## Job Settings
|
## Job Settings
|
||||||
|
|
||||||
Using these settings, you can determine the amount of work that will run concurrently for each task in microservices. Some tasks can be set to higher values on computers with powerful hardware and storage with good I/O capabilities.
|
Using these settings, you can determine the amount of work that will run concurrently for each task in microservices. Some tasks can be set to higher values on computers with powerful hardware and storage with good I/O capabilities.
|
||||||
@@ -19,6 +72,11 @@ this advice improves throughput, not latency, for example, it will make Smart Se
|
|||||||
|
|
||||||
It is important to remember that jobs like Smart Search, Face Detection, Facial Recognition, and Transcode Videos require a **lot** of processing power and therefore do not exaggerate the amount of jobs because you're probably thoroughly overloading the server.
|
It is important to remember that jobs like Smart Search, Face Detection, Facial Recognition, and Transcode Videos require a **lot** of processing power and therefore do not exaggerate the amount of jobs because you're probably thoroughly overloading the server.
|
||||||
|
|
||||||
|
:::danger IMPORTANT
|
||||||
|
If you increase the concurrency from the defaults we set, especially for thumbnail generation, make sure you do not increase them past the amount of CPU cores you have available.
|
||||||
|
Doing so can impact API responsiveness with no gain in thumbnail generation speed.
|
||||||
|
:::
|
||||||
|
|
||||||
:::info Facial Recognition Concurrency
|
:::info Facial Recognition Concurrency
|
||||||
The Facial Recognition Concurrency value cannot be changed because
|
The Facial Recognition Concurrency value cannot be changed because
|
||||||
[DBSCAN](https://www.youtube.com/watch?v=RDZUdRSDOok) is traditionally sequential, but there are parallel implementations of it out there. Our implementation isn't parallel.
|
[DBSCAN](https://www.youtube.com/watch?v=RDZUdRSDOok) is traditionally sequential, but there are parallel implementations of it out there. Our implementation isn't parallel.
|
||||||
@@ -87,17 +145,9 @@ The map can be adjusted via [OpenMapTiles](https://openmaptiles.org/styles/) for
|
|||||||
|
|
||||||
Immich supports [Reverse Geocoding](/docs/features/reverse-geocoding) using data from the [GeoNames](https://www.geonames.org/) geographical database.
|
Immich supports [Reverse Geocoding](/docs/features/reverse-geocoding) using data from the [GeoNames](https://www.geonames.org/) geographical database.
|
||||||
|
|
||||||
## OAuth Authentication
|
## Notification Settings
|
||||||
|
|
||||||
Immich supports OAuth Authentication. Read more about this feature and its configuration [here](/docs/administration/oauth).
|
SMTP server setup, for user creation notifications, new albums, etc. More information can be found [here](/docs/administration/email-notification)
|
||||||
|
|
||||||
## Password Authentication
|
|
||||||
|
|
||||||
The administrator can choose to disable login with username and password for the entire instance. This means that **no one**, including the system administrator, will be able to log using this method. If [OAuth Authentication](/docs/administration/oauth) is also disabled, no users will be able to login using **any** method. Changing this setting does not affect existing sessions, just new login attempts.
|
|
||||||
|
|
||||||
:::tip
|
|
||||||
You can always use the [Server CLI](/docs/administration/server-commands) to re-enable password login.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Server Settings
|
## Server Settings
|
||||||
|
|
||||||
@@ -125,27 +175,6 @@ p {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Thumbnail Settings
|
|
||||||
|
|
||||||
By default Immich creates 3 thumbnails for each asset,
|
|
||||||
Blurred (thumbhash) , Small (webp) , and Large (jpeg), using these settings you can change the quality for the thumbnail files that are created.
|
|
||||||
|
|
||||||
**Small thumbnail resolution**
|
|
||||||
Used when viewing groups of photos (main timeline, album view, etc.). Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness.
|
|
||||||
|
|
||||||
**Large thumbnail resolution**
|
|
||||||
Used when viewing a single photo and for machine learning. Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness.
|
|
||||||
|
|
||||||
**Quality**
|
|
||||||
Thumbnail quality from 1-100. Higher is better for quality but produces larger files.
|
|
||||||
|
|
||||||
**Prefer wide gamut**
|
|
||||||
Use display p3 for thumbnails. This better preserves the vibrance of images with wide color spaces, but images may appear differently on old devices with an old browser version. Srgb images are kept as srgb to avoid color shifts.
|
|
||||||
|
|
||||||
:::tip
|
|
||||||
The default resolution for Large thumbnails can be lowered from 1440p (default) to 1080p or 720p to save storage space.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Trash Settings
|
## Trash Settings
|
||||||
|
|
||||||
In the system administrator's option to set a trash for deleted files, these files will remain in the trash until the deletion date 30 days (default) or as defined by the system administrator.
|
In the system administrator's option to set a trash for deleted files, these files will remain in the trash until the deletion date 30 days (default) or as defined by the system administrator.
|
||||||
|
|||||||
@@ -13,6 +13,20 @@ Immich supports multiple users, each with their own library.
|
|||||||
|
|
||||||
<UserCreate />
|
<UserCreate />
|
||||||
|
|
||||||
|
## Send new user email notification
|
||||||
|
|
||||||
|
:::note
|
||||||
|
This option is only available if an SMTP server has been configured in the administrator settings.
|
||||||
|
:::
|
||||||
|
|
||||||
|
Admin can send a welcome email if the Email option is set, you can learn here how to set up the SMTP server in Immich.
|
||||||
|
|
||||||
|
<img
|
||||||
|
src={require('./img/send-user-email-notification.webp').default}
|
||||||
|
width="40%"
|
||||||
|
title="Send user email notification"
|
||||||
|
/>
|
||||||
|
|
||||||
## Set Storage Quota For User
|
## Set Storage Quota For User
|
||||||
|
|
||||||
Admin can specify the storage quota for the user as the instance's admin; once the limit is reached, the user won't be able to upload to the instance anymore.
|
Admin can specify the storage quota for the user as the instance's admin; once the limit is reached, the user won't be able to upload to the instance anymore.
|
||||||
|
|||||||
@@ -60,17 +60,17 @@ For RKMPP to work:
|
|||||||
#### Basic Setup
|
#### Basic Setup
|
||||||
|
|
||||||
1. If you do not already have it, download the latest [`hwaccel.transcoding.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
|
1. If you do not already have it, download the latest [`hwaccel.transcoding.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
|
||||||
2. In the `docker-compose.yml` under `immich-microservices`, uncomment the `extends` section and change `cpu` to the appropriate backend.
|
2. In the `docker-compose.yml` under `immich-server`, uncomment the `extends` section and change `cpu` to the appropriate backend.
|
||||||
|
|
||||||
- For VAAPI on WSL2, be sure to use `vaapi-wsl` rather than `vaapi`
|
- For VAAPI on WSL2, be sure to use `vaapi-wsl` rather than `vaapi`
|
||||||
|
|
||||||
3. Redeploy the `immich-microservices` container with these updated settings.
|
3. Redeploy the `immich-server` container with these updated settings.
|
||||||
4. In the Admin page under `Video transcoding settings`, change the hardware acceleration setting to the appropriate option and save.
|
4. In the Admin page under `Video transcoding settings`, change the hardware acceleration setting to the appropriate option and save.
|
||||||
5. (Optional) If using a compatible backend, you may enable hardware decoding for optimal performance.
|
5. (Optional) If using a compatible backend, you may enable hardware decoding for optimal performance.
|
||||||
|
|
||||||
#### Single Compose File
|
#### Single Compose File
|
||||||
|
|
||||||
Some platforms, including Unraid and Portainer, do not support multiple Compose files as of writing. As an alternative, you can "inline" the relevant contents of the [`hwaccel.transcoding.yml`][hw-file] file into the `immich-microservices` service directly.
|
Some platforms, including Unraid and Portainer, do not support multiple Compose files as of writing. As an alternative, you can "inline" the relevant contents of the [`hwaccel.transcoding.yml`][hw-file] file into the `immich-server` service directly.
|
||||||
|
|
||||||
For example, the `qsv` section in this file is:
|
For example, the `qsv` section in this file is:
|
||||||
|
|
||||||
@@ -79,21 +79,22 @@ devices:
|
|||||||
- /dev/dri:/dev/dri
|
- /dev/dri:/dev/dri
|
||||||
```
|
```
|
||||||
|
|
||||||
You can add this to the `immich-microservices` service instead of extending from `hwaccel.transcoding.yml`:
|
You can add this to the `immich-server` service instead of extending from `hwaccel.transcoding.yml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
immich-microservices:
|
immich-server:
|
||||||
container_name: immich_microservices
|
container_name: immich_server
|
||||||
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
|
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
|
||||||
# Note the lack of an `extends` section
|
# Note the lack of an `extends` section
|
||||||
devices:
|
devices:
|
||||||
- /dev/dri:/dev/dri
|
- /dev/dri:/dev/dri
|
||||||
command: ['start.sh', 'microservices']
|
|
||||||
volumes:
|
volumes:
|
||||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
ports:
|
||||||
|
- 2283:3001
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
- database
|
- database
|
||||||
|
|||||||
BIN
docs/docs/guides/img/email-settings.png
Normal file
|
After Width: | Height: | Size: 218 KiB |
BIN
docs/docs/guides/img/google-app-password.webp
Normal file
|
After Width: | Height: | Size: 17 KiB |
20
docs/docs/guides/smtp-gmail.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# SMTP settings using Gmail
|
||||||
|
|
||||||
|
This guide walks you through how to get the information you need to set up your Immich instance to send emails using Gmail's SMTP server.
|
||||||
|
|
||||||
|
## Create an app password
|
||||||
|
|
||||||
|
From your Google account settings
|
||||||
|
|
||||||
|
- Add [2-Step Verification](https://support.google.com/accounts/answer/185839) to your Google account (Required)
|
||||||
|
- [Create an app password](https://myaccount.google.com/apppasswords).
|
||||||
|
|
||||||
|
At the end of creating your app passwords, a password will be displayed; save it, it will be used for the password field when setting up the SMTP server in Immich.
|
||||||
|
|
||||||
|
<img src={require('./img/google-app-password.webp').default} title="Authorised redirect URIs" />
|
||||||
|
|
||||||
|
## Entering the SMTP credential in Immich
|
||||||
|
|
||||||
|
Entering your credential in Immich's email notification settings at `Administration -> Settings -> Notification Settings`
|
||||||
|
|
||||||
|
<img src={require('./img/email-settings.png').default} width="80%" title="SMTP settings" />
|
||||||
@@ -38,16 +38,19 @@ Regardless of filesystem, it is not recommended to use a network share for your
|
|||||||
|
|
||||||
## General
|
## General
|
||||||
|
|
||||||
| Variable | Description | Default | Containers | Workers |
|
| Variable | Description | Default | Containers | Workers |
|
||||||
| :------------------------------ | :---------------------------------------------- | :----------------------: | :----------------------- | :----------------- |
|
| :---------------------------------- | :---------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
|
||||||
| `TZ` | Timezone | | server | microservices |
|
| `TZ` | Timezone | | 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_MEDIA_LOCATION` | Media Location | `./upload`<sup>\*1</sup> | server | api, microservices |
|
| `IMMICH_MEDIA_LOCATION` | Media Location | `./upload`<sup>\*1</sup> | server | api, microservices |
|
||||||
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
|
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
|
||||||
| `IMMICH_WEB_ROOT` | Path of root index.html | `/usr/src/app/www` | server | api |
|
| `IMMICH_WEB_ROOT` | Path of root index.html | `/usr/src/app/www` | server | api |
|
||||||
| `IMMICH_REVERSE_GEOCODING_ROOT` | Path of reverse geocoding dump directory | `/usr/src/resources` | server | microservices |
|
| `IMMICH_REVERSE_GEOCODING_ROOT` | Path of reverse geocoding dump directory | `/usr/src/resources` | server | microservices |
|
||||||
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
|
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
|
||||||
|
| `CPU_CORES` | Amount of cores available to the immich server | auto-detected cpu core count | server | |
|
||||||
|
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
|
||||||
|
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
|
||||||
|
|
||||||
\*1: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`.
|
\*1: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`.
|
||||||
It only need to be set if the Immich deployment method is changing.
|
It only need to be set if the Immich deployment method is changing.
|
||||||
|
|||||||
@@ -94,6 +94,10 @@ const config = {
|
|||||||
srcDark: 'img/immich-logo-inline-dark.png',
|
srcDark: 'img/immich-logo-inline-dark.png',
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
|
{
|
||||||
|
type: 'custom-versionSwitcher',
|
||||||
|
position: 'right',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
to: '/docs/overview/introduction',
|
to: '/docs/overview/introduction',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
|
|||||||
59
docs/src/components/version-switcher.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import '@docusaurus/theme-classic/lib/theme/Unlisted/index';
|
||||||
|
import { useWindowSize } from '@docusaurus/theme-common';
|
||||||
|
import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export default function VersionSwitcher(): JSX.Element {
|
||||||
|
const [versions, setVersions] = useState([]);
|
||||||
|
const [label, setLabel] = useState('Versions');
|
||||||
|
|
||||||
|
const windowSize = useWindowSize();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getVersions() {
|
||||||
|
try {
|
||||||
|
let baseUrl = 'https://immich.app';
|
||||||
|
if (window.location.origin === 'http://localhost:3005') {
|
||||||
|
baseUrl = window.location.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${baseUrl}/archived-versions.json`);
|
||||||
|
|
||||||
|
const archiveVersions = await response.json();
|
||||||
|
|
||||||
|
const allVersions = [
|
||||||
|
{ label: 'Next', url: 'https://main.preview.immich.app' },
|
||||||
|
{ label: 'Latest', url: 'https://immich.app' },
|
||||||
|
...archiveVersions,
|
||||||
|
];
|
||||||
|
setVersions(allVersions);
|
||||||
|
|
||||||
|
const activeVersion = allVersions.find((version) => new URL(version.url).origin === window.location.origin);
|
||||||
|
if (activeVersion) {
|
||||||
|
setLabel(activeVersion.label);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch versions', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versions.length === 0) {
|
||||||
|
getVersions();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
versions.length > 0 && (
|
||||||
|
<DropdownNavbarItem
|
||||||
|
className="navbar__item"
|
||||||
|
label={label}
|
||||||
|
mobile={windowSize === 'mobile'}
|
||||||
|
items={versions.map(({ label, url }) => ({
|
||||||
|
label,
|
||||||
|
to: url,
|
||||||
|
target: '_self',
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -63,12 +63,14 @@ import {
|
|||||||
mdiVectorCombine,
|
mdiVectorCombine,
|
||||||
mdiVideo,
|
mdiVideo,
|
||||||
mdiWeb,
|
mdiWeb,
|
||||||
|
mdiContentDuplicate,
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Item, Timeline } from '../components/timeline';
|
import { Item, Timeline } from '../components/timeline';
|
||||||
|
|
||||||
const releases = {
|
const releases = {
|
||||||
|
'v1.106.0': new Date(2024, 5, 11),
|
||||||
'v1.104.0': new Date(2024, 4, 13),
|
'v1.104.0': new Date(2024, 4, 13),
|
||||||
'v1.103.0': new Date(2024, 3, 29),
|
'v1.103.0': new Date(2024, 3, 29),
|
||||||
'v1.102.0': new Date(2024, 3, 15),
|
'v1.102.0': new Date(2024, 3, 15),
|
||||||
@@ -216,13 +218,19 @@ const roadmap: Item[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const milestones: Item[] = [
|
const milestones: Item[] = [
|
||||||
// withRelease({
|
withRelease({
|
||||||
// icon: mdiVectorCombine,
|
icon: mdiContentDuplicate,
|
||||||
// title: 'Container consolidation',
|
title: 'Similar image detection',
|
||||||
// description:
|
description: 'Detect duplicate assets that aren’t exactly identical',
|
||||||
// 'The microservices container can be run as a worker within the server image, allowing us to remove it from the default stack.',
|
release: 'v1.106.0',
|
||||||
// release: 'v1.106.0',
|
}),
|
||||||
// }),
|
withRelease({
|
||||||
|
icon: mdiVectorCombine,
|
||||||
|
title: 'Container consolidation',
|
||||||
|
description:
|
||||||
|
'The microservices container can be run as a worker within the server image, allowing us to remove it from the default stack.',
|
||||||
|
release: 'v1.106.0',
|
||||||
|
}),
|
||||||
withRelease({
|
withRelease({
|
||||||
icon: mdiPencil,
|
icon: mdiPencil,
|
||||||
iconColor: 'saddlebrown',
|
iconColor: 'saddlebrown',
|
||||||
|
|||||||
7
docs/src/theme/NavbarItem/ComponentTypes.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import ComponentTypes from '@theme-original/NavbarItem/ComponentTypes';
|
||||||
|
import VersionSwitcher from '@site/src/components/version-switcher';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...ComponentTypes,
|
||||||
|
'custom-versionSwitcher': VersionSwitcher,
|
||||||
|
};
|
||||||
58
docs/static/archived-versions.json
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"label": "v1.106.3",
|
||||||
|
"url": "https://v1.106.3.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.106.2",
|
||||||
|
"url": "https://v1.106.2.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.106.1",
|
||||||
|
"url": "https://v1.106.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.105.1",
|
||||||
|
"url": "https://v1.105.1.archive.immich.app/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.105.0",
|
||||||
|
"url": "https://v1.105.0.archive.immich.app/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.104.0",
|
||||||
|
"url": "https://v1.104.0.archive.immich.app/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.103.1",
|
||||||
|
"url": "https://v1.103.1.archive.immich.app/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.103.0",
|
||||||
|
"url": "https://v1.103.0.archive.immich.app/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.102.3",
|
||||||
|
"url": "https://v1.102.3.archive.immich.app/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.102.2",
|
||||||
|
"url": "https://v1.102.2.archive.immich.app/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.102.1",
|
||||||
|
"url": "https://v1.102.1.archive.immich.app/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.102.0",
|
||||||
|
"url": "https://v1.102.0.archive.immich.app/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.101.0",
|
||||||
|
"url": "https://v1.101.0.archive.immich.app/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.100.0",
|
||||||
|
"url": "https://v1.100.0.archive.immich.app/"
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
docs/static/img/ios-app-store-badge.png
vendored
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 70 KiB |
@@ -27,7 +27,7 @@ services:
|
|||||||
- 2283:3001
|
- 2283:3001
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:6.2-alpine@sha256:e31ca60b18f7e9b78b573d156702471d4eda038803c0b8e6f01559f350031e93
|
image: redis:6.2-alpine@sha256:d6c2911ac51b289db208767581a5d154544f2b2fe4914ea5056443f62dc6e900
|
||||||
|
|
||||||
database:
|
database:
|
||||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
|
||||||
|
|||||||
10
e2e/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.105.1",
|
"version": "1.106.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.105.1",
|
"version": "1.106.3",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@immich/cli": "file:../cli",
|
"@immich/cli": "file:../cli",
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
},
|
},
|
||||||
"../cli": {
|
"../cli": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.0",
|
"version": "2.2.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -81,14 +81,14 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.105.1",
|
"version": "1.106.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oazapfts/runtime": "^1.0.2"
|
"@oazapfts/runtime": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.12.13",
|
"@types/node": "^20.11.0",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.105.1",
|
"version": "1.106.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1148,4 +1148,29 @@ describe('/asset', () => {
|
|||||||
expect(video.checksum).toStrictEqual(checksum);
|
expect(video.checksum).toStrictEqual(checksum);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('POST /assets/exist', () => {
|
||||||
|
it('ignores invalid deviceAssetIds', async () => {
|
||||||
|
const response = await utils.checkExistingAssets(user1.accessToken, {
|
||||||
|
deviceId: 'test-assets-exist',
|
||||||
|
deviceAssetIds: ['invalid', 'INVALID'],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.existingIds).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the IDs of existing assets', async () => {
|
||||||
|
await utils.createAsset(user1.accessToken, {
|
||||||
|
deviceId: 'test-assets-exist',
|
||||||
|
deviceAssetId: 'test-asset-0',
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await utils.checkExistingAssets(user1.accessToken, {
|
||||||
|
deviceId: 'test-assets-exist',
|
||||||
|
deviceAssetIds: ['test-asset-0'],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.existingIds).toEqual(['test-asset-0']);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
import { LibraryResponseDto, LoginResponseDto, ScanLibraryDto, getAllLibraries, scanLibrary } from '@immich/sdk';
|
import {
|
||||||
|
LibraryResponseDto,
|
||||||
|
LoginResponseDto,
|
||||||
|
ScanLibraryDto,
|
||||||
|
getAllLibraries,
|
||||||
|
removeOfflineFiles,
|
||||||
|
scanLibrary,
|
||||||
|
} from '@immich/sdk';
|
||||||
import { cpSync, existsSync } from 'node:fs';
|
import { cpSync, existsSync } from 'node:fs';
|
||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
import { userDto, uuidDto } from 'src/fixtures';
|
import { userDto, uuidDto } from 'src/fixtures';
|
||||||
@@ -384,6 +391,51 @@ describe('/libraries', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not try to delete offline files', async () => {
|
||||||
|
utils.createImageFile(`${testAssetDir}/temp/offline1/assetA.png`);
|
||||||
|
|
||||||
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
|
ownerId: admin.userId,
|
||||||
|
importPaths: [`${testAssetDirInternal}/temp/offline1`],
|
||||||
|
});
|
||||||
|
|
||||||
|
await scan(admin.accessToken, library.id);
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||||
|
|
||||||
|
const { assets: initialAssets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id });
|
||||||
|
expect(initialAssets).toEqual({
|
||||||
|
count: 1,
|
||||||
|
total: 1,
|
||||||
|
facets: [],
|
||||||
|
items: [expect.objectContaining({ originalFileName: 'assetA.png' })],
|
||||||
|
nextPage: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
utils.removeImageFile(`${testAssetDir}/temp/offline1/assetA.png`);
|
||||||
|
|
||||||
|
await scan(admin.accessToken, library.id);
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||||
|
|
||||||
|
const { assets: offlineAssets } = await utils.metadataSearch(admin.accessToken, {
|
||||||
|
libraryId: library.id,
|
||||||
|
isOffline: true,
|
||||||
|
});
|
||||||
|
expect(offlineAssets).toEqual({
|
||||||
|
count: 1,
|
||||||
|
total: 1,
|
||||||
|
facets: [],
|
||||||
|
items: [expect.objectContaining({ originalFileName: 'assetA.png' })],
|
||||||
|
nextPage: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
utils.createImageFile(`${testAssetDir}/temp/offline1/assetA.png`);
|
||||||
|
await removeOfflineFiles({ id: library.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetDelete', total: 1 });
|
||||||
|
|
||||||
|
expect(existsSync(`${testAssetDir}/temp/offline1/assetA.png`)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('should scan new files', async () => {
|
it('should scan new files', async () => {
|
||||||
const library = await utils.createLibrary(admin.accessToken, {
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
ownerId: admin.userId,
|
ownerId: admin.userId,
|
||||||
@@ -507,10 +559,10 @@ describe('/libraries', () => {
|
|||||||
it('should remove offline files', async () => {
|
it('should remove offline files', async () => {
|
||||||
const library = await utils.createLibrary(admin.accessToken, {
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
ownerId: admin.userId,
|
ownerId: admin.userId,
|
||||||
importPaths: [`${testAssetDirInternal}/temp`],
|
importPaths: [`${testAssetDirInternal}/temp/offline2`],
|
||||||
});
|
});
|
||||||
|
|
||||||
utils.createImageFile(`${testAssetDir}/temp/directoryA/assetB.png`);
|
utils.createImageFile(`${testAssetDir}/temp/offline2/assetA.png`);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||||
@@ -518,9 +570,9 @@ describe('/libraries', () => {
|
|||||||
const { assets: initialAssets } = await utils.metadataSearch(admin.accessToken, {
|
const { assets: initialAssets } = await utils.metadataSearch(admin.accessToken, {
|
||||||
libraryId: library.id,
|
libraryId: library.id,
|
||||||
});
|
});
|
||||||
expect(initialAssets.count).toBe(3);
|
expect(initialAssets.count).toBe(1);
|
||||||
|
|
||||||
utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetB.png`);
|
utils.removeImageFile(`${testAssetDir}/temp/offline2/assetA.png`);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||||
@@ -541,7 +593,7 @@ describe('/libraries', () => {
|
|||||||
|
|
||||||
const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
expect(assets.count).toBe(2);
|
expect(assets.count).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not remove online files', async () => {
|
it('should not remove online files', async () => {
|
||||||
|
|||||||
@@ -5,51 +5,61 @@ export const errorDto = {
|
|||||||
error: 'Unauthorized',
|
error: 'Unauthorized',
|
||||||
statusCode: 401,
|
statusCode: 401,
|
||||||
message: 'Authentication required',
|
message: 'Authentication required',
|
||||||
|
correlationId: expect.any(String),
|
||||||
},
|
},
|
||||||
forbidden: {
|
forbidden: {
|
||||||
error: 'Forbidden',
|
error: 'Forbidden',
|
||||||
statusCode: 403,
|
statusCode: 403,
|
||||||
message: expect.any(String),
|
message: expect.any(String),
|
||||||
|
correlationId: expect.any(String),
|
||||||
},
|
},
|
||||||
wrongPassword: {
|
wrongPassword: {
|
||||||
error: 'Bad Request',
|
error: 'Bad Request',
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
message: 'Wrong password',
|
message: 'Wrong password',
|
||||||
|
correlationId: expect.any(String),
|
||||||
},
|
},
|
||||||
invalidToken: {
|
invalidToken: {
|
||||||
error: 'Unauthorized',
|
error: 'Unauthorized',
|
||||||
statusCode: 401,
|
statusCode: 401,
|
||||||
message: 'Invalid user token',
|
message: 'Invalid user token',
|
||||||
|
correlationId: expect.any(String),
|
||||||
},
|
},
|
||||||
invalidShareKey: {
|
invalidShareKey: {
|
||||||
error: 'Unauthorized',
|
error: 'Unauthorized',
|
||||||
statusCode: 401,
|
statusCode: 401,
|
||||||
message: 'Invalid share key',
|
message: 'Invalid share key',
|
||||||
|
correlationId: expect.any(String),
|
||||||
},
|
},
|
||||||
invalidSharePassword: {
|
invalidSharePassword: {
|
||||||
error: 'Unauthorized',
|
error: 'Unauthorized',
|
||||||
statusCode: 401,
|
statusCode: 401,
|
||||||
message: 'Invalid password',
|
message: 'Invalid password',
|
||||||
|
correlationId: expect.any(String),
|
||||||
},
|
},
|
||||||
badRequest: (message: any = null) => ({
|
badRequest: (message: any = null) => ({
|
||||||
error: 'Bad Request',
|
error: 'Bad Request',
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
message: message ?? expect.anything(),
|
message: message ?? expect.anything(),
|
||||||
|
correlationId: expect.any(String),
|
||||||
}),
|
}),
|
||||||
noPermission: {
|
noPermission: {
|
||||||
error: 'Bad Request',
|
error: 'Bad Request',
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
message: expect.stringContaining('Not found or no'),
|
message: expect.stringContaining('Not found or no'),
|
||||||
|
correlationId: expect.any(String),
|
||||||
},
|
},
|
||||||
incorrectLogin: {
|
incorrectLogin: {
|
||||||
error: 'Unauthorized',
|
error: 'Unauthorized',
|
||||||
statusCode: 401,
|
statusCode: 401,
|
||||||
message: 'Incorrect email or password',
|
message: 'Incorrect email or password',
|
||||||
|
correlationId: expect.any(String),
|
||||||
},
|
},
|
||||||
alreadyHasAdmin: {
|
alreadyHasAdmin: {
|
||||||
error: 'Bad Request',
|
error: 'Bad Request',
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
message: 'The server already has an admin',
|
message: 'The server already has an admin',
|
||||||
|
correlationId: expect.any(String),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
AssetMediaCreateDto,
|
AssetMediaCreateDto,
|
||||||
AssetMediaResponseDto,
|
AssetMediaResponseDto,
|
||||||
AssetResponseDto,
|
AssetResponseDto,
|
||||||
|
CheckExistingAssetsDto,
|
||||||
CreateAlbumDto,
|
CreateAlbumDto,
|
||||||
CreateLibraryDto,
|
CreateLibraryDto,
|
||||||
MetadataSearchDto,
|
MetadataSearchDto,
|
||||||
@@ -10,6 +11,7 @@ import {
|
|||||||
SharedLinkCreateDto,
|
SharedLinkCreateDto,
|
||||||
UserAdminCreateDto,
|
UserAdminCreateDto,
|
||||||
ValidateLibraryDto,
|
ValidateLibraryDto,
|
||||||
|
checkExistingAssets,
|
||||||
createAlbum,
|
createAlbum,
|
||||||
createApiKey,
|
createApiKey,
|
||||||
createLibrary,
|
createLibrary,
|
||||||
@@ -323,6 +325,40 @@ export const utils = {
|
|||||||
return body as AssetMediaResponseDto;
|
return body as AssetMediaResponseDto;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
replaceAsset: async (
|
||||||
|
accessToken: string,
|
||||||
|
assetId: string,
|
||||||
|
dto?: Partial<Omit<AssetMediaCreateDto, 'assetData'>> & { assetData?: AssetData },
|
||||||
|
) => {
|
||||||
|
const _dto = {
|
||||||
|
deviceAssetId: 'test-1',
|
||||||
|
deviceId: 'test',
|
||||||
|
fileCreatedAt: new Date().toISOString(),
|
||||||
|
fileModifiedAt: new Date().toISOString(),
|
||||||
|
...dto,
|
||||||
|
};
|
||||||
|
|
||||||
|
const assetData = dto?.assetData?.bytes || makeRandomImage();
|
||||||
|
const filename = dto?.assetData?.filename || 'example.png';
|
||||||
|
|
||||||
|
if (dto?.assetData?.bytes) {
|
||||||
|
console.log(`Uploading ${filename}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const builder = request(app)
|
||||||
|
.put(`/assets/${assetId}/original`)
|
||||||
|
.attach('assetData', assetData, filename)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`);
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(_dto)) {
|
||||||
|
void builder.field(key, String(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
const { body } = await builder;
|
||||||
|
|
||||||
|
return body as AssetMediaResponseDto;
|
||||||
|
},
|
||||||
|
|
||||||
createImageFile: (path: string) => {
|
createImageFile: (path: string) => {
|
||||||
if (!existsSync(dirname(path))) {
|
if (!existsSync(dirname(path))) {
|
||||||
mkdirSync(dirname(path), { recursive: true });
|
mkdirSync(dirname(path), { recursive: true });
|
||||||
@@ -340,6 +376,9 @@ export const utils = {
|
|||||||
|
|
||||||
getAssetInfo: (accessToken: string, id: string) => getAssetInfo({ id }, { headers: asBearerAuth(accessToken) }),
|
getAssetInfo: (accessToken: string, id: string) => getAssetInfo({ id }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
|
checkExistingAssets: (accessToken: string, checkExistingAssetsDto: CheckExistingAssetsDto) =>
|
||||||
|
checkExistingAssets({ checkExistingAssetsDto }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
metadataSearch: async (accessToken: string, dto: MetadataSearchDto) => {
|
metadataSearch: async (accessToken: string, dto: MetadataSearchDto) => {
|
||||||
return searchMetadata({ metadataSearchDto: dto }, { headers: asBearerAuth(accessToken) });
|
return searchMetadata({ metadataSearchDto: dto }, { headers: asBearerAuth(accessToken) });
|
||||||
},
|
},
|
||||||
|
|||||||
57
e2e/src/web/specs/photo-viewer.e2e-spec.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
|
||||||
|
import { Page, expect, test } from '@playwright/test';
|
||||||
|
import { utils } from 'src/utils';
|
||||||
|
|
||||||
|
function imageLocator(page: Page) {
|
||||||
|
return page.getByAltText('Image taken on').locator('visible=true');
|
||||||
|
}
|
||||||
|
test.describe('Photo Viewer', () => {
|
||||||
|
let admin: LoginResponseDto;
|
||||||
|
let asset: AssetMediaResponseDto;
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
utils.initSdk();
|
||||||
|
await utils.resetDatabase();
|
||||||
|
admin = await utils.adminSetup();
|
||||||
|
asset = await utils.createAsset(admin.accessToken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
// before each test, login as user
|
||||||
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
await page.goto('/photos');
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('initially shows a loading spinner', async ({ page }) => {
|
||||||
|
await page.route(`/api/assets/${asset.id}/thumbnail**`, async (route) => {
|
||||||
|
// slow down the request for thumbnail, so spiner has chance to show up
|
||||||
|
await new Promise((f) => setTimeout(f, 2000));
|
||||||
|
await route.continue();
|
||||||
|
});
|
||||||
|
await page.goto(`/photos/${asset.id}`);
|
||||||
|
await page.waitForLoadState('load');
|
||||||
|
// this is the spinner
|
||||||
|
await page.waitForSelector('svg[role=status]');
|
||||||
|
await expect(page.getByRole('status')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('loads high resolution photo when zoomed', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${asset.id}`);
|
||||||
|
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail');
|
||||||
|
const box = await imageLocator(page).boundingBox();
|
||||||
|
expect(box).toBeTruthy;
|
||||||
|
const { x, y, width, height } = box!;
|
||||||
|
await page.mouse.move(x + width / 2, y + height / 2);
|
||||||
|
await page.mouse.wheel(0, -1);
|
||||||
|
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('original');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reloads photo when checksum changes', async ({ page }) => {
|
||||||
|
await page.goto(`/photos/${asset.id}`);
|
||||||
|
await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail');
|
||||||
|
const initialSrc = await imageLocator(page).getAttribute('src');
|
||||||
|
await utils.replaceAsset(admin.accessToken, asset.id);
|
||||||
|
await expect.poll(async () => await imageLocator(page).getAttribute('src')).not.toBe(initialSrc);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -12,8 +12,6 @@ from rich.logging import RichHandler
|
|||||||
from uvicorn import Server
|
from uvicorn import Server
|
||||||
from uvicorn.workers import UvicornWorker
|
from uvicorn.workers import UvicornWorker
|
||||||
|
|
||||||
from .schemas import ModelType
|
|
||||||
|
|
||||||
|
|
||||||
class PreloadModelData(BaseModel):
|
class PreloadModelData(BaseModel):
|
||||||
clip: str | None
|
clip: str | None
|
||||||
@@ -21,7 +19,7 @@ class PreloadModelData(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
cache_folder: str = "/cache"
|
cache_folder: Path = Path("/cache")
|
||||||
model_ttl: int = 300
|
model_ttl: int = 300
|
||||||
model_ttl_poll_s: int = 10
|
model_ttl_poll_s: int = 10
|
||||||
host: str = "0.0.0.0"
|
host: str = "0.0.0.0"
|
||||||
@@ -55,14 +53,6 @@ def clean_name(model_name: str) -> str:
|
|||||||
return model_name.split("/")[-1].translate(_clean_name)
|
return model_name.split("/")[-1].translate(_clean_name)
|
||||||
|
|
||||||
|
|
||||||
def get_cache_dir(model_name: str, model_type: ModelType) -> Path:
|
|
||||||
return Path(settings.cache_folder) / model_type.value / clean_name(model_name)
|
|
||||||
|
|
||||||
|
|
||||||
def get_hf_model_name(model_name: str) -> str:
|
|
||||||
return f"immich-app/{clean_name(model_name)}"
|
|
||||||
|
|
||||||
|
|
||||||
LOG_LEVELS: dict[str, int] = {
|
LOG_LEVELS: dict[str, int] = {
|
||||||
"critical": logging.ERROR,
|
"critical": logging.ERROR,
|
||||||
"error": logging.ERROR,
|
"error": logging.ERROR,
|
||||||
|
|||||||
@@ -6,22 +6,34 @@ import threading
|
|||||||
import time
|
import time
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
from functools import partial
|
||||||
from typing import Any, AsyncGenerator, Callable, Iterator
|
from typing import Any, AsyncGenerator, Callable, Iterator
|
||||||
from zipfile import BadZipFile
|
from zipfile import BadZipFile
|
||||||
|
|
||||||
import orjson
|
import orjson
|
||||||
from fastapi import Depends, FastAPI, Form, HTTPException, UploadFile
|
from fastapi import Depends, FastAPI, File, Form, HTTPException
|
||||||
from fastapi.responses import ORJSONResponse
|
from fastapi.responses import ORJSONResponse
|
||||||
from onnxruntime.capi.onnxruntime_pybind11_state import InvalidProtobuf, NoSuchFile
|
from onnxruntime.capi.onnxruntime_pybind11_state import InvalidProtobuf, NoSuchFile
|
||||||
|
from PIL.Image import Image
|
||||||
|
from pydantic import ValidationError
|
||||||
from starlette.formparsers import MultiPartParser
|
from starlette.formparsers import MultiPartParser
|
||||||
|
|
||||||
|
from app.models import get_model_deps
|
||||||
from app.models.base import InferenceModel
|
from app.models.base import InferenceModel
|
||||||
|
from app.models.transforms import decode_pil
|
||||||
|
|
||||||
from .config import PreloadModelData, log, settings
|
from .config import PreloadModelData, log, settings
|
||||||
from .models.cache import ModelCache
|
from .models.cache import ModelCache
|
||||||
from .schemas import (
|
from .schemas import (
|
||||||
|
InferenceEntries,
|
||||||
|
InferenceEntry,
|
||||||
|
InferenceResponse,
|
||||||
MessageResponse,
|
MessageResponse,
|
||||||
|
ModelIdentity,
|
||||||
|
ModelTask,
|
||||||
ModelType,
|
ModelType,
|
||||||
|
PipelineRequest,
|
||||||
|
T,
|
||||||
TextResponse,
|
TextResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,12 +75,21 @@ async def lifespan(_: FastAPI) -> AsyncGenerator[None, None]:
|
|||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
async def preload_models(preload_models: PreloadModelData) -> None:
|
async def preload_models(preload: PreloadModelData) -> None:
|
||||||
log.info(f"Preloading models: {preload_models}")
|
log.info(f"Preloading models: {preload}")
|
||||||
if preload_models.clip is not None:
|
if preload.clip is not None:
|
||||||
await load(await model_cache.get(preload_models.clip, ModelType.CLIP))
|
model = await model_cache.get(preload.clip, ModelType.TEXTUAL, ModelTask.SEARCH)
|
||||||
if preload_models.facial_recognition is not None:
|
await load(model)
|
||||||
await load(await model_cache.get(preload_models.facial_recognition, ModelType.FACIAL_RECOGNITION))
|
|
||||||
|
model = await model_cache.get(preload.clip, ModelType.VISUAL, ModelTask.SEARCH)
|
||||||
|
await load(model)
|
||||||
|
|
||||||
|
if preload.facial_recognition is not None:
|
||||||
|
model = await model_cache.get(preload.facial_recognition, ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION)
|
||||||
|
await load(model)
|
||||||
|
|
||||||
|
model = await model_cache.get(preload.facial_recognition, ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION)
|
||||||
|
await load(model)
|
||||||
|
|
||||||
|
|
||||||
def update_state() -> Iterator[None]:
|
def update_state() -> Iterator[None]:
|
||||||
@@ -81,6 +102,27 @@ def update_state() -> Iterator[None]:
|
|||||||
active_requests -= 1
|
active_requests -= 1
|
||||||
|
|
||||||
|
|
||||||
|
def get_entries(entries: str = Form()) -> InferenceEntries:
|
||||||
|
try:
|
||||||
|
request: PipelineRequest = orjson.loads(entries)
|
||||||
|
without_deps: list[InferenceEntry] = []
|
||||||
|
with_deps: list[InferenceEntry] = []
|
||||||
|
for task, types in request.items():
|
||||||
|
for type, entry in types.items():
|
||||||
|
parsed: InferenceEntry = {
|
||||||
|
"name": entry["modelName"],
|
||||||
|
"task": task,
|
||||||
|
"type": type,
|
||||||
|
"options": entry.get("options", {}),
|
||||||
|
}
|
||||||
|
dep = get_model_deps(parsed["name"], type, task)
|
||||||
|
(with_deps if dep else without_deps).append(parsed)
|
||||||
|
return without_deps, with_deps
|
||||||
|
except (orjson.JSONDecodeError, ValidationError, KeyError, AttributeError) as e:
|
||||||
|
log.error(f"Invalid request format: {e}")
|
||||||
|
raise HTTPException(422, "Invalid request format.")
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(lifespan=lifespan)
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
|
|
||||||
@@ -96,42 +138,63 @@ def ping() -> str:
|
|||||||
|
|
||||||
@app.post("/predict", dependencies=[Depends(update_state)])
|
@app.post("/predict", dependencies=[Depends(update_state)])
|
||||||
async def predict(
|
async def predict(
|
||||||
model_name: str = Form(alias="modelName"),
|
entries: InferenceEntries = Depends(get_entries),
|
||||||
model_type: ModelType = Form(alias="modelType"),
|
image: bytes | None = File(default=None),
|
||||||
options: str = Form(default="{}"),
|
|
||||||
text: str | None = Form(default=None),
|
text: str | None = Form(default=None),
|
||||||
image: UploadFile | None = None,
|
|
||||||
) -> Any:
|
) -> Any:
|
||||||
if image is not None:
|
if image is not None:
|
||||||
inputs: str | bytes = await image.read()
|
inputs: Image | str = await run(lambda: decode_pil(image))
|
||||||
elif text is not None:
|
elif text is not None:
|
||||||
inputs = text
|
inputs = text
|
||||||
else:
|
else:
|
||||||
raise HTTPException(400, "Either image or text must be provided")
|
raise HTTPException(400, "Either image or text must be provided")
|
||||||
try:
|
response = await run_inference(inputs, entries)
|
||||||
kwargs = orjson.loads(options)
|
return ORJSONResponse(response)
|
||||||
except orjson.JSONDecodeError:
|
|
||||||
raise HTTPException(400, f"Invalid options JSON: {options}")
|
|
||||||
|
|
||||||
model = await load(await model_cache.get(model_name, model_type, ttl=settings.model_ttl, **kwargs))
|
|
||||||
model.configure(**kwargs)
|
|
||||||
outputs = await run(model.predict, inputs)
|
|
||||||
return ORJSONResponse(outputs)
|
|
||||||
|
|
||||||
|
|
||||||
async def run(func: Callable[..., Any], inputs: Any) -> Any:
|
async def run_inference(payload: Image | str, entries: InferenceEntries) -> InferenceResponse:
|
||||||
|
outputs: dict[ModelIdentity, Any] = {}
|
||||||
|
response: InferenceResponse = {}
|
||||||
|
|
||||||
|
async def _run_inference(entry: InferenceEntry) -> None:
|
||||||
|
model = await model_cache.get(entry["name"], entry["type"], entry["task"], ttl=settings.model_ttl)
|
||||||
|
inputs = [payload]
|
||||||
|
for dep in model.depends:
|
||||||
|
try:
|
||||||
|
inputs.append(outputs[dep])
|
||||||
|
except KeyError:
|
||||||
|
message = f"Task {entry['task']} of type {entry['type']} depends on output of {dep}"
|
||||||
|
raise HTTPException(400, message)
|
||||||
|
model = await load(model)
|
||||||
|
output = await run(model.predict, *inputs, **entry["options"])
|
||||||
|
outputs[model.identity] = output
|
||||||
|
response[entry["task"]] = output
|
||||||
|
|
||||||
|
without_deps, with_deps = entries
|
||||||
|
await asyncio.gather(*[_run_inference(entry) for entry in without_deps])
|
||||||
|
if with_deps:
|
||||||
|
await asyncio.gather(*[_run_inference(entry) for entry in with_deps])
|
||||||
|
if isinstance(payload, Image):
|
||||||
|
response["imageHeight"], response["imageWidth"] = payload.height, payload.width
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
async def run(func: Callable[..., T], *args: Any, **kwargs: Any) -> T:
|
||||||
if thread_pool is None:
|
if thread_pool is None:
|
||||||
return func(inputs)
|
return func(*args, **kwargs)
|
||||||
return await asyncio.get_running_loop().run_in_executor(thread_pool, func, inputs)
|
partial_func = partial(func, *args, **kwargs)
|
||||||
|
return await asyncio.get_running_loop().run_in_executor(thread_pool, partial_func)
|
||||||
|
|
||||||
|
|
||||||
async def load(model: InferenceModel) -> InferenceModel:
|
async def load(model: InferenceModel) -> InferenceModel:
|
||||||
if model.loaded:
|
if model.loaded:
|
||||||
return model
|
return model
|
||||||
|
|
||||||
def _load(model: InferenceModel) -> None:
|
def _load(model: InferenceModel) -> InferenceModel:
|
||||||
with lock:
|
with lock:
|
||||||
model.load()
|
model.load()
|
||||||
|
return model
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await run(_load, model)
|
await run(_load, model)
|
||||||
|
|||||||
@@ -1,24 +1,40 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app.schemas import ModelType
|
from app.models.base import InferenceModel
|
||||||
|
from app.models.clip.textual import MClipTextualEncoder, OpenClipTextualEncoder
|
||||||
|
from app.models.clip.visual import OpenClipVisualEncoder
|
||||||
|
from app.schemas import ModelSource, ModelTask, ModelType
|
||||||
|
|
||||||
from .base import InferenceModel
|
from .constants import get_model_source
|
||||||
from .clip import MCLIPEncoder, OpenCLIPEncoder
|
from .facial_recognition.detection import FaceDetector
|
||||||
from .constants import is_insightface, is_mclip, is_openclip
|
from .facial_recognition.recognition import FaceRecognizer
|
||||||
from .facial_recognition import FaceRecognizer
|
|
||||||
|
|
||||||
|
|
||||||
def from_model_type(model_type: ModelType, model_name: str, **model_kwargs: Any) -> InferenceModel:
|
def get_model_class(model_name: str, model_type: ModelType, model_task: ModelTask) -> type[InferenceModel]:
|
||||||
match model_type:
|
source = get_model_source(model_name)
|
||||||
case ModelType.CLIP:
|
match source, model_type, model_task:
|
||||||
if is_openclip(model_name):
|
case ModelSource.OPENCLIP | ModelSource.MCLIP, ModelType.VISUAL, ModelTask.SEARCH:
|
||||||
return OpenCLIPEncoder(model_name, **model_kwargs)
|
return OpenClipVisualEncoder
|
||||||
elif is_mclip(model_name):
|
|
||||||
return MCLIPEncoder(model_name, **model_kwargs)
|
case ModelSource.OPENCLIP, ModelType.TEXTUAL, ModelTask.SEARCH:
|
||||||
case ModelType.FACIAL_RECOGNITION:
|
return OpenClipTextualEncoder
|
||||||
if is_insightface(model_name):
|
|
||||||
return FaceRecognizer(model_name, **model_kwargs)
|
case ModelSource.MCLIP, ModelType.TEXTUAL, ModelTask.SEARCH:
|
||||||
|
return MClipTextualEncoder
|
||||||
|
|
||||||
|
case ModelSource.INSIGHTFACE, ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION:
|
||||||
|
return FaceDetector
|
||||||
|
|
||||||
|
case ModelSource.INSIGHTFACE, ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION:
|
||||||
|
return FaceRecognizer
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
raise ValueError(f"Unknown model type {model_type}")
|
raise ValueError(f"Unknown model combination: {source}, {model_type}, {model_task}")
|
||||||
|
|
||||||
raise ValueError(f"Unknown {model_type} model {model_name}")
|
|
||||||
|
def from_model_type(model_name: str, model_type: ModelType, model_task: ModelTask, **kwargs: Any) -> InferenceModel:
|
||||||
|
return get_model_class(model_name, model_type, model_task)(model_name, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def get_model_deps(model_name: str, model_type: ModelType, model_task: ModelTask) -> list[tuple[ModelType, ModelTask]]:
|
||||||
|
return get_model_class(model_name, model_type, model_task).depends
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
from typing import Any
|
from typing import Any, ClassVar
|
||||||
|
|
||||||
import onnxruntime as ort
|
import onnxruntime as ort
|
||||||
from huggingface_hub import snapshot_download
|
from huggingface_hub import snapshot_download
|
||||||
@@ -11,13 +11,14 @@ from huggingface_hub import snapshot_download
|
|||||||
import ann.ann
|
import ann.ann
|
||||||
from app.models.constants import SUPPORTED_PROVIDERS
|
from app.models.constants import SUPPORTED_PROVIDERS
|
||||||
|
|
||||||
from ..config import get_cache_dir, get_hf_model_name, log, settings
|
from ..config import clean_name, log, settings
|
||||||
from ..schemas import ModelRuntime, ModelType
|
from ..schemas import ModelFormat, ModelIdentity, ModelSession, ModelTask, ModelType
|
||||||
from .ann import AnnSession
|
from .ann import AnnSession
|
||||||
|
|
||||||
|
|
||||||
class InferenceModel(ABC):
|
class InferenceModel(ABC):
|
||||||
_model_type: ModelType
|
depends: ClassVar[list[ModelIdentity]]
|
||||||
|
identity: ClassVar[ModelIdentity]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -26,16 +27,16 @@ class InferenceModel(ABC):
|
|||||||
providers: list[str] | None = None,
|
providers: list[str] | None = None,
|
||||||
provider_options: list[dict[str, Any]] | None = None,
|
provider_options: list[dict[str, Any]] | None = None,
|
||||||
sess_options: ort.SessionOptions | None = None,
|
sess_options: ort.SessionOptions | None = None,
|
||||||
preferred_runtime: ModelRuntime | None = None,
|
preferred_format: ModelFormat | None = None,
|
||||||
**model_kwargs: Any,
|
**model_kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.loaded = False
|
self.loaded = False
|
||||||
self.model_name = model_name
|
self.model_name = clean_name(model_name)
|
||||||
self.cache_dir = Path(cache_dir) if cache_dir is not None else self.cache_dir_default
|
self.cache_dir = Path(cache_dir) if cache_dir is not None else self.cache_dir_default
|
||||||
self.providers = providers if providers is not None else self.providers_default
|
self.providers = providers if providers is not None else self.providers_default
|
||||||
self.provider_options = provider_options if provider_options is not None else self.provider_options_default
|
self.provider_options = provider_options if provider_options is not None else self.provider_options_default
|
||||||
self.sess_options = sess_options if sess_options is not None else self.sess_options_default
|
self.sess_options = sess_options if sess_options is not None else self.sess_options_default
|
||||||
self.preferred_runtime = preferred_runtime if preferred_runtime is not None else self.preferred_runtime_default
|
self.preferred_format = preferred_format if preferred_format is not None else self.preferred_format_default
|
||||||
|
|
||||||
def download(self) -> None:
|
def download(self) -> None:
|
||||||
if not self.cached:
|
if not self.cached:
|
||||||
@@ -47,35 +48,36 @@ class InferenceModel(ABC):
|
|||||||
def load(self) -> None:
|
def load(self) -> None:
|
||||||
if self.loaded:
|
if self.loaded:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.download()
|
self.download()
|
||||||
log.info(f"Loading {self.model_type.replace('-', ' ')} model '{self.model_name}' to memory")
|
log.info(f"Loading {self.model_type.replace('-', ' ')} model '{self.model_name}' to memory")
|
||||||
self._load()
|
self.session = self._load()
|
||||||
self.loaded = True
|
self.loaded = True
|
||||||
|
|
||||||
def predict(self, inputs: Any, **model_kwargs: Any) -> Any:
|
def predict(self, *inputs: Any, **model_kwargs: Any) -> Any:
|
||||||
self.load()
|
self.load()
|
||||||
if model_kwargs:
|
if model_kwargs:
|
||||||
self.configure(**model_kwargs)
|
self.configure(**model_kwargs)
|
||||||
return self._predict(inputs)
|
return self._predict(*inputs, **model_kwargs)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def _predict(self, inputs: Any) -> Any: ...
|
def _predict(self, *inputs: Any, **model_kwargs: Any) -> Any: ...
|
||||||
|
|
||||||
def configure(self, **model_kwargs: Any) -> None:
|
def configure(self, **kwargs: Any) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _download(self) -> None:
|
def _download(self) -> None:
|
||||||
ignore_patterns = [] if self.preferred_runtime == ModelRuntime.ARMNN else ["*.armnn"]
|
ignore_patterns = [] if self.preferred_format == ModelFormat.ARMNN else ["*.armnn"]
|
||||||
snapshot_download(
|
snapshot_download(
|
||||||
get_hf_model_name(self.model_name),
|
f"immich-app/{clean_name(self.model_name)}",
|
||||||
cache_dir=self.cache_dir,
|
cache_dir=self.cache_dir,
|
||||||
local_dir=self.cache_dir,
|
local_dir=self.cache_dir,
|
||||||
local_dir_use_symlinks=False,
|
local_dir_use_symlinks=False,
|
||||||
ignore_patterns=ignore_patterns,
|
ignore_patterns=ignore_patterns,
|
||||||
)
|
)
|
||||||
|
|
||||||
@abstractmethod
|
def _load(self) -> ModelSession:
|
||||||
def _load(self) -> None: ...
|
return self._make_session(self.model_path)
|
||||||
|
|
||||||
def clear_cache(self) -> None:
|
def clear_cache(self) -> None:
|
||||||
if not self.cache_dir.exists():
|
if not self.cache_dir.exists():
|
||||||
@@ -99,7 +101,7 @@ class InferenceModel(ABC):
|
|||||||
self.cache_dir.unlink()
|
self.cache_dir.unlink()
|
||||||
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
def _make_session(self, model_path: Path) -> AnnSession | ort.InferenceSession:
|
def _make_session(self, model_path: Path) -> ModelSession:
|
||||||
if not model_path.is_file():
|
if not model_path.is_file():
|
||||||
onnx_path = model_path.with_suffix(".onnx")
|
onnx_path = model_path.with_suffix(".onnx")
|
||||||
if not onnx_path.is_file():
|
if not onnx_path.is_file():
|
||||||
@@ -124,9 +126,21 @@ class InferenceModel(ABC):
|
|||||||
raise ValueError(f"Unsupported model file type: {model_path.suffix}")
|
raise ValueError(f"Unsupported model file type: {model_path.suffix}")
|
||||||
return session
|
return session
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model_dir(self) -> Path:
|
||||||
|
return self.cache_dir / self.model_type.value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model_path(self) -> Path:
|
||||||
|
return self.model_dir / f"model.{self.preferred_format}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model_task(self) -> ModelTask:
|
||||||
|
return self.identity[1]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def model_type(self) -> ModelType:
|
def model_type(self) -> ModelType:
|
||||||
return self._model_type
|
return self.identity[0]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cache_dir(self) -> Path:
|
def cache_dir(self) -> Path:
|
||||||
@@ -138,11 +152,11 @@ class InferenceModel(ABC):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def cache_dir_default(self) -> Path:
|
def cache_dir_default(self) -> Path:
|
||||||
return get_cache_dir(self.model_name, self.model_type)
|
return settings.cache_folder / self.model_task.value / self.model_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cached(self) -> bool:
|
def cached(self) -> bool:
|
||||||
return self.cache_dir.is_dir() and any(self.cache_dir.iterdir())
|
return self.model_path.is_file()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def providers(self) -> list[str]:
|
def providers(self) -> list[str]:
|
||||||
@@ -226,14 +240,14 @@ class InferenceModel(ABC):
|
|||||||
return sess_options
|
return sess_options
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preferred_runtime(self) -> ModelRuntime:
|
def preferred_format(self) -> ModelFormat:
|
||||||
return self._preferred_runtime
|
return self._preferred_format
|
||||||
|
|
||||||
@preferred_runtime.setter
|
@preferred_format.setter
|
||||||
def preferred_runtime(self, preferred_runtime: ModelRuntime) -> None:
|
def preferred_format(self, preferred_format: ModelFormat) -> None:
|
||||||
log.debug(f"Setting preferred runtime to {preferred_runtime}")
|
log.debug(f"Setting preferred format to {preferred_format}")
|
||||||
self._preferred_runtime = preferred_runtime
|
self._preferred_format = preferred_format
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preferred_runtime_default(self) -> ModelRuntime:
|
def preferred_format_default(self) -> ModelFormat:
|
||||||
return ModelRuntime.ARMNN if ann.ann.is_available and settings.ann else ModelRuntime.ONNX
|
return ModelFormat.ARMNN if ann.ann.is_available and settings.ann else ModelFormat.ONNX
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ from aiocache.lock import OptimisticLock
|
|||||||
from aiocache.plugins import TimingPlugin
|
from aiocache.plugins import TimingPlugin
|
||||||
|
|
||||||
from app.models import from_model_type
|
from app.models import from_model_type
|
||||||
|
from app.models.base import InferenceModel
|
||||||
|
|
||||||
from ..schemas import ModelType, has_profiling
|
from ..schemas import ModelTask, ModelType, has_profiling
|
||||||
from .base import InferenceModel
|
|
||||||
|
|
||||||
|
|
||||||
class ModelCache:
|
class ModelCache:
|
||||||
@@ -31,28 +31,21 @@ class ModelCache:
|
|||||||
if profiling:
|
if profiling:
|
||||||
plugins.append(TimingPlugin())
|
plugins.append(TimingPlugin())
|
||||||
|
|
||||||
self.revalidate_enable = revalidate
|
self.should_revalidate = revalidate
|
||||||
|
|
||||||
self.cache = SimpleMemoryCache(timeout=timeout, plugins=plugins, namespace=None)
|
self.cache = SimpleMemoryCache(timeout=timeout, plugins=plugins, namespace=None)
|
||||||
|
|
||||||
async def get(self, model_name: str, model_type: ModelType, **model_kwargs: Any) -> InferenceModel:
|
async def get(
|
||||||
"""
|
self, model_name: str, model_type: ModelType, model_task: ModelTask, **model_kwargs: Any
|
||||||
Args:
|
) -> InferenceModel:
|
||||||
model_name: Name of model in the model hub used for the task.
|
key = f"{model_name}{model_type}{model_task}"
|
||||||
model_type: Model type or task, which determines which model zoo is used.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
model: The requested model.
|
|
||||||
"""
|
|
||||||
|
|
||||||
key = f"{model_name}{model_type.value}{model_kwargs.get('mode', '')}"
|
|
||||||
|
|
||||||
async with OptimisticLock(self.cache, key) as lock:
|
async with OptimisticLock(self.cache, key) as lock:
|
||||||
model: InferenceModel | None = await self.cache.get(key)
|
model: InferenceModel | None = await self.cache.get(key)
|
||||||
if model is None:
|
if model is None:
|
||||||
model = from_model_type(model_type, model_name, **model_kwargs)
|
model = from_model_type(model_name, model_type, model_task, **model_kwargs)
|
||||||
await lock.cas(model, ttl=model_kwargs.get("ttl", None))
|
await lock.cas(model, ttl=model_kwargs.get("ttl", None))
|
||||||
elif self.revalidate_enable:
|
elif self.should_revalidate:
|
||||||
await self.revalidate(key, model_kwargs.get("ttl", None))
|
await self.revalidate(key, model_kwargs.get("ttl", None))
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
|||||||
@@ -1,189 +0,0 @@
|
|||||||
import json
|
|
||||||
from abc import abstractmethod
|
|
||||||
from functools import cached_property
|
|
||||||
from io import BytesIO
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Literal
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
from numpy.typing import NDArray
|
|
||||||
from PIL import Image
|
|
||||||
from tokenizers import Encoding, Tokenizer
|
|
||||||
|
|
||||||
from app.config import clean_name, log
|
|
||||||
from app.models.transforms import crop, get_pil_resampling, normalize, resize, to_numpy
|
|
||||||
from app.schemas import ModelType
|
|
||||||
|
|
||||||
from .base import InferenceModel
|
|
||||||
|
|
||||||
|
|
||||||
class BaseCLIPEncoder(InferenceModel):
|
|
||||||
_model_type = ModelType.CLIP
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
model_name: str,
|
|
||||||
cache_dir: Path | str | None = None,
|
|
||||||
mode: Literal["text", "vision"] | None = None,
|
|
||||||
**model_kwargs: Any,
|
|
||||||
) -> None:
|
|
||||||
self.mode = mode
|
|
||||||
super().__init__(model_name, cache_dir, **model_kwargs)
|
|
||||||
|
|
||||||
def _load(self) -> None:
|
|
||||||
if self.mode == "text" or self.mode is None:
|
|
||||||
log.debug(f"Loading clip text model '{self.model_name}'")
|
|
||||||
self.text_model = self._make_session(self.textual_path)
|
|
||||||
log.debug(f"Loaded clip text model '{self.model_name}'")
|
|
||||||
|
|
||||||
if self.mode == "vision" or self.mode is None:
|
|
||||||
log.debug(f"Loading clip vision model '{self.model_name}'")
|
|
||||||
self.vision_model = self._make_session(self.visual_path)
|
|
||||||
log.debug(f"Loaded clip vision model '{self.model_name}'")
|
|
||||||
|
|
||||||
def _predict(self, image_or_text: Image.Image | str) -> NDArray[np.float32]:
|
|
||||||
if isinstance(image_or_text, bytes):
|
|
||||||
image_or_text = Image.open(BytesIO(image_or_text))
|
|
||||||
|
|
||||||
match image_or_text:
|
|
||||||
case Image.Image():
|
|
||||||
if self.mode == "text":
|
|
||||||
raise TypeError("Cannot encode image as text-only model")
|
|
||||||
outputs: NDArray[np.float32] = self.vision_model.run(None, self.transform(image_or_text))[0][0]
|
|
||||||
case str():
|
|
||||||
if self.mode == "vision":
|
|
||||||
raise TypeError("Cannot encode text as vision-only model")
|
|
||||||
outputs = self.text_model.run(None, self.tokenize(image_or_text))[0][0]
|
|
||||||
case _:
|
|
||||||
raise TypeError(f"Expected Image or str, but got: {type(image_or_text)}")
|
|
||||||
|
|
||||||
return outputs
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def tokenize(self, text: str) -> dict[str, NDArray[np.int32]]:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def transform(self, image: Image.Image) -> dict[str, NDArray[np.float32]]:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
|
||||||
def textual_dir(self) -> Path:
|
|
||||||
return self.cache_dir / "textual"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def visual_dir(self) -> Path:
|
|
||||||
return self.cache_dir / "visual"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def model_cfg_path(self) -> Path:
|
|
||||||
return self.cache_dir / "config.json"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def textual_path(self) -> Path:
|
|
||||||
return self.textual_dir / f"model.{self.preferred_runtime}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def visual_path(self) -> Path:
|
|
||||||
return self.visual_dir / f"model.{self.preferred_runtime}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def tokenizer_file_path(self) -> Path:
|
|
||||||
return self.textual_dir / "tokenizer.json"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def tokenizer_cfg_path(self) -> Path:
|
|
||||||
return self.textual_dir / "tokenizer_config.json"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def preprocess_cfg_path(self) -> Path:
|
|
||||||
return self.visual_dir / "preprocess_cfg.json"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def cached(self) -> bool:
|
|
||||||
return self.textual_path.is_file() and self.visual_path.is_file()
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def model_cfg(self) -> dict[str, Any]:
|
|
||||||
log.debug(f"Loading model config for CLIP model '{self.model_name}'")
|
|
||||||
model_cfg: dict[str, Any] = json.load(self.model_cfg_path.open())
|
|
||||||
log.debug(f"Loaded model config for CLIP model '{self.model_name}'")
|
|
||||||
return model_cfg
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def tokenizer_file(self) -> dict[str, Any]:
|
|
||||||
log.debug(f"Loading tokenizer file for CLIP model '{self.model_name}'")
|
|
||||||
tokenizer_file: dict[str, Any] = json.load(self.tokenizer_file_path.open())
|
|
||||||
log.debug(f"Loaded tokenizer file for CLIP model '{self.model_name}'")
|
|
||||||
return tokenizer_file
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def tokenizer_cfg(self) -> dict[str, Any]:
|
|
||||||
log.debug(f"Loading tokenizer config for CLIP model '{self.model_name}'")
|
|
||||||
tokenizer_cfg: dict[str, Any] = json.load(self.tokenizer_cfg_path.open())
|
|
||||||
log.debug(f"Loaded tokenizer config for CLIP model '{self.model_name}'")
|
|
||||||
return tokenizer_cfg
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def preprocess_cfg(self) -> dict[str, Any]:
|
|
||||||
log.debug(f"Loading visual preprocessing config for CLIP model '{self.model_name}'")
|
|
||||||
preprocess_cfg: dict[str, Any] = json.load(self.preprocess_cfg_path.open())
|
|
||||||
log.debug(f"Loaded visual preprocessing config for CLIP model '{self.model_name}'")
|
|
||||||
return preprocess_cfg
|
|
||||||
|
|
||||||
|
|
||||||
class OpenCLIPEncoder(BaseCLIPEncoder):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
model_name: str,
|
|
||||||
cache_dir: Path | str | None = None,
|
|
||||||
mode: Literal["text", "vision"] | None = None,
|
|
||||||
**model_kwargs: Any,
|
|
||||||
) -> None:
|
|
||||||
super().__init__(clean_name(model_name), cache_dir, mode, **model_kwargs)
|
|
||||||
|
|
||||||
def _load(self) -> None:
|
|
||||||
super()._load()
|
|
||||||
self._load_tokenizer()
|
|
||||||
|
|
||||||
size: list[int] | int = self.preprocess_cfg["size"]
|
|
||||||
self.size = size[0] if isinstance(size, list) else size
|
|
||||||
|
|
||||||
self.resampling = get_pil_resampling(self.preprocess_cfg["interpolation"])
|
|
||||||
self.mean = np.array(self.preprocess_cfg["mean"], dtype=np.float32)
|
|
||||||
self.std = np.array(self.preprocess_cfg["std"], dtype=np.float32)
|
|
||||||
|
|
||||||
def _load_tokenizer(self) -> Tokenizer:
|
|
||||||
log.debug(f"Loading tokenizer for CLIP model '{self.model_name}'")
|
|
||||||
|
|
||||||
text_cfg: dict[str, Any] = self.model_cfg["text_cfg"]
|
|
||||||
context_length: int = text_cfg.get("context_length", 77)
|
|
||||||
pad_token: str = self.tokenizer_cfg["pad_token"]
|
|
||||||
|
|
||||||
self.tokenizer: Tokenizer = Tokenizer.from_file(self.tokenizer_file_path.as_posix())
|
|
||||||
|
|
||||||
pad_id: int = self.tokenizer.token_to_id(pad_token)
|
|
||||||
self.tokenizer.enable_padding(length=context_length, pad_token=pad_token, pad_id=pad_id)
|
|
||||||
self.tokenizer.enable_truncation(max_length=context_length)
|
|
||||||
|
|
||||||
log.debug(f"Loaded tokenizer for CLIP model '{self.model_name}'")
|
|
||||||
|
|
||||||
def tokenize(self, text: str) -> dict[str, NDArray[np.int32]]:
|
|
||||||
tokens: Encoding = self.tokenizer.encode(text)
|
|
||||||
return {"text": np.array([tokens.ids], dtype=np.int32)}
|
|
||||||
|
|
||||||
def transform(self, image: Image.Image) -> dict[str, NDArray[np.float32]]:
|
|
||||||
image = resize(image, self.size)
|
|
||||||
image = crop(image, self.size)
|
|
||||||
image_np = to_numpy(image)
|
|
||||||
image_np = normalize(image_np, self.mean, self.std)
|
|
||||||
return {"image": np.expand_dims(image_np.transpose(2, 0, 1), 0)}
|
|
||||||
|
|
||||||
|
|
||||||
class MCLIPEncoder(OpenCLIPEncoder):
|
|
||||||
def tokenize(self, text: str) -> dict[str, NDArray[np.int32]]:
|
|
||||||
tokens: Encoding = self.tokenizer.encode(text)
|
|
||||||
return {
|
|
||||||
"input_ids": np.array([tokens.ids], dtype=np.int32),
|
|
||||||
"attention_mask": np.array([tokens.attention_mask], dtype=np.int32),
|
|
||||||
}
|
|
||||||
98
machine-learning/app/models/clip/textual.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import json
|
||||||
|
from abc import abstractmethod
|
||||||
|
from functools import cached_property
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from numpy.typing import NDArray
|
||||||
|
from tokenizers import Encoding, Tokenizer
|
||||||
|
|
||||||
|
from app.config import log
|
||||||
|
from app.models.base import InferenceModel
|
||||||
|
from app.schemas import ModelSession, ModelTask, ModelType
|
||||||
|
|
||||||
|
|
||||||
|
class BaseCLIPTextualEncoder(InferenceModel):
|
||||||
|
depends = []
|
||||||
|
identity = (ModelType.TEXTUAL, ModelTask.SEARCH)
|
||||||
|
|
||||||
|
def _predict(self, inputs: str, **kwargs: Any) -> NDArray[np.float32]:
|
||||||
|
res: NDArray[np.float32] = self.session.run(None, self.tokenize(inputs))[0][0]
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _load(self) -> ModelSession:
|
||||||
|
log.debug(f"Loading tokenizer for CLIP model '{self.model_name}'")
|
||||||
|
self.tokenizer = self._load_tokenizer()
|
||||||
|
log.debug(f"Loaded tokenizer for CLIP model '{self.model_name}'")
|
||||||
|
|
||||||
|
return super()._load()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _load_tokenizer(self) -> Tokenizer:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def tokenize(self, text: str) -> dict[str, NDArray[np.int32]]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model_cfg_path(self) -> Path:
|
||||||
|
return self.cache_dir / "config.json"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tokenizer_file_path(self) -> Path:
|
||||||
|
return self.model_dir / "tokenizer.json"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tokenizer_cfg_path(self) -> Path:
|
||||||
|
return self.model_dir / "tokenizer_config.json"
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def model_cfg(self) -> dict[str, Any]:
|
||||||
|
log.debug(f"Loading model config for CLIP model '{self.model_name}'")
|
||||||
|
model_cfg: dict[str, Any] = json.load(self.model_cfg_path.open())
|
||||||
|
log.debug(f"Loaded model config for CLIP model '{self.model_name}'")
|
||||||
|
return model_cfg
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def tokenizer_file(self) -> dict[str, Any]:
|
||||||
|
log.debug(f"Loading tokenizer file for CLIP model '{self.model_name}'")
|
||||||
|
tokenizer_file: dict[str, Any] = json.load(self.tokenizer_file_path.open())
|
||||||
|
log.debug(f"Loaded tokenizer file for CLIP model '{self.model_name}'")
|
||||||
|
return tokenizer_file
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def tokenizer_cfg(self) -> dict[str, Any]:
|
||||||
|
log.debug(f"Loading tokenizer config for CLIP model '{self.model_name}'")
|
||||||
|
tokenizer_cfg: dict[str, Any] = json.load(self.tokenizer_cfg_path.open())
|
||||||
|
log.debug(f"Loaded tokenizer config for CLIP model '{self.model_name}'")
|
||||||
|
return tokenizer_cfg
|
||||||
|
|
||||||
|
|
||||||
|
class OpenClipTextualEncoder(BaseCLIPTextualEncoder):
|
||||||
|
def _load_tokenizer(self) -> Tokenizer:
|
||||||
|
text_cfg: dict[str, Any] = self.model_cfg["text_cfg"]
|
||||||
|
context_length: int = text_cfg.get("context_length", 77)
|
||||||
|
pad_token: str = self.tokenizer_cfg["pad_token"]
|
||||||
|
|
||||||
|
tokenizer: Tokenizer = Tokenizer.from_file(self.tokenizer_file_path.as_posix())
|
||||||
|
|
||||||
|
pad_id: int = tokenizer.token_to_id(pad_token)
|
||||||
|
tokenizer.enable_padding(length=context_length, pad_token=pad_token, pad_id=pad_id)
|
||||||
|
tokenizer.enable_truncation(max_length=context_length)
|
||||||
|
|
||||||
|
return tokenizer
|
||||||
|
|
||||||
|
def tokenize(self, text: str) -> dict[str, NDArray[np.int32]]:
|
||||||
|
tokens: Encoding = self.tokenizer.encode(text)
|
||||||
|
return {"text": np.array([tokens.ids], dtype=np.int32)}
|
||||||
|
|
||||||
|
|
||||||
|
class MClipTextualEncoder(OpenClipTextualEncoder):
|
||||||
|
def tokenize(self, text: str) -> dict[str, NDArray[np.int32]]:
|
||||||
|
tokens: Encoding = self.tokenizer.encode(text)
|
||||||
|
return {
|
||||||
|
"input_ids": np.array([tokens.ids], dtype=np.int32),
|
||||||
|
"attention_mask": np.array([tokens.attention_mask], dtype=np.int32),
|
||||||
|
}
|
||||||
69
machine-learning/app/models/clip/visual.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import json
|
||||||
|
from abc import abstractmethod
|
||||||
|
from functools import cached_property
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from numpy.typing import NDArray
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from app.config import log
|
||||||
|
from app.models.base import InferenceModel
|
||||||
|
from app.models.transforms import crop_pil, decode_pil, get_pil_resampling, normalize, resize_pil, to_numpy
|
||||||
|
from app.schemas import ModelSession, ModelTask, ModelType
|
||||||
|
|
||||||
|
|
||||||
|
class BaseCLIPVisualEncoder(InferenceModel):
|
||||||
|
depends = []
|
||||||
|
identity = (ModelType.VISUAL, ModelTask.SEARCH)
|
||||||
|
|
||||||
|
def _predict(self, inputs: Image.Image | bytes, **kwargs: Any) -> NDArray[np.float32]:
|
||||||
|
image = decode_pil(inputs)
|
||||||
|
res: NDArray[np.float32] = self.session.run(None, self.transform(image))[0][0]
|
||||||
|
return res
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def transform(self, image: Image.Image) -> dict[str, NDArray[np.float32]]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model_cfg_path(self) -> Path:
|
||||||
|
return self.cache_dir / "config.json"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def preprocess_cfg_path(self) -> Path:
|
||||||
|
return self.model_dir / "preprocess_cfg.json"
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def model_cfg(self) -> dict[str, Any]:
|
||||||
|
log.debug(f"Loading model config for CLIP model '{self.model_name}'")
|
||||||
|
model_cfg: dict[str, Any] = json.load(self.model_cfg_path.open())
|
||||||
|
log.debug(f"Loaded model config for CLIP model '{self.model_name}'")
|
||||||
|
return model_cfg
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def preprocess_cfg(self) -> dict[str, Any]:
|
||||||
|
log.debug(f"Loading visual preprocessing config for CLIP model '{self.model_name}'")
|
||||||
|
preprocess_cfg: dict[str, Any] = json.load(self.preprocess_cfg_path.open())
|
||||||
|
log.debug(f"Loaded visual preprocessing config for CLIP model '{self.model_name}'")
|
||||||
|
return preprocess_cfg
|
||||||
|
|
||||||
|
|
||||||
|
class OpenClipVisualEncoder(BaseCLIPVisualEncoder):
|
||||||
|
def _load(self) -> ModelSession:
|
||||||
|
size: list[int] | int = self.preprocess_cfg["size"]
|
||||||
|
self.size = size[0] if isinstance(size, list) else size
|
||||||
|
|
||||||
|
self.resampling = get_pil_resampling(self.preprocess_cfg["interpolation"])
|
||||||
|
self.mean = np.array(self.preprocess_cfg["mean"], dtype=np.float32)
|
||||||
|
self.std = np.array(self.preprocess_cfg["std"], dtype=np.float32)
|
||||||
|
|
||||||
|
return super()._load()
|
||||||
|
|
||||||
|
def transform(self, image: Image.Image) -> dict[str, NDArray[np.float32]]:
|
||||||
|
image = resize_pil(image, self.size)
|
||||||
|
image = crop_pil(image, self.size)
|
||||||
|
image_np = to_numpy(image)
|
||||||
|
image_np = normalize(image_np, self.mean, self.std)
|
||||||
|
return {"image": np.expand_dims(image_np.transpose(2, 0, 1), 0)}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
from app.config import clean_name
|
from app.config import clean_name
|
||||||
|
from app.schemas import ModelSource
|
||||||
|
|
||||||
_OPENCLIP_MODELS = {
|
_OPENCLIP_MODELS = {
|
||||||
"RN50__openai",
|
"RN50__openai",
|
||||||
@@ -54,13 +55,16 @@ _INSIGHTFACE_MODELS = {
|
|||||||
SUPPORTED_PROVIDERS = ["CUDAExecutionProvider", "OpenVINOExecutionProvider", "CPUExecutionProvider"]
|
SUPPORTED_PROVIDERS = ["CUDAExecutionProvider", "OpenVINOExecutionProvider", "CPUExecutionProvider"]
|
||||||
|
|
||||||
|
|
||||||
def is_openclip(model_name: str) -> bool:
|
def get_model_source(model_name: str) -> ModelSource | None:
|
||||||
return clean_name(model_name) in _OPENCLIP_MODELS
|
cleaned_name = clean_name(model_name)
|
||||||
|
|
||||||
|
if cleaned_name in _INSIGHTFACE_MODELS:
|
||||||
|
return ModelSource.INSIGHTFACE
|
||||||
|
|
||||||
def is_mclip(model_name: str) -> bool:
|
if cleaned_name in _MCLIP_MODELS:
|
||||||
return clean_name(model_name) in _MCLIP_MODELS
|
return ModelSource.MCLIP
|
||||||
|
|
||||||
|
if cleaned_name in _OPENCLIP_MODELS:
|
||||||
|
return ModelSource.OPENCLIP
|
||||||
|
|
||||||
def is_insightface(model_name: str) -> bool:
|
return None
|
||||||
return clean_name(model_name) in _INSIGHTFACE_MODELS
|
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
from pathlib import Path
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
import cv2
|
|
||||||
import numpy as np
|
|
||||||
from insightface.model_zoo import ArcFaceONNX, RetinaFace
|
|
||||||
from insightface.utils.face_align import norm_crop
|
|
||||||
from numpy.typing import NDArray
|
|
||||||
|
|
||||||
from app.config import clean_name
|
|
||||||
from app.schemas import Face, ModelType, is_ndarray
|
|
||||||
|
|
||||||
from .base import InferenceModel
|
|
||||||
|
|
||||||
|
|
||||||
class FaceRecognizer(InferenceModel):
|
|
||||||
_model_type = ModelType.FACIAL_RECOGNITION
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
model_name: str,
|
|
||||||
min_score: float = 0.7,
|
|
||||||
cache_dir: Path | str | None = None,
|
|
||||||
**model_kwargs: Any,
|
|
||||||
) -> None:
|
|
||||||
self.min_score = model_kwargs.pop("minScore", min_score)
|
|
||||||
super().__init__(clean_name(model_name), cache_dir, **model_kwargs)
|
|
||||||
|
|
||||||
def _load(self) -> None:
|
|
||||||
self.det_model = RetinaFace(session=self._make_session(self.det_file))
|
|
||||||
self.rec_model = ArcFaceONNX(
|
|
||||||
self.rec_file.with_suffix(".onnx").as_posix(),
|
|
||||||
session=self._make_session(self.rec_file),
|
|
||||||
)
|
|
||||||
|
|
||||||
self.det_model.prepare(
|
|
||||||
ctx_id=0,
|
|
||||||
det_thresh=self.min_score,
|
|
||||||
input_size=(640, 640),
|
|
||||||
)
|
|
||||||
self.rec_model.prepare(ctx_id=0)
|
|
||||||
|
|
||||||
def _predict(self, image: NDArray[np.uint8] | bytes) -> list[Face]:
|
|
||||||
if isinstance(image, bytes):
|
|
||||||
decoded_image = cv2.imdecode(np.frombuffer(image, np.uint8), cv2.IMREAD_COLOR)
|
|
||||||
else:
|
|
||||||
decoded_image = image
|
|
||||||
assert is_ndarray(decoded_image, np.uint8)
|
|
||||||
bboxes, kpss = self.det_model.detect(decoded_image)
|
|
||||||
if bboxes.size == 0:
|
|
||||||
return []
|
|
||||||
assert is_ndarray(kpss, np.float32)
|
|
||||||
|
|
||||||
scores = bboxes[:, 4].tolist()
|
|
||||||
bboxes = bboxes[:, :4].round().tolist()
|
|
||||||
|
|
||||||
results = []
|
|
||||||
height, width, _ = decoded_image.shape
|
|
||||||
for (x1, y1, x2, y2), score, kps in zip(bboxes, scores, kpss):
|
|
||||||
cropped_img = norm_crop(decoded_image, kps)
|
|
||||||
embedding: NDArray[np.float32] = self.rec_model.get_feat(cropped_img)[0]
|
|
||||||
face: Face = {
|
|
||||||
"imageWidth": width,
|
|
||||||
"imageHeight": height,
|
|
||||||
"boundingBox": {
|
|
||||||
"x1": x1,
|
|
||||||
"y1": y1,
|
|
||||||
"x2": x2,
|
|
||||||
"y2": y2,
|
|
||||||
},
|
|
||||||
"score": score,
|
|
||||||
"embedding": embedding,
|
|
||||||
}
|
|
||||||
results.append(face)
|
|
||||||
return results
|
|
||||||
|
|
||||||
@property
|
|
||||||
def cached(self) -> bool:
|
|
||||||
return self.det_file.is_file() and self.rec_file.is_file()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def det_file(self) -> Path:
|
|
||||||
return self.cache_dir / "detection" / f"model.{self.preferred_runtime}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rec_file(self) -> Path:
|
|
||||||
return self.cache_dir / "recognition" / f"model.{self.preferred_runtime}"
|
|
||||||
|
|
||||||
def configure(self, **model_kwargs: Any) -> None:
|
|
||||||
self.det_model.det_thresh = model_kwargs.pop("minScore", self.det_model.det_thresh)
|
|
||||||
48
machine-learning/app/models/facial_recognition/detection.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from insightface.model_zoo import RetinaFace
|
||||||
|
from numpy.typing import NDArray
|
||||||
|
|
||||||
|
from app.models.base import InferenceModel
|
||||||
|
from app.models.transforms import decode_cv2
|
||||||
|
from app.schemas import FaceDetectionOutput, ModelSession, ModelTask, ModelType
|
||||||
|
|
||||||
|
|
||||||
|
class FaceDetector(InferenceModel):
|
||||||
|
depends = []
|
||||||
|
identity = (ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
model_name: str,
|
||||||
|
min_score: float = 0.7,
|
||||||
|
cache_dir: Path | str | None = None,
|
||||||
|
**model_kwargs: Any,
|
||||||
|
) -> None:
|
||||||
|
self.min_score = model_kwargs.pop("minScore", min_score)
|
||||||
|
super().__init__(model_name, cache_dir, **model_kwargs)
|
||||||
|
|
||||||
|
def _load(self) -> ModelSession:
|
||||||
|
session = self._make_session(self.model_path)
|
||||||
|
self.model = RetinaFace(session=session)
|
||||||
|
self.model.prepare(ctx_id=0, det_thresh=self.min_score, input_size=(640, 640))
|
||||||
|
|
||||||
|
return session
|
||||||
|
|
||||||
|
def _predict(self, inputs: NDArray[np.uint8] | bytes, **kwargs: Any) -> FaceDetectionOutput:
|
||||||
|
inputs = decode_cv2(inputs)
|
||||||
|
|
||||||
|
bboxes, landmarks = self._detect(inputs)
|
||||||
|
return {
|
||||||
|
"boxes": bboxes[:, :4].round(),
|
||||||
|
"scores": bboxes[:, 4],
|
||||||
|
"landmarks": landmarks,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _detect(self, inputs: NDArray[np.uint8] | bytes) -> tuple[NDArray[np.float32], NDArray[np.float32]]:
|
||||||
|
return self.model.detect(inputs) # type: ignore
|
||||||
|
|
||||||
|
def configure(self, **kwargs: Any) -> None:
|
||||||
|
self.model.det_thresh = kwargs.pop("minScore", self.model.det_thresh)
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import onnx
|
||||||
|
import onnxruntime as ort
|
||||||
|
from insightface.model_zoo import ArcFaceONNX
|
||||||
|
from insightface.utils.face_align import norm_crop
|
||||||
|
from numpy.typing import NDArray
|
||||||
|
from onnx.tools.update_model_dims import update_inputs_outputs_dims
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from app.config import clean_name, log
|
||||||
|
from app.models.base import InferenceModel
|
||||||
|
from app.models.transforms import decode_cv2
|
||||||
|
from app.schemas import FaceDetectionOutput, FacialRecognitionOutput, ModelSession, ModelTask, ModelType
|
||||||
|
|
||||||
|
|
||||||
|
class FaceRecognizer(InferenceModel):
|
||||||
|
depends = [(ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION)]
|
||||||
|
identity = (ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
model_name: str,
|
||||||
|
min_score: float = 0.7,
|
||||||
|
cache_dir: Path | str | None = None,
|
||||||
|
**model_kwargs: Any,
|
||||||
|
) -> None:
|
||||||
|
self.min_score = model_kwargs.pop("minScore", min_score)
|
||||||
|
super().__init__(clean_name(model_name), cache_dir, **model_kwargs)
|
||||||
|
|
||||||
|
def _load(self) -> ModelSession:
|
||||||
|
session = self._make_session(self.model_path)
|
||||||
|
if not self._has_batch_dim(session):
|
||||||
|
self._add_batch_dim(self.model_path)
|
||||||
|
session = self._make_session(self.model_path)
|
||||||
|
self.model = ArcFaceONNX(
|
||||||
|
self.model_path.with_suffix(".onnx").as_posix(),
|
||||||
|
session=session,
|
||||||
|
)
|
||||||
|
return session
|
||||||
|
|
||||||
|
def _predict(
|
||||||
|
self, inputs: NDArray[np.uint8] | bytes | Image.Image, faces: FaceDetectionOutput, **kwargs: Any
|
||||||
|
) -> FacialRecognitionOutput:
|
||||||
|
if faces["boxes"].shape[0] == 0:
|
||||||
|
return []
|
||||||
|
inputs = decode_cv2(inputs)
|
||||||
|
embeddings: NDArray[np.float32] = self.model.get_feat(self._crop(inputs, faces))
|
||||||
|
return self.postprocess(faces, embeddings)
|
||||||
|
|
||||||
|
def postprocess(self, faces: FaceDetectionOutput, embeddings: NDArray[np.float32]) -> FacialRecognitionOutput:
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"boundingBox": {"x1": x1, "y1": y1, "x2": x2, "y2": y2},
|
||||||
|
"embedding": embedding,
|
||||||
|
"score": score,
|
||||||
|
}
|
||||||
|
for (x1, y1, x2, y2), embedding, score in zip(faces["boxes"], embeddings, faces["scores"])
|
||||||
|
]
|
||||||
|
|
||||||
|
def _crop(self, image: NDArray[np.uint8], faces: FaceDetectionOutput) -> list[NDArray[np.uint8]]:
|
||||||
|
return [norm_crop(image, landmark) for landmark in faces["landmarks"]]
|
||||||
|
|
||||||
|
def _has_batch_dim(self, session: ort.InferenceSession) -> bool:
|
||||||
|
return not isinstance(session, ort.InferenceSession) or session.get_inputs()[0].shape[0] == "batch"
|
||||||
|
|
||||||
|
def _add_batch_dim(self, model_path: Path) -> None:
|
||||||
|
log.debug(f"Adding batch dimension to model {model_path}")
|
||||||
|
proto = onnx.load(model_path)
|
||||||
|
static_input_dims = [shape.dim_value for shape in proto.graph.input[0].type.tensor_type.shape.dim[1:]]
|
||||||
|
static_output_dims = [shape.dim_value for shape in proto.graph.output[0].type.tensor_type.shape.dim[1:]]
|
||||||
|
input_dims = {proto.graph.input[0].name: ["batch"] + static_input_dims}
|
||||||
|
output_dims = {proto.graph.output[0].name: ["batch"] + static_output_dims}
|
||||||
|
updated_proto = update_inputs_outputs_dims(proto, input_dims, output_dims)
|
||||||
|
onnx.save(updated_proto, model_path)
|
||||||
0
machine-learning/app/models/session.py
Normal file
@@ -1,3 +1,7 @@
|
|||||||
|
from io import BytesIO
|
||||||
|
from typing import IO
|
||||||
|
|
||||||
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -5,7 +9,7 @@ from PIL import Image
|
|||||||
_PIL_RESAMPLING_METHODS = {resampling.name.lower(): resampling for resampling in Image.Resampling}
|
_PIL_RESAMPLING_METHODS = {resampling.name.lower(): resampling for resampling in Image.Resampling}
|
||||||
|
|
||||||
|
|
||||||
def resize(img: Image.Image, size: int) -> Image.Image:
|
def resize_pil(img: Image.Image, size: int) -> Image.Image:
|
||||||
if img.width < img.height:
|
if img.width < img.height:
|
||||||
return img.resize((size, int((img.height / img.width) * size)), resample=Image.Resampling.BICUBIC)
|
return img.resize((size, int((img.height / img.width) * size)), resample=Image.Resampling.BICUBIC)
|
||||||
else:
|
else:
|
||||||
@@ -13,7 +17,7 @@ def resize(img: Image.Image, size: int) -> Image.Image:
|
|||||||
|
|
||||||
|
|
||||||
# https://stackoverflow.com/a/60883103
|
# https://stackoverflow.com/a/60883103
|
||||||
def crop(img: Image.Image, size: int) -> Image.Image:
|
def crop_pil(img: Image.Image, size: int) -> Image.Image:
|
||||||
left = int((img.size[0] / 2) - (size / 2))
|
left = int((img.size[0] / 2) - (size / 2))
|
||||||
upper = int((img.size[1] / 2) - (size / 2))
|
upper = int((img.size[1] / 2) - (size / 2))
|
||||||
right = left + size
|
right = left + size
|
||||||
@@ -23,14 +27,36 @@ def crop(img: Image.Image, size: int) -> Image.Image:
|
|||||||
|
|
||||||
|
|
||||||
def to_numpy(img: Image.Image) -> NDArray[np.float32]:
|
def to_numpy(img: Image.Image) -> NDArray[np.float32]:
|
||||||
return np.asarray(img.convert("RGB")).astype(np.float32) / 255.0
|
return np.asarray(img if img.mode == "RGB" else img.convert("RGB"), dtype=np.float32) / 255.0
|
||||||
|
|
||||||
|
|
||||||
def normalize(
|
def normalize(
|
||||||
img: NDArray[np.float32], mean: float | NDArray[np.float32], std: float | NDArray[np.float32]
|
img: NDArray[np.float32], mean: float | NDArray[np.float32], std: float | NDArray[np.float32]
|
||||||
) -> NDArray[np.float32]:
|
) -> NDArray[np.float32]:
|
||||||
return (img - mean) / std
|
return np.divide(img - mean, std, dtype=np.float32)
|
||||||
|
|
||||||
|
|
||||||
def get_pil_resampling(resample: str) -> Image.Resampling:
|
def get_pil_resampling(resample: str) -> Image.Resampling:
|
||||||
return _PIL_RESAMPLING_METHODS[resample.lower()]
|
return _PIL_RESAMPLING_METHODS[resample.lower()]
|
||||||
|
|
||||||
|
|
||||||
|
def pil_to_cv2(image: Image.Image) -> NDArray[np.uint8]:
|
||||||
|
return cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def decode_pil(image_bytes: bytes | IO[bytes] | Image.Image) -> Image.Image:
|
||||||
|
if isinstance(image_bytes, Image.Image):
|
||||||
|
return image_bytes
|
||||||
|
image = Image.open(BytesIO(image_bytes) if isinstance(image_bytes, bytes) else image_bytes)
|
||||||
|
image.load() # type: ignore
|
||||||
|
if not image.mode == "RGB":
|
||||||
|
image = image.convert("RGB")
|
||||||
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
def decode_cv2(image_bytes: NDArray[np.uint8] | bytes | Image.Image) -> NDArray[np.uint8]:
|
||||||
|
if isinstance(image_bytes, bytes):
|
||||||
|
image_bytes = decode_pil(image_bytes) # pillow is much faster than cv2
|
||||||
|
if isinstance(image_bytes, Image.Image):
|
||||||
|
return pil_to_cv2(image_bytes)
|
||||||
|
return image_bytes
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Protocol, TypedDict, TypeGuard
|
from typing import Any, Literal, Protocol, TypedDict, TypeGuard, TypeVar
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
import numpy.typing as npt
|
||||||
@@ -28,31 +28,87 @@ class BoundingBox(TypedDict):
|
|||||||
y2: int
|
y2: int
|
||||||
|
|
||||||
|
|
||||||
class ModelType(StrEnum):
|
class ModelTask(StrEnum):
|
||||||
CLIP = "clip"
|
|
||||||
FACIAL_RECOGNITION = "facial-recognition"
|
FACIAL_RECOGNITION = "facial-recognition"
|
||||||
|
SEARCH = "clip"
|
||||||
|
|
||||||
|
|
||||||
class ModelRuntime(StrEnum):
|
class ModelType(StrEnum):
|
||||||
ONNX = "onnx"
|
DETECTION = "detection"
|
||||||
|
RECOGNITION = "recognition"
|
||||||
|
TEXTUAL = "textual"
|
||||||
|
VISUAL = "visual"
|
||||||
|
|
||||||
|
|
||||||
|
class ModelFormat(StrEnum):
|
||||||
ARMNN = "armnn"
|
ARMNN = "armnn"
|
||||||
|
ONNX = "onnx"
|
||||||
|
|
||||||
|
|
||||||
|
class ModelSource(StrEnum):
|
||||||
|
INSIGHTFACE = "insightface"
|
||||||
|
MCLIP = "mclip"
|
||||||
|
OPENCLIP = "openclip"
|
||||||
|
|
||||||
|
|
||||||
|
ModelIdentity = tuple[ModelType, ModelTask]
|
||||||
|
|
||||||
|
|
||||||
|
class ModelSession(Protocol):
|
||||||
|
def run(
|
||||||
|
self,
|
||||||
|
output_names: list[str] | None,
|
||||||
|
input_feed: dict[str, npt.NDArray[np.float32]] | dict[str, npt.NDArray[np.int32]],
|
||||||
|
run_options: Any = None,
|
||||||
|
) -> list[npt.NDArray[np.float32]]: ...
|
||||||
|
|
||||||
|
|
||||||
class HasProfiling(Protocol):
|
class HasProfiling(Protocol):
|
||||||
profiling: dict[str, float]
|
profiling: dict[str, float]
|
||||||
|
|
||||||
|
|
||||||
class Face(TypedDict):
|
class FaceDetectionOutput(TypedDict):
|
||||||
|
boxes: npt.NDArray[np.float32]
|
||||||
|
scores: npt.NDArray[np.float32]
|
||||||
|
landmarks: npt.NDArray[np.float32]
|
||||||
|
|
||||||
|
|
||||||
|
class DetectedFace(TypedDict):
|
||||||
boundingBox: BoundingBox
|
boundingBox: BoundingBox
|
||||||
embedding: npt.NDArray[np.float32]
|
embedding: npt.NDArray[np.float32]
|
||||||
imageWidth: int
|
|
||||||
imageHeight: int
|
|
||||||
score: float
|
score: float
|
||||||
|
|
||||||
|
|
||||||
|
FacialRecognitionOutput = list[DetectedFace]
|
||||||
|
|
||||||
|
|
||||||
|
class PipelineEntry(TypedDict):
|
||||||
|
modelName: str
|
||||||
|
options: dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
|
PipelineRequest = dict[ModelTask, dict[ModelType, PipelineEntry]]
|
||||||
|
|
||||||
|
|
||||||
|
class InferenceEntry(TypedDict):
|
||||||
|
name: str
|
||||||
|
task: ModelTask
|
||||||
|
type: ModelType
|
||||||
|
options: dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
|
InferenceEntries = tuple[list[InferenceEntry], list[InferenceEntry]]
|
||||||
|
|
||||||
|
|
||||||
|
InferenceResponse = dict[ModelTask | Literal["imageHeight"] | Literal["imageWidth"], Any]
|
||||||
|
|
||||||
|
|
||||||
def has_profiling(obj: Any) -> TypeGuard[HasProfiling]:
|
def has_profiling(obj: Any) -> TypeGuard[HasProfiling]:
|
||||||
return hasattr(obj, "profiling") and isinstance(obj.profiling, dict)
|
return hasattr(obj, "profiling") and isinstance(obj.profiling, dict)
|
||||||
|
|
||||||
|
|
||||||
def is_ndarray(obj: Any, dtype: "type[np._DTypeScalar_co]") -> "TypeGuard[npt.NDArray[np._DTypeScalar_co]]":
|
def is_ndarray(obj: Any, dtype: "type[np._DTypeScalar_co]") -> "TypeGuard[npt.NDArray[np._DTypeScalar_co]]":
|
||||||
return isinstance(obj, np.ndarray) and obj.dtype == dtype
|
return isinstance(obj, np.ndarray) and obj.dtype == dtype
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|||||||
@@ -17,13 +17,15 @@ from pytest import MonkeyPatch
|
|||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
from app.main import load, preload_models
|
from app.main import load, preload_models
|
||||||
|
from app.models.clip.textual import MClipTextualEncoder, OpenClipTextualEncoder
|
||||||
|
from app.models.clip.visual import OpenClipVisualEncoder
|
||||||
|
from app.models.facial_recognition.detection import FaceDetector
|
||||||
|
from app.models.facial_recognition.recognition import FaceRecognizer
|
||||||
|
|
||||||
from .config import Settings, log, settings
|
from .config import Settings, log, settings
|
||||||
from .models.base import InferenceModel
|
from .models.base import InferenceModel
|
||||||
from .models.cache import ModelCache
|
from .models.cache import ModelCache
|
||||||
from .models.clip import MCLIPEncoder, OpenCLIPEncoder
|
from .schemas import ModelFormat, ModelTask, ModelType
|
||||||
from .models.facial_recognition import FaceRecognizer
|
|
||||||
from .schemas import ModelRuntime, ModelType
|
|
||||||
|
|
||||||
|
|
||||||
class TestBase:
|
class TestBase:
|
||||||
@@ -35,13 +37,13 @@ class TestBase:
|
|||||||
|
|
||||||
@pytest.mark.providers(CPU_EP)
|
@pytest.mark.providers(CPU_EP)
|
||||||
def test_sets_cpu_provider(self, providers: list[str]) -> None:
|
def test_sets_cpu_provider(self, providers: list[str]) -> None:
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
|
|
||||||
assert encoder.providers == self.CPU_EP
|
assert encoder.providers == self.CPU_EP
|
||||||
|
|
||||||
@pytest.mark.providers(CUDA_EP)
|
@pytest.mark.providers(CUDA_EP)
|
||||||
def test_sets_cuda_provider_if_available(self, providers: list[str]) -> None:
|
def test_sets_cuda_provider_if_available(self, providers: list[str]) -> None:
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
|
|
||||||
assert encoder.providers == self.CUDA_EP
|
assert encoder.providers == self.CUDA_EP
|
||||||
|
|
||||||
@@ -50,7 +52,7 @@ class TestBase:
|
|||||||
mocked = mocker.patch("app.models.base.ort.capi._pybind_state")
|
mocked = mocker.patch("app.models.base.ort.capi._pybind_state")
|
||||||
mocked.get_available_openvino_device_ids.return_value = ["GPU.0", "CPU"]
|
mocked.get_available_openvino_device_ids.return_value = ["GPU.0", "CPU"]
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
|
|
||||||
assert encoder.providers == self.OV_EP
|
assert encoder.providers == self.OV_EP
|
||||||
|
|
||||||
@@ -59,25 +61,25 @@ class TestBase:
|
|||||||
mocked = mocker.patch("app.models.base.ort.capi._pybind_state")
|
mocked = mocker.patch("app.models.base.ort.capi._pybind_state")
|
||||||
mocked.get_available_openvino_device_ids.return_value = ["CPU"]
|
mocked.get_available_openvino_device_ids.return_value = ["CPU"]
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
|
|
||||||
assert encoder.providers == self.CPU_EP
|
assert encoder.providers == self.CPU_EP
|
||||||
|
|
||||||
@pytest.mark.providers(CUDA_EP_OUT_OF_ORDER)
|
@pytest.mark.providers(CUDA_EP_OUT_OF_ORDER)
|
||||||
def test_sets_providers_in_correct_order(self, providers: list[str]) -> None:
|
def test_sets_providers_in_correct_order(self, providers: list[str]) -> None:
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
|
|
||||||
assert encoder.providers == self.CUDA_EP
|
assert encoder.providers == self.CUDA_EP
|
||||||
|
|
||||||
@pytest.mark.providers(TRT_EP)
|
@pytest.mark.providers(TRT_EP)
|
||||||
def test_ignores_unsupported_providers(self, providers: list[str]) -> None:
|
def test_ignores_unsupported_providers(self, providers: list[str]) -> None:
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
|
|
||||||
assert encoder.providers == self.CUDA_EP
|
assert encoder.providers == self.CUDA_EP
|
||||||
|
|
||||||
def test_sets_provider_kwarg(self) -> None:
|
def test_sets_provider_kwarg(self) -> None:
|
||||||
providers = ["CUDAExecutionProvider"]
|
providers = ["CUDAExecutionProvider"]
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", providers=providers)
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai", providers=providers)
|
||||||
|
|
||||||
assert encoder.providers == providers
|
assert encoder.providers == providers
|
||||||
|
|
||||||
@@ -85,7 +87,9 @@ class TestBase:
|
|||||||
mocked = mocker.patch("app.models.base.ort.capi._pybind_state")
|
mocked = mocker.patch("app.models.base.ort.capi._pybind_state")
|
||||||
mocked.get_available_openvino_device_ids.return_value = ["GPU.0", "CPU"]
|
mocked.get_available_openvino_device_ids.return_value = ["GPU.0", "CPU"]
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", providers=["OpenVINOExecutionProvider", "CPUExecutionProvider"])
|
encoder = OpenClipTextualEncoder(
|
||||||
|
"ViT-B-32__openai", providers=["OpenVINOExecutionProvider", "CPUExecutionProvider"]
|
||||||
|
)
|
||||||
|
|
||||||
assert encoder.provider_options == [
|
assert encoder.provider_options == [
|
||||||
{"device_type": "GPU_FP32", "cache_dir": (encoder.cache_dir / "openvino").as_posix()},
|
{"device_type": "GPU_FP32", "cache_dir": (encoder.cache_dir / "openvino").as_posix()},
|
||||||
@@ -93,7 +97,7 @@ class TestBase:
|
|||||||
]
|
]
|
||||||
|
|
||||||
def test_sets_provider_options_kwarg(self) -> None:
|
def test_sets_provider_options_kwarg(self) -> None:
|
||||||
encoder = OpenCLIPEncoder(
|
encoder = OpenClipTextualEncoder(
|
||||||
"ViT-B-32__openai",
|
"ViT-B-32__openai",
|
||||||
providers=["OpenVINOExecutionProvider", "CPUExecutionProvider"],
|
providers=["OpenVINOExecutionProvider", "CPUExecutionProvider"],
|
||||||
provider_options=[],
|
provider_options=[],
|
||||||
@@ -102,7 +106,7 @@ class TestBase:
|
|||||||
assert encoder.provider_options == []
|
assert encoder.provider_options == []
|
||||||
|
|
||||||
def test_sets_default_sess_options(self) -> None:
|
def test_sets_default_sess_options(self) -> None:
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
|
|
||||||
assert encoder.sess_options.execution_mode == ort.ExecutionMode.ORT_SEQUENTIAL
|
assert encoder.sess_options.execution_mode == ort.ExecutionMode.ORT_SEQUENTIAL
|
||||||
assert encoder.sess_options.inter_op_num_threads == 1
|
assert encoder.sess_options.inter_op_num_threads == 1
|
||||||
@@ -110,7 +114,9 @@ class TestBase:
|
|||||||
assert encoder.sess_options.enable_cpu_mem_arena is False
|
assert encoder.sess_options.enable_cpu_mem_arena is False
|
||||||
|
|
||||||
def test_sets_default_sess_options_does_not_set_threads_if_non_cpu_and_default_threads(self) -> None:
|
def test_sets_default_sess_options_does_not_set_threads_if_non_cpu_and_default_threads(self) -> None:
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", providers=["CUDAExecutionProvider", "CPUExecutionProvider"])
|
encoder = OpenClipTextualEncoder(
|
||||||
|
"ViT-B-32__openai", providers=["CUDAExecutionProvider", "CPUExecutionProvider"]
|
||||||
|
)
|
||||||
|
|
||||||
assert encoder.sess_options.inter_op_num_threads == 0
|
assert encoder.sess_options.inter_op_num_threads == 0
|
||||||
assert encoder.sess_options.intra_op_num_threads == 0
|
assert encoder.sess_options.intra_op_num_threads == 0
|
||||||
@@ -120,14 +126,16 @@ class TestBase:
|
|||||||
mock_settings.model_inter_op_threads = 2
|
mock_settings.model_inter_op_threads = 2
|
||||||
mock_settings.model_intra_op_threads = 4
|
mock_settings.model_intra_op_threads = 4
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", providers=["CUDAExecutionProvider", "CPUExecutionProvider"])
|
encoder = OpenClipTextualEncoder(
|
||||||
|
"ViT-B-32__openai", providers=["CUDAExecutionProvider", "CPUExecutionProvider"]
|
||||||
|
)
|
||||||
|
|
||||||
assert encoder.sess_options.inter_op_num_threads == 2
|
assert encoder.sess_options.inter_op_num_threads == 2
|
||||||
assert encoder.sess_options.intra_op_num_threads == 4
|
assert encoder.sess_options.intra_op_num_threads == 4
|
||||||
|
|
||||||
def test_sets_sess_options_kwarg(self) -> None:
|
def test_sets_sess_options_kwarg(self) -> None:
|
||||||
sess_options = ort.SessionOptions()
|
sess_options = ort.SessionOptions()
|
||||||
encoder = OpenCLIPEncoder(
|
encoder = OpenClipTextualEncoder(
|
||||||
"ViT-B-32__openai",
|
"ViT-B-32__openai",
|
||||||
providers=["OpenVINOExecutionProvider", "CPUExecutionProvider"],
|
providers=["OpenVINOExecutionProvider", "CPUExecutionProvider"],
|
||||||
provider_options=[],
|
provider_options=[],
|
||||||
@@ -137,43 +145,43 @@ class TestBase:
|
|||||||
assert sess_options is encoder.sess_options
|
assert sess_options is encoder.sess_options
|
||||||
|
|
||||||
def test_sets_default_cache_dir(self) -> None:
|
def test_sets_default_cache_dir(self) -> None:
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
|
|
||||||
assert encoder.cache_dir == Path(settings.cache_folder) / "clip" / "ViT-B-32__openai"
|
assert encoder.cache_dir == Path(settings.cache_folder) / "clip" / "ViT-B-32__openai"
|
||||||
|
|
||||||
def test_sets_cache_dir_kwarg(self) -> None:
|
def test_sets_cache_dir_kwarg(self) -> None:
|
||||||
cache_dir = Path("/test_cache")
|
cache_dir = Path("/test_cache")
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", cache_dir=cache_dir)
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir=cache_dir)
|
||||||
|
|
||||||
assert encoder.cache_dir == cache_dir
|
assert encoder.cache_dir == cache_dir
|
||||||
|
|
||||||
def test_sets_default_preferred_runtime(self, mocker: MockerFixture) -> None:
|
def test_sets_default_preferred_format(self, mocker: MockerFixture) -> None:
|
||||||
mocker.patch.object(settings, "ann", True)
|
mocker.patch.object(settings, "ann", True)
|
||||||
mocker.patch("ann.ann.is_available", False)
|
mocker.patch("ann.ann.is_available", False)
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
|
|
||||||
assert encoder.preferred_runtime == ModelRuntime.ONNX
|
assert encoder.preferred_format == ModelFormat.ONNX
|
||||||
|
|
||||||
def test_sets_default_preferred_runtime_to_armnn_if_available(self, mocker: MockerFixture) -> None:
|
def test_sets_default_preferred_format_to_armnn_if_available(self, mocker: MockerFixture) -> None:
|
||||||
mocker.patch.object(settings, "ann", True)
|
mocker.patch.object(settings, "ann", True)
|
||||||
mocker.patch("ann.ann.is_available", True)
|
mocker.patch("ann.ann.is_available", True)
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
|
|
||||||
assert encoder.preferred_runtime == ModelRuntime.ARMNN
|
assert encoder.preferred_format == ModelFormat.ARMNN
|
||||||
|
|
||||||
def test_sets_preferred_runtime_kwarg(self, mocker: MockerFixture) -> None:
|
def test_sets_preferred_format_kwarg(self, mocker: MockerFixture) -> None:
|
||||||
mocker.patch.object(settings, "ann", False)
|
mocker.patch.object(settings, "ann", False)
|
||||||
mocker.patch("ann.ann.is_available", False)
|
mocker.patch("ann.ann.is_available", False)
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", preferred_runtime=ModelRuntime.ARMNN)
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai", preferred_format=ModelFormat.ARMNN)
|
||||||
|
|
||||||
assert encoder.preferred_runtime == ModelRuntime.ARMNN
|
assert encoder.preferred_format == ModelFormat.ARMNN
|
||||||
|
|
||||||
def test_casts_cache_dir_string_to_path(self) -> None:
|
def test_casts_cache_dir_string_to_path(self) -> None:
|
||||||
cache_dir = "/test_cache"
|
cache_dir = "/test_cache"
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", cache_dir=cache_dir)
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir=cache_dir)
|
||||||
|
|
||||||
assert encoder.cache_dir == Path(cache_dir)
|
assert encoder.cache_dir == Path(cache_dir)
|
||||||
|
|
||||||
@@ -186,7 +194,7 @@ class TestBase:
|
|||||||
mocker.patch("app.models.base.Path", return_value=mock_cache_dir)
|
mocker.patch("app.models.base.Path", return_value=mock_cache_dir)
|
||||||
info = mocker.spy(log, "info")
|
info = mocker.spy(log, "info")
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", cache_dir=mock_cache_dir)
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir=mock_cache_dir)
|
||||||
encoder.clear_cache()
|
encoder.clear_cache()
|
||||||
|
|
||||||
mock_rmtree.assert_called_once_with(encoder.cache_dir)
|
mock_rmtree.assert_called_once_with(encoder.cache_dir)
|
||||||
@@ -201,7 +209,7 @@ class TestBase:
|
|||||||
mocker.patch("app.models.base.Path", return_value=mock_cache_dir)
|
mocker.patch("app.models.base.Path", return_value=mock_cache_dir)
|
||||||
warning = mocker.spy(log, "warning")
|
warning = mocker.spy(log, "warning")
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", cache_dir=mock_cache_dir)
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir=mock_cache_dir)
|
||||||
encoder.clear_cache()
|
encoder.clear_cache()
|
||||||
|
|
||||||
mock_rmtree.assert_not_called()
|
mock_rmtree.assert_not_called()
|
||||||
@@ -215,7 +223,7 @@ class TestBase:
|
|||||||
mock_cache_dir.is_dir.return_value = True
|
mock_cache_dir.is_dir.return_value = True
|
||||||
mocker.patch("app.models.base.Path", return_value=mock_cache_dir)
|
mocker.patch("app.models.base.Path", return_value=mock_cache_dir)
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", cache_dir=mock_cache_dir)
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir=mock_cache_dir)
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
encoder.clear_cache()
|
encoder.clear_cache()
|
||||||
|
|
||||||
@@ -230,7 +238,7 @@ class TestBase:
|
|||||||
mocker.patch("app.models.base.Path", return_value=mock_cache_dir)
|
mocker.patch("app.models.base.Path", return_value=mock_cache_dir)
|
||||||
warning = mocker.spy(log, "warning")
|
warning = mocker.spy(log, "warning")
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", cache_dir=mock_cache_dir)
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir=mock_cache_dir)
|
||||||
encoder.clear_cache()
|
encoder.clear_cache()
|
||||||
|
|
||||||
mock_rmtree.assert_not_called()
|
mock_rmtree.assert_not_called()
|
||||||
@@ -245,7 +253,7 @@ class TestBase:
|
|||||||
mock_model_path.with_suffix.return_value = mock_model_path
|
mock_model_path.with_suffix.return_value = mock_model_path
|
||||||
mock_ann = mocker.patch("app.models.base.AnnSession")
|
mock_ann = mocker.patch("app.models.base.AnnSession")
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
encoder._make_session(mock_model_path)
|
encoder._make_session(mock_model_path)
|
||||||
|
|
||||||
mock_ann.assert_called_once()
|
mock_ann.assert_called_once()
|
||||||
@@ -263,7 +271,7 @@ class TestBase:
|
|||||||
mock_ann = mocker.patch("app.models.base.AnnSession")
|
mock_ann = mocker.patch("app.models.base.AnnSession")
|
||||||
mock_ort = mocker.patch("app.models.base.ort.InferenceSession")
|
mock_ort = mocker.patch("app.models.base.ort.InferenceSession")
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
encoder._make_session(mock_armnn_path)
|
encoder._make_session(mock_armnn_path)
|
||||||
|
|
||||||
mock_ort.assert_called_once()
|
mock_ort.assert_called_once()
|
||||||
@@ -277,7 +285,7 @@ class TestBase:
|
|||||||
mock_ann = mocker.patch("app.models.base.AnnSession")
|
mock_ann = mocker.patch("app.models.base.AnnSession")
|
||||||
mock_ort = mocker.patch("app.models.base.ort.InferenceSession")
|
mock_ort = mocker.patch("app.models.base.ort.InferenceSession")
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
encoder._make_session(mock_model_path)
|
encoder._make_session(mock_model_path)
|
||||||
|
|
||||||
@@ -287,7 +295,7 @@ class TestBase:
|
|||||||
def test_download(self, mocker: MockerFixture) -> None:
|
def test_download(self, mocker: MockerFixture) -> None:
|
||||||
mock_snapshot_download = mocker.patch("app.models.base.snapshot_download")
|
mock_snapshot_download = mocker.patch("app.models.base.snapshot_download")
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", cache_dir="/path/to/cache")
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir="/path/to/cache")
|
||||||
encoder.download()
|
encoder.download()
|
||||||
|
|
||||||
mock_snapshot_download.assert_called_once_with(
|
mock_snapshot_download.assert_called_once_with(
|
||||||
@@ -298,10 +306,10 @@ class TestBase:
|
|||||||
ignore_patterns=["*.armnn"],
|
ignore_patterns=["*.armnn"],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_download_downloads_armnn_if_preferred_runtime(self, mocker: MockerFixture) -> None:
|
def test_download_downloads_armnn_if_preferred_format(self, mocker: MockerFixture) -> None:
|
||||||
mock_snapshot_download = mocker.patch("app.models.base.snapshot_download")
|
mock_snapshot_download = mocker.patch("app.models.base.snapshot_download")
|
||||||
|
|
||||||
encoder = OpenCLIPEncoder("ViT-B-32__openai", preferred_runtime=ModelRuntime.ARMNN)
|
encoder = OpenClipTextualEncoder("ViT-B-32__openai", preferred_format=ModelFormat.ARMNN)
|
||||||
encoder.download()
|
encoder.download()
|
||||||
|
|
||||||
mock_snapshot_download.assert_called_once_with(
|
mock_snapshot_download.assert_called_once_with(
|
||||||
@@ -323,21 +331,17 @@ class TestCLIP:
|
|||||||
mocker: MockerFixture,
|
mocker: MockerFixture,
|
||||||
clip_model_cfg: dict[str, Any],
|
clip_model_cfg: dict[str, Any],
|
||||||
clip_preprocess_cfg: Callable[[Path], dict[str, Any]],
|
clip_preprocess_cfg: Callable[[Path], dict[str, Any]],
|
||||||
clip_tokenizer_cfg: Callable[[Path], dict[str, Any]],
|
|
||||||
) -> None:
|
) -> None:
|
||||||
mocker.patch.object(OpenCLIPEncoder, "download")
|
mocker.patch.object(OpenClipVisualEncoder, "download")
|
||||||
mocker.patch.object(OpenCLIPEncoder, "model_cfg", clip_model_cfg)
|
mocker.patch.object(OpenClipVisualEncoder, "model_cfg", clip_model_cfg)
|
||||||
mocker.patch.object(OpenCLIPEncoder, "preprocess_cfg", clip_preprocess_cfg)
|
mocker.patch.object(OpenClipVisualEncoder, "preprocess_cfg", clip_preprocess_cfg)
|
||||||
mocker.patch.object(OpenCLIPEncoder, "tokenizer_cfg", clip_tokenizer_cfg)
|
|
||||||
|
|
||||||
mocked = mocker.patch.object(InferenceModel, "_make_session", autospec=True).return_value
|
mocked = mocker.patch.object(InferenceModel, "_make_session", autospec=True).return_value
|
||||||
mocked.run.return_value = [[self.embedding]]
|
mocked.run.return_value = [[self.embedding]]
|
||||||
mocker.patch("app.models.clip.Tokenizer.from_file", autospec=True)
|
|
||||||
|
|
||||||
clip_encoder = OpenCLIPEncoder("ViT-B-32__openai", cache_dir="test_cache", mode="vision")
|
clip_encoder = OpenClipVisualEncoder("ViT-B-32__openai", cache_dir="test_cache")
|
||||||
embedding = clip_encoder.predict(pil_image)
|
embedding = clip_encoder.predict(pil_image)
|
||||||
|
|
||||||
assert clip_encoder.mode == "vision"
|
|
||||||
assert isinstance(embedding, np.ndarray)
|
assert isinstance(embedding, np.ndarray)
|
||||||
assert embedding.shape[0] == clip_model_cfg["embed_dim"]
|
assert embedding.shape[0] == clip_model_cfg["embed_dim"]
|
||||||
assert embedding.dtype == np.float32
|
assert embedding.dtype == np.float32
|
||||||
@@ -347,22 +351,19 @@ class TestCLIP:
|
|||||||
self,
|
self,
|
||||||
mocker: MockerFixture,
|
mocker: MockerFixture,
|
||||||
clip_model_cfg: dict[str, Any],
|
clip_model_cfg: dict[str, Any],
|
||||||
clip_preprocess_cfg: Callable[[Path], dict[str, Any]],
|
|
||||||
clip_tokenizer_cfg: Callable[[Path], dict[str, Any]],
|
clip_tokenizer_cfg: Callable[[Path], dict[str, Any]],
|
||||||
) -> None:
|
) -> None:
|
||||||
mocker.patch.object(OpenCLIPEncoder, "download")
|
mocker.patch.object(OpenClipTextualEncoder, "download")
|
||||||
mocker.patch.object(OpenCLIPEncoder, "model_cfg", clip_model_cfg)
|
mocker.patch.object(OpenClipTextualEncoder, "model_cfg", clip_model_cfg)
|
||||||
mocker.patch.object(OpenCLIPEncoder, "preprocess_cfg", clip_preprocess_cfg)
|
mocker.patch.object(OpenClipTextualEncoder, "tokenizer_cfg", clip_tokenizer_cfg)
|
||||||
mocker.patch.object(OpenCLIPEncoder, "tokenizer_cfg", clip_tokenizer_cfg)
|
|
||||||
|
|
||||||
mocked = mocker.patch.object(InferenceModel, "_make_session", autospec=True).return_value
|
mocked = mocker.patch.object(InferenceModel, "_make_session", autospec=True).return_value
|
||||||
mocked.run.return_value = [[self.embedding]]
|
mocked.run.return_value = [[self.embedding]]
|
||||||
mocker.patch("app.models.clip.Tokenizer.from_file", autospec=True)
|
mocker.patch("app.models.clip.textual.Tokenizer.from_file", autospec=True)
|
||||||
|
|
||||||
clip_encoder = OpenCLIPEncoder("ViT-B-32__openai", cache_dir="test_cache", mode="text")
|
clip_encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir="test_cache")
|
||||||
embedding = clip_encoder.predict("test search query")
|
embedding = clip_encoder.predict("test search query")
|
||||||
|
|
||||||
assert clip_encoder.mode == "text"
|
|
||||||
assert isinstance(embedding, np.ndarray)
|
assert isinstance(embedding, np.ndarray)
|
||||||
assert embedding.shape[0] == clip_model_cfg["embed_dim"]
|
assert embedding.shape[0] == clip_model_cfg["embed_dim"]
|
||||||
assert embedding.dtype == np.float32
|
assert embedding.dtype == np.float32
|
||||||
@@ -372,19 +373,18 @@ class TestCLIP:
|
|||||||
self,
|
self,
|
||||||
mocker: MockerFixture,
|
mocker: MockerFixture,
|
||||||
clip_model_cfg: dict[str, Any],
|
clip_model_cfg: dict[str, Any],
|
||||||
clip_preprocess_cfg: Callable[[Path], dict[str, Any]],
|
|
||||||
clip_tokenizer_cfg: Callable[[Path], dict[str, Any]],
|
clip_tokenizer_cfg: Callable[[Path], dict[str, Any]],
|
||||||
) -> None:
|
) -> None:
|
||||||
mocker.patch.object(OpenCLIPEncoder, "download")
|
mocker.patch.object(OpenClipTextualEncoder, "download")
|
||||||
mocker.patch.object(OpenCLIPEncoder, "model_cfg", clip_model_cfg)
|
mocker.patch.object(OpenClipTextualEncoder, "model_cfg", clip_model_cfg)
|
||||||
mocker.patch.object(OpenCLIPEncoder, "preprocess_cfg", clip_preprocess_cfg)
|
mocker.patch.object(OpenClipTextualEncoder, "tokenizer_cfg", clip_tokenizer_cfg)
|
||||||
mocker.patch.object(OpenCLIPEncoder, "tokenizer_cfg", clip_tokenizer_cfg)
|
mocker.patch.object(InferenceModel, "_make_session", autospec=True).return_value
|
||||||
mock_tokenizer = mocker.patch("app.models.clip.Tokenizer.from_file", autospec=True).return_value
|
mock_tokenizer = mocker.patch("app.models.clip.textual.Tokenizer.from_file", autospec=True).return_value
|
||||||
mock_ids = [randint(0, 50000) for _ in range(77)]
|
mock_ids = [randint(0, 50000) for _ in range(77)]
|
||||||
mock_tokenizer.encode.return_value = SimpleNamespace(ids=mock_ids)
|
mock_tokenizer.encode.return_value = SimpleNamespace(ids=mock_ids)
|
||||||
|
|
||||||
clip_encoder = OpenCLIPEncoder("ViT-B-32__openai", cache_dir="test_cache", mode="text")
|
clip_encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir="test_cache")
|
||||||
clip_encoder._load_tokenizer()
|
clip_encoder._load()
|
||||||
tokens = clip_encoder.tokenize("test search query")
|
tokens = clip_encoder.tokenize("test search query")
|
||||||
|
|
||||||
assert "text" in tokens
|
assert "text" in tokens
|
||||||
@@ -397,20 +397,19 @@ class TestCLIP:
|
|||||||
self,
|
self,
|
||||||
mocker: MockerFixture,
|
mocker: MockerFixture,
|
||||||
clip_model_cfg: dict[str, Any],
|
clip_model_cfg: dict[str, Any],
|
||||||
clip_preprocess_cfg: Callable[[Path], dict[str, Any]],
|
|
||||||
clip_tokenizer_cfg: Callable[[Path], dict[str, Any]],
|
clip_tokenizer_cfg: Callable[[Path], dict[str, Any]],
|
||||||
) -> None:
|
) -> None:
|
||||||
mocker.patch.object(OpenCLIPEncoder, "download")
|
mocker.patch.object(MClipTextualEncoder, "download")
|
||||||
mocker.patch.object(OpenCLIPEncoder, "model_cfg", clip_model_cfg)
|
mocker.patch.object(MClipTextualEncoder, "model_cfg", clip_model_cfg)
|
||||||
mocker.patch.object(OpenCLIPEncoder, "preprocess_cfg", clip_preprocess_cfg)
|
mocker.patch.object(MClipTextualEncoder, "tokenizer_cfg", clip_tokenizer_cfg)
|
||||||
mocker.patch.object(OpenCLIPEncoder, "tokenizer_cfg", clip_tokenizer_cfg)
|
mocker.patch.object(InferenceModel, "_make_session", autospec=True).return_value
|
||||||
mock_tokenizer = mocker.patch("app.models.clip.Tokenizer.from_file", autospec=True).return_value
|
mock_tokenizer = mocker.patch("app.models.clip.textual.Tokenizer.from_file", autospec=True).return_value
|
||||||
mock_ids = [randint(0, 50000) for _ in range(77)]
|
mock_ids = [randint(0, 50000) for _ in range(77)]
|
||||||
mock_attention_mask = [randint(0, 1) for _ in range(77)]
|
mock_attention_mask = [randint(0, 1) for _ in range(77)]
|
||||||
mock_tokenizer.encode.return_value = SimpleNamespace(ids=mock_ids, attention_mask=mock_attention_mask)
|
mock_tokenizer.encode.return_value = SimpleNamespace(ids=mock_ids, attention_mask=mock_attention_mask)
|
||||||
|
|
||||||
clip_encoder = MCLIPEncoder("ViT-B-32__openai", cache_dir="test_cache", mode="text")
|
clip_encoder = MClipTextualEncoder("ViT-B-32__openai", cache_dir="test_cache")
|
||||||
clip_encoder._load_tokenizer()
|
clip_encoder._load()
|
||||||
tokens = clip_encoder.tokenize("test search query")
|
tokens = clip_encoder.tokenize("test search query")
|
||||||
|
|
||||||
assert "input_ids" in tokens
|
assert "input_ids" in tokens
|
||||||
@@ -430,59 +429,90 @@ class TestFaceRecognition:
|
|||||||
|
|
||||||
assert face_recognizer.min_score == 0.5
|
assert face_recognizer.min_score == 0.5
|
||||||
|
|
||||||
def test_basic(self, cv_image: cv2.Mat, mocker: MockerFixture) -> None:
|
def test_detection(self, cv_image: cv2.Mat, mocker: MockerFixture) -> None:
|
||||||
mocker.patch.object(FaceRecognizer, "load")
|
mocker.patch.object(FaceDetector, "load")
|
||||||
face_recognizer = FaceRecognizer("buffalo_s", min_score=0.0, cache_dir="test_cache")
|
face_detector = FaceDetector("buffalo_s", min_score=0.0, cache_dir="test_cache")
|
||||||
|
|
||||||
det_model = mock.Mock()
|
det_model = mock.Mock()
|
||||||
num_faces = 2
|
num_faces = 2
|
||||||
bbox = np.random.rand(num_faces, 4).astype(np.float32)
|
bbox = np.random.rand(num_faces, 4).astype(np.float32)
|
||||||
score = np.array([[0.67]] * num_faces).astype(np.float32)
|
scores = np.array([[0.67]] * num_faces).astype(np.float32)
|
||||||
kpss = np.random.rand(num_faces, 5, 2).astype(np.float32)
|
kpss = np.random.rand(num_faces, 5, 2).astype(np.float32)
|
||||||
det_model.detect.return_value = (np.concatenate([bbox, score], axis=-1), kpss)
|
det_model.detect.return_value = (np.concatenate([bbox, scores], axis=-1), kpss)
|
||||||
face_recognizer.det_model = det_model
|
face_detector.model = det_model
|
||||||
|
|
||||||
|
faces = face_detector.predict(cv_image)
|
||||||
|
|
||||||
|
assert isinstance(faces, dict)
|
||||||
|
assert isinstance(faces.get("boxes", None), np.ndarray)
|
||||||
|
assert isinstance(faces.get("landmarks", None), np.ndarray)
|
||||||
|
assert isinstance(faces.get("scores", None), np.ndarray)
|
||||||
|
assert np.equal(faces["boxes"], bbox.round()).all()
|
||||||
|
assert np.equal(faces["landmarks"], kpss).all()
|
||||||
|
assert np.equal(faces["scores"], scores).all()
|
||||||
|
det_model.detect.assert_called_once()
|
||||||
|
|
||||||
|
def test_recognition(self, cv_image: cv2.Mat, mocker: MockerFixture) -> None:
|
||||||
|
mocker.patch.object(FaceRecognizer, "load")
|
||||||
|
face_recognizer = FaceRecognizer("buffalo_s", min_score=0.0, cache_dir="test_cache")
|
||||||
|
|
||||||
|
num_faces = 2
|
||||||
|
bbox = np.random.rand(num_faces, 4).astype(np.float32)
|
||||||
|
scores = np.array([0.67] * num_faces).astype(np.float32)
|
||||||
|
kpss = np.random.rand(num_faces, 5, 2).astype(np.float32)
|
||||||
|
faces = {"boxes": bbox, "landmarks": kpss, "scores": scores}
|
||||||
|
|
||||||
rec_model = mock.Mock()
|
rec_model = mock.Mock()
|
||||||
embedding = np.random.rand(num_faces, 512).astype(np.float32)
|
embedding = np.random.rand(num_faces, 512).astype(np.float32)
|
||||||
rec_model.get_feat.return_value = embedding
|
rec_model.get_feat.return_value = embedding
|
||||||
face_recognizer.rec_model = rec_model
|
face_recognizer.model = rec_model
|
||||||
|
|
||||||
faces = face_recognizer.predict(cv_image)
|
faces = face_recognizer.predict(cv_image, faces)
|
||||||
|
|
||||||
|
assert isinstance(faces, list)
|
||||||
assert len(faces) == num_faces
|
assert len(faces) == num_faces
|
||||||
for face in faces:
|
for face in faces:
|
||||||
assert face["imageHeight"] == 800
|
assert isinstance(face.get("boundingBox"), dict)
|
||||||
assert face["imageWidth"] == 600
|
assert set(face["boundingBox"]) == {"x1", "y1", "x2", "y2"}
|
||||||
assert isinstance(face["embedding"], np.ndarray)
|
assert all(isinstance(val, np.float32) for val in face["boundingBox"].values())
|
||||||
|
assert isinstance(face.get("embedding"), np.ndarray)
|
||||||
assert face["embedding"].shape[0] == 512
|
assert face["embedding"].shape[0] == 512
|
||||||
assert face["embedding"].dtype == np.float32
|
assert isinstance(face.get("score", None), np.float32)
|
||||||
|
|
||||||
det_model.detect.assert_called_once()
|
rec_model.get_feat.assert_called_once()
|
||||||
assert rec_model.get_feat.call_count == num_faces
|
call_args = rec_model.get_feat.call_args_list[0].args
|
||||||
|
assert len(call_args) == 1
|
||||||
|
assert isinstance(call_args[0], list)
|
||||||
|
assert isinstance(call_args[0][0], np.ndarray)
|
||||||
|
assert call_args[0][0].shape == (112, 112, 3)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
class TestCache:
|
class TestCache:
|
||||||
async def test_caches(self, mock_get_model: mock.Mock) -> None:
|
async def test_caches(self, mock_get_model: mock.Mock) -> None:
|
||||||
model_cache = ModelCache()
|
model_cache = ModelCache()
|
||||||
await model_cache.get("test_model_name", ModelType.FACIAL_RECOGNITION)
|
await model_cache.get("test_model_name", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION)
|
||||||
await model_cache.get("test_model_name", ModelType.FACIAL_RECOGNITION)
|
await model_cache.get("test_model_name", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION)
|
||||||
assert len(model_cache.cache._cache) == 1
|
assert len(model_cache.cache._cache) == 1
|
||||||
mock_get_model.assert_called_once()
|
mock_get_model.assert_called_once()
|
||||||
|
|
||||||
async def test_kwargs_used(self, mock_get_model: mock.Mock) -> None:
|
async def test_kwargs_used(self, mock_get_model: mock.Mock) -> None:
|
||||||
model_cache = ModelCache()
|
model_cache = ModelCache()
|
||||||
await model_cache.get("test_model_name", ModelType.FACIAL_RECOGNITION, cache_dir="test_cache")
|
await model_cache.get(
|
||||||
mock_get_model.assert_called_once_with(ModelType.FACIAL_RECOGNITION, "test_model_name", cache_dir="test_cache")
|
"test_model_name", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION, cache_dir="test_cache"
|
||||||
|
)
|
||||||
|
mock_get_model.assert_called_once_with(
|
||||||
|
"test_model_name", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION, cache_dir="test_cache"
|
||||||
|
)
|
||||||
|
|
||||||
async def test_different_clip(self, mock_get_model: mock.Mock) -> None:
|
async def test_different_clip(self, mock_get_model: mock.Mock) -> None:
|
||||||
model_cache = ModelCache()
|
model_cache = ModelCache()
|
||||||
await model_cache.get("test_image_model_name", ModelType.CLIP)
|
await model_cache.get("test_model_name", ModelType.VISUAL, ModelTask.SEARCH)
|
||||||
await model_cache.get("test_text_model_name", ModelType.CLIP)
|
await model_cache.get("test_model_name", ModelType.TEXTUAL, ModelTask.SEARCH)
|
||||||
mock_get_model.assert_has_calls(
|
mock_get_model.assert_has_calls(
|
||||||
[
|
[
|
||||||
mock.call(ModelType.CLIP, "test_image_model_name"),
|
mock.call("test_model_name", ModelType.VISUAL, ModelTask.SEARCH),
|
||||||
mock.call(ModelType.CLIP, "test_text_model_name"),
|
mock.call("test_model_name", ModelType.TEXTUAL, ModelTask.SEARCH),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert len(model_cache.cache._cache) == 2
|
assert len(model_cache.cache._cache) == 2
|
||||||
@@ -490,19 +520,19 @@ class TestCache:
|
|||||||
@mock.patch("app.models.cache.OptimisticLock", autospec=True)
|
@mock.patch("app.models.cache.OptimisticLock", autospec=True)
|
||||||
async def test_model_ttl(self, mock_lock_cls: mock.Mock, mock_get_model: mock.Mock) -> None:
|
async def test_model_ttl(self, mock_lock_cls: mock.Mock, mock_get_model: mock.Mock) -> None:
|
||||||
model_cache = ModelCache()
|
model_cache = ModelCache()
|
||||||
await model_cache.get("test_model_name", ModelType.FACIAL_RECOGNITION, ttl=100)
|
await model_cache.get("test_model_name", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION, ttl=100)
|
||||||
mock_lock_cls.return_value.__aenter__.return_value.cas.assert_called_with(mock.ANY, ttl=100)
|
mock_lock_cls.return_value.__aenter__.return_value.cas.assert_called_with(mock.ANY, ttl=100)
|
||||||
|
|
||||||
@mock.patch("app.models.cache.SimpleMemoryCache.expire")
|
@mock.patch("app.models.cache.SimpleMemoryCache.expire")
|
||||||
async def test_revalidate_get(self, mock_cache_expire: mock.Mock, mock_get_model: mock.Mock) -> None:
|
async def test_revalidate_get(self, mock_cache_expire: mock.Mock, mock_get_model: mock.Mock) -> None:
|
||||||
model_cache = ModelCache(revalidate=True)
|
model_cache = ModelCache(revalidate=True)
|
||||||
await model_cache.get("test_model_name", ModelType.FACIAL_RECOGNITION, ttl=100)
|
await model_cache.get("test_model_name", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION, ttl=100)
|
||||||
await model_cache.get("test_model_name", ModelType.FACIAL_RECOGNITION, ttl=100)
|
await model_cache.get("test_model_name", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION, ttl=100)
|
||||||
mock_cache_expire.assert_called_once_with(mock.ANY, 100)
|
mock_cache_expire.assert_called_once_with(mock.ANY, 100)
|
||||||
|
|
||||||
async def test_profiling(self, mock_get_model: mock.Mock) -> None:
|
async def test_profiling(self, mock_get_model: mock.Mock) -> None:
|
||||||
model_cache = ModelCache(profiling=True)
|
model_cache = ModelCache(profiling=True)
|
||||||
await model_cache.get("test_model_name", ModelType.FACIAL_RECOGNITION, ttl=100)
|
await model_cache.get("test_model_name", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION, ttl=100)
|
||||||
profiling = await model_cache.get_profiling()
|
profiling = await model_cache.get_profiling()
|
||||||
assert isinstance(profiling, dict)
|
assert isinstance(profiling, dict)
|
||||||
assert profiling == model_cache.cache.profiling
|
assert profiling == model_cache.cache.profiling
|
||||||
@@ -510,9 +540,9 @@ class TestCache:
|
|||||||
async def test_loads_mclip(self) -> None:
|
async def test_loads_mclip(self) -> None:
|
||||||
model_cache = ModelCache()
|
model_cache = ModelCache()
|
||||||
|
|
||||||
model = await model_cache.get("XLM-Roberta-Large-Vit-B-32", ModelType.CLIP, mode="text")
|
model = await model_cache.get("XLM-Roberta-Large-Vit-B-32", ModelType.TEXTUAL, ModelTask.SEARCH)
|
||||||
|
|
||||||
assert isinstance(model, MCLIPEncoder)
|
assert isinstance(model, MClipTextualEncoder)
|
||||||
assert model.model_name == "XLM-Roberta-Large-Vit-B-32"
|
assert model.model_name == "XLM-Roberta-Large-Vit-B-32"
|
||||||
|
|
||||||
async def test_raises_exception_if_invalid_model_type(self) -> None:
|
async def test_raises_exception_if_invalid_model_type(self) -> None:
|
||||||
@@ -520,15 +550,55 @@ class TestCache:
|
|||||||
model_cache = ModelCache()
|
model_cache = ModelCache()
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
await model_cache.get("XLM-Roberta-Large-Vit-B-32", invalid, mode="text")
|
await model_cache.get("XLM-Roberta-Large-Vit-B-32", ModelType.TEXTUAL, invalid)
|
||||||
|
|
||||||
async def test_raises_exception_if_unknown_model_name(self) -> None:
|
async def test_raises_exception_if_unknown_model_name(self) -> None:
|
||||||
model_cache = ModelCache()
|
model_cache = ModelCache()
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
await model_cache.get("test_model_name", ModelType.CLIP, mode="text")
|
await model_cache.get("test_model_name", ModelType.TEXTUAL, ModelTask.SEARCH)
|
||||||
|
|
||||||
async def test_preloads_models(self, monkeypatch: MonkeyPatch, mock_get_model: mock.Mock) -> None:
|
async def test_preloads_clip_models(self, monkeypatch: MonkeyPatch, mock_get_model: mock.Mock) -> None:
|
||||||
|
os.environ["MACHINE_LEARNING_PRELOAD__CLIP"] = "ViT-B-32__openai"
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
assert settings.preload is not None
|
||||||
|
assert settings.preload.clip == "ViT-B-32__openai"
|
||||||
|
|
||||||
|
model_cache = ModelCache()
|
||||||
|
monkeypatch.setattr("app.main.model_cache", model_cache)
|
||||||
|
|
||||||
|
await preload_models(settings.preload)
|
||||||
|
mock_get_model.assert_has_calls(
|
||||||
|
[
|
||||||
|
mock.call("ViT-B-32__openai", ModelType.TEXTUAL, ModelTask.SEARCH),
|
||||||
|
mock.call("ViT-B-32__openai", ModelType.VISUAL, ModelTask.SEARCH),
|
||||||
|
],
|
||||||
|
any_order=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def test_preloads_facial_recognition_models(
|
||||||
|
self, monkeypatch: MonkeyPatch, mock_get_model: mock.Mock
|
||||||
|
) -> None:
|
||||||
|
os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION"] = "buffalo_s"
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
assert settings.preload is not None
|
||||||
|
assert settings.preload.facial_recognition == "buffalo_s"
|
||||||
|
|
||||||
|
model_cache = ModelCache()
|
||||||
|
monkeypatch.setattr("app.main.model_cache", model_cache)
|
||||||
|
|
||||||
|
await preload_models(settings.preload)
|
||||||
|
mock_get_model.assert_has_calls(
|
||||||
|
[
|
||||||
|
mock.call("buffalo_s", ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION),
|
||||||
|
mock.call("buffalo_s", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION),
|
||||||
|
],
|
||||||
|
any_order=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def test_preloads_all_models(self, monkeypatch: MonkeyPatch, mock_get_model: mock.Mock) -> None:
|
||||||
os.environ["MACHINE_LEARNING_PRELOAD__CLIP"] = "ViT-B-32__openai"
|
os.environ["MACHINE_LEARNING_PRELOAD__CLIP"] = "ViT-B-32__openai"
|
||||||
os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION"] = "buffalo_s"
|
os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION"] = "buffalo_s"
|
||||||
|
|
||||||
@@ -541,11 +611,15 @@ class TestCache:
|
|||||||
monkeypatch.setattr("app.main.model_cache", model_cache)
|
monkeypatch.setattr("app.main.model_cache", model_cache)
|
||||||
|
|
||||||
await preload_models(settings.preload)
|
await preload_models(settings.preload)
|
||||||
assert len(model_cache.cache._cache) == 2
|
mock_get_model.assert_has_calls(
|
||||||
assert mock_get_model.call_count == 2
|
[
|
||||||
await model_cache.get("ViT-B-32__openai", ModelType.CLIP, ttl=100)
|
mock.call("ViT-B-32__openai", ModelType.TEXTUAL, ModelTask.SEARCH),
|
||||||
await model_cache.get("buffalo_s", ModelType.FACIAL_RECOGNITION, ttl=100)
|
mock.call("ViT-B-32__openai", ModelType.VISUAL, ModelTask.SEARCH),
|
||||||
assert mock_get_model.call_count == 2
|
mock.call("buffalo_s", ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION),
|
||||||
|
mock.call("buffalo_s", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION),
|
||||||
|
],
|
||||||
|
any_order=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -572,7 +646,8 @@ class TestLoad:
|
|||||||
async def test_load_clears_cache_and_retries_if_os_error(self) -> None:
|
async def test_load_clears_cache_and_retries_if_os_error(self) -> None:
|
||||||
mock_model = mock.Mock(spec=InferenceModel)
|
mock_model = mock.Mock(spec=InferenceModel)
|
||||||
mock_model.model_name = "test_model_name"
|
mock_model.model_name = "test_model_name"
|
||||||
mock_model.model_type = ModelType.CLIP
|
mock_model.model_type = ModelType.VISUAL
|
||||||
|
mock_model.model_task = ModelTask.SEARCH
|
||||||
mock_model.load.side_effect = [OSError, None]
|
mock_model.load.side_effect = [OSError, None]
|
||||||
mock_model.loaded = False
|
mock_model.loaded = False
|
||||||
|
|
||||||
@@ -597,13 +672,15 @@ class TestEndpoints:
|
|||||||
|
|
||||||
response = deployed_app.post(
|
response = deployed_app.post(
|
||||||
"http://localhost:3003/predict",
|
"http://localhost:3003/predict",
|
||||||
data={"modelName": "ViT-B-32__openai", "modelType": "clip", "options": json.dumps({"mode": "vision"})},
|
data={"entries": json.dumps({"clip": {"visual": {"modelName": "ViT-B-32__openai"}}})},
|
||||||
files={"image": byte_image.getvalue()},
|
files={"image": byte_image.getvalue()},
|
||||||
)
|
)
|
||||||
|
|
||||||
actual = response.json()
|
actual = response.json()
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert np.allclose(expected, actual)
|
assert isinstance(actual, dict)
|
||||||
|
assert isinstance(actual.get("clip", None), list)
|
||||||
|
assert np.allclose(expected, actual["clip"])
|
||||||
|
|
||||||
def test_clip_text_endpoint(self, responses: dict[str, Any], deployed_app: TestClient) -> None:
|
def test_clip_text_endpoint(self, responses: dict[str, Any], deployed_app: TestClient) -> None:
|
||||||
expected = responses["clip"]["text"]
|
expected = responses["clip"]["text"]
|
||||||
@@ -611,38 +688,49 @@ class TestEndpoints:
|
|||||||
response = deployed_app.post(
|
response = deployed_app.post(
|
||||||
"http://localhost:3003/predict",
|
"http://localhost:3003/predict",
|
||||||
data={
|
data={
|
||||||
"modelName": "ViT-B-32__openai",
|
"entries": json.dumps(
|
||||||
"modelType": "clip",
|
{
|
||||||
|
"clip": {"textual": {"modelName": "ViT-B-32__openai"}},
|
||||||
|
},
|
||||||
|
),
|
||||||
"text": "test search query",
|
"text": "test search query",
|
||||||
"options": json.dumps({"mode": "text"}),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
actual = response.json()
|
actual = response.json()
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert np.allclose(expected, actual)
|
assert isinstance(actual, dict)
|
||||||
|
assert isinstance(actual.get("clip", None), list)
|
||||||
|
assert np.allclose(expected, actual["clip"])
|
||||||
|
|
||||||
def test_face_endpoint(self, pil_image: Image.Image, responses: dict[str, Any], deployed_app: TestClient) -> None:
|
def test_face_endpoint(self, pil_image: Image.Image, responses: dict[str, Any], deployed_app: TestClient) -> None:
|
||||||
byte_image = BytesIO()
|
byte_image = BytesIO()
|
||||||
pil_image.save(byte_image, format="jpeg")
|
pil_image.save(byte_image, format="jpeg")
|
||||||
expected = responses["facial-recognition"]
|
|
||||||
|
|
||||||
response = deployed_app.post(
|
response = deployed_app.post(
|
||||||
"http://localhost:3003/predict",
|
"http://localhost:3003/predict",
|
||||||
data={
|
data={
|
||||||
"modelName": "buffalo_l",
|
"entries": json.dumps(
|
||||||
"modelType": "facial-recognition",
|
{
|
||||||
"options": json.dumps({"minScore": 0.034}),
|
"facial-recognition": {
|
||||||
|
"detection": {"modelName": "buffalo_l", "options": {"minScore": 0.034}},
|
||||||
|
"recognition": {"modelName": "buffalo_l"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
},
|
},
|
||||||
files={"image": byte_image.getvalue()},
|
files={"image": byte_image.getvalue()},
|
||||||
)
|
)
|
||||||
|
|
||||||
actual = response.json()
|
actual = response.json()
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert len(expected) == len(actual)
|
assert isinstance(actual, dict)
|
||||||
for expected_face, actual_face in zip(expected, actual):
|
assert actual.get("imageHeight", None) == responses["imageHeight"]
|
||||||
assert expected_face["imageHeight"] == actual_face["imageHeight"]
|
assert actual.get("imageWidth", None) == responses["imageWidth"]
|
||||||
assert expected_face["imageWidth"] == actual_face["imageWidth"]
|
assert "facial-recognition" in actual and isinstance(actual["facial-recognition"], list)
|
||||||
|
assert len(actual["facial-recognition"]) == len(responses["facial-recognition"])
|
||||||
|
|
||||||
|
for expected_face, actual_face in zip(responses["facial-recognition"], actual["facial-recognition"]):
|
||||||
assert expected_face["boundingBox"] == actual_face["boundingBox"]
|
assert expected_face["boundingBox"] == actual_face["boundingBox"]
|
||||||
assert np.allclose(expected_face["embedding"], actual_face["embedding"])
|
assert np.allclose(expected_face["embedding"], actual_face["embedding"])
|
||||||
assert np.allclose(expected_face["score"], actual_face["score"])
|
assert np.allclose(expected_face["score"], actual_face["score"])
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ def on_test_start(environment: Environment, **kwargs: Any) -> None:
|
|||||||
global byte_image
|
global byte_image
|
||||||
assert environment.parsed_options is not None
|
assert environment.parsed_options is not None
|
||||||
image = Image.new("RGB", (environment.parsed_options.image_size, environment.parsed_options.image_size))
|
image = Image.new("RGB", (environment.parsed_options.image_size, environment.parsed_options.image_size))
|
||||||
byte_image = BytesIO()
|
|
||||||
image.save(byte_image, format="jpeg")
|
image.save(byte_image, format="jpeg")
|
||||||
|
|
||||||
|
|
||||||
@@ -45,34 +44,25 @@ class InferenceLoadTest(HttpUser):
|
|||||||
abstract: bool = True
|
abstract: bool = True
|
||||||
host = "http://127.0.0.1:3003"
|
host = "http://127.0.0.1:3003"
|
||||||
data: bytes
|
data: bytes
|
||||||
headers: dict[str, str] = {"Content-Type": "image/jpg"}
|
|
||||||
|
|
||||||
# re-use the image across all instances in a process
|
# re-use the image across all instances in a process
|
||||||
def on_start(self) -> None:
|
def on_start(self) -> None:
|
||||||
global byte_image
|
|
||||||
self.data = byte_image.getvalue()
|
self.data = byte_image.getvalue()
|
||||||
|
|
||||||
|
|
||||||
class CLIPTextFormDataLoadTest(InferenceLoadTest):
|
class CLIPTextFormDataLoadTest(InferenceLoadTest):
|
||||||
@task
|
@task
|
||||||
def encode_text(self) -> None:
|
def encode_text(self) -> None:
|
||||||
data = [
|
request = {"clip": {"textual": {"modelName": self.environment.parsed_options.clip_model}}}
|
||||||
("modelName", self.environment.parsed_options.clip_model),
|
data = [("entries", json.dumps(request)), ("text", "test search query")]
|
||||||
("modelType", "clip"),
|
|
||||||
("options", json.dumps({"mode": "text"})),
|
|
||||||
("text", "test search query"),
|
|
||||||
]
|
|
||||||
self.client.post("/predict", data=data)
|
self.client.post("/predict", data=data)
|
||||||
|
|
||||||
|
|
||||||
class CLIPVisionFormDataLoadTest(InferenceLoadTest):
|
class CLIPVisionFormDataLoadTest(InferenceLoadTest):
|
||||||
@task
|
@task
|
||||||
def encode_image(self) -> None:
|
def encode_image(self) -> None:
|
||||||
data = [
|
request = {"clip": {"visual": {"modelName": self.environment.parsed_options.clip_model, "options": {}}}}
|
||||||
("modelName", self.environment.parsed_options.clip_model),
|
data = [("entries", json.dumps(request))]
|
||||||
("modelType", "clip"),
|
|
||||||
("options", json.dumps({"mode": "vision"})),
|
|
||||||
]
|
|
||||||
files = {"image": self.data}
|
files = {"image": self.data}
|
||||||
self.client.post("/predict", data=data, files=files)
|
self.client.post("/predict", data=data, files=files)
|
||||||
|
|
||||||
@@ -80,11 +70,18 @@ class CLIPVisionFormDataLoadTest(InferenceLoadTest):
|
|||||||
class RecognitionFormDataLoadTest(InferenceLoadTest):
|
class RecognitionFormDataLoadTest(InferenceLoadTest):
|
||||||
@task
|
@task
|
||||||
def recognize(self) -> None:
|
def recognize(self) -> None:
|
||||||
data = [
|
request = {
|
||||||
("modelName", self.environment.parsed_options.face_model),
|
"facial-recognition": {
|
||||||
("modelType", "facial-recognition"),
|
"recognition": {
|
||||||
("options", json.dumps({"minScore": self.environment.parsed_options.face_min_score})),
|
"modelName": self.environment.parsed_options.face_model,
|
||||||
]
|
"options": {"minScore": self.environment.parsed_options.face_min_score},
|
||||||
|
},
|
||||||
|
"detection": {
|
||||||
|
"modelName": self.environment.parsed_options.face_model,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data = [("entries", json.dumps(request))]
|
||||||
files = {"image": self.data}
|
files = {"image": self.data}
|
||||||
|
|
||||||
self.client.post("/predict", data=data, files=files)
|
self.client.post("/predict", data=data, files=files)
|
||||||
|
|||||||
64
machine-learning/poetry.lock
generated
@@ -1236,13 +1236,13 @@ socks = ["socksio (==1.*)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "huggingface-hub"
|
name = "huggingface-hub"
|
||||||
version = "0.23.2"
|
version = "0.23.3"
|
||||||
description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
|
description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8.0"
|
python-versions = ">=3.8.0"
|
||||||
files = [
|
files = [
|
||||||
{file = "huggingface_hub-0.23.2-py3-none-any.whl", hash = "sha256:48727a16e704d409c4bb5913613308499664f22a99743435dc3a13b23c485827"},
|
{file = "huggingface_hub-0.23.3-py3-none-any.whl", hash = "sha256:22222c41223f1b7c209ae5511d2d82907325a0e3cdbce5f66949d43c598ff3bc"},
|
||||||
{file = "huggingface_hub-0.23.2.tar.gz", hash = "sha256:f6829b62d5fdecb452a76fdbec620cba4c1573655a8d710c1df71735fd9edbd2"},
|
{file = "huggingface_hub-0.23.3.tar.gz", hash = "sha256:1a1118a0b3dea3bab6c325d71be16f5ffe441d32f3ac7c348d6875911b694b5b"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -2054,18 +2054,18 @@ sympy = "*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opencv-python-headless"
|
name = "opencv-python-headless"
|
||||||
version = "4.9.0.80"
|
version = "4.10.0.82"
|
||||||
description = "Wrapper package for OpenCV python bindings."
|
description = "Wrapper package for OpenCV python bindings."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
files = [
|
files = [
|
||||||
{file = "opencv-python-headless-4.9.0.80.tar.gz", hash = "sha256:71a4cd8cf7c37122901d8e81295db7fb188730e33a0e40039a4e59c1030b0958"},
|
{file = "opencv-python-headless-4.10.0.82.tar.gz", hash = "sha256:de9e742c1b9540816fbd115b0b03841d41ed0c65566b0d7a5371f98b131b7e6d"},
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-macosx_10_16_x86_64.whl", hash = "sha256:2ea8a2edc4db87841991b2fbab55fc07b97ecb602e0f47d5d485bd75cee17c1a"},
|
{file = "opencv_python_headless-4.10.0.82-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a09ed50ba21cc5bf5d436cb0e784ad09c692d6b1d1454252772f6c8f2c7b4088"},
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:e0ee54e27be493e8f7850847edae3128e18b540dac1d7b2e4001b8944e11e1c6"},
|
{file = "opencv_python_headless-4.10.0.82-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:977a5fd21e1fe0d3d2134887db4441f8725abeae95150126302f31fcd9f548fa"},
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57ce2865e8fec431c6f97a81e9faaf23fa5be61011d0a75ccf47a3c0d65fa73d"},
|
{file = "opencv_python_headless-4.10.0.82-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db4ec6755838b0be12510bfc9ffb014779c612418f11f4f7e6f505c36124a3aa"},
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:976656362d68d9f40a5c66f83901430538002465f7db59142784f3893918f3df"},
|
{file = "opencv_python_headless-4.10.0.82-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a37fa5276967ecf6eb297295b16b28b7a2eb3b568ca0ee469fb1a5954de298"},
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-win32.whl", hash = "sha256:11e3849d83e6651d4e7699aadda9ec7ed7c38957cbbcb99db074f2a2d2de9670"},
|
{file = "opencv_python_headless-4.10.0.82-cp37-abi3-win32.whl", hash = "sha256:94736e9b322d13db4768fd35588ad5e8995e78e207263076bfbee18aac835ad5"},
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-win_amd64.whl", hash = "sha256:a8056c2cb37cd65dfcdf4153ca16f7362afcf3a50d600d6bb69c660fc61ee29c"},
|
{file = "opencv_python_headless-4.10.0.82-cp37-abi3-win_amd64.whl", hash = "sha256:c1822fa23d1641c0249ed5eb906f4c385f7959ff1bd601a776d56b0c18914af4"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -2438,13 +2438,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest"
|
name = "pytest"
|
||||||
version = "8.2.1"
|
version = "8.2.2"
|
||||||
description = "pytest: simple powerful testing with Python"
|
description = "pytest: simple powerful testing with Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"},
|
{file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"},
|
||||||
{file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"},
|
{file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -2799,28 +2799,28 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.4.7"
|
version = "0.4.8"
|
||||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "ruff-0.4.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e089371c67892a73b6bb1525608e89a2aca1b77b5440acf7a71dda5dac958f9e"},
|
{file = "ruff-0.4.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7663a6d78f6adb0eab270fa9cf1ff2d28618ca3a652b60f2a234d92b9ec89066"},
|
||||||
{file = "ruff-0.4.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:10f973d521d910e5f9c72ab27e409e839089f955be8a4c8826601a6323a89753"},
|
{file = "ruff-0.4.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eeceb78da8afb6de0ddada93112869852d04f1cd0f6b80fe464fd4e35c330913"},
|
||||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59c3d110970001dfa494bcd95478e62286c751126dfb15c3c46e7915fc49694f"},
|
{file = "ruff-0.4.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aad360893e92486662ef3be0a339c5ca3c1b109e0134fcd37d534d4be9fb8de3"},
|
||||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa9773c6c00f4958f73b317bc0fd125295110c3776089f6ef318f4b775f0abe4"},
|
{file = "ruff-0.4.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:284c2e3f3396fb05f5f803c9fffb53ebbe09a3ebe7dda2929ed8d73ded736deb"},
|
||||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07fc80bbb61e42b3b23b10fda6a2a0f5a067f810180a3760c5ef1b456c21b9db"},
|
{file = "ruff-0.4.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7354f921e3fbe04d2a62d46707e569f9315e1a613307f7311a935743c51a764"},
|
||||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fa4dafe3fe66d90e2e2b63fa1591dd6e3f090ca2128daa0be33db894e6c18648"},
|
{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:72584676164e15a68a15778fd1b17c28a519e7a0622161eb2debdcdabdc71883"},
|
||||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7c0083febdec17571455903b184a10026603a1de078428ba155e7ce9358c5f6"},
|
{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9678d5c9b43315f323af2233a04d747409d1e3aa6789620083a82d1066a35199"},
|
||||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad1b20e66a44057c326168437d680a2166c177c939346b19c0d6b08a62a37589"},
|
{file = "ruff-0.4.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704977a658131651a22b5ebeb28b717ef42ac6ee3b11e91dc87b633b5d83142b"},
|
||||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf5d818553add7511c38b05532d94a407f499d1a76ebb0cad0374e32bc67202"},
|
{file = "ruff-0.4.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05f8d6f0c3cce5026cecd83b7a143dcad503045857bc49662f736437380ad45"},
|
||||||
{file = "ruff-0.4.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:50e9651578b629baec3d1513b2534de0ac7ed7753e1382272b8d609997e27e83"},
|
{file = "ruff-0.4.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ea874950daca5697309d976c9afba830d3bf0ed66887481d6bca1673fc5b66a"},
|
||||||
{file = "ruff-0.4.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8874a9df7766cb956b218a0a239e0a5d23d9e843e4da1e113ae1d27ee420877a"},
|
{file = "ruff-0.4.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fc95aac2943ddf360376be9aa3107c8cf9640083940a8c5bd824be692d2216dc"},
|
||||||
{file = "ruff-0.4.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b9de9a6e49f7d529decd09381c0860c3f82fa0b0ea00ea78409b785d2308a567"},
|
{file = "ruff-0.4.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:384154a1c3f4bf537bac69f33720957ee49ac8d484bfc91720cc94172026ceed"},
|
||||||
{file = "ruff-0.4.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:13a1768b0691619822ae6d446132dbdfd568b700ecd3652b20d4e8bc1e498f78"},
|
{file = "ruff-0.4.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9d5ce97cacc99878aa0d084c626a15cd21e6b3d53fd6f9112b7fc485918e1fa"},
|
||||||
{file = "ruff-0.4.7-py3-none-win32.whl", hash = "sha256:769e5a51df61e07e887b81e6f039e7ed3573316ab7dd9f635c5afaa310e4030e"},
|
{file = "ruff-0.4.8-py3-none-win32.whl", hash = "sha256:6d795d7639212c2dfd01991259460101c22aabf420d9b943f153ab9d9706e6a9"},
|
||||||
{file = "ruff-0.4.7-py3-none-win_amd64.whl", hash = "sha256:9e3ab684ad403a9ed1226894c32c3ab9c2e0718440f6f50c7c5829932bc9e054"},
|
{file = "ruff-0.4.8-py3-none-win_amd64.whl", hash = "sha256:e14a3a095d07560a9d6769a72f781d73259655919d9b396c650fc98a8157555d"},
|
||||||
{file = "ruff-0.4.7-py3-none-win_arm64.whl", hash = "sha256:10f2204b9a613988e3484194c2c9e96a22079206b22b787605c255f130db5ed7"},
|
{file = "ruff-0.4.8-py3-none-win_arm64.whl", hash = "sha256:14019a06dbe29b608f6b7cbcec300e3170a8d86efaddb7b23405cb7f7dcaf780"},
|
||||||
{file = "ruff-0.4.7.tar.gz", hash = "sha256:2331d2b051dc77a289a653fcc6a42cce357087c5975738157cd966590b18b5e1"},
|
{file = "ruff-0.4.8.tar.gz", hash = "sha256:16d717b1d57b2e2fd68bd0bf80fb43931b79d05a7131aa477d66fc40fbd86268"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "machine-learning"
|
name = "machine-learning"
|
||||||
version = "1.105.1"
|
version = "1.106.3"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -213,8 +213,6 @@
|
|||||||
},
|
},
|
||||||
"facial-recognition": [
|
"facial-recognition": [
|
||||||
{
|
{
|
||||||
"imageWidth": 600,
|
|
||||||
"imageHeight": 800,
|
|
||||||
"boundingBox": {
|
"boundingBox": {
|
||||||
"x1": 690.0,
|
"x1": 690.0,
|
||||||
"y1": -89.0,
|
"y1": -89.0,
|
||||||
@@ -325,5 +323,7 @@
|
|||||||
-0.077056274, 0.002099529
|
-0.077056274, 0.002099529
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"imageWidth": 600,
|
||||||
|
"imageHeight": 800
|
||||||
}
|
}
|
||||||
|
|||||||
17
misc/release/archive-version.js
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#! /usr/bin/env node
|
||||||
|
const { readFileSync, writeFileSync } = require('node:fs');
|
||||||
|
|
||||||
|
const nextVersion = process.argv[2];
|
||||||
|
if (!nextVersion) {
|
||||||
|
console.log('Usage: archive-version.js <version>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = './docs/static/archived-versions.json';
|
||||||
|
const oldVersions = JSON.parse(readFileSync(filename));
|
||||||
|
const newVersions = [
|
||||||
|
{ label: `v${nextVersion}`, url: `https://v${nextVersion}.archive.immich.app` },
|
||||||
|
...oldVersions,
|
||||||
|
];
|
||||||
|
|
||||||
|
writeFileSync(filename, JSON.stringify(newVersions, null, 2) + '\n');
|
||||||
@@ -66,10 +66,12 @@ if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then
|
|||||||
npm --prefix server run build
|
npm --prefix server run build
|
||||||
make open-api
|
make open-api
|
||||||
npm --prefix open-api/typescript-sdk version "$SERVER_PUMP"
|
npm --prefix open-api/typescript-sdk version "$SERVER_PUMP"
|
||||||
npm --prefix web version "$SERVER_PUMP"
|
# TODO use $SERVER_PUMP once we pass 2.2.x
|
||||||
npm --prefix e2e version "$SERVER_PUMP"
|
npm --prefix cli version patch
|
||||||
npm --prefix web i --package-lock-only
|
|
||||||
npm --prefix cli i --package-lock-only
|
npm --prefix cli i --package-lock-only
|
||||||
|
npm --prefix web version "$SERVER_PUMP"
|
||||||
|
npm --prefix web i --package-lock-only
|
||||||
|
npm --prefix e2e version "$SERVER_PUMP"
|
||||||
npm --prefix e2e i --package-lock-only
|
npm --prefix e2e i --package-lock-only
|
||||||
poetry --directory machine-learning version "$SERVER_PUMP"
|
poetry --directory machine-learning version "$SERVER_PUMP"
|
||||||
fi
|
fi
|
||||||
@@ -83,4 +85,6 @@ sed -i "s/version_number: \"$CURRENT_SERVER\"$/version_number: \"$NEXT_SERVER\"/
|
|||||||
sed -i "s/\"android\.injected\.version\.code\" => $CURRENT_MOBILE,/\"android\.injected\.version\.code\" => $NEXT_MOBILE,/" mobile/android/fastlane/Fastfile
|
sed -i "s/\"android\.injected\.version\.code\" => $CURRENT_MOBILE,/\"android\.injected\.version\.code\" => $NEXT_MOBILE,/" mobile/android/fastlane/Fastfile
|
||||||
sed -i "s/^version: $CURRENT_SERVER+$CURRENT_MOBILE$/version: $NEXT_SERVER+$NEXT_MOBILE/" mobile/pubspec.yaml
|
sed -i "s/^version: $CURRENT_SERVER+$CURRENT_MOBILE$/version: $NEXT_SERVER+$NEXT_MOBILE/" mobile/pubspec.yaml
|
||||||
|
|
||||||
|
./misc/release/archive-version.js "$NEXT_SERVER"
|
||||||
|
|
||||||
echo "IMMICH_VERSION=v$NEXT_SERVER" >>"$GITHUB_ENV"
|
echo "IMMICH_VERSION=v$NEXT_SERVER" >>"$GITHUB_ENV"
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ platform :android do
|
|||||||
task: 'bundle',
|
task: 'bundle',
|
||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 140,
|
"android.injected.version.code" => 143,
|
||||||
"android.injected.version.name" => "1.105.1",
|
"android.injected.version.name" => "1.106.3",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||||
|
|||||||
@@ -5,17 +5,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000374">
|
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000381">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="84.292464">
|
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="52.832426">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="33.336934">
|
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="27.616558">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"action_common_back": "Back",
|
"action_common_back": "خلف",
|
||||||
"action_common_cancel": "يلغي",
|
"action_common_cancel": "يلغي",
|
||||||
"action_common_clear": "Clear",
|
"action_common_clear": "مسح",
|
||||||
"action_common_confirm": "Confirm",
|
"action_common_confirm": "تأكيد",
|
||||||
"action_common_update": "تحديث",
|
"action_common_update": "تحديث",
|
||||||
"add_to_album_bottom_sheet_added": "تمت الاضافة{album}",
|
"add_to_album_bottom_sheet_added": "تمت الاضافة{album}",
|
||||||
"add_to_album_bottom_sheet_already_exists": "موجودة مسبقا {album}",
|
"add_to_album_bottom_sheet_already_exists": "موجودة مسبقا {album}",
|
||||||
"advanced_settings_log_level_title": "تسجيل مستوى: {}",
|
"advanced_settings_log_level_title": "Log level: {}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "تكون بعض الأجهزة بطيئة للغاية في تحميل الصور المصغرة من الأصول الموجودة على الجهاز. قم بتنشيط هذا الإعداد لتحميل الصور البعيدة بدلاً من ذلك.",
|
"advanced_settings_prefer_remote_subtitle": "تكون بعض الأجهزة بطيئة للغاية في تحميل الصور المصغرة من الأصول الموجودة على الجهاز. قم بتنشيط هذا الإعداد لتحميل الصور البعيدة بدلاً من ذلك.",
|
||||||
"advanced_settings_prefer_remote_title": "تفضل الصور البعيدة",
|
"advanced_settings_prefer_remote_title": "تفضل الصور البعيدة",
|
||||||
"advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.",
|
"advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.",
|
||||||
@@ -18,11 +18,11 @@
|
|||||||
"album_info_card_backup_album_excluded": "مستبعد",
|
"album_info_card_backup_album_excluded": "مستبعد",
|
||||||
"album_info_card_backup_album_included": "متضمنة",
|
"album_info_card_backup_album_included": "متضمنة",
|
||||||
"album_thumbnail_card_item": "عنصر واحد",
|
"album_thumbnail_card_item": "عنصر واحد",
|
||||||
"album_thumbnail_card_items": "{} عناصر",
|
"album_thumbnail_card_items": "{} items",
|
||||||
"album_thumbnail_card_shared": " · . مشترك",
|
"album_thumbnail_card_shared": " · . مشترك",
|
||||||
"album_thumbnail_owned": "مملوكة",
|
"album_thumbnail_owned": "مملوكة",
|
||||||
"album_thumbnail_shared_by": "مشترك مع",
|
"album_thumbnail_shared_by": "Shared by {}",
|
||||||
"album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?",
|
"album_viewer_appbar_delete_confirm": "هل أنت متأكد أنك تريد حذف هذا الألبوم من حسابك؟",
|
||||||
"album_viewer_appbar_share_delete": "حذف الألبوم",
|
"album_viewer_appbar_share_delete": "حذف الألبوم",
|
||||||
"album_viewer_appbar_share_err_delete": "فشل في حذف الألبوم",
|
"album_viewer_appbar_share_err_delete": "فشل في حذف الألبوم",
|
||||||
"album_viewer_appbar_share_err_leave": "فشل في ترك الألبوم",
|
"album_viewer_appbar_share_err_leave": "فشل في ترك الألبوم",
|
||||||
@@ -38,20 +38,20 @@
|
|||||||
"app_bar_signout_dialog_ok": "نعم",
|
"app_bar_signout_dialog_ok": "نعم",
|
||||||
"app_bar_signout_dialog_title": "خروج",
|
"app_bar_signout_dialog_title": "خروج",
|
||||||
"archive_page_no_archived_assets": "لم يتم العثور على الأصول المؤرشفة",
|
"archive_page_no_archived_assets": "لم يتم العثور على الأصول المؤرشفة",
|
||||||
"archive_page_title": "أرشيف ({})",
|
"archive_page_title": "Archive ({})",
|
||||||
"asset_action_delete_err_read_only": "لا يمكن حذف الأصول ذات للقراءة فقط، وسوف يتم التخطي",
|
"asset_action_delete_err_read_only": "لا يمكن حذف الأصول ذات للقراءة فقط، وسوف يتم التخطي",
|
||||||
"asset_action_share_err_offline": "لا يمكن جلب الأصول غير المتصلة بالإنترنت، وسوف يتم التخطي",
|
"asset_action_share_err_offline": "لا يمكن جلب الأصول غير المتصلة بالإنترنت، وسوف يتم التخطي",
|
||||||
"asset_list_group_by_sub_title": "Group by",
|
"asset_list_group_by_sub_title": "تنظيم بواسطة",
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "تخطيط ديناميكي",
|
"asset_list_layout_settings_dynamic_layout_title": "تخطيط ديناميكي",
|
||||||
"asset_list_layout_settings_group_automatically": "تلقائي",
|
"asset_list_layout_settings_group_automatically": "تلقائي",
|
||||||
"asset_list_layout_settings_group_by": "مجموعة الأصول حسب",
|
"asset_list_layout_settings_group_by": "مجموعة الأصول حسب",
|
||||||
"asset_list_layout_settings_group_by_month": "شهر",
|
"asset_list_layout_settings_group_by_month": "شهر",
|
||||||
"asset_list_layout_settings_group_by_month_day": "شهر + يوم",
|
"asset_list_layout_settings_group_by_month_day": "شهر + يوم",
|
||||||
"asset_list_layout_sub_title": "Layout",
|
"asset_list_layout_sub_title": "تصميم",
|
||||||
"asset_list_settings_subtitle": "إعدادات تخطيط شبكة الصور",
|
"asset_list_settings_subtitle": "إعدادات تخطيط شبكة الصور",
|
||||||
"asset_list_settings_title": "شبكة الصور",
|
"asset_list_settings_title": "شبكة الصور",
|
||||||
"asset_viewer_settings_title": "Asset Viewer",
|
"asset_viewer_settings_title": "عارض الأصول",
|
||||||
"backup_album_selection_page_albums_device": "الألبومات الموجودة على الجهاز ({})",
|
"backup_album_selection_page_albums_device": "Albums on device ({})",
|
||||||
"backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء",
|
"backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء",
|
||||||
"backup_album_selection_page_assets_scatter": "يمكن أن تنتشر الأصول عبر ألبومات متعددة. وبالتالي، يمكن تضمين الألبومات أو استبعادها أثناء عملية النسخ الاحتياطي.",
|
"backup_album_selection_page_assets_scatter": "يمكن أن تنتشر الأصول عبر ألبومات متعددة. وبالتالي، يمكن تضمين الألبومات أو استبعادها أثناء عملية النسخ الاحتياطي.",
|
||||||
"backup_album_selection_page_select_albums": "حدد الألبومات",
|
"backup_album_selection_page_select_albums": "حدد الألبومات",
|
||||||
@@ -60,22 +60,22 @@
|
|||||||
"backup_all": "الجميع",
|
"backup_all": "الجميع",
|
||||||
"backup_background_service_backup_failed_message": "فشل في النسخ الاحتياطي للأصول. جارٍ إعادة المحاولة...",
|
"backup_background_service_backup_failed_message": "فشل في النسخ الاحتياطي للأصول. جارٍ إعادة المحاولة...",
|
||||||
"backup_background_service_connection_failed_message": "فشل في الاتصال بالخادم. جارٍ إعادة المحاولة...",
|
"backup_background_service_connection_failed_message": "فشل في الاتصال بالخادم. جارٍ إعادة المحاولة...",
|
||||||
"backup_background_service_current_upload_notification": "تحميل {}",
|
"backup_background_service_current_upload_notification": "Uploading {}",
|
||||||
"backup_background_service_default_notification": "التحقق من الأصول الجديدة ...",
|
"backup_background_service_default_notification": "التحقق من الأصول الجديدة ...",
|
||||||
"backup_background_service_error_title": "خطأ في النسخ الاحتياطي",
|
"backup_background_service_error_title": "خطأ في النسخ الاحتياطي",
|
||||||
"backup_background_service_in_progress_notification": "النسخ الاحتياطي للأصول الخاصة بك...",
|
"backup_background_service_in_progress_notification": "النسخ الاحتياطي للأصول الخاصة بك...",
|
||||||
"backup_background_service_upload_failure_notification": "فشل التحميل {}",
|
"backup_background_service_upload_failure_notification": "Failed to upload {}",
|
||||||
"backup_controller_page_albums": "ألبومات احتياطية",
|
"backup_controller_page_albums": "ألبومات احتياطية",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "قم بتمكين تحديث تطبيق الخلفية في الإعدادات > عام > تحديث تطبيق الخلفية لاستخدام النسخ الاحتياطي في الخلفية.",
|
"backup_controller_page_background_app_refresh_disabled_content": "قم بتمكين تحديث تطبيق الخلفية في الإعدادات > عام > تحديث تطبيق الخلفية لاستخدام النسخ الاحتياطي في الخلفية.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "تم تعطيل تحديث التطبيق في الخلفية",
|
"backup_controller_page_background_app_refresh_disabled_title": "تم تعطيل تحديث التطبيق في الخلفية",
|
||||||
"backup_controller_page_background_app_refresh_enable_button_text": "اذهب للاعدادات",
|
"backup_controller_page_background_app_refresh_enable_button_text": "اذهب للاعدادات",
|
||||||
"backup_controller_page_background_battery_info_link": "أرني كيف",
|
"backup_controller_page_background_battery_info_link": "أرني كيف",
|
||||||
"backup_controller_page_background_battery_info_message": "للحصول على أفضل تجربة نسخ احتياطي في الخلفية، يرجى تعطيل أي تحسينات للبطارية تقيد نشاط الخلفية لـ Immich.\n\nنظرًا لأن هذا خاص بالجهاز، يرجى البحث عن المعلومات المطلوبة للشركة المصنعة لجهازك.",
|
"backup_controller_page_background_battery_info_message": "للحصول على أفضل تجربة نسخ احتياطي في الخلفية، يرجى تعطيل أي تحسينات للبطارية تقيد نشاط الخلفية لـ تطبيق.\n\nنظرًا لأن هذا خاص بالجهاز، يرجى البحث عن المعلومات المطلوبة للشركة المصنعة لجهازك.",
|
||||||
"backup_controller_page_background_battery_info_ok": "نعم",
|
"backup_controller_page_background_battery_info_ok": "نعم",
|
||||||
"backup_controller_page_background_battery_info_title": "تحسين البطارية",
|
"backup_controller_page_background_battery_info_title": "تحسين البطارية",
|
||||||
"backup_controller_page_background_charging": "فقط أثناء الشحن",
|
"backup_controller_page_background_charging": "فقط أثناء الشحن",
|
||||||
"backup_controller_page_background_configure_error": "فشل في تكوين خدمة الخلفية",
|
"backup_controller_page_background_configure_error": "فشل في تكوين خدمة الخلفية",
|
||||||
"backup_controller_page_background_delay": "تأخير النسخ الاحتياطي للأصول الجديدة: {}",
|
"backup_controller_page_background_delay": "Delay new assets backup: {}",
|
||||||
"backup_controller_page_background_description": "قم بتشغيل خدمة الخلفية لإجراء نسخ احتياطي لأي أصول جديدة تلقائيًا دون الحاجة إلى فتح التطبيق",
|
"backup_controller_page_background_description": "قم بتشغيل خدمة الخلفية لإجراء نسخ احتياطي لأي أصول جديدة تلقائيًا دون الحاجة إلى فتح التطبيق",
|
||||||
"backup_controller_page_background_is_off": "تم إيقاف النسخ الاحتياطي التلقائي للخلفية",
|
"backup_controller_page_background_is_off": "تم إيقاف النسخ الاحتياطي التلقائي للخلفية",
|
||||||
"backup_controller_page_background_is_on": "النسخ الاحتياطي التلقائي للخلفية قيد التشغيل",
|
"backup_controller_page_background_is_on": "النسخ الاحتياطي التلقائي للخلفية قيد التشغيل",
|
||||||
@@ -86,12 +86,12 @@
|
|||||||
"backup_controller_page_backup_selected": "المحدد: ",
|
"backup_controller_page_backup_selected": "المحدد: ",
|
||||||
"backup_controller_page_backup_sub": "النسخ الاحتياطي للصور ومقاطع الفيديو",
|
"backup_controller_page_backup_sub": "النسخ الاحتياطي للصور ومقاطع الفيديو",
|
||||||
"backup_controller_page_cancel": "يلغي",
|
"backup_controller_page_cancel": "يلغي",
|
||||||
"backup_controller_page_created": "تم إنشاؤها على: {}",
|
"backup_controller_page_created": "Created on: {}",
|
||||||
"backup_controller_page_desc_backup": "قم بتشغيل النسخ الاحتياطي الأمامي لتحميل الأصول الجديدة تلقائيًا إلى الخادم عند فتح التطبيق.",
|
"backup_controller_page_desc_backup": "قم بتشغيل النسخ الاحتياطي الأمامي لتحميل الأصول الجديدة تلقائيًا إلى الخادم عند فتح التطبيق.",
|
||||||
"backup_controller_page_excluded": "مستبعد: ",
|
"backup_controller_page_excluded": "مستبعد: ",
|
||||||
"backup_controller_page_failed": "فشل ({})",
|
"backup_controller_page_failed": "Failed ({})",
|
||||||
"backup_controller_page_filename": "اسم الملف: {} [{}]",
|
"backup_controller_page_filename": "File name: {} [{}]",
|
||||||
"backup_controller_page_id": "رقم البطاقة: {}",
|
"backup_controller_page_id": "ID: {}",
|
||||||
"backup_controller_page_info": "معلومات النسخ الاحتياطي",
|
"backup_controller_page_info": "معلومات النسخ الاحتياطي",
|
||||||
"backup_controller_page_none_selected": "لم يتم التحديد",
|
"backup_controller_page_none_selected": "لم يتم التحديد",
|
||||||
"backup_controller_page_remainder": "بقية",
|
"backup_controller_page_remainder": "بقية",
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
"backup_controller_page_start_backup": "بدء النسخ الاحتياطي",
|
"backup_controller_page_start_backup": "بدء النسخ الاحتياطي",
|
||||||
"backup_controller_page_status_off": "النسخة الاحتياطية التلقائية غير فعالة",
|
"backup_controller_page_status_off": "النسخة الاحتياطية التلقائية غير فعالة",
|
||||||
"backup_controller_page_status_on": "النسخة الاحتياطية التلقائية فعالة",
|
"backup_controller_page_status_on": "النسخة الاحتياطية التلقائية فعالة",
|
||||||
"backup_controller_page_storage_format": "{} من {} المستخدم",
|
"backup_controller_page_storage_format": "{} of {} used",
|
||||||
"backup_controller_page_to_backup": "الألبومات الاحتياطية",
|
"backup_controller_page_to_backup": "الألبومات الاحتياطية",
|
||||||
"backup_controller_page_total": "المجموع",
|
"backup_controller_page_total": "المجموع",
|
||||||
"backup_controller_page_total_sub": "جميع الصور ومقاطع الفيديو الفريدة من ألبومات مختارة",
|
"backup_controller_page_total_sub": "جميع الصور ومقاطع الفيديو الفريدة من ألبومات مختارة",
|
||||||
@@ -115,22 +115,22 @@
|
|||||||
"backup_manual_in_progress": "قيد التحميل حاول مره اخرى",
|
"backup_manual_in_progress": "قيد التحميل حاول مره اخرى",
|
||||||
"backup_manual_success": "نجاح",
|
"backup_manual_success": "نجاح",
|
||||||
"backup_manual_title": "حالة التحميل",
|
"backup_manual_title": "حالة التحميل",
|
||||||
"backup_options_page_title": "Backup options",
|
"backup_options_page_title": "خيارات النسخ الاحتياطي",
|
||||||
"cache_settings_album_thumbnails": "صور صفحة المكتبة ({} الأصول)",
|
"cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
|
||||||
"cache_settings_clear_cache_button": "مسح ذاكرة التخزين المؤقت",
|
"cache_settings_clear_cache_button": "مسح ذاكرة التخزين المؤقت",
|
||||||
"cache_settings_clear_cache_button_title": "يقوم بمسح ذاكرة التخزين المؤقت للتطبيق.سيؤثر هذا بشكل كبير على أداء التطبيق حتى إعادة بناء ذاكرة التخزين المؤقت.",
|
"cache_settings_clear_cache_button_title": "يقوم بمسح ذاكرة التخزين المؤقت للتطبيق.سيؤثر هذا بشكل كبير على أداء التطبيق حتى إعادة بناء ذاكرة التخزين المؤقت.",
|
||||||
"cache_settings_duplicated_assets_clear_button": "واضح",
|
"cache_settings_duplicated_assets_clear_button": "واضح",
|
||||||
"cache_settings_duplicated_assets_subtitle": "الصور ومقاطع الفيديو اللتي تم تجاهلها المدرجة في التطبيق",
|
"cache_settings_duplicated_assets_subtitle": "الصور ومقاطع الفيديو اللتي تم تجاهلها المدرجة في التطبيق",
|
||||||
"cache_settings_duplicated_assets_title": "الأصول المكررة ({})",
|
"cache_settings_duplicated_assets_title": "Duplicated Assets ({})",
|
||||||
"cache_settings_image_cache_size": "حجم ذاكرة التخزين المؤقت للصور ({} الأصول)",
|
"cache_settings_image_cache_size": "Image cache size ({} assets)",
|
||||||
"cache_settings_statistics_album": "مكتبه الصور المصغره",
|
"cache_settings_statistics_album": "مكتبه الصور المصغره",
|
||||||
"cache_settings_statistics_assets": " ({})أصول ",
|
"cache_settings_statistics_assets": "{} assets ({})",
|
||||||
"cache_settings_statistics_full": "صور كاملة",
|
"cache_settings_statistics_full": "صور كاملة",
|
||||||
"cache_settings_statistics_shared": "صورة ألبوم مشتركة",
|
"cache_settings_statistics_shared": "صورة ألبوم مشتركة",
|
||||||
"cache_settings_statistics_thumbnail": "الصورة المصغرة",
|
"cache_settings_statistics_thumbnail": "الصورة المصغرة",
|
||||||
"cache_settings_statistics_title": "استخدام ذاكرة التخزين المؤقت",
|
"cache_settings_statistics_title": "استخدام ذاكرة التخزين المؤقت",
|
||||||
"cache_settings_subtitle": "تحكم في سلوك التخزين المؤقت لتطبيق الجوال Imich",
|
"cache_settings_subtitle": "تحكم في سلوك التخزين المؤقت لتطبيق الجوال.",
|
||||||
"cache_settings_thumbnail_size": "حجم ذاكرة التخزين المؤقت Thumbnail ({} الأصول)",
|
"cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
|
||||||
"cache_settings_tile_subtitle": "التحكم في سلوك التخزين المحلي",
|
"cache_settings_tile_subtitle": "التحكم في سلوك التخزين المحلي",
|
||||||
"cache_settings_tile_title": "التخزين المحلي",
|
"cache_settings_tile_title": "التخزين المحلي",
|
||||||
"cache_settings_title": "إعدادات التخزين المؤقت",
|
"cache_settings_title": "إعدادات التخزين المؤقت",
|
||||||
@@ -145,12 +145,12 @@
|
|||||||
"common_server_error": "يرجى التحقق من اتصال الشبكة الخاص بك ، والتأكد من أن الجهاز قابل للوصول وإصدارات التطبيق/الجهاز متوافقة.",
|
"common_server_error": "يرجى التحقق من اتصال الشبكة الخاص بك ، والتأكد من أن الجهاز قابل للوصول وإصدارات التطبيق/الجهاز متوافقة.",
|
||||||
"common_shared": "مشترك",
|
"common_shared": "مشترك",
|
||||||
"control_bottom_app_bar_add_to_album": "أضف إلى الألبوم",
|
"control_bottom_app_bar_add_to_album": "أضف إلى الألبوم",
|
||||||
"control_bottom_app_bar_album_info": "{} أغراض",
|
"control_bottom_app_bar_album_info": "{} items",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} العناصر المشتركة",
|
"control_bottom_app_bar_album_info_shared": "{} items · Shared",
|
||||||
"control_bottom_app_bar_archive": "أرشيف",
|
"control_bottom_app_bar_archive": "أرشيف",
|
||||||
"control_bottom_app_bar_create_new_album": "إنشاء ألبوم جديد",
|
"control_bottom_app_bar_create_new_album": "إنشاء ألبوم جديد",
|
||||||
"control_bottom_app_bar_delete": "يمسح",
|
"control_bottom_app_bar_delete": "يمسح",
|
||||||
"control_bottom_app_bar_delete_from_immich": " Immich حذف منال تطبيق",
|
"control_bottom_app_bar_delete_from_immich": " حذف منال تطبيق",
|
||||||
"control_bottom_app_bar_delete_from_local": "حذف من الجهاز",
|
"control_bottom_app_bar_delete_from_local": "حذف من الجهاز",
|
||||||
"control_bottom_app_bar_edit_location": "تحديد الوجهة",
|
"control_bottom_app_bar_edit_location": "تحديد الوجهة",
|
||||||
"control_bottom_app_bar_edit_time": "تحرير التاريخ والوقت",
|
"control_bottom_app_bar_edit_time": "تحرير التاريخ والوقت",
|
||||||
@@ -172,10 +172,10 @@
|
|||||||
"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",
|
||||||
"date_format": "E ، Lll D ، Y • H: MM A",
|
"date_format": "E ، Lll D ، Y • H: MM A",
|
||||||
"delete_dialog_alert": " Immich هذه العناصر سيتم حذفها بشكل دائم من جهازك ومن تطبيق",
|
"delete_dialog_alert": " هذه العناصر سيتم حذفها بشكل دائم من جهازك ومن تطبيق",
|
||||||
"delete_dialog_alert_local": " Immich العناصر التي تم حذفها من جهازك ولكنها موجوده في تطبيق",
|
"delete_dialog_alert_local": " العناصر التي تم حذفها من جهازك ولكنها موجوده في تطبيق",
|
||||||
"delete_dialog_alert_local_non_backed_up": "Immich بعض العناصر التي سيتم حذفها بشكل دائم ولا يوجد لها نسخه احتياطيه في تطبيق ",
|
"delete_dialog_alert_local_non_backed_up": "بعض العناصر التي سيتم حذفها بشكل دائم ولا يوجد لها نسخه احتياطيه في تطبيق ",
|
||||||
"delete_dialog_alert_remote": "Immich العناصر التي سيتم حذفها بشكل دائم من تطبيق",
|
"delete_dialog_alert_remote": "العناصر التي سيتم حذفها بشكل دائم من تطبيق",
|
||||||
"delete_dialog_cancel": "يلغي",
|
"delete_dialog_cancel": "يلغي",
|
||||||
"delete_dialog_ok": "يمسح",
|
"delete_dialog_ok": "يمسح",
|
||||||
"delete_dialog_ok_force": "احذف على أي حال",
|
"delete_dialog_ok_force": "احذف على أي حال",
|
||||||
@@ -194,15 +194,15 @@
|
|||||||
"exif_bottom_sheet_location": "موقع",
|
"exif_bottom_sheet_location": "موقع",
|
||||||
"exif_bottom_sheet_location_add": "إضافة موقع",
|
"exif_bottom_sheet_location_add": "إضافة موقع",
|
||||||
"exif_bottom_sheet_people": "الناس",
|
"exif_bottom_sheet_people": "الناس",
|
||||||
"exif_bottom_sheet_person_add_person": "Add name",
|
"exif_bottom_sheet_person_add_person": "اضف اسما",
|
||||||
"experimental_settings_new_asset_list_subtitle": "أعمال جارية",
|
"experimental_settings_new_asset_list_subtitle": "أعمال جارية",
|
||||||
"experimental_settings_new_asset_list_title": "تمكين شبكة الصور التجريبية",
|
"experimental_settings_new_asset_list_title": "تمكين شبكة الصور التجريبية",
|
||||||
"experimental_settings_subtitle": "استخدام على مسؤوليتك الخاصة!",
|
"experimental_settings_subtitle": "استخدام على مسؤوليتك الخاصة!",
|
||||||
"experimental_settings_title": "تجريبي",
|
"experimental_settings_title": "تجريبي",
|
||||||
"favorites_page_no_favorites": "لم يتم العثور على الأصول المفضلة",
|
"favorites_page_no_favorites": "لم يتم العثور على الأصول المفضلة",
|
||||||
"favorites_page_title": "المفضلة",
|
"favorites_page_title": "المفضلة",
|
||||||
"haptic_feedback_switch": "Enable haptic feedback",
|
"haptic_feedback_switch": "تمكين ردود الفعل اللمسية",
|
||||||
"haptic_feedback_title": "Haptic Feedback",
|
"haptic_feedback_title": "ردود فعل لمسية",
|
||||||
"home_page_add_to_album_conflicts": "تمت إضافة {تمت إضافة} الأصول إلى الألبوم {الألبوم}.{فشل} الأصول موجودة بالفعل في الألبوم.",
|
"home_page_add_to_album_conflicts": "تمت إضافة {تمت إضافة} الأصول إلى الألبوم {الألبوم}.{فشل} الأصول موجودة بالفعل في الألبوم.",
|
||||||
"home_page_add_to_album_err_local": "لا يمكن إضافة الأصول المحلية إلى الألبومات حتى الآن ، سوف يتخطى",
|
"home_page_add_to_album_err_local": "لا يمكن إضافة الأصول المحلية إلى الألبومات حتى الآن ، سوف يتخطى",
|
||||||
"home_page_add_to_album_success": "تمت إضافة {تمت إضافة} الأصول إلى الألبوم {الألبوم}.",
|
"home_page_add_to_album_success": "تمت إضافة {تمت إضافة} الأصول إلى الألبوم {الألبوم}.",
|
||||||
@@ -218,7 +218,7 @@
|
|||||||
"home_page_share_err_local": "لا يمكن مشاركة الأصول المحلية عبر الرابط ، سوف يتخطى",
|
"home_page_share_err_local": "لا يمكن مشاركة الأصول المحلية عبر الرابط ، سوف يتخطى",
|
||||||
"home_page_upload_err_limit": "لا يمكن إلا تحميل 30 أحد الأصول في وقت واحد ، سوف يتخطى",
|
"home_page_upload_err_limit": "لا يمكن إلا تحميل 30 أحد الأصول في وقت واحد ، سوف يتخطى",
|
||||||
"image_viewer_page_state_provider_download_error": "خطا في التحميل",
|
"image_viewer_page_state_provider_download_error": "خطا في التحميل",
|
||||||
"image_viewer_page_state_provider_download_started": "Download Started",
|
"image_viewer_page_state_provider_download_started": "بدأ التنزيل",
|
||||||
"image_viewer_page_state_provider_download_success": "تم التنزيل بنجاح",
|
"image_viewer_page_state_provider_download_success": "تم التنزيل بنجاح",
|
||||||
"image_viewer_page_state_provider_share_error": "خطأ في المشاركة",
|
"image_viewer_page_state_provider_share_error": "خطأ في المشاركة",
|
||||||
"library_page_albums": "ألبومات",
|
"library_page_albums": "ألبومات",
|
||||||
@@ -265,8 +265,8 @@
|
|||||||
"login_form_server_error": "لا يمكن الاتصال بالخادم.",
|
"login_form_server_error": "لا يمكن الاتصال بالخادم.",
|
||||||
"login_password_changed_error": "كان هناك خطأ في تحديث كلمة المرور الخاصة بك",
|
"login_password_changed_error": "كان هناك خطأ في تحديث كلمة المرور الخاصة بك",
|
||||||
"login_password_changed_success": "تم تحديث كلمة السر بنجاح",
|
"login_password_changed_success": "تم تحديث كلمة السر بنجاح",
|
||||||
"map_assets_in_bound": "{} صورة",
|
"map_assets_in_bound": "{} photo",
|
||||||
"map_assets_in_bounds": "{} الصور",
|
"map_assets_in_bounds": "{} photos",
|
||||||
"map_cannot_get_user_location": "لا يمكن الحصول على موقع المستخدم",
|
"map_cannot_get_user_location": "لا يمكن الحصول على موقع المستخدم",
|
||||||
"map_location_dialog_cancel": "يلغي",
|
"map_location_dialog_cancel": "يلغي",
|
||||||
"map_location_dialog_yes": "نعم",
|
"map_location_dialog_yes": "نعم",
|
||||||
@@ -279,27 +279,27 @@
|
|||||||
"map_settings_dark_mode": "الوضع المظلم",
|
"map_settings_dark_mode": "الوضع المظلم",
|
||||||
"map_settings_date_range_option_all": "الجميع",
|
"map_settings_date_range_option_all": "الجميع",
|
||||||
"map_settings_date_range_option_day": "24 ساعة الماضية",
|
"map_settings_date_range_option_day": "24 ساعة الماضية",
|
||||||
"map_settings_date_range_option_days": "الأيام الماضية {}",
|
"map_settings_date_range_option_days": "Past {} days",
|
||||||
"map_settings_date_range_option_year": "السنة الفائتة",
|
"map_settings_date_range_option_year": "السنة الفائتة",
|
||||||
"map_settings_date_range_option_years": "السنوات الماضية {}",
|
"map_settings_date_range_option_years": "Past {} years",
|
||||||
"map_settings_dialog_cancel": "يلغي",
|
"map_settings_dialog_cancel": "يلغي",
|
||||||
"map_settings_dialog_save": "يحفظ",
|
"map_settings_dialog_save": "يحفظ",
|
||||||
"map_settings_dialog_title": "إعدادات الخريطة",
|
"map_settings_dialog_title": "إعدادات الخريطة",
|
||||||
"map_settings_include_show_archived": "تشمل الأرشفة",
|
"map_settings_include_show_archived": "تشمل الأرشفة",
|
||||||
"map_settings_include_show_partners": "Include Partners",
|
"map_settings_include_show_partners": "تضمين الشركاء",
|
||||||
"map_settings_only_relative_range": "نطاق الموعد",
|
"map_settings_only_relative_range": "نطاق الموعد",
|
||||||
"map_settings_only_show_favorites": "اظهار المفضلة فقط",
|
"map_settings_only_show_favorites": "اظهار المفضلة فقط",
|
||||||
"map_settings_theme_settings": "مظهر الخريطة",
|
"map_settings_theme_settings": "مظهر الخريطة",
|
||||||
"map_zoom_to_see_photos": "قم بتصغيرها لرؤية الصور",
|
"map_zoom_to_see_photos": "قم بتصغيرها لرؤية الصور",
|
||||||
"memories_all_caught_up": "All caught up",
|
"memories_all_caught_up": "كل شيء محدث",
|
||||||
"memories_check_back_tomorrow": "Check back tomorrow for more memories",
|
"memories_check_back_tomorrow": "التحقق مرة أخرى غدا لمزيد من الذكريات",
|
||||||
"memories_start_over": "Start Over",
|
"memories_start_over": "ابدأ من جديد",
|
||||||
"memories_swipe_to_close": "Swipe up to close",
|
"memories_swipe_to_close": "اسحب لأعلى للإغلاق",
|
||||||
"monthly_title_text_date_format": "ط ط ط",
|
"monthly_title_text_date_format": "ط ط ط",
|
||||||
"motion_photos_page_title": "الصور المتحركة",
|
"motion_photos_page_title": "الصور المتحركة",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "لا يمكن تعديل تاريخ الأصول (المواد) للقراءة فقط، سوف يتخطى",
|
"multiselect_grid_edit_date_time_err_read_only": "لا يمكن تعديل تاريخ الأصول (المواد) للقراءة فقط، سوف يتخطى",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "لا يمكن تعديل موقع الأصول (المواد) للقراءة فقط، سوف يتخطى",
|
"multiselect_grid_edit_gps_err_read_only": "لا يمكن تعديل موقع الأصول (المواد) للقراءة فقط، سوف يتخطى",
|
||||||
"no_assets_to_show": "No assets to show",
|
"no_assets_to_show": "لا توجد أصول لعرضها",
|
||||||
"notification_permission_dialog_cancel": "يلغي",
|
"notification_permission_dialog_cancel": "يلغي",
|
||||||
"notification_permission_dialog_content": "لتمكين الإخطارات ، انتقل إلى الإعدادات و اختار السماح.",
|
"notification_permission_dialog_content": "لتمكين الإخطارات ، انتقل إلى الإعدادات و اختار السماح.",
|
||||||
"notification_permission_dialog_settings": "إعدادات",
|
"notification_permission_dialog_settings": "إعدادات",
|
||||||
@@ -307,14 +307,14 @@
|
|||||||
"notification_permission_list_tile_enable_button": "تمكين الإخطارات",
|
"notification_permission_list_tile_enable_button": "تمكين الإخطارات",
|
||||||
"notification_permission_list_tile_title": "إذن الإخطار",
|
"notification_permission_list_tile_title": "إذن الإخطار",
|
||||||
"partner_list_user_photos": "{user}'s photos",
|
"partner_list_user_photos": "{user}'s photos",
|
||||||
"partner_list_view_all": "View all",
|
"partner_list_view_all": "عرض الكل",
|
||||||
"partner_page_add_partner": "أضف شريكًا",
|
"partner_page_add_partner": "أضف شريكًا",
|
||||||
"partner_page_empty_message": "لم يتم مشاركة صورك بعد مع أي شريك.",
|
"partner_page_empty_message": "لم يتم مشاركة صورك بعد مع أي شريك.",
|
||||||
"partner_page_no_more_users": "لا مزيد من المستخدمين لإضافة",
|
"partner_page_no_more_users": "لا مزيد من المستخدمين لإضافة",
|
||||||
"partner_page_partner_add_failed": "فشل في إضافة شريك",
|
"partner_page_partner_add_failed": "فشل في إضافة شريك",
|
||||||
"partner_page_select_partner": "حدد شريكًا",
|
"partner_page_select_partner": "حدد شريكًا",
|
||||||
"partner_page_shared_to_title": "مشترك ل",
|
"partner_page_shared_to_title": "مشترك ل",
|
||||||
"partner_page_stop_sharing_content": "{} لن يكون قادرًا على الوصول إلى صورك.",
|
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
||||||
"partner_page_stop_sharing_title": "توقف عن مشاركة صورك؟",
|
"partner_page_stop_sharing_title": "توقف عن مشاركة صورك؟",
|
||||||
"partner_page_title": "شريك",
|
"partner_page_title": "شريك",
|
||||||
"permission_onboarding_back": "خلف",
|
"permission_onboarding_back": "خلف",
|
||||||
@@ -327,7 +327,7 @@
|
|||||||
"permission_onboarding_permission_granted": "تم تأمين التصريح! وضعك تمام.",
|
"permission_onboarding_permission_granted": "تم تأمين التصريح! وضعك تمام.",
|
||||||
"permission_onboarding_permission_limited": "إذن محدود. للسماح بالنسخ الاحتياطي للتطبيق وإدارة مجموعة المعرض بالكامل، امنح أذونات الصور والفيديو في الإعدادات.",
|
"permission_onboarding_permission_limited": "إذن محدود. للسماح بالنسخ الاحتياطي للتطبيق وإدارة مجموعة المعرض بالكامل، امنح أذونات الصور والفيديو في الإعدادات.",
|
||||||
"permission_onboarding_request": "يتطلب التطبيق إذنًا لعرض الصور ومقاطع الفيديو الخاصة بك",
|
"permission_onboarding_request": "يتطلب التطبيق إذنًا لعرض الصور ومقاطع الفيديو الخاصة بك",
|
||||||
"preferences_settings_title": "Preferences",
|
"preferences_settings_title": "التفضيلات",
|
||||||
"profile_drawer_app_logs": "السجلات",
|
"profile_drawer_app_logs": "السجلات",
|
||||||
"profile_drawer_client_out_of_date_major": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار رئيسي.",
|
"profile_drawer_client_out_of_date_major": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار رئيسي.",
|
||||||
"profile_drawer_client_out_of_date_minor": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار صغير.",
|
"profile_drawer_client_out_of_date_minor": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار صغير.",
|
||||||
@@ -342,18 +342,18 @@
|
|||||||
"recently_added_page_title": "أضيف مؤخرا",
|
"recently_added_page_title": "أضيف مؤخرا",
|
||||||
"scaffold_body_error_occurred": "حدث خطأ",
|
"scaffold_body_error_occurred": "حدث خطأ",
|
||||||
"search_bar_hint": "ابحث عن صورك",
|
"search_bar_hint": "ابحث عن صورك",
|
||||||
"search_filter_apply": "Apply filter",
|
"search_filter_apply": "اختار الفلتر ",
|
||||||
"search_filter_camera_make": "Make",
|
"search_filter_camera_make": "صنع",
|
||||||
"search_filter_camera_model": "Model",
|
"search_filter_camera_model": "نموذج",
|
||||||
"search_filter_display_option_archive": "Archive",
|
"search_filter_display_option_archive": "أرشيف",
|
||||||
"search_filter_display_option_favorite": "Favorite",
|
"search_filter_display_option_favorite": "مفضل",
|
||||||
"search_filter_display_option_not_in_album": "Not in album",
|
"search_filter_display_option_not_in_album": "ليس في الألبوم",
|
||||||
"search_filter_location_city": "City",
|
"search_filter_location_city": "مدينة",
|
||||||
"search_filter_location_country": "Country",
|
"search_filter_location_country": "دولة",
|
||||||
"search_filter_location_state": "State",
|
"search_filter_location_state": "ولاية",
|
||||||
"search_filter_media_type_all": "All",
|
"search_filter_media_type_all": "الجميع",
|
||||||
"search_filter_media_type_image": "Image",
|
"search_filter_media_type_image": "صورة",
|
||||||
"search_filter_media_type_video": "Video",
|
"search_filter_media_type_video": "شريط فيديو",
|
||||||
"search_page_categories": "فئات",
|
"search_page_categories": "فئات",
|
||||||
"search_page_favorites": "المفضلة",
|
"search_page_favorites": "المفضلة",
|
||||||
"search_page_motion_photos": "الصور المتحركه",
|
"search_page_motion_photos": "الصور المتحركه",
|
||||||
@@ -391,14 +391,15 @@
|
|||||||
"setting_image_viewer_original_title": "تحميل الصورة الأصلية",
|
"setting_image_viewer_original_title": "تحميل الصورة الأصلية",
|
||||||
"setting_image_viewer_preview_subtitle": "تمكين تحميل صورة متوسطة الدقة.تعطيل إما لتحميل مباشرة أو استخدام الصورة المصغرة مباشرة.",
|
"setting_image_viewer_preview_subtitle": "تمكين تحميل صورة متوسطة الدقة.تعطيل إما لتحميل مباشرة أو استخدام الصورة المصغرة مباشرة.",
|
||||||
"setting_image_viewer_preview_title": "تحميل صورة معاينة",
|
"setting_image_viewer_preview_title": "تحميل صورة معاينة",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_image_viewer_title": "الصور",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_apply": "تغيير الإعدادات",
|
||||||
"setting_notifications_notify_failures_grace_period": "أخطر فشل النسخ الاحتياطي في الخلفية: {}",
|
"setting_languages_title": "اللغات",
|
||||||
"setting_notifications_notify_hours": "{} ساعات",
|
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
||||||
|
"setting_notifications_notify_hours": "{} hours",
|
||||||
"setting_notifications_notify_immediately": "في الحال",
|
"setting_notifications_notify_immediately": "في الحال",
|
||||||
"setting_notifications_notify_minutes": "{} دقائق",
|
"setting_notifications_notify_minutes": "{} minutes",
|
||||||
"setting_notifications_notify_never": "أبداً",
|
"setting_notifications_notify_never": "أبداً",
|
||||||
"setting_notifications_notify_seconds": "{} ثانية",
|
"setting_notifications_notify_seconds": "{} seconds",
|
||||||
"setting_notifications_single_progress_subtitle": "معلومات التقدم التفصيلية تحميل لكل أصل",
|
"setting_notifications_single_progress_subtitle": "معلومات التقدم التفصيلية تحميل لكل أصل",
|
||||||
"setting_notifications_single_progress_title": "إظهار تقدم التفاصيل الاحتياطية الخلفية",
|
"setting_notifications_single_progress_title": "إظهار تقدم التفاصيل الاحتياطية الخلفية",
|
||||||
"setting_notifications_subtitle": "اضبط تفضيلات الإخطار",
|
"setting_notifications_subtitle": "اضبط تفضيلات الإخطار",
|
||||||
@@ -406,7 +407,10 @@
|
|||||||
"setting_notifications_total_progress_subtitle": "التقدم التحميل العام (تم القيام به/إجمالي الأصول)",
|
"setting_notifications_total_progress_subtitle": "التقدم التحميل العام (تم القيام به/إجمالي الأصول)",
|
||||||
"setting_notifications_total_progress_title": "إظهار النسخ الاحتياطي الخلفية التقدم المحرز",
|
"setting_notifications_total_progress_title": "إظهار النسخ الاحتياطي الخلفية التقدم المحرز",
|
||||||
"setting_pages_app_bar_settings": "إعدادات",
|
"setting_pages_app_bar_settings": "إعدادات",
|
||||||
"settings_require_restart": "يرجى إعادة تشغيل Imich لتطبيق هذا الإعداد",
|
"settings_require_restart": "يرجى إعادة تشغيل لتطبيق هذا الإعداد",
|
||||||
|
"setting_video_viewer_looping_subtitle": "تمكين تكرار مقطع فيديو تلقائيًا في عارض التفاصيل.",
|
||||||
|
"setting_video_viewer_looping_title": "تكرار مقطع فيديو تلقائيًا",
|
||||||
|
"setting_video_viewer_title": "أشرطة فيديو",
|
||||||
"share_add": "يضيف",
|
"share_add": "يضيف",
|
||||||
"share_add_photos": "إضافة الصور",
|
"share_add_photos": "إضافة الصور",
|
||||||
"share_add_title": "إضافة عنوان",
|
"share_add_title": "إضافة عنوان",
|
||||||
@@ -426,7 +430,7 @@
|
|||||||
"share_dialog_preparing": "تحضير...",
|
"share_dialog_preparing": "تحضير...",
|
||||||
"shared_link_app_bar_title": "روابط مشتركة",
|
"shared_link_app_bar_title": "روابط مشتركة",
|
||||||
"shared_link_clipboard_copied_massage": "نسخ إلى الحافظة",
|
"shared_link_clipboard_copied_massage": "نسخ إلى الحافظة",
|
||||||
"shared_link_clipboard_text": "وصلة: {}كلمة المرور: {}",
|
"shared_link_clipboard_text": "Link: {}\nPassword: {}",
|
||||||
"shared_link_create_app_bar_title": "إنشاء رابط للمشاركة",
|
"shared_link_create_app_bar_title": "إنشاء رابط للمشاركة",
|
||||||
"shared_link_create_error": "خطأ أثناء إنشاء رابط مشترك",
|
"shared_link_create_error": "خطأ أثناء إنشاء رابط مشترك",
|
||||||
"shared_link_create_info": "دع أي شخص لديه الرابط يرى الصور المحددة)",
|
"shared_link_create_info": "دع أي شخص لديه الرابط يرى الصور المحددة)",
|
||||||
@@ -439,11 +443,11 @@
|
|||||||
"shared_link_edit_description_hint": "أدخل وصف المشاركة",
|
"shared_link_edit_description_hint": "أدخل وصف المشاركة",
|
||||||
"shared_link_edit_expire_after": "تنتهي بعد",
|
"shared_link_edit_expire_after": "تنتهي بعد",
|
||||||
"shared_link_edit_expire_after_option_day": "يوم 1",
|
"shared_link_edit_expire_after_option_day": "يوم 1",
|
||||||
"shared_link_edit_expire_after_option_days": "{} أيام",
|
"shared_link_edit_expire_after_option_days": "{} days",
|
||||||
"shared_link_edit_expire_after_option_hour": "1 ساعة",
|
"shared_link_edit_expire_after_option_hour": "1 ساعة",
|
||||||
"shared_link_edit_expire_after_option_hours": "{} ساعات",
|
"shared_link_edit_expire_after_option_hours": "{} hours",
|
||||||
"shared_link_edit_expire_after_option_minute": "1 دقيقة",
|
"shared_link_edit_expire_after_option_minute": "1 دقيقة",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} دقائق",
|
"shared_link_edit_expire_after_option_minutes": "{} minutes",
|
||||||
"shared_link_edit_expire_after_option_months": "{} months",
|
"shared_link_edit_expire_after_option_months": "{} months",
|
||||||
"shared_link_edit_expire_after_option_never": "أبداً",
|
"shared_link_edit_expire_after_option_never": "أبداً",
|
||||||
"shared_link_edit_expire_after_option_year": "{} year",
|
"shared_link_edit_expire_after_option_year": "{} year",
|
||||||
@@ -454,21 +458,21 @@
|
|||||||
"shared_link_empty": "ليس لديك أي روابط مشتركة",
|
"shared_link_empty": "ليس لديك أي روابط مشتركة",
|
||||||
"shared_link_error_server_url_fetch": "لا يمكن جلب عنوان الخادم",
|
"shared_link_error_server_url_fetch": "لا يمكن جلب عنوان الخادم",
|
||||||
"shared_link_expired": "منتهي الصلاحية",
|
"shared_link_expired": "منتهي الصلاحية",
|
||||||
"shared_link_expires_day": "تنتهي في اليوم {}",
|
"shared_link_expires_day": "Expires in {} day",
|
||||||
"shared_link_expires_days": "تنتهي في {} أيام",
|
"shared_link_expires_days": "Expires in {} days",
|
||||||
"shared_link_expires_hour": "تنتهي في الساعة {}",
|
"shared_link_expires_hour": "Expires in {} hour",
|
||||||
"shared_link_expires_hours": "تنتهي في {} ساعات",
|
"shared_link_expires_hours": "Expires in {} hours",
|
||||||
"shared_link_expires_minute": "تنتهي في {} دقيقة",
|
"shared_link_expires_minute": "Expires in {} minute",
|
||||||
"shared_link_expires_minutes": "تنتهي في الدقائق {}",
|
"shared_link_expires_minutes": "Expires in {} minutes",
|
||||||
"shared_link_expires_never": "تنتهي ∞",
|
"shared_link_expires_never": "تنتهي ∞",
|
||||||
"shared_link_expires_second": "تنتهي في {} الثاني",
|
"shared_link_expires_second": "Expires in {} second",
|
||||||
"shared_link_expires_seconds": "تنتهي في ثواني {}",
|
"shared_link_expires_seconds": "Expires in {} seconds",
|
||||||
"shared_link_individual_shared": "Individual shared",
|
"shared_link_individual_shared": "Individual shared",
|
||||||
"shared_link_info_chip_download": "تحميل",
|
"shared_link_info_chip_download": "تحميل",
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
"shared_link_info_chip_upload": "رفع",
|
"shared_link_info_chip_upload": "رفع",
|
||||||
"shared_link_manage_links": "إدارة الروابط المشتركة",
|
"shared_link_manage_links": "إدارة الروابط المشتركة",
|
||||||
"shared_link_public_album": "Public album",
|
"shared_link_public_album": "الألبوم العام",
|
||||||
"share_done": "منتهي",
|
"share_done": "منتهي",
|
||||||
"share_invite": "دعوة إلى الألبوم",
|
"share_invite": "دعوة إلى الألبوم",
|
||||||
"sharing_page_album": "ألبومات مشتركة",
|
"sharing_page_album": "ألبومات مشتركة",
|
||||||
@@ -482,7 +486,7 @@
|
|||||||
"tab_controller_nav_search": "يبحث",
|
"tab_controller_nav_search": "يبحث",
|
||||||
"tab_controller_nav_sharing": "مشاركة",
|
"tab_controller_nav_sharing": "مشاركة",
|
||||||
"theme_setting_asset_list_storage_indicator_title": "عرض مؤشر التخزين على بلاط الأصول",
|
"theme_setting_asset_list_storage_indicator_title": "عرض مؤشر التخزين على بلاط الأصول",
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "عدد الأصول لكل صف ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})",
|
||||||
"theme_setting_dark_mode_switch": "الوضع المظلم",
|
"theme_setting_dark_mode_switch": "الوضع المظلم",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "اضبط جودة عارض الصورة التفصيلية",
|
"theme_setting_image_viewer_quality_subtitle": "اضبط جودة عارض الصورة التفصيلية",
|
||||||
"theme_setting_image_viewer_quality_title": "جودة عارض الصورة",
|
"theme_setting_image_viewer_quality_title": "جودة عارض الصورة",
|
||||||
@@ -497,13 +501,13 @@
|
|||||||
"trash_page_empty_trash_btn": "افرغ سله المهملات ",
|
"trash_page_empty_trash_btn": "افرغ سله المهملات ",
|
||||||
"trash_page_empty_trash_dialog_content": "هل تريد تفريغ أصولك المهملة؟ ستتم إزالة هذه العناصر نهائيًا من التطبيق",
|
"trash_page_empty_trash_dialog_content": "هل تريد تفريغ أصولك المهملة؟ ستتم إزالة هذه العناصر نهائيًا من التطبيق",
|
||||||
"trash_page_empty_trash_dialog_ok": "نعم",
|
"trash_page_empty_trash_dialog_ok": "نعم",
|
||||||
"trash_page_info": "سيتم حذف العناصر المحذوفة بشكل دائم بعد {} أيام",
|
"trash_page_info": "Trashed items will be permanently deleted after {} days",
|
||||||
"trash_page_no_assets": "لا توجد اصول في سله المهملات",
|
"trash_page_no_assets": "لا توجد اصول في سله المهملات",
|
||||||
"trash_page_restore": "الترجيع من سله المهملات",
|
"trash_page_restore": "الترجيع من سله المهملات",
|
||||||
"trash_page_restore_all": "استعادة الكل",
|
"trash_page_restore_all": "استعادة الكل",
|
||||||
"trash_page_select_assets_btn": "اختر الأصول ",
|
"trash_page_select_assets_btn": "اختر الأصول ",
|
||||||
"trash_page_select_btn": "يختار",
|
"trash_page_select_btn": "يختار",
|
||||||
"trash_page_title": "نفايات ({})",
|
"trash_page_title": "Trash ({})",
|
||||||
"upload_dialog_cancel": "يلغي",
|
"upload_dialog_cancel": "يلغي",
|
||||||
"upload_dialog_info": "هل تريد النسخ الاحتياطي للأصول (الأصول) المحددة إلى الخادم؟",
|
"upload_dialog_info": "هل تريد النسخ الاحتياطي للأصول (الأصول) المحددة إلى الخادم؟",
|
||||||
"upload_dialog_ok": "رفع",
|
"upload_dialog_ok": "رفع",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Načíst původní obrázek",
|
"setting_image_viewer_original_title": "Načíst původní obrázek",
|
||||||
"setting_image_viewer_preview_subtitle": "Umožňuje načíst obrázek se středním rozlišením. Zakažte, pokud chcete přímo načíst originál nebo použít pouze miniaturu.",
|
"setting_image_viewer_preview_subtitle": "Umožňuje načíst obrázek se středním rozlišením. Zakažte, pokud chcete přímo načíst originál nebo použít pouze miniaturu.",
|
||||||
"setting_image_viewer_preview_title": "Načíst náhled obrázku",
|
"setting_image_viewer_preview_title": "Načíst náhled obrázku",
|
||||||
|
"setting_image_viewer_title": "Obrázky",
|
||||||
"setting_languages_apply": "Použít",
|
"setting_languages_apply": "Použít",
|
||||||
"setting_languages_title": "Jazyk",
|
"setting_languages_title": "Jazyk",
|
||||||
"setting_notifications_notify_failures_grace_period": "Oznámení o selhání zálohování na pozadí: {}",
|
"setting_notifications_notify_failures_grace_period": "Oznámení o selhání zálohování na pozadí: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Zobrazit celkový průběh zálohování na pozadí",
|
"setting_notifications_total_progress_title": "Zobrazit celkový průběh zálohování na pozadí",
|
||||||
"setting_pages_app_bar_settings": "Nastavení",
|
"setting_pages_app_bar_settings": "Nastavení",
|
||||||
"settings_require_restart": "Pro použití tohoto nastavení restartujte Immich",
|
"settings_require_restart": "Pro použití tohoto nastavení restartujte Immich",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Povolení automatické smyčky videa v prohlížeči detailů.",
|
||||||
|
"setting_video_viewer_looping_title": "Smyčka",
|
||||||
|
"setting_video_viewer_title": "Videa",
|
||||||
"share_add": "Přidat",
|
"share_add": "Přidat",
|
||||||
"share_add_photos": "Přidat fotografie",
|
"share_add_photos": "Přidat fotografie",
|
||||||
"share_add_title": "Přidat název",
|
"share_add_title": "Přidat název",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Indlæs originalbillede",
|
"setting_image_viewer_original_title": "Indlæs originalbillede",
|
||||||
"setting_image_viewer_preview_subtitle": "Slå indlæsning af et mediumstørrelse billede til. Slå fra for enten direkte at indlæse originalen eller kun at bruge miniaturebilledet.",
|
"setting_image_viewer_preview_subtitle": "Slå indlæsning af et mediumstørrelse billede til. Slå fra for enten direkte at indlæse originalen eller kun at bruge miniaturebilledet.",
|
||||||
"setting_image_viewer_preview_title": "Indlæs forhåndsvisning af billedet",
|
"setting_image_viewer_preview_title": "Indlæs forhåndsvisning af billedet",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Anvend",
|
"setting_languages_apply": "Anvend",
|
||||||
"setting_languages_title": "Sprog",
|
"setting_languages_title": "Sprog",
|
||||||
"setting_notifications_notify_failures_grace_period": "Giv besked om fejl med sikkerhedskopiering i baggrunden: {}",
|
"setting_notifications_notify_failures_grace_period": "Giv besked om fejl med sikkerhedskopiering i baggrunden: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Vis samlet baggrundsuploadstatus",
|
"setting_notifications_total_progress_title": "Vis samlet baggrundsuploadstatus",
|
||||||
"setting_pages_app_bar_settings": "Indstillinger",
|
"setting_pages_app_bar_settings": "Indstillinger",
|
||||||
"settings_require_restart": "Genstart venligst Immich for at anvende denne ændring",
|
"settings_require_restart": "Genstart venligst Immich for at anvende denne ændring",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Tilføj",
|
"share_add": "Tilføj",
|
||||||
"share_add_photos": "Tilføj billeder",
|
"share_add_photos": "Tilføj billeder",
|
||||||
"share_add_title": "Tilføj en titel",
|
"share_add_title": "Tilføj en titel",
|
||||||
|
|||||||
@@ -52,10 +52,10 @@
|
|||||||
"asset_list_settings_title": "Fotogitter",
|
"asset_list_settings_title": "Fotogitter",
|
||||||
"asset_viewer_settings_title": "Fotoanzeige",
|
"asset_viewer_settings_title": "Fotoanzeige",
|
||||||
"backup_album_selection_page_albums_device": "Alben auf dem Gerät ({})",
|
"backup_album_selection_page_albums_device": "Alben auf dem Gerät ({})",
|
||||||
"backup_album_selection_page_albums_tap": "Einmalig tippen um einzuschließen, doppelt tippen um zu entfernen",
|
"backup_album_selection_page_albums_tap": "Einmalig tippen um das Album zu verwenden, doppelt tippen um es zu entfernen.",
|
||||||
"backup_album_selection_page_assets_scatter": "Elemente können sich über mehrere Alben verteilen. Daher können diese vor der Sicherung eingeschlossen oder ausgeschlossen werden",
|
"backup_album_selection_page_assets_scatter": "Elemente (Fotos / Videos) können sich über mehrere Alben verteilen. Daher können diese vor der Sicherung eingeschlossen oder ausgeschlossen werden.",
|
||||||
"backup_album_selection_page_select_albums": "Alben auswählen",
|
"backup_album_selection_page_select_albums": "Alben auswählen",
|
||||||
"backup_album_selection_page_selection_info": "Auswahl",
|
"backup_album_selection_page_selection_info": "Information",
|
||||||
"backup_album_selection_page_total_assets": "Elemente",
|
"backup_album_selection_page_total_assets": "Elemente",
|
||||||
"backup_all": "Alle",
|
"backup_all": "Alle",
|
||||||
"backup_background_service_backup_failed_message": "Fehler beim Sichern von Elementen. Probiere erneut...",
|
"backup_background_service_backup_failed_message": "Fehler beim Sichern von Elementen. Probiere erneut...",
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
"backup_controller_page_background_battery_info_title": "Batterieoptimierungen",
|
"backup_controller_page_background_battery_info_title": "Batterieoptimierungen",
|
||||||
"backup_controller_page_background_charging": "Nur während des Ladens",
|
"backup_controller_page_background_charging": "Nur während des Ladens",
|
||||||
"backup_controller_page_background_configure_error": "Konnte Hintergrundservice nicht konfigurieren",
|
"backup_controller_page_background_configure_error": "Konnte Hintergrundservice nicht konfigurieren",
|
||||||
"backup_controller_page_background_delay": "Sicherung neuer Elemente verzögern: {}",
|
"backup_controller_page_background_delay": "Sicherung neuer Elemente verzögern um: {}",
|
||||||
"backup_controller_page_background_description": "Schalte den Hintergrundservice ein, um neue Elemente automatisch im Hintergrund zu sichern ohne die App zu öffnen",
|
"backup_controller_page_background_description": "Schalte den Hintergrundservice ein, um neue Elemente automatisch im Hintergrund zu sichern ohne die App zu öffnen",
|
||||||
"backup_controller_page_background_is_off": "Automatische Sicherung im Hintergrund ist deaktiviert",
|
"backup_controller_page_background_is_off": "Automatische Sicherung im Hintergrund ist deaktiviert",
|
||||||
"backup_controller_page_background_is_on": "Automatische Sicherung im Hintergrund ist aktiviert",
|
"backup_controller_page_background_is_on": "Automatische Sicherung im Hintergrund ist aktiviert",
|
||||||
@@ -331,7 +331,7 @@
|
|||||||
"profile_drawer_app_logs": "Logs",
|
"profile_drawer_app_logs": "Logs",
|
||||||
"profile_drawer_client_out_of_date_major": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Major-Version.",
|
"profile_drawer_client_out_of_date_major": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Major-Version.",
|
||||||
"profile_drawer_client_out_of_date_minor": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.",
|
"profile_drawer_client_out_of_date_minor": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.",
|
||||||
"profile_drawer_client_server_up_to_date": "App und Server sind aktuell",
|
"profile_drawer_client_server_up_to_date": "Die App-Version / Server-Version sind aktuell.",
|
||||||
"profile_drawer_documentation": "Dokumentation",
|
"profile_drawer_documentation": "Dokumentation",
|
||||||
"profile_drawer_github": "GitHub",
|
"profile_drawer_github": "GitHub",
|
||||||
"profile_drawer_server_out_of_date_major": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Major-Version.",
|
"profile_drawer_server_out_of_date_major": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Major-Version.",
|
||||||
@@ -382,19 +382,19 @@
|
|||||||
"select_additional_user_for_sharing_page_suggestions": "Vorschläge",
|
"select_additional_user_for_sharing_page_suggestions": "Vorschläge",
|
||||||
"select_user_for_sharing_page_err_album": "Album konnte nicht erstellt werden",
|
"select_user_for_sharing_page_err_album": "Album konnte nicht erstellt werden",
|
||||||
"select_user_for_sharing_page_share_suggestions": "Empfehlungen",
|
"select_user_for_sharing_page_share_suggestions": "Empfehlungen",
|
||||||
"server_info_box_app_version": "App Version",
|
"server_info_box_app_version": "App-Version",
|
||||||
"server_info_box_latest_release": "Neueste Version",
|
"server_info_box_latest_release": "Neueste Version",
|
||||||
"server_info_box_server_url": "Server-URL",
|
"server_info_box_server_url": "Server-URL",
|
||||||
"server_info_box_server_version": "Server Version",
|
"server_info_box_server_version": "Server-Version",
|
||||||
"setting_image_viewer_help": "Der Detailbildbetrachter lädt zuerst die kleine Miniaturansicht, dann die Vorschau 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",
|
||||||
"setting_image_viewer_preview_subtitle": "Aktivieren, um ein Bild mit mittlerer Auflösung zu laden. Deaktivieren, um entweder das Original direkt zu laden oder nur die Miniaturansicht zu verwenden.",
|
"setting_image_viewer_preview_subtitle": "Aktivieren, um ein Bild mit mittlerer Auflösung zu laden. Deaktivieren, um entweder das Original direkt zu laden oder nur das Vorschaubild zu verwenden.",
|
||||||
"setting_image_viewer_preview_title": "Vorschaubild laden",
|
"setting_image_viewer_preview_title": "Vorschaubild laden",
|
||||||
"setting_image_viewer_title": "Bilder",
|
"setting_image_viewer_title": "Bilder",
|
||||||
"setting_languages_apply": "Anwenden",
|
"setting_languages_apply": "Anwenden",
|
||||||
"setting_languages_title": "Sprachen",
|
"setting_languages_title": "Sprachen",
|
||||||
"setting_notifications_notify_failures_grace_period": "Benachrichtigung über Fehler bei der Hintergrundsicherung: {}",
|
"setting_notifications_notify_failures_grace_period": "Benachrichtigung bei Fehler/n in der Hintergrundsicherung: {}",
|
||||||
"setting_notifications_notify_hours": "{} Stunden",
|
"setting_notifications_notify_hours": "{} Stunden",
|
||||||
"setting_notifications_notify_immediately": "sofort",
|
"setting_notifications_notify_immediately": "sofort",
|
||||||
"setting_notifications_notify_minutes": "{} Minuten",
|
"setting_notifications_notify_minutes": "{} Minuten",
|
||||||
@@ -407,10 +407,10 @@
|
|||||||
"setting_notifications_total_progress_subtitle": "Gesamter Upload-Fortschritt (abgeschlossen/Anzahl Elemente)",
|
"setting_notifications_total_progress_subtitle": "Gesamter Upload-Fortschritt (abgeschlossen/Anzahl Elemente)",
|
||||||
"setting_notifications_total_progress_title": "Zeige Gesamtfortschritt bei der Hintergrundsicherung",
|
"setting_notifications_total_progress_title": "Zeige Gesamtfortschritt bei der Hintergrundsicherung",
|
||||||
"setting_pages_app_bar_settings": "Einstellungen",
|
"setting_pages_app_bar_settings": "Einstellungen",
|
||||||
"setting_video_viewer_looping_subtitle": "Aktivieren, damit sich ein Video in der Detailansicht automatisch wiederholt.",
|
|
||||||
"setting_video_viewer_looping_title": "Wiederholen",
|
|
||||||
"setting_video_viewer_title": "Videos",
|
|
||||||
"settings_require_restart": "Bitte starte Immich neu, um diese Einstellung anzuwenden.",
|
"settings_require_restart": "Bitte starte Immich neu, um diese Einstellung anzuwenden.",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Aktiviere diese Option, um ein Video in der Detailansicht automatisch in einer Schleife anzuzeigen.",
|
||||||
|
"setting_video_viewer_looping_title": "Schleife / Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Hinzufügen",
|
"share_add": "Hinzufügen",
|
||||||
"share_add_photos": "Fotos hinzufügen",
|
"share_add_photos": "Fotos hinzufügen",
|
||||||
"share_add_title": "Titel hinzufügen",
|
"share_add_title": "Titel hinzufügen",
|
||||||
@@ -485,12 +485,12 @@
|
|||||||
"tab_controller_nav_photos": "Fotos",
|
"tab_controller_nav_photos": "Fotos",
|
||||||
"tab_controller_nav_search": "Suche",
|
"tab_controller_nav_search": "Suche",
|
||||||
"tab_controller_nav_sharing": "Teilen",
|
"tab_controller_nav_sharing": "Teilen",
|
||||||
"theme_setting_asset_list_storage_indicator_title": "Zeige Sicherungsstatus auf Miniaturbild",
|
"theme_setting_asset_list_storage_indicator_title": "Zeige Sicherungsstatus auf Vorschaubild",
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "Anzahl der Elemente pro Reihe ({})",
|
"theme_setting_asset_list_tiles_per_row_title": "Anzahl der Elemente pro Reihe ({})",
|
||||||
"theme_setting_dark_mode_switch": "Dunkler Modus",
|
"theme_setting_dark_mode_switch": "Dunkler Modus",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "Einstellen der Qualität des Detailbildbetrachters",
|
"theme_setting_image_viewer_quality_subtitle": "Einstellen der Qualität des Detailbildbetrachters",
|
||||||
"theme_setting_image_viewer_quality_title": "Qualität des Bildbetrachters",
|
"theme_setting_image_viewer_quality_title": "Qualität des Bildbetrachters",
|
||||||
"theme_setting_system_theme_switch": "Automatisch (Systemeinstellung folgen)",
|
"theme_setting_system_theme_switch": "Automatisch (Systemeinstellung)",
|
||||||
"theme_setting_theme_subtitle": "Wählen Sie die Themeneinstellung der App",
|
"theme_setting_theme_subtitle": "Wählen Sie die Themeneinstellung der App",
|
||||||
"theme_setting_theme_title": "Theme",
|
"theme_setting_theme_title": "Theme",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Das dreistufige Ladeverfahren kann die Performance beim Laden verbessern, erhöht allerdings den Datenverbrauch deutlich",
|
"theme_setting_three_stage_loading_subtitle": "Das dreistufige Ladeverfahren kann die Performance beim Laden verbessern, erhöht allerdings den Datenverbrauch deutlich",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Load original image",
|
"setting_image_viewer_original_title": "Load original image",
|
||||||
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
||||||
"setting_image_viewer_preview_title": "Load preview image",
|
"setting_image_viewer_preview_title": "Load preview image",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Show background backup total progress",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Settings",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
"settings_require_restart": "Please restart Immich to apply this setting",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Add",
|
"share_add": "Add",
|
||||||
"share_add_photos": "Add photos",
|
"share_add_photos": "Add photos",
|
||||||
"share_add_title": "Add a title",
|
"share_add_title": "Add a title",
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
||||||
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
||||||
"backup_controller_page_storage_format": "{} of {} used",
|
"backup_controller_page_storage_format": "{} of {} used",
|
||||||
"backup_controller_page_to_backup": "Albums to be backup",
|
"backup_controller_page_to_backup": "Albums to be backed up",
|
||||||
"backup_controller_page_total": "Total",
|
"backup_controller_page_total": "Total",
|
||||||
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
||||||
"backup_controller_page_turn_off": "Turn off foreground backup",
|
"backup_controller_page_turn_off": "Turn off foreground backup",
|
||||||
@@ -407,10 +407,10 @@
|
|||||||
"setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)",
|
"setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)",
|
||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Show background backup total progress",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Settings",
|
||||||
|
"settings_require_restart": "Please restart Immich to apply this setting",
|
||||||
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
"setting_video_viewer_looping_title": "Looping",
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
"setting_video_viewer_title": "Videos",
|
"setting_video_viewer_title": "Videos",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
|
||||||
"share_add": "Add",
|
"share_add": "Add",
|
||||||
"share_add_photos": "Add photos",
|
"share_add_photos": "Add photos",
|
||||||
"share_add_title": "Add a title",
|
"share_add_title": "Add a title",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Cargar imagen original",
|
"setting_image_viewer_original_title": "Cargar imagen original",
|
||||||
"setting_image_viewer_preview_subtitle": "Activar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
"setting_image_viewer_preview_subtitle": "Activar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
||||||
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Aplicar",
|
"setting_languages_apply": "Aplicar",
|
||||||
"setting_languages_title": "Idiomas",
|
"setting_languages_title": "Idiomas",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}",
|
"setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano",
|
"setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano",
|
||||||
"setting_pages_app_bar_settings": "Ajustes",
|
"setting_pages_app_bar_settings": "Ajustes",
|
||||||
"settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste",
|
"settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Agregar",
|
"share_add": "Agregar",
|
||||||
"share_add_photos": "Agregar fotos",
|
"share_add_photos": "Agregar fotos",
|
||||||
"share_add_title": "Agregar un título",
|
"share_add_title": "Agregar un título",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Cargar imagen original",
|
"setting_image_viewer_original_title": "Cargar imagen original",
|
||||||
"setting_image_viewer_preview_subtitle": "Activar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
"setting_image_viewer_preview_subtitle": "Activar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
||||||
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}",
|
"setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano",
|
"setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano",
|
||||||
"setting_pages_app_bar_settings": "Ajustes",
|
"setting_pages_app_bar_settings": "Ajustes",
|
||||||
"settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste",
|
"settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Agregar",
|
"share_add": "Agregar",
|
||||||
"share_add_photos": "Agregar fotos",
|
"share_add_photos": "Agregar fotos",
|
||||||
"share_add_title": "Agregar un título",
|
"share_add_title": "Agregar un título",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Cargar imagen original",
|
"setting_image_viewer_original_title": "Cargar imagen original",
|
||||||
"setting_image_viewer_preview_subtitle": "Activar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
"setting_image_viewer_preview_subtitle": "Activar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
||||||
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}",
|
"setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano",
|
"setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano",
|
||||||
"setting_pages_app_bar_settings": "Ajustes",
|
"setting_pages_app_bar_settings": "Ajustes",
|
||||||
"settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste",
|
"settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Agregar",
|
"share_add": "Agregar",
|
||||||
"share_add_photos": "Agregar fotos",
|
"share_add_photos": "Agregar fotos",
|
||||||
"share_add_title": "Agregar un título",
|
"share_add_title": "Agregar un título",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Cargar imagen original",
|
"setting_image_viewer_original_title": "Cargar imagen original",
|
||||||
"setting_image_viewer_preview_subtitle": "Activar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
"setting_image_viewer_preview_subtitle": "Activar para cargar una imagen de resolución media. Deshabilitar para cargar directamente la imagen original o usar una miniatura.",
|
||||||
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
"setting_image_viewer_preview_title": "Cargar imagen de previsualización",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}",
|
"setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano",
|
"setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano",
|
||||||
"setting_pages_app_bar_settings": "Configuración",
|
"setting_pages_app_bar_settings": "Configuración",
|
||||||
"settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste",
|
"settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Agregar",
|
"share_add": "Agregar",
|
||||||
"share_add_photos": "Agregar fotos",
|
"share_add_photos": "Agregar fotos",
|
||||||
"share_add_title": "Agregar un título",
|
"share_add_title": "Agregar un título",
|
||||||
|
|||||||
@@ -218,7 +218,7 @@
|
|||||||
"home_page_share_err_local": "Paikallisia kohteita ei voitu jakaa linkkien avulla. Hypätään yli",
|
"home_page_share_err_local": "Paikallisia kohteita ei voitu jakaa linkkien avulla. Hypätään yli",
|
||||||
"home_page_upload_err_limit": "Voit lähettää palvelimelle enintään 30 kohdetta kerrallaan, ohitetaan",
|
"home_page_upload_err_limit": "Voit lähettää palvelimelle enintään 30 kohdetta kerrallaan, ohitetaan",
|
||||||
"image_viewer_page_state_provider_download_error": "Lataus epäonnistui",
|
"image_viewer_page_state_provider_download_error": "Lataus epäonnistui",
|
||||||
"image_viewer_page_state_provider_download_started": "Download Started",
|
"image_viewer_page_state_provider_download_started": "Lataaminen aloitettu",
|
||||||
"image_viewer_page_state_provider_download_success": "Lataus onnistui",
|
"image_viewer_page_state_provider_download_success": "Lataus onnistui",
|
||||||
"image_viewer_page_state_provider_share_error": "Jakovirhe",
|
"image_viewer_page_state_provider_share_error": "Jakovirhe",
|
||||||
"library_page_albums": "Albumit",
|
"library_page_albums": "Albumit",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Lataa alkuperäinen kuva",
|
"setting_image_viewer_original_title": "Lataa alkuperäinen kuva",
|
||||||
"setting_image_viewer_preview_subtitle": "Ota käyttöön ladataksesi keskitarkkuuksinen kuva. Poista käytöstä ladataksesi alkuperäinen kuva tai käyttääksesi vain esikatselukuvaa.",
|
"setting_image_viewer_preview_subtitle": "Ota käyttöön ladataksesi keskitarkkuuksinen kuva. Poista käytöstä ladataksesi alkuperäinen kuva tai käyttääksesi vain esikatselukuvaa.",
|
||||||
"setting_image_viewer_preview_title": "Lataa esikatselukuva",
|
"setting_image_viewer_preview_title": "Lataa esikatselukuva",
|
||||||
|
"setting_image_viewer_title": "Kuvat",
|
||||||
"setting_languages_apply": "Käytä",
|
"setting_languages_apply": "Käytä",
|
||||||
"setting_languages_title": "Kieli",
|
"setting_languages_title": "Kieli",
|
||||||
"setting_notifications_notify_failures_grace_period": "Ilmoita taustavarmuuskopioinnin epäonnistumisista: {}",
|
"setting_notifications_notify_failures_grace_period": "Ilmoita taustavarmuuskopioinnin epäonnistumisista: {}",
|
||||||
@@ -407,10 +408,13 @@
|
|||||||
"setting_notifications_total_progress_title": "Näytä taustavarmuuskopioinnin kokonaisedistyminen",
|
"setting_notifications_total_progress_title": "Näytä taustavarmuuskopioinnin kokonaisedistyminen",
|
||||||
"setting_pages_app_bar_settings": "Asetukset",
|
"setting_pages_app_bar_settings": "Asetukset",
|
||||||
"settings_require_restart": "Käynnistä Immich uudelleen ottaaksesti tämän asetuksen käyttöön",
|
"settings_require_restart": "Käynnistä Immich uudelleen ottaaksesti tämän asetuksen käyttöön",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videot",
|
||||||
"share_add": "Lisää",
|
"share_add": "Lisää",
|
||||||
"share_add_photos": "Lisää kuvia",
|
"share_add_photos": "Lisää kuvia",
|
||||||
"share_add_title": "Lisää nimi",
|
"share_add_title": "Lisää nimi",
|
||||||
"share_assets_selected": "{} selected",
|
"share_assets_selected": "{} valittu",
|
||||||
"share_create_album": "Luo albumi",
|
"share_create_album": "Luo albumi",
|
||||||
"shared_album_activities_input_disable": "Kommentointi on kytketty pois päältä",
|
"shared_album_activities_input_disable": "Kommentointi on kytketty pois päältä",
|
||||||
"shared_album_activities_input_hint": "Sano jotain",
|
"shared_album_activities_input_hint": "Sano jotain",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Charger l'image originale",
|
"setting_image_viewer_original_title": "Charger l'image originale",
|
||||||
"setting_image_viewer_preview_subtitle": "Activer pour charger une image de résolution moyenne. Désactiver pour charger directement l'original ou utiliser uniquement la vignette.",
|
"setting_image_viewer_preview_subtitle": "Activer pour charger une image de résolution moyenne. Désactiver pour charger directement l'original ou utiliser uniquement la vignette.",
|
||||||
"setting_image_viewer_preview_title": "Charger l'image d'aperçu",
|
"setting_image_viewer_preview_title": "Charger l'image d'aperçu",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notifier les échecs de la sauvegarde en arrière-plan: {}",
|
"setting_notifications_notify_failures_grace_period": "Notifier les échecs de la sauvegarde en arrière-plan: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Afficher la progression totale de la sauvegarde en arrière-plan",
|
"setting_notifications_total_progress_title": "Afficher la progression totale de la sauvegarde en arrière-plan",
|
||||||
"setting_pages_app_bar_settings": "Paramètres",
|
"setting_pages_app_bar_settings": "Paramètres",
|
||||||
"settings_require_restart": "Veuillez redémarrer Immich pour appliquer ce paramètre",
|
"settings_require_restart": "Veuillez redémarrer Immich pour appliquer ce paramètre",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Ajouter",
|
"share_add": "Ajouter",
|
||||||
"share_add_photos": "Ajouter des photos",
|
"share_add_photos": "Ajouter des photos",
|
||||||
"share_add_title": "Ajouter un titre",
|
"share_add_title": "Ajouter un titre",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"action_common_back": "Back",
|
"action_common_back": "Retour",
|
||||||
"action_common_cancel": "Annuler",
|
"action_common_cancel": "Annuler",
|
||||||
"action_common_clear": "Clear",
|
"action_common_clear": "Vider",
|
||||||
"action_common_confirm": "Confirm",
|
"action_common_confirm": "Confirmer",
|
||||||
"action_common_update": "Mise à jour",
|
"action_common_update": "Mise à jour",
|
||||||
"add_to_album_bottom_sheet_added": "Ajouté à {album}",
|
"add_to_album_bottom_sheet_added": "Ajouté à {album}",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Déjà dans {album}",
|
"add_to_album_bottom_sheet_already_exists": "Déjà dans {album}",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"album_thumbnail_card_shared": " · Partagé",
|
"album_thumbnail_card_shared": " · Partagé",
|
||||||
"album_thumbnail_owned": "Possédé",
|
"album_thumbnail_owned": "Possédé",
|
||||||
"album_thumbnail_shared_by": "Partagé par {}",
|
"album_thumbnail_shared_by": "Partagé par {}",
|
||||||
"album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?",
|
"album_viewer_appbar_delete_confirm": "Êtes-vous sur de vouloir supprimer cet album de votre compte?",
|
||||||
"album_viewer_appbar_share_delete": "Supprimer l'album",
|
"album_viewer_appbar_share_delete": "Supprimer l'album",
|
||||||
"album_viewer_appbar_share_err_delete": "Échec de la suppression de l'album",
|
"album_viewer_appbar_share_err_delete": "Échec de la suppression de l'album",
|
||||||
"album_viewer_appbar_share_err_leave": "Impossible de quitter l'album",
|
"album_viewer_appbar_share_err_leave": "Impossible de quitter l'album",
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"archive_page_title": "Archive ({})",
|
"archive_page_title": "Archive ({})",
|
||||||
"asset_action_delete_err_read_only": "Impossible de supprimer le(s) élément(s) en lecture seule.",
|
"asset_action_delete_err_read_only": "Impossible de supprimer le(s) élément(s) en lecture seule.",
|
||||||
"asset_action_share_err_offline": "Impossible de récupérer le(s) élément(s) hors ligne.",
|
"asset_action_share_err_offline": "Impossible de récupérer le(s) élément(s) hors ligne.",
|
||||||
"asset_list_group_by_sub_title": "Groupe par",
|
"asset_list_group_by_sub_title": "Regrouper par",
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "Affichage dynamique",
|
"asset_list_layout_settings_dynamic_layout_title": "Affichage dynamique",
|
||||||
"asset_list_layout_settings_group_automatically": "Automatique",
|
"asset_list_layout_settings_group_automatically": "Automatique",
|
||||||
"asset_list_layout_settings_group_by": "Grouper les éléments par",
|
"asset_list_layout_settings_group_by": "Grouper les éléments par",
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
"backup_manual_in_progress": "Téléchargement déjà en cours. Essayez après un instant",
|
"backup_manual_in_progress": "Téléchargement déjà en cours. Essayez après un instant",
|
||||||
"backup_manual_success": "Succès ",
|
"backup_manual_success": "Succès ",
|
||||||
"backup_manual_title": "Statut du téléchargement ",
|
"backup_manual_title": "Statut du téléchargement ",
|
||||||
"backup_options_page_title": "Backup options",
|
"backup_options_page_title": "Options de sauvegarde",
|
||||||
"cache_settings_album_thumbnails": "Miniatures de la page bibliothèque ({} éléments)",
|
"cache_settings_album_thumbnails": "Miniatures de la page bibliothèque ({} éléments)",
|
||||||
"cache_settings_clear_cache_button": "Effacer le cache",
|
"cache_settings_clear_cache_button": "Effacer le cache",
|
||||||
"cache_settings_clear_cache_button_title": "Efface le cache de l'application. Cela aura un impact significatif sur les performances de l'application jusqu'à ce que le cache soit reconstruit.",
|
"cache_settings_clear_cache_button_title": "Efface le cache de l'application. Cela aura un impact significatif sur les performances de l'application jusqu'à ce que le cache soit reconstruit.",
|
||||||
@@ -218,7 +218,7 @@
|
|||||||
"home_page_share_err_local": "Impossible de partager par lien les médias locaux, cette opération est donc ignorée.",
|
"home_page_share_err_local": "Impossible de partager par lien les médias locaux, cette opération est donc ignorée.",
|
||||||
"home_page_upload_err_limit": "Limite de téléchargement de 30 éléments en même temps, demande ignorée",
|
"home_page_upload_err_limit": "Limite de téléchargement de 30 éléments en même temps, demande ignorée",
|
||||||
"image_viewer_page_state_provider_download_error": "Erreur de téléchargement",
|
"image_viewer_page_state_provider_download_error": "Erreur de téléchargement",
|
||||||
"image_viewer_page_state_provider_download_started": "Download Started",
|
"image_viewer_page_state_provider_download_started": "Téléchargement Démarré",
|
||||||
"image_viewer_page_state_provider_download_success": "Téléchargement réussi",
|
"image_viewer_page_state_provider_download_success": "Téléchargement réussi",
|
||||||
"image_viewer_page_state_provider_share_error": "Erreur de partage",
|
"image_viewer_page_state_provider_share_error": "Erreur de partage",
|
||||||
"library_page_albums": "Albums",
|
"library_page_albums": "Albums",
|
||||||
@@ -286,7 +286,7 @@
|
|||||||
"map_settings_dialog_save": "Sauvegarder",
|
"map_settings_dialog_save": "Sauvegarder",
|
||||||
"map_settings_dialog_title": "Paramètres de la carte",
|
"map_settings_dialog_title": "Paramètres de la carte",
|
||||||
"map_settings_include_show_archived": "Inclure les archives",
|
"map_settings_include_show_archived": "Inclure les archives",
|
||||||
"map_settings_include_show_partners": "Include Partners",
|
"map_settings_include_show_partners": "Inclure les partenaires",
|
||||||
"map_settings_only_relative_range": "Plage de dates",
|
"map_settings_only_relative_range": "Plage de dates",
|
||||||
"map_settings_only_show_favorites": "Afficher uniquement les favoris",
|
"map_settings_only_show_favorites": "Afficher uniquement les favoris",
|
||||||
"map_settings_theme_settings": "Thème de la carte",
|
"map_settings_theme_settings": "Thème de la carte",
|
||||||
@@ -299,15 +299,15 @@
|
|||||||
"motion_photos_page_title": "Photos avec mouvement",
|
"motion_photos_page_title": "Photos avec mouvement",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Impossible de modifier la date d'un élément d'actif en lecture seule.",
|
"multiselect_grid_edit_date_time_err_read_only": "Impossible de modifier la date d'un élément d'actif en lecture seule.",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Impossible de modifier l'emplacement d'un élément en lecture seule.",
|
"multiselect_grid_edit_gps_err_read_only": "Impossible de modifier l'emplacement d'un élément en lecture seule.",
|
||||||
"no_assets_to_show": "Aucun élément à afficher",
|
"no_assets_to_show": "Aucuns éléments à afficher",
|
||||||
"notification_permission_dialog_cancel": "Annuler",
|
"notification_permission_dialog_cancel": "Annuler",
|
||||||
"notification_permission_dialog_content": "Pour activer les notifications, allez dans Paramètres et sélectionnez Autoriser.",
|
"notification_permission_dialog_content": "Pour activer les notifications, allez dans Paramètres et sélectionnez Autoriser.",
|
||||||
"notification_permission_dialog_settings": "Paramètres",
|
"notification_permission_dialog_settings": "Paramètres",
|
||||||
"notification_permission_list_tile_content": "Accordez la permission d'activer les notifications.",
|
"notification_permission_list_tile_content": "Accordez la permission d'activer les notifications.",
|
||||||
"notification_permission_list_tile_enable_button": "Activer les notifications",
|
"notification_permission_list_tile_enable_button": "Activer les notifications",
|
||||||
"notification_permission_list_tile_title": "Permission de notification",
|
"notification_permission_list_tile_title": "Permission de notification",
|
||||||
"partner_list_user_photos": "{user}'s photos",
|
"partner_list_user_photos": "Photos de {user}",
|
||||||
"partner_list_view_all": "View all",
|
"partner_list_view_all": "Voir tous",
|
||||||
"partner_page_add_partner": "Ajouter un partenaire",
|
"partner_page_add_partner": "Ajouter un partenaire",
|
||||||
"partner_page_empty_message": "Vos photos ne sont pas encore partagées avec un partenaire.",
|
"partner_page_empty_message": "Vos photos ne sont pas encore partagées avec un partenaire.",
|
||||||
"partner_page_no_more_users": "Plus d'utilisateurs à ajouter",
|
"partner_page_no_more_users": "Plus d'utilisateurs à ajouter",
|
||||||
@@ -342,18 +342,18 @@
|
|||||||
"recently_added_page_title": "Récemment ajouté",
|
"recently_added_page_title": "Récemment ajouté",
|
||||||
"scaffold_body_error_occurred": "Une erreur s'est produite",
|
"scaffold_body_error_occurred": "Une erreur s'est produite",
|
||||||
"search_bar_hint": "Rechercher vos photos",
|
"search_bar_hint": "Rechercher vos photos",
|
||||||
"search_filter_apply": "Apply filter",
|
"search_filter_apply": "Appliquer le filtre",
|
||||||
"search_filter_camera_make": "Make",
|
"search_filter_camera_make": "Fabricant",
|
||||||
"search_filter_camera_model": "Model",
|
"search_filter_camera_model": "Modéle",
|
||||||
"search_filter_display_option_archive": "Archive",
|
"search_filter_display_option_archive": "Achive",
|
||||||
"search_filter_display_option_favorite": "Favorite",
|
"search_filter_display_option_favorite": "Favoris",
|
||||||
"search_filter_display_option_not_in_album": "Not in album",
|
"search_filter_display_option_not_in_album": "Pas dans un album",
|
||||||
"search_filter_location_city": "City",
|
"search_filter_location_city": "Ville",
|
||||||
"search_filter_location_country": "Country",
|
"search_filter_location_country": "Pays",
|
||||||
"search_filter_location_state": "State",
|
"search_filter_location_state": "Région",
|
||||||
"search_filter_media_type_all": "All",
|
"search_filter_media_type_all": "Tous",
|
||||||
"search_filter_media_type_image": "Image",
|
"search_filter_media_type_image": "Image",
|
||||||
"search_filter_media_type_video": "Video",
|
"search_filter_media_type_video": "Vidéo",
|
||||||
"search_page_categories": "Catégories",
|
"search_page_categories": "Catégories",
|
||||||
"search_page_favorites": "Favoris",
|
"search_page_favorites": "Favoris",
|
||||||
"search_page_motion_photos": "Photos animées",
|
"search_page_motion_photos": "Photos animées",
|
||||||
@@ -391,8 +391,9 @@
|
|||||||
"setting_image_viewer_original_title": "Charger l'image originale",
|
"setting_image_viewer_original_title": "Charger l'image originale",
|
||||||
"setting_image_viewer_preview_subtitle": "Activer pour charger une image de résolution moyenne. Désactiver pour charger directement l'original ou utiliser uniquement la miniature.",
|
"setting_image_viewer_preview_subtitle": "Activer pour charger une image de résolution moyenne. Désactiver pour charger directement l'original ou utiliser uniquement la miniature.",
|
||||||
"setting_image_viewer_preview_title": "Charger l'image d'aperçu",
|
"setting_image_viewer_preview_title": "Charger l'image d'aperçu",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_apply": "Appliquer",
|
||||||
|
"setting_languages_title": "Langues",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notifier les échecs de la sauvegarde en arrière-plan : {}",
|
"setting_notifications_notify_failures_grace_period": "Notifier les échecs de la sauvegarde en arrière-plan : {}",
|
||||||
"setting_notifications_notify_hours": "{} heures",
|
"setting_notifications_notify_hours": "{} heures",
|
||||||
"setting_notifications_notify_immediately": "immédiatement",
|
"setting_notifications_notify_immediately": "immédiatement",
|
||||||
@@ -407,10 +408,13 @@
|
|||||||
"setting_notifications_total_progress_title": "Afficher la progression totale de la sauvegarde en arrière-plan",
|
"setting_notifications_total_progress_title": "Afficher la progression totale de la sauvegarde en arrière-plan",
|
||||||
"setting_pages_app_bar_settings": "Paramètres",
|
"setting_pages_app_bar_settings": "Paramètres",
|
||||||
"settings_require_restart": "Veuillez redémarrer Immich pour appliquer ce paramètre",
|
"settings_require_restart": "Veuillez redémarrer Immich pour appliquer ce paramètre",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Activer pour voir les vidéos en boucle dans le lecteur détaillé",
|
||||||
|
"setting_video_viewer_looping_title": "Boucle",
|
||||||
|
"setting_video_viewer_title": "Vidéos",
|
||||||
"share_add": "Ajouter",
|
"share_add": "Ajouter",
|
||||||
"share_add_photos": "Ajouter des photos",
|
"share_add_photos": "Ajouter des photos",
|
||||||
"share_add_title": "Ajouter un titre",
|
"share_add_title": "Ajouter un titre",
|
||||||
"share_assets_selected": "{} selected",
|
"share_assets_selected": "{} séléctionné(s)",
|
||||||
"share_create_album": "Créer un album",
|
"share_create_album": "Créer un album",
|
||||||
"shared_album_activities_input_disable": "Les commentaires sont désactivés",
|
"shared_album_activities_input_disable": "Les commentaires sont désactivés",
|
||||||
"shared_album_activities_input_hint": "Dire quelque chose",
|
"shared_album_activities_input_hint": "Dire quelque chose",
|
||||||
@@ -444,9 +448,9 @@
|
|||||||
"shared_link_edit_expire_after_option_hours": "{} heures",
|
"shared_link_edit_expire_after_option_hours": "{} heures",
|
||||||
"shared_link_edit_expire_after_option_minute": "1 minute",
|
"shared_link_edit_expire_after_option_minute": "1 minute",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minutes",
|
"shared_link_edit_expire_after_option_minutes": "{} minutes",
|
||||||
"shared_link_edit_expire_after_option_months": "{} months",
|
"shared_link_edit_expire_after_option_months": "{} mois",
|
||||||
"shared_link_edit_expire_after_option_never": "Jamais",
|
"shared_link_edit_expire_after_option_never": "Jamais",
|
||||||
"shared_link_edit_expire_after_option_year": "{} year",
|
"shared_link_edit_expire_after_option_year": "{} ans",
|
||||||
"shared_link_edit_password": "Mot de passe",
|
"shared_link_edit_password": "Mot de passe",
|
||||||
"shared_link_edit_password_hint": "Saisir le mot de passe de partage",
|
"shared_link_edit_password_hint": "Saisir le mot de passe de partage",
|
||||||
"shared_link_edit_show_meta": "Afficher les métadonnées",
|
"shared_link_edit_show_meta": "Afficher les métadonnées",
|
||||||
@@ -463,12 +467,12 @@
|
|||||||
"shared_link_expires_never": "Expire ∞",
|
"shared_link_expires_never": "Expire ∞",
|
||||||
"shared_link_expires_second": "Expire dans {} seconde",
|
"shared_link_expires_second": "Expire dans {} seconde",
|
||||||
"shared_link_expires_seconds": "Expire dans {} secondes",
|
"shared_link_expires_seconds": "Expire dans {} secondes",
|
||||||
"shared_link_individual_shared": "Individual shared",
|
"shared_link_individual_shared": "Partagé individuellement",
|
||||||
"shared_link_info_chip_download": "Téléchargement",
|
"shared_link_info_chip_download": "Téléchargement",
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
"shared_link_info_chip_upload": "Chargement",
|
"shared_link_info_chip_upload": "Chargement",
|
||||||
"shared_link_manage_links": "Gérer les liens partagés",
|
"shared_link_manage_links": "Gérer les liens partagés",
|
||||||
"shared_link_public_album": "Public album",
|
"shared_link_public_album": "Album public",
|
||||||
"share_done": "Fait",
|
"share_done": "Fait",
|
||||||
"share_invite": "Inviter à l'album",
|
"share_invite": "Inviter à l'album",
|
||||||
"sharing_page_album": "Albums partagés",
|
"sharing_page_album": "Albums partagés",
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
"backup_controller_page_background_turn_off": "כבה שירות ברקע",
|
"backup_controller_page_background_turn_off": "כבה שירות ברקע",
|
||||||
"backup_controller_page_background_turn_on": "הפעל שירות ברקע",
|
"backup_controller_page_background_turn_on": "הפעל שירות ברקע",
|
||||||
"backup_controller_page_background_wifi": "רק ברשת אלחוטית",
|
"backup_controller_page_background_wifi": "רק ברשת אלחוטית",
|
||||||
"backup_controller_page_backup": "גיבוי",
|
"backup_controller_page_backup": "גובו",
|
||||||
"backup_controller_page_backup_selected": "נבחרו:",
|
"backup_controller_page_backup_selected": "נבחרו:",
|
||||||
"backup_controller_page_backup_sub": "תמונות וסרטונים מגובים",
|
"backup_controller_page_backup_sub": "תמונות וסרטונים מגובים",
|
||||||
"backup_controller_page_cancel": "ביטול",
|
"backup_controller_page_cancel": "ביטול",
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
"backup_controller_page_id": "מזהה: {}",
|
"backup_controller_page_id": "מזהה: {}",
|
||||||
"backup_controller_page_info": "פרטי גיבוי",
|
"backup_controller_page_info": "פרטי גיבוי",
|
||||||
"backup_controller_page_none_selected": "לא נבחרו",
|
"backup_controller_page_none_selected": "לא נבחרו",
|
||||||
"backup_controller_page_remainder": "יתרה",
|
"backup_controller_page_remainder": "בתור לגיבוי",
|
||||||
"backup_controller_page_remainder_sub": "תמונות וסרטונים שנותרו לגבות מתוך בחירה",
|
"backup_controller_page_remainder_sub": "תמונות וסרטונים שנותרו לגבות מתוך בחירה",
|
||||||
"backup_controller_page_select": "בחר",
|
"backup_controller_page_select": "בחר",
|
||||||
"backup_controller_page_server_storage": "אחסון שרת",
|
"backup_controller_page_server_storage": "אחסון שרת",
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
"backup_controller_page_total_sub": "כל התמונות והסרטונים הייחודיים מאלבומים שנבחרו",
|
"backup_controller_page_total_sub": "כל התמונות והסרטונים הייחודיים מאלבומים שנבחרו",
|
||||||
"backup_controller_page_turn_off": "כבה גיבוי חזית",
|
"backup_controller_page_turn_off": "כבה גיבוי חזית",
|
||||||
"backup_controller_page_turn_on": "הפעל גיבוי חזית",
|
"backup_controller_page_turn_on": "הפעל גיבוי חזית",
|
||||||
"backup_controller_page_uploading_file_info": "מעלה פרטי קובץ",
|
"backup_controller_page_uploading_file_info": "מידע על הקובץ",
|
||||||
"backup_err_only_album": "לא ניתן להסיר את האלבום היחידי",
|
"backup_err_only_album": "לא ניתן להסיר את האלבום היחידי",
|
||||||
"backup_info_card_assets": "נכסים",
|
"backup_info_card_assets": "נכסים",
|
||||||
"backup_manual_cancelled": "בוטל",
|
"backup_manual_cancelled": "בוטל",
|
||||||
@@ -218,7 +218,7 @@
|
|||||||
"home_page_share_err_local": "לא ניתן לשתף נכסים מקומיים על ידי קישור, מדלג",
|
"home_page_share_err_local": "לא ניתן לשתף נכסים מקומיים על ידי קישור, מדלג",
|
||||||
"home_page_upload_err_limit": "יכול רק להעלות מקסימום של 30 נכסים בכל פעם, מדלג",
|
"home_page_upload_err_limit": "יכול רק להעלות מקסימום של 30 נכסים בכל פעם, מדלג",
|
||||||
"image_viewer_page_state_provider_download_error": "שגיאת הורדה",
|
"image_viewer_page_state_provider_download_error": "שגיאת הורדה",
|
||||||
"image_viewer_page_state_provider_download_started": "Download Started",
|
"image_viewer_page_state_provider_download_started": "ההורדה החלה",
|
||||||
"image_viewer_page_state_provider_download_success": "הצלחת הורדה",
|
"image_viewer_page_state_provider_download_success": "הצלחת הורדה",
|
||||||
"image_viewer_page_state_provider_share_error": "שיתוף שגיאה",
|
"image_viewer_page_state_provider_share_error": "שיתוף שגיאה",
|
||||||
"library_page_albums": "אלבומים",
|
"library_page_albums": "אלבומים",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "טען תמונה מקורית",
|
"setting_image_viewer_original_title": "טען תמונה מקורית",
|
||||||
"setting_image_viewer_preview_subtitle": "אפשר לטעון תמונה ברזלוציה בינונית. השבת כדי או לטעון את המקורית או רק להשתמש בתמונה הממוזערת.",
|
"setting_image_viewer_preview_subtitle": "אפשר לטעון תמונה ברזלוציה בינונית. השבת כדי או לטעון את המקורית או רק להשתמש בתמונה הממוזערת.",
|
||||||
"setting_image_viewer_preview_title": "טען תמונת תצוגה מקדימה",
|
"setting_image_viewer_preview_title": "טען תמונת תצוגה מקדימה",
|
||||||
|
"setting_image_viewer_title": "תמונות",
|
||||||
"setting_languages_apply": "החל",
|
"setting_languages_apply": "החל",
|
||||||
"setting_languages_title": "שפות",
|
"setting_languages_title": "שפות",
|
||||||
"setting_notifications_notify_failures_grace_period": "להודיע על כשלים בגיבוי ברקע: {}",
|
"setting_notifications_notify_failures_grace_period": "להודיע על כשלים בגיבוי ברקע: {}",
|
||||||
@@ -407,10 +408,13 @@
|
|||||||
"setting_notifications_total_progress_title": "הראה סה״כ התקדמות גיבוי ברקע",
|
"setting_notifications_total_progress_title": "הראה סה״כ התקדמות גיבוי ברקע",
|
||||||
"setting_pages_app_bar_settings": "הגדרות",
|
"setting_pages_app_bar_settings": "הגדרות",
|
||||||
"settings_require_restart": "אנא הפעל מחדש את Immich כדי להחיל הגדרה זו",
|
"settings_require_restart": "אנא הפעל מחדש את Immich כדי להחיל הגדרה זו",
|
||||||
|
"setting_video_viewer_looping_subtitle": "אפשר וידיאו ברצף אוטומטית בחלון המידע",
|
||||||
|
"setting_video_viewer_looping_title": "לולאה",
|
||||||
|
"setting_video_viewer_title": "סרטונים",
|
||||||
"share_add": "הוסף",
|
"share_add": "הוסף",
|
||||||
"share_add_photos": "הוסף תמונות",
|
"share_add_photos": "הוסף תמונות",
|
||||||
"share_add_title": "הוסף כותרת",
|
"share_add_title": "הוסף כותרת",
|
||||||
"share_assets_selected": "{} selected",
|
"share_assets_selected": "{} נבחרו",
|
||||||
"share_create_album": "צור אלבום",
|
"share_create_album": "צור אלבום",
|
||||||
"shared_album_activities_input_disable": "התגובה מושבתת",
|
"shared_album_activities_input_disable": "התגובה מושבתת",
|
||||||
"shared_album_activities_input_hint": "הגב/י משהו",
|
"shared_album_activities_input_hint": "הגב/י משהו",
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
||||||
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
||||||
"backup_controller_page_storage_format": "{} of {} used",
|
"backup_controller_page_storage_format": "{} of {} used",
|
||||||
"backup_controller_page_to_backup": "Albums to be backup",
|
"backup_controller_page_to_backup": "Albums to be backed up",
|
||||||
"backup_controller_page_total": "Total",
|
"backup_controller_page_total": "Total",
|
||||||
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
||||||
"backup_controller_page_turn_off": "Turn off foreground backup",
|
"backup_controller_page_turn_off": "Turn off foreground backup",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Load original image",
|
"setting_image_viewer_original_title": "Load original image",
|
||||||
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
||||||
"setting_image_viewer_preview_title": "Load preview image",
|
"setting_image_viewer_preview_title": "Load preview image",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Show background backup total progress",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Settings",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
"settings_require_restart": "Please restart Immich to apply this setting",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Add",
|
"share_add": "Add",
|
||||||
"share_add_photos": "Add photos",
|
"share_add_photos": "Add photos",
|
||||||
"share_add_title": "Add a title",
|
"share_add_title": "Add a title",
|
||||||
|
|||||||
@@ -194,7 +194,7 @@
|
|||||||
"exif_bottom_sheet_location": "HELY",
|
"exif_bottom_sheet_location": "HELY",
|
||||||
"exif_bottom_sheet_location_add": "Hely hozzáadása",
|
"exif_bottom_sheet_location_add": "Hely hozzáadása",
|
||||||
"exif_bottom_sheet_people": "EMBEREK",
|
"exif_bottom_sheet_people": "EMBEREK",
|
||||||
"exif_bottom_sheet_person_add_person": "Név hozzáadása",
|
"exif_bottom_sheet_person_add_person": "Elnevez",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Fejlesztés alatt",
|
"experimental_settings_new_asset_list_subtitle": "Fejlesztés alatt",
|
||||||
"experimental_settings_new_asset_list_title": "Kisérleti képrács engedélyezése",
|
"experimental_settings_new_asset_list_title": "Kisérleti képrács engedélyezése",
|
||||||
"experimental_settings_subtitle": "Csak saját felelősségre használd!",
|
"experimental_settings_subtitle": "Csak saját felelősségre használd!",
|
||||||
@@ -363,9 +363,9 @@
|
|||||||
"search_page_person_add_name_dialog_cancel": "Mégsem",
|
"search_page_person_add_name_dialog_cancel": "Mégsem",
|
||||||
"search_page_person_add_name_dialog_hint": "Név",
|
"search_page_person_add_name_dialog_hint": "Név",
|
||||||
"search_page_person_add_name_dialog_save": "Mentés",
|
"search_page_person_add_name_dialog_save": "Mentés",
|
||||||
"search_page_person_add_name_dialog_title": "Név hozzáadása",
|
"search_page_person_add_name_dialog_title": "Elnevez",
|
||||||
"search_page_person_add_name_subtitle": "Név szerint gyorsan megtalálhatod a keresőben",
|
"search_page_person_add_name_subtitle": "Név szerint gyorsan megtalálhatod a keresőben",
|
||||||
"search_page_person_add_name_title": "Név hozzáadása",
|
"search_page_person_add_name_title": "Elnevez",
|
||||||
"search_page_person_edit_name": "Név módosítása",
|
"search_page_person_edit_name": "Név módosítása",
|
||||||
"search_page_places": "Helyek",
|
"search_page_places": "Helyek",
|
||||||
"search_page_recently_added": "Nemrég hozzáadott",
|
"search_page_recently_added": "Nemrég hozzáadott",
|
||||||
@@ -374,8 +374,8 @@
|
|||||||
"search_page_things": "Dolgok",
|
"search_page_things": "Dolgok",
|
||||||
"search_page_videos": "Videók",
|
"search_page_videos": "Videók",
|
||||||
"search_page_view_all_button": "Összes mutatása",
|
"search_page_view_all_button": "Összes mutatása",
|
||||||
"search_page_your_activity": "Tevékenységeid",
|
"search_page_your_activity": "Tevékenységek",
|
||||||
"search_page_your_map": "Térképed",
|
"search_page_your_map": "Térkép",
|
||||||
"search_result_page_new_search_hint": "Új keresés",
|
"search_result_page_new_search_hint": "Új keresés",
|
||||||
"search_suggestion_list_smart_search_hint_1": "Az intelligens keresés alapértelmezetten be van kapcsolva, metaadatokat így kereshetsz",
|
"search_suggestion_list_smart_search_hint_1": "Az intelligens keresés alapértelmezetten be van kapcsolva, metaadatokat így kereshetsz",
|
||||||
"search_suggestion_list_smart_search_hint_2": "m:keresési-kifejezés",
|
"search_suggestion_list_smart_search_hint_2": "m:keresési-kifejezés",
|
||||||
@@ -386,11 +386,12 @@
|
|||||||
"server_info_box_latest_release": "Legfrissebb Verzió",
|
"server_info_box_latest_release": "Legfrissebb Verzió",
|
||||||
"server_info_box_server_url": "Szerver Címe",
|
"server_info_box_server_url": "Szerver Címe",
|
||||||
"server_info_box_server_version": "Szerver Verzió",
|
"server_info_box_server_version": "Szerver Verzió",
|
||||||
"setting_image_viewer_help": "A képnézegető először a kis bélyegképet tölti be, aztán a közepes méretű előnézetet (ha elérhető), végül az eredetit (ha elérhető).",
|
"setting_image_viewer_help": "Az Elem Megjelenítő először a kis bélyegképet tölti be, aztán a közepes méretű előnézetet (ha elérhető), végül az eredetit (ha elérhető).",
|
||||||
"setting_image_viewer_original_subtitle": "Engedélyezi az eredeti teljes felbontású kép betöltését (nagy!). Kikapcsolva csökkenti az adathasználatot (a neten és az eszköz gyorsítótárán is).",
|
"setting_image_viewer_original_subtitle": "Engedélyezi az eredeti teljes felbontású kép betöltését (nagy!). Kikapcsolva csökkenti az adathasználatot (a neten és az eszköz gyorsítótárán is).",
|
||||||
"setting_image_viewer_original_title": "Eredeti kép betöltése",
|
"setting_image_viewer_original_title": "Eredeti kép betöltése",
|
||||||
"setting_image_viewer_preview_subtitle": "Engedélyezi a közepes felbontású kép betöltését. Kikapcsolva vagy az eredeti kép töltődik be, vagy csak a bélyegkép.",
|
"setting_image_viewer_preview_subtitle": "Engedélyezi a közepes felbontású kép betöltését. Kikapcsolva vagy az eredeti kép töltődik be, vagy csak a bélyegkép.",
|
||||||
"setting_image_viewer_preview_title": "Előnézet betöltése",
|
"setting_image_viewer_preview_title": "Előnézet betöltése",
|
||||||
|
"setting_image_viewer_title": "Képek",
|
||||||
"setting_languages_apply": "Alkalmaz",
|
"setting_languages_apply": "Alkalmaz",
|
||||||
"setting_languages_title": "Nyelvek",
|
"setting_languages_title": "Nyelvek",
|
||||||
"setting_notifications_notify_failures_grace_period": "Értesítés a háttérben történő mentés hibáiról: {}",
|
"setting_notifications_notify_failures_grace_period": "Értesítés a háttérben történő mentés hibáiról: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Mutassa a háttérben történő mentés teljes folyamatát",
|
"setting_notifications_total_progress_title": "Mutassa a háttérben történő mentés teljes folyamatát",
|
||||||
"setting_pages_app_bar_settings": "Beállítások",
|
"setting_pages_app_bar_settings": "Beállítások",
|
||||||
"settings_require_restart": "Ennek a beállításnak az érvénybe lépéséhez indítsd újra az Immich-et",
|
"settings_require_restart": "Ennek a beállításnak az érvénybe lépéséhez indítsd újra az Immich-et",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Engedélyezi a videók folyamatosan ismételt lejátszását az elem megjelenítőben",
|
||||||
|
"setting_video_viewer_looping_title": "Ismétlés",
|
||||||
|
"setting_video_viewer_title": "Videók",
|
||||||
"share_add": "Hozzáadás",
|
"share_add": "Hozzáadás",
|
||||||
"share_add_photos": "Fotók hozzáadása",
|
"share_add_photos": "Fotók hozzáadása",
|
||||||
"share_add_title": "Album neve",
|
"share_add_title": "Album neve",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Carica l'immagine originale",
|
"setting_image_viewer_original_title": "Carica l'immagine originale",
|
||||||
"setting_image_viewer_preview_subtitle": "Abilita per caricare un'immagine a risoluzione media.\nDisabilita per caricare direttamente l'immagine originale o usare la thumbnail.",
|
"setting_image_viewer_preview_subtitle": "Abilita per caricare un'immagine a risoluzione media.\nDisabilita per caricare direttamente l'immagine originale o usare la thumbnail.",
|
||||||
"setting_image_viewer_preview_title": "Carica immagine di anteprima",
|
"setting_image_viewer_preview_title": "Carica immagine di anteprima",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Applica",
|
"setting_languages_apply": "Applica",
|
||||||
"setting_languages_title": "Lingue",
|
"setting_languages_title": "Lingue",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notifica caricamenti falliti in background: {}",
|
"setting_notifications_notify_failures_grace_period": "Notifica caricamenti falliti in background: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Mostra avanzamento del backup in background",
|
"setting_notifications_total_progress_title": "Mostra avanzamento del backup in background",
|
||||||
"setting_pages_app_bar_settings": "Impostazioni",
|
"setting_pages_app_bar_settings": "Impostazioni",
|
||||||
"settings_require_restart": "Si prega di riavviare Immich perché vengano applicate le impostazioni",
|
"settings_require_restart": "Si prega di riavviare Immich perché vengano applicate le impostazioni",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Aggiungi",
|
"share_add": "Aggiungi",
|
||||||
"share_add_photos": "Aggiungi foto",
|
"share_add_photos": "Aggiungi foto",
|
||||||
"share_add_title": "Aggiungi un titolo ",
|
"share_add_title": "Aggiungi un titolo ",
|
||||||
|
|||||||
@@ -174,7 +174,7 @@
|
|||||||
"date_format": "MM月 DD日, EE • hh時mm分",
|
"date_format": "MM月 DD日, EE • hh時mm分",
|
||||||
"delete_dialog_alert": "サーバーとデバイスの両方から永久的に削除されます!",
|
"delete_dialog_alert": "サーバーとデバイスの両方から永久的に削除されます!",
|
||||||
"delete_dialog_alert_local": "選択された項目はデバイスから削除されますが、Immichには残ります",
|
"delete_dialog_alert_local": "選択された項目はデバイスから削除されますが、Immichには残ります",
|
||||||
"delete_dialog_alert_local_non_backed_up": "Immichにバックアップされていない項目があります。それらはデバイスからも永久に削除されます",
|
"delete_dialog_alert_local_non_backed_up": "Immichにバックアップされていない項目があります。デバイスからも永久に削除されます",
|
||||||
"delete_dialog_alert_remote": "選択された項目はImmichから永久に削除されます",
|
"delete_dialog_alert_remote": "選択された項目はImmichから永久に削除されます",
|
||||||
"delete_dialog_cancel": "キャンセル",
|
"delete_dialog_cancel": "キャンセル",
|
||||||
"delete_dialog_ok": "削除",
|
"delete_dialog_ok": "削除",
|
||||||
@@ -201,8 +201,8 @@
|
|||||||
"experimental_settings_title": "試験的機能",
|
"experimental_settings_title": "試験的機能",
|
||||||
"favorites_page_no_favorites": "お気に入り登録された写真またはビデオがありません",
|
"favorites_page_no_favorites": "お気に入り登録された写真またはビデオがありません",
|
||||||
"favorites_page_title": "お気に入り",
|
"favorites_page_title": "お気に入り",
|
||||||
"haptic_feedback_switch": "触覚フィードバック",
|
"haptic_feedback_switch": "ハプティックフィードバック",
|
||||||
"haptic_feedback_title": "触覚フィードバックを有効にする",
|
"haptic_feedback_title": "ハプティックフィードバックを有効にする",
|
||||||
"home_page_add_to_album_conflicts": "{album}に{added}枚写真を追加しました。追加済みの{failed}枚はスキップしました。",
|
"home_page_add_to_album_conflicts": "{album}に{added}枚写真を追加しました。追加済みの{failed}枚はスキップしました。",
|
||||||
"home_page_add_to_album_err_local": "まだアップロードされてない項目は、アルバムに登録できません",
|
"home_page_add_to_album_err_local": "まだアップロードされてない項目は、アルバムに登録できません",
|
||||||
"home_page_add_to_album_success": "{album}に{added}枚写真を追加しました",
|
"home_page_add_to_album_success": "{album}に{added}枚写真を追加しました",
|
||||||
@@ -299,7 +299,7 @@
|
|||||||
"motion_photos_page_title": "モーションフォト",
|
"motion_photos_page_title": "モーションフォト",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "読み取り専用の項目の日付を変更できません",
|
"multiselect_grid_edit_date_time_err_read_only": "読み取り専用の項目の日付を変更できません",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "読み取り専用の項目の位置情報を変更できません",
|
"multiselect_grid_edit_gps_err_read_only": "読み取り専用の項目の位置情報を変更できません",
|
||||||
"no_assets_to_show": "表示するアセットがありません",
|
"no_assets_to_show": "表示する項目がありません",
|
||||||
"notification_permission_dialog_cancel": "キャンセル",
|
"notification_permission_dialog_cancel": "キャンセル",
|
||||||
"notification_permission_dialog_content": "通知を許可するには設定を開いてオンにしてください",
|
"notification_permission_dialog_content": "通知を許可するには設定を開いてオンにしてください",
|
||||||
"notification_permission_dialog_settings": "設定",
|
"notification_permission_dialog_settings": "設定",
|
||||||
@@ -330,12 +330,12 @@
|
|||||||
"preferences_settings_title": "設定",
|
"preferences_settings_title": "設定",
|
||||||
"profile_drawer_app_logs": "ログ",
|
"profile_drawer_app_logs": "ログ",
|
||||||
"profile_drawer_client_out_of_date_major": "アプリが更新されてません。最新のバージョンに更新してください",
|
"profile_drawer_client_out_of_date_major": "アプリが更新されてません。最新のバージョンに更新してください",
|
||||||
"profile_drawer_client_out_of_date_minor": "アプリが更新されてません。最新のマイナーバージョンに更新してください",
|
"profile_drawer_client_out_of_date_minor": "アプリが更新されてません。最新のバージョンに更新してください",
|
||||||
"profile_drawer_client_server_up_to_date": "すべて最新です",
|
"profile_drawer_client_server_up_to_date": "すべて最新です",
|
||||||
"profile_drawer_documentation": "Immichのドキュメント",
|
"profile_drawer_documentation": "Immichのドキュメント",
|
||||||
"profile_drawer_github": "GitHub",
|
"profile_drawer_github": "GitHub",
|
||||||
"profile_drawer_server_out_of_date_major": "サーバーが更新されてません。最新のバージョンに更新してください",
|
"profile_drawer_server_out_of_date_major": "サーバーが更新されてません。最新のバージョンに更新してください",
|
||||||
"profile_drawer_server_out_of_date_minor": "サーバーが更新されてません。最新のマイナーバージョンに更新してください",
|
"profile_drawer_server_out_of_date_minor": "サーバーが更新されてません。最新のバージョンに更新してください",
|
||||||
"profile_drawer_settings": "設定",
|
"profile_drawer_settings": "設定",
|
||||||
"profile_drawer_sign_out": "サインアウト",
|
"profile_drawer_sign_out": "サインアウト",
|
||||||
"profile_drawer_trash": "ゴミ箱",
|
"profile_drawer_trash": "ゴミ箱",
|
||||||
@@ -347,7 +347,7 @@
|
|||||||
"search_filter_camera_model": "モデル",
|
"search_filter_camera_model": "モデル",
|
||||||
"search_filter_display_option_archive": "アーカイブ",
|
"search_filter_display_option_archive": "アーカイブ",
|
||||||
"search_filter_display_option_favorite": "お気に入り",
|
"search_filter_display_option_favorite": "お気に入り",
|
||||||
"search_filter_display_option_not_in_album": "アルバムにない",
|
"search_filter_display_option_not_in_album": "アルバムにありません",
|
||||||
"search_filter_location_city": "市町村",
|
"search_filter_location_city": "市町村",
|
||||||
"search_filter_location_country": "国",
|
"search_filter_location_country": "国",
|
||||||
"search_filter_location_state": "都道府県",
|
"search_filter_location_state": "都道府県",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "オリジナル画像を読み込む",
|
"setting_image_viewer_original_title": "オリジナル画像を読み込む",
|
||||||
"setting_image_viewer_preview_subtitle": "中画質の写真をロードしたいときにオンにしてください。直接最大画質の写真を表示したい場合はオフにしてください。(ロード中はサムネイルが代わりに表示されます)",
|
"setting_image_viewer_preview_subtitle": "中画質の写真をロードしたいときにオンにしてください。直接最大画質の写真を表示したい場合はオフにしてください。(ロード中はサムネイルが代わりに表示されます)",
|
||||||
"setting_image_viewer_preview_title": "プレビュー画像をロードする",
|
"setting_image_viewer_preview_title": "プレビュー画像をロードする",
|
||||||
|
"setting_image_viewer_title": "画像",
|
||||||
"setting_languages_apply": "適用する",
|
"setting_languages_apply": "適用する",
|
||||||
"setting_languages_title": "言語",
|
"setting_languages_title": "言語",
|
||||||
"setting_notifications_notify_failures_grace_period": "バックアップ失敗の通知: {}",
|
"setting_notifications_notify_failures_grace_period": "バックアップ失敗の通知: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "実行中のバックアップの進行状況を表示",
|
"setting_notifications_total_progress_title": "実行中のバックアップの進行状況を表示",
|
||||||
"setting_pages_app_bar_settings": "設定",
|
"setting_pages_app_bar_settings": "設定",
|
||||||
"settings_require_restart": "Immichを再起動して設定を適用してください",
|
"settings_require_restart": "Immichを再起動して設定を適用してください",
|
||||||
|
"setting_video_viewer_looping_subtitle": "有効にするとディテールビューで自動で動画がループします",
|
||||||
|
"setting_video_viewer_looping_title": "ループ中",
|
||||||
|
"setting_video_viewer_title": "ビデオ",
|
||||||
"share_add": "追加",
|
"share_add": "追加",
|
||||||
"share_add_photos": "写真を追加",
|
"share_add_photos": "写真を追加",
|
||||||
"share_add_title": "タイトルを追加",
|
"share_add_title": "タイトルを追加",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "원본 이미지 불러오기",
|
"setting_image_viewer_original_title": "원본 이미지 불러오기",
|
||||||
"setting_image_viewer_preview_subtitle": "중간 해상도 이미지를 로드하려면 활성화합니다. 원본을 직접 로드하거나 썸네일만 사용하려면 비활성화 하세요.",
|
"setting_image_viewer_preview_subtitle": "중간 해상도 이미지를 로드하려면 활성화합니다. 원본을 직접 로드하거나 썸네일만 사용하려면 비활성화 하세요.",
|
||||||
"setting_image_viewer_preview_title": "미리보기 이미지 불러오기",
|
"setting_image_viewer_preview_title": "미리보기 이미지 불러오기",
|
||||||
|
"setting_image_viewer_title": "이미지",
|
||||||
"setting_languages_apply": "적용",
|
"setting_languages_apply": "적용",
|
||||||
"setting_languages_title": "언어",
|
"setting_languages_title": "언어",
|
||||||
"setting_notifications_notify_failures_grace_period": "백그라운드 백업 실패 알림: {}",
|
"setting_notifications_notify_failures_grace_period": "백그라운드 백업 실패 알림: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "백그라운드 작업 전체 진행률 표시",
|
"setting_notifications_total_progress_title": "백그라운드 작업 전체 진행률 표시",
|
||||||
"setting_pages_app_bar_settings": "설정",
|
"setting_pages_app_bar_settings": "설정",
|
||||||
"settings_require_restart": "설정을 적용하려면 Immich를 다시 시작하세요.",
|
"settings_require_restart": "설정을 적용하려면 Immich를 다시 시작하세요.",
|
||||||
|
"setting_video_viewer_looping_subtitle": "상세 뷰어에서 비디오를 자동으로 반복하도록 활성화합니다.",
|
||||||
|
"setting_video_viewer_looping_title": "반복",
|
||||||
|
"setting_video_viewer_title": "비디오",
|
||||||
"share_add": "추가",
|
"share_add": "추가",
|
||||||
"share_add_photos": "사진 추가",
|
"share_add_photos": "사진 추가",
|
||||||
"share_add_title": "새 앨범제목",
|
"share_add_title": "새 앨범제목",
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
||||||
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
||||||
"backup_controller_page_storage_format": "{} of {} used",
|
"backup_controller_page_storage_format": "{} of {} used",
|
||||||
"backup_controller_page_to_backup": "Albums to be backup",
|
"backup_controller_page_to_backup": "Albums to be backed up",
|
||||||
"backup_controller_page_total": "Total",
|
"backup_controller_page_total": "Total",
|
||||||
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
||||||
"backup_controller_page_turn_off": "Turn off foreground backup",
|
"backup_controller_page_turn_off": "Turn off foreground backup",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Load original image",
|
"setting_image_viewer_original_title": "Load original image",
|
||||||
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
||||||
"setting_image_viewer_preview_title": "Load preview image",
|
"setting_image_viewer_preview_title": "Load preview image",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Show background backup total progress",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Settings",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
"settings_require_restart": "Please restart Immich to apply this setting",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Add",
|
"share_add": "Add",
|
||||||
"share_add_photos": "Add photos",
|
"share_add_photos": "Add photos",
|
||||||
"share_add_title": "Add a title",
|
"share_add_title": "Add a title",
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"action_common_back": "Back",
|
"action_common_back": "Atpakaļ",
|
||||||
"action_common_cancel": "Cancel",
|
"action_common_cancel": "Atcelt",
|
||||||
"action_common_clear": "Clear",
|
"action_common_clear": "Notīrīt",
|
||||||
"action_common_confirm": "Confirm",
|
"action_common_confirm": "Apstiprināt",
|
||||||
"action_common_update": "Update",
|
"action_common_update": "Atjaunināt",
|
||||||
"add_to_album_bottom_sheet_added": "Pievienots {album}",
|
"add_to_album_bottom_sheet_added": "Pievienots {album}",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Jau pievienots {album}",
|
"add_to_album_bottom_sheet_already_exists": "Jau pievienots {album}",
|
||||||
"advanced_settings_log_level_title": "Log level: {}",
|
"advanced_settings_log_level_title": "Žurnalēšanas līmenis: {}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Dažās ierīcēs sīktēli no ierīcē esošajiem resursiem tiek ielādēti ļoti lēni. Aktivizējiet šo iestatījumu, lai tā vietā ielādētu attālus attēlus.",
|
"advanced_settings_prefer_remote_subtitle": "Dažās ierīcēs sīktēli no ierīcē esošajiem resursiem tiek ielādēti ļoti lēni. Aktivizējiet šo iestatījumu, lai tā vietā ielādētu attālus attēlus.",
|
||||||
"advanced_settings_prefer_remote_title": "Dot priekšroku attāliem attēliem",
|
"advanced_settings_prefer_remote_title": "Dot priekšroku attāliem attēliem",
|
||||||
"advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.",
|
"advanced_settings_self_signed_ssl_subtitle": "Izlaiž servera galapunkta SSL sertifikātu verifikāciju. Nepieciešams pašparakstītajiem sertifikātiem.",
|
||||||
"advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates",
|
"advanced_settings_self_signed_ssl_title": "Atļaut pašparakstītus SSL sertifikātus",
|
||||||
"advanced_settings_tile_subtitle": "Lietotāja papildu iestatījumi",
|
"advanced_settings_tile_subtitle": "Lietotāja papildu iestatījumi",
|
||||||
"advanced_settings_tile_title": "Papildu",
|
"advanced_settings_tile_title": "Papildu",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Iespējot papildu aktīvus problēmu novēršanai",
|
"advanced_settings_troubleshooting_subtitle": "Iespējot papildu aktīvus problēmu novēršanai",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"album_thumbnail_card_shared": "· Koplietots",
|
"album_thumbnail_card_shared": "· Koplietots",
|
||||||
"album_thumbnail_owned": "Īpašumā",
|
"album_thumbnail_owned": "Īpašumā",
|
||||||
"album_thumbnail_shared_by": "Kopīgoja {}",
|
"album_thumbnail_shared_by": "Kopīgoja {}",
|
||||||
"album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?",
|
"album_viewer_appbar_delete_confirm": "Vai tiešām vēlaties dzēst šo albumu no sava konta?",
|
||||||
"album_viewer_appbar_share_delete": "Dzēst albumu",
|
"album_viewer_appbar_share_delete": "Dzēst albumu",
|
||||||
"album_viewer_appbar_share_err_delete": "Neizdevās izdzēst albumu",
|
"album_viewer_appbar_share_err_delete": "Neizdevās izdzēst albumu",
|
||||||
"album_viewer_appbar_share_err_leave": "Neizdevās pamest albumu",
|
"album_viewer_appbar_share_err_leave": "Neizdevās pamest albumu",
|
||||||
@@ -30,27 +30,27 @@
|
|||||||
"album_viewer_appbar_share_err_title": "Neizdevās mainīt albuma nosaukumu",
|
"album_viewer_appbar_share_err_title": "Neizdevās mainīt albuma nosaukumu",
|
||||||
"album_viewer_appbar_share_leave": "Pamest albumu",
|
"album_viewer_appbar_share_leave": "Pamest albumu",
|
||||||
"album_viewer_appbar_share_remove": "Noņemt no albuma",
|
"album_viewer_appbar_share_remove": "Noņemt no albuma",
|
||||||
"album_viewer_appbar_share_to": "Share To",
|
"album_viewer_appbar_share_to": "Kopīgot Uz",
|
||||||
"album_viewer_page_share_add_users": "Pievienot lietotājus",
|
"album_viewer_page_share_add_users": "Pievienot lietotājus",
|
||||||
"all_people_page_title": "Cilvēki",
|
"all_people_page_title": "Cilvēki",
|
||||||
"all_videos_page_title": "Videoklipi",
|
"all_videos_page_title": "Videoklipi",
|
||||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
"app_bar_signout_dialog_content": "Vai tiešām vēlaties izrakstīties?",
|
||||||
"app_bar_signout_dialog_ok": "Yes",
|
"app_bar_signout_dialog_ok": "Jā",
|
||||||
"app_bar_signout_dialog_title": "Sign out",
|
"app_bar_signout_dialog_title": "Izrakstīties",
|
||||||
"archive_page_no_archived_assets": "Nav atrasts neviens arhivēts aktīvs",
|
"archive_page_no_archived_assets": "Nav atrasts neviens arhivēts aktīvs",
|
||||||
"archive_page_title": "Arhīvs ({})",
|
"archive_page_title": "Arhīvs ({})",
|
||||||
"asset_action_delete_err_read_only": "Cannot delete read only asset(s), skipping",
|
"asset_action_delete_err_read_only": "Nevar dzēst read only aktīvu(-s), notiek izlaišana",
|
||||||
"asset_action_share_err_offline": "Cannot fetch offline asset(s), skipping",
|
"asset_action_share_err_offline": "Nevar iegūt bezsaistes aktīvu(-s), notiek izlaišana",
|
||||||
"asset_list_group_by_sub_title": "Group by",
|
"asset_list_group_by_sub_title": "Grupēt pēc",
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "Dinamiskais izkārtojums",
|
"asset_list_layout_settings_dynamic_layout_title": "Dinamiskais izkārtojums",
|
||||||
"asset_list_layout_settings_group_automatically": "Automātiski",
|
"asset_list_layout_settings_group_automatically": "Automātiski",
|
||||||
"asset_list_layout_settings_group_by": "Grupēt aktīvus pēc",
|
"asset_list_layout_settings_group_by": "Grupēt aktīvus pēc",
|
||||||
"asset_list_layout_settings_group_by_month": "Mēnesis",
|
"asset_list_layout_settings_group_by_month": "Mēnesis",
|
||||||
"asset_list_layout_settings_group_by_month_day": "Mēnesis + diena",
|
"asset_list_layout_settings_group_by_month_day": "Mēnesis + diena",
|
||||||
"asset_list_layout_sub_title": "Layout",
|
"asset_list_layout_sub_title": "Izvietojums",
|
||||||
"asset_list_settings_subtitle": "Fotorežģa izkārtojuma iestatījumi",
|
"asset_list_settings_subtitle": "Fotorežģa izkārtojuma iestatījumi",
|
||||||
"asset_list_settings_title": "Fotorežģis",
|
"asset_list_settings_title": "Fotorežģis",
|
||||||
"asset_viewer_settings_title": "Asset Viewer",
|
"asset_viewer_settings_title": "Aktīvu Skatītājs",
|
||||||
"backup_album_selection_page_albums_device": "Albumi ierīcē ({})",
|
"backup_album_selection_page_albums_device": "Albumi ierīcē ({})",
|
||||||
"backup_album_selection_page_albums_tap": "Pieskarieties, lai iekļautu, veiciet dubultskārienu, lai izslēgtu",
|
"backup_album_selection_page_albums_tap": "Pieskarieties, lai iekļautu, veiciet dubultskārienu, lai izslēgtu",
|
||||||
"backup_album_selection_page_assets_scatter": "Aktīvi var būt izmētāti pa vairākiem albumiem. Tādējādi dublēšanas procesā albumus var iekļaut vai neiekļaut.",
|
"backup_album_selection_page_assets_scatter": "Aktīvi var būt izmētāti pa vairākiem albumiem. Tādējādi dublēšanas procesā albumus var iekļaut vai neiekļaut.",
|
||||||
@@ -110,18 +110,18 @@
|
|||||||
"backup_controller_page_uploading_file_info": "Faila informācijas augšupielāde",
|
"backup_controller_page_uploading_file_info": "Faila informācijas augšupielāde",
|
||||||
"backup_err_only_album": "Nevar noņemt vienīgo albumu",
|
"backup_err_only_album": "Nevar noņemt vienīgo albumu",
|
||||||
"backup_info_card_assets": "aktīvi",
|
"backup_info_card_assets": "aktīvi",
|
||||||
"backup_manual_cancelled": "Cancelled",
|
"backup_manual_cancelled": "Atcelts",
|
||||||
"backup_manual_failed": "Neizdevās",
|
"backup_manual_failed": "Neizdevās",
|
||||||
"backup_manual_in_progress": "Upload already in progress. Try after sometime",
|
"backup_manual_in_progress": "Augšupielāde jau notiek. Mēģiniet pēc kāda laika atkārtoti",
|
||||||
"backup_manual_success": "Success",
|
"backup_manual_success": "Veiksmīgi",
|
||||||
"backup_manual_title": "Augšupielādes statuss",
|
"backup_manual_title": "Augšupielādes statuss",
|
||||||
"backup_options_page_title": "Backup options",
|
"backup_options_page_title": "Dublēšanas iestatījumi",
|
||||||
"cache_settings_album_thumbnails": "Bibliotēkas lapu sīktēli ({} aktīvi)",
|
"cache_settings_album_thumbnails": "Bibliotēkas lapu sīktēli ({} aktīvi)",
|
||||||
"cache_settings_clear_cache_button": "Iztīrīt kešatmiņu",
|
"cache_settings_clear_cache_button": "Iztīrīt kešatmiņu",
|
||||||
"cache_settings_clear_cache_button_title": "Iztīra aplikācijas kešatmiņu. Tas būtiski ietekmēs lietotnes veiktspēju, līdz kešatmiņa būs pārbūvēta.",
|
"cache_settings_clear_cache_button_title": "Iztīra aplikācijas kešatmiņu. Tas būtiski ietekmēs lietotnes veiktspēju, līdz kešatmiņa būs pārbūvēta.",
|
||||||
"cache_settings_duplicated_assets_clear_button": "CLEAR",
|
"cache_settings_duplicated_assets_clear_button": "NOTĪRĪT",
|
||||||
"cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app",
|
"cache_settings_duplicated_assets_subtitle": "Fotoattēli un videoklipi, kurus lietotne ir iekļāvusi melnajā sarakstā",
|
||||||
"cache_settings_duplicated_assets_title": "Duplicated Assets ({})",
|
"cache_settings_duplicated_assets_title": "Dublicētie Aktīvi ({})",
|
||||||
"cache_settings_image_cache_size": "Attēlu kešatmiņas lielums ({} aktīvi)",
|
"cache_settings_image_cache_size": "Attēlu kešatmiņas lielums ({} aktīvi)",
|
||||||
"cache_settings_statistics_album": "Bibliotēkas sīktēli",
|
"cache_settings_statistics_album": "Bibliotēkas sīktēli",
|
||||||
"cache_settings_statistics_assets": "{} aktīvi ({})",
|
"cache_settings_statistics_assets": "{} aktīvi ({})",
|
||||||
@@ -131,8 +131,8 @@
|
|||||||
"cache_settings_statistics_title": "Kešatmiņas lietojums",
|
"cache_settings_statistics_title": "Kešatmiņas lietojums",
|
||||||
"cache_settings_subtitle": "Kontrolēt Immich mobilās lietotnes kešdarbi",
|
"cache_settings_subtitle": "Kontrolēt Immich mobilās lietotnes kešdarbi",
|
||||||
"cache_settings_thumbnail_size": "Sīktēlu keša lielums ({} aktīvi)",
|
"cache_settings_thumbnail_size": "Sīktēlu keša lielums ({} aktīvi)",
|
||||||
"cache_settings_tile_subtitle": "Control the local storage behaviour",
|
"cache_settings_tile_subtitle": "Kontrolēt lokālās krātuves uzvedību",
|
||||||
"cache_settings_tile_title": "Local Storage",
|
"cache_settings_tile_title": "Lokālā Krātuve",
|
||||||
"cache_settings_title": "Kešdarbes iestatījumi",
|
"cache_settings_title": "Kešdarbes iestatījumi",
|
||||||
"change_password_form_confirm_password": "Apstiprināt Paroli",
|
"change_password_form_confirm_password": "Apstiprināt Paroli",
|
||||||
"change_password_form_description": "Sveiki {name},\n\nŠī ir pirmā reize, kad pierakstāties sistēmā, vai arī ir iesniegts pieprasījums mainīt paroli. Lūdzu, zemāk ievadiet jauno paroli.",
|
"change_password_form_description": "Sveiki {name},\n\nŠī ir pirmā reize, kad pierakstāties sistēmā, vai arī ir iesniegts pieprasījums mainīt paroli. Lūdzu, zemāk ievadiet jauno paroli.",
|
||||||
@@ -150,18 +150,18 @@
|
|||||||
"control_bottom_app_bar_archive": "Arhīvs",
|
"control_bottom_app_bar_archive": "Arhīvs",
|
||||||
"control_bottom_app_bar_create_new_album": "Izveidot jaunu albumu",
|
"control_bottom_app_bar_create_new_album": "Izveidot jaunu albumu",
|
||||||
"control_bottom_app_bar_delete": "Dzēst",
|
"control_bottom_app_bar_delete": "Dzēst",
|
||||||
"control_bottom_app_bar_delete_from_immich": "Delete from Immich",
|
"control_bottom_app_bar_delete_from_immich": "Dzēst no Immich",
|
||||||
"control_bottom_app_bar_delete_from_local": "Delete from device",
|
"control_bottom_app_bar_delete_from_local": "Dzēst no ierīces",
|
||||||
"control_bottom_app_bar_edit_location": "Edit Location",
|
"control_bottom_app_bar_edit_location": "Rediģēt Atrašanās Vietu",
|
||||||
"control_bottom_app_bar_edit_time": "Edit Date & Time",
|
"control_bottom_app_bar_edit_time": "Rediģēt Datumu un Laiku",
|
||||||
"control_bottom_app_bar_favorite": "Izlase",
|
"control_bottom_app_bar_favorite": "Izlase",
|
||||||
"control_bottom_app_bar_share": "Kopīgot",
|
"control_bottom_app_bar_share": "Kopīgot",
|
||||||
"control_bottom_app_bar_share_to": "Share To",
|
"control_bottom_app_bar_share_to": "Kopīgot Uz",
|
||||||
"control_bottom_app_bar_stack": "Stack",
|
"control_bottom_app_bar_stack": "Steks",
|
||||||
"control_bottom_app_bar_trash_from_immich": "Move to Trash",
|
"control_bottom_app_bar_trash_from_immich": "Pārvietot uz Atkritni",
|
||||||
"control_bottom_app_bar_unarchive": "Atarhivēt",
|
"control_bottom_app_bar_unarchive": "Atarhivēt",
|
||||||
"control_bottom_app_bar_unfavorite": "Unfavorite",
|
"control_bottom_app_bar_unfavorite": "Noņemt no Izlases",
|
||||||
"control_bottom_app_bar_upload": "Upload",
|
"control_bottom_app_bar_upload": "Augšupielādēt",
|
||||||
"create_album_page_untitled": "Bez nosaukuma",
|
"create_album_page_untitled": "Bez nosaukuma",
|
||||||
"create_shared_album_page_create": "Izveidot",
|
"create_shared_album_page_create": "Izveidot",
|
||||||
"create_shared_album_page_share": "Kopīgot",
|
"create_shared_album_page_share": "Kopīgot",
|
||||||
@@ -173,76 +173,76 @@
|
|||||||
"daily_title_text_date_year": "E, MMM dd, gggg",
|
"daily_title_text_date_year": "E, MMM dd, gggg",
|
||||||
"date_format": "E, LLL d, g • h:mm a",
|
"date_format": "E, LLL d, g • h:mm a",
|
||||||
"delete_dialog_alert": "Šie vienumi tiks neatgriezeniski dzēsti no Immich un jūsu ierīces",
|
"delete_dialog_alert": "Šie vienumi tiks neatgriezeniski dzēsti no Immich un jūsu ierīces",
|
||||||
"delete_dialog_alert_local": "These items will be permanently removed from your device but still be available on the Immich server",
|
"delete_dialog_alert_local": "Šie vienumi tiks neatgriezeniski dzēsti no jūsu ierīces, bet joprojām būs pieejami Immich serverī.",
|
||||||
"delete_dialog_alert_local_non_backed_up": "Some of the items aren't backed up to Immich and will be permanently removed from your device",
|
"delete_dialog_alert_local_non_backed_up": "Daži no šiem elementiem netiek dublēti Immich un tiks neatgriezeniski dzēsti no jūsu ierīces.",
|
||||||
"delete_dialog_alert_remote": "These items will be permanently deleted from the Immich server",
|
"delete_dialog_alert_remote": "Šie vienumi tiks neatgriezeniski dzēsti no Immich servera.",
|
||||||
"delete_dialog_cancel": "Atcelt",
|
"delete_dialog_cancel": "Atcelt",
|
||||||
"delete_dialog_ok": "Dzēst",
|
"delete_dialog_ok": "Dzēst",
|
||||||
"delete_dialog_ok_force": "Delete Anyway",
|
"delete_dialog_ok_force": "Tā pat dzēst",
|
||||||
"delete_dialog_title": "Neatgriezeniski Dzēst",
|
"delete_dialog_title": "Neatgriezeniski Dzēst",
|
||||||
"delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only",
|
"delete_local_dialog_ok_backed_up_only": "Dzēst tikai Dublētos",
|
||||||
"delete_local_dialog_ok_force": "Delete Anyway",
|
"delete_local_dialog_ok_force": "Tā pat dzēst",
|
||||||
"delete_shared_link_dialog_content": "Are you sure you want to delete this shared link?",
|
"delete_shared_link_dialog_content": "Vai tiešām vēlaties dzēst šo kopīgošanas saiti?",
|
||||||
"delete_shared_link_dialog_title": "Delete Shared Link",
|
"delete_shared_link_dialog_title": "Dzēst Kopīgošanas saiti",
|
||||||
"description_input_hint_text": "Pievienot aprakstu...",
|
"description_input_hint_text": "Pievienot aprakstu...",
|
||||||
"description_input_submit_error": "Atjauninot aprakstu, radās kļūda; papildinformāciju skatiet žurnālā",
|
"description_input_submit_error": "Atjauninot aprakstu, radās kļūda; papildinformāciju skatiet žurnālā",
|
||||||
"edit_date_time_dialog_date_time": "Date and Time",
|
"edit_date_time_dialog_date_time": "Datums un Laiks",
|
||||||
"edit_date_time_dialog_timezone": "Timezone",
|
"edit_date_time_dialog_timezone": "Laika zona",
|
||||||
"edit_location_dialog_title": "Location",
|
"edit_location_dialog_title": "Atrašanās vieta",
|
||||||
"exif_bottom_sheet_description": "Pievienot Aprakstu...",
|
"exif_bottom_sheet_description": "Pievienot Aprakstu...",
|
||||||
"exif_bottom_sheet_details": "INFORMĀCIJA",
|
"exif_bottom_sheet_details": "INFORMĀCIJA",
|
||||||
"exif_bottom_sheet_location": "ATRAŠANĀS VIETA",
|
"exif_bottom_sheet_location": "ATRAŠANĀS VIETA",
|
||||||
"exif_bottom_sheet_location_add": "Add a location",
|
"exif_bottom_sheet_location_add": "Pievienot atrašanās vietu",
|
||||||
"exif_bottom_sheet_people": "PEOPLE",
|
"exif_bottom_sheet_people": "CILVĒKI",
|
||||||
"exif_bottom_sheet_person_add_person": "Add name",
|
"exif_bottom_sheet_person_add_person": "Pievienot vārdu",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Izstrādes posmā",
|
"experimental_settings_new_asset_list_subtitle": "Izstrādes posmā",
|
||||||
"experimental_settings_new_asset_list_title": "Iespējot eksperimentālo fotorežģi",
|
"experimental_settings_new_asset_list_title": "Iespējot eksperimentālo fotorežģi",
|
||||||
"experimental_settings_subtitle": "Izmanto uzņemoties risku!",
|
"experimental_settings_subtitle": "Izmanto uzņemoties risku!",
|
||||||
"experimental_settings_title": "Eksperimentāls",
|
"experimental_settings_title": "Eksperimentāls",
|
||||||
"favorites_page_no_favorites": "Nav atrasti iecienītākie aktīvi",
|
"favorites_page_no_favorites": "Nav atrasti iecienītākie aktīvi",
|
||||||
"favorites_page_title": "Izlase",
|
"favorites_page_title": "Izlase",
|
||||||
"haptic_feedback_switch": "Enable haptic feedback",
|
"haptic_feedback_switch": "Iestatīt haptisku reakciju",
|
||||||
"haptic_feedback_title": "Haptic Feedback",
|
"haptic_feedback_title": "Haptiska Reakcija",
|
||||||
"home_page_add_to_album_conflicts": "Pievienoja {added} aktīvus albumam {album}. {failed} aktīvi jau ir albumā.",
|
"home_page_add_to_album_conflicts": "Pievienoja {added} aktīvus albumam {album}. {failed} aktīvi jau ir albumā.",
|
||||||
"home_page_add_to_album_err_local": "Albumiem vēl nevar pievienot lokālos aktīvus, notiek izlaišana",
|
"home_page_add_to_album_err_local": "Albumiem vēl nevar pievienot lokālos aktīvus, notiek izlaišana",
|
||||||
"home_page_add_to_album_success": "Pievienoja {added} aktīvus albumam {album}.",
|
"home_page_add_to_album_success": "Pievienoja {added} aktīvus albumam {album}.",
|
||||||
"home_page_album_err_partner": "Can not add partner assets to an album yet, skipping",
|
"home_page_album_err_partner": "Pagaidām nevar pievienot partnera aktīvus albumam, notiek izlaišana",
|
||||||
"home_page_archive_err_local": "Vēl nevar arhivēt lokālos aktīvus, notiek izlaišana",
|
"home_page_archive_err_local": "Vēl nevar arhivēt lokālos aktīvus, notiek izlaišana",
|
||||||
"home_page_archive_err_partner": "Can not archive partner assets, skipping",
|
"home_page_archive_err_partner": "Nevarēja arhivēt partnera aktīvus, notiek izlaišana",
|
||||||
"home_page_building_timeline": "Tiek izveidota laika skala",
|
"home_page_building_timeline": "Tiek izveidota laika skala",
|
||||||
"home_page_delete_err_partner": "Can not delete partner assets, skipping",
|
"home_page_delete_err_partner": "Nevarēja dzēst partnera aktīvus, notiek izlaišana",
|
||||||
"home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping",
|
"home_page_delete_remote_err_local": "Lokālie aktīvi dzēšanai attālinātajā izvēlē, tiek izlaists",
|
||||||
"home_page_favorite_err_local": "Vēl nevar pievienot izlaisei vietējos aktīvus, notiek izlaišana",
|
"home_page_favorite_err_local": "Vēl nevar pievienot izlaisei vietējos aktīvus, notiek izlaišana",
|
||||||
"home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping",
|
"home_page_favorite_err_partner": "Pagaidām nevar ievietot izlasē partnera aktīvus, notiek izlaišana",
|
||||||
"home_page_first_time_notice": "Ja šī ir pirmā reize, kad izmantojat aplikāciju, lūdzu, izvēlieties dublējuma albumu(s), lai laika skala varētu aizpildīt fotoattēlus un videoklipus albumā(os).",
|
"home_page_first_time_notice": "Ja šī ir pirmā reize, kad izmantojat aplikāciju, lūdzu, izvēlieties dublējuma albumu(s), lai laika skala varētu aizpildīt fotoattēlus un videoklipus albumā(os).",
|
||||||
"home_page_share_err_local": "Can not share local assets via link, skipping",
|
"home_page_share_err_local": "Caur saiti nevarēja kopīgot lokālos aktīvus, notiek izlaišana",
|
||||||
"home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping",
|
"home_page_upload_err_limit": "Vienlaikus var augšupielādēt ne vairāk kā 30 aktīvus, notiek izlaišana",
|
||||||
"image_viewer_page_state_provider_download_error": "Lejupielādes Kļūda",
|
"image_viewer_page_state_provider_download_error": "Lejupielādes Kļūda",
|
||||||
"image_viewer_page_state_provider_download_started": "Download Started",
|
"image_viewer_page_state_provider_download_started": "Lejupielāde Uzsākta",
|
||||||
"image_viewer_page_state_provider_download_success": "Lejupielāde Izdevās",
|
"image_viewer_page_state_provider_download_success": "Lejupielāde Izdevās",
|
||||||
"image_viewer_page_state_provider_share_error": "Share Error",
|
"image_viewer_page_state_provider_share_error": "Kopīgošanas Kļūda",
|
||||||
"library_page_albums": "Albums",
|
"library_page_albums": "Albums",
|
||||||
"library_page_archive": "Arhīvs",
|
"library_page_archive": "Arhīvs",
|
||||||
"library_page_device_albums": "Albumi ierīcē",
|
"library_page_device_albums": "Albumi ierīcē",
|
||||||
"library_page_favorites": "Izlase",
|
"library_page_favorites": "Izlase",
|
||||||
"library_page_new_album": "Jauns albums",
|
"library_page_new_album": "Jauns albums",
|
||||||
"library_page_sharing": "Kopīgošana",
|
"library_page_sharing": "Kopīgošana",
|
||||||
"library_page_sort_asset_count": "Number of assets",
|
"library_page_sort_asset_count": "Daudzums ar aktīviem",
|
||||||
"library_page_sort_created": "Jaunākais izveidotais",
|
"library_page_sort_created": "Jaunākais izveidotais",
|
||||||
"library_page_sort_last_modified": "Last modified",
|
"library_page_sort_last_modified": "Pēdējo reizi modificēts",
|
||||||
"library_page_sort_most_oldest_photo": "Oldest photo",
|
"library_page_sort_most_oldest_photo": "Vecākais fotoattēls",
|
||||||
"library_page_sort_most_recent_photo": "Most recent photo",
|
"library_page_sort_most_recent_photo": "Jaunākais fotoattēls",
|
||||||
"library_page_sort_title": "Albuma virsraksts",
|
"library_page_sort_title": "Albuma virsraksts",
|
||||||
"location_picker_choose_on_map": "Choose on map",
|
"location_picker_choose_on_map": "Izvēlēties uz kartes",
|
||||||
"location_picker_latitude": "Latitude",
|
"location_picker_latitude": "Ģeogrāfiskais platums",
|
||||||
"location_picker_latitude_error": "Enter a valid latitude",
|
"location_picker_latitude_error": "Ievadiet korektu ģeogrāfisko platumu",
|
||||||
"location_picker_latitude_hint": "Enter your latitude here",
|
"location_picker_latitude_hint": "Ievadiet savu ģeogrāfisko platumu šeit",
|
||||||
"location_picker_longitude": "Longitude",
|
"location_picker_longitude": "Ģeogrāfiskais garums",
|
||||||
"location_picker_longitude_error": "Enter a valid longitude",
|
"location_picker_longitude_error": "Ievadiet korektu ģeogrāfisko garumu",
|
||||||
"location_picker_longitude_hint": "Enter your longitude here",
|
"location_picker_longitude_hint": "Ievadiet savu ģeogrāfisko garumu šeit",
|
||||||
"login_disabled": "Login has been disabled",
|
"login_disabled": "Pieslēgšanās ir atslēgta",
|
||||||
"login_form_api_exception": "API izņēmums. Lūdzu, pārbaudiet servera URL un mēģiniet vēlreiz.",
|
"login_form_api_exception": "API izņēmums. Lūdzu, pārbaudiet servera URL un mēģiniet vēlreiz.",
|
||||||
"login_form_back_button_text": "Back",
|
"login_form_back_button_text": "Atpakaļ",
|
||||||
"login_form_button_text": "Pieteikties",
|
"login_form_button_text": "Pieteikties",
|
||||||
"login_form_email_hint": "jūsuepasts@email.com",
|
"login_form_email_hint": "jūsuepasts@email.com",
|
||||||
"login_form_endpoint_hint": "http://jūsu-servera-ip:ports/api",
|
"login_form_endpoint_hint": "http://jūsu-servera-ip:ports/api",
|
||||||
@@ -255,7 +255,7 @@
|
|||||||
"login_form_failed_get_oauth_server_config": "Pieslēdzoties, izmantojot OAuth, radās kļūda; pārbaudiet servera URL",
|
"login_form_failed_get_oauth_server_config": "Pieslēdzoties, izmantojot OAuth, radās kļūda; pārbaudiet servera URL",
|
||||||
"login_form_failed_get_oauth_server_disable": "OAuth līdzeklis šajā serverī nav pieejams",
|
"login_form_failed_get_oauth_server_disable": "OAuth līdzeklis šajā serverī nav pieejams",
|
||||||
"login_form_failed_login": "Radās kļūda, piesakoties, pārbaudiet servera URL, e-pastu un paroli",
|
"login_form_failed_login": "Radās kļūda, piesakoties, pārbaudiet servera URL, e-pastu un paroli",
|
||||||
"login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.",
|
"login_form_handshake_exception": "Ar serveri tika konstatēta Handshake Exception kļūda. Ja izmantojat pašparakstītu sertifikātu, tad iestatījumos iespējojiet pašparakstītu sertifikātu atbalstu.",
|
||||||
"login_form_label_email": "E-pasts",
|
"login_form_label_email": "E-pasts",
|
||||||
"login_form_label_password": "Parole",
|
"login_form_label_password": "Parole",
|
||||||
"login_form_next_button": "Nākošais",
|
"login_form_next_button": "Nākošais",
|
||||||
@@ -263,51 +263,51 @@
|
|||||||
"login_form_save_login": "Palikt pieteiktam",
|
"login_form_save_login": "Palikt pieteiktam",
|
||||||
"login_form_server_empty": "Ieraksties servera URL.",
|
"login_form_server_empty": "Ieraksties servera URL.",
|
||||||
"login_form_server_error": "Nevarēja izveidot savienojumu ar serveri.",
|
"login_form_server_error": "Nevarēja izveidot savienojumu ar serveri.",
|
||||||
"login_password_changed_error": "There was an error updating your password",
|
"login_password_changed_error": "Atjaunojot paroli radās kļūda",
|
||||||
"login_password_changed_success": "Password updated successfully",
|
"login_password_changed_success": "Parole veiksmīgi atjaunota",
|
||||||
"map_assets_in_bound": "{} photo",
|
"map_assets_in_bound": "{} fotoattēls",
|
||||||
"map_assets_in_bounds": "{} photos",
|
"map_assets_in_bounds": "{} fotoattēli",
|
||||||
"map_cannot_get_user_location": "Cannot get user's location",
|
"map_cannot_get_user_location": "Nevar iegūt lietotāja atrašanās vietu",
|
||||||
"map_location_dialog_cancel": "Cancel",
|
"map_location_dialog_cancel": "Atcelt",
|
||||||
"map_location_dialog_yes": "Yes",
|
"map_location_dialog_yes": "Jā",
|
||||||
"map_location_picker_page_use_location": "Use this location",
|
"map_location_picker_page_use_location": "Izvēlēties šo atrašanās vietu",
|
||||||
"map_location_service_disabled_content": "Location service needs to be enabled to display assets from your current location. Do you want to enable it now?",
|
"map_location_service_disabled_content": "Lai tiktu rādīti jūsu pašreizējās atrašanās vietas aktīvi, ir jāaktivizē atrašanās vietas pakalpojums. Vai vēlaties to iespējot tagad?",
|
||||||
"map_location_service_disabled_title": "Location Service disabled",
|
"map_location_service_disabled_title": "Atrašanās vietas Pakalpojums atslēgts",
|
||||||
"map_no_assets_in_bounds": "No photos in this area",
|
"map_no_assets_in_bounds": "Šajā lokācijā nav neviena fotoattēla",
|
||||||
"map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?",
|
"map_no_location_permission_content": "Atrašanās vietas atļauja ir nepieciešama, lai parādītu jūsu pašreizējās atrašanās vietas aktīvus. Vai vēlaties to atļaut tagad?",
|
||||||
"map_no_location_permission_title": "Location Permission denied",
|
"map_no_location_permission_title": "Atrašanās vietas Atļaujas liegtas",
|
||||||
"map_settings_dark_mode": "Dark mode",
|
"map_settings_dark_mode": "Tumšais režīms",
|
||||||
"map_settings_date_range_option_all": "All",
|
"map_settings_date_range_option_all": "Viss",
|
||||||
"map_settings_date_range_option_day": "Past 24 hours",
|
"map_settings_date_range_option_day": "Pēdējās 24 stundas",
|
||||||
"map_settings_date_range_option_days": "Past {} days",
|
"map_settings_date_range_option_days": "Pēdējās {} dienas",
|
||||||
"map_settings_date_range_option_year": "Past year",
|
"map_settings_date_range_option_year": "Pēdējo gadu",
|
||||||
"map_settings_date_range_option_years": "Past {} years",
|
"map_settings_date_range_option_years": "Pēdējos {} gadus",
|
||||||
"map_settings_dialog_cancel": "Cancel",
|
"map_settings_dialog_cancel": "Atcelt",
|
||||||
"map_settings_dialog_save": "Save",
|
"map_settings_dialog_save": "Saglabāt",
|
||||||
"map_settings_dialog_title": "Map Settings",
|
"map_settings_dialog_title": "Kartes Iestatījumi",
|
||||||
"map_settings_include_show_archived": "Include Archived",
|
"map_settings_include_show_archived": "Iekļaut Arhivētos",
|
||||||
"map_settings_include_show_partners": "Include Partners",
|
"map_settings_include_show_partners": "Iekļaut Partnerus",
|
||||||
"map_settings_only_relative_range": "Date range",
|
"map_settings_only_relative_range": "Datumu diapazons",
|
||||||
"map_settings_only_show_favorites": "Show Favorite Only",
|
"map_settings_only_show_favorites": "Rādīt tikai Izlasi",
|
||||||
"map_settings_theme_settings": "Map Theme",
|
"map_settings_theme_settings": "Kartes Dizains",
|
||||||
"map_zoom_to_see_photos": "Zoom out to see photos",
|
"map_zoom_to_see_photos": "Attāliniet, lai redzētu fotoattēlus",
|
||||||
"memories_all_caught_up": "All caught up",
|
"memories_all_caught_up": "Šobrīd, tas arī viss",
|
||||||
"memories_check_back_tomorrow": "Check back tomorrow for more memories",
|
"memories_check_back_tomorrow": "Priekš vairāk atmiņām atgriezieties rītdien.",
|
||||||
"memories_start_over": "Start Over",
|
"memories_start_over": "Sākt no jauna",
|
||||||
"memories_swipe_to_close": "Swipe up to close",
|
"memories_swipe_to_close": "Pavelciet uz augšu, lai aizvērtu",
|
||||||
"monthly_title_text_date_format": "MMMM g",
|
"monthly_title_text_date_format": "MMMM g",
|
||||||
"motion_photos_page_title": "Kustību Fotoattēli",
|
"motion_photos_page_title": "Kustību Fotoattēli",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping",
|
"multiselect_grid_edit_date_time_err_read_only": "Nevar rediģēt read only aktīva(-u) datumu, notiek izlaišana",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping",
|
"multiselect_grid_edit_gps_err_read_only": "Nevar rediģēt atrašanās vietu read only aktīva(-u) datumu, notiek izlaišana",
|
||||||
"no_assets_to_show": "No assets to show",
|
"no_assets_to_show": "Nav uzrādāmo aktīvu",
|
||||||
"notification_permission_dialog_cancel": "Atcelt",
|
"notification_permission_dialog_cancel": "Atcelt",
|
||||||
"notification_permission_dialog_content": "Lai iespējotu paziņojumus, atveriet Iestatījumi un atlasiet Atļaut.",
|
"notification_permission_dialog_content": "Lai iespējotu paziņojumus, atveriet Iestatījumi un atlasiet Atļaut.",
|
||||||
"notification_permission_dialog_settings": "Iestatījumi",
|
"notification_permission_dialog_settings": "Iestatījumi",
|
||||||
"notification_permission_list_tile_content": "Piešķirt atļauju, lai iespējotu paziņojumus.",
|
"notification_permission_list_tile_content": "Piešķirt atļauju, lai iespējotu paziņojumus.",
|
||||||
"notification_permission_list_tile_enable_button": "Iespējot Paziņojumus",
|
"notification_permission_list_tile_enable_button": "Iespējot Paziņojumus",
|
||||||
"notification_permission_list_tile_title": "Paziņojumu Atļaujas",
|
"notification_permission_list_tile_title": "Paziņojumu Atļaujas",
|
||||||
"partner_list_user_photos": "{user}'s photos",
|
"partner_list_user_photos": "{user} fotoattēli",
|
||||||
"partner_list_view_all": "View all",
|
"partner_list_view_all": "Apskatīt visu",
|
||||||
"partner_page_add_partner": "Pievienot partneri",
|
"partner_page_add_partner": "Pievienot partneri",
|
||||||
"partner_page_empty_message": "Jūsu fotogrāfijas pagaidām nav kopīgotas ar nevienu partneri.",
|
"partner_page_empty_message": "Jūsu fotogrāfijas pagaidām nav kopīgotas ar nevienu partneri.",
|
||||||
"partner_page_no_more_users": "Nav vairs lietotāju, kurus var pievienot",
|
"partner_page_no_more_users": "Nav vairs lietotāju, kurus var pievienot",
|
||||||
@@ -317,7 +317,7 @@
|
|||||||
"partner_page_stop_sharing_content": "{} vairs nevarēs piekļūt jūsu fotoattēliem.",
|
"partner_page_stop_sharing_content": "{} vairs nevarēs piekļūt jūsu fotoattēliem.",
|
||||||
"partner_page_stop_sharing_title": "Beigt kopīgot jūsu fotogrāfijas?",
|
"partner_page_stop_sharing_title": "Beigt kopīgot jūsu fotogrāfijas?",
|
||||||
"partner_page_title": "Partneris",
|
"partner_page_title": "Partneris",
|
||||||
"permission_onboarding_back": "Back",
|
"permission_onboarding_back": "Atpakaļ",
|
||||||
"permission_onboarding_continue_anyway": "Tomēr turpināt",
|
"permission_onboarding_continue_anyway": "Tomēr turpināt",
|
||||||
"permission_onboarding_get_started": "Darba sākšana",
|
"permission_onboarding_get_started": "Darba sākšana",
|
||||||
"permission_onboarding_go_to_settings": "Doties uz iestatījumiem",
|
"permission_onboarding_go_to_settings": "Doties uz iestatījumiem",
|
||||||
@@ -327,46 +327,46 @@
|
|||||||
"permission_onboarding_permission_granted": "Atļauja piešķirta! Jūs esat gatavi darbam.",
|
"permission_onboarding_permission_granted": "Atļauja piešķirta! Jūs esat gatavi darbam.",
|
||||||
"permission_onboarding_permission_limited": "Atļauja ierobežota. Lai atļautu Immich dublēšanu un varētu pārvaldīt visu galeriju kolekciju, sadaļā Iestatījumi piešķiriet fotoattēlu un video atļaujas.",
|
"permission_onboarding_permission_limited": "Atļauja ierobežota. Lai atļautu Immich dublēšanu un varētu pārvaldīt visu galeriju kolekciju, sadaļā Iestatījumi piešķiriet fotoattēlu un video atļaujas.",
|
||||||
"permission_onboarding_request": "Immich nepieciešama atļauja skatīt jūsu fotoattēlus un videoklipus.",
|
"permission_onboarding_request": "Immich nepieciešama atļauja skatīt jūsu fotoattēlus un videoklipus.",
|
||||||
"preferences_settings_title": "Preferences",
|
"preferences_settings_title": "Iestatījumi",
|
||||||
"profile_drawer_app_logs": "Žurnāli",
|
"profile_drawer_app_logs": "Žurnāli",
|
||||||
"profile_drawer_client_out_of_date_major": "Mobile App is out of date. Please update to the latest major version.",
|
"profile_drawer_client_out_of_date_major": "Mobilā Aplikācija ir novecojusi. Lūdzu atjaunojiet to uz jaunāko lielo versiju",
|
||||||
"profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.",
|
"profile_drawer_client_out_of_date_minor": "Mobilā Aplikācija ir novecojusi. Lūdzu atjaunojiet to uz jaunāko mazo versiju",
|
||||||
"profile_drawer_client_server_up_to_date": "Klients un serveris ir atjaunināti",
|
"profile_drawer_client_server_up_to_date": "Klients un serveris ir atjaunināti",
|
||||||
"profile_drawer_documentation": "Documentation",
|
"profile_drawer_documentation": "Dokumentācija",
|
||||||
"profile_drawer_github": "GitHub",
|
"profile_drawer_github": "GitHub",
|
||||||
"profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.",
|
"profile_drawer_server_out_of_date_major": "Serveris ir novecojis. Lūdzu atjaunojiet to uz jaunāko lielo versiju",
|
||||||
"profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.",
|
"profile_drawer_server_out_of_date_minor": "Serveris ir novecojis. Lūdzu atjaunojiet to uz jaunāko mazo versiju",
|
||||||
"profile_drawer_settings": "Iestatījumi",
|
"profile_drawer_settings": "Iestatījumi",
|
||||||
"profile_drawer_sign_out": "Izrakstīties",
|
"profile_drawer_sign_out": "Izrakstīties",
|
||||||
"profile_drawer_trash": "Trash",
|
"profile_drawer_trash": "Atkritne",
|
||||||
"recently_added_page_title": "Nesen Pievienotais",
|
"recently_added_page_title": "Nesen Pievienotais",
|
||||||
"scaffold_body_error_occurred": "Error occurred",
|
"scaffold_body_error_occurred": "Radās kļūda",
|
||||||
"search_bar_hint": "Meklēt Jūsu fotoattēlus",
|
"search_bar_hint": "Meklēt Jūsu fotoattēlus",
|
||||||
"search_filter_apply": "Apply filter",
|
"search_filter_apply": "Lietot filtru",
|
||||||
"search_filter_camera_make": "Make",
|
"search_filter_camera_make": "Firma",
|
||||||
"search_filter_camera_model": "Model",
|
"search_filter_camera_model": "Modelis",
|
||||||
"search_filter_display_option_archive": "Archive",
|
"search_filter_display_option_archive": "Arhīvs",
|
||||||
"search_filter_display_option_favorite": "Favorite",
|
"search_filter_display_option_favorite": "Izlase",
|
||||||
"search_filter_display_option_not_in_album": "Not in album",
|
"search_filter_display_option_not_in_album": "Nav albumā",
|
||||||
"search_filter_location_city": "City",
|
"search_filter_location_city": "Pilsēta",
|
||||||
"search_filter_location_country": "Country",
|
"search_filter_location_country": "Valsts",
|
||||||
"search_filter_location_state": "State",
|
"search_filter_location_state": "Štats",
|
||||||
"search_filter_media_type_all": "All",
|
"search_filter_media_type_all": "Viss",
|
||||||
"search_filter_media_type_image": "Image",
|
"search_filter_media_type_image": "Attēls",
|
||||||
"search_filter_media_type_video": "Video",
|
"search_filter_media_type_video": "Videoklips",
|
||||||
"search_page_categories": "Kategorijas",
|
"search_page_categories": "Kategorijas",
|
||||||
"search_page_favorites": "Izlase",
|
"search_page_favorites": "Izlase",
|
||||||
"search_page_motion_photos": "Kustību Fotoattēli",
|
"search_page_motion_photos": "Kustību Fotoattēli",
|
||||||
"search_page_no_objects": "Informācija par Objektiem nav pieejama",
|
"search_page_no_objects": "Informācija par Objektiem nav pieejama",
|
||||||
"search_page_no_places": "Nav pieejama Informācija par Vietām",
|
"search_page_no_places": "Nav pieejama Informācija par Vietām",
|
||||||
"search_page_people": "Cilvēki",
|
"search_page_people": "Cilvēki",
|
||||||
"search_page_person_add_name_dialog_cancel": "Cancel",
|
"search_page_person_add_name_dialog_cancel": "Atcelt",
|
||||||
"search_page_person_add_name_dialog_hint": "Name",
|
"search_page_person_add_name_dialog_hint": "Vārds",
|
||||||
"search_page_person_add_name_dialog_save": "Save",
|
"search_page_person_add_name_dialog_save": "Saglabāt",
|
||||||
"search_page_person_add_name_dialog_title": "Add a name",
|
"search_page_person_add_name_dialog_title": "Pievienot vārdu",
|
||||||
"search_page_person_add_name_subtitle": "Find them fast by name with search",
|
"search_page_person_add_name_subtitle": "Atrast viņus ātri pēc vārdiem izmantojot meklēšanu",
|
||||||
"search_page_person_add_name_title": "Add a name",
|
"search_page_person_add_name_title": "Pievienot vārdu",
|
||||||
"search_page_person_edit_name": "Edit name",
|
"search_page_person_edit_name": "Rediģēt vārdu",
|
||||||
"search_page_places": "Vietas",
|
"search_page_places": "Vietas",
|
||||||
"search_page_recently_added": "Nesen Pievienotais",
|
"search_page_recently_added": "Nesen Pievienotais",
|
||||||
"search_page_screenshots": "Ekrānuzņēmumi",
|
"search_page_screenshots": "Ekrānuzņēmumi",
|
||||||
@@ -375,7 +375,7 @@
|
|||||||
"search_page_videos": "Videoklipi",
|
"search_page_videos": "Videoklipi",
|
||||||
"search_page_view_all_button": "Apskatīt visu",
|
"search_page_view_all_button": "Apskatīt visu",
|
||||||
"search_page_your_activity": "Jūsu aktivitāte",
|
"search_page_your_activity": "Jūsu aktivitāte",
|
||||||
"search_page_your_map": "Your Map",
|
"search_page_your_map": "Jūsu Karte",
|
||||||
"search_result_page_new_search_hint": "Jauns Meklējums",
|
"search_result_page_new_search_hint": "Jauns Meklējums",
|
||||||
"search_suggestion_list_smart_search_hint_1": "Viedā meklēšana ir iespējota pēc noklusējuma, lai meklētu metadatus, izmantojiet sintaksi",
|
"search_suggestion_list_smart_search_hint_1": "Viedā meklēšana ir iespējota pēc noklusējuma, lai meklētu metadatus, izmantojiet sintaksi",
|
||||||
"search_suggestion_list_smart_search_hint_2": "m:jūsu-meklēšanas-frāze",
|
"search_suggestion_list_smart_search_hint_2": "m:jūsu-meklēšanas-frāze",
|
||||||
@@ -383,16 +383,17 @@
|
|||||||
"select_user_for_sharing_page_err_album": "Neizdevās izveidot albumu",
|
"select_user_for_sharing_page_err_album": "Neizdevās izveidot albumu",
|
||||||
"select_user_for_sharing_page_share_suggestions": "Ieteikumi",
|
"select_user_for_sharing_page_share_suggestions": "Ieteikumi",
|
||||||
"server_info_box_app_version": "Aplikācijas Versija",
|
"server_info_box_app_version": "Aplikācijas Versija",
|
||||||
"server_info_box_latest_release": "Latest Version",
|
"server_info_box_latest_release": "Jaunākā Versija",
|
||||||
"server_info_box_server_url": "Server URL",
|
"server_info_box_server_url": "Servera URL",
|
||||||
"server_info_box_server_version": "Servera Versija",
|
"server_info_box_server_version": "Servera Versija",
|
||||||
"setting_image_viewer_help": "Detaļu skatītājs vispirms ielādē mazo sīktēlu, pēc tam ielādē vidēja lieluma priekšskatījumu (ja iespējots), visbeidzot ielādē oriģinālu (ja iespējots).",
|
"setting_image_viewer_help": "Detaļu skatītājs vispirms ielādē mazo sīktēlu, pēc tam ielādē vidēja lieluma priekšskatījumu (ja iespējots), visbeidzot ielādē oriģinālu (ja iespējots).",
|
||||||
"setting_image_viewer_original_subtitle": "Iespējojiet sākotnējā pilnas izšķirtspējas attēla (liels!) ielādi. Atspējot lai samazinātu datu lietojumu (gan tīklā, gan ierīces kešatmiņā).",
|
"setting_image_viewer_original_subtitle": "Iespējojiet sākotnējā pilnas izšķirtspējas attēla (liels!) ielādi. Atspējot lai samazinātu datu lietojumu (gan tīklā, gan ierīces kešatmiņā).",
|
||||||
"setting_image_viewer_original_title": "Ielādēt oriģinālo attēlu",
|
"setting_image_viewer_original_title": "Ielādēt oriģinālo attēlu",
|
||||||
"setting_image_viewer_preview_subtitle": "Iespējojiet vidējas izšķirtspējas attēla ielādēšanu. Atspējojiet vai nu tiešu oriģināla ielādi, vai izmantojiet tikai sīktēlu.",
|
"setting_image_viewer_preview_subtitle": "Iespējojiet vidējas izšķirtspējas attēla ielādēšanu. Atspējojiet vai nu tiešu oriģināla ielādi, vai izmantojiet tikai sīktēlu.",
|
||||||
"setting_image_viewer_preview_title": "Ielādēt priekšskatījuma attēlu",
|
"setting_image_viewer_preview_title": "Ielādēt priekšskatījuma attēlu",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_image_viewer_title": "Attēli",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_apply": "Lietot",
|
||||||
|
"setting_languages_title": "Valodas",
|
||||||
"setting_notifications_notify_failures_grace_period": "Paziņot par fona dublēšanas kļūmēm: {}",
|
"setting_notifications_notify_failures_grace_period": "Paziņot par fona dublēšanas kļūmēm: {}",
|
||||||
"setting_notifications_notify_hours": "{} stundas",
|
"setting_notifications_notify_hours": "{} stundas",
|
||||||
"setting_notifications_notify_immediately": "nekavējoties",
|
"setting_notifications_notify_immediately": "nekavējoties",
|
||||||
@@ -407,75 +408,78 @@
|
|||||||
"setting_notifications_total_progress_title": "Rādīt fona dublējuma kopējo progresu",
|
"setting_notifications_total_progress_title": "Rādīt fona dublējuma kopējo progresu",
|
||||||
"setting_pages_app_bar_settings": "Iestatījumi",
|
"setting_pages_app_bar_settings": "Iestatījumi",
|
||||||
"settings_require_restart": "Lūdzu, restartējiet Immich, lai lietotu šo iestatījumu",
|
"settings_require_restart": "Lūdzu, restartējiet Immich, lai lietotu šo iestatījumu",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Iespējot, lai automātiski videoklips tiktu cikliski palaists detaļu skatītājā.",
|
||||||
|
"setting_video_viewer_looping_title": "Cikliski",
|
||||||
|
"setting_video_viewer_title": "Videoklipi",
|
||||||
"share_add": "Pievienot",
|
"share_add": "Pievienot",
|
||||||
"share_add_photos": "Pievienot fotoattēlus",
|
"share_add_photos": "Pievienot fotoattēlus",
|
||||||
"share_add_title": "Pievienot virsrakstu",
|
"share_add_title": "Pievienot virsrakstu",
|
||||||
"share_assets_selected": "{} selected",
|
"share_assets_selected": "{} izvēlēti",
|
||||||
"share_create_album": "Izveidot albumu",
|
"share_create_album": "Izveidot albumu",
|
||||||
"shared_album_activities_input_disable": "Comment is disabled",
|
"shared_album_activities_input_disable": "Komentāri atslēgti",
|
||||||
"shared_album_activities_input_hint": "Say something",
|
"shared_album_activities_input_hint": "Teikt kaut ko",
|
||||||
"shared_album_activity_remove_content": "Do you want to delete this activity?",
|
"shared_album_activity_remove_content": "Vai vēlaties šo aktivitāti dzēst?",
|
||||||
"shared_album_activity_remove_title": "Delete Activity",
|
"shared_album_activity_remove_title": "Dzēst Aktivitāti",
|
||||||
"shared_album_activity_setting_subtitle": "Let others respond",
|
"shared_album_activity_setting_subtitle": "Ļaut citiem atbildēt",
|
||||||
"shared_album_activity_setting_title": "Comments & likes",
|
"shared_album_activity_setting_title": "Komentāri un \"patīk\"",
|
||||||
"shared_album_section_people_action_error": "Error leaving/removing from album",
|
"shared_album_section_people_action_error": "Kļūme pametot/noņemot no albuma",
|
||||||
"shared_album_section_people_action_leave": "Remove user from album",
|
"shared_album_section_people_action_leave": "Noņemt lietotāju no albuma",
|
||||||
"shared_album_section_people_action_remove_user": "Remove user from album",
|
"shared_album_section_people_action_remove_user": "Noņemt lietotāju no albuma",
|
||||||
"shared_album_section_people_owner_label": "Owner",
|
"shared_album_section_people_owner_label": "Īpašnieks",
|
||||||
"shared_album_section_people_title": "PEOPLE",
|
"shared_album_section_people_title": "CILVĒKI",
|
||||||
"share_dialog_preparing": "Notiek sagatavošana...",
|
"share_dialog_preparing": "Notiek sagatavošana...",
|
||||||
"shared_link_app_bar_title": "Shared Links",
|
"shared_link_app_bar_title": "Kopīgotas Saites",
|
||||||
"shared_link_clipboard_copied_massage": "Copied to clipboard",
|
"shared_link_clipboard_copied_massage": "Ievietots starpliktuvē",
|
||||||
"shared_link_clipboard_text": "Link: {}\nPassword: {}",
|
"shared_link_clipboard_text": "Saite: {}\nParole: {}",
|
||||||
"shared_link_create_app_bar_title": "Create link to share",
|
"shared_link_create_app_bar_title": "Izveidot kopīgošanas saiti",
|
||||||
"shared_link_create_error": "Error while creating shared link",
|
"shared_link_create_error": "Kļūda izveidojot kopīgošanas saiti",
|
||||||
"shared_link_create_info": "Let anyone with the link see the selected photo(s)",
|
"shared_link_create_info": "Ļaut jebkuram ar saiti apskatīt izvēlēto(-os) attēlu(-us)",
|
||||||
"shared_link_create_submit_button": "Create link",
|
"shared_link_create_submit_button": "Izveidot saiti",
|
||||||
"shared_link_edit_allow_download": "Allow public user to download",
|
"shared_link_edit_allow_download": "Ļaut publiskiem lietotājiem lejupielādēt",
|
||||||
"shared_link_edit_allow_upload": "Allow public user to upload",
|
"shared_link_edit_allow_upload": "Ļaut publiskiem lietotājiem augšupielādēt",
|
||||||
"shared_link_edit_app_bar_title": "Edit link",
|
"shared_link_edit_app_bar_title": "Rediģēt saiti",
|
||||||
"shared_link_edit_change_expiry": "Change expiration time",
|
"shared_link_edit_change_expiry": "Izmainīt derīguma termiņu",
|
||||||
"shared_link_edit_description": "Description",
|
"shared_link_edit_description": "Apraksts",
|
||||||
"shared_link_edit_description_hint": "Enter the share description",
|
"shared_link_edit_description_hint": "Ievadiet kopīgojuma aprakstu",
|
||||||
"shared_link_edit_expire_after": "Expire after",
|
"shared_link_edit_expire_after": "Derīguma termiņš beidzas pēc",
|
||||||
"shared_link_edit_expire_after_option_day": "1 day",
|
"shared_link_edit_expire_after_option_day": "1 diena",
|
||||||
"shared_link_edit_expire_after_option_days": "{} days",
|
"shared_link_edit_expire_after_option_days": "{} dienas",
|
||||||
"shared_link_edit_expire_after_option_hour": "1 hour",
|
"shared_link_edit_expire_after_option_hour": "1 stunda",
|
||||||
"shared_link_edit_expire_after_option_hours": "{} hours",
|
"shared_link_edit_expire_after_option_hours": "{} stundas",
|
||||||
"shared_link_edit_expire_after_option_minute": "1 minute",
|
"shared_link_edit_expire_after_option_minute": "1 minūte",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minutes",
|
"shared_link_edit_expire_after_option_minutes": "{} minūtes",
|
||||||
"shared_link_edit_expire_after_option_months": "{} months",
|
"shared_link_edit_expire_after_option_months": "{} mēneši",
|
||||||
"shared_link_edit_expire_after_option_never": "Never",
|
"shared_link_edit_expire_after_option_never": "Nekad",
|
||||||
"shared_link_edit_expire_after_option_year": "{} year",
|
"shared_link_edit_expire_after_option_year": "{} gads",
|
||||||
"shared_link_edit_password": "Password",
|
"shared_link_edit_password": "Parole",
|
||||||
"shared_link_edit_password_hint": "Enter the share password",
|
"shared_link_edit_password_hint": "Ierakstīt kopīgojuma paroli",
|
||||||
"shared_link_edit_show_meta": "Show metadata",
|
"shared_link_edit_show_meta": "Rādīt metadatus",
|
||||||
"shared_link_edit_submit_button": "Update link",
|
"shared_link_edit_submit_button": "Atjaunināt saiti",
|
||||||
"shared_link_empty": "You don't have any shared links",
|
"shared_link_empty": "Jums nav nevienas kopīgotas saites",
|
||||||
"shared_link_error_server_url_fetch": "Cannot fetch the server url",
|
"shared_link_error_server_url_fetch": "Nevarēja ienest servera URL",
|
||||||
"shared_link_expired": "Expired",
|
"shared_link_expired": "Derīguma termiņš beidzās",
|
||||||
"shared_link_expires_day": "Expires in {} day",
|
"shared_link_expires_day": "Derīguma termiņš beigsies pēc {} dienas",
|
||||||
"shared_link_expires_days": "Expires in {} days",
|
"shared_link_expires_days": "Derīguma termiņš beigsies pēc {} dienām",
|
||||||
"shared_link_expires_hour": "Expires in {} hour",
|
"shared_link_expires_hour": "Derīguma termiņš beigsies pēc {} stundas",
|
||||||
"shared_link_expires_hours": "Expires in {} hours",
|
"shared_link_expires_hours": "Derīguma termiņš beigsies pēc {} stundām",
|
||||||
"shared_link_expires_minute": "Expires in {} minute",
|
"shared_link_expires_minute": "Derīguma termiņš beigsies pēc {} minūtes",
|
||||||
"shared_link_expires_minutes": "Expires in {} minutes",
|
"shared_link_expires_minutes": "Derīguma termiņš beidzas pēc {} minūtēm",
|
||||||
"shared_link_expires_never": "Expires ∞",
|
"shared_link_expires_never": "Derīguma termiņš beigsies ∞",
|
||||||
"shared_link_expires_second": "Expires in {} second",
|
"shared_link_expires_second": "Derīguma termiņš beigsies pēc {} sekundes",
|
||||||
"shared_link_expires_seconds": "Expires in {} seconds",
|
"shared_link_expires_seconds": "Derīguma termiņš beidzas pēc {} sekundēm",
|
||||||
"shared_link_individual_shared": "Individual shared",
|
"shared_link_individual_shared": "Individuāli kopīgots",
|
||||||
"shared_link_info_chip_download": "Download",
|
"shared_link_info_chip_download": "Lejupielādēt",
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
"shared_link_info_chip_upload": "Upload",
|
"shared_link_info_chip_upload": "Augšupielādēt",
|
||||||
"shared_link_manage_links": "Manage Shared links",
|
"shared_link_manage_links": "Pārvaldīt Kopīgotās saites",
|
||||||
"shared_link_public_album": "Public album",
|
"shared_link_public_album": "Publisks albums",
|
||||||
"share_done": "Done",
|
"share_done": "Gatavs",
|
||||||
"share_invite": "Uzaicināt albumā",
|
"share_invite": "Uzaicināt albumā",
|
||||||
"sharing_page_album": "Kopīgotie albumi",
|
"sharing_page_album": "Kopīgotie albumi",
|
||||||
"sharing_page_description": "Izveidojiet koplietojamus albumus, lai kopīgotu fotoattēlus un videoklipus ar Jūsu tīkla lietotājiem.",
|
"sharing_page_description": "Izveidojiet koplietojamus albumus, lai kopīgotu fotoattēlus un videoklipus ar Jūsu tīkla lietotājiem.",
|
||||||
"sharing_page_empty_list": "TUKŠS SARAKSTS",
|
"sharing_page_empty_list": "TUKŠS SARAKSTS",
|
||||||
"sharing_silver_appbar_create_shared_album": "Izveidot kopīgotu albumu",
|
"sharing_silver_appbar_create_shared_album": "Izveidot kopīgotu albumu",
|
||||||
"sharing_silver_appbar_shared_links": "Shared links",
|
"sharing_silver_appbar_shared_links": "Kopīgotās saites",
|
||||||
"sharing_silver_appbar_share_partner": "Dalīties ar partneri",
|
"sharing_silver_appbar_share_partner": "Dalīties ar partneri",
|
||||||
"tab_controller_nav_library": "Bibliotēka",
|
"tab_controller_nav_library": "Bibliotēka",
|
||||||
"tab_controller_nav_photos": "Fotoattēli",
|
"tab_controller_nav_photos": "Fotoattēli",
|
||||||
@@ -491,30 +495,30 @@
|
|||||||
"theme_setting_theme_title": "Dizains",
|
"theme_setting_theme_title": "Dizains",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Trīspakāpju ielāde var palielināt ielādēšanas veiktspēju, bet izraisa ievērojami lielāku tīkla noslodzi",
|
"theme_setting_three_stage_loading_subtitle": "Trīspakāpju ielāde var palielināt ielādēšanas veiktspēju, bet izraisa ievērojami lielāku tīkla noslodzi",
|
||||||
"theme_setting_three_stage_loading_title": "Iespējot trīspakāpju ielādi",
|
"theme_setting_three_stage_loading_title": "Iespējot trīspakāpju ielādi",
|
||||||
"translated_text_options": "Options",
|
"translated_text_options": "Iestatījumi",
|
||||||
"trash_page_delete": "Delete",
|
"trash_page_delete": "Dzēst",
|
||||||
"trash_page_delete_all": "Delete All",
|
"trash_page_delete_all": "Dzēst Visu",
|
||||||
"trash_page_empty_trash_btn": "Empty trash",
|
"trash_page_empty_trash_btn": "Iztukšot atkritni",
|
||||||
"trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich",
|
"trash_page_empty_trash_dialog_content": "Vai vēlaties iztukšot savus izmestos aktīvus? Tie tiks neatgriezeniski izņemti no Immich",
|
||||||
"trash_page_empty_trash_dialog_ok": "Ok",
|
"trash_page_empty_trash_dialog_ok": "Ok",
|
||||||
"trash_page_info": "Trashed items will be permanently deleted after {} days",
|
"trash_page_info": "Atkritnes vienumi tiks neatgriezeniski dzēsti pēc {} dienām",
|
||||||
"trash_page_no_assets": "No trashed assets",
|
"trash_page_no_assets": "Atkritnē nav aktīvu",
|
||||||
"trash_page_restore": "Restore",
|
"trash_page_restore": "Atjaunot",
|
||||||
"trash_page_restore_all": "Restore All",
|
"trash_page_restore_all": "Atjaunot Visu",
|
||||||
"trash_page_select_assets_btn": "Select assets",
|
"trash_page_select_assets_btn": "Atlasīt aktīvus",
|
||||||
"trash_page_select_btn": "Select",
|
"trash_page_select_btn": "Atlasīt",
|
||||||
"trash_page_title": "Trash ({})",
|
"trash_page_title": "Atkritne ({})",
|
||||||
"upload_dialog_cancel": "Cancel",
|
"upload_dialog_cancel": "Atcelt",
|
||||||
"upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?",
|
"upload_dialog_info": "Vai vēlaties veikt izvēlētā(-o) aktīva(-u) dublējumu uz servera?",
|
||||||
"upload_dialog_ok": "Upload",
|
"upload_dialog_ok": "Augšupielādēt",
|
||||||
"upload_dialog_title": "Upload Asset",
|
"upload_dialog_title": "Augšupielādēt Aktīvu",
|
||||||
"version_announcement_overlay_ack": "Atzīt",
|
"version_announcement_overlay_ack": "Atzīt",
|
||||||
"version_announcement_overlay_release_notes": "informācija par laidienu",
|
"version_announcement_overlay_release_notes": "informācija par laidienu",
|
||||||
"version_announcement_overlay_text_1": "Sveiks draugs, ir jauns izlaidums no",
|
"version_announcement_overlay_text_1": "Sveiks draugs, ir jauns izlaidums no",
|
||||||
"version_announcement_overlay_text_2": "lūdzu, veltiet laiku, lai apmeklētu",
|
"version_announcement_overlay_text_2": "lūdzu, veltiet laiku, lai apmeklētu",
|
||||||
"version_announcement_overlay_text_3": " un pārliecinieties, vai docker-compose un .env iestatījumi ir atjaunināti, lai novērstu jebkādas nepareizas konfigurācijas, īpaši, ja izmantojat WatchTower vai mehānismu, kas automātiski veic servera lietojumprogrammas atjaunināšanu.",
|
"version_announcement_overlay_text_3": " un pārliecinieties, vai docker-compose un .env iestatījumi ir atjaunināti, lai novērstu jebkādas nepareizas konfigurācijas, īpaši, ja izmantojat WatchTower vai mehānismu, kas automātiski veic servera lietojumprogrammas atjaunināšanu.",
|
||||||
"version_announcement_overlay_title": "Pieejama jauna servera versija \uD83C\uDF89",
|
"version_announcement_overlay_title": "Pieejama jauna servera versija \uD83C\uDF89",
|
||||||
"viewer_remove_from_stack": "Remove from Stack",
|
"viewer_remove_from_stack": "Noņemt no Steka",
|
||||||
"viewer_stack_use_as_main_asset": "Use as Main Asset",
|
"viewer_stack_use_as_main_asset": "Izmantot kā Galveno Aktīvu",
|
||||||
"viewer_unstack": "Un-Stack"
|
"viewer_unstack": "At-Stekot"
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
||||||
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
||||||
"backup_controller_page_storage_format": "{} of {} used",
|
"backup_controller_page_storage_format": "{} of {} used",
|
||||||
"backup_controller_page_to_backup": "Albums to be backup",
|
"backup_controller_page_to_backup": "Albums to be backed up",
|
||||||
"backup_controller_page_total": "Total",
|
"backup_controller_page_total": "Total",
|
||||||
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
||||||
"backup_controller_page_turn_off": "Turn off foreground backup",
|
"backup_controller_page_turn_off": "Turn off foreground backup",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Load original image",
|
"setting_image_viewer_original_title": "Load original image",
|
||||||
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
||||||
"setting_image_viewer_preview_title": "Load preview image",
|
"setting_image_viewer_preview_title": "Load preview image",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Show background backup total progress",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Settings",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
"settings_require_restart": "Please restart Immich to apply this setting",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Add",
|
"share_add": "Add",
|
||||||
"share_add_photos": "Add photos",
|
"share_add_photos": "Add photos",
|
||||||
"share_add_title": "Add a title",
|
"share_add_title": "Add a title",
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
"backup_controller_page_background_configure_error": "Konfigurering av bakgrunnstjenesten feilet",
|
"backup_controller_page_background_configure_error": "Konfigurering av bakgrunnstjenesten feilet",
|
||||||
"backup_controller_page_background_delay": "Forsink sikkerhetskopiering av nye objekter: {}",
|
"backup_controller_page_background_delay": "Forsink sikkerhetskopiering av nye objekter: {}",
|
||||||
"backup_controller_page_background_description": "Skru på bakgrunnstjenesten for å automatisk sikkerhetskopiere alle nye objekter uten å måtte åpne appen",
|
"backup_controller_page_background_description": "Skru på bakgrunnstjenesten for å automatisk sikkerhetskopiere alle nye objekter uten å måtte åpne appen",
|
||||||
"backup_controller_page_background_is_off": "Automatisk sikkerhetskopiering i bakgrunnener deaktivert",
|
"backup_controller_page_background_is_off": "Automatisk sikkerhetskopiering i bakgrunnen er deaktivert",
|
||||||
"backup_controller_page_background_is_on": "Automatisk sikkerhetskopiering i bakgrunnen er aktivert",
|
"backup_controller_page_background_is_on": "Automatisk sikkerhetskopiering i bakgrunnen er aktivert",
|
||||||
"backup_controller_page_background_turn_off": "Skru av bakgrunnstjenesten",
|
"backup_controller_page_background_turn_off": "Skru av bakgrunnstjenesten",
|
||||||
"backup_controller_page_background_turn_on": "Skru på bakgrunnstjenesten",
|
"backup_controller_page_background_turn_on": "Skru på bakgrunnstjenesten",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Last originalbildet",
|
"setting_image_viewer_original_title": "Last originalbildet",
|
||||||
"setting_image_viewer_preview_subtitle": "Aktiver for å laste et bilde av medium oppløsning. Deaktiver for å enten direkte laste inn originalen eller kun benytte miniatyrbilde.",
|
"setting_image_viewer_preview_subtitle": "Aktiver for å laste et bilde av medium oppløsning. Deaktiver for å enten direkte laste inn originalen eller kun benytte miniatyrbilde.",
|
||||||
"setting_image_viewer_preview_title": "Last forhåndsvisningsbilde",
|
"setting_image_viewer_preview_title": "Last forhåndsvisningsbilde",
|
||||||
|
"setting_image_viewer_title": "Bilder",
|
||||||
"setting_languages_apply": "Bekreft",
|
"setting_languages_apply": "Bekreft",
|
||||||
"setting_languages_title": "Språk",
|
"setting_languages_title": "Språk",
|
||||||
"setting_notifications_notify_failures_grace_period": "Varsle om sikkerhetskopieringsfeil i bakgrunnen: {}",
|
"setting_notifications_notify_failures_grace_period": "Varsle om sikkerhetskopieringsfeil i bakgrunnen: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Vis status på sikkerhetskopiering i bakgrunnen",
|
"setting_notifications_total_progress_title": "Vis status på sikkerhetskopiering i bakgrunnen",
|
||||||
"setting_pages_app_bar_settings": "Innstillinger",
|
"setting_pages_app_bar_settings": "Innstillinger",
|
||||||
"settings_require_restart": "Vennligst restart Immich for å aktivere denne innstillingen",
|
"settings_require_restart": "Vennligst restart Immich for å aktivere denne innstillingen",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Aktiver for å automatisk loope en video i detaljeviseren.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videoer",
|
||||||
"share_add": "Legg til",
|
"share_add": "Legg til",
|
||||||
"share_add_photos": "Legg til bilder",
|
"share_add_photos": "Legg til bilder",
|
||||||
"share_add_title": "Legg til tittel",
|
"share_add_title": "Legg til tittel",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"advanced_settings_troubleshooting_subtitle": "Schakel extra functies voor probleemoplossing in ",
|
"advanced_settings_troubleshooting_subtitle": "Schakel extra functies voor probleemoplossing in ",
|
||||||
"advanced_settings_troubleshooting_title": "Probleemoplossing",
|
"advanced_settings_troubleshooting_title": "Probleemoplossing",
|
||||||
"album_info_card_backup_album_excluded": "UITGESLOTEN",
|
"album_info_card_backup_album_excluded": "UITGESLOTEN",
|
||||||
"album_info_card_backup_album_included": "INGESLOTEN",
|
"album_info_card_backup_album_included": "INBEGREPEN",
|
||||||
"album_thumbnail_card_item": "1 item",
|
"album_thumbnail_card_item": "1 item",
|
||||||
"album_thumbnail_card_items": "{} items",
|
"album_thumbnail_card_items": "{} items",
|
||||||
"album_thumbnail_card_shared": " · Gedeeld",
|
"album_thumbnail_card_shared": " · Gedeeld",
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
"asset_viewer_settings_title": "Foto weergave",
|
"asset_viewer_settings_title": "Foto weergave",
|
||||||
"backup_album_selection_page_albums_device": "Albums op apparaat ({})",
|
"backup_album_selection_page_albums_device": "Albums op apparaat ({})",
|
||||||
"backup_album_selection_page_albums_tap": "Tik om in te voegen, dubbel tik om uit te sluiten",
|
"backup_album_selection_page_albums_tap": "Tik om in te voegen, dubbel tik om uit te sluiten",
|
||||||
"backup_album_selection_page_assets_scatter": "Assets kunnen over verschillende albums verdeeld zijn, dus albums kunnen ingesloten of uitgesloten zijn van het backup proces.",
|
"backup_album_selection_page_assets_scatter": "Assets kunnen over verschillende albums verdeeld zijn, dus albums kunnen inbegrepen of uitgesloten zijn van het backup proces.",
|
||||||
"backup_album_selection_page_select_albums": "Albums selecteren",
|
"backup_album_selection_page_select_albums": "Albums selecteren",
|
||||||
"backup_album_selection_page_selection_info": "Selectie info",
|
"backup_album_selection_page_selection_info": "Selectie info",
|
||||||
"backup_album_selection_page_total_assets": "Totaal unieke assets",
|
"backup_album_selection_page_total_assets": "Totaal unieke assets",
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
"backup_controller_page_background_app_refresh_disabled_title": "Verversen op achtergrond uitgeschakeld",
|
"backup_controller_page_background_app_refresh_disabled_title": "Verversen op achtergrond uitgeschakeld",
|
||||||
"backup_controller_page_background_app_refresh_enable_button_text": "Ga naar instellingen",
|
"backup_controller_page_background_app_refresh_enable_button_text": "Ga naar instellingen",
|
||||||
"backup_controller_page_background_battery_info_link": "Laat zien hoe",
|
"backup_controller_page_background_battery_info_link": "Laat zien hoe",
|
||||||
"backup_controller_page_background_battery_info_message": "Schakel voor de beste back-upervaring op de achtergrond alle batterijoptimalisaties uit, die de achtergrondactiviteit van Immich beperken.\n\nAangezien dit apparaatspecifiek is, zoek de vereiste informatie op voor de fabrikant van je apparaat.",
|
"backup_controller_page_background_battery_info_message": "Voor de beste back-upervaring, schakel je alle batterijoptimalisaties uit omdat deze op-de-achtergrondactiviteiten van Immich beperken.\n\nAangezien dit apparaatspecifiek is, zoek de vereiste informatie op voor de fabrikant van je apparaat.",
|
||||||
"backup_controller_page_background_battery_info_ok": "OK",
|
"backup_controller_page_background_battery_info_ok": "OK",
|
||||||
"backup_controller_page_background_battery_info_title": "Batterijoptimalisaties",
|
"backup_controller_page_background_battery_info_title": "Batterijoptimalisaties",
|
||||||
"backup_controller_page_background_charging": "Alleen tijdens opladen",
|
"backup_controller_page_background_charging": "Alleen tijdens opladen",
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
"control_bottom_app_bar_share": "Delen",
|
"control_bottom_app_bar_share": "Delen",
|
||||||
"control_bottom_app_bar_share_to": "Delen met",
|
"control_bottom_app_bar_share_to": "Delen met",
|
||||||
"control_bottom_app_bar_stack": "Stapel",
|
"control_bottom_app_bar_stack": "Stapel",
|
||||||
"control_bottom_app_bar_trash_from_immich": "Verplaatsen naar prullenbak",
|
"control_bottom_app_bar_trash_from_immich": "Naar prullenbak",
|
||||||
"control_bottom_app_bar_unarchive": "Herstellen",
|
"control_bottom_app_bar_unarchive": "Herstellen",
|
||||||
"control_bottom_app_bar_unfavorite": "Onfavoriet",
|
"control_bottom_app_bar_unfavorite": "Onfavoriet",
|
||||||
"control_bottom_app_bar_upload": "Uploaden",
|
"control_bottom_app_bar_upload": "Uploaden",
|
||||||
@@ -244,7 +244,7 @@
|
|||||||
"login_form_api_exception": "API fout. Controleer de server URL en probeer opnieuw.",
|
"login_form_api_exception": "API fout. Controleer de server URL en probeer opnieuw.",
|
||||||
"login_form_back_button_text": "Terug",
|
"login_form_back_button_text": "Terug",
|
||||||
"login_form_button_text": "Inloggen",
|
"login_form_button_text": "Inloggen",
|
||||||
"login_form_email_hint": "jouwemail@email.com",
|
"login_form_email_hint": "jouwemail@email.nl",
|
||||||
"login_form_endpoint_hint": "http://jouw-server-ip:poort/api",
|
"login_form_endpoint_hint": "http://jouw-server-ip:poort/api",
|
||||||
"login_form_endpoint_url": "Server-URL",
|
"login_form_endpoint_url": "Server-URL",
|
||||||
"login_form_err_http": "Voer http:// of https:// in",
|
"login_form_err_http": "Voer http:// of https:// in",
|
||||||
@@ -350,7 +350,7 @@
|
|||||||
"search_filter_display_option_not_in_album": "Niet in album",
|
"search_filter_display_option_not_in_album": "Niet in album",
|
||||||
"search_filter_location_city": "Stad",
|
"search_filter_location_city": "Stad",
|
||||||
"search_filter_location_country": "Land",
|
"search_filter_location_country": "Land",
|
||||||
"search_filter_location_state": "Staat",
|
"search_filter_location_state": "Status",
|
||||||
"search_filter_media_type_all": "Alle",
|
"search_filter_media_type_all": "Alle",
|
||||||
"search_filter_media_type_image": "Afbeelding",
|
"search_filter_media_type_image": "Afbeelding",
|
||||||
"search_filter_media_type_video": "Video",
|
"search_filter_media_type_video": "Video",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Originele afbeelding laden",
|
"setting_image_viewer_original_title": "Originele afbeelding laden",
|
||||||
"setting_image_viewer_preview_subtitle": "Schakel in om een afbeelding met middelgrote resolutie te laden. Schakel uit om alleen het origineel direct te laden of alleen de thumbnail te gebruiken.",
|
"setting_image_viewer_preview_subtitle": "Schakel in om een afbeelding met middelgrote resolutie te laden. Schakel uit om alleen het origineel direct te laden of alleen de thumbnail te gebruiken.",
|
||||||
"setting_image_viewer_preview_title": "Voorbeeldafbeelding laden",
|
"setting_image_viewer_preview_title": "Voorbeeldafbeelding laden",
|
||||||
|
"setting_image_viewer_title": "Afbeeldingen",
|
||||||
"setting_languages_apply": "Toepassen",
|
"setting_languages_apply": "Toepassen",
|
||||||
"setting_languages_title": "Taal",
|
"setting_languages_title": "Taal",
|
||||||
"setting_notifications_notify_failures_grace_period": "Fouten van back-up op de achtergrond melden: {}",
|
"setting_notifications_notify_failures_grace_period": "Fouten van back-up op de achtergrond melden: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Totale voortgang van achtergrondback-up tonen",
|
"setting_notifications_total_progress_title": "Totale voortgang van achtergrondback-up tonen",
|
||||||
"setting_pages_app_bar_settings": "Instellingen",
|
"setting_pages_app_bar_settings": "Instellingen",
|
||||||
"settings_require_restart": "Start Immich opnieuw op om deze instelling toe te passen",
|
"settings_require_restart": "Start Immich opnieuw op om deze instelling toe te passen",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Inschakelen om video's automatisch te herhalen in de detailweergave.",
|
||||||
|
"setting_video_viewer_looping_title": "Herhalen",
|
||||||
|
"setting_video_viewer_title": "Video's",
|
||||||
"share_add": "Toevoegen",
|
"share_add": "Toevoegen",
|
||||||
"share_add_photos": "Foto's toevoegen",
|
"share_add_photos": "Foto's toevoegen",
|
||||||
"share_add_title": "Titel toevoegen",
|
"share_add_title": "Titel toevoegen",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"action_common_back": "Back",
|
"action_common_back": "Cofnij",
|
||||||
"action_common_cancel": "Anuluj",
|
"action_common_cancel": "Anuluj",
|
||||||
"action_common_clear": "Clear",
|
"action_common_clear": "Wyrzuść",
|
||||||
"action_common_confirm": "Confirm",
|
"action_common_confirm": "Potwierdzać",
|
||||||
"action_common_update": "Aktualizuj",
|
"action_common_update": "Aktualizuj",
|
||||||
"add_to_album_bottom_sheet_added": "Dodano do {album}",
|
"add_to_album_bottom_sheet_added": "Dodano do {album}",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Już w {album}",
|
"add_to_album_bottom_sheet_already_exists": "Już w {album}",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"album_thumbnail_card_shared": "Udostępniony",
|
"album_thumbnail_card_shared": "Udostępniony",
|
||||||
"album_thumbnail_owned": "Posiadany",
|
"album_thumbnail_owned": "Posiadany",
|
||||||
"album_thumbnail_shared_by": "Udostępnione przez {}",
|
"album_thumbnail_shared_by": "Udostępnione przez {}",
|
||||||
"album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?",
|
"album_viewer_appbar_delete_confirm": "Czy na pewno chcesz usunąć ten album ze swojego konta?",
|
||||||
"album_viewer_appbar_share_delete": "Usuń album",
|
"album_viewer_appbar_share_delete": "Usuń album",
|
||||||
"album_viewer_appbar_share_err_delete": "Nie udało się usunąć albumu",
|
"album_viewer_appbar_share_err_delete": "Nie udało się usunąć albumu",
|
||||||
"album_viewer_appbar_share_err_leave": "Nie udało się wyjść z albumu",
|
"album_viewer_appbar_share_err_leave": "Nie udało się wyjść z albumu",
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
"backup_manual_in_progress": "Przesyłanie już trwa. Spróbuj po pewnym czasie",
|
"backup_manual_in_progress": "Przesyłanie już trwa. Spróbuj po pewnym czasie",
|
||||||
"backup_manual_success": "Sukces",
|
"backup_manual_success": "Sukces",
|
||||||
"backup_manual_title": "Stan przesyłania",
|
"backup_manual_title": "Stan przesyłania",
|
||||||
"backup_options_page_title": "Backup options",
|
"backup_options_page_title": "Opcje kopi zapasowej",
|
||||||
"cache_settings_album_thumbnails": "Miniatury stron bibliotek ({} zasobów)",
|
"cache_settings_album_thumbnails": "Miniatury stron bibliotek ({} zasobów)",
|
||||||
"cache_settings_clear_cache_button": "Wyczyść Cache",
|
"cache_settings_clear_cache_button": "Wyczyść Cache",
|
||||||
"cache_settings_clear_cache_button_title": "Czyści pamięć podręczną aplikacji. Wpłynie to znacząco na wydajność aplikacji, dopóki pamięć podręczna nie zostanie odbudowana.",
|
"cache_settings_clear_cache_button_title": "Czyści pamięć podręczną aplikacji. Wpłynie to znacząco na wydajność aplikacji, dopóki pamięć podręczna nie zostanie odbudowana.",
|
||||||
@@ -218,7 +218,7 @@
|
|||||||
"home_page_share_err_local": "Nie można udostępniać zasobów lokalnych za pośrednictwem linku, pomijajam",
|
"home_page_share_err_local": "Nie można udostępniać zasobów lokalnych za pośrednictwem linku, pomijajam",
|
||||||
"home_page_upload_err_limit": "Można przesłać maksymalnie 30 zasobów jednocześnie, pomijanie",
|
"home_page_upload_err_limit": "Można przesłać maksymalnie 30 zasobów jednocześnie, pomijanie",
|
||||||
"image_viewer_page_state_provider_download_error": "Błąd pobierania",
|
"image_viewer_page_state_provider_download_error": "Błąd pobierania",
|
||||||
"image_viewer_page_state_provider_download_started": "Download Started",
|
"image_viewer_page_state_provider_download_started": "Pobieranie rozpoczęte",
|
||||||
"image_viewer_page_state_provider_download_success": "Pobieranie zakończone",
|
"image_viewer_page_state_provider_download_success": "Pobieranie zakończone",
|
||||||
"image_viewer_page_state_provider_share_error": "Udostępnij błąd",
|
"image_viewer_page_state_provider_share_error": "Udostępnij błąd",
|
||||||
"library_page_albums": "Albumy",
|
"library_page_albums": "Albumy",
|
||||||
@@ -292,22 +292,22 @@
|
|||||||
"map_settings_theme_settings": "Map Theme",
|
"map_settings_theme_settings": "Map Theme",
|
||||||
"map_zoom_to_see_photos": "Pomniejsz, aby zobaczyć zdjęcia",
|
"map_zoom_to_see_photos": "Pomniejsz, aby zobaczyć zdjęcia",
|
||||||
"memories_all_caught_up": "All caught up",
|
"memories_all_caught_up": "All caught up",
|
||||||
"memories_check_back_tomorrow": "Check back tomorrow for more memories",
|
"memories_check_back_tomorrow": "Wróć jutro po więcej wspomnień",
|
||||||
"memories_start_over": "Start Over",
|
"memories_start_over": "Zacznij od nowa",
|
||||||
"memories_swipe_to_close": "Swipe up to close",
|
"memories_swipe_to_close": "Przesuń w górę, aby zamknąć",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
"motion_photos_page_title": "Zdjęcia ruchome",
|
"motion_photos_page_title": "Zdjęcia ruchome",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Nie można edytować daty zasobów tylko do odczytu, pomijanie",
|
"multiselect_grid_edit_date_time_err_read_only": "Nie można edytować daty zasobów tylko do odczytu, pomijanie",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Nie można edytować lokalizacji zasobów tylko do odczytu, pomijanie",
|
"multiselect_grid_edit_gps_err_read_only": "Nie można edytować lokalizacji zasobów tylko do odczytu, pomijanie",
|
||||||
"no_assets_to_show": "No assets to show",
|
"no_assets_to_show": "Brak zasobów do pokazania",
|
||||||
"notification_permission_dialog_cancel": "Anuluj",
|
"notification_permission_dialog_cancel": "Anuluj",
|
||||||
"notification_permission_dialog_content": "Aby włączyć powiadomienia, przejdź do Ustawień i wybierz opcję Zezwalaj.",
|
"notification_permission_dialog_content": "Aby włączyć powiadomienia, przejdź do Ustawień i wybierz opcję Zezwalaj.",
|
||||||
"notification_permission_dialog_settings": "Ustawienia",
|
"notification_permission_dialog_settings": "Ustawienia",
|
||||||
"notification_permission_list_tile_content": "Przyznaj uprawnienia, aby włączyć powiadomienia.",
|
"notification_permission_list_tile_content": "Przyznaj uprawnienia, aby włączyć powiadomienia.",
|
||||||
"notification_permission_list_tile_enable_button": "Włącz Powiadomienia",
|
"notification_permission_list_tile_enable_button": "Włącz Powiadomienia",
|
||||||
"notification_permission_list_tile_title": "Pozwolenie na powiadomienia",
|
"notification_permission_list_tile_title": "Pozwolenie na powiadomienia",
|
||||||
"partner_list_user_photos": "{user}'s photos",
|
"partner_list_user_photos": "{user} zdjęcia",
|
||||||
"partner_list_view_all": "View all",
|
"partner_list_view_all": "Pokaż wszystkie",
|
||||||
"partner_page_add_partner": "Dodaj partnera",
|
"partner_page_add_partner": "Dodaj partnera",
|
||||||
"partner_page_empty_message": "Twoje zdjęcia nie są udostępnione żadnemu partnerowi",
|
"partner_page_empty_message": "Twoje zdjęcia nie są udostępnione żadnemu partnerowi",
|
||||||
"partner_page_no_more_users": "Brak użytkowników do dodania",
|
"partner_page_no_more_users": "Brak użytkowników do dodania",
|
||||||
@@ -342,7 +342,7 @@
|
|||||||
"recently_added_page_title": "Ostatnio Dodane",
|
"recently_added_page_title": "Ostatnio Dodane",
|
||||||
"scaffold_body_error_occurred": "Wystąpił błąd",
|
"scaffold_body_error_occurred": "Wystąpił błąd",
|
||||||
"search_bar_hint": "Szukaj swoich zdjęć",
|
"search_bar_hint": "Szukaj swoich zdjęć",
|
||||||
"search_filter_apply": "Apply filter",
|
"search_filter_apply": "Zastosuj filtr",
|
||||||
"search_filter_camera_make": "Make",
|
"search_filter_camera_make": "Make",
|
||||||
"search_filter_camera_model": "Model",
|
"search_filter_camera_model": "Model",
|
||||||
"search_filter_display_option_archive": "Archiwum",
|
"search_filter_display_option_archive": "Archiwum",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Załaduj oryginalny obraz",
|
"setting_image_viewer_original_title": "Załaduj oryginalny obraz",
|
||||||
"setting_image_viewer_preview_subtitle": "Włącz ładowanie obrazu o średniej rozdzielczości. Wyłącz opcję bezpośredniego ładowania oryginału lub używania tylko miniatury.",
|
"setting_image_viewer_preview_subtitle": "Włącz ładowanie obrazu o średniej rozdzielczości. Wyłącz opcję bezpośredniego ładowania oryginału lub używania tylko miniatury.",
|
||||||
"setting_image_viewer_preview_title": "Załaduj obraz podglądu",
|
"setting_image_viewer_preview_title": "Załaduj obraz podglądu",
|
||||||
|
"setting_image_viewer_title": "Zdjęcia",
|
||||||
"setting_languages_apply": "Zastosuj",
|
"setting_languages_apply": "Zastosuj",
|
||||||
"setting_languages_title": "Języki",
|
"setting_languages_title": "Języki",
|
||||||
"setting_notifications_notify_failures_grace_period": "Powiadomienie o awariach kopii zapasowych w tle: {}",
|
"setting_notifications_notify_failures_grace_period": "Powiadomienie o awariach kopii zapasowych w tle: {}",
|
||||||
@@ -407,10 +408,13 @@
|
|||||||
"setting_notifications_total_progress_title": "Pokaż całkowity postęp tworzenia kopii zapasowej w tle",
|
"setting_notifications_total_progress_title": "Pokaż całkowity postęp tworzenia kopii zapasowej w tle",
|
||||||
"setting_pages_app_bar_settings": "Ustawienia",
|
"setting_pages_app_bar_settings": "Ustawienia",
|
||||||
"settings_require_restart": "Aby zastosować to ustawienie, uruchom ponownie Immich",
|
"settings_require_restart": "Aby zastosować to ustawienie, uruchom ponownie Immich",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Włącz automatyczne zapętlanie wideo w przeglądarce szczegółów.",
|
||||||
|
"setting_video_viewer_looping_title": "Zapętlenie",
|
||||||
|
"setting_video_viewer_title": "Filmy",
|
||||||
"share_add": "Dodaj",
|
"share_add": "Dodaj",
|
||||||
"share_add_photos": "Dodaj zdjęcia",
|
"share_add_photos": "Dodaj zdjęcia",
|
||||||
"share_add_title": "Dodaj tytuł",
|
"share_add_title": "Dodaj tytuł",
|
||||||
"share_assets_selected": "{} selected",
|
"share_assets_selected": "{} wybrano ",
|
||||||
"share_create_album": "Utwórz album",
|
"share_create_album": "Utwórz album",
|
||||||
"shared_album_activities_input_disable": "Komentarz jest wyłączony",
|
"shared_album_activities_input_disable": "Komentarz jest wyłączony",
|
||||||
"shared_album_activities_input_hint": "Powiedz coś",
|
"shared_album_activities_input_hint": "Powiedz coś",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Carregar imagem original",
|
"setting_image_viewer_original_title": "Carregar imagem original",
|
||||||
"setting_image_viewer_preview_subtitle": "Ative para carregar uma imagem de resolução média. Desative para carregar diretamente o original ou usar apenas a miniatura.",
|
"setting_image_viewer_preview_subtitle": "Ative para carregar uma imagem de resolução média. Desative para carregar diretamente o original ou usar apenas a miniatura.",
|
||||||
"setting_image_viewer_preview_title": "Carregar imagem de visualização",
|
"setting_image_viewer_preview_title": "Carregar imagem de visualização",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notifique falhas de backup em segundo plano: {}",
|
"setting_notifications_notify_failures_grace_period": "Notifique falhas de backup em segundo plano: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Mostrar progresso total do backup em segundo plano",
|
"setting_notifications_total_progress_title": "Mostrar progresso total do backup em segundo plano",
|
||||||
"setting_pages_app_bar_settings": "Configurações",
|
"setting_pages_app_bar_settings": "Configurações",
|
||||||
"settings_require_restart": "Reinicie o Immich para aplicar essa configuração",
|
"settings_require_restart": "Reinicie o Immich para aplicar essa configuração",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Adicionar",
|
"share_add": "Adicionar",
|
||||||
"share_add_photos": "Adicionar fotos",
|
"share_add_photos": "Adicionar fotos",
|
||||||
"share_add_title": "Adicione um título",
|
"share_add_title": "Adicione um título",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Încarcă fotografia originală",
|
"setting_image_viewer_original_title": "Încarcă fotografia originală",
|
||||||
"setting_image_viewer_preview_subtitle": "Activează pentru a încărca o imagine în rezoluție medie. Dezactivează pentru a încărca direct imaginea originală sau doar a utiliza miniatura.",
|
"setting_image_viewer_preview_subtitle": "Activează pentru a încărca o imagine în rezoluție medie. Dezactivează pentru a încărca direct imaginea originală sau doar a utiliza miniatura.",
|
||||||
"setting_image_viewer_preview_title": "Încarcă imaginea de previzualizare",
|
"setting_image_viewer_preview_title": "Încarcă imaginea de previzualizare",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notificare eșuări backup în fundal: {}",
|
"setting_notifications_notify_failures_grace_period": "Notificare eșuări backup în fundal: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Afișează progresul total al copiilor de siguranță în fundal",
|
"setting_notifications_total_progress_title": "Afișează progresul total al copiilor de siguranță în fundal",
|
||||||
"setting_pages_app_bar_settings": "Setări",
|
"setting_pages_app_bar_settings": "Setări",
|
||||||
"settings_require_restart": "Te rugăm să repornești Immich pentru a aplica această setare",
|
"settings_require_restart": "Te rugăm să repornești Immich pentru a aplica această setare",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Adaugă",
|
"share_add": "Adaugă",
|
||||||
"share_add_photos": "Adaugă fotografii",
|
"share_add_photos": "Adaugă fotografii",
|
||||||
"share_add_title": "Adaugă un titlu",
|
"share_add_title": "Adaugă un titlu",
|
||||||
|
|||||||
@@ -209,7 +209,7 @@
|
|||||||
"home_page_album_err_partner": "Пока не удается добавить объекты партнера в альбом, пропуск...",
|
"home_page_album_err_partner": "Пока не удается добавить объекты партнера в альбом, пропуск...",
|
||||||
"home_page_archive_err_local": "Пока невозможно добавить локальные объекты в архив, пропускаем",
|
"home_page_archive_err_local": "Пока невозможно добавить локальные объекты в архив, пропускаем",
|
||||||
"home_page_archive_err_partner": "Невозможно архивировать объекты партнера, пропуск...",
|
"home_page_archive_err_partner": "Невозможно архивировать объекты партнера, пропуск...",
|
||||||
"home_page_building_timeline": "Построение временной шкалы",
|
"home_page_building_timeline": "Построение хронологии",
|
||||||
"home_page_delete_err_partner": "Невозможно удалить объекты партнера, пропуск...",
|
"home_page_delete_err_partner": "Невозможно удалить объекты партнера, пропуск...",
|
||||||
"home_page_delete_remote_err_local": "Локальные объект(ы) уже в процессе удаления с сервера, пропуск...",
|
"home_page_delete_remote_err_local": "Локальные объект(ы) уже в процессе удаления с сервера, пропуск...",
|
||||||
"home_page_favorite_err_local": "Пока не удается добавить в избранное локальные объекты, пропуск...",
|
"home_page_favorite_err_local": "Пока не удается добавить в избранное локальные объекты, пропуск...",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Загружать исходное изображение",
|
"setting_image_viewer_original_title": "Загружать исходное изображение",
|
||||||
"setting_image_viewer_preview_subtitle": "Включите для загрузки изображения среднего разрешения.\nОтключите, чтобы загружать оригинал напрямую или использовать только миниатюру.",
|
"setting_image_viewer_preview_subtitle": "Включите для загрузки изображения среднего разрешения.\nОтключите, чтобы загружать оригинал напрямую или использовать только миниатюру.",
|
||||||
"setting_image_viewer_preview_title": "Загружать изображение для предварительного просмотра",
|
"setting_image_viewer_preview_title": "Загружать изображение для предварительного просмотра",
|
||||||
|
"setting_image_viewer_title": "Изображения",
|
||||||
"setting_languages_apply": "Применить",
|
"setting_languages_apply": "Применить",
|
||||||
"setting_languages_title": "Язык",
|
"setting_languages_title": "Язык",
|
||||||
"setting_notifications_notify_failures_grace_period": "Уведомлять об ошибках фонового резервного копирования: {}",
|
"setting_notifications_notify_failures_grace_period": "Уведомлять об ошибках фонового резервного копирования: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Показать общий прогресс фонового резервного копирования",
|
"setting_notifications_total_progress_title": "Показать общий прогресс фонового резервного копирования",
|
||||||
"setting_pages_app_bar_settings": "Настройки",
|
"setting_pages_app_bar_settings": "Настройки",
|
||||||
"settings_require_restart": "Пожалуйста, перезапустите приложение, чтобы изменения вступили в силу",
|
"settings_require_restart": "Пожалуйста, перезапустите приложение, чтобы изменения вступили в силу",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Включить циклическое воспроизведение видео",
|
||||||
|
"setting_video_viewer_looping_title": "Циклическое воспроизведение",
|
||||||
|
"setting_video_viewer_title": "Видео",
|
||||||
"share_add": "Добавить",
|
"share_add": "Добавить",
|
||||||
"share_add_photos": "Добавить фото",
|
"share_add_photos": "Добавить фото",
|
||||||
"share_add_title": "Добавить название",
|
"share_add_title": "Добавить название",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Načítať pôvodný obrázok",
|
"setting_image_viewer_original_title": "Načítať pôvodný obrázok",
|
||||||
"setting_image_viewer_preview_subtitle": "Povolením umožníte načítať obrázok so stredným rozlíšením. Zakážte, ak chcete priamo načítať originál alebo použiť iba miniatúru.",
|
"setting_image_viewer_preview_subtitle": "Povolením umožníte načítať obrázok so stredným rozlíšením. Zakážte, ak chcete priamo načítať originál alebo použiť iba miniatúru.",
|
||||||
"setting_image_viewer_preview_title": "Načítať náhľad obrázka",
|
"setting_image_viewer_preview_title": "Načítať náhľad obrázka",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Oznámenie o zlyhaní zálohovania na pozadí: {}",
|
"setting_notifications_notify_failures_grace_period": "Oznámenie o zlyhaní zálohovania na pozadí: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Zobraziť celkový priebeh zálohovania na pozadí",
|
"setting_notifications_total_progress_title": "Zobraziť celkový priebeh zálohovania na pozadí",
|
||||||
"setting_pages_app_bar_settings": "Nastavenia",
|
"setting_pages_app_bar_settings": "Nastavenia",
|
||||||
"settings_require_restart": "Na použitie tohto nastavenia reštartujte Immich",
|
"settings_require_restart": "Na použitie tohto nastavenia reštartujte Immich",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Pridať",
|
"share_add": "Pridať",
|
||||||
"share_add_photos": "Pridať fotografie",
|
"share_add_photos": "Pridať fotografie",
|
||||||
"share_add_title": "Pridať názov",
|
"share_add_title": "Pridať názov",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Naloži originalno sliko",
|
"setting_image_viewer_original_title": "Naloži originalno sliko",
|
||||||
"setting_image_viewer_preview_subtitle": "Omogoči nalaganje slike srednje ločljivosti. Onemogočite neposredno nalaganje izvirnika ali uporabo samo sličice.",
|
"setting_image_viewer_preview_subtitle": "Omogoči nalaganje slike srednje ločljivosti. Onemogočite neposredno nalaganje izvirnika ali uporabo samo sličice.",
|
||||||
"setting_image_viewer_preview_title": "Naloži predogled slike",
|
"setting_image_viewer_preview_title": "Naloži predogled slike",
|
||||||
|
"setting_image_viewer_title": "Slike",
|
||||||
"setting_languages_apply": "Uporabi",
|
"setting_languages_apply": "Uporabi",
|
||||||
"setting_languages_title": "Jeziki",
|
"setting_languages_title": "Jeziki",
|
||||||
"setting_notifications_notify_failures_grace_period": "Obvesti o napakah varnostnega kopiranja v ozadju: {}",
|
"setting_notifications_notify_failures_grace_period": "Obvesti o napakah varnostnega kopiranja v ozadju: {}",
|
||||||
@@ -407,10 +408,13 @@
|
|||||||
"setting_notifications_total_progress_title": "Prikaži skupni napredek varnostnega kopiranja v ozadju",
|
"setting_notifications_total_progress_title": "Prikaži skupni napredek varnostnega kopiranja v ozadju",
|
||||||
"setting_pages_app_bar_settings": "Nastavitve",
|
"setting_pages_app_bar_settings": "Nastavitve",
|
||||||
"settings_require_restart": "Znova zaženite Immich, da uporabite to nastavitev",
|
"settings_require_restart": "Znova zaženite Immich, da uporabite to nastavitev",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Omogočite samodejno ponavljanje videoposnetka v pregledovalniku podrobnosti.",
|
||||||
|
"setting_video_viewer_looping_title": "V zanki",
|
||||||
|
"setting_video_viewer_title": "Videoposnetki",
|
||||||
"share_add": "Dodaj",
|
"share_add": "Dodaj",
|
||||||
"share_add_photos": "Dodaj fotografije",
|
"share_add_photos": "Dodaj fotografije",
|
||||||
"share_add_title": "Dodaj naslov",
|
"share_add_title": "Dodaj naslov",
|
||||||
"share_assets_selected": "{} selected",
|
"share_assets_selected": "{} izbrano",
|
||||||
"share_create_album": "Ustvari album",
|
"share_create_album": "Ustvari album",
|
||||||
"shared_album_activities_input_disable": "Komentiranje je onemogočeno",
|
"shared_album_activities_input_disable": "Komentiranje je onemogočeno",
|
||||||
"shared_album_activities_input_hint": "Reci kaj",
|
"shared_album_activities_input_hint": "Reci kaj",
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
||||||
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
||||||
"backup_controller_page_storage_format": "{} of {} used",
|
"backup_controller_page_storage_format": "{} of {} used",
|
||||||
"backup_controller_page_to_backup": "Albums to be backup",
|
"backup_controller_page_to_backup": "Albums to be backed up",
|
||||||
"backup_controller_page_total": "Total",
|
"backup_controller_page_total": "Total",
|
||||||
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
||||||
"backup_controller_page_turn_off": "Turn off foreground backup",
|
"backup_controller_page_turn_off": "Turn off foreground backup",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Load original image",
|
"setting_image_viewer_original_title": "Load original image",
|
||||||
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
||||||
"setting_image_viewer_preview_title": "Load preview image",
|
"setting_image_viewer_preview_title": "Load preview image",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Show background backup total progress",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Settings",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
"settings_require_restart": "Please restart Immich to apply this setting",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Add",
|
"share_add": "Add",
|
||||||
"share_add_photos": "Add photos",
|
"share_add_photos": "Add photos",
|
||||||
"share_add_title": "Add a title",
|
"share_add_title": "Add a title",
|
||||||
|
|||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Učitaj originalnu sliku",
|
"setting_image_viewer_original_title": "Učitaj originalnu sliku",
|
||||||
"setting_image_viewer_preview_subtitle": "Aktiviraj učitavanje slika u srednjoj rezoluciji. Deaktiviraj da se direktno učitava original, ili da se samo koristi minijatura.",
|
"setting_image_viewer_preview_subtitle": "Aktiviraj učitavanje slika u srednjoj rezoluciji. Deaktiviraj da se direktno učitava original, ili da se samo koristi minijatura.",
|
||||||
"setting_image_viewer_preview_title": "Pregledaj sliku",
|
"setting_image_viewer_preview_title": "Pregledaj sliku",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Neuspešne rezervne kopije: {}",
|
"setting_notifications_notify_failures_grace_period": "Neuspešne rezervne kopije: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Prikaži ukupan napredak pozadinskog bekapovanja.\n\n",
|
"setting_notifications_total_progress_title": "Prikaži ukupan napredak pozadinskog bekapovanja.\n\n",
|
||||||
"setting_pages_app_bar_settings": "Opcije",
|
"setting_pages_app_bar_settings": "Opcije",
|
||||||
"settings_require_restart": "Restartujte Immich da primenite ovu promenu",
|
"settings_require_restart": "Restartujte Immich da primenite ovu promenu",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Dodaj",
|
"share_add": "Dodaj",
|
||||||
"share_add_photos": "Dodaj fotografije",
|
"share_add_photos": "Dodaj fotografije",
|
||||||
"share_add_title": "Dodaj naslov",
|
"share_add_title": "Dodaj naslov",
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
"backup_controller_page_status_off": "Automatic foreground backup is off",
|
||||||
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
"backup_controller_page_status_on": "Automatic foreground backup is on",
|
||||||
"backup_controller_page_storage_format": "{} of {} used",
|
"backup_controller_page_storage_format": "{} of {} used",
|
||||||
"backup_controller_page_to_backup": "Albums to be backup",
|
"backup_controller_page_to_backup": "Albums to be backed up",
|
||||||
"backup_controller_page_total": "Total",
|
"backup_controller_page_total": "Total",
|
||||||
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
||||||
"backup_controller_page_turn_off": "Turn off foreground backup",
|
"backup_controller_page_turn_off": "Turn off foreground backup",
|
||||||
@@ -391,6 +391,7 @@
|
|||||||
"setting_image_viewer_original_title": "Load original image",
|
"setting_image_viewer_original_title": "Load original image",
|
||||||
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
||||||
"setting_image_viewer_preview_title": "Load preview image",
|
"setting_image_viewer_preview_title": "Load preview image",
|
||||||
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_title": "Languages",
|
||||||
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
|
||||||
@@ -407,6 +408,9 @@
|
|||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Show background backup total progress",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Settings",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
"settings_require_restart": "Please restart Immich to apply this setting",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Enable to automatically loop a video in the detail viewer.",
|
||||||
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
|
"setting_video_viewer_title": "Videos",
|
||||||
"share_add": "Add",
|
"share_add": "Add",
|
||||||
"share_add_photos": "Add photos",
|
"share_add_photos": "Add photos",
|
||||||
"share_add_title": "Add a title",
|
"share_add_title": "Add a title",
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
{
|
{
|
||||||
"action_common_back": "Back",
|
"action_common_back": "Bakåt",
|
||||||
"action_common_cancel": "Cancel",
|
"action_common_cancel": "Avbryt",
|
||||||
"action_common_clear": "Clear",
|
"action_common_clear": "Rensa",
|
||||||
"action_common_confirm": "Confirm",
|
"action_common_confirm": "Bekräfta",
|
||||||
"action_common_update": "Update",
|
"action_common_update": "Uppdatera",
|
||||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
"add_to_album_bottom_sheet_added": "Tillagd till {album}",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
"add_to_album_bottom_sheet_already_exists": "Redan i {album}",
|
||||||
"advanced_settings_log_level_title": "Log level: {}",
|
"advanced_settings_log_level_title": "Loggnivå: {}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.",
|
"advanced_settings_prefer_remote_subtitle": "Vissa enheter är mycket långsamma på att ladda tumnaglar från resurser på enheten. Aktivera den här inställningen för att ladda bilder från servern istället.",
|
||||||
"advanced_settings_prefer_remote_title": "Prefer remote images",
|
"advanced_settings_prefer_remote_title": "Föredra bilder från servern",
|
||||||
"advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.",
|
"advanced_settings_self_signed_ssl_subtitle": "Hoppar över SSL-certifikatverifiering för serverändpunkten. Krävs för självsignerade certifikat.",
|
||||||
"advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates",
|
"advanced_settings_self_signed_ssl_title": "Tillåt självsignerade SSL-certifikat",
|
||||||
"advanced_settings_tile_subtitle": "Advanced user's settings",
|
"advanced_settings_tile_subtitle": "Avancerade användarinställningar",
|
||||||
"advanced_settings_tile_title": "Avancerad",
|
"advanced_settings_tile_title": "Avancerad",
|
||||||
"advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting",
|
"advanced_settings_troubleshooting_subtitle": "Aktivera funktioner för felsökning",
|
||||||
"advanced_settings_troubleshooting_title": "Troubleshooting",
|
"advanced_settings_troubleshooting_title": "Felsökning",
|
||||||
"album_info_card_backup_album_excluded": "EXKLUDERAD",
|
"album_info_card_backup_album_excluded": "EXKLUDERAD",
|
||||||
"album_info_card_backup_album_included": "INKLUDERAD",
|
"album_info_card_backup_album_included": "INKLUDERAD",
|
||||||
"album_thumbnail_card_item": "1 objekt",
|
"album_thumbnail_card_item": "1 objekt",
|
||||||
"album_thumbnail_card_items": "{} objekt",
|
"album_thumbnail_card_items": "{} objekt",
|
||||||
"album_thumbnail_card_shared": ". Delad",
|
"album_thumbnail_card_shared": " · Delad",
|
||||||
"album_thumbnail_owned": "Owned",
|
"album_thumbnail_owned": "Ägd",
|
||||||
"album_thumbnail_shared_by": "Delat av {}",
|
"album_thumbnail_shared_by": "Delat av {}",
|
||||||
"album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?",
|
"album_viewer_appbar_delete_confirm": "Är du säker på att du vill ta bort albumet från ditt konto?",
|
||||||
"album_viewer_appbar_share_delete": "Radera album",
|
"album_viewer_appbar_share_delete": "Radera album",
|
||||||
"album_viewer_appbar_share_err_delete": "Kunde inte radera album",
|
"album_viewer_appbar_share_err_delete": "Kunde inte radera album",
|
||||||
"album_viewer_appbar_share_err_leave": "Kunde inte lämna album",
|
"album_viewer_appbar_share_err_leave": "Kunde inte lämna album",
|
||||||
@@ -30,27 +30,27 @@
|
|||||||
"album_viewer_appbar_share_err_title": "Kunde inte ändra albumtitel",
|
"album_viewer_appbar_share_err_title": "Kunde inte ändra albumtitel",
|
||||||
"album_viewer_appbar_share_leave": "Lämna album",
|
"album_viewer_appbar_share_leave": "Lämna album",
|
||||||
"album_viewer_appbar_share_remove": "Ta bort från album",
|
"album_viewer_appbar_share_remove": "Ta bort från album",
|
||||||
"album_viewer_appbar_share_to": "Share To",
|
"album_viewer_appbar_share_to": "Dela Till",
|
||||||
"album_viewer_page_share_add_users": "Lägg till användare",
|
"album_viewer_page_share_add_users": "Lägg till användare",
|
||||||
"all_people_page_title": "People",
|
"all_people_page_title": "Personer",
|
||||||
"all_videos_page_title": "Videos",
|
"all_videos_page_title": "Videor",
|
||||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
"app_bar_signout_dialog_content": "Är du säker på att du vill logga ut?",
|
||||||
"app_bar_signout_dialog_ok": "Yes",
|
"app_bar_signout_dialog_ok": "Ja",
|
||||||
"app_bar_signout_dialog_title": "Sign out",
|
"app_bar_signout_dialog_title": "Logga ut",
|
||||||
"archive_page_no_archived_assets": "No archived assets found",
|
"archive_page_no_archived_assets": "Inga arkiverade resurser hittade",
|
||||||
"archive_page_title": "Arkivera ({})",
|
"archive_page_title": "Arkivera ({})",
|
||||||
"asset_action_delete_err_read_only": "Cannot delete read only asset(s), skipping",
|
"asset_action_delete_err_read_only": "Kan inte ta bort skrivskyddade resurser, hoppar över",
|
||||||
"asset_action_share_err_offline": "Cannot fetch offline asset(s), skipping",
|
"asset_action_share_err_offline": "Kan inte hämta offline-resurs(er), hoppar över",
|
||||||
"asset_list_group_by_sub_title": "Group by",
|
"asset_list_group_by_sub_title": "Gruppera på",
|
||||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamisk layout",
|
"asset_list_layout_settings_dynamic_layout_title": "Dynamisk layout",
|
||||||
"asset_list_layout_settings_group_automatically": "Automatic",
|
"asset_list_layout_settings_group_automatically": "Automatiskt",
|
||||||
"asset_list_layout_settings_group_by": "Gruppera bilder efter",
|
"asset_list_layout_settings_group_by": "Gruppera bilder efter",
|
||||||
"asset_list_layout_settings_group_by_month": "Månad",
|
"asset_list_layout_settings_group_by_month": "Månad",
|
||||||
"asset_list_layout_settings_group_by_month_day": "Månad + dag",
|
"asset_list_layout_settings_group_by_month_day": "Månad + dag",
|
||||||
"asset_list_layout_sub_title": "Layout",
|
"asset_list_layout_sub_title": "Layout",
|
||||||
"asset_list_settings_subtitle": "Layoutinställningar för bildrutnät",
|
"asset_list_settings_subtitle": "Layoutinställningar för bildrutnät",
|
||||||
"asset_list_settings_title": "Bildrutnät",
|
"asset_list_settings_title": "Bildrutnät",
|
||||||
"asset_viewer_settings_title": "Asset Viewer",
|
"asset_viewer_settings_title": "Resursvisare",
|
||||||
"backup_album_selection_page_albums_device": "Album på enhet ({})",
|
"backup_album_selection_page_albums_device": "Album på enhet ({})",
|
||||||
"backup_album_selection_page_albums_tap": "Tryck en gång för att inkludera, tryck två gånger för att exkludera",
|
"backup_album_selection_page_albums_tap": "Tryck en gång för att inkludera, tryck två gånger för att exkludera",
|
||||||
"backup_album_selection_page_assets_scatter": "Objekt kan vara utspridda över flera album. Därför kan album inkluderas eller exkluderas under säkerhetskopieringsprocessen",
|
"backup_album_selection_page_assets_scatter": "Objekt kan vara utspridda över flera album. Därför kan album inkluderas eller exkluderas under säkerhetskopieringsprocessen",
|
||||||
@@ -66,8 +66,8 @@
|
|||||||
"backup_background_service_in_progress_notification": "Säkerhetskopierar dina foton och videor...",
|
"backup_background_service_in_progress_notification": "Säkerhetskopierar dina foton och videor...",
|
||||||
"backup_background_service_upload_failure_notification": "Kunde inte ladda upp {}",
|
"backup_background_service_upload_failure_notification": "Kunde inte ladda upp {}",
|
||||||
"backup_controller_page_albums": "Säkerhetskopiera album",
|
"backup_controller_page_albums": "Säkerhetskopiera album",
|
||||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
"backup_controller_page_background_app_refresh_disabled_content": "Aktivera uppdatering i bakgrunden i Inställningar > Allmänt > Uppdatering I Bakgrunden för att använda säkerhetskopiering i bakgrunden.",
|
||||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
"backup_controller_page_background_app_refresh_disabled_title": "Uppdatering i bakgrunden är avaktiverat",
|
||||||
"backup_controller_page_background_app_refresh_enable_button_text": "Gå till inställningar",
|
"backup_controller_page_background_app_refresh_enable_button_text": "Gå till inställningar",
|
||||||
"backup_controller_page_background_battery_info_link": "Visa mig hur",
|
"backup_controller_page_background_battery_info_link": "Visa mig hur",
|
||||||
"backup_controller_page_background_battery_info_message": "För optimal säkerhetskopiering i bakgrunden bör du stänga av batterioptimering som begränsar bakgrundsaktivitet för Immich.\n\nEftersom detta är enhetsspecifikt så bör du söka instruktioner från din enhetstillverkare.",
|
"backup_controller_page_background_battery_info_message": "För optimal säkerhetskopiering i bakgrunden bör du stänga av batterioptimering som begränsar bakgrundsaktivitet för Immich.\n\nEftersom detta är enhetsspecifikt så bör du söka instruktioner från din enhetstillverkare.",
|
||||||
@@ -110,18 +110,18 @@
|
|||||||
"backup_controller_page_uploading_file_info": "Laddar upp filinformation",
|
"backup_controller_page_uploading_file_info": "Laddar upp filinformation",
|
||||||
"backup_err_only_album": "Kan inte ta bort det enda albumet",
|
"backup_err_only_album": "Kan inte ta bort det enda albumet",
|
||||||
"backup_info_card_assets": "objekt",
|
"backup_info_card_assets": "objekt",
|
||||||
"backup_manual_cancelled": "Cancelled",
|
"backup_manual_cancelled": "Avbrutet",
|
||||||
"backup_manual_failed": "Failed",
|
"backup_manual_failed": "Misslyckades",
|
||||||
"backup_manual_in_progress": "Upload already in progress. Try after sometime",
|
"backup_manual_in_progress": "Uppladdning pågår redan. Försök igen om en liten stund",
|
||||||
"backup_manual_success": "Success",
|
"backup_manual_success": "Klart",
|
||||||
"backup_manual_title": "Upload status",
|
"backup_manual_title": "Uppladdningsstatus",
|
||||||
"backup_options_page_title": "Backup options",
|
"backup_options_page_title": "Säkerhetskopieringsinställningar",
|
||||||
"cache_settings_album_thumbnails": "Miniatyrbilder för bibliotek ({} bilder och videor)",
|
"cache_settings_album_thumbnails": "Miniatyrbilder för bibliotek ({} bilder och videor)",
|
||||||
"cache_settings_clear_cache_button": "Rensa cacheminnet",
|
"cache_settings_clear_cache_button": "Rensa cacheminnet",
|
||||||
"cache_settings_clear_cache_button_title": "Rensar appens cacheminne. Detta kommer att avsevärt påverka appens prestanda tills cachen har byggts om.",
|
"cache_settings_clear_cache_button_title": "Rensar appens cacheminne. Detta kommer att avsevärt påverka appens prestanda tills cachen har byggts om.",
|
||||||
"cache_settings_duplicated_assets_clear_button": "CLEAR",
|
"cache_settings_duplicated_assets_clear_button": "RENSA",
|
||||||
"cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app",
|
"cache_settings_duplicated_assets_subtitle": "Foton och videor som är svartlistade av appen",
|
||||||
"cache_settings_duplicated_assets_title": "Duplicated Assets ({})",
|
"cache_settings_duplicated_assets_title": "Duplicerade Resurser ({})",
|
||||||
"cache_settings_image_cache_size": "Cacheminnets storlek ({} bilder och videor)",
|
"cache_settings_image_cache_size": "Cacheminnets storlek ({} bilder och videor)",
|
||||||
"cache_settings_statistics_album": "Miniatyrbilder för bibliotek",
|
"cache_settings_statistics_album": "Miniatyrbilder för bibliotek",
|
||||||
"cache_settings_statistics_assets": "{} bilder och videor ({})",
|
"cache_settings_statistics_assets": "{} bilder och videor ({})",
|
||||||
@@ -131,37 +131,37 @@
|
|||||||
"cache_settings_statistics_title": "Cacheförbrukning",
|
"cache_settings_statistics_title": "Cacheförbrukning",
|
||||||
"cache_settings_subtitle": "Hantera cachebeteendet för Immich-appen.",
|
"cache_settings_subtitle": "Hantera cachebeteendet för Immich-appen.",
|
||||||
"cache_settings_thumbnail_size": "Storlek på cacheminnet ({} bilder och videor)",
|
"cache_settings_thumbnail_size": "Storlek på cacheminnet ({} bilder och videor)",
|
||||||
"cache_settings_tile_subtitle": "Control the local storage behaviour",
|
"cache_settings_tile_subtitle": "Kontrollera beteende för lokal lagring",
|
||||||
"cache_settings_tile_title": "Local Storage",
|
"cache_settings_tile_title": "Lokal Lagring",
|
||||||
"cache_settings_title": "Cache Inställningar",
|
"cache_settings_title": "Cache Inställningar",
|
||||||
"change_password_form_confirm_password": "Bekräfta lösenord",
|
"change_password_form_confirm_password": "Bekräfta lösenord",
|
||||||
"change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
"change_password_form_description": "Hej {name},\n\nDet är antingen första gången du loggar in i systemet, eller så har det skett en förfrågan om återställning av ditt lösenord. Ange ditt nya lösenord nedan.",
|
||||||
"change_password_form_new_password": "Nytt lösenord",
|
"change_password_form_new_password": "Nytt lösenord",
|
||||||
"change_password_form_password_mismatch": "Passwords do not match",
|
"change_password_form_password_mismatch": "Lösenorden matchar inte",
|
||||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
"change_password_form_reenter_new_password": "Ange Nytt Lösenord Igen",
|
||||||
"common_add_to_album": "Add to album",
|
"common_add_to_album": "Lägg till, till album",
|
||||||
"common_change_password": "Ändra lösenord",
|
"common_change_password": "Ändra lösenord",
|
||||||
"common_create_new_album": "Skapa ett nytt album",
|
"common_create_new_album": "Skapa ett nytt album",
|
||||||
"common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.",
|
"common_server_error": "Kontrollera din nätverksanslutning, se till att servern går att nå och att app- och server-versioner är kompatibla.",
|
||||||
"common_shared": "Delad",
|
"common_shared": "Delad",
|
||||||
"control_bottom_app_bar_add_to_album": "Lägg till i album",
|
"control_bottom_app_bar_add_to_album": "Lägg till i album",
|
||||||
"control_bottom_app_bar_album_info": "{} objekt",
|
"control_bottom_app_bar_album_info": "{} objekt",
|
||||||
"control_bottom_app_bar_album_info_shared": "{} objekt • Delat",
|
"control_bottom_app_bar_album_info_shared": "{} objekt • Delat",
|
||||||
"control_bottom_app_bar_archive": "Archive",
|
"control_bottom_app_bar_archive": "Arkivera",
|
||||||
"control_bottom_app_bar_create_new_album": "Skapa nytt album",
|
"control_bottom_app_bar_create_new_album": "Skapa nytt album",
|
||||||
"control_bottom_app_bar_delete": "Radera",
|
"control_bottom_app_bar_delete": "Radera",
|
||||||
"control_bottom_app_bar_delete_from_immich": "Delete from Immich",
|
"control_bottom_app_bar_delete_from_immich": "Ta bort från Immich",
|
||||||
"control_bottom_app_bar_delete_from_local": "Delete from device",
|
"control_bottom_app_bar_delete_from_local": "Ta bort från enhet",
|
||||||
"control_bottom_app_bar_edit_location": "Edit Location",
|
"control_bottom_app_bar_edit_location": "Redigera plats",
|
||||||
"control_bottom_app_bar_edit_time": "Edit Date & Time",
|
"control_bottom_app_bar_edit_time": "Redigera Datum & Tid",
|
||||||
"control_bottom_app_bar_favorite": "Favorit",
|
"control_bottom_app_bar_favorite": "Favorit",
|
||||||
"control_bottom_app_bar_share": "Dela",
|
"control_bottom_app_bar_share": "Dela",
|
||||||
"control_bottom_app_bar_share_to": "Share To",
|
"control_bottom_app_bar_share_to": "Dela Till",
|
||||||
"control_bottom_app_bar_stack": "Stack",
|
"control_bottom_app_bar_stack": "Stapel",
|
||||||
"control_bottom_app_bar_trash_from_immich": "Move to Trash",
|
"control_bottom_app_bar_trash_from_immich": "Flytta till Papperskorgen",
|
||||||
"control_bottom_app_bar_unarchive": "Unarchive",
|
"control_bottom_app_bar_unarchive": "Avarkivera",
|
||||||
"control_bottom_app_bar_unfavorite": "Unfavorite",
|
"control_bottom_app_bar_unfavorite": "Avfavorisera",
|
||||||
"control_bottom_app_bar_upload": "Upload",
|
"control_bottom_app_bar_upload": "Ladda Upp",
|
||||||
"create_album_page_untitled": "Namnlös",
|
"create_album_page_untitled": "Namnlös",
|
||||||
"create_shared_album_page_create": "Skapa",
|
"create_shared_album_page_create": "Skapa",
|
||||||
"create_shared_album_page_share": "Dela",
|
"create_shared_album_page_share": "Dela",
|
||||||
@@ -173,76 +173,76 @@
|
|||||||
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
||||||
"date_format": "E d. LLL y • hh:mm",
|
"date_format": "E d. LLL y • hh:mm",
|
||||||
"delete_dialog_alert": "Dessa objekt kommer att raderas permanent från Immich och din enhet",
|
"delete_dialog_alert": "Dessa objekt kommer att raderas permanent från Immich och din enhet",
|
||||||
"delete_dialog_alert_local": "These items will be permanently removed from your device but still be available on the Immich server",
|
"delete_dialog_alert_local": "Dessa saker kommer att tas bort från din enhet men fortsatt vara tillgängliga på Immich-servern",
|
||||||
"delete_dialog_alert_local_non_backed_up": "Some of the items aren't backed up to Immich and will be permanently removed from your device",
|
"delete_dialog_alert_local_non_backed_up": "Några av sakerna har inte säkerhetskopierats till Immich och kommer att tas bort permanent från din enhet.",
|
||||||
"delete_dialog_alert_remote": "These items will be permanently deleted from the Immich server",
|
"delete_dialog_alert_remote": "Dessa saker kommer att tas bort permanent från Immich-servern",
|
||||||
"delete_dialog_cancel": "Avbryt",
|
"delete_dialog_cancel": "Avbryt",
|
||||||
"delete_dialog_ok": "Radera",
|
"delete_dialog_ok": "Radera",
|
||||||
"delete_dialog_ok_force": "Delete Anyway",
|
"delete_dialog_ok_force": "Ta Bort Ändå",
|
||||||
"delete_dialog_title": "Radera permanent",
|
"delete_dialog_title": "Radera permanent",
|
||||||
"delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only",
|
"delete_local_dialog_ok_backed_up_only": "Ta Bara Bort Säkerhetskopierade",
|
||||||
"delete_local_dialog_ok_force": "Delete Anyway",
|
"delete_local_dialog_ok_force": "Ta Bort Ändå",
|
||||||
"delete_shared_link_dialog_content": "Are you sure you want to delete this shared link?",
|
"delete_shared_link_dialog_content": "Är du säker på att du vill ta bort den här delade länken?",
|
||||||
"delete_shared_link_dialog_title": "Delete Shared Link",
|
"delete_shared_link_dialog_title": "Ta Bort Delad Länk",
|
||||||
"description_input_hint_text": "Lägg till beskrivning...",
|
"description_input_hint_text": "Lägg till beskrivning...",
|
||||||
"description_input_submit_error": "Error updating description, check the log for more details",
|
"description_input_submit_error": "Fel vid uppdatering av beskrivning, se loggen för fler detaljer",
|
||||||
"edit_date_time_dialog_date_time": "Date and Time",
|
"edit_date_time_dialog_date_time": "Datum och Tid",
|
||||||
"edit_date_time_dialog_timezone": "Timezone",
|
"edit_date_time_dialog_timezone": "Tidszon",
|
||||||
"edit_location_dialog_title": "Location",
|
"edit_location_dialog_title": "Plats",
|
||||||
"exif_bottom_sheet_description": "Lägg till beskrivning...",
|
"exif_bottom_sheet_description": "Lägg till beskrivning...",
|
||||||
"exif_bottom_sheet_details": "DETALJER",
|
"exif_bottom_sheet_details": "DETALJER",
|
||||||
"exif_bottom_sheet_location": "PLATS",
|
"exif_bottom_sheet_location": "PLATS",
|
||||||
"exif_bottom_sheet_location_add": "Add a location",
|
"exif_bottom_sheet_location_add": "Lägg till plats",
|
||||||
"exif_bottom_sheet_people": "PEOPLE",
|
"exif_bottom_sheet_people": "PERSONER",
|
||||||
"exif_bottom_sheet_person_add_person": "Add name",
|
"exif_bottom_sheet_person_add_person": "Lägg till namn",
|
||||||
"experimental_settings_new_asset_list_subtitle": "Under uppbyggnad",
|
"experimental_settings_new_asset_list_subtitle": "Under uppbyggnad",
|
||||||
"experimental_settings_new_asset_list_title": "Aktivera experimentellt fotorutnät",
|
"experimental_settings_new_asset_list_title": "Aktivera experimentellt fotorutnät",
|
||||||
"experimental_settings_subtitle": "Använd på egen risk!",
|
"experimental_settings_subtitle": "Använd på egen risk!",
|
||||||
"experimental_settings_title": "Experimentellt",
|
"experimental_settings_title": "Experimentellt",
|
||||||
"favorites_page_no_favorites": "No favorite assets found",
|
"favorites_page_no_favorites": "Inga favoritresurser hittades",
|
||||||
"favorites_page_title": "Favoriter",
|
"favorites_page_title": "Favoriter",
|
||||||
"haptic_feedback_switch": "Enable haptic feedback",
|
"haptic_feedback_switch": "Aktivera haptisk feedback",
|
||||||
"haptic_feedback_title": "Haptic Feedback",
|
"haptic_feedback_title": "Haptisk Feedback",
|
||||||
"home_page_add_to_album_conflicts": "Lade till {added} foton och videor i albumet {album}. {failed} foton och videor finns redan i albumet.",
|
"home_page_add_to_album_conflicts": "Lade till {added} foton och videor i albumet {album}. {failed} foton och videor finns redan i albumet.",
|
||||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
"home_page_add_to_album_err_local": "Kan inte lägga till lokala resurser till album ännu, hoppar över",
|
||||||
"home_page_add_to_album_success": "Lade till {added} foton och videor i albumet {album}.",
|
"home_page_add_to_album_success": "Lade till {added} foton och videor i albumet {album}.",
|
||||||
"home_page_album_err_partner": "Can not add partner assets to an album yet, skipping",
|
"home_page_album_err_partner": "Kan inte lägga till partner-resurser till album ännu, hoppar över",
|
||||||
"home_page_archive_err_local": "Can not archive local assets yet, skipping",
|
"home_page_archive_err_local": "Kan inte arkivera lokala resurser ännu, hoppar över",
|
||||||
"home_page_archive_err_partner": "Can not archive partner assets, skipping",
|
"home_page_archive_err_partner": "Kan inte arkivera partner-resurs, hoppar över",
|
||||||
"home_page_building_timeline": "Bygger tidslinjen",
|
"home_page_building_timeline": "Bygger tidslinjen",
|
||||||
"home_page_delete_err_partner": "Can not delete partner assets, skipping",
|
"home_page_delete_err_partner": "Kan inte ta bort partner-resurs, hoppar över",
|
||||||
"home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping",
|
"home_page_delete_remote_err_local": "Lokala resurser i urvalet för att ta bort från servern, hoppar över",
|
||||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
"home_page_favorite_err_local": "Kan inte favorisera lokala resurser ännu, hoppar över",
|
||||||
"home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping",
|
"home_page_favorite_err_partner": "Kan inte favorisera partner-resurser ännu, hoppar över",
|
||||||
"home_page_first_time_notice": "Om det här är första gången du använder appen, välj ett eller flera backup-album så att tidslinjen kan fyllas med foton och videor från albumen.",
|
"home_page_first_time_notice": "Om det här är första gången du använder appen, välj ett eller flera backup-album så att tidslinjen kan fyllas med foton och videor från albumen.",
|
||||||
"home_page_share_err_local": "Can not share local assets via link, skipping",
|
"home_page_share_err_local": "Kan inte dela lokal resurs via länk, hoppar över",
|
||||||
"home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping",
|
"home_page_upload_err_limit": "Kan bara ladda upp max 30 resurser åt gången, hoppar över",
|
||||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
"image_viewer_page_state_provider_download_error": "Fel Vid Nedladdning",
|
||||||
"image_viewer_page_state_provider_download_started": "Download Started",
|
"image_viewer_page_state_provider_download_started": "Nedladdning Påbörjad",
|
||||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
"image_viewer_page_state_provider_download_success": "Nedladdningen Lyckades",
|
||||||
"image_viewer_page_state_provider_share_error": "Share Error",
|
"image_viewer_page_state_provider_share_error": "Delningsfel",
|
||||||
"library_page_albums": "Album",
|
"library_page_albums": "Album",
|
||||||
"library_page_archive": "Arkiv",
|
"library_page_archive": "Arkiv",
|
||||||
"library_page_device_albums": "Albums on Device",
|
"library_page_device_albums": "Album på Enheten",
|
||||||
"library_page_favorites": "Favoriter",
|
"library_page_favorites": "Favoriter",
|
||||||
"library_page_new_album": "Nytt album",
|
"library_page_new_album": "Nytt album",
|
||||||
"library_page_sharing": "Delas",
|
"library_page_sharing": "Delas",
|
||||||
"library_page_sort_asset_count": "Number of assets",
|
"library_page_sort_asset_count": "Antal resurser",
|
||||||
"library_page_sort_created": "Senast skapad",
|
"library_page_sort_created": "Senast skapad",
|
||||||
"library_page_sort_last_modified": "Last modified",
|
"library_page_sort_last_modified": "Senast ändrad",
|
||||||
"library_page_sort_most_oldest_photo": "Oldest photo",
|
"library_page_sort_most_oldest_photo": "Äldsta foto",
|
||||||
"library_page_sort_most_recent_photo": "Most recent photo",
|
"library_page_sort_most_recent_photo": "Senaste foto",
|
||||||
"library_page_sort_title": "Albumtitel",
|
"library_page_sort_title": "Albumtitel",
|
||||||
"location_picker_choose_on_map": "Choose on map",
|
"location_picker_choose_on_map": "Välj på karta",
|
||||||
"location_picker_latitude": "Latitude",
|
"location_picker_latitude": "Latitud",
|
||||||
"location_picker_latitude_error": "Enter a valid latitude",
|
"location_picker_latitude_error": "Ange en giltig latitud",
|
||||||
"location_picker_latitude_hint": "Enter your latitude here",
|
"location_picker_latitude_hint": "Ange din latitud här",
|
||||||
"location_picker_longitude": "Longitude",
|
"location_picker_longitude": "Longitud",
|
||||||
"location_picker_longitude_error": "Enter a valid longitude",
|
"location_picker_longitude_error": "Ange en giltig longitud",
|
||||||
"location_picker_longitude_hint": "Enter your longitude here",
|
"location_picker_longitude_hint": "Ange din longitud här",
|
||||||
"login_disabled": "Login has been disabled",
|
"login_disabled": "Inloggning har inaktiverats",
|
||||||
"login_form_api_exception": "API exception. Please check the server URL and try again.",
|
"login_form_api_exception": "API-undantag. Kontrollera server-URL:en och försök igen.",
|
||||||
"login_form_back_button_text": "Back",
|
"login_form_back_button_text": "Bakåt",
|
||||||
"login_form_button_text": "Logga in",
|
"login_form_button_text": "Logga in",
|
||||||
"login_form_email_hint": "din.email@email.com",
|
"login_form_email_hint": "din.email@email.com",
|
||||||
"login_form_endpoint_hint": "http://din-server-ip:port/api",
|
"login_form_endpoint_hint": "http://din-server-ip:port/api",
|
||||||
@@ -255,144 +255,145 @@
|
|||||||
"login_form_failed_get_oauth_server_config": "Kunde inte logga in med OAuth. Kontrollera serverns webbadress",
|
"login_form_failed_get_oauth_server_config": "Kunde inte logga in med OAuth. Kontrollera serverns webbadress",
|
||||||
"login_form_failed_get_oauth_server_disable": "OAuth är inte tillgänglig på den här servern",
|
"login_form_failed_get_oauth_server_disable": "OAuth är inte tillgänglig på den här servern",
|
||||||
"login_form_failed_login": "Kunde inte logga in. Kontrollera serverns webbadress, email och lösenord.",
|
"login_form_failed_login": "Kunde inte logga in. Kontrollera serverns webbadress, email och lösenord.",
|
||||||
"login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.",
|
"login_form_handshake_exception": "Ett Undantag vid Handskakning med servern har skett. Aktivera stöd för självsignerade certifikat i inställningar om du använder ett självsignerat certifikat.",
|
||||||
"login_form_label_email": "Email",
|
"login_form_label_email": "Email",
|
||||||
"login_form_label_password": "Lösenord",
|
"login_form_label_password": "Lösenord",
|
||||||
"login_form_next_button": "Nästa",
|
"login_form_next_button": "Nästa",
|
||||||
"login_form_password_hint": "lösenord",
|
"login_form_password_hint": "lösenord",
|
||||||
"login_form_save_login": "Håll mig inloggad",
|
"login_form_save_login": "Håll mig inloggad",
|
||||||
"login_form_server_empty": "Enter a server URL.",
|
"login_form_server_empty": "Ange en server-URL.",
|
||||||
"login_form_server_error": "Kunde inte ansluta till servern",
|
"login_form_server_error": "Kunde inte ansluta till servern",
|
||||||
"login_password_changed_error": "There was an error updating your password",
|
"login_password_changed_error": "Ett fel uppstod vid uppdatering av ditt lösenord",
|
||||||
"login_password_changed_success": "Password updated successfully",
|
"login_password_changed_success": "Uppdatering av lösenord lyckades",
|
||||||
"map_assets_in_bound": "{} photo",
|
"map_assets_in_bound": "{} foto",
|
||||||
"map_assets_in_bounds": "{} photos",
|
"map_assets_in_bounds": "{} foton",
|
||||||
"map_cannot_get_user_location": "Cannot get user's location",
|
"map_cannot_get_user_location": "Kan inte hämta användarens plats",
|
||||||
"map_location_dialog_cancel": "Cancel",
|
"map_location_dialog_cancel": "Avbryt",
|
||||||
"map_location_dialog_yes": "Yes",
|
"map_location_dialog_yes": "Ja",
|
||||||
"map_location_picker_page_use_location": "Use this location",
|
"map_location_picker_page_use_location": "Använd den här platsen",
|
||||||
"map_location_service_disabled_content": "Location service needs to be enabled to display assets from your current location. Do you want to enable it now?",
|
"map_location_service_disabled_content": "Platstjänst måste vara aktiverad för att visa resurser från din nuvarande plats. Vill du aktivera den nu?",
|
||||||
"map_location_service_disabled_title": "Location Service disabled",
|
"map_location_service_disabled_title": "Platstjänst inaktiverad",
|
||||||
"map_no_assets_in_bounds": "No photos in this area",
|
"map_no_assets_in_bounds": "Inga foton i området",
|
||||||
"map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?",
|
"map_no_location_permission_content": "Platsrättighet är nödvändigt för att kunna visa resurser från din nuvarande plats. Vill du tillåta det nu?",
|
||||||
"map_no_location_permission_title": "Location Permission denied",
|
"map_no_location_permission_title": "Platsrättighet nekad",
|
||||||
"map_settings_dark_mode": "Dark mode",
|
"map_settings_dark_mode": "Mörkt tema",
|
||||||
"map_settings_date_range_option_all": "All",
|
"map_settings_date_range_option_all": "Alla",
|
||||||
"map_settings_date_range_option_day": "Past 24 hours",
|
"map_settings_date_range_option_day": "Senaste 24 timmarna",
|
||||||
"map_settings_date_range_option_days": "Past {} days",
|
"map_settings_date_range_option_days": "Senaste {} dagarna",
|
||||||
"map_settings_date_range_option_year": "Past year",
|
"map_settings_date_range_option_year": "Senaste året",
|
||||||
"map_settings_date_range_option_years": "Past {} years",
|
"map_settings_date_range_option_years": "Senaste {} åren",
|
||||||
"map_settings_dialog_cancel": "Cancel",
|
"map_settings_dialog_cancel": "Avbryt",
|
||||||
"map_settings_dialog_save": "Save",
|
"map_settings_dialog_save": "Spara",
|
||||||
"map_settings_dialog_title": "Map Settings",
|
"map_settings_dialog_title": "Kartinställningar",
|
||||||
"map_settings_include_show_archived": "Include Archived",
|
"map_settings_include_show_archived": "Inkludera Arkiverade",
|
||||||
"map_settings_include_show_partners": "Include Partners",
|
"map_settings_include_show_partners": "Inkludera Partners",
|
||||||
"map_settings_only_relative_range": "Date range",
|
"map_settings_only_relative_range": "Datumintervall",
|
||||||
"map_settings_only_show_favorites": "Show Favorite Only",
|
"map_settings_only_show_favorites": "Visa Endast Favoriter",
|
||||||
"map_settings_theme_settings": "Map Theme",
|
"map_settings_theme_settings": "Kart-tema",
|
||||||
"map_zoom_to_see_photos": "Zoom out to see photos",
|
"map_zoom_to_see_photos": "Zooma ut för att se foton",
|
||||||
"memories_all_caught_up": "All caught up",
|
"memories_all_caught_up": "Du är ikapp",
|
||||||
"memories_check_back_tomorrow": "Check back tomorrow for more memories",
|
"memories_check_back_tomorrow": "Kom tillbaka imorgon för fler minnen",
|
||||||
"memories_start_over": "Start Over",
|
"memories_start_over": "Börja Om",
|
||||||
"memories_swipe_to_close": "Swipe up to close",
|
"memories_swipe_to_close": "Svep upp för att stänga",
|
||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
"motion_photos_page_title": "Motion Photos",
|
"motion_photos_page_title": "Rörelsefoton",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping",
|
"multiselect_grid_edit_date_time_err_read_only": "Kan inte ändra datum på skrivskyddade resurser, hoppar över",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping",
|
"multiselect_grid_edit_gps_err_read_only": "Kan inte ändra plats på skrivskyddade resurser, hoppar över",
|
||||||
"no_assets_to_show": "No assets to show",
|
"no_assets_to_show": "Inga resurser att visa",
|
||||||
"notification_permission_dialog_cancel": "Avbryt",
|
"notification_permission_dialog_cancel": "Avbryt",
|
||||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
"notification_permission_dialog_content": "För att aktivera notiser, gå till Inställningar och välj tillåt",
|
||||||
"notification_permission_dialog_settings": "Inställningar",
|
"notification_permission_dialog_settings": "Inställningar",
|
||||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
"notification_permission_list_tile_content": "Tillåt rättighet för att slå på notiser.",
|
||||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
"notification_permission_list_tile_enable_button": "Aktivera Notiser",
|
||||||
"notification_permission_list_tile_title": "Notification Permission",
|
"notification_permission_list_tile_title": "Notisrättighet",
|
||||||
"partner_list_user_photos": "{user}'s photos",
|
"partner_list_user_photos": "{user}s foton",
|
||||||
"partner_list_view_all": "View all",
|
"partner_list_view_all": "Visa alla",
|
||||||
"partner_page_add_partner": "Lägg till partner",
|
"partner_page_add_partner": "Lägg till partner",
|
||||||
"partner_page_empty_message": "Your photos are not yet shared with any partner.",
|
"partner_page_empty_message": "Dina foton är inte ännu delade med någon partner.",
|
||||||
"partner_page_no_more_users": "No more users to add",
|
"partner_page_no_more_users": "Inga fler användare att lägga till",
|
||||||
"partner_page_partner_add_failed": "Failed to add partner",
|
"partner_page_partner_add_failed": "Misslyckades med att lägga till partner",
|
||||||
"partner_page_select_partner": "Select partner",
|
"partner_page_select_partner": "Välj partner",
|
||||||
"partner_page_shared_to_title": "Shared to",
|
"partner_page_shared_to_title": "Delad till",
|
||||||
"partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
|
"partner_page_stop_sharing_content": "{} kommer inte längre att komma åt dina foton.",
|
||||||
"partner_page_stop_sharing_title": "Sluta dela dina foton?",
|
"partner_page_stop_sharing_title": "Sluta dela dina foton?",
|
||||||
"partner_page_title": "Partner",
|
"partner_page_title": "Partner",
|
||||||
"permission_onboarding_back": "Back",
|
"permission_onboarding_back": "Bakåt",
|
||||||
"permission_onboarding_continue_anyway": "Continue anyway",
|
"permission_onboarding_continue_anyway": "Fortsätt ändå",
|
||||||
"permission_onboarding_get_started": "Kom igång",
|
"permission_onboarding_get_started": "Kom igång",
|
||||||
"permission_onboarding_go_to_settings": "Gå till inställningar",
|
"permission_onboarding_go_to_settings": "Gå till inställningar",
|
||||||
"permission_onboarding_grant_permission": "Tillåt",
|
"permission_onboarding_grant_permission": "Tillåt",
|
||||||
"permission_onboarding_log_out": "Logga ut",
|
"permission_onboarding_log_out": "Logga ut",
|
||||||
"permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.",
|
"permission_onboarding_permission_denied": "Rättighet nekad. För att använda Immich, tillåt foto- och video-rättigheter i Inställningar.",
|
||||||
"permission_onboarding_permission_granted": "Permission granted! You are all set.",
|
"permission_onboarding_permission_granted": "Rättigheten beviljad! Du är klar.",
|
||||||
"permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.",
|
"permission_onboarding_permission_limited": "Rättighet begränsad. För att låta Immich säkerhetskopiera och hantera hela ditt galleri, tillåt foto- och video-rättigheter i Inställningar.",
|
||||||
"permission_onboarding_request": "Immich kräver tillstånd för att se dina foton och videor.",
|
"permission_onboarding_request": "Immich kräver tillstånd för att se dina foton och videor.",
|
||||||
"preferences_settings_title": "Preferences",
|
"preferences_settings_title": "Inställningar",
|
||||||
"profile_drawer_app_logs": "Loggar",
|
"profile_drawer_app_logs": "Loggar",
|
||||||
"profile_drawer_client_out_of_date_major": "Mobile App is out of date. Please update to the latest major version.",
|
"profile_drawer_client_out_of_date_major": "Mobilappen är utdaterad. Uppdatera till senaste huvudversionen.",
|
||||||
"profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.",
|
"profile_drawer_client_out_of_date_minor": "Mobilappen är utdaterad. Uppdatera till senaste mindre versionen.",
|
||||||
"profile_drawer_client_server_up_to_date": "Klient och server är uppdaterade",
|
"profile_drawer_client_server_up_to_date": "Klient och server är uppdaterade",
|
||||||
"profile_drawer_documentation": "Documentation",
|
"profile_drawer_documentation": "Dokumentation",
|
||||||
"profile_drawer_github": "GitHub",
|
"profile_drawer_github": "GitHub",
|
||||||
"profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.",
|
"profile_drawer_server_out_of_date_major": "Servern är utdaterad. Uppdatera till senaste huvudversionen.",
|
||||||
"profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.",
|
"profile_drawer_server_out_of_date_minor": "Servern är utdaterad. Uppdatera till senaste mindre versionen.",
|
||||||
"profile_drawer_settings": "Inställningar",
|
"profile_drawer_settings": "Inställningar",
|
||||||
"profile_drawer_sign_out": "Logga ut",
|
"profile_drawer_sign_out": "Logga ut",
|
||||||
"profile_drawer_trash": "Trash",
|
"profile_drawer_trash": "Papperskorg",
|
||||||
"recently_added_page_title": "Nyligen tillagda",
|
"recently_added_page_title": "Nyligen tillagda",
|
||||||
"scaffold_body_error_occurred": "Error occurred",
|
"scaffold_body_error_occurred": "Fel uppstod",
|
||||||
"search_bar_hint": "Sök bland dina foton",
|
"search_bar_hint": "Sök bland dina foton",
|
||||||
"search_filter_apply": "Apply filter",
|
"search_filter_apply": "Aktivera filter",
|
||||||
"search_filter_camera_make": "Make",
|
"search_filter_camera_make": "Tillverkare",
|
||||||
"search_filter_camera_model": "Model",
|
"search_filter_camera_model": "Modell",
|
||||||
"search_filter_display_option_archive": "Archive",
|
"search_filter_display_option_archive": "Arkiv",
|
||||||
"search_filter_display_option_favorite": "Favorite",
|
"search_filter_display_option_favorite": "Favorit",
|
||||||
"search_filter_display_option_not_in_album": "Not in album",
|
"search_filter_display_option_not_in_album": "Ej i album",
|
||||||
"search_filter_location_city": "City",
|
"search_filter_location_city": "Stad",
|
||||||
"search_filter_location_country": "Country",
|
"search_filter_location_country": "Land",
|
||||||
"search_filter_location_state": "State",
|
"search_filter_location_state": "Stat",
|
||||||
"search_filter_media_type_all": "All",
|
"search_filter_media_type_all": "Alla",
|
||||||
"search_filter_media_type_image": "Image",
|
"search_filter_media_type_image": "Bild",
|
||||||
"search_filter_media_type_video": "Video",
|
"search_filter_media_type_video": "Videor",
|
||||||
"search_page_categories": "Kategorier",
|
"search_page_categories": "Kategorier",
|
||||||
"search_page_favorites": "Favoriter",
|
"search_page_favorites": "Favoriter",
|
||||||
"search_page_motion_photos": "Motion Photos",
|
"search_page_motion_photos": "Rörelsefoton",
|
||||||
"search_page_no_objects": "Inga objekt är tillgängliga",
|
"search_page_no_objects": "Inga objekt är tillgängliga",
|
||||||
"search_page_no_places": "Ingen platsinformation finns tillgänglig",
|
"search_page_no_places": "Ingen platsinformation finns tillgänglig",
|
||||||
"search_page_people": "People",
|
"search_page_people": "Personer",
|
||||||
"search_page_person_add_name_dialog_cancel": "Cancel",
|
"search_page_person_add_name_dialog_cancel": "Avbryt",
|
||||||
"search_page_person_add_name_dialog_hint": "Name",
|
"search_page_person_add_name_dialog_hint": "Namn",
|
||||||
"search_page_person_add_name_dialog_save": "Save",
|
"search_page_person_add_name_dialog_save": "Spara",
|
||||||
"search_page_person_add_name_dialog_title": "Add a name",
|
"search_page_person_add_name_dialog_title": "Lägg till ett namn",
|
||||||
"search_page_person_add_name_subtitle": "Find them fast by name with search",
|
"search_page_person_add_name_subtitle": "Hitta dem snabbt på namn med sök",
|
||||||
"search_page_person_add_name_title": "Add a name",
|
"search_page_person_add_name_title": "Lägg till ett namn",
|
||||||
"search_page_person_edit_name": "Edit name",
|
"search_page_person_edit_name": "Redigera namn",
|
||||||
"search_page_places": "Platser",
|
"search_page_places": "Platser",
|
||||||
"search_page_recently_added": "Nyligen tillagda",
|
"search_page_recently_added": "Nyligen tillagda",
|
||||||
"search_page_screenshots": "Screenshots",
|
"search_page_screenshots": "Skärmdumpar",
|
||||||
"search_page_selfies": "Selfies",
|
"search_page_selfies": "Selfies",
|
||||||
"search_page_things": "Saker",
|
"search_page_things": "Saker",
|
||||||
"search_page_videos": "Videos",
|
"search_page_videos": "Videor",
|
||||||
"search_page_view_all_button": "Visa alla",
|
"search_page_view_all_button": "Visa alla",
|
||||||
"search_page_your_activity": "Dina aktiviteter",
|
"search_page_your_activity": "Dina aktiviteter",
|
||||||
"search_page_your_map": "Your Map",
|
"search_page_your_map": "Din Karta",
|
||||||
"search_result_page_new_search_hint": "Ny sökning",
|
"search_result_page_new_search_hint": "Ny sökning",
|
||||||
"search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ",
|
"search_suggestion_list_smart_search_hint_1": "Smartsök är aktiverat som standard, för att söka efter metadata, använd syntaxen",
|
||||||
"search_suggestion_list_smart_search_hint_2": "m:ditt-sökord",
|
"search_suggestion_list_smart_search_hint_2": "m:ditt-sökord",
|
||||||
"select_additional_user_for_sharing_page_suggestions": "Förslag",
|
"select_additional_user_for_sharing_page_suggestions": "Förslag",
|
||||||
"select_user_for_sharing_page_err_album": "Kunde inte skapa nytt album",
|
"select_user_for_sharing_page_err_album": "Kunde inte skapa nytt album",
|
||||||
"select_user_for_sharing_page_share_suggestions": "Förslag",
|
"select_user_for_sharing_page_share_suggestions": "Förslag",
|
||||||
"server_info_box_app_version": "App version",
|
"server_info_box_app_version": "App version",
|
||||||
"server_info_box_latest_release": "Latest Version",
|
"server_info_box_latest_release": "Senaste Version",
|
||||||
"server_info_box_server_url": "Server URL",
|
"server_info_box_server_url": "Server-URL",
|
||||||
"server_info_box_server_version": "Server version",
|
"server_info_box_server_version": "Server version",
|
||||||
"setting_image_viewer_help": "Detaljerad vy laddar miniatyrer först. Efter detta laddas den medelstora förhandsgranskningen av bilden (om detta är aktiverat), och visar slutligen originalet (om detta är aktiverat).",
|
"setting_image_viewer_help": "Detaljerad vy laddar miniatyrer först. Efter detta laddas den medelstora förhandsgranskningen av bilden (om detta är aktiverat), och visar slutligen originalet (om detta är aktiverat).",
|
||||||
"setting_image_viewer_original_subtitle": "Aktivera för att ladda originalbilden i full storlek (stor!). Inaktivera för att minska dataanvändningen (både i nätverket och för enhetscache).",
|
"setting_image_viewer_original_subtitle": "Aktivera för att ladda originalbilden i full storlek (stor!). Inaktivera för att minska dataanvändningen (både i nätverket och för enhetscache).",
|
||||||
"setting_image_viewer_original_title": "Ladda originalbilden",
|
"setting_image_viewer_original_title": "Ladda originalbilden",
|
||||||
"setting_image_viewer_preview_subtitle": "Aktivera för att ladda en mellanstor bild. Stäng av för att antingen ladda originalet direkt eller bara använda miniatyrbilden.",
|
"setting_image_viewer_preview_subtitle": "Aktivera för att ladda en mellanstor bild. Stäng av för att antingen ladda originalet direkt eller bara använda miniatyrbilden.",
|
||||||
"setting_image_viewer_preview_title": "Ladda förhandsgranskning av bild",
|
"setting_image_viewer_preview_title": "Ladda förhandsgranskning av bild",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_image_viewer_title": "Bilder",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_apply": "Verkställ",
|
||||||
|
"setting_languages_title": "Språk",
|
||||||
"setting_notifications_notify_failures_grace_period": "Rapportera säkerhetskopieringsfel i bakgrunden: {}",
|
"setting_notifications_notify_failures_grace_period": "Rapportera säkerhetskopieringsfel i bakgrunden: {}",
|
||||||
"setting_notifications_notify_hours": "{} timmar",
|
"setting_notifications_notify_hours": "{} timmar",
|
||||||
"setting_notifications_notify_immediately": "omedelbart",
|
"setting_notifications_notify_immediately": "omedelbart",
|
||||||
@@ -407,75 +408,78 @@
|
|||||||
"setting_notifications_total_progress_title": "Visa totalt uppladdningsförlopp",
|
"setting_notifications_total_progress_title": "Visa totalt uppladdningsförlopp",
|
||||||
"setting_pages_app_bar_settings": "Inställningar",
|
"setting_pages_app_bar_settings": "Inställningar",
|
||||||
"settings_require_restart": "Starta om Immich för att tillämpa den här inställningen",
|
"settings_require_restart": "Starta om Immich för att tillämpa den här inställningen",
|
||||||
|
"setting_video_viewer_looping_subtitle": "Aktivera för att automatiskt loopa en video i detaljvisaren.",
|
||||||
|
"setting_video_viewer_looping_title": "Loopar",
|
||||||
|
"setting_video_viewer_title": "Videor",
|
||||||
"share_add": "Lägg till",
|
"share_add": "Lägg till",
|
||||||
"share_add_photos": "Lägg till foton",
|
"share_add_photos": "Lägg till foton",
|
||||||
"share_add_title": "Lägg till en titel",
|
"share_add_title": "Lägg till en titel",
|
||||||
"share_assets_selected": "{} selected",
|
"share_assets_selected": "{} valda",
|
||||||
"share_create_album": "Skapa album",
|
"share_create_album": "Skapa album",
|
||||||
"shared_album_activities_input_disable": "Comment is disabled",
|
"shared_album_activities_input_disable": "Kommentar är inaktiverad",
|
||||||
"shared_album_activities_input_hint": "Say something",
|
"shared_album_activities_input_hint": "Säg något",
|
||||||
"shared_album_activity_remove_content": "Do you want to delete this activity?",
|
"shared_album_activity_remove_content": "Vill du ta bort den här aktiviteten?",
|
||||||
"shared_album_activity_remove_title": "Delete Activity",
|
"shared_album_activity_remove_title": "Ta Bort Aktivitet",
|
||||||
"shared_album_activity_setting_subtitle": "Let others respond",
|
"shared_album_activity_setting_subtitle": "Låt andra svara",
|
||||||
"shared_album_activity_setting_title": "Comments & likes",
|
"shared_album_activity_setting_title": "Kommentarer & gillamarkeringar",
|
||||||
"shared_album_section_people_action_error": "Error leaving/removing from album",
|
"shared_album_section_people_action_error": "Fel vid lämnande/borttagning från album",
|
||||||
"shared_album_section_people_action_leave": "Remove user from album",
|
"shared_album_section_people_action_leave": "Ta bort användare från album",
|
||||||
"shared_album_section_people_action_remove_user": "Remove user from album",
|
"shared_album_section_people_action_remove_user": "Ta bort användare från album",
|
||||||
"shared_album_section_people_owner_label": "Owner",
|
"shared_album_section_people_owner_label": "Ägare",
|
||||||
"shared_album_section_people_title": "PEOPLE",
|
"shared_album_section_people_title": "PERSONER",
|
||||||
"share_dialog_preparing": "Förbereder...",
|
"share_dialog_preparing": "Förbereder...",
|
||||||
"shared_link_app_bar_title": "Shared Links",
|
"shared_link_app_bar_title": "Delade Länkar",
|
||||||
"shared_link_clipboard_copied_massage": "Copied to clipboard",
|
"shared_link_clipboard_copied_massage": "Kopierad till urklipp",
|
||||||
"shared_link_clipboard_text": "Link: {}\nPassword: {}",
|
"shared_link_clipboard_text": "Länk: {}\nLösenord: {}",
|
||||||
"shared_link_create_app_bar_title": "Create link to share",
|
"shared_link_create_app_bar_title": "Skapa länk att dela",
|
||||||
"shared_link_create_error": "Error while creating shared link",
|
"shared_link_create_error": "Fel vid skapande av delad länk",
|
||||||
"shared_link_create_info": "Let anyone with the link see the selected photo(s)",
|
"shared_link_create_info": "Tillåt alla med länken att se valda foton",
|
||||||
"shared_link_create_submit_button": "Create link",
|
"shared_link_create_submit_button": "Skapa länk",
|
||||||
"shared_link_edit_allow_download": "Allow public user to download",
|
"shared_link_edit_allow_download": "Tillåt publik användare att ladda ner",
|
||||||
"shared_link_edit_allow_upload": "Allow public user to upload",
|
"shared_link_edit_allow_upload": "Tillåt publik användare att ladda upp",
|
||||||
"shared_link_edit_app_bar_title": "Edit link",
|
"shared_link_edit_app_bar_title": "Redigera länk",
|
||||||
"shared_link_edit_change_expiry": "Change expiration time",
|
"shared_link_edit_change_expiry": "Ändra utgångstid",
|
||||||
"shared_link_edit_description": "Description",
|
"shared_link_edit_description": "Beskrivning",
|
||||||
"shared_link_edit_description_hint": "Enter the share description",
|
"shared_link_edit_description_hint": "Lägg till delnings-beskrivningen",
|
||||||
"shared_link_edit_expire_after": "Expire after",
|
"shared_link_edit_expire_after": "Går ut efter",
|
||||||
"shared_link_edit_expire_after_option_day": "1 day",
|
"shared_link_edit_expire_after_option_day": "1 dag",
|
||||||
"shared_link_edit_expire_after_option_days": "{} days",
|
"shared_link_edit_expire_after_option_days": "{} dagar",
|
||||||
"shared_link_edit_expire_after_option_hour": "1 hour",
|
"shared_link_edit_expire_after_option_hour": "1 timme",
|
||||||
"shared_link_edit_expire_after_option_hours": "{} hours",
|
"shared_link_edit_expire_after_option_hours": "{} timmar",
|
||||||
"shared_link_edit_expire_after_option_minute": "1 minute",
|
"shared_link_edit_expire_after_option_minute": "1 minut",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minutes",
|
"shared_link_edit_expire_after_option_minutes": "{} minuter",
|
||||||
"shared_link_edit_expire_after_option_months": "{} months",
|
"shared_link_edit_expire_after_option_months": "{} månader",
|
||||||
"shared_link_edit_expire_after_option_never": "Never",
|
"shared_link_edit_expire_after_option_never": "Aldrig",
|
||||||
"shared_link_edit_expire_after_option_year": "{} year",
|
"shared_link_edit_expire_after_option_year": "{} år",
|
||||||
"shared_link_edit_password": "Password",
|
"shared_link_edit_password": "Lösenord",
|
||||||
"shared_link_edit_password_hint": "Enter the share password",
|
"shared_link_edit_password_hint": "Ange delningslösenordet",
|
||||||
"shared_link_edit_show_meta": "Show metadata",
|
"shared_link_edit_show_meta": "Visa metadata",
|
||||||
"shared_link_edit_submit_button": "Update link",
|
"shared_link_edit_submit_button": "Uppdatera länk",
|
||||||
"shared_link_empty": "You don't have any shared links",
|
"shared_link_empty": "Du har inga delade länkar",
|
||||||
"shared_link_error_server_url_fetch": "Cannot fetch the server url",
|
"shared_link_error_server_url_fetch": "Kan inte hämta server-urlen",
|
||||||
"shared_link_expired": "Expired",
|
"shared_link_expired": "Gått ut",
|
||||||
"shared_link_expires_day": "Expires in {} day",
|
"shared_link_expires_day": "Går ut om {} dag",
|
||||||
"shared_link_expires_days": "Expires in {} days",
|
"shared_link_expires_days": "Går ut om {} dagar",
|
||||||
"shared_link_expires_hour": "Expires in {} hour",
|
"shared_link_expires_hour": "Går ut om {} timme",
|
||||||
"shared_link_expires_hours": "Expires in {} hours",
|
"shared_link_expires_hours": "Går ut om {} timmar",
|
||||||
"shared_link_expires_minute": "Expires in {} minute",
|
"shared_link_expires_minute": "Går ut om {} minut",
|
||||||
"shared_link_expires_minutes": "Expires in {} minutes",
|
"shared_link_expires_minutes": "Går ut om {} minuter",
|
||||||
"shared_link_expires_never": "Expires ∞",
|
"shared_link_expires_never": "Går ut ∞",
|
||||||
"shared_link_expires_second": "Expires in {} second",
|
"shared_link_expires_second": "Går ut om {} sekunder",
|
||||||
"shared_link_expires_seconds": "Expires in {} seconds",
|
"shared_link_expires_seconds": "Går ut om {} sekunder",
|
||||||
"shared_link_individual_shared": "Individual shared",
|
"shared_link_individual_shared": "Individdelad",
|
||||||
"shared_link_info_chip_download": "Download",
|
"shared_link_info_chip_download": "Ladda ner",
|
||||||
"shared_link_info_chip_metadata": "EXIF",
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
"shared_link_info_chip_upload": "Upload",
|
"shared_link_info_chip_upload": "Ladda upp",
|
||||||
"shared_link_manage_links": "Manage Shared links",
|
"shared_link_manage_links": "Hantera Delade länkar",
|
||||||
"shared_link_public_album": "Public album",
|
"shared_link_public_album": "Publikt album",
|
||||||
"share_done": "Done",
|
"share_done": "Klart",
|
||||||
"share_invite": "Bjuder in till album",
|
"share_invite": "Bjuder in till album",
|
||||||
"sharing_page_album": "Delade album",
|
"sharing_page_album": "Delade album",
|
||||||
"sharing_page_description": "Skapa delade album för att dela foton och video med personer i ditt nätverk.",
|
"sharing_page_description": "Skapa delade album för att dela foton och video med personer i ditt nätverk.",
|
||||||
"sharing_page_empty_list": "TOM LISTA",
|
"sharing_page_empty_list": "TOM LISTA",
|
||||||
"sharing_silver_appbar_create_shared_album": "Skapa delat album",
|
"sharing_silver_appbar_create_shared_album": "Skapa delat album",
|
||||||
"sharing_silver_appbar_shared_links": "Shared links",
|
"sharing_silver_appbar_shared_links": "Delada länkar",
|
||||||
"sharing_silver_appbar_share_partner": "Dela med partner",
|
"sharing_silver_appbar_share_partner": "Dela med partner",
|
||||||
"tab_controller_nav_library": "Bibliotek",
|
"tab_controller_nav_library": "Bibliotek",
|
||||||
"tab_controller_nav_photos": "Bilder",
|
"tab_controller_nav_photos": "Bilder",
|
||||||
@@ -491,30 +495,30 @@
|
|||||||
"theme_setting_theme_title": "Tema",
|
"theme_setting_theme_title": "Tema",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Trestegsladdning kan öka prestandan, men kan också leda till signifikant högre nätverksbelastning",
|
"theme_setting_three_stage_loading_subtitle": "Trestegsladdning kan öka prestandan, men kan också leda till signifikant högre nätverksbelastning",
|
||||||
"theme_setting_three_stage_loading_title": "Aktivera trestegsladdning",
|
"theme_setting_three_stage_loading_title": "Aktivera trestegsladdning",
|
||||||
"translated_text_options": "Options",
|
"translated_text_options": "Val",
|
||||||
"trash_page_delete": "Delete",
|
"trash_page_delete": "Ta Bort",
|
||||||
"trash_page_delete_all": "Delete All",
|
"trash_page_delete_all": "Ta Bort Alla",
|
||||||
"trash_page_empty_trash_btn": "Empty trash",
|
"trash_page_empty_trash_btn": "Töm papperskorg",
|
||||||
"trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich",
|
"trash_page_empty_trash_dialog_content": "Vill du ta bort dina slängda resurser? De kommer att tas bort permanent från Immich",
|
||||||
"trash_page_empty_trash_dialog_ok": "Ok",
|
"trash_page_empty_trash_dialog_ok": "Ok",
|
||||||
"trash_page_info": "Trashed items will be permanently deleted after {} days",
|
"trash_page_info": "Saker i papperskorgen tas bort permanent efter {} dagar",
|
||||||
"trash_page_no_assets": "No trashed assets",
|
"trash_page_no_assets": "Inga slängda resurser",
|
||||||
"trash_page_restore": "Restore",
|
"trash_page_restore": "Återställ",
|
||||||
"trash_page_restore_all": "Restore All",
|
"trash_page_restore_all": "Återställ Alla",
|
||||||
"trash_page_select_assets_btn": "Select assets",
|
"trash_page_select_assets_btn": "Välj resurser",
|
||||||
"trash_page_select_btn": "Select",
|
"trash_page_select_btn": "Välj",
|
||||||
"trash_page_title": "Trash ({})",
|
"trash_page_title": "Papperskorg ({})",
|
||||||
"upload_dialog_cancel": "Cancel",
|
"upload_dialog_cancel": "Avbryt",
|
||||||
"upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?",
|
"upload_dialog_info": "Vill du säkerhetskopiera de valda resurserna till servern?",
|
||||||
"upload_dialog_ok": "Upload",
|
"upload_dialog_ok": "Ladda Upp",
|
||||||
"upload_dialog_title": "Upload Asset",
|
"upload_dialog_title": "Ladda Upp Resurs",
|
||||||
"version_announcement_overlay_ack": "Bekräfta",
|
"version_announcement_overlay_ack": "Bekräfta",
|
||||||
"version_announcement_overlay_release_notes": "versionsinformation",
|
"version_announcement_overlay_release_notes": "versionsinformation",
|
||||||
"version_announcement_overlay_text_1": "Hej vännen, det finns en ny version av",
|
"version_announcement_overlay_text_1": "Hej vännen, det finns en ny version av",
|
||||||
"version_announcement_overlay_text_2": ". Ta gärna din tid att besöka ",
|
"version_announcement_overlay_text_2": ". Ta gärna din tid att besöka ",
|
||||||
"version_announcement_overlay_text_3": " för att se till att din docker-compose och .env-fil är uppdaterad för att undvika felkonfiguration, speciellt om du använder WatchTower eller liknande mekanism som automatiskt uppdaterar din container",
|
"version_announcement_overlay_text_3": " för att se till att din docker-compose och .env-fil är uppdaterad för att undvika felkonfiguration, speciellt om du använder WatchTower eller liknande mekanism som automatiskt uppdaterar din container",
|
||||||
"version_announcement_overlay_title": "Ny serverversion är tillgänglig \uD83C\uDF89",
|
"version_announcement_overlay_title": "Ny serverversion finns tillgänglig \uD83C\uDF89",
|
||||||
"viewer_remove_from_stack": "Remove from Stack",
|
"viewer_remove_from_stack": "Ta bort från Stapeln",
|
||||||
"viewer_stack_use_as_main_asset": "Use as Main Asset",
|
"viewer_stack_use_as_main_asset": "Använd som Huvudresurs",
|
||||||
"viewer_unstack": "Un-Stack"
|
"viewer_unstack": "Stapla Av"
|
||||||
}
|
}
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
"common_add_to_album": "เพิ่มเข้าอัลบั้ม",
|
"common_add_to_album": "เพิ่มเข้าอัลบั้ม",
|
||||||
"common_change_password": "เปลี่ยนรหัสผ่าน",
|
"common_change_password": "เปลี่ยนรหัสผ่าน",
|
||||||
"common_create_new_album": "สร้างอัลบั้มใหม่",
|
"common_create_new_album": "สร้างอัลบั้มใหม่",
|
||||||
"common_server_error": "กรุณาตรวจสอบการเชื่อมต่ออินเทอร์เน็ต ให้แน่ใจว่าเซิร์ฟเวอร์สามารถเข้าถึงได้ และเวอร์ชั่นแอพกับเซิร์ฟเวอร์เข้ากันได้",
|
"common_server_error": "กรุณาตรวจสอบการเชื่อมต่ออินเทอร์เน็ต ให้แน่ใจว่าเซิร์ฟเวอร์สามารถเข้าถึงได้ และเวอร์ชันแอพกับเซิร์ฟเวอร์เข้ากันได้",
|
||||||
"common_shared": "แชร์",
|
"common_shared": "แชร์",
|
||||||
"control_bottom_app_bar_add_to_album": "เพิ่มลงอัลบั้ม",
|
"control_bottom_app_bar_add_to_album": "เพิ่มลงอัลบั้ม",
|
||||||
"control_bottom_app_bar_album_info": "{} รายการ",
|
"control_bottom_app_bar_album_info": "{} รายการ",
|
||||||
@@ -201,8 +201,8 @@
|
|||||||
"experimental_settings_title": "ทดลอง",
|
"experimental_settings_title": "ทดลอง",
|
||||||
"favorites_page_no_favorites": "ไม่พบทรัพยากรในรายการโปรด",
|
"favorites_page_no_favorites": "ไม่พบทรัพยากรในรายการโปรด",
|
||||||
"favorites_page_title": "รายการโปรด",
|
"favorites_page_title": "รายการโปรด",
|
||||||
"haptic_feedback_switch": "Enable haptic feedback",
|
"haptic_feedback_switch": "เปิดการตอบสนองแบบสัมผัส",
|
||||||
"haptic_feedback_title": "Haptic Feedback",
|
"haptic_feedback_title": "การตอบสนองแบบสัมผัส",
|
||||||
"home_page_add_to_album_conflicts": "เพิ่ม {added} ทรัพยากรเข้าอัลบั้ม {album}. {failed} ทรัพยากรอยู่ในอัลบั้มอยู่แล้ว",
|
"home_page_add_to_album_conflicts": "เพิ่ม {added} ทรัพยากรเข้าอัลบั้ม {album}. {failed} ทรัพยากรอยู่ในอัลบั้มอยู่แล้ว",
|
||||||
"home_page_add_to_album_err_local": " ไม่สามารถเพิ่มทรัพยากรบนเครื่องเข้าอัลบั้ม กำลังข้าม",
|
"home_page_add_to_album_err_local": " ไม่สามารถเพิ่มทรัพยากรบนเครื่องเข้าอัลบั้ม กำลังข้าม",
|
||||||
"home_page_add_to_album_success": "Added {added} assets to album {album}.",
|
"home_page_add_to_album_success": "Added {added} assets to album {album}.",
|
||||||
@@ -218,7 +218,7 @@
|
|||||||
"home_page_share_err_local": "ไม่สามารถแชร์ผ่านลิงค์ได้ กำลังข้าม",
|
"home_page_share_err_local": "ไม่สามารถแชร์ผ่านลิงค์ได้ กำลังข้าม",
|
||||||
"home_page_upload_err_limit": "สามารถอัพโหลดได้มากสุดครั้งละ 30 ทรัพยากร กำลังข้าม",
|
"home_page_upload_err_limit": "สามารถอัพโหลดได้มากสุดครั้งละ 30 ทรัพยากร กำลังข้าม",
|
||||||
"image_viewer_page_state_provider_download_error": "ดาวน์โหลดผิดพลาด",
|
"image_viewer_page_state_provider_download_error": "ดาวน์โหลดผิดพลาด",
|
||||||
"image_viewer_page_state_provider_download_started": "Download Started",
|
"image_viewer_page_state_provider_download_started": "ดาวน์โหลดเริ่มต้น",
|
||||||
"image_viewer_page_state_provider_download_success": "ดาวน์โหลดสำเร็จ",
|
"image_viewer_page_state_provider_download_success": "ดาวน์โหลดสำเร็จ",
|
||||||
"image_viewer_page_state_provider_share_error": "แชร์ผิดพลาด",
|
"image_viewer_page_state_provider_share_error": "แชร์ผิดพลาด",
|
||||||
"library_page_albums": "Albums",
|
"library_page_albums": "Albums",
|
||||||
@@ -265,8 +265,8 @@
|
|||||||
"login_form_server_error": "ไม่สามารถติดต่อกับเซิร์ฟเวอร์",
|
"login_form_server_error": "ไม่สามารถติดต่อกับเซิร์ฟเวอร์",
|
||||||
"login_password_changed_error": "เกิดข้อผิดพลาดขณะเปลี่ยนรหัสผ่าน",
|
"login_password_changed_error": "เกิดข้อผิดพลาดขณะเปลี่ยนรหัสผ่าน",
|
||||||
"login_password_changed_success": "เปลี่ยนรหัสผ่านสำเร็จ",
|
"login_password_changed_success": "เปลี่ยนรหัสผ่านสำเร็จ",
|
||||||
"map_assets_in_bound": "{} photo",
|
"map_assets_in_bound": "{} รูปภาพ",
|
||||||
"map_assets_in_bounds": "{} photos",
|
"map_assets_in_bounds": "{} รูปภาพ",
|
||||||
"map_cannot_get_user_location": "ไม่สามารถหาตำแหน่งผู้ใช้งานได้",
|
"map_cannot_get_user_location": "ไม่สามารถหาตำแหน่งผู้ใช้งานได้",
|
||||||
"map_location_dialog_cancel": "ยกเลิก",
|
"map_location_dialog_cancel": "ยกเลิก",
|
||||||
"map_location_dialog_yes": "ใช่",
|
"map_location_dialog_yes": "ใช่",
|
||||||
@@ -298,8 +298,8 @@
|
|||||||
"monthly_title_text_date_format": "MMMM y",
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
"motion_photos_page_title": "ภาพเคลื่อนไหว",
|
"motion_photos_page_title": "ภาพเคลื่อนไหว",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping",
|
"multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping",
|
"multiselect_grid_edit_gps_err_read_only": "ไม่สามารถแก้",
|
||||||
"no_assets_to_show": "No assets to show",
|
"no_assets_to_show": "ไม่มีทรัพยากรให้แสดง",
|
||||||
"notification_permission_dialog_cancel": "ยกเลิก",
|
"notification_permission_dialog_cancel": "ยกเลิก",
|
||||||
"notification_permission_dialog_content": "เพื่อเปิดการแจ้งเตือน เข้าตั้งค่าแล้วกดอนุญาต",
|
"notification_permission_dialog_content": "เพื่อเปิดการแจ้งเตือน เข้าตั้งค่าแล้วกดอนุญาต",
|
||||||
"notification_permission_dialog_settings": "ตั้งค่า",
|
"notification_permission_dialog_settings": "ตั้งค่า",
|
||||||
@@ -382,17 +382,18 @@
|
|||||||
"select_additional_user_for_sharing_page_suggestions": "ข้อเสนอแนะ",
|
"select_additional_user_for_sharing_page_suggestions": "ข้อเสนอแนะ",
|
||||||
"select_user_for_sharing_page_err_album": "สร้างอัลบั้มล้มเหลว",
|
"select_user_for_sharing_page_err_album": "สร้างอัลบั้มล้มเหลว",
|
||||||
"select_user_for_sharing_page_share_suggestions": "Suggestions",
|
"select_user_for_sharing_page_share_suggestions": "Suggestions",
|
||||||
"server_info_box_app_version": "เวอร์ชั่นแอพ",
|
"server_info_box_app_version": "เวอร์ชันแอพ",
|
||||||
"server_info_box_latest_release": "เวอร์ชันล่าสุด",
|
"server_info_box_latest_release": "เวอร์ชันล่าสุด",
|
||||||
"server_info_box_server_url": "URL เซิร์ฟเวอร์",
|
"server_info_box_server_url": "URL เซิร์ฟเวอร์",
|
||||||
"server_info_box_server_version": "เวอร์ชั้นเซิร์ฟเวอร์",
|
"server_info_box_server_version": "เวอร์ชันเซิร์ฟเวอร์",
|
||||||
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
||||||
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
|
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
|
||||||
"setting_image_viewer_original_title": "โหลดรูปต้นฉบับ",
|
"setting_image_viewer_original_title": "โหลดรูปต้นฉบับ",
|
||||||
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
||||||
"setting_image_viewer_preview_title": "โหลดรูปภาพตัวอย่าง",
|
"setting_image_viewer_preview_title": "โหลดรูปภาพตัวอย่าง",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_image_viewer_title": "รูปภาพ",
|
||||||
"setting_languages_title": "Languages",
|
"setting_languages_apply": "บันทึก",
|
||||||
|
"setting_languages_title": "ภาษา",
|
||||||
"setting_notifications_notify_failures_grace_period": "แจ้งการสำรองข้อมูลในเบื้องหลังล้มเหลว: {}",
|
"setting_notifications_notify_failures_grace_period": "แจ้งการสำรองข้อมูลในเบื้องหลังล้มเหลว: {}",
|
||||||
"setting_notifications_notify_hours": "{} ชั่วโมง",
|
"setting_notifications_notify_hours": "{} ชั่วโมง",
|
||||||
"setting_notifications_notify_immediately": "immediately",
|
"setting_notifications_notify_immediately": "immediately",
|
||||||
@@ -407,10 +408,13 @@
|
|||||||
"setting_notifications_total_progress_title": "แสดงสถานะการสำรองข้อมูลในเบื้องหลังทั้งหมด",
|
"setting_notifications_total_progress_title": "แสดงสถานะการสำรองข้อมูลในเบื้องหลังทั้งหมด",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Settings",
|
||||||
"settings_require_restart": "กรุณารีสตาร์ท Immmich เพื่อใช้การตั้งค่า",
|
"settings_require_restart": "กรุณารีสตาร์ท Immmich เพื่อใช้การตั้งค่า",
|
||||||
|
"setting_video_viewer_looping_subtitle": "เปิดเพื่อให้วิดีโอวนลูปในที่ดูรายละเอียด",
|
||||||
|
"setting_video_viewer_looping_title": "วนลูป",
|
||||||
|
"setting_video_viewer_title": "วิดีโอ",
|
||||||
"share_add": "เพิ่ม",
|
"share_add": "เพิ่ม",
|
||||||
"share_add_photos": "เพิ่มรูปภาพ",
|
"share_add_photos": "เพิ่มรูปภาพ",
|
||||||
"share_add_title": "เพิ่มชื่อ",
|
"share_add_title": "เพิ่มชื่อ",
|
||||||
"share_assets_selected": "{} selected",
|
"share_assets_selected": "{} ถูกเลือก",
|
||||||
"share_create_album": "สร้างอัลบั้ม",
|
"share_create_album": "สร้างอัลบั้ม",
|
||||||
"shared_album_activities_input_disable": "คอมเมนต์ถูกปิด",
|
"shared_album_activities_input_disable": "คอมเมนต์ถูกปิด",
|
||||||
"shared_album_activities_input_hint": "พูดอะไรสักอย่าง",
|
"shared_album_activities_input_hint": "พูดอะไรสักอย่าง",
|
||||||
@@ -444,9 +448,9 @@
|
|||||||
"shared_link_edit_expire_after_option_hours": "{} hours",
|
"shared_link_edit_expire_after_option_hours": "{} hours",
|
||||||
"shared_link_edit_expire_after_option_minute": "1 minute",
|
"shared_link_edit_expire_after_option_minute": "1 minute",
|
||||||
"shared_link_edit_expire_after_option_minutes": "{} minutes",
|
"shared_link_edit_expire_after_option_minutes": "{} minutes",
|
||||||
"shared_link_edit_expire_after_option_months": "{} months",
|
"shared_link_edit_expire_after_option_months": "{} เดือน",
|
||||||
"shared_link_edit_expire_after_option_never": "ไม่กำหนด",
|
"shared_link_edit_expire_after_option_never": "ไม่กำหนด",
|
||||||
"shared_link_edit_expire_after_option_year": "{} year",
|
"shared_link_edit_expire_after_option_year": "{} ปี",
|
||||||
"shared_link_edit_password": "รหัสผ่าน",
|
"shared_link_edit_password": "รหัสผ่าน",
|
||||||
"shared_link_edit_password_hint": "กรอกรหัสผ่านแชร์",
|
"shared_link_edit_password_hint": "กรอกรหัสผ่านแชร์",
|
||||||
"shared_link_edit_show_meta": "แสดง metadata",
|
"shared_link_edit_show_meta": "แสดง metadata",
|
||||||
@@ -472,7 +476,7 @@
|
|||||||
"share_done": "เสร็จ",
|
"share_done": "เสร็จ",
|
||||||
"share_invite": "เชิญเข้าอัลบั้ม",
|
"share_invite": "เชิญเข้าอัลบั้ม",
|
||||||
"sharing_page_album": "อัลบั้มที่แชร์",
|
"sharing_page_album": "อัลบั้มที่แชร์",
|
||||||
"sharing_page_description": "สร้างอัลบั้มที่แชร์เพื่อแชร์รูปภาพและวิดีโอให้กับคนบนเครื่อข่ายคุณ",
|
"sharing_page_description": "สร้างอัลบั้มที่แชร์เพื่อแชร์รูปภาพและวิดีโอให้กับคนบนเครือข่ายคุณ",
|
||||||
"sharing_page_empty_list": "รายการว่างเปล่า",
|
"sharing_page_empty_list": "รายการว่างเปล่า",
|
||||||
"sharing_silver_appbar_create_shared_album": "สร้างอัลบั้มแชร์",
|
"sharing_silver_appbar_create_shared_album": "สร้างอัลบั้มแชร์",
|
||||||
"sharing_silver_appbar_shared_links": "ลิงก์ที่แชร์",
|
"sharing_silver_appbar_shared_links": "ลิงก์ที่แชร์",
|
||||||
@@ -508,12 +512,12 @@
|
|||||||
"upload_dialog_info": "คุณต้องการอัพโหลดทรัพยากรดังกล่าวบนเซิร์ฟเวอร์หรือไม่?",
|
"upload_dialog_info": "คุณต้องการอัพโหลดทรัพยากรดังกล่าวบนเซิร์ฟเวอร์หรือไม่?",
|
||||||
"upload_dialog_ok": "อัปโหลด",
|
"upload_dialog_ok": "อัปโหลด",
|
||||||
"upload_dialog_title": "อัปโหลดทรัพยากร",
|
"upload_dialog_title": "อัปโหลดทรัพยากร",
|
||||||
"version_announcement_overlay_ack": "Acknowledge",
|
"version_announcement_overlay_ack": "รับทราบ",
|
||||||
"version_announcement_overlay_release_notes": "release notes",
|
"version_announcement_overlay_release_notes": "release notes",
|
||||||
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
||||||
"version_announcement_overlay_text_2": "please take your time to visit the ",
|
"version_announcement_overlay_text_2": "please take your time to visit the ",
|
||||||
"version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
|
"version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
|
||||||
"version_announcement_overlay_title": "มีเวอร์ชั่นใหม่สำหรับเซิร์ฟเวอร์ \uD83C\uDF89",
|
"version_announcement_overlay_title": "มีเวอร์ชันใหม่สำหรับเซิร์ฟเวอร์ \uD83C\uDF89",
|
||||||
"viewer_remove_from_stack": "เอาออกจากที่ซ้อน",
|
"viewer_remove_from_stack": "เอาออกจากที่ซ้อน",
|
||||||
"viewer_stack_use_as_main_asset": "ใช้เป็นทรัพยากรหลัก",
|
"viewer_stack_use_as_main_asset": "ใช้เป็นทรัพยากรหลัก",
|
||||||
"viewer_unstack": "หยุดซ้อน"
|
"viewer_unstack": "หยุดซ้อน"
|
||||||
|
|||||||