Compare commits

..

6 Commits

Author SHA1 Message Date
Alex Tran 27fa817ea6 Merge branch 'main' of github.com:apeman76/immich into apeman76/main 2024-07-08 22:48:45 -05:00
Alex 4bf2ded729 Merge branch 'main' into main 2024-07-08 22:46:30 -05:00
Alex Tran fed7d0464a format fix 2024-07-08 22:44:29 -05:00
apeman 3ab67886b0 Fix: Update tests to include autorotate 2024-07-06 01:09:39 +02:00
apeman 2b06d4b284 Update tests to include autorotate 2024-07-06 01:08:14 +02:00
apeman 34bea0190e Add noautorotate to all HW recorders 2024-07-06 00:37:08 +02:00
96 changed files with 1319 additions and 1511 deletions
+1 -1
View File
@@ -1 +1 @@
20.15.1 20.15
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:20.15.1-alpine3.20@sha256:34b7aa411056c85dbf71d240d26516949b3f72b318d796c26b57caaa1df5639a as core FROM node:20.15.0-alpine3.20@sha256:df01469346db2bf1cfc1f7261aeab86b2960efa840fe2bd46d83ff339f463665 as core
WORKDIR /usr/src/open-api/typescript-sdk WORKDIR /usr/src/open-api/typescript-sdk
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
+8 -8
View File
@@ -1,12 +1,12 @@
{ {
"name": "@immich/cli", "name": "@immich/cli",
"version": "2.2.8", "version": "2.2.7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@immich/cli", "name": "@immich/cli",
"version": "2.2.8", "version": "2.2.7",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
@@ -22,7 +22,7 @@
"@types/cli-progress": "^3.11.0", "@types/cli-progress": "^3.11.0",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/mock-fs": "^4.13.1", "@types/mock-fs": "^4.13.1",
"@types/node": "^20.14.10", "@types/node": "^20.14.9",
"@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0", "@typescript-eslint/parser": "^7.0.0",
"@vitest/coverage-v8": "^1.2.2", "@vitest/coverage-v8": "^1.2.2",
@@ -49,14 +49,14 @@
}, },
"../open-api/typescript-sdk": { "../open-api/typescript-sdk": {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.108.0", "version": "1.107.2",
"dev": true, "dev": true,
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"@oazapfts/runtime": "^1.0.2" "@oazapfts/runtime": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.14.10", "@types/node": "^20.14.9",
"typescript": "^5.3.3" "typescript": "^5.3.3"
} }
}, },
@@ -1163,9 +1163,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.14.10", "version": "20.14.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz",
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@immich/cli", "name": "@immich/cli",
"version": "2.2.8", "version": "2.2.7",
"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",
@@ -18,7 +18,7 @@
"@types/cli-progress": "^3.11.0", "@types/cli-progress": "^3.11.0",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/mock-fs": "^4.13.1", "@types/mock-fs": "^4.13.1",
"@types/node": "^20.14.10", "@types/node": "^20.14.9",
"@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0", "@typescript-eslint/parser": "^7.0.0",
"@vitest/coverage-v8": "^1.2.2", "@vitest/coverage-v8": "^1.2.2",
@@ -64,6 +64,6 @@
"lodash-es": "^4.17.21" "lodash-es": "^4.17.21"
}, },
"volta": { "volta": {
"node": "20.15.1" "node": "20.15.0"
} }
} }
+1 -1
View File
@@ -73,7 +73,7 @@ services:
container_name: immich_prometheus container_name: immich_prometheus
ports: ports:
- 9090:9090 - 9090:9090
image: prom/prometheus@sha256:f20d3127bf2876f4a1df76246fca576b41ddf1125ed1c546fbd8b16ea55117e6 image: prom/prometheus@sha256:075b1ba2c4ebb04bc3a6ab86c06ec8d8099f8fda1c96ef6d104d9bb1def1d8bc
volumes: volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml - ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus - prometheus-data:/prometheus
+1 -1
View File
@@ -1 +1 @@
20.15.1 20.15
+1 -1
View File
@@ -56,6 +56,6 @@
"node": ">=20" "node": ">=20"
}, },
"volta": { "volta": {
"node": "20.15.1" "node": "20.15.0"
} }
} }
-4
View File
@@ -1,8 +1,4 @@
[ [
{
"label": "v1.108.0",
"url": "https://v1.108.0.archive.immich.app"
},
{ {
"label": "v1.107.2", "label": "v1.107.2",
"url": "https://v1.107.2.archive.immich.app" "url": "https://v1.107.2.archive.immich.app"
+1 -1
View File
@@ -1 +1 @@
20.15.1 20.15
+10 -12
View File
@@ -1,19 +1,19 @@
{ {
"name": "immich-e2e", "name": "immich-e2e",
"version": "1.108.0", "version": "1.107.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "immich-e2e", "name": "immich-e2e",
"version": "1.108.0", "version": "1.107.2",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"devDependencies": { "devDependencies": {
"@immich/cli": "file:../cli", "@immich/cli": "file:../cli",
"@immich/sdk": "file:../open-api/typescript-sdk", "@immich/sdk": "file:../open-api/typescript-sdk",
"@playwright/test": "^1.44.1", "@playwright/test": "^1.44.1",
"@types/luxon": "^3.4.2", "@types/luxon": "^3.4.2",
"@types/node": "^20.14.10", "@types/node": "^20.14.9",
"@types/pg": "^8.11.0", "@types/pg": "^8.11.0",
"@types/pngjs": "^6.0.4", "@types/pngjs": "^6.0.4",
"@types/supertest": "^6.0.2", "@types/supertest": "^6.0.2",
@@ -39,12 +39,11 @@
}, },
"../cli": { "../cli": {
"name": "@immich/cli", "name": "@immich/cli",
"version": "2.2.8", "version": "2.2.7",
"dev": true, "dev": true,
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"fastq": "^1.17.1",
"lodash-es": "^4.17.21" "lodash-es": "^4.17.21"
}, },
"bin": { "bin": {
@@ -56,7 +55,7 @@
"@types/cli-progress": "^3.11.0", "@types/cli-progress": "^3.11.0",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/mock-fs": "^4.13.1", "@types/mock-fs": "^4.13.1",
"@types/node": "^20.14.10", "@types/node": "^20.14.9",
"@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0", "@typescript-eslint/parser": "^7.0.0",
"@vitest/coverage-v8": "^1.2.2", "@vitest/coverage-v8": "^1.2.2",
@@ -74,7 +73,6 @@
"vite": "^5.0.12", "vite": "^5.0.12",
"vite-tsconfig-paths": "^4.3.2", "vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.2.2", "vitest": "^1.2.2",
"vitest-fetch-mock": "^0.2.2",
"yaml": "^2.3.1" "yaml": "^2.3.1"
}, },
"engines": { "engines": {
@@ -83,14 +81,14 @@
}, },
"../open-api/typescript-sdk": { "../open-api/typescript-sdk": {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.108.0", "version": "1.107.2",
"dev": true, "dev": true,
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"@oazapfts/runtime": "^1.0.2" "@oazapfts/runtime": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.14.10", "@types/node": "^20.14.9",
"typescript": "^5.3.3" "typescript": "^5.3.3"
} }
}, },
@@ -1233,9 +1231,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.14.10", "version": "20.14.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz",
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "immich-e2e", "name": "immich-e2e",
"version": "1.108.0", "version": "1.107.2",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
@@ -23,7 +23,7 @@
"@immich/sdk": "file:../open-api/typescript-sdk", "@immich/sdk": "file:../open-api/typescript-sdk",
"@playwright/test": "^1.44.1", "@playwright/test": "^1.44.1",
"@types/luxon": "^3.4.2", "@types/luxon": "^3.4.2",
"@types/node": "^20.14.10", "@types/node": "^20.14.9",
"@types/pg": "^8.11.0", "@types/pg": "^8.11.0",
"@types/pngjs": "^6.0.4", "@types/pngjs": "^6.0.4",
"@types/supertest": "^6.0.2", "@types/supertest": "^6.0.2",
@@ -47,6 +47,6 @@
"vitest": "^1.3.0" "vitest": "^1.3.0"
}, },
"volta": { "volta": {
"node": "20.15.1" "node": "20.15.0"
} }
} }
-6
View File
@@ -100,12 +100,6 @@ describe('/auth/*', () => {
expect(status).toBe(400); expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest()); expect(body).toEqual(errorDto.badRequest());
}); });
it('should reject an invalid email', async () => {
const { status, body } = await request(app).post('/auth/login').send({ email: [], password });
expect(status).toBe(400);
expect(body).toEqual(errorDto.invalidEmail);
});
} }
it('should accept a correct password', async () => { it('should accept a correct password', async () => {
-6
View File
@@ -61,12 +61,6 @@ export const errorDto = {
message: 'The server already has an admin', message: 'The server already has an admin',
correlationId: expect.any(String), correlationId: expect.any(String),
}, },
invalidEmail: {
error: 'Bad Request',
statusCode: 400,
message: ['email must be an email'],
correlationId: expect.any(String),
},
}; };
export const signupResponseDto = { export const signupResponseDto = {
-6
View File
@@ -168,12 +168,6 @@ def warning() -> Iterator[mock.Mock]:
yield mocked yield mocked
@pytest.fixture(scope="function")
def exception() -> Iterator[mock.Mock]:
with mock.patch.object(log, "exception") as mocked:
yield mocked
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def snapshot_download() -> Iterator[mock.Mock]: def snapshot_download() -> Iterator[mock.Mock]:
with mock.patch("app.models.base.snapshot_download") as mocked: with mock.patch("app.models.base.snapshot_download") as mocked:
+1 -12
View File
@@ -29,7 +29,6 @@ from .schemas import (
InferenceEntry, InferenceEntry,
InferenceResponse, InferenceResponse,
MessageResponse, MessageResponse,
ModelFormat,
ModelIdentity, ModelIdentity,
ModelTask, ModelTask,
ModelType, ModelType,
@@ -196,17 +195,7 @@ async def load(model: InferenceModel) -> InferenceModel:
if model.load_attempts > 1: if model.load_attempts > 1:
raise HTTPException(500, f"Failed to load model '{model.model_name}'") raise HTTPException(500, f"Failed to load model '{model.model_name}'")
with lock: with lock:
try: model.load()
model.load()
except FileNotFoundError as e:
if model.model_format == ModelFormat.ONNX:
raise e
log.exception(e)
log.warning(
f"{model.model_format.upper()} is available, but model '{model.model_name}' does not support it."
)
model.model_format = ModelFormat.ONNX
model.load()
return model return model
try: try:
+12 -11
View File
@@ -23,7 +23,7 @@ class InferenceModel(ABC):
self, self,
model_name: str, model_name: str,
cache_dir: Path | str | None = None, cache_dir: Path | str | None = None,
model_format: ModelFormat | None = None, preferred_format: ModelFormat | None = None,
session: ModelSession | None = None, session: ModelSession | None = None,
**model_kwargs: Any, **model_kwargs: Any,
) -> None: ) -> None:
@@ -31,7 +31,7 @@ class InferenceModel(ABC):
self.load_attempts = 0 self.load_attempts = 0
self.model_name = clean_name(model_name) self.model_name = clean_name(model_name)
self.cache_dir = Path(cache_dir) if cache_dir is not None else self._cache_dir_default self.cache_dir = Path(cache_dir) if cache_dir is not None else self._cache_dir_default
self.model_format = model_format if model_format is not None else self._model_format_default self.model_format = preferred_format if preferred_format is not None else self._model_format_default
if session is not None: if session is not None:
self.session = session self.session = session
@@ -48,7 +48,7 @@ class InferenceModel(ABC):
self.load_attempts += 1 self.load_attempts += 1
self.download() self.download()
attempt = f"Attempt #{self.load_attempts} to load" if self.load_attempts > 1 else "Loading" attempt = f"Attempt #{self.load_attempts + 1} to load" if self.load_attempts else "Loading"
log.info(f"{attempt} {self.model_type.replace('-', ' ')} model '{self.model_name}' to memory") log.info(f"{attempt} {self.model_type.replace('-', ' ')} model '{self.model_name}' to memory")
self.session = self._load() self.session = self._load()
self.loaded = True self.loaded = True
@@ -101,9 +101,6 @@ class InferenceModel(ABC):
self.cache_dir.mkdir(parents=True, exist_ok=True) self.cache_dir.mkdir(parents=True, exist_ok=True)
def _make_session(self, model_path: Path) -> ModelSession: def _make_session(self, model_path: Path) -> ModelSession:
if not model_path.is_file():
raise FileNotFoundError(f"Model file not found: {model_path}")
match model_path.suffix: match model_path.suffix:
case ".armnn": case ".armnn":
session: ModelSession = AnnSession(model_path) session: ModelSession = AnnSession(model_path)
@@ -147,13 +144,17 @@ class InferenceModel(ABC):
@property @property
def model_format(self) -> ModelFormat: def model_format(self) -> ModelFormat:
return self._model_format return self._preferred_format
@model_format.setter @model_format.setter
def model_format(self, model_format: ModelFormat) -> None: def model_format(self, preferred_format: ModelFormat) -> None:
log.debug(f"Setting model format to {model_format}") log.debug(f"Setting preferred format to {preferred_format}")
self._model_format = model_format self._preferred_format = preferred_format
@property @property
def _model_format_default(self) -> ModelFormat: def _model_format_default(self) -> ModelFormat:
return ModelFormat.ARMNN if ann.ann.is_available and settings.ann else ModelFormat.ONNX prefer_ann = ann.ann.is_available and settings.ann
ann_exists = (self.model_dir / "model.armnn").is_file()
if prefer_ann and not ann_exists:
log.warning(f"ARM NN is available, but '{self.model_name}' does not support ARM NN. Falling back to ONNX.")
return ModelFormat.ARMNN if prefer_ann and ann_exists else ModelFormat.ONNX
+1 -2
View File
@@ -22,12 +22,11 @@ class BaseCLIPTextualEncoder(InferenceModel):
return res return res
def _load(self) -> ModelSession: def _load(self) -> ModelSession:
session = super()._load()
log.debug(f"Loading tokenizer for CLIP model '{self.model_name}'") log.debug(f"Loading tokenizer for CLIP model '{self.model_name}'")
self.tokenizer = self._load_tokenizer() self.tokenizer = self._load_tokenizer()
log.debug(f"Loaded tokenizer for CLIP model '{self.model_name}'") log.debug(f"Loaded tokenizer for CLIP model '{self.model_name}'")
return session return super()._load()
@abstractmethod @abstractmethod
def _load_tokenizer(self) -> Tokenizer: def _load_tokenizer(self) -> Tokenizer:
@@ -1,3 +1,4 @@
from pathlib import Path
from typing import Any from typing import Any
import numpy as np import numpy as np
@@ -13,9 +14,15 @@ class FaceDetector(InferenceModel):
depends = [] depends = []
identity = (ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION) identity = (ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION)
def __init__(self, model_name: str, min_score: float = 0.7, **model_kwargs: Any) -> None: def __init__(
self,
model_name: str,
min_score: float = 0.7,
cache_dir: Path | str | None = None,
**model_kwargs: Any,
) -> None:
self.min_score = model_kwargs.pop("minScore", min_score) self.min_score = model_kwargs.pop("minScore", min_score)
super().__init__(model_name, **model_kwargs) super().__init__(model_name, cache_dir, **model_kwargs)
def _load(self) -> ModelSession: def _load(self) -> ModelSession:
session = self._make_session(self.model_path) session = self._make_session(self.model_path)
@@ -9,7 +9,7 @@ from numpy.typing import NDArray
from onnx.tools.update_model_dims import update_inputs_outputs_dims from onnx.tools.update_model_dims import update_inputs_outputs_dims
from PIL import Image from PIL import Image
from app.config import log from app.config import clean_name, log
from app.models.base import InferenceModel from app.models.base import InferenceModel
from app.models.transforms import decode_cv2 from app.models.transforms import decode_cv2
from app.schemas import FaceDetectionOutput, FacialRecognitionOutput, ModelFormat, ModelSession, ModelTask, ModelType from app.schemas import FaceDetectionOutput, FacialRecognitionOutput, ModelFormat, ModelSession, ModelTask, ModelType
@@ -20,14 +20,20 @@ class FaceRecognizer(InferenceModel):
depends = [(ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION)] depends = [(ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION)]
identity = (ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION) identity = (ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION)
def __init__(self, model_name: str, min_score: float = 0.7, **model_kwargs: Any) -> None: def __init__(
super().__init__(model_name, **model_kwargs) self,
model_name: str,
min_score: float = 0.7,
cache_dir: Path | str | None = None,
**model_kwargs: Any,
) -> None:
super().__init__(clean_name(model_name), cache_dir, **model_kwargs)
self.min_score = model_kwargs.pop("minScore", min_score) self.min_score = model_kwargs.pop("minScore", min_score)
self.batch = self.model_format == ModelFormat.ONNX self.batch = self.model_format == ModelFormat.ONNX
def _load(self) -> ModelSession: def _load(self) -> ModelSession:
session = self._make_session(self.model_path) session = self._make_session(self.model_path)
if self.batch and not has_batch_axis(session): if self.model_format == ModelFormat.ONNX and not has_batch_axis(session):
self._add_batch_axis(self.model_path) self._add_batch_axis(self.model_path)
session = self._make_session(self.model_path) session = self._make_session(self.model_path)
self.model = ArcFaceONNX( self.model = ArcFaceONNX(
+11 -74
View File
@@ -43,7 +43,7 @@ class TestBase:
assert encoder.cache_dir == cache_dir assert encoder.cache_dir == cache_dir
def test_sets_default_model_format(self, mocker: MockerFixture) -> None: def test_sets_default_preferred_format(self, mocker: MockerFixture) -> None:
mocker.patch.object(settings, "ann", True) mocker.patch.object(settings, "ann", True)
mocker.patch("ann.ann.is_available", False) mocker.patch("ann.ann.is_available", False)
@@ -51,7 +51,7 @@ class TestBase:
assert encoder.model_format == ModelFormat.ONNX assert encoder.model_format == ModelFormat.ONNX
def test_sets_default_model_format_to_armnn_if_available(self, path: mock.Mock, mocker: MockerFixture) -> None: def test_sets_default_preferred_format_to_armnn_if_available(self, path: mock.Mock, mocker: MockerFixture) -> None:
mocker.patch.object(settings, "ann", True) mocker.patch.object(settings, "ann", True)
mocker.patch("ann.ann.is_available", True) mocker.patch("ann.ann.is_available", True)
path.suffix = ".armnn" path.suffix = ".armnn"
@@ -60,11 +60,11 @@ class TestBase:
assert encoder.model_format == ModelFormat.ARMNN assert encoder.model_format == ModelFormat.ARMNN
def test_sets_model_format_kwarg(self, mocker: MockerFixture) -> None: def test_sets_preferred_format_kwarg(self, mocker: MockerFixture) -> None:
mocker.patch.object(settings, "ann", False) mocker.patch.object(settings, "ann", False)
mocker.patch("ann.ann.is_available", False) mocker.patch("ann.ann.is_available", False)
encoder = OpenClipTextualEncoder("ViT-B-32__openai", model_format=ModelFormat.ARMNN) encoder = OpenClipTextualEncoder("ViT-B-32__openai", preferred_format=ModelFormat.ARMNN)
assert encoder.model_format == ModelFormat.ARMNN assert encoder.model_format == ModelFormat.ARMNN
@@ -129,7 +129,7 @@ class TestBase:
) )
def test_download_downloads_armnn_if_preferred_format(self, snapshot_download: mock.Mock) -> None: def test_download_downloads_armnn_if_preferred_format(self, snapshot_download: mock.Mock) -> None:
encoder = OpenClipTextualEncoder("ViT-B-32__openai", model_format=ModelFormat.ARMNN) encoder = OpenClipTextualEncoder("ViT-B-32__openai", preferred_format=ModelFormat.ARMNN)
encoder.download() encoder.download()
snapshot_download.assert_called_once_with( snapshot_download.assert_called_once_with(
@@ -140,19 +140,6 @@ class TestBase:
ignore_patterns=[], ignore_patterns=[],
) )
def test_throws_exception_if_model_path_does_not_exist(
self, snapshot_download: mock.Mock, ort_session: mock.Mock, path: mock.Mock
) -> None:
path.return_value.__truediv__.return_value.__truediv__.return_value.is_file.return_value = False
encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir=path)
with pytest.raises(FileNotFoundError):
encoder.load()
snapshot_download.assert_called_once()
ort_session.assert_not_called()
@pytest.mark.usefixtures("ort_session") @pytest.mark.usefixtures("ort_session")
class TestOrtSession: class TestOrtSession:
@@ -480,18 +467,16 @@ class TestFaceRecognition:
assert isinstance(call_args[0][0], np.ndarray) assert isinstance(call_args[0][0], np.ndarray)
assert call_args[0][0].shape == (112, 112, 3) assert call_args[0][0].shape == (112, 112, 3)
def test_recognition_adds_batch_axis_for_ort( def test_recognition_adds_batch_axis_for_ort(self, ort_session: mock.Mock, mocker: MockerFixture) -> None:
self, ort_session: mock.Mock, path: mock.Mock, mocker: MockerFixture
) -> None:
onnx = mocker.patch("app.models.facial_recognition.recognition.onnx", autospec=True) onnx = mocker.patch("app.models.facial_recognition.recognition.onnx", autospec=True)
update_dims = mocker.patch( update_dims = mocker.patch(
"app.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True "app.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True
) )
mocker.patch("app.models.base.InferenceModel.download") mocker.patch("app.models.base.InferenceModel.download")
mocker.patch("app.models.facial_recognition.recognition.ArcFaceONNX") mocker.patch("app.models.facial_recognition.recognition.ArcFaceONNX")
ort_session.return_value.get_inputs.return_value = [SimpleNamespace(name="input.1", shape=(1, 3, 224, 224))] ort_session.return_value.get_inputs.return_value = [SimpleNamespace(name="input.1", shape=(1, 3, 224, 224))]
ort_session.return_value.get_outputs.return_value = [SimpleNamespace(name="output.1", shape=(1, 800))] ort_session.return_value.get_outputs.return_value = [SimpleNamespace(name="output.1", shape=(1, 800))]
path.return_value.__truediv__.return_value.__truediv__.return_value.suffix = ".onnx"
proto = mock.Mock() proto = mock.Mock()
@@ -507,30 +492,27 @@ class TestFaceRecognition:
onnx.load.return_value = proto onnx.load.return_value = proto
face_recognizer = FaceRecognizer("buffalo_s", cache_dir=path) face_recognizer = FaceRecognizer("buffalo_s")
face_recognizer.load() face_recognizer.load()
assert face_recognizer.batch is True assert face_recognizer.batch is True
update_dims.assert_called_once_with(proto, {"input.1": ["batch", 3, 224, 224]}, {"output.1": ["batch", 800]}) update_dims.assert_called_once_with(proto, {"input.1": ["batch", 3, 224, 224]}, {"output.1": ["batch", 800]})
onnx.save.assert_called_once_with(update_dims.return_value, face_recognizer.model_path) onnx.save.assert_called_once_with(update_dims.return_value, face_recognizer.model_path)
def test_recognition_does_not_add_batch_axis_if_exists( def test_recognition_does_not_add_batch_axis_if_exists(self, ort_session: mock.Mock, mocker: MockerFixture) -> None:
self, ort_session: mock.Mock, path: mock.Mock, mocker: MockerFixture
) -> None:
onnx = mocker.patch("app.models.facial_recognition.recognition.onnx", autospec=True) onnx = mocker.patch("app.models.facial_recognition.recognition.onnx", autospec=True)
update_dims = mocker.patch( update_dims = mocker.patch(
"app.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True "app.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True
) )
mocker.patch("app.models.base.InferenceModel.download") mocker.patch("app.models.base.InferenceModel.download")
mocker.patch("app.models.facial_recognition.recognition.ArcFaceONNX") mocker.patch("app.models.facial_recognition.recognition.ArcFaceONNX")
path.return_value.__truediv__.return_value.__truediv__.return_value.suffix = ".onnx"
inputs = [SimpleNamespace(name="input.1", shape=("batch", 3, 224, 224))] inputs = [SimpleNamespace(name="input.1", shape=("batch", 3, 224, 224))]
outputs = [SimpleNamespace(name="output.1", shape=("batch", 800))] outputs = [SimpleNamespace(name="output.1", shape=("batch", 800))]
ort_session.return_value.get_inputs.return_value = inputs ort_session.return_value.get_inputs.return_value = inputs
ort_session.return_value.get_outputs.return_value = outputs ort_session.return_value.get_outputs.return_value = outputs
face_recognizer = FaceRecognizer("buffalo_s", cache_dir=path) face_recognizer = FaceRecognizer("buffalo_s")
face_recognizer.load() face_recognizer.load()
assert face_recognizer.batch is True assert face_recognizer.batch is True
@@ -538,30 +520,6 @@ class TestFaceRecognition:
onnx.load.assert_not_called() onnx.load.assert_not_called()
onnx.save.assert_not_called() onnx.save.assert_not_called()
def test_recognition_does_not_add_batch_axis_for_armnn(
self, ann_session: mock.Mock, path: mock.Mock, mocker: MockerFixture
) -> None:
onnx = mocker.patch("app.models.facial_recognition.recognition.onnx", autospec=True)
update_dims = mocker.patch(
"app.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True
)
mocker.patch("app.models.base.InferenceModel.download")
mocker.patch("app.models.facial_recognition.recognition.ArcFaceONNX")
path.return_value.__truediv__.return_value.__truediv__.return_value.suffix = ".armnn"
inputs = [SimpleNamespace(name="input.1", shape=("batch", 3, 224, 224))]
outputs = [SimpleNamespace(name="output.1", shape=("batch", 800))]
ann_session.return_value.get_inputs.return_value = inputs
ann_session.return_value.get_outputs.return_value = outputs
face_recognizer = FaceRecognizer("buffalo_s", model_format=ModelFormat.ARMNN, cache_dir=path)
face_recognizer.load()
assert face_recognizer.batch is False
update_dims.assert_not_called()
onnx.load.assert_not_called()
onnx.save.assert_not_called()
@pytest.mark.asyncio @pytest.mark.asyncio
class TestCache: class TestCache:
@@ -735,7 +693,7 @@ class TestLoad:
mock_model.clear_cache.assert_called_once() mock_model.clear_cache.assert_called_once()
assert mock_model.load.call_count == 2 assert mock_model.load.call_count == 2
async def test_load_raises_if_os_error_and_already_retried(self) -> None: async def test_load_clears_cache_and_raises_if_os_error_and_already_retried(self) -> None:
mock_model = mock.Mock(spec=InferenceModel) mock_model = mock.Mock(spec=InferenceModel)
mock_model.model_name = "test_model_name" mock_model.model_name = "test_model_name"
mock_model.model_type = ModelType.VISUAL mock_model.model_type = ModelType.VISUAL
@@ -749,27 +707,6 @@ class TestLoad:
mock_model.clear_cache.assert_not_called() mock_model.clear_cache.assert_not_called()
mock_model.load.assert_not_called() mock_model.load.assert_not_called()
async def test_falls_back_to_onnx_if_other_format_does_not_exist(
self, exception: mock.Mock, warning: mock.Mock
) -> None:
mock_model = mock.Mock(spec=InferenceModel)
mock_model.model_name = "test_model_name"
mock_model.model_type = ModelType.VISUAL
mock_model.model_task = ModelTask.SEARCH
mock_model.model_format = ModelFormat.ARMNN
mock_model.loaded = False
mock_model.load_attempts = 0
error = FileNotFoundError()
mock_model.load.side_effect = [error, None]
await load(mock_model)
mock_model.clear_cache.assert_not_called()
assert mock_model.load.call_count == 2
exception.assert_called_once_with(error)
warning.assert_called_once_with("ARMNN is available, but model 'test_model_name' does not support it.")
mock_model.model_format = ModelFormat.ONNX
@pytest.mark.skipif( @pytest.mark.skipif(
not settings.test_full, not settings.test_full,
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "machine-learning" name = "machine-learning"
version = "1.108.0" version = "1.107.2"
description = "" description = ""
authors = ["Hau Tran <alex.tran1502@gmail.com>"] authors = ["Hau Tran <alex.tran1502@gmail.com>"]
readme = "README.md" readme = "README.md"
@@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="app.alextran.immich" <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="app.alextran.immich"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<application android:label="Immich" android:name=".ImmichApp" android:usesCleartextTraffic="true" <application android:label="Immich" android:name=".ImmichApp" android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher" android:requestLegacyExternalStorage="true" android:largeHeap="true"> android:icon="@mipmap/ic_launcher" android:requestLegacyExternalStorage="true">
<meta-data <meta-data
android:name="io.flutter.embedding.android.EnableImpeller" android:name="io.flutter.embedding.android.EnableImpeller"
+2 -2
View File
@@ -35,8 +35,8 @@ platform :android do
task: 'bundle', task: 'bundle',
build_type: 'Release', build_type: 'Release',
properties: { properties: {
"android.injected.version.code" => 148, "android.injected.version.code" => 147,
"android.injected.version.name" => "1.108.0", "android.injected.version.name" => "1.107.2",
} }
) )
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
+1 -1
View File
@@ -167,7 +167,7 @@ SPEC CHECKSUMS:
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
+1 -1
View File
@@ -19,7 +19,7 @@ platform :ios do
desc "iOS Beta" desc "iOS Beta"
lane :beta do lane :beta do
increment_version_number( increment_version_number(
version_number: "1.108.0" version_number: "1.107.2"
) )
increment_build_number( increment_build_number(
build_number: latest_testflight_build_number + 1, build_number: latest_testflight_build_number + 1,
+1 -1
View File
@@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.108.0 - API version: 1.107.2
- Generator version: 7.5.0 - Generator version: 7.5.0
- Build package: org.openapitools.codegen.languages.DartClientCodegen - Build package: org.openapitools.codegen.languages.DartClientCodegen
+2 -2
View File
@@ -1162,10 +1162,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: photo_manager name: photo_manager
sha256: "68d6099d07ce5033170f8368af8128a4555cf1d590a97242f83669552de989b1" sha256: "8cf79918f6de9843b394a1670fe1aec54ebcac852b4b4c9ef88211894547dc61"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.0" version: "3.0.0-dev.5"
photo_manager_image_provider: photo_manager_image_provider:
dependency: "direct main" dependency: "direct main"
description: description:
+2 -2
View File
@@ -2,7 +2,7 @@ name: immich_mobile
description: Immich - selfhosted backup media file on mobile phone description: Immich - selfhosted backup media file on mobile phone
publish_to: 'none' publish_to: 'none'
version: 1.108.0+148 version: 1.107.2+147
environment: environment:
sdk: '>=3.3.0 <4.0.0' sdk: '>=3.3.0 <4.0.0'
@@ -15,7 +15,7 @@ dependencies:
path_provider_ios: path_provider_ios:
# TODO: upgrade to stable after 3.0.1 is released. 3.0.0 is broken # TODO: upgrade to stable after 3.0.1 is released. 3.0.0 is broken
# https://github.com/fluttercandies/flutter_photo_manager/pull/990#issuecomment-2058066427 # https://github.com/fluttercandies/flutter_photo_manager/pull/990#issuecomment-2058066427
photo_manager: ^3.2.0 photo_manager: ^3.0.0-dev.5
photo_manager_image_provider: ^2.1.0 photo_manager_image_provider: ^2.1.0
flutter_hooks: ^0.20.4 flutter_hooks: ^0.20.4
hooks_riverpod: ^2.4.9 hooks_riverpod: ^2.4.9
+1 -1
View File
@@ -7007,7 +7007,7 @@
"info": { "info": {
"title": "Immich", "title": "Immich",
"description": "Immich API", "description": "Immich API",
"version": "1.108.0", "version": "1.107.2",
"contact": {} "contact": {}
}, },
"tags": [], "tags": [],
+1 -1
View File
@@ -1 +1 @@
20.15.1 20.15.0
+6 -6
View File
@@ -1,18 +1,18 @@
{ {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.108.0", "version": "1.107.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.108.0", "version": "1.107.2",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"@oazapfts/runtime": "^1.0.2" "@oazapfts/runtime": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.14.10", "@types/node": "^20.14.9",
"typescript": "^5.3.3" "typescript": "^5.3.3"
} }
}, },
@@ -22,9 +22,9 @@
"integrity": "sha512-8tKiYffhwTGHSHYGnZ3oneLGCjX0po/XAXQ5Ng9fqKkvIdl/xz8+Vh8i+6xjzZqvZ2pLVpUcuSfnvNI/x67L0g==" "integrity": "sha512-8tKiYffhwTGHSHYGnZ3oneLGCjX0po/XAXQ5Ng9fqKkvIdl/xz8+Vh8i+6xjzZqvZ2pLVpUcuSfnvNI/x67L0g=="
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.14.10", "version": "20.14.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz",
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.108.0", "version": "1.107.2",
"description": "Auto-generated TypeScript SDK for the Immich API", "description": "Auto-generated TypeScript SDK for the Immich API",
"type": "module", "type": "module",
"main": "./build/index.js", "main": "./build/index.js",
@@ -19,7 +19,7 @@
"@oazapfts/runtime": "^1.0.2" "@oazapfts/runtime": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.14.10", "@types/node": "^20.14.9",
"typescript": "^5.3.3" "typescript": "^5.3.3"
}, },
"repository": { "repository": {
@@ -28,6 +28,6 @@
"directory": "open-api/typescript-sdk" "directory": "open-api/typescript-sdk"
}, },
"volta": { "volta": {
"node": "20.15.1" "node": "20.15.0"
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
/** /**
* Immich * Immich
* 1.108.0 * 1.107.2
* DO NOT MODIFY - This file has been generated using oazapfts. * DO NOT MODIFY - This file has been generated using oazapfts.
* See https://www.npmjs.com/package/oazapfts * See https://www.npmjs.com/package/oazapfts
*/ */
+1 -1
View File
@@ -1 +1 @@
20.15.1 20.15
+3 -3
View File
@@ -1,5 +1,5 @@
# dev build # dev build
FROM ghcr.io/immich-app/base-server-dev:20240710@sha256:5944dac5d73dc54d733461db555786c823460e730f6793ea5ad4ee3843a75c7d as dev FROM ghcr.io/immich-app/base-server-dev:20240708@sha256:2a9e3231c34493cb861299d475c84c031e7f04519dbc895bbebb5017d479a3cb as dev
RUN apt-get install --no-install-recommends -yqq tini RUN apt-get install --no-install-recommends -yqq tini
WORKDIR /usr/src/app WORKDIR /usr/src/app
@@ -25,7 +25,7 @@ COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img
COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl
# web build # web build
FROM node:20.15.1-alpine3.20@sha256:34b7aa411056c85dbf71d240d26516949b3f72b318d796c26b57caaa1df5639a as web FROM node:20.15.0-alpine3.20@sha256:df01469346db2bf1cfc1f7261aeab86b2960efa840fe2bd46d83ff339f463665 as web
WORKDIR /usr/src/open-api/typescript-sdk WORKDIR /usr/src/open-api/typescript-sdk
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
@@ -41,7 +41,7 @@ RUN npm run build
# prod build # prod build
FROM ghcr.io/immich-app/base-server-prod:20240710@sha256:bc3cda314634467d5a92b78c09ba566246e00ee8495aac398f5c1000c5e26fa9 FROM ghcr.io/immich-app/base-server-prod:20240708@sha256:0af3a5bb036c9a4b6a5a51becaa6e94fe182f6bc97480d57e8f2e6f994bfa453
WORKDIR /usr/src/app WORKDIR /usr/src/app
ENV NODE_ENV=production \ ENV NODE_ENV=production \
+585 -409
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "immich", "name": "immich",
"version": "1.108.0", "version": "1.107.2",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,
@@ -46,7 +46,7 @@
"@nestjs/swagger": "^7.1.8", "@nestjs/swagger": "^7.1.8",
"@nestjs/typeorm": "^10.0.0", "@nestjs/typeorm": "^10.0.0",
"@nestjs/websockets": "^10.2.2", "@nestjs/websockets": "^10.2.2",
"@opentelemetry/auto-instrumentations-node": "^0.48.0", "@opentelemetry/auto-instrumentations-node": "^0.47.0",
"@opentelemetry/context-async-hooks": "^1.24.0", "@opentelemetry/context-async-hooks": "^1.24.0",
"@opentelemetry/exporter-prometheus": "^0.52.0", "@opentelemetry/exporter-prometheus": "^0.52.0",
"@opentelemetry/sdk-node": "^0.52.0", "@opentelemetry/sdk-node": "^0.52.0",
@@ -106,7 +106,7 @@
"@types/lodash": "^4.14.197", "@types/lodash": "^4.14.197",
"@types/mock-fs": "^4.13.1", "@types/mock-fs": "^4.13.1",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"@types/node": "^20.14.10", "@types/node": "^20.14.9",
"@types/nodemailer": "^6.4.14", "@types/nodemailer": "^6.4.14",
"@types/picomatch": "^2.3.3", "@types/picomatch": "^2.3.3",
"@types/semver": "^7.5.8", "@types/semver": "^7.5.8",
@@ -132,6 +132,6 @@
"vitest": "^1.5.0" "vitest": "^1.5.0"
}, },
"volta": { "volta": {
"node": "20.15.1" "node": "20.15.0"
} }
} }
+1 -2
View File
@@ -5,7 +5,6 @@ import { APIKeyEntity } from 'src/entities/api-key.entity';
import { SessionEntity } from 'src/entities/session.entity'; import { SessionEntity } from 'src/entities/session.entity';
import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { SharedLinkEntity } from 'src/entities/shared-link.entity';
import { UserEntity } from 'src/entities/user.entity'; import { UserEntity } from 'src/entities/user.entity';
import { toEmail } from 'src/validation';
export enum ImmichCookie { export enum ImmichCookie {
ACCESS_TOKEN = 'immich_access_token', ACCESS_TOKEN = 'immich_access_token',
@@ -42,7 +41,7 @@ export class AuthDto {
export class LoginCredentialDto { export class LoginCredentialDto {
@IsEmail({ require_tld: false }) @IsEmail({ require_tld: false })
@Transform(toEmail) @Transform(({ value }) => value?.toLowerCase())
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({ example: 'testuser@email.com' }) @ApiProperty({ example: 'testuser@email.com' })
email!: string; email!: string;
-16
View File
@@ -38,22 +38,6 @@ describe('create user DTO', () => {
expect(errors).toHaveLength(0); expect(errors).toHaveLength(0);
}); });
it('validates invalid email type', async () => {
let dto = plainToInstance(UserAdminCreateDto, {
email: [],
password: 'some password',
name: 'some name',
});
expect(await validate(dto)).toHaveLength(1);
dto = plainToInstance(UserAdminCreateDto, {
email: {},
password: 'some password',
name: 'some name',
});
expect(await validate(dto)).toHaveLength(1);
});
it('should allow emails without a tld', async () => { it('should allow emails without a tld', async () => {
const someEmail = 'test@test'; const someEmail = 'test@test';
+4 -1
View File
@@ -1,10 +1,11 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
import { AssetOrder } from 'src/entities/album.entity'; import { AlbumEntity, AssetOrder } from 'src/entities/album.entity';
import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity'; import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity';
import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { AssetEntity, AssetType } from 'src/entities/asset.entity';
import { ExifEntity } from 'src/entities/exif.entity'; import { ExifEntity } from 'src/entities/exif.entity';
import { PartnerEntity } from 'src/entities/partner.entity';
import { SmartInfoEntity } from 'src/entities/smart-info.entity'; import { SmartInfoEntity } from 'src/entities/smart-info.entity';
import { import {
AssetBuilderOptions, AssetBuilderOptions,
@@ -62,6 +63,8 @@ export class AssetRepository implements IAssetRepository {
@InjectRepository(ExifEntity) private exifRepository: Repository<ExifEntity>, @InjectRepository(ExifEntity) private exifRepository: Repository<ExifEntity>,
@InjectRepository(AssetJobStatusEntity) private jobStatusRepository: Repository<AssetJobStatusEntity>, @InjectRepository(AssetJobStatusEntity) private jobStatusRepository: Repository<AssetJobStatusEntity>,
@InjectRepository(SmartInfoEntity) private smartInfoRepository: Repository<SmartInfoEntity>, @InjectRepository(SmartInfoEntity) private smartInfoRepository: Repository<SmartInfoEntity>,
@InjectRepository(PartnerEntity) private partnerRepository: Repository<PartnerEntity>,
@InjectRepository(AlbumEntity) private albumRepository: Repository<AlbumEntity>,
) {} ) {}
async upsertExif(exif: Partial<ExifEntity>): Promise<void> { async upsertExif(exif: Partial<ExifEntity>): Promise<void> {
@@ -1,19 +1,22 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { DefaultReadTaskOptions, ExifTool, Tags } from 'exiftool-vendored'; import { DefaultReadTaskOptions, ExifTool, Tags } from 'exiftool-vendored';
import geotz from 'geo-tz'; import geotz from 'geo-tz';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { ExifEntity } from 'src/entities/exif.entity'; import { ExifEntity } from 'src/entities/exif.entity';
import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.interface'; import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.interface';
import { Instrumentation } from 'src/utils/instrumentation'; import { Instrumentation } from 'src/utils/instrumentation';
import { Repository } from 'typeorm'; import { DataSource, Repository } from 'typeorm';
@Instrumentation() @Instrumentation()
@Injectable() @Injectable()
export class MetadataRepository implements IMetadataRepository { export class MetadataRepository implements IMetadataRepository {
constructor( constructor(
@InjectRepository(ExifEntity) private exifRepository: Repository<ExifEntity>, @InjectRepository(ExifEntity) private exifRepository: Repository<ExifEntity>,
@InjectRepository(GeodataPlacesEntity) private geodataPlacesRepository: Repository<GeodataPlacesEntity>,
@InjectDataSource() private dataSource: DataSource,
@Inject(ILoggerRepository) private logger: ILoggerRepository, @Inject(ILoggerRepository) private logger: ILoggerRepository,
) { ) {
this.logger.setContext(MetadataRepository.name); this.logger.setContext(MetadataRepository.name);
@@ -1,117 +0,0 @@
import { defaults, SystemConfig } from 'src/config';
import { IAlbumRepository } from 'src/interfaces/album.interface';
import { IAssetRepository } from 'src/interfaces/asset.interface';
import { IJobRepository } from 'src/interfaces/job.interface';
import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { INotificationRepository } from 'src/interfaces/notification.interface';
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
import { IUserRepository } from 'src/interfaces/user.interface';
import { NotificationService } from 'src/services/notification.service';
import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newJobRepositoryMock } from 'test/repositories/job.repository.mock';
import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock';
import { newNotificationRepositoryMock } from 'test/repositories/notification.repository.mock';
import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock';
import { newUserRepositoryMock } from 'test/repositories/user.repository.mock';
import { Mocked } from 'vitest';
const configs = {
smtpDisabled: Object.freeze<SystemConfig>({
...defaults,
notifications: {
smtp: {
...defaults.notifications.smtp,
enabled: false,
},
},
}),
smtpEnabled: Object.freeze<SystemConfig>({
...defaults,
notifications: {
smtp: {
...defaults.notifications.smtp,
enabled: true,
},
},
}),
smtpTransport: Object.freeze<SystemConfig>({
...defaults,
notifications: {
smtp: {
...defaults.notifications.smtp,
enabled: true,
transport: {
ignoreCert: false,
host: 'localhost',
port: 587,
username: 'test',
password: 'test',
},
},
},
}),
};
describe(NotificationService.name, () => {
let sut: NotificationService;
let systemMock: Mocked<ISystemMetadataRepository>;
let notificationMock: Mocked<INotificationRepository>;
let userMock: Mocked<IUserRepository>;
let jobMock: Mocked<IJobRepository>;
let loggerMock: Mocked<ILoggerRepository>;
let assetMock: Mocked<IAssetRepository>;
let albumMock: Mocked<IAlbumRepository>;
beforeEach(() => {
systemMock = newSystemMetadataRepositoryMock();
notificationMock = newNotificationRepositoryMock();
userMock = newUserRepositoryMock();
jobMock = newJobRepositoryMock();
loggerMock = newLoggerRepositoryMock();
assetMock = newAssetRepositoryMock();
albumMock = newAlbumRepositoryMock();
sut = new NotificationService(systemMock, notificationMock, userMock, jobMock, loggerMock, assetMock, albumMock);
});
it('should work', () => {
expect(sut).toBeDefined();
});
describe('onConfigValidateEvent', () => {
it('validates smtp config when enabling smtp', async () => {
const oldConfig = configs.smtpDisabled;
const newConfig = configs.smtpEnabled;
notificationMock.verifySmtp.mockResolvedValue(true);
await expect(sut.onConfigValidateEvent({ oldConfig, newConfig })).resolves.not.toThrow();
expect(notificationMock.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport);
});
it('validates smtp config when transport changes', async () => {
const oldConfig = configs.smtpEnabled;
const newConfig = configs.smtpTransport;
notificationMock.verifySmtp.mockResolvedValue(true);
await expect(sut.onConfigValidateEvent({ oldConfig, newConfig })).resolves.not.toThrow();
expect(notificationMock.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport);
});
it('skips smtp validation when there are no changes', async () => {
const oldConfig = { ...configs.smtpEnabled };
const newConfig = { ...configs.smtpEnabled };
await expect(sut.onConfigValidateEvent({ oldConfig, newConfig })).resolves.not.toThrow();
expect(notificationMock.verifySmtp).not.toHaveBeenCalled();
});
it('skips smtp validation when smtp is disabled', async () => {
const oldConfig = { ...configs.smtpEnabled };
const newConfig = { ...configs.smtpDisabled };
await expect(sut.onConfigValidateEvent({ oldConfig, newConfig })).resolves.not.toThrow();
expect(notificationMock.verifySmtp).not.toHaveBeenCalled();
});
});
});
+2 -6
View File
@@ -1,5 +1,4 @@
import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
import { isEqual } from 'lodash';
import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants'; import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants';
import { SystemConfigCore } from 'src/cores/system-config.core'; import { SystemConfigCore } from 'src/cores/system-config.core';
import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto';
@@ -45,12 +44,9 @@ export class NotificationService implements OnEvents {
this.configCore = SystemConfigCore.create(systemMetadataRepository, logger); this.configCore = SystemConfigCore.create(systemMetadataRepository, logger);
} }
async onConfigValidateEvent({ oldConfig, newConfig }: SystemConfigUpdateEvent) { async onConfigValidateEvent({ newConfig }: SystemConfigUpdateEvent) {
try { try {
if ( if (newConfig.notifications.smtp.enabled) {
newConfig.notifications.smtp.enabled &&
!isEqual(oldConfig.notifications.smtp, newConfig.notifications.smtp)
) {
await this.notificationRepository.verifySmtp(newConfig.notifications.smtp.transport); await this.notificationRepository.verifySmtp(newConfig.notifications.smtp.transport);
} }
} catch (error: Error | any) { } catch (error: Error | any) {
+3 -6
View File
@@ -152,14 +152,11 @@ export function validateCronExpression(expression: string) {
return true; return true;
} }
type IValue = { value: unknown }; type IValue = { value: string };
export const toEmail = ({ value }: IValue) => (typeof value === 'string' ? value.toLowerCase() : value); export const toEmail = ({ value }: IValue) => (value ? value.toLowerCase() : value);
export const toSanitized = ({ value }: IValue) => { export const toSanitized = ({ value }: IValue) => sanitize((value || '').replaceAll('.', ''));
const input = typeof value === 'string' ? value : '';
return sanitize(input.replaceAll('.', ''));
};
export const isValidInteger = (value: number, options: { min?: number; max?: number }): value is number => { export const isValidInteger = (value: number, options: { min?: number; max?: number }): value is number => {
const { min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER } = options; const { min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER } = options;
+1 -1
View File
@@ -1 +1 @@
20.15.1 20.15
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:20.15.1-alpine3.20@sha256:34b7aa411056c85dbf71d240d26516949b3f72b318d796c26b57caaa1df5639a FROM node:20.15.0-alpine3.20@sha256:df01469346db2bf1cfc1f7261aeab86b2960efa840fe2bd46d83ff339f463665
RUN apk add --no-cache tini RUN apk add --no-cache tini
USER node USER node
+19 -19
View File
@@ -1,12 +1,12 @@
{ {
"name": "immich-web", "name": "immich-web",
"version": "1.108.0", "version": "1.107.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "immich-web", "name": "immich-web",
"version": "1.108.0", "version": "1.107.2",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"@formatjs/icu-messageformat-parser": "^2.7.8", "@formatjs/icu-messageformat-parser": "^2.7.8",
@@ -70,13 +70,13 @@
}, },
"../open-api/typescript-sdk": { "../open-api/typescript-sdk": {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.108.0", "version": "1.107.2",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"@oazapfts/runtime": "^1.0.2" "@oazapfts/runtime": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.14.10", "@types/node": "^20.14.9",
"typescript": "^5.3.3" "typescript": "^5.3.3"
} }
}, },
@@ -1658,30 +1658,30 @@
} }
}, },
"node_modules/@photo-sphere-viewer/core": { "node_modules/@photo-sphere-viewer/core": {
"version": "5.8.2", "version": "5.8.1",
"resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.8.2.tgz", "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.8.1.tgz",
"integrity": "sha512-7Ex8OLk5ihywT/WpYz/+No6BlGzo/XDbW8M3pe2diBEYU7xXfxQjhQ7WKFRuaKasNrCUNks8r6jM+pUkl4MOtg==", "integrity": "sha512-mxkVNtXSWHGtvPMtBJ+uXA/cxzo4dEZm74rffg14VvF1lmIR7wyqDGCXtdXjFYLRGHjiNuimhDhfaqbM/SoSYQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"three": "^0.166.1" "three": "^0.165.0"
} }
}, },
"node_modules/@photo-sphere-viewer/equirectangular-video-adapter": { "node_modules/@photo-sphere-viewer/equirectangular-video-adapter": {
"version": "5.8.2", "version": "5.8.1",
"resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.8.2.tgz", "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.8.1.tgz",
"integrity": "sha512-HaT7GsI0xydp9vaeZnWQy2jNa0TDb0CohecdlyfQNFtvG4WhpaLnibJgMQSc8m1GtsydK3cGN7HArD0fhAEyIA==", "integrity": "sha512-JUqRhLN+f4RtYmvFtZBpRH4y80DCY5y00HAc26rLAh6eGCs8s4By73n5+j0ICxoaAgARuvt+kw0cdcf18AhrIQ==",
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"@photo-sphere-viewer/core": "5.8.2" "@photo-sphere-viewer/core": "5.8.1"
} }
}, },
"node_modules/@photo-sphere-viewer/video-plugin": { "node_modules/@photo-sphere-viewer/video-plugin": {
"version": "5.8.2", "version": "5.8.1",
"resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.8.2.tgz", "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.8.1.tgz",
"integrity": "sha512-HKDRkIbGqj4/k0csLRVrLXebkreHINqnb4Os+70VAjSuaK4VxRlmFy5R/LYy6nA7SDxrJR57Nq4//n75DBBDkg==", "integrity": "sha512-EW8CAxAw3kyAyWvTZJFCsE+HzALYGXfcYYrbJjQLBuWObi8b1VSyT63zzzmPs9HwO9o745hyyeEOWbH5g1q5QA==",
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"@photo-sphere-viewer/core": "5.8.2" "@photo-sphere-viewer/core": "5.8.1"
} }
}, },
"node_modules/@polka/url": { "node_modules/@polka/url": {
@@ -8447,9 +8447,9 @@
} }
}, },
"node_modules/three": { "node_modules/three": {
"version": "0.166.1", "version": "0.165.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.166.1.tgz", "resolved": "https://registry.npmjs.org/three/-/three-0.165.0.tgz",
"integrity": "sha512-LtuafkKHHzm61AQA1be2MAYIw1IjmhOUxhBa0prrLpEMWbV7ijvxCRHjSgHPGp2493wLBzwKV46tA9nivLEgKg==", "integrity": "sha512-cc96IlVYGydeceu0e5xq70H8/yoVT/tXBxV/W8A/U6uOq7DXc4/s1Mkmnu6SqoYGhSRWWYFOhVwvq6V0VtbplA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/thumbhash": { "node_modules/thumbhash": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "immich-web", "name": "immich-web",
"version": "1.108.0", "version": "1.107.2",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"scripts": { "scripts": {
"dev": "vite dev --host 0.0.0.0 --port 3000", "dev": "vite dev --host 0.0.0.0 --port 3000",
@@ -83,6 +83,6 @@
"thumbhash": "^0.1.1" "thumbhash": "^0.1.1"
}, },
"volta": { "volta": {
"node": "20.15.1" "node": "20.15.0"
} }
} }
@@ -9,8 +9,8 @@
import { getConfig, getConfigDefaults, updateConfig, type SystemConfigDto } from '@immich/sdk'; import { getConfig, getConfigDefaults, updateConfig, type SystemConfigDto } from '@immich/sdk';
import { loadConfig } from '$lib/stores/server-config.store'; import { loadConfig } from '$lib/stores/server-config.store';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { onMount } from 'svelte'; import { createEventDispatcher, onMount } from 'svelte';
import type { SettingsResetOptions } from './admin-settings'; import type { SettingsEventType } from './admin-settings';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
export let config: SystemConfigDto; export let config: SystemConfigDto;
@@ -18,8 +18,10 @@
let savedConfig: SystemConfigDto; let savedConfig: SystemConfigDto;
let defaultConfig: SystemConfigDto; let defaultConfig: SystemConfigDto;
const handleReset = async (options: SettingsResetOptions) => { const dispatch = createEventDispatcher<{ save: void }>();
await (options.default ? resetToDefault(options.configKeys) : reset(options.configKeys));
const handleReset = async (detail: SettingsEventType['reset']) => {
await (detail.default ? resetToDefault(detail.configKeys) : reset(detail.configKeys));
}; };
export const handleSave = async (update: Partial<SystemConfigDto>) => { export const handleSave = async (update: Partial<SystemConfigDto>) => {
@@ -36,6 +38,8 @@
notificationController.show({ message: $t('settings_saved'), type: NotificationType.Info }); notificationController.show({ message: $t('settings_saved'), type: NotificationType.Info });
await loadConfig(); await loadConfig();
dispatch('save');
} catch (error) { } catch (error) {
handleError(error, $t('errors.unable_to_save_settings')); handleError(error, $t('errors.unable_to_save_settings'));
} }
@@ -1,15 +1,7 @@
import type { ResetOptions } from '$lib/utils/dipatch'; import type { ResetOptions } from '$lib/utils/dipatch';
import type { SystemConfigDto } from '@immich/sdk'; import type { SystemConfigDto } from '@immich/sdk';
export type SettingsResetOptions = ResetOptions & { configKeys: Array<keyof SystemConfigDto> }; export type SettingsEventType = {
export type SettingsResetEvent = (options: SettingsResetOptions) => void; reset: ResetOptions & { configKeys: Array<keyof SystemConfigDto> };
export type SettingsSaveEvent = (config: Partial<SystemConfigDto>) => void; save: Partial<SystemConfigDto>;
export type SettingsComponentProps = {
disabled?: boolean;
defaultConfig: SystemConfigDto;
config: SystemConfigDto;
savedConfig: SystemConfigDto;
onReset: SettingsResetEvent;
onSave: SettingsSaveEvent;
}; };
@@ -8,8 +8,9 @@
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
import { type SystemConfigDto } from '@immich/sdk'; import { type SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
import FormatMessage from '$lib/components/i18n/format-message.svelte'; import FormatMessage from '$lib/components/i18n/format-message.svelte';
@@ -17,8 +18,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
let isConfirmOpen = false; let isConfirmOpen = false;
@@ -38,7 +39,7 @@
} }
isConfirmOpen = false; isConfirmOpen = false;
onSave({ passwordLogin: config.passwordLogin, oauth: config.oauth }); dispatch('save', { passwordLogin: config.passwordLogin, oauth: config.oauth });
}; };
</script> </script>
@@ -239,8 +240,8 @@
showResetToDefault={!isEqual(savedConfig.passwordLogin, defaultConfig.passwordLogin) || showResetToDefault={!isEqual(savedConfig.passwordLogin, defaultConfig.passwordLogin) ||
!isEqual(savedConfig.oauth, defaultConfig.oauth)} !isEqual(savedConfig.oauth, defaultConfig.oauth)}
{disabled} {disabled}
onReset={(options) => onReset({ ...options, configKeys: ['passwordLogin', 'oauth'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['passwordLogin', 'oauth'] })}
onSave={() => handleSave(false)} on:save={() => handleSave(false)}
/> />
</div> </div>
</form> </form>
@@ -11,8 +11,9 @@
} from '@immich/sdk'; } from '@immich/sdk';
import { mdiHelpCircleOutline } from '@mdi/js'; import { mdiHelpCircleOutline } from '@mdi/js';
import { isEqual, sortBy } from 'lodash-es'; import { isEqual, sortBy } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
import SettingInputField, { import SettingInputField, {
SettingInputFieldType, SettingInputFieldType,
@@ -28,8 +29,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
</script> </script>
<div> <div>
@@ -367,8 +368,8 @@
<div class="ml-4"> <div class="ml-4">
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['ffmpeg'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['ffmpeg'] })}
onSave={() => onSave({ ffmpeg: config.ffmpeg })} on:save={() => dispatch('save', { ffmpeg: config.ffmpeg })}
showResetToDefault={!isEqual(savedConfig.ffmpeg, defaultConfig.ffmpeg)} showResetToDefault={!isEqual(savedConfig.ffmpeg, defaultConfig.ffmpeg)}
{disabled} {disabled}
/> />
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import { Colorspace, ImageFormat, type SystemConfigDto } from '@immich/sdk'; import { Colorspace, ImageFormat, type SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte'; import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
@@ -16,8 +17,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
</script> </script>
<div> <div>
@@ -113,8 +114,8 @@
<div class="ml-4"> <div class="ml-4">
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['image'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['image'] })}
onSave={() => onSave({ image: config.image })} on:save={() => dispatch('save', { image: config.image })}
showResetToDefault={!isEqual(savedConfig.image, defaultConfig.image)} showResetToDefault={!isEqual(savedConfig.image, defaultConfig.image)}
{disabled} {disabled}
/> />
@@ -2,8 +2,9 @@
import { getJobName } from '$lib/utils'; import { getJobName } from '$lib/utils';
import { JobName, type SystemConfigDto, type SystemConfigJobDto } from '@immich/sdk'; import { JobName, type SystemConfigDto, type SystemConfigJobDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
import SettingInputField, { import SettingInputField, {
SettingInputFieldType, SettingInputFieldType,
@@ -14,8 +15,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
const jobNames = [ const jobNames = [
JobName.ThumbnailGeneration, JobName.ThumbnailGeneration,
@@ -66,8 +67,8 @@
<div class="ml-4"> <div class="ml-4">
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['job'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['job'] })}
onSave={() => onSave({ job: config.job })} on:save={() => dispatch('save', { job: config.job })}
showResetToDefault={!isEqual(savedConfig.job, defaultConfig.job)} showResetToDefault={!isEqual(savedConfig.job, defaultConfig.job)}
{disabled} {disabled}
/> />
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import type { SystemConfigDto } from '@immich/sdk'; import type { SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
import SettingInputField, { import SettingInputField, {
SettingInputFieldType, SettingInputFieldType,
@@ -16,8 +17,6 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent;
$: cronExpressionOptions = [ $: cronExpressionOptions = [
{ title: $t('interval.night_at_midnight'), expression: '0 0 * * *' }, { title: $t('interval.night_at_midnight'), expression: '0 0 * * *' },
@@ -25,6 +24,8 @@
{ title: $t('interval.day_at_onepm'), expression: '0 13 * * *' }, { title: $t('interval.day_at_onepm'), expression: '0 13 * * *' },
{ title: $t('interval.hours', { values: { hours: 6 } }), expression: '0 */6 * * *' }, { title: $t('interval.hours', { values: { hours: 6 } }), expression: '0 */6 * * *' },
]; ];
const dispatch = createEventDispatcher<SettingsEventType>();
</script> </script>
<div> <div>
@@ -46,8 +47,8 @@
<div class="ml-4"> <div class="ml-4">
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['library'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['library'] })}
onSave={() => onSave({ library: config.library })} on:save={() => dispatch('save', { library: config.library })}
showResetToDefault={!isEqual(savedConfig.library, defaultConfig.library)} showResetToDefault={!isEqual(savedConfig.library, defaultConfig.library)}
{disabled} {disabled}
/> />
@@ -111,8 +112,8 @@
<div class="ml-4"> <div class="ml-4">
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['library'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['library'] })}
onSave={() => onSave({ library: config.library })} on:save={() => dispatch('save', { library: config.library })}
showResetToDefault={!isEqual(savedConfig.library, defaultConfig.library)} showResetToDefault={!isEqual(savedConfig.library, defaultConfig.library)}
{disabled} {disabled}
/> />
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import { LogLevel, type SystemConfigDto } from '@immich/sdk'; import { LogLevel, type SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte'; import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
@@ -12,8 +13,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
</script> </script>
<div> <div>
@@ -43,8 +44,8 @@
/> />
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['logging'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['logging'] })}
onSave={() => onSave({ logging: config.logging })} on:save={() => dispatch('save', { logging: config.logging })}
showResetToDefault={!isEqual(savedConfig.logging, defaultConfig.logging)} showResetToDefault={!isEqual(savedConfig.logging, defaultConfig.logging)}
{disabled} {disabled}
/> />
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import type { SystemConfigDto } from '@immich/sdk'; import type { SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
import SettingInputField, { import SettingInputField, {
@@ -18,8 +19,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
</script> </script>
<div class="mt-2"> <div class="mt-2">
@@ -180,8 +181,8 @@
</SettingAccordion> </SettingAccordion>
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['machineLearning'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['machineLearning'] })}
onSave={() => onSave({ machineLearning: config.machineLearning })} on:save={() => dispatch('save', { machineLearning: config.machineLearning })}
showResetToDefault={!isEqual(savedConfig.machineLearning, defaultConfig.machineLearning)} showResetToDefault={!isEqual(savedConfig.machineLearning, defaultConfig.machineLearning)}
{disabled} {disabled}
/> />
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import type { SystemConfigDto } from '@immich/sdk'; import type { SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
@@ -16,8 +17,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
</script> </script>
<div class="mt-2"> <div class="mt-2">
@@ -74,8 +75,8 @@
> >
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['map', 'reverseGeocoding'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['map', 'reverseGeocoding'] })}
onSave={() => onSave({ map: config.map, reverseGeocoding: config.reverseGeocoding })} on:save={() => dispatch('save', { map: config.map, reverseGeocoding: config.reverseGeocoding })}
showResetToDefault={!isEqual( showResetToDefault={!isEqual(
{ map: savedConfig.map, reverseGeocoding: savedConfig.reverseGeocoding }, { map: savedConfig.map, reverseGeocoding: savedConfig.reverseGeocoding },
{ map: defaultConfig.map, reverseGeocoding: defaultConfig.reverseGeocoding }, { map: defaultConfig.map, reverseGeocoding: defaultConfig.reverseGeocoding },
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import type { SystemConfigDto } from '@immich/sdk'; import type { SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
@@ -11,8 +12,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
</script> </script>
<div> <div>
@@ -25,8 +26,8 @@
{disabled} {disabled}
/> />
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['newVersionCheck'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['newVersionCheck'] })}
onSave={() => onSave({ newVersionCheck: config.newVersionCheck })} on:save={() => dispatch('save', { newVersionCheck: config.newVersionCheck })}
showResetToDefault={!isEqual(savedConfig.newVersionCheck, defaultConfig.newVersionCheck)} showResetToDefault={!isEqual(savedConfig.newVersionCheck, defaultConfig.newVersionCheck)}
{disabled} {disabled}
/> />
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import { sendTestEmail, type SystemConfigDto } from '@immich/sdk'; import { sendTestEmail, type SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingInputField, { import SettingInputField, {
SettingInputFieldType, SettingInputFieldType,
} from '$lib/components/shared-components/settings/setting-input-field.svelte'; } from '$lib/components/shared-components/settings/setting-input-field.svelte';
@@ -23,10 +24,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent;
let isSending = false; let isSending = false;
const dispatch = createEventDispatcher<SettingsEventType>();
const handleSendTestEmail = async () => { const handleSendTestEmail = async () => {
if (isSending) { if (isSending) {
@@ -57,7 +56,7 @@
}); });
if (!disabled) { if (!disabled) {
onSave({ notifications: config.notifications }); dispatch('save', { notifications: config.notifications });
} }
} catch (error) { } catch (error) {
handleError(error, $t('admin.notification_email_test_email_failed')); handleError(error, $t('admin.notification_email_test_email_failed'));
@@ -157,8 +156,8 @@
</div> </div>
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['notifications'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['notifications'] })}
onSave={() => onSave({ notifications: config.notifications })} on:save={() => dispatch('save', { notifications: config.notifications })}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import type { SystemConfigDto } from '@immich/sdk'; import type { SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingInputField, { import SettingInputField, {
SettingInputFieldType, SettingInputFieldType,
} from '$lib/components/shared-components/settings/setting-input-field.svelte'; } from '$lib/components/shared-components/settings/setting-input-field.svelte';
@@ -13,8 +14,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
</script> </script>
<div> <div>
@@ -39,8 +40,8 @@
<div class="ml-4"> <div class="ml-4">
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['server'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['server'] })}
onSave={() => onSave({ server: config.server })} on:save={() => dispatch('save', { server: config.server })}
showResetToDefault={!isEqual(savedConfig.server, defaultConfig.server)} showResetToDefault={!isEqual(savedConfig.server, defaultConfig.server)}
{disabled} {disabled}
/> />
@@ -10,8 +10,9 @@
import handlebar from 'handlebars'; import handlebar from 'handlebars';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import * as luxon from 'luxon'; import * as luxon from 'luxon';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SupportedDatetimePanel from './supported-datetime-panel.svelte'; import SupportedDatetimePanel from './supported-datetime-panel.svelte';
import SupportedVariablesPanel from './supported-variables-panel.svelte'; import SupportedVariablesPanel from './supported-variables-panel.svelte';
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
@@ -27,9 +28,8 @@
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let minified = false; export let minified = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent;
const dispatch = createEventDispatcher<SettingsEventType>();
let templateOptions: SystemConfigTemplateStorageOptionDto; let templateOptions: SystemConfigTemplateStorageOptionDto;
let selectedPreset = ''; let selectedPreset = '';
@@ -249,8 +249,8 @@
<slot /> <slot />
{:else} {:else}
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['storageTemplate'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['storageTemplate'] })}
onSave={() => onSave({ storageTemplate: config.storageTemplate })} on:save={() => dispatch('save', { storageTemplate: config.storageTemplate })}
showResetToDefault={!isEqual(savedConfig.storageTemplate, defaultConfig.storageTemplate) && !minified} showResetToDefault={!isEqual(savedConfig.storageTemplate, defaultConfig.storageTemplate) && !minified}
{disabled} {disabled}
/> />
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import type { SystemConfigDto } from '@immich/sdk'; import type { SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingTextarea from '$lib/components/shared-components/settings/setting-textarea.svelte'; import SettingTextarea from '$lib/components/shared-components/settings/setting-textarea.svelte';
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
@@ -11,8 +12,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
</script> </script>
<div> <div>
@@ -29,8 +30,8 @@
/> />
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['theme'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['theme'] })}
onSave={() => onSave({ theme: config.theme })} on:save={() => dispatch('save', { theme: config.theme })}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import type { SystemConfigDto } from '@immich/sdk'; import type { SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
import SettingInputField, { import SettingInputField, {
SettingInputFieldType, SettingInputFieldType,
@@ -14,8 +15,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
</script> </script>
<div> <div>
@@ -37,8 +38,8 @@
/> />
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['trash'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['trash'] })}
onSave={() => onSave({ trash: config.trash })} on:save={() => dispatch('save', { trash: config.trash })}
showResetToDefault={!isEqual(savedConfig.trash, defaultConfig.trash)} showResetToDefault={!isEqual(savedConfig.trash, defaultConfig.trash)}
{disabled} {disabled}
/> />
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import { type SystemConfigDto } from '@immich/sdk'; import { type SystemConfigDto } from '@immich/sdk';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; import type { SettingsEventType } from '../admin-settings';
import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
import SettingInputField, { import SettingInputField, {
@@ -14,8 +15,8 @@
export let defaultConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto;
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
export let onReset: SettingsResetEvent;
export let onSave: SettingsSaveEvent; const dispatch = createEventDispatcher<SettingsEventType>();
</script> </script>
<div> <div>
@@ -34,8 +35,8 @@
<div class="ml-4"> <div class="ml-4">
<SettingButtonsRow <SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['user'] })} on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['user'] })}
onSave={() => onSave({ user: config.user })} on:save={() => dispatch('save', { user: config.user })}
showResetToDefault={!isEqual(savedConfig.user, defaultConfig.user)} showResetToDefault={!isEqual(savedConfig.user, defaultConfig.user)}
{disabled} {disabled}
/> />
@@ -1,13 +0,0 @@
<script lang="ts">
import FormatMessage from '$lib/components/i18n/format-message.svelte';
import type { InterpolationValues } from '$lib/components/i18n/format-message.svelte';
export let key: string;
export let values: InterpolationValues = {};
</script>
<FormatMessage {key} {values} let:message let:tag>
{#if tag === 'b'}
<b>{message}</b>
{/if}
</FormatMessage>
@@ -1,10 +1,5 @@
<script lang="ts" context="module">
import type { FormatXMLElementFn, PrimitiveType } from 'intl-messageformat';
export type InterpolationValues = Record<string, PrimitiveType | FormatXMLElementFn<unknown>>;
</script>
<script lang="ts"> <script lang="ts">
import { IntlMessageFormat } from 'intl-messageformat'; import { IntlMessageFormat, type FormatXMLElementFn, type PrimitiveType } from 'intl-messageformat';
import { import {
TYPE, TYPE,
type MessageFormatElement, type MessageFormatElement,
@@ -13,6 +8,8 @@
} from '@formatjs/icu-messageformat-parser'; } from '@formatjs/icu-messageformat-parser';
import { locale as i18nLocale, json } from 'svelte-i18n'; import { locale as i18nLocale, json } from 'svelte-i18n';
type InterpolationValues = Record<string, PrimitiveType | FormatXMLElementFn<unknown>>;
type MessagePart = { type MessagePart = {
message: string; message: string;
tag?: string; tag?: string;
@@ -43,8 +43,8 @@
{config} {config}
{defaultConfig} {defaultConfig}
{savedConfig} {savedConfig}
onSave={(config) => handleSave(config)} on:save={({ detail }) => handleSave(detail)}
onReset={(options) => handleReset(options)} on:reset={({ detail }) => handleReset(detail)}
> >
<div class="flex pt-4"> <div class="flex pt-4">
<div class="w-full flex place-content-start"> <div class="w-full flex place-content-start">
@@ -1,4 +1,3 @@
import NotificationComponentTest from '$lib/components/shared-components/notification/__tests__/notification-component-test.svelte';
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import { cleanup, render, type RenderResult } from '@testing-library/svelte'; import { cleanup, render, type RenderResult } from '@testing-library/svelte';
import { NotificationType } from '../notification'; import { NotificationType } from '../notification';
@@ -38,24 +37,4 @@ describe('NotificationCard component', () => {
expect(sut.getByTestId('title')).toHaveTextContent('info'); expect(sut.getByTestId('title')).toHaveTextContent('info');
expect(sut.getByTestId('message')).toHaveTextContent('Notification message'); expect(sut.getByTestId('message')).toHaveTextContent('Notification message');
}); });
it('shows title and renders component', () => {
sut = render(NotificationCard, {
notification: {
id: 1234,
type: NotificationType.Info,
timeout: 1,
action: { type: 'discard' },
component: {
type: NotificationComponentTest,
props: {
href: 'link',
},
},
},
});
expect(sut.getByTestId('title')).toHaveTextContent('info');
expect(sut.getByTestId('message').innerHTML).toEqual('Notification <b>message</b> with <a href="link">link</a>');
});
}); });
@@ -1,5 +0,0 @@
<script lang="ts">
export let href: string;
</script>
Notification <b>message</b> with <a {href}>link</a>
@@ -2,18 +2,16 @@
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import Icon from '$lib/components/elements/icon.svelte'; import Icon from '$lib/components/elements/icon.svelte';
import { import {
isComponentNotification, type Notification,
notificationController, notificationController,
NotificationType, NotificationType,
type ComponentNotification,
type Notification,
} from '$lib/components/shared-components/notification/notification'; } from '$lib/components/shared-components/notification/notification';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { mdiCloseCircleOutline, mdiInformationOutline, mdiWindowClose } from '@mdi/js'; import { mdiCloseCircleOutline, mdiInformationOutline, mdiWindowClose } from '@mdi/js';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
export let notification: Notification | ComponentNotification; export let notification: Notification;
$: icon = notification.type === NotificationType.Error ? mdiCloseCircleOutline : mdiInformationOutline; $: icon = notification.type === NotificationType.Error ? mdiCloseCircleOutline : mdiInformationOutline;
$: hoverStyle = notification.action.type === 'discard' ? 'hover:cursor-pointer' : ''; $: hoverStyle = notification.action.type === 'discard' ? 'hover:cursor-pointer' : '';
@@ -95,8 +93,9 @@
</div> </div>
<p class="whitespace-pre-wrap pl-[28px] pr-[16px] text-sm" data-testid="message"> <p class="whitespace-pre-wrap pl-[28px] pr-[16px] text-sm" data-testid="message">
{#if isComponentNotification(notification)} {#if notification.html}
<svelte:component this={notification.component.type} {...notification.component.props} /> <!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html notification.message}
{:else} {:else}
{notification.message} {notification.message}
{/if} {/if}
@@ -1,4 +1,3 @@
import type { ComponentProps, ComponentType, SvelteComponent } from 'svelte';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
export enum NotificationType { export enum NotificationType {
@@ -16,6 +15,11 @@ export type Notification = {
id: number; id: number;
type: NotificationType; type: NotificationType;
message: string; message: string;
/**
* Allow HTML to be inserted within the message. Make sure to verify/encode
* variables that may be interpoalted into 'message'
*/
html?: boolean;
/** The action to take when the notification is clicked */ /** The action to take when the notification is clicked */
action: NotificationAction; action: NotificationAction;
button?: NotificationButton; button?: NotificationButton;
@@ -28,37 +32,13 @@ type NoopAction = { type: 'noop' };
export type NotificationAction = DiscardAction | NoopAction; export type NotificationAction = DiscardAction | NoopAction;
type Component<T extends ComponentType> = { export type NotificationOptions = Partial<Exclude<Notification, 'id'>> & { message: string };
type: T;
props: ComponentProps<InstanceType<T>>;
};
type BaseNotificationOptions<T, R extends keyof T> = Partial<Omit<T, 'id'>> & Pick<T, R>;
export type NotificationOptions = BaseNotificationOptions<Notification, 'message'>;
export type ComponentNotificationOptions<T extends ComponentType> = BaseNotificationOptions<
ComponentNotification<T>,
'component'
>;
export type ComponentNotification<T extends ComponentType = ComponentType<SvelteComponent>> = Omit<
Notification,
'message'
> & {
component: Component<T>;
};
export const isComponentNotification = <T extends ComponentType>(
notification: Notification | ComponentNotification<T>,
): notification is ComponentNotification<T> => {
return 'component' in notification;
};
function createNotificationList() { function createNotificationList() {
const notificationList = writable<(Notification | ComponentNotification)[]>([]); const notificationList = writable<Notification[]>([]);
let count = 1; let count = 1;
const show = <T>(options: T extends ComponentType ? ComponentNotificationOptions<T> : NotificationOptions) => { const show = (options: NotificationOptions) => {
notificationList.update((currentList) => { notificationList.update((currentList) => {
currentList.push({ currentList.push({
id: count++, id: count++,
@@ -1,12 +1,16 @@
<script lang="ts"> <script lang="ts">
import Button from '$lib/components/elements/buttons/button.svelte'; import Button from '$lib/components/elements/buttons/button.svelte';
import type { ResetOptions } from '$lib/utils/dipatch'; import type { ResetOptions } from '$lib/utils/dipatch';
import { createEventDispatcher } from 'svelte';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
const dispatch = createEventDispatcher<{
reset: ResetOptions;
save: void;
}>();
export let showResetToDefault = true; export let showResetToDefault = true;
export let disabled = false; export let disabled = false;
export let onReset: (options: ResetOptions) => void;
export let onSave: () => void;
</script> </script>
<div class="mt-8 flex justify-between gap-2"> <div class="mt-8 flex justify-between gap-2">
@@ -14,7 +18,7 @@
{#if showResetToDefault} {#if showResetToDefault}
<button <button
type="button" type="button"
on:click={() => onReset({ default: true })} on:click={() => dispatch('reset', { default: true })}
class="bg-none text-sm font-medium text-immich-primary hover:text-immich-primary/75 dark:text-immich-dark-primary hover:dark:text-immich-dark-primary/75" class="bg-none text-sm font-medium text-immich-primary hover:text-immich-primary/75 dark:text-immich-dark-primary hover:dark:text-immich-dark-primary/75"
> >
{$t('reset_to_default')} {$t('reset_to_default')}
@@ -23,7 +27,9 @@
</div> </div>
<div class="right"> <div class="right">
<Button {disabled} size="sm" color="gray" on:click={() => onReset({ default: false })}>{$t('reset')}</Button> <Button {disabled} size="sm" color="gray" on:click={() => dispatch('reset', { default: false })}
<Button type="submit" {disabled} size="sm" on:click={() => onSave()}>{$t('save')}</Button> >{$t('reset')}</Button
>
<Button type="submit" {disabled} size="sm" on:click={() => dispatch('save')}>{$t('save')}</Button>
</div> </div>
</div> </div>
-4
View File
@@ -722,12 +722,9 @@
"group_year": "تجميع حسب السنة", "group_year": "تجميع حسب السنة",
"has_quota": "ليده حصة", "has_quota": "ليده حصة",
"hi_user": "مرحبا {name} ({email})", "hi_user": "مرحبا {name} ({email})",
"hide_all_people": "إخفاء جميع الأشخاص",
"hide_gallery": "اخفاء المعرض", "hide_gallery": "اخفاء المعرض",
"hide_named_person": "إخفاء الشخص {name}",
"hide_password": "اخفاء كلمة المرور", "hide_password": "اخفاء كلمة المرور",
"hide_person": "اخفاء الشخص", "hide_person": "اخفاء الشخص",
"hide_unnamed_people": "إخفاء الأشخاص بدون إسم",
"host": "المضيف", "host": "المضيف",
"hour": "ساعة", "hour": "ساعة",
"image": "صورة", "image": "صورة",
@@ -1062,7 +1059,6 @@
"sharing_sidebar_description": "اعرض رابطًا للمشاركة في الشريط الجانبي", "sharing_sidebar_description": "اعرض رابطًا للمشاركة في الشريط الجانبي",
"shift_to_permanent_delete": "اضغط على ⇧ لحذف الأصل نهائيًا", "shift_to_permanent_delete": "اضغط على ⇧ لحذف الأصل نهائيًا",
"show_album_options": "إظهار خيارات الألبوم", "show_album_options": "إظهار خيارات الألبوم",
"show_all_people": "إظهار جميع الأشخاص",
"show_and_hide_people": "إظهار وإخفاء الأشخاص", "show_and_hide_people": "إظهار وإخفاء الأشخاص",
"show_file_location": "إظهار موقع الملف", "show_file_location": "إظهار موقع الملف",
"show_gallery": "عرض المعرض", "show_gallery": "عرض المعرض",
+10 -13
View File
@@ -128,7 +128,6 @@
"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_light_style": "Tema clar", "map_light_style": "Tema clar",
"map_manage_reverse_geocoding_settings": "Gestiona els paràmetres de <link>geocodificació inversa</link>",
"map_reverse_geocoding": "Geocodificació inversa", "map_reverse_geocoding": "Geocodificació inversa",
"map_reverse_geocoding_enable_description": "Habilita la geocodificació inversa", "map_reverse_geocoding_enable_description": "Habilita la geocodificació inversa",
"map_reverse_geocoding_settings": "Configuració de Geocodificació Inversa", "map_reverse_geocoding_settings": "Configuració de Geocodificació Inversa",
@@ -161,28 +160,28 @@
"notification_settings": "Configuració de notificacions", "notification_settings": "Configuració de notificacions",
"notification_settings_description": "Gestiona la configuració de notificacions, incloent-hi el correu electrònic", "notification_settings_description": "Gestiona la configuració de notificacions, incloent-hi el correu electrònic",
"oauth_auto_launch": "Execució automàtica", "oauth_auto_launch": "Execució automàtica",
"oauth_auto_launch_description": "Inicia el flux d'inici de sessió OAuth automàticament en accedir a la pàgina d'inici de sessió", "oauth_auto_launch_description": "",
"oauth_auto_register": "Registre automàtic", "oauth_auto_register": "",
"oauth_auto_register_description": "Registra nous usuaris automàticament després d'iniciar sessió amb OAuth", "oauth_auto_register_description": "",
"oauth_button_text": "Text del botó", "oauth_button_text": "",
"oauth_client_id": "ID Client", "oauth_client_id": "ID Client",
"oauth_client_secret": "Secret de Client", "oauth_client_secret": "Secret de Client",
"oauth_enable_description": "Iniciar sessió amb OAuth", "oauth_enable_description": "Iniciar sessió amb OAuth",
"oauth_issuer_url": "URL de l'emissor", "oauth_issuer_url": "URL de l'emissor",
"oauth_mobile_redirect_uri": "URI de redirecció mòbil", "oauth_mobile_redirect_uri": "URI de redirecció mòbil",
"oauth_mobile_redirect_uri_override": "Sobreescriu l'URI de redirecció mòbil", "oauth_mobile_redirect_uri_override": "",
"oauth_mobile_redirect_uri_override_description": "Habilita quan 'app.immich:/' és una URI de redirecció invàlida.", "oauth_mobile_redirect_uri_override_description": "Habilita quan 'app.immich:/' és una URI de redirecció invàlida.",
"oauth_scope": "Abast", "oauth_scope": "Abast",
"oauth_settings": "OAuth", "oauth_settings": "OAuth",
"oauth_settings_description": "Gestiona la configuració de l'inici de sessió OAuth", "oauth_settings_description": "Gestiona la configuració de l'inici de sessió OAuth",
"oauth_settings_more_details": "Per a més detalls sobre aquesta funcionalitat, consulteu la <link>documentació</link>.", "oauth_settings_more_details": "Per a més detalls sobre aquesta funcionalitat, consulteu la <link>documentació</link>.",
"oauth_signing_algorithm": "Algorisme de signatura", "oauth_signing_algorithm": "Algorisme de signatura",
"oauth_storage_label_claim": "Petició d'etiqueta d'emmagatzemament", "oauth_storage_label_claim": "",
"oauth_storage_label_claim_description": "Estableix automàticamentl'etiqueta d'emmagatzemament de l'usuari a aquest valor.", "oauth_storage_label_claim_description": "",
"oauth_storage_quota_claim": "Quota d'emmagatzemament reclamada", "oauth_storage_quota_claim": "Quota d'emmagatzemament reclamada",
"oauth_storage_quota_claim_description": "Estableix automàticament la quota d'emmagatzemament de l'usuari al valor d'aquest paràmetre.", "oauth_storage_quota_claim_description": "",
"oauth_storage_quota_default": "Quota d'emmagatzemament predeterminada (GiB)", "oauth_storage_quota_default": "Quota d'emmagatzemament predeterminada (GiB)",
"oauth_storage_quota_default_description": "Quota disponible en GB quan no s'estableixi cap valor (Entreu 0 per a quota il·limitada).", "oauth_storage_quota_default_description": "",
"offline_paths": "Rutes sense connexió", "offline_paths": "Rutes sense connexió",
"offline_paths_description": "Aquests resultats poden ser deguts a l'eliminació manual de fitxers que no formen part d'una biblioteca externa.", "offline_paths_description": "Aquests resultats poden ser deguts a l'eliminació manual de fitxers que no formen part d'una biblioteca externa.",
"password_enable_description": "Inicia sessió amb correu electrònic i contrasenya", "password_enable_description": "Inicia sessió amb correu electrònic i contrasenya",
@@ -219,9 +218,7 @@
"storage_template_hash_verification_enabled": "Verificació Hash habilitada", "storage_template_hash_verification_enabled": "Verificació Hash habilitada",
"storage_template_hash_verification_enabled_description": "Activa la verificació de hash. No la desactivis a menys que estiguis segur de les implicacions", "storage_template_hash_verification_enabled_description": "Activa la verificació de hash. No la desactivis a menys que estiguis segur de les implicacions",
"storage_template_migration": "Migració de plantilles d'emmagatzematge", "storage_template_migration": "Migració de plantilles d'emmagatzematge",
"storage_template_migration_description": "Aplica la <link>{template}</link> actual als elements pujats prèviament", "storage_template_migration_job": "",
"storage_template_migration_info": "Els canvis de plantilla només s'aplicaran a nous elements. Per a 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'emmagatzemament",
"storage_template_settings": "", "storage_template_settings": "",
"storage_template_settings_description": "", "storage_template_settings_description": "",
"theme_custom_css_settings": "", "theme_custom_css_settings": "",
-4
View File
@@ -723,12 +723,9 @@
"group_year": "Seskupit podle roku", "group_year": "Seskupit podle roku",
"has_quota": "Má kvótu", "has_quota": "Má kvótu",
"hi_user": "Ahoj {name} ({email})", "hi_user": "Ahoj {name} ({email})",
"hide_all_people": "Skrýt všechny lidi",
"hide_gallery": "Skrýt galerii", "hide_gallery": "Skrýt galerii",
"hide_named_person": "Skrýt osobu {name}",
"hide_password": "Skrýt heslo", "hide_password": "Skrýt heslo",
"hide_person": "Skrýt osobu", "hide_person": "Skrýt osobu",
"hide_unnamed_people": "Skrýt nejmenované lidi",
"host": "Hostitel", "host": "Hostitel",
"hour": "Hodina", "hour": "Hodina",
"image": "Obrázek", "image": "Obrázek",
@@ -1065,7 +1062,6 @@
"sharing_sidebar_description": "Zobrazit sekci Sdílení v postranním panelu", "sharing_sidebar_description": "Zobrazit sekci Sdílení v postranním panelu",
"shift_to_permanent_delete": "stiskněte ⇧ pro trvalé odstranění položky", "shift_to_permanent_delete": "stiskněte ⇧ pro trvalé odstranění položky",
"show_album_options": "Zobrazit možnosti alba", "show_album_options": "Zobrazit možnosti alba",
"show_all_people": "Zobrazit všechny lidi",
"show_and_hide_people": "Zobrazit a skrýt osoby", "show_and_hide_people": "Zobrazit a skrýt osoby",
"show_file_location": "Zobrazit umístění souboru", "show_file_location": "Zobrazit umístění souboru",
"show_gallery": "Zobrazit galerii", "show_gallery": "Zobrazit galerii",
+1 -5
View File
@@ -723,12 +723,9 @@
"group_year": "Gruppierung nach Jahr", "group_year": "Gruppierung nach Jahr",
"has_quota": "Hat Kontingent", "has_quota": "Hat Kontingent",
"hi_user": "Hallo {name} ({email})", "hi_user": "Hallo {name} ({email})",
"hide_all_people": "Alle Personen verbergen",
"hide_gallery": "Galerie verbergen", "hide_gallery": "Galerie verbergen",
"hide_named_person": "Person {name} verbergen",
"hide_password": "Passwort verbergen", "hide_password": "Passwort verbergen",
"hide_person": "Person verbergen", "hide_person": "Person verbergen",
"hide_unnamed_people": "Unbenannte Personen verbergen",
"host": "Host", "host": "Host",
"hour": "Stunde", "hour": "Stunde",
"image": "Bild", "image": "Bild",
@@ -809,7 +806,7 @@
"memory": "Erinnerung", "memory": "Erinnerung",
"menu": "Menü", "menu": "Menü",
"merge": "Zusammenführen", "merge": "Zusammenführen",
"merge_people": "Personen zusammenführen", "merge_people": "Leute zusammenführen",
"merge_people_limit": "Du kannst nur bis zu 5 Gesichter auf einmal zusammenführen", "merge_people_limit": "Du kannst nur bis zu 5 Gesichter auf einmal zusammenführen",
"merge_people_prompt": "Willst du diese Personen zusammenführen? Diese Aktion kann nicht rückgängig gemacht werden.", "merge_people_prompt": "Willst du diese Personen zusammenführen? Diese Aktion kann nicht rückgängig gemacht werden.",
"merge_people_successfully": "Personen erfolgreich zusammengeführt", "merge_people_successfully": "Personen erfolgreich zusammengeführt",
@@ -1064,7 +1061,6 @@
"sharing_sidebar_description": "Eine Verknüpfung zu Geteiltem in der Seitenleiste anzeigen", "sharing_sidebar_description": "Eine Verknüpfung zu Geteiltem in der Seitenleiste anzeigen",
"shift_to_permanent_delete": "Drücke ⇧, um die Datei endgültig zu löschen", "shift_to_permanent_delete": "Drücke ⇧, um die Datei endgültig zu löschen",
"show_album_options": "Album-Optionen anzeigen", "show_album_options": "Album-Optionen anzeigen",
"show_all_people": "Alle Personen anzeigen",
"show_and_hide_people": "Personen ein- & ausblenden", "show_and_hide_people": "Personen ein- & ausblenden",
"show_file_location": "Dateispeicherort anzeigen", "show_file_location": "Dateispeicherort anzeigen",
"show_gallery": "Galerie anzeigen", "show_gallery": "Galerie anzeigen",
+1 -1
View File
@@ -378,7 +378,7 @@
"assets": "Assets", "assets": "Assets",
"assets_added_count": "Added {count, plural, one {# asset} other {# assets}}", "assets_added_count": "Added {count, plural, one {# asset} other {# assets}}",
"assets_added_to_album_count": "Added {count, plural, one {# asset} other {# assets}} to the album", "assets_added_to_album_count": "Added {count, plural, one {# asset} other {# assets}} to the album",
"assets_added_to_name_count": "Added {count, plural, one {# asset} other {# assets}} to {hasName, select, true {<b>{name}</b>} other {new album}}", "assets_added_to_name_count": "Added {count, plural, one {# asset} other {# assets}} to {name}",
"assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_count": "{count, plural, one {# asset} other {# assets}}",
"assets_moved_to_trash_count": "Moved {count, plural, one {# asset} other {# assets}} to trash", "assets_moved_to_trash_count": "Moved {count, plural, one {# asset} other {# assets}} to trash",
"assets_permanently_deleted_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}", "assets_permanently_deleted_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}",
+5 -5
View File
@@ -47,9 +47,9 @@
"exclusion_pattern_description": "Poissulkevat määritteet mahdollistavat tiettyjen tiedostojen ja kansioiden jättämisen pois kirjastoasi skannatessa. Tästä on hyötyä jos kansiot sisältävät tiedostoja mitä et halua tuoda, kuten RAW-tiedostot.", "exclusion_pattern_description": "Poissulkevat määritteet mahdollistavat tiettyjen tiedostojen ja kansioiden jättämisen pois kirjastoasi skannatessa. Tästä on hyötyä jos kansiot sisältävät tiedostoja mitä et halua tuoda, kuten RAW-tiedostot.",
"external_library_created_at": "Ulkoinen kirjasto (luotu {date})", "external_library_created_at": "Ulkoinen kirjasto (luotu {date})",
"external_library_management": "Ulkoisen kirjaston hallinta", "external_library_management": "Ulkoisen kirjaston hallinta",
"face_detection": "Kasvojen haitseminen", "face_detection": "Kasvojentunnistus",
"face_detection_description": "Tunnista sisällön kasvoja käyttäen koneoppimista. Videojen osalta vain pikkukuva tunnistetaan. \"Kaikki\" (uudelleen)prosessoi koko sisällön. \"Puuttuvat\" prosessoi sisällön, jota ei vielä ole käyty läpi. Havaitut kasvot ryhmitellään jo tunnistettujen kanssa, tai lisätään uusina henkilöinä.", "face_detection_description": "Tunnista sisällön kasvoja käyttäen koneoppimista. Videojen osalta vain pikkukuva tunnistetaan. \"Kaikki\" (uudelleen)prosessoi koko sisällön. \"Puuttuvat\" prosessoi sisällön, jota ei vielä ole käyty läpi. Havaitut kasvot ryhmitellään jo tunnistettujen kanssa, tai lisätään uusina henkilöinä.",
"facial_recognition_job_description": "Ryhmitä havaitut kasvot henkilöihin. Tämä vaihe suoritetaan kun kasvot on ensin havaittu. \"Kaikki\" ryhmittelee kaikki kasvot. \"Puuttuvat\" vain ne, joille ei ole määritetty henkilöä.", "facial_recognition_job_description": "Ryhmitä havaitut kasvot henkilöihin. Tämä vaihe suoritetaan kun kasvojentunnistus on valmis. \"Kaikki\" ryhmittelee kaikki kasvot. \"Puuttuvat\" vain ne, joille ei ole määritetty henkilöä.",
"failed_job_command": "Komento {command} epäonnistui työlle {job}", "failed_job_command": "Komento {command} epäonnistui työlle {job}",
"force_delete_user_warning": "VAROITUS: Tämä poistaa käyttäjän ja kaikki mediat. Tätä ei voi perua, eikä tiedostoja voi palauttaa.", "force_delete_user_warning": "VAROITUS: Tämä poistaa käyttäjän ja kaikki mediat. Tätä ei voi perua, eikä tiedostoja voi palauttaa.",
"forcing_refresh_library_files": "Pakotetaan virkistämään kaikkien kirjastojen tiedostot", "forcing_refresh_library_files": "Pakotetaan virkistämään kaikkien kirjastojen tiedostot",
@@ -104,9 +104,9 @@
"machine_learning_facial_recognition": "Kasvojen tunnistus", "machine_learning_facial_recognition": "Kasvojen tunnistus",
"machine_learning_facial_recognition_description": "Havaitse, tunnista ja ryhmitä kuvien kasvoja", "machine_learning_facial_recognition_description": "Havaitse, tunnista ja ryhmitä kuvien kasvoja",
"machine_learning_facial_recognition_model": "Kasvojen tunnistusmalli", "machine_learning_facial_recognition_model": "Kasvojen tunnistusmalli",
"machine_learning_facial_recognition_model_description": "Mallit ovat listattuna kokonsa mukaan laskevaan järjestykseen. Suuremmat mallit ovat hitaampia ja käyttävät enemmän muistia, mutta tuottavat parempia tuloksia. Huomaa että sinun pitää ajaa kasvojen havaitseminen uudelleen kaikille kuville kun vaihdat mallia.", "machine_learning_facial_recognition_model_description": "Mallit ovat listattuna kokonsa mukaan laskevaan järjestykseen. Suuremmat mallit ovat hitaampia ja käyttävät enemmän muistia, mutta tuottavat parempia tuloksia. Huomaa että sinun pitää ajaa kasvojen tunnistus uudelleen kaikille kuville kun vaihdat mallia.",
"machine_learning_facial_recognition_setting": "Ota käyttöön kasvojen tunnistus", "machine_learning_facial_recognition_setting": "Ota käyttöön kasvojen tunnistus",
"machine_learning_facial_recognition_setting_description": "Jos ei käytössä, kuvia ei käsitellä kasvojen tunnistusta varten, eikä \"Tutki\" -sivun \"Ihmiset\" osiota täytetä.", "machine_learning_facial_recognition_setting_description": "Jos ei käytössä, kuvia ei käsitellä kasvojen tunnistusta varten, eikä \"Tutki\" -sivun \"Henkilöt\" osiota täytetä.",
"machine_learning_max_detection_distance": "Tunnistamisen enimmäiseroavaisuus", "machine_learning_max_detection_distance": "Tunnistamisen enimmäiseroavaisuus",
"machine_learning_max_detection_distance_description": "Kahden kuvan suurin eroavaisuus, milloin ne vielä määritellään kaksoiskappaleiksi, välillä 0.001-0.1. Korkeampi arvo havaitsee enemmän kaksoiskappaleita, mutta voi tuottaa virheellisiä havaintoja.", "machine_learning_max_detection_distance_description": "Kahden kuvan suurin eroavaisuus, milloin ne vielä määritellään kaksoiskappaleiksi, välillä 0.001-0.1. Korkeampi arvo havaitsee enemmän kaksoiskappaleita, mutta voi tuottaa virheellisiä havaintoja.",
"machine_learning_max_recognition_distance": "Suurin kasvojen eroavaisuus", "machine_learning_max_recognition_distance": "Suurin kasvojen eroavaisuus",
@@ -341,7 +341,7 @@
"api_key": "API-avain", "api_key": "API-avain",
"api_keys": "API-avaimet", "api_keys": "API-avaimet",
"app_settings": "Sovellusasetukset", "app_settings": "Sovellusasetukset",
"appears_in": "Esiintyy albumeissa", "appears_in": "Versiosta 1.106.4 lähtien käytetään vain aineiston sivupalkissa kuvaamaan termiä \"esiintyy [albumeissa]\"",
"archive": "Arkisto", "archive": "Arkisto",
"archive_or_unarchive_photo": "Arkistoi kuva tai palauta arkistosta", "archive_or_unarchive_photo": "Arkistoi kuva tai palauta arkistosta",
"archive_size": "Arkiston koko", "archive_size": "Arkiston koko",
-1
View File
@@ -724,7 +724,6 @@
"has_quota": "Quota", "has_quota": "Quota",
"hi_user": "Bonjour {name} ({email})", "hi_user": "Bonjour {name} ({email})",
"hide_gallery": "Masquer la galerie", "hide_gallery": "Masquer la galerie",
"hide_named_person": "Masquer {name}",
"hide_password": "Masquer le mot de passe", "hide_password": "Masquer le mot de passe",
"hide_person": "Masquer la personne", "hide_person": "Masquer la personne",
"host": "Hôte", "host": "Hôte",
+130 -133
View File
@@ -88,7 +88,7 @@
"library_settings": "ספרייה חיצונית", "library_settings": "ספרייה חיצונית",
"library_settings_description": "נהל הגדרות ספרייה חיצונית", "library_settings_description": "נהל הגדרות ספרייה חיצונית",
"library_tasks_description": "ביצוע משימות ספרייה", "library_tasks_description": "ביצוע משימות ספרייה",
"library_watching_enable_description": "עקוב אחר שינויי קבצים בספריות חיצוניות", "library_watching_enable_description": "עקוב אחר שינויי קובץ בספריות חיצוניות",
"library_watching_settings": "צפייה בספרייה (ניסיוני)", "library_watching_settings": "צפייה בספרייה (ניסיוני)",
"library_watching_settings_description": "צפה אוטומטית לאתר קבצים שהשתנו", "library_watching_settings_description": "צפה אוטומטית לאתר קבצים שהשתנו",
"logging_enable_description": "אפשר רישום ביומן", "logging_enable_description": "אפשר רישום ביומן",
@@ -107,7 +107,7 @@
"machine_learning_facial_recognition_model": "מודל זיהוי פנים", "machine_learning_facial_recognition_model": "מודל זיהוי פנים",
"machine_learning_facial_recognition_model_description": "הדגמים מפורטים בסדר גודל יורד. דגמים גדולים יותר הם איטיים יותר ומשתמשים ביותר זיכרון, אך מניבים תוצאות טובות יותר. שים לב שעליך להפעיל מחדש את משימת זיהוי הפנים עבור כל התמונות בעת שינוי מודל.", "machine_learning_facial_recognition_model_description": "הדגמים מפורטים בסדר גודל יורד. דגמים גדולים יותר הם איטיים יותר ומשתמשים ביותר זיכרון, אך מניבים תוצאות טובות יותר. שים לב שעליך להפעיל מחדש את משימת זיהוי הפנים עבור כל התמונות בעת שינוי מודל.",
"machine_learning_facial_recognition_setting": "אפשר זיהוי פנים", "machine_learning_facial_recognition_setting": "אפשר זיהוי פנים",
"machine_learning_facial_recognition_setting_description": "אם מושבת, תמונות לא יקודדו לזיהוי פנים ולא יאכלסו את קטע האנשים בעמוד \"אני רוצה לראות\".", "machine_learning_facial_recognition_setting_description": "אם מושבת, תמונות לא יקודדו לזיהוי פנים ולא יאכלסו את קטע האנשים בעמוד ה\"חקור\".",
"machine_learning_max_detection_distance": "מרחק איתור מרבי", "machine_learning_max_detection_distance": "מרחק איתור מרבי",
"machine_learning_max_detection_distance_description": "מרחק מרבי בין שתי תמונות להערכתן ככפילות, נע בין 0.001-0.1. ערכים גבוהים יותר יאתרו כפילויות נוספות, אך עלולים לגרום לתוצאות חיוביות שגויות.", "machine_learning_max_detection_distance_description": "מרחק מרבי בין שתי תמונות להערכתן ככפילות, נע בין 0.001-0.1. ערכים גבוהים יותר יאתרו כפילויות נוספות, אך עלולים לגרום לתוצאות חיוביות שגויות.",
"machine_learning_max_recognition_distance": "מרחק זיהוי מרבי", "machine_learning_max_recognition_distance": "מרחק זיהוי מרבי",
@@ -209,116 +209,116 @@
"server_settings_description": "נהל הגדרות שרת", "server_settings_description": "נהל הגדרות שרת",
"server_welcome_message": "הודעת פתיחה", "server_welcome_message": "הודעת פתיחה",
"server_welcome_message_description": "הודעה שמוצגת במסך ההתחברות.", "server_welcome_message_description": "הודעה שמוצגת במסך ההתחברות.",
"sidecar_job": "מטא-נתונים נלווים", "sidecar_job": "מטא נתונים של Sidecar",
"sidecar_job_description": "גלה או סנכרן מטא-נתונים נלווים ממערכת הקבצים", "sidecar_job_description": "גלה או סנכרן מטא-נתונים צדדיים ממערכת הקבצים",
"slideshow_duration_description": "מספר שניות להצגת כל תמונה", "slideshow_duration_description": "מספר שניות להצגת כל תמונה",
"smart_search_job_description": "הפעל למידת מכונה על נכסים כדי לתמוך בחיפוש חכם", "smart_search_job_description": "הפעל למידת מכונה על נכסים כדי לתמוך בחיפוש חכם",
"storage_template_date_time_description": "חותמת זמן יצירת הנכס משמשת לפרטי התאריך והשעה", "storage_template_date_time_description": "חותמת הזמן של נכסים משמש עבור תאריך ושעה",
"storage_template_date_time_sample": "זמן לדוגמא {date}", "storage_template_date_time_sample": "זמן לדוגמא {date}",
"storage_template_enable_description": "הפעל מנוע תבנית אחסון", "storage_template_enable_description": "הפעל מנוע תבנית אחסון",
"storage_template_hash_verification_enabled": "אימות גיבוב מופעל", "storage_template_hash_verification_enabled": "אימות גיבוב מופעל",
"storage_template_hash_verification_enabled_description": "מאפשר אימות גיבוב, אל תשבית/י זאת אלא אם כן את/ה בטוח בהשלכות", "storage_template_hash_verification_enabled_description": "מאפשר אימות hash, אל תשבית זאת אלא אם כן אתה בטוח בהשלכות",
"storage_template_migration": "העברת תבנית אחסון", "storage_template_migration": "העברת תבנית אחסון",
"storage_template_migration_description": "החל את <link>{template}</link> הנוכחית על נכסים שהועלו בעבר", "storage_template_migration_description": "החל את ה <link>{template}</link> על נכסים שהועלו",
"storage_template_migration_info": "שינויים בתבנית יחולו רק על נכסים חדשים. כדי להחיל באופן רטרואקטיבי את התבנית על נכסים שהועלו בעבר, הפעל את <link>{job}</link>.", "storage_template_migration_info": "שינויים בתבנית יחולו רק על נכסים חדשים. כדי להחיל את התבנית גם על נכסים שהועלו בעבר, הפעל את <link>{job}</link>.",
"storage_template_migration_job": "משימת העברת תבנית אחסון", "storage_template_migration_job": "משימת העברת תבנית אחסון",
"storage_template_more_details": "לפרטים נוספים אודות תכונה זו, עיין ב<template-link>תבנית האחסון</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": "כאשר מופעלת, תכונה זו תארגן אוטומטית קבצים בהתבסס על תבנית שהמשתמש הגדיר. עקב בעיות יציבות התכונה כבויה כברירת מחדל. לפרטים נוספים, בבקשה לראות את ה<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": "נהל את מבנה התיקיות ואת שם הקובץ של הנכס שמועלה",
"storage_template_user_label": "<code>{label}</code> היא תווית האחסון של המשתמש", "storage_template_user_label": "<code>{label}</code> היא תווית האחסון של המשתמש",
"system_settings": "הגדרות מערכת", "system_settings": "הגדרות מערכת",
"theme_custom_css_settings": "עיצוב בהתאמה אישית (CSS)", "theme_custom_css_settings": "עיצוב בהתאמה אישית (CSS)",
"theme_custom_css_settings_description": "גיליונות סגנון מדורגים (CSS) מאפשרים התאמה אישית של העיצוב של Immich.", "theme_custom_css_settings_description": "גיליונות סגנון מדורגים (CSS) מאפשרים התאמה אישית של העיצוב של Immich.",
"theme_settings": "הגדרות ערכת נושא", "theme_settings": "הגדרות ערכת נושא",
"theme_settings_description": "ניהול התאמה אישית של ממשק האינטרנט של Immich", "theme_settings_description": "התאמה אישית של ממשק האינטרנט של Immich",
"these_files_matched_by_checksum": "קבצים אלה תואמים לפי סיכומי הביקורת שלהם", "these_files_matched_by_checksum": "קבצים אלה מותאמים על ידי ה checksums שלהם",
"thumbnail_generation_job": "צור תמונות ממוזערות", "thumbnail_generation_job": "צור תמונות ממוזערות",
"thumbnail_generation_job_description": "יוצר תמונות ממוזערות גדולות, קטנות ומטושטשות עבור כל נכס, כמו גם תמונות ממוזערות עבור כל אדם", "thumbnail_generation_job_description": "יוצר תמונות ממוזערות גדולות, קטנות ומטושטשות עבור כל נכס, כמו גם תמונות ממוזערות עבור כל אדם",
"transcode_policy_description": "", "transcode_policy_description": "",
"transcoding_acceleration_api": "API האצה", "transcoding_acceleration_api": "Acceleration API",
"transcoding_acceleration_api_description": "ה-API שייצור אינטראקציה עם המכשיר שלך כדי להאיץ את המרת הקידוד. הגדרה זו היא 'המאמץ הטוב ביותר': היא תחזור לקידוד תוכנה במקרה של כשל. VP9 עשוי לעבוד או לא, תלוי בחומרה שלך.", "transcoding_acceleration_api_description": "ה-API שייצור אינטראקציה עם המכשיר שלך כדי להאיץ את ההמרה. הגדרה זו היא 'האפשרות הטובה ביותר': היא תחזור לקידוד תוכנה במקרה של כשל. VP9 עשוי לעבוד או לא, תלוי בחומרה שלך.",
"transcoding_acceleration_nvenc": "NVENC (דורש כרטיס מסך של NVIDIA)", "transcoding_acceleration_nvenc": "NVENC (דורש כרטיס מסך של NVIDIA)",
"transcoding_acceleration_qsv": "Quick Sync (דורש מעבד אינטל מדור 7 ואילך)", "transcoding_acceleration_qsv": "Quick Sync (דורש מעבד מדור 7 או יותר של Intel)",
"transcoding_acceleration_rkmpp": "RKMPP (רק במערכות על שבב של Rockchip)", "transcoding_acceleration_rkmpp": "RKMPP (עובד רק על Rockchip SOCs)",
"transcoding_acceleration_vaapi": "VAAPI", "transcoding_acceleration_vaapi": "VAAPI",
"transcoding_accepted_audio_codecs": "קודקים מקובלים של שמע", "transcoding_accepted_audio_codecs": "Codec אודיו מקובל",
"transcoding_accepted_audio_codecs_description": "בחר אילו קודקים של שמע אינם צריכים לעבור המרת קידוד. משמש רק עבור מדיניות המרת קידוד מסוימות.", "transcoding_accepted_audio_codecs_description": "בחר אילו רכיבי codecs אודיו אין צורך להמיר. משמש רק עבור מדיניות טרנסקוד מסוימות.",
"transcoding_accepted_video_codecs": "קודקים מקובלים של סרטונים", "transcoding_accepted_video_codecs": "Codec וידאו מקובל",
"transcoding_accepted_video_codecs_description": "בחר אילו קודקים של סרטונים אינם צריכים לעבור המרת קידוד. משמש רק עבור מדיניות המרת קידוד מסוימות.", "transcoding_accepted_video_codecs_description": "בחר אילו רכיבי Codec וידאו אין צורך להמיר. משמש רק עבור מדיניות טרנסקוד מסוימות.",
"transcoding_advanced_options_description": "אפשרויות שרוב המשתמשים לא צריכים לשנות", "transcoding_advanced_options_description": "אפשרויות שרוב המשתמשים לא צריכים לשנות",
"transcoding_audio_codec": "קודק שמע", "transcoding_audio_codec": "Codec אודיו",
"transcoding_audio_codec_description": "Opus היא האפשרות האיכותית ביותר, אך בעלת תאימות נמוכה יותר למכשירים או תוכנות ישנות.", "transcoding_audio_codec_description": "Opus היא האפשרות האיכותית ביותר, אך בעלת תאימות נמוכה יותר למכשירים או תוכנות ישנות.",
"transcoding_bitrate_description": "סרטונים גבוהים מקצב סיביות מרבי או לא בפורמט מקובל", "transcoding_bitrate_description": "סרטונים גבוהים מקצב סיביות מקסימלי או לא בפורמט מקובל",
"transcoding_codecs_learn_more": "כדי ללמוד עוד על המינוח בשימוש כאן, פנה אל תיעוד FFmpeg עבור <h264-link>קודק H.264</h264-link>, <hevc-link>קודק HEVC</hevc-link> ו<vp9-link>קודק VP9</vp9-link>.", "transcoding_codecs_learn_more": "כדי ללמוד עוד על המינוח בשימוש כאן, פנה אל תיעוד FFmpeg עבור <h264-link>קודק H.264</h264-link>, <hevc-link>קודק HEVC</hevc-link> ו<vp9-link>קודק VP9</vp9-link>.",
"transcoding_constant_quality_mode": "מצב איכות קבוע", "transcoding_constant_quality_mode": "מצב איכות קבוע",
"transcoding_constant_quality_mode_description": "ICQ טוב יותר מ-CQP, אך חלק מהתקני האצת החומרה אינם תומכים במצב זה. הגדרת אפשרות זו תעדיף את המצב שצוין בעת שימוש בקידוד מבוסס איכות. בהתעלמות מצד NVENC מכיוון שהוא אינו תומך ב-ICQ.", "transcoding_constant_quality_mode_description": "ICQ טוב יותר מ-CQP, אך חלק מהתקני האצת החומרה אינם תומכים במצב זה. הגדרת אפשרות זו תעדיף את המצב שצוין בעת שימוש בקידוד מבוסס איכות. מתעלם על ידי NVENC מכיוון שהוא אינו תומך ב-ICQ.",
"transcoding_constant_rate_factor": "גורם קצב קבוע (-crf)", "transcoding_constant_rate_factor": "גורם קצב קבוע (-crf)",
"transcoding_constant_rate_factor_description": "רמת איכות וידאו. ערכים אופייניים הם הערך 23 עבור H.264, הערך 28 עבור HEVC, הערך 31 עבור VP9, והערך 35 עבור AV1. נמוך יותר טוב יותר, אבל מייצר קבצים גדולים יותר.", "transcoding_constant_rate_factor_description": "רמת איכות וידאו. ערכים אופייניים הם 23 עבור H.264, הערך 28 עבור HEVC, הערך 31 עבור VP9 ו-35 עבור AV1. נמוך יותר טוב יותר, אבל מייצר קבצים גדולים יותר.",
"transcoding_disabled_description": "אין להמיר את הקידוד של שום סרטון, עלול לגרום לכך שההשמעה לא תפעל במכשירים מסוימים", "transcoding_disabled_description": "אין להמיר סרטונים כלשהם, ייתכן שסרטונים מסוימים לא יעבדו טוב במכשירים מסוימים",
"transcoding_hardware_acceleration": "האצת חומרה", "transcoding_hardware_acceleration": "Hardware Acceleration - האצת חומרה",
"transcoding_hardware_acceleration_description": "ניסיוני; הרבה יותר מהירה, אבל תהיה באיכות נמוכה יותר באותו קצב סיביות", "transcoding_hardware_acceleration_description": "ניסיוני; הרבה יותר מהיר, אבל תהיה באיכות נמוכה יותר באותו קצב סיביות",
"transcoding_hardware_decoding": "פענוח חומרה", "transcoding_hardware_decoding": "Hardware decoding - פענוח חומרה",
"transcoding_hardware_decoding_setting_description": "חל רק על NVENC, QSV ו-RKMPP. מאפשר האצה מקצה לקצה במקום רק להאיץ קידוד. ייתכן שלא יפעל על כל הסרטונים.", "transcoding_hardware_decoding_setting_description": "חל רק על NVENC, QSV ו-RKMPP. מאפשר האצה מקצה לקצה במקום רק להאיץ קידוד. ייתכן שלא יפעל על כל הסרטונים.",
"transcoding_hevc_codec": "קידוד HEVC", "transcoding_hevc_codec": "קידוד HEVC",
"transcoding_max_b_frames": "B-frames מרביים", "transcoding_max_b_frames": "B-frames מקסימלי",
"transcoding_max_b_frames_description": "ערכים גבוהים יותר משפרים את יעילות הדחיסה, אך מאטים את הקידוד. ייתכן שלא יהיה תואם עם האצת חומרה במכשירים ישנים יותר. 0 משבית את B-frames, בעוד ש1- מגדיר את הערך זה באופן אוטומטי.", "transcoding_max_b_frames_description": "ערכים גבוהים יותר משפרים את יעילות הדחיסה, אך מאטים את הקידוד. ייתכן שלא יהיה תואם להאצת חומרה במכשירים ישנים יותר. 0 משבית את B-frames, בעוד ש-1 מגדיר ערך זה באופן אוטומטי.",
"transcoding_max_bitrate": "קצב סיביות מרבי", "transcoding_max_bitrate": "קצב סיביות מרבי",
"transcoding_max_bitrate_description": "קביעת קצב סיביות מרבי יכולה להפוך את גדלי הקבצים לצפויים יותר בעלות קלה לאיכות. ב-720p, ערכים טיפוסיים הם 2600k עבור VP9 או HEVC, או 4500k עבור H.264. מושבת אם מוגדר ל-0.", "transcoding_max_bitrate_description": "קביעת קצב סיביות מקסימלי יכולה להפוך את גדלי הקבצים לצפויים יותר בעלות קלה לאיכות. ב-720p, ערכים טיפוסיים הם 2600k עבור VP9 או HEVC, או 4500k עבור H.264. מושבת אם מוגדר ל-0.",
"transcoding_max_keyframe_interval": "מרווח תמונת מפתח מרבי", "transcoding_max_keyframe_interval": "מרווח תמונת מפתח מרבי",
"transcoding_max_keyframe_interval_description": "מגדיר את מרחק הפריימים המרבי בין תמונות מפתח. ערכים נמוכים גורעים את יעילות הדחיסה, אך משפרים את זמני החיפוש ועשויים לשפר את האיכות בסצנות עם תנועה מהירה. 0 מגדיר ערך זה באופן אוטומטי.", "transcoding_max_keyframe_interval_description": "מגדיר את מרחק הפריימים המרבי בין פריימים מפתח. ערכים נמוכים מחמירים את יעילות הדחיסה, אך משפרים את זמני החיפוש ועשויים לשפר את האיכות בסצנות עם תנועה מהירה. 0 מגדיר ערך זה באופן אוטומטי.",
"transcoding_optimal_description": "סרטונים גבוהים מרזולוציית היעד או לא בפורמט מקובל", "transcoding_optimal_description": "סרטונים גבוהים מרזולוציית היעד או לא בפורמט מקובל",
"transcoding_preferred_hardware_device": "מכשיר חומרה מועדף", "transcoding_preferred_hardware_device": "מכשיר חומרה מועדף",
"transcoding_preferred_hardware_device_description": "חל רק על VAAPI ו-QSV. מגדיר את צומת ה-dri המשמש להמרת קידוד של חומרה.", "transcoding_preferred_hardware_device_description": "חל רק על VAAPI ו-QSV. מגדיר את צומת ה-dri המשמש לקידוד חומרה.",
"transcoding_preset_preset": "הגדרות קבועות מראש (-preset)", "transcoding_preset_preset": "תבנית (-preset)",
"transcoding_preset_preset_description": "מהירות דחיסה. הגדרות קבועות מראש איטיות יותר מייצרות קבצים קטנים יותר ומגבירות את האיכות כאשר מכוונים לקצב סיביות מסוים. VP9 מתעלם ממהירויות מעל 'מהיר יותר'.", "transcoding_preset_preset_description": "מהירות דחיסה. קביעות מוגדרות מראש איטיות יותר מייצרות קבצים קטנים יותר ומגבירות את האיכות בעת מיקוד לקצב סיביות מסוים. VP9 מתעלם ממהירויות מעל 'מהיר יותר'.",
"transcoding_reference_frames": "פריימים להתייחסות", "transcoding_reference_frames": "Reference frames",
"transcoding_reference_frames_description": "מספר הפריימים להתייחסות בעת דחיסה של פריים נתון. ערכים גבוהים יותר משפרים את יעילות הדחיסה, אך מאטים את הקידוד. 0 מגדיר את הערך זה באופן אוטומטי.", "transcoding_reference_frames_description": "מספר הפריימים להתייחסות בעת דחיסה של מסגרת נתונה. ערכים גבוהים יותר משפרים את יעילות הדחיסה, אך מאטים את הקידוד. 0 מגדיר ערך זה באופן אוטומטי.",
"transcoding_required_description": "רק סרטונים שאינם בפורמט מקובל", "transcoding_required_description": "רק סרטונים שאינם בפורמט מקובל",
"transcoding_settings": "הגדרות המרת קידוד סרטונים", "transcoding_settings": "הגדרות קידוד וידיאו",
"transcoding_settings_description": "נהל את הרזולוציה ומידע הקידוד של קבצי הסרטונים", "transcoding_settings_description": "נהל את הרזולוציה והקידוד של קבצי הווידאו",
"transcoding_target_resolution": "רזולוציה יעד", "transcoding_target_resolution": "רזולוציה רצויה",
"transcoding_target_resolution_description": "רזולוציות גבוהות יותר יכולות לשמר פרטים רבים יותר אך לוקחות זמן רב יותר לקידוד, יש להן גדלי קבצים גדולים יותר, ויכולות להפחית את תגובתיות האפליקציה.", "transcoding_target_resolution_description": "רזולוציות גבוהות יותר יכולות לשמר פרטים רבים יותר, אך ייקח זמן רב יותר לקידוד, יש להן גדלי קבצים גדולים יותר ויכולות להאיט את מהירות האפליקציה.",
"transcoding_temporal_aq": "Temporal AQ", "transcoding_temporal_aq": "Temporal AQ",
"transcoding_temporal_aq_description": "חל רק על NVENC. מגביר את האיכות של סצנות עם רמת פירוט גבוהה בהילוך איטי. ייתכן שלא יהיה תואם למכשירים ישנים יותר.", "transcoding_temporal_aq_description": "חל רק על NVENC. מגביר את האיכות של סצנות עם פרטים גבוהים, בתנועה נמוכה. ייתכן שלא יהיה תואם למכשירים ישנים יותר.",
"transcoding_threads": "תהליכונים", "transcoding_threads": "תהליכים במקביל",
"transcoding_threads_description": "ערכים גבוהים יותר מובילים לקידוד מהיר יותר, אך משאירים פחות מקום לשרת לעבד משימות אחרות בעודו פעיל. ערך זה לא צריך להיות יותר ממספר ליבות המעבד. ממקסם את הניצול אם מוגדר ל-0.", "transcoding_threads_description": "ערכים גבוהים יותר מובילים לקידוד מהיר יותר, אך מגבירים את העומס על השרת. ערך זה לא צריך להיות יותר ממספר ליבות המעבד. ממקסם את הניצול אם מוגדר ל-0.",
"transcoding_tone_mapping": "מיפוי גוונים", "transcoding_tone_mapping": "מיפוי גוונים",
"transcoding_tone_mapping_description": "מנסה לשמר את המראה של סרטוני HDR כשהם מומרים ל-SDR. כל אלגוריתם עושה פשרות שונות עבור צבע, פירוט ובהירות. Hable שומר על פרטים, Mobius שומר על צבע, ו Reinhard שומר על בהירות.", "transcoding_tone_mapping_description": "ניסיונות לשמר את המראה של סרטוני HDR כשהם מומרים ל-SDR. כל אלגוריתם עושה פשרות שונות עבור צבע, פירוט ובהירות. Hable שומר על פרטים, Mobius שומר על צבע, ו Reinhard שומר על בהירות.",
"transcoding_tone_mapping_npl": "בהירות שיא נומינלית למיפוי גוונים", "transcoding_tone_mapping_npl": "בהירות שיא נומינלית למיפוי גוונים",
"transcoding_tone_mapping_npl_description": "הצבעים יותאמו כך שיראו נורמליים לתצוגה של בהירות זו. באופן מנוגד לאינטואיציה, ערכים נמוכים מגבירים את בהירות הווידאו ולהיפך מכיוון שהוא מפצה על בהירות התצוגה. 0 מגדיר ערך זה באופן אוטומטי.", "transcoding_tone_mapping_npl_description": "הצבעים יותאמו כך שיראו נורמליים לתצוגה של בהירות זו. באופן מנוגד לאינטואיציה, ערכים נמוכים מגבירים את בהירות הווידאו ולהיפך מכיוון שהוא מפצה על בהירות התצוגה. 0 מגדיר ערך זה באופן אוטומטי.",
"transcoding_transcode_policy": "מדיניות המרת קידוד", "transcoding_transcode_policy": "מדיניות Transcode",
"transcoding_transcode_policy_description": "מדיניות לגבי מתי סרטון צריך לעבור המרת קידוד. סרטוני HDR תמיד יעברו המרת קידוד (למעט אם המרת הקידוד מושבתת).", "transcoding_transcode_policy_description": "מדיניות לגבי מתי יש להמיר סרטון. סרטוני HDR תמיד יקודדו (למעט אם ההמרה מושבתת).",
"transcoding_two_pass_encoding": "קידוד בשני מעברים", "transcoding_two_pass_encoding": "קידוד בשני מעברים",
"transcoding_two_pass_encoding_setting_description": "המר קידוד בשני מעברים כדי לייצר סרטונים מקודדים טובים יותר. כאשר קצב סיביות מרבי מופעל (נדרש כדי שהוא יעבוד עם H.264 ו-HEVC), מצב זה משתמש בטווח קצב סיביות המבוסס על קצב הסיביות המרבי ומתעלם מ-CRF. עבור VP9, ניתן להשתמש ב-CRF אם קצב סיביות מרבי מושבת.", "transcoding_two_pass_encoding_setting_description": "המרת קוד בשני מעברים כדי לייצר סרטונים מקודדים טובים יותר. כאשר קצב סיביות מקסימלי מופעל (נדרש כדי שהוא יעבוד עם H.264 ו-HEVC), מצב זה משתמש בטווח קצב סיביות המבוסס על קצב הסיביות המקסימלי ומתעלם מ-CRF. עבור VP9, ניתן להשתמש ב-CRF אם קצב סיביות מקסימלי מושבת.",
"transcoding_video_codec": "קידוד סרטון", "transcoding_video_codec": "וידאו Codec",
"transcoding_video_codec_description": "ל-VP9 יש יעילות גבוהה ותאימות לאינטרנט, אבל לוקח יותר זמן להמיר את הקידוד עבורו. HEVC מתפקד באופן דומה, אך בעל תאימות אינטרנט נמוכה יותר. H.264 תואם באופן נרחב ומהיר להמיר את קידודו, אבל הוא מייצר קבצים גדולים בהרבה. AV1 הוא הקידוד היעיל ביותר אך לוקה בתמיכה במכשירים ישנים יותר.", "transcoding_video_codec_description": "ל-VP9 יש יעילות גבוהה ותאימות לאינטרנט, אבל לוקח יותר זמן להמיר את הקידוד עבורו. HEVC מתפקד באופן דומה, אך בעל תאימות אינטרנט נמוכה יותר. H.264 תואם באופן נרחב ומהיר לקידוד, אך מייצר קבצים גדולים בהרבה. AV1 הוא ה-codec היעיל ביותר אך לוקה בתמיכה במכשירים ישנים יותר.",
"trash_enabled_description": "הפעל את תכונות פח האשפה", "trash_enabled_description": "הפעל את פח השאפה",
"trash_number_of_days": "מספר הימים", "trash_number_of_days": "מספר הימים",
"trash_number_of_days_description": "מספר הימים לשמירה על הנכסים בפח האשפה לפני הסרתם לצמיתות", "trash_number_of_days_description": "מספר הימים לשמירה על הנכסים באשפה לפני הסרתם לצמיתות",
"trash_settings": "הגדרות פח האשפה", "trash_settings": "הגדרות סל מחזור",
"trash_settings_description": "נהל את הגדרות סל האשפה", "trash_settings_description": "נהל את הגדרות סל המחזור",
"untracked_files": "קבצים ללא מעקב", "untracked_files": "קבצים ללא מעקב",
"untracked_files_description": "קבצים אלה אינם נמצאים במעקב של היישום. הם יכולים להיות תוצאות של העברות כושלות, העלאות שנקטעו, או שנותרו מאחור בגלל שיבוש בתוכנה", "untracked_files_description": "האפליקציה לא עוקבת אחר קבצים אלה. הם יכולים להיות תוצאות של העברות כושלות, העלאות עם הפרעה באמצע או שנותרו מאחור בגלל באג",
"user_delete_delay": "החשבון והנכסים של <b>{user}</b> יתוזמנו למחיקה לצמיתות בעוד {delay, plural, one {יום #} other {# ימים}}.", "user_delete_delay": "החשבון והנכסים של <b>{user}</b> יתוזמנו למחיקה לצמיתות בעוד {delay, plural, one {יום #} other {# ימים}}.",
"user_delete_delay_settings": "עיכוב מחיקה", "user_delete_delay_settings": "עיכוב מחיקה",
"user_delete_delay_settings_description": "מספר הימים לאחר ההסרה עד מחיקה לצמיתות של החשבון והנכסים של המשתמש. משימת מחיקת המשתמש פועלת בחצות כדי לבדוק אם יש משתמשים שמוכנים למחיקה. שינויים בהגדרה זו יוערכו בביצוע הבא.", "user_delete_delay_settings_description": "מספר הימים לאחר ההסרה עד מחיקה לצמיתות של החשבון והנכסים של המשתמש. משימת מחיקת המשתמש פועלת בחצות כדי לבדוק אם יש משתמשים שמוכנים למחיקה. שינויים בהגדרה זו יוערכו בביצוע הבא.",
"user_delete_immediately": "החשבון והנכסים של <b>{user}</b> יעמדו בתור למחיקה לצמיתות <b>באופן מיידי</b>.", "user_delete_immediately": "החשבון והנכסים של <b>{user}</b> יעמדו בתור למחיקה לצמיתות <b>באופן מיידי</b>.",
"user_delete_immediately_checkbox": "להעמיד משתמש ונכסים בתור למחיקה מיידית", "user_delete_immediately_checkbox": "שים משתמש ונכסים בתור למחיקה מיידית",
"user_management": "ניהול משתמשים", "user_management": "ניהול משתמשים",
"user_password_has_been_reset": "סיסמת המשתמש אופסה:", "user_password_has_been_reset": "סיסמת המשתמש אופסה:",
"user_password_reset_description": "אנא ספק את הסיסמה הזמנית למשתמש והודע לו שיש צורך לשנות את הסיסמה בכניסה הבאה שלו.", "user_password_reset_description": "אנא ספק את הסיסמה הזמנית למשתמש והודיע לו שיש צורך לשנות את הסיסמה בכניסה הבאה שלו.",
"user_restore_description": "החשבון של <b>{user}</b> ישוחזר.", "user_restore_description": "החשבון של <b>{user}</b> ישוחזר.",
"user_restore_scheduled_removal": "שחזר משתמש - מחיקה מתוזמנת ב {date, date, long}", "user_restore_scheduled_removal": "שחזר משתמש - מחיקה מתוזמנת ב {date, date, long}",
"user_settings": "הגדרות משתמש", "user_settings": "הגדרות משתמש",
"user_settings_description": "נהל הגדרות משתמש", "user_settings_description": "נהל הגדרות משתמש",
"user_successfully_removed": "המשתמש {email} הוסר בהצלחה.", "user_successfully_removed": "המשתמש {email} הוסר בהצלחה.",
"version_check_enabled_description": "אפשר בקשות רשת תקופתיות ל-GitHub כדי לבדוק אם יש מהדורות חדשות", "version_check_enabled_description": "אפשר בקשות אינטרנט תקופתיות ל-GitHub כדי לחפש גרסאות חדשות",
"version_check_settings": "בדיקת גרסה", "version_check_settings": "בדיקת גירסה",
"version_check_settings_description": "הפעל/השבת את ההודעה על גרסה חדשה", "version_check_settings_description": "הפעל/השבת את ההודעה על גרסה חדשה",
"video_conversion_job": "המרת קידוד סרטונים", "video_conversion_job": "Transcode לסרטונים",
"video_conversion_job_description": "המרת קידוד סרטונים לתאימות רחבה יותר עם דפדפנים ומכשירים" "video_conversion_job_description": "המרת קטעי וידאו לתאימות רחבה יותר עם דפדפנים ומכשירים"
}, },
"admin_email": "דוא\"ל מנהל", "admin_email": "דוא\"ל מנהל",
"admin_password": "סיסמת מנהל", "admin_password": "סיסמת מנהל",
@@ -328,7 +328,7 @@
"age_year_months": "גיל שנה 1, {months, plural, one {חודש #} other {# חודשים}}", "age_year_months": "גיל שנה 1, {months, plural, one {חודש #} other {# חודשים}}",
"age_years": "{years, plural, other {גיל #}}", "age_years": "{years, plural, other {גיל #}}",
"album_added": "אלבום התווסף", "album_added": "אלבום התווסף",
"album_added_notification_setting_description": "קבל/י הודעת דוא\"ל כאשר את/ה מתווספ/ת לאלבום משותף", "album_added_notification_setting_description": "קבל הודעת דוא\"ל כאשר אתה מתווסף לאלבום משותף",
"album_cover_updated": "עטיפת האלבום עודכנה", "album_cover_updated": "עטיפת האלבום עודכנה",
"album_delete_confirmation": "את/ה בטוח/ה שברצונך למחוק את האלבום {album}?\nאם האלבום הזה משותף, משתמשים אחרים לא יוכלו לגשת אליו יותר.", "album_delete_confirmation": "את/ה בטוח/ה שברצונך למחוק את האלבום {album}?\nאם האלבום הזה משותף, משתמשים אחרים לא יוכלו לגשת אליו יותר.",
"album_info_updated": "מידע האלבום עודכן", "album_info_updated": "מידע האלבום עודכן",
@@ -361,7 +361,7 @@
"app_settings": "הגדרות אפליקציה", "app_settings": "הגדרות אפליקציה",
"appears_in": "מוצג ב", "appears_in": "מוצג ב",
"archive": "ארכיון", "archive": "ארכיון",
"archive_or_unarchive_photo": "הוסף תמונה לארכיון או הסר אותה משם", "archive_or_unarchive_photo": "הוסף לארכיון או הוצא תמונה מהארכיון",
"archive_size": "גודל הארכיון", "archive_size": "גודל הארכיון",
"archive_size_description": "הגדר את גודל הארכיון להורדות (ב-GiB)", "archive_size_description": "הגדר את גודל הארכיון להורדות (ב-GiB)",
"archived": "בארכיון", "archived": "בארכיון",
@@ -372,9 +372,9 @@
"asset_adding_to_album": "מוסיף לאלבום...", "asset_adding_to_album": "מוסיף לאלבום...",
"asset_description_updated": "תיאור הנכס עודכן", "asset_description_updated": "תיאור הנכס עודכן",
"asset_filename_is_offline": "הנכס {filename} אינו מקוון", "asset_filename_is_offline": "הנכס {filename} אינו מקוון",
"asset_has_unassigned_faces": "לנכס יש פנים שלא הוקצו", "asset_has_unassigned_faces": "לנכס יש פרצופים שלא הוקצו",
"asset_hashing": "מגבב...", "asset_hashing": "מגבב...",
"asset_offline": "נכס לא מקוון", "asset_offline": "נכס לא זמין",
"asset_offline_description": "הנכס הזה אינו מקוון. Immich לא יכול לגשת למיקום הקובץ שלו. נא לוודא שהנכס זמין ואז סרוק מחדש את הספרייה.", "asset_offline_description": "הנכס הזה אינו מקוון. Immich לא יכול לגשת למיקום הקובץ שלו. נא לוודא שהנכס זמין ואז סרוק מחדש את הספרייה.",
"asset_skipped": "דולג", "asset_skipped": "דולג",
"asset_uploaded": "הועלה", "asset_uploaded": "הועלה",
@@ -388,14 +388,14 @@
"assets_moved_to_trash_count": "{count, plural, one {נכס # הועבר} other {# נכסים הועברו}} לאשפה", "assets_moved_to_trash_count": "{count, plural, one {נכס # הועבר} other {# נכסים הועברו}} לאשפה",
"assets_permanently_deleted_count": "{count, plural, one {נכס # נמחק} other {# נכסים נמחקו}} לצמיתות", "assets_permanently_deleted_count": "{count, plural, one {נכס # נמחק} other {# נכסים נמחקו}} לצמיתות",
"assets_removed_count": "{count, plural, one {נכס # הוסר} other {# נכסים הוסרו}}", "assets_removed_count": "{count, plural, one {נכס # הוסר} other {# נכסים הוסרו}}",
"assets_restore_confirmation": "האם את/ה בטוח שברצונך לשחזר את כל הנכסים שבאשפה? את/ה לא יכול/ה לבטל את הפעולה הזו!", "assets_restore_confirmation": "האם את/ה בטוח שברצונך לשחזר את כל הנכסים שבאשפה? את/ה לא יכול/ה לבטל את הפעולה הזו!",
"assets_restored_count": "{count, plural, one {נכס # שוחזר} other {# נכסים שוחזרו}}", "assets_restored_count": "{count, plural, one {נכס # שוחזר} other {# נכסים שוחזרו}}",
"assets_trashed_count": "{count, plural, one {נכס # הושלך} other {# נכסים הושלכו}} לאשפה", "assets_trashed_count": "{count, plural, one {נכס # הושלך} other {# נכסים הושלכו}} לאשפה",
"assets_were_part_of_album_count": "{count, plural, one {נכס היה} other {נכסים היו}} כבר חלק מהאלבום", "assets_were_part_of_album_count": "{count, plural, one {נכס היה} other {נכסים היו}} כבר חלק מהאלבום",
"authorized_devices": "מכשירים מורשים", "authorized_devices": "מכשירים מורשים",
"back": "חזרה", "back": "אחורה",
"back_close_deselect": "חזור, סגור, או בטל בחירה", "back_close_deselect": "חזור, סגור, או בטל בחירה",
"backward": "אחורה", "backward": "לאחור",
"birthdate_saved": "תאריך לידה נשמר בהצלחה", "birthdate_saved": "תאריך לידה נשמר בהצלחה",
"birthdate_set_description": "תאריך לידה משמש לחישוב הגיל של האדם הזה בזמן תצלום.", "birthdate_set_description": "תאריך לידה משמש לחישוב הגיל של האדם הזה בזמן תצלום.",
"blurred_background": "רקע מטושטש", "blurred_background": "רקע מטושטש",
@@ -424,10 +424,10 @@
"change_password": "שינוי סיסמה", "change_password": "שינוי סיסמה",
"change_password_description": "זאת או הפעם הראשונה שאת/ה מתחבר/ת למערכת או שנעשתה בקשה לשינוי הסיסמה שלך. נא להזין את הסיסמה החדשה למטה.", "change_password_description": "זאת או הפעם הראשונה שאת/ה מתחבר/ת למערכת או שנעשתה בקשה לשינוי הסיסמה שלך. נא להזין את הסיסמה החדשה למטה.",
"change_your_password": "החלף את הסיסמה שלך", "change_your_password": "החלף את הסיסמה שלך",
"changed_visibility_successfully": נראות שונתה בהצלחה", "changed_visibility_successfully": חשיפה השתנתה בהצלחה",
"check_all": "לסמן הכל", "check_all": "לסמן הכל",
"check_logs": "בדוק יומנים", "check_logs": "בדוק יומנים",
"choose_matching_people_to_merge": "בחר אנשים תואמים למיזוג", "choose_matching_people_to_merge": "בחר אנשים זהים למיזוג",
"city": "עיר", "city": "עיר",
"clear": "נקה", "clear": "נקה",
"clear_all": "נקה הכל", "clear_all": "נקה הכל",
@@ -435,15 +435,15 @@
"clear_value": "נקה ערך", "clear_value": "נקה ערך",
"close": "סגור", "close": "סגור",
"collapse": "כווץ", "collapse": "כווץ",
"collapse_all": "כווץ הכל", "collapse_all": "מזער הכל",
"color_theme": "צבע ערכת נושא", "color_theme": "צבע ערכת נושא",
"comment_deleted": "תגובה נמחקה", "comment_deleted": "תגובה נמחקה",
"comment_options": "אפשרויות תגובה", "comment_options": "אפשרויות תגובה",
"comments_and_likes": "תגובות & לייקים", "comments_and_likes": "תגובות & לייקים",
"comments_are_disabled": "תגובות מושבתות", "comments_are_disabled": "תגובות מושבתות",
"confirm": ישור", "confirm": שר",
"confirm_admin_password": "אשר סיסמת מנהל", "confirm_admin_password": "אשר סיסמת מנהל",
"confirm_delete_shared_link": "האם את/ה בטוח שברצונך למחוק את הקישור המשותף הזה?", "confirm_delete_shared_link": "האם אתה בטוח שברצונך למחוק את הקישור המשותף הזה?",
"confirm_password": "אשר סיסמה", "confirm_password": "אשר סיסמה",
"contain": "מכיל", "contain": "מכיל",
"context": "הקשר", "context": "הקשר",
@@ -454,26 +454,26 @@
"copy_file_path": "העתק את נתיב הקובץ", "copy_file_path": "העתק את נתיב הקובץ",
"copy_image": "העתק תמונה", "copy_image": "העתק תמונה",
"copy_link": "העתק קישור", "copy_link": "העתק קישור",
"copy_link_to_clipboard": "העתק קישור ללוח", "copy_link_to_clipboard": "העתק לינק ללוח",
"copy_password": "העתק סיסמה", "copy_password": "העתק סיסמה",
"copy_to_clipboard": "העתק ללוח", "copy_to_clipboard": "העתק ללוח",
"country": "ארץ", "country": "מדינה",
"cover": "מכסה", "cover": "עטיפה",
"covers": "עטיפות", "covers": "עטיפות",
"create": "צור", "create": "צור",
"create_album": "צור אלבום", "create_album": "צור אלבום",
"create_library": "צור ספרייה", "create_library": "צור ספרייה",
"create_link": "צור קישור", "create_link": "צור קישור",
"create_link_to_share": "צור קישור לשיתוף", "create_link_to_share": "צור קישור לשיתוף",
"create_link_to_share_description": "אפשר לכל אחד עם הקישור לראות את התמונות שנבחרו", "create_link_to_share_description": "תן לכל אחד עם הקישור לראות את התמונות שנבחרו",
"create_new_person": "צור אדם חדש", "create_new_person": "צור אדם חדש",
"create_new_person_hint": "הקצה את הנכסים שנבחרו לאדם חדש", "create_new_person_hint": "הקצה את הנכסים שנבחרו לאדם חדש",
"create_new_user": "צור משתמש חדש", "create_new_user": "צור משתמש חדש",
"create_user": "צור משתמש", "create_user": "צור משתמש",
"created": "נוצר", "created": "נוצר",
"current_device": "מכשיר נוכחי", "current_device": "מכשיר נוכחי",
"custom_locale": "אזור שפה מותאם אישית", "custom_locale": "מיקום מותאם אישית",
"custom_locale_description": "עצב תאריכים ומספרים על סמך השפה והאזור", "custom_locale_description": "תצוגת תאריכים ומספרים על סמך השפה והאזור",
"dark": "כהה", "dark": "כהה",
"date_after": "תאריך אחרי", "date_after": "תאריך אחרי",
"date_and_time": "תאריך ושעה", "date_and_time": "תאריך ושעה",
@@ -481,13 +481,13 @@
"date_of_birth_saved": "תאריך לידה נשמר בהצלחה", "date_of_birth_saved": "תאריך לידה נשמר בהצלחה",
"date_range": "טווח תאריכים", "date_range": "טווח תאריכים",
"day": "יום", "day": "יום",
"deduplicate_all": "בטל כפילויות של הכל", "deduplicate_all": "מחק כפילויות של הכל",
"default_locale": "שפה ואזור ברירת מחדל", "default_locale": "מיקום ברירת מחדל",
"default_locale_description": "עצב תאריכים ומספרים על סמך השפה והאזור של הדפדפן שלך", "default_locale_description": "הצג תאריכים ומספרים על סמך המיקום של הדפדפן שלך",
"delete": "מחק", "delete": "מחק",
"delete_album": "מחק אלבום", "delete_album": "מחק אלבום",
"delete_api_key_prompt": "האם את/ה בטוח שברצונך למחוק מפתח API זה?", "delete_api_key_prompt": "האם אתה בטוח שברצונך למחוק מפתח API זה?",
"delete_duplicates_confirmation": "האם את/ה בטוח שברצונך למחוק לצמיתות את הכפילויות האלה?", "delete_duplicates_confirmation": "האם אתה בטוח שברצונך למחוק לצמיתות את הכפילויות האלה?",
"delete_key": "מחק מפתח", "delete_key": "מחק מפתח",
"delete_library": "מחק ספרייה", "delete_library": "מחק ספרייה",
"delete_link": "מחק קישור", "delete_link": "מחק קישור",
@@ -498,25 +498,25 @@
"details": "פרטים", "details": "פרטים",
"direction": "כיוון", "direction": "כיוון",
"disabled": "מושבת", "disabled": "מושבת",
"disallow_edits": "אל תאפשר עריכות", "disallow_edits": "מנע עריכה",
"discover": "גלה", "discover": "גלה",
"dismiss_all_errors": "התעלם מכל השגיאות", "dismiss_all_errors": "התעלם מכל השגיאות",
"dismiss_error": "התעלם מהשגיאה", "dismiss_error": "התעלם מהשגיאה",
"display_options": "הצג אפשרויות", "display_options": "הצג אפשרויות",
"display_order": "סדר תצוגה", "display_order": "סדר הופעה",
"display_original_photos": "הצג תמונות מקוריות", "display_original_photos": "הצג תמונות מקוריות",
"display_original_photos_setting_description": "העדף להציג את התמונה המקורית בעת צפיית נכס במקום תמונות ממוזערות כאשר הנכס המקורי תומך בתצוגה בדפדפן. זה עלול לגרום לתמונות להיות מוצגות באיטיות.", "display_original_photos_setting_description": "העדפה להציג את התמונה המקורית בעת הצגת נכס במקום תמונות ממוזערות כאשר הנכס המקורי תומך בתצוגה בדפדפן. זה עלול לגרום לתמונות להיות מוצגות באיטיות.",
"do_not_show_again": "אל תציג את ההודעה הזאת שוב", "do_not_show_again": "אל תציג את ההודעה הזאת שוב",
"done": "בוצע", "done": "הסתיים",
"download": "הורדה", "download": "הורדה",
"download_settings": "הורדה", "download_settings": "הורדה",
"download_settings_description": "נהל הגדרות הקשורות להורדת נכסים", "download_settings_description": "נהל הגדרות הקשורות להורדת נכסים",
"downloading": "מוריד", "downloading": "בהורדה",
"downloading_asset_filename": "מוריד נכס {filename}", "downloading_asset_filename": "מוריד נכס {filename}",
"drop_files_to_upload": "זרוק קבצים לכל מקום כדי להעלות", "drop_files_to_upload": "זרוק קבצים לכל מקום כדי להעלות",
"duplicates": "כפילויות", "duplicates": "כפילויות",
"duplicates_description": פרד כל קבוצה על ידי ציון אילו, אם בכלל, הן כפילויות", "duplicates_description": גדר כל קבוצה על ידי ציון אילו, אם בכלל, הם כפילויות",
"duration": "משך זמן", "duration": "אורך",
"durations": { "durations": {
"days": "{days, plural, one {day} other {{days, number} days}}", "days": "{days, plural, one {day} other {{days, number} days}}",
"hours": "{hours, plural, one {hour} other {{hours, number} hours}}", "hours": "{hours, plural, one {hour} other {{hours, number} hours}}",
@@ -529,7 +529,7 @@
"edit_avatar": "ערוך תמונת פרופיל", "edit_avatar": "ערוך תמונת פרופיל",
"edit_date": "ערוך תאריך", "edit_date": "ערוך תאריך",
"edit_date_and_time": "ערוך תאריך ושעה", "edit_date_and_time": "ערוך תאריך ושעה",
"edit_exclusion_pattern": "ערוך דפוס החרגה", "edit_exclusion_pattern": "ערוך אי הכללה",
"edit_faces": "ערוך פנים", "edit_faces": "ערוך פנים",
"edit_import_path": "ערוך נתיב יבוא", "edit_import_path": "ערוך נתיב יבוא",
"edit_import_paths": "ערוך נתיבי ייבוא", "edit_import_paths": "ערוך נתיבי ייבוא",
@@ -542,10 +542,10 @@
"edit_user": "ערוך משתמש", "edit_user": "ערוך משתמש",
"edited": "נערך", "edited": "נערך",
"editor": "עורך", "editor": "עורך",
"email": "דוא\"ל", "email": "אימייל",
"empty": "", "empty": "",
"empty_album": "אלבום ריק", "empty_album": "אלבום ריק",
"empty_trash": "רוקן אשפה", "empty_trash": "רוקן סל מחזור",
"empty_trash_confirmation": "האם את/ה בטוח/ה שברצונך לרוקן את האשפה? זה יסיר לצמיתות את כל הנכסים באשפה מImmich.\nאת/ה לא יכול/ה לבטל פעולה זו!", "empty_trash_confirmation": "האם את/ה בטוח/ה שברצונך לרוקן את האשפה? זה יסיר לצמיתות את כל הנכסים באשפה מImmich.\nאת/ה לא יכול/ה לבטל פעולה זו!",
"enable": "הפעל", "enable": "הפעל",
"enabled": "מופעל", "enabled": "מופעל",
@@ -560,8 +560,8 @@
"cant_change_activity": "לא ניתן {enabled, select, true {להשבית} other {לאפשר}} פעילות", "cant_change_activity": "לא ניתן {enabled, select, true {להשבית} other {לאפשר}} פעילות",
"cant_change_asset_favorite": "לא ניתן לשנות עדיפות עבור נכס", "cant_change_asset_favorite": "לא ניתן לשנות עדיפות עבור נכס",
"cant_change_metadata_assets_count": "לא ניתן לשנות את המטא-נתונים של {count, plural, one {נכס #} other {# נכסים}}", "cant_change_metadata_assets_count": "לא ניתן לשנות את המטא-נתונים של {count, plural, one {נכס #} other {# נכסים}}",
"cant_get_faces": "לא ניתן לקבל פנים", "cant_get_faces": "לא ניתן לקבל פרצופים",
"cant_get_number_of_comments": "לא ניתן לקבל את מספר התגובות", "cant_get_number_of_comments": "לא ניתן לקבל מספר של תגובות",
"cant_search_people": "לא ניתן לחפש אנשים", "cant_search_people": "לא ניתן לחפש אנשים",
"cant_search_places": "לא ניתן לחפש מקומות", "cant_search_places": "לא ניתן לחפש מקומות",
"cleared_jobs": "משימות נוקו עבור: {job}", "cleared_jobs": "משימות נוקו עבור: {job}",
@@ -569,7 +569,7 @@
"error_adding_users_to_album": "שגיאה בהוספת משתמשים לאלבום", "error_adding_users_to_album": "שגיאה בהוספת משתמשים לאלבום",
"error_deleting_shared_user": "שגיאה במחיקת משתמש משותף", "error_deleting_shared_user": "שגיאה במחיקת משתמש משותף",
"error_downloading": "שגיאה בהורדת {filename}", "error_downloading": "שגיאה בהורדת {filename}",
"error_removing_assets_from_album": "שגיאה בהסרת נכסים מאלבום, בדוק את המסוף לפרטים נוספים", "error_removing_assets_from_album": "שגיאה בהסרת נכסים מאלבום, בדוק בלוח הבקרה לפרטים נוספים",
"error_selecting_all_assets": "שגיאה בבחירת כל הנכסים", "error_selecting_all_assets": "שגיאה בבחירת כל הנכסים",
"exclusion_pattern_already_exists": "דפוס החרגה זה כבר קיים.", "exclusion_pattern_already_exists": "דפוס החרגה זה כבר קיים.",
"failed_job_command": "הפקודה {command} נכשלה עבור המשימה: ‪‪{job}", "failed_job_command": "הפקודה {command} נכשלה עבור המשימה: ‪‪{job}",
@@ -585,17 +585,17 @@
"incorrect_email_or_password": "דוא\"ל או סיסמה שגויים", "incorrect_email_or_password": "דוא\"ל או סיסמה שגויים",
"paths_validation_failed": "{paths, plural, one {נתיב # נכשל} other {# נתיבים נכשלו}} אימות", "paths_validation_failed": "{paths, plural, one {נתיב # נכשל} other {# נתיבים נכשלו}} אימות",
"profile_picture_transparent_pixels": "תמונות פרופיל אינן יכולות לכלול פיקסלים שקופים. נא להגדיל ו/או להזיז את התמונה.", "profile_picture_transparent_pixels": "תמונות פרופיל אינן יכולות לכלול פיקסלים שקופים. נא להגדיל ו/או להזיז את התמונה.",
"quota_higher_than_disk_size": "את/ה מגדיר מכסה גבוהה יותר מגודל הדיסק", "quota_higher_than_disk_size": "אתה מגדיר מכסת אחסון גבוהה יותר מגודל הדיסק",
"repair_unable_to_check_items": "לא ניתן לסמן {count, select, one {פריט} other {פריטים}}", "repair_unable_to_check_items": "לא ניתן לסמן {count, select, one {item} other {items}}",
"unable_to_add_album_users": "לא ניתן להוסיף משתמשים לאלבום", "unable_to_add_album_users": "לא ניתן להוסיף משתמשים לאלבום",
"unable_to_add_assets_to_shared_link": "לא ניתן להוסיף נכסים לקישור משותף", "unable_to_add_assets_to_shared_link": "לא ניתן להוסיף נכסים לקישור משותף",
"unable_to_add_comment": "לא ניתן להוסיף תגובה", "unable_to_add_comment": "לא ניתן להוסיף תגובה",
"unable_to_add_exclusion_pattern": "לא ניתן להוסיף דפוס החרגה", "unable_to_add_exclusion_pattern": "לא ניתן להוסיף דפוס אי הכללה",
"unable_to_add_import_path": "לא ניתן להוסיף נתיב ייבוא", "unable_to_add_import_path": "לא ניתן להוסיף נתיב ייבוא",
"unable_to_add_partners": "לא ניתן להוסיף שותפים", "unable_to_add_partners": "לא ניתן להוסיף שותפים",
"unable_to_add_remove_archive": "לא ניתן {archived, select, true {להסיר נכס מ} other {להוסיף נכס ל}}ארכיון", "unable_to_add_remove_archive": "לא ניתן {archived, select, true {להסיר נכס מ} other {להוסיף נכס אל}}ארכיון",
"unable_to_add_remove_favorites": "לא ניתן {favorite, select, true {להוסיף נכס ל} other {להסיר נכס מ}}מועדפים", "unable_to_add_remove_favorites": "לא ניתן {favorite, select, true {להוסיף נכס ל} other {להסיר נכס מ}} מועדפים",
"unable_to_archive_unarchive": "לא ניתן {archived, select, true {להוסיף לארכיון} other {להוציא מארכיון}}", "unable_to_archive_unarchive": "לא ניתן {archived, select, true {להעביר לארכיון} other {להוציא מארכיון}}",
"unable_to_change_album_user_role": "לא ניתן לשנות את התפקיד של משתמש האלבום", "unable_to_change_album_user_role": "לא ניתן לשנות את התפקיד של משתמש האלבום",
"unable_to_change_date": "לא ניתן לשנות תאריך", "unable_to_change_date": "לא ניתן לשנות תאריך",
"unable_to_change_favorite": "לא ניתן לשנות עדיפות עבור נכס", "unable_to_change_favorite": "לא ניתן לשנות עדיפות עבור נכס",
@@ -607,7 +607,7 @@
"unable_to_complete_oauth_login": "לא ניתן להשלים התחברות OAuth", "unable_to_complete_oauth_login": "לא ניתן להשלים התחברות OAuth",
"unable_to_connect": "לא ניתן להתחבר", "unable_to_connect": "לא ניתן להתחבר",
"unable_to_connect_to_server": "לא ניתן להתחבר לשרת", "unable_to_connect_to_server": "לא ניתן להתחבר לשרת",
"unable_to_copy_to_clipboard": "לא ניתן להעתיק ללוח, ודא שאת/ה ניגש לדף דרך https", "unable_to_copy_to_clipboard": "לא ניתן להעתיק ללוח, ודא שאתה ניגש לדף דרך https",
"unable_to_create_admin_account": "לא ניתן ליצור חשבון מנהל", "unable_to_create_admin_account": "לא ניתן ליצור חשבון מנהל",
"unable_to_create_api_key": "לא ניתן ליצור מפתח API חדש", "unable_to_create_api_key": "לא ניתן ליצור מפתח API חדש",
"unable_to_create_library": "לא ניתן ליצור ספרייה", "unable_to_create_library": "לא ניתן ליצור ספרייה",
@@ -615,45 +615,45 @@
"unable_to_delete_album": "לא ניתן למחוק אלבום", "unable_to_delete_album": "לא ניתן למחוק אלבום",
"unable_to_delete_asset": "לא ניתן למחוק נכס", "unable_to_delete_asset": "לא ניתן למחוק נכס",
"unable_to_delete_assets": "שגיאה במחיקת נכסים", "unable_to_delete_assets": "שגיאה במחיקת נכסים",
"unable_to_delete_exclusion_pattern": "לא ניתן למחוק דפוס החרגה", "unable_to_delete_exclusion_pattern": "לא ניתן למחוק דפוס אי הכללה",
"unable_to_delete_import_path": "לא ניתן למחוק את נתיב הייבוא", "unable_to_delete_import_path": "לא ניתן למחוק את נתיב הייבוא",
"unable_to_delete_shared_link": "לא ניתן למחוק קישור משותף", "unable_to_delete_shared_link": "לא ניתן למחוק קישור משותף",
"unable_to_delete_user": "לא ניתן למחוק משתמש", "unable_to_delete_user": "לא ניתן למחוק משתמש",
"unable_to_download_files": "לא ניתן להוריד קבצים", "unable_to_download_files": "לא ניתן להוריד קבצים",
"unable_to_edit_exclusion_pattern": "לא ניתן לערוך דפוס החרגה", "unable_to_edit_exclusion_pattern": "לא ניתן לערוך דפוס אי הכללה",
"unable_to_edit_import_path": "לא ניתן לערוך את נתיב הייבוא", "unable_to_edit_import_path": "לא ניתן לערוך את נתיב הייבוא",
"unable_to_empty_trash": "לא ניתן לרוקן אשפה", "unable_to_empty_trash": "לא ניתן לרוקן אשפה",
"unable_to_enter_fullscreen": "לא ניתן להיכנס למצב מסך מלא", "unable_to_enter_fullscreen": "לא ניתן להיכנס למצב מסך מלא",
"unable_to_exit_fullscreen": "לא ניתן לצאת ממסך מלא", "unable_to_exit_fullscreen": "לא ניתן לצאת ממסך מלא",
"unable_to_get_comments_number": "לא ניתן להשיג את מספר התגובות", "unable_to_get_comments_number": "לא ניתן לקבל מספר של תגובות",
"unable_to_get_shared_link": "קבלת קישור משותף נכשלה", "unable_to_get_shared_link": "קבלת קישור משותף נכשלה",
"unable_to_hide_person": "לא ניתן להסתיר אדם", "unable_to_hide_person": "לא ניתן להסתיר אדם",
"unable_to_link_oauth_account": "לא ניתן לקשר חשבון OAuth", "unable_to_link_oauth_account": "לא ניתן לקשר חשבון OAuth",
"unable_to_load_album": "לא ניתן לטעון אלבום", "unable_to_load_album": "לא ניתן לטעון אלבום",
"unable_to_load_asset_activity": "לא ניתן לטעון את פעילות הנכס", "unable_to_load_asset_activity": "לא ניתן לטעון את פעילות הנכס",
"unable_to_load_items": "לא ניתן לטעון פריטים", "unable_to_load_items": "לא ניתן לטעון נכסים",
"unable_to_load_liked_status": "לא ניתן לטעון מצב 'אהבתי'", "unable_to_load_liked_status": "לא ניתן לטעון סטטוס 'אהבתי'",
"unable_to_log_out_all_devices": "לא ניתן לנתק את כל המכשירים", "unable_to_log_out_all_devices": "לא ניתן לנתק את כל המכשירים",
"unable_to_log_out_device": "לא ניתן לנתק מכשיר", "unable_to_log_out_device": "לא ניתן לנתק מכשיר",
"unable_to_login_with_oauth": "לא ניתן להתחבר באמצעות OAuth", "unable_to_login_with_oauth": "לא ניתן להתחבר באמצעות OAuth",
"unable_to_play_video": "לא ניתן להפעיל את הסרטון", "unable_to_play_video": "לא ניתן להפעיל את הסרטון",
"unable_to_reassign_assets_existing_person": "לא ניתן להקצות מחדש נכסים ל{name, select, null {אדם קיים} other {{name}}}", "unable_to_reassign_assets_existing_person": "לא ניתן להקצות נכסים ל{name, select, null {אדם קיים} other {{name}}}",
"unable_to_reassign_assets_new_person": "לא ניתן להקצות מחדש נכסים לאדם חדש", "unable_to_reassign_assets_new_person": "לא ניתן להקצות מחדש נכסים לאדם חדש",
"unable_to_refresh_user": "לא ניתן לרענן את המשתמש", "unable_to_refresh_user": "לא ניתן לרענן את המשתמש",
"unable_to_remove_album_users": "לא ניתן להסיר משתמשים מהאלבום", "unable_to_remove_album_users": "לא ניתן להסיר משתמשים מהאלבום",
"unable_to_remove_api_key": "לא ניתן להסיר מפתח API", "unable_to_remove_api_key": "לא ניתן להסיר מפתח API",
"unable_to_remove_assets_from_shared_link": "לא ניתן להסיר נכסים מקישור משותף", "unable_to_remove_assets_from_shared_link": "לא ניתן להסיר נכסים מקישור משותף",
"unable_to_remove_comment": "", "unable_to_remove_comment": "",
"unable_to_remove_library": "לא ניתן להסיר ספרייה", "unable_to_remove_library": "לא ניתן להסיר אלבום",
"unable_to_remove_offline_files": "לא ניתן להסיר קבצים לא מקוונים", "unable_to_remove_offline_files": "לא ניתן להסיר קבצים לא מקוונים",
"unable_to_remove_partner": "לא ניתן להסיר שותף", "unable_to_remove_partner": "לא ניתן להסיר שותף",
"unable_to_remove_reaction": "לא ניתן להסיר תגובה", "unable_to_remove_reaction": "לא ניתן למחוק תגובה",
"unable_to_remove_user": "", "unable_to_remove_user": "",
"unable_to_repair_items": "לא ניתן לתקן פריטים", "unable_to_repair_items": "לא ניתן לתקן נכסים",
"unable_to_reset_password": "לא ניתן לאפס סיסמה", "unable_to_reset_password": "לא ניתן לאפס סיסמה",
"unable_to_resolve_duplicate": "לא ניתן לפתור כפילות", "unable_to_resolve_duplicate": "לא ניתן לאחזר כפילות",
"unable_to_restore_assets": "לא ניתן לשחזר נכסים", "unable_to_restore_assets": "לא ניתן לשחזר נכסים",
"unable_to_restore_trash": "לא ניתן לשחזר אשפה", "unable_to_restore_trash": "לא ניתן לשחזר סל מחזור",
"unable_to_restore_user": "לא ניתן לשחזר משתמש", "unable_to_restore_user": "לא ניתן לשחזר משתמש",
"unable_to_save_album": "לא ניתן לשמור אלבום", "unable_to_save_album": "לא ניתן לשמור אלבום",
"unable_to_save_api_key": "לא ניתן לשמור מפתח API", "unable_to_save_api_key": "לא ניתן לשמור מפתח API",
@@ -663,17 +663,17 @@
"unable_to_save_settings": "לא ניתן לשמור הגדרות", "unable_to_save_settings": "לא ניתן לשמור הגדרות",
"unable_to_scan_libraries": "לא ניתן לסרוק ספריות", "unable_to_scan_libraries": "לא ניתן לסרוק ספריות",
"unable_to_scan_library": "לא ניתן לסרוק ספרייה", "unable_to_scan_library": "לא ניתן לסרוק ספרייה",
"unable_to_set_feature_photo": "לא ניתן לבחור תמונה מייצגת", "unable_to_set_feature_photo": "לא ניתן לבחור תמונה ראשית",
"unable_to_set_profile_picture": "לא ניתן לבחור תמונת פרופיל", "unable_to_set_profile_picture": "לא ניתן לבחור תמונת פרופיל",
"unable_to_submit_job": "לא ניתן לשלוח משימה", "unable_to_submit_job": "לא ניתן לשלוח משימה",
"unable_to_trash_asset": "לא ניתן להעביר נכס לאשפה", "unable_to_trash_asset": "לא ניתן להעביר נכס לסל המחזור",
"unable_to_unlink_account": "לא ניתן לבטל קישור חשבון", "unable_to_unlink_account": "לא ניתן לבטל קישור חשבון",
"unable_to_update_album_cover": "לא ניתן לעדכן עטיפת אלבום", "unable_to_update_album_cover": "לא ניתן לעדכן עטיפת אלבום",
"unable_to_update_album_info": "לא ניתן לעדכן פרטי אלבום", "unable_to_update_album_info": "לא ניתן לעדכן פרטי אלבום",
"unable_to_update_library": "לא ניתן לעדכן ספרייה", "unable_to_update_library": "לא ניתן לעדכן ספרייה",
"unable_to_update_location": "לא ניתן לעדכן מיקום", "unable_to_update_location": "לא ניתן לעדכן מיקום",
"unable_to_update_settings": "לא ניתן לעדכן הגדרות", "unable_to_update_settings": "לא ניתן לעדכן הגדרות",
"unable_to_update_timeline_display_status": "לא ניתן לעדכן את מצב תצוגת ציר הזמן", "unable_to_update_timeline_display_status": "לא ניתן לעדכן את סטטוס תצוגת ציר הזמן",
"unable_to_update_user": "לא ניתן לעדכן משתמש", "unable_to_update_user": "לא ניתן לעדכן משתמש",
"unable_to_upload_file": "לא ניתן להעלות קובץ" "unable_to_upload_file": "לא ניתן להעלות קובץ"
}, },
@@ -682,12 +682,12 @@
"every_night_at_twoam": "", "every_night_at_twoam": "",
"every_six_hours": "", "every_six_hours": "",
"exif": "Exif", "exif": "Exif",
"exit_slideshow": "צא ממצגת שקופיות", "exit_slideshow": "צא ממצב מצגת",
"expand_all": "הרחב הכל", "expand_all": "הרחב הכל",
"expire_after": "פג לאחר", "expire_after": "פג לאחר",
"expired": "פג", "expired": "פג",
"expires_date": "יפוג {date}", "expires_date": "יפוג {date}",
"explore": "אני רוצה לראות", "explore": "חקור",
"export": "ייצוא", "export": "ייצוא",
"export_as_json": "ייצוא כ JSON", "export_as_json": "ייצוא כ JSON",
"extension": "סיומת", "extension": "סיומת",
@@ -707,28 +707,25 @@
"files": "", "files": "",
"filetype": "סוג קובץ", "filetype": "סוג קובץ",
"filter_people": "סנן אנשים", "filter_people": "סנן אנשים",
"find_them_fast": "מצא אותם מהר לפי שם עם חיפוש", "find_them_fast": "מצא במהירות לפי חיפוש עם שם",
"fix_incorrect_match": "תקן התאמה שגויה", "fix_incorrect_match": "תקן התאמה שגויה",
"force_re-scan_library_files": "כפה סריקה מחדש של כל קבצי הספרייה", "force_re-scan_library_files": "כפה סריקה מחדש של כל נכסי הספרייה",
"forward": "קדימה", "forward": "קדימה",
"general": "ראשי", "general": "ראשי",
"get_help": "קבל עזרה", "get_help": "בקש תמיכה",
"getting_started": "תחילת העבודה", "getting_started": "בוא נתחיל",
"go_back": "חזור", "go_back": "חזור אחורה",
"go_to_search": "עבור לחיפוש", "go_to_search": "עבור לחיפוש",
"go_to_share_page": "עבור לדף השיתוף", "go_to_share_page": "עבור לדף השיתוף",
"group_albums_by": "קבץ אלבומים לפי..", "group_albums_by": "קבץ אלבומים לפי..",
"group_no": "אין קיבוץ", "group_no": "אין קיבוץ",
"group_owner": "קבץ לפי בעלים", "group_owner": "קבץ לפי בעלים",
"group_year": "קבץ לפי שנה", "group_year": "קבץ לפי שנה",
"has_quota": "יש מכסה", "has_quota": "יש מכסת אחסון",
"hi_user": "היי {name}, ({email})", "hi_user": "היי {name}, ({email})",
"hide_all_people": "הסתר את כל האנשים",
"hide_gallery": "הסתר גלרייה", "hide_gallery": "הסתר גלרייה",
"hide_named_person": "הסתר אדם {name}",
"hide_password": "הסתר סיסמה", "hide_password": "הסתר סיסמה",
"hide_person": "הסתר אדם", "hide_person": "הסתר אדם",
"hide_unnamed_people": "הסתר אנשים ללא שם",
"host": "מארח", "host": "מארח",
"hour": "שעה", "hour": "שעה",
"image": "תמונה", "image": "תמונה",
@@ -803,13 +800,13 @@
"matches": "התאמות", "matches": "התאמות",
"media_type": "סוג מדיה", "media_type": "סוג מדיה",
"memories": "זכרונות", "memories": "זכרונות",
"memories_setting_description": "נהל מה שאת/ה רואה ב\"היום לפני\"", "memories_setting_description": "נהל מה אתה רואה ב היום לפני",
"memory": "זיכרון", "memory": "זיכרון",
"menu": "תפריט", "menu": "תפריט",
"merge": "מזג", "merge": "מזג",
"merge_people": "מזג אנשים", "merge_people": "מזג אנשים",
"merge_people_limit": "ניתן למזג עד 5 פרצופים בכל פעם", "merge_people_limit": "ניתן למזג עד 5 פרצופים בכל פעם",
"merge_people_prompt": "האם את/ה רוצה למזג את האנשים האלה? פעולה זו היא בלתי הפיכה.", "merge_people_prompt": "האם אתה רוצה למזג את האנשים האלה? פעולה זו היא בלתי הפיכה.",
"merge_people_successfully": "מיזוג אנשים בוצע בהצלחה", "merge_people_successfully": "מיזוג אנשים בוצע בהצלחה",
"merged_people_count": "{count, plural, one {אדם # מוזג} other {# אנשים מוזגו}}", "merged_people_count": "{count, plural, one {אדם # מוזג} other {# אנשים מוזגו}}",
"minimize": "מזער", "minimize": "מזער",
@@ -896,7 +893,7 @@
"path": "נתיב", "path": "נתיב",
"pattern": "תבנית", "pattern": "תבנית",
"pause": "עצור", "pause": "עצור",
"pause_memories": "השהה זיכרונות", "pause_memories": "השהה",
"paused": "הושה", "paused": "הושה",
"pending": "ממתין", "pending": "ממתין",
"people": "אנשים", "people": "אנשים",
@@ -927,7 +924,7 @@
"play_or_pause_video": "הפעל או השהה סרטון", "play_or_pause_video": "הפעל או השהה סרטון",
"point": "", "point": "",
"port": "Port", "port": "Port",
"preset": "הגדרות קבועות מראש", "preset": "מוגדר מראש",
"preview": "תצוגה מקדימה", "preview": "תצוגה מקדימה",
"previous": "הקודם", "previous": "הקודם",
"previous_memory": "זיכרון קודם", "previous_memory": "זיכרון קודם",
+98 -102
View File
@@ -37,24 +37,24 @@
"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": "이 라이브러리를 삭제하시겠습니까? Immich에서 {count, plural, one {#개 항목} other {#개 항목}} 삭제며 되돌릴 수 없습니다. 파일은 디스크에 남아 있습니다.", "confirm_delete_library_assets": "이 라이브러리를 삭제하시겠습니까? Immich에서 {count, plural, one {#개 항목} other {항목 #개를 모두}} 삭제며 되돌릴 수 없습니다. 파일은 디스크에 남아 있습니다.",
"confirm_email_below": "계속 진행하려면 아래에 \"{email}\" 입력", "confirm_email_below": "계속 진행하려면 아래에 \"{email}\" 입력",
"confirm_reprocess_all_faces": "모든 얼굴을 다시 처리하시겠습니까? 이름이 지정된 인물을 포함한 모든 인물이 삭제됩니다.", "confirm_reprocess_all_faces": "모든 얼굴을 다시 처리하시겠습니까? 이름이 지정된 인물 삭제됩니다.",
"confirm_user_password_reset": "{user}의 비밀번호를 재설정하시겠습니까?", "confirm_user_password_reset": "{user}의 비밀번호를 재설정하시겠습니까?",
"crontab_guru": "Crontab Guru", "crontab_guru": "Crontab Guru",
"disable_login": "로그인 비활성화", "disable_login": "로그인 비활성화",
"disabled": "비활성화", "disabled": "비활성화",
"duplicate_detection_job_description": "콘텐츠 내 유사한 사진을 감지하기 위한 기계 학습 실행. 스마트 검색 활성화 필요", "duplicate_detection_job_description": "콘텐츠 내 유사한 사진을 감지하기 위한 기계 학습 실행. 스마트 검색 활성화 필요",
"exclusion_pattern_description": "제외 규칙을 사용하면 라이브러리 스캔 시 특정 파일과 폴더를 제외할 수 있습니다. 폴더에 RAW 파일 등 가져오고 싶지 않은 파일이 있는 경우 유용합니다.", "exclusion_pattern_description": "제외 규칙을 사용하면 라이브러리 스캔 시 파일과 폴더를 제외할 수 있습니다. 가져오지 않으려는 특정 파일(RAW 파일 등)이 포함된 폴더가 있는 경우 유용합니다.",
"external_library_created_at": "외부 라이브러리 ({date}에 생성)", "external_library_created_at": "외부 라이브러리 ({date}에 생성)",
"external_library_management": "외부 라이브러리 관리", "external_library_management": "외부 라이브러리 관리",
"face_detection": "얼굴 감지", "face_detection": "얼굴 감지",
"face_detection_description": "기계 학습을 통해 항목에서 얼굴을 감지합니다. 동영상의 경우 섬네일만 사용합니다. \"모두\"는 처리된 항목을 포함한 모든 항목을 대기열에 추가합니다. \"누락\"은 처리되지 않은 항목 대기열에 추가합니다. 감지된 얼굴은 얼굴 인식 작업에서 인물에 할당됩니다.", "face_detection_description": "기계 학습을 사용하여 콘텐츠에서 얼굴을 감지합니다. 동영상의 경우 섬네일만 사용합니다. \"모두\"는 모든 항목을 (재)처리합니다. \"누락\"은 아직 처리되지 않은 항목 대기열에 추가합니다. 감지된 얼굴은 이후 얼굴 인식 작업에서 인물로 그룹화됩니다.",
"facial_recognition_job_description": "감지된 얼굴을 인물에게 할당합니다. 얼굴 감지 작업이 완료된 후행됩니다. \"모두\"는 이미 할당된 얼굴을 포함한 모든 얼굴을 대기열에 추가합니다. \"누락\"은 할당되지 않은 얼굴 대기열에 추가합니다.", "facial_recognition_job_description": "감지된 얼굴을 인물로 그룹화합니다. 얼굴 감지 완료된 후에 실행됩니다. \"모두\"는 모든 얼굴을 (재)그룹화합니다. \"누락\"은 인물이 할당되지 않은 얼굴 대기열에 추가합니다.",
"failed_job_command": "{command} 명령이 다음 작업에서 실패: {job}", "failed_job_command": "{job} 작업의 {command} 실패",
"force_delete_user_warning": "경고: 사용자 모든 항목이 즉시 삭제됩니다. 이 작업은 되돌릴 수 없으며, 파일을 복구할 수 없습니다.", "force_delete_user_warning": "경고: 사용자 모든 콘텐츠가 즉시 삭제됩니다. 이 작업은 되돌릴 수 없으며, 파일을 복구할 수 없습니다.",
"forcing_refresh_library_files": "모든 파일을 다시 스캔하는 중", "forcing_refresh_library_files": "라이브러리의 모든 파일 강제 새로고침",
"image_format_description": "WebP는 JPEG보다 크기가 작으나 변환에 더 많은 시간이 소요됩니다.", "image_format_description": "WebP는 JPEG보다 크기가 작지만 변환 속도가 느립니다.",
"image_prefer_embedded_preview": "내장된 미리 보기 선호", "image_prefer_embedded_preview": "내장된 미리 보기 선호",
"image_prefer_embedded_preview_setting_description": "가능한 경우 RAW 사진에 내장된 미리 보기를 이미지 처리에 사용합니다. 일부 이미지에 대해 더 정확한 색상이 생성될 수 있지만, 미리 보기의 품질은 카메라마다 다르고, 더 많은 품질 하락이 있을 수 있습니다.", "image_prefer_embedded_preview_setting_description": "가능한 경우 RAW 사진에 내장된 미리 보기를 이미지 처리에 사용합니다. 일부 이미지에 대해 더 정확한 색상이 생성될 수 있지만, 미리 보기의 품질은 카메라마다 다르고, 더 많은 품질 하락이 있을 수 있습니다.",
"image_prefer_wide_gamut": "넓은 색 영역 선호", "image_prefer_wide_gamut": "넓은 색 영역 선호",
@@ -76,7 +76,7 @@
"job_status": "작업 상태", "job_status": "작업 상태",
"jobs_delayed": "작업 {jobCount, plural, other {#개}}가 지연됨", "jobs_delayed": "작업 {jobCount, plural, other {#개}}가 지연됨",
"jobs_failed": "작업 {jobCount, plural, other {#개}} 실패", "jobs_failed": "작업 {jobCount, plural, other {#개}} 실패",
"library_created": "{library} 라이브러리 생성됨", "library_created": "라이브러리 {library} 생성됨",
"library_cron_expression": "Cron 표현식", "library_cron_expression": "Cron 표현식",
"library_cron_expression_description": "cron 형식을 사용하여 스캔 주기를 설정합니다. 자세한 예제는 <link>Crontab Guru</link>를 확인하세요", "library_cron_expression_description": "cron 형식을 사용하여 스캔 주기를 설정합니다. 자세한 예제는 <link>Crontab Guru</link>를 확인하세요",
"library_cron_expression_presets": "Cron 표현식 사전 설정", "library_cron_expression_presets": "Cron 표현식 사전 설정",
@@ -88,14 +88,14 @@
"library_settings": "외부 라이브러리", "library_settings": "외부 라이브러리",
"library_settings_description": "외부 라이브러리 설정 관리", "library_settings_description": "외부 라이브러리 설정 관리",
"library_tasks_description": "라이브러리 작업 수행", "library_tasks_description": "라이브러리 작업 수행",
"library_watching_enable_description": "외부 라이브러리 파일 변경 감시", "library_watching_enable_description": "외부 라이브러리 파일 변경 감시",
"library_watching_settings": "라이브러리 감시 (실험 기능)", "library_watching_settings": "라이브러리 감시 (실험)",
"library_watching_settings_description": "변경된 파일을 자동으로 감지", "library_watching_settings_description": "변경된 파일을 자동으로 감지",
"logging_enable_description": "로깅 활성화", "logging_enable_description": "로깅 활성화",
"logging_level_description": "로깅이 활성화된 경우 사용할 로그 레벨을 선택합니다.", "logging_level_description": "로깅이 활성화된 경우 사용할 로그 레벨을 선택합니다.",
"logging_settings": "로깅", "logging_settings": "로깅",
"machine_learning_clip_model": "CLIP 모델", "machine_learning_clip_model": "CLIP 모델",
"machine_learning_clip_model_description": "CLIP 모델명의 경우 <link>이곳</link>을 참조하세요. 변경 후 모든 항목의 스마트 검색 작업을 다시 진행해야 합니다.", "machine_learning_clip_model_description": "CLIP 모델명 <link>이곳</link>에서 확인하세요. 변경 후 모든 항목의 스마트 검색 작업을 다시 진행해야 합니다.",
"machine_learning_duplicate_detection": "비슷한 이미지 감지", "machine_learning_duplicate_detection": "비슷한 이미지 감지",
"machine_learning_duplicate_detection_enabled": "비슷한 이미지 감지 활성화", "machine_learning_duplicate_detection_enabled": "비슷한 이미지 감지 활성화",
"machine_learning_duplicate_detection_enabled_description": "비활성화된 경우에도, 완전히 일치하는 항목은 여전히 감지됩니다.", "machine_learning_duplicate_detection_enabled_description": "비활성화된 경우에도, 완전히 일치하는 항목은 여전히 감지됩니다.",
@@ -105,11 +105,11 @@
"machine_learning_facial_recognition": "얼굴 인식", "machine_learning_facial_recognition": "얼굴 인식",
"machine_learning_facial_recognition_description": "이미지에서 얼굴 감지, 인식 및 그룹화", "machine_learning_facial_recognition_description": "이미지에서 얼굴 감지, 인식 및 그룹화",
"machine_learning_facial_recognition_model": "얼굴 인식 모델", "machine_learning_facial_recognition_model": "얼굴 인식 모델",
"machine_learning_facial_recognition_model_description": "크기에 따라 내림차순으로 나열됩니다. 대형 모델은 속도가 느리고 메모리를 많이 사용하지만 더 나은 결과를 생성합니다. 변경 후 모든 항목의 얼굴 인식 작업을 다시 진행해야 합니다.", "machine_learning_facial_recognition_model_description": "크기에 따라 내림차순으로 나열됩니다. 대형 모델은 느리고 메모리를 많이 사용하지만, 더 나은 결과를 보여줍니다. 변경 후 모든 항목의 얼굴 인식 작업을 다시 진행해야 합니다.",
"machine_learning_facial_recognition_setting": "얼굴 인식 활성화", "machine_learning_facial_recognition_setting": "얼굴 인식 활성화",
"machine_learning_facial_recognition_setting_description": "비활성화된 경우 이미지에서 얼굴 인식을 진행하지 않으며, 탐색 페이지에 인물 목록이 표시되지 않습니다.", "machine_learning_facial_recognition_setting_description": "비활성화된 경우 이미지에서 얼굴 인식을 진행하지 않으며, 탐색 페이지에 인물 목록이 표시되지 않습니다.",
"machine_learning_max_detection_distance": "최대 감지 거리", "machine_learning_max_detection_distance": "최대 감지 거리",
"machine_learning_max_detection_distance_description": "두 사진을 유사한 사진으로 간주하는 최댓값을 0.001에서 0.1 사이로 설정합니다. 값이 높을수록 더 많은 유사성을 판단하지만, 유사하지 않은 사진이 잘못 포함될 수 있습니다.", "machine_learning_max_detection_distance_description": "두 사진을 유사한 이미지로 간주하는 최댓값을 0.001에서 0.1 사이로 설정합니다. 높은 값은 더 많은 유사성을 감지하지만, 유사하지 않은 사진이 포함될 수 있습니다.",
"machine_learning_max_recognition_distance": "최대 인식 거리", "machine_learning_max_recognition_distance": "최대 인식 거리",
"machine_learning_max_recognition_distance_description": "같은 사람으로 간주되는 두 얼굴 사이의 최대 거리를 0에서 2 사이로 설정합니다. 이 값을 낮추면 두 사람을 같은 사람으로 분류하는 것을 방지할 수 있고, 높이면 같은 사람을 다른 두 사람으로 분류하는 것을 방지할 수 있습니다. 한 사람을 둘로 나누는 것보다 두 사람을 병합하는 것이 더 쉽기에, 가능한 임계값이 더 낮은 쪽을 선택하세요.", "machine_learning_max_recognition_distance_description": "같은 사람으로 간주되는 두 얼굴 사이의 최대 거리를 0에서 2 사이로 설정합니다. 이 값을 낮추면 두 사람을 같은 사람으로 분류하는 것을 방지할 수 있고, 높이면 같은 사람을 다른 두 사람으로 분류하는 것을 방지할 수 있습니다. 한 사람을 둘로 나누는 것보다 두 사람을 병합하는 것이 더 쉽기에, 가능한 임계값이 더 낮은 쪽을 선택하세요.",
"machine_learning_min_detection_score": "최소 탐지 점수", "machine_learning_min_detection_score": "최소 탐지 점수",
@@ -141,7 +141,7 @@
"migration_job_description": "콘텐츠의 섬네일 및 얼굴을 최신 폴더 구조로 마이그레이션", "migration_job_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": "주의: 추후 변경할 수 없습니다!",
"note_unlimited_quota": "참고: 무제한 할당량의 경우 0을 입력", "note_unlimited_quota": "참고: 무제한 할당량의 경우 0을 입력",
"notification_email_from_address": "보낸 사람 이메일", "notification_email_from_address": "보낸 사람 이메일",
@@ -184,15 +184,15 @@
"oauth_storage_quota_default": "기본 스토리지 할당량 (GiB)", "oauth_storage_quota_default": "기본 스토리지 할당량 (GiB)",
"oauth_storage_quota_default_description": "할당량을 요청하지 않은 경우 사용할 GiB 단위의 기본 할당량 (무제한 할당량을 설정하려면 0 입력).", "oauth_storage_quota_default_description": "할당량을 요청하지 않은 경우 사용할 GiB 단위의 기본 할당량 (무제한 할당량을 설정하려면 0 입력).",
"offline_paths": "누락된 파일", "offline_paths": "누락된 파일",
"offline_paths_description": "외부 라이브러리의 항목이 아닌 파일을 수동으로 삭제한 경우 발생할 수 있습니다.", "offline_paths_description": "외부 라이브러리가 아닌 서버 내의 파일을 직접 삭제한 경우 발생할 수 있습니다.",
"password_enable_description": "이메일과 비밀번호로 로그인", "password_enable_description": "이메일과 비밀번호로 로그인",
"password_settings": "비밀번호 로그인", "password_settings": "비밀번호 로그인",
"password_settings_description": "비밀번호 로그인 설정 관리", "password_settings_description": "비밀번호 로그인 설정 관리",
"paths_validated_successfully": "모든 경로가 성공적으로 검증됨", "paths_validated_successfully": "모든 경로가 성공적으로 검증됨",
"quota_size_gib": "할당량 크기 (GiB)", "quota_size_gib": "할당량 크기 (GiB)",
"refreshing_all_libraries": "모든 라이브러리 다시 스캔 중", "refreshing_all_libraries": "모든 라이브러리 새로고침",
"registration": "관리자 가입", "registration": "관리자 가입",
"registration_description": "첫 사용자이므로 관리자로 지정되었습니다. 이 계정에서 관리 작업 및 사용자 생성이 가능합니다.", "registration_description": "시스템의 첫 사용자이므로 관리자로 지정되었으며, 관리 작업 및 사용자 생성을 담당하게 됩니다.",
"removing_offline_files": "누락된 파일 제거 중", "removing_offline_files": "누락된 파일 제거 중",
"repair_all": "모두 수리", "repair_all": "모두 수리",
"repair_matched_items": "{count, plural, one {#개} other {#개}} 항목 일치", "repair_matched_items": "{count, plural, one {#개} other {#개}} 항목 일치",
@@ -201,7 +201,7 @@
"reset_settings_to_default": "설정을 기본값으로 복원", "reset_settings_to_default": "설정을 기본값으로 복원",
"reset_settings_to_recent_saved": "마지막으로 저장된 설정으로 복원", "reset_settings_to_recent_saved": "마지막으로 저장된 설정으로 복원",
"scanning_library_for_changed_files": "라이브러리의 변경된 파일 스캔 중", "scanning_library_for_changed_files": "라이브러리의 변경된 파일 스캔 중",
"scanning_library_for_new_files": "라이브러리의 새 파일 스캔 중", "scanning_library_for_new_files": "라이브러리의 새로운 파일 스캔 중",
"send_welcome_email": "환영 이메일 전송", "send_welcome_email": "환영 이메일 전송",
"server_external_domain_settings": "외부 도메인", "server_external_domain_settings": "외부 도메인",
"server_external_domain_settings_description": "공개 공유 링크에 사용할 도메인 (http(s):// 포함)", "server_external_domain_settings_description": "공개 공유 링크에 사용할 도메인 (http(s):// 포함)",
@@ -219,14 +219,14 @@
"storage_template_hash_verification_enabled": "해시 검증 활성화", "storage_template_hash_verification_enabled": "해시 검증 활성화",
"storage_template_hash_verification_enabled_description": "해시 검증을 활성화합니다. 이 설정의 결과를 확실히 이해하지 않는 한 비활성화하지 마세요", "storage_template_hash_verification_enabled_description": "해시 검증을 활성화합니다. 이 설정의 결과를 확실히 이해하지 않는 한 비활성화하지 마세요",
"storage_template_migration": "스토리지 템플릿 마이그레이션", "storage_template_migration": "스토리지 템플릿 마이그레이션",
"storage_template_migration_description": "기존에 업로드된 항목에 현재 <link>{template}</link> 적용", "storage_template_migration_description": "기존에 업로드된 콘텐츠에 현재 <link>{template}</link> 적용",
"storage_template_migration_info": "템플릿 변경 사항은 새 항목부터 적용됩니다. 기존 항목에 변경 사항을 적용하려면, <link>{job}</link>을 실행하세요.", "storage_template_migration_info": "변경한 템플릿은 새 업로드 항목부터 적용됩니다. 기존 항목들에도 적용하려면, <link>{job}</link>을 실행하세요.",
"storage_template_migration_job": "스토리지 템플릿 마이그레이션 작업", "storage_template_migration_job": "스토리지 템플릿 마이그레이션 작업",
"storage_template_more_details": "이 기능에 대한 자세한 정보는 <template-link>스토리지 템플릿</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": "이 기능을 활성화하면 사용자 정의 템플릿을 기반으로 파일이 자동으로 정리됩니다. 안정성 문제로 인해 기능이 기본적으로 비활성화되어 있습니다. 자세한 내용은 <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": "업로드되는 콘텐츠의 폴더 구조 및 파일 이름 관리",
"storage_template_user_label": "사용자의 스토리지 레이블: <code>{label}</code>", "storage_template_user_label": "사용자의 스토리지 레이블: <code>{label}</code>",
"system_settings": "시스템 설정", "system_settings": "시스템 설정",
"theme_custom_css_settings": "사용자 정의 CSS", "theme_custom_css_settings": "사용자 정의 CSS",
@@ -260,7 +260,7 @@
"transcoding_hardware_acceleration": "하드웨어 가속", "transcoding_hardware_acceleration": "하드웨어 가속",
"transcoding_hardware_acceleration_description": "실험적인 기능입니다; 훨씬 빠르지만 동일 비트레이트에서 낮은 품질을 가질 수 있습니다", "transcoding_hardware_acceleration_description": "실험적인 기능입니다; 훨씬 빠르지만 동일 비트레이트에서 낮은 품질을 가질 수 있습니다",
"transcoding_hardware_decoding": "하드웨어 디코딩", "transcoding_hardware_decoding": "하드웨어 디코딩",
"transcoding_hardware_decoding_setting_description": "NVENC와 QSV, RKMPP만 해당, 인코딩 가속에 엔드 투 엔드 가속을 가능하게 합니다. 모든 동영상에서 작동하지 않을 수 있습니다.", "transcoding_hardware_decoding_setting_description": "NVENC와 QSV, RKMPP적용됩니다. 인코딩 가속에 엔드 투 엔드 가속을 가능하게 합니다. 모든 동영상에서 작동하지 않을 수 있습니다.",
"transcoding_hevc_codec": "HEVC 코덱", "transcoding_hevc_codec": "HEVC 코덱",
"transcoding_max_b_frames": "최대 B 프레임", "transcoding_max_b_frames": "최대 B 프레임",
"transcoding_max_b_frames_description": "값을 높이면 압축 효율이 향상되지만 인코딩 속도가 느려집니다. 오래된 기기의 하드웨어 가속과 호환되지 않을 수 있습니다. 0은 B 프레임을 비활성화하며, -1은 자동으로 설정합니다.", "transcoding_max_b_frames_description": "값을 높이면 압축 효율이 향상되지만 인코딩 속도가 느려집니다. 오래된 기기의 하드웨어 가속과 호환되지 않을 수 있습니다. 0은 B 프레임을 비활성화하며, -1은 자동으로 설정합니다.",
@@ -270,7 +270,7 @@
"transcoding_max_keyframe_interval_description": "키프레임 간의 최대 프레임 거리를 설정합니다. 값이 낮을수록 압축 효율이 저하되지만, 검색 시간이 개선되고 빠른 움직임이 있는 장면에서 품질을 향상시킬 수 있습니다. 0으로 설정된 경우 자동으로 설정합니다.", "transcoding_max_keyframe_interval_description": "키프레임 간의 최대 프레임 거리를 설정합니다. 값이 낮을수록 압축 효율이 저하되지만, 검색 시간이 개선되고 빠른 움직임이 있는 장면에서 품질을 향상시킬 수 있습니다. 0으로 설정된 경우 자동으로 설정합니다.",
"transcoding_optimal_description": "설정한 해상도보다 높은 동영상 또는 허용되지 않는 형식의 동영상", "transcoding_optimal_description": "설정한 해상도보다 높은 동영상 또는 허용되지 않는 형식의 동영상",
"transcoding_preferred_hardware_device": "선호하는 하드웨어 기기", "transcoding_preferred_hardware_device": "선호하는 하드웨어 기기",
"transcoding_preferred_hardware_device_description": "VAAPI와 QSV만 해당, 하드웨어 트랜스코딩에 사용되는 DRI 노드를 설정합니다.", "transcoding_preferred_hardware_device_description": "VAAPI와 QSV적용됩니다. 하드웨어 트랜스코딩에 사용되는 DRI 노드를 설정합니다.",
"transcoding_preset_preset": "프리셋 (-preset)", "transcoding_preset_preset": "프리셋 (-preset)",
"transcoding_preset_preset_description": "압축 속도를 설정합니다. 느린 프리셋은 더 작은 파일을 생성하며, 특정 비트레이트가 목표인 경우 품질이 향상됩니다. VP9는 `faster` 이상의 속도를 무시합니다.", "transcoding_preset_preset_description": "압축 속도를 설정합니다. 느린 프리셋은 더 작은 파일을 생성하며, 특정 비트레이트가 목표인 경우 품질이 향상됩니다. VP9는 `faster` 이상의 속도를 무시합니다.",
"transcoding_reference_frames": "참조 프레임", "transcoding_reference_frames": "참조 프레임",
@@ -281,7 +281,7 @@
"transcoding_target_resolution": "목표 해상도", "transcoding_target_resolution": "목표 해상도",
"transcoding_target_resolution_description": "고해상도는 더 많은 세부 정보를 보존할 수 있지만 인코딩 시간이 길어지고, 파일 크기가 커지며, 앱의 응답성이 감소할 수 있습니다.", "transcoding_target_resolution_description": "고해상도는 더 많은 세부 정보를 보존할 수 있지만 인코딩 시간이 길어지고, 파일 크기가 커지며, 앱의 응답성이 감소할 수 있습니다.",
"transcoding_temporal_aq": "Temporal AQ", "transcoding_temporal_aq": "Temporal AQ",
"transcoding_temporal_aq_description": "NVENC만 해당, 세부 정보가 많고 움직임이 적은 장면의 품질을 향상시킵니다. 오래된 기기와 호환되지 않을 수 있습니다.", "transcoding_temporal_aq_description": "NVENC적용됩니다. 세부 정보가 많고 움직임이 적은 장면의 품질을 향상시킵니다. 오래된 기기와 호환되지 않을 수 있습니다.",
"transcoding_threads": "스레드", "transcoding_threads": "스레드",
"transcoding_threads_description": "높은 값으로 설정 시 더 빠른 인코딩이 가능하지만, 다른 작업 수행을 위한 서버 자원이 부족해질 수 있습니다. 이 값은 CPU 코어 수보다 작아야 합니다. 최대로 설정하려는 경우 0을 입력합니다.", "transcoding_threads_description": "높은 값으로 설정 시 더 빠른 인코딩이 가능하지만, 다른 작업 수행을 위한 서버 자원이 부족해질 수 있습니다. 이 값은 CPU 코어 수보다 작아야 합니다. 최대로 설정하려는 경우 0을 입력합니다.",
"transcoding_tone_mapping": "톤 매핑", "transcoding_tone_mapping": "톤 매핑",
@@ -291,7 +291,7 @@
"transcoding_transcode_policy": "트랜스코드 정책", "transcoding_transcode_policy": "트랜스코드 정책",
"transcoding_transcode_policy_description": "트랜스코드될 동영상을 설정합니다. HDR 동영상은 항상 트랜스코드됩니다(트랜스코드가 비활성화된 경우 제외).", "transcoding_transcode_policy_description": "트랜스코드될 동영상을 설정합니다. HDR 동영상은 항상 트랜스코드됩니다(트랜스코드가 비활성화된 경우 제외).",
"transcoding_two_pass_encoding": "투 패스 인코딩", "transcoding_two_pass_encoding": "투 패스 인코딩",
"transcoding_two_pass_encoding_setting_description": "인코딩된 동영상의 품질 향상을 위해 투 패스 인코딩을 사용합니다. 최대 비트레이트(H.264 및 HEVC에 필요)가 활성화된 경우 CRF를 사용하지 않고 최대 비트레이트 기반의 범위를 사용합니다. 최대 비트레이트가 비활성화된 경우 VP9에서 CRF를 사용할 수 있습니다.", "transcoding_two_pass_encoding_setting_description": "동영상을 더 나은 품질로 인코딩하기 위해 투 패스 인코딩합니다. 최대 비트레이트가 활성화된 경우(H.264 및 HEVC와 함께 작동하도록 필요), 이 모드는 최대 비트레이트 기반으로 한 비트레이트 범위를 사용하며 CRF를 무시합니다. VP9의 경우 최대 비트레이트가 비활성화되면 CRF를 사용할 수 있습니다.",
"transcoding_video_codec": "동영상 코덱", "transcoding_video_codec": "동영상 코덱",
"transcoding_video_codec_description": "VP9는 높은 효율성과 웹 호환성을 가지고 있지만, 변환에 오랜 시간이 소요됩니다. HEVC는 비슷한 성능을 보이나 웹 호환성이 낮습니다. H.264는 호환성이 높고 변환 속도가 빠르지만 파일 크기가 크며, AV1은 가장 효율적인 코덱이지만 오래된 기기와의 호환성이 낮습니다.", "transcoding_video_codec_description": "VP9는 높은 효율성과 웹 호환성을 가지고 있지만, 변환에 오랜 시간이 소요됩니다. HEVC는 비슷한 성능을 보이나 웹 호환성이 낮습니다. H.264는 호환성이 높고 변환 속도가 빠르지만 파일 크기가 크며, AV1은 가장 효율적인 코덱이지만 오래된 기기와의 호환성이 낮습니다.",
"trash_enabled_description": "휴지통 활성화", "trash_enabled_description": "휴지통 활성화",
@@ -303,17 +303,17 @@
"untracked_files_description": "애플리케이션에서 추적하지 못하는 파일 목록입니다. 이동 실패, 업로드 중단 또는 버그로 인해 발생할 수 있습니다", "untracked_files_description": "애플리케이션에서 추적하지 못하는 파일 목록입니다. 이동 실패, 업로드 중단 또는 버그로 인해 발생할 수 있습니다",
"user_delete_delay": "<b>{user}</b>의 계정 및 콘텐츠가 {delay, plural, one {#일} other {#일}} 뒤 영구적으로 삭제됩니다.", "user_delete_delay": "<b>{user}</b>의 계정 및 콘텐츠가 {delay, plural, one {#일} other {#일}} 뒤 영구적으로 삭제됩니다.",
"user_delete_delay_settings": "삭제 보류 기간", "user_delete_delay_settings": "삭제 보류 기간",
"user_delete_delay_settings_description": "사용자를 영구적으로 삭제하기 전 보류 기간을 설정합니다. 사용자 삭제 작업은 자정에 확인 및 진행됩니다. 변경 사항은 다음 실행 시 적용됩니다.", "user_delete_delay_settings_description": "사용자 계정 및 콘텐츠를 영구적으로 삭제하기 전 보류 기간을 설정합니다. 사용자 삭제 작업은 자정에 해당 기준을 만족하는 사용자를 확인한 후 진행됩니다. 변경 사항은 다음 실행 시 적용됩니다.",
"user_delete_immediately": "<b>{user}</b>의 계정 및 데이터가 <b>영구적으로 삭제됩니다</b> .", "user_delete_immediately": "<b>{user}</b>의 계정 및 콘텐츠가 <b>즉시</b> 영구 삭제 대기열에 추가됩니다.",
"user_delete_immediately_checkbox": "사용자 즉시 삭제", "user_delete_immediately_checkbox": "사용자와 컨텐츠를 즉시 삭제 대기열에 추가",
"user_management": "사용자 관리", "user_management": "사용자 관리",
"user_password_has_been_reset": "사용자의 비밀번호가 재설정되었습니다:", "user_password_has_been_reset": "사용자의 비밀번호가 재설정되었습니다:",
"user_password_reset_description": "사용자에게 비밀번호를 제공하세요. 임시 비밀번호로 로그인 비밀번호를 반드시 변경해야 합니다.", "user_password_reset_description": "사용자에게 임시 비밀번호를 제공하고 다음 로그인 비밀번호를 변경해야 한다고 알려주세요.",
"user_restore_description": "<b>{user}</b>의 계정이 복구되었습니다.", "user_restore_description": "<b>{user}</b>의 계정이 복구되었습니다.",
"user_restore_scheduled_removal": "사용자 복구 - {date, date, long}에 예약된 일정 삭제", "user_restore_scheduled_removal": "사용자 복구 - {date, date, long}에 예약된 일정 삭제",
"user_settings": "사용자 설정", "user_settings": "사용자 설정",
"user_settings_description": "사용자 설정 관리", "user_settings_description": "사용자 설정 관리",
"user_successfully_removed": "{email}이(가) 성공적으로 제거되었습니다.", "user_successfully_removed": "사용자 {email}이 성공적으로 제거되었습니다.",
"version_check_enabled_description": "최신 버전 확인을 위한 주기적인 GitHub 확인 활성화", "version_check_enabled_description": "최신 버전 확인을 위한 주기적인 GitHub 확인 활성화",
"version_check_settings": "버전 확인", "version_check_settings": "버전 확인",
"version_check_settings_description": "최신 버전 알림 설정 관리", "version_check_settings_description": "최신 버전 알림 설정 관리",
@@ -337,8 +337,8 @@
"album_name": "앨범 이름", "album_name": "앨범 이름",
"album_options": "앨범 옵션", "album_options": "앨범 옵션",
"album_remove_user": "사용자를 삭제하겠습니까?", "album_remove_user": "사용자를 삭제하겠습니까?",
"album_remove_user_confirmation": "이 앨범에서 {user} 사용자를 삭제하시겠습니까?", "album_remove_user_confirmation": "{user} 사용자를 삭제하시겠습니까?",
"album_share_no_users": "이미 모든 사용자와 이 앨범을 공유했거나 다른 사용자가 없습니다.", "album_share_no_users": "이미 모든 사용자와 공유했거나 공유할 사용자가 없는 것 같습니다.",
"album_updated": "앨범 업데이트", "album_updated": "앨범 업데이트",
"album_updated_setting_description": "공유 앨범에 새 항목이 추가되는 경우 이메일 알림을 받기", "album_updated_setting_description": "공유 앨범에 새 항목이 추가되는 경우 이메일 알림을 받기",
"album_user_left": "{album} 앨범에서 나옴", "album_user_left": "{album} 앨범에서 나옴",
@@ -351,11 +351,11 @@
"all_people": "모든 인물", "all_people": "모든 인물",
"all_videos": "모든 동영상", "all_videos": "모든 동영상",
"allow_dark_mode": "다크 모드 사용", "allow_dark_mode": "다크 모드 사용",
"allow_edits": "편집자로 변경", "allow_edits": "편집 허용",
"allow_public_user_to_download": "모든 사용자의 다운로드 허용", "allow_public_user_to_download": "모든 사용자의 다운로드 허용",
"allow_public_user_to_upload": "모든 사용자의 업로드 허용", "allow_public_user_to_upload": "모든 사용자의 업로드 허용",
"api_key": "API 키", "api_key": "API 키",
"api_key_description": "이 값은 한 번만 표시됩니다. 창을 닫기 전에 반드시 복사해세요.", "api_key_description": "이 값은 한 번만 표시됩니다. 창을 닫기 전에 반드시 복사해세요.",
"api_key_empty": "API 키 이름은 비어있을 수 없습니다", "api_key_empty": "API 키 이름은 비어있을 수 없습니다",
"api_keys": "API 키", "api_keys": "API 키",
"app_settings": "앱 설정", "app_settings": "앱 설정",
@@ -365,9 +365,9 @@
"archive_size": "압축 파일 크기", "archive_size": "압축 파일 크기",
"archive_size_description": "다운로드할 압축 파일의 크기 구성 (GiB 단위)", "archive_size_description": "다운로드할 압축 파일의 크기 구성 (GiB 단위)",
"archived": "보관됨", "archived": "보관됨",
"archived_count": "보관함으로 {count, plural, other {#개}} 항목 이동", "archived_count": "보관함으로 {count, plural, other {#개}} 항목 이동",
"are_these_the_same_person": "이들은 동일 인물인가요?", "are_these_the_same_person": "이들은 동일 인물인가요?",
"are_you_sure_to_do_this": "정말 이 작업을 수행하시겠습니까?", "are_you_sure_to_do_this": "정말 이 작업을 수행하시겠습니까?",
"asset_added_to_album": "앨범에 추가됨", "asset_added_to_album": "앨범에 추가됨",
"asset_adding_to_album": "앨범에 추가 중...", "asset_adding_to_album": "앨범에 추가 중...",
"asset_description_updated": "항목 설명이 업데이트됨", "asset_description_updated": "항목 설명이 업데이트됨",
@@ -375,14 +375,14 @@
"asset_has_unassigned_faces": "할당되지 않은 얼굴이 콘텐츠에 있음", "asset_has_unassigned_faces": "할당되지 않은 얼굴이 콘텐츠에 있음",
"asset_hashing": "해싱...", "asset_hashing": "해싱...",
"asset_offline": "항목 누락됨", "asset_offline": "항목 누락됨",
"asset_offline_description": "이 항목은 누락되었습니다. Immich가 파일 위치에 접근할 수 없습니다. 항목이 존재하는지, 접근이 가능한지 확인한 뒤 라이브러리를 다시 스캔하세요.", "asset_offline_description": "이 항목은 누락되었으며, 파일 위치에 접근할 수 없습니다. 파일에 접근이 가능한지 확인 라이브러리를 다시 스캔하세요.",
"asset_skipped": "건너뜀", "asset_skipped": "건너뜀",
"asset_uploaded": "업로드됨", "asset_uploaded": "업로드됨",
"asset_uploading": "업로드 중...", "asset_uploading": "업로드 중...",
"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": "{hasName, select, true {<b>{name}</b>} other {새 앨범}}에 {count, plural, one {#개 항목} other {#개 항목}}을 추가", "assets_added_to_name_count": "{count, plural, one {#개} other {#개}} 항목 {name}에 추가",
"assets_count": "{count, plural, one {#개} other {#개}} 항목", "assets_count": "{count, plural, one {#개} other {#개}} 항목",
"assets_moved_to_trash": "항목 {count, plural, one {#개} other {#개}}를 휴지통으로 이동함", "assets_moved_to_trash": "항목 {count, plural, one {#개} other {#개}}를 휴지통으로 이동함",
"assets_moved_to_trash_count": "휴지통으로 {count, plural, one {#개} other {#개}} 항목 이동됨", "assets_moved_to_trash_count": "휴지통으로 {count, plural, one {#개} other {#개}} 항목 이동됨",
@@ -391,12 +391,12 @@
"assets_restore_confirmation": "휴지통으로 이동한 항목을 모두 복원하시겠습니까? 이 작업은 되돌릴 수 없습니다!", "assets_restore_confirmation": "휴지통으로 이동한 항목을 모두 복원하시겠습니까? 이 작업은 되돌릴 수 없습니다!",
"assets_restored_count": "{count, plural, one {#개} other {#개}} 항목 복원됨", "assets_restored_count": "{count, plural, one {#개} other {#개}} 항목 복원됨",
"assets_trashed_count": "휴지통으로 {count, plural, one {#개} other {#개}} 항목 이동됨", "assets_trashed_count": "휴지통으로 {count, plural, one {#개} other {#개}} 항목 이동됨",
"assets_were_part_of_album_count": "앨범에 이미 {count, plural, one {개} other {개}} 항목이 존재함", "assets_were_part_of_album_count": "{count, plural, one {개} other {개}} 항목이 이미 앨범에 포함됨",
"authorized_devices": "인증된 기기", "authorized_devices": "인증된 기기",
"back": "뒤로", "back": "뒤로",
"back_close_deselect": "뒤로, 닫기 또는 선택 취소", "back_close_deselect": "뒤로, 닫기 또는 선택 취소",
"backward": "뒤로", "backward": "뒤로",
"birthdate_saved": "생년월일이 성공적으로 저장", "birthdate_saved": "생년월일이 성공적으로 저장되었습니다",
"birthdate_set_description": "생년월일은 사진 촬영 당시 인물의 나이를 계산하는 데 사용됩니다.", "birthdate_set_description": "생년월일은 사진 촬영 당시 인물의 나이를 계산하는 데 사용됩니다.",
"blurred_background": "흐린 배경", "blurred_background": "흐린 배경",
"build": "빌드", "build": "빌드",
@@ -422,7 +422,7 @@
"change_name": "이름 변경", "change_name": "이름 변경",
"change_name_successfully": "이름이 성공적으로 변경됨", "change_name_successfully": "이름이 성공적으로 변경됨",
"change_password": "비밀번호 변경", "change_password": "비밀번호 변경",
"change_password_description": " 로그인거나, 비밀번호가 초기화되어 비밀번호를 설정해야 합니다. 아래에 새 비밀번호를 입력해주세요.", "change_password_description": "시스템에 처음으로 로그인거나, 비밀번호 변경 요청이 있었습니다. 아래에 새 비밀번호를 입력해주세요.",
"change_your_password": "비밀번호 변경", "change_your_password": "비밀번호 변경",
"changed_visibility_successfully": "숨김 여부가 성공적으로 변경됨", "changed_visibility_successfully": "숨김 여부가 성공적으로 변경됨",
"check_all": "모두 확인", "check_all": "모두 확인",
@@ -473,7 +473,7 @@
"created": "생성됨", "created": "생성됨",
"current_device": "현재 기기", "current_device": "현재 기기",
"custom_locale": "사용자 지정 로케일", "custom_locale": "사용자 지정 로케일",
"custom_locale_description": "언어 및 지역에 따른 날짜 숫자 형식 지정", "custom_locale_description": "언어 및 지역에 따른 날짜 숫자 형식 지정",
"dark": "다크", "dark": "다크",
"date_after": "다음 날짜 이후", "date_after": "다음 날짜 이후",
"date_and_time": "날짜 및 시간", "date_and_time": "날짜 및 시간",
@@ -483,7 +483,7 @@
"day": "일", "day": "일",
"deduplicate_all": "비슷한 항목 모두 선택", "deduplicate_all": "비슷한 항목 모두 선택",
"default_locale": "기본 로케일", "default_locale": "기본 로케일",
"default_locale_description": "브라우저 로케일에 따른 날짜 숫자 형식 지정", "default_locale_description": "브라우저 로케일에 따른 날짜 숫자 형식 지정",
"delete": "삭제", "delete": "삭제",
"delete_album": "앨범 삭제", "delete_album": "앨범 삭제",
"delete_api_key_prompt": "이 API 키를 삭제하시겠습니까?", "delete_api_key_prompt": "이 API 키를 삭제하시겠습니까?",
@@ -498,7 +498,7 @@
"details": "상세 정보", "details": "상세 정보",
"direction": "방향", "direction": "방향",
"disabled": "비활성화", "disabled": "비활성화",
"disallow_edits": "뷰어로 변경", "disallow_edits": "편집 불가",
"discover": "탐색", "discover": "탐색",
"dismiss_all_errors": "모든 오류 무시", "dismiss_all_errors": "모든 오류 무시",
"dismiss_error": "오류 무시", "dismiss_error": "오류 무시",
@@ -506,7 +506,7 @@
"display_order": "표시 순서", "display_order": "표시 순서",
"display_original_photos": "원본 이미지 선호", "display_original_photos": "원본 이미지 선호",
"display_original_photos_setting_description": "항목 열람 시 웹과 호환되는 경우 섬네일 대신 원본 사진을 표시합니다. 사진 표시 속도가 느려질 수 있습니다.", "display_original_photos_setting_description": "항목 열람 시 웹과 호환되는 경우 섬네일 대신 원본 사진을 표시합니다. 사진 표시 속도가 느려질 수 있습니다.",
"do_not_show_again": "다시 표시하지 않음", "do_not_show_again": "이 메시지를 다시 표시하지 않음",
"done": "완료", "done": "완료",
"download": "다운로드", "download": "다운로드",
"download_settings": "다운로드", "download_settings": "다운로드",
@@ -525,28 +525,28 @@
"years": "" "years": ""
}, },
"edit": "편집", "edit": "편집",
"edit_album": "앨범 수정", "edit_album": "앨범 편집",
"edit_avatar": "프로필 편집", "edit_avatar": "아바타 편집",
"edit_date": "날짜 변경", "edit_date": "날짜 편집",
"edit_date_and_time": "날짜 및 시간 변경", "edit_date_and_time": "날짜 및 시간 편집",
"edit_exclusion_pattern": "제외 규칙 편집", "edit_exclusion_pattern": "제외 규칙 편집",
"edit_faces": "인물 변경", "edit_faces": "얼굴 편집",
"edit_import_path": "가져오기 경로 편집", "edit_import_path": "가져오기 경로 편집",
"edit_import_paths": "가져오기 경로 편집", "edit_import_paths": "가져오기 경로 편집",
"edit_key": "API 키 편집", "edit_key": "키 편집",
"edit_link": "링크 수정", "edit_link": "링크 편집",
"edit_location": "위치 변경", "edit_location": "경로 편집",
"edit_name": "이름 변경", "edit_name": "이름 변경",
"edit_people": "인물 수정", "edit_people": "인물 수정",
"edit_title": "제목 변경", "edit_title": "제목 편집",
"edit_user": "사용자 수정", "edit_user": "사용자 편집",
"edited": "수정됨", "edited": "수정됨",
"editor": "편집자", "editor": "편집자",
"email": "이메일", "email": "이메일",
"empty": "", "empty": "",
"empty_album": "", "empty_album": "",
"empty_trash": "휴지통 비우기", "empty_trash": "휴지통 비우기",
"empty_trash_confirmation": "휴지통을 비우시겠습니까? 휴지통에 있는 모든 항목이 Immich에서 영구적으로 제거됩니다. 이 작업은 되돌릴 수 없습니다!", "empty_trash_confirmation": "휴지통을 비우시겠습니까? 휴지통에 있는 모든 항목이 Immich에서 영구적으로 제거됩니다.\n이 작업은 되돌릴 수 없습니다!",
"enable": "사용", "enable": "사용",
"enabled": "활성화됨", "enabled": "활성화됨",
"end_date": "종료일", "end_date": "종료일",
@@ -572,10 +572,10 @@
"error_removing_assets_from_album": "앨범에서 항목 제거 중 오류 발생, 자세한 정보는 콘솔을 확인하세요", "error_removing_assets_from_album": "앨범에서 항목 제거 중 오류 발생, 자세한 정보는 콘솔을 확인하세요",
"error_selecting_all_assets": "모든 항목 선택 중 오류 발생", "error_selecting_all_assets": "모든 항목 선택 중 오류 발생",
"exclusion_pattern_already_exists": "이 제외 패턴은 이미 존재합니다.", "exclusion_pattern_already_exists": "이 제외 패턴은 이미 존재합니다.",
"failed_job_command": "{job} 작업 {command} 실패", "failed_job_command": "{job} 작업 {command} 실패",
"failed_to_create_album": "앨범 생성 실패", "failed_to_create_album": "앨범 생성 실패",
"failed_to_create_shared_link": "공유 링크 생성 실패", "failed_to_create_shared_link": "공유 링크 생성 실패",
"failed_to_edit_shared_link": "공유 링크 수정 실패", "failed_to_edit_shared_link": "공유 링크 편집 실패",
"failed_to_get_people": "인물 가져오기 실패", "failed_to_get_people": "인물 가져오기 실패",
"failed_to_load_asset": "항목 로드 실패", "failed_to_load_asset": "항목 로드 실패",
"failed_to_load_assets": "항목 로드 실패", "failed_to_load_assets": "항목 로드 실패",
@@ -620,8 +620,8 @@
"unable_to_delete_shared_link": "공유 링크를 삭제할 수 없음", "unable_to_delete_shared_link": "공유 링크를 삭제할 수 없음",
"unable_to_delete_user": "사용자를 삭제할 수 없음", "unable_to_delete_user": "사용자를 삭제할 수 없음",
"unable_to_download_files": "파일을 다운로드할 수 없음", "unable_to_download_files": "파일을 다운로드할 수 없음",
"unable_to_edit_exclusion_pattern": "제외 규칙을 변경할 수 없음", "unable_to_edit_exclusion_pattern": "제외 규칙을 편집할 수 없음",
"unable_to_edit_import_path": "가져오기 경로를 변경할 수 없음", "unable_to_edit_import_path": "가져오기 경로를 편집할 수 없음",
"unable_to_empty_trash": "휴지통을 비울 수 없음", "unable_to_empty_trash": "휴지통을 비울 수 없음",
"unable_to_enter_fullscreen": "전체 화면으로 전환할 수 없음", "unable_to_enter_fullscreen": "전체 화면으로 전환할 수 없음",
"unable_to_exit_fullscreen": "전체 화면을 종료할 수 없음", "unable_to_exit_fullscreen": "전체 화면을 종료할 수 없음",
@@ -686,7 +686,7 @@
"expand_all": "모두 확장", "expand_all": "모두 확장",
"expire_after": "만료일 설정", "expire_after": "만료일 설정",
"expired": "만료됨", "expired": "만료됨",
"expires_date": "{date} 만료", "expires_date": "{date} 만료",
"explore": "탐색", "explore": "탐색",
"export": "내보내기", "export": "내보내기",
"export_as_json": "JSON으로 내보내기", "export_as_json": "JSON으로 내보내기",
@@ -709,7 +709,7 @@
"filter_people": "인물 필터", "filter_people": "인물 필터",
"find_them_fast": "이름으로 검색하여 빠르게 찾기", "find_them_fast": "이름으로 검색하여 빠르게 찾기",
"fix_incorrect_match": "잘못된 일치 수리", "fix_incorrect_match": "잘못된 일치 수리",
"force_re-scan_library_files": "모든 파일 강제 다시 스캔", "force_re-scan_library_files": "모든 라이브러리 파일 강제 다시 스캔",
"forward": "앞으로", "forward": "앞으로",
"general": "일반", "general": "일반",
"get_help": "도움 받기", "get_help": "도움 받기",
@@ -723,17 +723,14 @@
"group_year": "연도로 그룹화", "group_year": "연도로 그룹화",
"has_quota": "할당량", "has_quota": "할당량",
"hi_user": "안녕하세요, {name}님 ({email})", "hi_user": "안녕하세요, {name}님 ({email})",
"hide_all_people": "모든 인물 숨기기",
"hide_gallery": "갤러리 숨기기", "hide_gallery": "갤러리 숨기기",
"hide_named_person": "인물 {name} 숨기기",
"hide_password": "비밀번호 숨기기", "hide_password": "비밀번호 숨기기",
"hide_person": "인물 숨기기", "hide_person": "인물 숨기기",
"hide_unnamed_people": "이름이 없는 인물 숨기기",
"host": "호스트", "host": "호스트",
"hour": "시간", "hour": "시간",
"image": "이미지", "image": "이미지",
"image_alt_text_date": "{date},", "image_alt_text_date": "{date}",
"image_alt_text_people": "{count, plural, =1 {{person1},} =2 {{person1} 및 {person2}} =3 {{person1}, {person2} 및 {person3}} other {{person1}, {person2}, 및 {others, number}명과 함께,}}", "image_alt_text_people": "{count, plural, =1 {{person1} 인물과 함께,} =2 {{person1} 및 {person2} 인물과 함께,} =3 {{person1}, {person2} 및 {person3} 인물과 함께,} other {{person1}, {person2}, 및 {others, number}명의 인물과 함께,}}",
"image_alt_text_place": "{country}, {city}에서", "image_alt_text_place": "{country}, {city}에서",
"image_taken": "{isVideo, select, true {동영상 촬영됨} other {사진 촬영됨}},", "image_taken": "{isVideo, select, true {동영상 촬영됨} other {사진 촬영됨}},",
"img": "", "img": "",
@@ -810,8 +807,8 @@
"menu": "메뉴", "menu": "메뉴",
"merge": "병합", "merge": "병합",
"merge_people": "인물 병합", "merge_people": "인물 병합",
"merge_people_limit": "한 번에 최대 5개의 얼굴까지만 병합할 수 있음", "merge_people_limit": "한 번에 최대 5개의 얼굴만 병합할 수 있음",
"merge_people_prompt": "다음 인물들을 병합하시겠습니까? 이 작업은 되돌릴 수 없습니다.", "merge_people_prompt": "선택한 인물들을 병합하시겠습니까? 이 작업은 되돌릴 수 없습니다.",
"merge_people_successfully": "인물을 성공적으로 병합함", "merge_people_successfully": "인물을 성공적으로 병합함",
"merged_people_count": "인물 {count, plural, one {#명} other {#명}}을 병합함", "merged_people_count": "인물 {count, plural, one {#명} other {#명}}을 병합함",
"minimize": "최소화", "minimize": "최소화",
@@ -834,23 +831,23 @@
"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": "사진과 동영상을 보관함으로 이동하여 목록에서 숨기기",
"no_assets_message": "이곳을 클릭하여 첫 이미지를 업로드하세요", "no_assets_message": "클릭하여 당신의 첫 이미지를 업로드하세요",
"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_name": "이름 없음", "no_name": "이름 없음",
"no_places": "장소 없음", "no_places": "장소 없음",
"no_results": "결과 없음", "no_results": "결과 없음",
"no_results_description": "다른 단어나 조금 더 일반적인 단어를 사용해 보세요", "no_results_description": "다른 단어나 조금 더 일반적인 단어를 사용해 보세요",
"no_shared_albums_message": "공유 앨범을 만들어 주변 사람들과 사진 및 동영상 공유", "no_shared_albums_message": "공유 앨범을 만들어 주변 사람들과 사진 및 동영상 공유",
"not_in_any_album": "어떤 앨범에도 없음", "not_in_any_album": "어떤 앨범에도 없음",
"note_apply_storage_label_to_previously_uploaded assets": "참고: 기존에 업로드한 항목에 스토리지 레이블을 적용하려면 다음 작업을 실행합니다,", "note_apply_storage_label_to_previously_uploaded assets": "참고: 기존에 업로드한 항목에 스토리지 레이블을 적용하려면 다음 작업을 실행합니다,",
"note_unlimited_quota": "참고: 0 입력 시 할당량 제한 없음", "note_unlimited_quota": "참고: 0 입력 시 할당량 제한 없음",
"notes": "참고", "notes": "참고",
"notification_toggle_setting_description": "이메일 알림 활성화", "notification_toggle_setting_description": "이메일 알림 활성화",
@@ -859,7 +856,7 @@
"oauth": "OAuth", "oauth": "OAuth",
"offline": "오프라인", "offline": "오프라인",
"offline_paths": "누락된 파일", "offline_paths": "누락된 파일",
"offline_paths_description": "외부 라이브러리의 항목이 아닌 파일을 수동으로 삭제한 경우 발생할 수 있습니다.", "offline_paths_description": "외부 라이브러리가 아닌 서버 내의 파일을 직접 삭제한 경우 발생할 수 있습니다.",
"ok": "확인", "ok": "확인",
"oldest_first": "오래된 순", "oldest_first": "오래된 순",
"onboarding": "온보딩", "onboarding": "온보딩",
@@ -869,7 +866,7 @@
"onboarding_welcome_user": "{user}님, 환영합니다", "onboarding_welcome_user": "{user}님, 환영합니다",
"online": "온라인", "online": "온라인",
"only_favorites": "즐겨찾기만", "only_favorites": "즐겨찾기만",
"only_refreshes_modified_files": "변경된 파일만 다시 스캔", "only_refreshes_modified_files": "수정된 파일만 새로고침",
"open_in_openstreetmap": "OpenStreetMap에서 열기", "open_in_openstreetmap": "OpenStreetMap에서 열기",
"open_the_search_filters": "검색 필터 열기", "open_the_search_filters": "검색 필터 열기",
"options": "옵션", "options": "옵션",
@@ -882,7 +879,7 @@
"owned": "소유함", "owned": "소유함",
"owner": "소유자", "owner": "소유자",
"partner": "파트너", "partner": "파트너",
"partner_can_access": "{partner}이(가) 접근할 수 있는 항목", "partner_can_access": "{partner} 접근할 수 있",
"partner_can_access_assets": "보관되거나 삭제된 항목을 제외한 모든 사진 및 동영상", "partner_can_access_assets": "보관되거나 삭제된 항목을 제외한 모든 사진 및 동영상",
"partner_can_access_location": "사진을 찍은 장소", "partner_can_access_location": "사진을 찍은 장소",
"partner_sharing": "파트너와 공유", "partner_sharing": "파트너와 공유",
@@ -909,14 +906,14 @@
"permanent_deletion_warning": "영구 삭제 경고", "permanent_deletion_warning": "영구 삭제 경고",
"permanent_deletion_warning_setting_description": "콘텐츠 영구 삭제 시 경고 메시지 표시", "permanent_deletion_warning_setting_description": "콘텐츠 영구 삭제 시 경고 메시지 표시",
"permanently_delete": "영구 삭제", "permanently_delete": "영구 삭제",
"permanently_delete_assets_count": "{count, plural, one {항목} other {항목}} 영구 삭제", "permanently_delete_assets_count": "{count, plural, one {} other {개}} 항목을 영구적으로 삭제",
"permanently_delete_assets_prompt": "정말 {count, plural, one {이 항목을} other {항목 <b>#</b>개를}} 영구적으로 삭제하시겠습니까? 앨범에 {count, plural, one {다음 항목} other {다음 항목}}이 포함된 경우 해당 항목도 삭제됩니다.", "permanently_delete_assets_prompt": "정말 {count, plural, one {이 항목을} other {항목 <b>#</b>개를}} 영구적으로 삭제하시겠습니까? 앨범에 포함된 {count, plural, one {항목} other {항목들도}} 삭제됩니다.",
"permanently_deleted_asset": "항목이 영구적으로 삭제됨", "permanently_deleted_asset": "콘텐츠가 영구적으로 삭제됨",
"permanently_deleted_assets": "항목 {count, plural, one {#개} other {#개}}가 영구적으로 삭제됨", "permanently_deleted_assets": "항목 {count, plural, one {#개} other {#개}}가 영구적으로 삭제됨",
"permanently_deleted_assets_count": "{count, plural, one {개} other {개}} 항목이 영구적으로 삭제됨", "permanently_deleted_assets_count": "{count, plural, one {개} other {개}} 항목이 영구적으로 삭제됨",
"person": "인물", "person": "인물",
"person_hidden": "{name}{hidden, select, true { (숨김)} other {}}", "person_hidden": "{name}{hidden, select, true { (숨김)} other {}}",
"photo_shared_all_users": "이미 모든 사용자와 이 사진을 공유했거나 다른 사용자가 없습니다.", "photo_shared_all_users": "이미 모든 사용자와 공유했거나 공유할 사용자가 없는 것 같습니다.",
"photos": "사진", "photos": "사진",
"photos_and_videos": "사진 & 동영상", "photos_and_videos": "사진 & 동영상",
"photos_count": "사진 {count, plural, one {{count, number}개} other {{count, number}개}}", "photos_count": "사진 {count, plural, one {{count, number}개} other {{count, number}개}}",
@@ -951,13 +948,13 @@
"recent": "최근", "recent": "최근",
"recent_searches": "최근 검색", "recent_searches": "최근 검색",
"refresh": "새로고침", "refresh": "새로고침",
"refresh_encoded_videos": "인코딩 작업 다시 진행", "refresh_encoded_videos": "인코딩된 동영상 새로고침",
"refresh_metadata": "메타데이터 갱신", "refresh_metadata": "메타데이터 새로고침",
"refresh_thumbnails": "섬네일 다시 생성", "refresh_thumbnails": "섬네일 새로고침",
"refreshed": "새로고침 완료", "refreshed": "새로고침 완료",
"refreshes_every_file": "모든 파일을 다시 스캔", "refreshes_every_file": "모든 파일을 새로 고칩니다",
"refreshing_encoded_video": "인코딩 작업을 다시 진행하는 중", "refreshing_encoded_video": "인코딩된 동영상 새로고침",
"refreshing_metadata": "메타데이터 갱신 중", "refreshing_metadata": "메타데이터 새로고침 중",
"regenerating_thumbnails": "섬네일 다시 생성 중", "regenerating_thumbnails": "섬네일 다시 생성 중",
"remove": "삭제", "remove": "삭제",
"remove_assets_album_confirmation": "항목 {count, plural, one {#개} other {#개}}를 앨범에서 제거하시겠습니까?", "remove_assets_album_confirmation": "항목 {count, plural, one {#개} other {#개}}를 앨범에서 제거하시겠습니까?",
@@ -991,7 +988,7 @@
"restore_user": "사용자 복원", "restore_user": "사용자 복원",
"restored_asset": "항목 복원됨", "restored_asset": "항목 복원됨",
"resume": "재개", "resume": "재개",
"retry_upload": "다시 시도", "retry_upload": "업로드 다시 시도",
"review_duplicates": "비슷한 항목 확인", "review_duplicates": "비슷한 항목 확인",
"role": "역할", "role": "역할",
"role_editor": "편집자", "role_editor": "편집자",
@@ -1002,7 +999,7 @@
"saved_settings": "설정 저장됨", "saved_settings": "설정 저장됨",
"say_something": "무엇이든 말해보세요", "say_something": "무엇이든 말해보세요",
"scan_all_libraries": "모든 라이브러리 스캔", "scan_all_libraries": "모든 라이브러리 스캔",
"scan_all_library_files": "모든 파일 다시 스캔", "scan_all_library_files": "모든 라이브러리 파일 다시 스캔",
"scan_new_library_files": "새 라이브러리 파일 스캔", "scan_new_library_files": "새 라이브러리 파일 스캔",
"scan_settings": "스캔 설정", "scan_settings": "스캔 설정",
"scanning_for_album": "앨범을 스캔하고 있습니다...", "scanning_for_album": "앨범을 스캔하고 있습니다...",
@@ -1027,7 +1024,7 @@
"see_all_people": "모든 인물 보기", "see_all_people": "모든 인물 보기",
"select_album_cover": "앨범 커버 선택", "select_album_cover": "앨범 커버 선택",
"select_all": "모두 선택", "select_all": "모두 선택",
"select_avatar_color": "프로필 색상 변경", "select_avatar_color": "아바타 색상 선택",
"select_face": "얼굴 선택", "select_face": "얼굴 선택",
"select_featured_photo": "대표 사진 선택", "select_featured_photo": "대표 사진 선택",
"select_from_computer": "컴퓨터에서 선택", "select_from_computer": "컴퓨터에서 선택",
@@ -1054,18 +1051,17 @@
"share": "공유", "share": "공유",
"shared": "공유됨", "shared": "공유됨",
"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_links": "공유 링크", "shared_links": "공유 링크",
"shared_photos_and_videos_count": "사진 & 동영상 {assetCount, plural, other {#개를 공유했습니다.}}", "shared_photos_and_videos_count": "사진 & 동영상 {assetCount, plural, other {#개를 공유했습니다.}}",
"shared_with_partner": "{partner}와(과) 공유됨", "shared_with_partner": "{partner}와 공유됨",
"sharing": "공유", "sharing": "공유",
"sharing_enter_password": "이 페이지를 보려면 비밀번호를 입력하세요.", "sharing_enter_password": "이 페이지를 보려면 비밀번호를 입력하세요.",
"sharing_sidebar_description": "사이드바에 공유 링크 표시", "sharing_sidebar_description": "사이드바에 공유 링크 표시",
"shift_to_permanent_delete": "⇧를 눌러 항목을 영구적으로 삭제", "shift_to_permanent_delete": "⇧를 눌러 항목을 영구적으로 삭제",
"show_album_options": "앨범 옵션 표시", "show_album_options": "앨범 옵션 표시",
"show_all_people": "모든 인물 보기",
"show_and_hide_people": "인물 표시 & 숨기기", "show_and_hide_people": "인물 표시 & 숨기기",
"show_file_location": "파일 위치 표시", "show_file_location": "파일 위치 표시",
"show_gallery": "갤러리 표시", "show_gallery": "갤러리 표시",
@@ -1103,8 +1099,8 @@
"state": "지역", "state": "지역",
"status": "상태", "status": "상태",
"stop_motion_photo": "모션 포토 정지", "stop_motion_photo": "모션 포토 정지",
"stop_photo_sharing": "공유를 중단하시겠습니까?", "stop_photo_sharing": "사진 공유를 중단하시겠습니까?",
"stop_photo_sharing_description": "더 이상 {partner}이(가) 당신의 사진에 접근할 수 없습니다.", "stop_photo_sharing_description": "더 이상 당신의 사진에 {partner} 접근할 수 없습니다.",
"stop_sharing_photos_with_user": "이 사용자와 사진 공유 중단", "stop_sharing_photos_with_user": "이 사용자와 사진 공유 중단",
"storage": "스토리지", "storage": "스토리지",
"storage_label": "스토리지 레이블", "storage_label": "스토리지 레이블",
@@ -1160,13 +1156,13 @@
"updated_password": "비밀번호 변경됨", "updated_password": "비밀번호 변경됨",
"upload": "업로드", "upload": "업로드",
"upload_concurrency": "업로드 동시성", "upload_concurrency": "업로드 동시성",
"upload_errors": "업로드가 완료되었습니다. {count, plural, one {#개} other {#개}} 항목에 오류가 발생했습니다. 업로드된 항목을 보려면 페이지를 새로고침하세요.", "upload_errors": "업로드가 완료되었습니다. {count, plural, one {#개의 오류} other {#개의 오류}}가 있습니다. 콘텐츠를 보려면 페이지를 새로고침하세요.",
"upload_progress": "{total}개 중 {processed}개 처리됨, {remaining}개 대기 중", "upload_progress": "{total}개 중 {processed}개 처리됨, {remaining}개 대기 중",
"upload_skipped_duplicates": "중복 {count, plural, one {#개} other {#개}} 건너뜀", "upload_skipped_duplicates": "중복 {count, plural, one {#개} other {#개}} 건너뜀",
"upload_status_duplicates": "중복", "upload_status_duplicates": "중복",
"upload_status_errors": "오류", "upload_status_errors": "오류",
"upload_status_uploaded": "업로드됨", "upload_status_uploaded": "업로드됨",
"upload_success": "업로드 성공, 업로드된 항목을 보려면 페이지를 새로고침하세요.", "upload_success": "업로드 성공, 업로드된 콘텐츠를 보려면 페이지를 새로고침하세요.",
"url": "URL", "url": "URL",
"usage": "사용량", "usage": "사용량",
"use_custom_date_range": "대신 맞춤 기간 사용", "use_custom_date_range": "대신 맞춤 기간 사용",
@@ -1184,8 +1180,8 @@
"version_announcement_closing": "당신의 친구, Alex가", "version_announcement_closing": "당신의 친구, Alex가",
"version_announcement_message": "안녕하세요, 새 버전의 Immich를 사용할 수 있습니다. 자세한 내용은 <link>릴리스 노트</link>를 확인하세요. WatchTower 등의 자동 업데이트 기능을 사용하는 경우, 의도하지 않은 동작을 방지하기 위해 <code>docker-compose.yml</code> 및 <code>.env</code> 구성이 최신인지 확인하세요.", "version_announcement_message": "안녕하세요, 새 버전의 Immich를 사용할 수 있습니다. 자세한 내용은 <link>릴리스 노트</link>를 확인하세요. WatchTower 등의 자동 업데이트 기능을 사용하는 경우, 의도하지 않은 동작을 방지하기 위해 <code>docker-compose.yml</code> 및 <code>.env</code> 구성이 최신인지 확인하세요.",
"video": "동영상", "video": "동영상",
"video_hover_setting": "마우스 오버 재생", "video_hover_setting": "동영상에 마우스를 올려 재생",
"video_hover_setting_description": "마우스를 동영상 위에 올리면 재생 시작니다. 비활성화된 경우에도 재생 아이콘에 마우스를 올리면 재생이 시작됩니다.", "video_hover_setting_description": "마우스를 동영상 섬네일 위에 올 재생 시작니다. 비활성화된 경우에도 재생 아이콘에 마우스를 올리면 재생이 시작됩니다.",
"videos": "동영상", "videos": "동영상",
"videos_count": "동영상 {count, plural, one {#개} other {#개}}", "videos_count": "동영상 {count, plural, one {#개} other {#개}}",
"view": "보기", "view": "보기",
+129 -175
View File
@@ -170,13 +170,13 @@
"transcoding_acceleration_nvenc": "", "transcoding_acceleration_nvenc": "",
"transcoding_acceleration_qsv": "", "transcoding_acceleration_qsv": "",
"transcoding_acceleration_rkmpp": "", "transcoding_acceleration_rkmpp": "",
"transcoding_acceleration_vaapi": "VAAPI", "transcoding_acceleration_vaapi": "",
"transcoding_accepted_audio_codecs": "", "transcoding_accepted_audio_codecs": "",
"transcoding_accepted_audio_codecs_description": "", "transcoding_accepted_audio_codecs_description": "",
"transcoding_accepted_video_codecs": "", "transcoding_accepted_video_codecs": "",
"transcoding_accepted_video_codecs_description": "", "transcoding_accepted_video_codecs_description": "",
"transcoding_advanced_options_description": "", "transcoding_advanced_options_description": "",
"transcoding_audio_codec": "Garso kodekas", "transcoding_audio_codec": "",
"transcoding_audio_codec_description": "", "transcoding_audio_codec_description": "",
"transcoding_bitrate_description": "", "transcoding_bitrate_description": "",
"transcoding_constant_quality_mode": "", "transcoding_constant_quality_mode": "",
@@ -188,7 +188,7 @@
"transcoding_hardware_acceleration_description": "", "transcoding_hardware_acceleration_description": "",
"transcoding_hardware_decoding": "", "transcoding_hardware_decoding": "",
"transcoding_hardware_decoding_setting_description": "", "transcoding_hardware_decoding_setting_description": "",
"transcoding_hevc_codec": "HEVC kodekas", "transcoding_hevc_codec": "",
"transcoding_max_b_frames": "", "transcoding_max_b_frames": "",
"transcoding_max_b_frames_description": "", "transcoding_max_b_frames_description": "",
"transcoding_max_bitrate": "", "transcoding_max_bitrate": "",
@@ -218,86 +218,65 @@
"transcoding_transcode_policy": "", "transcoding_transcode_policy": "",
"transcoding_two_pass_encoding": "", "transcoding_two_pass_encoding": "",
"transcoding_two_pass_encoding_setting_description": "", "transcoding_two_pass_encoding_setting_description": "",
"transcoding_video_codec": "Video kodekas", "transcoding_video_codec": "",
"transcoding_video_codec_description": "", "transcoding_video_codec_description": "",
"trash_enabled_description": "", "trash_enabled_description": "",
"trash_number_of_days": "Dienų skaičius", "trash_number_of_days": "",
"trash_number_of_days_description": "", "trash_number_of_days_description": "",
"trash_settings": "Šiukšliadėžės nustatymai", "trash_settings": "",
"trash_settings_description": "Tvarkyti šiukšliadėžės nustatymus", "trash_settings_description": "",
"user_delete_delay_settings": "", "user_delete_delay_settings": "",
"user_delete_delay_settings_description": "", "user_delete_delay_settings_description": "",
"user_password_has_been_reset": "Vartotojo slaptažodis buvo iš naujo nustatytas:", "user_settings": "",
"user_settings": "Vartotojo nustatymai", "user_settings_description": "",
"user_settings_description": "Valdyti vartotojo nustatymus",
"user_successfully_removed": "Vartotojas {email} sėkmingai pašalintas.",
"version_check_enabled_description": "", "version_check_enabled_description": "",
"version_check_settings": "Versijos tikrinimas", "version_check_settings": "",
"version_check_settings_description": "Įjungti/išjungti naujos versijos pranešimus", "version_check_settings_description": "",
"video_conversion_job_description": "" "video_conversion_job_description": ""
}, },
"admin_email": "Administratoriaus el. paštas", "admin_email": "",
"admin_password": "Administratoriaus slaptažodis", "admin_password": "",
"administration": "", "administration": "",
"advanced": "", "advanced": "",
"album_added": "Albumas pridėtas", "album_added": "",
"album_added_notification_setting_description": "", "album_added_notification_setting_description": "",
"album_cover_updated": "Albumo viršelis atnaujintas", "album_cover_updated": "",
"album_info_updated": "Albumo informacija atnaujinta", "album_info_updated": "",
"album_leave": "Palikti albumą?", "album_name": "",
"album_leave_confirmation": "Ar tikrai norite palikti albumą {album}?", "album_options": "",
"album_name": "Albumo pavadinimas", "album_updated": "",
"album_options": "Albumo parinktys",
"album_remove_user": "Pašalinti vartotoją?",
"album_remove_user_confirmation": "Ar tikrai norite pašalinti vartotoją {user}?",
"album_updated": "Albumas atnaujintas",
"album_updated_setting_description": "", "album_updated_setting_description": "",
"album_user_removed": "Pašalintas {user}", "albums": "",
"album_with_link_access": "Tegul visi, turintys nuorodą, mato šio albumo nuotraukas ir žmones.", "all": "",
"albums": "Albumai", "all_people": "",
"all": "Visi", "allow_dark_mode": "",
"all_albums": "Visi albumai", "allow_edits": "",
"all_people": "Visi žmonės", "api_key": "",
"all_videos": "Visi video", "api_keys": "",
"allow_dark_mode": "Leisti tamsųjį režimą", "app_settings": "",
"allow_edits": "Leisti redagavimus",
"allow_public_user_to_download": "Leisti viešam naudotojui atsisiųsti",
"allow_public_user_to_upload": "Leisti viešam naudotojui įkelti",
"api_key": "API raktas",
"api_keys": "API raktai",
"app_settings": "Programos nustatymai",
"appears_in": "", "appears_in": "",
"archive": "", "archive": "",
"archive_or_unarchive_photo": "", "archive_or_unarchive_photo": "",
"archive_size": "Archyvo dydis",
"archived": "", "archived": "",
"are_these_the_same_person": "Ar tai tas pats asmuo?",
"are_you_sure_to_do_this": "Ar tikrai norite tai daryti?",
"asset_added_to_album": "Pridėta į albumą",
"asset_adding_to_album": "Pridedama į albumą...",
"asset_offline": "", "asset_offline": "",
"asset_uploaded": "Įkelta",
"asset_uploading": "Įkeliama...",
"assets": "", "assets": "",
"authorized_devices": "Autorizuoti įrenginiai", "authorized_devices": "",
"back": "Atgal", "back": "",
"back_close_deselect": "Atgal, uždaryti arba atžymėti",
"backward": "", "backward": "",
"birthdate_saved": "Sėkmingai išsaugota gimimo data", "blurred_background": "",
"blurred_background": "Neryškus fonas", "camera": "",
"camera": "Fotoaparatas", "camera_brand": "",
"camera_brand": "Fotoaparato prekės ženklas", "camera_model": "",
"camera_model": "Fotoaparato modelis", "cancel": "",
"cancel": "Atšaukti", "cancel_search": "",
"cancel_search": "Atšaukti paiešką", "cannot_merge_people": "",
"cannot_merge_people": "Negalima sujungti asmenų",
"cannot_update_the_description": "", "cannot_update_the_description": "",
"cant_apply_changes": "", "cant_apply_changes": "",
"cant_get_faces": "", "cant_get_faces": "",
"cant_search_people": "", "cant_search_people": "",
"cant_search_places": "", "cant_search_places": "",
"change_date": "Pakeisti datą", "change_date": "",
"change_expiration_time": "Pakeisti galiojimo trukmę", "change_expiration_time": "",
"change_location": "Pakeisti vietovę", "change_location": "Pakeisti vietovę",
"change_name": "Pakeisti vardą", "change_name": "Pakeisti vardą",
"change_name_successfully": "", "change_name_successfully": "",
@@ -515,165 +494,140 @@
"go_to_share_page": "", "go_to_share_page": "",
"group_albums_by": "", "group_albums_by": "",
"has_quota": "", "has_quota": "",
"hi_user": "Labas {name} ({email})", "hide_gallery": "",
"hide_all_people": "Slėpti visus asmenis", "hide_password": "",
"hide_gallery": "Slėpti galeriją", "hide_person": "",
"hide_named_person": "Slėpti asmenį {name}",
"hide_password": "Slėpti slaptažodį",
"hide_person": "Slėpti asmenį",
"hide_unnamed_people": "Slėpti neįvardintus asmenis",
"host": "", "host": "",
"hour": "Valanda", "hour": "",
"image": "Nuotrauka", "image": "",
"img": "", "img": "",
"immich_logo": "", "immich_logo": "",
"import_from_json": "Importuoti iš JSON", "import_path": "",
"import_path": "Importavimo kelias", "in_archive": "",
"in_archive": "Archyve", "include_archived": "",
"include_archived": "Įtraukti archyvuotus", "include_shared_albums": "",
"include_shared_albums": "Įtraukti bendrinamus albumus",
"include_shared_partner_assets": "", "include_shared_partner_assets": "",
"individual_share": "", "individual_share": "",
"info": "Informacija", "info": "",
"interval": { "interval": {
"day_at_onepm": "Kiekvieną dieną 13:00", "day_at_onepm": "",
"hours": "", "hours": "",
"night_at_midnight": "Kiekvieną vidurnaktį", "night_at_midnight": "",
"night_at_twoam": "Kiekvieną naktį 02:00" "night_at_twoam": ""
}, },
"invite_people": "Kviesti žmones", "invite_people": "",
"invite_to_album": "Pakviesti į albumą", "invite_to_album": "",
"job_settings_description": "", "job_settings_description": "",
"jobs": "Darbai", "jobs": "",
"keep": "Palikti", "keep": "",
"keep_all": "Palikti visus", "keyboard_shortcuts": "",
"keyboard_shortcuts": "Spartieji klaviatūros klavišai", "language": "",
"language": "Kalba", "language_setting_description": "",
"language_setting_description": "Pasirinkti pageidaujamą kalbą", "last_seen": "",
"last_seen": "Paskutinį kartą matytas", "leave": "",
"latest_version": "Naujausia versija", "let_others_respond": "",
"leave": "Išeiti",
"let_others_respond": "Leisti kitiems reaguoti",
"level": "", "level": "",
"library": "Biblioteka", "library": "",
"library_options": "Bibliotekos pasirinktys", "library_options": "",
"light": "", "light": "",
"link_options": "Nuorodų parinktys", "link_options": "",
"link_to_oauth": "", "link_to_oauth": "",
"linked_oauth_account": "", "linked_oauth_account": "",
"list": "Sąrašas", "list": "",
"loading": "Kraunama", "loading": "",
"loading_search_results_failed": "", "loading_search_results_failed": "",
"log_out": "Atsijungti", "log_out": "",
"log_out_all_devices": "Atsijungti iš visų įrenginių", "log_out_all_devices": "",
"logged_out_all_devices": "Atsijungta iš visų įrenginių", "login_has_been_disabled": "",
"login": "Prisijungti",
"login_has_been_disabled": "Prisijungimas išjungtas.",
"logout_all_device_confirmation": "Ar tikrai norite atsijungti iš visų įrenginių?",
"logout_this_device_confirmation": "Ar tikrai norite atsijungti iš šio prietaiso?",
"look": "", "look": "",
"loop_videos": "", "loop_videos": "",
"loop_videos_description": "", "loop_videos_description": "",
"make": "", "make": "",
"manage_shared_links": "Bendrai naudojamų nuorodų tvarkymas", "manage_shared_links": "",
"manage_sharing_with_partners": "Valdyti dalijimąsi su partneriais", "manage_sharing_with_partners": "",
"manage_the_app_settings": "Valdyti programos nustatymus", "manage_the_app_settings": "",
"manage_your_account": "Valdyti savo paskyrą", "manage_your_account": "",
"manage_your_api_keys": "Valdyti savo API raktus", "manage_your_api_keys": "",
"manage_your_devices": "Valdyti prijungtus įrenginius", "manage_your_devices": "",
"manage_your_oauth_connection": "", "manage_your_oauth_connection": "",
"map": "Žemėlapis", "map": "",
"map_marker_with_image": "", "map_marker_with_image": "",
"map_settings": "Žemėlapio nustatymai", "map_settings": "",
"media_type": "Laikmenos tipas", "media_type": "",
"memories": "Atsiminimai", "memories": "",
"memories_setting_description": "Valdyti tai, ką matote savo prisiminimuose", "memories_setting_description": "",
"memory": "Atmintis", "menu": "",
"menu": "Meniu", "merge": "",
"merge": "Sujungti", "merge_people": "",
"merge_people": "Sujungti asmenis", "merge_people_successfully": "",
"merge_people_limit": "Vienu metu galite sujungti tik iki 5 veidų", "minimize": "",
"merge_people_prompt": "Ar norite sujungti šiuos asmenis? Šis veiksmas yra negrįžtamas.", "minute": "",
"merge_people_successfully": "Asmenys sėkmingai sujungti",
"minimize": "Sumažinti",
"minute": "Minutė",
"missing": "", "missing": "",
"model": "Modelis", "model": "",
"month": "Mėnesis", "month": "",
"more": "Daugiau", "more": "",
"moved_to_trash": "Perkelta į šiukšliadėžę", "moved_to_trash": "",
"my_albums": "Mano albumai", "my_albums": "",
"name": "Vardas", "name": "",
"name_or_nickname": "Vardas arba slapyvardis", "name_or_nickname": "",
"never": "Niekada", "never": "",
"new_api_key": "Naujas API raktas", "new_api_key": "",
"new_password": "Naujas slaptažodis", "new_password": "",
"new_person": "Naujas asmuo", "new_person": "",
"new_user_created": "Sukurtas naujas vartotojas", "new_user_created": "",
"new_version_available": "PRIEINAMA NAUJA VERSIJA",
"newest_first": "", "newest_first": "",
"next": "Sekantis", "next": "",
"next_memory": "Sekantis atsiminimas", "next_memory": "",
"no": "Ne", "no": "",
"no_albums_message": "Sukurkite albumą nuotraukoms ir vaizdo įrašams tvarkyti", "no_albums_message": "",
"no_albums_with_name_yet": "Atrodo, kad dar neturite albumų su šiuo pavadinimu.",
"no_albums_yet": "Atrodo, kad dar neturite albumų.",
"no_archived_assets_message": "", "no_archived_assets_message": "",
"no_assets_message": "SPUSTELĖKITE NORĖDAMI ĮKELTI PIRMĄJĄ NUOTRAUKĄ", "no_assets_message": "",
"no_duplicates_found": "Dublikatų nerasta.",
"no_exif_info_available": "", "no_exif_info_available": "",
"no_explore_results_message": "Įkelkite daugiau nuotraukų ir tyrinėkite savo kolekciją.", "no_explore_results_message": "",
"no_favorites_message": "", "no_favorites_message": "",
"no_libraries_message": "Sukurkite išorinę biblioteką nuotraukoms ir vaizdo įrašams peržiūrėti", "no_libraries_message": "",
"no_name": "Be vardo", "no_name": "",
"no_places": "", "no_places": "",
"no_results": "Nerasta", "no_results": "",
"no_results_description": "Pabandykite sinonimą arba bendresnį raktažodį",
"no_shared_albums_message": "", "no_shared_albums_message": "",
"not_in_any_album": "Nė viename albume", "not_in_any_album": "",
"notes": "Pastabos", "notes": "",
"notification_toggle_setting_description": "Įjungti el. pašto pranešimus", "notification_toggle_setting_description": "",
"notifications": "Pranešimai", "notifications": "",
"notifications_setting_description": "Tvarkyti pranešimus", "notifications_setting_description": "",
"oauth": "", "oauth": "",
"offline": "Neprisijungęs", "offline": "",
"ok": "Ok", "ok": "",
"oldest_first": "Seniausias pirmas", "oldest_first": "",
"onboarding_welcome_user": "Sveiki atvykę, {user}", "online": "",
"online": "Prisijungęs", "only_favorites": "",
"only_favorites": "Tik mėgstamiausi",
"only_refreshes_modified_files": "", "only_refreshes_modified_files": "",
"open_the_search_filters": "", "open_the_search_filters": "",
"options": "Pasirinktys", "options": "",
"or": "arba", "organize_your_library": "",
"organize_your_library": "Tvarkykite savo biblioteką",
"other": "", "other": "",
"other_devices": "", "other_devices": "",
"other_variables": "Kiti kintamieji", "other_variables": "",
"owned": "", "owned": "",
"owner": "Savininkas", "owner": "",
"partner": "Partneris",
"partner_can_access": "{partner} gali naudotis",
"partner_can_access_assets": "Visos jūsų nuotraukos ir vaizdo įrašai, išskyrus archyvuotus ir ištrintus",
"partner_can_access_location": "Vieta, kurioje darytos nuotraukos",
"partner_sharing": "", "partner_sharing": "",
"partners": "Partneriai", "partners": "",
"password": "Slaptažodis", "password": "",
"password_does_not_match": "Slaptažodis nesutampa", "password_does_not_match": "",
"password_required": "Reikalingas slaptažodis", "password_required": "",
"password_reset_success": "Slaptažodis sėkmingai atkurtas", "password_reset_success": "",
"past_durations": { "past_durations": {
"days": "", "days": "",
"hours": "", "hours": "",
"years": "" "years": ""
}, },
"path": "Kelias", "path": "",
"pattern": "", "pattern": "",
"pause": "", "pause": "",
"pause_memories": "", "pause_memories": "",
"paused": "", "paused": "",
"pending": "Laukiama", "pending": "",
"people": "Asmenys", "people": "",
"people_sidebar_description": "", "people_sidebar_description": "",
"perform_library_tasks": "", "perform_library_tasks": "",
"permanent_deletion_warning": "", "permanent_deletion_warning": "",
+11 -22
View File
@@ -27,37 +27,26 @@
"added_to_favorites": "Pievienots izlasei", "added_to_favorites": "Pievienots izlasei",
"added_to_favorites_count": "Pievienots {count} izlasei", "added_to_favorites_count": "Pievienots {count} izlasei",
"admin": { "admin": {
"add_exclusion_pattern_description": "Pievienojiet izlaišanas shēmas. Aizstājējzīmju izmantoša *, **, un ? tiek atbalstīta. Lai ignorētu visus failus jebkurā direktorijā ar nosaukumu “RAW”, izmantojiet “**/RAW/**”. Lai ignorētu visus failus, kas beidzas ar “. tif”, izmantojiet “**/*. tif”. Lai ignorētu absolūto ceļu, izmantojiet “/path/to/ignore/**”.", "authentication_settings": "",
"authentication_settings": "Autentifikācijas iestatījumi", "authentication_settings_description": "",
"authentication_settings_description": "Pārvaldīt paroles, OAuth un citus autentifikācijas iestatījumus",
"authentication_settings_disable_all": "Vai tiešām vēlaties atspējot visas pieteikšanās metodes? Pieteikšanās tiks pilnībā atspējota.",
"authentication_settings_reenable": "Lai atkārtoti iespējotu, izmantojiet <link>Servera Komandu</link>.",
"background_task_job": "Fona Uzdevumi",
"check_all": "Pārbaudīt Visu",
"cleared_jobs": "Notīrīti darbi priekš: {job}",
"config_set_by_file": "Konfigurāciju pašlaik iestata konfigurācijas fails",
"confirm_delete_library": "Vai tiešām vēlaties dzēst {library} bibliotēku?",
"confirm_email_below": "Lai apstiprinātu, zemāk ierakstiet “{email}”",
"confirm_reprocess_all_faces": "Vai tiešām vēlaties atkārtoti apstrādāt visas sejas? Tas arī atiestatīs cilvēkus ar vārdiem.",
"confirm_user_password_reset": "Vai tiešām vēlaties atiestatīt lietotāja {user} paroli?",
"crontab_guru": "", "crontab_guru": "",
"disable_login": "Atspējot pieteikšanos", "disable_login": "",
"disabled": "", "disabled": "",
"duplicate_detection_job_description": "Palaidiet mašīnmācīšanos uz līdzekļiem, lai noteiktu līdzīgus attēlus. Paļaujas uz Viedo Meklēšanu", "duplicate_detection_job_description": "",
"image_format_description": "", "image_format_description": "",
"image_prefer_embedded_preview": "", "image_prefer_embedded_preview": "",
"image_prefer_embedded_preview_setting_description": "", "image_prefer_embedded_preview_setting_description": "",
"image_prefer_wide_gamut": "", "image_prefer_wide_gamut": "",
"image_prefer_wide_gamut_setting_description": "", "image_prefer_wide_gamut_setting_description": "",
"image_preview_format": "Priekšskatījuma formāts", "image_preview_format": "",
"image_preview_resolution": "Priekšskatījuma izšķirtspēja", "image_preview_resolution": "",
"image_preview_resolution_description": "", "image_preview_resolution_description": "",
"image_quality": "Kvalitāte", "image_quality": "Kvalitāte",
"image_quality_description": "Attēla kvalitāte no 1 līdz 100. Augstāka kvalitāte ir labāka, bet veido lielākus failus. Šī opcija ietekmē Priekšskatījums un Sīktēls attēlus.", "image_quality_description": "",
"image_settings": "Attēla Iestatījumi", "image_settings": "",
"image_settings_description": "Ģenerēto attēlu kvalitātes un izšķirtspējas pārvaldība", "image_settings_description": "",
"image_thumbnail_format": "Sīktēlu formāts", "image_thumbnail_format": "",
"image_thumbnail_resolution": "Sīktēlu izšķirtspēja", "image_thumbnail_resolution": "",
"image_thumbnail_resolution_description": "", "image_thumbnail_resolution_description": "",
"job_settings": "", "job_settings": "",
"job_settings_description": "", "job_settings_description": "",
-4
View File
@@ -723,12 +723,9 @@
"group_year": "Groeperen op jaar", "group_year": "Groeperen op jaar",
"has_quota": "Heeft limiet", "has_quota": "Heeft limiet",
"hi_user": "Hallo {name} ({email})", "hi_user": "Hallo {name} ({email})",
"hide_all_people": "Verberg alle mensen",
"hide_gallery": "Gallerij verbergen", "hide_gallery": "Gallerij verbergen",
"hide_named_person": "Verberg persoon {name}",
"hide_password": "Verberg wachtwoord", "hide_password": "Verberg wachtwoord",
"hide_person": "Verberg persoon", "hide_person": "Verberg persoon",
"hide_unnamed_people": "Verberg mensen zonder naam",
"host": "Host", "host": "Host",
"hour": "Uur", "hour": "Uur",
"image": "Afbeelding", "image": "Afbeelding",
@@ -1065,7 +1062,6 @@
"sharing_sidebar_description": "Toon een link naar Delen in de zijbalk", "sharing_sidebar_description": "Toon een link naar Delen in de zijbalk",
"shift_to_permanent_delete": "druk op ⇧ om assets permanent te verwijderen", "shift_to_permanent_delete": "druk op ⇧ om assets permanent te verwijderen",
"show_album_options": "Toon albumopties", "show_album_options": "Toon albumopties",
"show_all_people": "Toon alle mensen",
"show_and_hide_people": "Toon & verberg mensen", "show_and_hide_people": "Toon & verberg mensen",
"show_file_location": "Toon bestandslocatie", "show_file_location": "Toon bestandslocatie",
"show_gallery": "Toon gallerij", "show_gallery": "Toon gallerij",
-3
View File
@@ -729,9 +729,6 @@
"host": "Host", "host": "Host",
"hour": "Godzina", "hour": "Godzina",
"image": "Zdjęcie", "image": "Zdjęcie",
"image_alt_text_date": "dnia {date}",
"image_alt_text_people": "{count, plural, =1 {z {person1}} =2 {z {person1} i {person2}} =3 {z {person1}, {person2} i {person3}} other {z {person1}, {person2} i {others, number} innymi}}",
"image_alt_text_place": "w {city}, {country}",
"img": "", "img": "",
"immich_logo": "Logo Immich", "immich_logo": "Logo Immich",
"immich_web_interface": "Interfejs internetowy Immich", "immich_web_interface": "Interfejs internetowy Immich",
+8 -12
View File
@@ -52,7 +52,7 @@
"face_detection_description": "Обнаружение лиц на ресурсах с помощью машинного обучения. Для видео учитывается только миниатюра. “Все” (пере)обрабатывает все ресурсы. “Отсутствующие” ресурсы помещаются в очередь пока не обработанных. Обнаруженные лица будут помещены в очередь для распознавания лиц после завершения обнаружения лиц, объединяя их в существующие или новые группы людей.", "face_detection_description": "Обнаружение лиц на ресурсах с помощью машинного обучения. Для видео учитывается только миниатюра. “Все” (пере)обрабатывает все ресурсы. “Отсутствующие” ресурсы помещаются в очередь пока не обработанных. Обнаруженные лица будут помещены в очередь для распознавания лиц после завершения обнаружения лиц, объединяя их в существующие или новые группы людей.",
"facial_recognition_job_description": "Группировка распознанных лиц по людям. Этот шаг выполняется после завершения обнаружения лиц. “Все” (пере)кластеризует все лица. “Отсутствующие” помещает лица без привязки к человеку в очередь.", "facial_recognition_job_description": "Группировка распознанных лиц по людям. Этот шаг выполняется после завершения обнаружения лиц. “Все” (пере)кластеризует все лица. “Отсутствующие” помещает лица без привязки к человеку в очередь.",
"failed_job_command": "Команда {command} не выполнена для задачи: {job}", "failed_job_command": "Команда {command} не выполнена для задачи: {job}",
"force_delete_user_warning": "ПРЕДУПРЕЖДЕНИЕ: Это приведет к немедленному удалению пользователя и всех ресурсов. Это невозможно отменить, и файлы не смогут быть восстановлены.", "force_delete_user_warning": "ПРЕДУПРЕЖДЕНИЕ: Это приведет к немедленному удалению пользователя и всех ресурсов. Это невозможно отменить, и файлы не могут быть восстановлены.",
"forcing_refresh_library_files": "Принудительное обновление всех файлов библиотеки", "forcing_refresh_library_files": "Принудительное обновление всех файлов библиотеки",
"image_format_description": "WebP создает файлы меньшего размера, чем JPEG, но кодируется медленнее.", "image_format_description": "WebP создает файлы меньшего размера, чем JPEG, но кодируется медленнее.",
"image_prefer_embedded_preview": "Предпочитать встроенное превью", "image_prefer_embedded_preview": "Предпочитать встроенное превью",
@@ -216,12 +216,12 @@
"storage_template_date_time_description": "Время создание объекта использовано как информация о времени съемки", "storage_template_date_time_description": "Время создание объекта использовано как информация о времени съемки",
"storage_template_date_time_sample": "Время выборки {date}", "storage_template_date_time_sample": "Время выборки {date}",
"storage_template_enable_description": "Включить механизм создания шаблонов хранения", "storage_template_enable_description": "Включить механизм создания шаблонов хранения",
"storage_template_hash_verification_enabled": "Включено проверку хеша", "storage_template_hash_verification_enabled": "Проверка хеша не удалась",
"storage_template_hash_verification_enabled_description": "Включает проверку хэша, не отключайте ее, если вы не уверены в последствиях", "storage_template_hash_verification_enabled_description": "Включает проверку хэша, не отключайте ее, если вы не уверены в последствиях",
"storage_template_migration": "Применение шаблона хранилища", "storage_template_migration": "Применение шаблона хранилища",
"storage_template_migration_description": "Применить текущий <link>{template}</link> к ранее загруженным ресурсам", "storage_template_migration_description": "Применить текущий <link>{template}</link> к ранее загруженным ресурсам",
"storage_template_migration_info": "Изменения шаблона будут применяться только к новым ресурсам. Чтобы применить шаблон к ранее загруженным ресурсам, запустите <link>{job}</link>.", "storage_template_migration_info": "Изменения шаблона будут применяться только к новым ресурсам. Чтобы применить шаблон к ранее загруженным ресурсам, запустите <link>{job}</link>.",
"storage_template_migration_job": "Задание миграции шаблона хранилища", "storage_template_migration_job": "Задание миграции хранилища",
"storage_template_more_details": "Для получения дополнительной информации об этой функции обратитесь к <template-link>Шаблону Хранилища</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": "При включении этой функции файлы будут автоматически организованы в соответствии с пользовательским шаблоном. Из-за проблем со стабильностью функция по умолчанию отключена. Дополнительную информацию можно найти в <link>документации</link>.",
"storage_template_path_length": "Примерная длина пути: <b>{length, number}</b>/{limit, number}", "storage_template_path_length": "Примерная длина пути: <b>{length, number}</b>/{limit, number}",
@@ -260,7 +260,7 @@
"transcoding_hardware_acceleration": "Аппаратное ускорение", "transcoding_hardware_acceleration": "Аппаратное ускорение",
"transcoding_hardware_acceleration_description": "Экспериментальный; намного быстрее, но будет иметь более низкое качество при той же скорости передачи данных", "transcoding_hardware_acceleration_description": "Экспериментальный; намного быстрее, но будет иметь более низкое качество при той же скорости передачи данных",
"transcoding_hardware_decoding": "Аппаратное декодирование", "transcoding_hardware_decoding": "Аппаратное декодирование",
"transcoding_hardware_decoding_setting_description": "Применяется только к NVENC, QSV и RKMPP. Обеспечивает сквозное ускорение, а не только ускорение кодирования. Может работать не на всех видео.", "transcoding_hardware_decoding_setting_description": "Применяется только к NVENC и RKMPP. Обеспечивает сквозное ускорение, а не только ускорение кодирования. Может работать не на всех видео.",
"transcoding_hevc_codec": "Кодек HEVC", "transcoding_hevc_codec": "Кодек HEVC",
"transcoding_max_b_frames": "Максимально промежуточных кадров", "transcoding_max_b_frames": "Максимально промежуточных кадров",
"transcoding_max_b_frames_description": "Более высокие значения повышают эффективность сжатия, но замедляют кодирование. Может быть несовместимо с аппаратным ускорением на старых устройствах. 0 отключает B-кадры, а -1 устанавливает это значение автоматически.", "transcoding_max_b_frames_description": "Более высокие значения повышают эффективность сжатия, но замедляют кодирование. Может быть несовместимо с аппаратным ускорением на старых устройствах. 0 отключает B-кадры, а -1 устанавливает это значение автоматически.",
@@ -297,7 +297,7 @@
"trash_enabled_description": "Включить корзину", "trash_enabled_description": "Включить корзину",
"trash_number_of_days": "Количество дней", "trash_number_of_days": "Количество дней",
"trash_number_of_days_description": "Количество дней, в течение которых объекты будут храниться в корзине, прежде чем они будут окончательно удалены", "trash_number_of_days_description": "Количество дней, в течение которых объекты будут храниться в корзине, прежде чем они будут окончательно удалены",
"trash_settings": "Настройки корзины", "trash_settings": "Настройки Корзины",
"trash_settings_description": "Управление настройками корзины", "trash_settings_description": "Управление настройками корзины",
"untracked_files": "НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ", "untracked_files": "НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ",
"untracked_files_description": "Приложение не отслеживает эти файлы. Они могут быть результатом неудачных перемещений, прерванных загрузок или пропущены из-за ошибки", "untracked_files_description": "Приложение не отслеживает эти файлы. Они могут быть результатом неудачных перемещений, прерванных загрузок или пропущены из-за ошибки",
@@ -723,12 +723,9 @@
"group_year": "Группировать по годам", "group_year": "Группировать по годам",
"has_quota": "Квота", "has_quota": "Квота",
"hi_user": "Привет {name} ({email})", "hi_user": "Привет {name} ({email})",
"hide_all_people": "Скрыть всех",
"hide_gallery": "Скрыть галерею", "hide_gallery": "Скрыть галерею",
"hide_named_person": "Скрыть {name}",
"hide_password": "Скрыть пароль", "hide_password": "Скрыть пароль",
"hide_person": "Скрыть персону", "hide_person": "Скрыть персону",
"hide_unnamed_people": "Скрыть людей без имени",
"host": "Хост", "host": "Хост",
"hour": "Час", "hour": "Час",
"image": "Изображения", "image": "Изображения",
@@ -932,7 +929,7 @@
"previous_memory": "Предыдущее воспоминание", "previous_memory": "Предыдущее воспоминание",
"previous_or_next_photo": "Предыдущая или следующая фотография", "previous_or_next_photo": "Предыдущая или следующая фотография",
"primary": "Главное", "primary": "Главное",
"profile_image_of_user": "Изображение профиля {user}", "profile_image_of_user": "Изображение профиля {title}",
"profile_picture_set": "Установлена картинка профиля.", "profile_picture_set": "Установлена картинка профиля.",
"public_album": "Публичный альбом", "public_album": "Публичный альбом",
"public_share": "Публичный доступ", "public_share": "Публичный доступ",
@@ -1025,7 +1022,7 @@
"select_all": "Выбрать все", "select_all": "Выбрать все",
"select_avatar_color": "Выбрать цвет аватара", "select_avatar_color": "Выбрать цвет аватара",
"select_face": "Выбрать лицо", "select_face": "Выбрать лицо",
"select_featured_photo": "Выбрать избранное фото", "select_featured_photo": "Выбрать фото профиля",
"select_from_computer": "Выберите с компьютера", "select_from_computer": "Выберите с компьютера",
"select_keep_all": "Оставить всё выбранное", "select_keep_all": "Оставить всё выбранное",
"select_library_owner": "Выбрать владельца библиотеки", "select_library_owner": "Выбрать владельца библиотеки",
@@ -1035,7 +1032,7 @@
"selected": "Выбрано", "selected": "Выбрано",
"selected_count": "{count, plural, one {# выбран} other {# выбрано}}", "selected_count": "{count, plural, one {# выбран} other {# выбрано}}",
"send_message": "Отправить сообщение", "send_message": "Отправить сообщение",
"send_welcome_email": "Отправить приветственное письмо", "send_welcome_email": "Отправить приветственную почту",
"server": "Сервер", "server": "Сервер",
"server_stats": "Статистика сервера", "server_stats": "Статистика сервера",
"server_version": "Версия Сервера", "server_version": "Версия Сервера",
@@ -1061,7 +1058,6 @@
"sharing_sidebar_description": "Отображать ссылку на общий доступ в боковой панели", "sharing_sidebar_description": "Отображать ссылку на общий доступ в боковой панели",
"shift_to_permanent_delete": "нажмите ⇧ чтобы удалить объект навсегда", "shift_to_permanent_delete": "нажмите ⇧ чтобы удалить объект навсегда",
"show_album_options": "Показать параметры альбома", "show_album_options": "Показать параметры альбома",
"show_all_people": "Показать всех людей",
"show_and_hide_people": "Показать и скрыть людей", "show_and_hide_people": "Показать и скрыть людей",
"show_file_location": "Показать расположение файла", "show_file_location": "Показать расположение файла",
"show_gallery": "Показать галерею", "show_gallery": "Показать галерею",
+3 -9
View File
@@ -29,21 +29,15 @@
"admin": { "admin": {
"authentication_settings": "Nastavenia overenia", "authentication_settings": "Nastavenia overenia",
"authentication_settings_description": "Spravovať heslo, protokol OAuth a ďalšie nastavenia overenia", "authentication_settings_description": "Spravovať heslo, protokol OAuth a ďalšie nastavenia overenia",
"authentication_settings_disable_all": "Naozaj chcete zakázať všetky spôsoby prihlásenia? Prihlásenie bude úplne zakázané.",
"authentication_settings_reenable": "Pre povolenie použite <link>Serverový príkaz</link>.",
"background_task_job": "Úlohy na pozadí", "background_task_job": "Úlohy na pozadí",
"check_all": "Skontrolovať všetko", "check_all": "Skontrolovať všetko",
"config_set_by_file": "Konfigurácia je v súčasnosti nastavená konfiguračným súborom", "config_set_by_file": "Konfigurácia je v súčasnosti nastavená konfiguračným súborom",
"confirm_delete_library": "Naozaj chcete vymazať knižnicu {library}?", "confirm_delete_library": "Naozaj chcete vymazať knižnicu {library}?",
"confirm_email_below": "Pre potvrdenie zadajte nižšie \"{email}\"", "confirm_email_below": "Pre potvrdenie zadajte nižšie \"{email}\"",
"confirm_user_password_reset": "Určite chcete resetovať heslo pre {user}?",
"crontab_guru": "", "crontab_guru": "",
"disable_login": "Zakázať prihlásenie", "disable_login": "",
"disabled": "", "disabled": "",
"duplicate_detection_job_description": "", "duplicate_detection_job_description": "",
"external_library_created_at": "Externá knižnica (vytvorená {date})",
"external_library_management": "Správa Externej Knižnice",
"face_detection": "Detekcia tváre",
"image_format_description": "", "image_format_description": "",
"image_prefer_embedded_preview": "Uprednostňovať vstavaný náhľad", "image_prefer_embedded_preview": "Uprednostňovať vstavaný náhľad",
"image_prefer_embedded_preview_setting_description": "Použiť vložené náhľady vo fotografiách RAW ako vstup pre spracovanie obrazu, ak sú k dispozícii. To môže vytvoriť presnejšie farby pre niektoré obrázky, ale kvalita náhľadu závisí od fotoaparátu a obrázok môže mať viac kompresných artefaktov.", "image_prefer_embedded_preview_setting_description": "Použiť vložené náhľady vo fotografiách RAW ako vstup pre spracovanie obrazu, ak sú k dispozícii. To môže vytvoriť presnejšie farby pre niektoré obrázky, ale kvalita náhľadu závisí od fotoaparátu a obrázok môže mať viac kompresných artefaktov.",
@@ -52,11 +46,11 @@
"image_preview_format": "", "image_preview_format": "",
"image_preview_resolution": "", "image_preview_resolution": "",
"image_preview_resolution_description": "", "image_preview_resolution_description": "",
"image_quality": "Kvalita", "image_quality": "",
"image_quality_description": "", "image_quality_description": "",
"image_settings": "", "image_settings": "",
"image_settings_description": "", "image_settings_description": "",
"image_thumbnail_format": "Formát náhľadu", "image_thumbnail_format": "",
"image_thumbnail_resolution": "", "image_thumbnail_resolution": "",
"image_thumbnail_resolution_description": "", "image_thumbnail_resolution_description": "",
"job_settings": "", "job_settings": "",
-8
View File
@@ -723,19 +723,12 @@
"group_year": "Grupirajte po godini", "group_year": "Grupirajte po godini",
"has_quota": "Ima kvotu", "has_quota": "Ima kvotu",
"hi_user": "Zdravo {name} ({email})", "hi_user": "Zdravo {name} ({email})",
"hide_all_people": "Sakrij sve osobe",
"hide_gallery": "Sakrij galeriju", "hide_gallery": "Sakrij galeriju",
"hide_named_person": "Sakrij osobu {name}",
"hide_password": "Sakrij lozinku", "hide_password": "Sakrij lozinku",
"hide_person": "Sakrij osobu", "hide_person": "Sakrij osobu",
"hide_unnamed_people": "Sakrij neimenovane osobe",
"host": "Domaćin (Host)", "host": "Domaćin (Host)",
"hour": "Sat", "hour": "Sat",
"image": "Fotografija", "image": "Fotografija",
"image_alt_text_date": "{date}",
"image_alt_text_people": "{count, plural, =1 {sa {person1}} =2 {sa {person1} i {person2}} =3 {sa {person1}, {person2}, i {person3}} other {sa {person1}, {person2}, i {others, number} others}}",
"image_alt_text_place": "u {city}, {country}",
"image_taken": "{isVideo, select, true {Video zapis snimljen} other {Fotografija uslikana}}",
"img": "", "img": "",
"immich_logo": "Logo Immich-a", "immich_logo": "Logo Immich-a",
"immich_web_interface": "Web interfejs Immich-a", "immich_web_interface": "Web interfejs Immich-a",
@@ -1064,7 +1057,6 @@
"sharing_sidebar_description": "Prikažite vezu do Deljenja na bočnoj traci", "sharing_sidebar_description": "Prikažite vezu do Deljenja na bočnoj traci",
"shift_to_permanent_delete": "pritisnite ⇧ da trajno izbrišete datoteku", "shift_to_permanent_delete": "pritisnite ⇧ da trajno izbrišete datoteku",
"show_album_options": "Prikaži opcije albuma", "show_album_options": "Prikaži opcije albuma",
"show_all_people": "Pokaži sve osobe",
"show_and_hide_people": "Otkrij i sakrij osobe", "show_and_hide_people": "Otkrij i sakrij osobe",
"show_file_location": "Prikaži lokaciju datoteke", "show_file_location": "Prikaži lokaciju datoteke",
"show_gallery": "Prikaži galeriju", "show_gallery": "Prikaži galeriju",
-2
View File
@@ -484,7 +484,6 @@
"expire_after": "Går ut efter", "expire_after": "Går ut efter",
"expired": "Gått ut", "expired": "Gått ut",
"explore": "Utforska", "explore": "Utforska",
"export_as_json": "Exportera som JSON",
"extension": "", "extension": "",
"external_libraries": "", "external_libraries": "",
"failed_to_get_people": "", "failed_to_get_people": "",
@@ -519,7 +518,6 @@
"image": "Bild", "image": "Bild",
"img": "", "img": "",
"immich_logo": "Immich Logo", "immich_logo": "Immich Logo",
"import_from_json": "Importera från JSON",
"import_path": "Importsökväg", "import_path": "Importsökväg",
"in_archive": "", "in_archive": "",
"include_archived": "Inkludera arkiverade", "include_archived": "Inkludera arkiverade",
+4 -8
View File
@@ -159,7 +159,7 @@
"notification_email_username_description": "Ім'я користувача для автентифікації на поштовому сервері", "notification_email_username_description": "Ім'я користувача для автентифікації на поштовому сервері",
"notification_enable_email_notifications": "Увімкнути сповіщення електронною поштою", "notification_enable_email_notifications": "Увімкнути сповіщення електронною поштою",
"notification_settings": "Налаштування сповіщень", "notification_settings": "Налаштування сповіщень",
"notification_settings_description": "Управління налаштуваннями сповіщень, включно із електронною поштою", "notification_settings_description": "Управління налаштуваннями сповіщень, включаючи електронну пошту",
"oauth_auto_launch": "Автозапуск", "oauth_auto_launch": "Автозапуск",
"oauth_auto_launch_description": "Автоматично запускати процес входу через OAuth при переході на сторінку входу", "oauth_auto_launch_description": "Автоматично запускати процес входу через OAuth при переході на сторінку входу",
"oauth_auto_register": "Автоматична реєстрація", "oauth_auto_register": "Автоматична реєстрація",
@@ -260,7 +260,7 @@
"transcoding_hardware_acceleration": "Апаратне прискорення", "transcoding_hardware_acceleration": "Апаратне прискорення",
"transcoding_hardware_acceleration_description": "Експериментальний режим: значно швидший, але при однаковому бітрейті може мати меншу якість", "transcoding_hardware_acceleration_description": "Експериментальний режим: значно швидший, але при однаковому бітрейті може мати меншу якість",
"transcoding_hardware_decoding": "Апаратне декодування", "transcoding_hardware_decoding": "Апаратне декодування",
"transcoding_hardware_decoding_setting_description": "Застосовується тільки до NVENC, QSV і RKMPP. Вмикає наскрізне прискорення на заміну лише прискорення кодування. Може працювати не з усіма відео.", "transcoding_hardware_decoding_setting_description": "Застосовується тільки до NVENC, QSV і RKMPP. Увімкнення повного прискорення замість лише прискорення кодування. Може не працювати з усіма відео.",
"transcoding_hevc_codec": "Кодек HEVC", "transcoding_hevc_codec": "Кодек HEVC",
"transcoding_max_b_frames": "Максимальна кількість проміжних кадрів", "transcoding_max_b_frames": "Максимальна кількість проміжних кадрів",
"transcoding_max_b_frames_description": "Вищі значення покращують ефективність стиснення, але збільшують час кодування. Можуть бути несумісні з апаратним прискоренням на старих пристроях. Значення 0 вимикає B-фрейми, а -1 автоматично налаштовує це значення.", "transcoding_max_b_frames_description": "Вищі значення покращують ефективність стиснення, але збільшують час кодування. Можуть бути несумісні з апаратним прискоренням на старих пристроях. Значення 0 вимикає B-фрейми, а -1 автоматично налаштовує це значення.",
@@ -420,7 +420,7 @@
"change_location": "Змінити місцезнаходження", "change_location": "Змінити місцезнаходження",
"change_name": "Змінити ім'я", "change_name": "Змінити ім'я",
"change_name_successfully": "Ім'я успішно змінено", "change_name_successfully": "Ім'я успішно змінено",
"change_password": "Змінити пароль", "change_password": "Змінити Пароль",
"change_password_description": "Це або перший раз, коли ви увійшли в систему, або було зроблено запит на зміну вашого пароля. Будь ласка, введіть новий пароль нижче.", "change_password_description": "Це або перший раз, коли ви увійшли в систему, або було зроблено запит на зміну вашого пароля. Будь ласка, введіть новий пароль нижче.",
"change_your_password": "Змініть свій пароль", "change_your_password": "Змініть свій пароль",
"changed_visibility_successfully": "Видимість успішно змінено", "changed_visibility_successfully": "Видимість успішно змінено",
@@ -722,16 +722,12 @@
"group_year": "Групувати за роками", "group_year": "Групувати за роками",
"has_quota": "Квота", "has_quota": "Квота",
"hi_user": "Привіт {name} ({email})", "hi_user": "Привіт {name} ({email})",
"hide_all_people": "Сховати всіх",
"hide_gallery": "Приховати галерею", "hide_gallery": "Приховати галерею",
"hide_named_person": "Приховати {name}",
"hide_password": "Приховати пароль", "hide_password": "Приховати пароль",
"hide_person": "Приховати людину", "hide_person": "Приховати людину",
"hide_unnamed_people": "Приховати людей без ім'я",
"host": "Хост", "host": "Хост",
"hour": "Година", "hour": "Година",
"image": "Зображення", "image": "Зображення",
"image_alt_text_place": "у {city}, {country}",
"img": "", "img": "",
"immich_logo": "Логотип Immich", "immich_logo": "Логотип Immich",
"immich_web_interface": "Веб інтерфейс Immich", "immich_web_interface": "Веб інтерфейс Immich",
@@ -930,7 +926,7 @@
"previous_memory": "Попередній спогад", "previous_memory": "Попередній спогад",
"previous_or_next_photo": "Попередня або наступна фотографія", "previous_or_next_photo": "Попередня або наступна фотографія",
"primary": "Головне", "primary": "Головне",
"profile_image_of_user": "Зображення профілю {user}", "profile_image_of_user": "Зображення профілю {title}",
"profile_picture_set": "Зображення профілю встановлено.", "profile_picture_set": "Зображення профілю встановлено.",
"public_album": "Публічний альбом", "public_album": "Публічний альбом",
"public_share": "Публічний доступ", "public_share": "Публічний доступ",
+9 -12
View File
@@ -112,10 +112,10 @@
"machine_learning_max_detection_distance_description": "Khoảng cách tối đa để hai ảnh được coi là trùng lặp, dao động từ 0,001 đến 0,1. Giá trị càng cao sẽ phát hiện được nhiều ảnh trùng lặp hơn, nhưng có thể bao gồm cả ảnh không thực sự giống nhau.", "machine_learning_max_detection_distance_description": "Khoảng cách tối đa để hai ảnh được coi là trùng lặp, dao động từ 0,001 đến 0,1. Giá trị càng cao sẽ phát hiện được nhiều ảnh trùng lặp hơn, nhưng có thể bao gồm cả ảnh không thực sự giống nhau.",
"machine_learning_max_recognition_distance": "Khoảng cách nhận dạng tối đa", "machine_learning_max_recognition_distance": "Khoảng cách nhận dạng tối đa",
"machine_learning_max_recognition_distance_description": "Khoảng cách tối đa để hai khuôn mặt được coi là cùng một người, dao động từ 0-2. Giảm giá trị này có thể ngăn chặn việc gán nhãn hai người cùng một người, trong khi tăng giá trị này có thể ngăn chặn việc gán nhãn cùng một người là hai người khác nhau. Lưu ý rằng việc gộp hai người lại với nhau dễ dàng hơn là tách một người thành hai, vì vậy hãy ưu tiên giá trị thấp khi có thể.", "machine_learning_max_recognition_distance_description": "Khoảng cách tối đa để hai khuôn mặt được coi là cùng một người, dao động từ 0-2. Giảm giá trị này có thể ngăn chặn việc gán nhãn hai người cùng một người, trong khi tăng giá trị này có thể ngăn chặn việc gán nhãn cùng một người là hai người khác nhau. Lưu ý rằng việc gộp hai người lại với nhau dễ dàng hơn là tách một người thành hai, vì vậy hãy ưu tiên giá trị thấp khi có thể.",
"machine_learning_min_detection_score": "Hệ số nhận dạng tối thiểu", "machine_learning_min_detection_score": "",
"machine_learning_min_detection_score_description": "Hệ số tự tin tối thiểu để khuôn mặt được phát hiện, từ 0 - 1. Hệ số càng thấp, nhiều khuôn mặt sẽ được nhận diện hơn nhưng có thể xảy ra sai sót.", "machine_learning_min_detection_score_description": "",
"machine_learning_min_recognized_faces": "Số khuôn mặt nhận được tối thiểu", "machine_learning_min_recognized_faces": "",
"machine_learning_min_recognized_faces_description": "Tối thiểu bao nhiêu khuôn mặt được nhận diện để tạo một người. Tăng giá trị này sẽ khiến cho Nhận diện Khuôn mặt chính xác hơn, nhưng sẽ tăng khả năng một khuôn mặt sẽ không được gán với 1 người.", "machine_learning_min_recognized_faces_description": "",
"machine_learning_settings": "Cài đặt Machine Learning", "machine_learning_settings": "Cài đặt Machine Learning",
"machine_learning_settings_description": "Quản lý các tính năng và cài đặt của machine learning", "machine_learning_settings_description": "Quản lý các tính năng và cài đặt của machine learning",
"machine_learning_smart_search": "Tìm kiếm thông minh", "machine_learning_smart_search": "Tìm kiếm thông minh",
@@ -123,20 +123,17 @@
"machine_learning_smart_search_enabled": "Bật tìm kiếm thông minh", "machine_learning_smart_search_enabled": "Bật tìm kiếm thông minh",
"machine_learning_smart_search_enabled_description": "Nếu vô hiệu hoá, hình ảnh sẽ không được mã hoá để tìm kiếm thông minh.", "machine_learning_smart_search_enabled_description": "Nếu vô hiệu hoá, hình ảnh sẽ không được mã hoá để tìm kiếm thông minh.",
"machine_learning_url_description": "Địa chỉ máy chủ machine learning", "machine_learning_url_description": "Địa chỉ máy chủ machine learning",
"manage_concurrency": "Quản lý tác vụ",
"manage_log_settings": "Quản lý cài đặt nhật ký", "manage_log_settings": "Quản lý cài đặt nhật ký",
"map_dark_style": "Giao diện tối", "map_dark_style": "Giao diện tối",
"map_enable_description": "Bật tính năng bản đồ", "map_enable_description": "Bật tính năng bản đồ",
"map_light_style": "Giao diện sáng", "map_light_style": "Giao diện sáng",
"map_manage_reverse_geocoding_settings": "Quản lý cài đặt<link>Mã hóa Địa lý Đảo ngược (Reverse Geocoding)</link>", "map_reverse_geocoding": "",
"map_reverse_geocoding": "Mã hoá Địa lý Đảo ngược", "map_reverse_geocoding_enable_description": "",
"map_reverse_geocoding_enable_description": "Bật mã hoá địa lý đảo ngược", "map_reverse_geocoding_settings": "",
"map_reverse_geocoding_settings": "Cài đặt Mã hoá Địa lý Đảo ngược",
"map_settings": "Cài đặt Bản đồ & GPS", "map_settings": "Cài đặt Bản đồ & GPS",
"map_settings_description": "Quản lý các cài đặt bản đồ", "map_settings_description": "Quản lý các cài đặt bản đồ",
"map_style_description": "Đường dẫn URL đến file tuỳ biến bản đồ style.json", "map_style_description": "",
"metadata_extraction_job": "Trích xuất metadata", "metadata_extraction_job_description": "",
"metadata_extraction_job_description": "Trích xuất metadata từ mỗi ảnh, ví dụ như GPS và kích thước",
"migration_job_description": "", "migration_job_description": "",
"note_cannot_be_changed_later": "LƯU Ý: Điều này không thể thay đổi sau này!", "note_cannot_be_changed_later": "LƯU Ý: Điều này không thể thay đổi sau này!",
"notification_email_from_address": "Địa chỉ email người gửi", "notification_email_from_address": "Địa chỉ email người gửi",
-4
View File
@@ -723,12 +723,9 @@
"group_year": "按年分组", "group_year": "按年分组",
"has_quota": "有限额", "has_quota": "有限额",
"hi_user": "你好,{name}{email}", "hi_user": "你好,{name}{email}",
"hide_all_people": "隐藏所有人物",
"hide_gallery": "隐藏相册", "hide_gallery": "隐藏相册",
"hide_named_person": "隐藏人物{name}",
"hide_password": "隐藏密码", "hide_password": "隐藏密码",
"hide_person": "隐藏人物", "hide_person": "隐藏人物",
"hide_unnamed_people": "隐藏未命名的人物",
"host": "主机", "host": "主机",
"hour": "时", "hour": "时",
"image": "图片", "image": "图片",
@@ -1064,7 +1061,6 @@
"sharing_sidebar_description": "在侧边栏中显示共享链接", "sharing_sidebar_description": "在侧边栏中显示共享链接",
"shift_to_permanent_delete": "按⇧永久删除项目", "shift_to_permanent_delete": "按⇧永久删除项目",
"show_album_options": "显示相册选项", "show_album_options": "显示相册选项",
"show_all_people": "显示所有人物",
"show_and_hide_people": "显示和隐藏人物", "show_and_hide_people": "显示和隐藏人物",
"show_file_location": "显示文件位置", "show_file_location": "显示文件位置",
"show_gallery": "显示图库", "show_gallery": "显示图库",
+4 -8
View File
@@ -1,5 +1,4 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import FormatBoldMessage from '$lib/components/i18n/format-bold-message.svelte';
import { NotificationType, notificationController } from '$lib/components/shared-components/notification/notification'; import { NotificationType, notificationController } from '$lib/components/shared-components/notification/notification';
import { AppRoute } from '$lib/constants'; import { AppRoute } from '$lib/constants';
import type { AssetInteractionStore } from '$lib/stores/asset-interaction.store'; import type { AssetInteractionStore } from '$lib/stores/asset-interaction.store';
@@ -10,6 +9,7 @@ import { preferences } from '$lib/stores/user.store';
import { downloadRequest, getKey, withError } from '$lib/utils'; import { downloadRequest, getKey, withError } from '$lib/utils';
import { createAlbum } from '$lib/utils/album-utils'; import { createAlbum } from '$lib/utils/album-utils';
import { getByteUnitString } from '$lib/utils/byte-units'; import { getByteUnitString } from '$lib/utils/byte-units';
import { encodeHTMLSpecialChars } from '$lib/utils/string-utils';
import { import {
addAssetsToAlbum as addAssets, addAssetsToAlbum as addAssets,
getAssetInfo, getAssetInfo,
@@ -63,17 +63,13 @@ export const addAssetsToNewAlbum = async (albumName: string, assetIds: string[])
if (!album) { if (!album) {
return; return;
} }
const displayName = albumName ? `<b>${encodeHTMLSpecialChars(albumName)}</b>` : 'new album';
const $t = get(t); const $t = get(t);
notificationController.show({ notificationController.show({
type: NotificationType.Info, type: NotificationType.Info,
timeout: 5000, timeout: 5000,
component: { message: $t('assets_added_to_name_count', { values: { count: assetIds.length, name: displayName } }),
type: FormatBoldMessage, html: true,
props: {
key: 'assets_added_to_name_count',
values: { count: assetIds.length, name: albumName, hasName: !!albumName },
},
},
button: { button: {
text: $t('view_album'), text: $t('view_album'),
onClick() { onClick() {
+9
View File
@@ -5,3 +5,12 @@ export const removeAccents = (str: string) => {
export const normalizeSearchString = (str: string) => { export const normalizeSearchString = (str: string) => {
return removeAccents(str.toLocaleLowerCase()); return removeAccents(str.toLocaleLowerCase());
}; };
export const encodeHTMLSpecialChars = (str: string) => {
return str
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#39;');
};
@@ -29,15 +29,28 @@
import { mdiAlert, mdiContentCopy, mdiDownload, mdiUpload } from '@mdi/js'; import { mdiAlert, mdiContentCopy, mdiDownload, mdiUpload } from '@mdi/js';
import type { PageData } from './$types'; import type { PageData } from './$types';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
import type { ComponentType, SvelteComponent } from 'svelte';
import type { SettingsComponentProps } from '$lib/components/admin-page/settings/admin-settings';
export let data: PageData; export let data: PageData;
let config = data.configs; let config = data.configs;
let handleSave: (update: Partial<SystemConfigDto>) => Promise<void>; let handleSave: (update: Partial<SystemConfigDto>) => Promise<void>;
type SettingsComponent = ComponentType<SvelteComponent<SettingsComponentProps>>; type Settings =
| typeof AuthSettings
| typeof JobSettings
| typeof LibrarySettings
| typeof LoggingSettings
| typeof MachineLearningSettings
| typeof MapSettings
| typeof ServerSettings
| typeof StorageTemplateSettings
| typeof ThemeSettings
| typeof ImageSettings
| typeof TrashSettings
| typeof NewVersionCheckSettings
| typeof NotificationSettings
| typeof FFmpegSettings
| typeof UserSettings;
const downloadConfig = () => { const downloadConfig = () => {
const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' }); const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
@@ -63,97 +76,97 @@
}; };
const settings: Array<{ const settings: Array<{
component: SettingsComponent; item: Settings;
title: string; title: string;
subtitle: string; subtitle: string;
key: string; key: string;
}> = [ }> = [
{ {
component: AuthSettings, item: AuthSettings,
title: $t('admin.authentication_settings'), title: $t('admin.authentication_settings'),
subtitle: $t('admin.authentication_settings_description'), subtitle: $t('admin.authentication_settings_description'),
key: 'image', key: 'image',
}, },
{ {
component: ImageSettings, item: ImageSettings,
title: $t('admin.image_settings'), title: $t('admin.image_settings'),
subtitle: $t('admin.image_settings_description'), subtitle: $t('admin.image_settings_description'),
key: 'image', key: 'image',
}, },
{ {
component: JobSettings, item: JobSettings,
title: $t('admin.job_settings'), title: $t('admin.job_settings'),
subtitle: $t('admin.job_settings_description'), subtitle: $t('admin.job_settings_description'),
key: 'job', key: 'job',
}, },
{ {
component: LibrarySettings, item: LibrarySettings,
title: $t('admin.library_settings'), title: $t('admin.library_settings'),
subtitle: $t('admin.library_settings_description'), subtitle: $t('admin.library_settings_description'),
key: 'external-library', key: 'external-library',
}, },
{ {
component: LoggingSettings, item: LoggingSettings,
title: $t('admin.logging_settings'), title: $t('admin.logging_settings'),
subtitle: $t('admin.manage_log_settings'), subtitle: $t('admin.manage_log_settings'),
key: 'logging', key: 'logging',
}, },
{ {
component: MachineLearningSettings, item: MachineLearningSettings,
title: $t('admin.machine_learning_settings'), title: $t('admin.machine_learning_settings'),
subtitle: $t('admin.machine_learning_settings_description'), subtitle: $t('admin.machine_learning_settings_description'),
key: 'machine-learning', key: 'machine-learning',
}, },
{ {
component: MapSettings, item: MapSettings,
title: $t('admin.map_settings'), title: $t('admin.map_settings'),
subtitle: $t('admin.map_settings_description'), subtitle: $t('admin.map_settings_description'),
key: 'location', key: 'location',
}, },
{ {
component: NotificationSettings, item: NotificationSettings,
title: $t('admin.notification_settings'), title: $t('admin.notification_settings'),
subtitle: $t('admin.notification_settings_description'), subtitle: $t('admin.notification_settings_description'),
key: 'notifications', key: 'notifications',
}, },
{ {
component: ServerSettings, item: ServerSettings,
title: $t('admin.server_settings'), title: $t('admin.server_settings'),
subtitle: $t('admin.server_settings_description'), subtitle: $t('admin.server_settings_description'),
key: 'server', key: 'server',
}, },
{ {
component: StorageTemplateSettings, item: StorageTemplateSettings,
title: $t('admin.storage_template_settings'), title: $t('admin.storage_template_settings'),
subtitle: $t('admin.storage_template_settings_description'), subtitle: $t('admin.storage_template_settings_description'),
key: 'storage-template', key: 'storage-template',
}, },
{ {
component: ThemeSettings, item: ThemeSettings,
title: $t('admin.theme_settings'), title: $t('admin.theme_settings'),
subtitle: $t('admin.theme_settings_description'), subtitle: $t('admin.theme_settings_description'),
key: 'theme', key: 'theme',
}, },
{ {
component: TrashSettings, item: TrashSettings,
title: $t('admin.trash_settings'), title: $t('admin.trash_settings'),
subtitle: $t('admin.trash_settings_description'), subtitle: $t('admin.trash_settings_description'),
key: 'trash', key: 'trash',
}, },
{ {
component: UserSettings, item: UserSettings,
title: $t('admin.user_settings'), title: $t('admin.user_settings'),
subtitle: $t('admin.user_settings_description'), subtitle: $t('admin.user_settings_description'),
key: 'user-settings', key: 'user-settings',
}, },
{ {
component: NewVersionCheckSettings, item: NewVersionCheckSettings,
title: $t('admin.version_check_settings'), title: $t('admin.version_check_settings'),
subtitle: $t('admin.version_check_settings_description'), subtitle: $t('admin.version_check_settings_description'),
key: 'version-check', key: 'version-check',
}, },
{ {
component: FFmpegSettings, item: FFmpegSettings,
title: $t('admin.transcoding_settings'), title: $t('admin.transcoding_settings'),
subtitle: $t('admin.transcoding_settings_description'), subtitle: $t('admin.transcoding_settings_description'),
key: 'video-transcoding', key: 'video-transcoding',
@@ -199,11 +212,12 @@
<section id="setting-content" class="flex place-content-center sm:mx-4"> <section id="setting-content" class="flex place-content-center sm:mx-4">
<section class="w-full pb-28 sm:w-5/6 md:w-[850px]"> <section class="w-full pb-28 sm:w-5/6 md:w-[850px]">
<SettingAccordionState queryParam={QueryParameter.IS_OPEN}> <SettingAccordionState queryParam={QueryParameter.IS_OPEN}>
{#each settings as { component: Component, title, subtitle, key }} {#each settings as { item, title, subtitle, key }}
<SettingAccordion {title} {subtitle} {key}> <SettingAccordion {title} {subtitle} {key}>
<Component <svelte:component
onSave={(config) => handleSave(config)} this={item}
onReset={(options) => handleReset(options)} on:save={({ detail }) => handleSave(detail)}
on:reset={({ detail }) => handleReset(detail)}
disabled={$featureFlags.configFile} disabled={$featureFlags.configFile}
{defaultConfig} {defaultConfig}
{config} {config}