Compare commits
408 Commits
chore/oxli
...
drift-auth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4677ceb03c | ||
|
|
ad65e9011a | ||
|
|
977c9b96ba | ||
|
|
aa2828ab33 | ||
|
|
a36840d7cc | ||
|
|
e34f46fa0d | ||
|
|
6170a3843c | ||
|
|
563e2ab503 | ||
|
|
79157e1043 | ||
|
|
02688a2a03 | ||
|
|
3b9bfceef0 | ||
|
|
089085fcdb | ||
|
|
fc68cf4f32 | ||
|
|
0051a9bba5 | ||
|
|
f27bdf7523 | ||
|
|
c1c9f30ea4 | ||
|
|
bc8cb9b671 | ||
|
|
a675922172 | ||
|
|
2bead445bd | ||
|
|
0e1c8c2b80 | ||
|
|
0174de19dd | ||
|
|
1a35a01149 | ||
|
|
08122d6871 | ||
|
|
92384c28de | ||
|
|
ab597155fa | ||
|
|
1d9cc4ca5f | ||
|
|
05d26dc683 | ||
|
|
c91382625c | ||
|
|
c7853fbe9d | ||
|
|
1a70896113 | ||
|
|
1011cdb376 | ||
|
|
f1cac122ed | ||
|
|
3c7f0a2900 | ||
|
|
277e39ac98 | ||
|
|
b3061f1e4f | ||
|
|
250548dea6 | ||
|
|
ac44f6d1e0 | ||
|
|
ab61bcfcc8 | ||
|
|
aa344a3989 | ||
|
|
2efca67217 | ||
|
|
97daf42fd5 | ||
|
|
5548033cae | ||
|
|
637eba6e08 | ||
|
|
f16457d2f9 | ||
|
|
7b41c6348c | ||
|
|
bd92f6b12d | ||
|
|
496b0c7076 | ||
|
|
166452640d | ||
|
|
30f6dc3a6e | ||
|
|
02c423b326 | ||
|
|
df318ac641 | ||
|
|
826eaedae6 | ||
|
|
737e768212 | ||
|
|
99e5b33969 | ||
|
|
7ea8783593 | ||
|
|
4d27f187ea | ||
|
|
1dc62fce5f | ||
|
|
5fc4393e7a | ||
|
|
dee6d072fb | ||
|
|
261818ddd9 | ||
|
|
fafb88d31c | ||
|
|
f929dc0816 | ||
|
|
9e94f52b05 | ||
|
|
5d244c6fec | ||
|
|
dcfe8d5ade | ||
|
|
635f5de186 | ||
|
|
9719965caf | ||
|
|
f33e1ad94c | ||
|
|
576f681b5c | ||
|
|
493d85b021 | ||
|
|
f32d4f15b6 | ||
|
|
7bae49ebd5 | ||
|
|
2e63b9d951 | ||
|
|
137f0d48c0 | ||
|
|
53acf08263 | ||
|
|
f32cd74232 | ||
|
|
546f841b2c | ||
|
|
8491fe459d | ||
|
|
2046dcc5b4 | ||
|
|
03ff425664 | ||
|
|
055b930066 | ||
|
|
531515daf9 | ||
|
|
b256c51b6b | ||
|
|
238dc7c085 | ||
|
|
184c7390a1 | ||
|
|
649221176c | ||
|
|
eae2471ab5 | ||
|
|
bfceed15da | ||
|
|
d9891f759e | ||
|
|
32f23b8d38 | ||
|
|
743b6644e9 | ||
|
|
34620e1e9a | ||
|
|
bcb968e3d1 | ||
|
|
e73abe0762 | ||
|
|
920d7de349 | ||
|
|
351701c4d6 | ||
|
|
68f249bc03 | ||
|
|
eca54871d0 | ||
|
|
b359eea124 | ||
|
|
c18f167e29 | ||
|
|
ba262fbaa8 | ||
|
|
59e7754bdc | ||
|
|
0acbf1199a | ||
|
|
daea57f7d2 | ||
|
|
82c3165247 | ||
|
|
3a854d77ac | ||
|
|
ccd0c35ca1 | ||
|
|
5f10a4cae7 | ||
|
|
9abb95d34a | ||
|
|
805ec3e351 | ||
|
|
a97ba4862f | ||
|
|
c699df002a | ||
|
|
33c29e4305 | ||
|
|
b0098d6d23 | ||
|
|
04aab6ecce | ||
|
|
47c0dc0d7e | ||
|
|
df581cc0d5 | ||
|
|
9e48ae3052 | ||
|
|
1d19d308e2 | ||
|
|
de4217cefc | ||
|
|
617a2f146d | ||
|
|
2b07d7ac63 | ||
|
|
1cc5ca14ca | ||
|
|
a625921e8f | ||
|
|
a17bba3328 | ||
|
|
4b3a4725c6 | ||
|
|
34f0f6c813 | ||
|
|
906d14c172 | ||
|
|
d087f7c870 | ||
|
|
de345a9524 | ||
|
|
badd7ea2a9 | ||
|
|
7d8f56b483 | ||
|
|
70b73145f1 | ||
|
|
d178c52ba6 | ||
|
|
55fe67dd20 | ||
|
|
ed4c7817e7 | ||
|
|
39c95f1280 | ||
|
|
4ddd3764b4 | ||
|
|
68db17028b | ||
|
|
1f50a0075e | ||
|
|
b19884d01e | ||
|
|
feff1899ee | ||
|
|
977d6452f6 | ||
|
|
f778adea92 | ||
|
|
818bdde317 | ||
|
|
fd48a33686 | ||
|
|
a918481c0b | ||
|
|
a201665b7e | ||
|
|
2a222fcfba | ||
|
|
d902e7f87d | ||
|
|
6278fe43c0 | ||
|
|
dfe6d27bbd | ||
|
|
51ab7498e9 | ||
|
|
4db76ddcf0 | ||
|
|
d03eb87058 | ||
|
|
a556de67b0 | ||
|
|
e703685d8d | ||
|
|
172388c455 | ||
|
|
df4a27e8a7 | ||
|
|
1f9813a28e | ||
|
|
bbfff45058 | ||
|
|
87dd09d103 | ||
|
|
dd94ad17aa | ||
|
|
a87c2e82cd | ||
|
|
a11ab4c3f7 | ||
|
|
ebf2f9fd7b | ||
|
|
683af67344 | ||
|
|
d149d6fa3f | ||
|
|
8c5269c002 | ||
|
|
cf91d9bdfc | ||
|
|
5579554532 | ||
|
|
7e35e6985e | ||
|
|
56756baea2 | ||
|
|
d5923241b5 | ||
|
|
cc471806fe | ||
|
|
4ce9bce414 | ||
|
|
2f5d75ce21 | ||
|
|
fb384fe90b | ||
|
|
73733370a2 | ||
|
|
4a2cf28882 | ||
|
|
181efb9010 | ||
|
|
b00d44a00c | ||
|
|
6044663e26 | ||
|
|
484529e61e | ||
|
|
445f9174ea | ||
|
|
7855974a29 | ||
|
|
ec603a008c | ||
|
|
14276f41d8 | ||
|
|
a644cabab6 | ||
|
|
b8e67d0ef9 | ||
|
|
ca78bc91b6 | ||
|
|
f2f3db3a79 | ||
|
|
c435bdb5d3 | ||
|
|
15da0d5a71 | ||
|
|
090d87f82e | ||
|
|
25efba8fe6 | ||
|
|
83afd49f5c | ||
|
|
639ede78c2 | ||
|
|
15be3437bf | ||
|
|
f59b0bab5a | ||
|
|
fa418d778b | ||
|
|
e0c4b8df6f | ||
|
|
7f9689b4bc | ||
|
|
e6f8bfdf5e | ||
|
|
8ccca04e27 | ||
|
|
53f80393bf | ||
|
|
e5e857edc3 | ||
|
|
590f96246d | ||
|
|
38d73f2bc6 | ||
|
|
96e3b96d57 | ||
|
|
36b018e355 | ||
|
|
214ca50406 | ||
|
|
29b3981609 | ||
|
|
a068a41c06 | ||
|
|
3c6e9e1191 | ||
|
|
db0415bbcc | ||
|
|
a5c431fbf5 | ||
|
|
a3d588f6bd | ||
|
|
21f500191a | ||
|
|
5011636d95 | ||
|
|
3f330c6476 | ||
|
|
bb8755021d | ||
|
|
93f9e118ad | ||
|
|
58ca1402ed | ||
|
|
32a7087883 | ||
|
|
53020852ec | ||
|
|
181a7e115f | ||
|
|
095ace8687 | ||
|
|
4c3fcdc745 | ||
|
|
fa5f30d9ca | ||
|
|
e60bc3c304 | ||
|
|
09cbc5d3f4 | ||
|
|
a2a9797fab | ||
|
|
3d35e65f27 | ||
|
|
df76735f4a | ||
|
|
6feca56da8 | ||
|
|
97aabe466e | ||
|
|
72a53f43c8 | ||
|
|
30b4f334d8 | ||
|
|
6c6a32c63e | ||
|
|
6fed223405 | ||
|
|
3105094a3d | ||
|
|
b96c95beda | ||
|
|
926ff075a3 | ||
|
|
934649c8df | ||
|
|
a43159f4ba | ||
|
|
ea3a14ed25 | ||
|
|
24a4cba953 | ||
|
|
fda22c83b9 | ||
|
|
2a8019726c | ||
|
|
5f76cdddc7 | ||
|
|
48be10e48b | ||
|
|
6c11ef62e8 | ||
|
|
65dce58aa4 | ||
|
|
64cc7239fe | ||
|
|
5f89c2d111 | ||
|
|
4621ec5ea2 | ||
|
|
881a96cdf9 | ||
|
|
b001ba44f5 | ||
|
|
afb444c92c | ||
|
|
027c4a8b34 | ||
|
|
eca9b56847 | ||
|
|
5b0575b956 | ||
|
|
05064f87f0 | ||
|
|
522cdbac99 | ||
|
|
9240bbc6ff | ||
|
|
3751f8bc57 | ||
|
|
88b8afb8d6 | ||
|
|
2e13543d5d | ||
|
|
bcfc967d77 | ||
|
|
7d0e8f50f7 | ||
|
|
c759233d8c | ||
|
|
bfe32c2bb9 | ||
|
|
6c7b2e4b5c | ||
|
|
7edbeb2ed6 | ||
|
|
4e59a55c1d | ||
|
|
c2d7337d12 | ||
|
|
c1b82bed9b | ||
|
|
9ca31abae9 | ||
|
|
ebcf133bea | ||
|
|
1923f1a887 | ||
|
|
ce14324c97 | ||
|
|
6a309129b7 | ||
|
|
bcb1bf4692 | ||
|
|
7f89999abe | ||
|
|
813186e618 | ||
|
|
20d9204ada | ||
|
|
3a9e79a452 | ||
|
|
03966146fe | ||
|
|
ecc58a8971 | ||
|
|
c705a7b280 | ||
|
|
ef278b4fb0 | ||
|
|
4cd633dc68 | ||
|
|
a18c6fa910 | ||
|
|
90aa0dc14d | ||
|
|
ce8c80dad0 | ||
|
|
81eb98d4e5 | ||
|
|
2b03802e9c | ||
|
|
484311e9bb | ||
|
|
366539bc4c | ||
|
|
69b1331026 | ||
|
|
af30d97668 | ||
|
|
9b047d30e4 | ||
|
|
6a5597b36b | ||
|
|
c10b795e99 | ||
|
|
b606d4fe73 | ||
|
|
4c2ad44303 | ||
|
|
698d3004b4 | ||
|
|
fe4d6edbdc | ||
|
|
798debfde3 | ||
|
|
6563fa608a | ||
|
|
1a90fc8e58 | ||
|
|
c707f9cef4 | ||
|
|
6fda863c08 | ||
|
|
373b654156 | ||
|
|
a5d84ba552 | ||
|
|
1dc8fa2979 | ||
|
|
0426699f13 | ||
|
|
8154ec29df | ||
|
|
3024cd343b | ||
|
|
0b44d4b6f2 | ||
|
|
a04c6ed80d | ||
|
|
1c50e19894 | ||
|
|
e61d7f2616 | ||
|
|
a6b0869714 | ||
|
|
9c25b8ba7d | ||
|
|
3c72f489d8 | ||
|
|
1f2c779b36 | ||
|
|
5c74f634b7 | ||
|
|
ecc99bfd16 | ||
|
|
ff4d70e351 | ||
|
|
42c2389eb5 | ||
|
|
33c9f88ba4 | ||
|
|
11c469907f | ||
|
|
7c43e6c3c8 | ||
|
|
00aa385972 | ||
|
|
a5ed453929 | ||
|
|
dd8969cb7d | ||
|
|
bce4f93c90 | ||
|
|
a4c0dc5007 | ||
|
|
d233a7d97a | ||
|
|
5cdbb65d28 | ||
|
|
3434544864 | ||
|
|
269bf4b344 | ||
|
|
f9435a538b | ||
|
|
10e2ec2841 | ||
|
|
fe91b44ab9 | ||
|
|
747a72120e | ||
|
|
910661e75c | ||
|
|
c8a135a7ae | ||
|
|
08d1cf5bde | ||
|
|
3e62497fd0 | ||
|
|
a1bc862a32 | ||
|
|
75bf3aa1be | ||
|
|
38e68d16f9 | ||
|
|
caf11fbb96 | ||
|
|
f99c6feac5 | ||
|
|
5122512f19 | ||
|
|
49ed212af8 | ||
|
|
e29103b69f | ||
|
|
14b771d7c7 | ||
|
|
07aa51638c | ||
|
|
0a9a520ed2 | ||
|
|
de81006367 | ||
|
|
e0144b4ece | ||
|
|
65e8d75e82 | ||
|
|
023bcffdb8 | ||
|
|
06f1d0dc4d | ||
|
|
c6641d4859 | ||
|
|
91cbd56c1c | ||
|
|
35280b94cc | ||
|
|
4c69511225 | ||
|
|
0684a3ada4 | ||
|
|
a0f44f147b | ||
|
|
15c488ccd9 | ||
|
|
bc062da11b | ||
|
|
8038ae1e7a | ||
|
|
f28c0d912c | ||
|
|
bd70824961 | ||
|
|
749f63e4a0 | ||
|
|
db68d1af9b | ||
|
|
864fe3d0d6 | ||
|
|
00536bf074 | ||
|
|
0d3efe229d | ||
|
|
3b0a803089 | ||
|
|
bcda2c6e22 | ||
|
|
7347f64958 | ||
|
|
176d53c1b3 | ||
|
|
5fc448bc97 | ||
|
|
3d0c851636 | ||
|
|
16fcb657b7 | ||
|
|
32b57bcbfc | ||
|
|
7f56443b24 | ||
|
|
189442e9c4 | ||
|
|
523fe5bef7 | ||
|
|
77a362f0c0 | ||
|
|
5f5308631e | ||
|
|
004c2f2496 | ||
|
|
e2dfbd66c3 | ||
|
|
de756d9497 | ||
|
|
103b83d2d6 | ||
|
|
f54cfa7a5a | ||
|
|
ed5b260eeb | ||
|
|
8923d5b0a3 | ||
|
|
2f3d4e15d2 | ||
|
|
c9bcae813b | ||
|
|
bddb43e1d4 | ||
|
|
176656b5f4 |
2
.devcontainer/.gitignore
vendored
2
.devcontainer/.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
.env
|
|
||||||
library
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:7c2e711a4f7b02f32d2da16192d5e05aa7c95279be4ce889cff5df316f251c1d
|
|
||||||
FROM ${BASEIMAGE}
|
|
||||||
|
|
||||||
# Flutter SDK
|
|
||||||
# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux
|
|
||||||
ENV FLUTTER_CHANNEL="stable"
|
|
||||||
ENV FLUTTER_VERSION="3.29.3"
|
|
||||||
ENV FLUTTER_HOME=/flutter
|
|
||||||
ENV PATH=${PATH}:${FLUTTER_HOME}/bin
|
|
||||||
|
|
||||||
# Flutter SDK
|
|
||||||
RUN mkdir -p ${FLUTTER_HOME} \
|
|
||||||
&& curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \
|
|
||||||
&& tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \
|
|
||||||
&& rm flutter.tar.xz \
|
|
||||||
&& chown -R 1000:1000 ${FLUTTER_HOME}
|
|
||||||
@@ -1,26 +1,67 @@
|
|||||||
{
|
{
|
||||||
"name": "Immich",
|
"name": "Immich - Backend, Frontend and ML",
|
||||||
"service": "immich-devcontainer",
|
"service": "immich-server",
|
||||||
|
"runServices": [
|
||||||
|
"immich-server",
|
||||||
|
"redis",
|
||||||
|
"database",
|
||||||
|
"immich-machine-learning"
|
||||||
|
],
|
||||||
"dockerComposeFile": [
|
"dockerComposeFile": [
|
||||||
"docker-compose.yml",
|
"../docker/docker-compose.dev.yml",
|
||||||
"../docker/docker-compose.dev.yml"
|
"./server/container-compose-overrides.yml"
|
||||||
],
|
],
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"Dart-Code.dart-code",
|
|
||||||
"Dart-Code.flutter",
|
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"dcmdev.dcm-vscode-extension",
|
|
||||||
"esbenp.prettier-vscode",
|
"esbenp.prettier-vscode",
|
||||||
"svelte.svelte-vscode"
|
"svelte.svelte-vscode",
|
||||||
|
"ms-vscode-remote.remote-containers",
|
||||||
|
"foxundermoon.shell-format",
|
||||||
|
"timonwong.shellcheck",
|
||||||
|
"rvest.vs-code-prettier-eslint",
|
||||||
|
"bluebrown.yamlfmt",
|
||||||
|
"vkrishna04.cspell-sync",
|
||||||
|
"vitest.explorer",
|
||||||
|
"ms-playwright.playwright",
|
||||||
|
"ms-azuretools.vscode-docker"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"forwardPorts": [],
|
"forwardPorts": [3000, 9231, 9230, 2283],
|
||||||
"initializeCommand": "bash .devcontainer/scripts/initializeCommand.sh",
|
"portsAttributes": {
|
||||||
"onCreateCommand": "bash .devcontainer/scripts/onCreateCommand.sh",
|
"3000": {
|
||||||
|
"label": "Immich - Frontend HTTP",
|
||||||
|
"description": "The frontend of the Immich project",
|
||||||
|
"onAutoForward": "openBrowserOnce"
|
||||||
|
},
|
||||||
|
"2283": {
|
||||||
|
"label": "Immich - API Server - HTTP",
|
||||||
|
"description": "The API server of the Immich project"
|
||||||
|
},
|
||||||
|
"9231": {
|
||||||
|
"label": "Immich - API Server - DEBUG",
|
||||||
|
"description": "The API server of the Immich project"
|
||||||
|
},
|
||||||
|
"9230": {
|
||||||
|
"label": "Immich - Workers - DEBUG",
|
||||||
|
"description": "The workers of the Immich project"
|
||||||
|
}
|
||||||
|
},
|
||||||
"overrideCommand": true,
|
"overrideCommand": true,
|
||||||
"workspaceFolder": "/immich",
|
"workspaceFolder": "/workspaces/immich",
|
||||||
"remoteUser": "node"
|
"remoteUser": "node",
|
||||||
|
"userEnvProbe": "loginInteractiveShell",
|
||||||
|
"remoteEnv": {
|
||||||
|
// The location where your uploaded files are stored
|
||||||
|
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./library}",
|
||||||
|
// Connection secret for postgres. You should change it to a random password
|
||||||
|
// Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||||
|
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
|
||||||
|
// The database username
|
||||||
|
"DB_USERNAME": "${localEnv:DB_USERNAME:postgres}",
|
||||||
|
// The database name
|
||||||
|
"DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
services:
|
|
||||||
immich-devcontainer:
|
|
||||||
build:
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
extra_hosts:
|
|
||||||
- 'host.docker.internal:host-gateway'
|
|
||||||
volumes:
|
|
||||||
- ..:/immich:cached
|
|
||||||
34
.devcontainer/mobile/container-compose-overrides.yml
Normal file
34
.devcontainer/mobile/container-compose-overrides.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
services:
|
||||||
|
immich-server:
|
||||||
|
build:
|
||||||
|
target: dev-container-mobile
|
||||||
|
environment:
|
||||||
|
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
|
||||||
|
volumes: !override # bind mount host to /workspaces/immich
|
||||||
|
- ..:/workspaces/immich
|
||||||
|
- cli_node_modules:/workspaces/immich/cli/node_modules
|
||||||
|
- e2e_node_modules:/workspaces/immich/e2e/node_modules
|
||||||
|
- open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules
|
||||||
|
- server_node_modules:/workspaces/immich/server/node_modules
|
||||||
|
- web_node_modules:/workspaces/immich/web/node_modules
|
||||||
|
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
||||||
|
- ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
|
||||||
|
database:
|
||||||
|
volumes:
|
||||||
|
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# Node modules for each service to avoid conflicts and ensure consistent dependencies
|
||||||
|
cli_node_modules:
|
||||||
|
e2e_node_modules:
|
||||||
|
open_api_node_modules:
|
||||||
|
server_node_modules:
|
||||||
|
web_node_modules:
|
||||||
|
|
||||||
|
# UPLOAD_LOCATION must be set to a absolute path or vol-upload
|
||||||
|
vol-upload:
|
||||||
|
|
||||||
|
# DB_DATA_LOCATION must be set to a absolute path or vol-database
|
||||||
|
vol-database:
|
||||||
52
.devcontainer/mobile/devcontainer.json
Normal file
52
.devcontainer/mobile/devcontainer.json
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"name": "Immich - Mobile",
|
||||||
|
"service": "immich-server",
|
||||||
|
"runServices": [
|
||||||
|
"immich-server",
|
||||||
|
"redis",
|
||||||
|
"database",
|
||||||
|
"immich-machine-learning"
|
||||||
|
],
|
||||||
|
"dockerComposeFile": [
|
||||||
|
"../../docker/docker-compose.dev.yml",
|
||||||
|
"./container-compose-overrides.yml"
|
||||||
|
],
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"Dart-Code.dart-code",
|
||||||
|
"Dart-Code.flutter",
|
||||||
|
"dcmdev.dcm-vscode-extension",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"svelte.svelte-vscode",
|
||||||
|
"ms-vscode-remote.remote-containers",
|
||||||
|
"foxundermoon.shell-format",
|
||||||
|
"timonwong.shellcheck",
|
||||||
|
"rvest.vs-code-prettier-eslint",
|
||||||
|
"bluebrown.yamlfmt",
|
||||||
|
"vkrishna04.cspell-sync",
|
||||||
|
"vitest.explorer",
|
||||||
|
"ms-playwright.playwright",
|
||||||
|
"ms-azuretools.vscode-docker"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forwardPorts": [],
|
||||||
|
"overrideCommand": true,
|
||||||
|
"workspaceFolder": "/workspaces/immich",
|
||||||
|
"remoteUser": "node",
|
||||||
|
"userEnvProbe": "loginInteractiveShell",
|
||||||
|
"remoteEnv": {
|
||||||
|
// The location where your uploaded files are stored
|
||||||
|
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}",
|
||||||
|
// Connection secret for postgres. You should change it to a random password
|
||||||
|
// Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||||
|
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
|
||||||
|
// The database username
|
||||||
|
"DB_USERNAME": "${localEnv:DB_USERNAME:postgres}",
|
||||||
|
// The database name
|
||||||
|
"DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# If .env file does not exist, create it by copying example.env from the docker folder
|
|
||||||
if [ ! -f ".devcontainer/.env" ]; then
|
|
||||||
cp docker/example.env .devcontainer/.env
|
|
||||||
fi
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Enable multiarch for arm64 if necessary
|
|
||||||
if [ "$(dpkg --print-architecture)" = "arm64" ]; then
|
|
||||||
sudo dpkg --add-architecture amd64 && \
|
|
||||||
sudo apt-get update && \
|
|
||||||
sudo apt-get install -y --no-install-recommends \
|
|
||||||
qemu-user-static \
|
|
||||||
libc6:amd64 \
|
|
||||||
libstdc++6:amd64 \
|
|
||||||
libgcc1:amd64
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install DCM
|
|
||||||
wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg
|
|
||||||
sudo echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
|
|
||||||
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install dcm
|
|
||||||
|
|
||||||
dart --disable-analytics
|
|
||||||
|
|
||||||
# Install immich
|
|
||||||
cd /immich || exit
|
|
||||||
make install-all
|
|
||||||
80
.devcontainer/server/container-common.sh
Executable file
80
.devcontainer/server/container-common.sh
Executable file
@@ -0,0 +1,80 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
export IMMICH_PORT="${DEV_SERVER_PORT:-2283}"
|
||||||
|
export DEV_PORT="${DEV_PORT:-3000}"
|
||||||
|
|
||||||
|
# search for immich directory inside workspace.
|
||||||
|
# /workspaces/immich is the bind mount, but other directories can be mounted if runing
|
||||||
|
# Devcontainer: Clone [repository|pull request] in container volumne
|
||||||
|
WORKSPACES_DIR="/workspaces"
|
||||||
|
IMMICH_DIR="$WORKSPACES_DIR/immich"
|
||||||
|
IMMICH_DEVCONTAINER_LOG="$HOME/immich-devcontainer.log"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
# Display command on console, log with timestamp to file
|
||||||
|
echo "$*"
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >>"$IMMICH_DEVCONTAINER_LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_cmd() {
|
||||||
|
# Ensure log directory exists
|
||||||
|
mkdir -p "$(dirname "$IMMICH_DEVCONTAINER_LOG")"
|
||||||
|
|
||||||
|
log "$@"
|
||||||
|
|
||||||
|
# Execute command: display normally on console, log with timestamps to file
|
||||||
|
"$@" 2>&1 | tee >(while IFS= read -r line; do
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $line" >>"$IMMICH_DEVCONTAINER_LOG"
|
||||||
|
done)
|
||||||
|
|
||||||
|
# Preserve exit status
|
||||||
|
return "${PIPESTATUS[0]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find directories excluding /workspaces/immich
|
||||||
|
mapfile -t other_dirs < <(find "$WORKSPACES_DIR" -mindepth 1 -maxdepth 1 -type d ! -path "$IMMICH_DIR" ! -name ".*")
|
||||||
|
|
||||||
|
if [ ${#other_dirs[@]} -gt 1 ]; then
|
||||||
|
log "Error: More than one directory found in $WORKSPACES_DIR other than $IMMICH_DIR."
|
||||||
|
exit 1
|
||||||
|
elif [ ${#other_dirs[@]} -eq 1 ]; then
|
||||||
|
export IMMICH_WORKSPACE="${other_dirs[0]}"
|
||||||
|
else
|
||||||
|
export IMMICH_WORKSPACE="$IMMICH_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Found immich workspace in $IMMICH_WORKSPACE"
|
||||||
|
log ""
|
||||||
|
|
||||||
|
fix_permissions() {
|
||||||
|
|
||||||
|
log "Fixing permissions for ${IMMICH_WORKSPACE}"
|
||||||
|
|
||||||
|
run_cmd sudo find "${IMMICH_WORKSPACE}/server/upload" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres/*" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres" -exec chown node {} +
|
||||||
|
|
||||||
|
# Change ownership for directories that exist
|
||||||
|
for dir in "${IMMICH_WORKSPACE}/.vscode" \
|
||||||
|
"${IMMICH_WORKSPACE}/cli/node_modules" \
|
||||||
|
"${IMMICH_WORKSPACE}/e2e/node_modules" \
|
||||||
|
"${IMMICH_WORKSPACE}/open-api/typescript-sdk/node_modules" \
|
||||||
|
"${IMMICH_WORKSPACE}/server/node_modules" \
|
||||||
|
"${IMMICH_WORKSPACE}/server/dist" \
|
||||||
|
"${IMMICH_WORKSPACE}/web/node_modules" \
|
||||||
|
"${IMMICH_WORKSPACE}/web/dist"; do
|
||||||
|
if [ -d "$dir" ]; then
|
||||||
|
run_cmd sudo chown node -R "$dir"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log ""
|
||||||
|
}
|
||||||
|
|
||||||
|
install_dependencies() {
|
||||||
|
|
||||||
|
log "Installing dependencies"
|
||||||
|
(
|
||||||
|
cd "${IMMICH_WORKSPACE}" || exit 1
|
||||||
|
export CI=1 FROZEN=1 OFFLINE=1
|
||||||
|
run_cmd make setup-web-dev setup-server-dev
|
||||||
|
)
|
||||||
|
log ""
|
||||||
|
}
|
||||||
49
.devcontainer/server/container-compose-overrides.yml
Normal file
49
.devcontainer/server/container-compose-overrides.yml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
services:
|
||||||
|
immich-server:
|
||||||
|
build:
|
||||||
|
target: dev-container-server
|
||||||
|
env_file: !reset []
|
||||||
|
hostname: immich-dev
|
||||||
|
environment:
|
||||||
|
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
|
||||||
|
volumes: !override
|
||||||
|
- ..:/workspaces/immich
|
||||||
|
- cli_node_modules:/workspaces/immich/cli/node_modules
|
||||||
|
- e2e_node_modules:/workspaces/immich/e2e/node_modules
|
||||||
|
- open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules
|
||||||
|
- server_node_modules:/workspaces/immich/server/node_modules
|
||||||
|
- web_node_modules:/workspaces/immich/web/node_modules
|
||||||
|
- ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/usr/src/app/upload
|
||||||
|
- ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/usr/src/app/upload/upload
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
|
||||||
|
immich-web:
|
||||||
|
env_file: !reset []
|
||||||
|
|
||||||
|
immich-machine-learning:
|
||||||
|
env_file: !reset []
|
||||||
|
|
||||||
|
database:
|
||||||
|
env_file: !reset []
|
||||||
|
environment: !override
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASSWORD-postgres}
|
||||||
|
POSTGRES_USER: ${DB_USERNAME-postgres}
|
||||||
|
POSTGRES_DB: ${DB_DATABASE_NAME-immich}
|
||||||
|
POSTGRES_INITDB_ARGS: '--data-checksums'
|
||||||
|
POSTGRES_HOST_AUTH_METHOD: md5
|
||||||
|
volumes:
|
||||||
|
- ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
redis:
|
||||||
|
env_file: !reset []
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# Node modules for each service to avoid conflicts and ensure consistent dependencies
|
||||||
|
cli_node_modules:
|
||||||
|
e2e_node_modules:
|
||||||
|
open_api_node_modules:
|
||||||
|
server_node_modules:
|
||||||
|
web_node_modules:
|
||||||
|
upload1-devcontainer-volume:
|
||||||
|
upload2-devcontainer-volume:
|
||||||
|
postgres-devcontainer-volume:
|
||||||
17
.devcontainer/server/container-start-backend.sh
Executable file
17
.devcontainer/server/container-start-backend.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# shellcheck source=common.sh
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source /immich-devcontainer/container-common.sh
|
||||||
|
|
||||||
|
log "Starting Nest API Server"
|
||||||
|
log ""
|
||||||
|
cd "${IMMICH_WORKSPACE}/server" || (
|
||||||
|
log "Immich workspace not found"
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
run_cmd node ./node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch
|
||||||
|
log "Nest API Server crashed with exit code $?. Respawning in 3s ..."
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
22
.devcontainer/server/container-start-frontend.sh
Executable file
22
.devcontainer/server/container-start-frontend.sh
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# shellcheck source=common.sh
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source /immich-devcontainer/container-common.sh
|
||||||
|
|
||||||
|
log "Starting Immich Web Frontend"
|
||||||
|
log ""
|
||||||
|
cd "${IMMICH_WORKSPACE}/web" || (
|
||||||
|
log "Immich Workspace not found"
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
|
||||||
|
until curl --output /dev/null --silent --head --fail "http://127.0.0.1:${IMMICH_PORT}/api/server/config"; do
|
||||||
|
log "Waiting for api server..."
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
run_cmd node ./node_modules/.bin/vite dev --host 0.0.0.0 --port "${DEV_PORT}"
|
||||||
|
log "Web crashed with exit code $?. Respawning in 3s ..."
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
20
.devcontainer/server/container-start.sh
Executable file
20
.devcontainer/server/container-start.sh
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# shellcheck source=common.sh
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source /immich-devcontainer/container-common.sh
|
||||||
|
|
||||||
|
log "Setting up Immich dev container..."
|
||||||
|
fix_permissions
|
||||||
|
|
||||||
|
log "Installing npm dependencies (node_modules)..."
|
||||||
|
install_dependencies
|
||||||
|
|
||||||
|
log "Setup complete, please wait while backend and frontend services automatically start"
|
||||||
|
log
|
||||||
|
log "If necessary, the services may be manually started using"
|
||||||
|
log
|
||||||
|
log "$ /immich-devcontainer/container-start-backend.sh"
|
||||||
|
log "$ /immich-devcontainer/container-start-frontend.sh"
|
||||||
|
log
|
||||||
|
log "From different terminal windows, as these scripts automatically restart the server"
|
||||||
|
log "on error, and will continuously run in a loop"
|
||||||
@@ -1,33 +1,41 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
.github/
|
.github/
|
||||||
.git/
|
.git/
|
||||||
|
.env*
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
|
||||||
|
**/Dockerfile
|
||||||
|
**/node_modules/
|
||||||
|
**/.pnpm-store/
|
||||||
|
**/dist/
|
||||||
|
**/coverage/
|
||||||
|
**/build/
|
||||||
|
|
||||||
design/
|
design/
|
||||||
docker/
|
docker/
|
||||||
!docker/scripts
|
!docker/scripts
|
||||||
|
|
||||||
docs/
|
docs/
|
||||||
|
!docs/package.json
|
||||||
|
!docs/package-lock.json
|
||||||
|
|
||||||
e2e/
|
e2e/
|
||||||
|
!e2e/package.json
|
||||||
|
!e2e/package-lock.json
|
||||||
|
|
||||||
fastlane/
|
fastlane/
|
||||||
machine-learning/
|
machine-learning/
|
||||||
misc/
|
misc/
|
||||||
mobile/
|
mobile/
|
||||||
|
|
||||||
cli/coverage/
|
|
||||||
cli/dist/
|
|
||||||
cli/node_modules/
|
|
||||||
|
|
||||||
open-api/typescript-sdk/build/
|
open-api/typescript-sdk/build/
|
||||||
open-api/typescript-sdk/node_modules/
|
!open-api/typescript-sdk/package.json
|
||||||
|
!open-api/typescript-sdk/package-lock.json
|
||||||
|
|
||||||
server/coverage/
|
|
||||||
server/node_modules/
|
|
||||||
server/upload/
|
server/upload/
|
||||||
server/src/queries
|
server/src/queries
|
||||||
server/dist/
|
|
||||||
server/www/
|
server/www/
|
||||||
|
|
||||||
web/node_modules/
|
|
||||||
web/coverage/
|
|
||||||
web/.svelte-kit
|
web/.svelte-kit
|
||||||
web/build/
|
|
||||||
web/.env
|
|
||||||
|
|||||||
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -12,6 +12,12 @@ mobile/lib/**/*.drift.dart linguist-generated=true
|
|||||||
mobile/drift_schemas/main/drift_schema_*.json -diff -merge
|
mobile/drift_schemas/main/drift_schema_*.json -diff -merge
|
||||||
mobile/drift_schemas/main/drift_schema_*.json linguist-generated=true
|
mobile/drift_schemas/main/drift_schema_*.json linguist-generated=true
|
||||||
|
|
||||||
|
mobile/lib/infrastructure/repositories/db.repository.steps.dart -diff -merge
|
||||||
|
mobile/lib/infrastructure/repositories/db.repository.steps.dart linguist-generated=true
|
||||||
|
|
||||||
|
mobile/test/drift/main/generated/** -diff -merge
|
||||||
|
mobile/test/drift/main/generated/** linguist-generated=true
|
||||||
|
|
||||||
open-api/typescript-sdk/fetch-client.ts -diff -merge
|
open-api/typescript-sdk/fetch-client.ts -diff -merge
|
||||||
open-api/typescript-sdk/fetch-client.ts linguist-generated=true
|
open-api/typescript-sdk/fetch-client.ts linguist-generated=true
|
||||||
|
|
||||||
|
|||||||
2
.github/.nvmrc
vendored
2
.github/.nvmrc
vendored
@@ -1 +1 @@
|
|||||||
22.16.0
|
22.17.1
|
||||||
|
|||||||
4
.github/.prettierignore
vendored
Normal file
4
.github/.prettierignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Ignore files for PNPM, NPM and YARN
|
||||||
|
pnpm-lock.yaml
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
6
.github/package-lock.json
generated
vendored
6
.github/package-lock.json
generated
vendored
@@ -9,9 +9,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.5.3",
|
"version": "3.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
||||||
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
54
.github/workflows/build-mobile.yml
vendored
54
.github/workflows/build-mobile.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
# Skip when PR from a fork
|
# Skip when PR from a fork
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && needs.pre-job.outputs.should_run == 'true' }}
|
if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && needs.pre-job.outputs.should_run == 'true' }}
|
||||||
runs-on: macos-14
|
runs-on: mich
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
@@ -66,24 +66,40 @@ jobs:
|
|||||||
ref: ${{ inputs.ref || github.sha }}
|
ref: ${{ inputs.ref || github.sha }}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Create the Keystore
|
||||||
|
env:
|
||||||
|
KEY_JKS: ${{ secrets.KEY_JKS }}
|
||||||
|
working-directory: ./mobile
|
||||||
|
run: printf "%s" $KEY_JKS | base64 -d > android/key.jks
|
||||||
|
|
||||||
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
|
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
cache: 'gradle'
|
|
||||||
|
- name: Restore Gradle Cache
|
||||||
|
id: cache-gradle-restore
|
||||||
|
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
~/.android/sdk
|
||||||
|
mobile/android/.gradle
|
||||||
|
mobile/.dart_tool
|
||||||
|
key: build-mobile-gradle-${{ runner.os }}-main
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Create the Keystore
|
- name: Setup Android SDK
|
||||||
env:
|
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2
|
||||||
KEY_JKS: ${{ secrets.KEY_JKS }}
|
with:
|
||||||
working-directory: ./mobile
|
packages: ''
|
||||||
run: echo $KEY_JKS | base64 -d > android/key.jks
|
|
||||||
|
|
||||||
- name: Get Packages
|
- name: Get Packages
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
@@ -103,12 +119,30 @@ jobs:
|
|||||||
ALIAS: ${{ secrets.ALIAS }}
|
ALIAS: ${{ secrets.ALIAS }}
|
||||||
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||||
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
|
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
|
||||||
|
IS_MAIN: ${{ github.ref == 'refs/heads/main' }}
|
||||||
run: |
|
run: |
|
||||||
flutter build apk --release
|
if [[ $IS_MAIN == 'true' ]]; then
|
||||||
flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64
|
flutter build apk --release
|
||||||
|
flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64
|
||||||
|
else
|
||||||
|
flutter build apk --debug --split-per-abi --target-platform android-arm64
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Publish Android Artifact
|
- name: Publish Android Artifact
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: release-apk-signed
|
name: release-apk-signed
|
||||||
path: mobile/build/app/outputs/flutter-apk/*.apk
|
path: mobile/build/app/outputs/flutter-apk/*.apk
|
||||||
|
|
||||||
|
- name: Save Gradle Cache
|
||||||
|
id: cache-gradle-save
|
||||||
|
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
~/.android/sdk
|
||||||
|
mobile/android/.gradle
|
||||||
|
mobile/.dart_tool
|
||||||
|
key: ${{ steps.cache-gradle-restore.outputs.cache-primary-key }}
|
||||||
|
|||||||
7
.github/workflows/cli.yml
vendored
7
.github/workflows/cli.yml
vendored
@@ -38,6 +38,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- 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
|
||||||
@@ -67,7 +70,7 @@ jobs:
|
|||||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
@@ -96,7 +99,7 @@ jobs:
|
|||||||
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||||
with:
|
with:
|
||||||
file: cli/Dockerfile
|
file: cli/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|||||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
@@ -76,6 +76,6 @@ jobs:
|
|||||||
# ./location_of_script_within_repo/buildscript.sh
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||||
with:
|
with:
|
||||||
category: '/language:${{matrix.language}}'
|
category: '/language:${{matrix.language}}'
|
||||||
|
|||||||
8
.github/workflows/docker.yml
vendored
8
.github/workflows/docker.yml
vendored
@@ -131,7 +131,7 @@ jobs:
|
|||||||
tag-suffix: '-rocm'
|
tag-suffix: '-rocm'
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
runner-mapping: '{"linux/amd64": "mich"}'
|
runner-mapping: '{"linux/amd64": "mich"}'
|
||||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0
|
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@129aeda75a450666ce96e8bc8126652e717917a7 # multi-runner-build-workflow-0.1.1
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
actions: read
|
actions: read
|
||||||
@@ -154,7 +154,7 @@ jobs:
|
|||||||
name: Build and Push Server
|
name: Build and Push Server
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0
|
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@129aeda75a450666ce96e8bc8126652e717917a7 # multi-runner-build-workflow-0.1.1
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
actions: read
|
actions: read
|
||||||
@@ -177,7 +177,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|
||||||
@@ -188,6 +188,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|||||||
2
.github/workflows/docs-build.yml
vendored
2
.github/workflows/docs-build.yml
vendored
@@ -57,6 +57,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './docs/.nvmrc'
|
node-version-file: './docs/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
2
.github/workflows/fix-format.yml
vendored
2
.github/workflows/fix-format.yml
vendored
@@ -32,6 +32,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Fix formatting
|
- name: Fix formatting
|
||||||
run: make install-all && make format-all
|
run: make install-all && make format-all
|
||||||
|
|||||||
13
.github/workflows/org-checks.yml
vendored
Normal file
13
.github/workflows/org-checks.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
name: Org Checks
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_review:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-approvals:
|
||||||
|
name: Check for Team/Admin Review
|
||||||
|
uses: immich-app/devtools/.github/workflows/required-approval.yml@main
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
contents: read
|
||||||
2
.github/workflows/pr-label-validation.yml
vendored
2
.github/workflows/pr-label-validation.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Require PR to have a changelog label
|
- name: Require PR to have a changelog label
|
||||||
uses: mheap/github-action-required-labels@fb29a14a076b0f74099f6198f77750e8fc236016 # v5.5.0
|
uses: mheap/github-action-required-labels@8afbe8ae6ab7647d0c9f0cfa7c2f939650d22509 # v5.5.1
|
||||||
with:
|
with:
|
||||||
mode: exactly
|
mode: exactly
|
||||||
count: 1
|
count: 1
|
||||||
|
|||||||
2
.github/workflows/prepare-release.yml
vendored
2
.github/workflows/prepare-release.yml
vendored
@@ -100,7 +100,7 @@ jobs:
|
|||||||
name: release-apk-signed
|
name: release-apk-signed
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Create draft release
|
||||||
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
tag_name: ${{ env.IMMICH_VERSION }}
|
tag_name: ${{ env.IMMICH_VERSION }}
|
||||||
|
|||||||
2
.github/workflows/sdk.yml
vendored
2
.github/workflows/sdk.yml
vendored
@@ -25,6 +25,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Build
|
- name: Build
|
||||||
|
|||||||
32
.github/workflows/static_analysis.yml
vendored
32
.github/workflows/static_analysis.yml
vendored
@@ -42,6 +42,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./mobile
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
@@ -49,34 +52,29 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: dart pub get
|
run: dart pub get
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Install DCM
|
- name: Install DCM
|
||||||
run: |
|
uses: CQLabs/setup-dcm@8697ae0790c0852e964a6ef1d768d62a6675481a # v2.0.1
|
||||||
sudo apt-get update
|
with:
|
||||||
wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
|
version: auto
|
||||||
sudo apt-get update
|
working-directory: ./mobile
|
||||||
sudo apt-get install dcm
|
|
||||||
|
|
||||||
- name: Generate translation file
|
- name: Generate translation file
|
||||||
run: make translation
|
run: make translation
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Run Build Runner
|
- name: Run Build Runner
|
||||||
run: make build
|
run: make build
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Generate platform API
|
- name: Generate platform API
|
||||||
run: make pigeon
|
run: make pigeon
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
@@ -98,19 +96,17 @@ jobs:
|
|||||||
|
|
||||||
- name: Run dart analyze
|
- name: Run dart analyze
|
||||||
run: dart analyze --fatal-infos
|
run: dart analyze --fatal-infos
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Run dart format
|
- name: Run dart format
|
||||||
run: dart format lib/ --set-exit-if-changed
|
# Ignore generated files manually until https://github.com/dart-lang/dart_style/issues/864 is resolved
|
||||||
working-directory: ./mobile
|
run: dart format --set-exit-if-changed $(find lib -name '*.dart' -not \( -name 'generated_plugin_registrant.dart' -o -name '*.g.dart' -o -name '*.drift.dart' \))
|
||||||
|
|
||||||
- name: Run dart custom_lint
|
- name: Run dart custom_lint
|
||||||
run: dart run custom_lint
|
run: dart run custom_lint
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
|
# TODO: Use https://github.com/CQLabs/dcm-action
|
||||||
- name: Run DCM
|
- name: Run DCM
|
||||||
run: dcm analyze lib
|
run: dcm analyze lib --fatal-style --fatal-warnings
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
zizmor:
|
zizmor:
|
||||||
name: zizmor
|
name: zizmor
|
||||||
@@ -134,7 +130,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Upload SARIF file
|
- name: Upload SARIF file
|
||||||
uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
category: zizmor
|
category: zizmor
|
||||||
|
|||||||
45
.github/workflows/test.yml
vendored
45
.github/workflows/test.yml
vendored
@@ -84,6 +84,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -101,7 +103,7 @@ jobs:
|
|||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run small tests & coverage
|
- name: Run small tests & coverage
|
||||||
run: npm run test:cov
|
run: npm test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
cli-unit-tests:
|
cli-unit-tests:
|
||||||
@@ -125,6 +127,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Setup typescript-sdk
|
- name: Setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -146,7 +150,7 @@ jobs:
|
|||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run unit tests & coverage
|
- name: Run unit tests & coverage
|
||||||
run: npm run test:cov
|
run: npm run test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
cli-unit-tests-win:
|
cli-unit-tests-win:
|
||||||
@@ -170,6 +174,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Setup typescript-sdk
|
- name: Setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -184,7 +190,7 @@ jobs:
|
|||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run unit tests & coverage
|
- name: Run unit tests & coverage
|
||||||
run: npm run test:cov
|
run: npm run test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
web-lint:
|
web-lint:
|
||||||
@@ -208,6 +214,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -249,6 +257,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -262,7 +272,7 @@ jobs:
|
|||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run unit tests & coverage
|
- name: Run unit tests & coverage
|
||||||
run: npm run test:cov
|
run: npm run test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
i18n-tests:
|
i18n-tests:
|
||||||
@@ -282,6 +292,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm --prefix=web ci
|
run: npm --prefix=web ci
|
||||||
@@ -326,6 +338,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -369,6 +383,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -402,6 +418,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -450,6 +468,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -461,7 +481,7 @@ jobs:
|
|||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: npx playwright install --with-deps chromium
|
run: npx playwright install chromium --only-shell
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Docker build
|
- name: Docker build
|
||||||
@@ -479,7 +499,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|
||||||
@@ -496,7 +516,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -568,6 +588,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './.github/.nvmrc'
|
node-version-file: './.github/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -587,7 +609,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Run ShellCheck
|
- name: Run ShellCheck
|
||||||
uses: ludeeus/action-shellcheck@master
|
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0
|
||||||
with:
|
with:
|
||||||
ignore_paths: >-
|
ignore_paths: >-
|
||||||
**/open-api/**
|
**/open-api/**
|
||||||
@@ -609,6 +631,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: npm --prefix=server ci
|
run: npm --prefix=server ci
|
||||||
@@ -644,7 +668,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:1f5583fe3397210a0fbc7f11b0cec18bacc4a99e3e8ea0548e9bd6bcf26ec37a
|
||||||
env:
|
env:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
@@ -670,6 +694,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -722,6 +748,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "ERROR: Generated SQL files not up to date!"
|
echo "ERROR: Generated SQL files not up to date!"
|
||||||
echo "Changed files: ${CHANGED_FILES}"
|
echo "Changed files: ${CHANGED_FILES}"
|
||||||
|
git diff
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
# mobile-integration-tests:
|
# mobile-integration-tests:
|
||||||
|
|||||||
2
.github/workflows/weblate-lock.yml
vendored
2
.github/workflows/weblate-lock.yml
vendored
@@ -52,6 +52,6 @@ jobs:
|
|||||||
permissions: {}
|
permissions: {}
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -24,3 +24,4 @@ mobile/android/fastlane/report.xml
|
|||||||
mobile/ios/fastlane/report.xml
|
mobile/ios/fastlane/report.xml
|
||||||
|
|
||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
|
.pnpm-store
|
||||||
|
|||||||
72
.vscode/tasks.json
vendored
Normal file
72
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Fix Permissions, Install Dependencies",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "[ -f /immich-devcontainer/container-start.sh ] && /immich-devcontainer/container-start.sh || exit 0",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"showReuseMessage": true,
|
||||||
|
"clear": false,
|
||||||
|
"group": "Devcontainer tasks",
|
||||||
|
"close": true
|
||||||
|
},
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "default"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Immich API Server (Nest)",
|
||||||
|
"dependsOn": ["Fix Permissions, Install Dependencies"],
|
||||||
|
"type": "shell",
|
||||||
|
"command": "[ -f /immich-devcontainer/container-start-backend.sh ] && /immich-devcontainer/container-start-backend.sh || exit 0",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"showReuseMessage": true,
|
||||||
|
"clear": false,
|
||||||
|
"group": "Devcontainer tasks",
|
||||||
|
"close": true
|
||||||
|
},
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "default"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Immich Web Server (Vite)",
|
||||||
|
"dependsOn": ["Fix Permissions, Install Dependencies"],
|
||||||
|
"type": "shell",
|
||||||
|
"command": "[ -f /immich-devcontainer/container-start-frontend.sh ] && /immich-devcontainer/container-start-frontend.sh || exit 0",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"showReuseMessage": true,
|
||||||
|
"clear": false,
|
||||||
|
"group": "Devcontainer tasks",
|
||||||
|
"close": true
|
||||||
|
},
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "default"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Immich Server and Web",
|
||||||
|
"dependsOn": ["Immich Web Server (Vite)", "Immich API Server (Nest)"],
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "folderOpen"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
30
Makefile
30
Makefile
@@ -1,27 +1,33 @@
|
|||||||
dev:
|
dev:
|
||||||
docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans || make dev-down
|
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans
|
||||||
|
|
||||||
dev-down:
|
dev-down:
|
||||||
docker compose -f ./docker/docker-compose.dev.yml down --remove-orphans
|
docker compose -f ./docker/docker-compose.dev.yml down --remove-orphans
|
||||||
|
|
||||||
dev-update:
|
dev-update:
|
||||||
docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
|
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
|
||||||
|
|
||||||
dev-scale:
|
dev-scale:
|
||||||
docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
|
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
|
||||||
|
|
||||||
.PHONY: e2e
|
.PHONY: e2e
|
||||||
e2e:
|
e2e:
|
||||||
docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans
|
@trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans
|
||||||
|
|
||||||
|
e2e-update:
|
||||||
|
@trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans
|
||||||
|
|
||||||
|
e2e-down:
|
||||||
|
docker compose -f ./e2e/docker-compose.yml down --remove-orphans
|
||||||
|
|
||||||
prod:
|
prod:
|
||||||
docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans
|
@trap 'make prod-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans
|
||||||
|
|
||||||
prod-down:
|
prod-down:
|
||||||
docker compose -f ./docker/docker-compose.prod.yml down --remove-orphans
|
docker compose -f ./docker/docker-compose.prod.yml down --remove-orphans
|
||||||
|
|
||||||
prod-scale:
|
prod-scale:
|
||||||
docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans
|
@trap 'make prod-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans
|
||||||
|
|
||||||
.PHONY: open-api
|
.PHONY: open-api
|
||||||
open-api:
|
open-api:
|
||||||
@@ -48,6 +54,8 @@ audit-%:
|
|||||||
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) audit fix
|
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) audit fix
|
||||||
install-%:
|
install-%:
|
||||||
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) i
|
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) i
|
||||||
|
ci-%:
|
||||||
|
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) ci
|
||||||
build-cli: build-sdk
|
build-cli: build-sdk
|
||||||
build-web: build-sdk
|
build-web: build-sdk
|
||||||
build-%: install-%
|
build-%: install-%
|
||||||
@@ -82,6 +90,7 @@ test-medium-dev:
|
|||||||
|
|
||||||
build-all: $(foreach M,$(filter-out e2e .github,$(MODULES)),build-$M) ;
|
build-all: $(foreach M,$(filter-out e2e .github,$(MODULES)),build-$M) ;
|
||||||
install-all: $(foreach M,$(MODULES),install-$M) ;
|
install-all: $(foreach M,$(MODULES),install-$M) ;
|
||||||
|
ci-all: $(foreach M,$(filter-out .github,$(MODULES)),ci-$M) ;
|
||||||
check-all: $(foreach M,$(filter-out sdk cli docs .github,$(MODULES)),check-$M) ;
|
check-all: $(foreach M,$(filter-out sdk cli docs .github,$(MODULES)),check-$M) ;
|
||||||
lint-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),lint-$M) ;
|
lint-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),lint-$M) ;
|
||||||
format-all: $(foreach M,$(filter-out sdk,$(MODULES)),format-$M) ;
|
format-all: $(foreach M,$(filter-out sdk,$(MODULES)),format-$M) ;
|
||||||
@@ -90,9 +99,12 @@ hygiene-all: lint-all format-all check-all sql audit-all;
|
|||||||
test-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),test-$M) ;
|
test-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),test-$M) ;
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
|
find . -name "node_modules" -type d -prune -exec rm -rf {} +
|
||||||
find . -name "dist" -type d -prune -exec rm -rf '{}' +
|
find . -name "dist" -type d -prune -exec rm -rf '{}' +
|
||||||
find . -name "build" -type d -prune -exec rm -rf '{}' +
|
find . -name "build" -type d -prune -exec rm -rf '{}' +
|
||||||
find . -name "svelte-kit" -type d -prune -exec rm -rf '{}' +
|
find . -name "svelte-kit" -type d -prune -exec rm -rf '{}' +
|
||||||
docker compose -f ./docker/docker-compose.dev.yml rm -v -f || true
|
command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml rm -v -f || true
|
||||||
docker compose -f ./e2e/docker-compose.yml rm -v -f || true
|
command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml rm -v -f || true
|
||||||
|
|
||||||
|
setup-server-dev: install-server
|
||||||
|
setup-web-dev: install-sdk build-sdk install-web
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
22.16.0
|
22.17.1
|
||||||
|
|||||||
2
cli/bin/immich
Executable file
2
cli/bin/immich
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import '../dist/index.js';
|
||||||
555
cli/package-lock.json
generated
555
cli/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.68",
|
"version": "2.2.73",
|
||||||
"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",
|
||||||
"bin": {
|
"bin": {
|
||||||
"immich": "dist/index.js"
|
"immich": "./bin/immich"
|
||||||
},
|
},
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/micromatch": "^4.0.9",
|
"@types/micromatch": "^4.0.9",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^22.15.29",
|
"@types/node": "^22.16.4",
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
"byte-size": "^9.0.0",
|
"byte-size": "^9.0.0",
|
||||||
"cli-progress": "^3.12.0",
|
"cli-progress": "^3.12.0",
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
"prettier-plugin-organize-imports": "^4.0.0",
|
"prettier-plugin-organize-imports": "^4.0.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"typescript-eslint": "^8.28.0",
|
"typescript-eslint": "^8.28.0",
|
||||||
"vite": "^6.0.0",
|
"vite": "^7.0.0",
|
||||||
"vite-tsconfig-paths": "^5.0.0",
|
"vite-tsconfig-paths": "^5.0.0",
|
||||||
"vitest": "^3.0.0",
|
"vitest": "^3.0.0",
|
||||||
"vitest-fetch-mock": "^0.4.0",
|
"vitest-fetch-mock": "^0.4.0",
|
||||||
@@ -69,6 +69,6 @@
|
|||||||
"micromatch": "^4.0.8"
|
"micromatch": "^4.0.8"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.16.0"
|
"node": "22.17.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,10 @@ const uploadBatch = async (files: string[], options: UploadOptionsDto) => {
|
|||||||
console.log(JSON.stringify({ newFiles, duplicates, newAssets }, undefined, 4));
|
console.log(JSON.stringify({ newFiles, duplicates, newAssets }, undefined, 4));
|
||||||
}
|
}
|
||||||
await updateAlbums([...newAssets, ...duplicates], options);
|
await updateAlbums([...newAssets, ...duplicates], options);
|
||||||
await deleteFiles(newFiles, options);
|
await deleteFiles(
|
||||||
|
newAssets.map(({ filepath }) => filepath),
|
||||||
|
options,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const startWatch = async (
|
export const startWatch = async (
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ name: immich-dev
|
|||||||
services:
|
services:
|
||||||
immich-server:
|
immich-server:
|
||||||
container_name: immich_server
|
container_name: immich_server
|
||||||
command: [ '/usr/src/app/bin/immich-dev' ]
|
command: ['immich-dev']
|
||||||
image: immich-server-dev:latest
|
image: immich-server-dev:latest
|
||||||
# extends:
|
# extends:
|
||||||
# file: hwaccel.transcoding.yml
|
# file: hwaccel.transcoding.yml
|
||||||
@@ -27,11 +27,11 @@ services:
|
|||||||
target: dev
|
target: dev
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ../server:/usr/src/app
|
- ../server:/usr/src/app/server
|
||||||
- ../open-api:/usr/src/open-api
|
- ../open-api:/usr/src/app/open-api
|
||||||
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
||||||
- ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload/upload
|
- ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload/upload
|
||||||
- /usr/src/app/node_modules
|
- /usr/src/app/server/node_modules
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
@@ -69,19 +69,20 @@ services:
|
|||||||
# Needed for rootless docker setup, see https://github.com/moby/moby/issues/45919
|
# Needed for rootless docker setup, see https://github.com/moby/moby/issues/45919
|
||||||
# user: 0:0
|
# user: 0:0
|
||||||
build:
|
build:
|
||||||
context: ../web
|
context: ../
|
||||||
command: [ '/usr/src/app/bin/immich-web' ]
|
dockerfile: web/Dockerfile
|
||||||
|
command: ['immich-web']
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
- 24678:24678
|
- 24678:24678
|
||||||
volumes:
|
volumes:
|
||||||
- ../web:/usr/src/app
|
- ../web:/usr/src/app/web
|
||||||
- ../i18n:/usr/src/i18n
|
- ../i18n:/usr/src/app/i18n
|
||||||
- ../open-api/:/usr/src/open-api/
|
- ../open-api/:/usr/src/app/open-api/
|
||||||
# - ../../ui:/usr/ui
|
# - ../../ui:/usr/ui
|
||||||
- /usr/src/app/node_modules
|
- /usr/src/app/web/node_modules
|
||||||
ulimits:
|
ulimits:
|
||||||
nofile:
|
nofile:
|
||||||
soft: 1048576
|
soft: 1048576
|
||||||
@@ -116,13 +117,13 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
image: docker.io/valkey/valkey:8-bookworm@sha256:facc1d2c3462975c34e10fccb167bfa92b0e0dbd992fc282c29a61c3243afb11
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
@@ -134,6 +135,7 @@ services:
|
|||||||
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
shm_size: 128mb
|
||||||
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
||||||
# immich-prometheus:
|
# immich-prometheus:
|
||||||
# container_name: immich_prometheus
|
# container_name: immich_prometheus
|
||||||
|
|||||||
@@ -56,14 +56,14 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
image: docker.io/valkey/valkey:8-bookworm@sha256:facc1d2c3462975c34e10fccb167bfa92b0e0dbd992fc282c29a61c3243afb11
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
@@ -75,6 +75,7 @@ services:
|
|||||||
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
shm_size: 128mb
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
||||||
@@ -82,7 +83,7 @@ services:
|
|||||||
container_name: immich_prometheus
|
container_name: immich_prometheus
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- 9090:9090
|
||||||
image: prom/prometheus@sha256:9abc6cf6aea7710d163dbb28d8eeb7dc5baef01e38fa4cd146a406dd9f07f70d
|
image: prom/prometheus@sha256:63805ebb8d2b3920190daf1cb14a60871b16fd38bed42b857a3182bc621f4996
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
@@ -91,10 +92,10 @@ services:
|
|||||||
# add data source for http://immich-prometheus:9090 to get started
|
# add data source for http://immich-prometheus:9090 to get started
|
||||||
immich-grafana:
|
immich-grafana:
|
||||||
container_name: immich_grafana
|
container_name: immich_grafana
|
||||||
command: [ './run.sh', '-disable-reporting' ]
|
command: ['./run.sh', '-disable-reporting']
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
image: grafana/grafana:12.0.1-ubuntu@sha256:65575bb9c761335e2ff30e364f21d38632e3b2e75f5f81d83cc92f44b9bbc055
|
image: grafana/grafana:12.0.2-ubuntu@sha256:0512d81cdeaaff0e370a9aa66027b465d1f1f04379c3a9c801a905fabbdbc7a5
|
||||||
volumes:
|
volumes:
|
||||||
- grafana-data:/var/lib/grafana
|
- grafana-data:/var/lib/grafana
|
||||||
|
|
||||||
|
|||||||
@@ -49,14 +49,14 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
image: docker.io/valkey/valkey:8-bookworm@sha256:facc1d2c3462975c34e10fccb167bfa92b0e0dbd992fc282c29a61c3243afb11
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
POSTGRES_USER: ${DB_USERNAME}
|
POSTGRES_USER: ${DB_USERNAME}
|
||||||
@@ -67,6 +67,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
|
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
|
||||||
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
||||||
|
shm_size: 128mb
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
22.16.0
|
22.17.1
|
||||||
|
|||||||
@@ -490,7 +490,7 @@ You can also scan the Postgres database file structure for errors:
|
|||||||
<details>
|
<details>
|
||||||
<summary>Scan for file structure errors</summary>
|
<summary>Scan for file structure errors</summary>
|
||||||
```bash
|
```bash
|
||||||
docker exec -it immich_postgres pg_amcheck --username=postgres --heapallindexed --parent-check --rootdescend --progress --all --install-missing
|
docker exec -it immich_postgres pg_amcheck --username=<DB_USERNAME> --heapallindexed --parent-check --rootdescend --progress --all --install-missing
|
||||||
```
|
```
|
||||||
|
|
||||||
A normal result will end something like this and return with an exit code of `0`:
|
A normal result will end something like this and return with an exit code of `0`:
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ Then please follow the steps in the following section for restoring the database
|
|||||||
<TabItem value="Linux system" label="Linux system" default>
|
<TabItem value="Linux system" label="Linux system" default>
|
||||||
|
|
||||||
```bash title='Backup'
|
```bash title='Backup'
|
||||||
docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres | gzip > "/path/to/backup/dump.sql.gz"
|
docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=<DB_USERNAME> | gzip > "/path/to/backup/dump.sql.gz"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash title='Restore'
|
```bash title='Restore'
|
||||||
@@ -79,7 +79,7 @@ docker compose up -d # Start remainder of Immich apps
|
|||||||
<TabItem value="Windows system (PowerShell)" label="Windows system (PowerShell)">
|
<TabItem value="Windows system (PowerShell)" label="Windows system (PowerShell)">
|
||||||
|
|
||||||
```powershell title='Backup'
|
```powershell title='Backup'
|
||||||
[System.IO.File]::WriteAllLines("C:\absolute\path\to\backup\dump.sql", (docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres))
|
[System.IO.File]::WriteAllLines("C:\absolute\path\to\backup\dump.sql", (docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=<DB_USERNAME>))
|
||||||
```
|
```
|
||||||
|
|
||||||
```powershell title='Restore'
|
```powershell title='Restore'
|
||||||
@@ -150,12 +150,10 @@ for more info read the [release notes](https://github.com/immich-app/immich/rele
|
|||||||
- Preview images (small thumbnails and large previews) for each asset and thumbnails for recognized faces.
|
- Preview images (small thumbnails and large previews) for each asset and thumbnails for recognized faces.
|
||||||
- Stored in `UPLOAD_LOCATION/thumbs/<userID>`.
|
- Stored in `UPLOAD_LOCATION/thumbs/<userID>`.
|
||||||
- **Encoded Assets:**
|
- **Encoded Assets:**
|
||||||
|
|
||||||
- Videos that have been re-encoded from the original for wider compatibility. The original is not removed.
|
- Videos that have been re-encoded from the original for wider compatibility. The original is not removed.
|
||||||
- Stored in `UPLOAD_LOCATION/encoded-video/<userID>`.
|
- Stored in `UPLOAD_LOCATION/encoded-video/<userID>`.
|
||||||
|
|
||||||
- **Postgres**
|
- **Postgres**
|
||||||
|
|
||||||
- The Immich database containing all the information to allow the system to function properly.
|
- The Immich database containing all the information to allow the system to function properly.
|
||||||
**Note:** This folder will only appear to users who have made the changes mentioned in [v1.102.0](https://github.com/immich-app/immich/discussions/8930) (an optional, non-mandatory change) or who started with this version.
|
**Note:** This folder will only appear to users who have made the changes mentioned in [v1.102.0](https://github.com/immich-app/immich/discussions/8930) (an optional, non-mandatory change) or who started with this version.
|
||||||
- Stored in `DB_DATA_LOCATION`.
|
- Stored in `DB_DATA_LOCATION`.
|
||||||
@@ -201,7 +199,6 @@ When you turn off the storage template engine, it will leave the assets in `UPLO
|
|||||||
- Temporarily located in `UPLOAD_LOCATION/upload/<userID>`.
|
- Temporarily located in `UPLOAD_LOCATION/upload/<userID>`.
|
||||||
- Transferred to `UPLOAD_LOCATION/library/<userID>` upon successful upload.
|
- Transferred to `UPLOAD_LOCATION/library/<userID>` upon successful upload.
|
||||||
- **Postgres**
|
- **Postgres**
|
||||||
|
|
||||||
- The Immich database containing all the information to allow the system to function properly.
|
- The Immich database containing all the information to allow the system to function properly.
|
||||||
**Note:** This folder will only appear to users who have made the changes mentioned in [v1.102.0](https://github.com/immich-app/immich/discussions/8930) (an optional, non-mandatory change) or who started with this version.
|
**Note:** This folder will only appear to users who have made the changes mentioned in [v1.102.0](https://github.com/immich-app/immich/discussions/8930) (an optional, non-mandatory change) or who started with this version.
|
||||||
- Stored in `DB_DATA_LOCATION`.
|
- Stored in `DB_DATA_LOCATION`.
|
||||||
|
|||||||
BIN
docs/docs/administration/img/admin-nightly-tasks.webp
Normal file
BIN
docs/docs/administration/img/admin-nightly-tasks.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
@@ -46,6 +46,12 @@ services:
|
|||||||
|
|
||||||
When a new asset is uploaded it kicks off a series of jobs, which include metadata extraction, thumbnail generation, machine learning tasks, and storage template migration, if enabled. To view the status of a job navigate to the Administration -> Jobs page.
|
When a new asset is uploaded it kicks off a series of jobs, which include metadata extraction, thumbnail generation, machine learning tasks, and storage template migration, if enabled. To view the status of a job navigate to the Administration -> Jobs page.
|
||||||
|
|
||||||
Additionally, some jobs run on a schedule, which is every night at midnight. This schedule, with the exception of [External Libraries](/docs/features/libraries) scanning, cannot be changed.
|
|
||||||
|
|
||||||
<img src={require('./img/admin-jobs.webp').default} width="60%" title="Admin jobs" />
|
<img src={require('./img/admin-jobs.webp').default} width="60%" title="Admin jobs" />
|
||||||
|
|
||||||
|
Additionally, some jobs (such as memories generation) run on a schedule, which is every night at midnight by default. To change when they run or enable/disable a job navigate to System Settings -> [Nightly Tasks Settings](https://my.immich.app/admin/system-settings?isOpen=nightly-tasks).
|
||||||
|
|
||||||
|
<img src={require('./img/admin-nightly-tasks.webp').default} width="60%" title="Admin nightly tasks" />
|
||||||
|
|
||||||
|
:::note
|
||||||
|
Some jobs ([External Libraries](/docs/features/libraries) scanning, Database Dump) are configured in their own sections in System Settings.
|
||||||
|
:::
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ Immich supports 3rd party authentication via [OpenID Connect][oidc] (OIDC), an i
|
|||||||
Before enabling OAuth in Immich, a new client application needs to be configured in the 3rd-party authentication server. While the specifics of this setup vary from provider to provider, the general approach should be the same.
|
Before enabling OAuth in Immich, a new client application needs to be configured in the 3rd-party authentication server. While the specifics of this setup vary from provider to provider, the general approach should be the same.
|
||||||
|
|
||||||
1. Create a new (Client) Application
|
1. Create a new (Client) Application
|
||||||
|
|
||||||
1. The **Provider** type should be `OpenID Connect` or `OAuth2`
|
1. The **Provider** type should be `OpenID Connect` or `OAuth2`
|
||||||
2. The **Client type** should be `Confidential`
|
2. The **Client type** should be `Confidential`
|
||||||
3. The **Application** type should be `Web`
|
3. The **Application** type should be `Web`
|
||||||
@@ -29,7 +28,6 @@ Before enabling OAuth in Immich, a new client application needs to be configured
|
|||||||
2. Configure Redirect URIs/Origins
|
2. Configure Redirect URIs/Origins
|
||||||
|
|
||||||
The **Sign-in redirect URIs** should include:
|
The **Sign-in redirect URIs** should include:
|
||||||
|
|
||||||
- `app.immich:///oauth-callback` - for logging in with OAuth from the [Mobile App](/docs/features/mobile-app.mdx)
|
- `app.immich:///oauth-callback` - for logging in with OAuth from the [Mobile App](/docs/features/mobile-app.mdx)
|
||||||
- `http://DOMAIN:PORT/auth/login` - for logging in with OAuth from the Web Client
|
- `http://DOMAIN:PORT/auth/login` - for logging in with OAuth from the Web Client
|
||||||
- `http://DOMAIN:PORT/user-settings` - for manually linking OAuth in the Web Client
|
- `http://DOMAIN:PORT/user-settings` - for manually linking OAuth in the Web Client
|
||||||
@@ -37,21 +35,17 @@ Before enabling OAuth in Immich, a new client application needs to be configured
|
|||||||
Redirect URIs should contain all the domains you will be using to access Immich. Some examples include:
|
Redirect URIs should contain all the domains you will be using to access Immich. Some examples include:
|
||||||
|
|
||||||
Mobile
|
Mobile
|
||||||
|
|
||||||
- `app.immich:///oauth-callback` (You **MUST** include this for iOS and Android mobile apps to work properly)
|
- `app.immich:///oauth-callback` (You **MUST** include this for iOS and Android mobile apps to work properly)
|
||||||
|
|
||||||
Localhost
|
Localhost
|
||||||
|
|
||||||
- `http://localhost:2283/auth/login`
|
- `http://localhost:2283/auth/login`
|
||||||
- `http://localhost:2283/user-settings`
|
- `http://localhost:2283/user-settings`
|
||||||
|
|
||||||
Local IP
|
Local IP
|
||||||
|
|
||||||
- `http://192.168.0.200:2283/auth/login`
|
- `http://192.168.0.200:2283/auth/login`
|
||||||
- `http://192.168.0.200:2283/user-settings`
|
- `http://192.168.0.200:2283/user-settings`
|
||||||
|
|
||||||
Hostname
|
Hostname
|
||||||
|
|
||||||
- `https://immich.example.com/auth/login`
|
- `https://immich.example.com/auth/login`
|
||||||
- `https://immich.example.com/user-settings`
|
- `https://immich.example.com/user-settings`
|
||||||
|
|
||||||
@@ -68,6 +62,7 @@ Once you have a new OAuth client application configured, Immich can be configure
|
|||||||
| Scope | string | openid email profile | Full list of scopes to send with the request (space delimited) |
|
| Scope | string | openid email profile | Full list of scopes to send with the request (space delimited) |
|
||||||
| Signing Algorithm | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) |
|
| Signing Algorithm | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) |
|
||||||
| Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** |
|
| Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** |
|
||||||
|
| Role Claim | string | immich_role | Claim mapping for the user's role. (should return "user" or "admin")**¹** |
|
||||||
| Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**¹** |
|
| Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**¹** |
|
||||||
| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (Enter 0 for unlimited quota) |
|
| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (Enter 0 for unlimited quota) |
|
||||||
| Button Text | string | Login with OAuth | Text for the OAuth button on the web |
|
| Button Text | string | Login with OAuth | Text for the OAuth button on the web |
|
||||||
|
|||||||
@@ -64,7 +64,13 @@ COMMIT;
|
|||||||
|
|
||||||
### Updating VectorChord
|
### Updating VectorChord
|
||||||
|
|
||||||
When installing a new version of VectorChord, you will need to manually update the extension by connecting to the Immich database and running `ALTER EXTENSION vchord UPDATE;`.
|
When installing a new version of VectorChord, you will need to manually update the extension and reindex by connecting to the Immich database and running:
|
||||||
|
|
||||||
|
```
|
||||||
|
ALTER EXTENSION vchord UPDATE;
|
||||||
|
REINDEX INDEX face_index;
|
||||||
|
REINDEX INDEX clip_index;
|
||||||
|
```
|
||||||
|
|
||||||
## Migrating to VectorChord
|
## Migrating to VectorChord
|
||||||
|
|
||||||
@@ -76,6 +82,8 @@ Support for pgvecto.rs will be dropped in a later release, hence we recommend al
|
|||||||
|
|
||||||
The easiest option is to have both extensions installed during the migration:
|
The easiest option is to have both extensions installed during the migration:
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Migration steps (automatic)</summary>
|
||||||
1. Ensure you still have pgvecto.rs installed
|
1. Ensure you still have pgvecto.rs installed
|
||||||
2. Install `pgvector` (`>= 0.7.0, < 1.0.0`). The easiest way to do this is on Debian/Ubuntu by adding the [PostgreSQL Apt repository][pg-apt] and then running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres version (e.g., `16`)
|
2. Install `pgvector` (`>= 0.7.0, < 1.0.0`). The easiest way to do this is on Debian/Ubuntu by adding the [PostgreSQL Apt repository][pg-apt] and then running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres version (e.g., `16`)
|
||||||
3. [Install VectorChord][vchord-install]
|
3. [Install VectorChord][vchord-install]
|
||||||
@@ -89,8 +97,12 @@ The easiest option is to have both extensions installed during the migration:
|
|||||||
11. Restart the Postgres database
|
11. Restart the Postgres database
|
||||||
12. Uninstall pgvecto.rs (e.g. `apt-get purge vectors-pg14` on Debian-based environments, replacing `pg14` as appropriate). `pgvector` must remain installed as it provides the data types used by `vchord`
|
12. Uninstall pgvecto.rs (e.g. `apt-get purge vectors-pg14` on Debian-based environments, replacing `pg14` as appropriate). `pgvector` must remain installed as it provides the data types used by `vchord`
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
If it is not possible to have both VectorChord and pgvecto.rs installed at the same time, you can perform the migration with more manual steps:
|
If it is not possible to have both VectorChord and pgvecto.rs installed at the same time, you can perform the migration with more manual steps:
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Migration steps (manual)</summary>
|
||||||
1. While pgvecto.rs is still installed, run the following SQL command using psql or your choice of database client. Take note of the number outputted by this command as you will need it later
|
1. While pgvecto.rs is still installed, run the following SQL command using psql or your choice of database client. Take note of the number outputted by this command as you will need it later
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
@@ -123,14 +135,20 @@ ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512);
|
|||||||
|
|
||||||
5. Start Immich and let it create new indices using VectorChord
|
5. Start Immich and let it create new indices using VectorChord
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
### Migrating from pgvector
|
### Migrating from pgvector
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Migration steps</summary>
|
||||||
1. Ensure you have at least 0.7.0 of pgvector installed. If it is below that, please upgrade it and run the SQL command `ALTER EXTENSION vector UPDATE;` using psql or your choice of database client
|
1. Ensure you have at least 0.7.0 of pgvector installed. If it is below that, please upgrade it and run the SQL command `ALTER EXTENSION vector UPDATE;` using psql or your choice of database client
|
||||||
2. Follow the Prerequisites to install VectorChord
|
2. Follow the Prerequisites to install VectorChord
|
||||||
3. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;`
|
3. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;`
|
||||||
4. Remove the `DB_VECTOR_EXTENSION=pgvector` environmental variable as it will make Immich still use pgvector if set
|
4. Remove the `DB_VECTOR_EXTENSION=pgvector` environmental variable as it will make Immich still use pgvector if set
|
||||||
5. Start Immich and let it create new indices using VectorChord
|
5. Start Immich and let it create new indices using VectorChord
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
Note that VectorChord itself uses pgvector types, so you should not uninstall pgvector after following these steps.
|
Note that VectorChord itself uses pgvector types, so you should not uninstall pgvector after following these steps.
|
||||||
|
|
||||||
[vchord-install]: https://docs.vectorchord.ai/vectorchord/getting-started/installation.html
|
[vchord-install]: https://docs.vectorchord.ai/vectorchord/getting-started/installation.html
|
||||||
|
|||||||
@@ -2,16 +2,17 @@
|
|||||||
|
|
||||||
The `immich-server` docker image comes preinstalled with an administrative CLI (`immich-admin`) that supports the following commands:
|
The `immich-server` docker image comes preinstalled with an administrative CLI (`immich-admin`) that supports the following commands:
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
| ------------------------ | ------------------------------------- |
|
| ------------------------ | ------------------------------------------------------------- |
|
||||||
| `help` | Display help |
|
| `help` | Display help |
|
||||||
| `reset-admin-password` | Reset the password for the admin user |
|
| `reset-admin-password` | Reset the password for the admin user |
|
||||||
| `disable-password-login` | Disable password login |
|
| `disable-password-login` | Disable password login |
|
||||||
| `enable-password-login` | Enable password login |
|
| `enable-password-login` | Enable password login |
|
||||||
| `enable-oauth-login` | Enable OAuth login |
|
| `enable-oauth-login` | Enable OAuth login |
|
||||||
| `disable-oauth-login` | Disable OAuth login |
|
| `disable-oauth-login` | Disable OAuth login |
|
||||||
| `list-users` | List Immich users |
|
| `list-users` | List Immich users |
|
||||||
| `version` | Print Immich version |
|
| `version` | Print Immich version |
|
||||||
|
| `change-media-location` | Change database file paths to align with a new media location |
|
||||||
|
|
||||||
## How to run a command
|
## How to run a command
|
||||||
|
|
||||||
@@ -88,3 +89,24 @@ Print Immich Version
|
|||||||
immich-admin version
|
immich-admin version
|
||||||
v1.129.0
|
v1.129.0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Change media location
|
||||||
|
|
||||||
|
```
|
||||||
|
immich-admin change-media-location
|
||||||
|
? Enter the previous value of IMMICH_MEDIA_LOCATION: /usr/src/app/upload
|
||||||
|
? Enter the new value of IMMICH_MEDIA_LOCATION: /data
|
||||||
|
|
||||||
|
Previous value: /usr/src/app/upload
|
||||||
|
Current value: /data
|
||||||
|
|
||||||
|
Changing database paths from "/usr/src/app/upload/*" to "/data/*"
|
||||||
|
|
||||||
|
? Do you want to proceed? [Y/n] y
|
||||||
|
|
||||||
|
Database file paths updated successfully! 🎉
|
||||||
|
|
||||||
|
You may now set IMMICH_MEDIA_LOCATION=/data and restart!
|
||||||
|
|
||||||
|
(please remember to update applicable volume mounts e.g. ${UPLOAD_LOCATION}:/data)
|
||||||
|
```
|
||||||
|
|||||||
480
docs/docs/developer/devcontainers.md
Normal file
480
docs/docs/developer/devcontainers.md
Normal file
@@ -0,0 +1,480 @@
|
|||||||
|
---
|
||||||
|
title: Devcontainers
|
||||||
|
sidebar_position: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Development with Dev Containers
|
||||||
|
|
||||||
|
Dev Containers provide a consistent, reproducible development environment using Docker containers. With a single click, you can get started with an Immich development environment on Mac, Linux, Windows, or in the cloud using GitHub Codespaces.
|
||||||
|
|
||||||
|
Get started fast!
|
||||||
|
|
||||||
|
[](https://codespaces.new/immich-app/immich/)
|
||||||
|
|
||||||
|
[Learn more about Dev Containers](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before getting started, ensure you have:
|
||||||
|
|
||||||
|
- **Docker Desktop** (latest version)
|
||||||
|
- [Mac](https://docs.docker.com/desktop/install/mac-install/)
|
||||||
|
- [Windows](https://docs.docker.com/desktop/install/windows-install/) (with WSL2 backend recommended)
|
||||||
|
- [Linux](https://docs.docker.com/desktop/install/linux-install/)
|
||||||
|
- **Visual Studio Code** with the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
||||||
|
- **Git** for cloning the repository
|
||||||
|
- At least **8GB of RAM** (16GB recommended)
|
||||||
|
- **20GB of free disk space**
|
||||||
|
|
||||||
|
:::tip Alternative Development Environments
|
||||||
|
While this guide focuses on VS Code, you have many options for Dev Container development:
|
||||||
|
|
||||||
|
**Local Editors:**
|
||||||
|
|
||||||
|
- [IntelliJ IDEA](https://www.jetbrains.com/help/idea/connect-to-devcontainer.html) - Full JetBrains IDE support
|
||||||
|
- [neovim](https://github.com/jamestthompson3/nvim-remote-containers) - Lightweight terminal-based editor
|
||||||
|
- [Emacs](https://github.com/emacs-lsp/lsp-docker) - Extensible text editor
|
||||||
|
- [DevContainer CLI](https://github.com/devcontainers/cli) - Command-line interface
|
||||||
|
|
||||||
|
**Cloud-Based Solutions:**
|
||||||
|
|
||||||
|
- [GitHub Codespaces](https://github.com/features/codespaces) - Fully integrated with GitHub, excellent devcontainer.json support
|
||||||
|
- [GitPod](https://www.gitpod.io) - SaaS platform with recent Dev Container support (historically used gitpod.yml)
|
||||||
|
|
||||||
|
**Self-Hostable Options:**
|
||||||
|
|
||||||
|
- [Coder](https://coder.com) - Enterprise-focused, requires Terraform knowledge, self-managed
|
||||||
|
- [DevPod](https://devpod.sh) - Client-only tool with excellent devcontainer.json support, works with any provider (local, cloud, or on-premise)
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Dev Container Services
|
||||||
|
|
||||||
|
The Dev Container environment consists of the following services:
|
||||||
|
|
||||||
|
| Service | Container Name | Description | Ports |
|
||||||
|
| ---------------- | ------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||||
|
| Server & Web | `immich-server` | Runs both API server and web frontend in development mode | 2283 (API)<br/>3000 (Web)<br/>9230 (Workers Debug)<br/>9231 (API Debug) |
|
||||||
|
| Database | `database` | PostgreSQL database | 5432 |
|
||||||
|
| Cache | `redis` | Valkey cache server | 6379 |
|
||||||
|
| Machine Learning | `immich-machine-learning` | Immich ML model inference server | 3003 |
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Step 1: Clone the Repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/immich-app/immich.git
|
||||||
|
cd immich
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Configure Environment Variables
|
||||||
|
|
||||||
|
The immich dev containers read environment variables from your shell environment, not from `.env` files. This allows them to work in cloud environments without pre-configuration.
|
||||||
|
|
||||||
|
:::important Configuration
|
||||||
|
When running locally, and if you want to create (or use an existing) DB and/or photo storage folder, you must set the `UPLOAD_LOCATION` variable in your shell environment before launching the Dev Container. This determines where uploaded files are stored and also where the DB stores it data.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set temporarily for current session
|
||||||
|
export UPLOAD_LOCATION=/opt/dev_upload_folder
|
||||||
|
|
||||||
|
# Or add to your shell profile for persistence
|
||||||
|
# (~/.bashrc, ~/.zshrc, ~/.bash_profile, etc.)
|
||||||
|
echo 'export UPLOAD_LOCATION=/opt/dev_upload_folder' >> ~/.bashrc
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Step 3: Launch the Dev Container
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
Immich development makes extensive use of specialized [base images](https://github.com/immich-app/base-images) for its docker-compose based development. For this reason, you won't be able to use VSCode's **_Clone Repository in a Container Volume_** command.
|
||||||
|
:::
|
||||||
|
|
||||||
|
#### Using VS Code UI:
|
||||||
|
|
||||||
|
1. Open the cloned repository in VS Code
|
||||||
|
2. Press `F1` or `Ctrl/Cmd+Shift+P` to open the command palette
|
||||||
|
3. Type and select "Dev Containers: Rebuild and Reopen in Container"
|
||||||
|
4. Select "Immich - Backend, Frontend and ML" from the list
|
||||||
|
5. Wait for the container to build and start (this may take several minutes on first run)
|
||||||
|
|
||||||
|
#### Using VS Code Quick Actions:
|
||||||
|
|
||||||
|
1. Open the repository in VS Code
|
||||||
|
2. You should see a popup asking if you want to reopen in a container
|
||||||
|
3. Click "Reopen in Container"
|
||||||
|
|
||||||
|
#### Using Command Line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Using the DevContainer CLI
|
||||||
|
devcontainer up --workspace-folder .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variable Details
|
||||||
|
|
||||||
|
### How Dev Containers Handle Environment Variables
|
||||||
|
|
||||||
|
Unlike the Immich developer setup based on Docker Compose which uses `.env` files, Immich Dev Containers read environment variables from your shell environment. This is configured in `.devcontainer/devcontainer.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"remoteEnv": {
|
||||||
|
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}",
|
||||||
|
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
|
||||||
|
"DB_USERNAME": "${localEnv:DB_USERNAME:postgres}",
|
||||||
|
"DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `${localEnv:VARIABLE:default}` syntax reads from your shell environment with optional defaults.
|
||||||
|
|
||||||
|
### Upload Location Path Resolution
|
||||||
|
|
||||||
|
The `UPLOAD_LOCATION` environment variable controls where files are stored:
|
||||||
|
|
||||||
|
**Default:** `./Library` (relative to the `docker` directory)
|
||||||
|
**Resolved to:** `<immich-root>/docker/Library`
|
||||||
|
|
||||||
|
**Bind Mounts Created:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# From .devcontainer/server/container-compose-overrides.yml
|
||||||
|
- ${UPLOAD_LOCATION-./Library}/photos:/workspaces/immich/server/upload
|
||||||
|
- ${UPLOAD_LOCATION-./Library}/postgres:/var/lib/postgresql/data
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Configuration
|
||||||
|
|
||||||
|
These variables have sensible defaults (for development) but can be customized:
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
| ------------------ | ---------- | ------------------- |
|
||||||
|
| `DB_PASSWORD` | `postgres` | PostgreSQL password |
|
||||||
|
| `DB_USERNAME` | `postgres` | PostgreSQL username |
|
||||||
|
| `DB_DATABASE_NAME` | `immich` | Database name |
|
||||||
|
|
||||||
|
### Setting Environment Variables
|
||||||
|
|
||||||
|
Add these to your shell profile (`~/.bashrc`, `~/.zshrc`, `~/.bash_profile`, etc.):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Required
|
||||||
|
export UPLOAD_LOCATION=./Library # or absolute path
|
||||||
|
|
||||||
|
# Optional (only if using non-default values)
|
||||||
|
export DB_PASSWORD=your_password
|
||||||
|
export DB_USERNAME=your_username
|
||||||
|
export DB_DATABASE_NAME=your_database
|
||||||
|
```
|
||||||
|
|
||||||
|
Remember to reload your shell configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/.bashrc # or ~/.zshrc, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Git Configuration
|
||||||
|
|
||||||
|
### SSH Keys and Authentication
|
||||||
|
|
||||||
|
To use your SSH keys for GitHub access inside the Dev Container:
|
||||||
|
|
||||||
|
1. **Start SSH Agent** on your host machine:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
eval "$(ssh-agent -s)"
|
||||||
|
ssh-add ~/.ssh/id_rsa # or your key path
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **VS Code automatically forwards your SSH agent** to the container
|
||||||
|
|
||||||
|
For detailed instructions, see the [VS Code guide on sharing Git credentials](https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials).
|
||||||
|
|
||||||
|
### Commit Signing
|
||||||
|
|
||||||
|
To use your SSH key for commit signing, see the [GitHub guide on SSH commit signing](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key#telling-git-about-your-ssh-key).
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Automatic Setup
|
||||||
|
|
||||||
|
When the Dev Container starts, it automatically:
|
||||||
|
|
||||||
|
1. **Runs post-create script** (`container-server-post-create.sh`):
|
||||||
|
- Adjusts file permissions for the `node` user
|
||||||
|
- Installs dependencies: `npm install` in all packages
|
||||||
|
- Builds TypeScript SDK: `npm run build` in `open-api/typescript-sdk`
|
||||||
|
|
||||||
|
2. **Starts development servers** via VS Code tasks:
|
||||||
|
- `Immich API Server (Nest)` - API server with hot-reloading on port 2283
|
||||||
|
- `Immich Web Server (Vite)` - Web frontend with hot-reloading on port 3000
|
||||||
|
- Both servers watch for file changes and recompile automatically
|
||||||
|
|
||||||
|
3. **Configures port forwarding**:
|
||||||
|
- Web UI: http://localhost:3000 (opens automatically)
|
||||||
|
- API: http://localhost:2283
|
||||||
|
- Debug ports: 9230 (workers), 9231 (API)
|
||||||
|
|
||||||
|
:::info
|
||||||
|
The Dev Container setup replaces the `make dev` command from the traditional setup. All services start automatically when you open the container.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Accessing Services
|
||||||
|
|
||||||
|
Once running, you can access:
|
||||||
|
|
||||||
|
| Service | URL | Description |
|
||||||
|
| -------- | --------------------- | ---------------------------------------------------------------------------------------------- |
|
||||||
|
| Web UI | http://localhost:3000 | Main web interface |
|
||||||
|
| API | http://localhost:2283 | REST API endpoints (Not used directly, web UI will expose this over http://localhost:3000/api) |
|
||||||
|
| Database | localhost:5432 | PostgreSQL (username: `postgres`) (Not used directly) |
|
||||||
|
|
||||||
|
### Connecting Mobile Apps
|
||||||
|
|
||||||
|
To connect the mobile app to your Dev Container:
|
||||||
|
|
||||||
|
1. Find your machine's IP address
|
||||||
|
2. In the mobile app, use: `http://YOUR_IP:3000/api`
|
||||||
|
3. Ensure your firewall allows connections on port 2283
|
||||||
|
|
||||||
|
### Making Code Changes
|
||||||
|
|
||||||
|
- **Server code** (`/server`): Changes trigger automatic restart
|
||||||
|
- **Web code** (`/web`): Changes trigger hot module replacement
|
||||||
|
- **Database migrations**: Run `npm run sync:sql` in the server directory
|
||||||
|
- **API changes**: Regenerate TypeScript SDK with `make open-api`
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
The Dev Container supports multiple ways to run tests:
|
||||||
|
|
||||||
|
#### Using Make Commands (Recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run tests for specific components
|
||||||
|
make test-server # Server unit tests
|
||||||
|
make test-web # Web unit tests
|
||||||
|
make test-e2e # End-to-end tests
|
||||||
|
make test-cli # CLI tests
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
make test-all # Runs tests for all components
|
||||||
|
|
||||||
|
# Medium tests (integration tests)
|
||||||
|
make test-medium-dev # End-to-end tests
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using NPM Directly
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Server tests
|
||||||
|
cd /workspaces/immich/server
|
||||||
|
npm test # Run all tests
|
||||||
|
npm run test:watch # Watch mode
|
||||||
|
npm run test:cov # Coverage report
|
||||||
|
|
||||||
|
# Web tests
|
||||||
|
cd /workspaces/immich/web
|
||||||
|
npm test # Run all tests
|
||||||
|
npm run test:watch # Watch mode
|
||||||
|
|
||||||
|
# E2E tests
|
||||||
|
cd /workspaces/immich/e2e
|
||||||
|
npm run test # Run API tests
|
||||||
|
npm run test:web # Run web UI tests
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Quality Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linting
|
||||||
|
make lint-server # Lint server code
|
||||||
|
make lint-web # Lint web code
|
||||||
|
make lint-all # Lint all components
|
||||||
|
|
||||||
|
# Formatting
|
||||||
|
make format-server # Format server code
|
||||||
|
make format-web # Format web code
|
||||||
|
make format-all # Format all code
|
||||||
|
|
||||||
|
# Type checking
|
||||||
|
make check-server # Type check server
|
||||||
|
make check-web # Type check web
|
||||||
|
make check-all # Check all components
|
||||||
|
|
||||||
|
# Complete hygiene check
|
||||||
|
make hygiene-all # Runs lint, format, check, SQL sync, and audit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Additional Make Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build commands
|
||||||
|
make build-server # Build server
|
||||||
|
make build-web # Build web app
|
||||||
|
make build-all # Build everything
|
||||||
|
|
||||||
|
# API generation
|
||||||
|
make open-api # Generate OpenAPI specs
|
||||||
|
make open-api-typescript # Generate TypeScript SDK
|
||||||
|
make open-api-dart # Generate Dart SDK
|
||||||
|
|
||||||
|
# Database
|
||||||
|
make sql # Sync database schema
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
make install-server # Install server dependencies
|
||||||
|
make install-web # Install web dependencies
|
||||||
|
make install-all # Install all dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
The Dev Container is pre-configured for debugging:
|
||||||
|
|
||||||
|
1. **API Server Debugging**:
|
||||||
|
- Set breakpoints in VS Code
|
||||||
|
- Press `F5` or use "Run and Debug" panel
|
||||||
|
- Select "Attach to Server" configuration
|
||||||
|
- Debug port: 9231
|
||||||
|
|
||||||
|
2. **Worker Debugging**:
|
||||||
|
- Use "Attach to Workers" configuration
|
||||||
|
- Debug port: 9230
|
||||||
|
|
||||||
|
3. **Web Debugging**:
|
||||||
|
- Use browser DevTools
|
||||||
|
- VS Code debugger for Chrome/Edge extensions supported
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### Permission Errors
|
||||||
|
|
||||||
|
**Problem**: `EACCES` or permission denied errors
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
- The Dev Container runs as the `node` user (UID 1000)
|
||||||
|
- If your host UID differs, you may see permission issues
|
||||||
|
- Try rebuilding the container: "Dev Containers: Rebuild Container"
|
||||||
|
|
||||||
|
#### Container Won't Start
|
||||||
|
|
||||||
|
**Problem**: Dev Container fails to start or build
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Check Docker is running: `docker ps`
|
||||||
|
2. Clean Docker resources: `docker system prune -a`
|
||||||
|
3. Check available disk space
|
||||||
|
4. Review Docker Desktop resource limits
|
||||||
|
|
||||||
|
#### Port Already in Use
|
||||||
|
|
||||||
|
**Problem**: "Port 3000/2283 is already in use"
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Check for conflicting services: `lsof -i :3000` (macOS/Linux)
|
||||||
|
2. Stop conflicting services or change port mappings
|
||||||
|
3. Restart Docker Desktop
|
||||||
|
|
||||||
|
#### Upload Location Not Set
|
||||||
|
|
||||||
|
**Problem**: Errors about missing UPLOAD_LOCATION
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Set the environment variable: `export UPLOAD_LOCATION=./Library`
|
||||||
|
2. Add to your shell profile for persistence
|
||||||
|
3. Restart your terminal and VS Code
|
||||||
|
|
||||||
|
#### Database Connection Failed
|
||||||
|
|
||||||
|
**Problem**: Cannot connect to PostgreSQL
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Ensure all containers are running: `docker ps`
|
||||||
|
2. Check logs: "Dev Containers: Show Container Log"
|
||||||
|
3. Verify database credentials match environment variables
|
||||||
|
|
||||||
|
### Getting Help
|
||||||
|
|
||||||
|
If you encounter issues:
|
||||||
|
|
||||||
|
1. Check container logs: View → Output → Select "Dev Containers"
|
||||||
|
2. Rebuild without cache: "Dev Containers: Rebuild Container Without Cache"
|
||||||
|
3. Review [common Docker issues](https://docs.docker.com/desktop/troubleshoot/)
|
||||||
|
4. Ask in [Discord](https://discord.immich.app) `#help-desk-support` channel
|
||||||
|
|
||||||
|
## Mobile Development
|
||||||
|
|
||||||
|
While the Dev Container focuses on server and web development, you can connect mobile apps for testing:
|
||||||
|
|
||||||
|
### Connecting iOS/Android Apps
|
||||||
|
|
||||||
|
1. **Ensure API is accessible**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find your machine's IP
|
||||||
|
# macOS
|
||||||
|
ipconfig getifaddr en0
|
||||||
|
# Linux
|
||||||
|
hostname -I
|
||||||
|
# Windows (in WSL2)
|
||||||
|
ip addr show eth0
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Configure mobile app**:
|
||||||
|
- Server URL: `http://YOUR_IP:2283/api`
|
||||||
|
- Ensure firewall allows port 2283
|
||||||
|
|
||||||
|
3. **For full mobile development**, see the [mobile development guide](/docs/developer/setup) which covers:
|
||||||
|
- Flutter setup
|
||||||
|
- Running on simulators/devices
|
||||||
|
- Mobile-specific debugging
|
||||||
|
|
||||||
|
## Advanced Configuration
|
||||||
|
|
||||||
|
### Custom VS Code Extensions
|
||||||
|
|
||||||
|
Add extensions to `.devcontainer/devcontainer.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"your.extension-id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Additional Services
|
||||||
|
|
||||||
|
To add services (e.g., Redis Commander), modify:
|
||||||
|
|
||||||
|
1. `/docker/docker-compose.dev.yml` - Add service definition
|
||||||
|
2. `/.devcontainer/server/container-compose-overrides.yml` - Add overrides if needed
|
||||||
|
|
||||||
|
### Resource Limits
|
||||||
|
|
||||||
|
Adjust Docker Desktop resources:
|
||||||
|
|
||||||
|
- **macOS/Windows**: Docker Desktop → Settings → Resources
|
||||||
|
- **Linux**: Modify Docker daemon configuration
|
||||||
|
|
||||||
|
Recommended minimums:
|
||||||
|
|
||||||
|
- CPU: 4 cores
|
||||||
|
- Memory: 8GB
|
||||||
|
- Disk: 20GB
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- Read the [architecture overview](/docs/developer/architecture)
|
||||||
|
- Learn about [database migrations](/docs/developer/database-migrations)
|
||||||
|
- Explore [API documentation](/docs/api)
|
||||||
|
- Join `#immich` on [Discord](https://discord.immich.app)
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template.
|
Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template.
|
||||||
|
|
||||||
You can enable this feature under [`Account Settings > Features > Folder View`](https://my.immich.app/user-settings?isOpen=feature+folders)
|
You can enable this feature under [`Account Settings > Features > Folders`](https://my.immich.app/user-settings?isOpen=feature+folders)
|
||||||
|
|
||||||
## Enable folder view
|
## Enable folder view
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ Internally, Immich uses the [glob](https://www.npmjs.com/package/glob) package t
|
|||||||
|
|
||||||
### Automatic watching (EXPERIMENTAL)
|
### Automatic watching (EXPERIMENTAL)
|
||||||
|
|
||||||
This feature - currently hidden in the config file - is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan.
|
This feature is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan.
|
||||||
|
|
||||||
If your photos are on a network drive, automatic file watching likely won't work. In that case, you will have to rely on a periodic library refresh to pull in your changes.
|
If your photos are on a network drive, automatic file watching likely won't work. In that case, you will have to rely on a periodic library refresh to pull in your changes.
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ _Remember to run `docker compose up -d` to register the changes. Make sure you c
|
|||||||
|
|
||||||
These actions must be performed by the Immich administrator.
|
These actions must be performed by the Immich administrator.
|
||||||
|
|
||||||
- Click on your avatar on the upper right corner
|
- Click on your avatar in the upper right corner
|
||||||
- Click on Administration -> External Libraries
|
- Click on Administration -> External Libraries
|
||||||
- Click on Create an external library…
|
- Click on Create an external library…
|
||||||
- Select which user owns the library, this can not be changed later
|
- Select which user owns the library, this can not be changed later
|
||||||
@@ -159,9 +159,7 @@ Within seconds, the assets from the old-pics and videos folders should show up i
|
|||||||
|
|
||||||
Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template.
|
Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template.
|
||||||
|
|
||||||
You can enable this feature under [`Account Settings > Features > Folder View`](https://my.immich.app/user-settings?isOpen=feature+folders)
|
You can enable this feature under [`Account Settings > Features > Folders`](https://my.immich.app/user-settings?isOpen=feature+folders)
|
||||||
|
|
||||||
The UI is currently only available for the web; mobile will come in a subsequent release.
|
|
||||||
|
|
||||||
<img src={require('./img/folder-view-1.webp').default} width="100%" title='Folder-view' />
|
<img src={require('./img/folder-view-1.webp').default} width="100%" title='Folder-view' />
|
||||||
|
|
||||||
@@ -171,7 +169,7 @@ The UI is currently only available for the web; mobile will come in a subsequent
|
|||||||
Only an admin can do this.
|
Only an admin can do this.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
You can define a custom interval for the trigger external library rescan under Administration -> Settings -> Library.
|
You can define a custom interval for the trigger external library rescan under Administration -> Settings -> External Library.
|
||||||
You can set the scanning interval using the preset or cron format. For more information you can refer to [Crontab Guru](https://crontab.guru/).
|
You can set the scanning interval using the preset or cron format. For more information you can refer to [Crontab Guru](https://crontab.guru/).
|
||||||
|
|
||||||
<img src={require('./img/library-custom-scan-interval.webp').default} width="75%" title='Set custom scan interval for external library' />
|
<img src={require('./img/library-custom-scan-interval.webp').default} width="75%" title='Set custom scan interval for external library' />
|
||||||
|
|||||||
@@ -88,9 +88,9 @@ It will only reflect files you add.
|
|||||||
:::
|
:::
|
||||||
|
|
||||||
If the same asset is in more than one album it will only sync to the first album it's in, after that it won't sync again even if the user clicks sync albums manually.
|
If the same asset is in more than one album it will only sync to the first album it's in, after that it won't sync again even if the user clicks sync albums manually.
|
||||||
To overcome this limitation, the files must be removed from the blacklist by
|
To overcome this limitation, the files must be removed from the ignore list by
|
||||||
App settings -> Advanced -> Duplicate Assets -> Clear
|
App settings -> Advanced -> Duplicate Assets -> Clear
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
Cleaning duplicate assets from the list will cause all the previously uploaded duplicate files to be re-uploaded, the files will not actually be uploaded and will be rejected on the server side (due to duplication) but will be synchronized to the album and at the end will be added to the black list again at the end of the synchronization.
|
Cleaning duplicate assets from the list will cause all the previously uploaded duplicate files to be re-uploaded, the files will not actually be uploaded and will be rejected on the server side (due to duplication) but will be synchronized to the album and at the end will be added to the ignore list again at the end of the synchronization.
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
|
|||||||
| `HEIC` | `.heic` | :white_check_mark: | |
|
| `HEIC` | `.heic` | :white_check_mark: | |
|
||||||
| `HEIF` | `.heif` | :white_check_mark: | |
|
| `HEIF` | `.heif` | :white_check_mark: | |
|
||||||
| `JPEG 2000` | `.jp2` | :white_check_mark: | |
|
| `JPEG 2000` | `.jp2` | :white_check_mark: | |
|
||||||
| `JPEG` | `.webp` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
|
| `JPEG` | `.jpeg` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
|
||||||
| `JPEG XL` | `.jxl` | :white_check_mark: | |
|
| `JPEG XL` | `.jxl` | :white_check_mark: | |
|
||||||
| `PNG` | `.png` | :white_check_mark: | |
|
| `PNG` | `.png` | :white_check_mark: | |
|
||||||
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
|
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ In the Immich web UI:
|
|||||||
- Click Add path
|
- Click Add path
|
||||||
<img src={require('./img/add-path-button.webp').default} width="50%" title="Add Path button" />
|
<img src={require('./img/add-path-button.webp').default} width="50%" title="Add Path button" />
|
||||||
|
|
||||||
- Enter **/usr/src/app/external** as the path and click Add
|
- Enter **/home/user/photos1** as the path and click Add
|
||||||
<img src={require('./img/add-path-field.webp').default} width="50%" title="Add Path field" />
|
<img src={require('./img/add-path-field.webp').default} width="50%" title="Add Path field" />
|
||||||
|
|
||||||
- Save the new path
|
- Save the new path
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 10 KiB |
@@ -52,9 +52,9 @@ REMOTE_BACKUP_PATH="/path/to/remote/backup/directory"
|
|||||||
### Local
|
### Local
|
||||||
|
|
||||||
# Backup Immich database
|
# Backup Immich database
|
||||||
docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres > "$UPLOAD_LOCATION"/database-backup/immich-database.sql
|
docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=<DB_USERNAME> > "$UPLOAD_LOCATION"/database-backup/immich-database.sql
|
||||||
# For deduplicating backup programs such as Borg or Restic, compressing the content can increase backup size by making it harder to deduplicate. If you are using a different program or still prefer to compress, you can use the following command instead:
|
# For deduplicating backup programs such as Borg or Restic, compressing the content can increase backup size by making it harder to deduplicate. If you are using a different program or still prefer to compress, you can use the following command instead:
|
||||||
# docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres | /usr/bin/gzip --rsyncable > "$UPLOAD_LOCATION"/database-backup/immich-database.sql.gz
|
# docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=<DB_USERNAME> | /usr/bin/gzip --rsyncable > "$UPLOAD_LOCATION"/database-backup/immich-database.sql.gz
|
||||||
|
|
||||||
### Append to local Borg repository
|
### Append to local Borg repository
|
||||||
borg create "$BACKUP_PATH/immich-borg::{now}" "$UPLOAD_LOCATION" --exclude "$UPLOAD_LOCATION"/thumbs/ --exclude "$UPLOAD_LOCATION"/encoded-video/
|
borg create "$BACKUP_PATH/immich-borg::{now}" "$UPLOAD_LOCATION" --exclude "$UPLOAD_LOCATION"/thumbs/ --exclude "$UPLOAD_LOCATION"/encoded-video/
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ The default configuration looks like this:
|
|||||||
"buttonText": "Login with OAuth",
|
"buttonText": "Login with OAuth",
|
||||||
"clientId": "",
|
"clientId": "",
|
||||||
"clientSecret": "",
|
"clientSecret": "",
|
||||||
"defaultStorageQuota": 0,
|
"defaultStorageQuota": null,
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"issuerUrl": "",
|
"issuerUrl": "",
|
||||||
"mobileOverrideEnabled": false,
|
"mobileOverrideEnabled": false,
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N
|
|||||||
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
|
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
|
||||||
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
|
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
|
||||||
| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
|
| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
|
||||||
| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `./upload`<sup>\*3</sup> | server | api, microservices |
|
| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `/usr/src/app/upload` | server | api, microservices |
|
||||||
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
|
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
|
||||||
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
|
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
|
||||||
| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | |
|
| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | |
|
||||||
@@ -49,9 +49,6 @@ These environment variables are used by the `docker-compose.yml` file and do **N
|
|||||||
|
|
||||||
\*2: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead.
|
\*2: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead.
|
||||||
|
|
||||||
\*3: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`.
|
|
||||||
It only needs to be set if the Immich deployment method is changing.
|
|
||||||
|
|
||||||
## Workers
|
## Workers
|
||||||
|
|
||||||
| Variable | Description | Default | Containers |
|
| Variable | Description | Default | Containers |
|
||||||
@@ -72,22 +69,25 @@ Information on the current workers can be found [here](/docs/administration/jobs
|
|||||||
|
|
||||||
## Database
|
## Database
|
||||||
|
|
||||||
| Variable | Description | Default | Containers |
|
| Variable | Description | Default | Containers |
|
||||||
| :---------------------------------- | :--------------------------------------------------------------------------- | :--------: | :----------------------------- |
|
| :---------------------------------- | :------------------------------------------------------------------------------------- | :--------: | :----------------------------- |
|
||||||
| `DB_URL` | Database URL | | server |
|
| `DB_URL` | Database URL | | server |
|
||||||
| `DB_HOSTNAME` | Database host | `database` | server |
|
| `DB_HOSTNAME` | Database host | `database` | server |
|
||||||
| `DB_PORT` | Database port | `5432` | server |
|
| `DB_PORT` | Database port | `5432` | server |
|
||||||
| `DB_USERNAME` | Database user | `postgres` | server, database<sup>\*1</sup> |
|
| `DB_USERNAME` | Database user | `postgres` | server, database<sup>\*1</sup> |
|
||||||
| `DB_PASSWORD` | Database password | `postgres` | server, database<sup>\*1</sup> |
|
| `DB_PASSWORD` | Database password | `postgres` | server, database<sup>\*1</sup> |
|
||||||
| `DB_DATABASE_NAME` | Database name | `immich` | server, database<sup>\*1</sup> |
|
| `DB_DATABASE_NAME` | Database name | `immich` | server, database<sup>\*1</sup> |
|
||||||
| `DB_SSL_MODE` | Database SSL mode | | server |
|
| `DB_SSL_MODE` | Database SSL mode | | server |
|
||||||
| `DB_VECTOR_EXTENSION`<sup>\*2</sup> | Database vector extension (one of [`vectorchord`, `pgvector`, `pgvecto.rs`]) | | server |
|
| `DB_VECTOR_EXTENSION`<sup>\*2</sup> | Database vector extension (one of [`vectorchord`, `pgvector`, `pgvecto.rs`]) | | server |
|
||||||
| `DB_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server |
|
| `DB_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server |
|
||||||
|
| `DB_STORAGE_TYPE` | Optimize concurrent IO on SSDs or sequential IO on HDDs ([`SSD`, `HDD`])<sup>\*3</sup> | `SSD` | server |
|
||||||
|
|
||||||
\*1: The values of `DB_USERNAME`, `DB_PASSWORD`, and `DB_DATABASE_NAME` are passed to the Postgres container as the variables `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` in `docker-compose.yml`.
|
\*1: The values of `DB_USERNAME`, `DB_PASSWORD`, and `DB_DATABASE_NAME` are passed to the Postgres container as the variables `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` in `docker-compose.yml`.
|
||||||
|
|
||||||
\*2: If not provided, the appropriate extension to use is auto-detected at startup by introspecting the database. When multiple extensions are installed, the order of preference is VectorChord, pgvecto.rs, pgvector.
|
\*2: If not provided, the appropriate extension to use is auto-detected at startup by introspecting the database. When multiple extensions are installed, the order of preference is VectorChord, pgvecto.rs, pgvector.
|
||||||
|
|
||||||
|
\*3: Uses either [`postgresql.ssd.conf`](https://github.com/immich-app/base-images/blob/main/postgres/postgresql.ssd.conf) or [`postgresql.hdd.conf`](https://github.com/immich-app/base-images/blob/main/postgres/postgresql.hdd.conf) which mainly controls the Postgres `effective_io_concurrency` setting to allow for concurrenct IO on SSDs and sequential IO on HDDs.
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
All `DB_` variables must be provided to all Immich workers, including `api` and `microservices`.
|
All `DB_` variables must be provided to all Immich workers, including `api` and `microservices`.
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ alt="Dot Env Example"
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
- Change the default `DB_PASSWORD`, and add custom database connection information if necessary.
|
- Change the default `DB_PASSWORD`, and add custom database connection information if necessary.
|
||||||
- Change `DB_DATA_LOCATION` to a folder where the database will be saved to disk.
|
- Change `DB_DATA_LOCATION` to a folder (absolute path) where the database will be saved to disk.
|
||||||
- Change `UPLOAD_LOCATION` to a folder where media (uploaded and generated) will be stored.
|
- Change `UPLOAD_LOCATION` to a folder (absolute path) where media (uploaded and generated) will be stored.
|
||||||
|
|
||||||
11. Click on "**Deploy the stack**".
|
11. Click on "**Deploy the stack**".
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ This is a community contribution and not officially supported by the Immich team
|
|||||||
|
|
||||||
Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/).
|
Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/).
|
||||||
|
|
||||||
**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/charts/tree/master/community/immich).**
|
**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/apps/tree/master/trains/community/immich).**
|
||||||
:::
|
:::
|
||||||
|
|
||||||
Immich can easily be installed on TrueNAS Community Edition via the **Community** train application.
|
Immich can easily be installed on TrueNAS Community Edition via the **Community** train application.
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich"
|
|||||||
5. Click "**Save Changes**", you will be prompted to edit stack UI labels, just leave this blank and click "**Ok**"
|
5. Click "**Save Changes**", you will be prompted to edit stack UI labels, just leave this blank and click "**Ok**"
|
||||||
6. Select the cog ⚙️ next to Immich, click "**Edit Stack**", then click "**Env File**"
|
6. Select the cog ⚙️ next to Immich, click "**Edit Stack**", then click "**Env File**"
|
||||||
7. Paste the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following:
|
7. Paste the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following:
|
||||||
|
|
||||||
- `UPLOAD_LOCATION`: Create a folder in your Images Unraid share and place the **absolute** location here > For example my _"images"_ share has a folder within it called _"immich"_. If I browse to this directory in the terminal and type `pwd` the output is `/mnt/user/images/immich`. This is the exact value I need to enter as my `UPLOAD_LOCATION`
|
- `UPLOAD_LOCATION`: Create a folder in your Images Unraid share and place the **absolute** location here > For example my _"images"_ share has a folder within it called _"immich"_. If I browse to this directory in the terminal and type `pwd` the output is `/mnt/user/images/immich`. This is the exact value I need to enter as my `UPLOAD_LOCATION`
|
||||||
- `DB_DATA_LOCATION`: Change this to use an Unraid share (preferably a cache pool, e.g. `/mnt/user/appdata/postgresql/data`). This uses the `appdata` share. Do also create the `postgresql` folder, by running `mkdir /mnt/user/{share_location}/postgresql/data`. If left at default it will try to use Unraid's `/boot/config/plugins/compose.manager/projects/[stack_name]/postgres` folder which it doesn't have permissions to, resulting in this container continuously restarting.
|
- `DB_DATA_LOCATION`: Change this to use an Unraid share (preferably a cache pool, e.g. `/mnt/user/appdata/postgresql/data`). This uses the `appdata` share. Do also create the `postgresql` folder, by running `mkdir /mnt/user/{share_location}/postgresql/data`. If left at default it will try to use Unraid's `/boot/config/plugins/compose.manager/projects/[stack_name]/postgres` folder which it doesn't have permissions to, resulting in this container continuously restarting.
|
||||||
|
|
||||||
|
|||||||
1533
docs/package-lock.json
generated
1533
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -16,8 +16,9 @@
|
|||||||
"write-heading-ids": "docusaurus write-heading-ids"
|
"write-heading-ids": "docusaurus write-heading-ids"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "~3.7.0",
|
"@docusaurus/core": "~3.8.0",
|
||||||
"@docusaurus/preset-classic": "~3.7.0",
|
"@docusaurus/preset-classic": "~3.8.0",
|
||||||
|
"@docusaurus/theme-common": "~3.8.0",
|
||||||
"@mdi/js": "^7.3.67",
|
"@mdi/js": "^7.3.67",
|
||||||
"@mdi/react": "^1.6.1",
|
"@mdi/react": "^1.6.1",
|
||||||
"@mdx-js/react": "^3.0.0",
|
"@mdx-js/react": "^3.0.0",
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"docusaurus-lunr-search": "^3.3.2",
|
"docusaurus-lunr-search": "^3.3.2",
|
||||||
"docusaurus-preset-openapi": "^0.7.5",
|
"docusaurus-preset-openapi": "^0.7.5",
|
||||||
|
"lunr": "^2.3.9",
|
||||||
"postcss": "^8.4.25",
|
"postcss": "^8.4.25",
|
||||||
"prism-react-renderer": "^2.3.1",
|
"prism-react-renderer": "^2.3.1",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
@@ -35,7 +37,7 @@
|
|||||||
"url": "^0.11.0"
|
"url": "^0.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "~3.7.0",
|
"@docusaurus/module-type-aliases": "~3.8.0",
|
||||||
"@docusaurus/tsconfig": "^3.7.0",
|
"@docusaurus/tsconfig": "^3.7.0",
|
||||||
"@docusaurus/types": "^3.7.0",
|
"@docusaurus/types": "^3.7.0",
|
||||||
"prettier": "^3.2.4",
|
"prettier": "^3.2.4",
|
||||||
@@ -57,6 +59,6 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.16.0"
|
"node": "22.17.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ const guides: CommunityGuidesProps[] = [
|
|||||||
description: 'Access Immich with an end-to-end encrypted connection.',
|
description: 'Access Immich with an end-to-end encrypted connection.',
|
||||||
url: 'https://meshnet.nordvpn.com/how-to/remote-files-media-access/immich-remote-access',
|
url: 'https://meshnet.nordvpn.com/how-to/remote-files-media-access/immich-remote-access',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Trust Self Signed Certificates with Immich - OAuth Setup',
|
||||||
|
description:
|
||||||
|
'Set up Certificate Authority trust with Immich, and your private OAuth2/OpenID service, while using a private CA for HTTPS commication.',
|
||||||
|
url: 'https://github.com/immich-app/immich/discussions/18614',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function CommunityGuide({ title, description, url }: CommunityGuidesProps): JSX.Element {
|
function CommunityGuide({ title, description, url }: CommunityGuidesProps): JSX.Element {
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ import React from 'react';
|
|||||||
import { Item, Timeline } from '../components/timeline';
|
import { Item, Timeline } from '../components/timeline';
|
||||||
|
|
||||||
const releases = {
|
const releases = {
|
||||||
|
'v1.135.0': new Date(2025, 5, 18),
|
||||||
'v1.133.0': new Date(2025, 4, 21),
|
'v1.133.0': new Date(2025, 4, 21),
|
||||||
'v1.130.0': new Date(2025, 2, 25),
|
'v1.130.0': new Date(2025, 2, 25),
|
||||||
'v1.127.0': new Date(2025, 1, 26),
|
'v1.127.0': new Date(2025, 1, 26),
|
||||||
@@ -196,14 +197,6 @@ const roadmap: Item[] = [
|
|||||||
description: 'Automate tasks with workflows',
|
description: 'Automate tasks with workflows',
|
||||||
getDateLabel: () => 'Planned for 2025',
|
getDateLabel: () => 'Planned for 2025',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
done: false,
|
|
||||||
icon: mdiTableKey,
|
|
||||||
iconColor: 'gray',
|
|
||||||
title: 'Fine grained access controls',
|
|
||||||
description: 'Granular access controls for users and api keys',
|
|
||||||
getDateLabel: () => 'Planned for 2025',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
done: false,
|
done: false,
|
||||||
icon: mdiImageEdit,
|
icon: mdiImageEdit,
|
||||||
@@ -239,12 +232,26 @@ const roadmap: Item[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const milestones: Item[] = [
|
const milestones: Item[] = [
|
||||||
|
{
|
||||||
|
icon: mdiStar,
|
||||||
|
iconColor: 'gold',
|
||||||
|
title: '70,000 Stars',
|
||||||
|
description: 'Reached 70K Stars on GitHub!',
|
||||||
|
getDateLabel: withLanguage(new Date(2025, 6, 9)),
|
||||||
|
},
|
||||||
|
withRelease({
|
||||||
|
icon: mdiTableKey,
|
||||||
|
iconColor: 'gray',
|
||||||
|
title: 'Fine grained access controls',
|
||||||
|
description: 'Granular access controls for api keys',
|
||||||
|
release: 'v1.135.0',
|
||||||
|
}),
|
||||||
withRelease({
|
withRelease({
|
||||||
icon: mdiCast,
|
icon: mdiCast,
|
||||||
iconColor: 'aqua',
|
iconColor: 'aqua',
|
||||||
title: 'Google Cast (web)',
|
title: 'Google Cast (web and mobile)',
|
||||||
description: 'Cast assets to Google Cast/Chromecast compatible devices',
|
description: 'Cast assets to Google Cast/Chromecast compatible devices',
|
||||||
release: 'v1.133.0',
|
release: 'v1.135.0',
|
||||||
}),
|
}),
|
||||||
withRelease({
|
withRelease({
|
||||||
icon: mdiLockOutline,
|
icon: mdiLockOutline,
|
||||||
|
|||||||
3
docs/static/_redirects
vendored
3
docs/static/_redirects
vendored
@@ -1,4 +1,5 @@
|
|||||||
/docs /docs/overview/introduction 307
|
/docs /docs/overview/welcome 307
|
||||||
|
/docs/ /docs/overview/welcome 307
|
||||||
/docs/mobile-app-beta-program /docs/features/mobile-app 307
|
/docs/mobile-app-beta-program /docs/features/mobile-app 307
|
||||||
/docs/contribution-guidelines /docs/overview/support-the-project#contributing 307
|
/docs/contribution-guidelines /docs/overview/support-the-project#contributing 307
|
||||||
/docs/install /docs/install/docker-compose 307
|
/docs/install /docs/install/docker-compose 307
|
||||||
|
|||||||
20
docs/static/archived-versions.json
vendored
20
docs/static/archived-versions.json
vendored
@@ -1,4 +1,24 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"label": "v1.136.0",
|
||||||
|
"url": "https://v1.136.0.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.135.3",
|
||||||
|
"url": "https://v1.135.3.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.135.2",
|
||||||
|
"url": "https://v1.135.2.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.135.1",
|
||||||
|
"url": "https://v1.135.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.135.0",
|
||||||
|
"url": "https://v1.135.0.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.134.0",
|
"label": "v1.134.0",
|
||||||
"url": "https://v1.134.0.archive.immich.app"
|
"url": "https://v1.134.0.archive.immich.app"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
22.16.0
|
22.17.1
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ name: immich-e2e
|
|||||||
services:
|
services:
|
||||||
immich-server:
|
immich-server:
|
||||||
container_name: immich-e2e-server
|
container_name: immich-e2e-server
|
||||||
command: ['./start.sh']
|
|
||||||
image: immich-server:latest
|
image: immich-server:latest
|
||||||
build:
|
build:
|
||||||
context: ../
|
context: ../
|
||||||
@@ -36,10 +35,10 @@ services:
|
|||||||
- 2285:2285
|
- 2285:2285
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
image: redis:6.2-alpine@sha256:7fe72c486b910f6b1a9769c937dad5d63648ddee82e056f47417542dd40825bb
|
||||||
|
|
||||||
database:
|
database:
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:9c704fb49ce27549df00f1b096cc93f8b0c959ef087507704d74954808f78a82
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:3aef84a0a4fabbda17ef115c3019ba0c914ec73e9f6e59203674322d858b8eea
|
||||||
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
|
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
|
|||||||
1288
e2e/package-lock.json
generated
1288
e2e/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.134.0",
|
"version": "1.136.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -24,8 +24,9 @@
|
|||||||
"@immich/cli": "file:../cli",
|
"@immich/cli": "file:../cli",
|
||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@playwright/test": "^1.44.1",
|
"@playwright/test": "^1.44.1",
|
||||||
|
"@socket.io/component-emitter": "^3.1.2",
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^22.15.29",
|
"@types/node": "^22.16.4",
|
||||||
"@types/oidc-provider": "^9.0.0",
|
"@types/oidc-provider": "^9.0.0",
|
||||||
"@types/pg": "^8.15.1",
|
"@types/pg": "^8.15.1",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-organize-imports": "^4.0.0",
|
"prettier-plugin-organize-imports": "^4.0.0",
|
||||||
|
"sharp": "^0.34.0",
|
||||||
"socket.io-client": "^4.7.4",
|
"socket.io-client": "^4.7.4",
|
||||||
"supertest": "^7.0.0",
|
"supertest": "^7.0.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
@@ -52,6 +54,6 @@
|
|||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.16.0"
|
"node": "22.17.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
ReactionType,
|
ReactionType,
|
||||||
createActivity as create,
|
createActivity as create,
|
||||||
createAlbum,
|
createAlbum,
|
||||||
|
removeAssetFromAlbum,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
@@ -342,5 +343,36 @@ describe('/activities', () => {
|
|||||||
|
|
||||||
expect(status).toBe(204);
|
expect(status).toBe(204);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return empty list when asset is removed', async () => {
|
||||||
|
const album3 = await createAlbum(
|
||||||
|
{
|
||||||
|
createAlbumDto: {
|
||||||
|
albumName: 'Album 3',
|
||||||
|
assetIds: [asset.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ headers: asBearerAuth(admin.accessToken) },
|
||||||
|
);
|
||||||
|
|
||||||
|
await createActivity({ albumId: album3.id, assetId: asset.id, type: ReactionType.Like });
|
||||||
|
|
||||||
|
await removeAssetFromAlbum(
|
||||||
|
{
|
||||||
|
id: album3.id,
|
||||||
|
bulkIdsDto: {
|
||||||
|
ids: [asset.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ headers: asBearerAuth(admin.accessToken) },
|
||||||
|
);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get('/activities')
|
||||||
|
.query({ albumId: album.id })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toEqual(200);
|
||||||
|
expect(body).toEqual([]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ describe('/api-keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await utils.resetDatabase(['api_keys']);
|
await utils.resetDatabase(['api_key']);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /api-keys', () => {
|
describe('POST /api-keys', () => {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { DateTime } from 'luxon';
|
|||||||
import { randomBytes } from 'node:crypto';
|
import { randomBytes } from 'node:crypto';
|
||||||
import { readFile, writeFile } from 'node:fs/promises';
|
import { readFile, writeFile } from 'node:fs/promises';
|
||||||
import { basename, join } from 'node:path';
|
import { basename, join } from 'node:path';
|
||||||
|
import sharp from 'sharp';
|
||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||||
import { makeRandomImage } from 'src/generators';
|
import { makeRandomImage } from 'src/generators';
|
||||||
@@ -40,6 +41,40 @@ const today = DateTime.fromObject({
|
|||||||
}) as DateTime<true>;
|
}) as DateTime<true>;
|
||||||
const yesterday = today.minus({ days: 1 });
|
const yesterday = today.minus({ days: 1 });
|
||||||
|
|
||||||
|
const createTestImageWithExif = async (filename: string, exifData: Record<string, any>) => {
|
||||||
|
// Generate unique color to ensure different checksums for each image
|
||||||
|
const r = Math.floor(Math.random() * 256);
|
||||||
|
const g = Math.floor(Math.random() * 256);
|
||||||
|
const b = Math.floor(Math.random() * 256);
|
||||||
|
|
||||||
|
// Create a 100x100 solid color JPEG using Sharp
|
||||||
|
const imageBytes = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
channels: 3,
|
||||||
|
background: { r, g, b },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.jpeg({ quality: 90 })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
// Add random suffix to filename to avoid collisions
|
||||||
|
const uniqueFilename = filename.replace('.jpg', `-${randomBytes(4).toString('hex')}.jpg`);
|
||||||
|
const filepath = join(tempDir, uniqueFilename);
|
||||||
|
await writeFile(filepath, imageBytes);
|
||||||
|
|
||||||
|
// Filter out undefined values before writing EXIF
|
||||||
|
const cleanExifData = Object.fromEntries(Object.entries(exifData).filter(([, value]) => value !== undefined));
|
||||||
|
|
||||||
|
await exiftool.write(filepath, cleanExifData);
|
||||||
|
|
||||||
|
// Re-read the image bytes after EXIF has been written
|
||||||
|
const finalImageBytes = await readFile(filepath);
|
||||||
|
|
||||||
|
return { filepath, imageBytes: finalImageBytes, filename: uniqueFilename };
|
||||||
|
};
|
||||||
|
|
||||||
describe('/asset', () => {
|
describe('/asset', () => {
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
let websocket: Socket;
|
let websocket: Socket;
|
||||||
@@ -1190,6 +1225,411 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('EXIF metadata extraction', () => {
|
||||||
|
describe('Additional date tag extraction', () => {
|
||||||
|
describe('Date-time vs time-only tag handling', () => {
|
||||||
|
it('should fall back to file timestamps when only time-only tags are available', async () => {
|
||||||
|
const { imageBytes, filename } = await createTestImageWithExif('time-only-fallback.jpg', {
|
||||||
|
TimeCreated: '2023:11:15 14:30:00', // Time-only tag, should not be used for dateTimeOriginal
|
||||||
|
// Exclude all date-time tags to force fallback to file timestamps
|
||||||
|
SubSecDateTimeOriginal: undefined,
|
||||||
|
DateTimeOriginal: undefined,
|
||||||
|
SubSecCreateDate: undefined,
|
||||||
|
SubSecMediaCreateDate: undefined,
|
||||||
|
CreateDate: undefined,
|
||||||
|
MediaCreateDate: undefined,
|
||||||
|
CreationDate: undefined,
|
||||||
|
DateTimeCreated: undefined,
|
||||||
|
GPSDateTime: undefined,
|
||||||
|
DateTimeUTC: undefined,
|
||||||
|
SonyDateTime2: undefined,
|
||||||
|
GPSDateStamp: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const oldDate = new Date('2020-01-01T00:00:00.000Z');
|
||||||
|
const asset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: imageBytes,
|
||||||
|
},
|
||||||
|
fileCreatedAt: oldDate.toISOString(),
|
||||||
|
fileModifiedAt: oldDate.toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id });
|
||||||
|
|
||||||
|
const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined();
|
||||||
|
// Should fall back to file timestamps, which we set to 2020-01-01
|
||||||
|
expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe(
|
||||||
|
new Date('2020-01-01T00:00:00.000Z').getTime(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prefer DateTimeOriginal over time-only tags', async () => {
|
||||||
|
const { imageBytes, filename } = await createTestImageWithExif('datetime-over-time.jpg', {
|
||||||
|
DateTimeOriginal: '2023:10:10 10:00:00', // Should be preferred
|
||||||
|
TimeCreated: '2023:11:15 14:30:00', // Should be ignored (time-only)
|
||||||
|
});
|
||||||
|
|
||||||
|
const asset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: imageBytes,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id });
|
||||||
|
|
||||||
|
const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined();
|
||||||
|
// Should use DateTimeOriginal, not TimeCreated
|
||||||
|
expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe(
|
||||||
|
new Date('2023-10-10T10:00:00.000Z').getTime(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GPSDateTime tag extraction', () => {
|
||||||
|
it('should extract GPSDateTime with GPS coordinates', async () => {
|
||||||
|
const { imageBytes, filename } = await createTestImageWithExif('gps-datetime.jpg', {
|
||||||
|
GPSDateTime: '2023:11:15 12:30:00Z',
|
||||||
|
GPSLatitude: 37.7749,
|
||||||
|
GPSLongitude: -122.4194,
|
||||||
|
// Exclude other date tags
|
||||||
|
SubSecDateTimeOriginal: undefined,
|
||||||
|
DateTimeOriginal: undefined,
|
||||||
|
SubSecCreateDate: undefined,
|
||||||
|
SubSecMediaCreateDate: undefined,
|
||||||
|
CreateDate: undefined,
|
||||||
|
MediaCreateDate: undefined,
|
||||||
|
CreationDate: undefined,
|
||||||
|
DateTimeCreated: undefined,
|
||||||
|
TimeCreated: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const asset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: imageBytes,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id });
|
||||||
|
|
||||||
|
const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined();
|
||||||
|
expect(assetInfo.exifInfo?.latitude).toBeCloseTo(37.7749, 4);
|
||||||
|
expect(assetInfo.exifInfo?.longitude).toBeCloseTo(-122.4194, 4);
|
||||||
|
expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe(
|
||||||
|
new Date('2023-11-15T12:30:00.000Z').getTime(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('CreateDate tag extraction', () => {
|
||||||
|
it('should extract CreateDate when available', async () => {
|
||||||
|
const { imageBytes, filename } = await createTestImageWithExif('create-date.jpg', {
|
||||||
|
CreateDate: '2023:11:15 10:30:00',
|
||||||
|
// Exclude other higher priority date tags
|
||||||
|
SubSecDateTimeOriginal: undefined,
|
||||||
|
DateTimeOriginal: undefined,
|
||||||
|
SubSecCreateDate: undefined,
|
||||||
|
SubSecMediaCreateDate: undefined,
|
||||||
|
MediaCreateDate: undefined,
|
||||||
|
CreationDate: undefined,
|
||||||
|
DateTimeCreated: undefined,
|
||||||
|
TimeCreated: undefined,
|
||||||
|
GPSDateTime: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const asset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: imageBytes,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id });
|
||||||
|
|
||||||
|
const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined();
|
||||||
|
expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe(
|
||||||
|
new Date('2023-11-15T10:30:00.000Z').getTime(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GPSDateStamp tag extraction', () => {
|
||||||
|
it('should fall back to file timestamps when only date-only tags are available', async () => {
|
||||||
|
const { imageBytes, filename } = await createTestImageWithExif('gps-datestamp.jpg', {
|
||||||
|
GPSDateStamp: '2023:11:15', // Date-only tag, should not be used for dateTimeOriginal
|
||||||
|
// Note: NOT including GPSTimeStamp to avoid automatic GPSDateTime creation
|
||||||
|
GPSLatitude: 51.5074,
|
||||||
|
GPSLongitude: -0.1278,
|
||||||
|
// Explicitly exclude all testable date-time tags to force fallback to file timestamps
|
||||||
|
DateTimeOriginal: undefined,
|
||||||
|
CreateDate: undefined,
|
||||||
|
CreationDate: undefined,
|
||||||
|
GPSDateTime: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const oldDate = new Date('2020-01-01T00:00:00.000Z');
|
||||||
|
const asset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: imageBytes,
|
||||||
|
},
|
||||||
|
fileCreatedAt: oldDate.toISOString(),
|
||||||
|
fileModifiedAt: oldDate.toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id });
|
||||||
|
|
||||||
|
const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined();
|
||||||
|
expect(assetInfo.exifInfo?.latitude).toBeCloseTo(51.5074, 4);
|
||||||
|
expect(assetInfo.exifInfo?.longitude).toBeCloseTo(-0.1278, 4);
|
||||||
|
// Should fall back to file timestamps, which we set to 2020-01-01
|
||||||
|
expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe(
|
||||||
|
new Date('2020-01-01T00:00:00.000Z').getTime(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: The following EXIF date tags are NOT effectively usable with JPEG test files:
|
||||||
|
*
|
||||||
|
* NOT WRITABLE to JPEG:
|
||||||
|
* - MediaCreateDate: Can be read from video files but not written to JPEG
|
||||||
|
* - DateTimeCreated: Read-only tag in JPEG format
|
||||||
|
* - DateTimeUTC: Cannot be written to JPEG files
|
||||||
|
* - SonyDateTime2: Proprietary Sony tag, not writable to JPEG
|
||||||
|
* - SubSecMediaCreateDate: Tag not defined for JPEG format
|
||||||
|
* - SourceImageCreateTime: Non-standard insta360 tag, not writable to JPEG
|
||||||
|
*
|
||||||
|
* WRITABLE but NOT READABLE from JPEG:
|
||||||
|
* - SubSecDateTimeOriginal: Can be written but not read back from JPEG
|
||||||
|
* - SubSecCreateDate: Can be written but not read back from JPEG
|
||||||
|
*
|
||||||
|
* EFFECTIVELY TESTABLE TAGS (writable and readable):
|
||||||
|
* - DateTimeOriginal ✓
|
||||||
|
* - CreateDate ✓
|
||||||
|
* - CreationDate ✓
|
||||||
|
* - GPSDateTime ✓
|
||||||
|
*
|
||||||
|
* The metadata service correctly handles non-readable tags and will fall back to
|
||||||
|
* file timestamps when only non-readable tags are present.
|
||||||
|
*/
|
||||||
|
|
||||||
|
describe('Date tag priority order', () => {
|
||||||
|
it('should respect the complete date tag priority order', async () => {
|
||||||
|
// Test cases using only EFFECTIVELY TESTABLE tags (writable AND readable from JPEG)
|
||||||
|
const testCases = [
|
||||||
|
{
|
||||||
|
name: 'DateTimeOriginal has highest priority among testable tags',
|
||||||
|
exifData: {
|
||||||
|
DateTimeOriginal: '2023:04:04 04:00:00', // TESTABLE - highest priority among readable tags
|
||||||
|
CreateDate: '2023:05:05 05:00:00', // TESTABLE
|
||||||
|
CreationDate: '2023:07:07 07:00:00', // TESTABLE
|
||||||
|
GPSDateTime: '2023:10:10 10:00:00', // TESTABLE
|
||||||
|
},
|
||||||
|
expectedDate: '2023-04-04T04:00:00.000Z',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CreateDate when DateTimeOriginal missing',
|
||||||
|
exifData: {
|
||||||
|
CreateDate: '2023:05:05 05:00:00', // TESTABLE
|
||||||
|
CreationDate: '2023:07:07 07:00:00', // TESTABLE
|
||||||
|
GPSDateTime: '2023:10:10 10:00:00', // TESTABLE
|
||||||
|
},
|
||||||
|
expectedDate: '2023-05-05T05:00:00.000Z',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CreationDate when standard EXIF tags missing',
|
||||||
|
exifData: {
|
||||||
|
CreationDate: '2023:07:07 07:00:00', // TESTABLE
|
||||||
|
GPSDateTime: '2023:10:10 10:00:00', // TESTABLE
|
||||||
|
},
|
||||||
|
expectedDate: '2023-07-07T07:00:00.000Z',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'GPSDateTime when no other testable date tags present',
|
||||||
|
exifData: {
|
||||||
|
GPSDateTime: '2023:10:10 10:00:00', // TESTABLE
|
||||||
|
Make: 'SONY',
|
||||||
|
},
|
||||||
|
expectedDate: '2023-10-10T10:00:00.000Z',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const testCase of testCases) {
|
||||||
|
const { imageBytes, filename } = await createTestImageWithExif(
|
||||||
|
`${testCase.name.replaceAll(/\s+/g, '-').toLowerCase()}.jpg`,
|
||||||
|
testCase.exifData,
|
||||||
|
);
|
||||||
|
|
||||||
|
const asset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: imageBytes,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id });
|
||||||
|
|
||||||
|
const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
expect(assetInfo.exifInfo?.dateTimeOriginal, `Failed for: ${testCase.name}`).toBeDefined();
|
||||||
|
expect(
|
||||||
|
new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime(),
|
||||||
|
`Date mismatch for: ${testCase.name}`,
|
||||||
|
).toBe(new Date(testCase.expectedDate).getTime());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Edge cases for date tag handling', () => {
|
||||||
|
it('should fall back to file timestamps with GPSDateStamp alone', async () => {
|
||||||
|
const { imageBytes, filename } = await createTestImageWithExif('gps-datestamp-only.jpg', {
|
||||||
|
GPSDateStamp: '2023:08:08', // Date-only tag, should not be used for dateTimeOriginal
|
||||||
|
// Intentionally no GPSTimeStamp
|
||||||
|
// Exclude all other date tags
|
||||||
|
SubSecDateTimeOriginal: undefined,
|
||||||
|
DateTimeOriginal: undefined,
|
||||||
|
SubSecCreateDate: undefined,
|
||||||
|
SubSecMediaCreateDate: undefined,
|
||||||
|
CreateDate: undefined,
|
||||||
|
MediaCreateDate: undefined,
|
||||||
|
CreationDate: undefined,
|
||||||
|
DateTimeCreated: undefined,
|
||||||
|
TimeCreated: undefined,
|
||||||
|
GPSDateTime: undefined,
|
||||||
|
DateTimeUTC: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const oldDate = new Date('2020-01-01T00:00:00.000Z');
|
||||||
|
const asset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: imageBytes,
|
||||||
|
},
|
||||||
|
fileCreatedAt: oldDate.toISOString(),
|
||||||
|
fileModifiedAt: oldDate.toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id });
|
||||||
|
|
||||||
|
const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined();
|
||||||
|
// Should fall back to file timestamps, which we set to 2020-01-01
|
||||||
|
expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe(
|
||||||
|
new Date('2020-01-01T00:00:00.000Z').getTime(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle all testable date tags present to verify complete priority order', async () => {
|
||||||
|
const { imageBytes, filename } = await createTestImageWithExif('all-testable-date-tags.jpg', {
|
||||||
|
// All TESTABLE date tags to JPEG format (writable AND readable)
|
||||||
|
DateTimeOriginal: '2023:04:04 04:00:00', // TESTABLE - highest priority among readable tags
|
||||||
|
CreateDate: '2023:05:05 05:00:00', // TESTABLE
|
||||||
|
CreationDate: '2023:07:07 07:00:00', // TESTABLE
|
||||||
|
GPSDateTime: '2023:10:10 10:00:00', // TESTABLE
|
||||||
|
// Note: Excluded non-testable tags:
|
||||||
|
// SubSec tags: writable but not readable from JPEG
|
||||||
|
// Non-writable tags: MediaCreateDate, DateTimeCreated, DateTimeUTC, SonyDateTime2, etc.
|
||||||
|
// Time-only/date-only tags: already excluded from EXIF_DATE_TAGS
|
||||||
|
});
|
||||||
|
|
||||||
|
const asset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: imageBytes,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id });
|
||||||
|
|
||||||
|
const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined();
|
||||||
|
// Should use DateTimeOriginal as it has the highest priority among testable tags
|
||||||
|
expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe(
|
||||||
|
new Date('2023-04-04T04:00:00.000Z').getTime(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use CreationDate when SubSec tags are missing', async () => {
|
||||||
|
const { imageBytes, filename } = await createTestImageWithExif('creation-date-priority.jpg', {
|
||||||
|
CreationDate: '2023:07:07 07:00:00', // WRITABLE
|
||||||
|
GPSDateTime: '2023:10:10 10:00:00', // WRITABLE
|
||||||
|
// Note: DateTimeCreated, DateTimeUTC, SonyDateTime2 are NOT writable to JPEG
|
||||||
|
// Note: TimeCreated and GPSDateStamp are excluded from EXIF_DATE_TAGS (time-only/date-only)
|
||||||
|
// Exclude SubSec and standard EXIF tags
|
||||||
|
SubSecDateTimeOriginal: undefined,
|
||||||
|
DateTimeOriginal: undefined,
|
||||||
|
SubSecCreateDate: undefined,
|
||||||
|
CreateDate: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const asset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: imageBytes,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id });
|
||||||
|
|
||||||
|
const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined();
|
||||||
|
// Should use CreationDate when available
|
||||||
|
expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe(
|
||||||
|
new Date('2023-07-07T07:00:00.000Z').getTime(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip invalid date formats and use next valid tag', async () => {
|
||||||
|
const { imageBytes, filename } = await createTestImageWithExif('invalid-date-handling.jpg', {
|
||||||
|
// Note: Testing invalid date handling with only WRITABLE tags
|
||||||
|
GPSDateTime: '2023:10:10 10:00:00', // WRITABLE - Valid date
|
||||||
|
CreationDate: '2023:13:13 13:00:00', // WRITABLE - Valid date
|
||||||
|
// Note: TimeCreated excluded (time-only), DateTimeCreated not writable to JPEG
|
||||||
|
// Exclude other date tags
|
||||||
|
SubSecDateTimeOriginal: undefined,
|
||||||
|
DateTimeOriginal: undefined,
|
||||||
|
SubSecCreateDate: undefined,
|
||||||
|
CreateDate: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const asset = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: {
|
||||||
|
filename,
|
||||||
|
bytes: imageBytes,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id });
|
||||||
|
|
||||||
|
const assetInfo = await getAssetInfo({ id: asset.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
expect(assetInfo.exifInfo?.dateTimeOriginal).toBeDefined();
|
||||||
|
// Should skip invalid dates and use the first valid one (GPSDateTime)
|
||||||
|
expect(new Date(assetInfo.exifInfo!.dateTimeOriginal!).getTime()).toBe(
|
||||||
|
new Date('2023-10-10T10:00:00.000Z').getTime(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('POST /assets/exist', () => {
|
describe('POST /assets/exist', () => {
|
||||||
it('ignores invalid deviceAssetIds', async () => {
|
it('ignores invalid deviceAssetIds', async () => {
|
||||||
const response = await utils.checkExistingAssets(user1.accessToken, {
|
const response = await utils.checkExistingAssets(user1.accessToken, {
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
import { LoginResponseDto, login, signUpAdmin } from '@immich/sdk';
|
|
||||||
import { loginDto, signupDto } from 'src/fixtures';
|
|
||||||
import { errorDto, loginResponseDto, signupResponseDto } from 'src/responses';
|
|
||||||
import { app, utils } from 'src/utils';
|
|
||||||
import request from 'supertest';
|
|
||||||
import { beforeEach, describe, expect, it } from 'vitest';
|
|
||||||
|
|
||||||
const { email, password } = signupDto.admin;
|
|
||||||
|
|
||||||
describe(`/auth/admin-sign-up`, () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await utils.resetDatabase();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('POST /auth/admin-sign-up', () => {
|
|
||||||
it(`should sign up the admin`, async () => {
|
|
||||||
const { status, body } = await request(app).post('/auth/admin-sign-up').send(signupDto.admin);
|
|
||||||
expect(status).toBe(201);
|
|
||||||
expect(body).toEqual(signupResponseDto.admin);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not allow a second admin to sign up', async () => {
|
|
||||||
await signUpAdmin({ signUpDto: signupDto.admin });
|
|
||||||
|
|
||||||
const { status, body } = await request(app).post('/auth/admin-sign-up').send(signupDto.admin);
|
|
||||||
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.alreadyHasAdmin);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('/auth/*', () => {
|
|
||||||
let admin: LoginResponseDto;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await utils.resetDatabase();
|
|
||||||
await signUpAdmin({ signUpDto: signupDto.admin });
|
|
||||||
admin = await login({ loginCredentialDto: loginDto.admin });
|
|
||||||
});
|
|
||||||
|
|
||||||
describe(`POST /auth/login`, () => {
|
|
||||||
it('should reject an incorrect password', async () => {
|
|
||||||
const { status, body } = await request(app).post('/auth/login').send({ email, password: 'incorrect' });
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.incorrectLogin);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should accept a correct password', async () => {
|
|
||||||
const { status, body, headers } = await request(app).post('/auth/login').send({ email, password });
|
|
||||||
expect(status).toBe(201);
|
|
||||||
expect(body).toEqual(loginResponseDto.admin);
|
|
||||||
|
|
||||||
const token = body.accessToken;
|
|
||||||
expect(token).toBeDefined();
|
|
||||||
|
|
||||||
const cookies = headers['set-cookie'];
|
|
||||||
expect(cookies).toHaveLength(3);
|
|
||||||
expect(cookies[0].split(';').map((item) => item.trim())).toEqual([
|
|
||||||
`immich_access_token=${token}`,
|
|
||||||
'Max-Age=34560000',
|
|
||||||
'Path=/',
|
|
||||||
expect.stringContaining('Expires='),
|
|
||||||
'HttpOnly',
|
|
||||||
'SameSite=Lax',
|
|
||||||
]);
|
|
||||||
expect(cookies[1].split(';').map((item) => item.trim())).toEqual([
|
|
||||||
'immich_auth_type=password',
|
|
||||||
'Max-Age=34560000',
|
|
||||||
'Path=/',
|
|
||||||
expect.stringContaining('Expires='),
|
|
||||||
'HttpOnly',
|
|
||||||
'SameSite=Lax',
|
|
||||||
]);
|
|
||||||
expect(cookies[2].split(';').map((item) => item.trim())).toEqual([
|
|
||||||
'immich_is_authenticated=true',
|
|
||||||
'Max-Age=34560000',
|
|
||||||
'Path=/',
|
|
||||||
expect.stringContaining('Expires='),
|
|
||||||
'SameSite=Lax',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('POST /auth/validateToken', () => {
|
|
||||||
it('should reject an invalid token', async () => {
|
|
||||||
const { status, body } = await request(app).post(`/auth/validateToken`).set('Authorization', 'Bearer 123');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.invalidToken);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should accept a valid token', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post(`/auth/validateToken`)
|
|
||||||
.send({})
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body).toEqual({ authStatus: true });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('POST /auth/change-password', () => {
|
|
||||||
it('should require the current password', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post(`/auth/change-password`)
|
|
||||||
.send({ password: 'wrong-password', newPassword: 'Password1234' })
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.wrongPassword);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should change the password', async () => {
|
|
||||||
const { status } = await request(app)
|
|
||||||
.post(`/auth/change-password`)
|
|
||||||
.send({ password, newPassword: 'Password1234' })
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
expect(status).toBe(200);
|
|
||||||
|
|
||||||
await login({
|
|
||||||
loginCredentialDto: {
|
|
||||||
email: 'admin@immich.cloud',
|
|
||||||
password: 'Password1234',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('POST /auth/logout', () => {
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post(`/auth/logout`);
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should logout the user', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post(`/auth/logout`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body).toEqual({
|
|
||||||
successful: true,
|
|
||||||
redirectUri: '/auth/login?autoLaunch=0',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
createMemory,
|
createMemory,
|
||||||
getMemory,
|
getMemory,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
import { createUserDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, asBearerAuth, utils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
@@ -17,7 +17,6 @@ describe('/memories', () => {
|
|||||||
let user: LoginResponseDto;
|
let user: LoginResponseDto;
|
||||||
let adminAsset: AssetMediaResponseDto;
|
let adminAsset: AssetMediaResponseDto;
|
||||||
let userAsset1: AssetMediaResponseDto;
|
let userAsset1: AssetMediaResponseDto;
|
||||||
let userAsset2: AssetMediaResponseDto;
|
|
||||||
let userMemory: MemoryResponseDto;
|
let userMemory: MemoryResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@@ -25,10 +24,9 @@ describe('/memories', () => {
|
|||||||
|
|
||||||
admin = await utils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
user = await utils.userSetup(admin.accessToken, createUserDto.user1);
|
user = await utils.userSetup(admin.accessToken, createUserDto.user1);
|
||||||
[adminAsset, userAsset1, userAsset2] = await Promise.all([
|
[adminAsset, userAsset1] = await Promise.all([
|
||||||
utils.createAsset(admin.accessToken),
|
utils.createAsset(admin.accessToken),
|
||||||
utils.createAsset(user.accessToken),
|
utils.createAsset(user.accessToken),
|
||||||
utils.createAsset(user.accessToken),
|
|
||||||
]);
|
]);
|
||||||
userMemory = await createMemory(
|
userMemory = await createMemory(
|
||||||
{
|
{
|
||||||
@@ -43,121 +41,7 @@ describe('/memories', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /memories', () => {
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/memories');
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('POST /memories', () => {
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).post('/memories');
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate data when type is on this day', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/memories')
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
|
||||||
.send({
|
|
||||||
type: 'on_this_day',
|
|
||||||
data: {},
|
|
||||||
memoryAt: new Date(2021).toISOString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(
|
|
||||||
errorDto.badRequest(['data.year must be a positive number', 'data.year must be an integer number']),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a new memory', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/memories')
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
|
||||||
.send({
|
|
||||||
type: 'on_this_day',
|
|
||||||
data: { year: 2021 },
|
|
||||||
memoryAt: new Date(2021).toISOString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(status).toBe(201);
|
|
||||||
expect(body).toEqual({
|
|
||||||
id: expect.any(String),
|
|
||||||
type: 'on_this_day',
|
|
||||||
data: { year: 2021 },
|
|
||||||
createdAt: expect.any(String),
|
|
||||||
updatedAt: expect.any(String),
|
|
||||||
isSaved: false,
|
|
||||||
memoryAt: expect.any(String),
|
|
||||||
ownerId: user.userId,
|
|
||||||
assets: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a new memory (with assets)', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/memories')
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
|
||||||
.send({
|
|
||||||
type: 'on_this_day',
|
|
||||||
data: { year: 2021 },
|
|
||||||
memoryAt: new Date(2021).toISOString(),
|
|
||||||
assetIds: [userAsset1.id, userAsset2.id],
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(status).toBe(201);
|
|
||||||
expect(body).toMatchObject({
|
|
||||||
id: expect.any(String),
|
|
||||||
assets: expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: userAsset1.id }),
|
|
||||||
expect.objectContaining({ id: userAsset2.id }),
|
|
||||||
]),
|
|
||||||
});
|
|
||||||
expect(body.assets).toHaveLength(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a new memory and ignore assets the user does not have access to', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.post('/memories')
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
|
||||||
.send({
|
|
||||||
type: 'on_this_day',
|
|
||||||
data: { year: 2021 },
|
|
||||||
memoryAt: new Date(2021).toISOString(),
|
|
||||||
assetIds: [userAsset1.id, adminAsset.id],
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(status).toBe(201);
|
|
||||||
expect(body).toMatchObject({
|
|
||||||
id: expect.any(String),
|
|
||||||
assets: [expect.objectContaining({ id: userAsset1.id })],
|
|
||||||
});
|
|
||||||
expect(body.assets).toHaveLength(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('GET /memories/:id', () => {
|
describe('GET /memories/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get(`/memories/${uuidDto.invalid}`);
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require a valid id', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get(`/memories/${uuidDto.invalid}`)
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require access', async () => {
|
it('should require access', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/memories/${userMemory.id}`)
|
.get(`/memories/${userMemory.id}`)
|
||||||
@@ -176,22 +60,6 @@ describe('/memories', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /memories/:id', () => {
|
describe('PUT /memories/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).put(`/memories/${uuidDto.invalid}`).send({ isSaved: true });
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require a valid id', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/memories/${uuidDto.invalid}`)
|
|
||||||
.send({ isSaved: true })
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require access', async () => {
|
it('should require access', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/memories/${userMemory.id}`)
|
.put(`/memories/${userMemory.id}`)
|
||||||
@@ -218,23 +86,6 @@ describe('/memories', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /memories/:id/assets', () => {
|
describe('PUT /memories/:id/assets', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/memories/${userMemory.id}/assets`)
|
|
||||||
.send({ ids: [userAsset1.id] });
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require a valid id', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/memories/${uuidDto.invalid}/assets`)
|
|
||||||
.send({ ids: [userAsset1.id] })
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require access', async () => {
|
it('should require access', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/memories/${userMemory.id}/assets`)
|
.put(`/memories/${userMemory.id}/assets`)
|
||||||
@@ -244,15 +95,6 @@ describe('/memories', () => {
|
|||||||
expect(body).toEqual(errorDto.noPermission);
|
expect(body).toEqual(errorDto.noPermission);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require a valid asset id', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/memories/${userMemory.id}/assets`)
|
|
||||||
.send({ ids: [uuidDto.invalid] })
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['each value in ids must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require asset access', async () => {
|
it('should require asset access', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/memories/${userMemory.id}/assets`)
|
.put(`/memories/${userMemory.id}/assets`)
|
||||||
@@ -279,23 +121,6 @@ describe('/memories', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /memories/:id/assets', () => {
|
describe('DELETE /memories/:id/assets', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.delete(`/memories/${userMemory.id}/assets`)
|
|
||||||
.send({ ids: [userAsset1.id] });
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require a valid id', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.delete(`/memories/${uuidDto.invalid}/assets`)
|
|
||||||
.send({ ids: [userAsset1.id] })
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require access', async () => {
|
it('should require access', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/memories/${userMemory.id}/assets`)
|
.delete(`/memories/${userMemory.id}/assets`)
|
||||||
@@ -305,15 +130,6 @@ describe('/memories', () => {
|
|||||||
expect(body).toEqual(errorDto.noPermission);
|
expect(body).toEqual(errorDto.noPermission);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require a valid asset id', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.delete(`/memories/${userMemory.id}/assets`)
|
|
||||||
.send({ ids: [uuidDto.invalid] })
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['each value in ids must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should only remove assets in the memory', async () => {
|
it('should only remove assets in the memory', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/memories/${userMemory.id}/assets`)
|
.delete(`/memories/${userMemory.id}/assets`)
|
||||||
@@ -340,21 +156,6 @@ describe('/memories', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /memories/:id', () => {
|
describe('DELETE /memories/:id', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).delete(`/memories/${uuidDto.invalid}`);
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require a valid id', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.delete(`/memories/${uuidDto.invalid}`)
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`);
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require access', async () => {
|
it('should require access', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/memories/${userMemory.id}`)
|
.delete(`/memories/${userMemory.id}`)
|
||||||
|
|||||||
@@ -227,6 +227,21 @@ describe(`/oauth`, () => {
|
|||||||
expect(user.storageLabel).toBe('user-username');
|
expect(user.storageLabel).toBe('user-username');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set the admin status from a role claim', async () => {
|
||||||
|
const callbackParams = await loginWithOAuth(OAuthUser.WITH_ROLE);
|
||||||
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
|
expect(status).toBe(201);
|
||||||
|
expect(body).toMatchObject({
|
||||||
|
accessToken: expect.any(String),
|
||||||
|
userId: expect.any(String),
|
||||||
|
userEmail: 'oauth-with-role@immich.app',
|
||||||
|
isAdmin: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await getMyUser({ headers: asBearerAuth(body.accessToken) });
|
||||||
|
expect(user.isAdmin).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('should work with RS256 signed tokens', async () => {
|
it('should work with RS256 signed tokens', async () => {
|
||||||
await setupOAuth(admin.accessToken, {
|
await setupOAuth(admin.accessToken, {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|||||||
@@ -11,11 +11,32 @@ describe('/people', () => {
|
|||||||
let hiddenPerson: PersonResponseDto;
|
let hiddenPerson: PersonResponseDto;
|
||||||
let multipleAssetsPerson: PersonResponseDto;
|
let multipleAssetsPerson: PersonResponseDto;
|
||||||
|
|
||||||
|
let nameAlicePerson: PersonResponseDto;
|
||||||
|
let nameBobPerson: PersonResponseDto;
|
||||||
|
let nameCharliePerson: PersonResponseDto;
|
||||||
|
let nameNullPerson4Assets: PersonResponseDto;
|
||||||
|
let nameNullPerson3Assets: PersonResponseDto;
|
||||||
|
let nameNullPerson1Asset: PersonResponseDto;
|
||||||
|
let nameBillPersonFavourite: PersonResponseDto;
|
||||||
|
let nameFreddyPersonFavourite: PersonResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await utils.resetDatabase();
|
await utils.resetDatabase();
|
||||||
admin = await utils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
|
|
||||||
[visiblePerson, hiddenPerson, multipleAssetsPerson] = await Promise.all([
|
[
|
||||||
|
visiblePerson,
|
||||||
|
hiddenPerson,
|
||||||
|
multipleAssetsPerson,
|
||||||
|
nameCharliePerson,
|
||||||
|
nameBobPerson,
|
||||||
|
nameAlicePerson,
|
||||||
|
nameNullPerson4Assets,
|
||||||
|
nameNullPerson3Assets,
|
||||||
|
nameNullPerson1Asset,
|
||||||
|
nameBillPersonFavourite,
|
||||||
|
nameFreddyPersonFavourite,
|
||||||
|
] = await Promise.all([
|
||||||
utils.createPerson(admin.accessToken, {
|
utils.createPerson(admin.accessToken, {
|
||||||
name: 'visible_person',
|
name: 'visible_person',
|
||||||
}),
|
}),
|
||||||
@@ -26,10 +47,39 @@ describe('/people', () => {
|
|||||||
utils.createPerson(admin.accessToken, {
|
utils.createPerson(admin.accessToken, {
|
||||||
name: 'multiple_assets_person',
|
name: 'multiple_assets_person',
|
||||||
}),
|
}),
|
||||||
|
// --- Setup for the specific sorting test ---
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: 'Charlie',
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: 'Bob',
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: 'Alice',
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: '',
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: '',
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: '',
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: 'Bill',
|
||||||
|
isFavorite: true,
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: 'Freddy',
|
||||||
|
isFavorite: true,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const asset1 = await utils.createAsset(admin.accessToken);
|
const asset1 = await utils.createAsset(admin.accessToken);
|
||||||
const asset2 = await utils.createAsset(admin.accessToken);
|
const asset2 = await utils.createAsset(admin.accessToken);
|
||||||
|
const asset3 = await utils.createAsset(admin.accessToken);
|
||||||
|
const asset4 = await utils.createAsset(admin.accessToken);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
|
||||||
@@ -37,6 +87,27 @@ describe('/people', () => {
|
|||||||
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
||||||
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
||||||
utils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }),
|
||||||
|
utils.createFace({ assetId: asset3.id, personId: multipleAssetsPerson.id }), // 4 assets
|
||||||
|
// Named persons
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameCharliePerson.id }), // 1 asset
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameBobPerson.id }),
|
||||||
|
utils.createFace({ assetId: asset2.id, personId: nameBobPerson.id }), // 2 assets
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameAlicePerson.id }), // 1 asset
|
||||||
|
// Null-named person 4 assets
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameNullPerson4Assets.id }),
|
||||||
|
utils.createFace({ assetId: asset2.id, personId: nameNullPerson4Assets.id }),
|
||||||
|
utils.createFace({ assetId: asset3.id, personId: nameNullPerson4Assets.id }),
|
||||||
|
utils.createFace({ assetId: asset4.id, personId: nameNullPerson4Assets.id }), // 4 assets
|
||||||
|
// Null-named person 3 assets
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameNullPerson3Assets.id }),
|
||||||
|
utils.createFace({ assetId: asset2.id, personId: nameNullPerson3Assets.id }),
|
||||||
|
utils.createFace({ assetId: asset3.id, personId: nameNullPerson3Assets.id }), // 3 assets
|
||||||
|
// Null-named person 1 asset
|
||||||
|
utils.createFace({ assetId: asset3.id, personId: nameNullPerson1Asset.id }),
|
||||||
|
// Favourite People
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameFreddyPersonFavourite.id }),
|
||||||
|
utils.createFace({ assetId: asset2.id, personId: nameFreddyPersonFavourite.id }),
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameBillPersonFavourite.id }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -51,27 +122,66 @@ describe('/people', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
total: 3,
|
total: 11,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [
|
people: [
|
||||||
|
expect.objectContaining({ name: 'Freddy' }),
|
||||||
|
expect.objectContaining({ name: 'Bill' }),
|
||||||
expect.objectContaining({ name: 'multiple_assets_person' }),
|
expect.objectContaining({ name: 'multiple_assets_person' }),
|
||||||
|
expect.objectContaining({ name: 'Bob' }),
|
||||||
|
expect.objectContaining({ name: 'Alice' }),
|
||||||
|
expect.objectContaining({ name: 'Charlie' }),
|
||||||
expect.objectContaining({ name: 'visible_person' }),
|
expect.objectContaining({ name: 'visible_person' }),
|
||||||
expect.objectContaining({ name: 'hidden_person' }),
|
expect.objectContaining({ id: nameNullPerson4Assets.id, name: '' }),
|
||||||
|
expect.objectContaining({ id: nameNullPerson3Assets.id, name: '' }),
|
||||||
|
expect.objectContaining({ name: 'hidden_person' }), // Should really be before the null names
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should sort visible people by asset count (desc), then by name (asc, nulls last)', async () => {
|
||||||
|
const { status, body } = await request(app).get('/people').set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.hasNextPage).toBe(false);
|
||||||
|
expect(body.total).toBe(11); // All persons
|
||||||
|
expect(body.hidden).toBe(1); // 'hidden_person'
|
||||||
|
|
||||||
|
const people = body.people as PersonResponseDto[];
|
||||||
|
|
||||||
|
expect(people.map((p) => p.id)).toEqual([
|
||||||
|
nameFreddyPersonFavourite.id, // name: 'Freddy', count: 2
|
||||||
|
nameBillPersonFavourite.id, // name: 'Bill', count: 1
|
||||||
|
multipleAssetsPerson.id, // name: 'multiple_assets_person', count: 3
|
||||||
|
nameBobPerson.id, // name: 'Bob', count: 2
|
||||||
|
nameAlicePerson.id, // name: 'Alice', count: 1
|
||||||
|
nameCharliePerson.id, // name: 'Charlie', count: 1
|
||||||
|
visiblePerson.id, // name: 'visible_person', count: 1
|
||||||
|
nameNullPerson4Assets.id, // name: '', count: 4
|
||||||
|
nameNullPerson3Assets.id, // name: '', count: 3
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(people.some((p) => p.id === hiddenPerson.id)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return only visible people', async () => {
|
it('should return only visible people', async () => {
|
||||||
const { status, body } = await request(app).get('/people').set('Authorization', `Bearer ${admin.accessToken}`);
|
const { status, body } = await request(app).get('/people').set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
total: 3,
|
total: 11,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [
|
people: [
|
||||||
|
expect.objectContaining({ name: 'Freddy' }),
|
||||||
|
expect.objectContaining({ name: 'Bill' }),
|
||||||
expect.objectContaining({ name: 'multiple_assets_person' }),
|
expect.objectContaining({ name: 'multiple_assets_person' }),
|
||||||
|
expect.objectContaining({ name: 'Bob' }),
|
||||||
|
expect.objectContaining({ name: 'Alice' }),
|
||||||
|
expect.objectContaining({ name: 'Charlie' }),
|
||||||
expect.objectContaining({ name: 'visible_person' }),
|
expect.objectContaining({ name: 'visible_person' }),
|
||||||
|
expect.objectContaining({ id: nameNullPerson4Assets.id, name: '' }),
|
||||||
|
expect.objectContaining({ id: nameNullPerson3Assets.id, name: '' }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -80,14 +190,14 @@ describe('/people', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/people')
|
.get('/people')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.query({ withHidden: true, page: 2, size: 1 });
|
.query({ withHidden: true, page: 5, size: 1 });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: true,
|
hasNextPage: true,
|
||||||
total: 3,
|
total: 11,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [expect.objectContaining({ name: 'visible_person' })],
|
people: [expect.objectContaining({ name: 'Alice' })],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -128,7 +238,7 @@ describe('/people', () => {
|
|||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual(expect.objectContaining({ assets: 2 }));
|
expect(body).toEqual(expect.objectContaining({ assets: 3 }));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -117,8 +117,25 @@ describe('/shared-links', () => {
|
|||||||
const resp = await request(shareUrl).get(`/${linkWithAssets.key}`);
|
const resp = await request(shareUrl).get(`/${linkWithAssets.key}`);
|
||||||
expect(resp.status).toBe(200);
|
expect(resp.status).toBe(200);
|
||||||
expect(resp.header['content-type']).toContain('text/html');
|
expect(resp.header['content-type']).toContain('text/html');
|
||||||
|
expect(resp.text).toContain(`<meta property="og:image" content="http://127.0.0.1:2285`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fall back to my.immich.app og:image meta tag for shared asset if Host header is not present', async () => {
|
||||||
|
const resp = await request(shareUrl).get(`/${linkWithAssets.key}`).set('Host', '');
|
||||||
|
expect(resp.status).toBe(200);
|
||||||
|
expect(resp.header['content-type']).toContain('text/html');
|
||||||
expect(resp.text).toContain(`<meta property="og:image" content="https://my.immich.app`);
|
expect(resp.text).toContain(`<meta property="og:image" content="https://my.immich.app`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return 404 for an invalid shared link', async () => {
|
||||||
|
const resp = await request(shareUrl).get(`/invalid-key`);
|
||||||
|
expect(resp.status).toBe(404);
|
||||||
|
expect(resp.header['content-type']).toContain('text/html');
|
||||||
|
expect(resp.text).not.toContain(`og:type`);
|
||||||
|
expect(resp.text).not.toContain(`og:title`);
|
||||||
|
expect(resp.text).not.toContain(`og:description`);
|
||||||
|
expect(resp.text).not.toContain(`og:image`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /shared-links', () => {
|
describe('GET /shared-links', () => {
|
||||||
|
|||||||
@@ -15,12 +15,6 @@ describe('/system-config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /system-config', () => {
|
describe('PUT /system-config', () => {
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).put('/system-config');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should always return the new config', async () => {
|
it('should always return the new config', async () => {
|
||||||
const config = await getSystemConfig(admin.accessToken);
|
const config = await getSystemConfig(admin.accessToken);
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ describe('/tags', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
// tagging assets eventually triggers metadata extraction which can impact other tests
|
// tagging assets eventually triggers metadata extraction which can impact other tests
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||||
await utils.resetDatabase(['tags']);
|
await utils.resetDatabase(['tag']);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /tags', () => {
|
describe('POST /tags', () => {
|
||||||
|
|||||||
@@ -1,230 +0,0 @@
|
|||||||
import {
|
|
||||||
AssetMediaResponseDto,
|
|
||||||
AssetVisibility,
|
|
||||||
LoginResponseDto,
|
|
||||||
SharedLinkType,
|
|
||||||
TimeBucketAssetResponseDto,
|
|
||||||
} from '@immich/sdk';
|
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import { createUserDto } from 'src/fixtures';
|
|
||||||
import { errorDto } from 'src/responses';
|
|
||||||
import { app, utils } from 'src/utils';
|
|
||||||
import request from 'supertest';
|
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
|
||||||
|
|
||||||
// TODO this should probably be a test util function
|
|
||||||
const today = DateTime.fromObject({
|
|
||||||
year: 2023,
|
|
||||||
month: 11,
|
|
||||||
day: 3,
|
|
||||||
}) as DateTime<true>;
|
|
||||||
const yesterday = today.minus({ days: 1 });
|
|
||||||
|
|
||||||
describe('/timeline', () => {
|
|
||||||
let admin: LoginResponseDto;
|
|
||||||
let user: LoginResponseDto;
|
|
||||||
let timeBucketUser: LoginResponseDto;
|
|
||||||
|
|
||||||
let user1Assets: AssetMediaResponseDto[];
|
|
||||||
let user2Assets: AssetMediaResponseDto[];
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await utils.resetDatabase();
|
|
||||||
admin = await utils.adminSetup({ onboarding: false });
|
|
||||||
[user, timeBucketUser] = await Promise.all([
|
|
||||||
utils.userSetup(admin.accessToken, createUserDto.create('1')),
|
|
||||||
utils.userSetup(admin.accessToken, createUserDto.create('time-bucket')),
|
|
||||||
]);
|
|
||||||
|
|
||||||
user1Assets = await Promise.all([
|
|
||||||
utils.createAsset(user.accessToken),
|
|
||||||
utils.createAsset(user.accessToken),
|
|
||||||
utils.createAsset(user.accessToken, {
|
|
||||||
isFavorite: true,
|
|
||||||
fileCreatedAt: yesterday.toISO(),
|
|
||||||
fileModifiedAt: yesterday.toISO(),
|
|
||||||
assetData: { filename: 'example.mp4' },
|
|
||||||
}),
|
|
||||||
utils.createAsset(user.accessToken),
|
|
||||||
utils.createAsset(user.accessToken),
|
|
||||||
]);
|
|
||||||
|
|
||||||
user2Assets = await Promise.all([
|
|
||||||
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-01-01').toISOString() }),
|
|
||||||
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-10').toISOString() }),
|
|
||||||
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-11').toISOString() }),
|
|
||||||
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-11').toISOString() }),
|
|
||||||
utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-12').toISOString() }),
|
|
||||||
]);
|
|
||||||
|
|
||||||
await utils.deleteAssets(timeBucketUser.accessToken, [user2Assets[4].id]);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('GET /timeline/buckets', () => {
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/timeline/buckets');
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get time buckets by month', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get('/timeline/buckets')
|
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`);
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
{ count: 3, timeBucket: '1970-02-01' },
|
|
||||||
{ count: 1, timeBucket: '1970-01-01' },
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not allow access for unrelated shared links', async () => {
|
|
||||||
const sharedLink = await utils.createSharedLink(user.accessToken, {
|
|
||||||
type: SharedLinkType.Individual,
|
|
||||||
assetIds: user1Assets.map(({ id }) => id),
|
|
||||||
});
|
|
||||||
|
|
||||||
const { status, body } = await request(app).get('/timeline/buckets').query({ key: sharedLink.key });
|
|
||||||
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(errorDto.noPermission);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return error if time bucket is requested with partners asset and archived', async () => {
|
|
||||||
const req1 = await request(app)
|
|
||||||
.get('/timeline/buckets')
|
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
|
||||||
.query({ withPartners: true, visibility: AssetVisibility.Archive });
|
|
||||||
|
|
||||||
expect(req1.status).toBe(400);
|
|
||||||
expect(req1.body).toEqual(errorDto.badRequest());
|
|
||||||
|
|
||||||
const req2 = await request(app)
|
|
||||||
.get('/timeline/buckets')
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
|
||||||
.query({ withPartners: true, visibility: undefined });
|
|
||||||
|
|
||||||
expect(req2.status).toBe(400);
|
|
||||||
expect(req2.body).toEqual(errorDto.badRequest());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return error if time bucket is requested with partners asset and favorite', async () => {
|
|
||||||
const req1 = await request(app)
|
|
||||||
.get('/timeline/buckets')
|
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
|
||||||
.query({ withPartners: true, isFavorite: true });
|
|
||||||
|
|
||||||
expect(req1.status).toBe(400);
|
|
||||||
expect(req1.body).toEqual(errorDto.badRequest());
|
|
||||||
|
|
||||||
const req2 = await request(app)
|
|
||||||
.get('/timeline/buckets')
|
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
|
||||||
.query({ withPartners: true, isFavorite: false });
|
|
||||||
|
|
||||||
expect(req2.status).toBe(400);
|
|
||||||
expect(req2.body).toEqual(errorDto.badRequest());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return error if time bucket is requested with partners asset and trash', async () => {
|
|
||||||
const req = await request(app)
|
|
||||||
.get('/timeline/buckets')
|
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
|
||||||
.query({ withPartners: true, isTrashed: true });
|
|
||||||
|
|
||||||
expect(req.status).toBe(400);
|
|
||||||
expect(req.body).toEqual(errorDto.badRequest());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('GET /timeline/bucket', () => {
|
|
||||||
it('should require authentication', async () => {
|
|
||||||
const { status, body } = await request(app).get('/timeline/bucket').query({
|
|
||||||
timeBucket: '1900-01-01',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(status).toBe(401);
|
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle 5 digit years', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get('/timeline/bucket')
|
|
||||||
.query({ timeBucket: '012345-01-01' })
|
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`);
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body).toEqual({
|
|
||||||
city: [],
|
|
||||||
country: [],
|
|
||||||
duration: [],
|
|
||||||
id: [],
|
|
||||||
visibility: [],
|
|
||||||
isFavorite: [],
|
|
||||||
isImage: [],
|
|
||||||
isTrashed: [],
|
|
||||||
livePhotoVideoId: [],
|
|
||||||
fileCreatedAt: [],
|
|
||||||
localOffsetHours: [],
|
|
||||||
ownerId: [],
|
|
||||||
projectionType: [],
|
|
||||||
ratio: [],
|
|
||||||
status: [],
|
|
||||||
thumbhash: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO enable date string validation while still accepting 5 digit years
|
|
||||||
// it('should fail if time bucket is invalid', async () => {
|
|
||||||
// const { status, body } = await request(app)
|
|
||||||
// .get('/timeline/bucket')
|
|
||||||
// .set('Authorization', `Bearer ${user.accessToken}`)
|
|
||||||
// .query({ timeBucket: 'foo' });
|
|
||||||
|
|
||||||
// expect(status).toBe(400);
|
|
||||||
// expect(body).toEqual(errorDto.badRequest);
|
|
||||||
// });
|
|
||||||
|
|
||||||
it('should return time bucket', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get('/timeline/bucket')
|
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
|
||||||
.query({ timeBucket: '1970-02-10' });
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body).toEqual({
|
|
||||||
city: [],
|
|
||||||
country: [],
|
|
||||||
duration: [],
|
|
||||||
id: [],
|
|
||||||
visibility: [],
|
|
||||||
isFavorite: [],
|
|
||||||
isImage: [],
|
|
||||||
isTrashed: [],
|
|
||||||
livePhotoVideoId: [],
|
|
||||||
fileCreatedAt: [],
|
|
||||||
localOffsetHours: [],
|
|
||||||
ownerId: [],
|
|
||||||
projectionType: [],
|
|
||||||
ratio: [],
|
|
||||||
status: [],
|
|
||||||
thumbhash: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return time bucket in trash', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get('/timeline/bucket')
|
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
|
||||||
.query({ timeBucket: '1970-02-01T00:00:00.000Z', isTrashed: true });
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
|
||||||
|
|
||||||
const timeBucket: TimeBucketAssetResponseDto = body;
|
|
||||||
expect(timeBucket.isTrashed).toEqual([true]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -97,7 +97,7 @@ describe(`immich upload`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await utils.resetDatabase(['assets', 'albums']);
|
await utils.resetDatabase(['asset', 'album']);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe(`immich upload /path/to/file.jpg`, () => {
|
describe(`immich upload /path/to/file.jpg`, () => {
|
||||||
|
|||||||
178
e2e/src/generate-date-tag-test-images.ts
Normal file
178
e2e/src/generate-date-tag-test-images.ts
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script to generate test images with additional EXIF date tags
|
||||||
|
* This creates actual JPEG images with embedded metadata for testing
|
||||||
|
* Images are generated into e2e/test-assets/metadata/dates/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { execSync } from 'node:child_process';
|
||||||
|
import { writeFileSync } from 'node:fs';
|
||||||
|
import { dirname, join } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import sharp from 'sharp';
|
||||||
|
|
||||||
|
interface TestImage {
|
||||||
|
filename: string;
|
||||||
|
description: string;
|
||||||
|
exifTags: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const testImages: TestImage[] = [
|
||||||
|
{
|
||||||
|
filename: 'time-created.jpg',
|
||||||
|
description: 'Image with TimeCreated tag',
|
||||||
|
exifTags: {
|
||||||
|
TimeCreated: '2023:11:15 14:30:00',
|
||||||
|
Make: 'Canon',
|
||||||
|
Model: 'EOS R5',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: 'gps-datetime.jpg',
|
||||||
|
description: 'Image with GPSDateTime and coordinates',
|
||||||
|
exifTags: {
|
||||||
|
GPSDateTime: '2023:11:15 12:30:00Z',
|
||||||
|
GPSLatitude: '37.7749',
|
||||||
|
GPSLongitude: '-122.4194',
|
||||||
|
GPSLatitudeRef: 'N',
|
||||||
|
GPSLongitudeRef: 'W',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: 'datetime-utc.jpg',
|
||||||
|
description: 'Image with DateTimeUTC tag',
|
||||||
|
exifTags: {
|
||||||
|
DateTimeUTC: '2023:11:15 10:30:00',
|
||||||
|
Make: 'Nikon',
|
||||||
|
Model: 'D850',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: 'gps-datestamp.jpg',
|
||||||
|
description: 'Image with GPSDateStamp and GPSTimeStamp',
|
||||||
|
exifTags: {
|
||||||
|
GPSDateStamp: '2023:11:15',
|
||||||
|
GPSTimeStamp: '08:30:00',
|
||||||
|
GPSLatitude: '51.5074',
|
||||||
|
GPSLongitude: '-0.1278',
|
||||||
|
GPSLatitudeRef: 'N',
|
||||||
|
GPSLongitudeRef: 'W',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: 'sony-datetime2.jpg',
|
||||||
|
description: 'Sony camera image with SonyDateTime2 tag',
|
||||||
|
exifTags: {
|
||||||
|
SonyDateTime2: '2023:11:15 06:30:00',
|
||||||
|
Make: 'SONY',
|
||||||
|
Model: 'ILCE-7RM5',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: 'date-priority-test.jpg',
|
||||||
|
description: 'Image with multiple date tags to test priority',
|
||||||
|
exifTags: {
|
||||||
|
SubSecDateTimeOriginal: '2023:01:01 01:00:00',
|
||||||
|
DateTimeOriginal: '2023:02:02 02:00:00',
|
||||||
|
SubSecCreateDate: '2023:03:03 03:00:00',
|
||||||
|
CreateDate: '2023:04:04 04:00:00',
|
||||||
|
CreationDate: '2023:05:05 05:00:00',
|
||||||
|
DateTimeCreated: '2023:06:06 06:00:00',
|
||||||
|
TimeCreated: '2023:07:07 07:00:00',
|
||||||
|
GPSDateTime: '2023:08:08 08:00:00',
|
||||||
|
DateTimeUTC: '2023:09:09 09:00:00',
|
||||||
|
GPSDateStamp: '2023:10:10',
|
||||||
|
SonyDateTime2: '2023:11:11 11:00:00',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: 'new-tags-only.jpg',
|
||||||
|
description: 'Image with only additional date tags (no standard tags)',
|
||||||
|
exifTags: {
|
||||||
|
TimeCreated: '2023:12:01 15:45:30',
|
||||||
|
GPSDateTime: '2023:12:01 13:45:30Z',
|
||||||
|
DateTimeUTC: '2023:12:01 13:45:30',
|
||||||
|
GPSDateStamp: '2023:12:01',
|
||||||
|
SonyDateTime2: '2023:12:01 08:45:30',
|
||||||
|
GPSLatitude: '40.7128',
|
||||||
|
GPSLongitude: '-74.0060',
|
||||||
|
GPSLatitudeRef: 'N',
|
||||||
|
GPSLongitudeRef: 'W',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const generateTestImages = async (): Promise<void> => {
|
||||||
|
// Target directory: e2e/test-assets/metadata/dates/
|
||||||
|
// Current file is in: e2e/src/
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
const targetDir = join(__dirname, '..', 'test-assets', 'metadata', 'dates');
|
||||||
|
|
||||||
|
console.log('Generating test images with additional EXIF date tags...');
|
||||||
|
console.log(`Target directory: ${targetDir}`);
|
||||||
|
|
||||||
|
for (const image of testImages) {
|
||||||
|
try {
|
||||||
|
const imagePath = join(targetDir, image.filename);
|
||||||
|
|
||||||
|
// Create unique JPEG file using Sharp
|
||||||
|
const r = Math.floor(Math.random() * 256);
|
||||||
|
const g = Math.floor(Math.random() * 256);
|
||||||
|
const b = Math.floor(Math.random() * 256);
|
||||||
|
|
||||||
|
const jpegData = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
channels: 3,
|
||||||
|
background: { r, g, b },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.jpeg({ quality: 90 })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
writeFileSync(imagePath, jpegData);
|
||||||
|
|
||||||
|
// Build exiftool command to add EXIF data
|
||||||
|
const exifArgs = Object.entries(image.exifTags)
|
||||||
|
.map(([tag, value]) => `-${tag}="${value}"`)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
const command = `exiftool ${exifArgs} -overwrite_original "${imagePath}"`;
|
||||||
|
|
||||||
|
console.log(`Creating ${image.filename}: ${image.description}`);
|
||||||
|
execSync(command, { stdio: 'pipe' });
|
||||||
|
|
||||||
|
// Verify the tags were written
|
||||||
|
const verifyCommand = `exiftool -json "${imagePath}"`;
|
||||||
|
const result = execSync(verifyCommand, { encoding: 'utf8' });
|
||||||
|
const metadata = JSON.parse(result)[0];
|
||||||
|
|
||||||
|
console.log(` ✓ Created with ${Object.keys(image.exifTags).length} EXIF tags`);
|
||||||
|
|
||||||
|
// Log first date tag found for verification
|
||||||
|
const firstDateTag = Object.keys(image.exifTags).find(
|
||||||
|
(tag) => tag.includes('Date') || tag.includes('Time') || tag.includes('Created'),
|
||||||
|
);
|
||||||
|
if (firstDateTag && metadata[firstDateTag]) {
|
||||||
|
console.log(` ✓ Verified ${firstDateTag}: ${metadata[firstDateTag]}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to create ${image.filename}:`, (error as Error).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\nTest image generation complete!');
|
||||||
|
console.log('Files created in:', targetDir);
|
||||||
|
console.log('\nTo test these images:');
|
||||||
|
console.log(`cd ${targetDir} && exiftool -time:all -gps:all *.jpg`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { generateTestImages };
|
||||||
|
|
||||||
|
// Run the generator if this file is executed directly
|
||||||
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
|
generateTestImages().catch(console.error);
|
||||||
|
}
|
||||||
@@ -116,6 +116,7 @@ export const deviceDto = {
|
|||||||
createdAt: expect.any(String),
|
createdAt: expect.any(String),
|
||||||
updatedAt: expect.any(String),
|
updatedAt: expect.any(String),
|
||||||
current: true,
|
current: true,
|
||||||
|
isPendingSyncReset: false,
|
||||||
deviceOS: '',
|
deviceOS: '',
|
||||||
deviceType: '',
|
deviceType: '',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export enum OAuthUser {
|
|||||||
NO_NAME = 'no-name',
|
NO_NAME = 'no-name',
|
||||||
WITH_QUOTA = 'with-quota',
|
WITH_QUOTA = 'with-quota',
|
||||||
WITH_USERNAME = 'with-username',
|
WITH_USERNAME = 'with-username',
|
||||||
|
WITH_ROLE = 'with-role',
|
||||||
}
|
}
|
||||||
|
|
||||||
const claims = [
|
const claims = [
|
||||||
@@ -34,6 +35,12 @@ const claims = [
|
|||||||
preferred_username: 'user-quota',
|
preferred_username: 'user-quota',
|
||||||
immich_quota: 25,
|
immich_quota: 25,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
sub: OAuthUser.WITH_ROLE,
|
||||||
|
email: 'oauth-with-role@immich.app',
|
||||||
|
email_verified: true,
|
||||||
|
immich_role: 'admin',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const withDefaultClaims = (sub: string) => ({
|
const withDefaultClaims = (sub: string) => ({
|
||||||
@@ -64,7 +71,15 @@ const setup = async () => {
|
|||||||
claims: {
|
claims: {
|
||||||
openid: ['sub'],
|
openid: ['sub'],
|
||||||
email: ['email', 'email_verified'],
|
email: ['email', 'email_verified'],
|
||||||
profile: ['name', 'given_name', 'family_name', 'preferred_username', 'immich_quota', 'immich_username'],
|
profile: [
|
||||||
|
'name',
|
||||||
|
'given_name',
|
||||||
|
'family_name',
|
||||||
|
'preferred_username',
|
||||||
|
'immich_quota',
|
||||||
|
'immich_username',
|
||||||
|
'immich_role',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
features: {
|
features: {
|
||||||
jwtUserinfo: {
|
jwtUserinfo: {
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import { io, type Socket } from 'socket.io-client';
|
|||||||
import { loginDto, signupDto } from 'src/fixtures';
|
import { loginDto, signupDto } from 'src/fixtures';
|
||||||
import { makeRandomImage } from 'src/generators';
|
import { makeRandomImage } from 'src/generators';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
|
export type { Emitter } from '@socket.io/component-emitter';
|
||||||
|
|
||||||
type CommandResponse = { stdout: string; stderr: string; exitCode: number | null };
|
type CommandResponse = { stdout: string; stderr: string; exitCode: number | null };
|
||||||
type EventType = 'assetUpload' | 'assetUpdate' | 'assetDelete' | 'userDelete' | 'assetHidden';
|
type EventType = 'assetUpload' | 'assetUpdate' | 'assetDelete' | 'userDelete' | 'assetHidden';
|
||||||
@@ -84,10 +85,10 @@ export const immichAdmin = (args: string[]) =>
|
|||||||
export const specialCharStrings = ["'", '"', ',', '{', '}', '*'];
|
export const specialCharStrings = ["'", '"', ',', '{', '}', '*'];
|
||||||
export const TEN_TIMES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
export const TEN_TIMES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
|
|
||||||
const executeCommand = (command: string, args: string[]) => {
|
const executeCommand = (command: string, args: string[], options?: { cwd?: string }) => {
|
||||||
let _resolve: (value: CommandResponse) => void;
|
let _resolve: (value: CommandResponse) => void;
|
||||||
const promise = new Promise<CommandResponse>((resolve) => (_resolve = resolve));
|
const promise = new Promise<CommandResponse>((resolve) => (_resolve = resolve));
|
||||||
const child = spawn(command, args, { stdio: 'pipe' });
|
const child = spawn(command, args, { stdio: 'pipe', cwd: options?.cwd });
|
||||||
|
|
||||||
let stdout = '';
|
let stdout = '';
|
||||||
let stderr = '';
|
let stderr = '';
|
||||||
@@ -153,19 +154,19 @@ export const utils = {
|
|||||||
|
|
||||||
tables = tables || [
|
tables = tables || [
|
||||||
// TODO e2e test for deleting a stack, since it is quite complex
|
// TODO e2e test for deleting a stack, since it is quite complex
|
||||||
'asset_stack',
|
'stack',
|
||||||
'libraries',
|
'library',
|
||||||
'shared_links',
|
'shared_link',
|
||||||
'person',
|
'person',
|
||||||
'albums',
|
'album',
|
||||||
'assets',
|
'asset',
|
||||||
'asset_faces',
|
'asset_face',
|
||||||
'activity',
|
'activity',
|
||||||
'api_keys',
|
'api_key',
|
||||||
'sessions',
|
'session',
|
||||||
'users',
|
'user',
|
||||||
'system_metadata',
|
'system_metadata',
|
||||||
'tags',
|
'tag',
|
||||||
];
|
];
|
||||||
|
|
||||||
const sql: string[] = [];
|
const sql: string[] = [];
|
||||||
@@ -174,7 +175,7 @@ export const utils = {
|
|||||||
if (table === 'system_metadata') {
|
if (table === 'system_metadata') {
|
||||||
sql.push(`DELETE FROM "system_metadata" where "key" NOT IN ('reverse-geocoding-state', 'system-flags');`);
|
sql.push(`DELETE FROM "system_metadata" where "key" NOT IN ('reverse-geocoding-state', 'system-flags');`);
|
||||||
} else {
|
} else {
|
||||||
sql.push(`DELETE FROM ${table} CASCADE;`);
|
sql.push(`DELETE FROM "${table}" CASCADE;`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +451,7 @@ export const utils = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.query('INSERT INTO asset_faces ("assetId", "personId") VALUES ($1, $2)', [assetId, personId]);
|
await client.query('INSERT INTO asset_face ("assetId", "personId") VALUES ($1, $2)', [assetId, personId]);
|
||||||
},
|
},
|
||||||
|
|
||||||
setPersonThumbnail: async (personId: string) => {
|
setPersonThumbnail: async (personId: string) => {
|
||||||
|
|||||||
Submodule e2e/test-assets updated: 8885d6d01c...37f60ea537
118
i18n/af.json
118
i18n/af.json
@@ -4,6 +4,7 @@
|
|||||||
"account_settings": "Rekeninginstellings",
|
"account_settings": "Rekeninginstellings",
|
||||||
"acknowledge": "Erken",
|
"acknowledge": "Erken",
|
||||||
"action": "Aksie",
|
"action": "Aksie",
|
||||||
|
"action_common_update": "Opdateur",
|
||||||
"actions": "Aksies",
|
"actions": "Aksies",
|
||||||
"active": "Aktief",
|
"active": "Aktief",
|
||||||
"activity": "Aktiwiteite",
|
"activity": "Aktiwiteite",
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
"add_a_location": "Voeg 'n ligging by",
|
"add_a_location": "Voeg 'n ligging by",
|
||||||
"add_a_name": "Voeg 'n naam by",
|
"add_a_name": "Voeg 'n naam by",
|
||||||
"add_a_title": "Voeg 'n titel by",
|
"add_a_title": "Voeg 'n titel by",
|
||||||
|
"add_endpoint": "Voeg Koppelvlakpunt by",
|
||||||
"add_exclusion_pattern": "Voeg uitsgluitingspatrone by",
|
"add_exclusion_pattern": "Voeg uitsgluitingspatrone by",
|
||||||
"add_import_path": "Voeg invoerpad by",
|
"add_import_path": "Voeg invoerpad by",
|
||||||
"add_location": "Voeg ligging by",
|
"add_location": "Voeg ligging by",
|
||||||
@@ -20,26 +22,30 @@
|
|||||||
"add_partner": "Voeg vennoot by",
|
"add_partner": "Voeg vennoot by",
|
||||||
"add_path": "Voeg pad by",
|
"add_path": "Voeg pad by",
|
||||||
"add_photos": "Voeg foto's by",
|
"add_photos": "Voeg foto's by",
|
||||||
|
"add_tag": "Voeg tag by",
|
||||||
"add_to": "Voeg by…",
|
"add_to": "Voeg by…",
|
||||||
"add_to_album": "Voeg na album",
|
"add_to_album": "Voeg na album",
|
||||||
"add_to_shared_album": "Voeg na gedeelde album",
|
"add_to_album_bottom_sheet_added": "By {album} bygevoeg",
|
||||||
|
"add_to_album_bottom_sheet_already_exists": "Reeds in {album}",
|
||||||
|
"add_to_shared_album": "Voeg toe aan gedeelde album",
|
||||||
"add_url": "Voeg URL by",
|
"add_url": "Voeg URL by",
|
||||||
"added_to_archive": "By argief gevoeg",
|
"added_to_archive": "By argief toegevoegd",
|
||||||
"added_to_favorites": "By gunstelinge gevoeg",
|
"added_to_favorites": "By gunstelinge toegevoegd",
|
||||||
"added_to_favorites_count": "Het {count, number} by gunstelinge gevoeg",
|
"added_to_favorites_count": "Het {count, number} by gunstelinge toegevoegd",
|
||||||
"admin": {
|
"admin": {
|
||||||
"add_exclusion_pattern_description": "Voeg uitsluitingspatrone by. Globbing met *, ** en ? word ondersteun. Om alle lêers in enige lêergids genaamd \"Raw\" te ignoreer, gebruik \"**/Raw/**\". Om alle lêers wat op \".tif\" eindig, te ignoreer, gebruik \"**/*.tif\". Om 'n absolute pad te ignoreer, gebruik \"/path/to/ignore/**\".",
|
"add_exclusion_pattern_description": "Voeg uitsluitingspatrone by. Globbing met *, ** en ? word ondersteun. Om alle lêers in enige lêergids genaamd \"Raw\" te ignoreer, gebruik \"**/Raw/**\". Om alle lêers wat op \".tif\" eindig, te ignoreer, gebruik \"**/*.tif\". Om 'n absolute pad te ignoreer, gebruik \"/path/to/ignore/**\".",
|
||||||
|
"admin_user": "Admin gebruiker",
|
||||||
"asset_offline_description": "Hierdie eksterne biblioteekbate word nie meer op skyf gevind nie en is na die asblik geskuif. As die lêer binne die biblioteek geskuif is, gaan jou tydlyn na vir die nuwe ooreenstemmende bate. Om hierdie bate te herstel, maak asseblief seker dat die lêerpad hieronder deur Immich verkry kan word en skandeer die biblioteek.",
|
"asset_offline_description": "Hierdie eksterne biblioteekbate word nie meer op skyf gevind nie en is na die asblik geskuif. As die lêer binne die biblioteek geskuif is, gaan jou tydlyn na vir die nuwe ooreenstemmende bate. Om hierdie bate te herstel, maak asseblief seker dat die lêerpad hieronder deur Immich verkry kan word en skandeer die biblioteek.",
|
||||||
"authentication_settings": "Verifikasie instellings",
|
"authentication_settings": "Verifikasie instellings",
|
||||||
"authentication_settings_description": "Bestuur wagwoord, OAuth en ander verifikasie instellings",
|
"authentication_settings_description": "Bestuur wagwoord, OAuth en ander verifikasie instellings",
|
||||||
"authentication_settings_disable_all": "Is jy seker jy wil alle aanmeldmetodes deaktiveer? Aanmelding sal heeltemal gedeaktiveer word.",
|
"authentication_settings_disable_all": "Is jy seker jy wil alle aanmeldmetodes deaktiveer? Aanmelding sal heeltemal gedeaktiveer word.",
|
||||||
"authentication_settings_reenable": "Om te heraktiveer, gebruik 'n <link>Server Command</link>.",
|
"authentication_settings_reenable": "Om te heraktiveer, gebruik 'n <link>Server Command</link>.",
|
||||||
"background_task_job": "Agtergrondtake",
|
"background_task_job": "Agtergrondtake",
|
||||||
"backup_database": "Rugsteun databasis",
|
"backup_database": "Skep Datastortlêer",
|
||||||
"backup_database_enable_description": "Aktiveer databasisrugsteun",
|
"backup_database_enable_description": "Aktiveer databasisrugsteun",
|
||||||
"backup_keep_last_amount": "Aantal vorige rugsteune om te hou",
|
"backup_keep_last_amount": "Aantal vorige rugsteune om te hou",
|
||||||
"backup_settings": "Rugsteun instellings",
|
"backup_settings": "Rugsteun instellings",
|
||||||
"backup_settings_description": "Bestuur databasis rugsteun instellings",
|
"backup_settings_description": "Bestuur databasis rugsteun instellings.",
|
||||||
"cleared_jobs": "Poste gevee vir: {job}",
|
"cleared_jobs": "Poste gevee vir: {job}",
|
||||||
"config_set_by_file": "Config word tans deur 'n konfigurasielêer gestel",
|
"config_set_by_file": "Config word tans deur 'n konfigurasielêer gestel",
|
||||||
"confirm_delete_library": "Is jy seker jy wil {library}-biblioteek uitvee?",
|
"confirm_delete_library": "Is jy seker jy wil {library}-biblioteek uitvee?",
|
||||||
@@ -47,6 +53,7 @@
|
|||||||
"confirm_email_below": "Om te bevestig, tik \"{email}\" hieronder",
|
"confirm_email_below": "Om te bevestig, tik \"{email}\" hieronder",
|
||||||
"confirm_reprocess_all_faces": "Is jy seker jy wil alle gesigte herverwerk? Dit sal ook genoemde mense skoonmaak.",
|
"confirm_reprocess_all_faces": "Is jy seker jy wil alle gesigte herverwerk? Dit sal ook genoemde mense skoonmaak.",
|
||||||
"confirm_user_password_reset": "Is jy seker jy wil {user} se wagwoord terugstel?",
|
"confirm_user_password_reset": "Is jy seker jy wil {user} se wagwoord terugstel?",
|
||||||
|
"confirm_user_pin_code_reset": "Is jy seker jy wil {user} se PIN kode herstel?",
|
||||||
"create_job": "Skep werk",
|
"create_job": "Skep werk",
|
||||||
"cron_expression": "Cron uitdrukking",
|
"cron_expression": "Cron uitdrukking",
|
||||||
"cron_expression_description": "Stel die skanderingsinterval in met die cron-formaat. Vir meer inligting verwys asseblief na bv. <link>Crontab Guru</link>",
|
"cron_expression_description": "Stel die skanderingsinterval in met die cron-formaat. Vir meer inligting verwys asseblief na bv. <link>Crontab Guru</link>",
|
||||||
@@ -56,10 +63,14 @@
|
|||||||
"exclusion_pattern_description": "Met uitsluitingspatrone kan jy lêers en vouers ignoreer wanneer jy jou biblioteek skandeer. Dit is nuttig as jy vouers het wat lêers bevat wat jy nie wil invoer nie, soos RAW-lêers.",
|
"exclusion_pattern_description": "Met uitsluitingspatrone kan jy lêers en vouers ignoreer wanneer jy jou biblioteek skandeer. Dit is nuttig as jy vouers het wat lêers bevat wat jy nie wil invoer nie, soos RAW-lêers.",
|
||||||
"external_library_management": "Eksterne Biblioteekbestuur",
|
"external_library_management": "Eksterne Biblioteekbestuur",
|
||||||
"face_detection": "Gesig deteksie",
|
"face_detection": "Gesig deteksie",
|
||||||
|
"face_detection_description": "Detecteer die gesigte in media deur middel van masjienleer. Vir videos word slegs die duimnaelskets oorweeg. “Herlaai” (ver)werk al die media weer. “Stel terug” verwyder boonop alle huidige gesigdata. “Onverwerk” plaas bates in die tou wat nog nie verwerk is nie. Gedekte gesigte sal ná voltooiing van Gesigdetectie vir Gesigherkenning in die tou geplaas word, om hulle in bestaande of nuwe persone te groepeer.",
|
||||||
|
"facial_recognition_job_description": "Groepeer gesigte in mense in. Die stap is vinniger nadat Gesig Deteksie klaar is. \"Herstel\" (her-)groepeer alle gesigte. \"Vermiste\" plaas gesigte in ry wat nie 'n persoon gekoppel het nie.",
|
||||||
"failed_job_command": "Opdrag {command} het misluk vir werk: {job}",
|
"failed_job_command": "Opdrag {command} het misluk vir werk: {job}",
|
||||||
"force_delete_user_warning": "WAARSKUWING: Dit sal onmiddellik die gebruiker en alle bates verwyder. Dit kan nie ontdoen word nie en die lêers kan nie herstel word nie.",
|
"force_delete_user_warning": "WAARSKUWING: Dit sal onmiddellik die gebruiker en alle bates verwyder. Dit kan nie ontdoen word nie en die lêers kan nie herstel word nie.",
|
||||||
"image_format": "Formaat",
|
"image_format": "Formaat",
|
||||||
"image_format_description": "WebP produseer kleiner lêers as JPEG, maar is stadiger om te enkodeer.",
|
"image_format_description": "WebP produseer kleiner lêers as JPEG, maar is stadiger om te enkodeer.",
|
||||||
|
"image_fullsize_description": "Vol grote prent met geen metadata, gebruik wanner ingezoem",
|
||||||
|
"image_fullsize_enabled": "Skakel aan vol grote prent generasie",
|
||||||
"image_prefer_embedded_preview": "Verkies ingebedde voorskou",
|
"image_prefer_embedded_preview": "Verkies ingebedde voorskou",
|
||||||
"image_prefer_wide_gamut": "Verkies wide gamut",
|
"image_prefer_wide_gamut": "Verkies wide gamut",
|
||||||
"image_prefer_wide_gamut_setting_description": "Gebruik Display P3 vir kleinkiekies. Dit behou die lewendheid van beelde met wye kleurruimtes beter, maar beelde kan anders verskyn op ou apparate met 'n ou blaaierweergawe. sRGB-beelde gebruik steeds sRGB om kleurverskuiwings te voorkom.",
|
"image_prefer_wide_gamut_setting_description": "Gebruik Display P3 vir kleinkiekies. Dit behou die lewendheid van beelde met wye kleurruimtes beter, maar beelde kan anders verskyn op ou apparate met 'n ou blaaierweergawe. sRGB-beelde gebruik steeds sRGB om kleurverskuiwings te voorkom.",
|
||||||
@@ -77,8 +88,99 @@
|
|||||||
"job_concurrency": "{job} gelyktydigheid",
|
"job_concurrency": "{job} gelyktydigheid",
|
||||||
"job_created": "Taak gemaak",
|
"job_created": "Taak gemaak",
|
||||||
"job_not_concurrency_safe": "Hierdie taak kan nie gelyktydig uitgevoer word nie.",
|
"job_not_concurrency_safe": "Hierdie taak kan nie gelyktydig uitgevoer word nie.",
|
||||||
"job_settings": "Agtergrondtaakinstellings"
|
"job_settings": "Agtergrondtaakinstellings",
|
||||||
|
"job_settings_description": "Bestuur werkgelyktydigheid",
|
||||||
|
"job_status": "Werkstatus",
|
||||||
|
"library_created": "Biblioteek geskep: {library}",
|
||||||
|
"library_deleted": "Biblioteek verwyder",
|
||||||
|
"library_import_path_description": "Spesifiseer 'n leer om in te neem. Hierdie leer, en al die sub leers, gaan geskandeer for vir prente en videos.",
|
||||||
|
"library_scanning": "Periodieke Skandering",
|
||||||
|
"library_scanning_description": "Stel periodieke skandering van biblioteek in",
|
||||||
|
"library_scanning_enable_description": "Aktiveer periodieke biblioteekskandering",
|
||||||
|
"library_settings": "Eksterne Biblioteek",
|
||||||
|
"map_settings": "Kaart",
|
||||||
|
"migration_job": "Migrasie",
|
||||||
|
"oauth_settings": "OAuth",
|
||||||
|
"transcoding_acceleration_vaapi": "VAAPI"
|
||||||
},
|
},
|
||||||
|
"administration": "Administrasie",
|
||||||
|
"advanced": "Gevorderde",
|
||||||
|
"albums": "Albums",
|
||||||
|
"all": "Alle",
|
||||||
|
"anti_clockwise": "Anti-kloksgewys",
|
||||||
|
"archive": "Argief",
|
||||||
|
"asset_skipped": "Oorgeslaan",
|
||||||
|
"asset_uploaded": "Opgelaai",
|
||||||
|
"asset_uploading": "Oplaai…",
|
||||||
|
"assets": "Bates",
|
||||||
|
"back": "Terug",
|
||||||
|
"backward": "Agteruit",
|
||||||
|
"build": "Bou",
|
||||||
|
"camera": "Kamera",
|
||||||
|
"cancel": "Kanselleer",
|
||||||
|
"city": "Stad",
|
||||||
|
"clockwise": "Kloksgewys",
|
||||||
|
"close": "Maak toe",
|
||||||
|
"color": "Kleur",
|
||||||
|
"confirm": "Bevestig",
|
||||||
|
"contain": "Bevat",
|
||||||
|
"context": "Konteks",
|
||||||
|
"continue": "Gaan voort",
|
||||||
|
"country": "Land",
|
||||||
|
"cover": "Bedek",
|
||||||
|
"create": "Skep",
|
||||||
|
"created": "Geskep",
|
||||||
|
"dark": "Donker",
|
||||||
|
"day": "Dag",
|
||||||
|
"delete": "Verwyder",
|
||||||
|
"description": "Beskrywing",
|
||||||
|
"details": "Besonderhede",
|
||||||
|
"direction": "Rigting",
|
||||||
|
"discover": "Ontdek",
|
||||||
|
"documentation": "Dokumentasie",
|
||||||
|
"done": "Klaar",
|
||||||
|
"download": "Aflaai",
|
||||||
|
"download_settings": "Aflaai",
|
||||||
|
"duplicates": "Duplikate",
|
||||||
|
"duration": "Duur",
|
||||||
|
"edit": "Wysig",
|
||||||
|
"edited": "Gewysigd",
|
||||||
"search_by_description": "Soek by beskrywing",
|
"search_by_description": "Soek by beskrywing",
|
||||||
"search_by_description_example": "Stapdag in Sapa"
|
"search_by_description_example": "Stapdag in Sapa",
|
||||||
|
"version": "Weergawe",
|
||||||
|
"version_announcement_closing": "Jou friend, Alex",
|
||||||
|
"version_history": "Weergawegeskiedenis",
|
||||||
|
"version_history_item": "{version} geinstaleerd op {date}",
|
||||||
|
"video": "Video",
|
||||||
|
"videos": "Video's",
|
||||||
|
"view": "Bekyk",
|
||||||
|
"view_album": "Bekyk Album",
|
||||||
|
"view_all": "Bekyk alle",
|
||||||
|
"view_all_users": "Bekyk alle gebruikers",
|
||||||
|
"view_in_timeline": "Bekyk in tydlyn",
|
||||||
|
"view_link": "Bekyk skakel",
|
||||||
|
"view_links": "Bekyk skakels",
|
||||||
|
"view_name": "Bekyk",
|
||||||
|
"view_next_asset": "Bekyk volgende bate",
|
||||||
|
"view_previous_asset": "Bekyk vorige bate",
|
||||||
|
"view_qr_code": "Bekyk QR-kode",
|
||||||
|
"view_stack": "Bekyk stapel",
|
||||||
|
"view_user": "Bekyk gebruiker",
|
||||||
|
"viewer_remove_from_stack": "Verwyder van stapel",
|
||||||
|
"viewer_stack_use_as_main_asset": "Gebruik as hoofbate",
|
||||||
|
"viewer_unstack": "Ontstapel",
|
||||||
|
"visibility_changed": "Sigbaarheid verander voor {count, plural, one {# person} other {# people}}",
|
||||||
|
"waiting": "Wag",
|
||||||
|
"warning": "Waaskuwing",
|
||||||
|
"week": "Week",
|
||||||
|
"welcome": "Welkom",
|
||||||
|
"welcome_to_immich": "Welkom by Immich",
|
||||||
|
"wifi_name": "Wi-Fi Naam",
|
||||||
|
"wrong_pin_code": "Verkeerde PIN-kode",
|
||||||
|
"year": "Jaar",
|
||||||
|
"years_ago": "{years, plural, one {# year} other {# years}} gelede",
|
||||||
|
"yes": "Ja",
|
||||||
|
"you_dont_have_any_shared_links": "Jy het geen gedeelde skakels",
|
||||||
|
"your_wifi_name": "Jou Wi-Fi naam",
|
||||||
|
"zoom_image": "Vergroot Prent"
|
||||||
}
|
}
|
||||||
|
|||||||
471
i18n/ar.json
471
i18n/ar.json
File diff suppressed because it is too large
Load Diff
276
i18n/be.json
276
i18n/be.json
@@ -22,6 +22,7 @@
|
|||||||
"add_partner": "Дадаць партнёра",
|
"add_partner": "Дадаць партнёра",
|
||||||
"add_path": "Дадаць шлях",
|
"add_path": "Дадаць шлях",
|
||||||
"add_photos": "Дадаць фота",
|
"add_photos": "Дадаць фота",
|
||||||
|
"add_tag": "Дадаць тэг",
|
||||||
"add_to": "Дадаць у…",
|
"add_to": "Дадаць у…",
|
||||||
"add_to_album": "Дадаць у альбом",
|
"add_to_album": "Дадаць у альбом",
|
||||||
"add_to_album_bottom_sheet_added": "Дададзена да {album}",
|
"add_to_album_bottom_sheet_added": "Дададзена да {album}",
|
||||||
@@ -33,28 +34,30 @@
|
|||||||
"added_to_favorites_count": "Дададзена {count, number} да абранага",
|
"added_to_favorites_count": "Дададзена {count, number} да абранага",
|
||||||
"admin": {
|
"admin": {
|
||||||
"add_exclusion_pattern_description": "Дадайце шаблоны выключэнняў. Падтрымліваецца выкарыстанне сімвалаў * , ** і ?. Каб ігнараваць усе файлы ў любой дырэкторыі з назвай \"Raw\", выкарыстоўвайце \"**/Raw/**\". Каб ігнараваць усе файлы, якія заканчваюцца на \".tif\", выкарыстоўвайце \"**/.tif\". Каб ігнараваць абсолютны шлях, выкарыстоўвайце \"/path/to/ignore/**\".",
|
"add_exclusion_pattern_description": "Дадайце шаблоны выключэнняў. Падтрымліваецца выкарыстанне сімвалаў * , ** і ?. Каб ігнараваць усе файлы ў любой дырэкторыі з назвай \"Raw\", выкарыстоўвайце \"**/Raw/**\". Каб ігнараваць усе файлы, якія заканчваюцца на \".tif\", выкарыстоўвайце \"**/.tif\". Каб ігнараваць абсолютны шлях, выкарыстоўвайце \"/path/to/ignore/**\".",
|
||||||
|
"admin_user": "Адміністратар",
|
||||||
"asset_offline_description": "Гэты знешні бібліятэчны актыў больш не знойдзены на дыску і быў перамешчаны ў сметніцу. Калі файл быў перамешчаны ў межах бібліятэкі, праверце вашу хроніку для новага адпаведнага актыва. Каб аднавіць гэты актыў, пераканайцеся, што шлях да файла ніжэй даступны для Immich і адскануйце бібліятэку.",
|
"asset_offline_description": "Гэты знешні бібліятэчны актыў больш не знойдзены на дыску і быў перамешчаны ў сметніцу. Калі файл быў перамешчаны ў межах бібліятэкі, праверце вашу хроніку для новага адпаведнага актыва. Каб аднавіць гэты актыў, пераканайцеся, што шлях да файла ніжэй даступны для Immich і адскануйце бібліятэку.",
|
||||||
"authentication_settings": "Налады праверкі сапраўднасці",
|
"authentication_settings": "Налады праверкі сапраўднасці",
|
||||||
"authentication_settings_description": "Кіраванне паролямі, OAuth, і іншыя налады праверкі сапраўднасці",
|
"authentication_settings_description": "Кіраванне паролямі, OAuth, і іншыя налады праверкі сапраўднасці",
|
||||||
"authentication_settings_disable_all": "Вы ўпэўнены, што жадаеце адключыць усе спосабы логіну? Логін будзе цалкам адключаны.",
|
"authentication_settings_disable_all": "Вы ўпэўнены, што жадаеце адключыць усе спосабы логіну? Логін будзе цалкам адключаны.",
|
||||||
"authentication_settings_reenable": "Каб зноў уключыць, выкарыстайце <link>Каманду сервера</link>.",
|
"authentication_settings_reenable": "Каб зноў уключыць, выкарыстайце <link>Каманду сервера</link>.",
|
||||||
"background_task_job": "Фонавыя заданні",
|
"background_task_job": "Фонавыя заданні",
|
||||||
"backup_database": "Рэзервовая копія базы даных",
|
"backup_database": "Стварыць рэзервовую копію базы даных",
|
||||||
"backup_database_enable_description": "Уключыць рэзерваванне базы даных",
|
"backup_database_enable_description": "Уключыць рэзерваванне базы даных",
|
||||||
"backup_keep_last_amount": "Колькасць папярэдніх рэзервовых копій для захавання",
|
"backup_keep_last_amount": "Колькасць папярэдніх рэзервовых копій для захавання",
|
||||||
"backup_settings": "Налады рэзервовага капіявання",
|
"backup_settings": "Налады рэзервовага капіявання",
|
||||||
"backup_settings_description": "Кіраванне наладамі дампа базы дадзеных. Заўвага: гэтыя задачы не кантралююцца, і ў выпадку няўдачы паведамленне адпраўлена не будзе.",
|
"backup_settings_description": "Кіраванне наладамі рэзервавання базы даных.",
|
||||||
"cleared_jobs": "Ачышчаны заданні для: {job}",
|
"cleared_jobs": "Ачышчаны заданні для: {job}",
|
||||||
"config_set_by_file": "Канфігурацыя ў зараз усталявана праз файл канфігурацыі",
|
"config_set_by_file": "Канфігурацыя зараз усталявана праз файл канфігурацыі",
|
||||||
"confirm_delete_library": "Вы ўпэўнены што жадаеце выдаліць {library} бібліятэку?",
|
"confirm_delete_library": "Вы ўпэўнены што жадаеце выдаліць бібліятэку {library}?",
|
||||||
"confirm_delete_library_assets": "Вы ўпэўнены, што хочаце выдаліць гэтую бібліятэку? Гэта прывядзе да выдалення {count, plural, one {# актыву} other {усіх # актываў}}, якія змяшчаюцца ў Immich, і гэта дзеянне немагчыма будзе адмяніць. Файлы застануцца на дыску.",
|
"confirm_delete_library_assets": "Вы ўпэўнены, што хочаце выдаліць гэтую бібліятэку? Гэта прывядзе да выдалення {count, plural, one {# актыву} other {усіх # актываў}}, якія змяшчаюцца ў Immich, і гэта дзеянне немагчыма будзе адмяніць. Файлы застануцца на дыску.",
|
||||||
"confirm_email_below": "Каб пацвердзіць, увядзіце \"{email}\" ніжэй",
|
"confirm_email_below": "Каб пацвердзіць, увядзіце \"{email}\" ніжэй",
|
||||||
"confirm_reprocess_all_faces": "Вы ўпэўнены, што хочаце пераапрацаваць усе твары? Гэта таксама прывядзе да выдалення імя людзей.",
|
"confirm_reprocess_all_faces": "Вы ўпэўнены, што хочаце пераапрацаваць усе твары? Гэта таксама прывядзе да выдалення імя людзей.",
|
||||||
"confirm_user_password_reset": "Вы ўпэўнены ў тым, што жадаеце скінуць пароль {user}?",
|
"confirm_user_password_reset": "Вы ўпэўнены ў тым, што жадаеце скінуць пароль {user}?",
|
||||||
|
"confirm_user_pin_code_reset": "Вы ўпэўнены ў тым, што жадаеце скінуць PIN-код {user}?",
|
||||||
"create_job": "Стварыць заданне",
|
"create_job": "Стварыць заданне",
|
||||||
"cron_expression": "Выраз Cron",
|
"cron_expression": "Выраз Cron",
|
||||||
"cron_expression_description": "Усталюйце інтэрвал сканавання, выкарыстоўваючы фармат cron. Для атрымання дадатковай інфармацыі, калі ласка, звярніцеся, напрыклад, да <link>Crontab Guru</link>",
|
"cron_expression_description": "Усталюйце інтэрвал сканавання, выкарыстоўваючы фармат cron. Для атрымання дадатковай інфармацыі, калі ласка, звярніцеся, напрыклад, да <link>Crontab Guru</link>",
|
||||||
"cron_expression_presets": "Прадустановкі выразаў Cron",
|
"cron_expression_presets": "Прадустаноўкі выразаў Cron",
|
||||||
"disable_login": "Адключыць уваход",
|
"disable_login": "Адключыць уваход",
|
||||||
"duplicate_detection_job_description": "Запусціць машыннае навучанне на актывах для выяўлення падобных выяў. Залежыць ад Smart Search",
|
"duplicate_detection_job_description": "Запусціць машыннае навучанне на актывах для выяўлення падобных выяў. Залежыць ад Smart Search",
|
||||||
"exclusion_pattern_description": "Шаблоны выключэння дазваляюць ігнараваць файлы і папкі пры сканаванні вашай бібліятэкі. Гэта карысна, калі ў вас ёсць папкі, якія змяшчаюць файлы, якія вы не хочаце імпартаваць, напрыклад, файлы RAW.",
|
"exclusion_pattern_description": "Шаблоны выключэння дазваляюць ігнараваць файлы і папкі пры сканаванні вашай бібліятэкі. Гэта карысна, калі ў вас ёсць папкі, якія змяшчаюць файлы, якія вы не хочаце імпартаваць, напрыклад, файлы RAW.",
|
||||||
@@ -71,15 +74,272 @@
|
|||||||
"image_fullsize_enabled_description": "Ствараць выяву ў поўным памеры для фарматаў, што не прыдатныя для вэб. Калі ўключана опцыя \"Аддаваць перавагу ўбудаванай праяве\", прагляды выкарыстоўваюцца непасрэдна без канвертацыі. Не ўплывае на вэб-прыдатныя фарматы, такія як JPEG.",
|
"image_fullsize_enabled_description": "Ствараць выяву ў поўным памеры для фарматаў, што не прыдатныя для вэб. Калі ўключана опцыя \"Аддаваць перавагу ўбудаванай праяве\", прагляды выкарыстоўваюцца непасрэдна без канвертацыі. Не ўплывае на вэб-прыдатныя фарматы, такія як JPEG.",
|
||||||
"image_fullsize_quality_description": "Якасць выявы ў поўным памеры ад 1 да 100. Больш высокае значэнне лепшае, але прыводзіць да павелічэння памеру файла.",
|
"image_fullsize_quality_description": "Якасць выявы ў поўным памеры ад 1 да 100. Больш высокае значэнне лепшае, але прыводзіць да павелічэння памеру файла.",
|
||||||
"image_fullsize_title": "Налады выявы ў поўным памеры",
|
"image_fullsize_title": "Налады выявы ў поўным памеры",
|
||||||
|
"image_prefer_embedded_preview_setting_description": "Выкарыстоўваць убудаваныя праявы ў RAW-фотаздымках ў якасці ўваходных дадзеных для апрацоўкі малюнкаў, калі магчыма. Гэта дазваляе атрымаць больш дакладныя колеры для некаторых відарысаў, але ж якасць праяў залежыць ад камеры, і на відарысе можа быць больш артэфактаў сціску.",
|
||||||
|
"image_prefer_wide_gamut": "Аддаць перавагу шырокай гаме",
|
||||||
"image_preview_title": "Налады папярэдняга прагляду",
|
"image_preview_title": "Налады папярэдняга прагляду",
|
||||||
"image_quality": "Якасць",
|
"image_quality": "Якасць",
|
||||||
"image_resolution": "Раздзяляльнасць",
|
"image_resolution": "Раздзяляльнасць",
|
||||||
"image_settings": "Налады відарыса",
|
"image_settings": "Налады відарыса",
|
||||||
"image_settings_description": "Кіруйце якасцю і раздзяляльнасцю сгенерыраваных відарысаў"
|
"image_settings_description": "Кіруйце якасцю і раздзяляльнасцю сгенерыраваных відарысаў",
|
||||||
|
"library_created": "Створана бібліятэка: {library}",
|
||||||
|
"library_deleted": "Бібліятэка выдалена",
|
||||||
|
"map_dark_style": "Цёмны стыль",
|
||||||
|
"map_enable_description": "Уключыць функцыі карты",
|
||||||
|
"map_gps_settings": "Налады карты і GPS",
|
||||||
|
"map_light_style": "Светлы стыль",
|
||||||
|
"map_settings": "Карта",
|
||||||
|
"map_settings_description": "Кіраванне наладамі карты",
|
||||||
|
"map_style_description": "URL-адрас style.json тэмы карты",
|
||||||
|
"metadata_settings": "Налады метаданых",
|
||||||
|
"oauth_button_text": "Тэкст кнопкі",
|
||||||
|
"oauth_settings": "OAuth",
|
||||||
|
"system_settings": "Сістэмныя налады",
|
||||||
|
"theme_settings": "Налады тэмы",
|
||||||
|
"transcoding_acceleration_vaapi": "VAAPI",
|
||||||
|
"transcoding_audio_codec": "Аудыякодэк",
|
||||||
|
"transcoding_video_codec": "Відэакодэк",
|
||||||
|
"trash_settings": "Налады сметніцы",
|
||||||
|
"trash_settings_description": "Кіраванне наладамі сметніцы",
|
||||||
|
"version_check_settings": "Праверка версіі",
|
||||||
|
"version_check_settings_description": "Уключыць/адключыць апавяшчэнні аб новай версіі"
|
||||||
},
|
},
|
||||||
|
"advanced_settings_troubleshooting_title": "Выпраўленне непаладак",
|
||||||
|
"album_added": "Альбом дададзены",
|
||||||
|
"album_name": "Назва альбома",
|
||||||
|
"album_remove_user": "Выдаліць карыстальніка?",
|
||||||
|
"album_updated": "Альбом абноўлены",
|
||||||
|
"albums": "Альбомы",
|
||||||
|
"all": "Усе",
|
||||||
|
"all_albums": "Усе альбомы",
|
||||||
|
"all_people": "Усе людзі",
|
||||||
|
"all_videos": "Усе відэа",
|
||||||
|
"app_bar_signout_dialog_ok": "Так",
|
||||||
|
"app_bar_signout_dialog_title": "Выйсці",
|
||||||
|
"app_settings": "Налады праграмы",
|
||||||
|
"archive": "Архіў",
|
||||||
|
"archive_size": "Памер архіва",
|
||||||
|
"asset_uploading": "Запампоўванне…",
|
||||||
|
"back": "Назад",
|
||||||
|
"backup_all": "Усе",
|
||||||
|
"backup_controller_page_background_wifi": "Толькі праз Wi-Fi",
|
||||||
|
"buy": "Купіць Immich",
|
||||||
|
"cache_settings_clear_cache_button": "Ачысціць кэш",
|
||||||
|
"cache_settings_tile_title": "Лакальнае сховішча",
|
||||||
|
"cancel": "Скасаваць",
|
||||||
|
"cancel_search": "Скасаваць пошук",
|
||||||
|
"canceled": "Скасавана",
|
||||||
|
"city": "Горад",
|
||||||
|
"clear": "Ачысціць",
|
||||||
|
"clear_all": "Ачысціць усё",
|
||||||
|
"client_cert_dialog_msg_confirm": "ОК",
|
||||||
|
"client_cert_enter_password": "Увядзіце пароль",
|
||||||
|
"client_cert_import": "Імпарт",
|
||||||
|
"close": "Закрыць",
|
||||||
|
"collapse": "Згарнуць",
|
||||||
|
"collapse_all": "Згарнуць усё",
|
||||||
|
"color": "Колер",
|
||||||
|
"color_theme": "Колеравая тэма",
|
||||||
|
"continue": "Працягнуць",
|
||||||
|
"control_bottom_app_bar_create_new_album": "Стварыць новы альбом",
|
||||||
|
"control_bottom_app_bar_delete_from_immich": "Выдаліць з Immich",
|
||||||
|
"control_bottom_app_bar_delete_from_local": "Выдаліць з прылады",
|
||||||
|
"control_bottom_app_bar_edit_location": "Рэдагаваць месцазнаходжанне",
|
||||||
|
"country": "Краіна",
|
||||||
|
"cover": "Вокладка",
|
||||||
|
"covers": "Вокладкі",
|
||||||
|
"create": "Стварыць",
|
||||||
|
"create_album": "Стварыць альбом",
|
||||||
|
"create_album_page_untitled": "Без назвы",
|
||||||
|
"create_library": "Стварыць бібліятэку",
|
||||||
|
"create_link": "Стварыць спасылку",
|
||||||
|
"create_new_user": "Стварыць новага карыстальніка",
|
||||||
|
"create_tag": "Стварыць тэг",
|
||||||
|
"create_user": "Стварыць карыстальніка",
|
||||||
|
"dark": "Цёмная",
|
||||||
|
"day": "Дзень",
|
||||||
|
"delete": "Выдаліць",
|
||||||
|
"delete_album": "Выдаліць альбом",
|
||||||
|
"delete_dialog_ok_force": "Усё адно выдаліць",
|
||||||
|
"delete_dialog_title": "Выдаліць назаўжды",
|
||||||
|
"delete_face": "Выдаліць твар",
|
||||||
|
"delete_key": "Выдаліць ключ",
|
||||||
|
"delete_library": "Выдаліць бібліятэку",
|
||||||
|
"delete_link": "Выдаліць спасылку",
|
||||||
|
"delete_local_dialog_ok_force": "Усё адно выдаліць",
|
||||||
|
"delete_others": "Выдаліць іншыя",
|
||||||
|
"delete_tag": "Выдаліць тэг",
|
||||||
|
"delete_user": "Выдаліць карыстальніка",
|
||||||
|
"discord": "Discord",
|
||||||
|
"documentation": "Дакументацыя",
|
||||||
|
"done": "Гатова",
|
||||||
|
"download": "Спампаваць",
|
||||||
|
"download_canceled": "Спампоўванне скасавана",
|
||||||
|
"download_complete": "Спампоўванне завершана",
|
||||||
|
"download_enqueue": "Спампоўванне дададзена ў чаргу",
|
||||||
|
"downloading": "Спампоўванне",
|
||||||
|
"edit": "Рэдагаваць",
|
||||||
|
"edit_album": "Рэдагаваць альбом",
|
||||||
|
"edit_avatar": "Рэдагаваць аватар",
|
||||||
|
"edit_date": "Рэдагаваць дату",
|
||||||
|
"edit_date_and_time": "Рэдагаваь дату і час",
|
||||||
|
"edit_description": "Рэдагаваць апісанне",
|
||||||
|
"edit_description_prompt": "Выберыце новае апісанне:",
|
||||||
|
"edit_faces": "Рэдагаваць твары",
|
||||||
|
"edit_import_path": "Рэдагаваць шлях імпарту",
|
||||||
|
"edit_import_paths": "Рэдагаваць шляхі імпарту",
|
||||||
|
"edit_key": "Рэдагаваць ключ",
|
||||||
|
"edit_link": "Рэдагаваць спасылку",
|
||||||
|
"edit_location": "Рэдагаваць месцазнаходжанне",
|
||||||
|
"edit_location_dialog_title": "Месцазнаходжанне",
|
||||||
|
"edit_name": "Рэдагаваць назву",
|
||||||
|
"edit_people": "Рэдагаваць людзей",
|
||||||
|
"edit_tag": "Рэдагаваць тэг",
|
||||||
|
"edit_title": "Рэдагаваць загаловак",
|
||||||
|
"edit_user": "Рэдагаваць карыстальніка",
|
||||||
|
"edited": "Адрэдагавана",
|
||||||
|
"editor": "Рэдактар",
|
||||||
|
"editor_close_without_save_prompt": "Змены не будуць захаваны",
|
||||||
|
"editor_close_without_save_title": "Закрыць рэдактар?",
|
||||||
|
"editor_crop_tool_h2_aspect_ratios": "Суадносіны бакоў",
|
||||||
|
"editor_crop_tool_h2_rotation": "Паварот",
|
||||||
|
"error": "Памылка",
|
||||||
|
"error_saving_image": "Памылка: {error}",
|
||||||
|
"exif": "Exif",
|
||||||
|
"exif_bottom_sheet_description": "Дадаць апісанне...",
|
||||||
|
"favorite": "У абраным",
|
||||||
|
"favorite_or_unfavorite_photo": "Дадаць або выдаліць фота з абранага",
|
||||||
|
"favorites": "Абраныя",
|
||||||
|
"file_name": "Назва файла",
|
||||||
|
"filename": "Назва файла",
|
||||||
|
"filetype": "Тып файла",
|
||||||
|
"filter": "Фільтр",
|
||||||
|
"forward": "Наперад",
|
||||||
|
"gcast_enabled": "Google Cast",
|
||||||
|
"general": "Агульныя",
|
||||||
|
"go_back": "Назад",
|
||||||
|
"go_to_folder": "Перайсці да папкі",
|
||||||
|
"hi_user": "Вітаем, {name} ({email})",
|
||||||
|
"hide_all_people": "Схаваць усіх людзей",
|
||||||
|
"hide_gallery": "Схаваць галерэю",
|
||||||
|
"hide_named_person": "Схаваць {name}",
|
||||||
|
"hide_password": "Схаваць пароль",
|
||||||
|
"hide_person": "Схаваць чалавека",
|
||||||
|
"image_viewer_page_state_provider_download_started": "Спампоўванне пачалося",
|
||||||
|
"immich_logo": "Лагатып Immich",
|
||||||
|
"interval": {
|
||||||
|
"day_at_onepm": "Кожны дзень а 13-й гадзіне",
|
||||||
|
"hours": "{hours, plural, one {Кожную гадзіну} few {Кожныя {hours, number} гадзіны} many {Кожныя {hours, number} гадзін} other {Кожныя {hours, number} гадзін}}",
|
||||||
|
"night_at_midnight": "Кожную ноч апоўначы",
|
||||||
|
"night_at_twoam": "Кожную ноч а 2-й гадзіне"
|
||||||
|
},
|
||||||
|
"language": "Мова",
|
||||||
|
"library": "Бібліятэка",
|
||||||
|
"light": "Светлая",
|
||||||
|
"login_form_back_button_text": "Назад",
|
||||||
|
"login_form_email_hint": "youremail@email.com",
|
||||||
|
"login_form_endpoint_hint": "http://your-server-ip:port",
|
||||||
|
"login_form_password_hint": "пароль",
|
||||||
|
"login_form_save_login": "Заставацца ў сістэме",
|
||||||
|
"main_menu": "Галоўнае меню",
|
||||||
|
"map_location_dialog_yes": "Так",
|
||||||
|
"map_settings_dark_mode": "Цёмны рэжым",
|
||||||
|
"map_settings_date_range_option_day": "Апошнія 24 гадзіны",
|
||||||
|
"map_settings_date_range_option_days": "Апошніх дзён: {days}",
|
||||||
|
"map_settings_date_range_option_year": "Апошні год",
|
||||||
|
"map_settings_date_range_option_years": "Апошніх год: {years}",
|
||||||
|
"map_settings_dialog_title": "Налады карты",
|
||||||
|
"map_settings_theme_settings": "Тэма карты",
|
||||||
|
"menu": "Меню",
|
||||||
|
"minute": "Хвіліна",
|
||||||
|
"month": "Месяц",
|
||||||
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
|
"my_albums": "Мае альбомы",
|
||||||
|
"name": "Імя",
|
||||||
|
"name_or_nickname": "Імя або псеўданім",
|
||||||
|
"next": "Далей",
|
||||||
|
"no": "Не",
|
||||||
|
"offline": "Па-за сеткай",
|
||||||
|
"ok": "ОК",
|
||||||
|
"online": "У сетцы",
|
||||||
|
"open": "Адкрыць",
|
||||||
|
"or": "або",
|
||||||
|
"partner_list_user_photos": "Фота карыстальніка {user}",
|
||||||
|
"pause": "Прыпыніць",
|
||||||
|
"people": "Людзі",
|
||||||
|
"permission_onboarding_back": "Назад",
|
||||||
|
"permission_onboarding_continue_anyway": "Усё адно працягнуць",
|
||||||
|
"photos": "Фота",
|
||||||
|
"photos_and_videos": "Фота і відэа",
|
||||||
|
"place": "Месца",
|
||||||
|
"places": "Месцы",
|
||||||
|
"port": "Порт",
|
||||||
|
"previous": "Папярэдняе",
|
||||||
|
"profile": "Профіль",
|
||||||
|
"profile_drawer_app_logs": "Журналы",
|
||||||
|
"profile_drawer_github": "GitHub",
|
||||||
|
"purchase_button_buy": "Купіць",
|
||||||
|
"purchase_button_buy_immich": "Купіць Immich",
|
||||||
|
"purchase_button_select": "Выбраць",
|
||||||
|
"remove": "Выдаліць",
|
||||||
|
"remove_from_album": "Выдаліць з альбома",
|
||||||
|
"remove_from_favorites": "Выдаліць з абраных",
|
||||||
|
"remove_tag": "Выдаліць тэг",
|
||||||
|
"remove_url": "Выдаліць URL-адрас",
|
||||||
|
"remove_user": "Выдаліць карыстальніка",
|
||||||
|
"rename": "Перайменаваць",
|
||||||
|
"repository": "Рэпазіторый",
|
||||||
|
"reset": "Скінуць",
|
||||||
|
"reset_password": "Скінуць пароль",
|
||||||
|
"restore": "Аднавіць",
|
||||||
|
"restore_all": "Аднавіць усё",
|
||||||
|
"restore_user": "Аднавіць карыстальніка",
|
||||||
|
"resume": "Узнавіць",
|
||||||
|
"role": "Роля",
|
||||||
|
"role_editor": "Рэдактар",
|
||||||
|
"role_viewer": "Глядач",
|
||||||
|
"save": "Захаваць",
|
||||||
|
"save_to_gallery": "Захаваць у галерэю",
|
||||||
|
"search_filter_date": "Дата",
|
||||||
|
"search_filter_location": "Месцазнаходжанне",
|
||||||
|
"search_filter_location_title": "Выберыце месцазнаходжанне",
|
||||||
|
"search_filter_media_type": "Тып медыя",
|
||||||
|
"search_filter_media_type_title": "Выберыце тып медыя",
|
||||||
|
"search_page_screenshots": "Здымкі экрана",
|
||||||
|
"search_page_selfies": "Сэлфі",
|
||||||
|
"search_page_things": "Рэчы",
|
||||||
|
"search_page_your_map": "Ваша карта",
|
||||||
|
"second": "Секунда",
|
||||||
|
"send_message": "Адправіць паведамленне",
|
||||||
|
"setting_languages_apply": "Ужыць",
|
||||||
|
"setting_notifications_notify_never": "ніколі",
|
||||||
|
"settings": "Налады",
|
||||||
|
"share_add_photos": "Дадаць фота",
|
||||||
|
"shared_album_section_people_title": "ЛЮДЗІ",
|
||||||
|
"shared_link_info_chip_metadata": "EXIF",
|
||||||
|
"sharing_page_empty_list": "ПУСТЫ СПІС",
|
||||||
|
"sign_out": "Выйсці",
|
||||||
|
"sign_up": "Зарэгістравацца",
|
||||||
|
"size": "Памер",
|
||||||
|
"sort_title": "Загаловак",
|
||||||
|
"source": "Крыніца",
|
||||||
|
"tag": "Тэг",
|
||||||
|
"tags": "Тэгі",
|
||||||
|
"theme": "Тэма",
|
||||||
|
"theme_selection": "Выбар тэмы",
|
||||||
"timeline": "Хроніка",
|
"timeline": "Хроніка",
|
||||||
"total": "Усяго",
|
"total": "Усяго",
|
||||||
|
"trash": "Сметніца",
|
||||||
|
"trash_page_delete_all": "Выдаліць усе",
|
||||||
|
"trash_page_restore_all": "Аднавіць усе",
|
||||||
|
"trash_page_title": "Сметніца ({count})",
|
||||||
|
"type": "Тып",
|
||||||
|
"undo": "Адрабіць",
|
||||||
|
"upload": "Запампаваць",
|
||||||
|
"upload_status_errors": "Памылкі",
|
||||||
|
"uploading": "Запампоўванне",
|
||||||
|
"url": "URL-адрас",
|
||||||
"user": "Карыстальнік",
|
"user": "Карыстальнік",
|
||||||
|
"user_has_been_deleted": "Гэты карыстальнік быў выдалены.",
|
||||||
"user_id": "ID карыстальніка",
|
"user_id": "ID карыстальніка",
|
||||||
"user_purchase_settings": "Купля",
|
"user_purchase_settings": "Купля",
|
||||||
"user_purchase_settings_description": "Кіруйце пакупкамі",
|
"user_purchase_settings_description": "Кіруйце пакупкамі",
|
||||||
@@ -112,14 +372,14 @@
|
|||||||
"view_next_asset": "Паказаць наступны аб'ект",
|
"view_next_asset": "Паказаць наступны аб'ект",
|
||||||
"view_previous_asset": "Праглядзець папярэдні аб'ект",
|
"view_previous_asset": "Праглядзець папярэдні аб'ект",
|
||||||
"view_stack": "Прагляд стэка",
|
"view_stack": "Прагляд стэка",
|
||||||
"visibility_changed": "Відзімасць змянілася для {count, plural, one {# чалавек(-аў)} астатніх {# чалавек}}",
|
"visibility_changed": "Бачнасць змянілася для {count, plural, one {# чалавека} other {# чалавек}}",
|
||||||
"waiting": "Чакаюць",
|
"waiting": "Чакаюць",
|
||||||
"warning": "Папярэджанне",
|
"warning": "Папярэджанне",
|
||||||
"week": "Тыдзень",
|
"week": "Тыдзень",
|
||||||
"welcome": "Вітаем",
|
"welcome": "Вітаем",
|
||||||
"welcome_to_immich": "Вітаем у Immich",
|
"welcome_to_immich": "Вітаем у Immich",
|
||||||
"year": "Год",
|
"year": "Год",
|
||||||
"years_ago": "{years, plural, one {# год} other {# гадоў}} таму",
|
"years_ago": "{years, plural, one {# год} few {# гады} many {# гадоў} other {# гадоў}} таму",
|
||||||
"yes": "Так",
|
"yes": "Так",
|
||||||
"you_dont_have_any_shared_links": "У вас няма абагуленых спасылак",
|
"you_dont_have_any_shared_links": "У вас няма абагуленых спасылак",
|
||||||
"zoom_image": "Павялічыць відарыс"
|
"zoom_image": "Павялічыць відарыс"
|
||||||
|
|||||||
204
i18n/bg.json
204
i18n/bg.json
@@ -34,6 +34,7 @@
|
|||||||
"added_to_favorites_count": "Добавени {count, number} към любими",
|
"added_to_favorites_count": "Добавени {count, number} към любими",
|
||||||
"admin": {
|
"admin": {
|
||||||
"add_exclusion_pattern_description": "Добави модели за изключване. Поддържа се \"globbing\" с помощта на *, ** и ?. За да игнорирате всички файлове в директория с име \"Raw\", използвайте \"**/Raw/**\". За да игнорирате всички файлове, завършващи на \".tif\", използвайте \"**/*.tif\". За да игнорирате абсолютен път, използвайте \"/path/to/ignore/**\".",
|
"add_exclusion_pattern_description": "Добави модели за изключване. Поддържа се \"globbing\" с помощта на *, ** и ?. За да игнорирате всички файлове в директория с име \"Raw\", използвайте \"**/Raw/**\". За да игнорирате всички файлове, завършващи на \".tif\", използвайте \"**/*.tif\". За да игнорирате абсолютен път, използвайте \"/path/to/ignore/**\".",
|
||||||
|
"admin_user": "Администратор",
|
||||||
"asset_offline_description": "Този външен библиотечен елемент не може да бъде открит на диска и е преместен в кошчето за боклук. Ако файлът е преместен в библиотеката, проверете вашата история за нов съответстващ елемент. За да възстановите елемента, моля проверете дали файловият път отдолу може да бъде достъпен от Immich и сканирайте библиотеката.",
|
"asset_offline_description": "Този външен библиотечен елемент не може да бъде открит на диска и е преместен в кошчето за боклук. Ако файлът е преместен в библиотеката, проверете вашата история за нов съответстващ елемент. За да възстановите елемента, моля проверете дали файловият път отдолу може да бъде достъпен от Immich и сканирайте библиотеката.",
|
||||||
"authentication_settings": "Настройки за удостоверяване",
|
"authentication_settings": "Настройки за удостоверяване",
|
||||||
"authentication_settings_description": "Управление на парола, OAuth и други настройки за удостоверяване",
|
"authentication_settings_description": "Управление на парола, OAuth и други настройки за удостоверяване",
|
||||||
@@ -52,7 +53,7 @@
|
|||||||
"confirm_email_below": "За потвърждение, моля въведете \"{email}\" отдолу",
|
"confirm_email_below": "За потвърждение, моля въведете \"{email}\" отдолу",
|
||||||
"confirm_reprocess_all_faces": "Сигурни ли сте, че искате да се обработят лицата отново? Това ще изчисти всички именувани хора.",
|
"confirm_reprocess_all_faces": "Сигурни ли сте, че искате да се обработят лицата отново? Това ще изчисти всички именувани хора.",
|
||||||
"confirm_user_password_reset": "Сигурни ли сте, че искате да нулирате паролата на {user}?",
|
"confirm_user_password_reset": "Сигурни ли сте, че искате да нулирате паролата на {user}?",
|
||||||
"confirm_user_pin_code_reset": "Наистина ли искаш да смениш PIN-кода на потребителя {user}?",
|
"confirm_user_pin_code_reset": "Наистина ли искате да смените PIN кода на потребителя {user}?",
|
||||||
"create_job": "Създайте задача",
|
"create_job": "Създайте задача",
|
||||||
"cron_expression": "Cron израз",
|
"cron_expression": "Cron израз",
|
||||||
"cron_expression_description": "Настрой интервала на сканиране използвайки cron формата. За повече информация <link>Crontab Guru</link>",
|
"cron_expression_description": "Настрой интервала на сканиране използвайки cron формата. За повече информация <link>Crontab Guru</link>",
|
||||||
@@ -165,12 +166,26 @@
|
|||||||
"metadata_settings_description": "Управление на настройките за метаданни",
|
"metadata_settings_description": "Управление на настройките за метаданни",
|
||||||
"migration_job": "Миграция",
|
"migration_job": "Миграция",
|
||||||
"migration_job_description": "Мигриране на миниатюрите за елементи и лица към най-новата структура на папките",
|
"migration_job_description": "Мигриране на миниатюрите за елементи и лица към най-новата структура на папките",
|
||||||
|
"nightly_tasks_cluster_faces_setting_description": "Изпълни разпознаване на лице за открити нови лица",
|
||||||
|
"nightly_tasks_cluster_new_faces_setting": "Разпознаване на нови лица",
|
||||||
|
"nightly_tasks_database_cleanup_setting": "Задачи по почистване на базата данни",
|
||||||
|
"nightly_tasks_database_cleanup_setting_description": "Премахни стари, ненужни записи от базата данни",
|
||||||
|
"nightly_tasks_generate_memories_setting": "Създаване на спомени",
|
||||||
|
"nightly_tasks_generate_memories_setting_description": "Създаване на нови спомени от съществуващи обекти",
|
||||||
|
"nightly_tasks_missing_thumbnails_setting": "Генериране на липсващи миниатюри",
|
||||||
|
"nightly_tasks_missing_thumbnails_setting_description": "Добавяне на обекти без миниатюра в опашката за създаване на миниатюра",
|
||||||
|
"nightly_tasks_settings": "Настройка на задачи за през нощта",
|
||||||
|
"nightly_tasks_settings_description": "Управление на задачите, изпълнявани през нощта",
|
||||||
|
"nightly_tasks_start_time_setting": "Време за начало",
|
||||||
|
"nightly_tasks_start_time_setting_description": "Време, когато сървъра ще започне изпълнение на нощни задачи",
|
||||||
|
"nightly_tasks_sync_quota_usage_setting": "Квота за синхронизация",
|
||||||
|
"nightly_tasks_sync_quota_usage_setting_description": "Обновяване на квотата според текущото потребление",
|
||||||
"no_paths_added": "Няма добавени пътища",
|
"no_paths_added": "Няма добавени пътища",
|
||||||
"no_pattern_added": "Няма добавен модел",
|
"no_pattern_added": "Няма добавен модел",
|
||||||
"note_apply_storage_label_previous_assets": "Забележка: За да приложите етикета за съхранение към предварително качени файлове, стартирайте",
|
"note_apply_storage_label_previous_assets": "Забележка: За да приложите етикета за съхранение към предварително качени файлове, стартирайте",
|
||||||
"note_cannot_be_changed_later": "ВНИМАНИЕ: Това не може да бъде променено по-късно!",
|
"note_cannot_be_changed_later": "ВНИМАНИЕ: Това не може да бъде променено по-късно!",
|
||||||
"notification_email_from_address": "От адрес",
|
"notification_email_from_address": "От адрес",
|
||||||
"notification_email_from_address_description": "Електронна поща на изпращача, например: \"Immich Photo Server <noreply@example.com>\". Използвай адрес, от който може да изпращаш имейли.",
|
"notification_email_from_address_description": "Електронна поща на изпращача, например: \"Immich Photo Server <noreply@example.com>\". Използвайте адрес, от който може да изпращате имейли.",
|
||||||
"notification_email_host_description": "Хост на сървъра за електронна поща (например: smtp.immich.app)",
|
"notification_email_host_description": "Хост на сървъра за електронна поща (например: smtp.immich.app)",
|
||||||
"notification_email_ignore_certificate_errors": "Игнорирайте сертификационни грешки",
|
"notification_email_ignore_certificate_errors": "Игнорирайте сертификационни грешки",
|
||||||
"notification_email_ignore_certificate_errors_description": "Игнорирай грешки свързани с валидация на TLS сертификат (не се препоръчва)",
|
"notification_email_ignore_certificate_errors_description": "Игнорирай грешки свързани с валидация на TLS сертификат (не се препоръчва)",
|
||||||
@@ -179,7 +194,7 @@
|
|||||||
"notification_email_sent_test_email_button": "Изпрати тестов имейл и запази",
|
"notification_email_sent_test_email_button": "Изпрати тестов имейл и запази",
|
||||||
"notification_email_setting_description": "Настройки за изпращане на имейл известия",
|
"notification_email_setting_description": "Настройки за изпращане на имейл известия",
|
||||||
"notification_email_test_email": "Изпрати тестов имейл",
|
"notification_email_test_email": "Изпрати тестов имейл",
|
||||||
"notification_email_test_email_failed": "Неуспешно изпращане на тестов имейл, провери променливите",
|
"notification_email_test_email_failed": "Неуспешно изпращане на тестов имейл, проверете настройките",
|
||||||
"notification_email_test_email_sent": "Тестов имейл беше изпратен на {email}. Проверете входящата си пощa.",
|
"notification_email_test_email_sent": "Тестов имейл беше изпратен на {email}. Проверете входящата си пощa.",
|
||||||
"notification_email_username_description": "Потребителско име за удостоверяване пред имейл сървъра",
|
"notification_email_username_description": "Потребителско име за удостоверяване пред имейл сървъра",
|
||||||
"notification_enable_email_notifications": "Включване на имейл известията",
|
"notification_enable_email_notifications": "Включване на имейл известията",
|
||||||
@@ -195,6 +210,8 @@
|
|||||||
"oauth_mobile_redirect_uri": "URI за мобилно пренасочване",
|
"oauth_mobile_redirect_uri": "URI за мобилно пренасочване",
|
||||||
"oauth_mobile_redirect_uri_override": "URI пренасочване за мобилни устройства",
|
"oauth_mobile_redirect_uri_override": "URI пренасочване за мобилни устройства",
|
||||||
"oauth_mobile_redirect_uri_override_description": "Разреши когато доставчика за OAuth удостоверяване не позволява за мобилни URI идентификатори, като ''{callback}''",
|
"oauth_mobile_redirect_uri_override_description": "Разреши когато доставчика за OAuth удостоверяване не позволява за мобилни URI идентификатори, като ''{callback}''",
|
||||||
|
"oauth_role_claim": "Потвърждение на роля",
|
||||||
|
"oauth_role_claim_description": "Автоматично предоставяне на административни права при наличие на това потвържение. Потвърждението може да има стойност 'user' или 'admin'.",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "Управление на настройките за вход с OAuth",
|
"oauth_settings_description": "Управление на настройките за вход с OAuth",
|
||||||
"oauth_settings_more_details": "За повече информация за функционалността, се потърсете в <link>docs</link>.",
|
"oauth_settings_more_details": "За повече информация за функционалността, се потърсете в <link>docs</link>.",
|
||||||
@@ -203,7 +220,7 @@
|
|||||||
"oauth_storage_quota_claim": "Заявка за квота за съхранение",
|
"oauth_storage_quota_claim": "Заявка за квота за съхранение",
|
||||||
"oauth_storage_quota_claim_description": "Автоматично задайте квотата за съхранение на потребителя със стойността от тази заявка.",
|
"oauth_storage_quota_claim_description": "Автоматично задайте квотата за съхранение на потребителя със стойността от тази заявка.",
|
||||||
"oauth_storage_quota_default": "Стандартна квота за съхранение (GiB)",
|
"oauth_storage_quota_default": "Стандартна квота за съхранение (GiB)",
|
||||||
"oauth_storage_quota_default_description": "Квота в GiB, която да се използва, когато не е предоставена заявка (Въведете 0 за неограничена квота).",
|
"oauth_storage_quota_default_description": "Квота в GiB, която да се използва, когато не е посочено друго.",
|
||||||
"oauth_timeout": "Време на изчакване при заявка",
|
"oauth_timeout": "Време на изчакване при заявка",
|
||||||
"oauth_timeout_description": "Време за изчакване на отговор на заявка, в милисекунди",
|
"oauth_timeout_description": "Време за изчакване на отговор на заявка, в милисекунди",
|
||||||
"password_enable_description": "Влизане с имейл и парола",
|
"password_enable_description": "Влизане с имейл и парола",
|
||||||
@@ -243,7 +260,7 @@
|
|||||||
"storage_template_migration_info": "Шаблона ще преобразува всички разширения на имената на файловете в долен регистър. Промените в шаблоните ще се прилагат само за нови елементи. За да приложите принудително шаблона към вече качени елементи, изпълнете <link>{job}</link>.",
|
"storage_template_migration_info": "Шаблона ще преобразува всички разширения на имената на файловете в долен регистър. Промените в шаблоните ще се прилагат само за нови елементи. За да приложите принудително шаблона към вече качени елементи, изпълнете <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "Задача за миграция на шаблона за съхранение",
|
"storage_template_migration_job": "Задача за миграция на шаблона за съхранение",
|
||||||
"storage_template_more_details": "За повече подробности относно тази функция се обърнете към шаблона <template-link>Storage Template</template-link> и неговите <implications-link> последствия </implications-link>",
|
"storage_template_more_details": "За повече подробности относно тази функция се обърнете към шаблона <template-link>Storage Template</template-link> и неговите <implications-link> последствия </implications-link>",
|
||||||
"storage_template_onboarding_description": "Когато е активирана, тази функция ще организира автоматично файлове въз основа на дефиниран от потребителя шаблон. Поради проблеми със стабилността, функцията е изключена по подразбиране. За повече информация, моля, вижте <link>документацията</link>.",
|
"storage_template_onboarding_description_v2": "Когато е разрешена, тази функция ще организира автоматично файловете, според шаблон, дефиниран от потребителя. За допълнителна информация, моля вижте <link>документацията</link>.",
|
||||||
"storage_template_path_length": "Ограничение на дължината на пътя: <b>{length, number}</b>/{limit, number}",
|
"storage_template_path_length": "Ограничение на дължината на пътя: <b>{length, number}</b>/{limit, number}",
|
||||||
"storage_template_settings": "Шаблон за съхранение",
|
"storage_template_settings": "Шаблон за съхранение",
|
||||||
"storage_template_settings_description": "Управление на структурата на папките и името на файла за качване",
|
"storage_template_settings_description": "Управление на структурата на папките и името на файла за качване",
|
||||||
@@ -356,7 +373,9 @@
|
|||||||
"admin_password": "Администраторска парола",
|
"admin_password": "Администраторска парола",
|
||||||
"administration": "Администрация",
|
"administration": "Администрация",
|
||||||
"advanced": "Разширено",
|
"advanced": "Разширено",
|
||||||
"advanced_settings_enable_alternate_media_filter_subtitle": "При синхронизация, използвай тази опция като филтър, основан на промяна на даден критерии. Опитай само в случай, че приложението има проблем с откриване на всички албуми.",
|
"advanced_settings_beta_timeline_subtitle": "Опитайте новите функции на приложението",
|
||||||
|
"advanced_settings_beta_timeline_title": "Бета версия на времевата линия",
|
||||||
|
"advanced_settings_enable_alternate_media_filter_subtitle": "При синхронизация, използвайте тази опция като филтър, основан на промяна на даден критерии. Опитайте само в случай, че приложението има проблем с откриване на всички албуми.",
|
||||||
"advanced_settings_enable_alternate_media_filter_title": "[ЕКСПЕРИМЕНТАЛНО] Използвай филтъра на алтернативното устройство за синхронизация на албуми",
|
"advanced_settings_enable_alternate_media_filter_title": "[ЕКСПЕРИМЕНТАЛНО] Използвай филтъра на алтернативното устройство за синхронизация на албуми",
|
||||||
"advanced_settings_log_level_title": "Ниво на запис в дневника: {level}",
|
"advanced_settings_log_level_title": "Ниво на запис в дневника: {level}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Някои устройства са твърде бавни за да генерират миниатюри. Активирай тази опция за да се зареждат винаги от сървъра.",
|
"advanced_settings_prefer_remote_subtitle": "Някои устройства са твърде бавни за да генерират миниатюри. Активирай тази опция за да се зареждат винаги от сървъра.",
|
||||||
@@ -392,7 +411,7 @@
|
|||||||
"album_updated_setting_description": "Получавайте известие по имейл, когато споделен албум има нови файлове",
|
"album_updated_setting_description": "Получавайте известие по имейл, когато споделен албум има нови файлове",
|
||||||
"album_user_left": "Напусна {album}",
|
"album_user_left": "Напусна {album}",
|
||||||
"album_user_removed": "Премахнат {user}",
|
"album_user_removed": "Премахнат {user}",
|
||||||
"album_viewer_appbar_delete_confirm": "Сигурен ли си, че искаш да изтриеш този албум от своя профил?",
|
"album_viewer_appbar_delete_confirm": "Сигурни ли сте, че искате да изтриете този албум от своя профил?",
|
||||||
"album_viewer_appbar_share_err_delete": "Неуспешно изтриване на албум",
|
"album_viewer_appbar_share_err_delete": "Неуспешно изтриване на албум",
|
||||||
"album_viewer_appbar_share_err_leave": "Неуспешно напускане на албум",
|
"album_viewer_appbar_share_err_leave": "Неуспешно напускане на албум",
|
||||||
"album_viewer_appbar_share_err_remove": "Проблем при пермахване на обекти от албума",
|
"album_viewer_appbar_share_err_remove": "Проблем при пермахване на обекти от албума",
|
||||||
@@ -426,6 +445,7 @@
|
|||||||
"app_settings": "Настройки ма приложението",
|
"app_settings": "Настройки ма приложението",
|
||||||
"appears_in": "Излиза в",
|
"appears_in": "Излиза в",
|
||||||
"archive": "Архив",
|
"archive": "Архив",
|
||||||
|
"archive_action_prompt": "{count} са добавени в Архива",
|
||||||
"archive_or_unarchive_photo": "Архивиране или деархивиране на снимка",
|
"archive_or_unarchive_photo": "Архивиране или деархивиране на снимка",
|
||||||
"archive_page_no_archived_assets": "Не са намерени обекти в архива",
|
"archive_page_no_archived_assets": "Не са намерени обекти в архива",
|
||||||
"archive_page_title": "Архив ({count})",
|
"archive_page_title": "Архив ({count})",
|
||||||
@@ -463,10 +483,12 @@
|
|||||||
"assets": "Елементи",
|
"assets": "Елементи",
|
||||||
"assets_added_count": "Добавено {count, plural, one {# asset} other {# assets}}",
|
"assets_added_count": "Добавено {count, plural, one {# asset} other {# assets}}",
|
||||||
"assets_added_to_album_count": "Добавен(и) са {count, plural, one {# актив} other {# актива}} в албума",
|
"assets_added_to_album_count": "Добавен(и) са {count, plural, one {# актив} other {# актива}} в албума",
|
||||||
"assets_added_to_name_count": "Добавен(и) са {count, plural, one {# актив} other {# актива}} към {hasName, select, true {<b>{name}</b>} other {нов албум}}",
|
"assets_cannot_be_added_to_album_count": "{count, plural, one {Обекта не може да се добави} other {Обектите не може да се добавят}} в албума",
|
||||||
"assets_count": "{count, plural, one {# актив} other {# актива}}",
|
"assets_count": "{count, plural, one {# актив} other {# актива}}",
|
||||||
"assets_deleted_permanently": "{count} обекта са изтрити завинаги",
|
"assets_deleted_permanently": "{count} обекта са изтрити завинаги",
|
||||||
"assets_deleted_permanently_from_server": "{count} обекта са изтити от Immich сървъра завинаги",
|
"assets_deleted_permanently_from_server": "{count} обекта са изтити от Immich сървъра завинаги",
|
||||||
|
"assets_downloaded_failed": "{count, plural, one {Зареден # файл} many {Заредени # файла} other {заредени # файла}}, {error} - неуспешно",
|
||||||
|
"assets_downloaded_successfully": "Успешно {count, plural, one {е качен # файл} many {са качени # файла} other {са качени # файла}}",
|
||||||
"assets_moved_to_trash_count": "Преместен(и) са {count, plural, one {# актив} other {# актива}} в кошчето",
|
"assets_moved_to_trash_count": "Преместен(и) са {count, plural, one {# актив} other {# актива}} в кошчето",
|
||||||
"assets_permanently_deleted_count": "Постоянно изтрит(и) са {count, plural, one {# актив} other {# актива}}",
|
"assets_permanently_deleted_count": "Постоянно изтрит(и) са {count, plural, one {# актив} other {# актива}}",
|
||||||
"assets_removed_count": "Премахнат(и) са {count, plural, one {# актив} other {# актива}}",
|
"assets_removed_count": "Премахнат(и) са {count, plural, one {# актив} other {# актива}}",
|
||||||
@@ -505,7 +527,7 @@
|
|||||||
"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": "За успешно архивиране във фонов режим, моля изключете оптимизациите на батерията, ограничаващи фоновата активност на Immich.\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": "Само при зареждане",
|
||||||
@@ -586,8 +608,8 @@
|
|||||||
"cannot_merge_people": "Не може да обединява хора",
|
"cannot_merge_people": "Не може да обединява хора",
|
||||||
"cannot_undo_this_action": "Не можете да отмените това действие!",
|
"cannot_undo_this_action": "Не можете да отмените това действие!",
|
||||||
"cannot_update_the_description": "Описанието не може да бъде актуализирано",
|
"cannot_update_the_description": "Описанието не може да бъде актуализирано",
|
||||||
"cast": "Промяна на регистъра",
|
"cast": "Поточно предаване",
|
||||||
"cast_description": "Настройка на наличните цели за промяна на регистъра",
|
"cast_description": "Настройка на наличните цели за предаване",
|
||||||
"change_date": "Промени датата",
|
"change_date": "Промени датата",
|
||||||
"change_description": "Промени описанието",
|
"change_description": "Промени описанието",
|
||||||
"change_display_order": "Промени реда на показване",
|
"change_display_order": "Промени реда на показване",
|
||||||
@@ -598,11 +620,11 @@
|
|||||||
"change_password": "Промени паролата",
|
"change_password": "Промени паролата",
|
||||||
"change_password_description": "Това е или първият път, когато влизате в системата, или е направена заявка за промяна на паролата ви. Моля, въведете новата парола по-долу.",
|
"change_password_description": "Това е или първият път, когато влизате в системата, или е направена заявка за промяна на паролата ви. Моля, въведете новата парола по-долу.",
|
||||||
"change_password_form_confirm_password": "Потвърди паролата",
|
"change_password_form_confirm_password": "Потвърди паролата",
|
||||||
"change_password_form_description": "Здравей {name},\n\nТова или е първото ти вписване в системата или има подадена заявка за смяна на паролата. Моля, въведи нова парола в полето по-долу.",
|
"change_password_form_description": "Здравейте {name},\n\nТова или е първото ви вписване в системата или има подадена заявка за смяна на паролата. Моля, въведете нова парола в полето по-долу.",
|
||||||
"change_password_form_new_password": "Нова парола",
|
"change_password_form_new_password": "Нова парола",
|
||||||
"change_password_form_password_mismatch": "Паролите не съвпадат",
|
"change_password_form_password_mismatch": "Паролите не съвпадат",
|
||||||
"change_password_form_reenter_new_password": "Повтори новата парола",
|
"change_password_form_reenter_new_password": "Повтори новата парола",
|
||||||
"change_pin_code": "Смени PIN-кода",
|
"change_pin_code": "Смени PIN кода",
|
||||||
"change_your_password": "Променете паролата си",
|
"change_your_password": "Променете паролата си",
|
||||||
"changed_visibility_successfully": "Видимостта е променена успешно",
|
"changed_visibility_successfully": "Видимостта е променена успешно",
|
||||||
"check_corrupt_asset_backup": "Провери за повредени архивни копия",
|
"check_corrupt_asset_backup": "Провери за повредени архивни копия",
|
||||||
@@ -635,17 +657,17 @@
|
|||||||
"comments_and_likes": "Коментари и харесвания",
|
"comments_and_likes": "Коментари и харесвания",
|
||||||
"comments_are_disabled": "Коментарите са деактивирани",
|
"comments_are_disabled": "Коментарите са деактивирани",
|
||||||
"common_create_new_album": "Създай нов албум",
|
"common_create_new_album": "Създай нов албум",
|
||||||
"common_server_error": "Моля, провери мрежовата връзка, убеди се, че сървъра е достъпен и версиите на сървъра и приложението са съвместими.",
|
"common_server_error": "Моля, проверете мрежовата връзка, убедете се, че сървъра е достъпен и версиите на сървъра и приложението са съвместими.",
|
||||||
"completed": "Завършено",
|
"completed": "Завършено",
|
||||||
"confirm": "Потвърди",
|
"confirm": "Потвърди",
|
||||||
"confirm_admin_password": "Потвърждаване на паролата на администратора",
|
"confirm_admin_password": "Потвърждаване на паролата на администратора",
|
||||||
"confirm_delete_face": "Сигурни ли сте, че искате да изтриете лицето на {name} от актива?",
|
"confirm_delete_face": "Сигурни ли сте, че искате да изтриете лицето на {name} от актива?",
|
||||||
"confirm_delete_shared_link": "Сигурни ли сте, че искате да изтриете тази споделена връзка?",
|
"confirm_delete_shared_link": "Сигурни ли сте, че искате да изтриете тази споделена връзка?",
|
||||||
"confirm_keep_this_delete_others": "Всички останали файлове в стека ще бъдат изтрити, с изключение на този файл. Сигурни ли сте, че искате да продължите?",
|
"confirm_keep_this_delete_others": "Всички останали файлове в стека ще бъдат изтрити, с изключение на този файл. Сигурни ли сте, че искате да продължите?",
|
||||||
"confirm_new_pin_code": "Потвърди новия PIN-код",
|
"confirm_new_pin_code": "Потвърди новия PIN код",
|
||||||
"confirm_password": "Потвърдете паролата",
|
"confirm_password": "Потвърдете паролата",
|
||||||
"confirm_tag_face": "Искаш ли да отбележиш това лице като {name}?",
|
"confirm_tag_face": "Искате ли да отбележите това лице като {name}?",
|
||||||
"confirm_tag_face_unnamed": "Искаш ли да отбележиш това лице?",
|
"confirm_tag_face_unnamed": "Искате ли да отбележите това лице?",
|
||||||
"connected_device": "Свързано устройство",
|
"connected_device": "Свързано устройство",
|
||||||
"connected_to": "Свързан към",
|
"connected_to": "Свързан към",
|
||||||
"contain": "В рамките на",
|
"contain": "В рамките на",
|
||||||
@@ -692,14 +714,14 @@
|
|||||||
"crop": "Изрежи",
|
"crop": "Изрежи",
|
||||||
"curated_object_page_title": "Неща",
|
"curated_object_page_title": "Неща",
|
||||||
"current_device": "Текущо устройство",
|
"current_device": "Текущо устройство",
|
||||||
"current_pin_code": "Сегашен PIN-код",
|
"current_pin_code": "Сегашен PIN код",
|
||||||
"current_server_address": "Настоящ адрес на сървъра",
|
"current_server_address": "Настоящ адрес на сървъра",
|
||||||
"custom_locale": "Персонализиран локал",
|
"custom_locale": "Персонализиран локал",
|
||||||
"custom_locale_description": "Форматиране на дати и числа в зависимост от езика и региона",
|
"custom_locale_description": "Форматиране на дати и числа в зависимост от езика и региона",
|
||||||
"daily_title_text_date": "E, dd MMM",
|
"daily_title_text_date": "E, dd MMM",
|
||||||
"daily_title_text_date_year": "E, dd MMM yyyy",
|
"daily_title_text_date_year": "E, dd MMM yyyy",
|
||||||
"dark": "Тъмен",
|
"dark": "Тъмен",
|
||||||
"darkTheme": "Превключи на тъмна тема",
|
"dark_theme": "Тъмна тема",
|
||||||
"date_after": "Дата след",
|
"date_after": "Дата след",
|
||||||
"date_and_time": "Дата и час",
|
"date_and_time": "Дата и час",
|
||||||
"date_before": "Дата преди",
|
"date_before": "Дата преди",
|
||||||
@@ -713,8 +735,9 @@
|
|||||||
"deduplication_info": "Информация за дедупликацията",
|
"deduplication_info": "Информация за дедупликацията",
|
||||||
"deduplication_info_description": "За автоматично предварително избиране на ресурси и премахване на дубликати на едро, разглеждаме:",
|
"deduplication_info_description": "За автоматично предварително избиране на ресурси и премахване на дубликати на едро, разглеждаме:",
|
||||||
"default_locale": "Локализация по подразбиране",
|
"default_locale": "Локализация по подразбиране",
|
||||||
"default_locale_description": "Форматиране на дати и числа в зависимост от местоположението на браузъра",
|
"default_locale_description": "Форматиране на дати и числа в зависимост от езиковата настройка на браузъра",
|
||||||
"delete": "Изтрий",
|
"delete": "Изтрий",
|
||||||
|
"delete_action_prompt": "{count} са изтрити завинаги",
|
||||||
"delete_album": "Изтрий албум",
|
"delete_album": "Изтрий албум",
|
||||||
"delete_api_key_prompt": "Сигурни ли сте, че искате да изтриете този API ключ?",
|
"delete_api_key_prompt": "Сигурни ли сте, че искате да изтриете този API ключ?",
|
||||||
"delete_dialog_alert": "Тези обекти ще бъдат изтрити завинаги и от Immich сървъра и от устройството",
|
"delete_dialog_alert": "Тези обекти ще бъдат изтрити завинаги и от Immich сървъра и от устройството",
|
||||||
@@ -728,19 +751,20 @@
|
|||||||
"delete_key": "Изтрий ключ",
|
"delete_key": "Изтрий ключ",
|
||||||
"delete_library": "Изтрий библиотека",
|
"delete_library": "Изтрий библиотека",
|
||||||
"delete_link": "Изтрий линк",
|
"delete_link": "Изтрий линк",
|
||||||
|
"delete_local_action_prompt": "{count} са изтрити локално",
|
||||||
"delete_local_dialog_ok_backed_up_only": "Изтрий локално само архивираните",
|
"delete_local_dialog_ok_backed_up_only": "Изтрий локално само архивираните",
|
||||||
"delete_local_dialog_ok_force": "Въпреки това изтрий",
|
"delete_local_dialog_ok_force": "Въпреки това изтрий",
|
||||||
"delete_others": "Изтрий останалите",
|
"delete_others": "Изтрий останалите",
|
||||||
"delete_shared_link": "Изтриване на споделен линк",
|
"delete_shared_link": "Изтриване на споделен линк",
|
||||||
"delete_shared_link_dialog_title": "Изтрий споделената връзка",
|
"delete_shared_link_dialog_title": "Изтрий споделената връзка",
|
||||||
"delete_tag": "Изтрий таг",
|
"delete_tag": "Изтрий таг",
|
||||||
"delete_tag_confirmation_prompt": "Сигурни ли сте, че искате да изтриете таг {tagName}?",
|
"delete_tag_confirmation_prompt": "Сигурни ли сте, че искате да изтриете тага {tagName}?",
|
||||||
"delete_user": "Изтрий потребител",
|
"delete_user": "Изтрий потребител",
|
||||||
"deleted_shared_link": "Изтрит споделен линк",
|
"deleted_shared_link": "Изтрит споделен линк",
|
||||||
"deletes_missing_assets": "Изтрива файлове, които липсват на диска",
|
"deletes_missing_assets": "Изтрива файлове, които липсват на диска",
|
||||||
"description": "Описание",
|
"description": "Описание",
|
||||||
"description_input_hint_text": "Добави описание...",
|
"description_input_hint_text": "Добави описание...",
|
||||||
"description_input_submit_error": "Неуспешно обновяване на описанието. За подробности виж в дневника",
|
"description_input_submit_error": "Неуспешно обновяване на описанието. За подробности вижте в дневника",
|
||||||
"details": "Детайли",
|
"details": "Детайли",
|
||||||
"direction": "Посока",
|
"direction": "Посока",
|
||||||
"disabled": "Изключено",
|
"disabled": "Изключено",
|
||||||
@@ -758,6 +782,7 @@
|
|||||||
"documentation": "Документация",
|
"documentation": "Документация",
|
||||||
"done": "Готово",
|
"done": "Готово",
|
||||||
"download": "Изтегли",
|
"download": "Изтегли",
|
||||||
|
"download_action_prompt": "Зареждане на {count} обекта",
|
||||||
"download_canceled": "Изтеглянето е отменено",
|
"download_canceled": "Изтеглянето е отменено",
|
||||||
"download_complete": "Изтеглянето завърши",
|
"download_complete": "Изтеглянето завърши",
|
||||||
"download_enqueue": "Изтеглянето е добавено в опашката",
|
"download_enqueue": "Изтеглянето е добавено в опашката",
|
||||||
@@ -795,6 +820,7 @@
|
|||||||
"edit_key": "Редактиране на ключ",
|
"edit_key": "Редактиране на ключ",
|
||||||
"edit_link": "Редактиране на линк",
|
"edit_link": "Редактиране на линк",
|
||||||
"edit_location": "Редактиране на местоположението",
|
"edit_location": "Редактиране на местоположението",
|
||||||
|
"edit_location_action_prompt": "{count} локации са редактирани",
|
||||||
"edit_location_dialog_title": "Местоположение",
|
"edit_location_dialog_title": "Местоположение",
|
||||||
"edit_name": "Редактиране на име",
|
"edit_name": "Редактиране на име",
|
||||||
"edit_people": "Редактиране на хора",
|
"edit_people": "Редактиране на хора",
|
||||||
@@ -813,13 +839,13 @@
|
|||||||
"empty_trash": "Изпразване на кош",
|
"empty_trash": "Изпразване на кош",
|
||||||
"empty_trash_confirmation": "Сигурни ли сте, че искате да изпразните кошчето? Това ще премахне всичко в кошчето за постоянно от Immich.\nНе можете да отмените това действие!",
|
"empty_trash_confirmation": "Сигурни ли сте, че искате да изпразните кошчето? Това ще премахне всичко в кошчето за постоянно от Immich.\nНе можете да отмените това действие!",
|
||||||
"enable": "Включване",
|
"enable": "Включване",
|
||||||
"enable_biometric_auth_description": "Въведи своя ПИН-код, за да разрешиш биометрично удостоверяване",
|
"enable_biometric_auth_description": "Въведете вашия PIN код, за да разрешите биометрично удостоверяване",
|
||||||
"enabled": "Включено",
|
"enabled": "Включено",
|
||||||
"end_date": "Крайна дата",
|
"end_date": "Крайна дата",
|
||||||
"enqueued": "Наредено в опашката",
|
"enqueued": "Наредено в опашката",
|
||||||
"enter_wifi_name": "Въведи име на Wi-Fi",
|
"enter_wifi_name": "Въведи име на Wi-Fi",
|
||||||
"enter_your_pin_code": "Въведи твоя PIN-код",
|
"enter_your_pin_code": "Въведете вашия PIN код",
|
||||||
"enter_your_pin_code_subtitle": "Въведи твоя PIN-код, за да достъпиш заключена папка",
|
"enter_your_pin_code_subtitle": "Въвеждане на PIN код, за достъп до заключена папка",
|
||||||
"error": "Грешка",
|
"error": "Грешка",
|
||||||
"error_change_sort_album": "Неуспешна промяна на реда на сортиране на албум",
|
"error_change_sort_album": "Неуспешна промяна на реда на сортиране на албум",
|
||||||
"error_delete_face": "Грешка при изтриване на лице от актива",
|
"error_delete_face": "Грешка при изтриване на лице от актива",
|
||||||
@@ -919,7 +945,7 @@
|
|||||||
"unable_to_remove_partner": "Неуспешно премахване на партньор",
|
"unable_to_remove_partner": "Неуспешно премахване на партньор",
|
||||||
"unable_to_remove_reaction": "Неуспешно премахване на реакцията",
|
"unable_to_remove_reaction": "Неуспешно премахване на реакцията",
|
||||||
"unable_to_reset_password": "Неуспешно смяна на паролата",
|
"unable_to_reset_password": "Неуспешно смяна на паролата",
|
||||||
"unable_to_reset_pin_code": "Неуспешно нулиране на PIN-кода",
|
"unable_to_reset_pin_code": "Неуспешно нулиране на PIN кода",
|
||||||
"unable_to_resolve_duplicate": "Неуспешно справяне с дублирането",
|
"unable_to_resolve_duplicate": "Неуспешно справяне с дублирането",
|
||||||
"unable_to_restore_assets": "Неуспешно възстановяване на елементи",
|
"unable_to_restore_assets": "Неуспешно възстановяване на елементи",
|
||||||
"unable_to_restore_trash": "Неуспешно възстановяване от кошчето",
|
"unable_to_restore_trash": "Неуспешно възстановяване от кошчето",
|
||||||
@@ -980,6 +1006,7 @@
|
|||||||
"failed_to_load_assets": "Неуспешно зареждане на елементи",
|
"failed_to_load_assets": "Неуспешно зареждане на елементи",
|
||||||
"failed_to_load_folder": "Неуспешно зареждане на папка",
|
"failed_to_load_folder": "Неуспешно зареждане на папка",
|
||||||
"favorite": "Любим",
|
"favorite": "Любим",
|
||||||
|
"favorite_action_prompt": "{count} са добавени в Любими",
|
||||||
"favorite_or_unfavorite_photo": "Добави или премахни снимка от Любими",
|
"favorite_or_unfavorite_photo": "Добави или премахни снимка от Любими",
|
||||||
"favorites": "Любими",
|
"favorites": "Любими",
|
||||||
"favorites_page_no_favorites": "Не са намерени любими обекти",
|
"favorites_page_no_favorites": "Не са намерени любими обекти",
|
||||||
@@ -1004,7 +1031,7 @@
|
|||||||
"gcast_enabled_description": "За да работи тази функция зарежда външни ресурси от Google.",
|
"gcast_enabled_description": "За да работи тази функция зарежда външни ресурси от Google.",
|
||||||
"general": "Общи",
|
"general": "Общи",
|
||||||
"get_help": "Помощ",
|
"get_help": "Помощ",
|
||||||
"get_wifiname_error": "Неуспешно получаване името на Wi-Fi мрежата. Моля, убеди се, че са предоставени нужните разрешения на приложението и има връзка с Wi-Fi",
|
"get_wifiname_error": "Неуспешно получаване името на Wi-Fi мрежата. Моля, убедете се, че са предоставени нужните разрешения на приложението и има връзка с Wi-Fi",
|
||||||
"getting_started": "Как да започнем",
|
"getting_started": "Как да започнем",
|
||||||
"go_back": "Връщане назад",
|
"go_back": "Връщане назад",
|
||||||
"go_to_folder": "Отиди в папката",
|
"go_to_folder": "Отиди в папката",
|
||||||
@@ -1043,7 +1070,7 @@
|
|||||||
"home_page_delete_remote_err_local": "Локални обекти не могат да се изтриват от сървъра, пропускане",
|
"home_page_delete_remote_err_local": "Локални обекти не могат да се изтриват от сървъра, пропускане",
|
||||||
"home_page_favorite_err_local": "Локални обекти все още не могат да се правят любими, пропускане",
|
"home_page_favorite_err_local": "Локални обекти все още не могат да се правят любими, пропускане",
|
||||||
"home_page_favorite_err_partner": "Партньорски обекти все още не могат да се правят любими, пропускане",
|
"home_page_favorite_err_partner": "Партньорски обекти все още не могат да се правят любими, пропускане",
|
||||||
"home_page_first_time_notice": "Ако за първи път използваш приложението, моля избери албум за архивиране, за да може обектите от времевата линия да се записват в него",
|
"home_page_first_time_notice": "Ако за първи път използвате приложението, моля изберете албум за архивиране, за да може обектите от времевата линия да се записват в него",
|
||||||
"home_page_locked_error_local": "Локални обекти не могат да се преместят в заключена папка, пропускане",
|
"home_page_locked_error_local": "Локални обекти не могат да се преместят в заключена папка, пропускане",
|
||||||
"home_page_locked_error_partner": "Партньорски обекти не могат да се преместят в заключена папка, пропускане",
|
"home_page_locked_error_partner": "Партньорски обекти не могат да се преместят в заключена папка, пропускане",
|
||||||
"home_page_share_err_local": "Локални обекти не могат да се споделят чрез връзка, пропускане",
|
"home_page_share_err_local": "Локални обекти не могат да се споделят чрез връзка, пропускане",
|
||||||
@@ -1094,6 +1121,7 @@
|
|||||||
"ios_debug_info_last_sync_at": "Синхронизирано на {dateTime}",
|
"ios_debug_info_last_sync_at": "Синхронизирано на {dateTime}",
|
||||||
"ios_debug_info_no_processes_queued": "Няма фонови процеси в опашката",
|
"ios_debug_info_no_processes_queued": "Няма фонови процеси в опашката",
|
||||||
"ios_debug_info_no_sync_yet": "Все още не е изпълнявана задача за фонова синхронизация",
|
"ios_debug_info_no_sync_yet": "Все още не е изпълнявана задача за фонова синхронизация",
|
||||||
|
"ios_debug_info_processes_queued": "{count, plural, one {{count} фонов процес} many {{count} фонови процеса} other {{count} фонови процеса}} в опашката",
|
||||||
"ios_debug_info_processing_ran_at": "Започната обработка на {dateTime}",
|
"ios_debug_info_processing_ran_at": "Започната обработка на {dateTime}",
|
||||||
"items_count": "{count, plural, one {# елемент} other {# елементи}}",
|
"items_count": "{count, plural, one {# елемент} other {# елементи}}",
|
||||||
"jobs": "Задачи",
|
"jobs": "Задачи",
|
||||||
@@ -1103,7 +1131,7 @@
|
|||||||
"kept_this_deleted_others": "Запази този елемент и другите изтрити {count, plural, one {# елемент} other {# елемента}}",
|
"kept_this_deleted_others": "Запази този елемент и другите изтрити {count, plural, one {# елемент} other {# елемента}}",
|
||||||
"keyboard_shortcuts": "Бързи клавишни комбинации",
|
"keyboard_shortcuts": "Бързи клавишни комбинации",
|
||||||
"language": "Език",
|
"language": "Език",
|
||||||
"language_no_results_subtitle": "Опитай да коригираш термина си за търсене",
|
"language_no_results_subtitle": "Опитайте да коригирате термина си за търсене",
|
||||||
"language_no_results_title": "Не са намерени езици",
|
"language_no_results_title": "Не са намерени езици",
|
||||||
"language_search_hint": "Търсене на езици...",
|
"language_search_hint": "Търсене на езици...",
|
||||||
"language_setting_description": "Изберете предпочитан език",
|
"language_setting_description": "Изберете предпочитан език",
|
||||||
@@ -1122,6 +1150,7 @@
|
|||||||
"library_page_sort_created": "Дата на създаване",
|
"library_page_sort_created": "Дата на създаване",
|
||||||
"library_page_sort_last_modified": "Последна промяна",
|
"library_page_sort_last_modified": "Последна промяна",
|
||||||
"library_page_sort_title": "Заглавие на албума",
|
"library_page_sort_title": "Заглавие на албума",
|
||||||
|
"licenses": "Лицензи",
|
||||||
"light": "Светло",
|
"light": "Светло",
|
||||||
"like_deleted": "Като изтрит",
|
"like_deleted": "Като изтрит",
|
||||||
"link_motion_video": "Линк към видео",
|
"link_motion_video": "Линк към видео",
|
||||||
@@ -1138,13 +1167,14 @@
|
|||||||
"location_permission_content": "За да работи функцията автоматично превключване, Immich се нуждае от разрешение за точно местоположение, за да може да чете името на текущата Wi-Fi мрежа",
|
"location_permission_content": "За да работи функцията автоматично превключване, Immich се нуждае от разрешение за точно местоположение, за да може да чете името на текущата Wi-Fi мрежа",
|
||||||
"location_picker_choose_on_map": "Избери на карта",
|
"location_picker_choose_on_map": "Избери на карта",
|
||||||
"location_picker_latitude_error": "Въведи правилна ширина",
|
"location_picker_latitude_error": "Въведи правилна ширина",
|
||||||
"location_picker_latitude_hint": "Въведи ширината тук",
|
"location_picker_latitude_hint": "Въведете географска ширина тук",
|
||||||
"location_picker_longitude_error": "Въведи правилна дължина",
|
"location_picker_longitude_error": "Въведи правилна дължина",
|
||||||
"location_picker_longitude_hint": "Въведи дължината тук",
|
"location_picker_longitude_hint": "Въведете географска дължина тук",
|
||||||
"lock": "Заключи",
|
"lock": "Заключи",
|
||||||
"locked_folder": "Заключена папка",
|
"locked_folder": "Заключена папка",
|
||||||
"log_out": "Излизане",
|
"log_out": "Излизане",
|
||||||
"log_out_all_devices": "Излизане с всички устройства",
|
"log_out_all_devices": "Излизане с всички устройства",
|
||||||
|
"logged_in_as": "Вписан като {user}",
|
||||||
"logged_out_all_devices": "Успешно излизане от всички устройства",
|
"logged_out_all_devices": "Успешно излизане от всички устройства",
|
||||||
"logged_out_device": "Успешно излизане от устройство",
|
"logged_out_device": "Успешно излизане от устройство",
|
||||||
"login": "Вписване",
|
"login": "Вписване",
|
||||||
@@ -1161,8 +1191,8 @@
|
|||||||
"login_form_err_trailing_whitespace": "Интервал в края",
|
"login_form_err_trailing_whitespace": "Интервал в края",
|
||||||
"login_form_failed_get_oauth_server_config": "Грешка при вписване с OAuth, провери URL на сървъра",
|
"login_form_failed_get_oauth_server_config": "Грешка при вписване с OAuth, провери URL на сървъра",
|
||||||
"login_form_failed_get_oauth_server_disable": "На този сървър OAuth не е достъпна",
|
"login_form_failed_get_oauth_server_disable": "На този сървър OAuth не е достъпна",
|
||||||
"login_form_failed_login": "Грешка при вписване, провери URL, имейла и паролата",
|
"login_form_failed_login": "Грешка при вписване, проверете URL, имейла и паролата",
|
||||||
"login_form_handshake_exception": "Грешка при договаряне на връзката със сървъра. Ако използваш самоподписан сертификат, разреши в настройкте използване на самоподписан сертификат.",
|
"login_form_handshake_exception": "Грешка при договаряне на връзката със сървъра. Ако използвате самоподписан сертификат, разрешете в настройкте използване на самоподписан сертификат.",
|
||||||
"login_form_password_hint": "парола",
|
"login_form_password_hint": "парола",
|
||||||
"login_form_save_login": "Остани вписан",
|
"login_form_save_login": "Остани вписан",
|
||||||
"login_form_server_empty": "Въведи URL на сървъра.",
|
"login_form_server_empty": "Въведи URL на сървъра.",
|
||||||
@@ -1192,12 +1222,12 @@
|
|||||||
"map_cannot_get_user_location": "Не можах да получа местоположението",
|
"map_cannot_get_user_location": "Не можах да получа местоположението",
|
||||||
"map_location_dialog_yes": "Да",
|
"map_location_dialog_yes": "Да",
|
||||||
"map_location_picker_page_use_location": "Използвай това местоположение",
|
"map_location_picker_page_use_location": "Използвай това местоположение",
|
||||||
"map_location_service_disabled_content": "За да се показват обектите от текущото място, трябва да бъде включена услугата за местоположение. Искаш ли да я включиш сега?",
|
"map_location_service_disabled_content": "За да се показват обектите от текущото място, трябва да бъде включена услугата за местоположение. Искате ли да я включите сега?",
|
||||||
"map_location_service_disabled_title": "Услугата за местоположение е изключена",
|
"map_location_service_disabled_title": "Услугата за местоположение е изключена",
|
||||||
"map_marker_for_images": "Маркери на картата за снимки направени в {city}, {country}",
|
"map_marker_for_images": "Маркери на картата за снимки направени в {city}, {country}",
|
||||||
"map_marker_with_image": "Маркер на картата с изображение",
|
"map_marker_with_image": "Маркер на картата с изображение",
|
||||||
"map_no_assets_in_bounds": "Няма снимки от този район",
|
"map_no_assets_in_bounds": "Няма снимки от този район",
|
||||||
"map_no_location_permission_content": "За да се показват обектите от текущото място, трябва разрешение за определяне на местоположението. Искаш ли да предоставиш разрешение сега?",
|
"map_no_location_permission_content": "За да се показват обектите от текущото място, трябва разрешение за определяне на местоположението. Искате ли да предоставите разрешение сега?",
|
||||||
"map_no_location_permission_title": "Отказан достъп до местоположение",
|
"map_no_location_permission_title": "Отказан достъп до местоположение",
|
||||||
"map_settings": "Настройки на картата",
|
"map_settings": "Настройки на картата",
|
||||||
"map_settings_dark_mode": "Тъмен режим",
|
"map_settings_dark_mode": "Тъмен режим",
|
||||||
@@ -1240,8 +1270,11 @@
|
|||||||
"more": "Още",
|
"more": "Още",
|
||||||
"move": "Премести",
|
"move": "Премести",
|
||||||
"move_off_locked_folder": "Извади от заключената папка",
|
"move_off_locked_folder": "Извади от заключената папка",
|
||||||
|
"move_to_lock_folder_action_prompt": "{count} са добавени в заключената папка",
|
||||||
"move_to_locked_folder": "Премести в заключена папка",
|
"move_to_locked_folder": "Премести в заключена папка",
|
||||||
"move_to_locked_folder_confirmation": "Тези снимки и видеа ще бъдат изтрити от всички албуми и ще са достъпни само в заключената папка",
|
"move_to_locked_folder_confirmation": "Тези снимки и видеа ще бъдат изтрити от всички албуми и ще са достъпни само в заключената папка",
|
||||||
|
"moved_to_archive": "{count, plural, one {# обект е преместен} many {# обекта са преместени} other {# обекта са преместени}} в архива",
|
||||||
|
"moved_to_library": "{count, plural, one {# обект е преместен} many {# обекта са преместени} other {# обекта са преместени}} в библиотеката",
|
||||||
"moved_to_trash": "Преместено в кошчето",
|
"moved_to_trash": "Преместено в кошчето",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Не може да се редактира датата на обект само за четене, пропускане",
|
"multiselect_grid_edit_date_time_err_read_only": "Не може да се редактира датата на обект само за четене, пропускане",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Не може да се редактира местоположението на обект само за четене, пропускане",
|
"multiselect_grid_edit_gps_err_read_only": "Не може да се редактира местоположението на обект само за четене, пропускане",
|
||||||
@@ -1257,14 +1290,14 @@
|
|||||||
"new_password": "Нова парола",
|
"new_password": "Нова парола",
|
||||||
"new_person": "Нов човек",
|
"new_person": "Нов човек",
|
||||||
"new_pin_code": "Нов PIN код",
|
"new_pin_code": "Нов PIN код",
|
||||||
"new_pin_code_subtitle": "Това е първи достъп до заключена папка. Създай PIN код за защитен достъп до тази страница",
|
"new_pin_code_subtitle": "Това е първи достъп до заключена папка. Създайте PIN код за защитен достъп до тази страница",
|
||||||
"new_user_created": "Създаден нов потребител",
|
"new_user_created": "Създаден нов потребител",
|
||||||
"new_version_available": "НАЛИЧНА НОВА ВЕРСИЯ",
|
"new_version_available": "НАЛИЧНА НОВА ВЕРСИЯ",
|
||||||
"newest_first": "Най-новите първи",
|
"newest_first": "Най-новите първи",
|
||||||
"next": "Следващо",
|
"next": "Следващо",
|
||||||
"next_memory": "Следващ спомен",
|
"next_memory": "Следващ спомен",
|
||||||
"no": "Не",
|
"no": "Не",
|
||||||
"no_albums_message": "Създаване на албум за организиране на снимки и видеоклипове",
|
"no_albums_message": "Създайте албум за организиране на снимки и видеоклипове",
|
||||||
"no_albums_with_name_yet": "Изглежда, че все още нямате албуми с това име.",
|
"no_albums_with_name_yet": "Изглежда, че все още нямате албуми с това име.",
|
||||||
"no_albums_yet": "Изглежда, че все още нямате албуми.",
|
"no_albums_yet": "Изглежда, че все още нямате албуми.",
|
||||||
"no_archived_assets_message": "Архивирайте снимки и видеоклипове, за да ги скриете от изгледа на Снимки",
|
"no_archived_assets_message": "Архивирайте снимки и видеоклипове, за да ги скриете от изгледа на Снимки",
|
||||||
@@ -1274,8 +1307,8 @@
|
|||||||
"no_duplicates_found": "Не бяха открити дубликати.",
|
"no_duplicates_found": "Не бяха открити дубликати.",
|
||||||
"no_exif_info_available": "Няма exif информация",
|
"no_exif_info_available": "Няма exif информация",
|
||||||
"no_explore_results_message": "Качете още снимки, за да разгледате колекцията си.",
|
"no_explore_results_message": "Качете още снимки, за да разгледате колекцията си.",
|
||||||
"no_favorites_message": "Добавяне на любими, за да намерите бързо най-добрите си снимки и видеоклипове",
|
"no_favorites_message": "Добавете в любими, за да намирате бързо най-добрите си снимки и видеоклипове",
|
||||||
"no_libraries_message": "Създаване на външна библиотека за разглеждане на снимки и видеоклипове",
|
"no_libraries_message": "Създайте външна библиотека за да разглеждате снимки и видеоклипове",
|
||||||
"no_locked_photos_message": "Снимките и видеата в заключената папка са скрити и не се показват при разглеждане на библиотеката.",
|
"no_locked_photos_message": "Снимките и видеата в заключената папка са скрити и не се показват при разглеждане на библиотеката.",
|
||||||
"no_name": "Без име",
|
"no_name": "Без име",
|
||||||
"no_notifications": "Няма известия",
|
"no_notifications": "Няма известия",
|
||||||
@@ -1303,7 +1336,7 @@
|
|||||||
"oldest_first": "Най-старите първи",
|
"oldest_first": "Най-старите първи",
|
||||||
"on_this_device": "На това устройство",
|
"on_this_device": "На това устройство",
|
||||||
"onboarding": "Въвеждане",
|
"onboarding": "Въвеждане",
|
||||||
"onboarding_locale_description": "Избери предпочитан език. По-късно може да го промениш в Настройки.",
|
"onboarding_locale_description": "Изберете предпочитан език. По-късно може да го промените в Настройки.",
|
||||||
"onboarding_privacy_description": "Следните (незадължителни) функции разчитат на външни услуги и могат да бъдат деактивирани по всяко време в настройките.",
|
"onboarding_privacy_description": "Следните (незадължителни) функции разчитат на външни услуги и могат да бъдат деактивирани по всяко време в настройките.",
|
||||||
"onboarding_server_welcome_description": "Да направим общите настройки на вашата инсталация.",
|
"onboarding_server_welcome_description": "Да направим общите настройки на вашата инсталация.",
|
||||||
"onboarding_theme_description": "Изберете цветова тема. Може да я промените по-късно в настройките.",
|
"onboarding_theme_description": "Изберете цветова тема. Може да я промените по-късно в настройките.",
|
||||||
@@ -1335,7 +1368,7 @@
|
|||||||
"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} вече няма да има достъп до твоите снимки.",
|
"partner_page_stop_sharing_content": "{partner} вече няма да има достъп до вашите снимки.",
|
||||||
"partner_sharing": "Споделяне с партньори",
|
"partner_sharing": "Споделяне с партньори",
|
||||||
"partners": "Партньори",
|
"partners": "Партньори",
|
||||||
"password": "Парола",
|
"password": "Парола",
|
||||||
@@ -1372,7 +1405,7 @@
|
|||||||
"permission_onboarding_go_to_settings": "Отиди в настройки",
|
"permission_onboarding_go_to_settings": "Отиди в настройки",
|
||||||
"permission_onboarding_permission_denied": "Отказан достъп. За да ползваш Immich, разреши в настройките достъп до снимки и видео.",
|
"permission_onboarding_permission_denied": "Отказан достъп. За да ползваш Immich, разреши в настройките достъп до снимки и видео.",
|
||||||
"permission_onboarding_permission_granted": "Предоставено е разрешение! Всичко е готово.",
|
"permission_onboarding_permission_granted": "Предоставено е разрешение! Всичко е готово.",
|
||||||
"permission_onboarding_permission_limited": "Ограничен достъп. За да може Immich да архивира и управлява галерията, предостави достъп до снимки и видео в настройките.",
|
"permission_onboarding_permission_limited": "Ограничен достъп. За да може Immich да архивира и управлява галерията, предоставете достъп до снимки и видео в настройките.",
|
||||||
"permission_onboarding_request": "Immich се нуждае от разрешение за преглед на снимки и видео.",
|
"permission_onboarding_request": "Immich се нуждае от разрешение за преглед на снимки и видео.",
|
||||||
"person": "Човек",
|
"person": "Човек",
|
||||||
"person_birthdate": "Дата на раждане {date}",
|
"person_birthdate": "Дата на раждане {date}",
|
||||||
@@ -1383,10 +1416,10 @@
|
|||||||
"photos_count": "{count, plural, one {{count, number} Снимка} other {{count, number} Снимки}}",
|
"photos_count": "{count, plural, one {{count, number} Снимка} other {{count, number} Снимки}}",
|
||||||
"photos_from_previous_years": "Снимки от предходни години",
|
"photos_from_previous_years": "Снимки от предходни години",
|
||||||
"pick_a_location": "Избери локация",
|
"pick_a_location": "Избери локация",
|
||||||
"pin_code_changed_successfully": "Успешно сменен PIN-код",
|
"pin_code_changed_successfully": "Успешно сменен PIN код",
|
||||||
"pin_code_reset_successfully": "Успешно нулиран PIN-код",
|
"pin_code_reset_successfully": "Успешно нулиран PIN код",
|
||||||
"pin_code_setup_successfully": "Успешно зададен PIN-код",
|
"pin_code_setup_successfully": "Успешно зададен PIN код",
|
||||||
"pin_verification": "Проверка на PIN-кода",
|
"pin_verification": "Проверка на PIN кода",
|
||||||
"place": "Местоположение",
|
"place": "Местоположение",
|
||||||
"places": "Местоположения",
|
"places": "Местоположения",
|
||||||
"places_count": "{count, plural, one {{count, number} Място} other {{count, number} Места}}",
|
"places_count": "{count, plural, one {{count, number} Място} other {{count, number} Места}}",
|
||||||
@@ -1440,7 +1473,7 @@
|
|||||||
"purchase_lifetime_description": "Покупка за цял живот",
|
"purchase_lifetime_description": "Покупка за цял живот",
|
||||||
"purchase_option_title": "ОПЦИИ ЗА ЗАКУПУВАНЕ",
|
"purchase_option_title": "ОПЦИИ ЗА ЗАКУПУВАНЕ",
|
||||||
"purchase_panel_info_1": "Създаването на Immich отнема много време и усилия, и имаме инженери на пълно работно време, които работят по него, за да го направим възможно най-добро. Нашата мисия е софтуерът с отворен код и етичните бизнес практики да се превърнат в устойчив източник на доходи за разработчиците и да създадем екосистема, която уважава личната неприкосновеност и предлага истински алтернативи на експлоататорските облачни услуги.",
|
"purchase_panel_info_1": "Създаването на Immich отнема много време и усилия, и имаме инженери на пълно работно време, които работят по него, за да го направим възможно най-добро. Нашата мисия е софтуерът с отворен код и етичните бизнес практики да се превърнат в устойчив източник на доходи за разработчиците и да създадем екосистема, която уважава личната неприкосновеност и предлага истински алтернативи на експлоататорските облачни услуги.",
|
||||||
"purchase_panel_info_2": "Тъй като сме ангажирани да не добавяме платени стени, тази покупка няма да ви предостави допълнителни функции в Immich. Ние разчитаме на потребители като вас, за да подкрепяте продължаващото развитие на Immich.",
|
"purchase_panel_info_2": "Тъй като сме обещали да не добавяме платени функции, тази покупка няма да ви предостави допълнителни функции в Immich. Ние разчитаме на потребители като вас, за да подкрепите продължаване на развитието на Immich.",
|
||||||
"purchase_panel_title": "Поддържайте проекта",
|
"purchase_panel_title": "Поддържайте проекта",
|
||||||
"purchase_per_server": "на сървър",
|
"purchase_per_server": "на сървър",
|
||||||
"purchase_per_user": "на потребител",
|
"purchase_per_user": "на потребител",
|
||||||
@@ -1487,9 +1520,11 @@
|
|||||||
"remove_custom_date_range": "Премахни зададения диапазон от дати",
|
"remove_custom_date_range": "Премахни зададения диапазон от дати",
|
||||||
"remove_deleted_assets": "Премахни Изтритите Елементи",
|
"remove_deleted_assets": "Премахни Изтритите Елементи",
|
||||||
"remove_from_album": "Премахни от албума",
|
"remove_from_album": "Премахни от албума",
|
||||||
|
"remove_from_album_action_prompt": "{count} са премахнати от албума",
|
||||||
"remove_from_favorites": "Премахни от Любими",
|
"remove_from_favorites": "Премахни от Любими",
|
||||||
|
"remove_from_lock_folder_action_prompt": "{count} са премахнати от заключената папка",
|
||||||
"remove_from_locked_folder": "Махни от заключената папка",
|
"remove_from_locked_folder": "Махни от заключената папка",
|
||||||
"remove_from_locked_folder_confirmation": "Сигурен ли си, че искаш тези снимки и видеа да бъдат извадени от заключената папка? Те ще бъдат видими в библиотеката.",
|
"remove_from_locked_folder_confirmation": "Сигурни ли си, че искате тези снимки и видеа да бъдат извадени от заключената папка? Те ще бъдат видими в библиотеката.",
|
||||||
"remove_from_shared_link": "Премахни от споделения линк",
|
"remove_from_shared_link": "Премахни от споделения линк",
|
||||||
"remove_memory": "Премахни спомените",
|
"remove_memory": "Премахни спомените",
|
||||||
"remove_photo_from_memory": "Премахни снимките от спомените",
|
"remove_photo_from_memory": "Премахни снимките от спомените",
|
||||||
@@ -1514,7 +1549,7 @@
|
|||||||
"reset": "Нулиране",
|
"reset": "Нулиране",
|
||||||
"reset_password": "Нулиране на паролата",
|
"reset_password": "Нулиране на паролата",
|
||||||
"reset_people_visibility": "Нулиране на видимостта на хората",
|
"reset_people_visibility": "Нулиране на видимостта на хората",
|
||||||
"reset_pin_code": "Нулирай PIN-кода",
|
"reset_pin_code": "Нулирай PIN кода",
|
||||||
"reset_to_default": "Връщане на фабрични настройки",
|
"reset_to_default": "Връщане на фабрични настройки",
|
||||||
"resolve_duplicates": "Реши дубликатите",
|
"resolve_duplicates": "Реши дубликатите",
|
||||||
"resolved_all_duplicates": "Всички дубликати са решени",
|
"resolved_all_duplicates": "Всички дубликати са решени",
|
||||||
@@ -1579,8 +1614,8 @@
|
|||||||
"search_page_selfies": "Селфита",
|
"search_page_selfies": "Селфита",
|
||||||
"search_page_things": "Предмети",
|
"search_page_things": "Предмети",
|
||||||
"search_page_view_all_button": "Виж всички",
|
"search_page_view_all_button": "Виж всички",
|
||||||
"search_page_your_activity": "Твоите действия",
|
"search_page_your_activity": "Вашите действия",
|
||||||
"search_page_your_map": "Твоята карта",
|
"search_page_your_map": "Вашата карта",
|
||||||
"search_people": "Търсете на хора",
|
"search_people": "Търсете на хора",
|
||||||
"search_places": "Търсене на места",
|
"search_places": "Търсене на места",
|
||||||
"search_rating": "Търси по рейтинг…",
|
"search_rating": "Търси по рейтинг…",
|
||||||
@@ -1588,7 +1623,7 @@
|
|||||||
"search_settings": "Търсене на настройки",
|
"search_settings": "Търсене на настройки",
|
||||||
"search_state": "Търсене на щат...",
|
"search_state": "Търсене на щат...",
|
||||||
"search_suggestion_list_smart_search_hint_1": "Умното търсене е включено по подразбиране, за търсене в метаданните използвай специален синтакс ",
|
"search_suggestion_list_smart_search_hint_1": "Умното търсене е включено по подразбиране, за търсене в метаданните използвай специален синтакс ",
|
||||||
"search_suggestion_list_smart_search_hint_2": "m:твоя-термин-за-търсене",
|
"search_suggestion_list_smart_search_hint_2": "m:вашия-термин-за-търсене",
|
||||||
"search_tags": "Търсене на етикети...",
|
"search_tags": "Търсене на етикети...",
|
||||||
"search_timezone": "Търсене на часова зона...",
|
"search_timezone": "Търсене на часова зона...",
|
||||||
"search_type": "Тип на търсене",
|
"search_type": "Тип на търсене",
|
||||||
@@ -1600,6 +1635,7 @@
|
|||||||
"select_album_cover": "Изберете обложка на албум",
|
"select_album_cover": "Изберете обложка на албум",
|
||||||
"select_all": "Изберете всички",
|
"select_all": "Изберете всички",
|
||||||
"select_all_duplicates": "Избери всички дубликати",
|
"select_all_duplicates": "Избери всички дубликати",
|
||||||
|
"select_all_in": "Избери всички от групата {group}",
|
||||||
"select_avatar_color": "Изберете цвят на аватара",
|
"select_avatar_color": "Изберете цвят на аватара",
|
||||||
"select_face": "Изберете лице",
|
"select_face": "Изберете лице",
|
||||||
"select_featured_photo": "Избери представителна снимка",
|
"select_featured_photo": "Избери представителна снимка",
|
||||||
@@ -1656,15 +1692,16 @@
|
|||||||
"settings": "Настройки",
|
"settings": "Настройки",
|
||||||
"settings_require_restart": "Моля, за да се приложи настройката рестартирай Immich",
|
"settings_require_restart": "Моля, за да се приложи настройката рестартирай Immich",
|
||||||
"settings_saved": "Настройките са запазени",
|
"settings_saved": "Настройките са запазени",
|
||||||
"setup_pin_code": "Задай PIN-код",
|
"setup_pin_code": "Задай PIN код",
|
||||||
"share": "Споделяне",
|
"share": "Споделяне",
|
||||||
|
"share_action_prompt": "{count} споделени обекта",
|
||||||
"share_add_photos": "Добави снимки",
|
"share_add_photos": "Добави снимки",
|
||||||
"share_assets_selected": "{count} избрани",
|
"share_assets_selected": "{count} избрани",
|
||||||
"share_dialog_preparing": "Подготовка...",
|
"share_dialog_preparing": "Подготовка...",
|
||||||
"share_link": "Връзка за споделяне",
|
"share_link": "Връзка за споделяне",
|
||||||
"shared": "Споделено",
|
"shared": "Споделено",
|
||||||
"shared_album_activities_input_disable": "Коментарите са изключени",
|
"shared_album_activities_input_disable": "Коментарите са изключени",
|
||||||
"shared_album_activity_remove_content": "Искаш ли да изтриеш тази активност?",
|
"shared_album_activity_remove_content": "Искате ли да изтриете тази активност?",
|
||||||
"shared_album_activity_remove_title": "Изтрий",
|
"shared_album_activity_remove_title": "Изтрий",
|
||||||
"shared_album_section_people_action_error": "Грешка при напускане/премахване от албума",
|
"shared_album_section_people_action_error": "Грешка при напускане/премахване от албума",
|
||||||
"shared_album_section_people_action_leave": "Премахване на потребител от албума",
|
"shared_album_section_people_action_leave": "Премахване на потребител от албума",
|
||||||
@@ -1672,7 +1709,7 @@
|
|||||||
"shared_album_section_people_title": "ХОРА",
|
"shared_album_section_people_title": "ХОРА",
|
||||||
"shared_by": "Споделено от",
|
"shared_by": "Споделено от",
|
||||||
"shared_by_user": "Споделено от {user}",
|
"shared_by_user": "Споделено от {user}",
|
||||||
"shared_by_you": "Споделено от теб",
|
"shared_by_you": "Споделено от вас",
|
||||||
"shared_from_partner": "Снимки от {partner}",
|
"shared_from_partner": "Снимки от {partner}",
|
||||||
"shared_intent_upload_button_progress_text": "{current} / {total} Заредено",
|
"shared_intent_upload_button_progress_text": "{current} / {total} Заредено",
|
||||||
"shared_link_app_bar_title": "Споделени връзки",
|
"shared_link_app_bar_title": "Споделени връзки",
|
||||||
@@ -1712,7 +1749,7 @@
|
|||||||
"sharing": "Споделени",
|
"sharing": "Споделени",
|
||||||
"sharing_enter_password": "Моля, въведете паролата, за да видите тази страница.",
|
"sharing_enter_password": "Моля, въведете паролата, за да видите тази страница.",
|
||||||
"sharing_page_album": "Споделени албуми",
|
"sharing_page_album": "Споделени албуми",
|
||||||
"sharing_page_description": "Създай споделени албуми за да споделиш снимки и видеа с хора от твоята мрежа.",
|
"sharing_page_description": "Създайте споделени албуми за да споделите снимки и видеа с хора от вашата мрежа.",
|
||||||
"sharing_page_empty_list": "ПРАЗЕН СПИСЪК",
|
"sharing_page_empty_list": "ПРАЗЕН СПИСЪК",
|
||||||
"sharing_sidebar_description": "Покажи връзка към Споделяне в страничната лента",
|
"sharing_sidebar_description": "Покажи връзка към Споделяне в страничната лента",
|
||||||
"sharing_silver_appbar_create_shared_album": "Нов споделен албум",
|
"sharing_silver_appbar_create_shared_album": "Нов споделен албум",
|
||||||
@@ -1759,6 +1796,7 @@
|
|||||||
"sort_title": "Заглавие",
|
"sort_title": "Заглавие",
|
||||||
"source": "Код",
|
"source": "Код",
|
||||||
"stack": "Събери",
|
"stack": "Събери",
|
||||||
|
"stack_action_prompt": "{count} са групирани",
|
||||||
"stack_duplicates": "Подреждане на дубликати",
|
"stack_duplicates": "Подреждане на дубликати",
|
||||||
"stack_select_one_photo": "Избери една главна снимка за събраните снимки",
|
"stack_select_one_photo": "Избери една главна снимка за събраните снимки",
|
||||||
"stack_selected_photos": "Подреждане на избрани снимки",
|
"stack_selected_photos": "Подреждане на избрани снимки",
|
||||||
@@ -1787,6 +1825,7 @@
|
|||||||
"sync": "Синхронизиране",
|
"sync": "Синхронизиране",
|
||||||
"sync_albums": "Синхронизиране на албуми",
|
"sync_albums": "Синхронизиране на албуми",
|
||||||
"sync_albums_manual_subtitle": "Синхронизирай всички заредени видеа и снимки в избраните архивни албуми",
|
"sync_albums_manual_subtitle": "Синхронизирай всички заредени видеа и снимки в избраните архивни албуми",
|
||||||
|
"sync_upload_album_setting_subtitle": "Създавайте и зареждайте снимки и видеа в избрани албуми в Immich",
|
||||||
"tag": "Таг",
|
"tag": "Таг",
|
||||||
"tag_assets": "Тагни елементи",
|
"tag_assets": "Тагни елементи",
|
||||||
"tag_created": "Създаден етикет: {tag}",
|
"tag_created": "Създаден етикет: {tag}",
|
||||||
@@ -1800,6 +1839,19 @@
|
|||||||
"theme": "Тема",
|
"theme": "Тема",
|
||||||
"theme_selection": "Избор на тема",
|
"theme_selection": "Избор на тема",
|
||||||
"theme_selection_description": "Автоматично задаване на светла или тъмна тема въз основа на системните предпочитания на вашия браузър",
|
"theme_selection_description": "Автоматично задаване на светла или тъмна тема въз основа на системните предпочитания на вашия браузър",
|
||||||
|
"theme_setting_asset_list_storage_indicator_title": "Показвай индикатор за хранилището в заглавията на обектите",
|
||||||
|
"theme_setting_asset_list_tiles_per_row_title": "Брой обекти на ред ({count})",
|
||||||
|
"theme_setting_colorful_interface_subtitle": "Нанеси основен цвят върху фоновите повърхности.",
|
||||||
|
"theme_setting_colorful_interface_title": "Цветове на интерфейса",
|
||||||
|
"theme_setting_image_viewer_quality_subtitle": "Регулиране на качеството на програмата за преглед на детайлни изображения",
|
||||||
|
"theme_setting_image_viewer_quality_title": "Качество на прегледа на изображения",
|
||||||
|
"theme_setting_primary_color_subtitle": "Избери цвят за основните действия и акценти.",
|
||||||
|
"theme_setting_primary_color_title": "Основен цвят",
|
||||||
|
"theme_setting_system_primary_color_title": "Използвай от системата",
|
||||||
|
"theme_setting_system_theme_switch": "Автоматично (Според системната настройка)",
|
||||||
|
"theme_setting_theme_subtitle": "Задай настройки на цветовата тема на приложението",
|
||||||
|
"theme_setting_three_stage_loading_subtitle": "Три-степенното зареждане може да увеличи производителността, но ще увеличи значително и мрежовия трафик",
|
||||||
|
"theme_setting_three_stage_loading_title": "Включи три-степенно зареждане",
|
||||||
"they_will_be_merged_together": "Те ще бъдат обединени",
|
"they_will_be_merged_together": "Те ще бъдат обединени",
|
||||||
"third_party_resources": "Ресурси от трети страни",
|
"third_party_resources": "Ресурси от трети страни",
|
||||||
"time_based_memories": "Спомени, базирани на времето",
|
"time_based_memories": "Спомени, базирани на времето",
|
||||||
@@ -1815,15 +1867,29 @@
|
|||||||
"total": "Общо",
|
"total": "Общо",
|
||||||
"total_usage": "Общо използвано",
|
"total_usage": "Общо използвано",
|
||||||
"trash": "Кошче",
|
"trash": "Кошче",
|
||||||
|
"trash_action_prompt": "{count} са преместени в коша",
|
||||||
"trash_all": "Изхвърли всички",
|
"trash_all": "Изхвърли всички",
|
||||||
"trash_count": "В Кошчето {count, number}",
|
"trash_count": "В Кошчето {count, number}",
|
||||||
"trash_delete_asset": "Вкарай в Кошчето/Изтрий елемент",
|
"trash_delete_asset": "Вкарай в Кошчето/Изтрий елемент",
|
||||||
|
"trash_emptied": "Коша е изпразнен",
|
||||||
"trash_no_results_message": "Изтритите снимки и видеоклипове ще се показват тук.",
|
"trash_no_results_message": "Изтритите снимки и видеоклипове ще се показват тук.",
|
||||||
|
"trash_page_delete_all": "Изтрий всичко",
|
||||||
|
"trash_page_empty_trash_dialog_content": "Искате ли да изпразня коша? Тези обекти ще бъдат изтрити завинаги от Immich",
|
||||||
|
"trash_page_info": "Обектите в коша ще бъдат изтривани завинаги след {days} дни",
|
||||||
|
"trash_page_no_assets": "Коша е празен",
|
||||||
|
"trash_page_restore_all": "Възстановяване на всички",
|
||||||
|
"trash_page_select_assets_btn": "Избери обекти",
|
||||||
|
"trash_page_title": "В коша ({count})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Изхвърлените в кошчето елементи ще бъдат изтрити за постоянно след {days, plural, one {# ден} other {# дни}}.",
|
"trashed_items_will_be_permanently_deleted_after": "Изхвърлените в кошчето елементи ще бъдат изтрити за постоянно след {days, plural, one {# ден} other {# дни}}.",
|
||||||
"type": "Тип",
|
"type": "Тип",
|
||||||
|
"unable_to_change_pin_code": "Невъзможна промяна на PIN кода",
|
||||||
|
"unable_to_setup_pin_code": "Неуспешно задаване на PIN кода",
|
||||||
"unarchive": "Разархивирай",
|
"unarchive": "Разархивирай",
|
||||||
|
"unarchive_action_prompt": "{count} са премахнати от Архива",
|
||||||
"unarchived_count": "{count, plural, other {Неархивирани #}}",
|
"unarchived_count": "{count, plural, other {Неархивирани #}}",
|
||||||
|
"undo": "Отмени",
|
||||||
"unfavorite": "Премахване от любимите",
|
"unfavorite": "Премахване от любимите",
|
||||||
|
"unfavorite_action_prompt": "{count} са премахнати от Любими",
|
||||||
"unhide_person": "Покажи отново човека",
|
"unhide_person": "Покажи отново човека",
|
||||||
"unknown": "Неизвестно",
|
"unknown": "Неизвестно",
|
||||||
"unknown_country": "Непозната Държава",
|
"unknown_country": "Непозната Държава",
|
||||||
@@ -1839,12 +1905,18 @@
|
|||||||
"unsaved_change": "Незапазена промяна",
|
"unsaved_change": "Незапазена промяна",
|
||||||
"unselect_all": "Деселектирайте всички",
|
"unselect_all": "Деселектирайте всички",
|
||||||
"unselect_all_duplicates": "От маркирай всички дубликати",
|
"unselect_all_duplicates": "От маркирай всички дубликати",
|
||||||
|
"unselect_all_in": "Премахни избора на всички от групата {group}",
|
||||||
"unstack": "Разкачи",
|
"unstack": "Разкачи",
|
||||||
|
"unstack_action_prompt": "{count} са разгрупирани",
|
||||||
"unstacked_assets_count": "Разкачени {count, plural, one {# елемент} other {# елементи}}",
|
"unstacked_assets_count": "Разкачени {count, plural, one {# елемент} other {# елементи}}",
|
||||||
|
"untagged": "Немаркирани",
|
||||||
"up_next": "Следващ",
|
"up_next": "Следващ",
|
||||||
|
"updated_at": "Обновено",
|
||||||
"updated_password": "Паролата е актуализирана",
|
"updated_password": "Паролата е актуализирана",
|
||||||
"upload": "Качване",
|
"upload": "Качване",
|
||||||
"upload_concurrency": "Успоредни качвания",
|
"upload_concurrency": "Успоредни качвания",
|
||||||
|
"upload_dialog_info": "Искате ли да архивирате на сървъра избраните обекти?",
|
||||||
|
"upload_dialog_title": "Качи обект",
|
||||||
"upload_errors": "Качването е завъшено с {count, plural, one {# грешка} other {# грешки}}, обновете страницата за да видите новите елементи.",
|
"upload_errors": "Качването е завъшено с {count, plural, one {# грешка} other {# грешки}}, обновете страницата за да видите новите елементи.",
|
||||||
"upload_progress": "Остават {remaining, number} - Обработени {processed, number}/{total, number}",
|
"upload_progress": "Остават {remaining, number} - Обработени {processed, number}/{total, number}",
|
||||||
"upload_skipped_duplicates": "Прескочени {count, plural, one {# дублиран елемент} other {# дублирани елементи}}",
|
"upload_skipped_duplicates": "Прескочени {count, plural, one {# дублиран елемент} other {# дублирани елементи}}",
|
||||||
@@ -1852,13 +1924,22 @@
|
|||||||
"upload_status_errors": "Грешки",
|
"upload_status_errors": "Грешки",
|
||||||
"upload_status_uploaded": "Качено",
|
"upload_status_uploaded": "Качено",
|
||||||
"upload_success": "Качването е успешно, опреснете страницата, за да видите новите файлове.",
|
"upload_success": "Качването е успешно, опреснете страницата, за да видите новите файлове.",
|
||||||
|
"upload_to_immich": "Казване в Immich ({count})",
|
||||||
|
"uploading": "Качваме",
|
||||||
|
"url": "URL",
|
||||||
"usage": "Потребление",
|
"usage": "Потребление",
|
||||||
|
"use_biometric": "Използвай биометрия",
|
||||||
|
"use_current_connection": "използвай текущата връзка",
|
||||||
"use_custom_date_range": "Използвайте собствен диапазон от дати вместо това",
|
"use_custom_date_range": "Използвайте собствен диапазон от дати вместо това",
|
||||||
"user": "Потребител",
|
"user": "Потребител",
|
||||||
|
"user_has_been_deleted": "Този потребител е премахнат.",
|
||||||
"user_id": "Потребител ИД",
|
"user_id": "Потребител ИД",
|
||||||
"user_liked": "{user} хареса {type, select, photo {тази снимка} video {това видео} asset {този елемент} other {}}",
|
"user_liked": "{user} хареса {type, select, photo {тази снимка} video {това видео} asset {този елемент} other {}}",
|
||||||
|
"user_pin_code_settings": "PIN код",
|
||||||
|
"user_pin_code_settings_description": "Управлявайте настройките на PIN кода",
|
||||||
|
"user_privacy": "Поверителност на потребителите",
|
||||||
"user_purchase_settings": "Покупка",
|
"user_purchase_settings": "Покупка",
|
||||||
"user_purchase_settings_description": "Управлявай покупката си",
|
"user_purchase_settings_description": "Управлявайте покупката си",
|
||||||
"user_role_set": "Задай {user} като {role}",
|
"user_role_set": "Задай {user} като {role}",
|
||||||
"user_usage_detail": "Подробности за използването на потребителя",
|
"user_usage_detail": "Подробности за използването на потребителя",
|
||||||
"user_usage_stats": "Статистика за използването на акаунта",
|
"user_usage_stats": "Статистика за използването на акаунта",
|
||||||
@@ -1867,6 +1948,7 @@
|
|||||||
"users": "Потребители",
|
"users": "Потребители",
|
||||||
"utilities": "Инструменти",
|
"utilities": "Инструменти",
|
||||||
"validate": "Валидиране",
|
"validate": "Валидиране",
|
||||||
|
"validate_endpoint_error": "Моля, въведи правилен URL",
|
||||||
"variables": "Променливи",
|
"variables": "Променливи",
|
||||||
"version": "Версия",
|
"version": "Версия",
|
||||||
"version_announcement_closing": "Твой приятел, Алекс",
|
"version_announcement_closing": "Твой приятел, Алекс",
|
||||||
@@ -1888,16 +1970,24 @@
|
|||||||
"view_name": "Прегледай",
|
"view_name": "Прегледай",
|
||||||
"view_next_asset": "Преглед на следващия файл",
|
"view_next_asset": "Преглед на следващия файл",
|
||||||
"view_previous_asset": "Преглед на предишния файл",
|
"view_previous_asset": "Преглед на предишния файл",
|
||||||
|
"view_qr_code": "Виж QR кода",
|
||||||
"view_stack": "Покажи в стек",
|
"view_stack": "Покажи в стек",
|
||||||
|
"view_user": "Виж потребителя",
|
||||||
|
"viewer_remove_from_stack": "Премахване от опашката",
|
||||||
|
"viewer_stack_use_as_main_asset": "Използвай като основен",
|
||||||
|
"viewer_unstack": "Премахни от опашката",
|
||||||
"visibility_changed": "Видимостта е променена за {count, plural, one {# човек} other {# човека}}",
|
"visibility_changed": "Видимостта е променена за {count, plural, one {# човек} other {# човека}}",
|
||||||
"waiting": "в изчакване",
|
"waiting": "в изчакване",
|
||||||
"warning": "Внимание",
|
"warning": "Внимание",
|
||||||
"week": "Седмица",
|
"week": "Седмица",
|
||||||
"welcome": "Добре дошли",
|
"welcome": "Добре дошли",
|
||||||
"welcome_to_immich": "Добре дошли в Immich",
|
"welcome_to_immich": "Добре дошли в Immich",
|
||||||
|
"wifi_name": "Wi-Fi мрежа",
|
||||||
|
"wrong_pin_code": "Грешен PIN код",
|
||||||
"year": "Година",
|
"year": "Година",
|
||||||
"years_ago": "преди {years, plural, one {# година} other {# години}}",
|
"years_ago": "преди {years, plural, one {# година} other {# години}}",
|
||||||
"yes": "Да",
|
"yes": "Да",
|
||||||
"you_dont_have_any_shared_links": "Нямате споделени връзки",
|
"you_dont_have_any_shared_links": "Нямате споделени връзки",
|
||||||
|
"your_wifi_name": "Вашата Wi-Fi мрежа",
|
||||||
"zoom_image": "Увеличаване на изображението"
|
"zoom_image": "Увеличаване на изображението"
|
||||||
}
|
}
|
||||||
|
|||||||
84
i18n/bn.json
84
i18n/bn.json
@@ -8,10 +8,92 @@
|
|||||||
"actions": "কর্ম",
|
"actions": "কর্ম",
|
||||||
"active": "সচল",
|
"active": "সচল",
|
||||||
"activity": "কার্যকলাপ",
|
"activity": "কার্যকলাপ",
|
||||||
|
"activity_changed": "একটিভিটি এখন {enabled, select, true {চালু} other {বন্ধ}} আছে",
|
||||||
"add": "যোগ করুন",
|
"add": "যোগ করুন",
|
||||||
"add_a_description": "একটি বিবরণ যোগ করুন",
|
"add_a_description": "একটি বিবরণ যোগ করুন",
|
||||||
"add_a_location": "একটি অবস্থান যোগ করুন",
|
"add_a_location": "একটি অবস্থান যোগ করুন",
|
||||||
"add_a_name": "একটি নাম যোগ করুন",
|
"add_a_name": "একটি নাম যোগ করুন",
|
||||||
"add_a_title": "একটি শিরোনাম যোগ করুন",
|
"add_a_title": "একটি শিরোনাম যোগ করুন",
|
||||||
"add_endpoint": "এন্ডপয়েন্ট যোগ করুন"
|
"add_endpoint": "এন্ডপয়েন্ট যোগ করুন",
|
||||||
|
"add_exclusion_pattern": "বহির্ভূতকরণ নমুনা",
|
||||||
|
"add_import_path": "ইমপোর্ট করার পাথ যুক্ত করুন",
|
||||||
|
"add_location": "অবস্থান যুক্ত করুন",
|
||||||
|
"add_more_users": "আরো ব্যবহারকারী যুক্ত করুন",
|
||||||
|
"add_partner": "অংশীদার যোগ করুন",
|
||||||
|
"add_path": "পাথ যুক্ত করুন",
|
||||||
|
"add_photos": "ছবি যুক্ত করুন",
|
||||||
|
"add_tag": "ট্যাগ যুক্ত করুন",
|
||||||
|
"add_to": "যুক্ত করুন…",
|
||||||
|
"add_to_album": "এলবাম এ যোগ করুন",
|
||||||
|
"add_to_album_bottom_sheet_added": "{album} এ যোগ করা হয়েছে",
|
||||||
|
"add_to_album_bottom_sheet_already_exists": "{album} এ আগে থেকেই আছে",
|
||||||
|
"add_to_shared_album": "শেয়ার করা অ্যালবামে যোগ করুন",
|
||||||
|
"add_url": "লিঙ্ক যোগ করুন",
|
||||||
|
"added_to_archive": "আর্কাইভ এ যোগ করা হয়েছে",
|
||||||
|
"added_to_favorites": "ফেভারিটে যোগ করা হয়েছে",
|
||||||
|
"added_to_favorites_count": "পছন্দের তালিকায় {count, number} যোগ করা হয়েছে",
|
||||||
|
"admin": {
|
||||||
|
"add_exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন যোগ করুন। *, **, এবং ? ব্যবহার করে গ্লোবিং করা সম্ভব। \"Raw\" নামের যেকোনো ডিরেক্টরিতে থাকা সমস্ত ফাইল বাদ দিতে \"**/Raw/**\" ব্যবহার করুন। \".tif\" দিয়ে শেষ হওয়া সমস্ত ফাইল বাদ দিতে \"**/*.tif\" ব্যবহার করুন। একটি সম্পূর্ণ পাথ বাদ দিতে, \"/path/to/ignore/**\" ব্যবহার করুন।",
|
||||||
|
"admin_user": "এডমিন ইউজার",
|
||||||
|
"asset_offline_description": "এই বহিরাগত লাইব্রেরি সম্পদটি আর ডিস্কে পাওয়া যাচ্ছে না এবং ট্র্যাশে সরানো হয়েছে। যদি ফাইলটি লাইব্রেরির মধ্যে সরানো হয়ে থাকে, তাহলে নতুন সংশ্লিষ্ট সম্পদের জন্য আপনার টাইমলাইন পরীক্ষা করুন। এই সম্পদটি পুনরুদ্ধার করতে, দয়া করে নিশ্চিত করুন যে নীচের ফাইল পাথটি Immich দ্বারা অ্যাক্সেস করা যেতে পারে এবং লাইব্রেরিটি স্ক্যান করুন।",
|
||||||
|
"authentication_settings": "প্রমাণীকরণ সেটিংস",
|
||||||
|
"authentication_settings_description": "পাসওয়ার্ড, OAuth এবং অন্যান্য প্রমাণীকরণ সেটিংস পরিচালনা করুন",
|
||||||
|
"authentication_settings_disable_all": "আপনি কি নিশ্চিত যে আপনি সমস্ত লগইন পদ্ধতি অক্ষম করতে চান? লগইন সম্পূর্ণরূপে অক্ষম করা হবে।",
|
||||||
|
"authentication_settings_reenable": "পুনরায় সক্ষম করতে, একটি <link>সার্ভার কমান্ড</link> ব্যবহার করুন।",
|
||||||
|
"background_task_job": "ব্যাকগ্রাউন্ড টাস্ক",
|
||||||
|
"backup_database": "ডাটাবেস ডাম্প তৈরি করুন",
|
||||||
|
"backup_database_enable_description": "ডাটাবেস ডাম্প সক্রিয় করুন",
|
||||||
|
"backup_keep_last_amount": "আগের ডাম্পের পরিমাণ রাখা হবে",
|
||||||
|
"backup_settings": "ডাটাবেস ডাম্প সেটিংস",
|
||||||
|
"backup_settings_description": "ডাটাবেস ডাম্প সেটিংস পরিচালনা করুন।",
|
||||||
|
"cleared_jobs": "{job} এর জন্য jobs খালি করা হয়েছে",
|
||||||
|
"config_set_by_file": "কনফিগ বর্তমানে একটি কনফিগ ফাইল দ্বারা সেট করা আছে",
|
||||||
|
"confirm_delete_library": "আপনি কি নিশ্চিত যে আপনি {library} লাইব্রেরি মুছে ফেলতে চান?",
|
||||||
|
"confirm_delete_library_assets": "আপনি কি নিশ্চিত যে আপনি এই লাইব্রেরিটি মুছে ফেলতে চান? এটি Immich থেকে {count, plural, one {# contained asset} other {all # contained asset}} মুছে ফেলবে এবং পূর্বাবস্থায় ফেরানো যাবে না। ফাইলগুলি ডিস্কে থাকবে।",
|
||||||
|
"confirm_email_below": "নিশ্চিত করতে, নিচে \"{email}\" টাইপ করুন",
|
||||||
|
"confirm_reprocess_all_faces": "আপনি কি নিশ্চিত যে আপনি সমস্ত মুখ পুনরায় প্রক্রিয়া করতে চান? এটি নামযুক্ত ব্যক্তিদেরও মুছে ফেলবে।",
|
||||||
|
"confirm_user_password_reset": "আপনি কি নিশ্চিত যে আপনি {user} এর পাসওয়ার্ড রিসেট করতে চান?",
|
||||||
|
"confirm_user_pin_code_reset": "আপনি কি নিশ্চিত যে আপনি {user} এর পিন কোড রিসেট করতে চান?",
|
||||||
|
"create_job": "job তৈরি করুন",
|
||||||
|
"cron_expression": "ক্রোন এক্সপ্রেশন",
|
||||||
|
"cron_expression_description": "ক্রোন ফর্ম্যাট ব্যবহার করে স্ক্যানিং ব্যবধান সেট করুন। আরও তথ্যের জন্য দয়া করে দেখুন যেমন <link>Crontab Guru</link>",
|
||||||
|
"cron_expression_presets": "ক্রোন এক্সপ্রেশন প্রিসেট",
|
||||||
|
"disable_login": "লগইন অক্ষম করুন",
|
||||||
|
"duplicate_detection_job_description": "অনুরূপ ছবি সনাক্ত করতে সম্পদগুলিতে মেশিন লার্নিং চালান। স্মার্ট অনুসন্ধানের উপর নির্ভর করে",
|
||||||
|
"exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন ব্যবহার করে আপনি আপনার লাইব্রেরি স্ক্যান করার সময় ফাইল এবং ফোল্ডারগুলিকে উপেক্ষা করতে পারবেন। যদি আপনার এমন ফোল্ডার থাকে যেখানে এমন ফাইল থাকে যা আপনি আমদানি করতে চান না, যেমন RAW ফাইল।",
|
||||||
|
"external_library_management": "বহিরাগত গ্রন্থাগার ব্যবস্থাপনা",
|
||||||
|
"face_detection": "মুখ সনাক্তকরণ",
|
||||||
|
"face_detection_description": "মেশিন লার্নিং ব্যবহার করে অ্যাসেটে থাকা মুখগুলি সনাক্ত করুন। ভিডিওগুলির জন্য, শুধুমাত্র থাম্বনেইল বিবেচনা করা হয়। \"রিফ্রেশ\" (পুনরায়) সমস্ত অ্যাসেট প্রক্রিয়া করে। \"রিসেট\" অতিরিক্তভাবে সমস্ত বর্তমান মুখের ডেটা সাফ করে। \"অনুপস্থিত\" অ্যাসেটগুলিকে সারিবদ্ধ করে যা এখনও প্রক্রিয়া করা হয়নি। সনাক্ত করা মুখগুলিকে ফেসিয়াল রিকগনিশনের জন্য সারিবদ্ধ করা হবে, ফেসিয়াল ডিটেকশন সম্পূর্ণ হওয়ার পরে, বিদ্যমান বা নতুন ব্যক্তিদের মধ্যে গোষ্ঠীবদ্ধ করে।",
|
||||||
|
"facial_recognition_job_description": "শনাক্ত করা মুখগুলিকে মানুষের মধ্যে গোষ্ঠীভুক্ত করুন। মুখ সনাক্তকরণ সম্পূর্ণ হওয়ার পরে এই ধাপটি চলে। \"রিসেট\" (পুনরায়) সমস্ত মুখকে ক্লাস্টার করে। \"অনুপস্থিত\" মুখগুলিকে সারিতে রাখে যেখানে কোনও ব্যক্তিকে বরাদ্দ করা হয়নি।",
|
||||||
|
"failed_job_command": "কমান্ড {command} কাজের জন্য ব্যর্থ হয়েছে: {job}",
|
||||||
|
"force_delete_user_warning": "সতর্কতা: এটি ব্যবহারকারী এবং সমস্ত সম্পদ অবিলম্বে সরিয়ে ফেলবে। এটি পূর্বাবস্থায় ফেরানো যাবে না এবং ফাইলগুলি পুনরুদ্ধার করা যাবে না।",
|
||||||
|
"image_format": "ফরম্যাট",
|
||||||
|
"image_format_description": "WebP JPEG এর তুলনায় ছোট ফাইল তৈরি করে, কিন্তু এনকোড করতে ধীর।",
|
||||||
|
"image_fullsize_description": "জুম ইন করার সময় ব্যবহৃত স্ট্রিপড মেটাডেটা সহ পূর্ণ আকারের ছবি",
|
||||||
|
"image_fullsize_enabled": "পূর্ণ-আকারের ছবি তৈরি সক্ষম করুন",
|
||||||
|
"image_fullsize_enabled_description": "ওয়েব-বান্ধব নয় এমন ফর্ম্যাটের জন্য পূর্ণ-আকারের ছবি তৈরি করুন। \"এমবেডেড প্রিভিউ পছন্দ করুন\" সক্ষম করা থাকলে, রূপান্তর ছাড়াই এমবেডেড প্রিভিউ সরাসরি ব্যবহার করা হয়। JPEG-এর মতো ওয়েব-বান্ধব ফর্ম্যাটগুলিকে প্রভাবিত করে না।",
|
||||||
|
"image_fullsize_quality_description": "পূর্ণ-আকারের ছবির মান ১-১০০। উচ্চতর হলে ভালো, কিন্তু আরও বড় ফাইল তৈরি হয়।",
|
||||||
|
"image_fullsize_title": "পূর্ণ-আকারের চিত্র সেটিংস",
|
||||||
|
"image_prefer_embedded_preview": "এম্বেড করা প্রিভিউ পছন্দ করুন",
|
||||||
|
"image_prefer_embedded_preview_setting_description": "ছবি প্রক্রিয়াকরণের জন্য এবং যখনই উপলব্ধ থাকবে তখন RAW ফটোতে এমবেডেড প্রিভিউ ব্যবহার করুন। এটি কিছু ছবির জন্য আরও সঠিক রঙ তৈরি করতে পারে, তবে প্রিভিউয়ের মান ক্যামেরা-নির্ভর এবং ছবিতে আরও কম্প্রেশন আর্টিফ্যাক্ট থাকতে পারে।",
|
||||||
|
"image_prefer_wide_gamut": "প্রশস্ত পরিসর পছন্দ করুন",
|
||||||
|
"image_prefer_wide_gamut_setting_description": "থাম্বনেইলের জন্য ডিসপ্লে P3 ব্যবহার করুন। এটি প্রশস্ত রঙের স্থান সহ ছবির প্রাণবন্ততা আরও ভালভাবে সংরক্ষণ করে, তবে পুরানো ব্রাউজার সংস্করণ সহ পুরানো ডিভাইসগুলিতে ছবিগুলি ভিন্নভাবে প্রদর্শিত হতে পারে। রঙের পরিবর্তন এড়াতে sRGB ছবিগুলিকে sRGB হিসাবে রাখা হয়।",
|
||||||
|
"image_preview_description": "স্ট্রিপড মেটাডেটা সহ মাঝারি আকারের ছবি, একটি একক সম্পদ দেখার সময় এবং মেশিন লার্নিংয়ের জন্য ব্যবহৃত হয়",
|
||||||
|
"image_preview_quality_description": "১-১০০ এর মধ্যে প্রিভিউ কোয়ালিটি। বেশি হলে ভালো, কিন্তু বড় ফাইল তৈরি হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে। কম মান সেট করলে মেশিন লার্নিং কোয়ালিটির উপর প্রভাব পড়তে পারে।",
|
||||||
|
"image_preview_title": "প্রিভিউ সেটিংস",
|
||||||
|
"image_quality": "গুণমান",
|
||||||
|
"image_resolution": "রেজোলিউশন",
|
||||||
|
"image_resolution_description": "উচ্চ রেজোলিউশনের ক্ষেত্রে আরও বিস্তারিত তথ্য সংরক্ষণ করা সম্ভব কিন্তু এনকোড করতে বেশি সময় লাগে, ফাইলের আকার বড় হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে।",
|
||||||
|
"image_settings": "চিত্র সেটিংস",
|
||||||
|
"image_settings_description": "তৈরি করা ছবির মান এবং রেজোলিউশন পরিচালনা করুন",
|
||||||
|
"image_thumbnail_description": "মেটাডেটা বাদ দেওয়া ছোট থাম্বনেইল, মূল টাইমলাইনের মতো ছবির গ্রুপ দেখার সময় ব্যবহৃত হয়",
|
||||||
|
"image_thumbnail_quality_description": "থাম্বনেইলের মান ১-১০০। বেশি হলে ভালো, কিন্তু বড় ফাইল তৈরি হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে।",
|
||||||
|
"image_thumbnail_title": "থাম্বনেল সেটিংস",
|
||||||
|
"job_concurrency": "{job} কনকারেন্সি",
|
||||||
|
"job_created": "Job তৈরি হয়েছে",
|
||||||
|
"job_not_concurrency_safe": "এই কাজটি সমকালীন-নিরাপদ নয়।",
|
||||||
|
"job_settings": "কাজের সেটিংস",
|
||||||
|
"job_settings_description": "কাজের সমান্তরালতা পরিচালনা করুন",
|
||||||
|
"job_status": "চাকরির অবস্থা"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
63
i18n/ca.json
63
i18n/ca.json
@@ -34,6 +34,7 @@
|
|||||||
"added_to_favorites_count": "{count, number} afegits als preferits",
|
"added_to_favorites_count": "{count, number} afegits als preferits",
|
||||||
"admin": {
|
"admin": {
|
||||||
"add_exclusion_pattern_description": "Afegeix patrons d'exclusió. Es permet englobar fent ús de *, **, i ?. Per a ignorar els fitxers de qualsevol directori anomenat \"Raw\" introduïu \"**/Raw/**\". Per a ignorar els fitxers acabats en \".tif\" introduïu \"**/*.tif\". Per a ignorar una ruta absoluta, utilitzeu \"/ruta/a/ignorar/**\".",
|
"add_exclusion_pattern_description": "Afegeix patrons d'exclusió. Es permet englobar fent ús de *, **, i ?. Per a ignorar els fitxers de qualsevol directori anomenat \"Raw\" introduïu \"**/Raw/**\". Per a ignorar els fitxers acabats en \".tif\" introduïu \"**/*.tif\". Per a ignorar una ruta absoluta, utilitzeu \"/ruta/a/ignorar/**\".",
|
||||||
|
"admin_user": "Administrador",
|
||||||
"asset_offline_description": "Aquest recurs de la biblioteca externa ja no es troba al disc i s'ha mogut a la paperera. Si el fitxer s'ha mogut dins de la biblioteca, comproveu la vostra línia de temps per trobar el nou recurs corresponent. Per restaurar aquest recurs, assegureu-vos que Immich pugui accedir a la ruta del fitxer següent i escanegeu la biblioteca.",
|
"asset_offline_description": "Aquest recurs de la biblioteca externa ja no es troba al disc i s'ha mogut a la paperera. Si el fitxer s'ha mogut dins de la biblioteca, comproveu la vostra línia de temps per trobar el nou recurs corresponent. Per restaurar aquest recurs, assegureu-vos que Immich pugui accedir a la ruta del fitxer següent i escanegeu la biblioteca.",
|
||||||
"authentication_settings": "Configuració de l'autenticació",
|
"authentication_settings": "Configuració de l'autenticació",
|
||||||
"authentication_settings_description": "Gestiona la contrasenya, OAuth i altres configuracions de l'autenticació",
|
"authentication_settings_description": "Gestiona la contrasenya, OAuth i altres configuracions de l'autenticació",
|
||||||
@@ -58,7 +59,7 @@
|
|||||||
"cron_expression_description": "Estableix l'interval d'escaneig amb el format cron. Per obtenir més informació, consulteu, p.e <link>Crontab Guru</link>",
|
"cron_expression_description": "Estableix l'interval d'escaneig amb el format cron. Per obtenir més informació, consulteu, p.e <link>Crontab Guru</link>",
|
||||||
"cron_expression_presets": "Ajustos predefinits d'expressions Cron",
|
"cron_expression_presets": "Ajustos predefinits d'expressions Cron",
|
||||||
"disable_login": "Deshabiliteu l'inici de sessió",
|
"disable_login": "Deshabiliteu l'inici de sessió",
|
||||||
"duplicate_detection_job_description": "Executa l'aprenentatge automàtic en els elements per a detectar imatges semblants. Fa servir l'Smart Search",
|
"duplicate_detection_job_description": "Executa l'aprenentatge automàtic en els elements per a detectar imatges semblants. Fa servir la cerca intel·ligent",
|
||||||
"exclusion_pattern_description": "Els patrons d'exclusió permeten ignorar fitxers i carpetes quan escanegeu una llibreria. Això és útil si teniu carpetes que contenen fitxer que no voleu importar, com els fitxers RAW.",
|
"exclusion_pattern_description": "Els patrons d'exclusió permeten ignorar fitxers i carpetes quan escanegeu una llibreria. Això és útil si teniu carpetes que contenen fitxer que no voleu importar, com els fitxers RAW.",
|
||||||
"external_library_management": "Gestió de llibreries externes",
|
"external_library_management": "Gestió de llibreries externes",
|
||||||
"face_detection": "Detecció de cares",
|
"face_detection": "Detecció de cares",
|
||||||
@@ -88,7 +89,7 @@
|
|||||||
"image_thumbnail_description": "Miniatura petita amb metadades eliminades, que s'utilitza quan es visualitzen grups de fotos com la línia de temps principal",
|
"image_thumbnail_description": "Miniatura petita amb metadades eliminades, que s'utilitza quan es visualitzen grups de fotos com la línia de temps principal",
|
||||||
"image_thumbnail_quality_description": "Qualitat de miniatura d'1 a 100. Més alt és millor, però produeix fitxers més grans i pot reduir la capacitat de resposta de l'aplicació.",
|
"image_thumbnail_quality_description": "Qualitat de miniatura d'1 a 100. Més alt és millor, però produeix fitxers més grans i pot reduir la capacitat de resposta de l'aplicació.",
|
||||||
"image_thumbnail_title": "Configuració de miniatures",
|
"image_thumbnail_title": "Configuració de miniatures",
|
||||||
"job_concurrency": "{job} concurrència",
|
"job_concurrency": "{job} simultàniament",
|
||||||
"job_created": "Tasca creada",
|
"job_created": "Tasca creada",
|
||||||
"job_not_concurrency_safe": "Aquesta tasca no és segura per a la conconcurrència.",
|
"job_not_concurrency_safe": "Aquesta tasca no és segura per a la conconcurrència.",
|
||||||
"job_settings": "Configuració de les tasques",
|
"job_settings": "Configuració de les tasques",
|
||||||
@@ -104,7 +105,7 @@
|
|||||||
"library_scanning_enable_description": "Habilita l'escaneig periòdic de biblioteques",
|
"library_scanning_enable_description": "Habilita l'escaneig periòdic de biblioteques",
|
||||||
"library_settings": "Llibreria externes",
|
"library_settings": "Llibreria externes",
|
||||||
"library_settings_description": "Gestiona la configuració de les llibreries externes",
|
"library_settings_description": "Gestiona la configuració de les llibreries externes",
|
||||||
"library_tasks_description": "Escaneja biblioteques externes per arxius nous o canviats",
|
"library_tasks_description": "Escaneja les biblioteques externes per trobar arxius nous o canviats",
|
||||||
"library_watching_enable_description": "Consultar llibreries externes per detectar canvis en fitxers",
|
"library_watching_enable_description": "Consultar llibreries externes per detectar canvis en fitxers",
|
||||||
"library_watching_settings": "Monitoratge de la llibreria (EXPERIMENTAL)",
|
"library_watching_settings": "Monitoratge de la llibreria (EXPERIMENTAL)",
|
||||||
"library_watching_settings_description": "Monitorització automàtica de fitxers modificats",
|
"library_watching_settings_description": "Monitorització automàtica de fitxers modificats",
|
||||||
@@ -112,19 +113,19 @@
|
|||||||
"logging_level_description": "Quan està habilitat, quin nivell de registre es vol emprar.",
|
"logging_level_description": "Quan està habilitat, quin nivell de registre es vol emprar.",
|
||||||
"logging_settings": "Registre",
|
"logging_settings": "Registre",
|
||||||
"machine_learning_clip_model": "Model CLIP",
|
"machine_learning_clip_model": "Model CLIP",
|
||||||
"machine_learning_clip_model_description": "El nom d'un model CLIP que apareix a <link>aquí</link>. Tingues en compte que has de tornar a executar l'Smart Search' per a totes les imatges quan es canvia de model.",
|
"machine_learning_clip_model_description": "El nom d'un model CLIP que apareix a <link>aquí</link>. Tingues en compte que has de tornar a executar la cerca intel·ligent per a totes les imatges quan es canvia de model.",
|
||||||
"machine_learning_duplicate_detection": "Detecció de duplicats",
|
"machine_learning_duplicate_detection": "Detecció de duplicats",
|
||||||
"machine_learning_duplicate_detection_enabled": "Activa detecció de duplicats",
|
"machine_learning_duplicate_detection_enabled": "Activa la detecció de duplicats",
|
||||||
"machine_learning_duplicate_detection_enabled_description": "Si es deshabilitat, els elements exactament idèntics encara es desduplicaran.",
|
"machine_learning_duplicate_detection_enabled_description": "Si està desactivada, els elements idèntics encara es desduplicaran.",
|
||||||
"machine_learning_duplicate_detection_setting_description": "Usa incrustacions CLIP per a trobar prossibles duplicats",
|
"machine_learning_duplicate_detection_setting_description": "Usa incrustacions CLIP per a trobar prossibles duplicats",
|
||||||
"machine_learning_enabled": "Activa l'aprenentatge automàtic",
|
"machine_learning_enabled": "Activa l'aprenentatge automàtic",
|
||||||
"machine_learning_enabled_description": "Si està deshabilitat, totes les funcions ML es deshabilitaran sense tenir en compte la configuració següent.",
|
"machine_learning_enabled_description": "Si està desactivat, totes les funcions ML es deshabilitaran sense tenir en compte la configuració següent.",
|
||||||
"machine_learning_facial_recognition": "Reconeixement facial",
|
"machine_learning_facial_recognition": "Reconeixement facial",
|
||||||
"machine_learning_facial_recognition_description": "Detecta, reconeix i agrupa cares a les imatges",
|
"machine_learning_facial_recognition_description": "Detecta, reconeix i agrupa cares a les imatges",
|
||||||
"machine_learning_facial_recognition_model": "Model de reconeixement facial",
|
"machine_learning_facial_recognition_model": "Model de reconeixement facial",
|
||||||
"machine_learning_facial_recognition_model_description": "Els models es llisten en ordre descent segons la mida. Els models més grans són més lents i usen més memòria però produeixen millors resultats. Tingueu en compte que després de canviar un model haureu de tornar a executar la tasca de detecció de cares per a totes les imatges.",
|
"machine_learning_facial_recognition_model_description": "Els models es llisten en ordre descent segons la mida. Els models més grans són més lents i usen més memòria però produeixen millors resultats. Tingueu en compte que després de canviar un model haureu de tornar a executar la tasca de detecció de cares per a totes les imatges.",
|
||||||
"machine_learning_facial_recognition_setting": "Activa reconeixement facial",
|
"machine_learning_facial_recognition_setting": "Activa el reconeixement facial",
|
||||||
"machine_learning_facial_recognition_setting_description": "Si està deshabilitat, les imatges no es codificaran pel reconeixement facial i no s'afegiran a la secció Persones de la pàgina Explorar.",
|
"machine_learning_facial_recognition_setting_description": "Si està desactivat, les imatges no es codificaran pel reconeixement facial i no s'afegiran a la secció Persones de la pàgina Explorar.",
|
||||||
"machine_learning_max_detection_distance": "Distància màxima de detecció",
|
"machine_learning_max_detection_distance": "Distància màxima de detecció",
|
||||||
"machine_learning_max_detection_distance_description": "Diferència màxima entre dues imatges per a considerar-les duplicades, en un rang d'entre 0.001-0.1. Com més elevat el valor més detecció de duplicats, però pot resultar en falsos positius.",
|
"machine_learning_max_detection_distance_description": "Diferència màxima entre dues imatges per a considerar-les duplicades, en un rang d'entre 0.001-0.1. Com més elevat el valor més detecció de duplicats, però pot resultar en falsos positius.",
|
||||||
"machine_learning_max_recognition_distance": "Màxima diferència de reconeixement",
|
"machine_learning_max_recognition_distance": "Màxima diferència de reconeixement",
|
||||||
@@ -135,17 +136,17 @@
|
|||||||
"machine_learning_min_recognized_faces_description": "El nombre mínim de cares reconegudes per crear una persona. Augmentar aquest valor fa que el reconeixement facial sigui més precís, però augmenta la possibilitat que una cara no sigui assignada a una persona.",
|
"machine_learning_min_recognized_faces_description": "El nombre mínim de cares reconegudes per crear una persona. Augmentar aquest valor fa que el reconeixement facial sigui més precís, però augmenta la possibilitat que una cara no sigui assignada a una persona.",
|
||||||
"machine_learning_settings": "Configuració d'aprenentatge automàtic",
|
"machine_learning_settings": "Configuració d'aprenentatge automàtic",
|
||||||
"machine_learning_settings_description": "Gestiona funcions i configuració d'aprenentatge automàtic",
|
"machine_learning_settings_description": "Gestiona funcions i configuració d'aprenentatge automàtic",
|
||||||
"machine_learning_smart_search": "Cerca Intel·ligent",
|
"machine_learning_smart_search": "Cerca intel·ligent",
|
||||||
"machine_learning_smart_search_description": "Cerca imatges semànticament emprant incrustacions CLIP",
|
"machine_learning_smart_search_description": "Cerca imatges semànticament emprant incrustacions CLIP",
|
||||||
"machine_learning_smart_search_enabled": "Activa la cerca intel·ligent",
|
"machine_learning_smart_search_enabled": "Activa la cerca intel·ligent",
|
||||||
"machine_learning_smart_search_enabled_description": "Si està deshabilitat les imatges no es codificaran per la cerca intel·ligent.",
|
"machine_learning_smart_search_enabled_description": "Si està desactivada, les imatges no es codificaran per la cerca intel·ligent.",
|
||||||
"machine_learning_url_description": "L'URL del servidor d'aprenentatge automàtic. Si es proporciona més d'un URL, s'intentarà accedir a cada servidor en ordre fins que un d'ells respongui correctament.",
|
"machine_learning_url_description": "L'URL del servidor d'aprenentatge automàtic. Si es proporciona més d'un URL, s'intentarà accedir a cada servidor en ordre fins que un d'ells respongui correctament.",
|
||||||
"manage_concurrency": "Gestiona la concurrència",
|
"manage_concurrency": "Gestiona la concurrència",
|
||||||
"manage_log_settings": "Gestiona la configuració del registre",
|
"manage_log_settings": "Gestiona la configuració del registre",
|
||||||
"map_dark_style": "Tema fosc",
|
"map_dark_style": "Tema fosc",
|
||||||
"map_enable_description": "Habilita característiques del mapa",
|
"map_enable_description": "Habilita característiques del mapa",
|
||||||
"map_gps_settings": "Configuració de mapa i GPS",
|
"map_gps_settings": "Configuració del mapa i GPS",
|
||||||
"map_gps_settings_description": "Gestiona la configuració de mapa i GPS (Geocodificació inversa)",
|
"map_gps_settings_description": "Gestiona la configuració del mapa i GPS (Geocodificació inversa)",
|
||||||
"map_implications": "La funció mapa depèn del servei extern de tesel·les (tiles.immich.cloud)",
|
"map_implications": "La funció mapa depèn del servei extern de tesel·les (tiles.immich.cloud)",
|
||||||
"map_light_style": "Tema clar",
|
"map_light_style": "Tema clar",
|
||||||
"map_manage_reverse_geocoding_settings": "Gestiona els paràmetres de <link>geocodificació inversa</link>",
|
"map_manage_reverse_geocoding_settings": "Gestiona els paràmetres de <link>geocodificació inversa</link>",
|
||||||
@@ -165,6 +166,10 @@
|
|||||||
"metadata_settings_description": "Administrar la configuració de les metadades",
|
"metadata_settings_description": "Administrar la configuració de les metadades",
|
||||||
"migration_job": "Migració",
|
"migration_job": "Migració",
|
||||||
"migration_job_description": "Migra les miniatures d'elements i cares cap a la nova estructura de carpetes",
|
"migration_job_description": "Migra les miniatures d'elements i cares cap a la nova estructura de carpetes",
|
||||||
|
"nightly_tasks_cluster_new_faces_setting": "Agrupa cares noves",
|
||||||
|
"nightly_tasks_database_cleanup_setting": "Tasques de neteja de la base de dades",
|
||||||
|
"nightly_tasks_database_cleanup_setting_description": "Netegeu les dades antigues i caducades de la base de dades",
|
||||||
|
"nightly_tasks_missing_thumbnails_setting": "Generar les miniatures restants",
|
||||||
"no_paths_added": "No s'ha afegit cap ruta",
|
"no_paths_added": "No s'ha afegit cap ruta",
|
||||||
"no_pattern_added": "Cap patró aplicat",
|
"no_pattern_added": "Cap patró aplicat",
|
||||||
"note_apply_storage_label_previous_assets": "Nota: Per aplicar l'etiquetatge d'emmagatzematge a elements pujats prèviament, executeu la",
|
"note_apply_storage_label_previous_assets": "Nota: Per aplicar l'etiquetatge d'emmagatzematge a elements pujats prèviament, executeu la",
|
||||||
@@ -195,6 +200,8 @@
|
|||||||
"oauth_mobile_redirect_uri": "URI de redirecció mòbil",
|
"oauth_mobile_redirect_uri": "URI de redirecció mòbil",
|
||||||
"oauth_mobile_redirect_uri_override": "Sobreescriu l'URI de redirecció mòbil",
|
"oauth_mobile_redirect_uri_override": "Sobreescriu l'URI de redirecció mòbil",
|
||||||
"oauth_mobile_redirect_uri_override_description": "Habilita quan el proveïdor d'OAuth no permet una URI mòbil, com ara ''{callback}''",
|
"oauth_mobile_redirect_uri_override_description": "Habilita quan el proveïdor d'OAuth no permet una URI mòbil, com ara ''{callback}''",
|
||||||
|
"oauth_role_claim": "Concessió de rol",
|
||||||
|
"oauth_role_claim_description": "Atorgar accés d'administrador automàticament segons la presència d'aquesta concessió. La concessió pot ser 'usuari' o 'admin'.",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "Gestiona la configuració de l'inici de sessió OAuth",
|
"oauth_settings_description": "Gestiona la configuració de l'inici de sessió OAuth",
|
||||||
"oauth_settings_more_details": "Per a més detalls sobre aquesta funcionalitat, consulteu la <link>documentació</link>.",
|
"oauth_settings_more_details": "Per a més detalls sobre aquesta funcionalitat, consulteu la <link>documentació</link>.",
|
||||||
@@ -243,7 +250,7 @@
|
|||||||
"storage_template_migration_info": "Les extensions es convertiran a minúscules. Els canvis de plantilla només s'aplicaran a nous elements. Per aplicar la plantilla rectroactivament a elements pujats prèviament, executeu la <link>{job}</link>.",
|
"storage_template_migration_info": "Les extensions es convertiran a minúscules. Els canvis de plantilla només s'aplicaran a nous elements. Per aplicar la plantilla rectroactivament a elements pujats prèviament, executeu la <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "Tasca de migració de la plantilla d'emmagatzematge",
|
"storage_template_migration_job": "Tasca de migració de la plantilla d'emmagatzematge",
|
||||||
"storage_template_more_details": "Per obtenir més detalls sobre aquesta funció, consulteu la <template-link>Storage Template</template-link> i les seves <implications-link>implications</implications-link>",
|
"storage_template_more_details": "Per obtenir més detalls sobre aquesta funció, consulteu la <template-link>Storage Template</template-link> i les seves <implications-link>implications</implications-link>",
|
||||||
"storage_template_onboarding_description": "Quan està activada, aquesta funció organitzarà automàticament els fitxers en funció d'una plantilla definida per l'usuari. A causa de problemes d'estabilitat, la funció s'ha desactivat de manera predeterminada. Per obtenir més informació, consulteu la <link>documentation</link>.",
|
"storage_template_onboarding_description_v2": "Un cop habilitada, aquesta funció organitzarà automàticament els fitxers a partir d'una plantilla definida per l'usuari. Per a més informació, podeu consultar la <link>documentació</link>.",
|
||||||
"storage_template_path_length": "Límit aproximat de longitud de la ruta: <b>{length, number}</b>/{limit, number}",
|
"storage_template_path_length": "Límit aproximat de longitud de la ruta: <b>{length, number}</b>/{limit, number}",
|
||||||
"storage_template_settings": "Plantilla d'emmagatzematge",
|
"storage_template_settings": "Plantilla d'emmagatzematge",
|
||||||
"storage_template_settings_description": "Gestiona l'estructura de les carpetes i el nom del fitxers dels elements pujats",
|
"storage_template_settings_description": "Gestiona l'estructura de les carpetes i el nom del fitxers dels elements pujats",
|
||||||
@@ -260,7 +267,7 @@
|
|||||||
"template_settings": "Plantilles de notificació",
|
"template_settings": "Plantilles de notificació",
|
||||||
"template_settings_description": "Gestiona les plantilles personalitzades per les notificacions",
|
"template_settings_description": "Gestiona les plantilles personalitzades per les notificacions",
|
||||||
"theme_custom_css_settings": "CSS personalitzat",
|
"theme_custom_css_settings": "CSS personalitzat",
|
||||||
"theme_custom_css_settings_description": "Els Fulls d'Estil en Cascada permeten personalitzar el disseny d'Immich.",
|
"theme_custom_css_settings_description": "Els fulls d'estil en cascada permeten personalitzar el disseny d'Immich.",
|
||||||
"theme_settings": "Configuració del tema",
|
"theme_settings": "Configuració del tema",
|
||||||
"theme_settings_description": "Gestiona la personalització de la interfície web Immich",
|
"theme_settings_description": "Gestiona la personalització de la interfície web Immich",
|
||||||
"thumbnail_generation_job": "Generar miniatures",
|
"thumbnail_generation_job": "Generar miniatures",
|
||||||
@@ -354,12 +361,12 @@
|
|||||||
},
|
},
|
||||||
"admin_email": "Correu de l'administrador",
|
"admin_email": "Correu de l'administrador",
|
||||||
"admin_password": "Contrasenya de l'administrador",
|
"admin_password": "Contrasenya de l'administrador",
|
||||||
"administration": "Administrador",
|
"administration": "Administració",
|
||||||
"advanced": "Avançat",
|
"advanced": "Avançat",
|
||||||
"advanced_settings_enable_alternate_media_filter_subtitle": "Feu servir aquesta opció per filtrar els continguts multimèdia durant la sincronització segons criteris alternatius. Només proveu-ho si teniu problemes amb l'aplicació per detectar tots els àlbums.",
|
"advanced_settings_enable_alternate_media_filter_subtitle": "Feu servir aquesta opció per filtrar els continguts multimèdia durant la sincronització segons criteris alternatius. Només proveu-ho si teniu problemes amb l'aplicació per detectar tots els àlbums.",
|
||||||
"advanced_settings_enable_alternate_media_filter_title": "Utilitza el filtre de sincronització d'àlbums de dispositius alternatius",
|
"advanced_settings_enable_alternate_media_filter_title": "Utilitza el filtre de sincronització d'àlbums de dispositius alternatius",
|
||||||
"advanced_settings_log_level_title": "Nivell de registre: {level}",
|
"advanced_settings_log_level_title": "Nivell de registre: {level}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Alguns dispositius són molt lents en carregar miniatures dels elements del dispositiu. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.",
|
"advanced_settings_prefer_remote_subtitle": "Alguns dispositius són molt lents en carregar miniatures dels elements locals. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.",
|
||||||
"advanced_settings_prefer_remote_title": "Prefereix imatges remotes",
|
"advanced_settings_prefer_remote_title": "Prefereix imatges remotes",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Definiu les capçaleres de proxy que Immich per enviar amb cada sol·licitud de xarxa",
|
"advanced_settings_proxy_headers_subtitle": "Definiu les capçaleres de proxy que Immich per enviar amb cada sol·licitud de xarxa",
|
||||||
"advanced_settings_proxy_headers_title": "Capçaleres de proxy",
|
"advanced_settings_proxy_headers_title": "Capçaleres de proxy",
|
||||||
@@ -426,6 +433,7 @@
|
|||||||
"app_settings": "Configuració de l'app",
|
"app_settings": "Configuració de l'app",
|
||||||
"appears_in": "Apareix a",
|
"appears_in": "Apareix a",
|
||||||
"archive": "Arxiu",
|
"archive": "Arxiu",
|
||||||
|
"archive_action_prompt": "{count} afegit a Arxiu",
|
||||||
"archive_or_unarchive_photo": "Arxivar o desarxivar fotografia",
|
"archive_or_unarchive_photo": "Arxivar o desarxivar fotografia",
|
||||||
"archive_page_no_archived_assets": "No s'ha trobat res arxivat",
|
"archive_page_no_archived_assets": "No s'ha trobat res arxivat",
|
||||||
"archive_page_title": "Arxiu({count})",
|
"archive_page_title": "Arxiu({count})",
|
||||||
@@ -463,11 +471,12 @@
|
|||||||
"assets": "Elements",
|
"assets": "Elements",
|
||||||
"assets_added_count": "{count, plural, one {Afegit un element} other {Afegits # elements}}",
|
"assets_added_count": "{count, plural, one {Afegit un element} other {Afegits # elements}}",
|
||||||
"assets_added_to_album_count": "{count, plural, one {Afegit un element} other {Afegits # elements}} a l'àlbum",
|
"assets_added_to_album_count": "{count, plural, one {Afegit un element} other {Afegits # elements}} a l'àlbum",
|
||||||
"assets_added_to_name_count": "{count, plural, one {S'ha afegit # recurs} other {S'han afegit # recursos}} a {hasName, select, true {<b>{name}</b>} other {new album}}",
|
|
||||||
"assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no es pot afegir a l'àlbum",
|
"assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no es pot afegir a l'àlbum",
|
||||||
"assets_count": "{count, plural, one {# recurs} other {# recursos}}",
|
"assets_count": "{count, plural, one {# recurs} other {# recursos}}",
|
||||||
"assets_deleted_permanently": "{count} element(s) esborrats permanentment",
|
"assets_deleted_permanently": "{count} element(s) esborrats permanentment",
|
||||||
"assets_deleted_permanently_from_server": "{count} element(s) esborrats permanentment del servidor d'Immich",
|
"assets_deleted_permanently_from_server": "{count} element(s) esborrats permanentment del servidor d'Immich",
|
||||||
|
"assets_downloaded_failed": "{count, plural, one {S'ha baixat un arxiu - {error} l'arxiu ha fallat} other {S'han baixat # arxius - {error} els arxius han fallat}}",
|
||||||
|
"assets_downloaded_successfully": "{count, plural, one {S'ha baixat un arxiu amb èxit} other {S'han baixat # arxius amb èxit}}",
|
||||||
"assets_moved_to_trash_count": "{count, plural, one {# recurs mogut} other {# recursos moguts}} a la paperera",
|
"assets_moved_to_trash_count": "{count, plural, one {# recurs mogut} other {# recursos moguts}} a la paperera",
|
||||||
"assets_permanently_deleted_count": "{count, plural, one {# recurs esborrat} other {# recursos esborrats}} permanentment",
|
"assets_permanently_deleted_count": "{count, plural, one {# recurs esborrat} other {# recursos esborrats}} permanentment",
|
||||||
"assets_removed_count": "{count, plural, one {# element eliminat} other {# elements eliminats}}",
|
"assets_removed_count": "{count, plural, one {# element eliminat} other {# elements eliminats}}",
|
||||||
@@ -551,7 +560,7 @@
|
|||||||
"backup_setting_subtitle": "Gestiona la configuració de càrrega en segon pla i en primer pla",
|
"backup_setting_subtitle": "Gestiona la configuració de càrrega en segon pla i en primer pla",
|
||||||
"backward": "Enrere",
|
"backward": "Enrere",
|
||||||
"biometric_auth_enabled": "Autentificació biomètrica activada",
|
"biometric_auth_enabled": "Autentificació biomètrica activada",
|
||||||
"biometric_locked_out": "Esteu bloquejat fora de l'autenticació biomètrica",
|
"biometric_locked_out": "Esteu bloquejats fora de l'autenticació biomètrica",
|
||||||
"biometric_no_options": "No hi ha opcions biomètriques disponibles",
|
"biometric_no_options": "No hi ha opcions biomètriques disponibles",
|
||||||
"biometric_not_available": "L'autenticació biomètrica no està disponible en aquest dispositiu",
|
"biometric_not_available": "L'autenticació biomètrica no està disponible en aquest dispositiu",
|
||||||
"birthdate_saved": "Data de naixement guardada amb èxit",
|
"birthdate_saved": "Data de naixement guardada amb èxit",
|
||||||
@@ -700,7 +709,7 @@
|
|||||||
"daily_title_text_date": "E, dd MMM",
|
"daily_title_text_date": "E, dd MMM",
|
||||||
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
||||||
"dark": "Fosc",
|
"dark": "Fosc",
|
||||||
"darkTheme": "Activa/desactiva el tema fosc",
|
"dark_theme": "Canviar a tema fosc",
|
||||||
"date_after": "Data posterior a",
|
"date_after": "Data posterior a",
|
||||||
"date_and_time": "Data i hora",
|
"date_and_time": "Data i hora",
|
||||||
"date_before": "Data anterior a",
|
"date_before": "Data anterior a",
|
||||||
@@ -716,6 +725,7 @@
|
|||||||
"default_locale": "Localització predeterminada",
|
"default_locale": "Localització predeterminada",
|
||||||
"default_locale_description": "Format de dates i números segons la configuració del navegador",
|
"default_locale_description": "Format de dates i números segons la configuració del navegador",
|
||||||
"delete": "Esborra",
|
"delete": "Esborra",
|
||||||
|
"delete_action_prompt": "{count} eliminats permanentment",
|
||||||
"delete_album": "Esborra l'àlbum",
|
"delete_album": "Esborra l'àlbum",
|
||||||
"delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?",
|
"delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?",
|
||||||
"delete_dialog_alert": "Aquests elements seran eliminats de manera permanent d'Immich i del vostre dispositiu",
|
"delete_dialog_alert": "Aquests elements seran eliminats de manera permanent d'Immich i del vostre dispositiu",
|
||||||
@@ -796,6 +806,7 @@
|
|||||||
"edit_key": "Edita clau",
|
"edit_key": "Edita clau",
|
||||||
"edit_link": "Edita enllaç",
|
"edit_link": "Edita enllaç",
|
||||||
"edit_location": "Edita ubicació",
|
"edit_location": "Edita ubicació",
|
||||||
|
"edit_location_action_prompt": "{count} ubicacions editades",
|
||||||
"edit_location_dialog_title": "Ubicació",
|
"edit_location_dialog_title": "Ubicació",
|
||||||
"edit_name": "Edita el nom",
|
"edit_name": "Edita el nom",
|
||||||
"edit_people": "Edita la gent",
|
"edit_people": "Edita la gent",
|
||||||
@@ -981,6 +992,7 @@
|
|||||||
"failed_to_load_assets": "Error carregant recursos",
|
"failed_to_load_assets": "Error carregant recursos",
|
||||||
"failed_to_load_folder": "No s'ha pogut carregar la carpeta",
|
"failed_to_load_folder": "No s'ha pogut carregar la carpeta",
|
||||||
"favorite": "Preferit",
|
"favorite": "Preferit",
|
||||||
|
"favorite_action_prompt": "{count} afegit a Favorits",
|
||||||
"favorite_or_unfavorite_photo": "Foto preferida o no preferida",
|
"favorite_or_unfavorite_photo": "Foto preferida o no preferida",
|
||||||
"favorites": "Preferits",
|
"favorites": "Preferits",
|
||||||
"favorites_page_no_favorites": "No s'han trobat preferits",
|
"favorites_page_no_favorites": "No s'han trobat preferits",
|
||||||
@@ -1095,6 +1107,7 @@
|
|||||||
"ios_debug_info_last_sync_at": "Darrera sincronització {dateTime}",
|
"ios_debug_info_last_sync_at": "Darrera sincronització {dateTime}",
|
||||||
"ios_debug_info_no_processes_queued": "No hi ha processos en segon pla en cua",
|
"ios_debug_info_no_processes_queued": "No hi ha processos en segon pla en cua",
|
||||||
"ios_debug_info_no_sync_yet": "Encara no s'ha executat cap tasca de sincronització en segon pla",
|
"ios_debug_info_no_sync_yet": "Encara no s'ha executat cap tasca de sincronització en segon pla",
|
||||||
|
"ios_debug_info_processes_queued": "{count, plural, one {Un procés en segon pla a la cua} other {{count} processos en segon pla a la cua}}",
|
||||||
"ios_debug_info_processing_ran_at": "El processament s'ha executat {dateTime}",
|
"ios_debug_info_processing_ran_at": "El processament s'ha executat {dateTime}",
|
||||||
"items_count": "{count, plural, one {# element} other {# elements}}",
|
"items_count": "{count, plural, one {# element} other {# elements}}",
|
||||||
"jobs": "Tasques",
|
"jobs": "Tasques",
|
||||||
@@ -1132,7 +1145,7 @@
|
|||||||
"list": "Llista",
|
"list": "Llista",
|
||||||
"loading": "Carregant",
|
"loading": "Carregant",
|
||||||
"loading_search_results_failed": "No s'han pogut carregar els resultats de la cerca",
|
"loading_search_results_failed": "No s'han pogut carregar els resultats de la cerca",
|
||||||
"local_asset_cast_failed": "No es pot convertir un actiu que no s'ha penjat al servidor.",
|
"local_asset_cast_failed": "No es pot convertir un actiu que no s'ha penjat al servidor",
|
||||||
"local_network": "Xarxa local",
|
"local_network": "Xarxa local",
|
||||||
"local_network_sheet_info": "L'aplicació es connectarà al servidor mitjançant aquest URL quan utilitzeu la xarxa Wi-Fi especificada",
|
"local_network_sheet_info": "L'aplicació es connectarà al servidor mitjançant aquest URL quan utilitzeu la xarxa Wi-Fi especificada",
|
||||||
"location_permission": "Permís d'ubicació",
|
"location_permission": "Permís d'ubicació",
|
||||||
@@ -1146,6 +1159,7 @@
|
|||||||
"locked_folder": "Carpeta bloquejada",
|
"locked_folder": "Carpeta bloquejada",
|
||||||
"log_out": "Tanca la sessió",
|
"log_out": "Tanca la sessió",
|
||||||
"log_out_all_devices": "Tanqueu la sessió de tots els dispositius",
|
"log_out_all_devices": "Tanqueu la sessió de tots els dispositius",
|
||||||
|
"logged_in_as": "Sessió iniciada com a {user}",
|
||||||
"logged_out_all_devices": "S'ha tancat la sessió de tots els dispositius",
|
"logged_out_all_devices": "S'ha tancat la sessió de tots els dispositius",
|
||||||
"logged_out_device": "Dispositiu tancat",
|
"logged_out_device": "Dispositiu tancat",
|
||||||
"login": "Iniciar sessió",
|
"login": "Iniciar sessió",
|
||||||
@@ -1241,6 +1255,7 @@
|
|||||||
"more": "Més",
|
"more": "Més",
|
||||||
"move": "Moure",
|
"move": "Moure",
|
||||||
"move_off_locked_folder": "Moure fora de la carpeta bloquejada",
|
"move_off_locked_folder": "Moure fora de la carpeta bloquejada",
|
||||||
|
"move_to_lock_folder_action_prompt": "{count} afegides a la carpeta protegida",
|
||||||
"move_to_locked_folder": "Moure a la carpeta bloquejada",
|
"move_to_locked_folder": "Moure a la carpeta bloquejada",
|
||||||
"move_to_locked_folder_confirmation": "Aquestes fotos i vídeos seran eliminades de tots els àlbums, i només podran ser vistes des de la carpeta bloquejada",
|
"move_to_locked_folder_confirmation": "Aquestes fotos i vídeos seran eliminades de tots els àlbums, i només podran ser vistes des de la carpeta bloquejada",
|
||||||
"moved_to_archive": "S'han mogut {count, plural, one {# asset} other {# assets}} a l'arxiu",
|
"moved_to_archive": "S'han mogut {count, plural, one {# asset} other {# assets}} a l'arxiu",
|
||||||
@@ -1491,6 +1506,7 @@
|
|||||||
"remove_deleted_assets": "Suprimeix fitxers fora de línia",
|
"remove_deleted_assets": "Suprimeix fitxers fora de línia",
|
||||||
"remove_from_album": "Treu de l'àlbum",
|
"remove_from_album": "Treu de l'àlbum",
|
||||||
"remove_from_favorites": "Eliminar dels preferits",
|
"remove_from_favorites": "Eliminar dels preferits",
|
||||||
|
"remove_from_lock_folder_action_prompt": "{count} eliminades de la carpeta protegida",
|
||||||
"remove_from_locked_folder": "Elimina de la carpeta bloquejada",
|
"remove_from_locked_folder": "Elimina de la carpeta bloquejada",
|
||||||
"remove_from_locked_folder_confirmation": "Segur que vols moure aquestes fotos i vídeos fora de la carpeta bloquejada? Seran visibles a la teva biblioteca.",
|
"remove_from_locked_folder_confirmation": "Segur que vols moure aquestes fotos i vídeos fora de la carpeta bloquejada? Seran visibles a la teva biblioteca.",
|
||||||
"remove_from_shared_link": "Eliminar de l'enllaç compartit",
|
"remove_from_shared_link": "Eliminar de l'enllaç compartit",
|
||||||
@@ -1603,6 +1619,7 @@
|
|||||||
"select_album_cover": "Seleccionar la portada de l'àlbum",
|
"select_album_cover": "Seleccionar la portada de l'àlbum",
|
||||||
"select_all": "Selecciona-ho tot",
|
"select_all": "Selecciona-ho tot",
|
||||||
"select_all_duplicates": "Seleccioneu tots els duplicats",
|
"select_all_duplicates": "Seleccioneu tots els duplicats",
|
||||||
|
"select_all_in": "Selecciona tot en {group}",
|
||||||
"select_avatar_color": "Tria color de l'avatar",
|
"select_avatar_color": "Tria color de l'avatar",
|
||||||
"select_face": "Selecciona cara",
|
"select_face": "Selecciona cara",
|
||||||
"select_featured_photo": "Selecciona foto principal",
|
"select_featured_photo": "Selecciona foto principal",
|
||||||
@@ -1832,6 +1849,7 @@
|
|||||||
"total": "Total",
|
"total": "Total",
|
||||||
"total_usage": "Ús total",
|
"total_usage": "Ús total",
|
||||||
"trash": "Paperera",
|
"trash": "Paperera",
|
||||||
|
"trash_action_prompt": "{count} mogudes a la brossa",
|
||||||
"trash_all": "Envia-ho tot a la paperera",
|
"trash_all": "Envia-ho tot a la paperera",
|
||||||
"trash_count": "Paperera {count, number}",
|
"trash_count": "Paperera {count, number}",
|
||||||
"trash_delete_asset": "Esborra/Elimina element",
|
"trash_delete_asset": "Esborra/Elimina element",
|
||||||
@@ -1849,9 +1867,11 @@
|
|||||||
"unable_to_change_pin_code": "No es pot canviar el codi PIN",
|
"unable_to_change_pin_code": "No es pot canviar el codi PIN",
|
||||||
"unable_to_setup_pin_code": "No s'ha pogut configurar el codi PIN",
|
"unable_to_setup_pin_code": "No s'ha pogut configurar el codi PIN",
|
||||||
"unarchive": "Desarxivar",
|
"unarchive": "Desarxivar",
|
||||||
|
"unarchive_action_prompt": "{count} eliminades de l'arxiu",
|
||||||
"unarchived_count": "{count, plural, other {# elements desarxivats}}",
|
"unarchived_count": "{count, plural, other {# elements desarxivats}}",
|
||||||
"undo": "Desfer",
|
"undo": "Desfer",
|
||||||
"unfavorite": "Reverteix preferit",
|
"unfavorite": "Reverteix preferit",
|
||||||
|
"unfavorite_action_prompt": "{count} eliminades de preferits",
|
||||||
"unhide_person": "Mostra persona",
|
"unhide_person": "Mostra persona",
|
||||||
"unknown": "Desconegut",
|
"unknown": "Desconegut",
|
||||||
"unknown_country": "País Desconegut",
|
"unknown_country": "País Desconegut",
|
||||||
@@ -1867,6 +1887,7 @@
|
|||||||
"unsaved_change": "Canvi no desat",
|
"unsaved_change": "Canvi no desat",
|
||||||
"unselect_all": "Deselecciona-ho tot",
|
"unselect_all": "Deselecciona-ho tot",
|
||||||
"unselect_all_duplicates": "Desmarqueu tots els duplicats",
|
"unselect_all_duplicates": "Desmarqueu tots els duplicats",
|
||||||
|
"unselect_all_in": "Desseleccionar tots els elements de {group}",
|
||||||
"unstack": "Desapila",
|
"unstack": "Desapila",
|
||||||
"unstacked_assets_count": "No apilat {count, plural, one {# recurs} other {# recursos}}",
|
"unstacked_assets_count": "No apilat {count, plural, one {# recurs} other {# recursos}}",
|
||||||
"up_next": "Pròxim",
|
"up_next": "Pròxim",
|
||||||
|
|||||||
100
i18n/cs.json
100
i18n/cs.json
@@ -34,6 +34,7 @@
|
|||||||
"added_to_favorites_count": "Přidáno {count, number} do oblíbených",
|
"added_to_favorites_count": "Přidáno {count, number} do oblíbených",
|
||||||
"admin": {
|
"admin": {
|
||||||
"add_exclusion_pattern_description": "Přidání vzorů vyloučení. Podporováno je globování pomocí *, ** a ?. Chcete-li ignorovat všechny soubory v jakémkoli adresáři s názvem \"Raw\", použijte \"**/Raw/**\". Chcete-li ignorovat všechny soubory končící na \".tif\", použijte \"**/*.tif\". Chcete-li ignorovat absolutní cestu, použijte příkaz \"/path/to/ignore/**\".",
|
"add_exclusion_pattern_description": "Přidání vzorů vyloučení. Podporováno je globování pomocí *, ** a ?. Chcete-li ignorovat všechny soubory v jakémkoli adresáři s názvem \"Raw\", použijte \"**/Raw/**\". Chcete-li ignorovat všechny soubory končící na \".tif\", použijte \"**/*.tif\". Chcete-li ignorovat absolutní cestu, použijte příkaz \"/path/to/ignore/**\".",
|
||||||
|
"admin_user": "Administrátor",
|
||||||
"asset_offline_description": "Tato položka externí knihovny se již na disku nenachází a byla přesunuta do koše. Pokud byl soubor přesunut v rámci knihovny, zkontrolujte časovou osu a vyhledejte nové odpovídající položku. Chcete-li tuto položku obnovit, ujistěte se, že je cesta k níže uvedenému souboru přístupná pomocí aplikace Immich a prohledejte knihovnu.",
|
"asset_offline_description": "Tato položka externí knihovny se již na disku nenachází a byla přesunuta do koše. Pokud byl soubor přesunut v rámci knihovny, zkontrolujte časovou osu a vyhledejte nové odpovídající položku. Chcete-li tuto položku obnovit, ujistěte se, že je cesta k níže uvedenému souboru přístupná pomocí aplikace Immich a prohledejte knihovnu.",
|
||||||
"authentication_settings": "Přihlašování",
|
"authentication_settings": "Přihlašování",
|
||||||
"authentication_settings_description": "Správa hesel, OAuth a dalších nastavení ověření",
|
"authentication_settings_description": "Správa hesel, OAuth a dalších nastavení ověření",
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
"backup_database": "Vytvořit výpis databáze",
|
"backup_database": "Vytvořit výpis databáze",
|
||||||
"backup_database_enable_description": "Povolit výpisy z databáze",
|
"backup_database_enable_description": "Povolit výpisy z databáze",
|
||||||
"backup_keep_last_amount": "Počet předchozích výpisů, které se mají ponechat",
|
"backup_keep_last_amount": "Počet předchozích výpisů, které se mají ponechat",
|
||||||
"backup_settings": "Nastavení výpisu databáze",
|
"backup_settings": "Zálohování databáze",
|
||||||
"backup_settings_description": "Správa nastavení výpisu databáze.",
|
"backup_settings_description": "Správa nastavení výpisu databáze.",
|
||||||
"cleared_jobs": "Hotové úlohy pro: {job}",
|
"cleared_jobs": "Hotové úlohy pro: {job}",
|
||||||
"config_set_by_file": "Konfigurace je aktuálně prováděna konfiguračním souborem",
|
"config_set_by_file": "Konfigurace je aktuálně prováděna konfiguračním souborem",
|
||||||
@@ -165,6 +166,20 @@
|
|||||||
"metadata_settings_description": "Správa nastavení metadat",
|
"metadata_settings_description": "Správa nastavení metadat",
|
||||||
"migration_job": "Migrace",
|
"migration_job": "Migrace",
|
||||||
"migration_job_description": "Migrace miniatur snímků a obličejů do nejnovější struktury složek",
|
"migration_job_description": "Migrace miniatur snímků a obličejů do nejnovější struktury složek",
|
||||||
|
"nightly_tasks_cluster_faces_setting_description": "Spustit rozpoznávání obličeje na nově nalezených obličejích",
|
||||||
|
"nightly_tasks_cluster_new_faces_setting": "Seskupit nové tváře",
|
||||||
|
"nightly_tasks_database_cleanup_setting": "Úlohy čištění databáze",
|
||||||
|
"nightly_tasks_database_cleanup_setting_description": "Vyčistit databázi od starých dat, jejichž platnost vypršela",
|
||||||
|
"nightly_tasks_generate_memories_setting": "Vytváření vzpomínek",
|
||||||
|
"nightly_tasks_generate_memories_setting_description": "Vytváření nových vzpomínek z položek",
|
||||||
|
"nightly_tasks_missing_thumbnails_setting": "Generovat chybějící miniatury",
|
||||||
|
"nightly_tasks_missing_thumbnails_setting_description": "Řadit položky bez miniatur do fronty pro generování miniatur",
|
||||||
|
"nightly_tasks_settings": "Noční úlohy",
|
||||||
|
"nightly_tasks_settings_description": "Správa nočních úkolů",
|
||||||
|
"nightly_tasks_start_time_setting": "Čas zahájení",
|
||||||
|
"nightly_tasks_start_time_setting_description": "Čas, kdy server spustí noční úlohy",
|
||||||
|
"nightly_tasks_sync_quota_usage_setting": "Synchronizace využití kvóty",
|
||||||
|
"nightly_tasks_sync_quota_usage_setting_description": "Aktualizovat kvótu úložiště uživatele na základě aktuálního využití",
|
||||||
"no_paths_added": "Nebyly přidány žádné cesty",
|
"no_paths_added": "Nebyly přidány žádné cesty",
|
||||||
"no_pattern_added": "Nebyl přidán žádný vzor",
|
"no_pattern_added": "Nebyl přidán žádný vzor",
|
||||||
"note_apply_storage_label_previous_assets": "Upozornění: Pro uplatnění Štítku úložiště na dříve nahrané položky spusťte",
|
"note_apply_storage_label_previous_assets": "Upozornění: Pro uplatnění Štítku úložiště na dříve nahrané položky spusťte",
|
||||||
@@ -195,6 +210,8 @@
|
|||||||
"oauth_mobile_redirect_uri": "Mobilní přesměrování URI",
|
"oauth_mobile_redirect_uri": "Mobilní přesměrování URI",
|
||||||
"oauth_mobile_redirect_uri_override": "Přepsat mobilní přesměrování URI",
|
"oauth_mobile_redirect_uri_override": "Přepsat mobilní přesměrování URI",
|
||||||
"oauth_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například ''{callback}''",
|
"oauth_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například ''{callback}''",
|
||||||
|
"oauth_role_claim": "Deklarace Role",
|
||||||
|
"oauth_role_claim_description": "Automaticky udělit přístup správce na základě přítomnosti této deklarace. Deklarace může mít hodnotu 'user' nebo 'admin'.",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "Správa nastavení OAuth přihlášení",
|
"oauth_settings_description": "Správa nastavení OAuth přihlášení",
|
||||||
"oauth_settings_more_details": "Další podrobnosti o této funkci naleznete v <link>dokumentaci</link>.",
|
"oauth_settings_more_details": "Další podrobnosti o této funkci naleznete v <link>dokumentaci</link>.",
|
||||||
@@ -203,7 +220,7 @@
|
|||||||
"oauth_storage_quota_claim": "Deklarace kvóty úložiště",
|
"oauth_storage_quota_claim": "Deklarace kvóty úložiště",
|
||||||
"oauth_storage_quota_claim_description": "Automaticky nastavit kvótu úložiště uživatele na hodnotu této deklarace.",
|
"oauth_storage_quota_claim_description": "Automaticky nastavit kvótu úložiště uživatele na hodnotu této deklarace.",
|
||||||
"oauth_storage_quota_default": "Výchozí kvóta úložiště (GiB)",
|
"oauth_storage_quota_default": "Výchozí kvóta úložiště (GiB)",
|
||||||
"oauth_storage_quota_default_description": "Kvóta v GiB, která se použije, pokud není poskytnuta žádná deklarace (pro neomezenou kvótu zadejte 0).",
|
"oauth_storage_quota_default_description": "Kvóta v GiB, která se použije, pokud není poskytnuta žádná deklarace.",
|
||||||
"oauth_timeout": "Časový limit požadavku",
|
"oauth_timeout": "Časový limit požadavku",
|
||||||
"oauth_timeout_description": "Časový limit pro požadavky v milisekundách",
|
"oauth_timeout_description": "Časový limit pro požadavky v milisekundách",
|
||||||
"password_enable_description": "Přihlášení pomocí e-mailu a hesla",
|
"password_enable_description": "Přihlášení pomocí e-mailu a hesla",
|
||||||
@@ -243,7 +260,7 @@
|
|||||||
"storage_template_migration_info": "Šablona úložiště převede všechny přípony na malá písmena. Změny šablon se uplatní pouze u nových položek. Chcete-li šablonu zpětně použít na dříve nahrané položky, spusťte <link>{job}</link>.",
|
"storage_template_migration_info": "Šablona úložiště převede všechny přípony na malá písmena. Změny šablon se uplatní pouze u nových položek. Chcete-li šablonu zpětně použít na dříve nahrané položky, spusťte <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "Úloha migrace šablony úložiště",
|
"storage_template_migration_job": "Úloha migrace šablony úložiště",
|
||||||
"storage_template_more_details": "Další podrobnosti o této funkci naleznete v sekci <template-link>Šablona úložiště</template-link> včetně jejích <implications-link>důsledků</implications-link>",
|
"storage_template_more_details": "Další podrobnosti o této funkci naleznete v sekci <template-link>Šablona úložiště</template-link> včetně jejích <implications-link>důsledků</implications-link>",
|
||||||
"storage_template_onboarding_description": "Je-li tato funkce povolena, automaticky uspořádá soubory na základě uživatelem definované šablony. Z důvodu problémů se stabilitou byla tato funkce ve výchozím nastavení vypnuta. Další informace naleznete v <link>dokumentaci</link>.",
|
"storage_template_onboarding_description_v2": "Pokud je tato funkce povolena, automaticky uspořádá soubory na základě uživatelem definované šablony. Další informace naleznete v <link>dokumentaci</link>.",
|
||||||
"storage_template_path_length": "Přibližný limit délky cesty: <b>{length, number}</b>/{limit, number}",
|
"storage_template_path_length": "Přibližný limit délky cesty: <b>{length, number}</b>/{limit, number}",
|
||||||
"storage_template_settings": "Šablona úložiště",
|
"storage_template_settings": "Šablona úložiště",
|
||||||
"storage_template_settings_description": "Správa struktury složek a názvů nahraných souborů",
|
"storage_template_settings_description": "Správa struktury složek a názvů nahraných souborů",
|
||||||
@@ -356,10 +373,12 @@
|
|||||||
"admin_password": "Heslo správce",
|
"admin_password": "Heslo správce",
|
||||||
"administration": "Administrace",
|
"administration": "Administrace",
|
||||||
"advanced": "Pokročilé",
|
"advanced": "Pokročilé",
|
||||||
|
"advanced_settings_beta_timeline_subtitle": "Vyzkoušejte nové prostředí aplikace",
|
||||||
|
"advanced_settings_beta_timeline_title": "Časová osa beta verze",
|
||||||
"advanced_settings_enable_alternate_media_filter_subtitle": "Tuto možnost použijte k filtrování médií během synchronizace na základě alternativních kritérií. Tuto možnost vyzkoušejte pouze v případě, že máte problémy s detekcí všech alb v aplikaci.",
|
"advanced_settings_enable_alternate_media_filter_subtitle": "Tuto možnost použijte k filtrování médií během synchronizace na základě alternativních kritérií. Tuto možnost vyzkoušejte pouze v případě, že máte problémy s detekcí všech alb v aplikaci.",
|
||||||
"advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] Použít alternativní filtr pro synchronizaci alb zařízení",
|
"advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] Použít alternativní filtr pro synchronizaci alb zařízení",
|
||||||
"advanced_settings_log_level_title": "Úroveň protokolování: {level}",
|
"advanced_settings_log_level_title": "Úroveň protokolování: {level}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z prostředků v zařízení velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.",
|
"advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z lokálních prostředků velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.",
|
||||||
"advanced_settings_prefer_remote_title": "Preferovat vzdálené obrázky",
|
"advanced_settings_prefer_remote_title": "Preferovat vzdálené obrázky",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, které by měl Immich odesílat s každým síťovým požadavkem",
|
"advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, které by měl Immich odesílat s každým síťovým požadavkem",
|
||||||
"advanced_settings_proxy_headers_title": "Proxy hlavičky",
|
"advanced_settings_proxy_headers_title": "Proxy hlavičky",
|
||||||
@@ -387,6 +406,7 @@
|
|||||||
"album_options": "Možnosti alba",
|
"album_options": "Možnosti alba",
|
||||||
"album_remove_user": "Odebrat uživatele?",
|
"album_remove_user": "Odebrat uživatele?",
|
||||||
"album_remove_user_confirmation": "Opravdu chcete odebrat uživatele {user}?",
|
"album_remove_user_confirmation": "Opravdu chcete odebrat uživatele {user}?",
|
||||||
|
"album_search_not_found": "Nebyla nalezena žádná alba odpovídající vašemu hledání",
|
||||||
"album_share_no_users": "Zřejmě jste toto album sdíleli se všemi uživateli, nebo nemáte žádného uživatele, se kterým byste ho mohli sdílet.",
|
"album_share_no_users": "Zřejmě jste toto album sdíleli se všemi uživateli, nebo nemáte žádného uživatele, se kterým byste ho mohli sdílet.",
|
||||||
"album_updated": "Album aktualizováno",
|
"album_updated": "Album aktualizováno",
|
||||||
"album_updated_setting_description": "Dostávat e-mailová oznámení o nových položkách sdíleného alba",
|
"album_updated_setting_description": "Dostávat e-mailová oznámení o nových položkách sdíleného alba",
|
||||||
@@ -406,6 +426,7 @@
|
|||||||
"albums_default_sort_order": "Výchozí řazení alb",
|
"albums_default_sort_order": "Výchozí řazení alb",
|
||||||
"albums_default_sort_order_description": "Výchozí řazení položek při vytváření nových alb.",
|
"albums_default_sort_order_description": "Výchozí řazení položek při vytváření nových alb.",
|
||||||
"albums_feature_description": "Sbírky položek, které lze sdílet s ostatními uživateli.",
|
"albums_feature_description": "Sbírky položek, které lze sdílet s ostatními uživateli.",
|
||||||
|
"albums_on_device_count": "Alba v zařízení ({count})",
|
||||||
"all": "Vše",
|
"all": "Vše",
|
||||||
"all_albums": "Všechna alba",
|
"all_albums": "Všechna alba",
|
||||||
"all_people": "Všichni lidé",
|
"all_people": "Všichni lidé",
|
||||||
@@ -426,7 +447,8 @@
|
|||||||
"app_settings": "Aplikace",
|
"app_settings": "Aplikace",
|
||||||
"appears_in": "Vyskytuje se v",
|
"appears_in": "Vyskytuje se v",
|
||||||
"archive": "Archiv",
|
"archive": "Archiv",
|
||||||
"archive_or_unarchive_photo": "Archivovat nebo odarchivovat fotku",
|
"archive_action_prompt": "{count} přidaných do archivu",
|
||||||
|
"archive_or_unarchive_photo": "Přidat nebo odebrat fotku z archivu",
|
||||||
"archive_page_no_archived_assets": "Nebyla nalezena žádná archivovaná média",
|
"archive_page_no_archived_assets": "Nebyla nalezena žádná archivovaná média",
|
||||||
"archive_page_title": "Archiv ({count})",
|
"archive_page_title": "Archiv ({count})",
|
||||||
"archive_size": "Velikost archivu",
|
"archive_size": "Velikost archivu",
|
||||||
@@ -463,14 +485,13 @@
|
|||||||
"assets": "Položky",
|
"assets": "Položky",
|
||||||
"assets_added_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}}",
|
"assets_added_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}}",
|
||||||
"assets_added_to_album_count": "Do alba {count, plural, one {byla přidána # položka} few {byly přidány # položky} other {bylo přidáno # položek}}",
|
"assets_added_to_album_count": "Do alba {count, plural, one {byla přidána # položka} few {byly přidány # položky} other {bylo přidáno # položek}}",
|
||||||
"assets_added_to_name_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}} do {hasName, select, true {alba <b>{name}</b>} other {nového alba}}",
|
|
||||||
"assets_cannot_be_added_to_album_count": "{count, plural, one {Položku} other {Položky}} nelze přidat do alba",
|
"assets_cannot_be_added_to_album_count": "{count, plural, one {Položku} other {Položky}} nelze přidat do alba",
|
||||||
"assets_count": "{count, plural, one {# položka} few {# položky} other {# položek}}",
|
"assets_count": "{count, plural, one {# položka} few {# položky} other {# položek}}",
|
||||||
"assets_deleted_permanently": "{count} položek trvale odstraněno",
|
"assets_deleted_permanently": "{count} položek trvale odstraněno",
|
||||||
"assets_deleted_permanently_from_server": "{count} položek trvale odstraněno z Immich serveru",
|
"assets_deleted_permanently_from_server": "{count} položek trvale odstraněno z Immich serveru",
|
||||||
"assets_downloaded_failed": "{count, plural, one {Stažen # soubor - {error} souborů selhalo} few {Staženy # soubory - {error} souborů selhalo} other {Staženo # souborů - {error} souborů selhalo}}",
|
"assets_downloaded_failed": "{count, plural, one {Stažen # soubor - {error} souborů selhalo} few {Staženy # soubory - {error} souborů selhalo} other {Staženo # souborů - {error} souborů selhalo}}",
|
||||||
"assets_downloaded_successfully": "{count, plural, one {Úspěšně stažen # soubor} few {Úspěšně staženy # soubory} other {Úspěšně staženo # souborů}}",
|
"assets_downloaded_successfully": "{count, plural, one {Úspěšně stažen # soubor} few {Úspěšně staženy # soubory} other {Úspěšně staženo # souborů}}",
|
||||||
"assets_moved_to_trash_count": "Do koše {count, plural, one {přesunuta # položka} few {přesunuty # položky} other {přesunuto # položek}}",
|
"assets_moved_to_trash_count": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do koše",
|
||||||
"assets_permanently_deleted_count": "Trvale {count, plural, one {smazána # položka} few {smazány # položky} other {smazáno # položek}}",
|
"assets_permanently_deleted_count": "Trvale {count, plural, one {smazána # položka} few {smazány # položky} other {smazáno # položek}}",
|
||||||
"assets_removed_count": "{count, plural, one {Odstraněna # položka} few {Odstraněny # položky} other {Odstraněno # položek}}",
|
"assets_removed_count": "{count, plural, one {Odstraněna # položka} few {Odstraněny # položky} other {Odstraněno # položek}}",
|
||||||
"assets_removed_permanently_from_device": "{count} položek trvale odstraněno z vašeho zařízení",
|
"assets_removed_permanently_from_device": "{count} položek trvale odstraněno z vašeho zařízení",
|
||||||
@@ -552,6 +573,8 @@
|
|||||||
"backup_options_page_title": "Nastavení záloh",
|
"backup_options_page_title": "Nastavení záloh",
|
||||||
"backup_setting_subtitle": "Správa nastavení zálohování na pozadí a na popředí",
|
"backup_setting_subtitle": "Správa nastavení zálohování na pozadí a na popředí",
|
||||||
"backward": "Pozpátku",
|
"backward": "Pozpátku",
|
||||||
|
"beta_sync": "Stav synchronizace beta verze",
|
||||||
|
"beta_sync_subtitle": "Správa nového systému synchronizace",
|
||||||
"biometric_auth_enabled": "Biometrické ověřování je povoleno",
|
"biometric_auth_enabled": "Biometrické ověřování je povoleno",
|
||||||
"biometric_locked_out": "Jste vyloučeni z biometrického ověřování",
|
"biometric_locked_out": "Jste vyloučeni z biometrického ověřování",
|
||||||
"biometric_no_options": "Biometrické možnosti nejsou k dispozici",
|
"biometric_no_options": "Biometrické možnosti nejsou k dispozici",
|
||||||
@@ -569,7 +592,7 @@
|
|||||||
"cache_settings_clear_cache_button": "Vymazat vyrovnávací paměť",
|
"cache_settings_clear_cache_button": "Vymazat vyrovnávací paměť",
|
||||||
"cache_settings_clear_cache_button_title": "Vymaže vyrovnávací paměť aplikace. To výrazně ovlivní výkon aplikace, dokud se vyrovnávací paměť neobnoví.",
|
"cache_settings_clear_cache_button_title": "Vymaže vyrovnávací paměť aplikace. To výrazně ovlivní výkon aplikace, dokud se vyrovnávací paměť neobnoví.",
|
||||||
"cache_settings_duplicated_assets_clear_button": "VYMAZAT",
|
"cache_settings_duplicated_assets_clear_button": "VYMAZAT",
|
||||||
"cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace zařadila na černou listinu",
|
"cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace ignoruje",
|
||||||
"cache_settings_duplicated_assets_title": "Duplicitní položky ({count})",
|
"cache_settings_duplicated_assets_title": "Duplicitní položky ({count})",
|
||||||
"cache_settings_statistics_album": "Knihovna náhledů",
|
"cache_settings_statistics_album": "Knihovna náhledů",
|
||||||
"cache_settings_statistics_full": "Kompletní fotografie",
|
"cache_settings_statistics_full": "Kompletní fotografie",
|
||||||
@@ -586,6 +609,7 @@
|
|||||||
"cancel": "Zrušit",
|
"cancel": "Zrušit",
|
||||||
"cancel_search": "Zrušit vyhledávání",
|
"cancel_search": "Zrušit vyhledávání",
|
||||||
"canceled": "Zrušeno",
|
"canceled": "Zrušeno",
|
||||||
|
"canceling": "Rušení",
|
||||||
"cannot_merge_people": "Nelze sloučit osoby",
|
"cannot_merge_people": "Nelze sloučit osoby",
|
||||||
"cannot_undo_this_action": "Tuto akci nelze vrátit zpět!",
|
"cannot_undo_this_action": "Tuto akci nelze vrátit zpět!",
|
||||||
"cannot_update_the_description": "Nelze aktualizovat popis",
|
"cannot_update_the_description": "Nelze aktualizovat popis",
|
||||||
@@ -702,7 +726,7 @@
|
|||||||
"daily_title_text_date": "EEEE, d. MMMM",
|
"daily_title_text_date": "EEEE, d. MMMM",
|
||||||
"daily_title_text_date_year": "EEEE, d. MMMM y",
|
"daily_title_text_date_year": "EEEE, d. MMMM y",
|
||||||
"dark": "Tmavý",
|
"dark": "Tmavý",
|
||||||
"darkTheme": "Přepnout tmavý motiv",
|
"dark_theme": "Přepnout tmavý motiv",
|
||||||
"date_after": "Datum po",
|
"date_after": "Datum po",
|
||||||
"date_and_time": "Datum a čas",
|
"date_and_time": "Datum a čas",
|
||||||
"date_before": "Datum před",
|
"date_before": "Datum před",
|
||||||
@@ -718,6 +742,7 @@
|
|||||||
"default_locale": "Výchozí jazyk",
|
"default_locale": "Výchozí jazyk",
|
||||||
"default_locale_description": "Formátovat datumy a čísla podle místního prostředí prohlížeče",
|
"default_locale_description": "Formátovat datumy a čísla podle místního prostředí prohlížeče",
|
||||||
"delete": "Smazat",
|
"delete": "Smazat",
|
||||||
|
"delete_action_prompt": "{count} trvale smazaných",
|
||||||
"delete_album": "Smazat album",
|
"delete_album": "Smazat album",
|
||||||
"delete_api_key_prompt": "Opravdu chcete tento API klíč odstranit?",
|
"delete_api_key_prompt": "Opravdu chcete tento API klíč odstranit?",
|
||||||
"delete_dialog_alert": "Tyto položky budou trvale smazány z aplikace Immich i z vašeho zařízení",
|
"delete_dialog_alert": "Tyto položky budou trvale smazány z aplikace Immich i z vašeho zařízení",
|
||||||
@@ -731,6 +756,7 @@
|
|||||||
"delete_key": "Smazat klíč",
|
"delete_key": "Smazat klíč",
|
||||||
"delete_library": "Smazat knihovnu",
|
"delete_library": "Smazat knihovnu",
|
||||||
"delete_link": "Smazat odkaz",
|
"delete_link": "Smazat odkaz",
|
||||||
|
"delete_local_action_prompt": "{count} smazáno lokálně",
|
||||||
"delete_local_dialog_ok_backed_up_only": "Smazat pouze zálohované",
|
"delete_local_dialog_ok_backed_up_only": "Smazat pouze zálohované",
|
||||||
"delete_local_dialog_ok_force": "Přesto smazat",
|
"delete_local_dialog_ok_force": "Přesto smazat",
|
||||||
"delete_others": "Odstranit ostatní",
|
"delete_others": "Odstranit ostatní",
|
||||||
@@ -744,6 +770,7 @@
|
|||||||
"description": "Popis",
|
"description": "Popis",
|
||||||
"description_input_hint_text": "Přidat popis...",
|
"description_input_hint_text": "Přidat popis...",
|
||||||
"description_input_submit_error": "Chyba aktualizace popisu, další podrobnosti najdete v logu",
|
"description_input_submit_error": "Chyba aktualizace popisu, další podrobnosti najdete v logu",
|
||||||
|
"deselect_all": "Zrušit výběr všech",
|
||||||
"details": "Podrobnosti",
|
"details": "Podrobnosti",
|
||||||
"direction": "Směr",
|
"direction": "Směr",
|
||||||
"disabled": "Zakázáno",
|
"disabled": "Zakázáno",
|
||||||
@@ -761,6 +788,7 @@
|
|||||||
"documentation": "Dokumentace",
|
"documentation": "Dokumentace",
|
||||||
"done": "Hotovo",
|
"done": "Hotovo",
|
||||||
"download": "Stáhnout",
|
"download": "Stáhnout",
|
||||||
|
"download_action_prompt": "Stahování {count} položek",
|
||||||
"download_canceled": "Stahování zrušeno",
|
"download_canceled": "Stahování zrušeno",
|
||||||
"download_complete": "Stahování kompletní",
|
"download_complete": "Stahování kompletní",
|
||||||
"download_enqueue": "Stahování ve frontě",
|
"download_enqueue": "Stahování ve frontě",
|
||||||
@@ -798,6 +826,7 @@
|
|||||||
"edit_key": "Upravit klíč",
|
"edit_key": "Upravit klíč",
|
||||||
"edit_link": "Upravit odkaz",
|
"edit_link": "Upravit odkaz",
|
||||||
"edit_location": "Upravit polohu",
|
"edit_location": "Upravit polohu",
|
||||||
|
"edit_location_action_prompt": "{count} upravených poloh",
|
||||||
"edit_location_dialog_title": "Poloha",
|
"edit_location_dialog_title": "Poloha",
|
||||||
"edit_name": "Upravit jméno",
|
"edit_name": "Upravit jméno",
|
||||||
"edit_people": "Upravit lidi",
|
"edit_people": "Upravit lidi",
|
||||||
@@ -816,6 +845,7 @@
|
|||||||
"empty_trash": "Vyprázdnit koš",
|
"empty_trash": "Vyprázdnit koš",
|
||||||
"empty_trash_confirmation": "Opravdu chcete vysypat koš? Tím se z Immiche trvale odstraní všechny položky v koši.\nTuto akci nelze vrátit zpět!",
|
"empty_trash_confirmation": "Opravdu chcete vysypat koš? Tím se z Immiche trvale odstraní všechny položky v koši.\nTuto akci nelze vrátit zpět!",
|
||||||
"enable": "Povolit",
|
"enable": "Povolit",
|
||||||
|
"enable_backup": "Povolit zálohování",
|
||||||
"enable_biometric_auth_description": "Zadejte váš PIN kód pro povolení biometrického ověřování",
|
"enable_biometric_auth_description": "Zadejte váš PIN kód pro povolení biometrického ověřování",
|
||||||
"enabled": "Povoleno",
|
"enabled": "Povoleno",
|
||||||
"end_date": "Konečné datum",
|
"end_date": "Konečné datum",
|
||||||
@@ -860,7 +890,7 @@
|
|||||||
"failed_to_load_people": "Chyba načítání osob",
|
"failed_to_load_people": "Chyba načítání osob",
|
||||||
"failed_to_remove_product_key": "Nepodařilo se odebrat klíč produktu",
|
"failed_to_remove_product_key": "Nepodařilo se odebrat klíč produktu",
|
||||||
"failed_to_stack_assets": "Nepodařilo se seskupit položky",
|
"failed_to_stack_assets": "Nepodařilo se seskupit položky",
|
||||||
"failed_to_unstack_assets": "Nepodařilo se rozložit položky",
|
"failed_to_unstack_assets": "Nepodařilo se zrušit seskupení položek",
|
||||||
"failed_to_update_notification_status": "Nepodařilo se aktualizovat stav oznámení",
|
"failed_to_update_notification_status": "Nepodařilo se aktualizovat stav oznámení",
|
||||||
"import_path_already_exists": "Tato cesta importu již existuje.",
|
"import_path_already_exists": "Tato cesta importu již existuje.",
|
||||||
"incorrect_email_or_password": "Nesprávný e-mail nebo heslo",
|
"incorrect_email_or_password": "Nesprávný e-mail nebo heslo",
|
||||||
@@ -875,7 +905,7 @@
|
|||||||
"unable_to_add_partners": "Nelze přidat partnery",
|
"unable_to_add_partners": "Nelze přidat partnery",
|
||||||
"unable_to_add_remove_archive": "Nelze {archived, select, true {odstranit položku z} other {přidat položku do}} archivu",
|
"unable_to_add_remove_archive": "Nelze {archived, select, true {odstranit položku z} other {přidat položku do}} archivu",
|
||||||
"unable_to_add_remove_favorites": "Nelze {favorite, select, true {oblíbit položku} other {zrušit oblíbení položky}}",
|
"unable_to_add_remove_favorites": "Nelze {favorite, select, true {oblíbit položku} other {zrušit oblíbení položky}}",
|
||||||
"unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odarchivovat}}",
|
"unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odebrat z archivu}}",
|
||||||
"unable_to_change_album_user_role": "Nelze změnit roli uživatele alba",
|
"unable_to_change_album_user_role": "Nelze změnit roli uživatele alba",
|
||||||
"unable_to_change_date": "Nelze změnit datum",
|
"unable_to_change_date": "Nelze změnit datum",
|
||||||
"unable_to_change_description": "Nelze změnit popis",
|
"unable_to_change_description": "Nelze změnit popis",
|
||||||
@@ -983,6 +1013,7 @@
|
|||||||
"failed_to_load_assets": "Nepodařilo se načíst položky",
|
"failed_to_load_assets": "Nepodařilo se načíst položky",
|
||||||
"failed_to_load_folder": "Nepodařilo se načíst složku",
|
"failed_to_load_folder": "Nepodařilo se načíst složku",
|
||||||
"favorite": "Oblíbit",
|
"favorite": "Oblíbit",
|
||||||
|
"favorite_action_prompt": "{count} přidáno do Oblíbených",
|
||||||
"favorite_or_unfavorite_photo": "Oblíbit nebo zrušit oblíbení fotky",
|
"favorite_or_unfavorite_photo": "Oblíbit nebo zrušit oblíbení fotky",
|
||||||
"favorites": "Oblíbené",
|
"favorites": "Oblíbené",
|
||||||
"favorites_page_no_favorites": "Nebyla nalezena žádná oblíbená média",
|
"favorites_page_no_favorites": "Nebyla nalezena žádná oblíbená média",
|
||||||
@@ -1022,6 +1053,9 @@
|
|||||||
"haptic_feedback_switch": "Povolit dotykovou zpětnou vazbu",
|
"haptic_feedback_switch": "Povolit dotykovou zpětnou vazbu",
|
||||||
"haptic_feedback_title": "Dotyková zpětná vazba",
|
"haptic_feedback_title": "Dotyková zpětná vazba",
|
||||||
"has_quota": "Má kvótu",
|
"has_quota": "Má kvótu",
|
||||||
|
"hash_asset": "Hash položky",
|
||||||
|
"hashed_assets": "Hashované položky",
|
||||||
|
"hashing": "Hashování",
|
||||||
"header_settings_add_header_tip": "Přidat hlavičku",
|
"header_settings_add_header_tip": "Přidat hlavičku",
|
||||||
"header_settings_field_validator_msg": "Hodnota nemůže být prázdná",
|
"header_settings_field_validator_msg": "Hodnota nemůže být prázdná",
|
||||||
"header_settings_header_name_input": "Název hlavičky",
|
"header_settings_header_name_input": "Název hlavičky",
|
||||||
@@ -1054,6 +1088,7 @@
|
|||||||
"host": "Hostitel",
|
"host": "Hostitel",
|
||||||
"hour": "Hodina",
|
"hour": "Hodina",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
|
"idle": "Nečinnost",
|
||||||
"ignore_icloud_photos": "Ignorovat fotografie na iCloudu",
|
"ignore_icloud_photos": "Ignorovat fotografie na iCloudu",
|
||||||
"ignore_icloud_photos_description": "Fotografie uložené na iCloudu se nebudou nahrávat na Immich server",
|
"ignore_icloud_photos_description": "Fotografie uložené na iCloudu se nebudou nahrávat na Immich server",
|
||||||
"image": "Obrázek",
|
"image": "Obrázek",
|
||||||
@@ -1126,6 +1161,7 @@
|
|||||||
"library_page_sort_created": "Naposledy vytvořené",
|
"library_page_sort_created": "Naposledy vytvořené",
|
||||||
"library_page_sort_last_modified": "Naposledy upraveno",
|
"library_page_sort_last_modified": "Naposledy upraveno",
|
||||||
"library_page_sort_title": "Podle názvu alba",
|
"library_page_sort_title": "Podle názvu alba",
|
||||||
|
"licenses": "Licence",
|
||||||
"light": "Světlý",
|
"light": "Světlý",
|
||||||
"like_deleted": "Lajk smazán",
|
"like_deleted": "Lajk smazán",
|
||||||
"link_motion_video": "Připojit pohyblivé video",
|
"link_motion_video": "Připojit pohyblivé video",
|
||||||
@@ -1135,7 +1171,9 @@
|
|||||||
"list": "Seznam",
|
"list": "Seznam",
|
||||||
"loading": "Načítání",
|
"loading": "Načítání",
|
||||||
"loading_search_results_failed": "Načítání výsledků vyhledávání se nezdařilo",
|
"loading_search_results_failed": "Načítání výsledků vyhledávání se nezdařilo",
|
||||||
|
"local": "Místní",
|
||||||
"local_asset_cast_failed": "Nelze odeslat položku, která není nahraná na serveru",
|
"local_asset_cast_failed": "Nelze odeslat položku, která není nahraná na serveru",
|
||||||
|
"local_assets": "Místní položky",
|
||||||
"local_network": "Místní síť",
|
"local_network": "Místní síť",
|
||||||
"local_network_sheet_info": "Aplikace se při použití zadané sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL",
|
"local_network_sheet_info": "Aplikace se při použití zadané sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL",
|
||||||
"location_permission": "Oprávnění polohy",
|
"location_permission": "Oprávnění polohy",
|
||||||
@@ -1149,6 +1187,7 @@
|
|||||||
"locked_folder": "Uzamčená složka",
|
"locked_folder": "Uzamčená složka",
|
||||||
"log_out": "Odhlásit",
|
"log_out": "Odhlásit",
|
||||||
"log_out_all_devices": "Odhlásit všechna zařízení",
|
"log_out_all_devices": "Odhlásit všechna zařízení",
|
||||||
|
"logged_in_as": "Přihlášen jako {user}",
|
||||||
"logged_out_all_devices": "Všechna zařízení odhlášena",
|
"logged_out_all_devices": "Všechna zařízení odhlášena",
|
||||||
"logged_out_device": "Zařízení odhlášeno",
|
"logged_out_device": "Zařízení odhlášeno",
|
||||||
"login": "Přihlášení",
|
"login": "Přihlášení",
|
||||||
@@ -1244,10 +1283,11 @@
|
|||||||
"more": "Více",
|
"more": "Více",
|
||||||
"move": "Přesunout",
|
"move": "Přesunout",
|
||||||
"move_off_locked_folder": "Přesunout z uzamčené složky",
|
"move_off_locked_folder": "Přesunout z uzamčené složky",
|
||||||
|
"move_to_lock_folder_action_prompt": "{count} přidaných do uzamčené složky",
|
||||||
"move_to_locked_folder": "Přesunout do uzamčené složky",
|
"move_to_locked_folder": "Přesunout do uzamčené složky",
|
||||||
"move_to_locked_folder_confirmation": "Tyto fotky a videa budou odstraněny ze všech alb a bude je možné zobrazit pouze v uzamčené složce",
|
"move_to_locked_folder_confirmation": "Tyto fotky a videa budou odstraněny ze všech alb a bude je možné zobrazit pouze v uzamčené složce",
|
||||||
"moved_to_archive": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do archivu",
|
"moved_to_archive": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do archivu",
|
||||||
"moved_to_library": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do knihovny",
|
"moved_to_library": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do knihovny",
|
||||||
"moved_to_trash": "Přesunuto do koše",
|
"moved_to_trash": "Přesunuto do koše",
|
||||||
"multiselect_grid_edit_date_time_err_read_only": "Nelze upravit datum položek pouze pro čtení, přeskakuji",
|
"multiselect_grid_edit_date_time_err_read_only": "Nelze upravit datum položek pouze pro čtení, přeskakuji",
|
||||||
"multiselect_grid_edit_gps_err_read_only": "Nelze upravit polohu položek pouze pro čtení, přeskakuji",
|
"multiselect_grid_edit_gps_err_read_only": "Nelze upravit polohu položek pouze pro čtení, přeskakuji",
|
||||||
@@ -1290,6 +1330,7 @@
|
|||||||
"no_results": "Žádné výsledky",
|
"no_results": "Žádné výsledky",
|
||||||
"no_results_description": "Zkuste použít synonymum nebo obecnější klíčové slovo",
|
"no_results_description": "Zkuste použít synonymum nebo obecnější klíčové slovo",
|
||||||
"no_shared_albums_message": "Vytvořte si album a sdílejte fotografie a videa s lidmi ve své síti",
|
"no_shared_albums_message": "Vytvořte si album a sdílejte fotografie a videa s lidmi ve své síti",
|
||||||
|
"no_uploads_in_progress": "Neprobíhá žádné nahrávání",
|
||||||
"not_in_any_album": "Bez alba",
|
"not_in_any_album": "Bez alba",
|
||||||
"not_selected": "Není vybráno",
|
"not_selected": "Není vybráno",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "Upozornění: Chcete-li použít štítek úložiště na dříve nahrané položky, spusťte příkaz",
|
"note_apply_storage_label_to_previously_uploaded assets": "Upozornění: Chcete-li použít štítek úložiště na dříve nahrané položky, spusťte příkaz",
|
||||||
@@ -1327,6 +1368,7 @@
|
|||||||
"original": "originál",
|
"original": "originál",
|
||||||
"other": "Ostatní",
|
"other": "Ostatní",
|
||||||
"other_devices": "Ostatní zařízení",
|
"other_devices": "Ostatní zařízení",
|
||||||
|
"other_entities": "Ostatní entity",
|
||||||
"other_variables": "Další proměnné",
|
"other_variables": "Další proměnné",
|
||||||
"owned": "Vlastní",
|
"owned": "Vlastní",
|
||||||
"owner": "Vlastník",
|
"owner": "Vlastník",
|
||||||
@@ -1458,6 +1500,7 @@
|
|||||||
"purchase_server_description_2": "Stav podporovatele",
|
"purchase_server_description_2": "Stav podporovatele",
|
||||||
"purchase_server_title": "Server",
|
"purchase_server_title": "Server",
|
||||||
"purchase_settings_server_activated": "Produktový klíč serveru spravuje správce",
|
"purchase_settings_server_activated": "Produktový klíč serveru spravuje správce",
|
||||||
|
"queue_status": "Ve frontě {count}/{total}",
|
||||||
"rating": "Hodnocení hvězdičkami",
|
"rating": "Hodnocení hvězdičkami",
|
||||||
"rating_clear": "Vyčistit hodnocení",
|
"rating_clear": "Vyčistit hodnocení",
|
||||||
"rating_count": "{count, plural, one {# hvězdička} few {# hvězdičky} other {# hvězdček}}",
|
"rating_count": "{count, plural, one {# hvězdička} few {# hvězdičky} other {# hvězdček}}",
|
||||||
@@ -1486,6 +1529,8 @@
|
|||||||
"refreshing_faces": "Obnovování obličejů",
|
"refreshing_faces": "Obnovování obličejů",
|
||||||
"refreshing_metadata": "Obnovování metadat",
|
"refreshing_metadata": "Obnovování metadat",
|
||||||
"regenerating_thumbnails": "Regenerace miniatur",
|
"regenerating_thumbnails": "Regenerace miniatur",
|
||||||
|
"remote": "Vzdálený",
|
||||||
|
"remote_assets": "Vzdálené položky",
|
||||||
"remove": "Odstranit",
|
"remove": "Odstranit",
|
||||||
"remove_assets_album_confirmation": "Opravdu chcete z alba odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?",
|
"remove_assets_album_confirmation": "Opravdu chcete z alba odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?",
|
||||||
"remove_assets_shared_link_confirmation": "Opravdu chcete ze sdíleného odkazu odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?",
|
"remove_assets_shared_link_confirmation": "Opravdu chcete ze sdíleného odkazu odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?",
|
||||||
@@ -1493,7 +1538,9 @@
|
|||||||
"remove_custom_date_range": "Odstranit vlastní rozsah datumů",
|
"remove_custom_date_range": "Odstranit vlastní rozsah datumů",
|
||||||
"remove_deleted_assets": "Odstranit offline soubory",
|
"remove_deleted_assets": "Odstranit offline soubory",
|
||||||
"remove_from_album": "Odstranit z alba",
|
"remove_from_album": "Odstranit z alba",
|
||||||
|
"remove_from_album_action_prompt": "{count} odstraněných z alba",
|
||||||
"remove_from_favorites": "Odstranit z oblíbených",
|
"remove_from_favorites": "Odstranit z oblíbených",
|
||||||
|
"remove_from_lock_folder_action_prompt": "{count} odebraných z uzamčené složky",
|
||||||
"remove_from_locked_folder": "Odstranit z uzamčené složky",
|
"remove_from_locked_folder": "Odstranit z uzamčené složky",
|
||||||
"remove_from_locked_folder_confirmation": "Opravdu chcete tyto fotky a videa přesunout z uzamčené složky? Budou viditelné ve vaší knihovně.",
|
"remove_from_locked_folder_confirmation": "Opravdu chcete tyto fotky a videa přesunout z uzamčené složky? Budou viditelné ve vaší knihovně.",
|
||||||
"remove_from_shared_link": "Odstranit ze sdíleného odkazu",
|
"remove_from_shared_link": "Odstranit ze sdíleného odkazu",
|
||||||
@@ -1521,11 +1568,15 @@
|
|||||||
"reset_password": "Obnovit heslo",
|
"reset_password": "Obnovit heslo",
|
||||||
"reset_people_visibility": "Obnovit viditelnost lidí",
|
"reset_people_visibility": "Obnovit viditelnost lidí",
|
||||||
"reset_pin_code": "Resetovat PIN kód",
|
"reset_pin_code": "Resetovat PIN kód",
|
||||||
|
"reset_sqlite": "Obnovit SQLite databázi",
|
||||||
|
"reset_sqlite_confirmation": "Jste si jisti, že chcete obnovit SQLite databázi? Pro opětovnou synchronizaci dat se budete muset odhlásit a znovu přihlásit",
|
||||||
|
"reset_sqlite_success": "Obnovení SQLite databáze proběhlo úspěšně",
|
||||||
"reset_to_default": "Obnovit výchozí nastavení",
|
"reset_to_default": "Obnovit výchozí nastavení",
|
||||||
"resolve_duplicates": "Vyřešit duplicity",
|
"resolve_duplicates": "Vyřešit duplicity",
|
||||||
"resolved_all_duplicates": "Vyřešeny všechny duplicity",
|
"resolved_all_duplicates": "Vyřešeny všechny duplicity",
|
||||||
"restore": "Obnovit",
|
"restore": "Obnovit",
|
||||||
"restore_all": "Obnovit vše",
|
"restore_all": "Obnovit vše",
|
||||||
|
"restore_trash_action_prompt": "{count} obnoveno z koše",
|
||||||
"restore_user": "Obnovit uživatele",
|
"restore_user": "Obnovit uživatele",
|
||||||
"restored_asset": "Položka obnovena",
|
"restored_asset": "Položka obnovena",
|
||||||
"resume": "Pokračovat",
|
"resume": "Pokračovat",
|
||||||
@@ -1534,6 +1585,7 @@
|
|||||||
"role": "Role",
|
"role": "Role",
|
||||||
"role_editor": "Editor",
|
"role_editor": "Editor",
|
||||||
"role_viewer": "Divák",
|
"role_viewer": "Divák",
|
||||||
|
"running": "Probíhá",
|
||||||
"save": "Uložit",
|
"save": "Uložit",
|
||||||
"save_to_gallery": "Uložit do galerie",
|
"save_to_gallery": "Uložit do galerie",
|
||||||
"saved_api_key": "API klíč uložen",
|
"saved_api_key": "API klíč uložen",
|
||||||
@@ -1606,6 +1658,7 @@
|
|||||||
"select_album_cover": "Vybrat obal alba",
|
"select_album_cover": "Vybrat obal alba",
|
||||||
"select_all": "Vybrat vše",
|
"select_all": "Vybrat vše",
|
||||||
"select_all_duplicates": "Vybrat všechny duplicity",
|
"select_all_duplicates": "Vybrat všechny duplicity",
|
||||||
|
"select_all_in": "Vybrat vše ve skupině {group}",
|
||||||
"select_avatar_color": "Vyberte barvu avatara",
|
"select_avatar_color": "Vyberte barvu avatara",
|
||||||
"select_face": "Vybrat obličej",
|
"select_face": "Vybrat obličej",
|
||||||
"select_featured_photo": "Vybrat hlavní fotografii",
|
"select_featured_photo": "Vybrat hlavní fotografii",
|
||||||
@@ -1664,6 +1717,7 @@
|
|||||||
"settings_saved": "Nastavení uloženo",
|
"settings_saved": "Nastavení uloženo",
|
||||||
"setup_pin_code": "Nastavení PIN kódu",
|
"setup_pin_code": "Nastavení PIN kódu",
|
||||||
"share": "Sdílet",
|
"share": "Sdílet",
|
||||||
|
"share_action_prompt": "Sdíleno {count} položek",
|
||||||
"share_add_photos": "Přidat fotografie",
|
"share_add_photos": "Přidat fotografie",
|
||||||
"share_assets_selected": "{count} vybráno",
|
"share_assets_selected": "{count} vybráno",
|
||||||
"share_dialog_preparing": "Připravuji...",
|
"share_dialog_preparing": "Připravuji...",
|
||||||
@@ -1765,6 +1819,7 @@
|
|||||||
"sort_title": "Název alba",
|
"sort_title": "Název alba",
|
||||||
"source": "Zdroj",
|
"source": "Zdroj",
|
||||||
"stack": "Seskupit",
|
"stack": "Seskupit",
|
||||||
|
"stack_action_prompt": "{count} seskupeno",
|
||||||
"stack_duplicates": "Seskupit duplicity",
|
"stack_duplicates": "Seskupit duplicity",
|
||||||
"stack_select_one_photo": "Vyberte jednu hlavní fotografii pro seskupení",
|
"stack_select_one_photo": "Vyberte jednu hlavní fotografii pro seskupení",
|
||||||
"stack_selected_photos": "Seskupení vybraných fotografií",
|
"stack_selected_photos": "Seskupení vybraných fotografií",
|
||||||
@@ -1784,6 +1839,7 @@
|
|||||||
"storage_quota": "Kvóta úložiště",
|
"storage_quota": "Kvóta úložiště",
|
||||||
"storage_usage": "Využito {used} z {available}",
|
"storage_usage": "Využito {used} z {available}",
|
||||||
"submit": "Odeslat",
|
"submit": "Odeslat",
|
||||||
|
"success": "Úspěch",
|
||||||
"suggestions": "Návrhy",
|
"suggestions": "Návrhy",
|
||||||
"sunrise_on_the_beach": "Východ slunce na pláži",
|
"sunrise_on_the_beach": "Východ slunce na pláži",
|
||||||
"support": "Podpora",
|
"support": "Podpora",
|
||||||
@@ -1793,6 +1849,8 @@
|
|||||||
"sync": "Synchronizovat",
|
"sync": "Synchronizovat",
|
||||||
"sync_albums": "Synchronizovat alba",
|
"sync_albums": "Synchronizovat alba",
|
||||||
"sync_albums_manual_subtitle": "Synchronizovat všechna nahraná videa a fotografie do vybraných záložních alb",
|
"sync_albums_manual_subtitle": "Synchronizovat všechna nahraná videa a fotografie do vybraných záložních alb",
|
||||||
|
"sync_local": "Synchronizovat místní",
|
||||||
|
"sync_remote": "Synchronizovat vzdálené",
|
||||||
"sync_upload_album_setting_subtitle": "Vytvořit a nahrát fotografie a videa do vybraných alb na Immich",
|
"sync_upload_album_setting_subtitle": "Vytvořit a nahrát fotografie a videa do vybraných alb na Immich",
|
||||||
"tag": "Značka",
|
"tag": "Značka",
|
||||||
"tag_assets": "Přiřadit značku",
|
"tag_assets": "Přiřadit značku",
|
||||||
@@ -1803,6 +1861,7 @@
|
|||||||
"tag_updated": "Aktualizována značka: {tag}",
|
"tag_updated": "Aktualizována značka: {tag}",
|
||||||
"tagged_assets": "Přiřazena značka {count, plural, one {# položce} other {# položkám}}",
|
"tagged_assets": "Přiřazena značka {count, plural, one {# položce} other {# položkám}}",
|
||||||
"tags": "Značky",
|
"tags": "Značky",
|
||||||
|
"tap_to_run_job": "Klepnutím na spustíte úlohu",
|
||||||
"template": "Šablona",
|
"template": "Šablona",
|
||||||
"theme": "Motiv",
|
"theme": "Motiv",
|
||||||
"theme_selection": "Výběr motivu",
|
"theme_selection": "Výběr motivu",
|
||||||
@@ -1835,6 +1894,7 @@
|
|||||||
"total": "Celkem",
|
"total": "Celkem",
|
||||||
"total_usage": "Celkové využití",
|
"total_usage": "Celkové využití",
|
||||||
"trash": "Koš",
|
"trash": "Koš",
|
||||||
|
"trash_action_prompt": "{count} přesunutých do koše",
|
||||||
"trash_all": "Vyhodit vše",
|
"trash_all": "Vyhodit vše",
|
||||||
"trash_count": "Vyhodit {count, number}",
|
"trash_count": "Vyhodit {count, number}",
|
||||||
"trash_delete_asset": "Vyhodit/Smazat položku",
|
"trash_delete_asset": "Vyhodit/Smazat položku",
|
||||||
@@ -1851,10 +1911,12 @@
|
|||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
"unable_to_change_pin_code": "Nelze změnit PIN kód",
|
"unable_to_change_pin_code": "Nelze změnit PIN kód",
|
||||||
"unable_to_setup_pin_code": "Nelze nastavit PIN kód",
|
"unable_to_setup_pin_code": "Nelze nastavit PIN kód",
|
||||||
"unarchive": "Odarchivovat",
|
"unarchive": "Odebrat z archivu",
|
||||||
|
"unarchive_action_prompt": "{count} odstraněných z archivu",
|
||||||
"unarchived_count": "{count, plural, one {Odarchivována #} few {Odarchivovány #} other {Odarchivováno #}}",
|
"unarchived_count": "{count, plural, one {Odarchivována #} few {Odarchivovány #} other {Odarchivováno #}}",
|
||||||
"undo": "Vrátit zpět",
|
"undo": "Vrátit zpět",
|
||||||
"unfavorite": "Zrušit oblíbení",
|
"unfavorite": "Zrušit oblíbení",
|
||||||
|
"unfavorite_action_prompt": "{count} odstraněných z oblíbených",
|
||||||
"unhide_person": "Zrušit skrytí osoby",
|
"unhide_person": "Zrušit skrytí osoby",
|
||||||
"unknown": "Neznámý",
|
"unknown": "Neznámý",
|
||||||
"unknown_country": "Neznámá země",
|
"unknown_country": "Neznámá země",
|
||||||
@@ -1870,13 +1932,17 @@
|
|||||||
"unsaved_change": "Neuložená změna",
|
"unsaved_change": "Neuložená změna",
|
||||||
"unselect_all": "Zrušit výběr všech",
|
"unselect_all": "Zrušit výběr všech",
|
||||||
"unselect_all_duplicates": "Zrušit výběr všech duplicit",
|
"unselect_all_duplicates": "Zrušit výběr všech duplicit",
|
||||||
|
"unselect_all_in": "Zrušit výběr ve skupině {group}",
|
||||||
"unstack": "Zrušit seskupení",
|
"unstack": "Zrušit seskupení",
|
||||||
"unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položiek}}",
|
"unstack_action_prompt": "{count} seskupených zrušeno",
|
||||||
|
"unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položek}}",
|
||||||
|
"untagged": "Neoznačeno",
|
||||||
"up_next": "To je prozatím vše",
|
"up_next": "To je prozatím vše",
|
||||||
"updated_at": "Aktualizováno",
|
"updated_at": "Aktualizováno",
|
||||||
"updated_password": "Heslo aktualizováno",
|
"updated_password": "Heslo aktualizováno",
|
||||||
"upload": "Nahrát",
|
"upload": "Nahrát",
|
||||||
"upload_concurrency": "Souběžnost nahrávání",
|
"upload_concurrency": "Souběžnost nahrávání",
|
||||||
|
"upload_details": "Detaily nahrávání",
|
||||||
"upload_dialog_info": "Chcete zálohovat vybrané položky na server?",
|
"upload_dialog_info": "Chcete zálohovat vybrané položky na server?",
|
||||||
"upload_dialog_title": "Nahrát položku",
|
"upload_dialog_title": "Nahrát položku",
|
||||||
"upload_errors": "Nahrávání bylo dokončeno s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku pro zobrazení nových položek.",
|
"upload_errors": "Nahrávání bylo dokončeno s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku pro zobrazení nových položek.",
|
||||||
@@ -1908,6 +1974,7 @@
|
|||||||
"user_usage_stats_description": "Zobrazit statistiky používání účtu",
|
"user_usage_stats_description": "Zobrazit statistiky používání účtu",
|
||||||
"username": "Uživateleské jméno",
|
"username": "Uživateleské jméno",
|
||||||
"users": "Uživatelé",
|
"users": "Uživatelé",
|
||||||
|
"users_added_to_album_count": "{count, plural, one {Přidán # uživatel} few {Přidány # uživatelé} other {Přidáno # uživatelů}} do alba",
|
||||||
"utilities": "Nástroje",
|
"utilities": "Nástroje",
|
||||||
"validate": "Ověřit",
|
"validate": "Ověřit",
|
||||||
"validate_endpoint_error": "Zadejte platné URL",
|
"validate_endpoint_error": "Zadejte platné URL",
|
||||||
@@ -1926,6 +1993,7 @@
|
|||||||
"view_album": "Zobrazit album",
|
"view_album": "Zobrazit album",
|
||||||
"view_all": "Zobrazit vše",
|
"view_all": "Zobrazit vše",
|
||||||
"view_all_users": "Zobrazit všechny uživatele",
|
"view_all_users": "Zobrazit všechny uživatele",
|
||||||
|
"view_details": "Zobrazit podrobnosti",
|
||||||
"view_in_timeline": "Zobrazit na časové ose",
|
"view_in_timeline": "Zobrazit na časové ose",
|
||||||
"view_link": "Zobrazit odkaz",
|
"view_link": "Zobrazit odkaz",
|
||||||
"view_links": "Zobrazit odkazy",
|
"view_links": "Zobrazit odkazy",
|
||||||
@@ -1937,7 +2005,7 @@
|
|||||||
"view_user": "Zobrazit uživatele",
|
"view_user": "Zobrazit uživatele",
|
||||||
"viewer_remove_from_stack": "Odstranit ze zásobníku",
|
"viewer_remove_from_stack": "Odstranit ze zásobníku",
|
||||||
"viewer_stack_use_as_main_asset": "Použít jako hlavní položku",
|
"viewer_stack_use_as_main_asset": "Použít jako hlavní položku",
|
||||||
"viewer_unstack": "Rozbalit zásobník",
|
"viewer_unstack": "Zrušit zásobník",
|
||||||
"visibility_changed": "Viditelnost změněna u {count, plural, one {# osoby} few {# osob} other {# lidí}}",
|
"visibility_changed": "Viditelnost změněna u {count, plural, one {# osoby} few {# osob} other {# lidí}}",
|
||||||
"waiting": "Čekající",
|
"waiting": "Čekající",
|
||||||
"warning": "Upozornění",
|
"warning": "Upozornění",
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
"added_to_favorites_count": "Tilføjet {count, number} til favoritter",
|
"added_to_favorites_count": "Tilføjet {count, number} til favoritter",
|
||||||
"admin": {
|
"admin": {
|
||||||
"add_exclusion_pattern_description": "Tilføj udelukkelsesmønstre. Globbing ved hjælp af *, ** og ? understøttes. For at ignorere alle filer i enhver mappe med navnet \"Raw\", brug \"**/Raw/**\". For at ignorere alle filer, der slutter på \".tif\", brug \"**/*.tif\". For at ignorere en absolut sti, brug \"/sti/til/ignoreret/**\".",
|
"add_exclusion_pattern_description": "Tilføj udelukkelsesmønstre. Globbing ved hjælp af *, ** og ? understøttes. For at ignorere alle filer i enhver mappe med navnet \"Raw\", brug \"**/Raw/**\". For at ignorere alle filer, der slutter på \".tif\", brug \"**/*.tif\". For at ignorere en absolut sti, brug \"/sti/til/ignoreret/**\".",
|
||||||
|
"admin_user": "Administrator bruger",
|
||||||
"asset_offline_description": "Denne eksterne biblioteksressource findes ikke længere på disken og er blevet flyttet til papirkurven. Hvis filen blev flyttet inde i biblioteket, skal du tjekke din tidslinje for den nye tilsvarende ressource. For at gendanne denne ressource skal du sikre, at filstien nedenfor kan tilgås af Immich og scanne biblioteket.",
|
"asset_offline_description": "Denne eksterne biblioteksressource findes ikke længere på disken og er blevet flyttet til papirkurven. Hvis filen blev flyttet inde i biblioteket, skal du tjekke din tidslinje for den nye tilsvarende ressource. For at gendanne denne ressource skal du sikre, at filstien nedenfor kan tilgås af Immich og scanne biblioteket.",
|
||||||
"authentication_settings": "Godkendelsesindstillinger",
|
"authentication_settings": "Godkendelsesindstillinger",
|
||||||
"authentication_settings_description": "Administrer adgangskode, OAuth og andre godkendelsesindstillinger",
|
"authentication_settings_description": "Administrer adgangskode, OAuth og andre godkendelsesindstillinger",
|
||||||
@@ -165,6 +166,7 @@
|
|||||||
"metadata_settings_description": "Håndtér metadataindstillinger",
|
"metadata_settings_description": "Håndtér metadataindstillinger",
|
||||||
"migration_job": "Migrering",
|
"migration_job": "Migrering",
|
||||||
"migration_job_description": "Migrér miniaturebilleder for aktiver og ansigter til den seneste mappestruktur",
|
"migration_job_description": "Migrér miniaturebilleder for aktiver og ansigter til den seneste mappestruktur",
|
||||||
|
"nightly_tasks_cluster_faces_setting_description": "Kør ansigtsgenkendelse på nye ansigter",
|
||||||
"no_paths_added": "Ingen stier tilføjet",
|
"no_paths_added": "Ingen stier tilføjet",
|
||||||
"no_pattern_added": "Intet mønster tilføjet",
|
"no_pattern_added": "Intet mønster tilføjet",
|
||||||
"note_apply_storage_label_previous_assets": "Bemærk: For at anvende Lagringsmærkatet på tidligere uploadede mediefiler, kør",
|
"note_apply_storage_label_previous_assets": "Bemærk: For at anvende Lagringsmærkatet på tidligere uploadede mediefiler, kør",
|
||||||
@@ -243,7 +245,6 @@
|
|||||||
"storage_template_migration_info": "Lager-skabelonen vil konvertere alle filendelser til små bogstaver. Skabelonændringer vil kun gælde for nye mediefiler. For at anvende skabelonen retroaktivt på tidligere uploadede mediefiler skal du køre <link>{job}</link>.",
|
"storage_template_migration_info": "Lager-skabelonen vil konvertere alle filendelser til små bogstaver. Skabelonændringer vil kun gælde for nye mediefiler. For at anvende skabelonen retroaktivt på tidligere uploadede mediefiler skal du køre <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "Lager Skabelon Migreringsjob",
|
"storage_template_migration_job": "Lager Skabelon Migreringsjob",
|
||||||
"storage_template_more_details": "For flere detaljer om denne funktion, referer til <template-link>Lager Skabelonen</template-link> og dens <implications-link>implikationer</implications-link>",
|
"storage_template_more_details": "For flere detaljer om denne funktion, referer til <template-link>Lager Skabelonen</template-link> og dens <implications-link>implikationer</implications-link>",
|
||||||
"storage_template_onboarding_description": "Når denne funktion er aktiveret, vil den automatisk organisere filer baseret på en brugerdefineret skabelon. På grund af stabilitetsproblemer er funktionen som standard slået fra. For mere information, se <link>dokumentation</link>.",
|
|
||||||
"storage_template_path_length": "Anslået sti-længde begrænsning <b>{length, number}</b>/{limit, number}",
|
"storage_template_path_length": "Anslået sti-længde begrænsning <b>{length, number}</b>/{limit, number}",
|
||||||
"storage_template_settings": "Lagringsskabelon",
|
"storage_template_settings": "Lagringsskabelon",
|
||||||
"storage_template_settings_description": "Administrer mappestrukturen og filnavnet for den uploadede mediefil",
|
"storage_template_settings_description": "Administrer mappestrukturen og filnavnet for den uploadede mediefil",
|
||||||
@@ -463,7 +464,6 @@
|
|||||||
"assets": "elementer",
|
"assets": "elementer",
|
||||||
"assets_added_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}}",
|
"assets_added_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}}",
|
||||||
"assets_added_to_album_count": "{count, plural, one {# mediefil} other {# mediefiler}} tilføjet til albummet",
|
"assets_added_to_album_count": "{count, plural, one {# mediefil} other {# mediefiler}} tilføjet til albummet",
|
||||||
"assets_added_to_name_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}} til {hasName, select, true {<b>{name}</b>} other {nyt album}}",
|
|
||||||
"assets_cannot_be_added_to_album_count": "{count, plural, one {Billed} other {Billeder}} kan ikke blive tilføjet til album",
|
"assets_cannot_be_added_to_album_count": "{count, plural, one {Billed} other {Billeder}} kan ikke blive tilføjet til album",
|
||||||
"assets_count": "{count, plural, one {# mediefil} other {# mediefiler}}",
|
"assets_count": "{count, plural, one {# mediefil} other {# mediefiler}}",
|
||||||
"assets_deleted_permanently": "{count} element(er) blev fjernet permanent",
|
"assets_deleted_permanently": "{count} element(er) blev fjernet permanent",
|
||||||
@@ -702,7 +702,6 @@
|
|||||||
"daily_title_text_date": "E, dd MMM",
|
"daily_title_text_date": "E, dd MMM",
|
||||||
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
||||||
"dark": "Mørk",
|
"dark": "Mørk",
|
||||||
"darkTheme": "Skift til mørkt tema",
|
|
||||||
"date_after": "Dato efter",
|
"date_after": "Dato efter",
|
||||||
"date_and_time": "Dato og klokkeslæt",
|
"date_and_time": "Dato og klokkeslæt",
|
||||||
"date_before": "Dato før",
|
"date_before": "Dato før",
|
||||||
|
|||||||
82
i18n/de.json
82
i18n/de.json
@@ -34,6 +34,7 @@
|
|||||||
"added_to_favorites_count": "{count, number} zu Favoriten hinzugefügt",
|
"added_to_favorites_count": "{count, number} zu Favoriten hinzugefügt",
|
||||||
"admin": {
|
"admin": {
|
||||||
"add_exclusion_pattern_description": "Ausschlussmuster hinzufügen. Platzhalter, wie *, **, und ? werden unterstützt. Um alle Dateien in einem Verzeichnis namens „Raw\" zu ignorieren, „**/Raw/**“ verwenden. Um alle Dateien zu ignorieren, die auf „.tif“ enden, „**/*.tif“ verwenden. Um einen absoluten Pfad zu ignorieren, „/pfad/zum/ignorieren/**“ verwenden.",
|
"add_exclusion_pattern_description": "Ausschlussmuster hinzufügen. Platzhalter, wie *, **, und ? werden unterstützt. Um alle Dateien in einem Verzeichnis namens „Raw\" zu ignorieren, „**/Raw/**“ verwenden. Um alle Dateien zu ignorieren, die auf „.tif“ enden, „**/*.tif“ verwenden. Um einen absoluten Pfad zu ignorieren, „/pfad/zum/ignorieren/**“ verwenden.",
|
||||||
|
"admin_user": "Administrator",
|
||||||
"asset_offline_description": "Diese Datei einer externen Bibliothek befindet sich nicht mehr auf der Festplatte und wurde in den Papierkorb verschoben. Falls die Datei innerhalb der Bibliothek verschoben wurde, überprüfe deine Zeitleiste auf die neue entsprechende Datei. Um diese Datei wiederherzustellen, stelle bitte sicher, dass Immich auf den unten stehenden Dateipfad zugreifen kann und scanne die Bibliothek.",
|
"asset_offline_description": "Diese Datei einer externen Bibliothek befindet sich nicht mehr auf der Festplatte und wurde in den Papierkorb verschoben. Falls die Datei innerhalb der Bibliothek verschoben wurde, überprüfe deine Zeitleiste auf die neue entsprechende Datei. Um diese Datei wiederherzustellen, stelle bitte sicher, dass Immich auf den unten stehenden Dateipfad zugreifen kann und scanne die Bibliothek.",
|
||||||
"authentication_settings": "Authentifizierungseinstellungen",
|
"authentication_settings": "Authentifizierungseinstellungen",
|
||||||
"authentication_settings_description": "Passwort-, OAuth- und sonstige Authentifizierungseinstellungen verwalten",
|
"authentication_settings_description": "Passwort-, OAuth- und sonstige Authentifizierungseinstellungen verwalten",
|
||||||
@@ -55,9 +56,9 @@
|
|||||||
"confirm_user_pin_code_reset": "Bist du sicher, dass du den PIN Code von {user} zurücksetzen möchtest?",
|
"confirm_user_pin_code_reset": "Bist du sicher, dass du den PIN Code von {user} zurücksetzen möchtest?",
|
||||||
"create_job": "Aufgabe erstellen",
|
"create_job": "Aufgabe erstellen",
|
||||||
"cron_expression": "Cron Zeitangabe",
|
"cron_expression": "Cron Zeitangabe",
|
||||||
"cron_expression_description": "Setze ein Intervall für die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z.B der <link>Crontab Guru</link>",
|
"cron_expression_description": "Setze ein Intervall für die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z. B. der <link>Crontab Guru</link>",
|
||||||
"cron_expression_presets": "Nützliche Zeitangaben für Cron",
|
"cron_expression_presets": "Nützliche Zeitangaben für Cron",
|
||||||
"disable_login": "Login deaktvieren",
|
"disable_login": "Login deaktivieren",
|
||||||
"duplicate_detection_job_description": "Diese Aufgabe führt das maschinelle Lernen für jede Datei aus, um Duplikate zu finden. Diese Aufgabe beruht auf der intelligenten Suche",
|
"duplicate_detection_job_description": "Diese Aufgabe führt das maschinelle Lernen für jede Datei aus, um Duplikate zu finden. Diese Aufgabe beruht auf der intelligenten Suche",
|
||||||
"exclusion_pattern_description": "Mit Ausschlussmustern können Dateien und Ordner beim Scannen Ihrer Bibliothek ignoriert werden. Dies ist nützlich, wenn du Ordner hast, die Dateien enthalten, die du nicht importieren möchtest, wie z. B. RAW-Dateien.",
|
"exclusion_pattern_description": "Mit Ausschlussmustern können Dateien und Ordner beim Scannen Ihrer Bibliothek ignoriert werden. Dies ist nützlich, wenn du Ordner hast, die Dateien enthalten, die du nicht importieren möchtest, wie z. B. RAW-Dateien.",
|
||||||
"external_library_management": "Verwaltung externer Bibliotheken",
|
"external_library_management": "Verwaltung externer Bibliotheken",
|
||||||
@@ -165,6 +166,20 @@
|
|||||||
"metadata_settings_description": "Metadaten-Einstellungen verwalten",
|
"metadata_settings_description": "Metadaten-Einstellungen verwalten",
|
||||||
"migration_job": "Migration",
|
"migration_job": "Migration",
|
||||||
"migration_job_description": "Diese Aufgabe migriert Miniaturansichten für Dateien und Gesichter in die neueste Ordnerstruktur",
|
"migration_job_description": "Diese Aufgabe migriert Miniaturansichten für Dateien und Gesichter in die neueste Ordnerstruktur",
|
||||||
|
"nightly_tasks_cluster_faces_setting_description": "Gesichtsidentifikation auf neu erkannten Gesichtern ausführen",
|
||||||
|
"nightly_tasks_cluster_new_faces_setting": "Neue Gesichter gruppieren",
|
||||||
|
"nightly_tasks_database_cleanup_setting": "Datenbankbereinigungs-Aufgaben",
|
||||||
|
"nightly_tasks_database_cleanup_setting_description": "Alte, abgelaufene Daten aus der Datenbank bereinigen",
|
||||||
|
"nightly_tasks_generate_memories_setting": "Erinnerungen generieren",
|
||||||
|
"nightly_tasks_generate_memories_setting_description": "Neue Erinnerungen aus Dateien erstellen",
|
||||||
|
"nightly_tasks_missing_thumbnails_setting": "Fehlende Miniaturansichten generieren",
|
||||||
|
"nightly_tasks_missing_thumbnails_setting_description": "Dateien ohne Miniaturansicht in die Warteschlange zur Miniaturansicht-Generierung hinzufügen",
|
||||||
|
"nightly_tasks_settings": "Einstellungen für nächtliche Aufgaben",
|
||||||
|
"nightly_tasks_settings_description": "Nächtliche Aufgaben verwalten",
|
||||||
|
"nightly_tasks_start_time_setting": "Startzeit",
|
||||||
|
"nightly_tasks_start_time_setting_description": "Die Zeit, zu der der Server mit der Ausführung der nächtlichen Aufgaben beginnt",
|
||||||
|
"nightly_tasks_sync_quota_usage_setting": "Kontingentnutzung synchronisieren",
|
||||||
|
"nightly_tasks_sync_quota_usage_setting_description": "Benutzerspeicherkontingent basierend auf der aktuellen Nutzung aktualisieren",
|
||||||
"no_paths_added": "Keine Pfade hinzugefügt",
|
"no_paths_added": "Keine Pfade hinzugefügt",
|
||||||
"no_pattern_added": "Kein Ausschlussmuster hinzugefügt",
|
"no_pattern_added": "Kein Ausschlussmuster hinzugefügt",
|
||||||
"note_apply_storage_label_previous_assets": "Hinweis: Um den Speicherpfad auf die vorher hochgeladenen Dateien anzuwenden, starte den",
|
"note_apply_storage_label_previous_assets": "Hinweis: Um den Speicherpfad auf die vorher hochgeladenen Dateien anzuwenden, starte den",
|
||||||
@@ -195,6 +210,8 @@
|
|||||||
"oauth_mobile_redirect_uri": "Mobile Umleitungs-URI",
|
"oauth_mobile_redirect_uri": "Mobile Umleitungs-URI",
|
||||||
"oauth_mobile_redirect_uri_override": "Mobile Umleitungs-URI überschreiben",
|
"oauth_mobile_redirect_uri_override": "Mobile Umleitungs-URI überschreiben",
|
||||||
"oauth_mobile_redirect_uri_override_description": "Einschalten, wenn der OAuth-Anbieter keine mobile URI wie ''{callback}'' erlaubt",
|
"oauth_mobile_redirect_uri_override_description": "Einschalten, wenn der OAuth-Anbieter keine mobile URI wie ''{callback}'' erlaubt",
|
||||||
|
"oauth_role_claim": "Rollen-Claim",
|
||||||
|
"oauth_role_claim_description": "Gewähre automatisch Admin-Zugriff basierend auf dem Vorhandensein dieses Claims. Der Claim kann entweder 'user' oder 'admin' sein.",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "OAuth-Anmeldeeinstellungen verwalten",
|
"oauth_settings_description": "OAuth-Anmeldeeinstellungen verwalten",
|
||||||
"oauth_settings_more_details": "Weitere Informationen zu dieser Funktion findest du in der <link>Dokumentation</link>.",
|
"oauth_settings_more_details": "Weitere Informationen zu dieser Funktion findest du in der <link>Dokumentation</link>.",
|
||||||
@@ -203,7 +220,7 @@
|
|||||||
"oauth_storage_quota_claim": "Speicherkontingentangabe",
|
"oauth_storage_quota_claim": "Speicherkontingentangabe",
|
||||||
"oauth_storage_quota_claim_description": "Setzen Sie das Speicherkontingent des Benutzers automatisch auf den angegebenen Wert.",
|
"oauth_storage_quota_claim_description": "Setzen Sie das Speicherkontingent des Benutzers automatisch auf den angegebenen Wert.",
|
||||||
"oauth_storage_quota_default": "Standard-Speicherplatzkontingent (GiB)",
|
"oauth_storage_quota_default": "Standard-Speicherplatzkontingent (GiB)",
|
||||||
"oauth_storage_quota_default_description": "Kontingent in GiB, das verwendet werden soll, wenn keines übermittelt wird (gib 0 für ein unbegrenztes Kontingent ein).",
|
"oauth_storage_quota_default_description": "Kontingent in GiB, das verwendet werden soll, wenn keines übermittelt wird.",
|
||||||
"oauth_timeout": "Zeitüberschreitung bei Anfrage",
|
"oauth_timeout": "Zeitüberschreitung bei Anfrage",
|
||||||
"oauth_timeout_description": "Zeitüberschreitung für Anfragen in Millisekunden",
|
"oauth_timeout_description": "Zeitüberschreitung für Anfragen in Millisekunden",
|
||||||
"password_enable_description": "Mit E-Mail und Passwort anmelden",
|
"password_enable_description": "Mit E-Mail und Passwort anmelden",
|
||||||
@@ -243,7 +260,7 @@
|
|||||||
"storage_template_migration_info": "Die Speichervorlage wird alle Dateierweiterungen in Kleinbuchstaben umwandeln. Vorlagenänderungen gelten nur für neue Dateien. Um die Vorlage rückwirkend auf bereits hochgeladene Assets anzuwenden, führe den <link>{job}</link> aus.",
|
"storage_template_migration_info": "Die Speichervorlage wird alle Dateierweiterungen in Kleinbuchstaben umwandeln. Vorlagenänderungen gelten nur für neue Dateien. Um die Vorlage rückwirkend auf bereits hochgeladene Assets anzuwenden, führe den <link>{job}</link> aus.",
|
||||||
"storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe",
|
"storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe",
|
||||||
"storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter <template-link>Speichervorlage</template-link> und dessen <implications-link>Implikationen</implications-link>",
|
"storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter <template-link>Speichervorlage</template-link> und dessen <implications-link>Implikationen</implications-link>",
|
||||||
"storage_template_onboarding_description": "Wenn aktiviert, sortiert diese Funktion Dateien automatisch basierend auf einer benutzerdefinierten Vorlage. Aufgrund von Stabilitätsproblemen ist die Funktion standardmäßig deaktiviert. Weitere Informationen findest du in der <link>Dokumentation</link>.",
|
"storage_template_onboarding_description_v2": "Wenn aktiviert, werden Dateien automatisch nach einer benutzerdefinierten Vorlage organisiert. Für mehr Informationen siehe die <link>Dokumentation</link>.",
|
||||||
"storage_template_path_length": "Ungefähres Pfadlängen-Limit: <b>{length, number}</b>/{limit, number}",
|
"storage_template_path_length": "Ungefähres Pfadlängen-Limit: <b>{length, number}</b>/{limit, number}",
|
||||||
"storage_template_settings": "Speichervorlage",
|
"storage_template_settings": "Speichervorlage",
|
||||||
"storage_template_settings_description": "Die Ordnerstruktur und den Dateinamen der hochgeladenen Datei verwalten",
|
"storage_template_settings_description": "Die Ordnerstruktur und den Dateinamen der hochgeladenen Datei verwalten",
|
||||||
@@ -356,10 +373,12 @@
|
|||||||
"admin_password": "Administrator Passwort",
|
"admin_password": "Administrator Passwort",
|
||||||
"administration": "Verwaltung",
|
"administration": "Verwaltung",
|
||||||
"advanced": "Erweitert",
|
"advanced": "Erweitert",
|
||||||
|
"advanced_settings_beta_timeline_subtitle": "Probier die neue App-Erfahrung aus",
|
||||||
|
"advanced_settings_beta_timeline_title": "Beta-Timeline",
|
||||||
"advanced_settings_enable_alternate_media_filter_subtitle": "Verwende diese Option, um Medien während der Synchronisierung nach anderen Kriterien zu filtern. Versuchen dies nur, wenn Probleme mit der Erkennung aller Alben durch die App auftreten.",
|
"advanced_settings_enable_alternate_media_filter_subtitle": "Verwende diese Option, um Medien während der Synchronisierung nach anderen Kriterien zu filtern. Versuchen dies nur, wenn Probleme mit der Erkennung aller Alben durch die App auftreten.",
|
||||||
"advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTELL] Benutze alternativen Filter für Synchronisierung der Gerätealben",
|
"advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTELL] Benutze alternativen Filter für Synchronisierung der Gerätealben",
|
||||||
"advanced_settings_log_level_title": "Log-Level: {level}",
|
"advanced_settings_log_level_title": "Log-Level: {level}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von Miniaturbildern direkt aus dem Gerät. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.",
|
"advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von lokalen Vorschaubildern. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.",
|
||||||
"advanced_settings_prefer_remote_title": "Server-Bilder bevorzugen",
|
"advanced_settings_prefer_remote_title": "Server-Bilder bevorzugen",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Definiere einen Proxy-Header, den Immich bei jeder Netzwerkanfrage mitschicken soll",
|
"advanced_settings_proxy_headers_subtitle": "Definiere einen Proxy-Header, den Immich bei jeder Netzwerkanfrage mitschicken soll",
|
||||||
"advanced_settings_proxy_headers_title": "Proxy-Headers",
|
"advanced_settings_proxy_headers_title": "Proxy-Headers",
|
||||||
@@ -387,6 +406,7 @@
|
|||||||
"album_options": "Albumoptionen",
|
"album_options": "Albumoptionen",
|
||||||
"album_remove_user": "Nutzer entfernen?",
|
"album_remove_user": "Nutzer entfernen?",
|
||||||
"album_remove_user_confirmation": "Bist du sicher, dass du {user} entfernen willst?",
|
"album_remove_user_confirmation": "Bist du sicher, dass du {user} entfernen willst?",
|
||||||
|
"album_search_not_found": "Keine Alben gefunden, die zur Suche passen",
|
||||||
"album_share_no_users": "Es sieht so aus, als hättest du dieses Album mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.",
|
"album_share_no_users": "Es sieht so aus, als hättest du dieses Album mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.",
|
||||||
"album_updated": "Album aktualisiert",
|
"album_updated": "Album aktualisiert",
|
||||||
"album_updated_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn ein freigegebenes Album neue Dateien enthält",
|
"album_updated_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn ein freigegebenes Album neue Dateien enthält",
|
||||||
@@ -406,6 +426,7 @@
|
|||||||
"albums_default_sort_order": "Standard Album Sortierung",
|
"albums_default_sort_order": "Standard Album Sortierung",
|
||||||
"albums_default_sort_order_description": "Sortierreihenfolge der Dateien bei der Erstellung neuer Alben.",
|
"albums_default_sort_order_description": "Sortierreihenfolge der Dateien bei der Erstellung neuer Alben.",
|
||||||
"albums_feature_description": "Sammlung an Alben die mit anderen Benutzern geteilt werden können.",
|
"albums_feature_description": "Sammlung an Alben die mit anderen Benutzern geteilt werden können.",
|
||||||
|
"albums_on_device_count": "Alben auf dem Gerät ({count})",
|
||||||
"all": "Alle",
|
"all": "Alle",
|
||||||
"all_albums": "Alle Alben",
|
"all_albums": "Alle Alben",
|
||||||
"all_people": "Alle Personen",
|
"all_people": "Alle Personen",
|
||||||
@@ -426,6 +447,7 @@
|
|||||||
"app_settings": "App-Einstellungen",
|
"app_settings": "App-Einstellungen",
|
||||||
"appears_in": "Erscheint in",
|
"appears_in": "Erscheint in",
|
||||||
"archive": "Archiv",
|
"archive": "Archiv",
|
||||||
|
"archive_action_prompt": "{count} zum Archiv hinzugefügt",
|
||||||
"archive_or_unarchive_photo": "Foto archivieren bzw. Archivierung aufheben",
|
"archive_or_unarchive_photo": "Foto archivieren bzw. Archivierung aufheben",
|
||||||
"archive_page_no_archived_assets": "Keine archivierten Inhalte gefunden",
|
"archive_page_no_archived_assets": "Keine archivierten Inhalte gefunden",
|
||||||
"archive_page_title": "Archiv ({count})",
|
"archive_page_title": "Archiv ({count})",
|
||||||
@@ -463,7 +485,6 @@
|
|||||||
"assets": "Dateien",
|
"assets": "Dateien",
|
||||||
"assets_added_count": "{count, plural, one {# Datei} other {# Dateien}} hinzugefügt",
|
"assets_added_count": "{count, plural, one {# Datei} other {# Dateien}} hinzugefügt",
|
||||||
"assets_added_to_album_count": "{count, plural, one {# Datei} other {# Dateien}} zum Album hinzugefügt",
|
"assets_added_to_album_count": "{count, plural, one {# Datei} other {# Dateien}} zum Album hinzugefügt",
|
||||||
"assets_added_to_name_count": "{count, plural, one {# Element} other {# Elemente}} zu {hasName, select, true {<b>{name}</b>} other {neuem Album}} hinzugefügt",
|
|
||||||
"assets_cannot_be_added_to_album_count": "{count, plural, one {Datei kann}other {Dateien können}} nicht zum Album hinzugefügt werden",
|
"assets_cannot_be_added_to_album_count": "{count, plural, one {Datei kann}other {Dateien können}} nicht zum Album hinzugefügt werden",
|
||||||
"assets_count": "{count, plural, one {# Datei} other {# Dateien}}",
|
"assets_count": "{count, plural, one {# Datei} other {# Dateien}}",
|
||||||
"assets_deleted_permanently": "{count} Element(e) permanent gelöscht",
|
"assets_deleted_permanently": "{count} Element(e) permanent gelöscht",
|
||||||
@@ -552,6 +573,8 @@
|
|||||||
"backup_options_page_title": "Sicherungsoptionen",
|
"backup_options_page_title": "Sicherungsoptionen",
|
||||||
"backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund",
|
"backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund",
|
||||||
"backward": "Rückwärts",
|
"backward": "Rückwärts",
|
||||||
|
"beta_sync": "Status des Beta Sync",
|
||||||
|
"beta_sync_subtitle": "Verwalte das neue Synchronisierungssystem",
|
||||||
"biometric_auth_enabled": "Biometrische Authentifizierung aktiviert",
|
"biometric_auth_enabled": "Biometrische Authentifizierung aktiviert",
|
||||||
"biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen",
|
"biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen",
|
||||||
"biometric_no_options": "Keine biometrischen Optionen verfügbar",
|
"biometric_no_options": "Keine biometrischen Optionen verfügbar",
|
||||||
@@ -586,6 +609,7 @@
|
|||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
"cancel_search": "Suche abbrechen",
|
"cancel_search": "Suche abbrechen",
|
||||||
"canceled": "Abgebrochen",
|
"canceled": "Abgebrochen",
|
||||||
|
"canceling": "Abbrechen",
|
||||||
"cannot_merge_people": "Personen können nicht zusammengeführt werden",
|
"cannot_merge_people": "Personen können nicht zusammengeführt werden",
|
||||||
"cannot_undo_this_action": "Diese Aktion kann nicht rückgängig gemacht werden!",
|
"cannot_undo_this_action": "Diese Aktion kann nicht rückgängig gemacht werden!",
|
||||||
"cannot_update_the_description": "Beschreibung kann nicht aktualisiert werden",
|
"cannot_update_the_description": "Beschreibung kann nicht aktualisiert werden",
|
||||||
@@ -702,7 +726,7 @@
|
|||||||
"daily_title_text_date": "E, dd MMM",
|
"daily_title_text_date": "E, dd MMM",
|
||||||
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
||||||
"dark": "Dunkel",
|
"dark": "Dunkel",
|
||||||
"darkTheme": "Dunkles Theme umschalten",
|
"dark_theme": "Dunkle Ansicht umschalten",
|
||||||
"date_after": "Datum nach",
|
"date_after": "Datum nach",
|
||||||
"date_and_time": "Datum und Zeit",
|
"date_and_time": "Datum und Zeit",
|
||||||
"date_before": "Datum vor",
|
"date_before": "Datum vor",
|
||||||
@@ -718,6 +742,7 @@
|
|||||||
"default_locale": "Standard-Sprache",
|
"default_locale": "Standard-Sprache",
|
||||||
"default_locale_description": "Datumsangaben und Zahlen basierend auf dem Gebietsschema des Browsers formatieren",
|
"default_locale_description": "Datumsangaben und Zahlen basierend auf dem Gebietsschema des Browsers formatieren",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
|
"delete_action_prompt": "{count} endgültig gelöscht",
|
||||||
"delete_album": "Album löschen",
|
"delete_album": "Album löschen",
|
||||||
"delete_api_key_prompt": "Bist du sicher, dass du diesen API-Schlüssel löschen willst?",
|
"delete_api_key_prompt": "Bist du sicher, dass du diesen API-Schlüssel löschen willst?",
|
||||||
"delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt",
|
"delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt",
|
||||||
@@ -731,6 +756,7 @@
|
|||||||
"delete_key": "Schlüssel löschen",
|
"delete_key": "Schlüssel löschen",
|
||||||
"delete_library": "Bibliothek löschen",
|
"delete_library": "Bibliothek löschen",
|
||||||
"delete_link": "Link löschen",
|
"delete_link": "Link löschen",
|
||||||
|
"delete_local_action_prompt": "{count} lokal gelöscht",
|
||||||
"delete_local_dialog_ok_backed_up_only": "Nur gesicherte Inhalte löschen",
|
"delete_local_dialog_ok_backed_up_only": "Nur gesicherte Inhalte löschen",
|
||||||
"delete_local_dialog_ok_force": "Trotzdem löschen",
|
"delete_local_dialog_ok_force": "Trotzdem löschen",
|
||||||
"delete_others": "Andere löschen",
|
"delete_others": "Andere löschen",
|
||||||
@@ -744,6 +770,7 @@
|
|||||||
"description": "Beschreibung",
|
"description": "Beschreibung",
|
||||||
"description_input_hint_text": "Beschreibung hinzufügen...",
|
"description_input_hint_text": "Beschreibung hinzufügen...",
|
||||||
"description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log für mehr Details nachsehen",
|
"description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log für mehr Details nachsehen",
|
||||||
|
"deselect_all": "Alle abwählen",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
"direction": "Richtung",
|
"direction": "Richtung",
|
||||||
"disabled": "Deaktiviert",
|
"disabled": "Deaktiviert",
|
||||||
@@ -761,6 +788,7 @@
|
|||||||
"documentation": "Dokumentation",
|
"documentation": "Dokumentation",
|
||||||
"done": "Fertig",
|
"done": "Fertig",
|
||||||
"download": "Herunterladen",
|
"download": "Herunterladen",
|
||||||
|
"download_action_prompt": "Herunterladen von {count} Dateien",
|
||||||
"download_canceled": "Download abgebrochen",
|
"download_canceled": "Download abgebrochen",
|
||||||
"download_complete": "Download vollständig",
|
"download_complete": "Download vollständig",
|
||||||
"download_enqueue": "Download in die Warteschlange gesetzt",
|
"download_enqueue": "Download in die Warteschlange gesetzt",
|
||||||
@@ -798,6 +826,7 @@
|
|||||||
"edit_key": "Schlüssel bearbeiten",
|
"edit_key": "Schlüssel bearbeiten",
|
||||||
"edit_link": "Link bearbeiten",
|
"edit_link": "Link bearbeiten",
|
||||||
"edit_location": "Standort bearbeiten",
|
"edit_location": "Standort bearbeiten",
|
||||||
|
"edit_location_action_prompt": "{count} Geolokationen angepasst",
|
||||||
"edit_location_dialog_title": "Ort bearbeiten",
|
"edit_location_dialog_title": "Ort bearbeiten",
|
||||||
"edit_name": "Name bearbeiten",
|
"edit_name": "Name bearbeiten",
|
||||||
"edit_people": "Personen bearbeiten",
|
"edit_people": "Personen bearbeiten",
|
||||||
@@ -816,6 +845,7 @@
|
|||||||
"empty_trash": "Papierkorb leeren",
|
"empty_trash": "Papierkorb leeren",
|
||||||
"empty_trash_confirmation": "Bist du sicher, dass du den Papierkorb leeren willst?\nDies entfernt alle Dateien im Papierkorb endgültig aus Immich und kann nicht rückgängig gemacht werden!",
|
"empty_trash_confirmation": "Bist du sicher, dass du den Papierkorb leeren willst?\nDies entfernt alle Dateien im Papierkorb endgültig aus Immich und kann nicht rückgängig gemacht werden!",
|
||||||
"enable": "Aktivieren",
|
"enable": "Aktivieren",
|
||||||
|
"enable_backup": "Sicherung aktivieren",
|
||||||
"enable_biometric_auth_description": "Gib deinen PIN Code ein, um die biometrische Authentifizierung zu aktivieren",
|
"enable_biometric_auth_description": "Gib deinen PIN Code ein, um die biometrische Authentifizierung zu aktivieren",
|
||||||
"enabled": "Aktiviert",
|
"enabled": "Aktiviert",
|
||||||
"end_date": "Enddatum",
|
"end_date": "Enddatum",
|
||||||
@@ -983,6 +1013,7 @@
|
|||||||
"failed_to_load_assets": "Laden der Assets fehlgeschlagen",
|
"failed_to_load_assets": "Laden der Assets fehlgeschlagen",
|
||||||
"failed_to_load_folder": "Fehler beim Laden des Ordners",
|
"failed_to_load_folder": "Fehler beim Laden des Ordners",
|
||||||
"favorite": "Favorit",
|
"favorite": "Favorit",
|
||||||
|
"favorite_action_prompt": "{count} zu den Favoriten hinzugefügt",
|
||||||
"favorite_or_unfavorite_photo": "Favorisiertes oder nicht favorisiertes Foto",
|
"favorite_or_unfavorite_photo": "Favorisiertes oder nicht favorisiertes Foto",
|
||||||
"favorites": "Favoriten",
|
"favorites": "Favoriten",
|
||||||
"favorites_page_no_favorites": "Keine favorisierten Inhalte gefunden",
|
"favorites_page_no_favorites": "Keine favorisierten Inhalte gefunden",
|
||||||
@@ -1022,6 +1053,9 @@
|
|||||||
"haptic_feedback_switch": "Haptisches Feedback aktivieren",
|
"haptic_feedback_switch": "Haptisches Feedback aktivieren",
|
||||||
"haptic_feedback_title": "Haptisches Feedback",
|
"haptic_feedback_title": "Haptisches Feedback",
|
||||||
"has_quota": "Kontingent",
|
"has_quota": "Kontingent",
|
||||||
|
"hash_asset": "Dateihash",
|
||||||
|
"hashed_assets": "Gehashte Dateien",
|
||||||
|
"hashing": "Hashen",
|
||||||
"header_settings_add_header_tip": "Header hinzufügen",
|
"header_settings_add_header_tip": "Header hinzufügen",
|
||||||
"header_settings_field_validator_msg": "Der Wert darf nicht leer sein",
|
"header_settings_field_validator_msg": "Der Wert darf nicht leer sein",
|
||||||
"header_settings_header_name_input": "Header-Name",
|
"header_settings_header_name_input": "Header-Name",
|
||||||
@@ -1054,6 +1088,7 @@
|
|||||||
"host": "Host",
|
"host": "Host",
|
||||||
"hour": "Stunde",
|
"hour": "Stunde",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
|
"idle": "Untätig",
|
||||||
"ignore_icloud_photos": "iCloud Fotos ignorieren",
|
"ignore_icloud_photos": "iCloud Fotos ignorieren",
|
||||||
"ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen",
|
"ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen",
|
||||||
"image": "Bild",
|
"image": "Bild",
|
||||||
@@ -1126,6 +1161,7 @@
|
|||||||
"library_page_sort_created": "Zuletzt erstellt",
|
"library_page_sort_created": "Zuletzt erstellt",
|
||||||
"library_page_sort_last_modified": "Zuletzt bearbeitet",
|
"library_page_sort_last_modified": "Zuletzt bearbeitet",
|
||||||
"library_page_sort_title": "Titel des Albums",
|
"library_page_sort_title": "Titel des Albums",
|
||||||
|
"licenses": "Lizenzen",
|
||||||
"light": "Hell",
|
"light": "Hell",
|
||||||
"like_deleted": "Like gelöscht",
|
"like_deleted": "Like gelöscht",
|
||||||
"link_motion_video": "Bewegungsvideo verknüpfen",
|
"link_motion_video": "Bewegungsvideo verknüpfen",
|
||||||
@@ -1135,7 +1171,9 @@
|
|||||||
"list": "Liste",
|
"list": "Liste",
|
||||||
"loading": "Laden",
|
"loading": "Laden",
|
||||||
"loading_search_results_failed": "Laden von Suchergebnissen fehlgeschlagen",
|
"loading_search_results_failed": "Laden von Suchergebnissen fehlgeschlagen",
|
||||||
|
"local": "Lokal",
|
||||||
"local_asset_cast_failed": "Eine Datei, die nicht auf den Server hochgeladen wurde, kann nicht gecastet werden",
|
"local_asset_cast_failed": "Eine Datei, die nicht auf den Server hochgeladen wurde, kann nicht gecastet werden",
|
||||||
|
"local_assets": "Lokale Dateien",
|
||||||
"local_network": "Lokales Netzwerk",
|
"local_network": "Lokales Netzwerk",
|
||||||
"local_network_sheet_info": "Die App stellt über diese URL eine Verbindung zum Server her, wenn sie das angegebene WLAN-Netzwerk verwendet",
|
"local_network_sheet_info": "Die App stellt über diese URL eine Verbindung zum Server her, wenn sie das angegebene WLAN-Netzwerk verwendet",
|
||||||
"location_permission": "Standort Genehmigung",
|
"location_permission": "Standort Genehmigung",
|
||||||
@@ -1149,6 +1187,7 @@
|
|||||||
"locked_folder": "Gesperrter Ordner",
|
"locked_folder": "Gesperrter Ordner",
|
||||||
"log_out": "Abmelden",
|
"log_out": "Abmelden",
|
||||||
"log_out_all_devices": "Alle Geräte abmelden",
|
"log_out_all_devices": "Alle Geräte abmelden",
|
||||||
|
"logged_in_as": "Angemeldet als {user}",
|
||||||
"logged_out_all_devices": "Alle Geräte abgemeldet",
|
"logged_out_all_devices": "Alle Geräte abgemeldet",
|
||||||
"logged_out_device": "Gerät abgemeldet",
|
"logged_out_device": "Gerät abgemeldet",
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
@@ -1244,6 +1283,7 @@
|
|||||||
"more": "Mehr",
|
"more": "Mehr",
|
||||||
"move": "Verschieben",
|
"move": "Verschieben",
|
||||||
"move_off_locked_folder": "Aus dem gesperrten Ordner verschieben",
|
"move_off_locked_folder": "Aus dem gesperrten Ordner verschieben",
|
||||||
|
"move_to_lock_folder_action_prompt": "{count} zum gesperrten Ordner hinzugefügt",
|
||||||
"move_to_locked_folder": "In den gesperrten Ordner verschieben",
|
"move_to_locked_folder": "In den gesperrten Ordner verschieben",
|
||||||
"move_to_locked_folder_confirmation": "Diese Fotos und Videos werden aus allen Alben entfernt und können nur noch im gesperrten Ordner angezeigt werden",
|
"move_to_locked_folder_confirmation": "Diese Fotos und Videos werden aus allen Alben entfernt und können nur noch im gesperrten Ordner angezeigt werden",
|
||||||
"moved_to_archive": "{count, plural, one {# Datei} other {# Dateien}} archiviert",
|
"moved_to_archive": "{count, plural, one {# Datei} other {# Dateien}} archiviert",
|
||||||
@@ -1290,6 +1330,7 @@
|
|||||||
"no_results": "Keine Ergebnisse",
|
"no_results": "Keine Ergebnisse",
|
||||||
"no_results_description": "Versuche es mit einem Synonym oder einem allgemeineren Stichwort",
|
"no_results_description": "Versuche es mit einem Synonym oder einem allgemeineren Stichwort",
|
||||||
"no_shared_albums_message": "Erstelle ein Album, um Fotos und Videos mit Personen in deinem Netzwerk zu teilen",
|
"no_shared_albums_message": "Erstelle ein Album, um Fotos und Videos mit Personen in deinem Netzwerk zu teilen",
|
||||||
|
"no_uploads_in_progress": "Kein Upload in Bearbeitung",
|
||||||
"not_in_any_album": "In keinem Album",
|
"not_in_any_album": "In keinem Album",
|
||||||
"not_selected": "Nicht ausgewählt",
|
"not_selected": "Nicht ausgewählt",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den",
|
"note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den",
|
||||||
@@ -1327,6 +1368,7 @@
|
|||||||
"original": "Original",
|
"original": "Original",
|
||||||
"other": "Sonstiges",
|
"other": "Sonstiges",
|
||||||
"other_devices": "Andere Geräte",
|
"other_devices": "Andere Geräte",
|
||||||
|
"other_entities": "Andere Entitäten",
|
||||||
"other_variables": "Sonstige Variablen",
|
"other_variables": "Sonstige Variablen",
|
||||||
"owned": "Eigenes",
|
"owned": "Eigenes",
|
||||||
"owner": "Besitzer",
|
"owner": "Besitzer",
|
||||||
@@ -1458,6 +1500,7 @@
|
|||||||
"purchase_server_description_2": "Unterstützerstatus",
|
"purchase_server_description_2": "Unterstützerstatus",
|
||||||
"purchase_server_title": "Server",
|
"purchase_server_title": "Server",
|
||||||
"purchase_settings_server_activated": "Der Server-Produktschlüssel wird durch den Administrator verwaltet",
|
"purchase_settings_server_activated": "Der Server-Produktschlüssel wird durch den Administrator verwaltet",
|
||||||
|
"queue_status": "Warteschlange {count}/{total}",
|
||||||
"rating": "Bewertung",
|
"rating": "Bewertung",
|
||||||
"rating_clear": "Bewertung löschen",
|
"rating_clear": "Bewertung löschen",
|
||||||
"rating_count": "{count, plural, one {# Stern} other {# Sterne}}",
|
"rating_count": "{count, plural, one {# Stern} other {# Sterne}}",
|
||||||
@@ -1486,6 +1529,8 @@
|
|||||||
"refreshing_faces": "Gesichter werden aktualisiert",
|
"refreshing_faces": "Gesichter werden aktualisiert",
|
||||||
"refreshing_metadata": "Metadaten werden aktualisiert",
|
"refreshing_metadata": "Metadaten werden aktualisiert",
|
||||||
"regenerating_thumbnails": "Miniaturansichten werden neu erstellt",
|
"regenerating_thumbnails": "Miniaturansichten werden neu erstellt",
|
||||||
|
"remote": "Entfernt",
|
||||||
|
"remote_assets": "Entfernte Dateien",
|
||||||
"remove": "Entfernen",
|
"remove": "Entfernen",
|
||||||
"remove_assets_album_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} aus dem Album entfernen willst?",
|
"remove_assets_album_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} aus dem Album entfernen willst?",
|
||||||
"remove_assets_shared_link_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} von diesem geteilten Link entfernen willst?",
|
"remove_assets_shared_link_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} von diesem geteilten Link entfernen willst?",
|
||||||
@@ -1493,7 +1538,9 @@
|
|||||||
"remove_custom_date_range": "Benutzerdefinierten Datumsbereich entfernen",
|
"remove_custom_date_range": "Benutzerdefinierten Datumsbereich entfernen",
|
||||||
"remove_deleted_assets": "Offline-Dateien entfernen",
|
"remove_deleted_assets": "Offline-Dateien entfernen",
|
||||||
"remove_from_album": "Aus Album entfernen",
|
"remove_from_album": "Aus Album entfernen",
|
||||||
|
"remove_from_album_action_prompt": "{count} vom Album entfernt",
|
||||||
"remove_from_favorites": "Aus Favoriten entfernen",
|
"remove_from_favorites": "Aus Favoriten entfernen",
|
||||||
|
"remove_from_lock_folder_action_prompt": "{count} aus dem gesperrten Ordner entfernt",
|
||||||
"remove_from_locked_folder": "Aus gesperrtem Ordner entfernen",
|
"remove_from_locked_folder": "Aus gesperrtem Ordner entfernen",
|
||||||
"remove_from_locked_folder_confirmation": "Bist du sicher, dass du diese Fotos und Videos aus dem gesperrten Ordner entfernen möchtest? Sie werden wieder in deiner Bibliothek sichtbar sein.",
|
"remove_from_locked_folder_confirmation": "Bist du sicher, dass du diese Fotos und Videos aus dem gesperrten Ordner entfernen möchtest? Sie werden wieder in deiner Bibliothek sichtbar sein.",
|
||||||
"remove_from_shared_link": "Aus geteiltem Link entfernen",
|
"remove_from_shared_link": "Aus geteiltem Link entfernen",
|
||||||
@@ -1521,11 +1568,15 @@
|
|||||||
"reset_password": "Passwort zurücksetzen",
|
"reset_password": "Passwort zurücksetzen",
|
||||||
"reset_people_visibility": "Sichtbarkeit von Personen zurücksetzen",
|
"reset_people_visibility": "Sichtbarkeit von Personen zurücksetzen",
|
||||||
"reset_pin_code": "PIN Code zurücksetzen",
|
"reset_pin_code": "PIN Code zurücksetzen",
|
||||||
|
"reset_sqlite": "SQLite Datenbank zurücksetzen",
|
||||||
|
"reset_sqlite_confirmation": "Bist du sicher, dass du die SQLite-Datenbank zurücksetzen willst? Du musst dich ab- und wieder anmelden, um die Daten neu zu synchronisieren",
|
||||||
|
"reset_sqlite_success": "SQLite Datenbank erfolgreich zurückgesetzt",
|
||||||
"reset_to_default": "Auf Standard zurücksetzen",
|
"reset_to_default": "Auf Standard zurücksetzen",
|
||||||
"resolve_duplicates": "Duplikate entfernen",
|
"resolve_duplicates": "Duplikate entfernen",
|
||||||
"resolved_all_duplicates": "Alle Duplikate aufgelöst",
|
"resolved_all_duplicates": "Alle Duplikate aufgelöst",
|
||||||
"restore": "Wiederherstellen",
|
"restore": "Wiederherstellen",
|
||||||
"restore_all": "Alle wiederherstellen",
|
"restore_all": "Alle wiederherstellen",
|
||||||
|
"restore_trash_action_prompt": "{count} aus dem Papierkorb wiederhergestellt",
|
||||||
"restore_user": "Nutzer wiederherstellen",
|
"restore_user": "Nutzer wiederherstellen",
|
||||||
"restored_asset": "Datei wiederhergestellt",
|
"restored_asset": "Datei wiederhergestellt",
|
||||||
"resume": "Fortsetzen",
|
"resume": "Fortsetzen",
|
||||||
@@ -1534,6 +1585,7 @@
|
|||||||
"role": "Rolle",
|
"role": "Rolle",
|
||||||
"role_editor": "Bearbeiter",
|
"role_editor": "Bearbeiter",
|
||||||
"role_viewer": "Betrachter",
|
"role_viewer": "Betrachter",
|
||||||
|
"running": "Läuft",
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"save_to_gallery": "In Galerie speichern",
|
"save_to_gallery": "In Galerie speichern",
|
||||||
"saved_api_key": "API-Schlüssel wurde gespeichert",
|
"saved_api_key": "API-Schlüssel wurde gespeichert",
|
||||||
@@ -1606,6 +1658,7 @@
|
|||||||
"select_album_cover": "Album-Cover auswählen",
|
"select_album_cover": "Album-Cover auswählen",
|
||||||
"select_all": "Alles auswählen",
|
"select_all": "Alles auswählen",
|
||||||
"select_all_duplicates": "Alle Duplikate auswählen",
|
"select_all_duplicates": "Alle Duplikate auswählen",
|
||||||
|
"select_all_in": "Alle in {group} auswählen",
|
||||||
"select_avatar_color": "Avatar-Farbe auswählen",
|
"select_avatar_color": "Avatar-Farbe auswählen",
|
||||||
"select_face": "Gesicht auswählen",
|
"select_face": "Gesicht auswählen",
|
||||||
"select_featured_photo": "Anzeigebild auswählen",
|
"select_featured_photo": "Anzeigebild auswählen",
|
||||||
@@ -1664,6 +1717,7 @@
|
|||||||
"settings_saved": "Einstellungen gespeichert",
|
"settings_saved": "Einstellungen gespeichert",
|
||||||
"setup_pin_code": "Einen PIN Code festlegen",
|
"setup_pin_code": "Einen PIN Code festlegen",
|
||||||
"share": "Teilen",
|
"share": "Teilen",
|
||||||
|
"share_action_prompt": "{count} Dateien geteilt",
|
||||||
"share_add_photos": "Fotos hinzufügen",
|
"share_add_photos": "Fotos hinzufügen",
|
||||||
"share_assets_selected": "{count} ausgewählt",
|
"share_assets_selected": "{count} ausgewählt",
|
||||||
"share_dialog_preparing": "Vorbereiten...",
|
"share_dialog_preparing": "Vorbereiten...",
|
||||||
@@ -1765,6 +1819,7 @@
|
|||||||
"sort_title": "Titel",
|
"sort_title": "Titel",
|
||||||
"source": "Quellcode",
|
"source": "Quellcode",
|
||||||
"stack": "Stapel",
|
"stack": "Stapel",
|
||||||
|
"stack_action_prompt": "{count} gestapelt",
|
||||||
"stack_duplicates": "Duplikate stapeln",
|
"stack_duplicates": "Duplikate stapeln",
|
||||||
"stack_select_one_photo": "Hauptfoto für den Stapel auswählen",
|
"stack_select_one_photo": "Hauptfoto für den Stapel auswählen",
|
||||||
"stack_selected_photos": "Ausgewählte Fotos stapeln",
|
"stack_selected_photos": "Ausgewählte Fotos stapeln",
|
||||||
@@ -1784,6 +1839,7 @@
|
|||||||
"storage_quota": "Speicherplatz-Kontingent",
|
"storage_quota": "Speicherplatz-Kontingent",
|
||||||
"storage_usage": "{used} von {available} verwendet",
|
"storage_usage": "{used} von {available} verwendet",
|
||||||
"submit": "Bestätigen",
|
"submit": "Bestätigen",
|
||||||
|
"success": "Erfolgreich",
|
||||||
"suggestions": "Vorschläge",
|
"suggestions": "Vorschläge",
|
||||||
"sunrise_on_the_beach": "Sonnenaufgang am Strand",
|
"sunrise_on_the_beach": "Sonnenaufgang am Strand",
|
||||||
"support": "Unterstützung",
|
"support": "Unterstützung",
|
||||||
@@ -1793,6 +1849,8 @@
|
|||||||
"sync": "Synchronisieren",
|
"sync": "Synchronisieren",
|
||||||
"sync_albums": "Alben synchronisieren",
|
"sync_albums": "Alben synchronisieren",
|
||||||
"sync_albums_manual_subtitle": "Synchronisiere alle hochgeladenen Videos und Fotos in die ausgewählten Backup-Alben",
|
"sync_albums_manual_subtitle": "Synchronisiere alle hochgeladenen Videos und Fotos in die ausgewählten Backup-Alben",
|
||||||
|
"sync_local": "Lokal synchronisieren",
|
||||||
|
"sync_remote": "Entfernt synchronisieren",
|
||||||
"sync_upload_album_setting_subtitle": "Erstelle deine ausgewählten Alben in Immich und lade die Fotos und Videos dort hoch",
|
"sync_upload_album_setting_subtitle": "Erstelle deine ausgewählten Alben in Immich und lade die Fotos und Videos dort hoch",
|
||||||
"tag": "Tag",
|
"tag": "Tag",
|
||||||
"tag_assets": "Dateien taggen",
|
"tag_assets": "Dateien taggen",
|
||||||
@@ -1803,6 +1861,7 @@
|
|||||||
"tag_updated": "Tag aktualisiert: {tag}",
|
"tag_updated": "Tag aktualisiert: {tag}",
|
||||||
"tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged",
|
"tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged",
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
|
"tap_to_run_job": "Tippen um den Job zu starten",
|
||||||
"template": "Vorlage",
|
"template": "Vorlage",
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
"theme_selection": "Themenauswahl",
|
"theme_selection": "Themenauswahl",
|
||||||
@@ -1835,6 +1894,7 @@
|
|||||||
"total": "Gesamt",
|
"total": "Gesamt",
|
||||||
"total_usage": "Gesamtnutzung",
|
"total_usage": "Gesamtnutzung",
|
||||||
"trash": "Papierkorb",
|
"trash": "Papierkorb",
|
||||||
|
"trash_action_prompt": "{count} in den Papierkorb verschoben",
|
||||||
"trash_all": "Alle löschen",
|
"trash_all": "Alle löschen",
|
||||||
"trash_count": "Papierkorb {count, number}",
|
"trash_count": "Papierkorb {count, number}",
|
||||||
"trash_delete_asset": "Datei löschen/in den Papierkorb verschieben",
|
"trash_delete_asset": "Datei löschen/in den Papierkorb verschieben",
|
||||||
@@ -1852,9 +1912,11 @@
|
|||||||
"unable_to_change_pin_code": "PIN Code konnte nicht geändert werden",
|
"unable_to_change_pin_code": "PIN Code konnte nicht geändert werden",
|
||||||
"unable_to_setup_pin_code": "PIN Code konnte nicht festgelegt werden",
|
"unable_to_setup_pin_code": "PIN Code konnte nicht festgelegt werden",
|
||||||
"unarchive": "Entarchivieren",
|
"unarchive": "Entarchivieren",
|
||||||
|
"unarchive_action_prompt": "{count} aus dem Archiv entfernt",
|
||||||
"unarchived_count": "{count, plural, other {# entarchiviert}}",
|
"unarchived_count": "{count, plural, other {# entarchiviert}}",
|
||||||
"undo": "Rückgängig",
|
"undo": "Rückgängig",
|
||||||
"unfavorite": "Entfavorisieren",
|
"unfavorite": "Entfavorisieren",
|
||||||
|
"unfavorite_action_prompt": "{count} aus den Favoriten entfernt",
|
||||||
"unhide_person": "Person einblenden",
|
"unhide_person": "Person einblenden",
|
||||||
"unknown": "Unbekannt",
|
"unknown": "Unbekannt",
|
||||||
"unknown_country": "Unbekanntes Land",
|
"unknown_country": "Unbekanntes Land",
|
||||||
@@ -1870,13 +1932,17 @@
|
|||||||
"unsaved_change": "Ungespeicherte Änderung",
|
"unsaved_change": "Ungespeicherte Änderung",
|
||||||
"unselect_all": "Alles abwählen",
|
"unselect_all": "Alles abwählen",
|
||||||
"unselect_all_duplicates": "Alle Duplikate abwählen",
|
"unselect_all_duplicates": "Alle Duplikate abwählen",
|
||||||
|
"unselect_all_in": "Alle in {group} abwählen",
|
||||||
"unstack": "Entstapeln",
|
"unstack": "Entstapeln",
|
||||||
|
"unstack_action_prompt": "{count} entstapelt",
|
||||||
"unstacked_assets_count": "{count, plural, one {# Datei} other {# Dateien}} entstapelt",
|
"unstacked_assets_count": "{count, plural, one {# Datei} other {# Dateien}} entstapelt",
|
||||||
|
"untagged": "Ohne Tag",
|
||||||
"up_next": "Weiter",
|
"up_next": "Weiter",
|
||||||
"updated_at": "Aktualisiert",
|
"updated_at": "Aktualisiert",
|
||||||
"updated_password": "Passwort aktualisiert",
|
"updated_password": "Passwort aktualisiert",
|
||||||
"upload": "Hochladen",
|
"upload": "Hochladen",
|
||||||
"upload_concurrency": "Parallelität beim Hochladen",
|
"upload_concurrency": "Parallelität beim Hochladen",
|
||||||
|
"upload_details": "Upload Details",
|
||||||
"upload_dialog_info": "Willst du die ausgewählten Elemente auf dem Server sichern?",
|
"upload_dialog_info": "Willst du die ausgewählten Elemente auf dem Server sichern?",
|
||||||
"upload_dialog_title": "Element hochladen",
|
"upload_dialog_title": "Element hochladen",
|
||||||
"upload_errors": "Hochladen mit {count, plural, one {# Fehler} other {# Fehlern}} abgeschlossen, aktualisiere die Seite, um neu hochgeladene Dateien zu sehen.",
|
"upload_errors": "Hochladen mit {count, plural, one {# Fehler} other {# Fehlern}} abgeschlossen, aktualisiere die Seite, um neu hochgeladene Dateien zu sehen.",
|
||||||
@@ -1908,6 +1974,7 @@
|
|||||||
"user_usage_stats_description": "Statistiken zur Kontonutzung anzeigen",
|
"user_usage_stats_description": "Statistiken zur Kontonutzung anzeigen",
|
||||||
"username": "Nutzername",
|
"username": "Nutzername",
|
||||||
"users": "Benutzer",
|
"users": "Benutzer",
|
||||||
|
"users_added_to_album_count": "{count, plural, one {# Benutzer} other {# Benutzer}} zum Album hinzugefügt",
|
||||||
"utilities": "Hilfsmittel",
|
"utilities": "Hilfsmittel",
|
||||||
"validate": "Validieren",
|
"validate": "Validieren",
|
||||||
"validate_endpoint_error": "Bitte gib eine gültige URL ein",
|
"validate_endpoint_error": "Bitte gib eine gültige URL ein",
|
||||||
@@ -1926,6 +1993,7 @@
|
|||||||
"view_album": "Album anzeigen",
|
"view_album": "Album anzeigen",
|
||||||
"view_all": "Alles anzeigen",
|
"view_all": "Alles anzeigen",
|
||||||
"view_all_users": "Alle Nutzer anzeigen",
|
"view_all_users": "Alle Nutzer anzeigen",
|
||||||
|
"view_details": "Details ansehen",
|
||||||
"view_in_timeline": "In Zeitleiste anzeigen",
|
"view_in_timeline": "In Zeitleiste anzeigen",
|
||||||
"view_link": "Link anzeigen",
|
"view_link": "Link anzeigen",
|
||||||
"view_links": "Links anzeigen",
|
"view_links": "Links anzeigen",
|
||||||
|
|||||||
45
i18n/el.json
45
i18n/el.json
@@ -22,6 +22,7 @@
|
|||||||
"add_partner": "Προσθήκη συνεργάτη",
|
"add_partner": "Προσθήκη συνεργάτη",
|
||||||
"add_path": "Προσθήκη διαδρομής",
|
"add_path": "Προσθήκη διαδρομής",
|
||||||
"add_photos": "Προσθήκη φωτογραφιών",
|
"add_photos": "Προσθήκη φωτογραφιών",
|
||||||
|
"add_tag": "Προσθήκη ετικέτας",
|
||||||
"add_to": "Προσθήκη σε…",
|
"add_to": "Προσθήκη σε…",
|
||||||
"add_to_album": "Προσθήκη σε άλμπουμ",
|
"add_to_album": "Προσθήκη σε άλμπουμ",
|
||||||
"add_to_album_bottom_sheet_added": "Προστέθηκε στο {album}",
|
"add_to_album_bottom_sheet_added": "Προστέθηκε στο {album}",
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
"added_to_favorites_count": "Προστέθηκαν {count, number} στα αγαπημένα",
|
"added_to_favorites_count": "Προστέθηκαν {count, number} στα αγαπημένα",
|
||||||
"admin": {
|
"admin": {
|
||||||
"add_exclusion_pattern_description": "Προσθέστε μοτίβα αποκλεισμού. Υποστηρίζεται η επιλογή πολλών με *, **, και ?. Για να αγνοηθούν όλα τα αρχεία σε έναν φάκελο με το όνομα \"Raw\", χρησιμοποιήστε \"**/Raw/**\". Για να αγνοηθούν όλα τα αρχεία με κατάληξη \".tif\", χρησιμοποιήστε \"**/*.tif\". Για να αγνοηθεί μία απόλυτη διαδρομή, χρησιμοποιήστε \"/path/to/ignore/**\".",
|
"add_exclusion_pattern_description": "Προσθέστε μοτίβα αποκλεισμού. Υποστηρίζεται η επιλογή πολλών με *, **, και ?. Για να αγνοηθούν όλα τα αρχεία σε έναν φάκελο με το όνομα \"Raw\", χρησιμοποιήστε \"**/Raw/**\". Για να αγνοηθούν όλα τα αρχεία με κατάληξη \".tif\", χρησιμοποιήστε \"**/*.tif\". Για να αγνοηθεί μία απόλυτη διαδρομή, χρησιμοποιήστε \"/path/to/ignore/**\".",
|
||||||
|
"admin_user": "Διαχειριστής",
|
||||||
"asset_offline_description": "Αυτό το στοιχείο εξωτερικής βιβλιοθήκης δε βρίσκεται πλέον στο δίσκο και έχει μεταφερθεί στα απορρίμματα. Εάν το αρχείο έχει μετακινηθεί εντός της βιβλιοθήκης, ελέγξτε το χρονολόγιο φωτογραφιών σας για το νέο αντίστοιχο στοιχείο. Για να επαναφέρετε αυτό το στοιχείο, βεβαιωθείτε ότι το παρακάτω μονοπάτι αρχείου είναι προσβάσιμο από το Immich και σαρώστε τη βιβλιοθήκη.",
|
"asset_offline_description": "Αυτό το στοιχείο εξωτερικής βιβλιοθήκης δε βρίσκεται πλέον στο δίσκο και έχει μεταφερθεί στα απορρίμματα. Εάν το αρχείο έχει μετακινηθεί εντός της βιβλιοθήκης, ελέγξτε το χρονολόγιο φωτογραφιών σας για το νέο αντίστοιχο στοιχείο. Για να επαναφέρετε αυτό το στοιχείο, βεβαιωθείτε ότι το παρακάτω μονοπάτι αρχείου είναι προσβάσιμο από το Immich και σαρώστε τη βιβλιοθήκη.",
|
||||||
"authentication_settings": "Ρυθμίσεις Ελέγχου Ταυτότητας",
|
"authentication_settings": "Ρυθμίσεις Ελέγχου Ταυτότητας",
|
||||||
"authentication_settings_description": "Διαχείριση κωδικού πρόσβασης, OAuth και άλλων ρυθμίσεων ελέγχου ταυτότητας",
|
"authentication_settings_description": "Διαχείριση κωδικού πρόσβασης, OAuth και άλλων ρυθμίσεων ελέγχου ταυτότητας",
|
||||||
@@ -43,7 +45,7 @@
|
|||||||
"backup_database_enable_description": "Ενεργοποίηση dumps βάσης δεδομένων",
|
"backup_database_enable_description": "Ενεργοποίηση dumps βάσης δεδομένων",
|
||||||
"backup_keep_last_amount": "Ποσότητα προηγούμενων dumps που πρέπει να διατηρηθούν",
|
"backup_keep_last_amount": "Ποσότητα προηγούμενων dumps που πρέπει να διατηρηθούν",
|
||||||
"backup_settings": "Ρυθμίσεις dump βάσης δεδομένων",
|
"backup_settings": "Ρυθμίσεις dump βάσης δεδομένων",
|
||||||
"backup_settings_description": "Διαχείριση ρυθμίσεων dump της βάσης δεδομένων. Σημείωση: Αυτές οι εργασίες δεν παρακολουθούνται και δεν θα ειδοποιηθείτε για αποτυχία.",
|
"backup_settings_description": "Διαχείριση ρυθμίσεων dump της βάσης δεδομένων.",
|
||||||
"cleared_jobs": "Εκκαθαρίστηκαν οι εργασίες για: {job}",
|
"cleared_jobs": "Εκκαθαρίστηκαν οι εργασίες για: {job}",
|
||||||
"config_set_by_file": "Η παραμετροποίηση γίνεται, προς το παρόν, μέσω ενός αρχείου παραμέτρων",
|
"config_set_by_file": "Η παραμετροποίηση γίνεται, προς το παρόν, μέσω ενός αρχείου παραμέτρων",
|
||||||
"confirm_delete_library": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τη βιβλιοθήκη {library};",
|
"confirm_delete_library": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τη βιβλιοθήκη {library};",
|
||||||
@@ -169,7 +171,7 @@
|
|||||||
"note_apply_storage_label_previous_assets": "Σημείωση: Για να εφαρμοστεί η Ετικέτα Αποθήκευσης σε στοιχεία που είχαν αναρτηθεί παλαιότερα, εκτέλεσε το",
|
"note_apply_storage_label_previous_assets": "Σημείωση: Για να εφαρμοστεί η Ετικέτα Αποθήκευσης σε στοιχεία που είχαν αναρτηθεί παλαιότερα, εκτέλεσε το",
|
||||||
"note_cannot_be_changed_later": "ΣΗΜΕΊΩΣΗ: Αυτό δεν μπορεί να τροποποιηθεί αργότερα!",
|
"note_cannot_be_changed_later": "ΣΗΜΕΊΩΣΗ: Αυτό δεν μπορεί να τροποποιηθεί αργότερα!",
|
||||||
"notification_email_from_address": "Διεύθυνση αποστολέα",
|
"notification_email_from_address": "Διεύθυνση αποστολέα",
|
||||||
"notification_email_from_address_description": "Διεύθυνση αποστολέα, πχ: \"Immich Photo Server <noreply@example.com>\"",
|
"notification_email_from_address_description": "Διεύθυνση αποστολέα, πχ: \"Immich Photo Server <noreply@example.com>\". Βεβαιωθείτε ότι έχετε δικαίωμα χρήσης της διεύθυνσης που χρησιμοποιείτε.",
|
||||||
"notification_email_host_description": "Πάροχος του email server (πχ smtp.immich.app)",
|
"notification_email_host_description": "Πάροχος του email server (πχ smtp.immich.app)",
|
||||||
"notification_email_ignore_certificate_errors": "Παράβλεψη των σφαλμάτων πιστοποίησης",
|
"notification_email_ignore_certificate_errors": "Παράβλεψη των σφαλμάτων πιστοποίησης",
|
||||||
"notification_email_ignore_certificate_errors_description": "Παράβλεψη σφαλμάτων επικύρωσης της πιστοποίησης TLS (δεν προτείνεται)",
|
"notification_email_ignore_certificate_errors_description": "Παράβλεψη σφαλμάτων επικύρωσης της πιστοποίησης TLS (δεν προτείνεται)",
|
||||||
@@ -202,7 +204,7 @@
|
|||||||
"oauth_storage_quota_claim": "Δήλωση ποσοστού αποθήκευσης",
|
"oauth_storage_quota_claim": "Δήλωση ποσοστού αποθήκευσης",
|
||||||
"oauth_storage_quota_claim_description": "Ορίζει αυτόματα το ποσοστό αποθήκευσης του χρήστη στη δηλωμένη τιμή.",
|
"oauth_storage_quota_claim_description": "Ορίζει αυτόματα το ποσοστό αποθήκευσης του χρήστη στη δηλωμένη τιμή.",
|
||||||
"oauth_storage_quota_default": "Προεπιλεγμένο όριο αποθήκευσης (GiB)",
|
"oauth_storage_quota_default": "Προεπιλεγμένο όριο αποθήκευσης (GiB)",
|
||||||
"oauth_storage_quota_default_description": "Ποσοστό σε GiB που θα χρησιμοποιηθεί όταν δεν ορίζεται από τη δηλωμένη τιμή (Εισάγετε 0 για απεριόριστο ποσοστό).",
|
"oauth_storage_quota_default_description": "Ποσοστό σε GiB που θα χρησιμοποιηθεί όταν δεν ορίζεται από τη δηλωμένη τιμή.",
|
||||||
"oauth_timeout": "Χρονικό όριο Αιτήματος",
|
"oauth_timeout": "Χρονικό όριο Αιτήματος",
|
||||||
"oauth_timeout_description": "Χρονικό όριο Αιτήματος σε milliseconds",
|
"oauth_timeout_description": "Χρονικό όριο Αιτήματος σε milliseconds",
|
||||||
"password_enable_description": "Σύνδεση με ηλεκτρονικό ταχυδρομείο",
|
"password_enable_description": "Σύνδεση με ηλεκτρονικό ταχυδρομείο",
|
||||||
@@ -242,7 +244,7 @@
|
|||||||
"storage_template_migration_info": "Το πρότυπο αποθήκευσης θα μετατρέψει όλες τις επεκτάσεις σε πεζά γράμματα. Οι αλλαγές στο πρότυπο θα ισχύουν μόνο για νέα περιουσιακά στοιχεία. Για να εφαρμόσετε αναδρομικά το πρότυπο σε περιουσιακά στοιχεία που έχουν μεταφορτωθεί προηγουμένως, εκτελέστε το <link>{job}</link>.",
|
"storage_template_migration_info": "Το πρότυπο αποθήκευσης θα μετατρέψει όλες τις επεκτάσεις σε πεζά γράμματα. Οι αλλαγές στο πρότυπο θα ισχύουν μόνο για νέα περιουσιακά στοιχεία. Για να εφαρμόσετε αναδρομικά το πρότυπο σε περιουσιακά στοιχεία που έχουν μεταφορτωθεί προηγουμένως, εκτελέστε το <link>{job}</link>.",
|
||||||
"storage_template_migration_job": "Εργασία Μεταφοράς Προτύπων Αποθήκευσης",
|
"storage_template_migration_job": "Εργασία Μεταφοράς Προτύπων Αποθήκευσης",
|
||||||
"storage_template_more_details": "Για περισσότερες λεπτομέρειες σχετικά με αυτήν τη δυνατότητα, ανατρέξτε στο <template-link>Πρότυπο Αποθήκευσης</template-link> και στις <implications-link>συνέπειές</implications-link> του",
|
"storage_template_more_details": "Για περισσότερες λεπτομέρειες σχετικά με αυτήν τη δυνατότητα, ανατρέξτε στο <template-link>Πρότυπο Αποθήκευσης</template-link> και στις <implications-link>συνέπειές</implications-link> του",
|
||||||
"storage_template_onboarding_description": "Όταν ενεργοποιηθεί, αυτή η δυνατότητα θα οργανώνει αυτόματα τα αρχεία με βάση ένα πρότυπο που καθορίζεται από τον χρήστη. Λόγω θεμάτων σταθερότητας, η δυνατότητα είναι απενεργοποιημένη από προεπιλογή. Για περισσότερες πληροφορίες, παρακαλώ δείτε την <link>τεκμηρίωση</link>.",
|
"storage_template_onboarding_description_v2": "Όταν είναι ενεργοποιημένη, αυτή η λειτουργία θα οργανώνει αυτόματα τα αρχεία με βάση ένα πρότυπο που ορίζεται από το χρήστη. Για περισσότερες πληροφορίες, παρακαλώ ανατρέξτε στις <link>οδηγίες χρήσης</link>.",
|
||||||
"storage_template_path_length": "Όριο μήκους διαδρομής: <b>{length, number}</b>/{limit, number}, κατά προσέγγιση",
|
"storage_template_path_length": "Όριο μήκους διαδρομής: <b>{length, number}</b>/{limit, number}, κατά προσέγγιση",
|
||||||
"storage_template_settings": "Πρότυπο Αποθήκευσης",
|
"storage_template_settings": "Πρότυπο Αποθήκευσης",
|
||||||
"storage_template_settings_description": "Διαχείριση της δομής φακέλου και του ονόματος, του ανεβασμένου αρχείου",
|
"storage_template_settings_description": "Διαχείριση της δομής φακέλου και του ονόματος, του ανεβασμένου αρχείου",
|
||||||
@@ -402,6 +404,9 @@
|
|||||||
"album_with_link_access": "Επιτρέψτε σε οποιονδήποτε έχει τον σύνδεσμο, να δει τις φωτογραφίες και τα άτομα σε αυτό το άλμπουμ.",
|
"album_with_link_access": "Επιτρέψτε σε οποιονδήποτε έχει τον σύνδεσμο, να δει τις φωτογραφίες και τα άτομα σε αυτό το άλμπουμ.",
|
||||||
"albums": "Άλμπουμ",
|
"albums": "Άλμπουμ",
|
||||||
"albums_count": "{count, plural, one {{count, number} Άλμπουμ} other {{count, number} Άλμπουμ}}",
|
"albums_count": "{count, plural, one {{count, number} Άλμπουμ} other {{count, number} Άλμπουμ}}",
|
||||||
|
"albums_default_sort_order": "Προεπιλεγμένη ταξινόμηση άλμπουμ",
|
||||||
|
"albums_default_sort_order_description": "Αρχική ταξινόμηση κατά τη δημιουργία νέων άλμπουμ.",
|
||||||
|
"albums_feature_description": "Συλλογές στοιχείων που μπορούν να κοινοποιηθούν σε άλλους χρήστες.",
|
||||||
"all": "Όλα",
|
"all": "Όλα",
|
||||||
"all_albums": "Όλα τα άλμπουμ",
|
"all_albums": "Όλα τα άλμπουμ",
|
||||||
"all_people": "Όλα τα άτομα",
|
"all_people": "Όλα τα άτομα",
|
||||||
@@ -459,10 +464,12 @@
|
|||||||
"assets": "Αντικείμενα",
|
"assets": "Αντικείμενα",
|
||||||
"assets_added_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}}",
|
"assets_added_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}}",
|
||||||
"assets_added_to_album_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο άλμπουμ",
|
"assets_added_to_album_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο άλμπουμ",
|
||||||
"assets_added_to_name_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο {hasName, select, true {<b>{name}</b>} other {νέο άλμπουμ}}",
|
"assets_cannot_be_added_to_album_count": "{count, plural, one {Στοιχείο} other {Στοιχεία}} δεν μπορούν να προστεθούν στο άλμπουμ",
|
||||||
"assets_count": "{count, plural, one {# αρχείο} other {# αρχεία}}",
|
"assets_count": "{count, plural, one {# αρχείο} other {# αρχεία}}",
|
||||||
"assets_deleted_permanently": "{count} τα στοιχεία διαγράφηκαν οριστικά",
|
"assets_deleted_permanently": "{count} τα στοιχεία διαγράφηκαν οριστικά",
|
||||||
"assets_deleted_permanently_from_server": "{count} στοιχεία διαγράφηκαν οριστικά από το διακομιστή Immich",
|
"assets_deleted_permanently_from_server": "{count} στοιχεία διαγράφηκαν οριστικά από το διακομιστή Immich",
|
||||||
|
"assets_downloaded_failed": "{count, plural, one {Έγινε λήψη # αρχείου - {error} αρχείο απέτυχε} other {Έγινε λήψη # αρχείων - {error} αρχεία απέτυχαν}}",
|
||||||
|
"assets_downloaded_successfully": "{count, plural, one {Έγινε λήψη # αρχείου επιτυχώς} other {Έγινε λήψη # αρχείων επιτυχώς}}",
|
||||||
"assets_moved_to_trash_count": "Μετακινήθηκαν {count, plural, one {# αρχείο} other {# αρχεία}} στον κάδο απορριμμάτων",
|
"assets_moved_to_trash_count": "Μετακινήθηκαν {count, plural, one {# αρχείο} other {# αρχεία}} στον κάδο απορριμμάτων",
|
||||||
"assets_permanently_deleted_count": "Διαγράφηκαν μόνιμα {count, plural, one {# αρχείο} other {# αρχεία}}",
|
"assets_permanently_deleted_count": "Διαγράφηκαν μόνιμα {count, plural, one {# αρχείο} other {# αρχεία}}",
|
||||||
"assets_removed_count": "Αφαιρέθηκαν {count, plural, one {# αρχείο} other {# αρχεία}}",
|
"assets_removed_count": "Αφαιρέθηκαν {count, plural, one {# αρχείο} other {# αρχεία}}",
|
||||||
@@ -477,6 +484,7 @@
|
|||||||
"authorized_devices": "Εξουσιοδοτημένες Συσκευές",
|
"authorized_devices": "Εξουσιοδοτημένες Συσκευές",
|
||||||
"automatic_endpoint_switching_subtitle": "Σύνδεση τοπικά μέσω του καθορισμένου Wi-Fi όταν είναι διαθέσιμο και χρήση εναλλακτικών συνδέσεων αλλού",
|
"automatic_endpoint_switching_subtitle": "Σύνδεση τοπικά μέσω του καθορισμένου Wi-Fi όταν είναι διαθέσιμο και χρήση εναλλακτικών συνδέσεων αλλού",
|
||||||
"automatic_endpoint_switching_title": "Αυτόματη εναλλαγή URL",
|
"automatic_endpoint_switching_title": "Αυτόματη εναλλαγή URL",
|
||||||
|
"autoplay_slideshow": "Αυτόματη αναπαραγωγή παρουσίασης",
|
||||||
"back": "Πίσω",
|
"back": "Πίσω",
|
||||||
"back_close_deselect": "Πίσω, κλείσιμο ή αποεπιλογή",
|
"back_close_deselect": "Πίσω, κλείσιμο ή αποεπιλογή",
|
||||||
"background_location_permission": "Άδεια τοποθεσίας στο παρασκήνιο",
|
"background_location_permission": "Άδεια τοποθεσίας στο παρασκήνιο",
|
||||||
@@ -641,6 +649,7 @@
|
|||||||
"confirm_password": "Επιβεβαίωση κωδικού",
|
"confirm_password": "Επιβεβαίωση κωδικού",
|
||||||
"confirm_tag_face": "Θέλετε να επισημάνετε αυτό το πρόσωπο ως {name};",
|
"confirm_tag_face": "Θέλετε να επισημάνετε αυτό το πρόσωπο ως {name};",
|
||||||
"confirm_tag_face_unnamed": "Θέλετε να επισημάνετε αυτό το πρόσωπο;",
|
"confirm_tag_face_unnamed": "Θέλετε να επισημάνετε αυτό το πρόσωπο;",
|
||||||
|
"connected_device": "Συνδεδεμένη συσκευή",
|
||||||
"connected_to": "Συνδεδεμένο με",
|
"connected_to": "Συνδεδεμένο με",
|
||||||
"contain": "Περιέχει",
|
"contain": "Περιέχει",
|
||||||
"context": "Συμφραζόμενα",
|
"context": "Συμφραζόμενα",
|
||||||
@@ -740,6 +749,7 @@
|
|||||||
"disallow_edits": "Απαγόρευση επεξεργασιών",
|
"disallow_edits": "Απαγόρευση επεξεργασιών",
|
||||||
"discord": "Πλατφόρμα Discord",
|
"discord": "Πλατφόρμα Discord",
|
||||||
"discover": "Ανίχνευση",
|
"discover": "Ανίχνευση",
|
||||||
|
"discovered_devices": "Διαθέσιμες συσκευές",
|
||||||
"dismiss_all_errors": "Παράβλεψη όλων των σφαλμάτων",
|
"dismiss_all_errors": "Παράβλεψη όλων των σφαλμάτων",
|
||||||
"dismiss_error": "Παράβλεψη σφάλματος",
|
"dismiss_error": "Παράβλεψη σφάλματος",
|
||||||
"display_options": "Επιλογές εμφάνισης",
|
"display_options": "Επιλογές εμφάνισης",
|
||||||
@@ -1096,6 +1106,9 @@
|
|||||||
"kept_this_deleted_others": "Διατηρήθηκε αυτό το στοιχείο και διαγράφηκε/καν {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
"kept_this_deleted_others": "Διατηρήθηκε αυτό το στοιχείο και διαγράφηκε/καν {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
||||||
"keyboard_shortcuts": "Συντομεύσεις πληκτρολογίου",
|
"keyboard_shortcuts": "Συντομεύσεις πληκτρολογίου",
|
||||||
"language": "Γλώσσα",
|
"language": "Γλώσσα",
|
||||||
|
"language_no_results_subtitle": "Δοκιμάστε να αλλάξετε τον όρο αναζήτησης",
|
||||||
|
"language_no_results_title": "Δε βρέθηκαν γλώσσες",
|
||||||
|
"language_search_hint": "Αναζήτηση γλωσσών...",
|
||||||
"language_setting_description": "Επιλέξτε τη γλώσσα που προτιμάτε",
|
"language_setting_description": "Επιλέξτε τη γλώσσα που προτιμάτε",
|
||||||
"last_seen": "Τελευταία προβολή",
|
"last_seen": "Τελευταία προβολή",
|
||||||
"latest_version": "Τελευταία Έκδοση",
|
"latest_version": "Τελευταία Έκδοση",
|
||||||
@@ -1121,6 +1134,7 @@
|
|||||||
"list": "Λίστα",
|
"list": "Λίστα",
|
||||||
"loading": "Φόρτωση",
|
"loading": "Φόρτωση",
|
||||||
"loading_search_results_failed": "Η φόρτωση αποτελεσμάτων αναζήτησης απέτυχε",
|
"loading_search_results_failed": "Η φόρτωση αποτελεσμάτων αναζήτησης απέτυχε",
|
||||||
|
"local_asset_cast_failed": "Αδυναμία μετάδοσης στοιχείου που δεν έχει ανέβει στον διακομιστή",
|
||||||
"local_network": "Τοπικό δίκτυο",
|
"local_network": "Τοπικό δίκτυο",
|
||||||
"local_network_sheet_info": "Η εφαρμογή θα συνδεθεί με τον διακομιστή μέσω αυτού του URL όταν χρησιμοποιείται το καθορισμένο δίκτυο Wi-Fi",
|
"local_network_sheet_info": "Η εφαρμογή θα συνδεθεί με τον διακομιστή μέσω αυτού του URL όταν χρησιμοποιείται το καθορισμένο δίκτυο Wi-Fi",
|
||||||
"location_permission": "Άδεια τοποθεσίας",
|
"location_permission": "Άδεια τοποθεσίας",
|
||||||
@@ -1134,6 +1148,7 @@
|
|||||||
"locked_folder": "Κλειδωμένος φάκελος",
|
"locked_folder": "Κλειδωμένος φάκελος",
|
||||||
"log_out": "Αποσύνδεση",
|
"log_out": "Αποσύνδεση",
|
||||||
"log_out_all_devices": "Αποσύνδεση από Όλες τις Συσκευές",
|
"log_out_all_devices": "Αποσύνδεση από Όλες τις Συσκευές",
|
||||||
|
"logged_in_as": "Συνδεδεμένος ως {user}",
|
||||||
"logged_out_all_devices": "Όλες οι συσκευές αποσυνδέθηκαν",
|
"logged_out_all_devices": "Όλες οι συσκευές αποσυνδέθηκαν",
|
||||||
"logged_out_device": "Αποσυνδεδεμένη συσκευή",
|
"logged_out_device": "Αποσυνδεδεμένη συσκευή",
|
||||||
"login": "Είσοδος",
|
"login": "Είσοδος",
|
||||||
@@ -1165,7 +1180,7 @@
|
|||||||
"look": "Εμφάνιση",
|
"look": "Εμφάνιση",
|
||||||
"loop_videos": "Επανάληψη βίντεο",
|
"loop_videos": "Επανάληψη βίντεο",
|
||||||
"loop_videos_description": "Ενεργοποιήστε την αυτόματη επανάληψη ενός βίντεο στο πρόγραμμα προβολής λεπτομερειών.",
|
"loop_videos_description": "Ενεργοποιήστε την αυτόματη επανάληψη ενός βίντεο στο πρόγραμμα προβολής λεπτομερειών.",
|
||||||
"main_branch_warning": "Χρησιμοποιείτε μια έκδοση σε ανάπτυξη· συνιστούμε ανεπιφύλακτα τη χρήση μιας επίσημης έκδοσης!",
|
"main_branch_warning": "Χρησιμοποιείτε μια έκδοση σε ανάπτυξη· συνιστούμε ανεπιφύλακτα τη χρήση μιας τελικής έκδοσης!",
|
||||||
"main_menu": "Κύριο μενού",
|
"main_menu": "Κύριο μενού",
|
||||||
"make": "Κατασκευαστής",
|
"make": "Κατασκευαστής",
|
||||||
"manage_shared_links": "Διαχείριση κοινόχρηστων συνδέσμων",
|
"manage_shared_links": "Διαχείριση κοινόχρηστων συνδέσμων",
|
||||||
@@ -1261,6 +1276,7 @@
|
|||||||
"no_archived_assets_message": "Αρχειοθετήστε φωτογραφίες και βίντεο για να τα αποκρύψετε από την Προβολή Φωτογραφιών",
|
"no_archived_assets_message": "Αρχειοθετήστε φωτογραφίες και βίντεο για να τα αποκρύψετε από την Προβολή Φωτογραφιών",
|
||||||
"no_assets_message": "ΚΑΝΤΕ ΚΛΙΚ ΓΙΑ ΝΑ ΑΝΕΒΑΣΕΤΕ ΤΗΝ ΠΡΩΤΗ ΣΑΣ ΦΩΤΟΓΡΑΦΙΑ",
|
"no_assets_message": "ΚΑΝΤΕ ΚΛΙΚ ΓΙΑ ΝΑ ΑΝΕΒΑΣΕΤΕ ΤΗΝ ΠΡΩΤΗ ΣΑΣ ΦΩΤΟΓΡΑΦΙΑ",
|
||||||
"no_assets_to_show": "Δεν υπάρχουν στοιχεία προς εμφάνιση",
|
"no_assets_to_show": "Δεν υπάρχουν στοιχεία προς εμφάνιση",
|
||||||
|
"no_cast_devices_found": "Δε βρέθηκαν συσκευές μετάδοσης",
|
||||||
"no_duplicates_found": "Δεν βρέθηκαν διπλότυπα.",
|
"no_duplicates_found": "Δεν βρέθηκαν διπλότυπα.",
|
||||||
"no_exif_info_available": "Καμία πληροφορία exif διαθέσιμη",
|
"no_exif_info_available": "Καμία πληροφορία exif διαθέσιμη",
|
||||||
"no_explore_results_message": "Ανεβάστε περισσότερες φωτογραφίες για να περιηγηθείτε στη συλλογή σας.",
|
"no_explore_results_message": "Ανεβάστε περισσότερες φωτογραφίες για να περιηγηθείτε στη συλλογή σας.",
|
||||||
@@ -1293,8 +1309,11 @@
|
|||||||
"oldest_first": "Τα παλαιότερα πρώτα",
|
"oldest_first": "Τα παλαιότερα πρώτα",
|
||||||
"on_this_device": "Σε αυτή τη συσκευή",
|
"on_this_device": "Σε αυτή τη συσκευή",
|
||||||
"onboarding": "Οδηγός εκκίνησης",
|
"onboarding": "Οδηγός εκκίνησης",
|
||||||
"onboarding_privacy_description": "Οι παρακάτω (προαιρετικές) λειτουργίες βασίζονται σε εξωτερικές υπηρεσίες και μπορούν να απενεργοποιηθούν ανά πάσα στιγμή από τις ρυθμίσεις διαχείρισης.",
|
"onboarding_locale_description": "Επιλέξτε την γλώσσα που προτιμάτε. Μπορείτε να την αλλάξετε αργότερα από τις ρυθμίσεις.",
|
||||||
|
"onboarding_privacy_description": "Οι παρακάτω (προαιρετικές) λειτουργίες βασίζονται σε εξωτερικές υπηρεσίες και μπορούν να απενεργοποιηθούν ανά πάσα στιγμή από τις ρυθμίσεις.",
|
||||||
|
"onboarding_server_welcome_description": "Ας ξεκινήσουμε με μερικές συνηθισμένες ρυθμίσεις.",
|
||||||
"onboarding_theme_description": "Επιλέξτε ένα θέμα χρώματος για το προφίλ σας. Μπορείτε να το αλλάξετε αργότερα στις ρυθμίσεις σας.",
|
"onboarding_theme_description": "Επιλέξτε ένα θέμα χρώματος για το προφίλ σας. Μπορείτε να το αλλάξετε αργότερα στις ρυθμίσεις σας.",
|
||||||
|
"onboarding_user_welcome_description": "Ας ξεκινήσουμε!",
|
||||||
"onboarding_welcome_user": "Καλωσόρισες, {user}",
|
"onboarding_welcome_user": "Καλωσόρισες, {user}",
|
||||||
"online": "Σε σύνδεση",
|
"online": "Σε σύνδεση",
|
||||||
"only_favorites": "Μόνο αγαπημένα",
|
"only_favorites": "Μόνο αγαπημένα",
|
||||||
@@ -1480,6 +1499,7 @@
|
|||||||
"remove_from_shared_link": "Αφαίρεση από τον κοινόχρηστο σύνδεσμο",
|
"remove_from_shared_link": "Αφαίρεση από τον κοινόχρηστο σύνδεσμο",
|
||||||
"remove_memory": "Αφαίρεση ανάμνησης",
|
"remove_memory": "Αφαίρεση ανάμνησης",
|
||||||
"remove_photo_from_memory": "Αφαίρεση φωτογραφίας από την ανάμνηση",
|
"remove_photo_from_memory": "Αφαίρεση φωτογραφίας από την ανάμνηση",
|
||||||
|
"remove_tag": "Αφαίρεση ετικέτας",
|
||||||
"remove_url": "Αφαίρεση Συνδέσμου",
|
"remove_url": "Αφαίρεση Συνδέσμου",
|
||||||
"remove_user": "Αφαίρεση χρήστη",
|
"remove_user": "Αφαίρεση χρήστη",
|
||||||
"removed_api_key": "Αφαιρέθηκε το API Key: {name}",
|
"removed_api_key": "Αφαιρέθηκε το API Key: {name}",
|
||||||
@@ -1586,6 +1606,7 @@
|
|||||||
"select_album_cover": "Επιλέξτε εξώφυλλο άλμπουμ",
|
"select_album_cover": "Επιλέξτε εξώφυλλο άλμπουμ",
|
||||||
"select_all": "Επιλογή όλων",
|
"select_all": "Επιλογή όλων",
|
||||||
"select_all_duplicates": "Επιλογή όλων των διπλότυπων",
|
"select_all_duplicates": "Επιλογή όλων των διπλότυπων",
|
||||||
|
"select_all_in": "Επιλογή όλων στο {group}",
|
||||||
"select_avatar_color": "Επιλέξτε χρώμα avatar",
|
"select_avatar_color": "Επιλέξτε χρώμα avatar",
|
||||||
"select_face": "Επιλογή προσώπου",
|
"select_face": "Επιλογή προσώπου",
|
||||||
"select_featured_photo": "Επιλέξτε φωτογραφία για προβολή",
|
"select_featured_photo": "Επιλέξτε φωτογραφία για προβολή",
|
||||||
@@ -1606,6 +1627,7 @@
|
|||||||
"server_info_box_server_url": "URL διακομιστή",
|
"server_info_box_server_url": "URL διακομιστή",
|
||||||
"server_offline": "Διακομιστής Εκτός Σύνδεσης",
|
"server_offline": "Διακομιστής Εκτός Σύνδεσης",
|
||||||
"server_online": "Διακομιστής Σε Σύνδεση",
|
"server_online": "Διακομιστής Σε Σύνδεση",
|
||||||
|
"server_privacy": "Απόρρητο Διακομιστή",
|
||||||
"server_stats": "Στατιστικά Διακομιστή",
|
"server_stats": "Στατιστικά Διακομιστή",
|
||||||
"server_version": "Έκδοση Διακομιστή",
|
"server_version": "Έκδοση Διακομιστή",
|
||||||
"set": "Ορισμός",
|
"set": "Ορισμός",
|
||||||
@@ -1615,6 +1637,7 @@
|
|||||||
"set_date_of_birth": "Ορισμός ημερομηνίας γέννησης",
|
"set_date_of_birth": "Ορισμός ημερομηνίας γέννησης",
|
||||||
"set_profile_picture": "Ορισμός εικόνας προφίλ",
|
"set_profile_picture": "Ορισμός εικόνας προφίλ",
|
||||||
"set_slideshow_to_fullscreen": "Ορίστε την παρουσίαση σε πλήρη οθόνη",
|
"set_slideshow_to_fullscreen": "Ορίστε την παρουσίαση σε πλήρη οθόνη",
|
||||||
|
"set_stack_primary_asset": "Ορισμός ως κύριο στοιχείο",
|
||||||
"setting_image_viewer_help": "Το πρόγραμμα προβολής λεπτομερειών φορτώνει πρώτα τη μικρογραφία, στη συνέχεια φορτώνει την προεπισκόπηση μεσαίου μεγέθους (αν είναι ενεργοποιημένη), τέλος φορτώνει το πρωτότυπο (αν είναι ενεργοποιημένο).",
|
"setting_image_viewer_help": "Το πρόγραμμα προβολής λεπτομερειών φορτώνει πρώτα τη μικρογραφία, στη συνέχεια φορτώνει την προεπισκόπηση μεσαίου μεγέθους (αν είναι ενεργοποιημένη), τέλος φορτώνει το πρωτότυπο (αν είναι ενεργοποιημένο).",
|
||||||
"setting_image_viewer_original_subtitle": "Ενεργοποιήστε τη φόρτωση της πρωτότυπης εικόνας πλήρους ανάλυσης (μεγάλη!). Απενεργοποιήστε για να μειώσετε τη χρήση δεδομένων (τόσο στο δίκτυο όσο και στην κρυφή μνήμη της συσκευής).",
|
"setting_image_viewer_original_subtitle": "Ενεργοποιήστε τη φόρτωση της πρωτότυπης εικόνας πλήρους ανάλυσης (μεγάλη!). Απενεργοποιήστε για να μειώσετε τη χρήση δεδομένων (τόσο στο δίκτυο όσο και στην κρυφή μνήμη της συσκευής).",
|
||||||
"setting_image_viewer_original_title": "Φόρτωση πρωτότυπης εικόνας",
|
"setting_image_viewer_original_title": "Φόρτωση πρωτότυπης εικόνας",
|
||||||
@@ -1752,6 +1775,7 @@
|
|||||||
"start_date": "Από",
|
"start_date": "Από",
|
||||||
"state": "Νομός",
|
"state": "Νομός",
|
||||||
"status": "Κατάσταση",
|
"status": "Κατάσταση",
|
||||||
|
"stop_casting": "Διακοπή μετάδοσης",
|
||||||
"stop_motion_photo": "Διέκοψε την Φωτογραφία Κίνησης",
|
"stop_motion_photo": "Διέκοψε την Φωτογραφία Κίνησης",
|
||||||
"stop_photo_sharing": "Διακοπή κοινής χρήσης των φωτογραφιών σας;",
|
"stop_photo_sharing": "Διακοπή κοινής χρήσης των φωτογραφιών σας;",
|
||||||
"stop_photo_sharing_description": "Ο χρήστης {partner} δεν θα έχει πλέον πρόσβαση στις φωτογραφίες σας.",
|
"stop_photo_sharing_description": "Ο χρήστης {partner} δεν θα έχει πλέον πρόσβαση στις φωτογραφίες σας.",
|
||||||
@@ -1810,7 +1834,7 @@
|
|||||||
"to_trash": "Κάδος απορριμμάτων",
|
"to_trash": "Κάδος απορριμμάτων",
|
||||||
"toggle_settings": "Εναλλαγή ρυθμίσεων",
|
"toggle_settings": "Εναλλαγή ρυθμίσεων",
|
||||||
"total": "Σύνολο",
|
"total": "Σύνολο",
|
||||||
"total_usage": "Συνολική χρήση",
|
"total_usage": "Συνολικη χρηση",
|
||||||
"trash": "Κάδος απορριμμάτων",
|
"trash": "Κάδος απορριμμάτων",
|
||||||
"trash_all": "Διαγραφή Όλων",
|
"trash_all": "Διαγραφή Όλων",
|
||||||
"trash_count": "Διαγραφή {count, number}",
|
"trash_count": "Διαγραφή {count, number}",
|
||||||
@@ -1830,6 +1854,7 @@
|
|||||||
"unable_to_setup_pin_code": "Αδυναμία ρύθμισης κωδικού PIN",
|
"unable_to_setup_pin_code": "Αδυναμία ρύθμισης κωδικού PIN",
|
||||||
"unarchive": "Αναίρεση αρχειοθέτησης",
|
"unarchive": "Αναίρεση αρχειοθέτησης",
|
||||||
"unarchived_count": "{count, plural, other {Αρχειοθετήσεις αναιρέθηκαν #}}",
|
"unarchived_count": "{count, plural, other {Αρχειοθετήσεις αναιρέθηκαν #}}",
|
||||||
|
"undo": "Αναίρεση",
|
||||||
"unfavorite": "Αποεπιλογή από τα αγαπημένα",
|
"unfavorite": "Αποεπιλογή από τα αγαπημένα",
|
||||||
"unhide_person": "Αναίρεση απόκρυψης ατόμου",
|
"unhide_person": "Αναίρεση απόκρυψης ατόμου",
|
||||||
"unknown": "Άγνωστο",
|
"unknown": "Άγνωστο",
|
||||||
@@ -1846,6 +1871,7 @@
|
|||||||
"unsaved_change": "Μη αποθηκευμένη αλλαγή",
|
"unsaved_change": "Μη αποθηκευμένη αλλαγή",
|
||||||
"unselect_all": "Αποεπιλογή όλων",
|
"unselect_all": "Αποεπιλογή όλων",
|
||||||
"unselect_all_duplicates": "Αποεπιλογή όλων των διπλότυπων",
|
"unselect_all_duplicates": "Αποεπιλογή όλων των διπλότυπων",
|
||||||
|
"unselect_all_in": "Αποεπιλογή όλων στο {group}",
|
||||||
"unstack": "Αποστοίβαξη",
|
"unstack": "Αποστοίβαξη",
|
||||||
"unstacked_assets_count": "Αποστοιβάξατε {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
"unstacked_assets_count": "Αποστοιβάξατε {count, plural, one {# στοιχείο} other {# στοιχεία}}",
|
||||||
"up_next": "Ακολουθεί",
|
"up_next": "Ακολουθεί",
|
||||||
@@ -1875,10 +1901,11 @@
|
|||||||
"user_liked": "Στο χρήστη {user} αρέσει {type, select, photo {αυτή η φωτογραφία} video {αυτό το βίντεο} asset {αυτό το αντικείμενο} other {it}}",
|
"user_liked": "Στο χρήστη {user} αρέσει {type, select, photo {αυτή η φωτογραφία} video {αυτό το βίντεο} asset {αυτό το αντικείμενο} other {it}}",
|
||||||
"user_pin_code_settings": "Κωδικός PIN",
|
"user_pin_code_settings": "Κωδικός PIN",
|
||||||
"user_pin_code_settings_description": "Διαχειριστείτε τον κωδικό PIN σας",
|
"user_pin_code_settings_description": "Διαχειριστείτε τον κωδικό PIN σας",
|
||||||
|
"user_privacy": "Απόρρητο Χρήστη",
|
||||||
"user_purchase_settings": "Αγορά",
|
"user_purchase_settings": "Αγορά",
|
||||||
"user_purchase_settings_description": "Διαχείριση Αγοράς",
|
"user_purchase_settings_description": "Διαχείριση Αγοράς",
|
||||||
"user_role_set": "Ορισμός {user} ως {role}",
|
"user_role_set": "Ορισμός {user} ως {role}",
|
||||||
"user_usage_detail": "Λεπτομέρειες χρήσης του χρήστη",
|
"user_usage_detail": "Λεπτομερειες χρησης του χρηστη",
|
||||||
"user_usage_stats": "Στατιστικά χρήσης λογαριασμού",
|
"user_usage_stats": "Στατιστικά χρήσης λογαριασμού",
|
||||||
"user_usage_stats_description": "Προβολή στατιστικών χρήσης λογαριασμού",
|
"user_usage_stats_description": "Προβολή στατιστικών χρήσης λογαριασμού",
|
||||||
"username": "Όνομα Χρήστη",
|
"username": "Όνομα Χρήστη",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user