Compare commits

...

94 Commits

Author SHA1 Message Date
mertalev
3946e8c0b2 don't close client 2025-09-02 13:17:40 -04:00
mertalev
1dbac30993 update other usages 2025-09-02 13:10:27 -04:00
mertalev
831fb5a2f9 set defaults 2025-09-02 13:03:30 -04:00
mertalev
8d272f8abc init before app launch 2025-09-02 12:38:11 -04:00
mertalev
3a2b572e0b custom user agent 2025-09-02 12:38:11 -04:00
mertalev
942b27241a fix hot reload 2025-09-02 12:38:11 -04:00
mertalev
482526475b uppercase http method 2025-09-02 12:38:11 -04:00
mertalev
c4bd24277a platform clients 2025-09-02 12:38:11 -04:00
Mert
873f7921da fix(mobile): ensure current asset is set in asset viewer (#21504) 2025-09-02 11:03:44 -04:00
Mert
f06b054087 fix(mobile): decoding at higher resolution than necessary (#21503) 2025-09-02 11:03:17 -04:00
bo0tzz
0df910c0cd fix: don't run close-dupes workflow unnecessarily (#21333) 2025-09-02 16:04:07 +02:00
Derock
5b8d72e91a fix: filter null duplicate assets (#21507) 2025-09-02 01:20:01 -04:00
Yaros
e7b0a47be2 feat(mobile): copy file name by long-press (#21469) 2025-09-01 20:43:18 -05:00
Brandon Wees
60af3a4003 fix: show TabShellRoute when cold starting from deeplink (#21376) 2025-09-01 20:42:54 -05:00
Mert
6a4b6699e3 fix(mobile): increase thumbnail resolution (#21502)
increase thumbnail resolution
2025-09-01 20:19:38 -05:00
renovate[bot]
7d57fd1320 fix(deps): update machine-learning (#21296) 2025-09-01 19:44:19 -04:00
Snowknight26
bbc1c8186c fix(web): Show full date when hovering over photos date groups (#21462) 2025-08-31 16:30:35 -05:00
Brandon Wees
b76d69c0e5 fix(mobile): readonly mode disable tabs when in landscape mode (#21475)
fix: readonly mode disable bottom tabs when in landscape mode
2025-08-31 16:28:29 -05:00
Dag Stuan
fd2b7a344c fix(web): wait for image to load before playing memories. (#19757) 2025-08-31 08:50:33 -05:00
Weblate (bot)
03dafba522 chore(web): update translations (#21130)
Co-authored-by: AR7YK <jias.smarthome@gmail.com>
Co-authored-by: AbuKareem Tuffaha <abukareem.tuffaha@gmail.com>
Co-authored-by: Alberto Serluca <alberto.ser11@gmail.com>
Co-authored-by: Alfred Makne Poulsen <alfred@omj.dk>
Co-authored-by: Amir <amirikmel@gmail.com>
Co-authored-by: Aravinth <aravinth@tuta.io>
Co-authored-by: Arnau Mora <arnyminer.z@gmail.com>
Co-authored-by: Bora Atıcı <boratici.acc@gmail.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
Co-authored-by: Davide <bbrienza99@gmail.com>
Co-authored-by: Denis Pacquier <denis.pacquier@gmail.com>
Co-authored-by: DevServs <bonov@mail.ru>
Co-authored-by: Dmitry Banny <dj.icecore@gmail.com>
Co-authored-by: Dominique Bégin <dominique.begin.0@gmail.com>
Co-authored-by: FarSniper <ozmatlik@gmail.com>
Co-authored-by: Felipe Garcia <garcia.o.felipe@gmail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Florian Ostertag <florian.kuepper@gmail.com>
Co-authored-by: Gustavo de León <alfonso.gus.deleon@gmail.com>
Co-authored-by: Indrek Haav <indrek.haav@hotmail.com>
Co-authored-by: Jane <asetmalik@gmail.com>
Co-authored-by: JarodSch <jarod@e-post.me>
Co-authored-by: Jordy H <jordy@hoebergen.net>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Junghyuk Kwon <kwon@junghy.uk>
Co-authored-by: Leo Bottaro <github@leobottaro.com>
Co-authored-by: Marc Casillas <mcasillassu@gmail.com>
Co-authored-by: Martin Popovski <martinkozle@yahoo.com>
Co-authored-by: Matjaž T <matjaz@moj-svet.si>
Co-authored-by: Mikko Asikainen <mikko@asikainen.com>
Co-authored-by: Mārtiņš Bruņenieks <martinsb@gmail.com>
Co-authored-by: Nico Kaiser <nico@kaiser.me>
Co-authored-by: Olaf Nielsen <solluh@mail.de>
Co-authored-by: PontusÖsterlindh <pontus@osterlindh.com>
Co-authored-by: Sergey Katsubo <skatsubo@gmail.com>
Co-authored-by: Shihfu Juan <xlion@xlion.tw>
Co-authored-by: Sylvain Pichon <service@spichon.fr>
Co-authored-by: Taiki M <vexingly-many-mace@duck.com>
Co-authored-by: Tijs-B <tijs.bergmans@telenet.be>
Co-authored-by: Tony Kindermann <tonykindermann@gmail.com>
Co-authored-by: User 123456789 <user123456789@users.noreply.hosted.weblate.org>
Co-authored-by: Vegard Fladby <vegard@fladby.org>
Co-authored-by: Xo <xocodokie@users.noreply.hosted.weblate.org>
Co-authored-by: Yuki Ejima <ktd.gems@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: chamdim <chamdim@protonmail.com>
Co-authored-by: gablilli <gabriele.lilli0511@gmail.com>
Co-authored-by: namdomnau <namdomnau@users.noreply.hosted.weblate.org>
Co-authored-by: pyccl <changcongliang@163.com>
Co-authored-by: thestrudl <rok.vidmar1997@gmail.com>
Co-authored-by: waclaw66 <waclaw66@seznam.cz>
Co-authored-by: Максим Горпиніч <gorpinicmaksim0@gmail.com>
2025-08-31 00:52:11 +02:00
github-actions
f15376a107 chore: version v1.140.1 2025-08-30 19:13:06 +00:00
Brandon Wees
32955915dd fix: show "preparing" when sharing in beta timeline (#21390)
* fix: show "preparing" when sharing in beta timeline

* embed dialog inside of share_action_button

* dont await the share sheet so "preparing" dialog disappears once share sheet presents

this mimics old timeline behavior

* chore: lint
2025-08-30 13:51:32 -05:00
Alex
aacb27ea5f fix: network criteria for upload LivePhotos (#21386) 2025-08-30 18:45:42 +00:00
Alex
d6b8c0926f chore: post release tasks (#21385) 2025-08-30 13:45:29 -05:00
Snowknight26
225af973c1 fix(web): Prevent changing asset location triggering keyboard shortcuts (#21451)
fix(web): Prevent changing asset location triggering asset keyboard shortcuts
2025-08-30 13:39:25 -05:00
Brandon Wees
b3372064e0 fix: default zoom level when location is not set (#21428) 2025-08-30 13:33:11 -05:00
Mert
303307e1ac fix(mobile): memory lane query (#21422) 2025-08-29 19:33:58 -05:00
Aaron Liu
f75c9dfe37 fix(devcontainer): logging typo (#21415) 2025-08-29 20:54:42 +00:00
Sergey Katsubo
f5954f4c9b chore(docs): Avoid /data in external library examples (#21357)
* Avoid /data for external libraries

* Remove mention of microservice containers

* Update docs/docs/features/libraries.md

Co-authored-by: Matthew Momjian <50788000+mmomjian@users.noreply.github.com>

---------

Co-authored-by: Matthew Momjian <50788000+mmomjian@users.noreply.github.com>
2025-08-29 10:24:21 -05:00
Min Idzelis
147accd957 fix: fix docker perms for dev (#21359) 2025-08-28 22:07:29 -04:00
Mert
9487241481 fix(server): refresh faces query (#21380) 2025-08-28 20:23:40 -04:00
Sergey Katsubo
460e1d4715 fix(server): folder sort order (#21383) 2025-08-28 20:22:40 -04:00
github-actions
b6223af5ca chore: version v1.140.0 2025-08-28 18:50:45 +00:00
Sudheer Reddy Puthana
8853079c54 feat(mobile): add read only mode (#19368)
* feat(mobile): Add Kid (Readonly) Mode toggle

This commit introduces a "Kid (Readonly) Mode" feature.

- Adds a `KidModeProvider` to manage the state of Kid Mode.
- Implements a `KidModeCheckbox` widget in the app bar dialog to toggle Kid Mode.
- When Kid Mode is enabled,
  - Disables selecting the multigrid & the bottom bar
  - Removes the top bar from view

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

Reverts the changes to devtools_options.yaml file

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

refactor: replace Kid Mode with Readonly Mode

This commit replaces the "Kid Mode" feature with a more generic "Readonly Mode".

- Renamed `KidModeProvider` to `ReadonlyModeProvider`.
- Readonly Mode state is now persisted in app settings.
- Added a new app setting `allowUserAvatarOverride` to toggle read-only mode.
- Updated translations.
- Added a message in the app bar dialog indicating when read-only mode is active.

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

Address comments -

- Removes the `allowUserAvatarOverride` setting.
- Hides the bottom gallery bar when read-only mode is enabled.
- Adds an icon on the main app bar when read-only mode is enabled with a snackbar.

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

Update to snackbar

- When toggling readonly mode from either the settings or the app bar, a snackbar notification will now appear.
- The readonly mode message in the profile drawer has been restyled.
- The upload button in the app bar is now hidden when readonly mode is enabled.

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

Removes clearing of snackbar

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

Address Comments

- Consolidated snackbar messages for enabling/disabling readonly mode.
- Ensured the "Select All" icon in asset group titles is hidden in readonly mode.

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

Adds in the missing translation keys for readonly_mode

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

Fix translation

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

Fix check failure for BorderRadius

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

Changes:
- Adjusted AppBar background color in readonly mode.
- Removes cross-out pencil icon button in favor of above.
- Hides the "Edit" icon next to date/time, disable description and onTap for people and location when readonly mode is enabled.

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

Address comments from Alex

- Moved readonly mode check to `GalleryAppBar` to hide the entire `TopControlAppBar` when readonly mode is enabled.
- Changed `toggleReadonlyMode` in `ImmichAppBar` to directly toggle the state.

Signed-off-by: Sudheer Puthana <Sud-Puth@users.noreply.github.com>

migrate readonly mode to new beta timeline

remove readonly mode from legacy UI

only show readonly functionality when on beta timeline

simplify selection icon

update generated provider

chore: more formatting

* fix: bad merge

* chore: use Notifier for readonlyModeProvider

* fix: drag select now honors readonly mode

* fix: disable asset bottom sheet in readonly

* fix: disable editing user icon when in readonly

* chore: remove generated file

* fix: disable tabs instead entire tab bar

This solves the issues with the scrubber

* chore: remove unneeded import

* chore: lint

* remove unused condition in bottomsheet

---------

Co-authored-by: Brandon Wees <brandonwees@gmail.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2025-08-28 17:30:15 +00:00
Johann
662d44536e feat(web): add geolocation utility (#20758)
* feat(geolocation):  add geolocation utility

* feat(web): geolocation utility - fix code review - 1

* feat(web): geolocation utility - fix code review - 2

* chore: cleanup

* chore: feedback

* feat(web): add animation and text

animation on locations change and action text on thumbnail

* styling, messages and filtering

* selected color

* format i18n

* fix lint

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2025-08-28 16:54:11 +00:00
xCJPECKOVERx
80fa5ec198 fix(web): Slideshow fade occurs when not in slideshow (#21326)
- ensure slideshow transition only shows when both enabled and in a slideshow
2025-08-28 11:47:53 -05:00
shenlong
0df88fc22b feat: beta background sync (#21243)
* feat: ios background sync

# Conflicts:
#	mobile/ios/Runner/Info.plist

* feat: Android sync

* add local sync worker and rename stuff

* group upload notifications

* uncomment onresume beta handling

* rename methods

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2025-08-28 14:11:54 +00:00
Snowknight26
e78144ea31 fix(web): Translate confirmation modal header and action buttons (#21330)
fix(web): Translate confirmation modal
2025-08-27 22:00:50 -05:00
Mert
227789225a fix(mobile): allow gestures in asset viewer before image is loaded (#21354)
* allow gestures while loading

* disable zoom
2025-08-27 21:52:51 -05:00
renovate[bot]
1298a74230 chore(deps): pin busybox docker tag to ab33eac (#21280)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-27 21:44:19 -05:00
Yaros
a3808c26ce fix(web): middle click not working on videos (#21304)
Co-authored-by: Alex <alex.tran1502@gmail.com>
2025-08-27 21:43:39 -05:00
Yaros
e2169f5316 fix(mobile): fast animations when "disable animations" enabled (#21309)
* fix(mobile): disable animations speed android

* use animationBehavior instead of workaround
2025-08-27 21:42:38 -05:00
Alex
f65dabd43a chore: post release tasks (#21228) 2025-08-27 21:17:56 -05:00
Mert
a5841a8bf4 fix(mobile): memory lane rebuild (#21350)
* avoid unnecessary timeline rebuild

* add key

* handle disabled memories

* avoid rebuild if no memories
2025-08-27 21:16:41 -05:00
Mert
dc6ac3aaec fix(mobile): thumbnail requests not being cancelled (#21331)
* fix requests not being cancelled

* handle thumbhash
2025-08-27 17:40:45 -04:00
prajwal
ae104ad7cc fix(web): add primary text color to file upload toast (#21340)
* fix:add primary text color to file upload toast

* fix:make progress bar visible in dark mode

* fix:make it text-primary

---------

Co-authored-by: prajwal <prajwal@hopbox.in>
2025-08-27 15:51:43 -04:00
Jason Rasmussen
868d5f56e2 fix: motion video extraction race condition (#21285)
fix: motion video extraction race ccondition
2025-08-27 15:10:55 -04:00
Jason Rasmussen
88072910da feat: asset metadata (#20446) 2025-08-27 14:31:23 -04:00
Jason Rasmussen
25a94bd117 fix(web): sign up double click (#21349) 2025-08-27 14:21:34 -04:00
Jason Rasmussen
76eaee3657 fix: timeline scroll error handling (#21324) 2025-08-26 17:07:26 -05:00
Yaros
d5fec0edab fix(mobile): capitalize month & day labels in beta timeline (#21323)
fix(mobile): capitalize month & day labels
2025-08-26 21:32:40 +00:00
xCJPECKOVERx
a7821a0b79 feat(web): Album picker shortcut info (#21273)
* - add shortcut info to album modal footer

* styling

* translation

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2025-08-26 21:31:58 +00:00
xCJPECKOVERx
73e67ebfea fix(web): album multi-select filter doesn't include other selected albums (#21322)
- submit albums directly from selected ids instead of albumModalRows
2025-08-26 16:30:24 -05:00
Snowknight26
0eaa054218 feat(web): Refresh album page after sharing (#21283) 2025-08-26 16:23:21 -05:00
Alex
2024d06cb7 chore: faq commercial guidelines (#21320) 2025-08-26 20:47:42 +00:00
Yaros
204299d500 fix(mobile): user storage quota not showing (#21263) 2025-08-26 15:34:19 -05:00
Jason Rasmussen
70e59c00d5 fix: invalid storage quota with decimals (#21271) 2025-08-26 14:46:29 -04:00
Mert
5405810a38 fix(mobile): skip animation for offscreen thumbnails (#21277) 2025-08-26 11:49:20 -04:00
Mert
e67265cef2 fix(mobile): caching thumbnails to disk (#21275) 2025-08-26 11:49:12 -04:00
renovate[bot]
19c53609e1 chore(deps): update ghcr.io/immich-app/postgres:14-vectorchord0.3.0 docker digest to 7a4469b (#21286)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-26 11:07:27 +02:00
Jason Rasmussen
0d0bb0e2d9 fix(web): suppress auto-play errors (#21282) 2025-08-25 23:51:56 -04:00
Jason Rasmussen
8f1b505ba0 fix: prevent an offline asset from being used as a person feature photo (#21278) 2025-08-25 22:40:56 -04:00
Jason Rasmussen
d04675fb41 fix: dev-scripts (#21270)
fix/dev-scripts
2025-08-25 17:10:59 -04:00
Jason Rasmussen
acfd40b77a fix: album start/end dates on shared links (#21268) 2025-08-25 17:10:31 -04:00
Jason Rasmussen
840e43430c fix: docs typo (#21269) 2025-08-25 22:43:37 +02:00
Jason Rasmussen
a3e0c6cef5 fix: ignore abort errors (#21262) 2025-08-25 16:42:30 -04:00
Jason Rasmussen
63088b22e0 fix(web): handle multiple downloads in safari (#21259) 2025-08-25 12:59:59 -05:00
xCJPECKOVERx
d9d8beb92f fix(web): Duplicate arrow shortcuts go to next/previous duplicate when viewing assets (#21200)
- get assetviewer state and don't handle next/previous duplicate if isViewing
2025-08-25 13:33:48 -04:00
Jason Rasmussen
38a8a67be9 fix(web): allow numeric input fields to be zero (#21258) 2025-08-25 13:31:32 -04:00
Jason Rasmussen
7531ffcbfb refactor: service worker (#21250) 2025-08-25 11:52:57 -05:00
xCJPECKOVERx
d5f3629c49 fix(web): Album multi-select 'm' shortcut prevents typing m in title box (#21249)
change album multi-select shortcut to ctrl
2025-08-25 11:52:26 -05:00
Alex
be5b4cb1d1 chore: patch createdAt in AssetResponseDto (#21254) 2025-08-25 16:33:21 +00:00
Wingy
5fb8d651ec feat: expose createdAt in getAssetInfo (#21184)
* Expose createdAt in getAssetInfo

* Add missing createdAt fields
2025-08-25 10:27:21 -05:00
Luke Hagar
c2313f7a99 feat: add support for custom headers to TS SDK (#21205)
* Add support for custom headers

* fix: added assertNoApiKey function
2025-08-25 10:25:21 -05:00
Min Idzelis
59627e2b4c fix: devcontainer after pnpm changes (#21227) 2025-08-25 10:24:31 -05:00
gablilli
530bf059ad docs: update italian README: better wording, add some important sections, fixed links and alt texts (#21221) 2025-08-25 15:15:39 +00:00
github-actions
b44d2a241d chore: version v1.139.4 2025-08-25 02:39:18 +00:00
Vietbao Tran
1af10ded74 fix: wait for watched files to finish being written (#17100) (#21180)
This makes the external library watcher wait for files in watched directories to finish being written before queuing jobs for each file.
2025-08-24 21:33:24 -05:00
xCJPECKOVERx
3f1e11afcc chore(server): Improve add to multiple albums via bulk checks and inserts (#21052)
* - add addAssetIdsToAlbums to album repo
- update albumService to determine all albums and assets with access and coalesce into one set of album_assets to insert

* - remove hasAsset check (unnecessary)

* - lint

* - cleanup

* - remove success counts from addAssetsToAlbums results
- Fix tests

* open-api

* await album update
2025-08-24 21:33:10 -05:00
shenlong
28dce2d0df fix: use composite cache key in user circle avatar (#21220)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2025-08-24 21:32:24 -05:00
Alex
605764f226 chore: post release tasks (#21191) 2025-08-24 21:31:56 -05:00
Min Idzelis
44e1c83c84 fix: isolate docker host/container filesystem for node_modules and build output (#21167) 2025-08-24 13:09:45 -05:00
Lorenzo Farnararo
0729887c9c fix(web): handle edge cases in timeToSeconds function to prevent crashes (#21019)
Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
2025-08-23 22:42:37 +02:00
Nicholas
3bfa8b7575 fix: border around dark theme button on onboarding page (#20846)
fix border around dark theme button
2025-08-23 15:28:00 -05:00
Alex
3138048b96 fix: cannot load thumbnail from unknown content length (#21192)
* fix: cannot load thumbnail from unknown content length

* pr feedback

* pr feedback
2025-08-23 15:25:12 -05:00
github-actions
f8b41ea8aa chore: version v1.139.3 2025-08-23 16:37:46 +00:00
pojlFDlxCOvZ4Kg8y1l4
1d33ed6bed docs: update oauth.md - Authentik link leads to Page Not Found error (#21186)
Update oauth.md

Updated Authentik link
2025-08-23 16:30:41 +00:00
shenlong
2be1a58c5b fix: prefer local video if available (#21119)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2025-08-23 11:18:57 -05:00
Jason Rasmussen
03e7922589 fix: local offset hours (#21147) 2025-08-23 11:09:36 -05:00
Alex
801af34d9a fix: sync flow block oAuth login page navigation (#21187) 2025-08-23 16:09:00 +00:00
Alex
bedaa729e9 chore: post release tasks (#21140) 2025-08-23 11:06:13 -05:00
Alex
13c8a6e61d fix: parse correct metadata to userDto for SQlite store implmentation (#21154) 2025-08-23 11:02:24 -05:00
Alex
01edf6533b fix: shared album asset count query (#21157) 2025-08-23 10:46:40 -05:00
DevServices
30d0bea4df fix(web): add to multiple albums translation doesn't have plural formatting (#21087)
Co-authored-by: xCJPECKOVERx <cjpeckover@hotmail.ca>
2025-08-22 18:52:40 +02:00
252 changed files with 7652 additions and 1584 deletions

View File

@@ -5,7 +5,8 @@
"immich-server",
"redis",
"database",
"immich-machine-learning"
"immich-machine-learning",
"init"
],
"dockerComposeFile": [
"../docker/docker-compose.dev.yml",

View File

@@ -11,8 +11,22 @@ services:
- ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
- ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/data/upload
- /etc/localtime:/etc/localtime:ro
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
immich-web:
env_file: !reset []
init:
env_file: !reset []
command: sh -c 'find /data -maxdepth 1 ! -path "/data/postgres" -type d -exec chown ${UID:-1000}:${GID:-1000} {} + 2>/dev/null || true; for path in /usr/src/app/.pnpm-store /usr/src/app/server/node_modules /usr/src/app/server/dist /usr/src/app/.github/node_modules /usr/src/app/cli/node_modules /usr/src/app/docs/node_modules /usr/src/app/e2e/node_modules /usr/src/app/open-api/typescript-sdk/node_modules /usr/src/app/web/.svelte-kit /usr/src/app/web/coverage /usr/src/app/node_modules /usr/src/app/web/node_modules; do [ -e "$$path" ] && chown -R ${UID:-1000}:${GID:-1000} "$$path" || true; done'
immich-machine-learning:
env_file: !reset []
database:

View File

@@ -11,7 +11,7 @@ run_cmd pnpm --filter immich install
log "Starting Nest API Server"
log ""
cd "${IMMICH_WORKSPACE}/server" || (
log "Immich workspace not found"jj
log "Immich workspace not found"
exit 1
)

View File

@@ -8,8 +8,18 @@ name: Close likely duplicates
permissions: {}
jobs:
should_run:
runs-on: ubuntu-latest
outputs:
should_run: ${{ steps.should_run.outputs.run }}
steps:
- id: should_run
run: echo "run=${{ github.event_name == 'issues' || github.event.discussion.category.name == 'Feature Request' }}" >> $GITHUB_OUTPUT
get_body:
runs-on: ubuntu-latest
needs: should_run
if: ${{ needs.should_run.outputs.should_run == 'true' }}
env:
EVENT: ${{ toJSON(github.event) }}
outputs:
@@ -22,7 +32,8 @@ jobs:
get_checkbox_json:
runs-on: ubuntu-latest
needs: get_body
needs: [get_body, should_run]
if: ${{ needs.should_run.outputs.should_run == 'true' }}
container:
image: yshavit/mdq:0.8.0@sha256:c69224d34224a0043d9a3ee46679ba4a2a25afaac445f293d92afe13cd47fcea
outputs:
@@ -31,14 +42,15 @@ jobs:
- id: get_checkbox
env:
BODY: ${{ needs.get_body.outputs.body }}
# TODO: We should detect if the checkbox is missing entirely and also close_and_comment in that case.
run: |
JSON=$(echo "$BODY" | base64 -d | /mdq --output json '# I have searched | - [?] Yes')
echo "json=$JSON" >> $GITHUB_OUTPUT
close_and_comment:
runs-on: ubuntu-latest
needs: get_checkbox_json
if: ${{ !fromJSON(needs.get_checkbox_json.outputs.json).items[0].list[0].checked }}
needs: [get_checkbox_json, should_run]
if: ${{ needs.should_run.outputs.should_run == 'true' && !fromJSON(needs.get_checkbox_json.outputs.json).items[0].list[0].checked }}
permissions:
issues: write
discussions: write

View File

@@ -569,7 +569,8 @@ jobs:
- name: Build the app
run: pnpm --filter immich build
- name: Run API generation
run: make open-api
run: ./bin/generate-open-api.sh
working-directory: open-api
- name: Find file changes
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
id: verify-changed-files

View File

@@ -4,34 +4,13 @@ module.exports = {
if (!pkg.name) {
return pkg;
}
switch (pkg.name) {
case "exiftool-vendored":
if (pkg.optionalDependencies["exiftool-vendored.pl"]) {
// make exiftool-vendored.pl a regular dependency
pkg.dependencies["exiftool-vendored.pl"] =
pkg.optionalDependencies["exiftool-vendored.pl"];
delete pkg.optionalDependencies["exiftool-vendored.pl"];
}
break;
case "sharp":
const optionalDeps = Object.keys(pkg.optionalDependencies).filter(
(dep) => dep.startsWith("@img")
);
for (const dep of optionalDeps) {
// remove all optionalDependencies from sharp (they will be compiled from source), except:
// include the precompiled musl version of sharp, for web
// include precompiled linux-x64 version of sharp, for server (stage: web-prod)
// include precompiled linux-arm64 version of sharp, for server (stage: web-prod)
if (
dep.includes("musl") ||
dep.includes("linux-x64") ||
dep.includes("linux-arm64")
) {
continue;
}
delete pkg.optionalDependencies[dep];
}
break;
if (pkg.name === "exiftool-vendored") {
if (pkg.optionalDependencies["exiftool-vendored.pl"]) {
// make exiftool-vendored.pl a regular dependency
pkg.dependencies["exiftool-vendored.pl"] =
pkg.optionalDependencies["exiftool-vendored.pl"];
delete pkg.optionalDependencies["exiftool-vendored.pl"];
}
}
return pkg;
},

View File

@@ -1,29 +1,29 @@
dev:
dev: prepare-volumes
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans
dev-down:
docker compose -f ./docker/docker-compose.dev.yml down --remove-orphans
dev-update:
dev-update: prepare-volumes
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
dev-scale:
dev-scale: prepare-volumes
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
dev-docs:
dev-docs: prepare-volumes
npm --prefix docs run start
.PHONY: e2e
e2e:
@trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans
e2e: prepare-volumes
@trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --remove-orphans
e2e-update:
e2e-update: prepare-volumes
@trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans
e2e-down:
docker compose -f ./e2e/docker-compose.yml down --remove-orphans
prod:
prod:
@trap 'make prod-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans
prod-down:
@@ -33,16 +33,16 @@ prod-scale:
@trap 'make prod-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans
.PHONY: open-api
open-api:
open-api: prepare-volumes
cd ./open-api && bash ./bin/generate-open-api.sh
open-api-dart:
open-api-dart: prepare-volumes
cd ./open-api && bash ./bin/generate-open-api.sh dart
open-api-typescript:
open-api-typescript: prepare-volumes
cd ./open-api && bash ./bin/generate-open-api.sh typescript
sql:
sql: prepare-volumes
pnpm --filter immich run sync:sql
attach-server:
@@ -51,6 +51,47 @@ attach-server:
renovate:
LOG_LEVEL=debug npx renovate --platform=local --repository-cache=reset
# Directories that need to be created for volumes or build output
VOLUME_DIRS = \
./.pnpm-store \
./web/.svelte-kit \
./web/node_modules \
./web/coverage \
./e2e/node_modules \
./docs/node_modules \
./server/node_modules \
./open-api/typescript-sdk/node_modules \
./.github/node_modules \
./node_modules \
./cli/node_modules
# Include .env file if it exists
-include docker/.env
# Helper function to chown, on error suggest remediation and exit
define safe_chown
if chown $(2) $(or $(UID),1000):$(or $(GID),1000) "$(1)" 2>/dev/null; then \
true; \
else \
echo "Permission denied when changing owner of volumes and upload location. Try running 'sudo make prepare-volumes' first."; \
exit 1; \
fi;
endef
# create empty directories and chown
prepare-volumes:
@$(foreach dir,$(VOLUME_DIRS),mkdir -p $(dir);)
@$(foreach dir,$(VOLUME_DIRS),$(call safe_chown,$(dir),-R))
ifneq ($(UPLOAD_LOCATION),)
ifeq ($(filter /%,$(UPLOAD_LOCATION)),)
@mkdir -p "docker/$(UPLOAD_LOCATION)"
@$(call safe_chown,docker/$(UPLOAD_LOCATION),)
else
@mkdir -p "$(UPLOAD_LOCATION)"
@$(call safe_chown,$(UPLOAD_LOCATION),)
endif
endif
MODULES = e2e server web cli sdk docs .github
# directory to package name mapping function
@@ -126,8 +167,9 @@ clean:
find . -name ".svelte-kit" -type d -prune -exec rm -rf '{}' +
find . -name "coverage" -type d -prune -exec rm -rf '{}' +
find . -name ".pnpm-store" -type d -prune -exec rm -rf '{}' +
command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml rm -v -f || true
command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml rm -v -f || true
command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml down -v --remove-orphans || true
command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml down -v --remove-orphans || true
setup-server-dev: install-server
setup-web-dev: install-sdk build-sdk install-web

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "2.2.82",
"version": "2.2.86",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",

View File

@@ -32,6 +32,17 @@ services:
- ${UPLOAD_LOCATION}/photos:/data
- ${UPLOAD_LOCATION}/photos/upload:/data/upload
- /etc/localtime:/etc/localtime:ro
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
env_file:
- .env
environment:
@@ -84,6 +95,17 @@ services:
- 24678:24678
volumes:
- ..:/usr/src/app
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
ulimits:
nofile:
soft: 1048576
@@ -163,13 +185,35 @@ services:
init:
container_name: init
image: busybox
image: busybox@sha256:ab33eacc8251e3807b85bb6dba570e4698c3998eca6f0fc2ccb60575a563ea74
env_file:
- .env
user: 0:0
command: sh -c 'for path in /usr/src/app/.pnpm-store /usr/src/app/server/node_modules /usr/src/app/.github/node_modules /usr/src/app/cli/node_modules /usr/src/app/docs/node_modules /usr/src/app/e2e/node_modules /usr/src/app/open-api/typescript-sdk/node_modules /usr/src/app/web/.svelte-kit /usr/src/app/web/coverage /usr/src/app/node_modules /usr/src/app/web/node_modules; do [ -e "$$path" ] && chown -R ${UID:-1000}:${GID:-1000} "$$path" || true; done'
command: sh -c 'find /data -maxdepth 1 -type d -exec chown ${UID:-1000}:${GID:-1000} {} + 2>/dev/null || true; for path in /usr/src/app/.pnpm-store /usr/src/app/server/node_modules /usr/src/app/server/dist /usr/src/app/.github/node_modules /usr/src/app/cli/node_modules /usr/src/app/docs/node_modules /usr/src/app/e2e/node_modules /usr/src/app/open-api/typescript-sdk/node_modules /usr/src/app/web/.svelte-kit /usr/src/app/web/coverage /usr/src/app/node_modules /usr/src/app/web/node_modules; do [ -e "$$path" ] && chown -R ${UID:-1000}:${GID:-1000} "$$path" || true; done'
volumes:
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
volumes:
model-cache:
prometheus-data:
grafana-data:
pnpm-store:
server-node_modules:
web-node_modules:
github-node_modules:
cli-node_modules:
docs-node_modules:
e2e-node_modules:
sdk-node_modules:
app-node_modules:
sveltekit:
coverage:

View File

@@ -1,5 +1,31 @@
# FAQ
## Commercial Guidelines
### Are you open to commercial partnerships and collaborations?
We are working to commercialize Immich and we'd love for you to help us by making Immich better. FUTO is dedicated to developing sustainable models for developing open source software for our customers. We want our customers to be delighted by the products our engineers deliver, and we want our engineers to be paid when they succeed.
If you wish to use Immich in a commercial product not owned by FUTO, we have the following requirements:
- Plugin Integrations: Integrations for other platforms are typically approved, provided proper notification is given.
- Reseller Partnerships: Must adhere to the guidelines outlined below regarding trademark usage, and proper representation.
- Strategic Collaborations: We welcome discussions about mutually beneficial partnerships that enhance the value proposition for both organizations.
### What are your guidelines for resellers and trademark usage?
For organizations seeking to resell Immich, we have established the following guidelines to protect our brand integrity and ensure proper representation.
- We request that resellers do not display our trademarks on their websites or marketing materials. If such usage is discovered, we will contact you to request removal.
- Do not misrepresent your reseller site or services as being officially affiliated with or endorsed by Immich or our development team.
- For small resellers who wish to contribute financially to Immich's development, we recommend directing your customers to purchase licenses directy from us rather than attempting to broker revenue-sharing arrangements. We ask that you refrain from misrepresenting reseller activities as directly supporting our development work.
When in doubt or if you have an edge case scenario, we encourage you to contact us directly via email to discuss the use of our trademark. We can provide clear guidance on what is acceptable and what is not. You can reach out at: questions@immich.app
## User
### How can I reset the admin password?

View File

@@ -10,7 +10,7 @@ Unable to set `app.immich:///oauth-callback` as a valid redirect URI? See [Mobil
Immich supports 3rd party authentication via [OpenID Connect][oidc] (OIDC), an identity layer built on top of OAuth2. OIDC is supported by most identity providers, including:
- [Authentik](https://goauthentik.io/integrations/sources/oauth/#openid-connect)
- [Authentik](https://integrations.goauthentik.io/media/immich/)
- [Authelia](https://www.authelia.com/integration/openid-connect/immich/)
- [Okta](https://www.okta.com/openid-connect/)
- [Google](https://developers.google.com/identity/openid-connect/openid-connect)
@@ -88,7 +88,7 @@ The `.well-known/openid-configuration` part of the url is optional and will be a
## Auto Launch
When Auto Launch is enabled, the login page will automatically redirect the user to the OAuth authorization url, to login with OAuth. To access the login screen again, use the browser's back button, or navigate directly to `/auth/login?autoLaunch=0`.
Auto Launch can also be enabled on a per-request basis by navigating to `/auth/login?authLaunch=1`, this can be useful in situations where Immich is called from e.g. Nextcloud using the _External sites_ app and the _oidc_ app so as to enable users to directly interact with a logged-in instance of Immich.
Auto Launch can also be enabled on a per-request basis by navigating to `/auth/login?autoLaunch=1`, this can be useful in situations where Immich is called from e.g. Nextcloud using the _External sites_ app and the _oidc_ app so as to enable users to directly interact with a logged-in instance of Immich.
## Mobile Redirect URI

View File

@@ -33,7 +33,7 @@ Sometimes, an external library will not scan correctly. This can happen if Immic
- Are the permissions set correctly?
- Make sure you are using forward slashes (`/`) and not backward slashes.
To validate that Immich can reach your external library, start a shell inside the container. Run `docker exec -it immich_server bash` to a bash shell. If your import path is `/data/import/photos`, check it with `ls /data/import/photos`. Do the same check for the same in any microservices containers.
To validate that Immich can reach your external library, start a shell inside the container. Run `docker exec -it immich_server bash` to a bash shell. If your import path is `/mnt/photos`, check it with `ls /mnt/photos`. If you are using a dedicated microservices container, make sure to add the same mount point and check for availability within the microservices container as well.
### Exclusion Patterns

View File

@@ -1,4 +1,20 @@
[
{
"label": "v1.140.1",
"url": "https://v1.140.1.archive.immich.app"
},
{
"label": "v1.140.0",
"url": "https://v1.140.0.archive.immich.app"
},
{
"label": "v1.139.4",
"url": "https://v1.139.4.archive.immich.app"
},
{
"label": "v1.139.3",
"url": "https://v1.139.3.archive.immich.app"
},
{
"label": "v1.139.2",
"url": "https://v1.139.2.archive.immich.app"

View File

@@ -38,7 +38,7 @@ services:
image: redis:6.2-alpine@sha256:7fe72c486b910f6b1a9769c937dad5d63648ddee82e056f47417542dd40825bb
database:
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:0e763a2383d56f90364fcd72767ac41400cd30d2627f407f7e7960c9f1923c21
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:7a4469b9484e37bf2630a60bc2f02f086dae898143b599ecc1c93f619849ef6b
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
environment:
POSTGRES_PASSWORD: postgres

View File

@@ -1,6 +1,6 @@
{
"name": "immich-e2e",
"version": "1.139.2",
"version": "1.140.1",
"description": "",
"main": "index.js",
"type": "module",

View File

@@ -1059,6 +1059,7 @@
"filter_people": "تصفية الاشخاص",
"filter_places": "تصفية الاماكن",
"find_them_fast": "يمكنك العثور عليها بسرعة بالاسم من خلال البحث",
"first": "الاول",
"fix_incorrect_match": "إصلاح المطابقة غير الصحيحة",
"folder": "مجلد",
"folder_not_found": "لم يتم العثور على المجلد",
@@ -1180,6 +1181,7 @@
"language_search_hint": "البحث عن لغات...",
"language_setting_description": "اختر لغتك المفضلة",
"large_files": "ملفات كبيرة",
"last": "الاخير",
"last_seen": "اخر ظهور",
"latest_version": "احدث اصدار",
"latitude": "خط العرض",

View File

@@ -233,7 +233,10 @@
"asset_skipped_in_trash": "У сметніцы",
"asset_uploaded": "Запампавана",
"asset_uploading": "Запампоўванне…",
"assets_were_part_of_albums_count": "{count, plural, one {Актыў ужо быў} other {Актывы ужо былі}} часткай альбому",
"authorized_devices": "Аўтарызаваныя прылады",
"automatic_endpoint_switching_subtitle": "Падключацца лакальна па вылучаным Wi-Fi, калі гэта магчыма, і выкарыстоўваць альтэрнатыўныя падключэння ў іншых месцах",
"automatic_endpoint_switching_title": "Аўтаматычнае пераключэнне URL",
"back": "Назад",
"backup_album_selection_page_albums_device": "Альбомы на прыладзе ({count})",
"backup_all": "Усе",

View File

@@ -2,7 +2,7 @@
"about": "Quant a",
"account": "Compte",
"account_settings": "Configuració del compte",
"acknowledge": "D'acord",
"acknowledge": "Base de coneixement",
"action": "Acció",
"action_common_update": "Actualitzar",
"actions": "Accions",
@@ -28,6 +28,9 @@
"add_to_album": "Afegir a un l'àlbum",
"add_to_album_bottom_sheet_added": "Afegit a {album}",
"add_to_album_bottom_sheet_already_exists": "Ja està a {album}",
"add_to_album_toggle": "Commutar selecció de {album}",
"add_to_albums": "Afegir als àlbums",
"add_to_albums_count": "Afegir als àlbums ({count})",
"add_to_shared_album": "Afegir a un àlbum compartit",
"add_url": "Afegir URL",
"added_to_archive": "Afegit als arxivats",
@@ -497,7 +500,9 @@
"assets": "Elements",
"assets_added_count": "{count, plural, one {Afegit un element} other {Afegits # elements}}",
"assets_added_to_album_count": "{count, plural, one {Afegit un element} other {Afegits # elements}} a l'àlbum",
"assets_added_to_albums_count": "Afegits {assetTotal, plural, one {# recurs} other {# recursos}} a {albumTotal, plural, one {# album} other {# albums}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no es pot afegir a l'àlbum",
"assets_cannot_be_added_to_albums": "{count, plural, one {El recurs} other {Els recursos}} no poden ser afegits a cap dels àlbums",
"assets_count": "{count, plural, one {# recurs} other {# recursos}}",
"assets_deleted_permanently": "{count} element(s) esborrats permanentment",
"assets_deleted_permanently_from_server": "{count} element(s) esborrats permanentment del servidor d'Immich",
@@ -514,6 +519,7 @@
"assets_trashed_count": "{count, plural, one {# element enviat} other {# elements enviats}} a la paperera",
"assets_trashed_from_server": "{count} element(s) enviat a la paperera del servidor d'Immich",
"assets_were_part_of_album_count": "{count, plural, one {L'element ja és} other {Els elements ja són}} part de l'àlbum",
"assets_were_part_of_albums_count": "{count, plural, one {El recurs ja formava} other {Els recursos ja formaven}} part dels àlbums",
"authorized_devices": "Dispositius autoritzats",
"automatic_endpoint_switching_subtitle": "Connecteu-vos localment a través de la Wi-Fi designada quan estigui disponible i utilitzeu connexions alternatives en altres llocs",
"automatic_endpoint_switching_title": "Canvi automàtic d'URL",
@@ -583,9 +589,13 @@
"backup_manual_in_progress": "La pujada ja està en curs. Torneu-ho a provar més tard",
"backup_manual_success": "Èxit",
"backup_manual_title": "Estat de pujada",
"backup_options": "Opcions de Còpia de Seguretat",
"backup_options_page_title": "Opcions de còpia de seguretat",
"backup_setting_subtitle": "Gestiona la configuració de càrrega en segon pla i en primer pla",
"backup_settings_subtitle": "Administra la configuració de pujada",
"backward": "Enrere",
"beta_sync": "Estat de la sincronització beta",
"beta_sync_subtitle": "Administra el nou sistema de sincronització",
"biometric_auth_enabled": "Autentificació biomètrica activada",
"biometric_locked_out": "Esteu bloquejats fora de l'autenticació biomètrica",
"biometric_no_options": "No hi ha opcions biomètriques disponibles",
@@ -620,6 +630,7 @@
"cancel": "Cancel·la",
"cancel_search": "Cancel·la la cerca",
"canceled": "Cancel·lat",
"canceling": "Cancel·lant",
"cannot_merge_people": "No es pot fusionar gent",
"cannot_undo_this_action": "Aquesta acció no es pot desfer!",
"cannot_update_the_description": "No es pot actualitzar la descripció",
@@ -651,6 +662,7 @@
"clear": "Buida",
"clear_all": "Neteja-ho tot",
"clear_all_recent_searches": "Esborra totes les cerques recents",
"clear_file_cache": "Buida la memòria cau de fitxers",
"clear_message": "Neteja el missatge",
"clear_value": "Neteja el valor",
"client_cert_dialog_msg_confirm": "OK",
@@ -721,6 +733,7 @@
"create_new_user": "Crea un usuari nou",
"create_shared_album_page_share_add_assets": "AFEGEIX ELEMENTS",
"create_shared_album_page_share_select_photos": "Escull fotografies",
"create_shared_link": "Crea un enllaç compartit",
"create_tag": "Crear etiqueta",
"create_tag_description": "Crear una nova etiqueta. Per les etiquetes aniuades, escriu la ruta comperta de l'etiqueta, incloses les barres diagonals.",
"create_user": "Crea un usuari",
@@ -733,6 +746,7 @@
"current_server_address": "Adreça actual del servidor",
"custom_locale": "Localització personalitzada",
"custom_locale_description": "Format de dates i números segons la llengua i regió",
"custom_url": "URL personalitzada",
"daily_title_text_date": "E, dd MMM",
"daily_title_text_date_year": "E, dd MMM, yyyy",
"dark": "Fosc",
@@ -744,6 +758,7 @@
"date_of_birth_saved": "Data de naixement guardada amb èxit",
"date_range": "Interval de dates",
"day": "Dia",
"days": "Dies",
"deduplicate_all": "Desduplica-ho tot",
"deduplication_criteria_1": "Mida d'imatge en bytes",
"deduplication_criteria_2": "Quantitat de dades EXIF",
@@ -752,6 +767,7 @@
"default_locale": "Localització predeterminada",
"default_locale_description": "Format de dates i números segons la configuració del navegador",
"delete": "Esborra",
"delete_action_confirmation_message": "Segur que vols eliminar aquest recurs? Aquesta acció el mourà a la paperera del servidor, i et preguntarà si el vols eliminar localment",
"delete_action_prompt": "{count} eliminats",
"delete_album": "Esborra l'àlbum",
"delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?",
@@ -766,9 +782,12 @@
"delete_key": "Suprimeix la clau",
"delete_library": "Suprimeix la Llibreria",
"delete_link": "Esborra l'enllaç",
"delete_local_action_prompt": "{count} eliminats localment",
"delete_local_dialog_ok_backed_up_only": "Esborrar només les que tinguin còpia de seguretat",
"delete_local_dialog_ok_force": "Suprimeix de totes maneres",
"delete_others": "Suprimeix altres",
"delete_permanently": "Eliminar permanentment",
"delete_permanently_action_prompt": "{count} eliminats permanentment",
"delete_shared_link": "Odstranit sdílený odkaz",
"delete_shared_link_dialog_title": "Suprimeix l'enllaç compartit",
"delete_tag": "Eliminar etiqueta",
@@ -779,6 +798,7 @@
"description": "Descripció",
"description_input_hint_text": "Afegeix descripció...",
"description_input_submit_error": "S'ha produït un error en actualitzar la descripció, comproveu el registre per a més detalls",
"deselect_all": "Deseleccionar Tots",
"details": "Detalls",
"direction": "Direcció",
"disabled": "Desactivat",
@@ -796,6 +816,7 @@
"documentation": "Documentació",
"done": "Fet",
"download": "Descarregar",
"download_action_prompt": "Baixant {count} recursos",
"download_canceled": "Descàrrega cancel·lada",
"download_complete": "Descàrrega completada",
"download_enqueue": "Descàrrega en cua",
@@ -822,8 +843,12 @@
"edit": "Editar",
"edit_album": "Edita l'àlbum",
"edit_avatar": "Edita l'avatar",
"edit_birthday": "Editar aniversari",
"edit_date": "Edita la data",
"edit_date_and_time": "Edita data i hora",
"edit_date_and_time_action_prompt": "{count} dates i hores editades",
"edit_date_and_time_by_offset": "Canviar data mitjançant diferència",
"edit_date_and_time_by_offset_interval": "Nou rang de dates: {from}-{to}",
"edit_description": "Edita la descripció",
"edit_description_prompt": "Si us plau, selecciona una nova descripció:",
"edit_exclusion_pattern": "Edita patró d'exclusió",
@@ -852,6 +877,7 @@
"empty_trash": "Buidar la paperera",
"empty_trash_confirmation": "Esteu segur que voleu buidar la paperera? Això eliminarà tots els recursos a la paperera permanentment d'Immich.\nNo podeu desfer aquesta acció!",
"enable": "Activar",
"enable_backup": "Habilitar Còpia de Seguretat",
"enable_biometric_auth_description": "Introduïu el codi PIN per a habilitar l'autenticació biomètrica",
"enabled": "Activat",
"end_date": "Data final",
@@ -895,6 +921,7 @@
"failed_to_load_notifications": "Error en carregar les notificacions",
"failed_to_load_people": "No s'han pogut carregar les persones",
"failed_to_remove_product_key": "No s'ha pogut eliminar la clau del producte",
"failed_to_reset_pin_code": "No s'ha pogut reiniciar el codi PIN",
"failed_to_stack_assets": "No s'han pogut apilar els elements",
"failed_to_unstack_assets": "No s'han pogut desapilar els elements",
"failed_to_update_notification_status": "Error en actualitzar l'estat de les notificacions",
@@ -903,6 +930,7 @@
"paths_validation_failed": "{paths, plural, one {# ruta} other {# rutes}} no ha pogut validar",
"profile_picture_transparent_pixels": "Les fotos de perfil no poden tenir píxels transparents. Per favor, feu zoom in, mogueu la imatge o ambdues.",
"quota_higher_than_disk_size": "Heu establert una quota més gran que la mida de disc",
"something_went_wrong": "Alguna cosa ha anat malament",
"unable_to_add_album_users": "No es poden afegir usuaris a l'àlbum",
"unable_to_add_assets_to_shared_link": "No s'han pogut afegir els elements a l'enllaç compartit",
"unable_to_add_comment": "No es pot afegir el comentari",
@@ -988,6 +1016,7 @@
},
"exif": "EXIF",
"exif_bottom_sheet_description": "Afegeix descripció...",
"exif_bottom_sheet_description_error": "No s'ha pogut actualitzar la descripció",
"exif_bottom_sheet_details": "DETALLS",
"exif_bottom_sheet_location": "UBICACIÓ",
"exif_bottom_sheet_people": "PERSONES",
@@ -1005,6 +1034,8 @@
"explorer": "Explorador",
"export": "Exporta",
"export_as_json": "Exportar com a JSON",
"export_database": "Exportar base de dades",
"export_database_description": "Exportar la base de dades SQLite",
"extension": "Extensió",
"external": "Extern",
"external_libraries": "Llibreries externes",
@@ -1031,11 +1062,13 @@
"filter_people": "Filtra persones",
"filter_places": "Filtrar per llocs",
"find_them_fast": "Trobeu-los ràpidament pel nom amb la cerca",
"first": "Primer",
"fix_incorrect_match": "Corregiu la coincidència incorrecta",
"folder": "Carpeta",
"folder_not_found": "Carpeta no trobada",
"folders": "Carpetes",
"folders_feature_description": "Explorar la vista de carpetes per les fotos i vídeos del sistema d'arxius",
"forgot_pin_code_question": "Has oblidat el teu PIN?",
"forward": "Endavant",
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Aquesta funció carrega recursos externs de Google per funcionar.",
@@ -1056,6 +1089,9 @@
"haptic_feedback_switch": "Activa la resposta hàptica",
"haptic_feedback_title": "Resposta Hàptica",
"has_quota": "Quota",
"hash_asset": "Hash del recurs",
"hashed_assets": "Recursos amb hash",
"hashing": "Hashing",
"header_settings_add_header_tip": "Afegeix Capçalera",
"header_settings_field_validator_msg": "El valor no pot estar buit",
"header_settings_header_name_input": "Nom de la capçalera",
@@ -1087,7 +1123,9 @@
"home_page_upload_err_limit": "Només es poden pujar un màxim de 30 elements alhora, ometent",
"host": "Amfitrió",
"hour": "Hora",
"hours": "Hores",
"id": "ID",
"idle": "En espera",
"ignore_icloud_photos": "Ignora fotos d'iCloud",
"ignore_icloud_photos_description": "Les fotos emmagatzemades a iCloud no es penjaran al servidor Immich",
"image": "Imatge",
@@ -1145,10 +1183,13 @@
"language_no_results_title": "No s'han trobat idiomes",
"language_search_hint": "Cerca idiomes...",
"language_setting_description": "Seleccioneu el vostre idioma",
"large_files": "Fitxers Grans",
"last": "Últim",
"last_seen": "Vist per últim cop",
"latest_version": "Última versió",
"latitude": "Latitud",
"leave": "Marxar",
"leave_album": "Abandonar àlbum",
"lens_model": "Model de lents",
"let_others_respond": "Deixa que els altres responguin",
"level": "Nivell",
@@ -1160,7 +1201,9 @@
"library_page_sort_created": "Creat més recentment",
"library_page_sort_last_modified": "Darrera modificació",
"library_page_sort_title": "Títol de l'àlbum",
"licenses": "Llicències",
"light": "Llum",
"like": "M'agrada",
"like_deleted": "M'agrada suprimit",
"link_motion_video": "Enllaçar vídeo en moviment",
"link_to_oauth": "Enllaç a OAuth",
@@ -1168,7 +1211,9 @@
"list": "Llista",
"loading": "Carregant",
"loading_search_results_failed": "No s'han pogut carregar els resultats de la cerca",
"local": "Local",
"local_asset_cast_failed": "No es pot convertir un actiu que no s'ha penjat al servidor",
"local_assets": "Recursos Locals",
"local_network": "Xarxa local",
"local_network_sheet_info": "L'aplicació es connectarà al servidor mitjançant aquest URL quan utilitzeu la xarxa Wi-Fi especificada",
"location_permission": "Permís d'ubicació",
@@ -1191,7 +1236,7 @@
"login_form_back_button_text": "Enrere",
"login_form_email_hint": "elteu@correu.cat",
"login_form_endpoint_hint": "http://ip-del-servidor:port",
"login_form_endpoint_url": "URL del servidor",
"login_form_endpoint_url": "URL del punt final del servidor",
"login_form_err_http": "Especifica http:// o https://",
"login_form_err_invalid_email": "Adreça de correu electrònic no vàlida",
"login_form_err_invalid_url": "URL no vàlid",
@@ -1269,6 +1314,7 @@
"merged_people_count": "Combinades {count, plural, one {# persona} other {# persones}}",
"minimize": "Minimitza",
"minute": "Minut",
"minutes": "Minuts",
"missing": "Restants",
"model": "Model",
"month": "Mes",
@@ -1288,6 +1334,9 @@
"my_albums": "Els meus àlbums",
"name": "Nom",
"name_or_nickname": "Nom o sobrenom",
"network_requirement_photos_upload": "Fes servir dades mòbils per a còpies de seguretat de fotos",
"network_requirement_videos_upload": "Fes servir dades mòbils per a còpies de seguretat de videos",
"network_requirements_updated": "Han canviat els requeriments de xarxa, reiniciant la cua",
"networking_settings": "Xarxes",
"networking_subtitle": "Gestiona la configuració del endpoint del servidor",
"never": "Mai",
@@ -1323,6 +1372,7 @@
"no_results": "Sense resultats",
"no_results_description": "Proveu un sinònim o una paraula clau més general",
"no_shared_albums_message": "Creeu un àlbum per compartir fotos i vídeos amb persones a la vostra xarxa",
"no_uploads_in_progress": "Cap pujada en progrés",
"not_in_any_album": "En cap àlbum",
"not_selected": "No seleccionat",
"note_apply_storage_label_to_previously_uploaded assets": "Nota: per aplicar l'etiqueta d'emmagatzematge als actius penjats anteriorment, executeu el",
@@ -1338,6 +1388,7 @@
"oauth": "OAuth",
"official_immich_resources": "Recursos oficials d'Immich",
"offline": "Fora de línia",
"offset": "Diferència",
"ok": "D'acord",
"oldest_first": "El més vell primer",
"on_this_device": "En aquest dispositiu",
@@ -1360,6 +1411,7 @@
"original": "original",
"other": "Altres",
"other_devices": "Altres dispositius",
"other_entities": "Altres entitats",
"other_variables": "Altres variables",
"owned": "Propi",
"owner": "Propietari",
@@ -1414,6 +1466,9 @@
"permission_onboarding_permission_limited": "Permís limitat. Per a permetre que Immich faci còpies de seguretat i gestioni tota la col·lecció de la galeria, concediu permisos de fotos i vídeos a Configuració.",
"permission_onboarding_request": "Immich requereix permís per veure les vostres fotos i vídeos.",
"person": "Persona",
"person_age_months": "{months, plural, one {# mes} other {# mesos}} d'antiguitat",
"person_age_year_months": "1 any, {months, plural, one {# mes} other {# mesos}} d'antiguitat",
"person_age_years": "{years, plural, other {# anys}} d'antiguitat",
"person_birthdate": "Nascut a {date}",
"person_hidden": "{name}{hidden, select, true { (ocultat)} other {}}",
"photo_shared_all_users": "Sembla que has compartit les teves fotos amb tots els usuaris o no tens cap usuari amb qui compartir-les.",
@@ -1491,6 +1546,7 @@
"purchase_server_description_2": "Estat del contribuent",
"purchase_server_title": "Servidor",
"purchase_settings_server_activated": "La clau de producte del servidor la gestiona l'administrador",
"queue_status": "En cua {count}/{total}",
"rating": "Valoració",
"rating_clear": "Esborrar valoració",
"rating_count": "{count, plural, one {# estrella} other {# estrelles}}",
@@ -1519,6 +1575,8 @@
"refreshing_faces": "Refrescant cares",
"refreshing_metadata": "Actualitzant les metadades",
"regenerating_thumbnails": "Regenerant les miniatures",
"remote": "Remot",
"remote_assets": "Recursos Remots",
"remove": "Eliminar",
"remove_assets_album_confirmation": "Confirmes que vols eliminar {count, plural, one {# recurs} other {# recursos}} de l'àlbum?",
"remove_assets_shared_link_confirmation": "Esteu segur que voleu eliminar {count, plural, one {# recurs} other {# recursos}} d'aquest enllaç compartit?",
@@ -1526,6 +1584,7 @@
"remove_custom_date_range": "Elimina l'interval de dates personalitzat",
"remove_deleted_assets": "Suprimeix fitxers fora de línia",
"remove_from_album": "Treu de l'àlbum",
"remove_from_album_action_prompt": "{count} eliminats de l'àlbum",
"remove_from_favorites": "Eliminar dels preferits",
"remove_from_lock_folder_action_prompt": "{count} eliminades de la carpeta protegida",
"remove_from_locked_folder": "Elimina de la carpeta bloquejada",
@@ -1555,19 +1614,28 @@
"reset_password": "Restablir contrasenya",
"reset_people_visibility": "Restablir la visibilitat de les persones",
"reset_pin_code": "Restablir el codi PIN",
"reset_pin_code_description": "Si has oblidat el teu codi PIN, pots contactar amb l'administrador del servidor per a reiniciar-lo",
"reset_pin_code_success": "Codi PIN reiniciat correctament",
"reset_pin_code_with_password": "Sempre pots reiniciar el codi PIN amb la teva contrasenya",
"reset_sqlite": "Reiniciar base de dades SQLite",
"reset_sqlite_confirmation": "Segur que vols reiniciar la base de dades SQLite? Hauràs de tancar la sessió i tornar a accedir per a resincronitzar les dades",
"reset_sqlite_success": "S'ha reiniciat la base de dades correctament",
"reset_to_default": "Restableix els valors predeterminats",
"resolve_duplicates": "Resoldre duplicats",
"resolved_all_duplicates": "Tots els duplicats resolts",
"restore": "Recupera",
"restore_all": "Restaurar-ho tot",
"restore_trash_action_prompt": "{count} recuperats de la paperera",
"restore_user": "Restaurar l'usuari",
"restored_asset": "Element restaurat",
"resume": "Reprendre",
"retry_upload": "Torna a provar de pujar",
"review_duplicates": "Revisar duplicats",
"review_large_files": "Revisar fitxers grans",
"role": "Rol",
"role_editor": "Editor",
"role_viewer": "Visor",
"running": "En execució",
"save": "Desa",
"save_to_gallery": "Desa a galeria",
"saved_api_key": "Clau d'API guardada",
@@ -1699,6 +1767,7 @@
"settings_saved": "Configuració desada",
"setup_pin_code": "Configurar un codi PIN",
"share": "Comparteix",
"share_action_prompt": "Compartits {count} recursos",
"share_add_photos": "Afegeix fotografies",
"share_assets_selected": "{count} seleccionats",
"share_dialog_preparing": "S'està preparant...",
@@ -1720,6 +1789,7 @@
"shared_link_clipboard_copied_massage": "S'ha copiat al porta-retalls",
"shared_link_clipboard_text": "Enllaç: {link}\nContrasenya: {password}",
"shared_link_create_error": "S'ha produït un error en crear l'enllaç compartit",
"shared_link_custom_url_description": "Accedeix a aquest enllaç compartit amb una URL personalitzada",
"shared_link_edit_description_hint": "Introduïu la descripció de compartició",
"shared_link_edit_expire_after_option_day": "1 dia",
"shared_link_edit_expire_after_option_days": "{count} dies",
@@ -1745,6 +1815,7 @@
"shared_link_info_chip_metadata": "EXIF",
"shared_link_manage_links": "Gestiona els enllaços compartits",
"shared_link_options": "Opcions d'enllaços compartits",
"shared_link_password_description": "Requereix una contrasenya per accedir a aquest enllaç compartit",
"shared_links": "Enllaços compartits",
"shared_links_description": "Comparteix fotos i vídeos amb un enllaç",
"shared_photos_and_videos_count": "{assetCount, plural, other {# fotos i vídeos compartits.}}",
@@ -1794,12 +1865,14 @@
"sort_created": "Data de creació",
"sort_items": "Nombre d'elements",
"sort_modified": "Data de modificació",
"sort_newest": "Foto més nova",
"sort_oldest": "Foto més antiga",
"sort_people_by_similarity": "Ordenar personar per semblança",
"sort_recent": "Foto més recent",
"sort_title": "Títol",
"source": "Font",
"stack": "Apila",
"stack_action_prompt": "{count} apilats",
"stack_duplicates": "Aplica duplicats",
"stack_select_one_photo": "Selecciona una imatge principal per la pila",
"stack_selected_photos": "Apila les fotos seleccionades",
@@ -1819,6 +1892,7 @@
"storage_quota": "Quota d'emmagatzematge",
"storage_usage": "{used} de {available} en ús",
"submit": "Envia",
"success": "Amb èxit",
"suggestions": "Suggeriments",
"sunrise_on_the_beach": "Albada a la platja",
"support": "Suport",
@@ -1828,6 +1902,8 @@
"sync": "Sincronitza",
"sync_albums": "Sincronitzar àlbums",
"sync_albums_manual_subtitle": "Sincronitza tots els vídeos i fotos penjats amb els àlbums de còpia de seguretat seleccionats",
"sync_local": "Sincronitza Local",
"sync_remote": "Sincronitza Remot",
"sync_upload_album_setting_subtitle": "Creeu i pugeu les seves fotos i vídeos als àlbums seleccionats a Immich",
"tag": "Etiqueta",
"tag_assets": "Etiquetar actius",
@@ -1838,6 +1914,7 @@
"tag_updated": "Etiqueta actualizada: {tag}",
"tagged_assets": "{count, plural, one {#Etiquetat} other {#Etiquetats}} {count, plural, one {# actiu} other {# actius}}",
"tags": "Etiquetes",
"tap_to_run_job": "Toca per executar el treball",
"template": "Plantilla",
"theme": "Tema",
"theme_selection": "Selecció de tema",
@@ -1910,15 +1987,20 @@
"unselect_all_duplicates": "Desmarqueu tots els duplicats",
"unselect_all_in": "Desseleccionar tots els elements de {group}",
"unstack": "Desapila",
"unstack_action_prompt": "{count} sense apilar",
"unstacked_assets_count": "No apilat {count, plural, one {# recurs} other {# recursos}}",
"untagged": "Sense etiqueta",
"up_next": "Pròxim",
"updated_at": "Actualitzat",
"updated_password": "Contrasenya actualitzada",
"upload": "Pujar",
"upload_action_prompt": "{count} a la cua per a pujar",
"upload_concurrency": "Concurrència de pujades",
"upload_details": "Detalls de la Pujada",
"upload_dialog_info": "Vols fer còpia de seguretat dels elements seleccionats al servidor?",
"upload_dialog_title": "Puja elements",
"upload_errors": "Càrrega completada amb {count, plural, one {# error} other {# errors}}, actualitzeu la pàgina per veure els nous elements carregats.",
"upload_finished": "Pujada finalitzada",
"upload_progress": "Restant {remaining, number} - Processat {processed, number}/{total, number}",
"upload_skipped_duplicates": "{count, plural, one {S'ha omès # recurs duplicat} other {S'han omès # recursos duplicats}}",
"upload_status_duplicates": "Duplicats",
@@ -1927,6 +2009,7 @@
"upload_success": "Pujada correcta, actualitza la pàgina per veure nous recursos de pujada.",
"upload_to_immich": "Puja a Immich ({count})",
"uploading": "Pujant",
"uploading_media": "Pujant mitjans",
"url": "URL",
"usage": "Ús",
"use_biometric": "Empra biometria",
@@ -1947,6 +2030,7 @@
"user_usage_stats_description": "Veure les estadístiques d'ús del compte",
"username": "Nom d'usuari",
"users": "Usuaris",
"users_added_to_album_count": "{count, plural, one {S'ha afegit # usuari} other {S'han afegit # usuaris}} a l'àlbum",
"utilities": "Utilitats",
"validate": "Valida",
"validate_endpoint_error": "Per favor introdueix un URL vàlid",
@@ -1965,6 +2049,7 @@
"view_album": "Veure l'àlbum",
"view_all": "Veure tot",
"view_all_users": "Mostra tot els usuaris",
"view_details": "Veure Detalls",
"view_in_timeline": "Mostrar a la línia de temps",
"view_link": "Veure enllaç",
"view_links": "Mostra enllaços",

View File

@@ -257,7 +257,7 @@
"server_settings_description": "Správa nastavení serveru",
"server_welcome_message": "Uvítací zpráva",
"server_welcome_message_description": "Zpráva, která se zobrazí na přihlašovací stránce.",
"sidecar_job": "Sidecar metadata",
"sidecar_job": "Postranní metadata",
"sidecar_job_description": "Objevování nebo synchronizace sidecar metadat ze systému souborů",
"slideshow_duration_description": "Počet sekund pro zobrazení každého obrázku",
"smart_search_job_description": "Strojové učení na objektech pro podporu inteligentního vyhledávání",
@@ -341,11 +341,11 @@
"transcoding_settings_description": "Správa rozlišení a kódování videosouborů",
"transcoding_target_resolution": "Cílové rozlišení",
"transcoding_target_resolution_description": "Vyšší rozlišení mohou zachovat více detailů, ale jejich kódování trvá déle, mají větší velikost souboru a mohou snížit odezvu aplikace.",
"transcoding_temporal_aq": "Temporal AQ",
"transcoding_temporal_aq": "Časové AQ",
"transcoding_temporal_aq_description": "Platí pouze pro NVENC. Zvyšuje kvalitu scén s vysokým počtem detailů a malým počtem pohybů. Nemusí být kompatibilní se staršími zařízeními.",
"transcoding_threads": "Vlákna",
"transcoding_threads_description": "Vyšší hodnoty vedou k rychlejšímu kódování, ale ponechávají serveru méně prostoru pro zpracování jiných úloh. Tato hodnota by neměla být vyšší než počet jader procesoru. Maximalizuje využití, pokud je nastavena na 0.",
"transcoding_tone_mapping": "Tone-mapping",
"transcoding_tone_mapping": "Mapování tónů",
"transcoding_tone_mapping_description": "Snaží se zachovat vzhled videí HDR při převodu na SDR. Každý algoritmus dělá různé kompromisy v oblasti barev, detailů a jasu. Hable zachovává detaily, Mobius zachovává barvy a Reinhard zachovává jas.",
"transcoding_transcode_policy": "Zásady překódování",
"transcoding_transcode_policy_description": "Zásady, kdy má být video překódováno. Videa HDR budou překódována vždy (kromě případů, kdy je překódování zakázáno).",
@@ -388,7 +388,7 @@
"administration": "Administrace",
"advanced": "Pokročilé",
"advanced_settings_beta_timeline_subtitle": "Vyzkoušejte nové prostředí aplikace",
"advanced_settings_beta_timeline_title": "Beta verze časové osy",
"advanced_settings_beta_timeline_title": "Časová osa (beta)",
"advanced_settings_enable_alternate_media_filter_subtitle": "Tuto možnost použijte k filtrování médií během synchronizace na základě alternativních kritérií. Tuto možnost vyzkoušejte pouze v případě, že máte problémy s detekcí všech alb v aplikaci.",
"advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] Použít alternativní filtr pro synchronizaci alb zařízení",
"advanced_settings_log_level_title": "Úroveň protokolování: {level}",
@@ -396,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Preferovat vzdálené obrázky",
"advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, které by měl Immich odesílat s každým síťovým požadavkem",
"advanced_settings_proxy_headers_title": "Proxy hlavičky",
"advanced_settings_readonly_mode_subtitle": "Povoluje režim pouze pro čtení, ve kterém lze fotografie pouze prohlížet, ale funkce jako výběr více obrázků, sdílení, přenos, mazání jsou zakázány. Povolení/zakázání režimu pouze pro čtení pomocí avatara uživatele na hlavní obrazovce",
"advanced_settings_readonly_mode_title": "Režim pouze pro čtení",
"advanced_settings_self_signed_ssl_subtitle": "Vynechá ověření SSL certifikátu serveru. Vyžadováno pro self-signed certifikáty.",
"advanced_settings_self_signed_ssl_title": "Povolit self-signed SSL certifikáty",
"advanced_settings_sync_remote_deletions_subtitle": "Automaticky odstranit nebo obnovit položku v tomto zařízení, když je tato akce provedena na webu",
@@ -461,6 +463,7 @@
"app_bar_signout_dialog_title": "Odhlásit se",
"app_settings": "Aplikace",
"appears_in": "Vyskytuje se v",
"apply_count": "Použít ({count, number})",
"archive": "Archiv",
"archive_action_prompt": "{count} přidaných do archivu",
"archive_or_unarchive_photo": "Přidat nebo odebrat fotku z archivu",
@@ -500,7 +503,7 @@
"assets": "Položky",
"assets_added_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}}",
"assets_added_to_album_count": "Do alba {count, plural, one {byla přidána # položka} few {byly přidány # položky} other {bylo přidáno # položek}}",
"assets_added_to_albums_count": "{assetTotal, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}} do {albumTotal} alb",
"assets_added_to_albums_count": "{assetTotal, plural, one {Přidána # položka} few{Přidány # položky} other {Přidáno # položek}} do {albumTotal, plural, one {# alba} other {# alb}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Položku} other {Položky}} nelze přidat do alba",
"assets_cannot_be_added_to_albums": "{count, plural, one {Položku} other {Položky}} nelze přidat do žádného z alb",
"assets_count": "{count, plural, one {# položka} few {# položky} other {# položek}}",
@@ -1073,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Tato funkce načítá externí zdroje z Googlu, aby mohla fungovat.",
"general": "Obecné",
"geolocation_instruction_all_have_location": "Všechny položky k tomuto datu již mají údaje o poloze. Zkuste zobrazit všechny položky nebo vyberte jiné datum",
"geolocation_instruction_location": "Klikněte na položku s GPS souřadnicemi, abyste mohli použít její polohu, nebo vyberte polohu přímo z mapy",
"geolocation_instruction_no_date": "Vyberte datum, abyste mohli spravovat údaje o poloze pro fotografie a videa z daného dne",
"geolocation_instruction_no_photos": "Pro tento den nebyly nalezeny žádné fotografie ani videa. Vyberte jiné datum pro jejich zobrazení",
"get_help": "Získat pomoc",
"get_wifiname_error": "Nepodařilo se získat název Wi-Fi. Zkontrolujte, zda jste udělili potřebná oprávnění a zda jste připojeni k Wi-Fi síti",
"getting_started": "Začínáme",
"go_back": "Přejít zpět",
"go_to_folder": "Přejít do složky",
"go_to_search": "Přejít na vyhledávání",
"gps": "GPS",
"gps_missing": "Bez GPS",
"grant_permission": "Udělit oprávnění",
"group_albums_by": "Seskupit alba podle...",
"group_country": "Seskupit podle země",
@@ -1218,7 +1227,7 @@
"local_network_sheet_info": "Aplikace se při použití zadané sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL",
"location_permission": "Oprávnění polohy",
"location_permission_content": "Aby bylo možné používat funkci automatického přepínání, potřebuje Immich oprávnění k přesné poloze, aby mohl přečíst název aktuální sítě Wi-Fi",
"location_picker_choose_on_map": "Vyberte na mapě",
"location_picker_choose_on_map": "Vybrat na mapě",
"location_picker_latitude_error": "Zadejte platnou zeměpisnou šířku",
"location_picker_latitude_hint": "Zadejte vlastní zeměpisnou šířku",
"location_picker_longitude_error": "Zadejte platnou zeměpisnou délku",
@@ -1262,6 +1271,7 @@
"main_branch_warning": "Používáte vývojovou verzi; důrazně doporučujeme používat verzi z vydání!",
"main_menu": "Hlavní nabídka",
"make": "Výrobce",
"manage_geolocation": "Spravovat polohu",
"manage_shared_links": "Spravovat sdílené odkazy",
"manage_sharing_with_partners": "Správa sdílení s partnery",
"manage_the_app_settings": "Správa nastavení aplikace",
@@ -1452,7 +1462,7 @@
"permanent_deletion_warning_setting_description": "Zobrazit varování při trvalém odstranění položek",
"permanently_delete": "Trvale odstranit",
"permanently_delete_assets_count": "Trvale smazat {count, plural, one {položku} other {položky}}",
"permanently_delete_assets_prompt": "Opravdu chcete trvale smazat {count, plural, one {tuto položku} few {tyto <b>#</b> položky} other {těchto <b>#</b> položek}}? Tím {count, plural, one {ji také odstraníte z jejích} other {je také odstraníte z jejich}} alb.",
"permanently_delete_assets_prompt": "Opravdu chcete trvale smazat {count, plural, one {tento soubor?} other {tyto <b>#</b> soubory?}} Tím se také odstraní {count, plural, one {z jeho} other {z jejich}} alba.",
"permanently_deleted_asset": "Položka trvale odstraněna",
"permanently_deleted_assets_count": "{count, plural, one {Položka trvale vymazána} other {Položky trvale vymazány}}",
"permission": "Oprávnění",
@@ -1508,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "Mobilní aplikace je zastaralá. Aktualizujte ji na nejnovější verzi.",
"profile_drawer_client_server_up_to_date": "Klient a server jsou aktuální",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Režim pouze pro čtení je aktivován. Dvojitým klepnutím na ikonu avatara uživatele režim ukončíte.",
"profile_drawer_server_out_of_date_major": "Server je zastaralý. Aktualizujte na nejnovější hlavní verzi.",
"profile_drawer_server_out_of_date_minor": "Server je zastaralý. Aktualizujte je na nejnovější verzi.",
"profile_image_of_user": "Profilový obrázek uživatele {user}",
@@ -1553,6 +1564,8 @@
"rating_description": "Zobrazit EXIF hodnocení v informačním panelu",
"reaction_options": "Možnosti reakce",
"read_changelog": "Přečtěte si seznam změn",
"readonly_mode_disabled": "Režim pouze pro čtení je deaktivován",
"readonly_mode_enabled": "Režim pouze pro čtení povolen",
"reassign": "Přeřadit",
"reassigned_assets_to_existing_person": "Přeřadit {count, plural, one {# položku} few {# položky} other {# položek}} na {name, select, null {existující osobu} other {{name}}}",
"reassigned_assets_to_new_person": "{count, plural, one {Přeřazena # položka} few {Přeřazeny # položky} other {Přeřazeno # položek}} na novou osobu",
@@ -1722,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Nepodařilo se vytvořit album",
"selected": "Vybráno",
"selected_count": "{count, plural, one {# vybraný} few {# vybrané} other {# vybraných}}",
"selected_gps_coordinates": "vybrané GPS souřadnice",
"send_message": "Odeslat zprávu",
"send_welcome_email": "Poslat uvítací e-mail",
"server_endpoint": "Koncový bod serveru",
@@ -1832,8 +1846,10 @@
"shift_to_permanent_delete": "stiskněte ⇧ pro trvalé odstranění položky",
"show_album_options": "Zobrazit možnosti alba",
"show_albums": "Zobrazit alba",
"show_all_assets": "Zobrazit všechny položky",
"show_all_people": "Zobrazit všechny lidi",
"show_and_hide_people": "Zobrazit a skrýt osoby",
"show_assets_without_location": "Zobrazit položky bez polohy",
"show_file_location": "Zobrazit umístění souboru",
"show_gallery": "Zobrazit galerii",
"show_hidden_people": "Zobrazit skryté lidi",
@@ -1941,7 +1957,9 @@
"to_change_password": "Změnit heslo",
"to_favorite": "Oblíbit",
"to_login": "Přihlásit",
"to_multi_select": "na vícenásobný výběr",
"to_parent": "Přejít k rodiči",
"to_select": "vybrat",
"to_trash": "Vyhodit",
"toggle_settings": "Přepnout nastavení",
"total": "Celkem",
@@ -1991,6 +2009,7 @@
"unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položek}}",
"untagged": "Neoznačeno",
"up_next": "To je prozatím vše",
"update_location_action_prompt": "Aktualizovat polohu {count} vybraných položek pomocí:",
"updated_at": "Aktualizováno",
"updated_password": "Heslo aktualizováno",
"upload": "Nahrát",
@@ -2015,6 +2034,7 @@
"use_biometric": "Použít biometrické údaje",
"use_current_connection": "použít aktuální připojení",
"use_custom_date_range": "Použít vlastní rozsah dat",
"use_this_location": "Klikněte pro použití polohy",
"user": "Uživatel",
"user_has_been_deleted": "Tento uživatel byl smazán.",
"user_id": "ID uživatele",

View File

@@ -28,6 +28,9 @@
"add_to_album": "Tilføj til album",
"add_to_album_bottom_sheet_added": "Tilføjet til {album}",
"add_to_album_bottom_sheet_already_exists": "Allerede i {album}",
"add_to_album_toggle": "Skift selektion for {album}",
"add_to_albums": "Tilføj til albummer",
"add_to_albums_count": "Tilføj til albummer({count})",
"add_to_shared_album": "Tilføj til delt album",
"add_url": "Tilføj URL",
"added_to_archive": "Tilføjet til arkiv",
@@ -51,6 +54,7 @@
"backup_onboarding_description": "En <backblaze-link>3-2-1 backup strategy</backblaze-link> anbefales for at beskytte dine data. En altomfattende backupløsning skulle gerne have kopier af dine uploadede billeder og videoer, samt Immich databasen.",
"backup_onboarding_footer": "Referer venligst til <link>dokumentationen</link> for mere information om hvordan Immich backes op.",
"backup_onboarding_parts_title": "En 3-2-1 backup inkluderer:",
"backup_onboarding_title": "Backupper",
"backup_settings": "Database Backup-indstillinger",
"backup_settings_description": "Administrer backupindstillinger for database.",
"cleared_jobs": "Ryddet jobs til: {job}",

View File

@@ -18,7 +18,7 @@
"add_endpoint": "Endpunkt hinzufügen",
"add_exclusion_pattern": "Ausschlussmuster hinzufügen",
"add_import_path": "Importpfad hinzufügen",
"add_location": "Ort hinzufügen",
"add_location": "Standort hinzufügen",
"add_more_users": "Weitere Nutzer hinzufügen",
"add_partner": "Partner hinzufügen",
"add_path": "Pfad hinzufügen",
@@ -396,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Server-Bilder bevorzugen",
"advanced_settings_proxy_headers_subtitle": "Definiere einen Proxy-Header, den Immich bei jeder Netzwerkanfrage mitschicken soll",
"advanced_settings_proxy_headers_title": "Proxy-Headers",
"advanced_settings_readonly_mode_subtitle": "Aktiviert den schreibgeschützten Modus, in dem die Fotos nur angezeigt werden können. Funktionen wie das Auswählen mehrerer Bilder, das Teilen, das Übertragen und das Löschen sind deaktiviert. Aktivieren/Deaktiviere den schreibgeschützten Modus über den Benutzer-Avatar auf dem Hauptbildschirm",
"advanced_settings_readonly_mode_title": "Schreibgeschützter Modus",
"advanced_settings_self_signed_ssl_subtitle": "Verifizierung von SSL-Zertifikaten vom Server überspringen. Notwendig bei selbstsignierten Zertifikaten.",
"advanced_settings_self_signed_ssl_title": "Selbstsignierte SSL-Zertifikate erlauben",
"advanced_settings_sync_remote_deletions_subtitle": "Automatisches Löschen oder Wiederherstellen einer Datei auf diesem Gerät, wenn diese Aktion im Web durchgeführt wird",
@@ -461,6 +463,7 @@
"app_bar_signout_dialog_title": "Abmelden",
"app_settings": "App-Einstellungen",
"appears_in": "Erscheint in",
"apply_count": "Anwenden ({count, number})",
"archive": "Archiv",
"archive_action_prompt": "{count} zum Archiv hinzugefügt",
"archive_or_unarchive_photo": "Foto archivieren bzw. Archivierung aufheben",
@@ -500,7 +503,7 @@
"assets": "Dateien",
"assets_added_count": "{count, plural, one {# Datei} other {# Dateien}} hinzugefügt",
"assets_added_to_album_count": "{count, plural, one {# Datei} other {# Dateien}} zum Album hinzugefügt",
"assets_added_to_albums_count": "{assetTotal} Dateien zu {albumTotal} Alben hinzugefügt",
"assets_added_to_albums_count": "{assetTotal, plural, one {# Datei} other {# Dateien}} zu {albumTotal, plural, one {# Album} other {# Alben}} hinzugefügt",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Datei kann}other {Dateien können}} nicht zum Album hinzugefügt werden",
"assets_cannot_be_added_to_albums": "{count, plural, one {Datei kann} other {Dateien können}} nicht zu den Alben hinzugefügt werden",
"assets_count": "{count, plural, one {# Datei} other {# Dateien}}",
@@ -640,7 +643,7 @@
"change_description": "Beschreibung anpassen",
"change_display_order": "Anzeigereihenfolge ändern",
"change_expiration_time": "Verfallszeitpunkt ändern",
"change_location": "Ort ändern",
"change_location": "Standort ändern",
"change_name": "Name ändern",
"change_name_successfully": "Name wurde erfolgreich geändert",
"change_password": "Passwort ändern",
@@ -703,7 +706,7 @@
"control_bottom_app_bar_create_new_album": "Neues Album erstellen",
"control_bottom_app_bar_delete_from_immich": "Aus Immich löschen",
"control_bottom_app_bar_delete_from_local": "Vom Gerät löschen",
"control_bottom_app_bar_edit_location": "Ort bearbeiten",
"control_bottom_app_bar_edit_location": "Standort bearbeiten",
"control_bottom_app_bar_edit_time": "Datum und Uhrzeit bearbeiten",
"control_bottom_app_bar_share_link": "Link teilen",
"control_bottom_app_bar_share_to": "Teilen mit",
@@ -859,7 +862,7 @@
"edit_link": "Link bearbeiten",
"edit_location": "Standort bearbeiten",
"edit_location_action_prompt": "{count} Geolokationen angepasst",
"edit_location_dialog_title": "Ort bearbeiten",
"edit_location_dialog_title": "Standort bearbeiten",
"edit_name": "Name bearbeiten",
"edit_people": "Personen bearbeiten",
"edit_tag": "Tag bearbeiten",
@@ -944,7 +947,7 @@
"unable_to_change_date": "Datum kann nicht verändert werden",
"unable_to_change_description": "Ändern der Beschreibung nicht möglich",
"unable_to_change_favorite": "Es konnte der Favoritenstatus für diese Datei nicht geändert werden",
"unable_to_change_location": "Ort kann nicht verändert werden",
"unable_to_change_location": "Standort kann nicht verändert werden",
"unable_to_change_password": "Passwort konnte nicht geändert werden",
"unable_to_change_visibility": "Sichtbarkeit von {count, plural, one {einer Person} other {# Personen}} konnte nicht geändert werden",
"unable_to_complete_oauth_login": "OAuth-Anmeldung konnte nicht abgeschlossen werden",
@@ -1008,7 +1011,7 @@
"unable_to_update_album_cover": "Album-Cover konnte nicht aktualisiert werden",
"unable_to_update_album_info": "Album-Info konnte nicht aktualisiert werden",
"unable_to_update_library": "Die Bibliothek konnte nicht aktualisiert werden",
"unable_to_update_location": "Der Ort konnte nicht aktualisiert werden",
"unable_to_update_location": "Der Standort konnte nicht aktualisiert werden",
"unable_to_update_settings": "Die Einstellungen konnten nicht aktualisiert werden",
"unable_to_update_timeline_display_status": "Status der Zeitleistenanzeige konnte nicht aktualisiert werden",
"unable_to_update_user": "Der Nutzer konnte nicht aktualisiert werden",
@@ -1073,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Diese Funktion lädt externe Quellen von Google, um zu funktionieren.",
"general": "Allgemein",
"geolocation_instruction_all_have_location": "Alle Dateien für dieses Daten enthalten bereits Standortangaben. Versuche alle Dateien anzuzeigen oder wähle ein anderes Datum",
"geolocation_instruction_location": "Klicke auf eine Datei mit GPS Koordinaten um diesen Standort zu verwenden oder wähle einen Standort direkt auf der Karte",
"geolocation_instruction_no_date": "Wähle ein Datum um die Standortangaben der Fotos und Videos dieses Datums zu verwalten",
"geolocation_instruction_no_photos": "Keine Fotos oder Videos an diesem Datum gefunden. Wähle ein anderes Datum",
"get_help": "Hilfe erhalten",
"get_wifiname_error": "WLAN-Name konnte nicht ermittelt werden. Vergewissere dich, dass die erforderlichen Berechtigungen erteilt wurden und du mit einem WLAN-Netzwerk verbunden bist",
"getting_started": "Erste Schritte",
"go_back": "Zurück",
"go_to_folder": "Gehe zu Ordner",
"go_to_search": "Zur Suche gehen",
"gps": "GPS",
"gps_missing": "Kein GPS",
"grant_permission": "Erlaubnis gewähren",
"group_albums_by": "Alben gruppieren nach...",
"group_country": "Nach Land gruppieren",
@@ -1184,6 +1193,7 @@
"language_search_hint": "Sprachen durchsuchen...",
"language_setting_description": "Wähle deine bevorzugte Sprache",
"large_files": "Große Dateien",
"last": "Letzte",
"last_seen": "Zuletzt gesehen",
"latest_version": "Aktuellste Version",
"latitude": "Breitengrad",
@@ -1261,6 +1271,7 @@
"main_branch_warning": "Du benutzt eine Entwicklungsversion. Wir empfehlen dringend, eine Release-Version zu verwenden!",
"main_menu": "Hauptmenü",
"make": "Marke",
"manage_geolocation": "Standort verwalten",
"manage_shared_links": "Freigegebene Links verwalten",
"manage_sharing_with_partners": "Gemeinsame Nutzung mit Partnern verwalten",
"manage_the_app_settings": "App-Einstellungen verwalten",
@@ -1507,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.",
"profile_drawer_client_server_up_to_date": "Die App- und Server-Versionen sind aktuell",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Schreibgeschützter Modus aktiviert. Tippe zweimal auf das Benutzer-Avatar-Symbol, um den Modus zu verlassen.",
"profile_drawer_server_out_of_date_major": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Major-Version.",
"profile_drawer_server_out_of_date_minor": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.",
"profile_image_of_user": "Profilbild von {user}",
@@ -1552,6 +1564,8 @@
"rating_description": "Stellt die EXIF-Bewertung im Informationsbereich dar",
"reaction_options": "Reaktionsmöglichkeiten",
"read_changelog": "Changelog lesen",
"readonly_mode_disabled": "Schreibgeschützter Modus deaktiviert",
"readonly_mode_enabled": "Schreibgeschützter Modus aktiviert",
"reassign": "Neu zuweisen",
"reassigned_assets_to_existing_person": "{count, plural, one {# Datei wurde} other {# Dateien wurden}} {name, select, null {einer vorhandenen Person} other {{name}}} zugewiesen",
"reassigned_assets_to_new_person": "{count, plural, one {# Datei wurde} other {# Dateien wurden}} einer neuen Person zugewiesen",
@@ -1721,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Album konnte nicht erstellt werden",
"selected": "Ausgewählt",
"selected_count": "{count, plural, other {# ausgewählt}}",
"selected_gps_coordinates": "Ausgewählte GPS Koordinaten",
"send_message": "Nachricht senden",
"send_welcome_email": "Begrüssungsmail senden",
"server_endpoint": "Server-Endpunkt",
@@ -1831,8 +1846,10 @@
"shift_to_permanent_delete": "Drücke ⇧, um die Datei endgültig zu löschen",
"show_album_options": "Album-Optionen anzeigen",
"show_albums": "Alben anzeigen",
"show_all_assets": "Alle Dateien anzeigen",
"show_all_people": "Alle Personen anzeigen",
"show_and_hide_people": "Personen ein- & ausblenden",
"show_assets_without_location": "Zeige Dateien ohne Ortsangabe",
"show_file_location": "Dateispeicherort anzeigen",
"show_gallery": "Galerie anzeigen",
"show_hidden_people": "Ausgeblendete Personen anzeigen",
@@ -1940,7 +1957,9 @@
"to_change_password": "Passwort ändern",
"to_favorite": "Zu Favoriten hinzufügen",
"to_login": "Anmelden",
"to_multi_select": "zur Mehrfachauswahl",
"to_parent": "Gehe zum Übergeordneten",
"to_select": "zum Auswählen",
"to_trash": "In den Papierkorb verschieben",
"toggle_settings": "Einstellungen umschalten",
"total": "Gesamt",
@@ -1990,6 +2009,7 @@
"unstacked_assets_count": "{count, plural, one {# Datei} other {# Dateien}} entstapelt",
"untagged": "Ohne Tag",
"up_next": "Weiter",
"update_location_action_prompt": "Aktualsiere den Ort von {count} ausgewählten Dateien mit:",
"updated_at": "Aktualisiert",
"updated_password": "Passwort aktualisiert",
"upload": "Hochladen",
@@ -2014,6 +2034,7 @@
"use_biometric": "Biometrie verwenden",
"use_current_connection": "aktuelle Verbindung verwenden",
"use_custom_date_range": "Stattdessen einen benutzerdefinierten Datumsbereich verwenden",
"use_this_location": "Klicken um Ort zu verwenden",
"user": "Nutzer",
"user_has_been_deleted": "Dieser Benutzer wurde gelöscht.",
"user_id": "Nutzer-ID",

View File

@@ -500,7 +500,7 @@
"assets": "Αντικείμενα",
"assets_added_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}}",
"assets_added_to_album_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο άλμπουμ",
"assets_added_to_albums_count": "Προστέθηκε {assetTotal, plural, one {# στοιχείο} other {# στοιχεία}} στα {albumTotal} άλμπουμ",
"assets_added_to_albums_count": "Προστέθηκαν {assetTotal, plural, one {# αρχείο} other {# αρχεία}} σε {albumTotal, plural, one {# άλμπουμ} other {# άλμπουμ}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Στοιχείο} other {Στοιχεία}} δεν μπορούν να προστεθούν στο άλμπουμ",
"assets_cannot_be_added_to_albums": "Δεν μπορεί να προστεθεί κανένα {count, plural, one {στοιχείο} other {στοιχεία}} σε κανένα από τα άλμπουμ",
"assets_count": "{count, plural, one {# αρχείο} other {# αρχεία}}",

View File

@@ -396,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Prefer remote images",
"advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request",
"advanced_settings_proxy_headers_title": "Proxy Headers",
"advanced_settings_readonly_mode_subtitle": "Enables the read-only mode where the photos can be only viewed, things like selecting multiple images, sharing, casting, delete are all disabled. Enable/Disable read-only via user avatar from the main screen",
"advanced_settings_readonly_mode_title": "Read-only Mode",
"advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.",
"advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates",
"advanced_settings_sync_remote_deletions_subtitle": "Automatically delete or restore an asset on this device when that action is taken on the web",
@@ -461,6 +463,7 @@
"app_bar_signout_dialog_title": "Sign out",
"app_settings": "App Settings",
"appears_in": "Appears in",
"apply_count": "Apply ({count, number})",
"archive": "Archive",
"archive_action_prompt": "{count} added to Archive",
"archive_or_unarchive_photo": "Archive or unarchive photo",
@@ -500,7 +503,7 @@
"assets": "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_albums_count": "Added {assetTotal, plural, one {# asset} other {# assets}} to {albumTotal} albums",
"assets_added_to_albums_count": "Added {assetTotal, plural, one {# asset} other {# assets}} to {albumTotal, plural, one {# album} other {# albums}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} cannot be added to the album",
"assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} cannot be added to any of the albums",
"assets_count": "{count, plural, one {# asset} other {# assets}}",
@@ -1073,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "This feature loads external resources from Google in order to work.",
"general": "General",
"geolocation_instruction_all_have_location": "All assets for this date already have location data. Try showing all assets or select a different date",
"geolocation_instruction_location": "Click on an asset with GPS coordinates to use its location, or select a location directly from the map",
"geolocation_instruction_no_date": "Select a date to manage location data for photos and videos from that day",
"geolocation_instruction_no_photos": "No photos or videos found for this date. Select a different date to show them",
"get_help": "Get Help",
"get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network",
"getting_started": "Getting Started",
"go_back": "Go back",
"go_to_folder": "Go to folder",
"go_to_search": "Go to search",
"gps": "GPS",
"gps_missing": "No GPS",
"grant_permission": "Grant permission",
"group_albums_by": "Group albums by...",
"group_country": "Group by country",
@@ -1262,6 +1271,7 @@
"main_branch_warning": "You're using a development version; we strongly recommend using a release version!",
"main_menu": "Main menu",
"make": "Make",
"manage_geolocation": "Manage location",
"manage_shared_links": "Manage shared links",
"manage_sharing_with_partners": "Manage sharing with partners",
"manage_the_app_settings": "Manage the app settings",
@@ -1508,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.",
"profile_drawer_client_server_up_to_date": "Client and Server are up-to-date",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Read-only mode enabled. Double-tap the user avatar icon to exit.",
"profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.",
"profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.",
"profile_image_of_user": "Profile image of {user}",
@@ -1553,6 +1564,8 @@
"rating_description": "Display the EXIF rating in the info panel",
"reaction_options": "Reaction options",
"read_changelog": "Read Changelog",
"readonly_mode_disabled": "Read-only mode disabled",
"readonly_mode_enabled": "Read-only mode enabled",
"reassign": "Reassign",
"reassigned_assets_to_existing_person": "Re-assigned {count, plural, one {# asset} other {# assets}} to {name, select, null {an existing person} other {{name}}}",
"reassigned_assets_to_new_person": "Re-assigned {count, plural, one {# asset} other {# assets}} to a new person",
@@ -1722,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Failed to create album",
"selected": "Selected",
"selected_count": "{count, plural, other {# selected}}",
"selected_gps_coordinates": "selected gps coordinates",
"send_message": "Send message",
"send_welcome_email": "Send welcome email",
"server_endpoint": "Server Endpoint",
@@ -1832,8 +1846,10 @@
"shift_to_permanent_delete": "press ⇧ to permanently delete asset",
"show_album_options": "Show album options",
"show_albums": "Show albums",
"show_all_assets": "Show all assets",
"show_all_people": "Show all people",
"show_and_hide_people": "Show & hide people",
"show_assets_without_location": "Show assets without location",
"show_file_location": "Show file location",
"show_gallery": "Show gallery",
"show_hidden_people": "Show hidden people",
@@ -1941,7 +1957,9 @@
"to_change_password": "Change password",
"to_favorite": "Favorite",
"to_login": "Login",
"to_multi_select": "to multi-select",
"to_parent": "Go to parent",
"to_select": "to select",
"to_trash": "Trash",
"toggle_settings": "Toggle settings",
"total": "Total",
@@ -1991,6 +2009,7 @@
"unstacked_assets_count": "Un-stacked {count, plural, one {# asset} other {# assets}}",
"untagged": "Untagged",
"up_next": "Up next",
"update_location_action_prompt": "Update the location of {count} selected assets with:",
"updated_at": "Updated",
"updated_password": "Updated password",
"upload": "Upload",
@@ -2015,6 +2034,7 @@
"use_biometric": "Use biometric",
"use_current_connection": "use current connection",
"use_custom_date_range": "Use custom date range instead",
"use_this_location": "Click to use location",
"user": "User",
"user_has_been_deleted": "This user has been deleted.",
"user_id": "User ID",

View File

@@ -28,6 +28,9 @@
"add_to_album": "Incluir en álbum",
"add_to_album_bottom_sheet_added": "Agregado a {album}",
"add_to_album_bottom_sheet_already_exists": "Ya se encuentra en {album}",
"add_to_album_toggle": "Alternar selección para el {album}",
"add_to_albums": "Incluir en álbumes",
"add_to_albums_count": "Incluir en {count} álbumes",
"add_to_shared_album": "Incluir en álbum compartido",
"add_url": "Agregar URL",
"added_to_archive": "Agregado al Archivado",
@@ -393,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Preferir imágenes remotas",
"advanced_settings_proxy_headers_subtitle": "Configura headers HTTP que Immich incluirá en cada petición de red",
"advanced_settings_proxy_headers_title": "Cabeceras Proxy",
"advanced_settings_readonly_mode_subtitle": "Habilita el modo de solo lectura donde las fotografías sólo pueden ser vistas, funciones como seleccionar múltiples imágenes, compartir, transmitir, eliminar son deshabilitadas. Habilita/Deshabilita solo lectura vía el avatar del usuario en la pantalla principal",
"advanced_settings_readonly_mode_title": "Modo Solo lectura",
"advanced_settings_self_signed_ssl_subtitle": "Omitir verificación del certificado SSL del servidor. Requerido para certificados autofirmados.",
"advanced_settings_self_signed_ssl_title": "Permitir certificados autofirmados",
"advanced_settings_sync_remote_deletions_subtitle": "Eliminar o restaurar automáticamente un recurso en este dispositivo cuando se realice esa acción en la web",
@@ -458,6 +463,7 @@
"app_bar_signout_dialog_title": "Cerrar sesión",
"app_settings": "Ajustes de la aplicacion",
"appears_in": "Aparece en",
"apply_count": "Aplicar ({count, number})",
"archive": "Archivo",
"archive_action_prompt": "{count} agregado(s) al archivo",
"archive_or_unarchive_photo": "Archivar o restaurar foto",
@@ -497,7 +503,9 @@
"assets": "elementos",
"assets_added_count": "{count, plural, one {# elemento agregado} other {# elementos agregados}}",
"assets_added_to_album_count": "{count, plural, one {# elemento agregado} other {# elementos agregados}} al álbum",
"assets_added_to_albums_count": "{assetTotal, plural, one {# agregado} other {# agregados}} {albumTotal, plural, one {# al álbum} other {# a los álbumes}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {El elemento no se puede agregar al álbum} other {Los elementos no se pueden agregar al álbum}}",
"assets_cannot_be_added_to_albums": "{count, plural, one {El elemento} other {Los elementos}} no se {count, plural, one {puede} other {pueden}} agregar a ninguno de los álbumes",
"assets_count": "{count, plural, one {# activo} other {# activos}}",
"assets_deleted_permanently": "{count} elemento(s) eliminado(s) permanentemente",
"assets_deleted_permanently_from_server": "{count} recurso(s) eliminado(s) de forma permanente del servidor de Immich",
@@ -514,6 +522,7 @@
"assets_trashed_count": "Borrado {count, plural, one {# elemento} other {# elementos}}",
"assets_trashed_from_server": "{count} recurso(s) enviado(s) a la papelera desde el servidor de Immich",
"assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} ya forma parte del álbum",
"assets_were_part_of_albums_count": "{count, plural, one {El elemento ya es} other {Los elementos ya son}} parte de los álbumes",
"authorized_devices": "Dispositivos Autorizados",
"automatic_endpoint_switching_subtitle": "Conectarse localmente a través de la Wi-Fi designada cuando esté disponible y usar conexiones alternativas en otros lugares",
"automatic_endpoint_switching_title": "Cambio automático de URL",
@@ -1034,7 +1043,7 @@
"external": "Externo",
"external_libraries": "Bibliotecas externas",
"external_network": "Red externa",
"external_network_sheet_info": "Cuando no tengas conexión con tu red Wi-Fi preferida, la aplicación se conectará al servidor utilizando la primera de las URL siguientes a la que pueda acceder. Las URL se probarán de arriba hacia abajo.",
"external_network_sheet_info": "Cuando no tengas conexión con tu red Wi-Fi preferida, la aplicación se conectará al servidor utilizando la primera de las URL siguientes a la que pueda acceder, empezando de arriba hacia abajo",
"face_unassigned": "Sin asignar",
"failed": "Fallido",
"failed_to_authenticate": "Fallo al autentificar",
@@ -1056,6 +1065,7 @@
"filter_people": "Filtrar personas",
"filter_places": "Filtrar lugares",
"find_them_fast": "Encuéntrelos rápidamente por nombre con la búsqueda",
"first": "Primero",
"fix_incorrect_match": "Corregir coincidencia incorrecta",
"folder": "Carpeta",
"folder_not_found": "Carpeta no encontrada",
@@ -1066,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Esta funcionalidad carga recursos externos desde Google para poder funcionar.",
"general": "General",
"geolocation_instruction_all_have_location": "Todos los assets de esta fecha ya tienen datos de ubicación. Prueba mostrando todos los assets o selecciona otra fecha",
"geolocation_instruction_location": "Da click en un asset con coordenadas GPS para usar su ubicacion, o selecciona una ubicacion directamente en el mapa",
"geolocation_instruction_no_date": "Seleccione una fecha para administrar los datos de ubicación de las fotos y los videos de ese día",
"geolocation_instruction_no_photos": "No se encontraron fotos ni vídeos para esta fecha. Seleccione otra fecha para mostrarlos",
"get_help": "Solicitar ayuda",
"get_wifiname_error": "No se pudo obtener el nombre de la red Wi-Fi. Asegúrate de haber concedido los permisos necesarios y de estar conectado a una red Wi-Fi",
"getting_started": "Comenzamos",
"go_back": "Volver atrás",
"go_to_folder": "Ir al directorio",
"go_to_search": "Ir a búsqueda",
"gps": "GPS",
"gps_missing": "Sin GPS",
"grant_permission": "Conceder permiso",
"group_albums_by": "Agrupar álbumes por...",
"group_country": "Agrupar por país",
@@ -1177,6 +1193,7 @@
"language_search_hint": "Buscar idiomas...",
"language_setting_description": "Selecciona tu idioma preferido",
"large_files": "Archivos Grandes",
"last": "Último",
"last_seen": "Ultima vez visto",
"latest_version": "Última versión",
"latitude": "Latitud",
@@ -1254,6 +1271,7 @@
"main_branch_warning": "Está utilizando una versión de desarrollo; ¡le recomendamos encarecidamente que utilice una versión de lanzamiento!",
"main_menu": "Menú principal",
"make": "Marca",
"manage_geolocation": "Administrar ubicación",
"manage_shared_links": "Administrar enlaces compartidos",
"manage_sharing_with_partners": "Gestionar el uso compartido con compañeros",
"manage_the_app_settings": "Administrar la configuración de la aplicación",
@@ -1337,7 +1355,7 @@
"new_password": "Nueva contraseña",
"new_person": "Nueva persona",
"new_pin_code": "Nuevo PIN",
"new_pin_code_subtitle": "Esta es la primera vez que accedes a la carpeta protegida. Crea un PIN seguro para acceder a esta página.",
"new_pin_code_subtitle": "Esta es la primera vez que accedes a la carpeta protegida. Crea un código PIN seguro para acceder a esta página",
"new_user_created": "Nuevo usuario creado",
"new_version_available": "NUEVA VERSIÓN DISPONIBLE",
"newest_first": "El más reciente primero",
@@ -1500,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "La app está desactualizada. Por favor actualiza a la última versión menor.",
"profile_drawer_client_server_up_to_date": "Cliente y Servidor están actualizados",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Modo Solo lectura habilitado. Toque dos veces el ícono del avatar del usuario para salir.",
"profile_drawer_server_out_of_date_major": "El servidor está desactualizado. Por favor actualiza a la última versión principal.",
"profile_drawer_server_out_of_date_minor": "El servidor está desactualizado. Por favor actualiza a la última versión menor.",
"profile_image_of_user": "Foto de perfil de {user}",
@@ -1545,6 +1564,8 @@
"rating_description": "Mostrar la clasificación exif en el panel de información",
"reaction_options": "Opciones de reacción",
"read_changelog": "Leer registro de cambios",
"readonly_mode_disabled": "Modo Solo lectura deshabilitado",
"readonly_mode_enabled": "Modo Solo lectura habilitado",
"reassign": "Reasignar",
"reassigned_assets_to_existing_person": "Reasignado {count, plural, one {# elemento} other {# elementos}} a {name, select, null {una persona existente} other {{name}}}",
"reassigned_assets_to_new_person": "Reasignado {count, plural, one {# elemento} other {# elementos}} a un nuevo usuario",
@@ -1714,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Fallo al crear el álbum",
"selected": "Seleccionado",
"selected_count": "{count, plural, one {# seleccionado} other {# seleccionados}}",
"selected_gps_coordinates": "coordenadas gps seleccionadas",
"send_message": "Enviar mensaje",
"send_welcome_email": "Enviar correo de bienvenida",
"server_endpoint": "Punto final del servidor",
@@ -1824,8 +1846,10 @@
"shift_to_permanent_delete": "presiona ⇧ para eliminar permanentemente el archivo",
"show_album_options": "Mostrar opciones del álbum",
"show_albums": "Mostrar álbumes",
"show_all_assets": "Mostrar todos los assets",
"show_all_people": "Mostrar todas las personas",
"show_and_hide_people": "Mostrar y ocultar personas",
"show_assets_without_location": "Mostrar assets sin ubicación",
"show_file_location": "Mostrar carpeta del archivo",
"show_gallery": "Ver galería",
"show_hidden_people": "Mostrar personas ocultas",
@@ -1862,7 +1886,7 @@
"sort_people_by_similarity": "Ordenar personas por similitud",
"sort_recent": "Foto más reciente",
"sort_title": "Título",
"source": "Origen",
"source": "Fuente",
"stack": "Apilar",
"stack_action_prompt": "{count} apilados",
"stack_duplicates": "Apilar duplicados",
@@ -1933,7 +1957,9 @@
"to_change_password": "Cambiar contraseña",
"to_favorite": "A los favoritos",
"to_login": "Iniciar Sesión",
"to_multi_select": "para multi selección",
"to_parent": "Ir a los padres",
"to_select": "para seleccionar",
"to_trash": "Descartar",
"toggle_settings": "Alternar ajustes",
"total": "Total",
@@ -1983,6 +2009,7 @@
"unstacked_assets_count": "Desapilado(s) {count, plural, one {# elemento} other {# elementos}}",
"untagged": "Sin etiqueta",
"up_next": "A continuación",
"update_location_action_prompt": "Actualiza la ubicación de {count} assets seleccionados con:",
"updated_at": "Actualizado",
"updated_password": "Contraseña actualizada",
"upload": "Subir",
@@ -2007,6 +2034,7 @@
"use_biometric": "Uso biométrico",
"use_current_connection": "Usar conexión actual",
"use_custom_date_range": "Usa un intervalo de fechas personalizado",
"use_this_location": "Click para usar ubicación",
"user": "Usuario",
"user_has_been_deleted": "Este usuario ha sido eliminado.",
"user_id": "Id. de usuario",
@@ -2042,7 +2070,7 @@
"view_all": "Ver todas",
"view_all_users": "Mostrar todos los usuarios",
"view_details": "Ver Detalles",
"view_in_timeline": "Mostrar en la línea de tiempo",
"view_in_timeline": "Ver en la línea de tiempo",
"view_link": "Ver enlace",
"view_links": "Mostrar enlaces",
"view_name": "Ver",

View File

@@ -396,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Eelista kaugpilte",
"advanced_settings_proxy_headers_subtitle": "Määra vaheserveri päised, mida Immich peaks iga päringuga saatma",
"advanced_settings_proxy_headers_title": "Vaheserveri päised",
"advanced_settings_readonly_mode_subtitle": "Lülitab sisse kirjutuskaitserežiimi, milles saab fotosid ainult vaadata ning toimingud nagu mitme pildi valimine, jagamine, edastamine ja kustutamine on keelatud. Lülita kirjutuskaitserežiim sisse/välja põhiekraanil oleva avatari kaudu",
"advanced_settings_readonly_mode_title": "Kirjutuskaitserežiim",
"advanced_settings_self_signed_ssl_subtitle": "Jätab serveri lõpp-punkti SSL-sertifikaadi kontrolli vahele. Nõutud endasigneeritud sertifikaatide jaoks.",
"advanced_settings_self_signed_ssl_title": "Luba endasigneeritud SSL-sertifikaadid",
"advanced_settings_sync_remote_deletions_subtitle": "Kustuta või taasta üksus selles seadmes automaatself, kui sama tegevus toimub veebis",
@@ -461,6 +463,7 @@
"app_bar_signout_dialog_title": "Logi välja",
"app_settings": "Rakenduse seaded",
"appears_in": "Albumid",
"apply_count": "Rakenda ({count, number})",
"archive": "Arhiiv",
"archive_action_prompt": "{count} lisatud arhiivi",
"archive_or_unarchive_photo": "Arhiveeri või taasta foto",
@@ -500,7 +503,7 @@
"assets": "Üksused",
"assets_added_count": "{count, plural, one {# üksus} other {# üksust}} lisatud",
"assets_added_to_album_count": "{count, plural, one {# üksus} other {# üksust}} albumisse lisatud",
"assets_added_to_albums_count": "{assetTotal, plural, one {# üksus} other {# üksust}} lisatud {albumTotal} albumisse",
"assets_added_to_albums_count": "{assetTotal, plural, one {# üksus} other {# üksust}} lisatud {albumTotal, plural, one {# albumisse} other {# albumisse}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Üksust} other {Üksuseid}} ei saa albumisse lisada",
"assets_cannot_be_added_to_albums": "{count, plural, one {Üksust} other {Üksuseid}} ei saa lisada ühtegi albumisse",
"assets_count": "{count, plural, one {# üksus} other {# üksust}}",
@@ -1073,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "See funktsionaalsus laadib töötamiseks Google'st väliseid ressursse.",
"general": "Üldine",
"geolocation_instruction_all_have_location": "Kõigil selle kuupäevaga üksustel on juba asukoht. Proovi kuvada kõiki üksuseid või vali teine kuupäev",
"geolocation_instruction_location": "Klõpsa GPS-koordinaatidega üksusel, et kasutada selle asukohta, või vali asukoht otse kaardilt",
"geolocation_instruction_no_date": "Vali kuupäev, et kõigi selle kuupäevaga fotode ja videote asukohti hallata",
"geolocation_instruction_no_photos": "Selle kuupäevaga fotosid ja videosid ei leitud. Vali mõni muu kuupäev",
"get_help": "Küsi abi",
"get_wifiname_error": "WiFi-võrgu nime ei õnnestunud lugeda. Veendu, et oled andnud vajalikud load ja oled WiFi-võrguga ühendatud",
"getting_started": "Alustamine",
"go_back": "Tagasi",
"go_to_folder": "Mine kausta",
"go_to_search": "Otsingusse",
"gps": "GPS",
"gps_missing": "GPS puudub",
"grant_permission": "Anna luba",
"group_albums_by": "Grupeeri albumid...",
"group_country": "Grupeeri riigi kaupa",
@@ -1262,6 +1271,7 @@
"main_branch_warning": "Sa kasutad arendusversiooni; soovitame tungivalt kasutada väljalaskeversiooni!",
"main_menu": "Peamenüü",
"make": "Mark",
"manage_geolocation": "Halda asukohta",
"manage_shared_links": "Halda jagatud linke",
"manage_sharing_with_partners": "Halda partneritega jagamist",
"manage_the_app_settings": "Halda rakenduse seadeid",
@@ -1508,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "Mobiilirakendus on aegunud. Palun uuenda uusimale väikesele versioonile.",
"profile_drawer_client_server_up_to_date": "Klient ja server on uuendatud",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Kirjutuskaitserežiim sisse lülitatud. Väljumiseks topeltpuuduta avatari ikooni.",
"profile_drawer_server_out_of_date_major": "Server on aegunud. Palun uuenda uusimale suurele versioonile.",
"profile_drawer_server_out_of_date_minor": "Server on aegunud. Palun uuenda uusimale väikesele versioonile.",
"profile_image_of_user": "Kasutaja {user} profiilipilt",
@@ -1553,6 +1564,8 @@
"rating_description": "Kuva infopaneelis EXIF hinnangut",
"reaction_options": "Reaktsiooni valikud",
"read_changelog": "Vaata muudatuste ülevaadet",
"readonly_mode_disabled": "Kirjutuskaitserežiim välja lülitatud",
"readonly_mode_enabled": "Kirjutuskaitserežiim sisse lülitatud",
"reassign": "Määra uuesti",
"reassigned_assets_to_existing_person": "{count, plural, one {# üksus} other {# üksust}} seostatud {name, select, null {olemasoleva isikuga} other {isikuga {name}}}",
"reassigned_assets_to_new_person": "{count, plural, one {# üksus} other {# üksust}} seostatud uue isikuga",
@@ -1722,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Albumi lisamine ebaõnnestus",
"selected": "Valitud",
"selected_count": "{count, plural, other {# valitud}}",
"selected_gps_coordinates": "valitud GPS-koordinaadid",
"send_message": "Saada sõnum",
"send_welcome_email": "Saada tervituskiri",
"server_endpoint": "Serveri lõpp-punkt",
@@ -1832,8 +1846,10 @@
"shift_to_permanent_delete": "vajuta ⇧, et üksus jäädavalt kustutada",
"show_album_options": "Näita albumi valikuid",
"show_albums": "Näita albumeid",
"show_all_assets": "Kuva kõik üksused",
"show_all_people": "Näita kõiki isikuid",
"show_and_hide_people": "Näita ja peida isikuid",
"show_assets_without_location": "Kuva ilma asukohata üksused",
"show_file_location": "Näita faili asukohta",
"show_gallery": "Näita galeriid",
"show_hidden_people": "Kuva peidetud inimesed",
@@ -1991,6 +2007,7 @@
"unstacked_assets_count": "{count, plural, one {# üksus} other {# üksust}} eraldatud",
"untagged": "Sildistamata",
"up_next": "Järgmine",
"update_location_action_prompt": "Uuenda {count} valitud üksuse asukoht:",
"updated_at": "Uuendatud",
"updated_password": "Parool muudetud",
"upload": "Laadi üles",
@@ -2015,6 +2032,7 @@
"use_biometric": "Kasuta biomeetriat",
"use_current_connection": "kasuta praegust ühendust",
"use_custom_date_range": "Kasuta kohandatud kuupäevavahemikku",
"use_this_location": "Klõpsa asukoha kasutamiseks",
"user": "Kasutaja",
"user_has_been_deleted": "See kasutaja on kustutatud.",
"user_id": "Kasutaja ID",

View File

@@ -28,6 +28,7 @@
"add_to_album": "Lisää albumiin",
"add_to_album_bottom_sheet_added": "Lisätty albumiin {album}",
"add_to_album_bottom_sheet_already_exists": "Kohde on jo albumissa {album}",
"add_to_album_toggle": "Vaihda albumin {album} valintaa",
"add_to_albums": "Lisää albumeihin",
"add_to_albums_count": "Lisää albumeihin ({count})",
"add_to_shared_album": "Lisää jaettuun albumiin",
@@ -499,7 +500,9 @@
"assets": "Kohteet",
"assets_added_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}}",
"assets_added_to_album_count": "Albumiin lisätty {count, plural, one {# kohde} other {# kohdetta}}",
"assets_added_to_albums_count": "Lisätty {assetTotal, plural, one {# aineisto} other {# aaineistoa}} {albumTotal, plural, one {# albumiin} other {# albumeihin}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Kohdetta} other {Kohdetta}} ei voida lisätä albumiin",
"assets_cannot_be_added_to_albums": "{count, plural, one {Aineisto} other {Aineistoa}} ei voi lisätä mihinkään albumiin",
"assets_count": "{count, plural, one {# media} other {# mediaa}}",
"assets_deleted_permanently": "{count} kohdetta poistettu pysyvästi",
"assets_deleted_permanently_from_server": "{count} objektia poistettu pysyvästi Immich-palvelimelta",
@@ -516,6 +519,7 @@
"assets_trashed_count": "{count, plural, one {# media} other {# mediaa}} siirretty roskakoriin",
"assets_trashed_from_server": "{count} kohdetta siirretty roskakoriin Immich-palvelimelta",
"assets_were_part_of_album_count": "{count, plural, one {Media oli} other {Mediat olivat}} jo albumissa",
"assets_were_part_of_albums_count": "{count, plural, one {Aineisto} other {Aineistoa}} oli jo albumeissa",
"authorized_devices": "Valtuutetut laitteet",
"automatic_endpoint_switching_subtitle": "Yhdistä paikallisesti nimetyn Wi-Fi-yhteyden kautta, kun se on saatavilla, ja käytä vaihtoehtoisia yhteyksiä muualla",
"automatic_endpoint_switching_title": "Automaattinen URL-osoitteen vaihto",
@@ -763,7 +767,7 @@
"default_locale": "Oletuskieliasetus",
"default_locale_description": "Muotoile päivämäärät ja numerot selaimesi kielen mukaan",
"delete": "Poista",
"delete_action_confirmation_message": "Haluatko varmasti poistaa tämän aineiston? Tämä toiminto siirtää aineiston palvelimen roskakoriin ja kysyy, haluatko poistaa sen myös paikallisesti.",
"delete_action_confirmation_message": "Haluatko varmasti poistaa tämän aineiston? Tämä toiminto siirtää aineiston palvelimen roskakoriin ja kysyy, haluatko poistaa sen myös paikallisesti",
"delete_action_prompt": "{count} poistettu",
"delete_album": "Poista albumi",
"delete_api_key_prompt": "Haluatko varmasti poistaa tämän API-avaimen?",
@@ -1058,6 +1062,7 @@
"filter_people": "Suodata henkilöt",
"filter_places": "Suodata paikkoja",
"find_them_fast": "Löydä nopeasti hakemalla nimellä",
"first": "Ensimmäinen",
"fix_incorrect_match": "Korjaa virheellinen osuma",
"folder": "Kansio",
"folder_not_found": "Kansiota ei löytynyt",
@@ -1179,6 +1184,7 @@
"language_search_hint": "Etsi kieliä...",
"language_setting_description": "Valitse suosimasi kieli",
"large_files": "Suuret tiedostot",
"last": "Viimeinen",
"last_seen": "Viimeksi nähty",
"latest_version": "Viimeisin versio",
"latitude": "Leveysaste",
@@ -1197,6 +1203,7 @@
"library_page_sort_title": "Albumin otsikko",
"licenses": "Lisenssit",
"light": "Vaalea",
"like": "Tykkää",
"like_deleted": "Tykkäys poistettu",
"link_motion_video": "Linkitä liikevideo",
"link_to_oauth": "Linkki OAuth",
@@ -1459,9 +1466,9 @@
"permission_onboarding_permission_limited": "Rajoitettu käyttöoikeus. Salliaksesi Immichin varmuuskopioida ja hallita koko kuvakirjastoasi, myönnä oikeus kuviin ja videoihin asetuksista.",
"permission_onboarding_request": "Immich vaatii käyttöoikeuden kuvien ja videoiden käyttämiseen.",
"person": "Henkilö",
"person_age_months": "{months} kuukautta vanha",
"person_age_year_months": "1 vuosi ja {months} kuukautta vanha",
"person_age_years": "{years} vuotta vanha",
"person_age_months": "{months, plural, one {# kuukauden} other {# kuukauden}} ikäinen",
"person_age_year_months": "1 vuosi ja {months, plural, one {# kuukauden} other {# kuukautta}} vanha",
"person_age_years": "{years, plural, other {# vuotta}} vanha",
"person_birthdate": "Syntynyt {date}",
"person_hidden": "{name}{hidden, select, true { (piilotettu)} other {}}",
"photo_shared_all_users": "Näyttää että olet jakanut kuvasi kaikkien käyttäjien kanssa, tai sinulla ei ole käyttäjää kenelle jakaa.",
@@ -1858,6 +1865,7 @@
"sort_created": "Luontipäivä",
"sort_items": "Tietueiden määrä",
"sort_modified": "Muokkauspäivä",
"sort_newest": "Uusin kuva",
"sort_oldest": "Vanhin kuva",
"sort_people_by_similarity": "Lajittele ihmiset samankaltaisuuden mukaan",
"sort_recent": "Tuorein kuva",

View File

@@ -396,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Préférer les images externes",
"advanced_settings_proxy_headers_subtitle": "Ajoutez des en-têtes personnalisés à chaque requête réseau",
"advanced_settings_proxy_headers_title": "En-têtes de proxy",
"advanced_settings_readonly_mode_subtitle": "Active le mode lecture seule, où les photos peuvent seulement être visualisées, et les actions comme les sélections multiples, le partage, la diffusion, la suppression sont désactivées. Activer/désactiver la lecture seule via l'image de l'utilisateur depuis l'écran d'accueil",
"advanced_settings_readonly_mode_title": "Mode lecture seule",
"advanced_settings_self_signed_ssl_subtitle": "Permet d'ignorer la vérification du certificat SSL pour le point d'accès du serveur. Requis pour les certificats auto-signés.",
"advanced_settings_self_signed_ssl_title": "Autoriser les certificats SSL auto-signés",
"advanced_settings_sync_remote_deletions_subtitle": "Supprimer ou restaurer automatiquement un média sur cet appareil lorsqu'une action a été faite sur le web",
@@ -461,7 +463,8 @@
"app_bar_signout_dialog_title": "Se déconnecter",
"app_settings": "Paramètres de l'application",
"appears_in": "Apparaît dans",
"archive": "Archiver",
"apply_count": "Appliquer ({count, number})",
"archive": "Archive",
"archive_action_prompt": "{count} ajouté(s) à l'archive",
"archive_or_unarchive_photo": "Archiver ou désarchiver une photo",
"archive_page_no_archived_assets": "Aucun élément archivé n'a été trouvé",
@@ -500,7 +503,7 @@
"assets": "Médias",
"assets_added_count": "{count, plural, one {# média ajouté} other {# médias ajoutés}}",
"assets_added_to_album_count": "{count, plural, one {# média ajouté} other {# médias ajoutés}} à l'album",
"assets_added_to_albums_count": "{assetTotal, plural, one {# média ajouté} other {# médias ajoutés}} à {albumTotal} album(s)",
"assets_added_to_albums_count": "{assetTotal, plural, one {# média ajouté} other {# médias ajoutés}} à {albumTotal, plural, one {# album} other {# albums}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Le média ne peut pas être ajouté} other {Les médias ne peuvent pas être ajoutés}} à l'album",
"assets_cannot_be_added_to_albums": "{count, plural, one {Le média ne peut être ajouté} other {Les médias ne peuvent être ajoutés}} à aucun des albums",
"assets_count": "{count, plural, one {# média} other {# médias}}",
@@ -1073,12 +1076,18 @@
"gcast_enabled": "Diffusion Google Cast",
"gcast_enabled_description": "Cette fonctionnalité charge des ressources externes depuis Google pour fonctionner.",
"general": "Général",
"geolocation_instruction_all_have_location": "Tous les médias pour cette date ont déjà des données de localisation. Essayez d'afficher tous les médias ou sélectionnez une date différente",
"geolocation_instruction_location": "Cliquez sur un média avec des coordonnées GPS pour utiliser sa localisation, ou bien sélectionnez une localisation directement sur la carte",
"geolocation_instruction_no_date": "Sélectionnez une date pour gérer les données de localisation pour les photos et vidéos de ce jour",
"geolocation_instruction_no_photos": "Aucune photo ou vidéo trouvée pour cette date. Sélectionnez une date différente pour en afficher",
"get_help": "Obtenir de l'aide",
"get_wifiname_error": "Impossible d'obtenir le nom du réseau wifi. Assurez-vous d'avoir donné les permissions nécessaires à l'application et que vous êtes connecté à un réseau wifi",
"getting_started": "Commencer",
"go_back": "Retour",
"go_to_folder": "Dossier",
"go_to_search": "Faire une recherche",
"gps": "GPS",
"gps_missing": "Pas de GPS",
"grant_permission": "Accorder les permissions",
"group_albums_by": "Grouper les albums par...",
"group_country": "Grouper par pays",
@@ -1262,6 +1271,7 @@
"main_branch_warning": "Vous utilisez une version de développement. Nous vous recommandons fortement d'utiliser une version stable!",
"main_menu": "Menu principal",
"make": "Marque",
"manage_geolocation": "Gérer la localisation",
"manage_shared_links": "Gérer les liens partagés",
"manage_sharing_with_partners": "Gérer le partage avec les partenaires",
"manage_the_app_settings": "Gérer les paramètres de l'application",
@@ -1508,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "L'application mobile est obsolète. Veuillez effectuer la mise à jour vers la dernière version mineure.",
"profile_drawer_client_server_up_to_date": "Le client et le serveur sont à jour",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Mode lecture seule activé. Faites un appui double sur l'image de l'utilisateur pour quitter.",
"profile_drawer_server_out_of_date_major": "Le serveur est obsolète. Veuillez mettre à jour vers la dernière version majeure.",
"profile_drawer_server_out_of_date_minor": "Le serveur est obsolète. Veuillez mettre à jour vers la dernière version mineure.",
"profile_image_of_user": "Image de profil de {user}",
@@ -1553,6 +1564,8 @@
"rating_description": "Afficher l'évaluation EXIF dans le panneau d'information",
"reaction_options": "Options de réaction",
"read_changelog": "Lire les changements",
"readonly_mode_disabled": "Mode lecture seule désactivé",
"readonly_mode_enabled": "Mode lecture seule activé",
"reassign": "Réattribuer",
"reassigned_assets_to_existing_person": "{count, plural, one {# média réattribué} other {# médias réattribués}} à {name, select, null {une personne existante} other {{name}}}",
"reassigned_assets_to_new_person": "{count, plural, one {# média réattribué} other {# médias réattribués}} à une nouvelle personne",
@@ -1634,7 +1647,7 @@
"review_large_files": "Consulter les fichiers volumineux",
"role": "Rôle",
"role_editor": "Éditeur",
"role_viewer": "Visionneuse",
"role_viewer": "Visionneur",
"running": "En cours",
"save": "Sauvegarder",
"save_to_gallery": "Enregistrer",
@@ -1722,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Échec de la création de l'album",
"selected": "Sélectionné",
"selected_count": "{count, plural, one {# sélectionné} other {# sélectionnés}}",
"selected_gps_coordinates": "coordonnées GPS sélectionnées",
"send_message": "Envoyer un message",
"send_welcome_email": "Envoyer un courriel de bienvenue",
"server_endpoint": "Adresse du serveur",
@@ -1832,8 +1846,10 @@
"shift_to_permanent_delete": "appuyez sur ⇧ pour supprimer définitivement le média",
"show_album_options": "Afficher les options de l'album",
"show_albums": "Montrer les albums",
"show_all_assets": "Montrer tous les médias",
"show_all_people": "Montrer toutes les personnes",
"show_and_hide_people": "Afficher / Masquer les personnes",
"show_assets_without_location": "Montrer les médias sans localisation",
"show_file_location": "Afficher l'emplacement du fichier",
"show_gallery": "Afficher la galerie",
"show_hidden_people": "Afficher les personnes masquées",
@@ -1941,7 +1957,9 @@
"to_change_password": "Modifier le mot de passe",
"to_favorite": "Ajouter aux favoris",
"to_login": "Se connecter",
"to_multi_select": "pour faire une sélection multiple",
"to_parent": "Aller au dossier parent",
"to_select": "pour faire une sélection",
"to_trash": "Corbeille",
"toggle_settings": "Inverser les paramètres",
"total": "Total",
@@ -1991,6 +2009,7 @@
"unstacked_assets_count": "{count, plural, one {# média dépilé} other {# médias dépilés}}",
"untagged": "Sans étiquette",
"up_next": "Suite",
"update_location_action_prompt": "Mettre à jour la localisation des {count} médias sélectionnés avec :",
"updated_at": "Mis à jour à",
"updated_password": "Mot de passe mis à jour",
"upload": "Envoyer",
@@ -2015,6 +2034,7 @@
"use_biometric": "Utiliser l'authentification biométrique",
"use_current_connection": "Utiliser le réseau actuel",
"use_custom_date_range": "Utilisez une plage de date personnalisée à la place",
"use_this_location": "Cliquez pour utiliser la localisation",
"user": "Utilisateur",
"user_has_been_deleted": "Cet utilisateur à été supprimé.",
"user_id": "ID Utilisateur",

View File

@@ -28,6 +28,9 @@
"add_to_album": "הוספה לאלבום",
"add_to_album_bottom_sheet_added": "נוסף ל {album}",
"add_to_album_bottom_sheet_already_exists": "כבר ב {album}",
"add_to_album_toggle": "החלפת מצב בחירה עבור {album}",
"add_to_albums": "הוספה לאלבומים",
"add_to_albums_count": "Add to albums ({count})",
"add_to_shared_album": "הוספה לאלבום משותף",
"add_url": "הוספת קישור",
"added_to_archive": "נוסף לארכיון",
@@ -355,6 +358,9 @@
"trash_number_of_days_description": "מספר הימים לשמירה של תמונות באשפה לפני הסרתם לצמיתות",
"trash_settings": "הגדרות האשפה",
"trash_settings_description": "ניהול הגדרות האשפה",
"unlink_all_oauth_accounts": "ביטול קישור כל חשבונות OAuth",
"unlink_all_oauth_accounts_description": "זכור לבטל את הקישור של כל חשבונות ה-OAuth לפני ההגירה לספק חדש.",
"unlink_all_oauth_accounts_prompt": "האם באמת ברצונך לבטל את הקישור של כל חשבונות ה-OAuth? פעולה זו תאפס את מזהה ה-OAuth עבור כל משתמש ואינה ניתנת לביטול.",
"user_cleanup_job": "ניקוי משתמשים",
"user_delete_delay": "החשבון והתמונות של <b>{user}</b> יתוזמנו למחיקה לצמיתות בעוד {delay, plural, one {יום #} other {# ימים}}.",
"user_delete_delay_settings": "עיכוב מחיקה",
@@ -494,7 +500,9 @@
"assets": "תמונות וסרטונים",
"assets_added_count": "{count, plural, one {נוספה תומנה #} other {נוספו # תמונות}}",
"assets_added_to_album_count": "{count, plural, one {נוספה תמונה #} other {נוספו # תמונות}} לאלבום",
"assets_added_to_albums_count": "{assetTotal, plural, one {נוסף פריט #} other {נוספו # פריטים}} אל {albumTotal, plural, one {אלבום #} other {# אלבומים}}",
"assets_cannot_be_added_to_album_count": "לא ניתן להוסיף את ה{count, plural, one {תמונה} other {תמונות}} לאלבום",
"assets_cannot_be_added_to_albums": "לא ניתן להוסיף {count, plural, one {פריט} other {פריטים}} לאף אחד מהאלבומים",
"assets_count": "{count, plural, one {תמונה #} other {# תמונות}}",
"assets_deleted_permanently": "{count} תמונות נמחקו לצמיתות",
"assets_deleted_permanently_from_server": "{count} תמונות נמחקו לצמיתות משרת ה-Immich",
@@ -511,6 +519,7 @@
"assets_trashed_count": "{count, plural, one {תמונה # הושלכה} other {# תמונות הושלכו}} לאשפה",
"assets_trashed_from_server": "{count} תמונות הועברו לאשפה מהשרת",
"assets_were_part_of_album_count": "{count, plural, one {תמונה הייתה} other {תמונות היו}} כבר חלק מהאלבום",
"assets_were_part_of_albums_count": "{count, plural, one {הפריט כבר היה} other {הפריטים כבר היו}} חלק מהאלבומים",
"authorized_devices": "מכשירים מורשים",
"automatic_endpoint_switching_subtitle": "התחבר מקומית דרך אינטרנט אלחוטי ייעודי כאשר זמין והשתמש בחיבורים חלופיים במקומות אחרים",
"automatic_endpoint_switching_title": "החלפת כתובת אוטומטית",
@@ -749,6 +758,7 @@
"date_of_birth_saved": "תאריך לידה נשמר בהצלחה",
"date_range": "טווח תאריכים",
"day": "יום",
"days": "ימים",
"deduplicate_all": "ביטול כל הכפילויות",
"deduplication_criteria_1": "גודל תמונה בבתים",
"deduplication_criteria_2": "כמות נתוני EXIF",
@@ -836,6 +846,9 @@
"edit_birthday": "עריכת יום הולדת",
"edit_date": "ערוך תאריך",
"edit_date_and_time": "ערוך תאריך ושעה",
"edit_date_and_time_action_prompt": "{count} תאריך ושעה נערכו",
"edit_date_and_time_by_offset": "שינוי תאריך לפי קיזוז",
"edit_date_and_time_by_offset_interval": "טווח תאריכים חדש: {from} עד {to}",
"edit_description": "ערוך תיאור",
"edit_description_prompt": "אנא בחר תיאור חדש:",
"edit_exclusion_pattern": "ערוך דפוס החרגה",
@@ -908,6 +921,7 @@
"failed_to_load_notifications": "אירעה שגיאה בעת טעינת ההתראות",
"failed_to_load_people": "נכשל באחזור אנשים",
"failed_to_remove_product_key": "הסרת מפתח מוצר נכשלה",
"failed_to_reset_pin_code": "איפוס קוד PIN נכשל",
"failed_to_stack_assets": "יצירת ערימת תמונות נכשלה",
"failed_to_unstack_assets": "ביטול ערימת תמונות נכשלה",
"failed_to_update_notification_status": "שגיאה בעדכון ההתראה",
@@ -916,6 +930,7 @@
"paths_validation_failed": "{paths, plural, one {נתיב # נכשל} other {# נתיבים נכשלו}} אימות",
"profile_picture_transparent_pixels": "תמונות פרופיל אינן יכולות לכלול פיקסלים שקופים. נא להגדיל ו/או להזיז את התמונה.",
"quota_higher_than_disk_size": "הגדרת מכסה גבוהה יותר מגודל הדיסק",
"something_went_wrong": "משהו השתבש",
"unable_to_add_album_users": "לא ניתן להוסיף משתמשים לאלבום",
"unable_to_add_assets_to_shared_link": "לא ניתן להוסיף תמונות לקישור משותף",
"unable_to_add_comment": "לא ניתן להוסיף תגובה",
@@ -1047,11 +1062,13 @@
"filter_people": "סנן אנשים",
"filter_places": "סינון מקומות",
"find_them_fast": "מצא אותם מהר לפי שם עם חיפוש",
"first": "ראשון",
"fix_incorrect_match": "תקן התאמה שגויה",
"folder": "תיקיה",
"folder_not_found": "תיקיה לא נמצאה",
"folders": "תיקיות",
"folders_feature_description": "עיון בתצוגת התיקייה עבור התמונות והסרטונים שבמערכת הקבצים",
"forgot_pin_code_question": "שחכת את ה-PIN שלך?",
"forward": "קדימה",
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "תכונה זאת טוענת משאבים חיצוניים מגוגל בכדי לפעול.",
@@ -1106,6 +1123,7 @@
"home_page_upload_err_limit": "ניתן להעלות רק מקסימום של 30 תמונות בכל פעם, מדלג",
"host": "מארח",
"hour": "שעה",
"hours": "שעות",
"id": "מזהה",
"idle": "ממתין",
"ignore_icloud_photos": "התעלם מתמונות iCloud",
@@ -1166,10 +1184,12 @@
"language_search_hint": "חפש שפות...",
"language_setting_description": "בחר את השפה המועדפת עליך",
"large_files": "קבצים גדולים",
"last": "אחרון",
"last_seen": "נראה לאחרונה",
"latest_version": "גרסה עדכנית ביותר",
"latitude": "קו רוחב",
"leave": "לעזוב",
"leave_album": "עזיבת אלבום",
"lens_model": "דגם עדשה",
"let_others_respond": "אפשר לאחרים להגיב",
"level": "רמה",
@@ -1183,6 +1203,7 @@
"library_page_sort_title": "כותרת אלבום",
"licenses": "רישיונות",
"light": "בהיר",
"like": "אהבתי",
"like_deleted": "לייק נמחק",
"link_motion_video": "קשר סרטון תנועה",
"link_to_oauth": "קישור ל-OAuth",
@@ -1249,7 +1270,7 @@
"manage_your_devices": "ניהול המכשירים המחוברים שלך",
"manage_your_oauth_connection": "ניהול חיבור ה-OAuth שלך",
"map": "מפה",
"map_assets_in_bounds": "{count, plural, one {תמונה #} other {# תמונות}}",
"map_assets_in_bounds": "{count, plural, =0 {אין תמונות באזור זה} one {תמונה #} other {# תמונות}}",
"map_cannot_get_user_location": "לא ניתן לקבוע את מיקום המשתמש",
"map_location_dialog_yes": "כן",
"map_location_picker_page_use_location": "השתמש במיקום הזה",
@@ -1293,6 +1314,7 @@
"merged_people_count": "{count, plural, one {אדם # מוזג} other {# אנשים מוזגו}}",
"minimize": "מזער",
"minute": "דקה",
"minutes": "דקות",
"missing": "חסרים",
"model": "דגם",
"month": "חודש",
@@ -1366,6 +1388,7 @@
"oauth": "OAuth",
"official_immich_resources": "מקורות רשמיים של Immich",
"offline": "לא מקוון",
"offset": "קיזוז",
"ok": "בסדר",
"oldest_first": "הישן ביותר ראשון",
"on_this_device": "במכשיר הזה",
@@ -1443,6 +1466,9 @@
"permission_onboarding_permission_limited": "הרשאה מוגבלת. כדי לתת ליישום לגבות ולנהל את כל אוסף הגלריה שלך, הענק הרשאה לתמונות וסרטונים בהגדרות.",
"permission_onboarding_request": "היישום דורש הרשאה כדי לראות את התמונות והסרטונים שלך.",
"person": "אדם",
"person_age_months": "בן {months, plural, one {חודש #} other {# חודשים}}",
"person_age_year_months": "בן שנה, {months, plural, one {חודש #} other {# חודשים}}",
"person_age_years": "בן {years, plural, other {# שנים}}",
"person_birthdate": "נולד בתאריך {date}",
"person_hidden": "{name}{hidden, select, true { (מוסתר)} other {}}",
"photo_shared_all_users": "נראה ששיתפת את התמונות שלך עם כל המשתמשים או שאין לך אף משתמש לשתף איתו.",
@@ -1588,6 +1614,9 @@
"reset_password": "איפוס סיסמה",
"reset_people_visibility": "אפס את נראות האנשים",
"reset_pin_code": "אפס קוד PIN",
"reset_pin_code_description": "אם שחכת את קוד ה-PIN שלך, באפשרותך ליצור קשר עם מנהל השרת כדי לאפס אותו",
"reset_pin_code_success": "קוד ה-PIN אופס בהצלחה",
"reset_pin_code_with_password": "באפשרותך תמיד לאפס את קוד ה-PIN שלך עם הסיסמה שלך",
"reset_sqlite": "אפס את מסד הנתונים SQLite",
"reset_sqlite_confirmation": "האם אתה בטוח שברצונך לאפס את מסד הנתונים SQLite? יהיה עליך להתנתק ולהתחבר מחדש כדי לסנכרן את הנתונים מחדש",
"reset_sqlite_success": "איפוס מסד הנתונים SQLite בוצע בהצלחה",
@@ -1836,6 +1865,7 @@
"sort_created": "תאריך יצירה",
"sort_items": "מספר פריטים",
"sort_modified": "תאריך שינוי",
"sort_newest": "תמונה הכי חדשה",
"sort_oldest": "תמונה הכי ישנה",
"sort_people_by_similarity": "מיין אנשים לפי דמיון",
"sort_recent": "תמונה אחרונה ביותר",
@@ -1911,7 +1941,9 @@
"to_change_password": "שנה סיסמה",
"to_favorite": "מועדף",
"to_login": "כניסה",
"to_multi_select": "לבחור מרובות",
"to_parent": "לך להורה",
"to_select": "לבחור",
"to_trash": "אשפה",
"toggle_settings": "החלף מצב הגדרות",
"total": "סה\"כ",

View File

@@ -52,7 +52,7 @@
"backup_onboarding_2_description": "copie locali su diversi dispositivi. Ciò include i file principali e un backup di tali file a livello locale.",
"backup_onboarding_3_description": "copie totali dei tuoi dati, compresi i file originali. Ciò include 1 copia offsite e 2 copie locali.",
"backup_onboarding_description": "Per proteggere i tuoi dati, è consigliato adottare una strategia di backup <backblaze-link>3-2-1</backblaze-link>. Per una soluzione di backup completa, è consigliato conservare copie delle foto/video caricati e del database Immich.",
"backup_onboarding_footer": "Per ulteriori informazioni sul backup di Immich, consultare la <link>documentazione</link>.",
"backup_onboarding_footer": "Per ulteriori informazioni sul backup di Immich, consulta la <link>documentazione</link>.",
"backup_onboarding_parts_title": "Un backup 3-2-1 include:",
"backup_onboarding_title": "Backup",
"backup_settings": "Impostazioni Dump database",
@@ -62,7 +62,7 @@
"confirm_delete_library": "Sei sicuro di voler cancellare la libreria {library}?",
"confirm_delete_library_assets": "Sei sicuro di voler cancellare questa libreria? Questo cancellerà {count, plural, one {# asset} other {tutti e # gli assets}} da Immich senza possibilità di tornare indietro. I file non verranno cancellati.",
"confirm_email_below": "Per confermare, scrivi \"{email}\" qui sotto",
"confirm_reprocess_all_faces": "Sei sicuro di voler riprocessare tutti i volti? Questo cancellerà tutte le persone nominate.",
"confirm_reprocess_all_faces": "Sei sicuro di voler riprocessare tutti i volti? Questo cancellerà anche tutte le persone associate.",
"confirm_user_password_reset": "Sei sicuro di voler resettare la password di {user}?",
"confirm_user_pin_code_reset": "Sicuro di voler resettare il codice PIN di {user}?",
"create_job": "Crea Processo",
@@ -79,27 +79,27 @@
"failed_job_command": "Il comando {command} è fallito per il processo: {job}",
"force_delete_user_warning": "ATTENZIONE: Questo rimuoverà immediatamente l'utente e tutti i suoi assets. Non è possibile tornare indietro e i file non potranno essere recuperati.",
"image_format": "Formato",
"image_format_description": "WebP produce file più piccoli rispetto a JPEG, ma l'encoding è più lento.",
"image_fullsize_description": "Le immagini con dimensioni reali senza metadati sono utilizzate durante lo zoom",
"image_fullsize_enabled": "Abilita la generazione delle immagini con dimensioni reali",
"image_fullsize_enabled_description": "Genera immagini con dimensioni reali per i formati non web-friendly. Quando \"Preferisci l'anteprima integrata\" è abilitata, le anteprime integrate saranno usate senza conversione. Non riguarda le immagini web-friendly come il JPEG.",
"image_fullsize_quality_description": "Qualità delle immagini con dimensioni reali da 1 a 100. Più è alto il valore più la qualità sarà alta come anche la grandezza dei file.",
"image_fullsize_title": "Impostazioni Immagini con dimensioni reali",
"image_format_description": "WebP produce file più piccoli rispetto a JPEG, ma è più lento da codificare.",
"image_fullsize_description": "Immagini a dimensioni reali senza metadati, sono utilizzate durante lo zoom",
"image_fullsize_enabled": "Abilita la generazione delle immagini a dimensioni reali",
"image_fullsize_enabled_description": "Genera immagini a dimensioni reali per i formati non web-friendly. Quando è abilitata l'opzione \"Preferisci l'anteprima integrata\", le anteprime integrate saranno utilizzate direttamente senza conversione. Non influisce sui formati web-friendly come JPEG.",
"image_fullsize_quality_description": "Qualità delle immagini a dimensioni reali da 1 a 100. Un valore più alto è migliore ma produce file più grandi.",
"image_fullsize_title": "Impostazioni delle immagini a dimensioni reali",
"image_prefer_embedded_preview": "Preferisci l'anteprima integrata",
"image_prefer_embedded_preview_setting_description": "Usa l'anteprima integrata nelle foto RAW come input per l'elaborazione delle immagini, se disponibile. Questo permette un miglioramento dei colori per alcune immagini, ma la qualità delle anteprime dipende dalla macchina fotografica. Inoltre le immagini potrebbero presentare artefatti di compressione.",
"image_prefer_wide_gamut": "Preferisci gamut più ampio",
"image_prefer_wide_gamut_setting_description": "Usa lo spazio colore Display P3 per le anteprime. Questo aiuta a mantenere la vivacità delle immagini con spazi colore più ampi, tuttavia potrebbe non mostrare correttamente le immagini con dispositivi e browser obsoleti. Le immagini sRGB vengono preservate per evitare alterazioni del colore.",
"image_preview_description": "Immagine di medie dimensioni con metadati eliminati, utilizzata durante la visualizzazione di una singola risorsa e per l'apprendimento automatico",
"image_preview_quality_description": "Qualità dell'anteprima da 1 a 100. Elevata è migliore ma produce file più pesanti e può ridurre la reattività dell'app. Impostare un valore basso può influenzare negativamente la qualità del machine learning.",
"image_preview_description": "Immagine a media dimensione senza metadati, utilizzata durante la visualizzazione di una singola risorsa e per il machine learning",
"image_preview_quality_description": "Qualità dell'anteprima da 1 a 100. Più alto è meglio ma produce file più pesanti e può ridurre la reattività dell'app. Impostare un valore basso può influenzare negativamente la qualità del machine learning.",
"image_preview_title": "Impostazioni dell'anteprima",
"image_quality": "Qualità",
"image_resolution": "Risoluzione",
"image_resolution_description": "Risoluzioni più elevate possono preservare più dettagli ma richiedere più tempo per la codifica, avere dimensioni di file più grandi e possono ridurre la reattività dell'app.",
"image_resolution_description": "Risoluzioni più elevate possono preservare più dettagli ma richiedere più tempo per la codifica, avere dimensioni di file più grandi e ridurre la reattività dell'app.",
"image_settings": "Impostazioni delle immagini",
"image_settings_description": "Gestisci qualità e risoluzione delle immagini generate",
"image_thumbnail_description": "Miniatura piccola senza metadati, utilizzata durante la visualizzazione di gruppi di foto come la sequenza temporale principale",
"image_thumbnail_quality_description": "Qualità delle anteprime da 1 a 100. Un valore più alto è migliore ma produce file più grandi e può ridurre la reattività dell'app.",
"image_thumbnail_title": "Impostazioni della copertina",
"image_thumbnail_description": "Miniatura piccola senza metadati, utilizzata durante la visualizzazione di gruppi di foto come nella galleria principale",
"image_thumbnail_quality_description": "Qualità delle miniature da 1 a 100. Un valore più alto è migliore ma produce file più grandi e può ridurre la reattività dell'app.",
"image_thumbnail_title": "Impostazioni delle miniature",
"job_concurrency": "Concorrenza {job}",
"job_created": "Processo creato",
"job_not_concurrency_safe": "Questo processo non è eseguibile in maniera concorrente.",
@@ -130,7 +130,7 @@
"machine_learning_duplicate_detection_enabled_description": "Se disattivo, risorse perfettamente identiche saranno comunque deduplicate.",
"machine_learning_duplicate_detection_setting_description": "Utilizza i CLIP embeddings per trovare possibili duplicati",
"machine_learning_enabled": "Attiva machine learning",
"machine_learning_enabled_description": "Se disabilitato, tutte le funzioni di ML saranno disabilitate ignorando le importazioni sottostanti.",
"machine_learning_enabled_description": "Se disabilitato, tutte le funzioni di ML saranno disabilitate ignorando le impostazioni sottostanti.",
"machine_learning_facial_recognition": "Riconoscimento Facciale",
"machine_learning_facial_recognition_description": "Rileva, riconosci e raggruppa volti nelle immagini",
"machine_learning_facial_recognition_model": "Modello di riconoscimento facciale",
@@ -143,7 +143,7 @@
"machine_learning_max_recognition_distance_description": "La distanza massima tra due volti per essere considerati la stessa persona, che varia da 0 a 2. Abbassare questo valore può prevenire l'etichettatura di due persone come se fossero la stessa persona, mentre aumentarlo può prevenire l'etichettatura della stessa persona come se fossero due persone diverse. Nota che è più facile unire due persone che separare una persona in due, quindi è preferibile mantenere una soglia più bassa quando possibile.",
"machine_learning_min_detection_score": "Punteggio minimo di rilevazione",
"machine_learning_min_detection_score_description": "Punteggio di confidenza minimo per rilevare un volto, da 0 a 1. Valori più bassi rileveranno più volti, ma potrebbero generare risultati fasulli.",
"machine_learning_min_recognized_faces": "Minimo volti rilevati",
"machine_learning_min_recognized_faces": "Minimo numero di volti rilevati",
"machine_learning_min_recognized_faces_description": "Il numero minimo di volti riconosciuti per creare una persona. Aumentando questo valore si rende il riconoscimento facciale più preciso, ma aumenta la possibilità che un volto non venga assegnato a una persona.",
"machine_learning_settings": "Impostazioni Machine Learning",
"machine_learning_settings_description": "Gestisci le impostazioni e le funzionalità del machine learning",
@@ -164,7 +164,7 @@
"map_reverse_geocoding": "Geocodifica inversa",
"map_reverse_geocoding_enable_description": "Abilita geocodifica inversa",
"map_reverse_geocoding_settings": "Impostazioni Geocodifica Inversa",
"map_settings": "Impostazioni Mappa e Posizione",
"map_settings": "Mappa",
"map_settings_description": "Gestisci impostazioni mappa",
"map_style_description": "URL per un tema della mappa style.json",
"memory_cleanup_job": "Pulizia dei vecchi Ricordi",
@@ -181,14 +181,14 @@
"nightly_tasks_cluster_new_faces_setting": "Raggruppa nuovi volti",
"nightly_tasks_database_cleanup_setting": "Processi di pulizia del database",
"nightly_tasks_database_cleanup_setting_description": "Ripulisci il database da file vecchi e scaduti",
"nightly_tasks_generate_memories_setting": "Genera ricordi",
"nightly_tasks_generate_memories_setting_description": "Genera nuovi ricordi a partire dalle risorse",
"nightly_tasks_generate_memories_setting": "Genera Ricordi",
"nightly_tasks_generate_memories_setting_description": "Genera nuovi Ricordi a partire dalle risorse",
"nightly_tasks_missing_thumbnails_setting": "Genera anteprime mancanti",
"nightly_tasks_missing_thumbnails_setting_description": "Metti in coda le risorse senza miniatura per la generazione delle anteprime",
"nightly_tasks_settings": "Impostazioni delle attività notturne",
"nightly_tasks_settings_description": "Gestisci attività notturne",
"nightly_tasks_start_time_setting": "Tempo di avvio",
"nightly_tasks_start_time_setting_description": "Il tempo in cui il server fa partire le attività notturne",
"nightly_tasks_start_time_setting": "Orario di avvio",
"nightly_tasks_start_time_setting_description": "L'orario in cui il server fa partire le attività notturne",
"nightly_tasks_sync_quota_usage_setting": "Sincronizza la quota di utilizzo",
"nightly_tasks_sync_quota_usage_setting_description": "Aggiorna la quota di spazio dell'utente in base all'utilizzo corrente",
"no_paths_added": "Nessun percorso aggiunto",
@@ -199,10 +199,10 @@
"notification_email_from_address_description": "Indirizzo email del mittente, ad esempio: \"Immich Photo Server <noreply@example.com>\". Assicurati di utilizzare un indirizzo da cui sei autorizzato a inviare email.",
"notification_email_host_description": "Host del server email (es. smtp.immich.app)",
"notification_email_ignore_certificate_errors": "Ignora errori di certificato",
"notification_email_ignore_certificate_errors_description": "Ignora errori di validazione del certificato TLS (sconsigliato)",
"notification_email_ignore_certificate_errors_description": "Ignora errori TLS di validazione del certificato (sconsigliato)",
"notification_email_password_description": "Password da usare per l'autenticazione con il server email",
"notification_email_port_description": "Porta del server email (es. 25, 465, 587)",
"notification_email_sent_test_email_button": "Invia email di test e salva",
"notification_email_sent_test_email_button": "Invia email di prova e salva",
"notification_email_setting_description": "Impostazioni per le notifiche via email",
"notification_email_test_email": "Invia email di prova",
"notification_email_test_email_failed": "Impossibile inviare email di prova, controlla i valori inseriti",
@@ -218,14 +218,14 @@
"oauth_button_text": "Testo pulsante",
"oauth_client_secret_description": "Richiesto se PKCE (Proof Key for Code Exchange) non è supportato dal provider OAuth",
"oauth_enable_description": "Login con OAuth",
"oauth_mobile_redirect_uri": "URI reindirizzamento mobile",
"oauth_mobile_redirect_uri_override": "Sovrascrivi URI reindirizzamento cellulare",
"oauth_mobile_redirect_uri": "URI di reindirizzamento per app mobile",
"oauth_mobile_redirect_uri_override": "Sovrascrivi URI di reindirizzamento per app mobile",
"oauth_mobile_redirect_uri_override_description": "Abilita quando il gestore OAuth non consente un URL come ''{callback}''",
"oauth_role_claim": "Claim del ruolo",
"oauth_role_claim_description": "Concedi automaticamente l'accesso come amministratore in base alla presenza di questo claim. Il claim può essere 'user' o 'admin'.",
"oauth_settings": "OAuth",
"oauth_settings_description": "Gestisci impostazioni di login OAuth",
"oauth_settings_more_details": "Per più dettagli riguardo a questa funzionalità, consulta <link>la documentazione</link>.",
"oauth_settings_more_details": "Per maggiori informazioni su questa funzionalità, consulta <link>la documentazione</link>.",
"oauth_storage_label_claim": "Dichiarazione di ambito(claim) etichetta archiviazione",
"oauth_storage_label_claim_description": "Imposta automaticamente l'etichetta dell'archiviazione dell'utente al valore di questa dichiarazione di ambito(claim).",
"oauth_storage_quota_claim": "Dichiarazione di ambito(claim) limite archiviazione",
@@ -240,7 +240,7 @@
"paths_validated_successfully": "Percorsi validati con successo",
"person_cleanup_job": "Pulizia Persona",
"quota_size_gib": "Dimensione Archiviazione (GiB)",
"refreshing_all_libraries": "Aggiorna tutte le librerie",
"refreshing_all_libraries": "Aggiornando tutte le librerie",
"registration": "Registrazione amministratore",
"registration_description": "Poiché sei il primo utente del sistema, sarai assegnato come Amministratore e sarai responsabile dei task amministrativi, e utenti aggiuntivi saranno creati da te.",
"require_password_change_on_login": "Richiedi all'utente di cambiare password al primo accesso",
@@ -269,11 +269,11 @@
"storage_template_migration": "Migrazione modello archiviazione",
"storage_template_migration_description": "Applica il <link>{template}</link> attuale agli asset caricati in precedenza",
"storage_template_migration_info": "Le modifiche al modello di archiviazione verranno applicate solo agli asset nuovi. Per applicare le modifiche retroattivamente esegui <link>{job}</link>.",
"storage_template_migration_job": "Processo Migrazione Modello di Archiviazione",
"storage_template_more_details": "Per maggiori informazioni riguardo a questa funzionalità, consulta il <template-link>Modello Archiviazione</template-link> e le sue <implications-link>conseguenze</implications-link>",
"storage_template_migration_job": "Processo di migrazione del Modello di Archiviazione",
"storage_template_more_details": "Per maggiori informazioni riguardo a questa funzionalità, consulta il <template-link>Modello di Archiviazione</template-link> e le sue <implications-link>conseguenze</implications-link>",
"storage_template_onboarding_description_v2": "Se attiva, questa funzionalità organizzerà automaticamente i file utilizzando un modello definito dall'utente. Per maggiori informazioni, consultare la <link>documentazione</link>.",
"storage_template_path_length": "Limite approssimativo lunghezza percorso: <b>{length, number}</b>/{limit, number}",
"storage_template_settings": "Modello Archiviazione",
"storage_template_settings": "Modello di Archiviazione",
"storage_template_settings_description": "Gestisci la struttura delle cartelle e il nome degli asset caricati",
"storage_template_user_label": "<code>{label}</code> è l'etichetta di archiviazione dell'utente",
"system_settings": "Impostazioni di sistema",
@@ -283,7 +283,7 @@
"template_email_invite_album": "Modello di invito all'album",
"template_email_preview": "Anteprima",
"template_email_settings": "Template Email",
"template_email_update_album": "Modello di aggiornamento dell'album",
"template_email_update_album": "Aggiorna template dell'album",
"template_email_welcome": "Modello di email di benvenuto",
"template_settings": "Templates Notifiche",
"template_settings_description": "Gestisci i modelli personalizzati per le notifiche",
@@ -502,6 +502,7 @@
"assets_added_to_album_count": "{count, plural, one {# asset aggiunto} other {# asset aggiunti}} all'album",
"assets_added_to_albums_count": "Aggiunto {assetTotal, plural, one {# elemento} other {# elementi}} a {albumTotal, plural, one {# album} other {# album}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {L'elemento} other {Gli elementi}} non possono essere aggiunti all'album",
"assets_cannot_be_added_to_albums": "Non é stato possibile aggiungere {count, plural, one {l'elemento} other {gli elementi}} a nessun album",
"assets_count": "{count, plural, one {# elemento} other {# elementi}}",
"assets_deleted_permanently": "{count} elementi cancellati definitivamente",
"assets_deleted_permanently_from_server": "{count} elementi cancellati definitivamente dal server Immich",
@@ -518,6 +519,7 @@
"assets_trashed_count": "{count, plural, one {Spostato # asset} other {Spostati # assets}} nel cestino",
"assets_trashed_from_server": "{count} elementi cestinati dal server Immich",
"assets_were_part_of_album_count": "{count, plural, one {L'asset era} other {Gli asset erano}} già parte dell'album",
"assets_were_part_of_albums_count": "{count, plural, one {L'elemento fa} other {Gli elementi fanno}} già parte degli album",
"authorized_devices": "Dispositivi autorizzati",
"automatic_endpoint_switching_subtitle": "Connetti localmente alla rete Wi-Fi specificata, se disponibile; altrimenti utilizza connessioni alternative",
"automatic_endpoint_switching_title": "Cambio automatico di URL",
@@ -682,7 +684,7 @@
"comments_and_likes": "Commenti & mi piace",
"comments_are_disabled": "I commenti sono disabilitati",
"common_create_new_album": "Crea nuovo Album",
"common_server_error": "Si prega di controllare la connessione network, che il server sia raggiungibile e che le versione del server e app sono gli stessi.",
"common_server_error": "Verifica la connessione di rete, assicurati che il server sia raggiungibile e che le versioni dellapp e del server siano compatibili.",
"completed": "Completato",
"confirm": "Conferma",
"confirm_admin_password": "Conferma password dell'amministratore",
@@ -733,7 +735,7 @@
"create_shared_album_page_share_select_photos": "Seleziona foto",
"create_shared_link": "Crea link condiviso",
"create_tag": "Crea tag",
"create_tag_description": "Crea un nuovo tag. Per i tag annidati, si prega di inserire il percorso completo del tag tra cui barre oblique.",
"create_tag_description": "Crea un nuovo tag. Per i tag nidificati, inserisci il percorso completo del tag includendo le barre oblique (/).",
"create_user": "Crea utente",
"created": "Creato",
"created_at": "Creato il",
@@ -1060,6 +1062,7 @@
"filter_people": "Filtra persone",
"filter_places": "Filtra luoghi",
"find_them_fast": "Trovale velocemente con la ricerca",
"first": "Primo",
"fix_incorrect_match": "Correggi corrispondenza errata",
"folder": "Cartella",
"folder_not_found": "Cartella non trovata",
@@ -1181,6 +1184,7 @@
"language_search_hint": "Cerca linguaggi...",
"language_setting_description": "Seleziona la tua lingua predefinita",
"large_files": "File pesanti",
"last": "Ultimo",
"last_seen": "Ultimo accesso",
"latest_version": "Ultima Versione",
"latitude": "Latitudine",
@@ -1330,8 +1334,8 @@
"my_albums": "I miei album",
"name": "Nome",
"name_or_nickname": "Nome o soprannome",
"network_requirement_photos_upload": "Utilizzare i dati del cellulare per il backup delle foto",
"network_requirement_videos_upload": "Utilizzare i dati del cellulare per il backup dei video",
"network_requirement_photos_upload": "Utilizza la connessione dati per il backup delle foto",
"network_requirement_videos_upload": "Utilizza la connessione dati per il backup dei video",
"network_requirements_updated": "Requisiti di rete modificati, coda di backup reimpostata",
"networking_settings": "Rete",
"networking_subtitle": "Gestisci le impostazioni riguardanti gli endpoint del server",
@@ -1560,7 +1564,7 @@
"recently_added_page_title": "Aggiunti di recente",
"recently_taken": "Scattate di recente",
"recently_taken_page_title": "Scattate di Recente",
"refresh": "Aggiorna",
"refresh": "Ricarica",
"refresh_encoded_videos": "Ricarica video codificati",
"refresh_faces": "Aggiorna volti",
"refresh_metadata": "Ricarica metadati",
@@ -1937,7 +1941,9 @@
"to_change_password": "Modifica password",
"to_favorite": "Preferito",
"to_login": "Accedi",
"to_multi_select": "per selezione multipla",
"to_parent": "Sali di un livello",
"to_select": "per selezionare",
"to_trash": "Cancella",
"toggle_settings": "Attiva/disattiva impostazioni",
"total": "Totale",

View File

@@ -28,6 +28,9 @@
"add_to_album": "アルバムに追加",
"add_to_album_bottom_sheet_added": "{album}に追加",
"add_to_album_bottom_sheet_already_exists": "{album}に追加済み",
"add_to_album_toggle": "{album}の選択を切り替え",
"add_to_albums": "アルバムに追加",
"add_to_albums_count": "{count}つのアルバムへ追加",
"add_to_shared_album": "共有アルバムに追加",
"add_url": "URLを追加",
"added_to_archive": "アーカイブにしました",
@@ -218,6 +221,7 @@
"oauth_mobile_redirect_uri": "モバイル用リダイレクトURI",
"oauth_mobile_redirect_uri_override": "モバイル用リダイレクトURI上書き",
"oauth_mobile_redirect_uri_override_description": "''{callback}''など、モバイルURIがOAuthプロバイダーによって許可されていない場合に有効にしてください",
"oauth_role_claim": "役職を主張する",
"oauth_role_claim_description": "管理者になると申し立てが行われた場合、自動的に管理者アクセスがその人に付与されるようにします。",
"oauth_settings": "OAuth",
"oauth_settings_description": "OAuthログイン設定を管理します",
@@ -392,6 +396,8 @@
"advanced_settings_prefer_remote_title": "リモートを優先する",
"advanced_settings_proxy_headers_subtitle": "プロキシヘッダを設定する",
"advanced_settings_proxy_headers_title": "プロキシヘッダ",
"advanced_settings_readonly_mode_subtitle": "読み取り専用モードを有効にすると、写真の複数選択や、共有、削除、キャスト機能が無効になります。メインスクリーンのユーザーアバターから有効/無効を切り替えられます",
"advanced_settings_readonly_mode_title": "読み取り専用モード",
"advanced_settings_self_signed_ssl_subtitle": "SSLのチェックをスキップする。自己署名証明書が必要です。",
"advanced_settings_self_signed_ssl_title": "自己署名証明書を許可する",
"advanced_settings_sync_remote_deletions_subtitle": "Webでこの操作を行った際に、自動的にこのデバイス上から当該アセットを削除または復元する",
@@ -457,6 +463,7 @@
"app_bar_signout_dialog_title": "サインアウト",
"app_settings": "アプリ設定",
"appears_in": "これらに含まれます",
"apply_count": "適用 ({count, number})",
"archive": "アーカイブ",
"archive_action_prompt": "アーカイブに{count}項目追加しました",
"archive_or_unarchive_photo": "写真をアーカイブまたはアーカイブ解除",
@@ -496,7 +503,9 @@
"assets": "アセット",
"assets_added_count": "{count, plural, one {#個} other {#個}}のアセットを追加しました",
"assets_added_to_album_count": "{count, plural, one {#個} other {#個}}のアセットをアルバムに追加しました",
"assets_added_to_albums_count": "{assetTotal, plural, one {# 項目} other {# 項目}}を{albumTotal, plural, one {# つのアルバム} other {# つのアルバム}}に追加しました",
"assets_cannot_be_added_to_album_count": "{count, plural, one {アセット} other {アセット}} はアルバムに追加できません",
"assets_cannot_be_added_to_albums": "{count, plural, one {項目} other {項目}} をどのアルバムにも追加できませんでした",
"assets_count": "{count, plural, one {#個} other {#個}}のアセット",
"assets_deleted_permanently": "{count}項目を完全に削除しました",
"assets_deleted_permanently_from_server": "サーバー上の{count}項目を完全に削除しました",
@@ -513,6 +522,7 @@
"assets_trashed_count": "{count, plural, one {#個} other {#個}}のアセットをごみ箱に移動しました",
"assets_trashed_from_server": "サーバー上の{count}項目をゴミ箱に移動しました",
"assets_were_part_of_album_count": "{count, plural, one {個} other {個}}のアセットは既にアルバムの一部です",
"assets_were_part_of_albums_count": "{count, plural, one {項目は} other {項目は}}すでにアルバムに追加されているものでした",
"authorized_devices": "認可済みデバイス",
"automatic_endpoint_switching_subtitle": "指定されたWi-Fiに接続時のみローカル接続を行い、他のネットワーク下では通常通りの接続を行います",
"automatic_endpoint_switching_title": "自動URL切り替え",
@@ -699,7 +709,7 @@
"control_bottom_app_bar_edit_location": "位置情報を編集",
"control_bottom_app_bar_edit_time": "撮影日時を編集",
"control_bottom_app_bar_share_link": "共有リンク",
"control_bottom_app_bar_share_to": "次のユーザーに共有: ",
"control_bottom_app_bar_share_to": "次のユーザーに共有:",
"control_bottom_app_bar_trash_from_immich": "ゴミ箱に入れる",
"copied_image_to_clipboard": "画像をクリップボードにコピーしました。",
"copied_to_clipboard": "クリップボードにコピーしました!",
@@ -1055,6 +1065,7 @@
"filter_people": "人物を絞り込み",
"filter_places": "場所をフィルター",
"find_them_fast": "名前で検索して素早く発見",
"first": "はじめ",
"fix_incorrect_match": "間違った一致を修正",
"folder": "フォルダー",
"folder_not_found": "フォルダーが見つかりませんでした",
@@ -1065,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "この機能は動作のためにGoogleのリソースを読み込みます。",
"general": "一般",
"geolocation_instruction_all_have_location": "この日付のすべての項目に位置情報がすでについています。すべての項目を表示を試みるか別の日付を選択してください",
"geolocation_instruction_location": "位置情報付きの項目をクリックして、その位置情報を利用します。あるいは、地図上の地点を直接選ぶことも可能です",
"geolocation_instruction_no_date": "日付を選択して、その日の写真や動画の位置情報を管理しましょう",
"geolocation_instruction_no_photos": "この日付に写真や動画が無いようです。別の日付を選択してみてください",
"get_help": "助けを求める",
"get_wifiname_error": "Wi-Fiの名前(SSID)が入手できませんでした。Wi-Fiに繋がってるのと必要な権限を許可したか確認してください",
"getting_started": "はじめる",
"go_back": "戻る",
"go_to_folder": "フォルダへ",
"go_to_search": "検索へ",
"gps": "GPS",
"gps_missing": "GPS無",
"grant_permission": "許可する",
"group_albums_by": "これでアルバムをグループ化…",
"group_country": "国でグループ化",
@@ -1176,6 +1193,7 @@
"language_search_hint": "言語を検索",
"language_setting_description": "優先言語を選択してください",
"large_files": "大きいサイズのファイル",
"last": "最後",
"last_seen": "最新の活動",
"latest_version": "最新バージョン",
"latitude": "緯度",
@@ -1253,6 +1271,7 @@
"main_branch_warning": "開発版を使っているようです。リリース版の使用を強く推奨します!",
"main_menu": "メインメニュー",
"make": "メーカー",
"manage_geolocation": "位置情報を編集",
"manage_shared_links": "共有済みのリンクを管理",
"manage_sharing_with_partners": "パートナーとの共有を管理します",
"manage_the_app_settings": "アプリの設定を管理します",
@@ -1416,7 +1435,7 @@
"partner_page_no_more_users": "追加できるユーザーがもういません",
"partner_page_partner_add_failed": "パートナーの追加に失敗",
"partner_page_select_partner": "パートナーを選択",
"partner_page_shared_to_title": "次のユーザーと共有します: ",
"partner_page_shared_to_title": "次のユーザーと共有します:",
"partner_page_stop_sharing_content": "{partner}は今後あなたの写真へアクセスできなくなります",
"partner_sharing": "パートナとの共有",
"partners": "パートナー",
@@ -1499,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "アプリが更新されてません。最新のバージョンに更新してください",
"profile_drawer_client_server_up_to_date": "すべて最新版です",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "読み取り専用モードが有効です。ユーザーのアイコンをダブルタップして読み取り専用モードを解除してください。",
"profile_drawer_server_out_of_date_major": "サーバーが更新されてません。最新のバージョンに更新してください",
"profile_drawer_server_out_of_date_minor": "サーバーが更新されてません。最新のバージョンに更新してください",
"profile_image_of_user": "{user} のプロフィール画像",
@@ -1544,6 +1564,8 @@
"rating_description": "情報欄にEXIFの評価を表示",
"reaction_options": "リアクションの選択",
"read_changelog": "変更履歴を読む",
"readonly_mode_disabled": "読み取り専用モード無効",
"readonly_mode_enabled": "読み取り専用モード有効",
"reassign": "再割り当て",
"reassigned_assets_to_existing_person": "{count, plural, one {#個} other {#個}}のアセットを{name, select, null {既存の人物} other {{name}}}に再割り当てしました",
"reassigned_assets_to_new_person": "{count, plural, one {#個} other {#個}}のアセットを新しい人物に割り当てました",
@@ -1713,11 +1735,12 @@
"select_user_for_sharing_page_err_album": "アルバム作成に失敗",
"selected": "選択済み",
"selected_count": "{count, plural, other {#個選択済み}}",
"selected_gps_coordinates": "選択された位置情報",
"send_message": "メッセージを送信",
"send_welcome_email": "ウェルカムメールを送信",
"server_endpoint": "サーバーエンドポイント",
"server_info_box_app_version": "アプリのバージョン",
"server_info_box_server_url": " サーバーのURL",
"server_info_box_server_url": "サーバーのURL",
"server_offline": "サーバーがオフラインです",
"server_online": "サーバーがオンラインです",
"server_privacy": "サーバープライバシー",
@@ -1823,8 +1846,10 @@
"shift_to_permanent_delete": "⇧を押してアセットを完全に削除",
"show_album_options": "アルバム設定を表示",
"show_albums": "アルバムを表示",
"show_all_assets": "すべての項目を表示",
"show_all_people": "全ての人物を表示",
"show_and_hide_people": "人物を表示/非表示",
"show_assets_without_location": "位置情報無しの項目を表示",
"show_file_location": "ファイルの場所を表示",
"show_gallery": "ギャラリーを表示",
"show_hidden_people": "非表示の人物を表示",
@@ -1932,7 +1957,9 @@
"to_change_password": "パスワードを変更",
"to_favorite": "お気に入り",
"to_login": "ログイン",
"to_multi_select": "複数選択",
"to_parent": "上位の階層へ",
"to_select": "選択",
"to_trash": "ゴミ箱",
"toggle_settings": "設定をトグル",
"total": "合計",
@@ -1982,6 +2009,7 @@
"unstacked_assets_count": "{count, plural, one {#個のアセット} other {#個のアセット}}をスタックから解除しました",
"untagged": "タグを解除",
"up_next": "次へ",
"update_location_action_prompt": "{count}項目を右記の位置情報にアップデートします:",
"updated_at": "更新",
"updated_password": "パスワードを更新しました",
"upload": "アップロード",
@@ -2006,6 +2034,7 @@
"use_biometric": "生体認証をご利用ください",
"use_current_connection": "現在の接続情報を使用",
"use_custom_date_range": "代わりにカスタム日付範囲を使用",
"use_this_location": "クリックして位置情報を使う",
"user": "ユーザー",
"user_has_been_deleted": "このユーザーは削除されました",
"user_id": "ユーザーID",

View File

@@ -1,11 +1,21 @@
{
"about": "Туралы",
"account": "Тіркелгі",
"add": "қосу",
"add_a_description": "сипаттаманы қосу",
"add_a_location": "суретті түсірген жерді қосы",
"add_a_name": "Атын қосу",
"add_birthday": "Туған күнін қосу",
"add_location": "жерді қосу",
"add_more_users": "қосымша адамдарды тіркеу",
"add_partner": "жолдасты қосу",
"add_photos": "суреттерді қосу",
"add_tag": "тегті қосу",
"add_to": "қосу…",
"add_to_album": "альбомға қосу",
"add_to_album_bottom_sheet_added": "{album}'ға қосылған",
"add_to_album_bottom_sheet_already_exists": "Онсыз да {album} болған",
"add_to_albums": "альбомдарға қосу",
"add_to_shared_album": "бөліскен альбомға қосу",
"add_url": "URL таңдау",
"added_to_archive": "Архивке жіберілген",

View File

@@ -48,12 +48,12 @@
"backup_database": "데이터베이스 덤프 생성",
"backup_database_enable_description": "데이터베이스 덤프 활성화",
"backup_keep_last_amount": "보관할 이전 덤프 수",
"backup_onboarding_1_description": "클라우드나 다른 물리적 위치에 보관하는 외부 백업본",
"backup_onboarding_2_description": "서로 다른 장치에 저장된 로컬 사본. 여기에는 원본 파일과 해당 파일의 로컬 백업이 포함됩니다.",
"backup_onboarding_3_description": "원본 파일을 포함해 데이터의 총 사본은 3개입니다. 이 중 하나는 외부 백업이고 2개는 로컬 백업입니다.",
"backup_onboarding_description": "데이터를 보호하기 위해 <backblaze-link>3-2-1 백업 전략</backblaze-link>을 권장합니다. 완전한 백업을 위해 업로드한 사진 및 동영상뿐 아니라 Immich 데이터베이스도 함께 보관해야 합니다.",
"backup_onboarding_footer": "Immich 백업에 대한 자세한 내용은 <link>문서</link>를 참조하세요.",
"backup_onboarding_parts_title": "3-2-1 백업에는 다음이 포함됩니다:",
"backup_onboarding_1_description": "개는 클라우드나 다른 물리적 위치에 보관합니다.",
"backup_onboarding_2_description": "개는 서로 다른 로컬 장치에 보관하고,",
"backup_onboarding_3_description": "개의 데이터 사본을 만듭니다.",
"backup_onboarding_description": "소중한 데이터를 안전하게 보호하기 위해 <backblaze-link>3-2-1 백업 전략</backblaze-link> 사용을 권장합니다. Immich를 백업할 때 업로드한 사진 및 동영상뿐 아니라 데이터베이스도 함께 보관해야 한다는 점을 잊지 마세요.",
"backup_onboarding_footer": "Immich 백업에 대한 자세한 내용은 <link>공식 문서</link>를 참조하세요.",
"backup_onboarding_parts_title": "3-2-1 백업이란:",
"backup_onboarding_title": "백업",
"backup_settings": "데이터베이스 덤프 설정",
"backup_settings_description": "데이터베이스 덤프 주기와 보관 기간을 설정합니다.",
@@ -115,7 +115,7 @@
"library_scanning_description": "주기적인 라이브러리 스캔을 구성합니다.",
"library_scanning_enable_description": "주기적인 라이브러리 스캔 활성화",
"library_settings": "외부 라이브러리",
"library_settings_description": "외부 라이브러리 변경 감시 및 스캔 주기를 설정합니다.",
"library_settings_description": "외부 라이브러리 스캔 및 감시 기능을 설정합니다.",
"library_tasks_description": "외부 라이브러리에서 새 항목 또는 변경된 항목을 스캔",
"library_watching_enable_description": "외부 라이브러리 파일 변경 감시",
"library_watching_settings": "라이브러리 감시 (실험적)",
@@ -146,7 +146,7 @@
"machine_learning_min_recognized_faces": "최소 인식 얼굴",
"machine_learning_min_recognized_faces_description": "인물을 생성하기 위해 인식할 얼굴 수의 최솟값을 설정합니다. 값이 높으면 얼굴 인식이 정확해지지만 감지된 얼굴이 인물에 할당되지 않을 가능성이 증가합니다.",
"machine_learning_settings": "기계 학습 설정",
"machine_learning_settings_description": "기계 학습 사용할 모델 세부 설정을 관리합니다.",
"machine_learning_settings_description": "기계 학습 사용할 모델 세부 설정을 관리합니다.",
"machine_learning_smart_search": "스마트 검색",
"machine_learning_smart_search_description": "CLIP 임베딩으로 의미 기반 검색을 사용합니다.",
"machine_learning_smart_search_enabled": "스마트 검색 활성화",
@@ -157,7 +157,7 @@
"map_dark_style": "다크 스타일",
"map_enable_description": "지도 기능 활성화",
"map_gps_settings": "지도 및 GPS 설정",
"map_gps_settings_description": "지도 사용자 정의하거나 역지오코딩 사용 여부를 관리합니다.",
"map_gps_settings_description": "지도 사용자 정의 역지오코딩 설정을 관리합니다.",
"map_implications": "지도 기능은 외부 타일 서비스(tiles.immich.cloud)에 의존합니다.",
"map_light_style": "라이트 스타일",
"map_manage_reverse_geocoding_settings": "<link>역지오코딩</link> 설정을 관리합니다.",
@@ -174,23 +174,23 @@
"metadata_faces_import_setting": "얼굴 가져오기 활성화",
"metadata_faces_import_setting_description": "사이드카 파일의 이미지 EXIF 데이터에서 얼굴 가져오기",
"metadata_settings": "메타데이터 설정",
"metadata_settings_description": "메타데이터 사용 설정을 관리합니다.",
"metadata_settings_description": "메타데이터 설정을 관리합니다.",
"migration_job": "마이그레이션",
"migration_job_description": "각 항목의 섬네일 및 인물의 얼굴을 최신 폴더 구조 마이그레이션",
"nightly_tasks_cluster_faces_setting_description": "새로 감지된 얼굴에 대하여 얼굴 인식을 실행",
"nightly_tasks_cluster_new_faces_setting": "새 얼굴 묶기",
"nightly_tasks_database_cleanup_setting": "데이터베이스 정리 작업",
"nightly_tasks_database_cleanup_setting_description": "데이터베이스에서 오래되었거나 만료된 데이터 정리",
"nightly_tasks_generate_memories_setting": "메모리 생성",
"nightly_tasks_generate_memories_setting_description": "항목에서 새로운 메모리 만들기",
"migration_job_description": "각 항목의 섬네일 및 인물의 얼굴을 최신 폴더 구조에 맞게 마이그레이션",
"nightly_tasks_cluster_faces_setting_description": "새로 감지된 얼굴의 인식 작업을 실행합니다.",
"nightly_tasks_cluster_new_faces_setting": "새 얼굴 그룹화",
"nightly_tasks_database_cleanup_setting": "데이터베이스 정리",
"nightly_tasks_database_cleanup_setting_description": "데이터베이스에서 오래되었거나 불필요한 데이터 정리합니다.",
"nightly_tasks_generate_memories_setting": "추억 생성",
"nightly_tasks_generate_memories_setting_description": "사진 및 동영상에서 새로운 추억 모음을 생성합니다.",
"nightly_tasks_missing_thumbnails_setting": "누락된 섬네일 생성",
"nightly_tasks_missing_thumbnails_setting_description": "섬네일이 없는 항목을 섬네일 생성 대기열에 추가",
"nightly_tasks_settings": "야간 작업 설정",
"nightly_tasks_settings_description": "간에 수행되는 작업을 관리합니다.",
"nightly_tasks_missing_thumbnails_setting_description": "섬네일 생성 대기열에 누락된 항목을 추가합니다.",
"nightly_tasks_settings": "정기 작업 설정",
"nightly_tasks_settings_description": "특정 시간에 수행되는 작업을 관리합니다.",
"nightly_tasks_start_time_setting": "시작 시간",
"nightly_tasks_start_time_setting_description": "서버가 야간 작업을 시작 시간",
"nightly_tasks_start_time_setting_description": "서버가 작업을 시작하는 시간",
"nightly_tasks_sync_quota_usage_setting": "사용량 동기화",
"nightly_tasks_sync_quota_usage_setting_description": "현재 사용량 기반으로 사용자의 저장 공간 할당량 업데이트",
"nightly_tasks_sync_quota_usage_setting_description": "사용자의 저장 공간 할당량을 현재 사용량 기반으로 갱신합니다.",
"no_paths_added": "추가된 경로 없음",
"no_pattern_added": "추가된 규칙 없음",
"note_apply_storage_label_previous_assets": "참고: 이전에 업로드한 항목에도 스토리지 레이블을 적용하려면 다음을 실행합니다,",
@@ -221,14 +221,14 @@
"oauth_mobile_redirect_uri": "모바일 리다이렉트 URI",
"oauth_mobile_redirect_uri_override": "모바일 리다이렉트 URI 오버라이드",
"oauth_mobile_redirect_uri_override_description": "OAuth 공급자가 ''{callback}'' 등의 모바일 URI를 허용하지 않는 경우 활성화하세요.",
"oauth_role_claim": "역할 선택",
"oauth_role_claim_description": "요청한 항목을 사용자의 역할로 자동 설정합니다. '사용자' 또는 '관리자'를 선택할 수 있습니다.",
"oauth_role_claim": "역할 클레임",
"oauth_role_claim_description": "요청한 클레임을 사용자의 역할로 자동 설정합니다. 'user' 또는 'admin'을 선택할 수 있습니다.",
"oauth_settings": "OAuth",
"oauth_settings_description": "OAuth 로그인 설정을 관리합니다.",
"oauth_settings_more_details": "이 기능에 대한 자세한 내용은 <link>문서</link>를 참조하세요.",
"oauth_storage_label_claim": "스토리지 레이블 선택",
"oauth_storage_label_claim_description": "요청한 값을 사용자 스토리지 레이블로 자동 설정합니다.",
"oauth_storage_quota_claim": "스토리지 할당량 선택",
"oauth_storage_label_claim": "스토리지 라벨 클레임",
"oauth_storage_label_claim_description": "클레임의 값을 사용자 스토리지 레이블로 자동 설정합니다.",
"oauth_storage_quota_claim": "스토리지 용량 클레임",
"oauth_storage_quota_claim_description": "요청한 값을 사용자 스토리지 할당량으로 자동 설정합니다.",
"oauth_storage_quota_default": "기본 스토리지 할당량 (GiB)",
"oauth_storage_quota_default_description": "입력하지 않은 경우 사용할 GiB 단위의 할당량",
@@ -274,7 +274,7 @@
"storage_template_onboarding_description_v2": "활성화하면 사용자 정의 템플릿에 따라 파일이 자동으로 분류됩니다. 자세한 내용은 <link>문서</link>를 참조하세요.",
"storage_template_path_length": "대략적인 경로 길이 제한: <b>{length, number}</b>/{limit, number}",
"storage_template_settings": "스토리지 템플릿",
"storage_template_settings_description": "업로드된 항목의 폴더 구조 및 파일명을 관리합니다.",
"storage_template_settings_description": "업로드된 항목의 파일명 및 폴더 구조를 관리합니다.",
"storage_template_user_label": "사용자의 스토리지 레이블: <code>{label}</code>",
"system_settings": "시스템 설정",
"tag_cleanup_job": "태그 정리",
@@ -318,7 +318,7 @@
"transcoding_encoding_options": "인코딩 옵션",
"transcoding_encoding_options_description": "코덱, 해상도, 품질 및 기타 인코딩 옵션을 설정합니다.",
"transcoding_hardware_acceleration": "하드웨어 가속",
"transcoding_hardware_acceleration_description": "(실험적) 트랜스코딩 속도 빨라지지만 동일 비트레이트에서 품질이 상대적으로 저하될 수 있습니다.",
"transcoding_hardware_acceleration_description": "(실험적) 트랜스코딩 속도 빨라지지만 동일 비트레이트에서 품질이 상대적으로 저하될 수 있습니다.",
"transcoding_hardware_decoding": "하드웨어 디코딩",
"transcoding_hardware_decoding_setting_description": "종단간 가속으로 디코딩부터 인코딩까지 전체 과정을 가속합니다. 일부 동영상에서는 작동하지 않을 수 있습니다.",
"transcoding_max_b_frames": "최대 B-프레임",
@@ -337,7 +337,7 @@
"transcoding_reference_frames": "참조 프레임 수",
"transcoding_reference_frames_description": "특정 프레임을 압축할 때 참조할 프레임 수를 설정합니다. 값을 높이면 압축 효율이 향상되지만 인코딩 속도가 느려집니다. 0을 입력하면 자동으로 설정합니다.",
"transcoding_required_description": "허용 포맷이 아닌 동영상만 트랜스코딩",
"transcoding_settings": "동영상 트랜스코딩 설정",
"transcoding_settings": "트랜스코딩 설정",
"transcoding_settings_description": "트랜스코딩할 동영상 및 처리 방법을 관리합니다.",
"transcoding_target_resolution": "목표 해상도",
"transcoding_target_resolution_description": "해상도를 높이면 세부 정보가 더 많이 보존되지만, 인코딩 시간이 늘어나고 파일 크기가 커져 앱 반응 속도가 느려질 수 있습니다.",
@@ -348,16 +348,16 @@
"transcoding_tone_mapping": "톤 매핑",
"transcoding_tone_mapping_description": "HDR 영상을 SDR로 변환할 때 사용할 톤 매핑 알고리즘을 설정합니다. Hable은 디테일, Mobius는 색상, Reinhard는 밝기에 중점을 두며 '비활성화'는 톤 매핑을 사용하지 않습니다.",
"transcoding_transcode_policy": "트랜스코드 기준",
"transcoding_transcode_policy_description": "동영상을 트랜스코딩할 기준을 설정합니다. HDR 영상은 트랜스코딩이 비활성화된 경우 제외하면 항상 트랜스코딩됩니다.",
"transcoding_transcode_policy_description": "동영상을 트랜스코딩할 기준을 설정합니다. HDR 영상은 항상 트랜스코딩됩니다. (트랜스코딩이 비활성화된 경우 제외)",
"transcoding_two_pass_encoding": "2패스 인코딩",
"transcoding_two_pass_encoding_setting_description": "2패스 인코딩을 사용해 인코딩 품질을 높입니다. H.264 및 HEVC의 경우 CRF를 무시하고 최대 비트레이트 기반의 비트레이트 범위를 사용합니다. VP9의 경우 최대 비트레이트를 비활성화하면 CRF를 사용할 수 있습니다.",
"transcoding_video_codec": "동영상 코덱",
"transcoding_video_codec_description": "VP9는 효율적이고 웹 호환성이 높으나 트랜스코딩이 오래 걸립니다. HEVC 역시 비슷하 웹 호환성이 낮습니다. H.264는 호환성이 가장 높지만 처리된 파일의 크기가 크고, AV1은 가장 효율적이지만 오래된 기기와의 호환성이 낮습니다.",
"transcoding_video_codec_description": "VP9는 효율적이고 웹 호환성이 높으나 트랜스코딩이 오래 걸립니다. HEVC는 VP9와 비슷하지만 웹 호환성이 낮습니다. H.264는 호환성이 가장 높으나 처리된 파일의 크기가 크고, AV1은 가장 효율적이지만 오래된 기기와의 호환성이 낮습니다.",
"trash_enabled_description": "휴지통 기능 활성화",
"trash_number_of_days": "휴지통 보관 기간",
"trash_number_of_days_description": "항목을 영구적으로 삭제하기 전까지 휴지통에 보관할 기간(일)",
"trash_settings": "휴지통 설정",
"trash_settings_description": "휴지통 기능 설정을 관리합니다.",
"trash_settings_description": "휴지통 기능 설정합니다.",
"unlink_all_oauth_accounts": "모든 OAuth 계정 연결 해제",
"unlink_all_oauth_accounts_description": "새 공급자로 이전하려면 먼저 모든 OAuth 계정의 연결을 해제해야 합니다.",
"unlink_all_oauth_accounts_prompt": "모든 OAuth 계정 연결을 해제하시겠습니까? 각 사용자의 OAuth ID를 초기화하며 되돌릴 수 없습니다.",
@@ -379,7 +379,7 @@
"version_check_enabled_description": "버전 확인 활성화",
"version_check_implications": "주기적으로 Github에 요청을 보내 새 버전을 확인합니다.",
"version_check_settings": "버전 확인",
"version_check_settings_description": "새 버전 알림 기능을 관리합니다.",
"version_check_settings_description": "새 버전 확인 및 알림 기능을 관리합니다.",
"video_conversion_job": "동영상 트랜스코드",
"video_conversion_job_description": "다양한 브라우저 및 기기와의 호환성을 위한 동영상 트랜스코드"
},
@@ -396,6 +396,7 @@
"advanced_settings_prefer_remote_title": "서버 이미지 선호",
"advanced_settings_proxy_headers_subtitle": "Immich가 네트워크 요청 시 사용할 프록시 헤더를 정의합니다.",
"advanced_settings_proxy_headers_title": "프록시 헤더",
"advanced_settings_readonly_mode_title": "읽기 전용 모드",
"advanced_settings_self_signed_ssl_subtitle": "서버 엔드포인트의 SSL 인증서 검증을 건너뜁니다. 자체 서명 인증서를 사용하는 경우 활성화하세요.",
"advanced_settings_self_signed_ssl_title": "자체 서명된 SSL 인증서 허용",
"advanced_settings_sync_remote_deletions_subtitle": "웹에서 삭제하거나 복원한 항목을 이 기기에서도 자동으로 처리하도록 설정",
@@ -439,8 +440,8 @@
"albums": "앨범",
"albums_count": "앨범 {count, plural, one {{count, number}개} other {{count, number}개}}",
"albums_default_sort_order": "기본 앨범 정렬 순서",
"albums_default_sort_order_description": "새 앨범 생성할 때 기본적으로 항목을 정렬할 순서.",
"albums_feature_description": "다른 사용자와 공유할 수 있는 항목 모음.",
"albums_default_sort_order_description": "새 앨범 생성 시 적용되는 기본 정렬을 설정합니다.",
"albums_feature_description": "여러 사진과 동영상을 한곳에 모아 둘 수 있습니다.",
"albums_on_device_count": "기기의 앨범 ({count}개)",
"all": "모두",
"all_albums": "모든 앨범",
@@ -461,13 +462,14 @@
"app_bar_signout_dialog_title": "로그아웃",
"app_settings": "앱 설정",
"appears_in": "다음 앨범에 포함됨",
"apply_count": "적용 ({count, number})",
"archive": "보관함",
"archive_action_prompt": "보관함으로 항목 {count}개 이동됨",
"archive_or_unarchive_photo": "보관 처리 또는 해제",
"archive_page_no_archived_assets": "보관된 항목 없음",
"archive_page_title": "보관함 ({count})",
"archive_size": "압축 파일 크기",
"archive_size_description": "다운로드할 압축 파일의 크기 구성 (GiB 단위)",
"archive_size_description": "지정한 크기를 초과하면 여러 개의 파일로 분할됩니다. (GiB 단위)",
"archived": "보관함",
"archived_count": "보관함으로 항목 {count, plural, other {#개}} 이동됨",
"are_these_the_same_person": "동일한 인물인가요?",
@@ -500,7 +502,7 @@
"assets": "항목",
"assets_added_count": "항목 {count, plural, one {#개} other {#개}} 추가됨",
"assets_added_to_album_count": "앨범에 항목 {count, plural, one {#개} other {#개}} 추가됨",
"assets_added_to_albums_count": "{assetTotal, plural, one {항목 #개} other {항목 #개}}가 앨범 {albumTotal}개에 추가됨",
"assets_added_to_albums_count": "{albumTotal, plural, one {앨범에} other {앨범 #개}} {assetTotal, plural, one {항목이} other {항목 #개가}} 추가됨",
"assets_cannot_be_added_to_album_count": "{count, plural, one {앨범에 항목을} other {일부 항목을 앨범에}} 추가할 수 없습니다.",
"assets_cannot_be_added_to_albums": "{count, plural, one {항목을} other {항목들을}} 어느 앨범에도 추가할 수 없습니다.",
"assets_count": "{count, plural, one {#개} other {#개}} 항목",
@@ -520,12 +522,12 @@
"assets_trashed_from_server": "서버 항목 {count}개 휴지통으로 이동됨",
"assets_were_part_of_album_count": "앨범에 이미 포함된 {count, plural, one {항목} other {항목}}입니다.",
"assets_were_part_of_albums_count": "이미 앨범에 포함된 {count, plural, one {항목} other {항목}}입니다.",
"authorized_devices": "인증된 기기",
"authorized_devices": " 기기",
"automatic_endpoint_switching_subtitle": "지정된 Wi-Fi가 사용 가능한 경우 내부망으로 연결하고, 그 외의 경우 다른 연결 방식을 사용합니다.",
"automatic_endpoint_switching_title": "자동 URL 전환",
"autoplay_slideshow": "슬라이드 쇼 자동 재생",
"back": "뒤로",
"back_close_deselect": "뒤로, 닫기, 선택 취소",
"back_close_deselect": "뒤로, 닫기 또는 선택 해제",
"background_location_permission": "백그라운드 위치 권한",
"background_location_permission_content": "Immich가 백그라운드에서 실행 중일 때 네트워크를 전환하려면 Wi-Fi 네트워크 이름을 확인해야 하며, 이를 위해 '정확한 위치' 권한을 항상 허용해야 합니다.",
"backup": "백업",
@@ -723,14 +725,14 @@
"create": "생성",
"create_album": "앨범 생성",
"create_album_page_untitled": "제목 없음",
"create_library": "라이브러리 생성",
"create_library": "라이브러리",
"create_link": "링크 생성",
"create_link_to_share": "공유 링크 생성",
"create_link_to_share_description": "링크가 있는 경우 누구나 선택한 사진을 볼 수 있습니다.",
"create_new": "새로 만들기",
"create_new_person": "인물 생성",
"create_new_person_hint": "선택한 항목의 인물을 새 인물로 변경",
"create_new_user": "사용자 계정 생성",
"create_new_user": "사용자 생성",
"create_shared_album_page_share_add_assets": "항목 추가",
"create_shared_album_page_share_select_photos": "사진 선택",
"create_shared_link": "공유 링크 생성",
@@ -811,7 +813,7 @@
"display_options": "표시 옵션",
"display_order": "표시 순서",
"display_original_photos": "원본 사진 표시",
"display_original_photos_setting_description": "원본 사진이 웹과 호환되는 경우 섬네일이 아닌 원본을 표시합니다. 사진 표시되는 속도가 느려질 수 있습니다.",
"display_original_photos_setting_description": "항목을 표시할 때 웹과 호환되는 경우 원본을 표시합니다. 사진 표시 속도가 느려질 수 있습니다.",
"do_not_show_again": "이 메시지를 다시 표시하지 않음",
"documentation": "문서",
"done": "완료",
@@ -823,12 +825,12 @@
"download_error": "다운로드 오류",
"download_failed": "다운로드 실패",
"download_finished": "다운로드가 완료되었습니다.",
"download_include_embedded_motion_videos": "내장된 동영상",
"download_include_embedded_motion_videos_description": "모션 포토에 내장된 동영상을 별 파일로 포함",
"download_include_embedded_motion_videos": "모션 포토 영상",
"download_include_embedded_motion_videos_description": "모션 포토에 포함된 동영상을 별도의 파일로 분리해 저장합니다.",
"download_notfound": "다운로드할 수 없음",
"download_paused": "다운로드 일시 중지됨",
"download_settings": "다운로드",
"download_settings_description": "파일 다운로드 방식 및 관련 설정을 관리합니다.",
"download_settings_description": "파일 다운로드 설정을 관리합니다.",
"download_started": "다운로드가 시작되었습니다.",
"download_sucess": "다운로드가 완료되었습니다.",
"download_sucess_android": "항목이 DCIM/Immich 폴더에 다운로드되었습니다.",
@@ -1060,14 +1062,14 @@
"filetype": "파일 형식",
"filter": "필터",
"filter_people": "인물 필터",
"filter_places": "장소 필터",
"filter_places": "장소 필터",
"find_them_fast": "이름으로 검색하여 빠르게 찾기",
"first": "첫 번째",
"fix_incorrect_match": "잘못된 분류 수정",
"folder": "폴더",
"folder_not_found": "폴더를 찾을 수 없음",
"folders": "폴더",
"folders_feature_description": "파일 시스템에 있는 사진과 동영상을 폴더 로 탐색",
"folders_feature_description": "파일 시스템 사진과 동영상을 폴더 보기로 탐색합니다.",
"forgot_pin_code_question": "PIN 번호를 잊어버렸나요?",
"forward": "앞으로",
"gcast_enabled": "구글 캐스트",
@@ -1182,7 +1184,7 @@
"language_no_results_subtitle": "다른 검색어를 사용해 보세요.",
"language_no_results_title": "결과 없음",
"language_search_hint": "언어 검색...",
"language_setting_description": "선호하는 언어 선택",
"language_setting_description": "사용할 언어 선택하세요.",
"large_files": "큰 파일",
"last": "마지막",
"last_seen": "최근 활동",
@@ -1258,7 +1260,7 @@
"longitude": "경도",
"look": "보기",
"loop_videos": "동영상 반복",
"loop_videos_description": "상세 보기에서 자동으로 동영상을 반복 재생합니다.",
"loop_videos_description": "상세 보기에서 영상을 반복 재생합니다.",
"main_branch_warning": "개발 버전을 사용 중입니다. 정식 릴리스 버전 사용을 권장합니다!",
"main_menu": "메인 메뉴",
"make": "제조사",
@@ -1266,7 +1268,7 @@
"manage_sharing_with_partners": "공유할 파트너를 초대하거나 제거합니다.",
"manage_the_app_settings": "앱 동작 및 표시 환경을 사용자 정의합니다.",
"manage_your_account": "사용자의 계정 정보를 확인하고 변경합니다.",
"manage_your_api_keys": "API 키를 생성, 수정 또는 삭제하거나 권한을 관리합니다.",
"manage_your_api_keys": "API 키를 생성, 삭제하거나 권한을 관리합니다.",
"manage_your_devices": "현재 로그인한 기기를 확인하고 로그아웃할 수 있습니다.",
"manage_your_oauth_connection": "OAuth 연결을 관리합니다.",
"map": "지도",
@@ -1448,8 +1450,8 @@
"people_edits_count": "인물 {count, plural, one {#명} other {#명}}이 수정되었습니다.",
"people_feature_description": "사진과 동영상을 인물 그룹별로 탐색",
"people_sidebar_description": "사이드바에 인물 링크 표시",
"permanent_deletion_warning": "영구적으로 삭제 경고",
"permanent_deletion_warning_setting_description": "항목을 영구적으로 삭제할 때 경고 메시지 표시",
"permanent_deletion_warning": "영구 삭제 경고",
"permanent_deletion_warning_setting_description": "항목을 완전히 삭제하기 전 경고 메시지 표시합니다.",
"permanently_delete": "영구 삭제",
"permanently_delete_assets_count": "{count, plural, one {항목} other {항목}} 영구 삭제",
"permanently_delete_assets_prompt": "{count, plural, one {이 항목을} other {항목 <b>#</b>개를}} 영구적으로 삭제하시겠습니까? {count, plural, one {항목이} other {항목이}} 앨범에 포함된 경우 앨범에서 제거됩니다.",
@@ -1496,10 +1498,10 @@
"preview": "미리 보기",
"previous": "이전",
"previous_memory": "이전 추억",
"previous_or_next_day": "다음/이전 날",
"previous_or_next_month": "다음/이전 달",
"previous_or_next_photo": "이전/다음 사진",
"previous_or_next_year": "다음/이전 해",
"previous_or_next_day": "이전/다음",
"previous_or_next_month": "이전/다음",
"previous_or_next_photo": "이전/다음 사진으로",
"previous_or_next_year": "이전/다음 연도로",
"primary": "기본",
"privacy": "개인정보",
"profile": "프로필",
@@ -1760,7 +1762,7 @@
"setting_notifications_total_progress_subtitle": "전체 업로드 진행률 (완료/총 항목)",
"setting_notifications_total_progress_title": "백그라운드 백업 전체 진행률 표시",
"setting_video_viewer_looping_title": "반복",
"setting_video_viewer_original_video_subtitle": "서버에서 동영상 스트리밍할 때, 트랜스코딩된 버전이 있더라도 원본을 재생합니다. 이로 인해 버퍼링이 발생할 수 있습니다. 기기에 있는 영상은 이 설정과 관계없이 항상 원본 화질로 재생됩니다.",
"setting_video_viewer_original_video_subtitle": "동영상 스트리밍 트랜스코딩된 파일 대신 원본을 재생합니다. 재생 시 버퍼링이 발생할 수 있습니다. 로컬에 있는 영상은 항상 원본 화질로 재생됩니다.",
"setting_video_viewer_original_video_title": "원본 동영상 강제 사용",
"settings": "설정",
"settings_require_restart": "설정을 적용하려면 Immich를 다시 시작해야 합니다.",
@@ -1841,7 +1843,7 @@
"show_in_timeline_setting_description": "타임라인에 이 사용자의 사진과 동영상을 표시",
"show_keyboard_shortcuts": "키보드 단축키 표시",
"show_metadata": "메타데이터 표시",
"show_or_hide_info": "정보 표시/숨기기",
"show_or_hide_info": "정보 표시 또는 숨기기",
"show_password": "비밀번호 표시",
"show_person_options": "인물 옵션 표시",
"show_progress_bar": "진행 표시줄 표시",
@@ -1917,8 +1919,8 @@
"tap_to_run_job": "탭하여 작업 실행",
"template": "템플릿",
"theme": "테마",
"theme_selection": "테마 설정",
"theme_selection_description": "브라우저의 시스템 설정에 따라 자동으로 라이트/다크 테마 설정",
"theme_selection": "테마 선택",
"theme_selection_description": "시스템의 다크 모드 설정에 따라 테마를 자동으로 적용합니다.",
"theme_setting_asset_list_storage_indicator_title": "타일에 서버 동기화 상태 표시",
"theme_setting_asset_list_tiles_per_row_title": "한 줄에 표시할 항목 수 ({count})",
"theme_setting_colorful_interface_subtitle": "기본 색상을 배경에 적용",
@@ -1941,7 +1943,9 @@
"to_change_password": "비밀번호 변경",
"to_favorite": "즐겨찾기",
"to_login": "로그인",
"to_multi_select": "다중 선택",
"to_parent": "상위 항목으로",
"to_select": "선택",
"to_trash": "삭제",
"toggle_settings": "설정 변경",
"total": "전체",
@@ -1954,7 +1958,7 @@
"trash_emptied": "휴지통을 비웠습니다.",
"trash_no_results_message": "삭제된 사진과 동영상이 여기에 표시됩니다.",
"trash_page_delete_all": "모두 삭제",
"trash_page_empty_trash_dialog_content": "휴지통을 비우시겠습니까? 휴지통의 모든 항목이 Immich에서 영구적으로 제됩니다.",
"trash_page_empty_trash_dialog_content": "휴지통을 비우시겠습니까? 휴지통의 모든 항목이 Immich에서 영구적으로 제됩니다.",
"trash_page_info": "휴지통으로 이동된 항목은 {days}일 후 영구적으로 삭제됩니다.",
"trash_page_no_assets": "휴지통이 비어 있음",
"trash_page_restore_all": "모두 복원",
@@ -2023,11 +2027,11 @@
"user_pin_code_settings_description": "PIN 코드를 변경하거나 잊어버린 경우 초기화합니다.",
"user_privacy": "개인정보",
"user_purchase_settings": "구매",
"user_purchase_settings_description": "구매 설정을 관리하고 제품 키를 등록 또는 제거합니다.",
"user_purchase_settings_description": "구매 설정을 관리하고 키를 등록 제거합니다.",
"user_role_set": "{user}에게 {role} 역할 지정",
"user_usage_detail": "사용자 사용량 상세",
"user_usage_stats": "계정 사용량 통계",
"user_usage_stats_description": "사진 및 동영상의 총 개수와 범주별 현황을 확인합니다.",
"user_usage_stats": "사용량 통계",
"user_usage_stats_description": "계정의 전체 및 범주별 사용량을 확인합니다.",
"username": "계정명",
"users": "사용자",
"users_added_to_album_count": "사용자 {count, plural, one {#명} other {#명}}을 앨범에 추가했습니다.",
@@ -2041,8 +2045,8 @@
"version_history": "버전 기록",
"version_history_item": "{date} {version} 설치",
"video": "동영상",
"video_hover_setting": "마우스 오버로 영상 미리보기",
"video_hover_setting_description": "영상 섬네일 위에 마우스를 올리면 미리보기 재생니다. 비활성화해도 재생 아이콘에 마우스를 올려 미리볼 수 있습니다.",
"video_hover_setting": "섬네일 영상 미리보기",
"video_hover_setting_description": "섬네일 위에 마우스를 올리면 미리보기 재생니다. 비활성화해도 재생 아이콘에 마우스를 올려 미리볼 수 있습니다.",
"videos": "동영상",
"videos_count": "동영상 {count, plural, one {#개} other {#개}}",
"view": "보기",

View File

@@ -56,7 +56,7 @@
"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_reprocess_all_faces": "Vai tiešām vēlies atkārtoti apstrādāt visas sejas? Tas arī atiestatīs personas ar vārdiem.",
"confirm_user_password_reset": "Vai tiešām vēlaties atiestatīt lietotāja {user} paroli?",
"create_job": "Izveidot uzdevumu",
"cron_expression": "Cron izteiksme",
@@ -263,7 +263,7 @@
"albums_on_device_count": "Albumi ierīcē ({count})",
"all": "Viss",
"all_albums": "Visi albumi",
"all_people": "Visi cilvēki",
"all_people": "Visas personas",
"all_videos": "Visi video",
"allow_dark_mode": "Atļaut tumšo režīmu",
"allow_edits": "Atļaut labošanu",
@@ -303,7 +303,7 @@
"asset_uploaded": "Augšupielādēts",
"asset_uploading": "Augšupielādē…",
"asset_viewer_settings_title": "Aktīvu Skatītājs",
"assets": "aktīvi",
"assets": "Faili",
"assets_added_count": "Pievienoja {count, plural, one {# failu} other {# failus}}",
"assets_added_to_album_count": "Pievienoja albumam {count, plural, one {# failu} other {# failus}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Failu} other {Failus}} nevar pievienot albumam",
@@ -314,14 +314,15 @@
"assets_trashed_from_server": "{count} faili pārvietoti uz Immich servera atkritni",
"authorized_devices": "Autorizētās ierīces",
"automatic_endpoint_switching_title": "Automātiska URL pārslēgšana",
"autoplay_slideshow": "Automātiska slaidrādes atskaņošana",
"back": "Atpakaļ",
"backup": "Dublēšana",
"backup_album_selection_page_albums_device": "Albumi ierīcē ({count})",
"backup_album_selection_page_albums_tap": "Pieskarieties, lai iekļautu, veiciet dubultskārienu, lai izslēgtu",
"backup_album_selection_page_assets_scatter": "Aktīvi var būt izmētāti pa vairākiem albumiem. Tādējādi dublēšanas procesā albumus var iekļaut vai neiekļaut.",
"backup_album_selection_page_assets_scatter": "Faili var būt izmētāti pa vairākiem albumiem. Tādējādi dublēšanas procesā albumus var iekļaut vai neiekļaut.",
"backup_album_selection_page_select_albums": "Atlasīt albumus",
"backup_album_selection_page_selection_info": "Atlases informācija",
"backup_album_selection_page_total_assets": "Kopā unikālie aktīvi",
"backup_album_selection_page_total_assets": "Unikālo failu kopsumma",
"backup_all": "Viss",
"backup_background_service_backup_failed_message": "Neizdevās dublēt līdzekļus. Notiek atkārtota mēģināšana…",
"backup_background_service_connection_failed_message": "Neizdevās izveidot savienojumu ar serveri. Notiek atkārtota mēģināšana…",
@@ -370,7 +371,7 @@
"backup_controller_page_turn_on": "Ieslēgt priekšplāna dublēšanu",
"backup_controller_page_uploading_file_info": "Faila informācijas augšupielāde",
"backup_err_only_album": "Nevar noņemt vienīgo albumu",
"backup_info_card_assets": "aktīvi",
"backup_info_card_assets": "faili",
"backup_manual_cancelled": "Atcelts",
"backup_manual_in_progress": "Augšupielāde jau notiek. Mēģiniet pēc kāda laika atkārtoti",
"backup_manual_success": "Veiksmīgi",
@@ -406,7 +407,9 @@
"camera": "Fotokamera",
"cancel": "Atcelt",
"canceling": "Atceļ",
"cannot_merge_people": "Nevar apvienot cilvēkus",
"cannot_merge_people": "Nevar apvienot personas",
"cast": "Pārraidīt",
"cast_description": "Konfigurēt pieejamos pārraides galamērķus",
"change_date": "Mainīt datumu",
"change_description": "Mainīt aprakstu",
"change_display_order": "Mainīt attēlošanas secību",
@@ -421,7 +424,7 @@
"change_password_form_password_mismatch": "Paroles nesakrīt",
"change_password_form_reenter_new_password": "Atkārtoti ievadīt jaunu paroli",
"change_pin_code": "Nomainīt PIN kodu",
"choose_matching_people_to_merge": "Izvēlies atbilstošus cilvēkus apvienošanai",
"choose_matching_people_to_merge": "Izvēlies atbilstošas personas apvienošanai",
"city": "Pilsēta",
"clear": "Notīrīt",
"clear_all": "Notīrīt visu",
@@ -468,6 +471,8 @@
"created_at": "Izveidots",
"curated_object_page_title": "Lietas",
"current_pin_code": "Esošais PIN kods",
"custom_locale": "Pielāgota lokalizācija",
"custom_locale_description": "Formatēt datumus un skaitļus atbilstoši valodai un reģionam",
"custom_url": "Pielāgots URL",
"daily_title_text_date_year": "E, MMM dd, gggg",
"date_after": "Datums pēc",
@@ -483,6 +488,8 @@
"deduplication_criteria_2": "EXIF datu skaitu",
"deduplication_info": "Deduplicēšanas informācija",
"deduplication_info_description": "Lai automātiski atzīmētu failus un masveidā noņemtu dublikātus, mēs skatāmies uz:",
"default_locale": "Noklusējuma lokalizācija",
"default_locale_description": "Formatēt datumus un skaitļus atbilstoši pārlūka lokalizācijai",
"delete": "Dzēst",
"delete_album": "Dzēst albumu",
"delete_dialog_alert": "Šie vienumi tiks neatgriezeniski dzēsti no Immich un jūsu ierīces",
@@ -510,6 +517,7 @@
"direction": "Secība",
"discord": "Discord",
"display_order": "Attēlošanas secība",
"display_original_photos": "Rādīt oriģinālās fotogrāfijas",
"documentation": "Dokumentācija",
"done": "Gatavs",
"download": "Lejupielādēt",
@@ -556,6 +564,7 @@
"email_notifications": "E-pasta paziņojumi",
"empty_folder": "Šī mape ir tukša",
"empty_trash": "Iztukšot atkritni",
"enable_backup": "Ieslēgt dublēšanu",
"enable_biometric_auth_description": "Lai iespējotu biometrisko autentifikāciju, Ievadiet savu PIN kodu",
"end_date": "Beigu datums",
"enqueued": "Ierindots",
@@ -567,7 +576,21 @@
"errors": {
"cant_get_faces": "Nevar iegūt sejas",
"cant_search_people": "Neizdevās veikt peronu meklēšanu",
"exclusion_pattern_already_exists": "Šāds izslēgšanas paraugs jau pastāv.",
"failed_to_create_album": "Neizdevās izveidot albumu",
"failed_to_create_shared_link": "Neizdevās izvedot kopīgošanas saiti",
"failed_to_edit_shared_link": "Neizdevās labot kopīgoto saiti",
"failed_to_get_people": "Neizdevās iegūt personas",
"failed_to_keep_this_delete_others": "Neizdevās paturēt šo failu un dzēst pārējos failus",
"failed_to_load_asset": "Neizdevās ielādēt failu",
"failed_to_load_assets": "Neizdevās ielādēt failus",
"failed_to_load_notifications": "Neizdevās ielādēt paziņojumus",
"failed_to_load_people": "Neizdevās ielādēt personas",
"failed_to_remove_product_key": "Neizdevās noņemt produkta atslēgu",
"failed_to_reset_pin_code": "Neizdevās atiestatīt PIN kodu",
"failed_to_stack_assets": "Neizdevās apvienot failus kaudzē",
"failed_to_unstack_assets": "Neizdevās atcelt failu apvienošanu kaudzē",
"failed_to_update_notification_status": "Neizdevās mainīt paziņojuma statusu",
"import_path_already_exists": "Šis importa ceļš jau pastāv.",
"incorrect_email_or_password": "Nepareizs e-pasts vai parole",
"profile_picture_transparent_pixels": "Profila attēlos nevar būt caurspīdīgi pikseļi. Lūdzu, palielini un/vai pārvieto attēlu.",
@@ -587,7 +610,7 @@
"exif_bottom_sheet_description": "Pievienot Aprakstu...",
"exif_bottom_sheet_details": "INFORMĀCIJA",
"exif_bottom_sheet_location": "ATRAŠANĀS VIETA",
"exif_bottom_sheet_people": "CILVĒKI",
"exif_bottom_sheet_people": "PERSONAS",
"exif_bottom_sheet_person_add_person": "Pievienot vārdu",
"exit_slideshow": "Iziet no slīdrādes",
"experimental_settings_new_asset_list_subtitle": "Izstrādes posmā",
@@ -615,7 +638,7 @@
"filename": "Faila nosaukums",
"filetype": "Faila tips",
"filter": "Filtrēt",
"filter_people": "Filtrēt cilvēkus",
"filter_people": "Filtrēt personas",
"filter_places": "Filtrēt vietas",
"first": "Pirmais",
"folder": "Mape",
@@ -624,6 +647,7 @@
"forgot_pin_code_question": "Aizmirsi savu PIN?",
"forward": "Uz priekšu",
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Šī funkcija darbojas, lejupielādējot ārējos resursus no Google.",
"get_help": "Saņemt palīdzību",
"get_wifiname_error": "Nevarēja iegūt Wi-Fi nosaukumu. Pārliecinies, ka esi piešķīris nepieciešamās atļaujas un esi savienots ar Wi-Fi tīklu",
"getting_started": "Pirmie soļi",
@@ -644,13 +668,13 @@
"hashed_assets": "Faili ar izveidotām jaucējvērtībām",
"hashing": "Veido jaucējvērtības",
"header_settings_field_validator_msg": "Vērtība nevar būt tukša",
"hide_all_people": "Paslēpt visus cilvēkus",
"hide_all_people": "Paslēpt visas personas",
"hide_gallery": "Paslēpt galeriju",
"hide_named_person": "Paslēpt personu {name}",
"hide_password": "Paslēpt paroli",
"hide_person": "Paslēpt personu",
"hide_unnamed_people": "Paslēpt nenosauktas personas",
"home_page_add_to_album_conflicts": "Pievienoja {added} aktīvus albumam {album}. {failed} aktīvi jau ir albumā.",
"home_page_add_to_album_conflicts": "Pievienoja {added} failus albumam {album}. {failed} faili jau ir albumā.",
"home_page_add_to_album_err_local": "Albumiem vēl nevar pievienot lokālos aktīvus, notiek izlaišana",
"home_page_add_to_album_success": "Pievienoja {added} aktīvus albumam {album}.",
"home_page_album_err_partner": "Pagaidām nevar pievienot partnera aktīvus albumam, notiek izlaišana",
@@ -658,7 +682,7 @@
"home_page_archive_err_partner": "Nevarēja arhivēt partnera aktīvus, notiek izlaišana",
"home_page_building_timeline": "Tiek izveidota laika skala",
"home_page_delete_err_partner": "Nevarēja dzēst partnera aktīvus, notiek izlaišana",
"home_page_delete_remote_err_local": "Lokālie aktīvi dzēšanai attālinātajā izvēlē, tiek izlaists",
"home_page_delete_remote_err_local": "Lokālie faili dzēšanai attālinātajā izvēlē, tiek izlaists",
"home_page_favorite_err_local": "Vēl nevar pievienot izlasei vietējos failus, izlaiž",
"home_page_favorite_err_partner": "Pagaidām nevar ievietot izlasē partnera failus, izlaiž",
"home_page_first_time_notice": "Ja šī ir pirmā reize, kad izmanto lietotni, lūdzu, izvēlies dublējamo albumu, lai laika skalā varētu aizpildīt fotoattēlus un videoklipus",
@@ -695,16 +719,20 @@
"invalid_date_format": "Nederīgs datuma formāts",
"invite_people": "Ielūgt cilvēkus",
"invite_to_album": "Uzaicināt albumā",
"ios_debug_info_fetch_ran_at": "Ielasīšana notika {dateTime}",
"ios_debug_info_last_sync_at": "Pēdējā sinhronizācija {dateTime}",
"ios_debug_info_no_processes_queued": "Nav ierindotu fona procesu",
"ios_debug_info_processing_ran_at": "Apstrāde notika {dateTime}",
"jobs": "Uzdevumi",
"keep": "Paturēt",
"keep_all": "Paturēt visus",
"keep_this_delete_others": "Paturēt šo, dzēst citus",
"keyboard_shortcuts": "Tastatūras saīsnes",
"language": "Valoda",
"language_no_results_subtitle": "Mēģini pielāgot meklēšanas terminu",
"language_no_results_title": "Nav atrasta neviena valoda",
"language_search_hint": "Meklēt valodas...",
"language_setting_description": "Izvēlieties vēlamo valodu",
"language_setting_description": "Izvēlies vēlamo valodu",
"large_files": "Lielie faili",
"last": "Pēdējais",
"last_seen": "Pēdējo reizi redzēts",
@@ -725,7 +753,9 @@
"licenses": "Licences",
"list": "Saraksts",
"loading": "Ielādē",
"location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name",
"local_network": "Lokālais tīkls",
"location_permission": "Atrašanās vietas atļauja",
"location_permission_content": "Lai izmantotu automātiskās pārslēgšanās funkciju, Immich ir nepieciešama precīzas atrašanās vietas atļauja, lai varētu nolasīt pašreizējā Wi-Fi tīkla nosaukumu",
"location_picker_choose_on_map": "Izvēlēties uz kartes",
"location_picker_latitude_error": "Ievadiet korektu ģeogrāfisko platumu",
"location_picker_latitude_hint": "Ievadiet savu ģeogrāfisko platumu šeit",
@@ -771,7 +801,7 @@
"map_cannot_get_user_location": "Nevar iegūt lietotāja atrašanās vietu",
"map_location_dialog_yes": "Jā",
"map_location_picker_page_use_location": "Izvēlēties šo atrašanās vietu",
"map_location_service_disabled_content": "Lai tiktu rādīti jūsu pašreizējās atrašanās vietas aktīvi, ir jāaktivizē atrašanās vietas pakalpojums. Vai vēlaties to iespējot tagad?",
"map_location_service_disabled_content": "Lai tiktu rādīti jūsu pašreizējās atrašanās vietas faili, ir jāaktivizē atrašanās vietas pakalpojums. Vai vēlaties to iespējot tagad?",
"map_location_service_disabled_title": "Atrašanās vietas Pakalpojums atslēgts",
"map_marker_for_images": "Kartes marķieris attēliem, kas uzņemti {city}, {country}",
"map_marker_with_image": "Kartes marķieris ar attēlu",
@@ -799,10 +829,10 @@
"memory": "Atmiņa",
"menu": "Izvēlne",
"merge": "Apvienot",
"merge_people": "Cilvēku apvienošana",
"merge_people": "Personu apvienošana",
"merge_people_limit": "Vienlaikus var apvienot ne vairāk kā 5 sejas",
"merge_people_prompt": "Vai vēlies apvienot šos cilvēkus? Šī darbība ir neatgriezeniska.",
"merge_people_successfully": "Cilvēki veiksmīgi apvienoti",
"merge_people_prompt": "Vai vēlies apvienot šīs personas? Šī darbība ir neatceļama.",
"merge_people_successfully": "Personas veiksmīgi apvienotas",
"minimize": "Minimizēt",
"minute": "Minūte",
"minutes": "Minūtes",
@@ -867,6 +897,7 @@
"ok": "Labi",
"onboarding": "Uzņemšana",
"onboarding_locale_description": "Izvēlies vēlamo valodu. To vēlāk var mainīt iestatījumos.",
"onboarding_server_welcome_description": "Iestatīsim šo instanci ar dažiem vispārīgiem iestatījumiem.",
"onboarding_theme_description": "Izvēlies savas instances krāsu motīvu. To vēlāk var mainīt iestatījumos.",
"onboarding_user_welcome_description": "Sāksim darbu!",
"online": "Tiešsaistē",
@@ -901,7 +932,10 @@
"pause": "Pauzēt",
"pause_memories": "Pauzēt atmiņas",
"paused": "Nopauzēts",
"people": "Cilvēki",
"people": "Personas",
"people_sidebar_description": "Parādīt saiti uz personām sānu joslā",
"permission": "Atļauja",
"permission_empty": "Tava atļauja nedrīkst būt tukša",
"permission_onboarding_back": "Atpakaļ",
"permission_onboarding_continue_anyway": "Tomēr turpināt",
"permission_onboarding_get_started": "Darba sākšana",
@@ -926,6 +960,9 @@
"preview": "Priekšskatījums",
"previous": "Iepriekšējais",
"previous_memory": "Iepriekšējā atmiņa",
"previous_or_next_day": "Dienu uz priekšu/atpakaļ",
"previous_or_next_month": "Mēnesi uz priekšu/atpakaļ",
"previous_or_next_year": "Gadu uz priekšu/atpakaļ",
"privacy": "Privātums",
"profile": "Profils",
"profile_drawer_app_logs": "Žurnāli",
@@ -1008,7 +1045,7 @@
"rescan": "Pārskenēt atkārtoti",
"reset": "Atiestatīt",
"reset_password": "Atiestatīt paroli",
"reset_people_visibility": "Atiestatīt cilvēku redzamību",
"reset_people_visibility": "Atiestatīt personu redzamību",
"reset_pin_code": "Atiestatīt PIN kodu",
"reset_to_default": "Atiestatīt noklusējuma iestatījumus",
"resolve_duplicates": "Atrisināt dublēšanās gadījumus",
@@ -1056,8 +1093,8 @@
"search_filter_media_type": "Multivides veids",
"search_filter_media_type_title": "Izvēlies multivides veidu",
"search_for_existing_person": "Meklēt esošu personu",
"search_no_people": "Nav cilvēku",
"search_no_people_named": "Nav cilvēku ar vārdu \"{name}\"",
"search_no_people": "Nav personu",
"search_no_people_named": "Nav personas ar vārdu \"{name}\"",
"search_page_categories": "Kategorijas",
"search_page_motion_photos": "Kustību Fotoattēli",
"search_page_no_objects": "Informācija par Objektiem nav pieejama",
@@ -1068,25 +1105,26 @@
"search_page_view_all_button": "Apskatīt visu",
"search_page_your_activity": "Jūsu aktivitāte",
"search_page_your_map": "Jūsu Karte",
"search_people": "Meklēt cilvēkus",
"search_people": "Meklēt personas",
"search_result_page_new_search_hint": "Jauns Meklējums",
"search_suggestion_list_smart_search_hint_1": "Viedā meklēšana pēc noklusējuma ir iespējota, lai meklētu metadatos, izmanto sintaksi ",
"search_suggestion_list_smart_search_hint_2": "m:jūsu-meklēšanas-frāze",
"search_type": "Meklēšanas veids",
"search_your_photos": "Meklēt Jūsu fotoattēlus",
"second": "Sekunde",
"see_all_people": "Skatīt visus cilvēkus",
"see_all_people": "Skatīt visas personas",
"select_album_cover": "Izvēlieties albuma vāciņu",
"select_all_duplicates": "Atlasīt visus dublikātus",
"select_from_computer": "Izvēlēties no datora",
"select_keep_all": "Atzīmēt visus paturēšanai",
"select_photos": "Fotoattēlu Izvēle",
"select_trash_all": "Atzīmēt visus pārvietošanai uz atkritni",
"select_trash_all": "Atzīmēt visus dzēšanai",
"select_user_for_sharing_page_err_album": "Neizdevās izveidot albumu",
"selected": "Izvēlētie",
"server_info_box_app_version": "Aplikācijas Versija",
"server_info_box_server_url": "Servera URL",
"server_online": "Serveris tiešsaistē",
"server_privacy": "Servera privātums",
"server_stats": "Servera statistika",
"server_version": "Servera versija",
"set_date_of_birth": "Iestatīt dzimšanas datumu",
@@ -1106,7 +1144,7 @@
"setting_notifications_single_progress_subtitle": "Detalizēta augšupielādes progresa informācija par katru aktīvu",
"setting_notifications_single_progress_title": "Rādīt fona dublējuma detalizēto progresu",
"setting_notifications_subtitle": "Paziņojumu preferenču pielāgošana",
"setting_notifications_total_progress_subtitle": "Kopējais augšupielādes progress (pabeigti/kopējie aktīvi)",
"setting_notifications_total_progress_subtitle": "Kopējais augšupielādes progress (pabeigti/kopējie faili)",
"setting_notifications_total_progress_title": "Rādīt fona dublējuma kopējo progresu",
"setting_video_viewer_looping_title": "Cikliski",
"settings": "Iestatījumi",
@@ -1123,7 +1161,7 @@
"shared_album_section_people_action_error": "Kļūme pametot/noņemot no albuma",
"shared_album_section_people_action_leave": "Noņemt lietotāju no albuma",
"shared_album_section_people_action_remove_user": "Noņemt lietotāju no albuma",
"shared_album_section_people_title": "CILVĒKI",
"shared_album_section_people_title": "PERSONAS",
"shared_intent_upload_button_progress_text": "Augšupielādēti {current} / {total}",
"shared_link_app_bar_title": "Kopīgotas Saites",
"shared_link_clipboard_copied_massage": "Ievietots starpliktuvē",
@@ -1159,23 +1197,37 @@
"sharing_page_album": "Kopīgotie albumi",
"sharing_page_description": "Izveidojiet koplietojamus albumus, lai kopīgotu fotoattēlus un videoklipus ar Jūsu tīkla lietotājiem.",
"sharing_page_empty_list": "TUKŠS SARAKSTS",
"sharing_sidebar_description": "Parādīt saiti uz kopīgošanu sānu joslā",
"sharing_silver_appbar_create_shared_album": "Izveidot kopīgotu albumu",
"sharing_silver_appbar_share_partner": "Dalīties ar partneri",
"show_album_options": "Rādīt albuma iespējas",
"show_albums": "Rādīt albumus",
"show_all_people": "Rādīt visus cilvēkus",
"show_and_hide_people": "Rādīt un slēpt cilvēkus",
"show_all_people": "Rādīt visas personas",
"show_and_hide_people": "Rādīt un slēpt personas",
"show_file_location": "Rādīt faila atrašanās vietu",
"show_gallery": "Rādīt galeriju",
"show_hidden_people": "Rādīt paslēptos cilvēkus",
"show_hidden_people": "Rādīt paslēptās personas",
"show_in_timeline": "Parādīt laika skalā",
"show_in_timeline_setting_description": "Rādīt šī lietotāja fotogrāfijas un video tavā laika skalā",
"show_keyboard_shortcuts": "Rādīt tastatūras saīsnes",
"show_metadata": "Rādīt metadatus",
"show_or_hide_info": "Rādīt vai slēpt informāciju",
"show_password": "Parādīt paroli",
"show_person_options": "Rādīt personas opcijas",
"show_progress_bar": "Rādīt progresa joslu",
"show_search_options": "Rādīt meklēšanas opcijas",
"show_shared_links": "Rādīt kopīgotās saites",
"show_slideshow_transition": "Rādīt slīdrādes pāreju",
"show_supporter_badge": "Atbalstītāja nozīmīte",
"show_supporter_badge_description": "Rādīt atbalstītāja nozīmīti",
"shuffle": "Jaukta",
"sidebar": "Sānu josla",
"sidebar_display_description": "Parādīt saiti uz skatu sānu joslā",
"sign_out": "Iziet",
"sign_up": "Reģistrēties",
"size": "Izmērs",
"skip_to_content": "Pāriet uz saturu",
"skip_to_folders": "Pāriet uz mapēm",
"slideshow": "Slīdrāde",
"slideshow_settings": "Slīdrādes iestatījumi",
"sort_albums_by": "Kārtot albumus pēc...",
@@ -1184,7 +1236,7 @@
"sort_modified": "Izmaiņu datums",
"sort_newest": "Jaunākā fotogrāfija",
"sort_oldest": "Vecākā fotogrāfija",
"sort_people_by_similarity": "Sakārtot cilvēkus pēc līdzības",
"sort_people_by_similarity": "Sakārtot personas pēc līdzības",
"sort_recent": "Nesenākā fotogrāfija",
"sort_title": "Nosaukums",
"source": "Pirmkods",
@@ -1206,8 +1258,13 @@
"theme": "Dizains",
"theme_setting_asset_list_storage_indicator_title": "Rādīt krātuves indikatoru uz aktīvu elementiem",
"theme_setting_asset_list_tiles_per_row_title": "Failu skaits rindā ({count})",
"theme_setting_colorful_interface_subtitle": "Piemērot pamatkrāsu fona virsmām.",
"theme_setting_colorful_interface_title": "Krāsaina saskarne",
"theme_setting_image_viewer_quality_subtitle": "Attēlu skatītāja detaļu kvalitātes pielāgošana",
"theme_setting_image_viewer_quality_title": "Attēlu skatītāja kvalitāte",
"theme_setting_primary_color_subtitle": "Izvēlies krāsu galvenajām darbībām un akcentiem.",
"theme_setting_primary_color_title": "Pamatkrāsa",
"theme_setting_system_primary_color_title": "Izmantot sistēmas krāsu",
"theme_setting_system_theme_switch": "Automātisks (sekot sistēmas iestatījumiem)",
"theme_setting_theme_subtitle": "Izvēlieties programmas dizaina iestatījumu",
"theme_setting_three_stage_loading_subtitle": "Trīspakāpju ielāde var palielināt ielādēšanas veiktspēju, bet izraisa ievērojami lielāku tīkla noslodzi",
@@ -1225,7 +1282,7 @@
"total_usage": "Kopējais lietojums",
"trash": "Atkritne",
"trash_action_prompt": "{count} pārvietoja uz atkritni",
"trash_all": "Dzēst Visu",
"trash_all": "Dzēst visu",
"trash_count": "Pārvietot uz atkritni {count, number}",
"trash_delete_asset": "Pārvietot uz atkritni/dzēst failu",
"trash_emptied": "Atkritne iztukšota",
@@ -1262,17 +1319,21 @@
"upload_status_errors": "Kļūdas",
"upload_status_uploaded": "Augšupielādēts",
"upload_to_immich": "Augšupielādēt Immich ({count})",
"uploading": "Augšupielādē",
"uploading_media": "Augšupielādē failus",
"url": "URL",
"usage": "Lietojums",
"use_biometric": "Izmantot biometrisko autentifikāciju",
"use_current_connection": "izmantot pašreizējo savienojumu",
"user": "Lietotājs",
"user_has_been_deleted": "Šis lietotājs ir dzēsts.",
"user_id": "Lietotāja ID",
"user_pin_code_settings": "PIN kods",
"user_privacy": "Lietotāju privātums",
"user_purchase_settings": "Iegādāties",
"user_purchase_settings_description": "Pirkuma pārvaldība",
"user_usage_detail": "Informācija par lietotāju lietojumu",
"user_usage_stats": "Konta izmantošanas statistika",
"user_usage_stats_description": "Skatīt konta lietojuma statistiku",
"username": "Lietotājvārds",
"users": "Lietotāji",

View File

@@ -4,6 +4,7 @@
"account_settings": "Поставки за профилот",
"acknowledge": "Прочитано",
"action": "Акција",
"action_common_update": "Ажурирај",
"actions": "Акции",
"active": "Активни",
"activity": "Активност",
@@ -77,7 +78,8 @@
"system_settings": "Системски поставки",
"thumbnail_generation_job": "Генерирај сликички",
"transcoding_acceleration_vaapi": "VAAPI",
"transcoding_threads": "Нишки"
"transcoding_threads": "Нишки",
"transcoding_tone_mapping": "Тонско мапирање"
},
"admin_email": "Администрациска Е-пошта",
"admin_password": "Администрациска лозинка",
@@ -93,11 +95,15 @@
"asset_hashing": "Хеширање…",
"asset_offline": "Средството е офлајн",
"asset_skipped": "Пропуштено",
"asset_uploaded": "Прикачено",
"asset_uploading": "Прикачување…",
"assets": "Средства",
"authorized_devices": "Авторизирани уреди",
"back": "Назад",
"backup": "Резервна копија",
"backward": "Наназад",
"blurred_background": "Заматена позадина",
"build": "Верзија",
"camera": "Камера",
"camera_brand": "Марка на камера",
"camera_model": "Модел на камера",
@@ -162,15 +168,18 @@
"enabled": "Овозможено",
"end_date": "Краен датум",
"error": "Грешка",
"exif": "Exif",
"expand_all": "Прошири ги сите",
"expire_after": "Да истече после",
"expired": "Истечено",
"explore": "Истражи",
"explorer": "Прегледувач",
"export": "Извези",
"extension": "Екстензија",
"external": "Екстерно",
"external_libraries": "Екстерни библиотеки",
"face_unassigned": "Недоделено",
"failed": "Неуспешно",
"favorite": "Омилено",
"favorites": "Омилени",
"features": "Функии",
@@ -229,8 +238,10 @@
"no_results": "Нема резултати",
"notes": "Белешки",
"notifications": "Нотификации",
"oauth": "OAuth",
"offline": "Офлајн",
"ok": "Ок",
"onboarding": "Воведување",
"online": "Онлајн",
"options": "Опции",
"or": "или",

View File

@@ -396,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Foretrekk eksterne bilder",
"advanced_settings_proxy_headers_subtitle": "Definer proxy headere som Immich skal benytte ved enhver nettverksrequest",
"advanced_settings_proxy_headers_title": "Proxy headere",
"advanced_settings_readonly_mode_subtitle": "Aktiverer skrivebeskyttet modus der bildene bare kan vises. Ting som å velge flere bilder, dele, caste og slette er deaktivert. Aktiver/deaktiver skrivebeskyttet modus via brukerens avatar fra hovedskjermen",
"advanced_settings_readonly_mode_title": "Skrivebeskyttet modus",
"advanced_settings_self_signed_ssl_subtitle": "Hopper over SSL sertifikatverifikasjon for server-endepunkt. Påkrevet for selvsignerte sertifikater.",
"advanced_settings_self_signed_ssl_title": "Tillat selvsignerte SSL sertifikater",
"advanced_settings_sync_remote_deletions_subtitle": "Automatisk slette eller gjenopprette filer på denne enheten hvis den handlingen har blitt gjort på nettsiden",
@@ -461,6 +463,7 @@
"app_bar_signout_dialog_title": "Logg ut",
"app_settings": "Appinstillinger",
"appears_in": "Vises i",
"apply_count": "Bruk ({count, number})",
"archive": "Arkiv",
"archive_action_prompt": "{count} lagt til i arkivet",
"archive_or_unarchive_photo": "Arkiver eller ta ut av arkivet",
@@ -500,7 +503,7 @@
"assets": "Filer",
"assets_added_count": "Lagt til {count, plural, one {# objekt} other {# objekter}}",
"assets_added_to_album_count": "Lagt til {count, plural, one {# objekter} other {# objekt}} i album",
"assets_added_to_albums_count": "Lagt til {assetTotal, plural, one {# asset} other {# assets}} til {albumTotal} albumer",
"assets_added_to_albums_count": "Lagt til {assetTotal, plural, one {# asset} other {# assets}} til {albumTotal, plural, one {# album} other {# albums}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Objektet} other {Objektene}} kan ikke legges til i albumet",
"assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} kan ikke legges til i noen av albumene",
"assets_count": "{count, plural, one {# fil} other {# filer}}",
@@ -1073,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Denne funksjonen laster eksterne ressurser fra Google for å fungere.",
"general": "Generelt",
"geolocation_instruction_all_have_location": "Alle objekter for denne datoen har allerede lokasjonsdata. Prøv å vise alle objekter eller velg en annen dato",
"geolocation_instruction_location": "Klikk på et objekt med GPS-koordinater for å bruke posisjonen, eller velg en posisjon direkte fra kartet",
"geolocation_instruction_no_date": "Velg en dato for å administrere posisjonsdata for bilder og videoer fra den dagen",
"geolocation_instruction_no_photos": "Ingen bilder eller videoer funnet for denne datoen. Velg en annen dato for å vise dem",
"get_help": "Få Hjelp",
"get_wifiname_error": "Kunne ikke hente Wi-Fi-navnet. Sørg for at du har gitt de nødvendige tillatelsene og er koblet til et Wi-Fi-nettverk",
"getting_started": "Kom i gang",
"go_back": "Gå tilbake",
"go_to_folder": "Gå til mappe",
"go_to_search": "Gå til søk",
"gps": "GPS",
"gps_missing": "Ingen GPS",
"grant_permission": "Gi tillatelse",
"group_albums_by": "Grupper album etter...",
"group_country": "Grupper etter land",
@@ -1262,6 +1271,7 @@
"main_branch_warning": "Du bruker en utviklingsversjon; vi anbefaler på det sterkeste og bruke en utgitt versjon!",
"main_menu": "Hovedmeny",
"make": "Merke",
"manage_geolocation": "Administrer plassering",
"manage_shared_links": "Håndter delte linker",
"manage_sharing_with_partners": "Administrer deling med partnere",
"manage_the_app_settings": "Administrer appinnstillingene",
@@ -1508,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "Mobilapp er utdatert. Vennligst oppdater til nyeste versjon.",
"profile_drawer_client_server_up_to_date": "Klient og server er oppdatert",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Skrivebeskyttet modus er aktivert. Dobbelttrykk på brukerens avatarikon for å avslutte.",
"profile_drawer_server_out_of_date_major": "Server er utdatert. Vennligst oppdater til nyeste versjon.",
"profile_drawer_server_out_of_date_minor": "Server er utdatert. Vennligst oppdater til nyeste versjon.",
"profile_image_of_user": "Profil bilde av {user}",
@@ -1553,6 +1564,8 @@
"rating_description": "Hvis EXIF vurdering i informasjons panelet",
"reaction_options": "Reaksjonsalternativer",
"read_changelog": "Les endringslogg",
"readonly_mode_disabled": "Skrivebeskyttet modus deaktivert",
"readonly_mode_enabled": "Skrivebeskyttet modus aktivert",
"reassign": "Tilordne på nytt",
"reassigned_assets_to_existing_person": "Flyttet {count, plural, one {# objekt} other {# objekter}} to {name, select, null {en eksisterende person} other {{name}}}",
"reassigned_assets_to_new_person": "Flyttet {count, plural, one {# objekt} other {# objekter}} til en ny person",
@@ -1722,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Feilet ved oppretting av album",
"selected": "Valgt",
"selected_count": "{count, plural, other {# valgt}}",
"selected_gps_coordinates": "valgte GPS-koordinater",
"send_message": "Send melding",
"send_welcome_email": "Send velkomstmelding",
"server_endpoint": "Server endepunkt",
@@ -1832,8 +1846,10 @@
"shift_to_permanent_delete": "trykk ⇧ for å slette eiendeler permanent",
"show_album_options": "Vis albumalternativer",
"show_albums": "Vis album",
"show_all_assets": "Via alle objekter",
"show_all_people": "Vis alle mennesker",
"show_and_hide_people": "Vis og skjul personer",
"show_assets_without_location": "Vis objekter uten lokasjon",
"show_file_location": "Vis filplassering",
"show_gallery": "Vis galleri",
"show_hidden_people": "Vis skjulte personer",
@@ -1941,7 +1957,9 @@
"to_change_password": "Endre passord",
"to_favorite": "Favoritt",
"to_login": "Logg inn",
"to_multi_select": "for multivalg",
"to_parent": "Gå til overodnet",
"to_select": "for valg",
"to_trash": "Papirkurv",
"toggle_settings": "Bytt innstillinger",
"total": "Total",
@@ -1991,6 +2009,7 @@
"unstacked_assets_count": "Ikke stablet {count, plural, one {# objekt} other {# objekter}}",
"untagged": "Umerket",
"up_next": "Neste",
"update_location_action_prompt": "Oppdater plasseringen til {count} valgte objekter med:",
"updated_at": "Oppdatert",
"updated_password": "Passord oppdatert",
"upload": "Last opp",
@@ -2015,6 +2034,7 @@
"use_biometric": "Bruk biometri",
"use_current_connection": "bruk nåværende tilkobling",
"use_custom_date_range": "Bruk egendefinert datoperiode i stedet",
"use_this_location": "Trykk for å bruke lokasjon",
"user": "Bruker",
"user_has_been_deleted": "Denne brukeren har blitt slettet.",
"user_id": "Bruker ID",

View File

@@ -462,7 +462,7 @@
"app_settings": "App instellingen",
"appears_in": "Komt voor in",
"archive": "Archief",
"archive_action_prompt": "{count} toegevoegd aan archief",
"archive_action_prompt": "{count} item(s) toegevoegd aan het archief",
"archive_or_unarchive_photo": "Foto archiveren of uit het archief halen",
"archive_page_no_archived_assets": "Geen gearchiveerde items gevonden",
"archive_page_title": "Archief ({count})",
@@ -500,7 +500,7 @@
"assets": "Items",
"assets_added_count": "{count, plural, one {# item} other {# items}} toegevoegd",
"assets_added_to_album_count": "{count, plural, one {# item} other {# items}} aan het album toegevoegd",
"assets_added_to_albums_count": "{assetTotal, plural, one {# asset} other {# assets}} toegevoegd aan {albumTotal} albums",
"assets_added_to_albums_count": "{assetTotal, plural, one {# asset} other {# assets}} toegevoegd aan {albumTotal, plural, one {# album} other {#albums}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {# item} other {# items}} konden niet aan album toegevoegd worden",
"assets_cannot_be_added_to_albums": "{count, plural, one {Middel kan} other {Middelen kunnen}} niet toegevoegd worden aan de albums",
"assets_count": "{count, plural, one {# item} other {# items}}",
@@ -707,7 +707,7 @@
"control_bottom_app_bar_edit_time": "Datum & tijd bewerken",
"control_bottom_app_bar_share_link": "Link delen",
"control_bottom_app_bar_share_to": "Delen met",
"control_bottom_app_bar_trash_from_immich": "Naar prullenbak",
"control_bottom_app_bar_trash_from_immich": "Verwijderen uit Immich",
"copied_image_to_clipboard": "Afbeelding gekopieerd naar klembord.",
"copied_to_clipboard": "Gekopieerd naar klembord!",
"copy_error": "Fout bij kopiëren",
@@ -768,7 +768,7 @@
"default_locale_description": "Formatteer datums en getallen op basis van de landinstellingen van je browser",
"delete": "Verwijderen",
"delete_action_confirmation_message": "Weet je zeker dat je dit item wilt verwijderen? Deze actie zorgt ervoor dat het item naar de prullenbak van de server wordt verplaatst en je wordt gevraagd of je deze ook lokaal wilt verwijderen",
"delete_action_prompt": "{count} verwijderd",
"delete_action_prompt": "{count} item(s) verwijderd",
"delete_album": "Album verwijderen",
"delete_api_key_prompt": "Weet je zeker dat je deze API-sleutel wilt verwijderen?",
"delete_dialog_alert": "Deze items zullen permanent verwijderd worden van Immich en je apparaat",
@@ -782,12 +782,12 @@
"delete_key": "Verwijder key",
"delete_library": "Verwijder bibliotheek",
"delete_link": "Verwijder link",
"delete_local_action_prompt": "{count} lokaal verwijderd",
"delete_local_action_prompt": "{count} item(s) lokaal verwijderd",
"delete_local_dialog_ok_backed_up_only": "Verwijder alleen met back-up",
"delete_local_dialog_ok_force": "Toch verwijderen",
"delete_others": "Andere verwijderen",
"delete_permanently": "Permanent verwijderen",
"delete_permanently_action_prompt": "{count} permanent verwijderd",
"delete_permanently_action_prompt": "{count} item(s) permanent verwijderd",
"delete_shared_link": "Verwijder gedeelde link",
"delete_shared_link_dialog_title": "Verwijder gedeelde link",
"delete_tag": "Tag verwijderen",
@@ -816,7 +816,7 @@
"documentation": "Documentatie",
"done": "Klaar",
"download": "Downloaden",
"download_action_prompt": "{count} items downloaden",
"download_action_prompt": "{count} item(s) aan het downloaden",
"download_canceled": "Download geannuleerd",
"download_complete": "Download voltooid",
"download_enqueue": "Download in wachtrij",
@@ -846,7 +846,7 @@
"edit_birthday": "Wijzig verjaardag",
"edit_date": "Datum bewerken",
"edit_date_and_time": "Datum en tijd bewerken",
"edit_date_and_time_action_prompt": "{count} datum en tijd bewerkt",
"edit_date_and_time_action_prompt": "Datum en tijd bijgewerkt van {count} item(s)",
"edit_date_and_time_by_offset": "Wijzigen datum door verschuiving",
"edit_date_and_time_by_offset_interval": "Nieuw datuminterval: {from}-{to}",
"edit_description": "Beschrijving bewerken",
@@ -858,7 +858,7 @@
"edit_key": "Key bewerken",
"edit_link": "Link bewerken",
"edit_location": "Locatie bewerken",
"edit_location_action_prompt": "{count} locatie(s) aangepast",
"edit_location_action_prompt": "Locatie bijgewerkt van {count} item(s)",
"edit_location_dialog_title": "Locatie",
"edit_name": "Naam bewerken",
"edit_people": "Mensen bewerken",
@@ -908,7 +908,7 @@
"error_deleting_shared_user": "Fout bij verwijderen van gedeelde gebruiker",
"error_downloading": "Fout bij downloaden {filename}",
"error_hiding_buy_button": "Fout bij het verbergen van de koop knop",
"error_removing_assets_from_album": "Fout bij verwijderen van items uit album, controleer de console voor meer details",
"error_removing_assets_from_album": "Fout bij het verwijderen van items uit het album, controleer de console voor meer details",
"error_selecting_all_assets": "Fout bij selecteren van alle items",
"exclusion_pattern_already_exists": "Dit uitsluitingspatroon bestaat al.",
"failed_to_create_album": "Fout bij maken van album",
@@ -1047,7 +1047,7 @@
"failed_to_load_assets": "Kan items niet laden",
"failed_to_load_folder": "Laden van map mislukt",
"favorite": "Favoriet",
"favorite_action_prompt": "{count}toegevoegd aan Favorieten",
"favorite_action_prompt": "{count} item(s) toegevoegd aan je favorieten",
"favorite_or_unfavorite_photo": "Foto markeren als of verwijderen uit favorieten",
"favorites": "Favorieten",
"favorites_page_no_favorites": "Geen favoriete items gevonden",
@@ -1322,7 +1322,7 @@
"more": "Meer",
"move": "Verplaats",
"move_off_locked_folder": "Verplaats uit vergrendelde map",
"move_to_lock_folder_action_prompt": "{count} toegevoegd aan de vergrendelde map",
"move_to_lock_folder_action_prompt": "{count} item(s) toegevoegd aan de vergrendelde map",
"move_to_locked_folder": "Verplaats naar vergrendelde map",
"move_to_locked_folder_confirmation": "Deze fotos en videos worden uit alle albums verwijderd en zijn alleen te bekijken in de vergrendelde map",
"moved_to_archive": "{count, plural, one {# item} other {# items}} verplaatst naar archief",
@@ -1583,10 +1583,10 @@
"remove_assets_title": "Items verwijderen?",
"remove_custom_date_range": "Aangepast datumbereik verwijderen",
"remove_deleted_assets": "Verwijder offline bestanden",
"remove_from_album": "Verwijder uit album",
"remove_from_album_action_prompt": "{count} verwijderd uit het album",
"remove_from_album": "Verwijderen uit album",
"remove_from_album_action_prompt": "{count} item(s) verwijderd uit het album",
"remove_from_favorites": "Verwijderen uit favorieten",
"remove_from_lock_folder_action_prompt": "{count} verwijderd uit de vergrendelde map",
"remove_from_lock_folder_action_prompt": "{count} item(s) verwijderd uit de vergrendelde map",
"remove_from_locked_folder": "Verwijder uit de vergrendelde map",
"remove_from_locked_folder_confirmation": "Weet je zeker dat je deze foto's en video's uit de vergrendelde map wilt verplaatsen? Ze zijn dan weer zichtbaar in je bibliotheek.",
"remove_from_shared_link": "Verwijderen uit gedeelde link",
@@ -1625,7 +1625,7 @@
"resolved_all_duplicates": "Alle duplicaten opgelost",
"restore": "Herstellen",
"restore_all": "Herstel alle",
"restore_trash_action_prompt": "{count} teruggezet uit prullenbak",
"restore_trash_action_prompt": "{count} item(s) teruggehaald uit de prullenbak",
"restore_user": "Gebruiker herstellen",
"restored_asset": "Item hersteld",
"resume": "Hervatten",
@@ -1767,9 +1767,9 @@
"settings_saved": "Instellingen opgeslagen",
"setup_pin_code": "Stel een PIN code in",
"share": "Delen",
"share_action_prompt": "{count} items gedeeld",
"share_action_prompt": "{count} item(s) gedeeld",
"share_add_photos": "Foto's toevoegen",
"share_assets_selected": "{count} geselecteerd",
"share_assets_selected": "{count} item(s) geselecteerd",
"share_dialog_preparing": "Voorbereiden...",
"share_link": "Link delen",
"shared": "Gedeeld",
@@ -1872,7 +1872,7 @@
"sort_title": "Titel",
"source": "Bron",
"stack": "Stapel",
"stack_action_prompt": "{count} gestapeld",
"stack_action_prompt": "{count} item(s) gestapeld",
"stack_duplicates": "Stapel duplicaten",
"stack_select_one_photo": "Selecteer één primaire foto voor de stapel",
"stack_selected_photos": "Geselecteerde foto's stapelen",
@@ -1947,7 +1947,7 @@
"total": "Totaal",
"total_usage": "Totaal gebruik",
"trash": "Prullenbak",
"trash_action_prompt": "{count} verwijderd naar de prullenbak",
"trash_action_prompt": "{count} item(s) verplaatst naar de prullenbak",
"trash_all": "Verplaats alle naar prullenbak",
"trash_count": "{count, number} naar prullenbak",
"trash_delete_asset": "Items naar prullenbak verplaatsen of verwijderen",
@@ -1969,7 +1969,7 @@
"unarchived_count": "{count, plural, other {# verwijderd uit archief}}",
"undo": "Ongedaan maken",
"unfavorite": "Verwijderen uit favorieten",
"unfavorite_action_prompt": "{count} verwijderd uit favorieten",
"unfavorite_action_prompt": "{count} verwijderd uit je favorieten",
"unhide_person": "Persoon zichtbaar maken",
"unknown": "Onbekend",
"unknown_country": "Onbekend Land",
@@ -1987,14 +1987,14 @@
"unselect_all_duplicates": "Deselecteer alle duplicaten",
"unselect_all_in": "Deselecteer alles in {group}",
"unstack": "Ontstapelen",
"unstack_action_prompt": "{count} ontstapeld",
"unstack_action_prompt": "{count} item(s) ontstapeld",
"unstacked_assets_count": "{count, plural, one {# item} other {# items}} ontstapeld",
"untagged": "Ongemarkeerd",
"up_next": "Volgende",
"updated_at": "Geüpdatet",
"updated_password": "Wachtwoord bijgewerkt",
"upload": "Uploaden",
"upload_action_prompt": "{count} in de wachtrij voor uploaden",
"upload_action_prompt": "{count} item(s) staan in de wachtrij voor uploaden",
"upload_concurrency": "Aantal gelijktijdige uploads",
"upload_details": "Uploaddetails",
"upload_dialog_info": "Wil je een backup maken van de geselecteerde item(s) op de server?",
@@ -2018,7 +2018,7 @@
"user": "Gebruiker",
"user_has_been_deleted": "Deze gebruiker is verwijderd.",
"user_id": "Gebruikers ID",
"user_liked": "{user} heeft {type, select, photo {deze foto} video {deze video} item {dit bestand} other {dit}} geliket",
"user_liked": "{user} heeft {type, select, photo {deze foto} video {deze video} asset {} other {dit item}} geliket",
"user_pin_code_settings": "PIN Code",
"user_pin_code_settings_description": "Beheer je PIN code",
"user_privacy": "Gebruikersprivacy",

View File

@@ -500,7 +500,7 @@
"assets": "Zasoby",
"assets_added_count": "Dodano {count, plural, one {# zasób} few {# zasoby} other {# zasobów}}",
"assets_added_to_album_count": "Dodano {count, plural, one {# zasób} few {# zasoby} other {# zasobów}} do albumu",
"assets_added_to_albums_count": "Dodano {assetTotal, plural, one {# zasób} few {# zasoby} many {# zasobów} other {# zasobów}} do {albumTotal} albumów",
"assets_added_to_albums_count": "Dodano {assetTotal, plural, one {# zasób} few {# zasoby} other {# zasobów}} do {albumTotal, plural, one {# albumu} other {# albumów}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {sztuka Elementu} other {szt. Elementów}} nie może być dodana do albumu",
"assets_cannot_be_added_to_albums": "{count, plural, one {Zasób nie może być dodany} other {Zasoby nie mogą być dodane}} do żadnego z albumów",
"assets_count": "{count, plural, one {# zasób} few {# zasoby} other {# zasobów}}",
@@ -1071,7 +1071,7 @@
"forgot_pin_code_question": "Nie pamiętasz kodu PIN?",
"forward": "Do przodu",
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Ta funkcja pobiera zewnętrzne zasoby z Google, aby działać.",
"gcast_enabled_description": "Ta funkcja , aby działać, ładuje zewnętrzne zasoby z Google.",
"general": "Ogólne",
"get_help": "Pomoc",
"get_wifiname_error": "Nie można uzyskać nazwy Wi-Fi. Upewnij się, że udzieliłeś niezbędnych uprawnień i jesteś połączony z siecią Wi-Fi",
@@ -1394,7 +1394,7 @@
"on_this_device": "Na tym urządzeniu",
"onboarding": "Wdrożenie",
"onboarding_locale_description": "Wybierz preferowany język. Można to później zmienić w ustawieniach.",
"onboarding_privacy_description": "Śledzenie (opcjonalne) funkcja opiera się na zewnętrznych usługach i może zostać wyłączona w dowolnym momencie w ustawieniach.",
"onboarding_privacy_description": "Następujące (opcjonalne) funkcje opiera się na usługach zewnętrznych i można je w dowolnym momencie wyłączyć w ustawieniach.",
"onboarding_server_welcome_description": "Skonfigurujmy twoją instancję z kilkoma typowymi ustawieniami.",
"onboarding_theme_description": "Wybierz motyw kolorystyczny dla twojej instancji. Możesz go później zmienić w ustawieniach.",
"onboarding_user_welcome_description": "Zaczynamy!",
@@ -1941,7 +1941,9 @@
"to_change_password": "Zmień hasło",
"to_favorite": "Dodaj do ulubionych",
"to_login": "Zaloguj się",
"to_multi_select": "aby wybrać wiele",
"to_parent": "Idź do rodzica",
"to_select": "aby wybrać",
"to_trash": "Kosz",
"toggle_settings": "Przełącz ustawienia",
"total": "Całkowity",

View File

@@ -28,6 +28,9 @@
"add_to_album": "Adicionar ao álbum",
"add_to_album_bottom_sheet_added": "Adicionado ao {album}",
"add_to_album_bottom_sheet_already_exists": "Já existe em {album}",
"add_to_album_toggle": "Alternar a seleção de {album}",
"add_to_albums": "Adicionar aos álbuns",
"add_to_albums_count": "Adicionar aos álbuns ({count})",
"add_to_shared_album": "Adicionar ao álbum compartilhado",
"add_url": "Adicionar URL",
"added_to_archive": "Adicionado ao arquivo",
@@ -393,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Preferir imagens do servidor",
"advanced_settings_proxy_headers_subtitle": "Defina os cabeçalhos do proxy que o Immich deve enviar em todas comunicações com a rede",
"advanced_settings_proxy_headers_title": "Cabeçalhos do Proxy",
"advanced_settings_readonly_mode_subtitle": "Ativar o modo de apenas visualização dos arquivos. As outras ações, como: selecionar várias imagens, compartilhar, transmitir ou deletar serão desabilitadas. Ative ou Desative este modo clicando na foto do usuário na tela principal",
"advanced_settings_readonly_mode_title": "Modo de apenas visualização",
"advanced_settings_self_signed_ssl_subtitle": "Ignora a verificação do certificado SSL do servidor. Obrigatório para certificados auto assinados.",
"advanced_settings_self_signed_ssl_title": "Permitir certificados SSL auto assinados",
"advanced_settings_sync_remote_deletions_subtitle": "Excluir ou restaurar os arquivos automaticamente neste dispositivo quando essas ações forem realizada na interface web",
@@ -458,6 +463,7 @@
"app_bar_signout_dialog_title": "Sair",
"app_settings": "Configurações do Aplicativo",
"appears_in": "Aparece em",
"apply_count": "Aplicar ({count, number})",
"archive": "Arquivar",
"archive_action_prompt": "{count} mídias arquivadas",
"archive_or_unarchive_photo": "Arquivar ou desarquivar foto",
@@ -497,7 +503,9 @@
"assets": "Arquivos",
"assets_added_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}}",
"assets_added_to_album_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} ao álbum",
"assets_added_to_albums_count": "{assetTotal, plural, one {# Arquivo adicionado} other {# Arquivos adicionados}} {albumTotal, plural, one {# ao álbum} other {# aos álbuns}}",
"assets_cannot_be_added_to_album_count": "Não foi possível adicionar {count, plural, one {o arquivo} other {os arquivos}} ao álbum",
"assets_cannot_be_added_to_albums": "{count, plural, one {Arquivo não pode ser adicionado} other {Arquivos não podem ser adicionados}} a nenhum álbum",
"assets_count": "{count, plural, one {# arquivo} other {# arquivos}}",
"assets_deleted_permanently": "{count} arquivo(s) deletado(s) permanentemente",
"assets_deleted_permanently_from_server": "{count} arquivo(s) deletado(s) permanentemente do servidor Immich",
@@ -514,6 +522,7 @@
"assets_trashed_count": "{count, plural, one {# arquivo movido para a lixeira} other {# arquivos movidos para a lixeira}}",
"assets_trashed_from_server": "{count} arquivos foram enviados para a lixeira",
"assets_were_part_of_album_count": "{count, plural, one {O arquivo já faz} other {Os arquivos já fazem}} parte do álbum",
"assets_were_part_of_albums_count": "{count, plural, one {Arquivo já existe} other {Arquivos já existem}} nos álbuns",
"authorized_devices": "Dispositivos Autorizados",
"automatic_endpoint_switching_subtitle": "Conecte-se localmente quando estiver em uma rede uma Wi-Fi específica e use conexões alternativas em outras redes",
"automatic_endpoint_switching_title": "Troca automática de URL",
@@ -1056,6 +1065,7 @@
"filter_people": "Filtrar pessoas",
"filter_places": "Filtrar lugares",
"find_them_fast": "Encontre pelo nome em uma pesquisa",
"first": "Primeiro",
"fix_incorrect_match": "Corrigir correspondência incorreta",
"folder": "Pasta",
"folder_not_found": "Pasta não encontrada",
@@ -1066,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Esta funcionalidade carrega recursos externos do Google para funcionar.",
"general": "Geral",
"geolocation_instruction_all_have_location": "Todos arquivos nesta data já contem dados de localização. Tente exibir todos os arquivos ou selecione uma data diferente",
"geolocation_instruction_location": "Selecione o arquivo com as coordenadas de GPS desejada, ou selecione a localização diretamente no mapa",
"geolocation_instruction_no_date": "Selecione uma data para gerenciar os dados de localização das fotos e vídeos daquele dia",
"geolocation_instruction_no_photos": "Nenhuma foto ou vídeo encontrado nesta data. Selecione uma data diferente para ser exibida",
"get_help": "Obter Ajuda",
"get_wifiname_error": "Não foi possível obter o nome do Wi-Fi. Verifique se concedeu as permissões necessárias e se está conectado a uma rede Wi-Fi",
"getting_started": "Primeiros passos",
"go_back": "Voltar",
"go_to_folder": "Ir para a pasta",
"go_to_search": "Ir para a pesquisa",
"gps": "GPS",
"gps_missing": "Sem GPS",
"grant_permission": "Conceder permissão",
"group_albums_by": "Agrupar álbuns por...",
"group_country": "Agrupar por país",
@@ -1177,6 +1193,7 @@
"language_search_hint": "Procure idiomas...",
"language_setting_description": "Selecione seu Idioma preferido",
"large_files": "Arquivos Grandes",
"last": "Último",
"last_seen": "Visto pela ultima vez",
"latest_version": "Versão mais recente",
"latitude": "Latitude",
@@ -1254,6 +1271,7 @@
"main_branch_warning": "Você está utilizando uma versão de desenvolvimento. É fortemente recomendado que utilize uma versão estável!",
"main_menu": "Menu Principal",
"make": "Marca",
"manage_geolocation": "Gerenciar localização",
"manage_shared_links": "Gerir links partilhados",
"manage_sharing_with_partners": "Gerenciar compartilhamento com parceiros",
"manage_the_app_settings": "Gerenciar configurações do app",
@@ -1500,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "O aplicativo está desatualizado. Por favor, atualize para a versão mais recente.",
"profile_drawer_client_server_up_to_date": "Cliente e Servidor estão atualizados",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Modo apenas visualização ativado. Toque duas vezes na foto do usuário para sair deste modo.",
"profile_drawer_server_out_of_date_major": "O servidor está desatualizado. Atualize para a versão principal mais recente.",
"profile_drawer_server_out_of_date_minor": "O servidor está desatualizado. Atualize para a versão mais recente.",
"profile_image_of_user": "Imagem do perfil de {user}",
@@ -1545,6 +1564,8 @@
"rating_description": "Exibir o EXIF de classificação no painel de informações",
"reaction_options": "Opções de reação",
"read_changelog": "Ler Novidades",
"readonly_mode_disabled": "Modo apenas visualização desativado",
"readonly_mode_enabled": "Modo apenas visualização ativado",
"reassign": "Reatribuir",
"reassigned_assets_to_existing_person": "{count, plural, one {# arquivo reatribuído} other {# arquivos reatribuídos}} a {name, select, null {uma pessoa} other {{name}}}",
"reassigned_assets_to_new_person": "{count, plural, one {# arquivo reatribuído} other {# arquivos reatribuídos}} a uma nova pessoa",
@@ -1714,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Falha ao criar álbum",
"selected": "Selecionados",
"selected_count": "{count, plural, one {# selecionado} other {# selecionados}}",
"selected_gps_coordinates": "selecione as coordenadas de GPS",
"send_message": "Enviar mensagem",
"send_welcome_email": "Enviar E-mail de boas vindas",
"server_endpoint": "URL do servidor",
@@ -1824,8 +1846,10 @@
"shift_to_permanent_delete": "pressione ⇧ para excluir permanentemente o arquivo",
"show_album_options": "Exibir opções do álbum",
"show_albums": "Exibir álbuns",
"show_all_assets": "Ver todos arquivos",
"show_all_people": "Mostrar todas as pessoas",
"show_and_hide_people": "Mostrar & ocultar pessoas",
"show_assets_without_location": "Ver arquivos sem localização",
"show_file_location": "Exibir local do arquivo",
"show_gallery": "Exibir galeria",
"show_hidden_people": "Exibir pessoas ocultadas",
@@ -1857,7 +1881,7 @@
"sort_created": "Data de criação",
"sort_items": "Número de itens",
"sort_modified": "Data de modificação",
"sort_newest": "Foto mais recente",
"sort_newest": "Foto mais nova",
"sort_oldest": "Foto mais antiga",
"sort_people_by_similarity": "Ordenar pessoas por semelhança",
"sort_recent": "Foto mais recente",
@@ -1933,7 +1957,9 @@
"to_change_password": "Alterar senha",
"to_favorite": "Favorito",
"to_login": "Iniciar sessão",
"to_multi_select": "selecionar vários",
"to_parent": "Voltar para nível acima",
"to_select": "selecionar",
"to_trash": "Mover para a lixeira",
"toggle_settings": "Alternar configurações",
"total": "Total",
@@ -1983,6 +2009,7 @@
"unstacked_assets_count": "{count, plural, one {# arquivo retirado} other {# arquivos retirados}} do grupo",
"untagged": "Marcador removido",
"up_next": "A seguir",
"update_location_action_prompt": "Atualizar a localização de {count} arquivos selecionados para:",
"updated_at": "Atualizado em",
"updated_password": "Senha atualizada",
"upload": "Enviar",
@@ -2007,6 +2034,7 @@
"use_biometric": "Usar biometria",
"use_current_connection": "usar conexão atual",
"use_custom_date_range": "Usar intervalo de datas personalizado",
"use_this_location": "Clique para marcar o local",
"user": "Usuário",
"user_has_been_deleted": "Este usuário foi excluído.",
"user_id": "ID do usuário",

View File

@@ -389,17 +389,19 @@
"advanced": "Расширенные",
"advanced_settings_beta_timeline_subtitle": "Попробуйте новый функционал приложения",
"advanced_settings_beta_timeline_title": "Бета-версия временной шкалы",
"advanced_settings_enable_alternate_media_filter_subtitle": "Используйте этот параметр для фильтрации медиафайлов во время синхронизации на основе альтернативных критериев. Пробуйте только в том случае, если у вас есть проблемы с обнаружением приложением всех альбомов.",
"advanced_settings_enable_alternate_media_filter_title": "[ЭКСПЕРИМЕНТАЛЬНО] Использование фильтра синхронизации альбомов альтернативных устройств",
"advanced_settings_enable_alternate_media_filter_subtitle": "Подбор объектов для синхронизации на основе альтернативных критериев. Пробуйте включать только в том случае, если в приложении есть проблемы с обнаружением всех альбомов.",
"advanced_settings_enable_alternate_media_filter_title": "[ЭКСПЕРИМЕНТАЛЬНО] Использование альтернативного способа синхронизации альбомов на устройстве",
"advanced_settings_log_level_title": "Уровень логирования: {level}",
"advanced_settings_prefer_remote_subtitle": "Некоторые устройства очень медленно загружают локальные миниатюры. Активируйте эту настройку, чтобы изображения всегда загружались с сервера.",
"advanced_settings_prefer_remote_title": "Предпочитать фото на сервере",
"advanced_settings_proxy_headers_subtitle": "Определите заголовки прокси-сервера, которые Immich должен отправлять с каждым сетевым запросом",
"advanced_settings_proxy_headers_title": "Заголовки прокси",
"advanced_settings_readonly_mode_subtitle": "Включает режим «только просмотр», в котором можно только просматривать объекты. Функции выбора нескольких объектов, публикации, трансляции и удаления будут недоступны. Включить/отключить режим «только просмотр» можно с помощью значка аватара пользователя на главном экране.",
"advanced_settings_readonly_mode_title": "Режим «только просмотр»",
"advanced_settings_self_signed_ssl_subtitle": "Пропускать проверку SSL-сертификата сервера. Требуется для самоподписанных сертификатов.",
"advanced_settings_self_signed_ssl_title": "Разрешить самоподписанные SSL-сертификаты",
"advanced_settings_sync_remote_deletions_subtitle": "Автоматически удалять или восстанавливать объект на этом устройстве, когда это действие выполняется через веб-интерфейс",
"advanced_settings_sync_remote_deletions_title": "Синхронизация удаленных удалений [ЭКСПЕРИМЕНТАЛЬНО]",
"advanced_settings_sync_remote_deletions_subtitle": "Автоматически удалять или восстанавливать объекты на этом устройстве, когда это действие выполняется через веб-интерфейс",
"advanced_settings_sync_remote_deletions_title": "[ЭКСПЕРИМЕНТАЛЬНО] Синхронизация удаления объектов",
"advanced_settings_tile_subtitle": "Расширенные настройки",
"advanced_settings_troubleshooting_subtitle": "Включить расширенные возможности для решения проблем",
"advanced_settings_troubleshooting_title": "Решение проблем",
@@ -461,6 +463,7 @@
"app_bar_signout_dialog_title": "Выйти",
"app_settings": "Параметры приложения",
"appears_in": "Добавлено в",
"apply_count": "Применить ({count, number})",
"archive": "Архив",
"archive_action_prompt": "Объекты добавлены в Архив ({count} шт.)",
"archive_or_unarchive_photo": "Архивировать или разархивировать фото",
@@ -594,7 +597,7 @@
"backup_setting_subtitle": "Настройка активного и фонового резервного копирования",
"backup_settings_subtitle": "Настройка загрузки объектов",
"backward": "Назад",
"beta_sync": "Статус бета-синхронизации",
"beta_sync": "Статус синхронизации",
"beta_sync_subtitle": "Управление новой системой синхронизации",
"biometric_auth_enabled": "Биометрическая аутентификация включена",
"biometric_locked_out": "Вам закрыт доступ к биометрической аутентификации",
@@ -782,7 +785,7 @@
"delete_key": "Удалить ключ",
"delete_library": "Удалить библиотеку",
"delete_link": "Удалить ссылку",
"delete_local_action_prompt": "Объекты удалены локально ({count} шт.)",
"delete_local_action_prompt": "Объекты удалены с устройства ({count} шт.)",
"delete_local_dialog_ok_backed_up_only": "Удалить только резервные копии",
"delete_local_dialog_ok_force": "Все равно удалить",
"delete_others": "Удалить остальные",
@@ -1073,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Этот функционал требует загрузки внешних ресурсов с серверов Google.",
"general": "Общие",
"geolocation_instruction_all_have_location": "Все объекты в этом периоде уже содержат данные о местоположении. Включите отображение всех объектов или укажите другой период.",
"geolocation_instruction_location": "Выберите объект с имеющимися координатами, чтобы использовать их, либо вручную укажите место на карте",
"geolocation_instruction_no_date": "Укажите дату для управления координатами мест съёмки за этот день",
"geolocation_instruction_no_photos": "Не найдено объектов в этом периоде. Укажите другую дату.",
"get_help": "Получить помощь",
"get_wifiname_error": "Не удалось получить имя Wi-Fi сети. Убедитесь, что вы подключены к сети и предоставили приложению необходимые разрешения",
"getting_started": "Старт",
"go_back": "Назад",
"go_to_folder": "Перейти в папку",
"go_to_search": "Перейти к поиску",
"gps": "Есть координаты",
"gps_missing": "Нет координат",
"grant_permission": "Предоставить разрешение",
"group_albums_by": "Группировать альбомы по...",
"group_country": "Группировать по странам",
@@ -1262,6 +1271,7 @@
"main_branch_warning": "Вы используете версию приложения для разработки. Настоятельно рекомендуется перейти на релизную версию приложения!",
"main_menu": "Главное меню",
"make": "Производитель",
"manage_geolocation": "Управление местами съёмки",
"manage_shared_links": "Управление публичными ссылками",
"manage_sharing_with_partners": "Управление обменом информацией с партнерами. Эта функция позволяет вашему партнеру видеть ваши фотографии и видеозаписи, кроме тех, которые находятся в Архиве и Корзине",
"manage_the_app_settings": "Управление настройками приложения",
@@ -1508,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "Версия мобильного приложения устарела. Пожалуйста, обновите его.",
"profile_drawer_client_server_up_to_date": "Клиент и сервер обновлены",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Включён режим «только просмотр». Дважды коснитесь значка аватара пользователя, чтобы выйти.",
"profile_drawer_server_out_of_date_major": "Версия сервера устарела. Пожалуйста, обновите его.",
"profile_drawer_server_out_of_date_minor": "Версия сервера устарела. Пожалуйста, обновите его.",
"profile_image_of_user": "Изображение профиля {user}",
@@ -1553,6 +1564,8 @@
"rating_description": "Показывать рейтинг в панели информации",
"reaction_options": "Опции реакций",
"read_changelog": "Прочитать список изменений",
"readonly_mode_disabled": "Режим «только просмотр» отключён",
"readonly_mode_enabled": "Режим «только просмотр» включён",
"reassign": "Переназначить",
"reassigned_assets_to_existing_person": "Лица на {count, plural, one {# объекте} other {# объектах}} переназначены на {name, select, null {другого человека} other {человека с именем {name}}}",
"reassigned_assets_to_new_person": "Лица на {count, plural, one {# объекте} other {# объектах}} переназначены на нового человека",
@@ -1618,7 +1631,7 @@
"reset_pin_code_success": "PIN-код успешно сброшен",
"reset_pin_code_with_password": "Вы всегда можете сбросить PIN-код с помощью пароля",
"reset_sqlite": "Очистить базу данных SQLite",
"reset_sqlite_confirmation": "Вы уверены, что хотите очистить базу данных SQLite? Вам потребуется выйти из системы и снова войти для повторной синхронизации данных.",
"reset_sqlite_confirmation": "Вы действительно хотите очистить базу данных SQLite? Вам потребуется выйти из приложения и снова войти для повторной синхронизации данных.",
"reset_sqlite_success": "База данных SQLite успешно очищена",
"reset_to_default": "Восстановление значений по умолчанию",
"resolve_duplicates": "Устранить дубликаты",
@@ -1722,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Не удалось создать альбом",
"selected": "Выбрано",
"selected_count": "{count, plural, one {Выбран # объект} many {Выбрано # объектов} other {Выбрано # объекта}}",
"selected_gps_coordinates": "выбранные координаты",
"send_message": "Отправить сообщение",
"send_welcome_email": "Отправить приветственное письмо",
"server_endpoint": "Адрес сервера",
@@ -1832,8 +1846,10 @@
"shift_to_permanent_delete": "нажмите ⇧ чтобы удалить объект навсегда",
"show_album_options": "Показать параметры альбома",
"show_albums": "Показать альбомы",
"show_all_assets": "Показать все объекты",
"show_all_people": "Показать всех людей",
"show_and_hide_people": "Показать и скрыть людей",
"show_assets_without_location": "Показать объекты без координат",
"show_file_location": "Показать расположение файла",
"show_gallery": "Показать галерею",
"show_hidden_people": "Показать скрытых людей",
@@ -1941,7 +1957,9 @@
"to_change_password": "Изменить пароль",
"to_favorite": "Добавить в избранное",
"to_login": "Вход",
"to_multi_select": "выбрать несколько",
"to_parent": "Вернуться назад",
"to_select": "выбрать",
"to_trash": "Корзина",
"toggle_settings": "Переключение настроек",
"total": "Всего",
@@ -1991,6 +2009,7 @@
"unstacked_assets_count": "{count, plural, one {Разгруппирован # объект} many {Разгруппировано # объектов} other {Разгруппировано # объекта}}",
"untagged": "Без тегов",
"up_next": "Следующее",
"update_location_action_prompt": "Установить следующие координаты у выбранных объектов ({count} шт.):",
"updated_at": "Обновлён",
"updated_password": "Пароль изменён",
"upload": "Загрузить",
@@ -2015,6 +2034,7 @@
"use_biometric": "Использовать биометрию",
"use_current_connection": "Использовать текущее подключение",
"use_custom_date_range": "Использовать пользовательский диапазон дат",
"use_this_location": "Выбрать это место",
"user": "Пользователь",
"user_has_been_deleted": "Этот пользователь был удалён.",
"user_id": "ID пользователя",

View File

@@ -396,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Uprednostniť vzdialené obrázky",
"advanced_settings_proxy_headers_subtitle": "Určite hlavičky proxy servera, ktoré by mal Immich posielať s každou požiadavkou na sieť",
"advanced_settings_proxy_headers_title": "Proxy hlavičky",
"advanced_settings_readonly_mode_subtitle": "Aktivuje režim iba na čítanie, v ktorom je možné fotografie iba prezerať, pričom funkcie ako výber viacerých obrázkov, zdieľanie, prenášanie a mazanie sú deaktivované. Aktivácia/deaktivácia režimu iba na čítanie prostredníctvom obrázku používateľa na hlavnej obrazovke",
"advanced_settings_readonly_mode_title": "Režim iba na čítanie",
"advanced_settings_self_signed_ssl_subtitle": "Preskakuje overovanie SSL certifikátom zo strany servera. Vyžaduje sa pre samo-podpísané certifikáty.",
"advanced_settings_self_signed_ssl_title": "Povoliť samo-podpísané SSL certifikáty",
"advanced_settings_sync_remote_deletions_subtitle": "Automaticky vymazať alebo obnoviť položku na tomto zariadení, keď sa táto akcia vykoná na webe",
@@ -461,6 +463,7 @@
"app_bar_signout_dialog_title": "Odhlásiť sa",
"app_settings": "Nastavenia aplikácie",
"appears_in": "Vyskytuje sa v",
"apply_count": "Použiť ({count, number})",
"archive": "Archív",
"archive_action_prompt": "{count} pridaných do archívu",
"archive_or_unarchive_photo": "Archivácia alebo odarchivovanie fotografie",
@@ -500,7 +503,7 @@
"assets": "Položky",
"assets_added_count": "{count, plural, one {Pridaná # položka} few {Pridané # položky} other {Pridaných # položek}}",
"assets_added_to_album_count": "Do albumu {count, plural, one {bola pridaná # položka} few {boli pridané # položky} other {bolo pridaných # položiek}}",
"assets_added_to_albums_count": "{assetTotal, plural, one {Pridaná # položka} few {Pridané # položky} other {Pridaných # položiek}} do {albumTotal} albumov",
"assets_added_to_albums_count": "{assetTotal, plural, one {Pridaná # položka} few {Pridané # položky} other {Pridaných # položiek}} do {albumTotal, plural, one {# albumu} other {# albumov}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {položku} other {položiek}} nie je možné pridať do albumu",
"assets_cannot_be_added_to_albums": "{count, plural, one {položka} few {položky} other {položiek}} nie je možné pridať do žiadneho albumu",
"assets_count": "{count, plural, one {# položka} few {# položky} other {# položiek}}",
@@ -1073,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Táto funkcia načítava externé zdroje zo spoločnosti Google, aby mohla fungovať.",
"general": "Všeobecné",
"geolocation_instruction_all_have_location": "Všetky položky pre tento dátum už majú údaje o polohe. Skúste zobraziť všetky položky alebo vyberte iný dátum",
"geolocation_instruction_location": "Kliknite na položku s GPS súradnicami, aby ste použili jej polohu, alebo vyberte polohu priamo z mapy",
"geolocation_instruction_no_date": "Vyberte dátum, aby ste mohli spravovať údaje o polohe pre fotografie a videá z daného dňa",
"geolocation_instruction_no_photos": "Pre tento dátum neboli nájdené žiadne fotografie ani videá. Vyberte iný dátum, aby sa zobrazili",
"get_help": "Získať pomoc",
"get_wifiname_error": "Nepodarilo sa získať názov Wi-Fi siete. Uistite sa, že ste udelili potrebné oprávnenia a ste pripojení k sieti Wi-Fi",
"getting_started": "Začíname",
"go_back": "Vrátiť sa späť",
"go_to_folder": "Prejsť do priečinka",
"go_to_search": "Prejsť na vyhľadávanie",
"gps": "GPS",
"gps_missing": "Žiadne GPS",
"grant_permission": "Udeliť povolenie",
"group_albums_by": "Zoskupiť albumy podľa...",
"group_country": "Zoskupenie podľa krajiny",
@@ -1262,6 +1271,7 @@
"main_branch_warning": "Používate vývojársku verziu; dôrazne odporúčame používať vydané verzie!",
"main_menu": "Hlavná ponuka",
"make": "Výrobca",
"manage_geolocation": "Spravovať polohu",
"manage_shared_links": "Spravovať zdieľané odkazy",
"manage_sharing_with_partners": "Spravovať zdieľanie s partnermi",
"manage_the_app_settings": "Spravovať nastavenia aplikácie",
@@ -1508,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "Mobilná aplikácia je zastaralá. Prosím aktualizujte na najnovšiu verziu.",
"profile_drawer_client_server_up_to_date": "Klient a server sú aktuálne",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Režim iba na čítanie je aktivovaný. Dvojitým ťuknutím na ikonu obrázku používateľa režim opustíte.",
"profile_drawer_server_out_of_date_major": "Server je zastaralý. Prosím aktualizujte na najnovšiu verziu.",
"profile_drawer_server_out_of_date_minor": "Server je zastaralý. Prosím aktualizujte na najnovšiu verziu.",
"profile_image_of_user": "Profilový obrázok používateľa {user}",
@@ -1553,6 +1564,8 @@
"rating_description": "Zobraziť EXIF hodnotenie v informačnom paneli",
"reaction_options": "Možnosti reakcie",
"read_changelog": "Prečítať zoznam zmien",
"readonly_mode_disabled": "Režim iba na čítanie je vypnutý",
"readonly_mode_enabled": "Režim iba na čítanie je zapnutý",
"reassign": "Preradiť",
"reassigned_assets_to_existing_person": "Opätovne {count, plural, one {priradená # položka} few {priradené # položky} other {priradených # položiek}} k {name, select, null {existujúcej osobe} other {{name}}}",
"reassigned_assets_to_new_person": "Opätovne {count, plural, one {priradená # položka} few {priradené # položky} other {priradených # položiek}} novej osobe",
@@ -1700,7 +1713,7 @@
"search_tags": "Hľadať štítky...",
"search_timezone": "Hľadať časovú zónu...",
"search_type": "Typ hľadania",
"search_your_photos": "Hľadajte svoje fotky",
"search_your_photos": "Vyhľadávanie vo vašich fotografiách",
"searching_locales": "Hľadám lokality...",
"second": "Sekundy",
"see_all_people": "Pozrieť všetky osoby",
@@ -1722,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Nepodarilo sa vytvoriť album",
"selected": "Vybrané",
"selected_count": "{count, plural, one {# vybraná} few {# vybrané} other {# vybraných}}",
"selected_gps_coordinates": "vybrané GPS súradnice",
"send_message": "Odoslať správu",
"send_welcome_email": "Odoslať uvítací e-mail",
"server_endpoint": "Koncový bod servera",
@@ -1832,8 +1846,10 @@
"shift_to_permanent_delete": "stlačte ⇧ na trvalé vymazanie položky",
"show_album_options": "Zobraziť možnosti albumu",
"show_albums": "Zobraziť albumy",
"show_all_assets": "Zobraziť všetky položky",
"show_all_people": "Zobraziť všetkých ľudí",
"show_and_hide_people": "Zobraziť a skryť ľudí",
"show_assets_without_location": "Zobraziť položky bez polohy",
"show_file_location": "Zobraziť umiestnenie súboru",
"show_gallery": "Zobraziť galériu",
"show_hidden_people": "Zobraziť skrytých ľudí",
@@ -1941,7 +1957,9 @@
"to_change_password": "Zmeniť heslo",
"to_favorite": "Obľúbiť",
"to_login": "Prihlásiť",
"to_multi_select": "na viacnásobný výber",
"to_parent": "Prejsť k nadradenému",
"to_select": "na výber",
"to_trash": "Kôš",
"toggle_settings": "Prepnúť nastavenie",
"total": "Celkom",
@@ -1991,6 +2009,7 @@
"unstacked_assets_count": "Zrušenie zoskupenia pre {count, plural, one {# položku} few {# položky} other {# položiek}}",
"untagged": "Bez štítku",
"up_next": "To je všetko",
"update_location_action_prompt": "Aktualizovať polohu {count} vybraných položiek pomocou:",
"updated_at": "Aktualizované",
"updated_password": "Heslo zmenené",
"upload": "Nahrať",
@@ -2015,6 +2034,7 @@
"use_biometric": "Použiť biometrické údaje",
"use_current_connection": "použiť aktuálne pripojenie",
"use_custom_date_range": "Použiť radšej vlastný rozsah dátumov",
"use_this_location": "Kliknutím použite polohu",
"user": "Používateľ",
"user_has_been_deleted": "Tento používateľ bol vymazaný.",
"user_id": "ID používateľa",

View File

@@ -28,6 +28,9 @@
"add_to_album": "Dodaj v album",
"add_to_album_bottom_sheet_added": "Dodano v {album}",
"add_to_album_bottom_sheet_already_exists": "Že v {album}",
"add_to_album_toggle": "Preklopi izbiro za {album}",
"add_to_albums": "Dodaj v albume",
"add_to_albums_count": "Dodaj v albume ({count})",
"add_to_shared_album": "Dodaj k deljenemu albumu",
"add_url": "Dodaj URL",
"added_to_archive": "Dodano v arhiv",
@@ -393,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Uporabi raje oddaljene slike",
"advanced_settings_proxy_headers_subtitle": "Določi proxy glavo, ki jo naj Immich pošlje ob vsaki mrežni zahtevi",
"advanced_settings_proxy_headers_title": "Proxy glave",
"advanced_settings_readonly_mode_subtitle": "Omogoči način samo za branje, kjer si je mogoče fotografije samo ogledati, funkcije, kot so izbiranje več slik, deljenje, predvajanje in brisanje, so onemogočene. Omogoči/onemogoči način samo za branje prek uporabniškega avatarja na glavnem zaslonu",
"advanced_settings_readonly_mode_title": "Način samo za branje",
"advanced_settings_self_signed_ssl_subtitle": "Preskoči preverjanje potrdila SSL za končno točko strežnika. Zahtevano za samopodpisana potrdila.",
"advanced_settings_self_signed_ssl_title": "Dovoli samopodpisana SSL potrdila",
"advanced_settings_sync_remote_deletions_subtitle": "Samodejno izbriši ali obnovi sredstvo v tej napravi, ko je to dejanje izvedeno v spletu",
@@ -458,6 +463,7 @@
"app_bar_signout_dialog_title": "Odjava",
"app_settings": "Nastavitve aplikacije",
"appears_in": "Pojavi se v",
"apply_count": "Uporabi ({count, number})",
"archive": "Arhiv",
"archive_action_prompt": "v arhiv je dodanih {count}",
"archive_or_unarchive_photo": "Arhivirajte ali odstranite fotografijo iz arhiva",
@@ -497,7 +503,9 @@
"assets": "Sredstva",
"assets_added_count": "Dodano{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}",
"assets_added_to_album_count": "Dodano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v album",
"assets_added_to_albums_count": "Dodano {assetTotal, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v {albumTotal, plural, one {# album} two {# albuma} few {# albume} other {# albumov}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Sredstvo} two {Sredstvi} few {Sredstva} other {Sredstev}} ni mogoče dodati v album",
"assets_cannot_be_added_to_albums": "{count, plural, one {Sredstvo} two {Sredstvi} few {Sredstva} other {Sredstev}} ni mogoče dodati v noben album",
"assets_count": "{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}",
"assets_deleted_permanently": "trajno izrisana sredstva {count}",
"assets_deleted_permanently_from_server": "trajno izbrisana sredstva iz strežnika Immich {count}",
@@ -514,6 +522,7 @@
"assets_trashed_count": "V smetnjak {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}",
"assets_trashed_from_server": "sredstva iz strežnika Immich v smetnjaku {count}",
"assets_were_part_of_album_count": "{count, plural, one {sredstvo je} two {sredstvi sta} few {sredstva so} other {sredstev je}} že del albuma",
"assets_were_part_of_albums_count": "{count, plural, one {Sredstvo je} two {Sredstvi sta} few {Sredstva so} other {Sredstev je}} že del albumov",
"authorized_devices": "Pooblaščene naprave",
"automatic_endpoint_switching_subtitle": "Povežite se lokalno prek določenega omrežja Wi-Fi, ko je na voljo, in uporabite druge povezave drugje",
"automatic_endpoint_switching_title": "Samodejno preklapljanje URL-jev",
@@ -1056,6 +1065,7 @@
"filter_people": "Filtriraj ljudi",
"filter_places": "Filtriraj kraje",
"find_them_fast": "Z iskanjem jih hitro poiščite po imenu",
"first": "Prvi",
"fix_incorrect_match": "Popravi napačno ujemanje",
"folder": "Mapa",
"folder_not_found": "Ne najdem mape",
@@ -1066,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Ta funkcija za delovanje nalaga zunanje vire iz Googla.",
"general": "Splošno",
"geolocation_instruction_all_have_location": "Vsa sredstva za ta datum že imajo podatke o lokaciji. Poskusite prikazati vsa sredstva ali izberite drug datum",
"geolocation_instruction_location": "Kliknite na sredstvo z GPS koordinatami, da uporabite njegovo lokacijo, ali pa izberite lokacijo neposredno na zemljevidu",
"geolocation_instruction_no_date": "Izberite datum za upravljanje podatkov o lokaciji za fotografije in videoposnetke s tega dne",
"geolocation_instruction_no_photos": "Za ta datum ni bilo najdenih fotografij ali videoposnetkov. Izberite drug datum, da jih prikažete",
"get_help": "Poiščite pomoč",
"get_wifiname_error": "Imena Wi-Fi ni bilo mogoče dobiti. Prepričajte se, da ste podelili potrebna dovoljenja in ste povezani v omrežje Wi-Fi",
"getting_started": "Začetek",
"go_back": "Pojdi nazaj",
"go_to_folder": "Pojdi na mapo",
"go_to_search": "Pojdi na iskanje",
"gps": "GPS",
"gps_missing": "Brez GPS-a",
"grant_permission": "Podeli dovoljenje",
"group_albums_by": "Združi albume po ...",
"group_country": "Združi po državah",
@@ -1177,6 +1193,7 @@
"language_search_hint": "Iskanje jezikov...",
"language_setting_description": "Izberite želeni jezik",
"large_files": "Velike datoteke",
"last": "Zadnji",
"last_seen": "Nazadnje viden",
"latest_version": "Najnovejša različica",
"latitude": "Zemljepisna širina",
@@ -1254,6 +1271,7 @@
"main_branch_warning": "Uporabljate razvojno različico; močno priporočamo uporabo izdajne različice!",
"main_menu": "Glavni meni",
"make": "Izdelava",
"manage_geolocation": "Upravljanje lokacije",
"manage_shared_links": "Upravljanje povezav v skupni rabi",
"manage_sharing_with_partners": "Upravljajte skupno rabo s partnerji",
"manage_the_app_settings": "Upravljajte nastavitve aplikacije",
@@ -1500,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "Mobilna aplikacija je zastarela. Posodobite na najnovejšo manjšo različico.",
"profile_drawer_client_server_up_to_date": "Odjemalec in strežnik sta posodobljena",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Način samo za branje je omogočen. Za izhod dvakrat tapnite ikono uporabniškega avatarja.",
"profile_drawer_server_out_of_date_major": "Strežnik je zastarel. Posodobite na najnovejšo glavno različico.",
"profile_drawer_server_out_of_date_minor": "Strežnik je zastarel. Posodobite na najnovejšo manjšo različico.",
"profile_image_of_user": "Profilna slika uporabnika {user}",
@@ -1545,6 +1564,8 @@
"rating_description": "Prikažite oceno EXIF v informacijski plošči",
"reaction_options": "Možnosti reakcije",
"read_changelog": "Preberi dnevnik sprememb",
"readonly_mode_disabled": "Način samo za branje je onemogočen",
"readonly_mode_enabled": "Način samo za branje je omogočen",
"reassign": "Prerazporedi",
"reassigned_assets_to_existing_person": "Ponovno dodeljeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} za {name, select, null {an existing person} other {{name}}}",
"reassigned_assets_to_new_person": "Ponovno dodeljeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} za novo osebo",
@@ -1714,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Albuma ni bilo mogoče ustvariti",
"selected": "Izbrano",
"selected_count": "{count, plural, other {# izbranih}}",
"selected_gps_coordinates": "izbrane GPS koordinate",
"send_message": "Pošlji sporočilo",
"send_welcome_email": "Pošlji pozdravno e-pošto",
"server_endpoint": "Končna točka strežnika",
@@ -1824,8 +1846,10 @@
"shift_to_permanent_delete": "pritisni ⇧ za trajno brisanje sredstva",
"show_album_options": "Prikaži možnosti albuma",
"show_albums": "Prikaži albume",
"show_all_assets": "Prikaži vsa sredstva",
"show_all_people": "Prikaži vse osebe",
"show_and_hide_people": "Prikaži & skrij osebe",
"show_assets_without_location": "Prikaži sredstva brez lokacije",
"show_file_location": "Pokaži lokacijo datoteke",
"show_gallery": "Prikaži galerijo",
"show_hidden_people": "Prikaži skrite osebe",
@@ -1933,7 +1957,9 @@
"to_change_password": "Spremeni geslo",
"to_favorite": "Priljubljen",
"to_login": "Prijava",
"to_multi_select": "izbira več elementov",
"to_parent": "Pojdi na prvotno",
"to_select": "na izbiro",
"to_trash": "Smetnjak",
"toggle_settings": "Preklopi na nastavitve",
"total": "Skupno",
@@ -1983,6 +2009,7 @@
"unstacked_assets_count": "Razloži {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}",
"untagged": "Neoznačeno",
"up_next": "Naslednja",
"update_location_action_prompt": "Posodobi lokacijo izbranih sredstev {count} s/z:",
"updated_at": "Posodobljeno",
"updated_password": "Posodobljeno geslo",
"upload": "Naloži",
@@ -2007,6 +2034,7 @@
"use_biometric": "Uporabite biometrične podatke",
"use_current_connection": "uporabi trenutno povezavo",
"use_custom_date_range": "Namesto tega uporabite časovno obdobje po meri",
"use_this_location": "Kliknite za uporabo lokacije",
"user": "Uporabnik",
"user_has_been_deleted": "Ta uporabnik je bil izbrisan.",
"user_id": "ID uporabnika",

View File

@@ -396,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Föredra bilder från servern",
"advanced_settings_proxy_headers_subtitle": "Definiera proxy-headers som Immich ska skicka med i varje närverksanrop",
"advanced_settings_proxy_headers_title": "Proxy-headers",
"advanced_settings_readonly_mode_subtitle": "Aktiverar skrivskyddat läge där foton endast kan visas. Saker som att välja flera bilder, dela, casta och ta bort är alla inaktiverade. Aktivera/inaktivera skrivskyddat läge via användaravatar från huvudskärmen",
"advanced_settings_readonly_mode_title": "Skrivskyddat läge",
"advanced_settings_self_signed_ssl_subtitle": "Hoppar över SSL-certifikatverifiering för serverändpunkten. Krävs för självsignerade certifikat.",
"advanced_settings_self_signed_ssl_title": "Tillåt självsignerade SSL-certifikat",
"advanced_settings_sync_remote_deletions_subtitle": "Radera eller återställ automatiskt en resurs på den här enheten när den åtgärden utförs på webben",
@@ -461,6 +463,7 @@
"app_bar_signout_dialog_title": "Logga ut",
"app_settings": "Appinställningar",
"appears_in": "Visas i",
"apply_count": "Tillämpa ({count, number})",
"archive": "Arkiv",
"archive_action_prompt": "{count} adderade till Arkiv",
"archive_or_unarchive_photo": "Arkivera eller oarkivera fotot",
@@ -500,7 +503,7 @@
"assets": "Objekt",
"assets_added_count": "La till {count, plural, one {# asset} other {# assets}}",
"assets_added_to_album_count": "Lade till {count, plural, one {# asset} other {# assets}} i albumet",
"assets_added_to_albums_count": "Lade till {assetTotal, plural, one {# asset} other {# assets}} till {albumTotal} album",
"assets_added_to_albums_count": "Lade till {assetTotal, plural, one {# asset} other {# assets}} till {albumTotal, plural, one {# album} other {# albums}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} kan inte läggas till i albumet",
"assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} kan inte läggas till i något av albumen",
"assets_count": "{count, plural, one {# objekt} other {# objekt}}",
@@ -1073,12 +1076,18 @@
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Denna funktion läser in externa resurser från Google för att fungera.",
"general": "Allmänt",
"geolocation_instruction_all_have_location": "Alla tillgångar för detta datum har redan platsdata. Försök att visa alla tillgångar eller välj ett annat datum",
"geolocation_instruction_location": "Klicka på en tillgång med GPS-koordinater för att använda dess plats, eller välj en plats direkt från kartan",
"geolocation_instruction_no_date": "Välj ett datum för att hantera platsdata för foton och videor från den dagen",
"geolocation_instruction_no_photos": "Inga foton eller videor hittades för detta datum. Välj ett annat datum för att visa dem",
"get_help": "Få hjälp",
"get_wifiname_error": "Kunde inte hämta Wi-Fi-namn. Säkerställ att du tillåtit nödvändiga rättigheter och är ansluten till ett Wi-Fi-nätverk",
"getting_started": "Komma igång",
"go_back": "Gå tillbaka",
"go_to_folder": "Gå till mapp",
"go_to_search": "Gå till sök",
"gps": "GPS",
"gps_missing": "Ingen GPS",
"grant_permission": "Ge tillåtelse",
"group_albums_by": "Gruppera album efter...",
"group_country": "Gruppera per land",
@@ -1262,6 +1271,7 @@
"main_branch_warning": "Du använder en utvecklingsversion. Vi rekommenderar starkt att du använder en utgiven version!",
"main_menu": "Huvudmeny",
"make": "Tillverkare",
"manage_geolocation": "Hantera plats",
"manage_shared_links": "Hantera Delade länkar",
"manage_sharing_with_partners": "Hantera delning med partner",
"manage_the_app_settings": "Hantera appinställningarna",
@@ -1504,12 +1514,13 @@
"privacy": "Sekretess",
"profile": "Profil",
"profile_drawer_app_logs": "Loggar",
"profile_drawer_client_out_of_date_major": "Mobilappen är utdaterad. Uppdatera till senaste huvudversionen.",
"profile_drawer_client_out_of_date_minor": "Mobilappen är utdaterad. Uppdatera till senaste mindre versionen.",
"profile_drawer_client_out_of_date_major": "Mobilappen är föråldrad. Uppdatera till senaste versionen.",
"profile_drawer_client_out_of_date_minor": "Mobilappen är föråldrad. Uppdatera till senaste versionen.",
"profile_drawer_client_server_up_to_date": "Klient och server är uppdaterade",
"profile_drawer_github": "GitHub",
"profile_drawer_server_out_of_date_major": "Servern är utdaterad. Uppdatera till senaste huvudversionen.",
"profile_drawer_server_out_of_date_minor": "Servern är utdaterad. Uppdatera till senaste mindre versionen.",
"profile_drawer_readonly_mode": "Skrivskyddat läge aktiverat. Dubbeltryck på användaravatarikonen för att avsluta",
"profile_drawer_server_out_of_date_major": "Servern har en föråldrad mjukvara. Uppdatera till senaste versionen.",
"profile_drawer_server_out_of_date_minor": "Servern har en föråldrad mjukvara. Uppdatera till senaste versionen.",
"profile_image_of_user": "{user} profilbild",
"profile_picture_set": "Profilbild vald.",
"public_album": "Publikt album",
@@ -1553,6 +1564,8 @@
"rating_description": "Visa EXIF betyget i informationspanelen",
"reaction_options": "Alternativ för reaktion",
"read_changelog": "Läs ändringslogg",
"readonly_mode_disabled": "Skrivskyddat läge inaktiverat",
"readonly_mode_enabled": "Skrivskyddat läge aktiverat",
"reassign": "Omfördela",
"reassigned_assets_to_existing_person": "Tilldelade om {count, plural, one {# objekt} other {# objekt}} till {name, select, null {an existing person} other {{name}}}",
"reassigned_assets_to_new_person": "Tilldelade om {count, plural, one {# objekt} other {# objekt}} till en ny persson",
@@ -1722,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Kunde inte skapa nytt album",
"selected": "Valda",
"selected_count": "{count, plural, other {# valda}}",
"selected_gps_coordinates": "valda GPS-koordinater",
"send_message": "Skicka meddelande",
"send_welcome_email": "Skicka välkomstmejl",
"server_endpoint": "Server-endpoint",
@@ -1832,8 +1846,10 @@
"shift_to_permanent_delete": "tryck på ⇧ för att permanent radera tillgången",
"show_album_options": "Visa albumalternativ",
"show_albums": "Visa album",
"show_all_assets": "Visa alla tillgångar",
"show_all_people": "Visa alla personer",
"show_and_hide_people": "Visa & göm personer",
"show_assets_without_location": "Visa tillgångar utan plats",
"show_file_location": "Visa sökväg",
"show_gallery": "Visa galleri",
"show_hidden_people": "Visa gömda personer",
@@ -1941,7 +1957,9 @@
"to_change_password": "Ändra lösenord",
"to_favorite": "Favorit",
"to_login": "Logga in",
"to_multi_select": "för att välja flera",
"to_parent": "Gå till förälder",
"to_select": "för att välja",
"to_trash": "Papperskorg",
"toggle_settings": "Växla inställningar",
"total": "Totalt",
@@ -1991,6 +2009,7 @@
"unstacked_assets_count": "Avstaplade {count, plural, one {# asset} other {# assets}}",
"untagged": "Otaggad",
"up_next": "Kommande",
"update_location_action_prompt": "Uppdatera platsen för {count} valda tillgångar med:",
"updated_at": "Uppdaterat",
"updated_password": "Lösenordet har uppdaterats",
"upload": "Ladda upp",
@@ -2015,6 +2034,7 @@
"use_biometric": "Använd biometri",
"use_current_connection": "Använd aktuell anslutning",
"use_custom_date_range": "Använd anpassat datumintervall istället",
"use_this_location": "Klicka för att använda plats",
"user": "Användare",
"user_has_been_deleted": "Den här användaren har raderats.",
"user_id": "Användar-ID",

View File

@@ -4,6 +4,7 @@
"account_settings": "கணக்கு அமைவுகள்",
"acknowledge": "ஒப்புக்கொள்கிறேன்",
"action": "செயல்",
"action_common_update": "மேம்படுத்து",
"actions": "செயல்கள்",
"active": "செயல்பாட்டில்",
"activity": "செயல்பாடுகள்",
@@ -14,6 +15,7 @@
"add_a_name": "பெயரை சேர்க்கவும்",
"add_a_title": "தலைப்பு சேர்க்கவும்",
"add_birthday": "பிறந்தநாளைச் சேர்க்கவும்",
"add_endpoint": "சேவை நிரலை சேர்",
"add_exclusion_pattern": "விலக்கு வடிவத்தைச் சேர்க்கவும்",
"add_import_path": "இறக்குமதி பாதையை (இம்போர்ட் பாத்) சேர்க்கவும்",
"add_location": "இடத்தைச் சேர்க்கவும்",
@@ -22,10 +24,13 @@
"add_path": "பாதை (பாத்) சேர்க்கவும்",
"add_photos": "புகைப்படங்களை சேர்க்கவும்",
"add_tag": "குறியைச் சேர்க்க",
"add_to": "சேர்க்க...",
"add_to": "சேர்க்க",
"add_to_album": "ஆல்பமில் சேர்க்க",
"add_to_album_bottom_sheet_added": "{album}-இல் சேர்க்கப்பட்டது",
"add_to_album_bottom_sheet_already_exists": "ஏற்கனவே {album}-இல் உள்ளது",
"add_to_album_toggle": "{album} க்கான தேர்வை மாற்று",
"add_to_albums": "ஆல்பத்தில் சேர்",
"add_to_albums_count": "ஆல்பங்களில் சேர்({count})",
"add_to_shared_album": "பகிரப்பட்ட ஆல்பமில் சேர்க்க",
"add_url": "URL ஐச் சேர்க்கவும்",
"added_to_archive": "காப்பகத்தில் சேர்க்கப்பட்டது",
@@ -40,19 +45,26 @@
"authentication_settings_disable_all": "எல்லா உள்நுழைவு முறைகளையும் நிச்சயமாக முடக்க விரும்புகிறீர்களா? உள்நுழைவு முற்றிலும் முடக்கப்படும்.",
"authentication_settings_reenable": "மீண்டும் இயக்க, <link> சர்வர் கட்டளை</link> பயன்படுத்தவும்.",
"background_task_job": "பின்னணி பணிகள்",
"backup_database": "காப்பு தரவுத்தளம்",
"backup_database": "தரவுத்தள காப்புப்பிரதியை உருவாக்கு",
"backup_database_enable_description": "தரவுத்தள காப்புப்பிரதிகளை இயக்கவும்",
"backup_keep_last_amount": "வைத்திருக்க முந்தைய காப்புப்பிரதிகளின் அளவு",
"backup_onboarding_1_description": "கிளவுட் அல்லது வேறு இடத்தில் ஆஃப்சைட் நகல்.",
"backup_onboarding_2_description": "வெவ்வேறு சாதனங்களில் உள்ள நகல் பிரதிகள். இதில் முக்கிய கோப்புகள் மற்றும் அந்தக் கோப்புகளின் நகல் காப்புப்பிரதி ஆகியவை அடங்கும்.",
"backup_onboarding_3_description": "உங்கள் தரவின் மொத்த கோப்புகள் அசல் மற்றும் நகல்கள் உட்பட. இதில் 1 வெளிப்புற நகல் மற்றும் 2 சாதனப் பிரதிகள் அடங்கும்.",
"backup_onboarding_description": "உங்கள் தரவை பாதுகாப்பதற்காக ஒரு <backblaze-link>3-2-1 காப்புப் பிரதி</backblaze-link> பரிந்துரைக்கப்படுகிறது. முழுமையான காப்பு பாதுகாப்பு தீர்விற்காக, நீங்கள் பதிவேற்றிய புகைப்படங்கள்/வீடியோக்கள் மற்றும் Immich தரவுத்தளத்தின் நகல்களையும் வைத்திருக்க வேண்டும்.",
"backup_onboarding_footer": "Immich-ஐ தரவு நகல் காப்பு எடுப்பது பற்றிய மேலும் தகவலுக்கு, தயவுசெய்து <link>ஆவணத்தை</link> பார்க்கவும்.",
"backup_onboarding_parts_title": "3-2-1 காப்புப்பிரதியில் பின்வருவன அடங்கும்:",
"backup_onboarding_title": "காப்புப்பிரதிகள்",
"backup_settings": "காப்பு அமைப்புகள்",
"backup_settings_description": "தரவுத்தள காப்புப்பிரதி அமைப்புகளை நிர்வகிக்கவும்",
"cleared_jobs": "முடித்த வேலைகள்: {job}",
"config_set_by_file": "config தற்போது ஒரு config கோப்பு மூலம் அமைக்கப்பட்டுள்ளது",
"config_set_by_file": "கட்டமைப்பு, தற்போது ஒரு கட்டமைப்பு கோப்பு மூலம் அமைக்கப்பட்டுள்ளது",
"confirm_delete_library": "{library} படங்கள் நூலகத்தை நிச்சயமாக நீக்க விரும்புகிறீர்களா?",
"confirm_delete_library_assets": "இந்த நூலகத்தை நிச்சயமாக நீக்க விரும்புகிறீர்களா? இது Immich இலிருந்து {count, plural, one {# contained asset} other {all # contained assets}} நீக்கிவிடும், மேலும் செயல்தவிர்க்க முடியாது. கோப்புகள் வட்டில் இருக்கும்.",
"confirm_email_below": "உறுதிப்படுத்த, கீழே \"{email}\" என தட்டச்சு செய்யவும்",
"confirm_reprocess_all_faces": "எல்லா முகங்களையும் மீண்டும் செயலாக்க விரும்புகிறீர்களா? இது பெயரிடப்பட்ட நபர்களையும் அழிக்கும்.",
"confirm_user_password_reset": "{user} இன் கடவுச்சொல்லை நிச்சயமாக மீட்டமைக்க விரும்புகிறீர்களா?",
"confirm_user_pin_code_reset": "{user} இன் பின் குறியீட்டை மீட்டமைக்க விரும்புகிறீர்களா?",
"create_job": "வேலையை உருவாக்கு",
"cron_expression": "க்ரோன் வெளிப்பாடு",
"cron_expression_description": "CRON வடிவமைப்பைப் பயன்படுத்தி ச்கேனிங் இடைவெளியை அமைக்கவும். மேலும் தகவலுக்கு எ.கா. <இணைப்பு> க்ரோன்டாப் குரு </இணைப்பு>",
@@ -68,6 +80,11 @@
"force_delete_user_warning": "எச்சரிக்கை: இது பயனரையும் அனைத்து புகைப்பட சொத்துகளையும் உடனடியாக அகற்றும். இதை செயல்தவிர்க்க முடியாது மற்றும் புகைப்படங்களை மீட்டெடுக்க முடியாது.",
"image_format": "வடிவம்",
"image_format_description": "WebP, JPEG ஐ விட சிறிய கோப்புகளை உருவாக்குகிறது, ஆனால் குறியாக்கம் செய்ய மெதுவாக உள்ளது.",
"image_fullsize_description": "பெரிதாக்கும்போது பயன்படுத்தப்படும், அகற்றப்பட்ட மெட்டாடேட்டாவுடன் கூடிய முழு அளவிலான படம்",
"image_fullsize_enabled": "முழு அளவிலான பட உருவாக்கத்தை இயக்கு",
"image_fullsize_enabled_description": "இணைய இணக்கமில்லா வடிவங்களுக்கு முழு அளவிலான படத்தை உருவாக்கவும். \"உட்பொதிக்கப்பட்ட மாதிரிக்காட்சியை விரும்பு\" இயக்கப்பட்டிருக்கும் போது, உட்பொதிக்கப்பட்ட மாதிரிக்காட்சிகள் மாற்றமின்றி நேரடியாகப் பயன்படுத்தப்படும். JPEG போன்ற இணைய நட்பு வடிவங்களைப் பாதிக்காது.",
"image_fullsize_quality_description": "முழு அளவிலான படத் தரம் 1-100 வரை. அதிகமான எண் சிறந்தது, ஆனால் பெரிய கோப்புகளை உருவாக்கும்.",
"image_fullsize_title": "முழு அளவிலான பட அமைப்புகள்",
"image_prefer_embedded_preview": "உட்பொதிந்த படத்தை முன்னிடு",
"image_prefer_embedded_preview_setting_description": "கிடைக்கும்போது பட செயலாக்கத்திற்கான உள்ளீடாக மூல புகைப்படங்களில் உட்பொதிக்கப்பட்ட மாதிரிக்காட்சிகளைப் பயன்படுத்தவும். இது சில படங்களுக்கு மிகவும் துல்லியமான வண்ணங்களை உருவாக்க முடியும், ஆனால் முன்னோட்டத்தின் தகுதி கேமரா சார்ந்தது மற்றும் படத்தில் அதிக சுருக்க கலைப்பொருட்கள் இருக்கலாம்.",
"image_prefer_wide_gamut": "அகன்ற வண்ணவரம்பு தேர்வு",

View File

@@ -28,6 +28,8 @@
"add_to_album": "Albüme ekle",
"add_to_album_bottom_sheet_added": "{album} albümüne eklendi",
"add_to_album_bottom_sheet_already_exists": "Zaten {album} albümüne ekli",
"add_to_albums": "Albümlere ekle",
"add_to_albums_count": "{count} albümlerine ekle",
"add_to_shared_album": "Paylaşılan albüme ekle",
"add_url": "URL ekle",
"added_to_archive": "Arşive eklendi",
@@ -355,6 +357,9 @@
"trash_number_of_days_description": "Varlıkların kalıcı olarak silinmeden önce çöpte kaç gün tutulacağı",
"trash_settings": "Çöp ayarları",
"trash_settings_description": "Çöp ayarlarını yönet",
"unlink_all_oauth_accounts": "Tüm OAuth hesaplarının bağlantısını kaldır",
"unlink_all_oauth_accounts_description": "Yeni bir sağlayıcıya geçmeden önce tüm OAuth hesaplarını kaldırılmayı unutmayın.",
"unlink_all_oauth_accounts_prompt": "Tüm OAuth hesaplarını kaldırmak istediğinizden emin misiniz? Bu, her kullanıcı için OAuth kimliğini sıfırlar ve geri alınamaz.",
"user_cleanup_job": "Kullanıcı temizleme",
"user_delete_delay": "<b>{user}</b> hesabı ve varlıkları {delay, plural, one {# day} other {# days}} gün içinde kalıcı olarak silinmek için planlandı.",
"user_delete_delay_settings": "Silme gecikmesi",
@@ -580,8 +585,10 @@
"backup_manual_in_progress": "Yükleme halihazırda devam ediyor. Bir süre sonra deneyin",
"backup_manual_success": "Başarılı",
"backup_manual_title": "Yükleme durumu",
"backup_options": "Yedekleme Seçenekleri",
"backup_options_page_title": "Yedekleme seçenekleri",
"backup_setting_subtitle": "Arka planda ve ön planda yükleme ayarlarını düzenle",
"backup_settings_subtitle": "Yükleme ayarlarını yönet",
"backward": "Geriye doğru",
"beta_sync": "Beta Senkronizasyon Durumu",
"beta_sync_subtitle": "Yeni senkronizasyon sistemini yönetin",
@@ -651,6 +658,7 @@
"clear": "Temiz",
"clear_all": "Hepsini temizle",
"clear_all_recent_searches": "Son aramaların hepsini temizle",
"clear_file_cache": "Dosya Önbelleği Temizle",
"clear_message": "Mesajı Temizle",
"clear_value": "Değeri Temizle",
"client_cert_dialog_msg_confirm": "Tamam",
@@ -721,6 +729,7 @@
"create_new_user": "Yeni kullanıcı oluştur",
"create_shared_album_page_share_add_assets": "İÇERİK EKLE",
"create_shared_album_page_share_select_photos": "Fotoğrafları Seç",
"create_shared_link": "Paylaşılan bağlantı oluştur",
"create_tag": "Etiket oluştur",
"create_tag_description": "Yeni bir etiket oluşturun. İç içe geçmiş etiketler için, etiketi tam yolu ve eğik çizgileri de dahil ederek giriniz.",
"create_user": "Kullanıcı oluştur",
@@ -745,6 +754,7 @@
"date_of_birth_saved": "Doğum günü başarı ile kaydedildi",
"date_range": "Tarih aralığı",
"day": "Gün",
"days": "Günler",
"deduplicate_all": "Tüm kopyaları kaldır",
"deduplication_criteria_1": "Resim boyutu (bayt olarak)",
"deduplication_criteria_2": "EXIF veri sayısı",
@@ -829,9 +839,11 @@
"edit": "Düzenle",
"edit_album": "Albümü düzenle",
"edit_avatar": "Avatarı Düzenle",
"edit_birthday": "Doğum Günü Düzenle",
"edit_birthday": "Doğum gününü düzenle",
"edit_date": "Tarihi Düzenle",
"edit_date_and_time": "Tarih ve zamanı düzenleyin",
"edit_date_and_time_action_prompt": "{count} tarih ve zaman düzenlendi",
"edit_date_and_time_by_offset_interval": "Yeni tarih aralığı: {from}'dan {to}'a kadar",
"edit_description": "Açıklamayı düzenle",
"edit_description_prompt": "Lütfen yeni bir açıklama seçin:",
"edit_exclusion_pattern": "Hariç tutma desenini düzenle",
@@ -904,6 +916,7 @@
"failed_to_load_notifications": "Bildirim yüklenemedi",
"failed_to_load_people": "Kişiler yüklenemedi",
"failed_to_remove_product_key": "Ürün anahtarı kaldırılamadı",
"failed_to_reset_pin_code": "Pin kodu sıfırlanamadı",
"failed_to_stack_assets": "Varlıklar yığınlanamadı",
"failed_to_unstack_assets": "Varlıkların yığını kaldırılamadı",
"failed_to_update_notification_status": "Bildirim durumu güncellenemedi",
@@ -912,6 +925,7 @@
"paths_validation_failed": "{paths, plural, one {# Yol} other {# Yollar}} doğrulanamadı",
"profile_picture_transparent_pixels": "Profil resimleri şeffaf piksele sahip olamaz. Lütfen resme yakınlaştırın ve/veya resmi hareket ettirin.",
"quota_higher_than_disk_size": "Disk boyutundan daha yüksek bir kota belirlediniz",
"something_went_wrong": "Bir şeyler ters gitti",
"unable_to_add_album_users": "Kullanıcılar albüme eklenemiyor",
"unable_to_add_assets_to_shared_link": "Varlıklar paylaşılan bağlantıya eklenemiyor",
"unable_to_add_comment": "Yorum eklenemiyor",
@@ -1043,11 +1057,13 @@
"filter_people": "Kişileri filtrele",
"filter_places": "Yerleri süz",
"find_them_fast": "Adlarına göre hızlıca bul",
"first": "İlk",
"fix_incorrect_match": "Yanlış eşleştirmeyi düzelt",
"folder": "Klasör",
"folder_not_found": "Klasör bulunamadı",
"folders": "Klasörler",
"folders_feature_description": "Dosya sistemindeki fotoğraf ve videoları klasör görünümüyle keşfedin",
"forgot_pin_code_question": "PIN'inizi mi unuttunuz?",
"forward": "İleri",
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "Bu özellik, çalışabilmek için Google'dan harici kaynaklar yükler.",
@@ -1102,6 +1118,7 @@
"home_page_upload_err_limit": "Aynı anda en fazla 30 öğe yüklenebilir, atlanabilir",
"host": "Ana bilgisayar",
"hour": "Saat",
"hours": "Saatler",
"id": "ID",
"idle": "Boşta",
"ignore_icloud_photos": "iCloud Fotoğraflarını Yok Say",
@@ -1162,10 +1179,12 @@
"language_search_hint": "Dilleri ara...",
"language_setting_description": "Tercih ettiğiniz dili seçiniz",
"large_files": "Büyük Dosyalar",
"last": "Son",
"last_seen": "Son görülme",
"latest_version": "En son versiyon",
"latitude": "Enlem",
"leave": "Ayrıl",
"leave_album": "Albümden çık",
"lens_model": "Mercek modeli",
"let_others_respond": "Diğerlerinin yanıt vermesine izin ver",
"level": "Seviye",
@@ -1179,6 +1198,7 @@
"library_page_sort_title": "Albüm başlığı",
"licenses": "Lisanslar",
"light": "Açık",
"like": "Beğen",
"like_deleted": "Beğeni silindi",
"link_motion_video": "Hareket videosunu bağla",
"link_to_oauth": "OAuth'a bağla",
@@ -1289,6 +1309,7 @@
"merged_people_count": "{count, plural, one {# kişi} other {# kişi}} birleştirildi",
"minimize": "Küçült",
"minute": "Dakika",
"minutes": "Dakikalar",
"missing": "Eksik",
"model": "Model",
"month": "Ay",
@@ -1308,6 +1329,9 @@
"my_albums": "Albümlerim",
"name": "İsim",
"name_or_nickname": "İsim veya takma isim",
"network_requirement_photos_upload": "Fotoğrafları yedeklemek için mobil veriyi kullan",
"network_requirement_videos_upload": "Videoları yedeklemek için mobil veriyi kullan",
"network_requirements_updated": "Ağ durumu değişti, yedekleme kuyruğu sıfırlandı",
"networking_settings": "Ağ Ayarları",
"networking_subtitle": "Sunucu uç nokta ayarlarını düzenle",
"never": "Asla",
@@ -1436,6 +1460,7 @@
"permission_onboarding_permission_limited": "Sınırlı izin. Immich'in tüm fotoğrav ve videolarınızı yedeklemesine ve yönetmesine izin vermek için Ayarlar'da fotoğraf ve video izinlerini verin.",
"permission_onboarding_request": "Immich'in fotoğraflarınızı ve videolarınızı görüntüleyebilmesi için izne ihtiyacı var.",
"person": "Kişi",
"person_age_years": "{years, plural, other {# sene}} önce",
"person_birthdate": "{date} tarihinde doğdu",
"person_hidden": "{name}{hidden, select, true { (gizli)} other {}}",
"photo_shared_all_users": "Fotoğraflarınızı tüm kullanıcılarla paylaştınız gibi görünüyor veya paylaşacak kullanıcı bulunmuyor.",
@@ -1581,6 +1606,9 @@
"reset_password": "Şifreyi sıfırla",
"reset_people_visibility": "Kişilerin görünürlüğünü sıfırla",
"reset_pin_code": "PIN kodunu sıfırlayın",
"reset_pin_code_description": "Pin kodunuzu unuttuysanız, sıfırlamak için sunucu yöneticisiyle iletişime geçebilirsiniz",
"reset_pin_code_success": "Pin kodu başarıyla sıfırlandı",
"reset_pin_code_with_password": "Pin kodunuzu her zaman şifrenizle sıfırlayabilirsiniz",
"reset_sqlite": "SQLite Veritabanını Sıfırla",
"reset_sqlite_confirmation": "SQLite veritabanını sıfırlamak istediğinizden emin misiniz? Verileri yeniden senkronize etmek için oturumu kapatıp tekrar oturum açmanız gerekecektir",
"reset_sqlite_success": "SQLite veritabanını başarıyla sıfırladınız",
@@ -1829,6 +1857,7 @@
"sort_created": "Oluşturulma tarihi",
"sort_items": "Öğe sayısı",
"sort_modified": "Değişiklik tarihi",
"sort_newest": "En yeni fotoğraf",
"sort_oldest": "En eski fotoğraf",
"sort_people_by_similarity": "İnsanları benzerliğe göre sırala",
"sort_recent": "En yeni fotoğraf",
@@ -1904,7 +1933,9 @@
"to_change_password": "Şifreyi değiştir",
"to_favorite": "Gözdelere ekle",
"to_login": "Oturum aç",
"to_multi_select": "çoklu seçim için",
"to_parent": "Üst öğeye git",
"to_select": "seçmek için",
"to_trash": "Çöpe taşı",
"toggle_settings": "Ayarları değiştir",
"total": "Toplam",

View File

@@ -396,6 +396,8 @@
"advanced_settings_prefer_remote_title": "Перевага віддаленим зображенням",
"advanced_settings_proxy_headers_subtitle": "Визначте заголовки проксі-сервера, які Immich має надсилати з кожним мережевим запитом",
"advanced_settings_proxy_headers_title": "Проксі-заголовки",
"advanced_settings_readonly_mode_subtitle": "Увімкнення режиму тільки для читання, в якому фотографії можна тільки переглядати, а такі функції, як вибір декількох зображень, спільний доступ, передача, видалення, вимкнені. Увімкнення/вимкнення режиму тільки для читання за допомогою аватара користувача на головному екрані",
"advanced_settings_readonly_mode_title": "Режим лише для читання",
"advanced_settings_self_signed_ssl_subtitle": "Пропускає перевірку SSL-сертифіката сервера. Потрібне для самопідписаних сертифікатів.",
"advanced_settings_self_signed_ssl_title": "Дозволити самопідписані SSL-сертифікати",
"advanced_settings_sync_remote_deletions_subtitle": "Автоматично видаляти або відновлювати ресурс на цьому пристрої, коли ця дія виконується в веб-інтерфейсі",
@@ -461,6 +463,7 @@
"app_bar_signout_dialog_title": "Вийти з аккаунта",
"app_settings": "Налаштування програми",
"appears_in": "З'являється в",
"apply_count": "Застосувати ({count, number})",
"archive": "Архівувати",
"archive_action_prompt": "{count} додано до архіву",
"archive_or_unarchive_photo": "Архівувати або розархівувати фото",
@@ -500,7 +503,7 @@
"assets": "елементи",
"assets_added_count": "Додано {count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}}",
"assets_added_to_album_count": "Додано {count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}} до альбому",
"assets_added_to_albums_count": "Додано {assetTotal, plural, one {# актив} other {# активи}} до {albumTotal} альбомів",
"assets_added_to_albums_count": "Додано {assetTotal, plural, one {# актив} other {# активи}} до {albumTotal, plural, one {# альбом} other {# альбом}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Ресурс} other {Ресурси}} не можна додати до альбому",
"assets_cannot_be_added_to_albums": "{count, plural, one {Актив} other {Активи}} не можна додати до жодного з альбомів",
"assets_count": "{count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}}",
@@ -1073,12 +1076,18 @@
"gcast_enabled": "Google Cast'",
"gcast_enabled_description": "Ця функція завантажує зовнішні ресурси з Google для своєї роботи.",
"general": "Загальні",
"geolocation_instruction_all_have_location": "Усі об’єкти для цієї дати вже мають дані про місцезнаходження. Спробуйте показати всі об’єкти або виберіть іншу дату",
"geolocation_instruction_location": "Натисніть на об'єкт із GPS-координатами, щоб використати його місцезнаходження, або виберіть місцезнаходження безпосередньо на карті",
"geolocation_instruction_no_date": "Виберіть дату для керування даними про місцезнаходження для фотографій і відео за цей день",
"geolocation_instruction_no_photos": "Для цієї дати не знайдено фотографій чи відео. Виберіть іншу дату, щоб показати їх",
"get_help": "Отримати допомогу",
"get_wifiname_error": "Не вдалося отримати назву Wi-Fi. Переконайтеся, що ви надали необхідні дозволи та підключені до Wi-Fi мережі",
"getting_started": "Початок",
"go_back": "Повернутися назад",
"go_to_folder": "Перейти до папки",
"go_to_search": "Перейти до пошуку",
"gps": "GPS",
"gps_missing": "Немає GPS",
"grant_permission": "Надати дозвіл",
"group_albums_by": "Групувати альбоми за...",
"group_country": "Групувати за країною",
@@ -1262,6 +1271,7 @@
"main_branch_warning": "Ви використовуєте версію для розробників; настійно рекомендуємо використовувати релізну версію!",
"main_menu": "Головне меню",
"make": "Виробник",
"manage_geolocation": "Керувати місцезнаходженням",
"manage_shared_links": "Керування спільними посиланнями",
"manage_sharing_with_partners": "Керуйте спільним використанням з партнерами",
"manage_the_app_settings": "Керування налаштуваннями програми",
@@ -1508,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "Мобільний додаток застарів. Будь ласка, оновіть до останньої мінорної версії.",
"profile_drawer_client_server_up_to_date": "Клієнт та сервер — актуальні",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "Режим лише для читання ввімкнено. Двічі торкніться значка аватара користувача, щоб вийти.",
"profile_drawer_server_out_of_date_major": "Сервер застарів. Будь ласка, оновіть до останньої мажорної версії.",
"profile_drawer_server_out_of_date_minor": "Сервер застарів. Будь ласка, оновіть до останньої мінорної версії.",
"profile_image_of_user": "Зображення профілю {user}",
@@ -1553,6 +1564,8 @@
"rating_description": "Показувати рейтинг EXIF на інформаційній панелі",
"reaction_options": "Опції реакції",
"read_changelog": "Прочитати зміни в оновленні",
"readonly_mode_disabled": "Режим лише для читання вимкнено",
"readonly_mode_enabled": "Режим лише для читання ввімкнено",
"reassign": "Перепризначити",
"reassigned_assets_to_existing_person": "Перепризначено {count, plural, one {# ресурс} few {# ресурси} many {# ресурсів} other {# ресурсів}} {name, select, null {існуючій особі} other {{name}}}",
"reassigned_assets_to_new_person": "Перепризначено {count, plural, one {# ресурс} other {# ресурси}} новій особі",
@@ -1722,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "Не вдалося створити альбом",
"selected": "Обрано",
"selected_count": "{count, plural, one {# обраний} other {# обраних}}",
"selected_gps_coordinates": "вибрані GPS-координати",
"send_message": "Надіслати повідомлення",
"send_welcome_email": "Надішліть вітальний лист",
"server_endpoint": "Кінцева точка сервера",
@@ -1832,8 +1846,10 @@
"shift_to_permanent_delete": "натисніть ⇧ щоб видалити об'єкт назавжди",
"show_album_options": "Показати параметри альбому",
"show_albums": "Показувати альбоми",
"show_all_assets": "Показати всі ресурси",
"show_all_people": "Показати всіх людей",
"show_and_hide_people": "Показати та приховати людей",
"show_assets_without_location": "Показати активи без місцезнаходження",
"show_file_location": "Показати розташування файлу",
"show_gallery": "Показати галерею",
"show_hidden_people": "Показати прихованих людей",
@@ -1941,7 +1957,9 @@
"to_change_password": "Змінити пароль",
"to_favorite": "Обране",
"to_login": "Вхід",
"to_multi_select": "для багаторазового вибору",
"to_parent": "Повернутись назад",
"to_select": "вибрати",
"to_trash": "Смітник",
"toggle_settings": "Перемикання налаштувань",
"total": "Усього",
@@ -1991,6 +2009,7 @@
"unstacked_assets_count": "Розгорнути {count, plural, one {# ресурс} few {# ресурси} many {# ресурсів} other {# ресурсів}}",
"untagged": "Без тегів",
"up_next": "Наступне",
"update_location_action_prompt": "Оновити розташування вибраних об’єктів ({count}) за допомогою:",
"updated_at": "Оновлено",
"updated_password": "Пароль оновлено",
"upload": "Завантажити",
@@ -2015,6 +2034,7 @@
"use_biometric": "Використовувати біометрію",
"use_current_connection": "використовувати поточне підключення",
"use_custom_date_range": "Використовувати користувацький діапазон дат",
"use_this_location": "Натисніть, щоб використовувати місцезнаходження",
"user": "Користувач",
"user_has_been_deleted": "Користувача видалено.",
"user_id": "ID Користувача",

View File

@@ -132,7 +132,7 @@
"machine_learning_enabled": "啟用機器學習",
"machine_learning_enabled_description": "若停用,則無視下方的設定,所有機器學習的功能都將停用。",
"machine_learning_facial_recognition": "人臉辨識",
"machine_learning_facial_recognition_description": "偵測、辨識並對圖片中的臉孔分",
"machine_learning_facial_recognition_description": "偵測、辨識並對圖片中的臉孔分",
"machine_learning_facial_recognition_model": "人臉辨識模型",
"machine_learning_facial_recognition_model_description": "模型順序由大至小排列。大的模型較慢且使用較多記憶體,但成效較佳。更換模型後需對所有影像重新執行「人臉辨識」。",
"machine_learning_facial_recognition_setting": "啟用人臉辨識",
@@ -500,7 +500,7 @@
"assets": "媒體",
"assets_added_count": "已新增 {count, plural, one {# 個媒體} other {# 個媒體}}",
"assets_added_to_album_count": "已將 {count, plural, one {# 個媒體} other {# 個媒體}}加入相簿",
"assets_added_to_albums_count": "已新增 {assetTotal, plural, one {# 個} other {# 個}}項目到{albumTotal}相簿中",
"assets_added_to_albums_count": "已新增 {assetTotal, plural, one {# 個} other {# 個}}項目到 {albumTotal, plural, one {# 個} other {# 個}}相簿中",
"assets_cannot_be_added_to_album_count": "無法將 {count, plural, one {媒體} other {媒體}} 加入至相簿",
"assets_cannot_be_added_to_albums": "{count, plural, one {個} other {個}}項目無法被加入相簿",
"assets_count": "{count, plural, one {# 個媒體} other {# 個媒體}}",
@@ -1047,28 +1047,28 @@
"failed_to_load_assets": "無法載入媒體",
"failed_to_load_folder": "無法載入資料夾",
"favorite": "收藏",
"favorite_action_prompt": "已新增 {count} 個到我的最愛",
"favorite_action_prompt": "已新增 {count} 個到收藏",
"favorite_or_unfavorite_photo": "收藏或取消收藏照片",
"favorites": "收藏",
"favorites_page_no_favorites": "未找到收藏項目",
"feature_photo_updated": "特色照片已更新",
"features": "功能",
"features_setting_description": "管理應用程式功能",
"file_name": "檔",
"file_name_or_extension": "檔或副檔名",
"file_name": "檔案名稱",
"file_name_or_extension": "檔案名稱或副檔名",
"filename": "檔案名稱",
"filetype": "檔案類型",
"filter": "濾鏡",
"filter_people": "篩選人物",
"filter_places": "篩選地點",
"find_them_fast": "搜尋名稱快速找",
"find_them_fast": "透過搜尋名稱快速找到他們",
"first": "第一個",
"fix_incorrect_match": "修復不相符的",
"folder": "資料夾",
"folder_not_found": "找到資料夾",
"folder_not_found": "找到資料夾",
"folders": "資料夾",
"folders_feature_description": "資料夾瀏覽檔案系統中的照片和影片",
"forgot_pin_code_question": "忘記 PIN 碼?",
"folders_feature_description": "透過資料夾檢視瀏覽檔案系統中的相片與影片",
"forgot_pin_code_question": "忘記您的 PIN 碼?",
"forward": "由新至舊",
"gcast_enabled": "Google Cast",
"gcast_enabled_description": "此功能需要從 Google 載入外部資源才能正常運作。",
@@ -1077,87 +1077,87 @@
"get_wifiname_error": "無法取得 Wi-Fi 名稱。請確認您已授予必要的權限,並已連接至 Wi-Fi 網路",
"getting_started": "開始使用",
"go_back": "返回",
"go_to_folder": "轉至資料夾",
"go_to_folder": "前往資料夾",
"go_to_search": "前往搜尋",
"grant_permission": "授予權限",
"group_albums_by": "分類群組的方式...",
"group_country": "按國家分",
"group_no": "無分組",
"group_owner": "按擁有者分",
"group_country": "按國家分",
"group_no": "沒有分類",
"group_owner": "按擁有者分",
"group_places_by": "分類地點的方式...",
"group_year": "按年份分",
"haptic_feedback_switch": "啓用振動反饋",
"haptic_feedback_title": "振動反饋",
"has_quota": "配額",
"hash_asset": "雜湊項目",
"hashed_assets": "已雜湊項目",
"hashing": "計算雜湊值",
"group_year": "按年份分",
"haptic_feedback_switch": "啟用震動回饋",
"haptic_feedback_title": "震動回饋",
"has_quota": "已設定配額",
"hash_asset": "雜湊媒體",
"hashed_assets": "已雜湊的媒體",
"hashing": "正在計算雜湊值",
"header_settings_add_header_tip": "新增標頭",
"header_settings_field_validator_msg": "設定不可為空",
"header_settings_field_validator_msg": "不可為空",
"header_settings_header_name_input": "標頭名稱",
"header_settings_header_value_input": "標頭值",
"headers_settings_tile_subtitle": "定義代理標頭,套用於每次網請求",
"headers_settings_tile_subtitle": "定義應用程式在每次網請求時應附帶的代理標頭",
"headers_settings_tile_title": "自定義代理標頭",
"hi_user": "嗨!{name}{email}",
"hide_all_people": "隱藏所有人物",
"hide_gallery": "隱藏畫廊",
"hide_gallery": "隱藏媒體庫",
"hide_named_person": "隱藏 {name}",
"hide_password": "隱藏密碼",
"hide_person": "隱藏人物",
"hide_unnamed_people": "隱藏未命名人物",
"home_page_add_to_album_conflicts": "已在相簿 {album} 中新增 {added} 個項目。其中 {failed} 個項目已經在相簿中。",
"home_page_add_to_album_err_local": "暫不能將本地項目新增到相簿,略過",
"home_page_add_to_album_success": "已在相簿 {album} 中新增 {added} 個項目。",
"home_page_album_err_partner": "暫無法將親朋好友的項目新增到相簿,略過",
"home_page_archive_err_local": "暫無法封存本地項目,略過",
"home_page_archive_err_partner": "無法封存親朋好友的項目,略過",
"home_page_building_timeline": "正在生成時間",
"home_page_delete_err_partner": "無法刪除親朋好友的項目,略過",
"home_page_delete_remote_err_local": "遙距項目刪除模式,略過本地項目",
"hide_unnamed_people": "隱藏未命名人物",
"home_page_add_to_album_conflicts": "已將 {added} 個媒體新增到相簿 {album}。{failed} 個媒體已在該相簿中。",
"home_page_add_to_album_err_local": "暫不能將本地媒體新增到相簿,略過",
"home_page_add_to_album_success": "已在 {album} 相簿中新增 {added} 個媒體。",
"home_page_album_err_partner": "暫時不能無法將親朋好友的媒體新增到相簿,略過",
"home_page_archive_err_local": "暫時不能封存本地媒體,已略過",
"home_page_archive_err_partner": "無法封存親朋好友的媒體,已略過",
"home_page_building_timeline": "正在建立時間",
"home_page_delete_err_partner": "無法刪除親朋好友的媒體,已略過",
"home_page_delete_remote_err_local": "刪除遠端媒體的選取中包含本機媒體,已略過",
"home_page_favorite_err_local": "暫不能收藏本地項目,略過",
"home_page_favorite_err_partner": "暫無法收藏親朋好友的項目,略過",
"home_page_first_time_notice": "如果這是您第一次使用本程式,請確保選擇一個要備份的相簿,以將照片與影片加入時間軸",
"home_page_locked_error_local": "無法移動本機檔案至鎖定的資料夾,已略過",
"home_page_locked_error_partner": "無法移動他人分享的檔案至鎖定的資料夾,已略過",
"home_page_share_err_local": "無法通過鏈接共享本地項目,略過",
"home_page_upload_err_limit": "一次最多只能上傳 30 個項目,略過",
"home_page_locked_error_partner": "無法移動親朋好友分享的媒體至鎖定的資料夾,已略過",
"home_page_share_err_local": "無法通過連結共享本地媒體,已略過",
"home_page_upload_err_limit": "一次最多只能上傳 30 個媒體,已略過",
"host": "主機",
"hour": "小時",
"hours": "小時",
"id": "ID",
"idle": "閒置",
"ignore_icloud_photos": "忽略iCloud照片",
"ignore_icloud_photos_description": "存儲在iCloud中的照片不會上傳至Immich伺服器",
"ignore_icloud_photos": "忽略 iCloud 照片",
"ignore_icloud_photos_description": "儲存在 iCloud 中的照片不會上傳至 Immich 伺服器",
"image": "圖片",
"image_alt_text_date": "{isVideo, select, true {影片} other {圖片}}拍攝於 {date}",
"image_alt_text_date_1_person": "{isVideo, select, true {影片} other {圖片}} 與 {person1} 一同於 {date} 拍攝",
"image_alt_text_date_2_people": "{isVideo, select, true {影片} other {圖片}} 與 {person1} 和 {person2} 一同於 {date} 拍攝",
"image_alt_text_date_3_people": "{isVideo, select, true {影片} other {圖片}} 與 {person1}、{person2} 和 {person3} 一同於 {date} 拍攝",
"image_alt_text_date_4_or_more_people": "{isVideo, select, true {影片} other {圖片}} 與 {person1}、{person2} 和其他 {additionalCount, number} 人於 {date} 拍攝",
"image_alt_text_date_place": "{date}在 {country} - {city} 拍攝的{isVideo, select, true {影片} other {圖片}}",
"image_alt_text_date_place_1_person": "{isVideo, select, true {影片} other {圖片}} {city}、{country},與 {person1} 一同 {date} 拍攝",
"image_alt_text_date_place_2_people": "{isVideo, select, true {影片} other {圖片}} {city}、{country}與 {person1} 和 {person2} 一同於 {date} 拍攝",
"image_alt_text_date_place_3_people": "{isVideo, select, true {影片} other {圖片}} {city}、{country}與 {person1}、{person2} 和 {person3} 一同於 {date} 拍攝",
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {影片} other {圖片}} {city}、{country}與 {person1}、{person2} 和其他 {additionalCount, number} 人於 {date} 拍攝",
"image_saved_successfully": "圖片已儲存",
"image_viewer_page_state_provider_download_started": "下載動",
"image_alt_text_date_2_people": "{person1} 和 {person2} 一同於 {date} 拍攝的{isVideo, select, true {影片} other {圖片}}",
"image_alt_text_date_3_people": "{person1}、{person2} 和 {person3} 一同於 {date} 拍攝的{isVideo, select, true {影片} other {圖片}}",
"image_alt_text_date_4_or_more_people": "{person1}、{person2} 和其他 {additionalCount, number} 人於 {date} 拍攝的{isVideo, select, true {影片} other {圖片}}",
"image_alt_text_date_place": "{date} 在 {country} - {city} 拍攝的{isVideo, select, true {影片} other {圖片}}",
"image_alt_text_date_place_1_person": "在 {country} - {city},與 {person1} 一同 {date} 拍攝的{isVideo, select, true {影片} other {圖片}}",
"image_alt_text_date_place_2_people": "在 {country} - {city} 與 {person1} 和 {person2} 一同於 {date} 拍攝的{isVideo, select, true {影片} other {圖片}}",
"image_alt_text_date_place_3_people": "在 {country} - {city} 與 {person1}、{person2} 和 {person3} 一同於 {date} 拍攝的{isVideo, select, true {影片} other {圖片}}",
"image_alt_text_date_place_4_or_more_people": "在 {country} - {city} 與 {person1}、{person2} 和其他 {additionalCount, number} 人於 {date} 拍攝的{isVideo, select, true {影片} other {圖片}}",
"image_saved_successfully": "已儲存圖片",
"image_viewer_page_state_provider_download_started": "下載已啟動",
"image_viewer_page_state_provider_download_success": "下載成功",
"image_viewer_page_state_provider_share_error": "享時發生錯誤",
"image_viewer_page_state_provider_share_error": "享時發生錯誤",
"immich_logo": "Immich 標誌",
"immich_web_interface": "Immich 網頁介面",
"import_from_json": "匯入 JSON",
"import_from_json": " JSON 匯入",
"import_path": "匯入路徑",
"in_albums": "在 {count, plural, other {# 本相簿}}中",
"in_archive": "封存",
"in_albums": "在 {count, plural, one {# 本相簿} other {# 本相簿}}中",
"in_archive": "封存",
"include_archived": "包含已封存",
"include_shared_albums": "包含共享相簿",
"include_shared_partner_assets": "包括共享親朋好友檔案",
"include_shared_partner_assets": "包括共享親朋好友的媒體",
"individual_share": "個別分享",
"individual_shares": "個別分享",
"info": "資訊",
"interval": {
"day_at_onepm": "每天下午 1 點",
"hours": "每 {hours, plural, other {{hours, number} 小時}}",
"hours": "每 {hours, plural, one {小時} other {{hours, number} 小時}}",
"night_at_midnight": "每晚午夜",
"night_at_twoam": "每晚凌晨 2 點"
},
@@ -1165,13 +1165,13 @@
"invalid_date_format": "無效的日期格式",
"invite_people": "邀請人員",
"invite_to_album": "邀請至相簿",
"ios_debug_info_fetch_ran_at": "{dateTime}執行取回",
"ios_debug_info_last_sync_at": "{dateTime}最後一次同步",
"ios_debug_info_fetch_ran_at": "抓取已於 {dateTime} 執行",
"ios_debug_info_last_sync_at": "上次同步於 {dateTime}",
"ios_debug_info_no_processes_queued": "無排程中的背景程序",
"ios_debug_info_no_sync_yet": "尚未執行任何背景同步任務",
"ios_debug_info_processes_queued": "{count, plural, one {{count} 個背景程序已排程} other {{count} 個背景程序已排程}}",
"ios_debug_info_processing_ran_at": "程序於{dateTime}執行",
"items_count": "{count, plural, other {# 個項目}}",
"ios_debug_info_processing_ran_at": "於 {dateTime} 執行處理",
"items_count": "{count, plural, one {# 個項目} other {# 個項目}}",
"jobs": "任務",
"keep": "保留",
"keep_all": "全部保留",
@@ -1446,7 +1446,7 @@
"pending": "待處理",
"people": "人物",
"people_edits_count": "編輯了 {count, plural, one {# 位人士} other {# 位人士}}",
"people_feature_description": "以人物分瀏覽照片和影片",
"people_feature_description": "以人物分瀏覽照片和影片",
"people_sidebar_description": "在側邊欄顯示「人物」的連結",
"permanent_deletion_warning": "永久刪除警告",
"permanent_deletion_warning_setting_description": "在永久刪除檔案時顯示警告",
@@ -1908,7 +1908,7 @@
"tag": "標籤",
"tag_assets": "標記檔案",
"tag_created": "已建立標籤:{tag}",
"tag_feature_description": "以邏輯標記要旨分瀏覽照片和影片",
"tag_feature_description": "以邏輯標記要旨分瀏覽照片和影片",
"tag_not_found_question": "找不到標籤?<link>建立新標籤。</link>",
"tag_people": "標籤人物",
"tag_updated": "已更新標籤:{tag}",

View File

@@ -396,6 +396,8 @@
"advanced_settings_prefer_remote_title": "优先远程项目",
"advanced_settings_proxy_headers_subtitle": "定义代理标头,应用于 Immich 的每次网络请求",
"advanced_settings_proxy_headers_title": "代理标头",
"advanced_settings_readonly_mode_subtitle": "启用只读模式,在该模式下只能查看照片,多选、共享、投屏、删除等操作都被禁用。从主屏幕通过用户头像启用/禁用只读",
"advanced_settings_readonly_mode_title": "只读模式",
"advanced_settings_self_signed_ssl_subtitle": "跳过对服务器 的 SSL 证书验证(该选项适用于使用自签名证书的服务器)。",
"advanced_settings_self_signed_ssl_title": "允许自签名 SSL 证书",
"advanced_settings_sync_remote_deletions_subtitle": "在网页上执行操作时,自动删除或还原该设备中的项目",
@@ -461,6 +463,7 @@
"app_bar_signout_dialog_title": "退出登录",
"app_settings": "应用设置",
"appears_in": "出现于",
"apply_count": "应用 ({count, number}个资产)",
"archive": "归档",
"archive_action_prompt": "已将 {count} 项添加到归档",
"archive_or_unarchive_photo": "归档或取消归档照片",
@@ -500,7 +503,7 @@
"assets": "项目",
"assets_added_count": "已添加{count, plural, one {#个项目} other {#个项目}}",
"assets_added_to_album_count": "已添加{count, plural, one {#个项目} other {#个项目}}到相册",
"assets_added_to_albums_count": "已添加 {assetTotal, plural, one {# 个项目} other {# 个项目}}到 {albumTotal} 个相册",
"assets_added_to_albums_count": "已添加 {assetTotal, plural, one {# 个项目} other {# 个项目}}到 {albumTotal, plural, one {# 个相册} other {# 个相册}}",
"assets_cannot_be_added_to_album_count": "无法添加 {count, plural, one {个项目} other {个项目}} 到相册中",
"assets_cannot_be_added_to_albums": "无法添加 {count, plural, one {个项目} other {个项目}} 到相册",
"assets_count": "{count, plural, one {#个项目} other {#个项目}}",
@@ -895,7 +898,7 @@
"errors": {
"cannot_navigate_next_asset": "无法导航到下一个项目",
"cannot_navigate_previous_asset": "无法导航到上一个项目",
"cant_apply_changes": "无应用更改",
"cant_apply_changes": "无应用更改",
"cant_change_activity": "无法{enabled, select, true {禁用} other {启用}}活动",
"cant_change_asset_favorite": "无法修改项目的收藏属性",
"cant_change_metadata_assets_count": "无法修改{count, plural, one {#个项目} other {#个项目}}的元数据",
@@ -1073,12 +1076,18 @@
"gcast_enabled": "Google Cast 投屏",
"gcast_enabled_description": "该功能需要加载来自 Google 的外部资源。",
"general": "通用",
"geolocation_instruction_all_have_location": "此日期的所有资产都已具有位置数据。尝试显示所有资产或选择其他日期",
"geolocation_instruction_location": "点击带有GPS坐标的资产以使用其位置或直接从地图上选择位置",
"geolocation_instruction_no_date": "选择一个日期来管理当天照片和视频的位置数据",
"geolocation_instruction_no_photos": "没有找到此日期的照片或视频。选择其他日期显示它们",
"get_help": "获取帮助",
"get_wifiname_error": "无法获取 Wi-Fi 名称。确保已授予必要的权限,并已连接到 Wi-Fi 网络",
"getting_started": "入门",
"go_back": "返回",
"go_to_folder": "进入文件夹",
"go_to_search": "前往搜索",
"gps": "有GPS信息",
"gps_missing": "无GPS信息",
"grant_permission": "获取权限",
"group_albums_by": "相册分组依据...",
"group_country": "按国家分组",
@@ -1218,11 +1227,11 @@
"local_network_sheet_info": "当使用指定的 Wi-Fi 网络时,应用程序将通过此 URL 访问服务器",
"location_permission": "定位权限",
"location_permission_content": "使用自动切换功能Immich 需要精确定位权限,以获取当前 Wi-Fi 网络名称",
"location_picker_choose_on_map": "在地图上选择",
"location_picker_latitude_error": "输入有效的纬度",
"location_picker_latitude_hint": "请在此处输入您的纬度",
"location_picker_longitude_error": "输入有效的经度",
"location_picker_longitude_hint": "请在此处输入您的经度",
"location_picker_choose_on_map": "在地图上定位",
"location_picker_latitude_error": "输入有效的纬度",
"location_picker_latitude_hint": "请在此处输入纬度",
"location_picker_longitude_error": "输入有效的经度",
"location_picker_longitude_hint": "请在此处输入经度",
"lock": "锁定",
"locked_folder": "锁定文件夹",
"log_out": "注销",
@@ -1262,6 +1271,7 @@
"main_branch_warning": "您当前使用的是开发版我们强烈建议您使用正式发行版release版",
"main_menu": "主菜单",
"make": "品牌",
"manage_geolocation": "管理坐标位置",
"manage_shared_links": "管理共享链接",
"manage_sharing_with_partners": "管理与同伴的共享",
"manage_the_app_settings": "管理应用设置",
@@ -1357,7 +1367,7 @@
"no_albums_yet": "貌似您还没有创建相册。",
"no_archived_assets_message": "归档照片和视频以便在照片视图中隐藏它们",
"no_assets_message": "点击上传您的第一张照片",
"no_assets_to_show": "无项目展示",
"no_assets_to_show": "没有要显示的资产",
"no_cast_devices_found": "未找到投放设备",
"no_duplicates_found": "未发现重复项。",
"no_exif_info_available": "没有可用的 EXIF 信息",
@@ -1508,6 +1518,7 @@
"profile_drawer_client_out_of_date_minor": "客户端有小版本升级,请尽快升级至最新版。",
"profile_drawer_client_server_up_to_date": "客户端和服务端都是最新的",
"profile_drawer_github": "GitHub",
"profile_drawer_readonly_mode": "只读模式已启用。双击用户头像图标退出。",
"profile_drawer_server_out_of_date_major": "服务端有大版本升级,请尽快升级至最新版。",
"profile_drawer_server_out_of_date_minor": "服务端有小版本升级,请尽快升级至最新版。",
"profile_image_of_user": "{user}的个人资料图片",
@@ -1553,6 +1564,8 @@
"rating_description": "在信息面板中展示 EXIF 星级",
"reaction_options": "回应选项",
"read_changelog": "阅读更新日志",
"readonly_mode_disabled": "只读模式已禁用",
"readonly_mode_enabled": "只读模式已启用",
"reassign": "重新指派",
"reassigned_assets_to_existing_person": "重新指派{count, plural, one {#个项目} other {#个项目}}到{name, select, null {已存在的人物} other {{name}}}",
"reassigned_assets_to_new_person": "重新指派{count, plural, one {#个项目} other {#个项目}}到新的人物",
@@ -1722,6 +1735,7 @@
"select_user_for_sharing_page_err_album": "创建相册失败",
"selected": "已选择",
"selected_count": "{count, plural, other {#项已选择}}",
"selected_gps_coordinates": "选定的GPS坐标",
"send_message": "发送消息",
"send_welcome_email": "发送欢迎邮件",
"server_endpoint": "服务器 URL",
@@ -1832,8 +1846,10 @@
"shift_to_permanent_delete": "按住 ⇧ Shift 键永久删除项目",
"show_album_options": "显示相册选项",
"show_albums": "显示相册",
"show_all_assets": "显示所有资产",
"show_all_people": "显示所有人物",
"show_and_hide_people": "显示和隐藏人物",
"show_assets_without_location": "显示不带GPS定位的资产",
"show_file_location": "显示文件位置",
"show_gallery": "显示图库",
"show_hidden_people": "显示隐藏人物",
@@ -1941,7 +1957,9 @@
"to_change_password": "修改密码",
"to_favorite": "收藏",
"to_login": "登录",
"to_multi_select": "多选",
"to_parent": "返回上一级",
"to_select": "选择",
"to_trash": "放入回收站",
"toggle_settings": "切换设置",
"total": "总计",
@@ -1991,6 +2009,7 @@
"unstacked_assets_count": "{count, plural, one {#个项目} other {#个项目}}已取消堆叠",
"untagged": "无标签",
"up_next": "下一个",
"update_location_action_prompt": "更新 {count} 个所选资产的位置:",
"updated_at": "已更新",
"updated_password": "更新密码",
"upload": "上传",
@@ -2015,6 +2034,7 @@
"use_biometric": "使用生物识别",
"use_current_connection": "使用当前连接",
"use_custom_date_range": "自定义日期范围",
"use_this_location": "单击以使用此位置",
"user": "用户",
"user_has_been_deleted": "此用户已被删除。",
"user_id": "用户 ID",

View File

@@ -59,7 +59,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
RUN apt-get update && apt-get install -y --no-install-recommends g++
COPY --from=ghcr.io/astral-sh/uv:latest@sha256:f64ad69940b634e75d2e4d799eb5238066c5eeda49f76e782d4873c3d014ea33 /uv /uvx /bin/
COPY --from=ghcr.io/astral-sh/uv:latest@sha256:f3660c56d5b08d6c516360981bedc439f499b9bf37f46a216018da3777a74011 /uv /uvx /bin/
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \

213
machine-learning/uv.lock generated
View File

@@ -1341,7 +1341,7 @@ wheels = [
[[package]]
name = "locust"
version = "2.38.1"
version = "2.39.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "configargparse" },
@@ -1353,6 +1353,8 @@ dependencies = [
{ name = "locust-cloud" },
{ name = "msgpack" },
{ name = "psutil" },
{ name = "python-engineio" },
{ name = "python-socketio", extra = ["client"] },
{ name = "pywin32", marker = "sys_platform == 'win32'" },
{ name = "pyzmq" },
{ name = "requests" },
@@ -1361,9 +1363,9 @@ dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
{ name = "werkzeug" },
]
sdist = { url = "https://files.pythonhosted.org/packages/be/03/2f92b75d971e6043cca6fcec59ceccfa800a1324425a74950603d8cac33a/locust-2.38.1.tar.gz", hash = "sha256:4ad9f2f9e7d56b7747ba67cb16e47ca0466b3908f402f50660f15f37621a5218", size = 1406572, upload-time = "2025-08-12T11:38:52.007Z" }
sdist = { url = "https://files.pythonhosted.org/packages/95/c8/10aa5445c404eed389b56877e6714c1787190cc09dd70059ce3765979ec5/locust-2.39.1.tar.gz", hash = "sha256:6bdd19e27edf9a1c84391d6cf6e9a737dfb832be7dfbf39053191ae31b9cc498", size = 1409902, upload-time = "2025-08-29T17:41:01.544Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dd/f6/4a8087f44abd67bb8cc51fba52dcfdddc09d69c154819d56b7da4c79f9ad/locust-2.38.1-py3-none-any.whl", hash = "sha256:34978219ee0d682a135fd4c67f287c26725e7b3fa83d34d65be70efdb42ab4d1", size = 1424130, upload-time = "2025-08-12T11:38:49.707Z" },
{ url = "https://files.pythonhosted.org/packages/ec/b3/b2f4b2ca88b1e72eba7be2b2982533b887f8b709d222db78eb9602aa5121/locust-2.39.1-py3-none-any.whl", hash = "sha256:fd5148f2f1a4ed34aee968abc4393674e69d1b5e1b54db50a397f6eb09ce0b04", size = 1428155, upload-time = "2025-08-29T17:41:00.245Z" },
]
[[package]]
@@ -1838,79 +1840,79 @@ wheels = [
[[package]]
name = "orjson"
version = "3.11.2"
version = "3.11.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/1d/5e0ae38788bdf0721326695e65fdf41405ed535f633eb0df0f06f57552fa/orjson-3.11.2.tar.gz", hash = "sha256:91bdcf5e69a8fd8e8bdb3de32b31ff01d2bd60c1e8d5fe7d5afabdcf19920309", size = 5470739, upload-time = "2025-08-12T15:12:28.626Z" }
sdist = { url = "https://files.pythonhosted.org/packages/be/4d/8df5f83256a809c22c4d6792ce8d43bb503be0fb7a8e4da9025754b09658/orjson-3.11.3.tar.gz", hash = "sha256:1c0603b1d2ffcd43a411d64797a19556ef76958aef1c182f22dc30860152a98a", size = 5482394, upload-time = "2025-08-26T17:46:43.171Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a1/7b/7aebe925c6b1c46c8606a960fe1d6b681fccd4aaf3f37cd647c3309d6582/orjson-3.11.2-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d6b8a78c33496230a60dc9487118c284c15ebdf6724386057239641e1eb69761", size = 226896, upload-time = "2025-08-12T15:10:22.02Z" },
{ url = "https://files.pythonhosted.org/packages/7d/39/c952c9b0d51063e808117dd1e53668a2e4325cc63cfe7df453d853ee8680/orjson-3.11.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc04036eeae11ad4180d1f7b5faddb5dab1dee49ecd147cd431523869514873b", size = 111845, upload-time = "2025-08-12T15:10:24.963Z" },
{ url = "https://files.pythonhosted.org/packages/f5/dc/90b7f29be38745eeacc30903b693f29fcc1097db0c2a19a71ffb3e9f2a5f/orjson-3.11.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c04325839c5754c253ff301cee8aaed7442d974860a44447bb3be785c411c27", size = 116395, upload-time = "2025-08-12T15:10:26.314Z" },
{ url = "https://files.pythonhosted.org/packages/10/c2/fe84ba63164c22932b8d59b8810e2e58590105293a259e6dd1bfaf3422c9/orjson-3.11.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32769e04cd7fdc4a59854376211145a1bbbc0aea5e9d6c9755d3d3c301d7c0df", size = 118768, upload-time = "2025-08-12T15:10:27.605Z" },
{ url = "https://files.pythonhosted.org/packages/a9/ce/d9748ec69b1a4c29b8e2bab8233e8c41c583c69f515b373f1fb00247d8c9/orjson-3.11.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ff285d14917ea1408a821786e3677c5261fa6095277410409c694b8e7720ae0", size = 120887, upload-time = "2025-08-12T15:10:29.153Z" },
{ url = "https://files.pythonhosted.org/packages/c1/66/b90fac8e4a76e83f981912d7f9524d402b31f6c1b8bff3e498aa321c326c/orjson-3.11.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2662f908114864b63ff75ffe6ffacf996418dd6cc25e02a72ad4bda81b1ec45a", size = 123650, upload-time = "2025-08-12T15:10:30.602Z" },
{ url = "https://files.pythonhosted.org/packages/33/81/56143898d1689c7f915ac67703efb97e8f2f8d5805ce8c2c3fd0f2bb6e3d/orjson-3.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab463cf5d08ad6623a4dac1badd20e88a5eb4b840050c4812c782e3149fe2334", size = 121287, upload-time = "2025-08-12T15:10:31.868Z" },
{ url = "https://files.pythonhosted.org/packages/80/de/f9c6d00c127be766a3739d0d85b52a7c941e437d8dd4d573e03e98d0f89c/orjson-3.11.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:64414241bde943cbf3c00d45fcb5223dca6d9210148ba984aae6b5d63294502b", size = 119637, upload-time = "2025-08-12T15:10:33.078Z" },
{ url = "https://files.pythonhosted.org/packages/67/4c/ab70c7627022d395c1b4eb5badf6196b7144e82b46a3a17ed2354f9e592d/orjson-3.11.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:7773e71c0ae8c9660192ff144a3d69df89725325e3d0b6a6bb2c50e5ebaf9b84", size = 392478, upload-time = "2025-08-12T15:10:34.669Z" },
{ url = "https://files.pythonhosted.org/packages/77/91/d890b873b69311db4fae2624c5603c437df9c857fb061e97706dac550a77/orjson-3.11.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:652ca14e283b13ece35bf3a86503c25592f294dbcfc5bb91b20a9c9a62a3d4be", size = 134343, upload-time = "2025-08-12T15:10:35.978Z" },
{ url = "https://files.pythonhosted.org/packages/47/16/1aa248541b4830274a079c4aeb2aa5d1ff17c3f013b1d0d8d16d0848f3de/orjson-3.11.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:26e99e98df8990ecfe3772bbdd7361f602149715c2cbc82e61af89bfad9528a4", size = 123887, upload-time = "2025-08-12T15:10:37.601Z" },
{ url = "https://files.pythonhosted.org/packages/95/e4/7419833c55ac8b5f385d00c02685a260da1f391e900fc5c3e0b797e0d506/orjson-3.11.2-cp310-cp310-win32.whl", hash = "sha256:5814313b3e75a2be7fe6c7958201c16c4560e21a813dbad25920752cecd6ad66", size = 124560, upload-time = "2025-08-12T15:10:38.966Z" },
{ url = "https://files.pythonhosted.org/packages/74/f8/27ca7ef3e194c462af32ce1883187f5ec483650c559166f0de59c4c2c5f0/orjson-3.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:dc471ce2225ab4c42ca672f70600d46a8b8e28e8d4e536088c1ccdb1d22b35ce", size = 119700, upload-time = "2025-08-12T15:10:40.911Z" },
{ url = "https://files.pythonhosted.org/packages/78/7d/e295df1ac9920cbb19fb4c1afa800e86f175cb657143aa422337270a4782/orjson-3.11.2-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:888b64ef7eaeeff63f773881929434a5834a6a140a63ad45183d59287f07fc6a", size = 226502, upload-time = "2025-08-12T15:10:42.284Z" },
{ url = "https://files.pythonhosted.org/packages/65/21/ffb0f10ea04caf418fb4e7ad1fda4b9ab3179df9d7a33b69420f191aadd5/orjson-3.11.2-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:83387cc8b26c9fa0ae34d1ea8861a7ae6cff8fb3e346ab53e987d085315a728e", size = 115999, upload-time = "2025-08-12T15:10:43.738Z" },
{ url = "https://files.pythonhosted.org/packages/90/d5/8da1e252ac3353d92e6f754ee0c85027c8a2cda90b6899da2be0df3ef83d/orjson-3.11.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7e35f003692c216d7ee901b6b916b5734d6fc4180fcaa44c52081f974c08e17", size = 111563, upload-time = "2025-08-12T15:10:45.301Z" },
{ url = "https://files.pythonhosted.org/packages/4f/81/baabc32e52c570b0e4e1044b1bd2ccbec965e0de3ba2c13082255efa2006/orjson-3.11.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a0a4c29ae90b11d0c00bcc31533854d89f77bde2649ec602f512a7e16e00640", size = 116222, upload-time = "2025-08-12T15:10:46.92Z" },
{ url = "https://files.pythonhosted.org/packages/8d/b7/da2ad55ad80b49b560dce894c961477d0e76811ee6e614b301de9f2f8728/orjson-3.11.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:585d712b1880f68370108bc5534a257b561672d1592fae54938738fe7f6f1e33", size = 118594, upload-time = "2025-08-12T15:10:48.488Z" },
{ url = "https://files.pythonhosted.org/packages/61/be/014f7eab51449f3c894aa9bbda2707b5340c85650cb7d0db4ec9ae280501/orjson-3.11.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d08e342a7143f8a7c11f1c4033efe81acbd3c98c68ba1b26b96080396019701f", size = 120700, upload-time = "2025-08-12T15:10:49.811Z" },
{ url = "https://files.pythonhosted.org/packages/cf/ae/c217903a30c51341868e2d8c318c59a8413baa35af54d7845071c8ccd6fe/orjson-3.11.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c0f84fc50398773a702732c87cd622737bf11c0721e6db3041ac7802a686fb", size = 123433, upload-time = "2025-08-12T15:10:51.06Z" },
{ url = "https://files.pythonhosted.org/packages/57/c2/b3c346f78b1ff2da310dd300cb0f5d32167f872b4d3bb1ad122c889d97b0/orjson-3.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:140f84e3c8d4c142575898c91e3981000afebf0333df753a90b3435d349a5fe5", size = 121061, upload-time = "2025-08-12T15:10:52.381Z" },
{ url = "https://files.pythonhosted.org/packages/00/c8/c97798f6010327ffc75ad21dd6bca11ea2067d1910777e798c2849f1c68f/orjson-3.11.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96304a2b7235e0f3f2d9363ddccdbfb027d27338722fe469fe656832a017602e", size = 119410, upload-time = "2025-08-12T15:10:53.692Z" },
{ url = "https://files.pythonhosted.org/packages/37/fd/df720f7c0e35694617b7f95598b11a2cb0374661d8389703bea17217da53/orjson-3.11.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3d7612bb227d5d9582f1f50a60bd55c64618fc22c4a32825d233a4f2771a428a", size = 392294, upload-time = "2025-08-12T15:10:55.079Z" },
{ url = "https://files.pythonhosted.org/packages/ba/52/0120d18f60ab0fe47531d520372b528a45c9a25dcab500f450374421881c/orjson-3.11.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a134587d18fe493befc2defffef2a8d27cfcada5696cb7234de54a21903ae89a", size = 134134, upload-time = "2025-08-12T15:10:56.568Z" },
{ url = "https://files.pythonhosted.org/packages/ec/10/1f967671966598366de42f07e92b0fc694ffc66eafa4b74131aeca84915f/orjson-3.11.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0b84455e60c4bc12c1e4cbaa5cfc1acdc7775a9da9cec040e17232f4b05458bd", size = 123745, upload-time = "2025-08-12T15:10:57.907Z" },
{ url = "https://files.pythonhosted.org/packages/43/eb/76081238671461cfd0f47e0c24f408ffa66184237d56ef18c33e86abb612/orjson-3.11.2-cp311-cp311-win32.whl", hash = "sha256:f0660efeac223f0731a70884e6914a5f04d613b5ae500744c43f7bf7b78f00f9", size = 124393, upload-time = "2025-08-12T15:10:59.267Z" },
{ url = "https://files.pythonhosted.org/packages/26/76/cc598c1811ba9ba935171267b02e377fc9177489efce525d478a2999d9cc/orjson-3.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:955811c8405251d9e09cbe8606ad8fdef49a451bcf5520095a5ed38c669223d8", size = 119561, upload-time = "2025-08-12T15:11:00.559Z" },
{ url = "https://files.pythonhosted.org/packages/d8/17/c48011750f0489006f7617b0a3cebc8230f36d11a34e7e9aca2085f07792/orjson-3.11.2-cp311-cp311-win_arm64.whl", hash = "sha256:2e4d423a6f838552e3a6d9ec734b729f61f88b1124fd697eab82805ea1a2a97d", size = 114186, upload-time = "2025-08-12T15:11:01.931Z" },
{ url = "https://files.pythonhosted.org/packages/40/02/46054ebe7996a8adee9640dcad7d39d76c2000dc0377efa38e55dc5cbf78/orjson-3.11.2-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:901d80d349d8452162b3aa1afb82cec5bee79a10550660bc21311cc61a4c5486", size = 226528, upload-time = "2025-08-12T15:11:03.317Z" },
{ url = "https://files.pythonhosted.org/packages/e2/c6/6b6f0b4d8aea1137436546b990f71be2cd8bd870aa2f5aa14dba0fcc95dc/orjson-3.11.2-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:cf3bd3967a360e87ee14ed82cb258b7f18c710dacf3822fb0042a14313a673a1", size = 115931, upload-time = "2025-08-12T15:11:04.759Z" },
{ url = "https://files.pythonhosted.org/packages/ae/05/4205cc97c30e82a293dd0d149b1a89b138ebe76afeca66fc129fa2aa4e6a/orjson-3.11.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26693dde66910078229a943e80eeb99fdce6cd2c26277dc80ead9f3ab97d2131", size = 111382, upload-time = "2025-08-12T15:11:06.468Z" },
{ url = "https://files.pythonhosted.org/packages/50/c7/b8a951a93caa821f9272a7c917115d825ae2e4e8768f5ddf37968ec9de01/orjson-3.11.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad4c8acb50a28211c33fc7ef85ddf5cb18d4636a5205fd3fa2dce0411a0e30c", size = 116271, upload-time = "2025-08-12T15:11:07.845Z" },
{ url = "https://files.pythonhosted.org/packages/17/03/1006c7f8782d5327439e26d9b0ec66500ea7b679d4bbb6b891d2834ab3ee/orjson-3.11.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:994181e7f1725bb5f2d481d7d228738e0743b16bf319ca85c29369c65913df14", size = 119086, upload-time = "2025-08-12T15:11:09.329Z" },
{ url = "https://files.pythonhosted.org/packages/44/61/57d22bc31f36a93878a6f772aea76b2184102c6993dea897656a66d18c74/orjson-3.11.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dbb79a0476393c07656b69c8e763c3cc925fa8e1d9e9b7d1f626901bb5025448", size = 120724, upload-time = "2025-08-12T15:11:10.674Z" },
{ url = "https://files.pythonhosted.org/packages/78/a9/4550e96b4c490c83aea697d5347b8f7eb188152cd7b5a38001055ca5b379/orjson-3.11.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:191ed27a1dddb305083d8716af413d7219f40ec1d4c9b0e977453b4db0d6fb6c", size = 123577, upload-time = "2025-08-12T15:11:12.015Z" },
{ url = "https://files.pythonhosted.org/packages/3a/86/09b8cb3ebd513d708ef0c92d36ac3eebda814c65c72137b0a82d6d688fc4/orjson-3.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0afb89f16f07220183fd00f5f297328ed0a68d8722ad1b0c8dcd95b12bc82804", size = 121195, upload-time = "2025-08-12T15:11:13.399Z" },
{ url = "https://files.pythonhosted.org/packages/37/68/7b40b39ac2c1c644d4644e706d0de6c9999764341cd85f2a9393cb387661/orjson-3.11.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6ab6e6b4e93b1573a026b6ec16fca9541354dd58e514b62c558b58554ae04307", size = 119234, upload-time = "2025-08-12T15:11:15.134Z" },
{ url = "https://files.pythonhosted.org/packages/40/7c/bb6e7267cd80c19023d44d8cbc4ea4ed5429fcd4a7eb9950f50305697a28/orjson-3.11.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9cb23527efb61fb75527df55d20ee47989c4ee34e01a9c98ee9ede232abf6219", size = 392250, upload-time = "2025-08-12T15:11:16.604Z" },
{ url = "https://files.pythonhosted.org/packages/64/f2/6730ace05583dbca7c1b406d59f4266e48cd0d360566e71482420fb849fc/orjson-3.11.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a4dd1268e4035af21b8a09e4adf2e61f87ee7bf63b86d7bb0a237ac03fad5b45", size = 134572, upload-time = "2025-08-12T15:11:18.205Z" },
{ url = "https://files.pythonhosted.org/packages/96/0f/7d3e03a30d5aac0432882b539a65b8c02cb6dd4221ddb893babf09c424cc/orjson-3.11.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff8b155b145eaf5a9d94d2c476fbe18d6021de93cf36c2ae2c8c5b775763f14e", size = 123869, upload-time = "2025-08-12T15:11:19.554Z" },
{ url = "https://files.pythonhosted.org/packages/45/80/1513265eba6d4a960f078f4b1d2bff94a571ab2d28c6f9835e03dfc65cc6/orjson-3.11.2-cp312-cp312-win32.whl", hash = "sha256:ae3bb10279d57872f9aba68c9931aa71ed3b295fa880f25e68da79e79453f46e", size = 124430, upload-time = "2025-08-12T15:11:20.914Z" },
{ url = "https://files.pythonhosted.org/packages/fb/61/eadf057b68a332351eeb3d89a4cc538d14f31cd8b5ec1b31a280426ccca2/orjson-3.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:d026e1967239ec11a2559b4146a61d13914504b396f74510a1c4d6b19dfd8732", size = 119598, upload-time = "2025-08-12T15:11:22.372Z" },
{ url = "https://files.pythonhosted.org/packages/6b/3f/7f4b783402143d965ab7e9a2fc116fdb887fe53bdce7d3523271cd106098/orjson-3.11.2-cp312-cp312-win_arm64.whl", hash = "sha256:59f8d5ad08602711af9589375be98477d70e1d102645430b5a7985fdbf613b36", size = 114052, upload-time = "2025-08-12T15:11:23.762Z" },
{ url = "https://files.pythonhosted.org/packages/c2/f3/0dd6b4750eb556ae4e2c6a9cb3e219ec642e9c6d95f8ebe5dc9020c67204/orjson-3.11.2-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a079fdba7062ab396380eeedb589afb81dc6683f07f528a03b6f7aae420a0219", size = 226419, upload-time = "2025-08-12T15:11:25.517Z" },
{ url = "https://files.pythonhosted.org/packages/44/d5/e67f36277f78f2af8a4690e0c54da6b34169812f807fd1b4bfc4dbcf9558/orjson-3.11.2-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:6a5f62ebbc530bb8bb4b1ead103647b395ba523559149b91a6c545f7cd4110ad", size = 115803, upload-time = "2025-08-12T15:11:27.357Z" },
{ url = "https://files.pythonhosted.org/packages/24/37/ff8bc86e0dacc48f07c2b6e20852f230bf4435611bab65e3feae2b61f0ae/orjson-3.11.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7df6c7b8b0931feb3420b72838c3e2ba98c228f7aa60d461bc050cf4ca5f7b2", size = 111337, upload-time = "2025-08-12T15:11:28.805Z" },
{ url = "https://files.pythonhosted.org/packages/b9/25/37d4d3e8079ea9784ea1625029988e7f4594ce50d4738b0c1e2bf4a9e201/orjson-3.11.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6f59dfea7da1fced6e782bb3699718088b1036cb361f36c6e4dd843c5111aefe", size = 116222, upload-time = "2025-08-12T15:11:30.18Z" },
{ url = "https://files.pythonhosted.org/packages/b7/32/a63fd9c07fce3b4193dcc1afced5dd4b0f3a24e27556604e9482b32189c9/orjson-3.11.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edf49146520fef308c31aa4c45b9925fd9c7584645caca7c0c4217d7900214ae", size = 119020, upload-time = "2025-08-12T15:11:31.59Z" },
{ url = "https://files.pythonhosted.org/packages/b4/b6/400792b8adc3079a6b5d649264a3224d6342436d9fac9a0ed4abc9dc4596/orjson-3.11.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50995bbeb5d41a32ad15e023305807f561ac5dcd9bd41a12c8d8d1d2c83e44e6", size = 120721, upload-time = "2025-08-12T15:11:33.035Z" },
{ url = "https://files.pythonhosted.org/packages/40/f3/31ab8f8c699eb9e65af8907889a0b7fef74c1d2b23832719a35da7bb0c58/orjson-3.11.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cc42960515076eb639b705f105712b658c525863d89a1704d984b929b0577d1", size = 123574, upload-time = "2025-08-12T15:11:34.433Z" },
{ url = "https://files.pythonhosted.org/packages/bd/a6/ce4287c412dff81878f38d06d2c80845709c60012ca8daf861cb064b4574/orjson-3.11.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56777cab2a7b2a8ea687fedafb84b3d7fdafae382165c31a2adf88634c432fa", size = 121225, upload-time = "2025-08-12T15:11:36.133Z" },
{ url = "https://files.pythonhosted.org/packages/69/b0/7a881b2aef4fed0287d2a4fbb029d01ed84fa52b4a68da82bdee5e50598e/orjson-3.11.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:07349e88025b9b5c783077bf7a9f401ffbfb07fd20e86ec6fc5b7432c28c2c5e", size = 119201, upload-time = "2025-08-12T15:11:37.642Z" },
{ url = "https://files.pythonhosted.org/packages/cf/98/a325726b37f7512ed6338e5e65035c3c6505f4e628b09a5daf0419f054ea/orjson-3.11.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:45841fbb79c96441a8c58aa29ffef570c5df9af91f0f7a9572e5505e12412f15", size = 392193, upload-time = "2025-08-12T15:11:39.153Z" },
{ url = "https://files.pythonhosted.org/packages/cb/4f/a7194f98b0ce1d28190e0c4caa6d091a3fc8d0107ad2209f75c8ba398984/orjson-3.11.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13d8d8db6cd8d89d4d4e0f4161acbbb373a4d2a4929e862d1d2119de4aa324ac", size = 134548, upload-time = "2025-08-12T15:11:40.768Z" },
{ url = "https://files.pythonhosted.org/packages/e8/5e/b84caa2986c3f472dc56343ddb0167797a708a8d5c3be043e1e2677b55df/orjson-3.11.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51da1ee2178ed09c00d09c1b953e45846bbc16b6420965eb7a913ba209f606d8", size = 123798, upload-time = "2025-08-12T15:11:42.164Z" },
{ url = "https://files.pythonhosted.org/packages/9c/5b/e398449080ce6b4c8fcadad57e51fa16f65768e1b142ba90b23ac5d10801/orjson-3.11.2-cp313-cp313-win32.whl", hash = "sha256:51dc033df2e4a4c91c0ba4f43247de99b3cbf42ee7a42ee2b2b2f76c8b2f2cb5", size = 124402, upload-time = "2025-08-12T15:11:44.036Z" },
{ url = "https://files.pythonhosted.org/packages/b3/66/429e4608e124debfc4790bfc37131f6958e59510ba3b542d5fc163be8e5f/orjson-3.11.2-cp313-cp313-win_amd64.whl", hash = "sha256:29d91d74942b7436f29b5d1ed9bcfc3f6ef2d4f7c4997616509004679936650d", size = 119498, upload-time = "2025-08-12T15:11:45.864Z" },
{ url = "https://files.pythonhosted.org/packages/7b/04/f8b5f317cce7ad3580a9ad12d7e2df0714dfa8a83328ecddd367af802f5b/orjson-3.11.2-cp313-cp313-win_arm64.whl", hash = "sha256:4ca4fb5ac21cd1e48028d4f708b1bb13e39c42d45614befd2ead004a8bba8535", size = 114051, upload-time = "2025-08-12T15:11:47.555Z" },
{ url = "https://files.pythonhosted.org/packages/74/83/2c363022b26c3c25b3708051a19d12f3374739bb81323f05b284392080c0/orjson-3.11.2-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3dcba7101ea6a8d4ef060746c0f2e7aa8e2453a1012083e1ecce9726d7554cb7", size = 226406, upload-time = "2025-08-12T15:11:49.445Z" },
{ url = "https://files.pythonhosted.org/packages/b0/a7/aa3c973de0b33fc93b4bd71691665ffdfeae589ea9d0625584ab10a7d0f5/orjson-3.11.2-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:15d17bdb76a142e1f55d91913e012e6e6769659daa6bfef3ef93f11083137e81", size = 115788, upload-time = "2025-08-12T15:11:50.992Z" },
{ url = "https://files.pythonhosted.org/packages/ef/f2/e45f233dfd09fdbb052ec46352363dca3906618e1a2b264959c18f809d0b/orjson-3.11.2-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:53c9e81768c69d4b66b8876ec3c8e431c6e13477186d0db1089d82622bccd19f", size = 111318, upload-time = "2025-08-12T15:11:52.495Z" },
{ url = "https://files.pythonhosted.org/packages/3e/23/cf5a73c4da6987204cbbf93167f353ff0c5013f7c5e5ef845d4663a366da/orjson-3.11.2-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d4f13af59a7b84c1ca6b8a7ab70d608f61f7c44f9740cd42409e6ae7b6c8d8b7", size = 121231, upload-time = "2025-08-12T15:11:53.941Z" },
{ url = "https://files.pythonhosted.org/packages/40/1d/47468a398ae68a60cc21e599144e786e035bb12829cb587299ecebc088f1/orjson-3.11.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bde64aa469b5ee46cc960ed241fae3721d6a8801dacb2ca3466547a2535951e4", size = 119204, upload-time = "2025-08-12T15:11:55.409Z" },
{ url = "https://files.pythonhosted.org/packages/4d/d9/f99433d89b288b5bc8836bffb32a643f805e673cf840ef8bab6e73ced0d1/orjson-3.11.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:b5ca86300aeb383c8fa759566aca065878d3d98c3389d769b43f0a2e84d52c5f", size = 392237, upload-time = "2025-08-12T15:11:57.18Z" },
{ url = "https://files.pythonhosted.org/packages/d4/dc/1b9d80d40cebef603325623405136a29fb7d08c877a728c0943dd066c29a/orjson-3.11.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:24e32a558ebed73a6a71c8f1cbc163a7dd5132da5270ff3d8eeb727f4b6d1bc7", size = 134578, upload-time = "2025-08-12T15:11:58.844Z" },
{ url = "https://files.pythonhosted.org/packages/45/b3/72e7a4c5b6485ef4e83ef6aba7f1dd041002bad3eb5d1d106ca5b0fc02c6/orjson-3.11.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e36319a5d15b97e4344110517450396845cc6789aed712b1fbf83c1bd95792f6", size = 123799, upload-time = "2025-08-12T15:12:00.352Z" },
{ url = "https://files.pythonhosted.org/packages/c8/3e/a3d76b392e7acf9b34dc277171aad85efd6accc75089bb35b4c614990ea9/orjson-3.11.2-cp314-cp314-win32.whl", hash = "sha256:40193ada63fab25e35703454d65b6afc71dbc65f20041cb46c6d91709141ef7f", size = 124461, upload-time = "2025-08-12T15:12:01.854Z" },
{ url = "https://files.pythonhosted.org/packages/fb/e3/75c6a596ff8df9e4a5894813ff56695f0a218e6ea99420b4a645c4f7795d/orjson-3.11.2-cp314-cp314-win_amd64.whl", hash = "sha256:7c8ac5f6b682d3494217085cf04dadae66efee45349ad4ee2a1da3c97e2305a8", size = 119494, upload-time = "2025-08-12T15:12:03.337Z" },
{ url = "https://files.pythonhosted.org/packages/5b/3d/9e74742fc261c5ca473c96bb3344d03995869e1dc6402772c60afb97736a/orjson-3.11.2-cp314-cp314-win_arm64.whl", hash = "sha256:21cf261e8e79284242e4cb1e5924df16ae28255184aafeff19be1405f6d33f67", size = 114046, upload-time = "2025-08-12T15:12:04.87Z" },
{ url = "https://files.pythonhosted.org/packages/9b/64/4a3cef001c6cd9c64256348d4c13a7b09b857e3e1cbb5185917df67d8ced/orjson-3.11.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:29cb1f1b008d936803e2da3d7cba726fc47232c45df531b29edf0b232dd737e7", size = 238600, upload-time = "2025-08-26T17:44:36.875Z" },
{ url = "https://files.pythonhosted.org/packages/10/ce/0c8c87f54f79d051485903dc46226c4d3220b691a151769156054df4562b/orjson-3.11.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97dceed87ed9139884a55db8722428e27bd8452817fbf1869c58b49fecab1120", size = 123526, upload-time = "2025-08-26T17:44:39.574Z" },
{ url = "https://files.pythonhosted.org/packages/ef/d0/249497e861f2d438f45b3ab7b7b361484237414945169aa285608f9f7019/orjson-3.11.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:58533f9e8266cb0ac298e259ed7b4d42ed3fa0b78ce76860626164de49e0d467", size = 128075, upload-time = "2025-08-26T17:44:40.672Z" },
{ url = "https://files.pythonhosted.org/packages/e5/64/00485702f640a0fd56144042a1ea196469f4a3ae93681871564bf74fa996/orjson-3.11.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c212cfdd90512fe722fa9bd620de4d46cda691415be86b2e02243242ae81873", size = 130483, upload-time = "2025-08-26T17:44:41.788Z" },
{ url = "https://files.pythonhosted.org/packages/64/81/110d68dba3909171bf3f05619ad0cf187b430e64045ae4e0aa7ccfe25b15/orjson-3.11.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff835b5d3e67d9207343effb03760c00335f8b5285bfceefd4dc967b0e48f6a", size = 132539, upload-time = "2025-08-26T17:44:43.12Z" },
{ url = "https://files.pythonhosted.org/packages/79/92/dba25c22b0ddfafa1e6516a780a00abac28d49f49e7202eb433a53c3e94e/orjson-3.11.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5aa4682912a450c2db89cbd92d356fef47e115dffba07992555542f344d301b", size = 135390, upload-time = "2025-08-26T17:44:44.199Z" },
{ url = "https://files.pythonhosted.org/packages/44/1d/ca2230fd55edbd87b58a43a19032d63a4b180389a97520cc62c535b726f9/orjson-3.11.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d18dd34ea2e860553a579df02041845dee0af8985dff7f8661306f95504ddf", size = 132966, upload-time = "2025-08-26T17:44:45.719Z" },
{ url = "https://files.pythonhosted.org/packages/6e/b9/96bbc8ed3e47e52b487d504bd6861798977445fbc410da6e87e302dc632d/orjson-3.11.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8b11701bc43be92ea42bd454910437b355dfb63696c06fe953ffb40b5f763b4", size = 131349, upload-time = "2025-08-26T17:44:46.862Z" },
{ url = "https://files.pythonhosted.org/packages/c4/3c/418fbd93d94b0df71cddf96b7fe5894d64a5d890b453ac365120daec30f7/orjson-3.11.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90368277087d4af32d38bd55f9da2ff466d25325bf6167c8f382d8ee40cb2bbc", size = 404087, upload-time = "2025-08-26T17:44:48.079Z" },
{ url = "https://files.pythonhosted.org/packages/5b/a9/2bfd58817d736c2f63608dec0c34857339d423eeed30099b126562822191/orjson-3.11.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd7ff459fb393358d3a155d25b275c60b07a2c83dcd7ea962b1923f5a1134569", size = 146067, upload-time = "2025-08-26T17:44:49.302Z" },
{ url = "https://files.pythonhosted.org/packages/33/ba/29023771f334096f564e48d82ed855a0ed3320389d6748a9c949e25be734/orjson-3.11.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f8d902867b699bcd09c176a280b1acdab57f924489033e53d0afe79817da37e6", size = 135506, upload-time = "2025-08-26T17:44:50.558Z" },
{ url = "https://files.pythonhosted.org/packages/39/62/b5a1eca83f54cb3aa11a9645b8a22f08d97dbd13f27f83aae7c6666a0a05/orjson-3.11.3-cp310-cp310-win32.whl", hash = "sha256:bb93562146120bb51e6b154962d3dadc678ed0fce96513fa6bc06599bb6f6edc", size = 136352, upload-time = "2025-08-26T17:44:51.698Z" },
{ url = "https://files.pythonhosted.org/packages/e3/c0/7ebfaa327d9a9ed982adc0d9420dbce9a3fec45b60ab32c6308f731333fa/orjson-3.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:976c6f1975032cc327161c65d4194c549f2589d88b105a5e3499429a54479770", size = 131539, upload-time = "2025-08-26T17:44:52.974Z" },
{ url = "https://files.pythonhosted.org/packages/cd/8b/360674cd817faef32e49276187922a946468579fcaf37afdfb6c07046e92/orjson-3.11.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d2ae0cc6aeb669633e0124531f342a17d8e97ea999e42f12a5ad4adaa304c5f", size = 238238, upload-time = "2025-08-26T17:44:54.214Z" },
{ url = "https://files.pythonhosted.org/packages/05/3d/5fa9ea4b34c1a13be7d9046ba98d06e6feb1d8853718992954ab59d16625/orjson-3.11.3-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ba21dbb2493e9c653eaffdc38819b004b7b1b246fb77bfc93dc016fe664eac91", size = 127713, upload-time = "2025-08-26T17:44:55.596Z" },
{ url = "https://files.pythonhosted.org/packages/e5/5f/e18367823925e00b1feec867ff5f040055892fc474bf5f7875649ecfa586/orjson-3.11.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00f1a271e56d511d1569937c0447d7dce5a99a33ea0dec76673706360a051904", size = 123241, upload-time = "2025-08-26T17:44:57.185Z" },
{ url = "https://files.pythonhosted.org/packages/0f/bd/3c66b91c4564759cf9f473251ac1650e446c7ba92a7c0f9f56ed54f9f0e6/orjson-3.11.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b67e71e47caa6680d1b6f075a396d04fa6ca8ca09aafb428731da9b3ea32a5a6", size = 127895, upload-time = "2025-08-26T17:44:58.349Z" },
{ url = "https://files.pythonhosted.org/packages/82/b5/dc8dcd609db4766e2967a85f63296c59d4722b39503e5b0bf7fd340d387f/orjson-3.11.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7d012ebddffcce8c85734a6d9e5f08180cd3857c5f5a3ac70185b43775d043d", size = 130303, upload-time = "2025-08-26T17:44:59.491Z" },
{ url = "https://files.pythonhosted.org/packages/48/c2/d58ec5fd1270b2aa44c862171891adc2e1241bd7dab26c8f46eb97c6c6f1/orjson-3.11.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd759f75d6b8d1b62012b7f5ef9461d03c804f94d539a5515b454ba3a6588038", size = 132366, upload-time = "2025-08-26T17:45:00.654Z" },
{ url = "https://files.pythonhosted.org/packages/73/87/0ef7e22eb8dd1ef940bfe3b9e441db519e692d62ed1aae365406a16d23d0/orjson-3.11.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6890ace0809627b0dff19cfad92d69d0fa3f089d3e359a2a532507bb6ba34efb", size = 135180, upload-time = "2025-08-26T17:45:02.424Z" },
{ url = "https://files.pythonhosted.org/packages/bb/6a/e5bf7b70883f374710ad74faf99bacfc4b5b5a7797c1d5e130350e0e28a3/orjson-3.11.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d4a5e041ae435b815e568537755773d05dac031fee6a57b4ba70897a44d9d2", size = 132741, upload-time = "2025-08-26T17:45:03.663Z" },
{ url = "https://files.pythonhosted.org/packages/bd/0c/4577fd860b6386ffaa56440e792af01c7882b56d2766f55384b5b0e9d39b/orjson-3.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d68bf97a771836687107abfca089743885fb664b90138d8761cce61d5625d55", size = 131104, upload-time = "2025-08-26T17:45:04.939Z" },
{ url = "https://files.pythonhosted.org/packages/66/4b/83e92b2d67e86d1c33f2ea9411742a714a26de63641b082bdbf3d8e481af/orjson-3.11.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfc27516ec46f4520b18ef645864cee168d2a027dbf32c5537cb1f3e3c22dac1", size = 403887, upload-time = "2025-08-26T17:45:06.228Z" },
{ url = "https://files.pythonhosted.org/packages/6d/e5/9eea6a14e9b5ceb4a271a1fd2e1dec5f2f686755c0fab6673dc6ff3433f4/orjson-3.11.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f66b001332a017d7945e177e282a40b6997056394e3ed7ddb41fb1813b83e824", size = 145855, upload-time = "2025-08-26T17:45:08.338Z" },
{ url = "https://files.pythonhosted.org/packages/45/78/8d4f5ad0c80ba9bf8ac4d0fc71f93a7d0dc0844989e645e2074af376c307/orjson-3.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:212e67806525d2561efbfe9e799633b17eb668b8964abed6b5319b2f1cfbae1f", size = 135361, upload-time = "2025-08-26T17:45:09.625Z" },
{ url = "https://files.pythonhosted.org/packages/0b/5f/16386970370178d7a9b438517ea3d704efcf163d286422bae3b37b88dbb5/orjson-3.11.3-cp311-cp311-win32.whl", hash = "sha256:6e8e0c3b85575a32f2ffa59de455f85ce002b8bdc0662d6b9c2ed6d80ab5d204", size = 136190, upload-time = "2025-08-26T17:45:10.962Z" },
{ url = "https://files.pythonhosted.org/packages/09/60/db16c6f7a41dd8ac9fb651f66701ff2aeb499ad9ebc15853a26c7c152448/orjson-3.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:6be2f1b5d3dc99a5ce5ce162fc741c22ba9f3443d3dd586e6a1211b7bc87bc7b", size = 131389, upload-time = "2025-08-26T17:45:12.285Z" },
{ url = "https://files.pythonhosted.org/packages/3e/2a/bb811ad336667041dea9b8565c7c9faf2f59b47eb5ab680315eea612ef2e/orjson-3.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:fafb1a99d740523d964b15c8db4eabbfc86ff29f84898262bf6e3e4c9e97e43e", size = 126120, upload-time = "2025-08-26T17:45:13.515Z" },
{ url = "https://files.pythonhosted.org/packages/3d/b0/a7edab2a00cdcb2688e1c943401cb3236323e7bfd2839815c6131a3742f4/orjson-3.11.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8c752089db84333e36d754c4baf19c0e1437012242048439c7e80eb0e6426e3b", size = 238259, upload-time = "2025-08-26T17:45:15.093Z" },
{ url = "https://files.pythonhosted.org/packages/e1/c6/ff4865a9cc398a07a83342713b5932e4dc3cb4bf4bc04e8f83dedfc0d736/orjson-3.11.3-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:9b8761b6cf04a856eb544acdd82fc594b978f12ac3602d6374a7edb9d86fd2c2", size = 127633, upload-time = "2025-08-26T17:45:16.417Z" },
{ url = "https://files.pythonhosted.org/packages/6e/e6/e00bea2d9472f44fe8794f523e548ce0ad51eb9693cf538a753a27b8bda4/orjson-3.11.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b13974dc8ac6ba22feaa867fc19135a3e01a134b4f7c9c28162fed4d615008a", size = 123061, upload-time = "2025-08-26T17:45:17.673Z" },
{ url = "https://files.pythonhosted.org/packages/54/31/9fbb78b8e1eb3ac605467cb846e1c08d0588506028b37f4ee21f978a51d4/orjson-3.11.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f83abab5bacb76d9c821fd5c07728ff224ed0e52d7a71b7b3de822f3df04e15c", size = 127956, upload-time = "2025-08-26T17:45:19.172Z" },
{ url = "https://files.pythonhosted.org/packages/36/88/b0604c22af1eed9f98d709a96302006915cfd724a7ebd27d6dd11c22d80b/orjson-3.11.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6fbaf48a744b94091a56c62897b27c31ee2da93d826aa5b207131a1e13d4064", size = 130790, upload-time = "2025-08-26T17:45:20.586Z" },
{ url = "https://files.pythonhosted.org/packages/0e/9d/1c1238ae9fffbfed51ba1e507731b3faaf6b846126a47e9649222b0fd06f/orjson-3.11.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc779b4f4bba2847d0d2940081a7b6f7b5877e05408ffbb74fa1faf4a136c424", size = 132385, upload-time = "2025-08-26T17:45:22.036Z" },
{ url = "https://files.pythonhosted.org/packages/a3/b5/c06f1b090a1c875f337e21dd71943bc9d84087f7cdf8c6e9086902c34e42/orjson-3.11.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd4b909ce4c50faa2192da6bb684d9848d4510b736b0611b6ab4020ea6fd2d23", size = 135305, upload-time = "2025-08-26T17:45:23.4Z" },
{ url = "https://files.pythonhosted.org/packages/a0/26/5f028c7d81ad2ebbf84414ba6d6c9cac03f22f5cd0d01eb40fb2d6a06b07/orjson-3.11.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524b765ad888dc5518bbce12c77c2e83dee1ed6b0992c1790cc5fb49bb4b6667", size = 132875, upload-time = "2025-08-26T17:45:25.182Z" },
{ url = "https://files.pythonhosted.org/packages/fe/d4/b8df70d9cfb56e385bf39b4e915298f9ae6c61454c8154a0f5fd7efcd42e/orjson-3.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:84fd82870b97ae3cdcea9d8746e592b6d40e1e4d4527835fc520c588d2ded04f", size = 130940, upload-time = "2025-08-26T17:45:27.209Z" },
{ url = "https://files.pythonhosted.org/packages/da/5e/afe6a052ebc1a4741c792dd96e9f65bf3939d2094e8b356503b68d48f9f5/orjson-3.11.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fbecb9709111be913ae6879b07bafd4b0785b44c1eb5cac8ac76da048b3885a1", size = 403852, upload-time = "2025-08-26T17:45:28.478Z" },
{ url = "https://files.pythonhosted.org/packages/f8/90/7bbabafeb2ce65915e9247f14a56b29c9334003536009ef5b122783fe67e/orjson-3.11.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9dba358d55aee552bd868de348f4736ca5a4086d9a62e2bfbbeeb5629fe8b0cc", size = 146293, upload-time = "2025-08-26T17:45:29.86Z" },
{ url = "https://files.pythonhosted.org/packages/27/b3/2d703946447da8b093350570644a663df69448c9d9330e5f1d9cce997f20/orjson-3.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eabcf2e84f1d7105f84580e03012270c7e97ecb1fb1618bda395061b2a84a049", size = 135470, upload-time = "2025-08-26T17:45:31.243Z" },
{ url = "https://files.pythonhosted.org/packages/38/70/b14dcfae7aff0e379b0119c8a812f8396678919c431efccc8e8a0263e4d9/orjson-3.11.3-cp312-cp312-win32.whl", hash = "sha256:3782d2c60b8116772aea8d9b7905221437fdf53e7277282e8d8b07c220f96cca", size = 136248, upload-time = "2025-08-26T17:45:32.567Z" },
{ url = "https://files.pythonhosted.org/packages/35/b8/9e3127d65de7fff243f7f3e53f59a531bf6bb295ebe5db024c2503cc0726/orjson-3.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:79b44319268af2eaa3e315b92298de9a0067ade6e6003ddaef72f8e0bedb94f1", size = 131437, upload-time = "2025-08-26T17:45:34.949Z" },
{ url = "https://files.pythonhosted.org/packages/51/92/a946e737d4d8a7fd84a606aba96220043dcc7d6988b9e7551f7f6d5ba5ad/orjson-3.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:0e92a4e83341ef79d835ca21b8bd13e27c859e4e9e4d7b63defc6e58462a3710", size = 125978, upload-time = "2025-08-26T17:45:36.422Z" },
{ url = "https://files.pythonhosted.org/packages/fc/79/8932b27293ad35919571f77cb3693b5906cf14f206ef17546052a241fdf6/orjson-3.11.3-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:af40c6612fd2a4b00de648aa26d18186cd1322330bd3a3cc52f87c699e995810", size = 238127, upload-time = "2025-08-26T17:45:38.146Z" },
{ url = "https://files.pythonhosted.org/packages/1c/82/cb93cd8cf132cd7643b30b6c5a56a26c4e780c7a145db6f83de977b540ce/orjson-3.11.3-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:9f1587f26c235894c09e8b5b7636a38091a9e6e7fe4531937534749c04face43", size = 127494, upload-time = "2025-08-26T17:45:39.57Z" },
{ url = "https://files.pythonhosted.org/packages/a4/b8/2d9eb181a9b6bb71463a78882bcac1027fd29cf62c38a40cc02fc11d3495/orjson-3.11.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61dcdad16da5bb486d7227a37a2e789c429397793a6955227cedbd7252eb5a27", size = 123017, upload-time = "2025-08-26T17:45:40.876Z" },
{ url = "https://files.pythonhosted.org/packages/b4/14/a0e971e72d03b509190232356d54c0f34507a05050bd026b8db2bf2c192c/orjson-3.11.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11c6d71478e2cbea0a709e8a06365fa63da81da6498a53e4c4f065881d21ae8f", size = 127898, upload-time = "2025-08-26T17:45:42.188Z" },
{ url = "https://files.pythonhosted.org/packages/8e/af/dc74536722b03d65e17042cc30ae586161093e5b1f29bccda24765a6ae47/orjson-3.11.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff94112e0098470b665cb0ed06efb187154b63649403b8d5e9aedeb482b4548c", size = 130742, upload-time = "2025-08-26T17:45:43.511Z" },
{ url = "https://files.pythonhosted.org/packages/62/e6/7a3b63b6677bce089fe939353cda24a7679825c43a24e49f757805fc0d8a/orjson-3.11.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae8b756575aaa2a855a75192f356bbda11a89169830e1439cfb1a3e1a6dde7be", size = 132377, upload-time = "2025-08-26T17:45:45.525Z" },
{ url = "https://files.pythonhosted.org/packages/fc/cd/ce2ab93e2e7eaf518f0fd15e3068b8c43216c8a44ed82ac2b79ce5cef72d/orjson-3.11.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9416cc19a349c167ef76135b2fe40d03cea93680428efee8771f3e9fb66079d", size = 135313, upload-time = "2025-08-26T17:45:46.821Z" },
{ url = "https://files.pythonhosted.org/packages/d0/b4/f98355eff0bd1a38454209bbc73372ce351ba29933cb3e2eba16c04b9448/orjson-3.11.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b822caf5b9752bc6f246eb08124c3d12bf2175b66ab74bac2ef3bbf9221ce1b2", size = 132908, upload-time = "2025-08-26T17:45:48.126Z" },
{ url = "https://files.pythonhosted.org/packages/eb/92/8f5182d7bc2a1bed46ed960b61a39af8389f0ad476120cd99e67182bfb6d/orjson-3.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:414f71e3bdd5573893bf5ecdf35c32b213ed20aa15536fe2f588f946c318824f", size = 130905, upload-time = "2025-08-26T17:45:49.414Z" },
{ url = "https://files.pythonhosted.org/packages/1a/60/c41ca753ce9ffe3d0f67b9b4c093bdd6e5fdb1bc53064f992f66bb99954d/orjson-3.11.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:828e3149ad8815dc14468f36ab2a4b819237c155ee1370341b91ea4c8672d2ee", size = 403812, upload-time = "2025-08-26T17:45:51.085Z" },
{ url = "https://files.pythonhosted.org/packages/dd/13/e4a4f16d71ce1868860db59092e78782c67082a8f1dc06a3788aef2b41bc/orjson-3.11.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac9e05f25627ffc714c21f8dfe3a579445a5c392a9c8ae7ba1d0e9fb5333f56e", size = 146277, upload-time = "2025-08-26T17:45:52.851Z" },
{ url = "https://files.pythonhosted.org/packages/8d/8b/bafb7f0afef9344754a3a0597a12442f1b85a048b82108ef2c956f53babd/orjson-3.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e44fbe4000bd321d9f3b648ae46e0196d21577cf66ae684a96ff90b1f7c93633", size = 135418, upload-time = "2025-08-26T17:45:54.806Z" },
{ url = "https://files.pythonhosted.org/packages/60/d4/bae8e4f26afb2c23bea69d2f6d566132584d1c3a5fe89ee8c17b718cab67/orjson-3.11.3-cp313-cp313-win32.whl", hash = "sha256:2039b7847ba3eec1f5886e75e6763a16e18c68a63efc4b029ddf994821e2e66b", size = 136216, upload-time = "2025-08-26T17:45:57.182Z" },
{ url = "https://files.pythonhosted.org/packages/88/76/224985d9f127e121c8cad882cea55f0ebe39f97925de040b75ccd4b33999/orjson-3.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:29be5ac4164aa8bdcba5fa0700a3c9c316b411d8ed9d39ef8a882541bd452fae", size = 131362, upload-time = "2025-08-26T17:45:58.56Z" },
{ url = "https://files.pythonhosted.org/packages/e2/cf/0dce7a0be94bd36d1346be5067ed65ded6adb795fdbe3abd234c8d576d01/orjson-3.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:18bd1435cb1f2857ceb59cfb7de6f92593ef7b831ccd1b9bfb28ca530e539dce", size = 125989, upload-time = "2025-08-26T17:45:59.95Z" },
{ url = "https://files.pythonhosted.org/packages/ef/77/d3b1fef1fc6aaeed4cbf3be2b480114035f4df8fa1a99d2dac1d40d6e924/orjson-3.11.3-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:cf4b81227ec86935568c7edd78352a92e97af8da7bd70bdfdaa0d2e0011a1ab4", size = 238115, upload-time = "2025-08-26T17:46:01.669Z" },
{ url = "https://files.pythonhosted.org/packages/e4/6d/468d21d49bb12f900052edcfbf52c292022d0a323d7828dc6376e6319703/orjson-3.11.3-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:bc8bc85b81b6ac9fc4dae393a8c159b817f4c2c9dee5d12b773bddb3b95fc07e", size = 127493, upload-time = "2025-08-26T17:46:03.466Z" },
{ url = "https://files.pythonhosted.org/packages/67/46/1e2588700d354aacdf9e12cc2d98131fb8ac6f31ca65997bef3863edb8ff/orjson-3.11.3-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:88dcfc514cfd1b0de038443c7b3e6a9797ffb1b3674ef1fd14f701a13397f82d", size = 122998, upload-time = "2025-08-26T17:46:04.803Z" },
{ url = "https://files.pythonhosted.org/packages/3b/94/11137c9b6adb3779f1b34fd98be51608a14b430dbc02c6d41134fbba484c/orjson-3.11.3-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d61cd543d69715d5fc0a690c7c6f8dcc307bc23abef9738957981885f5f38229", size = 132915, upload-time = "2025-08-26T17:46:06.237Z" },
{ url = "https://files.pythonhosted.org/packages/10/61/dccedcf9e9bcaac09fdabe9eaee0311ca92115699500efbd31950d878833/orjson-3.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2b7b153ed90ababadbef5c3eb39549f9476890d339cf47af563aea7e07db2451", size = 130907, upload-time = "2025-08-26T17:46:07.581Z" },
{ url = "https://files.pythonhosted.org/packages/0e/fd/0e935539aa7b08b3ca0f817d73034f7eb506792aae5ecc3b7c6e679cdf5f/orjson-3.11.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7909ae2460f5f494fecbcd10613beafe40381fd0316e35d6acb5f3a05bfda167", size = 403852, upload-time = "2025-08-26T17:46:08.982Z" },
{ url = "https://files.pythonhosted.org/packages/4a/2b/50ae1a5505cd1043379132fdb2adb8a05f37b3e1ebffe94a5073321966fd/orjson-3.11.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2030c01cbf77bc67bee7eef1e7e31ecf28649353987775e3583062c752da0077", size = 146309, upload-time = "2025-08-26T17:46:10.576Z" },
{ url = "https://files.pythonhosted.org/packages/cd/1d/a473c158e380ef6f32753b5f39a69028b25ec5be331c2049a2201bde2e19/orjson-3.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a0169ebd1cbd94b26c7a7ad282cf5c2744fce054133f959e02eb5265deae1872", size = 135424, upload-time = "2025-08-26T17:46:12.386Z" },
{ url = "https://files.pythonhosted.org/packages/da/09/17d9d2b60592890ff7382e591aa1d9afb202a266b180c3d4049b1ec70e4a/orjson-3.11.3-cp314-cp314-win32.whl", hash = "sha256:0c6d7328c200c349e3a4c6d8c83e0a5ad029bdc2d417f234152bf34842d0fc8d", size = 136266, upload-time = "2025-08-26T17:46:13.853Z" },
{ url = "https://files.pythonhosted.org/packages/15/58/358f6846410a6b4958b74734727e582ed971e13d335d6c7ce3e47730493e/orjson-3.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:317bbe2c069bbc757b1a2e4105b64aacd3bc78279b66a6b9e51e846e4809f804", size = 131351, upload-time = "2025-08-26T17:46:15.27Z" },
{ url = "https://files.pythonhosted.org/packages/28/01/d6b274a0635be0468d4dbd9cafe80c47105937a0d42434e805e67cd2ed8b/orjson-3.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:e8f6a7a27d7b7bec81bd5924163e9af03d49bbb63013f107b48eb5d16db711bc", size = 125985, upload-time = "2025-08-26T17:46:16.67Z" },
]
[[package]]
@@ -2530,27 +2532,28 @@ wheels = [
[[package]]
name = "ruff"
version = "0.12.8"
version = "0.12.11"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4b/da/5bd7565be729e86e1442dad2c9a364ceeff82227c2dece7c29697a9795eb/ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033", size = 5242373, upload-time = "2025-08-07T19:05:47.268Z" }
sdist = { url = "https://files.pythonhosted.org/packages/de/55/16ab6a7d88d93001e1ae4c34cbdcfb376652d761799459ff27c1dc20f6fa/ruff-0.12.11.tar.gz", hash = "sha256:c6b09ae8426a65bbee5425b9d0b82796dbb07cb1af045743c79bfb163001165d", size = 5347103, upload-time = "2025-08-28T13:59:08.87Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c9/1e/c843bfa8ad1114fab3eb2b78235dda76acd66384c663a4e0415ecc13aa1e/ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513", size = 11675315, upload-time = "2025-08-07T19:05:06.15Z" },
{ url = "https://files.pythonhosted.org/packages/24/ee/af6e5c2a8ca3a81676d5480a1025494fd104b8896266502bb4de2a0e8388/ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc", size = 12456653, upload-time = "2025-08-07T19:05:09.759Z" },
{ url = "https://files.pythonhosted.org/packages/99/9d/e91f84dfe3866fa648c10512904991ecc326fd0b66578b324ee6ecb8f725/ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec", size = 11659690, upload-time = "2025-08-07T19:05:12.551Z" },
{ url = "https://files.pythonhosted.org/packages/fe/ac/a363d25ec53040408ebdd4efcee929d48547665858ede0505d1d8041b2e5/ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53", size = 11896923, upload-time = "2025-08-07T19:05:14.821Z" },
{ url = "https://files.pythonhosted.org/packages/58/9f/ea356cd87c395f6ade9bb81365bd909ff60860975ca1bc39f0e59de3da37/ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f", size = 11477612, upload-time = "2025-08-07T19:05:16.712Z" },
{ url = "https://files.pythonhosted.org/packages/1a/46/92e8fa3c9dcfd49175225c09053916cb97bb7204f9f899c2f2baca69e450/ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f", size = 13182745, upload-time = "2025-08-07T19:05:18.709Z" },
{ url = "https://files.pythonhosted.org/packages/5e/c4/f2176a310f26e6160deaf661ef60db6c3bb62b7a35e57ae28f27a09a7d63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609", size = 14206885, upload-time = "2025-08-07T19:05:21.025Z" },
{ url = "https://files.pythonhosted.org/packages/87/9d/98e162f3eeeb6689acbedbae5050b4b3220754554526c50c292b611d3a63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a", size = 13639381, upload-time = "2025-08-07T19:05:23.423Z" },
{ url = "https://files.pythonhosted.org/packages/81/4e/1b7478b072fcde5161b48f64774d6edd59d6d198e4ba8918d9f4702b8043/ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb", size = 12613271, upload-time = "2025-08-07T19:05:25.507Z" },
{ url = "https://files.pythonhosted.org/packages/e8/67/0c3c9179a3ad19791ef1b8f7138aa27d4578c78700551c60d9260b2c660d/ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818", size = 12847783, upload-time = "2025-08-07T19:05:28.14Z" },
{ url = "https://files.pythonhosted.org/packages/4e/2a/0b6ac3dd045acf8aa229b12c9c17bb35508191b71a14904baf99573a21bd/ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea", size = 11702672, upload-time = "2025-08-07T19:05:30.413Z" },
{ url = "https://files.pythonhosted.org/packages/9d/ee/f9fdc9f341b0430110de8b39a6ee5fa68c5706dc7c0aa940817947d6937e/ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3", size = 11440626, upload-time = "2025-08-07T19:05:32.492Z" },
{ url = "https://files.pythonhosted.org/packages/89/fb/b3aa2d482d05f44e4d197d1de5e3863feb13067b22c571b9561085c999dc/ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161", size = 12462162, upload-time = "2025-08-07T19:05:34.449Z" },
{ url = "https://files.pythonhosted.org/packages/18/9f/5c5d93e1d00d854d5013c96e1a92c33b703a0332707a7cdbd0a4880a84fb/ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46", size = 12913212, upload-time = "2025-08-07T19:05:36.541Z" },
{ url = "https://files.pythonhosted.org/packages/71/13/ab9120add1c0e4604c71bfc2e4ef7d63bebece0cfe617013da289539cef8/ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3", size = 11694382, upload-time = "2025-08-07T19:05:38.468Z" },
{ url = "https://files.pythonhosted.org/packages/f6/dc/a2873b7c5001c62f46266685863bee2888caf469d1edac84bf3242074be2/ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e", size = 12740482, upload-time = "2025-08-07T19:05:40.391Z" },
{ url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718, upload-time = "2025-08-07T19:05:42.866Z" },
{ url = "https://files.pythonhosted.org/packages/d6/a2/3b3573e474de39a7a475f3fbaf36a25600bfeb238e1a90392799163b64a0/ruff-0.12.11-py3-none-linux_armv6l.whl", hash = "sha256:93fce71e1cac3a8bf9200e63a38ac5c078f3b6baebffb74ba5274fb2ab276065", size = 11979885, upload-time = "2025-08-28T13:58:26.654Z" },
{ url = "https://files.pythonhosted.org/packages/76/e4/235ad6d1785a2012d3ded2350fd9bc5c5af8c6f56820e696b0118dfe7d24/ruff-0.12.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8e33ac7b28c772440afa80cebb972ffd823621ded90404f29e5ab6d1e2d4b93", size = 12742364, upload-time = "2025-08-28T13:58:30.256Z" },
{ url = "https://files.pythonhosted.org/packages/2c/0d/15b72c5fe6b1e402a543aa9d8960e0a7e19dfb079f5b0b424db48b7febab/ruff-0.12.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d69fb9d4937aa19adb2e9f058bc4fbfe986c2040acb1a4a9747734834eaa0bfd", size = 11920111, upload-time = "2025-08-28T13:58:33.677Z" },
{ url = "https://files.pythonhosted.org/packages/3e/c0/f66339d7893798ad3e17fa5a1e587d6fd9806f7c1c062b63f8b09dda6702/ruff-0.12.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:411954eca8464595077a93e580e2918d0a01a19317af0a72132283e28ae21bee", size = 12160060, upload-time = "2025-08-28T13:58:35.74Z" },
{ url = "https://files.pythonhosted.org/packages/03/69/9870368326db26f20c946205fb2d0008988aea552dbaec35fbacbb46efaa/ruff-0.12.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a2c0a2e1a450f387bf2c6237c727dd22191ae8c00e448e0672d624b2bbd7fb0", size = 11799848, upload-time = "2025-08-28T13:58:38.051Z" },
{ url = "https://files.pythonhosted.org/packages/25/8c/dd2c7f990e9b3a8a55eee09d4e675027d31727ce33cdb29eab32d025bdc9/ruff-0.12.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ca4c3a7f937725fd2413c0e884b5248a19369ab9bdd850b5781348ba283f644", size = 13536288, upload-time = "2025-08-28T13:58:40.046Z" },
{ url = "https://files.pythonhosted.org/packages/7a/30/d5496fa09aba59b5e01ea76775a4c8897b13055884f56f1c35a4194c2297/ruff-0.12.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4d1df0098124006f6a66ecf3581a7f7e754c4df7644b2e6704cd7ca80ff95211", size = 14490633, upload-time = "2025-08-28T13:58:42.285Z" },
{ url = "https://files.pythonhosted.org/packages/9b/2f/81f998180ad53445d403c386549d6946d0748e536d58fce5b5e173511183/ruff-0.12.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a8dd5f230efc99a24ace3b77e3555d3fbc0343aeed3fc84c8d89e75ab2ff793", size = 13888430, upload-time = "2025-08-28T13:58:44.641Z" },
{ url = "https://files.pythonhosted.org/packages/87/71/23a0d1d5892a377478c61dbbcffe82a3476b050f38b5162171942a029ef3/ruff-0.12.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dc75533039d0ed04cd33fb8ca9ac9620b99672fe7ff1533b6402206901c34ee", size = 12913133, upload-time = "2025-08-28T13:58:47.039Z" },
{ url = "https://files.pythonhosted.org/packages/80/22/3c6cef96627f89b344c933781ed38329bfb87737aa438f15da95907cbfd5/ruff-0.12.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fc58f9266d62c6eccc75261a665f26b4ef64840887fc6cbc552ce5b29f96cc8", size = 13169082, upload-time = "2025-08-28T13:58:49.157Z" },
{ url = "https://files.pythonhosted.org/packages/05/b5/68b3ff96160d8b49e8dd10785ff3186be18fd650d356036a3770386e6c7f/ruff-0.12.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5a0113bd6eafd545146440225fe60b4e9489f59eb5f5f107acd715ba5f0b3d2f", size = 13139490, upload-time = "2025-08-28T13:58:51.593Z" },
{ url = "https://files.pythonhosted.org/packages/59/b9/050a3278ecd558f74f7ee016fbdf10591d50119df8d5f5da45a22c6afafc/ruff-0.12.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0d737b4059d66295c3ea5720e6efc152623bb83fde5444209b69cd33a53e2000", size = 11958928, upload-time = "2025-08-28T13:58:53.943Z" },
{ url = "https://files.pythonhosted.org/packages/f9/bc/93be37347db854806904a43b0493af8d6873472dfb4b4b8cbb27786eb651/ruff-0.12.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:916fc5defee32dbc1fc1650b576a8fed68f5e8256e2180d4d9855aea43d6aab2", size = 11764513, upload-time = "2025-08-28T13:58:55.976Z" },
{ url = "https://files.pythonhosted.org/packages/7a/a1/1471751e2015a81fd8e166cd311456c11df74c7e8769d4aabfbc7584c7ac/ruff-0.12.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c984f07d7adb42d3ded5be894fb4007f30f82c87559438b4879fe7aa08c62b39", size = 12745154, upload-time = "2025-08-28T13:58:58.16Z" },
{ url = "https://files.pythonhosted.org/packages/68/ab/2542b14890d0f4872dd81b7b2a6aed3ac1786fae1ce9b17e11e6df9e31e3/ruff-0.12.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e07fbb89f2e9249f219d88331c833860489b49cdf4b032b8e4432e9b13e8a4b9", size = 13227653, upload-time = "2025-08-28T13:59:00.276Z" },
{ url = "https://files.pythonhosted.org/packages/22/16/2fbfc61047dbfd009c58a28369a693a1484ad15441723be1cd7fe69bb679/ruff-0.12.11-py3-none-win32.whl", hash = "sha256:c792e8f597c9c756e9bcd4d87cf407a00b60af77078c96f7b6366ea2ce9ba9d3", size = 11944270, upload-time = "2025-08-28T13:59:02.347Z" },
{ url = "https://files.pythonhosted.org/packages/08/a5/34276984705bfe069cd383101c45077ee029c3fe3b28225bf67aa35f0647/ruff-0.12.11-py3-none-win_amd64.whl", hash = "sha256:a3283325960307915b6deb3576b96919ee89432ebd9c48771ca12ee8afe4a0fd", size = 13046600, upload-time = "2025-08-28T13:59:04.751Z" },
{ url = "https://files.pythonhosted.org/packages/84/a8/001d4a7c2b37623a3fd7463208267fb906df40ff31db496157549cfd6e72/ruff-0.12.11-py3-none-win_arm64.whl", hash = "sha256:bae4d6e6a2676f8fb0f98b74594a048bae1b944aab17e9f5d504062303c6dbea", size = 12135290, upload-time = "2025-08-28T13:59:06.933Z" },
]
[[package]]
@@ -2919,11 +2922,11 @@ wheels = [
[[package]]
name = "types-pyyaml"
version = "6.0.12.20250809"
version = "6.0.12.20250822"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/36/21/52ffdbddea3c826bc2758d811ccd7f766912de009c5cf096bd5ebba44680/types_pyyaml-6.0.12.20250809.tar.gz", hash = "sha256:af4a1aca028f18e75297da2ee0da465f799627370d74073e96fee876524f61b5", size = 17385, upload-time = "2025-08-09T03:14:34.867Z" }
sdist = { url = "https://files.pythonhosted.org/packages/49/85/90a442e538359ab5c9e30de415006fb22567aa4301c908c09f19e42975c2/types_pyyaml-6.0.12.20250822.tar.gz", hash = "sha256:259f1d93079d335730a9db7cff2bcaf65d7e04b4a56b5927d49a612199b59413", size = 17481, upload-time = "2025-08-22T03:02:16.209Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/35/3e/0346d09d6e338401ebf406f12eaf9d0b54b315b86f1ec29e34f1a0aedae9/types_pyyaml-6.0.12.20250809-py3-none-any.whl", hash = "sha256:032b6003b798e7de1a1ddfeefee32fac6486bdfe4845e0ae0e7fb3ee4512b52f", size = 20277, upload-time = "2025-08-09T03:14:34.055Z" },
{ url = "https://files.pythonhosted.org/packages/32/8e/8f0aca667c97c0d76024b37cffa39e76e2ce39ca54a38f285a64e6ae33ba/types_pyyaml-6.0.12.20250822-py3-none-any.whl", hash = "sha256:1fe1a5e146aa315483592d292b72a172b65b946a6d98aa6ddd8e4aa838ab7098", size = 20314, upload-time = "2025-08-22T03:02:15.002Z" },
]
[[package]]
@@ -2940,29 +2943,29 @@ wheels = [
[[package]]
name = "types-setuptools"
version = "80.9.0.20250809"
version = "80.9.0.20250822"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/43/4f/d78a04083ee3cc0a7c14406afb2f1e7b63e70da95b777571d665d89b1765/types_setuptools-80.9.0.20250809.tar.gz", hash = "sha256:e986ba37ffde364073d76189e1d79d9928fb6f5278c7d07589cde353d0218864", size = 41209, upload-time = "2025-08-09T03:14:24.977Z" }
sdist = { url = "https://files.pythonhosted.org/packages/19/bd/1e5f949b7cb740c9f0feaac430e301b8f1c5f11a81e26324299ea671a237/types_setuptools-80.9.0.20250822.tar.gz", hash = "sha256:070ea7716968ec67a84c7f7768d9952ff24d28b65b6594797a464f1b3066f965", size = 41296, upload-time = "2025-08-22T03:02:08.771Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ca/1d/ad4fd409b377904324cbd2dc3a11e29ba13e2cf603c5a14cd88a35da5be0/types_setuptools-80.9.0.20250809-py3-none-any.whl", hash = "sha256:7c6539b4c7ac7b4ab4db2be66d8a58fb1e28affa3ee3834be48acafd94f5976a", size = 63160, upload-time = "2025-08-09T03:14:23.639Z" },
{ url = "https://files.pythonhosted.org/packages/b6/2d/475bf15c1cdc172e7a0d665b6e373ebfb1e9bf734d3f2f543d668b07a142/types_setuptools-80.9.0.20250822-py3-none-any.whl", hash = "sha256:53bf881cb9d7e46ed12c76ef76c0aaf28cfe6211d3fab12e0b83620b1a8642c3", size = 63179, upload-time = "2025-08-22T03:02:07.643Z" },
]
[[package]]
name = "types-simplejson"
version = "3.20.0.20250326"
version = "3.20.0.20250822"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/af/14/e26fc55e1ea56f9ea470917d3e2f8240e6d043ca914181021d04115ae0f7/types_simplejson-3.20.0.20250326.tar.gz", hash = "sha256:b2689bc91e0e672d7a5a947b4cb546b76ae7ddc2899c6678e72a10bf96cd97d2", size = 10489, upload-time = "2025-03-26T02:53:35.825Z" }
sdist = { url = "https://files.pythonhosted.org/packages/df/6b/96d43a90cd202bd552cdd871858a11c138fe5ef11aeb4ed8e8dc51389257/types_simplejson-3.20.0.20250822.tar.gz", hash = "sha256:2b0bfd57a6beed3b932fd2c3c7f8e2f48a7df3978c9bba43023a32b3741a95b0", size = 10608, upload-time = "2025-08-22T03:03:35.36Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/bf/d3f3a5ba47fd18115e8446d39f025b85905d2008677c29ee4d03b4cddd57/types_simplejson-3.20.0.20250326-py3-none-any.whl", hash = "sha256:db1ddea7b8f7623b27a137578f22fc6c618db8c83ccfb1828ca0d2f0ec11efa7", size = 10462, upload-time = "2025-03-26T02:53:35.036Z" },
{ url = "https://files.pythonhosted.org/packages/3c/9f/8e2c9e6aee9a2ff34f2ffce6ccd9c26edeef6dfd366fde611dc2e2c00ab9/types_simplejson-3.20.0.20250822-py3-none-any.whl", hash = "sha256:b5e63ae220ac7a1b0bb9af43b9cb8652237c947981b2708b0c776d3b5d8fa169", size = 10417, upload-time = "2025-08-22T03:03:34.485Z" },
]
[[package]]
name = "types-ujson"
version = "5.10.0.20250326"
version = "5.10.0.20250822"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cc/5c/c974451c4babdb4ae3588925487edde492d59a8403010b4642a554d09954/types_ujson-5.10.0.20250326.tar.gz", hash = "sha256:5469e05f2c31ecb3c4c0267cc8fe41bcd116826fbb4ded69801a645c687dd014", size = 8340, upload-time = "2025-03-26T02:53:39.197Z" }
sdist = { url = "https://files.pythonhosted.org/packages/5c/bd/d372d44534f84864a96c19a7059d9b4d29db8541828b8b9dc3040f7a46d0/types_ujson-5.10.0.20250822.tar.gz", hash = "sha256:0a795558e1f78532373cf3f03f35b1f08bc60d52d924187b97995ee3597ba006", size = 8437, upload-time = "2025-08-22T03:02:19.433Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3e/c9/8a73a5f8fa6e70fc02eed506d5ac0ae9ceafbd2b8c9ad34a7de0f29900d6/types_ujson-5.10.0.20250326-py3-none-any.whl", hash = "sha256:acc0913f569def62ef6a892c8a47703f65d05669a3252391a97765cf207dca5b", size = 7644, upload-time = "2025-03-26T02:53:38.2Z" },
{ url = "https://files.pythonhosted.org/packages/d7/f2/d812543c350674d8b3f6e17c8922248ee3bb752c2a76f64beb8c538b40cf/types_ujson-5.10.0.20250822-py3-none-any.whl", hash = "sha256:3e9e73a6dc62ccc03449d9ac2c580cd1b7a8e4873220db498f7dd056754be080", size = 7657, upload-time = "2025-08-22T03:02:18.699Z" },
]
[[package]]

View File

@@ -65,7 +65,7 @@ if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then
pnpm install --frozen-lockfile --prefix server
pnpm --prefix server run build
make open-api
( cd ./open-api && bash ./bin/generate-open-api.sh )
jq --arg version "$NEXT_SERVER" '.version = $version' open-api/typescript-sdk/package.json > open-api/typescript-sdk/package.json.tmp && mv open-api/typescript-sdk/package.json.tmp open-api/typescript-sdk/package.json

View File

@@ -81,6 +81,7 @@ custom_lint:
# acceptable exceptions for the time being (until Isar is fully replaced)
- lib/providers/app_life_cycle.provider.dart
- integration_test/test_utils/general_helper.dart
- lib/domain/services/background_worker.service.dart
- lib/main.dart
- lib/pages/album/album_asset_selection.page.dart
- lib/routing/router.dart

View File

@@ -5,15 +5,15 @@ import androidx.work.Configuration
import androidx.work.WorkManager
class ImmichApp : Application() {
override fun onCreate() {
super.onCreate()
val config = Configuration.Builder().build()
WorkManager.initialize(this, config)
// always start BackupWorker after WorkManager init; this fixes the following bug:
// After the process is killed (by user or system), the first trigger (taking a new picture) is lost.
// Thus, the BackupWorker is not started. If the system kills the process after each initialization
// (because of low memory etc.), the backup is never performed.
// As a workaround, we also run a backup check when initializing the application
ContentObserverWorker.startBackupWorker(context = this, delayMilliseconds = 0)
}
}
override fun onCreate() {
super.onCreate()
val config = Configuration.Builder().build()
WorkManager.initialize(this, config)
// always start BackupWorker after WorkManager init; this fixes the following bug:
// After the process is killed (by user or system), the first trigger (taking a new picture) is lost.
// Thus, the BackupWorker is not started. If the system kills the process after each initialization
// (because of low memory etc.), the backup is never performed.
// As a workaround, we also run a backup check when initializing the application
ContentObserverWorker.startBackupWorker(context = this, delayMilliseconds = 0)
}
}

View File

@@ -1,8 +1,10 @@
package app.alextran.immich
import android.content.Context
import android.os.Build
import android.os.ext.SdkExtensions
import androidx.annotation.NonNull
import app.alextran.immich.background.BackgroundWorkerApiImpl
import app.alextran.immich.background.BackgroundWorkerFgHostApi
import app.alextran.immich.images.ThumbnailApi
import app.alextran.immich.images.ThumbnailsImpl
import app.alextran.immich.sync.NativeSyncApi
@@ -12,19 +14,26 @@ import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterFragmentActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
flutterEngine.plugins.add(BackgroundServicePlugin())
flutterEngine.plugins.add(HttpSSLOptionsPlugin())
// No need to set up method channel here as it's now handled in the plugin
registerPlugins(this, flutterEngine)
}
val nativeSyncApiImpl =
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) < 1) {
NativeSyncApiImpl26(this)
} else {
NativeSyncApiImpl30(this)
}
NativeSyncApi.setUp(flutterEngine.dartExecutor.binaryMessenger, nativeSyncApiImpl)
ThumbnailApi.setUp(flutterEngine.dartExecutor.binaryMessenger, ThumbnailsImpl(this))
companion object {
fun registerPlugins(ctx: Context, flutterEngine: FlutterEngine) {
flutterEngine.plugins.add(BackgroundServicePlugin())
flutterEngine.plugins.add(HttpSSLOptionsPlugin())
val messenger = flutterEngine.dartExecutor.binaryMessenger
val nativeSyncApiImpl =
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) < 1) {
NativeSyncApiImpl26(ctx)
} else {
NativeSyncApiImpl30(ctx)
}
NativeSyncApi.setUp(messenger, nativeSyncApiImpl)
ThumbnailApi.setUp(messenger, ThumbnailsImpl(ctx))
BackgroundWorkerFgHostApi.setUp(messenger, BackgroundWorkerApiImpl(ctx))
}
}
}

View File

@@ -0,0 +1,238 @@
// Autogenerated from Pigeon (v26.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
package app.alextran.immich.background
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMethodCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private object BackgroundWorkerPigeonUtils {
fun createConnectionError(channelName: String): FlutterError {
return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") }
fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
}
fun wrapError(exception: Throwable): List<Any?> {
return if (exception is FlutterError) {
listOf(
exception.code,
exception.message,
exception.details
)
} else {
listOf(
exception.javaClass.simpleName,
exception.toString(),
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
)
}
}
}
/**
* Error class for passing custom error details to Flutter via a thrown PlatformException.
* @property code The error code.
* @property message The error message.
* @property details The error details. Must be a datatype supported by the api codec.
*/
class FlutterError (
val code: String,
override val message: String? = null,
val details: Any? = null
) : Throwable()
private open class BackgroundWorkerPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return super.readValueOfType(type, buffer)
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
super.writeValue(stream, value)
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface BackgroundWorkerFgHostApi {
fun enableSyncWorker()
fun enableUploadWorker(callbackHandle: Long)
fun disableUploadWorker()
companion object {
/** The codec used by BackgroundWorkerFgHostApi. */
val codec: MessageCodec<Any?> by lazy {
BackgroundWorkerPigeonCodec()
}
/** Sets up an instance of `BackgroundWorkerFgHostApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(binaryMessenger: BinaryMessenger, api: BackgroundWorkerFgHostApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableSyncWorker$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> = try {
api.enableSyncWorker()
listOf(null)
} catch (exception: Throwable) {
BackgroundWorkerPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableUploadWorker$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val callbackHandleArg = args[0] as Long
val wrapped: List<Any?> = try {
api.enableUploadWorker(callbackHandleArg)
listOf(null)
} catch (exception: Throwable) {
BackgroundWorkerPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disableUploadWorker$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> = try {
api.disableUploadWorker()
listOf(null)
} catch (exception: Throwable) {
BackgroundWorkerPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface BackgroundWorkerBgHostApi {
fun onInitialized()
companion object {
/** The codec used by BackgroundWorkerBgHostApi. */
val codec: MessageCodec<Any?> by lazy {
BackgroundWorkerPigeonCodec()
}
/** Sets up an instance of `BackgroundWorkerBgHostApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(binaryMessenger: BinaryMessenger, api: BackgroundWorkerBgHostApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.onInitialized$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> = try {
api.onInitialized()
listOf(null)
} catch (exception: Throwable) {
BackgroundWorkerPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */
class BackgroundWorkerFlutterApi(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") {
companion object {
/** The codec used by BackgroundWorkerFlutterApi. */
val codec: MessageCodec<Any?> by lazy {
BackgroundWorkerPigeonCodec()
}
}
fun onLocalSync(maxSecondsArg: Long?, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onLocalSync$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(maxSecondsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName)))
}
}
}
fun onIosUpload(isRefreshArg: Boolean, maxSecondsArg: Long?, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(isRefreshArg, maxSecondsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName)))
}
}
}
fun onAndroidUpload(callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onAndroidUpload$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(null) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName)))
}
}
}
fun cancel(callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.cancel$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(null) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName)))
}
}
}
}

View File

@@ -0,0 +1,162 @@
package app.alextran.immich.background
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.work.ListenableWorker
import androidx.work.WorkerParameters
import app.alextran.immich.MainActivity
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture
import io.flutter.FlutterInjector
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor.DartCallback
import io.flutter.embedding.engine.loader.FlutterLoader
import io.flutter.view.FlutterCallbackInformation
private const val TAG = "BackgroundWorker"
enum class BackgroundTaskType {
LOCAL_SYNC,
UPLOAD,
}
class BackgroundWorker(context: Context, params: WorkerParameters) :
ListenableWorker(context, params), BackgroundWorkerBgHostApi {
private val ctx: Context = context.applicationContext
/// The Flutter loader that loads the native Flutter library and resources.
/// This must be initialized before starting the Flutter engine.
private var loader: FlutterLoader = FlutterInjector.instance().flutterLoader()
/// The Flutter engine created specifically for background execution.
/// This is a separate instance from the main Flutter engine that handles the UI.
/// It operates in its own isolate and doesn't share memory with the main engine.
/// Must be properly started, registered, and torn down during background execution.
private var engine: FlutterEngine? = null
// Used to call methods on the flutter side
private var flutterApi: BackgroundWorkerFlutterApi? = null
/// Result returned when the background task completes. This is used to signal
/// to the WorkManager that the task has finished, either successfully or with failure.
private val completionHandler: SettableFuture<Result> = SettableFuture.create()
/// Flag to track whether the background task has completed to prevent duplicate completions
private var isComplete = false
init {
if (!loader.initialized()) {
loader.startInitialization(ctx)
}
}
override fun startWork(): ListenableFuture<Result> {
Log.i(TAG, "Starting background upload worker")
loader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) {
engine = FlutterEngine(ctx)
// Retrieve the callback handle stored by the main Flutter app
// This handle points to the Flutter function that should be executed in the background
val callbackHandle =
ctx.getSharedPreferences(BackgroundWorkerApiImpl.SHARED_PREF_NAME, Context.MODE_PRIVATE)
.getLong(BackgroundWorkerApiImpl.SHARED_PREF_CALLBACK_HANDLE, 0L)
if (callbackHandle == 0L) {
// Without a valid callback handle, we cannot start the Flutter background execution
complete(Result.failure())
return@ensureInitializationCompleteAsync
}
// Start the Flutter engine with the specified callback as the entry point
val callback = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle)
if (callback == null) {
complete(Result.failure())
return@ensureInitializationCompleteAsync
}
// Register custom plugins
MainActivity.registerPlugins(ctx, engine!!)
flutterApi =
BackgroundWorkerFlutterApi(binaryMessenger = engine!!.dartExecutor.binaryMessenger)
BackgroundWorkerBgHostApi.setUp(
binaryMessenger = engine!!.dartExecutor.binaryMessenger,
api = this
)
engine!!.dartExecutor.executeDartCallback(
DartCallback(ctx.assets, loader.findAppBundlePath(), callback)
)
}
return completionHandler
}
/**
* Called by the Flutter side when it has finished initialization and is ready to receive commands.
* Routes the appropriate task type (refresh or processing) to the corresponding Flutter method.
* This method acts as a bridge between the native Android background task system and Flutter.
*/
override fun onInitialized() {
val taskTypeIndex = inputData.getInt(BackgroundWorkerApiImpl.WORKER_DATA_TASK_TYPE, 0)
val taskType = BackgroundTaskType.entries[taskTypeIndex]
when (taskType) {
BackgroundTaskType.LOCAL_SYNC -> flutterApi?.onLocalSync(null) { handleHostResult(it) }
BackgroundTaskType.UPLOAD -> flutterApi?.onAndroidUpload { handleHostResult(it) }
}
}
/**
* Called when the system has to stop this worker because constraints are
* no longer met or the system needs resources for more important tasks
* This is also called when the worker has been explicitly cancelled or replaced
*/
override fun onStopped() {
Log.d(TAG, "About to stop BackupWorker")
if (isComplete) {
return
}
Handler(Looper.getMainLooper()).postAtFrontOfQueue {
if (flutterApi != null) {
flutterApi?.cancel {
complete(Result.failure())
}
}
}
Handler(Looper.getMainLooper()).postDelayed({
complete(Result.failure())
}, 5000)
}
private fun handleHostResult(result: kotlin.Result<Unit>) {
if (isComplete) {
return
}
result.fold(
onSuccess = { _ -> complete(Result.success()) },
onFailure = { _ -> onStopped() }
)
}
/**
* Cleans up resources by destroying the Flutter engine context and invokes the completion handler.
* This method ensures that the background task is marked as complete, releases the Flutter engine,
* and notifies the caller of the task's success or failure. This is the final step in the
* background task lifecycle and should only be called once per task instance.
*
* - Parameter success: Indicates whether the background task completed successfully
*/
private fun complete(success: Result) {
isComplete = true
engine?.destroy()
flutterApi = null
completionHandler.set(success)
}
}

View File

@@ -0,0 +1,92 @@
package app.alextran.immich.background
import android.content.Context
import android.provider.MediaStore
import android.util.Log
import androidx.core.content.edit
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import java.util.concurrent.TimeUnit
private const val TAG = "BackgroundUploadImpl"
class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
private val ctx: Context = context.applicationContext
override fun enableSyncWorker() {
enqueueMediaObserver(ctx)
Log.i(TAG, "Scheduled media observer")
}
override fun enableUploadWorker(callbackHandle: Long) {
updateUploadEnabled(ctx, true)
updateCallbackHandle(ctx, callbackHandle)
Log.i(TAG, "Scheduled background upload tasks")
}
override fun disableUploadWorker() {
updateUploadEnabled(ctx, false)
WorkManager.getInstance(ctx).cancelUniqueWork(BACKGROUND_WORKER_NAME)
Log.i(TAG, "Cancelled background upload tasks")
}
companion object {
private const val BACKGROUND_WORKER_NAME = "immich/BackgroundWorkerV1"
private const val OBSERVER_WORKER_NAME = "immich/MediaObserverV1"
const val WORKER_DATA_TASK_TYPE = "taskType"
const val SHARED_PREF_NAME = "Immich::Background"
const val SHARED_PREF_BACKUP_ENABLED = "Background::backup::enabled"
const val SHARED_PREF_CALLBACK_HANDLE = "Background::backup::callbackHandle"
private fun updateUploadEnabled(context: Context, enabled: Boolean) {
context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE).edit {
putBoolean(SHARED_PREF_BACKUP_ENABLED, enabled)
}
}
private fun updateCallbackHandle(context: Context, callbackHandle: Long) {
context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE).edit {
putLong(SHARED_PREF_CALLBACK_HANDLE, callbackHandle)
}
}
fun enqueueMediaObserver(ctx: Context) {
val constraints = Constraints.Builder()
.addContentUriTrigger(MediaStore.Images.Media.INTERNAL_CONTENT_URI, true)
.addContentUriTrigger(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true)
.addContentUriTrigger(MediaStore.Video.Media.INTERNAL_CONTENT_URI, true)
.addContentUriTrigger(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true)
.setTriggerContentUpdateDelay(5, TimeUnit.SECONDS)
.setTriggerContentMaxDelay(1, TimeUnit.MINUTES)
.build()
val work = OneTimeWorkRequest.Builder(MediaObserver::class.java)
.setConstraints(constraints)
.build()
WorkManager.getInstance(ctx)
.enqueueUniqueWork(OBSERVER_WORKER_NAME, ExistingWorkPolicy.REPLACE, work)
Log.i(TAG, "Enqueued media observer worker with name: $OBSERVER_WORKER_NAME")
}
fun enqueueBackgroundWorker(ctx: Context, taskType: BackgroundTaskType) {
val constraints = Constraints.Builder().setRequiresBatteryNotLow(true).build()
val data = Data.Builder()
data.putInt(WORKER_DATA_TASK_TYPE, taskType.ordinal)
val work = OneTimeWorkRequest.Builder(BackgroundWorker::class.java)
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES)
.setInputData(data.build()).build()
WorkManager.getInstance(ctx)
.enqueueUniqueWork(BACKGROUND_WORKER_NAME, ExistingWorkPolicy.REPLACE, work)
Log.i(TAG, "Enqueued background worker with name: $BACKGROUND_WORKER_NAME")
}
}
}

View File

@@ -0,0 +1,34 @@
package app.alextran.immich.background
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters
class MediaObserver(context: Context, params: WorkerParameters) : Worker(context, params) {
private val ctx: Context = context.applicationContext
override fun doWork(): Result {
Log.i("MediaObserver", "Content change detected, starting background worker")
// Enqueue backup worker only if there are new media changes
if (triggeredContentUris.isNotEmpty()) {
val type =
if (isBackupEnabled(ctx)) BackgroundTaskType.UPLOAD else BackgroundTaskType.LOCAL_SYNC
BackgroundWorkerApiImpl.enqueueBackgroundWorker(ctx, type)
}
// Re-enqueue itself to listen for future changes
BackgroundWorkerApiImpl.enqueueMediaObserver(ctx)
return Result.success()
}
private fun isBackupEnabled(context: Context): Boolean {
val prefs =
context.getSharedPreferences(
BackgroundWorkerApiImpl.SHARED_PREF_NAME,
Context.MODE_PRIVATE
)
return prefs.getBoolean(BackgroundWorkerApiImpl.SHARED_PREF_BACKUP_ENABLED, false)
}
}

View File

@@ -202,8 +202,7 @@ class ThumbnailsImpl(context: Context) : ThumbnailApi {
val source = ImageDecoder.createSource(resolver, uri)
signal.throwIfCanceled()
ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
val sampleSize =
getSampleSize(info.size.width, info.size.height, targetWidth, targetHeight)
val sampleSize = max(1, min(info.size.width / targetWidth, info.size.height / targetHeight))
decoder.setTargetSampleSize(sampleSize)
decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))
@@ -216,15 +215,4 @@ class ThumbnailsImpl(context: Context) : ThumbnailApi {
ref.get()
}
}
private fun getSampleSize(fullWidth: Int, fullHeight: Int, reqWidth: Int, reqHeight: Int): Int {
return 1 shl max(
0, floor(
min(
log2(fullWidth / reqWidth.toDouble()),
log2(fullHeight / reqHeight.toDouble()),
)
).toInt()
)
}
}

View File

@@ -35,8 +35,8 @@ platform :android do
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.version.code" => 3007,
"android.injected.version.name" => "1.139.2",
"android.injected.version.code" => 3011,
"android.injected.version.name" => "1.140.1",
}
)
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')

View File

@@ -6,6 +6,9 @@ PODS:
- FlutterMacOS
- connectivity_plus (0.0.1):
- Flutter
- cupertino_http (0.0.1):
- Flutter
- FlutterMacOS
- device_info_plus (0.0.1):
- Flutter
- DKImagePickerController/Core (4.3.9):
@@ -77,6 +80,8 @@ PODS:
- Flutter
- network_info_plus (0.0.1):
- Flutter
- objective_c (0.0.1):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
@@ -136,6 +141,7 @@ DEPENDENCIES:
- background_downloader (from `.symlinks/plugins/background_downloader/ios`)
- bonsoir_darwin (from `.symlinks/plugins/bonsoir_darwin/darwin`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- cupertino_http (from `.symlinks/plugins/cupertino_http/darwin`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
@@ -154,6 +160,7 @@ DEPENDENCIES:
- maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`)
- native_video_player (from `.symlinks/plugins/native_video_player/ios`)
- network_info_plus (from `.symlinks/plugins/network_info_plus/ios`)
- objective_c (from `.symlinks/plugins/objective_c/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
@@ -184,6 +191,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/bonsoir_darwin/darwin"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
cupertino_http:
:path: ".symlinks/plugins/cupertino_http/darwin"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
file_picker:
@@ -220,6 +229,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/native_video_player/ios"
network_info_plus:
:path: ".symlinks/plugins/network_info_plus/ios"
objective_c:
:path: ".symlinks/plugins/objective_c/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
@@ -249,6 +260,7 @@ SPEC CHECKSUMS:
background_downloader: 50e91d979067b82081aba359d7d916b3ba5fadad
bonsoir_darwin: 29c7ccf356646118844721f36e1de4b61f6cbd0e
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
cupertino_http: 94ac07f5ff090b8effa6c5e2c47871d48ab7c86c
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
@@ -270,6 +282,7 @@ SPEC CHECKSUMS:
maplibre_gl: 3c924e44725147b03dda33430ad216005b40555f
native_video_player: b65c58951ede2f93d103a25366bdebca95081265
network_info_plus: cf61925ab5205dce05a4f0895989afdb6aade5fc
objective_c: 89e720c30d716b036faf9c9684022048eee1eee2
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d

View File

@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
@@ -16,6 +16,9 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
B21E34AA2E5AFD2B0031FDB9 /* BackgroundWorkerApiImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21E34A92E5AFD210031FDB9 /* BackgroundWorkerApiImpl.swift */; };
B21E34AC2E5B09190031FDB9 /* BackgroundWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */; };
B2BE315F2E5E5229006EEF88 /* BackgroundWorker.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */; };
D218389C4A4C4693F141F7D1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */; };
F02538E92DFBCBDD008C3FA3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
F0B57D3A2DF764BD00DC5BCC /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */; };
@@ -92,6 +95,9 @@
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.profile.xcconfig"; sourceTree = "<group>"; };
B21E34A92E5AFD210031FDB9 /* BackgroundWorkerApiImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorkerApiImpl.swift; sourceTree = "<group>"; };
B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorker.swift; sourceTree = "<group>"; };
B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorker.g.swift; sourceTree = "<group>"; };
E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
F0B57D382DF764BD00DC5BCC /* WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
@@ -123,6 +129,8 @@
/* Begin PBXFileSystemSynchronizedRootGroup section */
B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
path = Sync;
sourceTree = "<group>";
};
@@ -235,6 +243,7 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
B21E34A62E5AF9760031FDB9 /* Background */,
B2CF7F8C2DDE4EBB00744BF6 /* Sync */,
FA9973382CF6DF4B000EF859 /* Runner.entitlements */,
65DD438629917FAD0047FFA8 /* BackgroundSync */,
@@ -252,6 +261,16 @@
path = Runner;
sourceTree = "<group>";
};
B21E34A62E5AF9760031FDB9 /* Background */ = {
isa = PBXGroup;
children = (
B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */,
B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */,
B21E34A92E5AFD210031FDB9 /* BackgroundWorkerApiImpl.swift */,
);
path = Background;
sourceTree = "<group>";
};
FAC6F8B62D287F120078CB2F /* ShareExtension */ = {
isa = PBXGroup;
children = (
@@ -488,10 +507,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
@@ -520,10 +543,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
@@ -538,10 +565,13 @@
files = (
65F32F31299BD2F800CE9261 /* BackgroundServicePlugin.swift in Sources */,
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
B21E34AC2E5B09190031FDB9 /* BackgroundWorker.swift in Sources */,
FEAFA8732E4D42F4001E47FE /* Thumbhash.swift in Sources */,
FED3B1962E253E9B0030FD97 /* ThumbnailsImpl.swift in Sources */,
B21E34AA2E5AFD2B0031FDB9 /* BackgroundWorkerApiImpl.swift in Sources */,
FED3B1972E253E9B0030FD97 /* Thumbnails.g.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
B2BE315F2E5E5229006EEF88 /* BackgroundWorker.g.swift in Sources */,
65F32F33299D349D00CE9261 /* BackgroundSyncWorker.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -667,7 +697,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 215;
CURRENT_PROJECT_VERSION = 219;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@@ -811,7 +841,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 215;
CURRENT_PROJECT_VERSION = 219;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@@ -841,7 +871,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 215;
CURRENT_PROJECT_VERSION = 219;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@@ -875,7 +905,7 @@
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 215;
CURRENT_PROJECT_VERSION = 219;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -918,7 +948,7 @@
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 215;
CURRENT_PROJECT_VERSION = 219;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -958,7 +988,7 @@
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 215;
CURRENT_PROJECT_VERSION = 219;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -997,7 +1027,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 215;
CURRENT_PROJECT_VERSION = 219;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -1041,7 +1071,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 215;
CURRENT_PROJECT_VERSION = 219;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -1082,7 +1112,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 215;
CURRENT_PROJECT_VERSION = 219;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;

View File

@@ -19,13 +19,12 @@ import UIKit
}
GeneratedPluginRegistrant.register(with: self)
BackgroundServicePlugin.registerBackgroundProcessing()
BackgroundServicePlugin.register(with: self.registrar(forPlugin: "BackgroundServicePlugin")!)
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
NativeSyncApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: NativeSyncApiImpl())
ThumbnailApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: ThumbnailApiImpl())
AppDelegate.registerPlugins(binaryMessenger: controller.binaryMessenger)
BackgroundServicePlugin.register(with: self.registrar(forPlugin: "BackgroundServicePlugin")!)
BackgroundServicePlugin.registerBackgroundProcessing()
BackgroundWorkerApiImpl.registerBackgroundProcessing()
BackgroundServicePlugin.setPluginRegistrantCallback { registry in
if !registry.hasPlugin("org.cocoapods.path-provider-foundation") {
@@ -51,4 +50,10 @@ import UIKit
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
public static func registerPlugins(binaryMessenger: FlutterBinaryMessenger) {
NativeSyncApiSetup.setUp(binaryMessenger: binaryMessenger, api: NativeSyncApiImpl())
ThumbnailApiSetup.setUp(binaryMessenger: binaryMessenger, api: ThumbnailApiImpl())
BackgroundWorkerFgHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: BackgroundWorkerApiImpl())
}
}

View File

@@ -0,0 +1,245 @@
// Autogenerated from Pigeon (v26.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
import Foundation
#if os(iOS)
import Flutter
#elseif os(macOS)
import FlutterMacOS
#else
#error("Unsupported platform.")
#endif
private func wrapResult(_ result: Any?) -> [Any?] {
return [result]
}
private func wrapError(_ error: Any) -> [Any?] {
if let pigeonError = error as? PigeonError {
return [
pigeonError.code,
pigeonError.message,
pigeonError.details,
]
}
if let flutterError = error as? FlutterError {
return [
flutterError.code,
flutterError.message,
flutterError.details,
]
}
return [
"\(error)",
"\(type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)",
]
}
private func createConnectionError(withChannelName channelName: String) -> PigeonError {
return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "")
}
private func isNullish(_ value: Any?) -> Bool {
return value is NSNull || value == nil
}
private func nilOrValue<T>(_ value: Any?) -> T? {
if value is NSNull { return nil }
return value as! T?
}
private class BackgroundWorkerPigeonCodecReader: FlutterStandardReader {
}
private class BackgroundWorkerPigeonCodecWriter: FlutterStandardWriter {
}
private class BackgroundWorkerPigeonCodecReaderWriter: FlutterStandardReaderWriter {
override func reader(with data: Data) -> FlutterStandardReader {
return BackgroundWorkerPigeonCodecReader(data: data)
}
override func writer(with data: NSMutableData) -> FlutterStandardWriter {
return BackgroundWorkerPigeonCodecWriter(data: data)
}
}
class BackgroundWorkerPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
static let shared = BackgroundWorkerPigeonCodec(readerWriter: BackgroundWorkerPigeonCodecReaderWriter())
}
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol BackgroundWorkerFgHostApi {
func enableSyncWorker() throws
func enableUploadWorker(callbackHandle: Int64) throws
func disableUploadWorker() throws
}
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
class BackgroundWorkerFgHostApiSetup {
static var codec: FlutterStandardMessageCodec { BackgroundWorkerPigeonCodec.shared }
/// Sets up an instance of `BackgroundWorkerFgHostApi` to handle messages through the `binaryMessenger`.
static func setUp(binaryMessenger: FlutterBinaryMessenger, api: BackgroundWorkerFgHostApi?, messageChannelSuffix: String = "") {
let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
let enableSyncWorkerChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableSyncWorker\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
enableSyncWorkerChannel.setMessageHandler { _, reply in
do {
try api.enableSyncWorker()
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
}
}
} else {
enableSyncWorkerChannel.setMessageHandler(nil)
}
let enableUploadWorkerChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableUploadWorker\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
enableUploadWorkerChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let callbackHandleArg = args[0] as! Int64
do {
try api.enableUploadWorker(callbackHandle: callbackHandleArg)
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
}
}
} else {
enableUploadWorkerChannel.setMessageHandler(nil)
}
let disableUploadWorkerChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disableUploadWorker\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
disableUploadWorkerChannel.setMessageHandler { _, reply in
do {
try api.disableUploadWorker()
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
}
}
} else {
disableUploadWorkerChannel.setMessageHandler(nil)
}
}
}
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol BackgroundWorkerBgHostApi {
func onInitialized() throws
}
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
class BackgroundWorkerBgHostApiSetup {
static var codec: FlutterStandardMessageCodec { BackgroundWorkerPigeonCodec.shared }
/// Sets up an instance of `BackgroundWorkerBgHostApi` to handle messages through the `binaryMessenger`.
static func setUp(binaryMessenger: FlutterBinaryMessenger, api: BackgroundWorkerBgHostApi?, messageChannelSuffix: String = "") {
let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
let onInitializedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.onInitialized\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
onInitializedChannel.setMessageHandler { _, reply in
do {
try api.onInitialized()
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
}
}
} else {
onInitializedChannel.setMessageHandler(nil)
}
}
}
/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.
protocol BackgroundWorkerFlutterApiProtocol {
func onLocalSync(maxSeconds maxSecondsArg: Int64?, completion: @escaping (Result<Void, PigeonError>) -> Void)
func onIosUpload(isRefresh isRefreshArg: Bool, maxSeconds maxSecondsArg: Int64?, completion: @escaping (Result<Void, PigeonError>) -> Void)
func onAndroidUpload(completion: @escaping (Result<Void, PigeonError>) -> Void)
func cancel(completion: @escaping (Result<Void, PigeonError>) -> Void)
}
class BackgroundWorkerFlutterApi: BackgroundWorkerFlutterApiProtocol {
private let binaryMessenger: FlutterBinaryMessenger
private let messageChannelSuffix: String
init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") {
self.binaryMessenger = binaryMessenger
self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
}
var codec: BackgroundWorkerPigeonCodec {
return BackgroundWorkerPigeonCodec.shared
}
func onLocalSync(maxSeconds maxSecondsArg: Int64?, completion: @escaping (Result<Void, PigeonError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onLocalSync\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([maxSecondsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName: channelName)))
return
}
if listResponse.count > 1 {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(()))
}
}
}
func onIosUpload(isRefresh isRefreshArg: Bool, maxSeconds maxSecondsArg: Int64?, completion: @escaping (Result<Void, PigeonError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([isRefreshArg, maxSecondsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName: channelName)))
return
}
if listResponse.count > 1 {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(()))
}
}
}
func onAndroidUpload(completion: @escaping (Result<Void, PigeonError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onAndroidUpload\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage(nil) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName: channelName)))
return
}
if listResponse.count > 1 {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(()))
}
}
}
func cancel(completion: @escaping (Result<Void, PigeonError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.cancel\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage(nil) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName: channelName)))
return
}
if listResponse.count > 1 {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(()))
}
}
}
}

View File

@@ -0,0 +1,202 @@
import BackgroundTasks
import Flutter
enum BackgroundTaskType { case localSync, refreshUpload, processingUpload }
/*
* DEBUG: Testing Background Tasks in Xcode
*
* To test background task functionality during development:
* 1. Pause the application in Xcode debugger
* 2. In the debugger console, enter one of the following commands:
## For local sync (short-running sync):
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"app.alextran.immich.background.localSync"]
## For background refresh (short-running sync):
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"app.alextran.immich.background.refreshUpload"]
## For background processing (long-running upload):
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"app.alextran.immich.background.processingUpload"]
* To simulate task expiration (useful for testing expiration handlers):
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"app.alextran.immich.background.localSync"]
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"app.alextran.immich.background.refreshUpload"]
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"app.alextran.immich.background.processingUpload"]
* 3. Resume the application to see the background code execute
*
* NOTE: This must be tested on a physical device, not in the simulator.
* In testing, only the background processing task can be reliably simulated.
* These commands submit the respective task to BGTaskScheduler for immediate processing.
* Use the expiration commands to test how the app handles iOS terminating background tasks.
*/
/// The background worker which creates a new Flutter VM, communicates with it
/// to run the backup job, and then finishes execution and calls back to its callback handler.
/// This class manages a separate Flutter engine instance for background execution,
/// independent of the main UI Flutter engine.
class BackgroundWorker: BackgroundWorkerBgHostApi {
private let taskType: BackgroundTaskType
/// The maximum number of seconds to run the task before timing out
private let maxSeconds: Int?
/// Callback function to invoke when the background task completes
private let completionHandler: (_ success: Bool) -> Void
/// The Flutter engine created specifically for background execution.
/// This is a separate instance from the main Flutter engine that handles the UI.
/// It operates in its own isolate and doesn't share memory with the main engine.
/// Must be properly started, registered, and torn down during background execution.
private let engine = FlutterEngine(name: "BackgroundImmich")
/// Used to call methods on the flutter side
private var flutterApi: BackgroundWorkerFlutterApi?
/// Flag to track whether the background task has completed to prevent duplicate completions
private var isComplete = false
/**
* Initializes a new background worker with the specified task type and execution constraints.
* Creates a new Flutter engine instance for background execution and sets up the necessary
* communication channels between native iOS and Flutter code.
*
* - Parameters:
* - taskType: The type of background task to execute (upload or sync task)
* - maxSeconds: Optional maximum execution time in seconds before the task is cancelled
* - completionHandler: Callback function invoked when the task completes, with success status
*/
init(taskType: BackgroundTaskType, maxSeconds: Int?, completionHandler: @escaping (_ success: Bool) -> Void) {
self.taskType = taskType
self.maxSeconds = maxSeconds
self.completionHandler = completionHandler
// Should be initialized only after the engine starts running
self.flutterApi = nil
}
/**
* Starts the background Flutter engine and begins execution of the background task.
* Retrieves the callback handle from UserDefaults, looks up the Flutter callback,
* starts the engine, and sets up a timeout timer if specified.
*/
func run() {
// Retrieve the callback handle stored by the main Flutter app
// This handle points to the Flutter function that should be executed in the background
let callbackHandle = Int64(UserDefaults.standard.string(
forKey: BackgroundWorkerApiImpl.backgroundUploadCallbackHandleKey) ?? "0") ?? 0
if callbackHandle == 0 {
// Without a valid callback handle, we cannot start the Flutter background execution
complete(success: false)
return
}
// Use the callback handle to retrieve the actual Flutter callback information
guard let callback = FlutterCallbackCache.lookupCallbackInformation(callbackHandle) else {
// The callback handle is invalid or the callback was not found
complete(success: false)
return
}
// Start the Flutter engine with the specified callback as the entry point
let isRunning = engine.run(
withEntrypoint: callback.callbackName,
libraryURI: callback.callbackLibraryPath
)
// Verify that the Flutter engine started successfully
if !isRunning {
complete(success: false)
return
}
// Register plugins in the new engine
GeneratedPluginRegistrant.register(with: engine)
// Register custom plugins
AppDelegate.registerPlugins(binaryMessenger: engine.binaryMessenger)
flutterApi = BackgroundWorkerFlutterApi(binaryMessenger: engine.binaryMessenger)
BackgroundWorkerBgHostApiSetup.setUp(binaryMessenger: engine.binaryMessenger, api: self)
// Set up a timeout timer if maxSeconds was specified to prevent runaway background tasks
if maxSeconds != nil {
// Schedule a timer to cancel the task after the specified timeout period
Timer.scheduledTimer(withTimeInterval: TimeInterval(maxSeconds!), repeats: false) { _ in
self.cancel()
}
}
}
/**
* Called by the Flutter side when it has finished initialization and is ready to receive commands.
* Routes the appropriate task type (refresh or processing) to the corresponding Flutter method.
* This method acts as a bridge between the native iOS background task system and Flutter.
*/
func onInitialized() throws {
switch self.taskType {
case .refreshUpload, .processingUpload:
flutterApi?.onIosUpload(isRefresh: self.taskType == .refreshUpload,
maxSeconds: maxSeconds.map { Int64($0) }, completion: { result in
self.handleHostResult(result: result)
})
case .localSync:
flutterApi?.onLocalSync(maxSeconds: maxSeconds.map { Int64($0) }, completion: { result in
self.handleHostResult(result: result)
})
}
}
/**
* Cancels the currently running background task, either due to timeout or external request.
* Sends a cancel signal to the Flutter side and sets up a fallback timer to ensure
* the completion handler is eventually called even if Flutter doesn't respond.
*/
func cancel() {
if isComplete {
return
}
isComplete = true
flutterApi?.cancel { result in
self.complete(success: false)
}
// Fallback safety mechanism: ensure completion is called within 2 seconds
// This prevents the background task from hanging indefinitely if Flutter doesn't respond
Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in
self.complete(success: false)
}
}
/**
* Handles the result from Flutter API calls and determines the success/failure status.
* Converts Flutter's Result type to a simple boolean success indicator for task completion.
*
* - Parameter result: The result returned from a Flutter API call
*/
private func handleHostResult(result: Result<Void, PigeonError>) {
switch result {
case .success(): self.complete(success: true)
case .failure(_): self.cancel()
}
}
/**
* Cleans up resources by destroying the Flutter engine context and invokes the completion handler.
* This method ensures that the background task is marked as complete, releases the Flutter engine,
* and notifies the caller of the task's success or failure. This is the final step in the
* background task lifecycle and should only be called once per task instance.
*
* - Parameter success: Indicates whether the background task completed successfully
*/
private func complete(success: Bool) {
isComplete = true
engine.destroyContext()
completionHandler(success)
}
}

View File

@@ -0,0 +1,155 @@
import BackgroundTasks
class BackgroundWorkerApiImpl: BackgroundWorkerFgHostApi {
func enableSyncWorker() throws {
BackgroundWorkerApiImpl.scheduleLocalSync()
print("BackgroundUploadImpl:enableSyncWorker Local Sync worker scheduled")
}
func enableUploadWorker(callbackHandle: Int64) throws {
BackgroundWorkerApiImpl.updateUploadEnabled(true)
// Store the callback handle for later use when starting background Flutter isolates
BackgroundWorkerApiImpl.updateUploadCallbackHandle(callbackHandle)
BackgroundWorkerApiImpl.scheduleRefreshUpload()
BackgroundWorkerApiImpl.scheduleProcessingUpload()
print("BackgroundUploadImpl:enableUploadWorker Scheduled background upload tasks")
}
func disableUploadWorker() throws {
BackgroundWorkerApiImpl.updateUploadEnabled(false)
BackgroundWorkerApiImpl.cancelUploadTasks()
print("BackgroundUploadImpl:disableUploadWorker Disabled background upload tasks")
}
public static let backgroundUploadEnabledKey = "immich:background:backup:enabled"
public static let backgroundUploadCallbackHandleKey = "immich:background:backup:callbackHandle"
private static let localSyncTaskID = "app.alextran.immich.background.localSync"
private static let refreshUploadTaskID = "app.alextran.immich.background.refreshUpload"
private static let processingUploadTaskID = "app.alextran.immich.background.processingUpload"
private static func updateUploadEnabled(_ isEnabled: Bool) {
return UserDefaults.standard.set(isEnabled, forKey: BackgroundWorkerApiImpl.backgroundUploadEnabledKey)
}
private static func updateUploadCallbackHandle(_ callbackHandle: Int64) {
return UserDefaults.standard.set(String(callbackHandle), forKey: BackgroundWorkerApiImpl.backgroundUploadCallbackHandleKey)
}
private static func cancelUploadTasks() {
BackgroundWorkerApiImpl.updateUploadEnabled(false)
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: refreshUploadTaskID);
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: processingUploadTaskID);
}
public static func registerBackgroundProcessing() {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: processingUploadTaskID, using: nil) { task in
if task is BGProcessingTask {
handleBackgroundProcessing(task: task as! BGProcessingTask)
}
}
BGTaskScheduler.shared.register(
forTaskWithIdentifier: refreshUploadTaskID, using: nil) { task in
if task is BGAppRefreshTask {
handleBackgroundRefresh(task: task as! BGAppRefreshTask, taskType: .refreshUpload)
}
}
BGTaskScheduler.shared.register(
forTaskWithIdentifier: localSyncTaskID, using: nil) { task in
if task is BGAppRefreshTask {
handleBackgroundRefresh(task: task as! BGAppRefreshTask, taskType: .localSync)
}
}
}
private static func scheduleLocalSync() {
let backgroundRefresh = BGAppRefreshTaskRequest(identifier: localSyncTaskID)
backgroundRefresh.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60) // 5 mins
do {
try BGTaskScheduler.shared.submit(backgroundRefresh)
} catch {
print("Could not schedule the local sync task \(error.localizedDescription)")
}
}
private static func scheduleRefreshUpload() {
let backgroundRefresh = BGAppRefreshTaskRequest(identifier: refreshUploadTaskID)
backgroundRefresh.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60) // 5 mins
do {
try BGTaskScheduler.shared.submit(backgroundRefresh)
} catch {
print("Could not schedule the refresh upload task \(error.localizedDescription)")
}
}
private static func scheduleProcessingUpload() {
let backgroundProcessing = BGProcessingTaskRequest(identifier: processingUploadTaskID)
backgroundProcessing.requiresNetworkConnectivity = true
backgroundProcessing.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 15 mins
do {
try BGTaskScheduler.shared.submit(backgroundProcessing)
} catch {
print("Could not schedule the processing upload task \(error.localizedDescription)")
}
}
private static func handleBackgroundRefresh(task: BGAppRefreshTask, taskType: BackgroundTaskType) {
scheduleRefreshUpload()
// Restrict the refresh task to run only for a maximum of 20 seconds
runBackgroundWorker(task: task, taskType: taskType, maxSeconds: 20)
}
private static func handleBackgroundProcessing(task: BGProcessingTask) {
scheduleProcessingUpload()
// There are no restrictions for processing tasks. Although, the OS could signal expiration at any time
runBackgroundWorker(task: task, taskType: .processingUpload, maxSeconds: nil)
}
/**
* Executes the background worker within the context of a background task.
* This method creates a BackgroundWorker, sets up task expiration handling,
* and manages the synchronization between the background task and the Flutter engine.
*
* - Parameters:
* - task: The iOS background task that provides the execution context
* - taskType: The type of background operation to perform (refresh or processing)
* - maxSeconds: Optional timeout for the operation in seconds
*/
private static func runBackgroundWorker(task: BGTask, taskType: BackgroundTaskType, maxSeconds: Int?) {
let semaphore = DispatchSemaphore(value: 0)
var isSuccess = true
let backgroundWorker = BackgroundWorker(taskType: taskType, maxSeconds: maxSeconds) { success in
isSuccess = success
semaphore.signal()
}
task.expirationHandler = {
DispatchQueue.main.async {
backgroundWorker.cancel()
}
isSuccess = false
// Schedule a timer to signal the semaphore after 2 seconds
Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in
semaphore.signal()
}
}
DispatchQueue.main.async {
backgroundWorker.run()
}
semaphore.wait()
task.setTaskCompleted(success: isSuccess)
print("Background task completed with success: \(isSuccess)")
}
}

View File

@@ -6,6 +6,9 @@
<string>$(CUSTOM_GROUP_ID)</string>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>app.alextran.immich.background.localSync</string>
<string>app.alextran.immich.background.refreshUpload</string>
<string>app.alextran.immich.background.processingUpload</string>
<string>app.alextran.immich.backgroundFetch</string>
<string>app.alextran.immich.backgroundProcessing</string>
</array>
@@ -78,7 +81,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.138.1</string>
<string>1.140.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@@ -105,7 +108,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>215</string>
<string>219</string>
<key>FLTEnableImpeller</key>
<true />
<key>ITSAppUsesNonExemptEncryption</key>
@@ -134,6 +137,9 @@
<string>We need to access the camera to let you take beautiful video using this app</string>
<key>NSFaceIDUsageDescription</key>
<string>We need to use FaceID to allow access to your locked folder</string>
<key>NSLocalNetworkUsageDescription</key>
<string>We need local network permission to connect to the local server using IP address and
allow the casting feature to work</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We require this permission to access the local WiFi name for background upload mechanism</string>
<key>NSLocationUsageDescription</key>
@@ -180,8 +186,5 @@
<true />
<key>io.flutter.embedded_views_preview</key>
<true />
<key>NSLocalNetworkUsageDescription</key>
<string>We need local network permission to connect to the local server using IP address and
allow the casting feature to work</string>
</dict>
</plist>
</plist>

View File

@@ -22,7 +22,7 @@ platform :ios do
path: "./Runner.xcodeproj",
)
increment_version_number(
version_number: "1.139.2"
version_number: "1.140.1"
)
increment_build_number(
build_number: latest_testflight_build_number + 1,

View File

@@ -67,6 +67,9 @@ enum StoreKey<T> {
loadOriginalVideo<bool>._(136),
manageLocalMediaAndroid<bool>._(137),
// Read-only Mode settings
readonlyModeEnabled<bool>._(138),
// Experimental stuff
photoManagerCustomFilter<bool>._(1000),
betaPromptShown<bool>._(1001),

View File

@@ -75,6 +75,8 @@ profileChangedAt: $profileChangedAt
bool? isPartnerSharedWith,
bool? hasProfileImage,
DateTime? profileChangedAt,
int? quotaSizeInBytes,
int? quotaUsageInBytes,
}) => UserDto(
id: id ?? this.id,
email: email ?? this.email,
@@ -88,6 +90,8 @@ profileChangedAt: $profileChangedAt
isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith,
hasProfileImage: hasProfileImage ?? this.hasProfileImage,
profileChangedAt: profileChangedAt ?? this.profileChangedAt,
quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes,
quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes,
);
@override
@@ -105,7 +109,9 @@ profileChangedAt: $profileChangedAt
other.memoryEnabled == memoryEnabled &&
other.inTimeline == inTimeline &&
other.hasProfileImage == hasProfileImage &&
other.profileChangedAt.isAtSameMomentAs(profileChangedAt);
other.profileChangedAt.isAtSameMomentAs(profileChangedAt) &&
other.quotaSizeInBytes == quotaSizeInBytes &&
other.quotaUsageInBytes == quotaUsageInBytes;
}
@override
@@ -121,7 +127,9 @@ profileChangedAt: $profileChangedAt
isPartnerSharedBy.hashCode ^
isPartnerSharedWith.hashCode ^
hasProfileImage.hashCode ^
profileChangedAt.hashCode;
profileChangedAt.hashCode ^
quotaSizeInBytes.hashCode ^
quotaUsageInBytes.hashCode;
}
class PartnerUserDto {

View File

@@ -74,7 +74,6 @@ isOnboarded: $isOnboarded,
int get hashCode => isOnboarded.hashCode;
}
// TODO: wait to be overwritten
class Preferences {
final bool foldersEnabled;
final bool memoriesEnabled;
@@ -133,17 +132,17 @@ class Preferences {
factory Preferences.fromMap(Map<String, Object?> map) {
return Preferences(
foldersEnabled: map["folders-Enabled"] as bool? ?? false,
memoriesEnabled: map["memories-Enabled"] as bool? ?? true,
peopleEnabled: map["people-Enabled"] as bool? ?? true,
ratingsEnabled: map["ratings-Enabled"] as bool? ?? false,
sharedLinksEnabled: map["sharedLinks-Enabled"] as bool? ?? true,
tagsEnabled: map["tags-Enabled"] as bool? ?? false,
foldersEnabled: (map["folders"] as Map<String, Object?>?)?["enabled"] as bool? ?? false,
memoriesEnabled: (map["memories"] as Map<String, Object?>?)?["enabled"] as bool? ?? true,
peopleEnabled: (map["people"] as Map<String, Object?>?)?["enabled"] as bool? ?? true,
ratingsEnabled: (map["ratings"] as Map<String, Object?>?)?["enabled"] as bool? ?? false,
sharedLinksEnabled: (map["sharedLinks"] as Map<String, Object?>?)?["enabled"] as bool? ?? true,
tagsEnabled: (map["tags"] as Map<String, Object?>?)?["enabled"] as bool? ?? false,
userAvatarColor: AvatarColor.values.firstWhere(
(e) => e.value == map["avatar-Color"] as String?,
(e) => e.value == (map["avatar"] as Map<String, Object?>?)?["color"] as String?,
orElse: () => AvatarColor.primary,
),
showSupportBadge: map["purchase-ShowSupportBadge"] as bool? ?? true,
showSupportBadge: (map["purchase"] as Map<String, Object?>?)?["showSupportBadge"] as bool? ?? true,
);
}
@@ -213,7 +212,7 @@ class License {
factory License.fromMap(Map<String, Object?> map) {
return License(
activatedAt: map["activatedAt"] as DateTime,
activatedAt: DateTime.parse(map["activatedAt"] as String),
activationKey: map["activationKey"] as String,
licenseKey: map["licenseKey"] as String,
);

View File

@@ -17,9 +17,14 @@ class AssetService {
_localAssetRepository = localAssetRepository,
_platform = const LocalPlatform();
Future<BaseAsset?> getAsset(BaseAsset asset) {
final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).id;
return asset is LocalAsset ? _localAssetRepository.get(id) : _remoteAssetRepository.get(id);
}
Stream<BaseAsset?> watchAsset(BaseAsset asset) {
final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).id;
return asset is LocalAsset ? _localAssetRepository.watchAsset(id) : _remoteAssetRepository.watchAsset(id);
return asset is LocalAsset ? _localAssetRepository.watch(id) : _remoteAssetRepository.watch(id);
}
Future<RemoteAsset?> getRemoteAsset(String id) {

View File

@@ -0,0 +1,232 @@
import 'dart:async';
import 'dart:ui';
import 'package:background_downloader/background_downloader.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
import 'package:immich_mobile/platform/background_worker_api.g.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/services/auth.service.dart';
import 'package:immich_mobile/services/localization.service.dart';
import 'package:immich_mobile/services/upload.service.dart';
import 'package:immich_mobile/utils/bootstrap.dart';
import 'package:immich_mobile/utils/http_ssl_options.dart';
import 'package:isar/isar.dart';
import 'package:logging/logging.dart';
class BackgroundWorkerFgService {
final BackgroundWorkerFgHostApi _foregroundHostApi;
const BackgroundWorkerFgService(this._foregroundHostApi);
// TODO: Move this call to native side once old timeline is removed
Future<void> enableSyncService() => _foregroundHostApi.enableSyncWorker();
Future<void> enableUploadService() => _foregroundHostApi.enableUploadWorker(
PluginUtilities.getCallbackHandle(_backgroundSyncNativeEntrypoint)!.toRawHandle(),
);
Future<void> disableUploadService() => _foregroundHostApi.disableUploadWorker();
}
class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
late final ProviderContainer _ref;
final Isar _isar;
final Drift _drift;
final DriftLogger _driftLogger;
final BackgroundWorkerBgHostApi _backgroundHostApi;
final Logger _logger = Logger('BackgroundUploadBgService');
bool _isCleanedUp = false;
BackgroundWorkerBgService({required Isar isar, required Drift drift, required DriftLogger driftLogger})
: _isar = isar,
_drift = drift,
_driftLogger = driftLogger,
_backgroundHostApi = BackgroundWorkerBgHostApi() {
_ref = ProviderContainer(
overrides: [
dbProvider.overrideWithValue(isar),
isarProvider.overrideWithValue(isar),
driftProvider.overrideWith(driftOverride(drift)),
],
);
BackgroundWorkerFlutterApi.setUp(this);
}
bool get _isBackupEnabled => _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
Future<void> init() async {
await loadTranslations();
HttpSSLOptions.apply(applyNative: false);
await _ref.read(authServiceProvider).setOpenApiServiceEndpoint();
// Initialize the file downloader
await FileDownloader().configure(
globalConfig: [
// maxConcurrent: 6, maxConcurrentByHost(server):6, maxConcurrentByGroup: 3
(Config.holdingQueue, (6, 6, 3)),
// On Android, if files are larger than 256MB, run in foreground service
(Config.runInForegroundIfFileLargerThan, 256),
],
);
await FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false);
await FileDownloader().trackTasks();
configureFileDownloaderNotifications();
// Notify the host that the background upload service has been initialized and is ready to use
await _backgroundHostApi.onInitialized();
}
@override
Future<void> onLocalSync(int? maxSeconds) async {
_logger.info('Local background syncing started');
final sw = Stopwatch()..start();
final timeout = maxSeconds != null ? Duration(seconds: maxSeconds) : null;
await _syncAssets(hashTimeout: timeout, syncRemote: false);
sw.stop();
_logger.info("Local sync completed in ${sw.elapsed.inSeconds}s");
}
/* We do the following on Android upload
* - Sync local assets
* - Hash local assets 3 / 6 minutes
* - Sync remote assets
* - Check and requeue upload tasks
*/
@override
Future<void> onAndroidUpload() async {
_logger.info('Android background processing started');
final sw = Stopwatch()..start();
await _syncAssets(hashTimeout: Duration(minutes: _isBackupEnabled ? 3 : 6));
await _handleBackup(processBulk: false);
await _cleanup();
sw.stop();
_logger.info("Android background processing completed in ${sw.elapsed.inSeconds}s");
}
/* We do the following on background upload
* - Sync local assets
* - Hash local assets
* - Sync remote assets
* - Check and requeue upload tasks
*
* The native side will not send the maxSeconds value for processing tasks
*/
@override
Future<void> onIosUpload(bool isRefresh, int? maxSeconds) async {
_logger.info('iOS background upload started with maxSeconds: ${maxSeconds}s');
final sw = Stopwatch()..start();
final timeout = isRefresh ? const Duration(seconds: 5) : Duration(minutes: _isBackupEnabled ? 3 : 6);
await _syncAssets(hashTimeout: timeout);
final backupFuture = _handleBackup();
if (maxSeconds != null) {
await backupFuture.timeout(Duration(seconds: maxSeconds - 1), onTimeout: () {});
} else {
await backupFuture;
}
await _cleanup();
sw.stop();
_logger.info("iOS background upload completed in ${sw.elapsed.inSeconds}s");
}
@override
Future<void> cancel() async {
_logger.warning("Background upload cancelled");
await _cleanup();
}
Future<void> _cleanup() async {
if (_isCleanedUp) {
return;
}
_isCleanedUp = true;
await _ref.read(backgroundSyncProvider).cancel();
await _ref.read(backgroundSyncProvider).cancelLocal();
await _isar.close();
await _drift.close();
await _driftLogger.close();
_ref.dispose();
}
Future<void> _handleBackup({bool processBulk = true}) async {
if (!_isBackupEnabled) {
return;
}
final currentUser = _ref.read(currentUserProvider);
if (currentUser == null) {
return;
}
if (processBulk) {
return _ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id);
}
final activeTask = await _ref.read(uploadServiceProvider).getActiveTasks(currentUser.id);
if (activeTask.isNotEmpty) {
await _ref.read(uploadServiceProvider).resumeBackup();
} else {
await _ref.read(uploadServiceProvider).startBackupSerial(currentUser.id);
}
}
Future<void> _syncAssets({Duration? hashTimeout, bool syncRemote = true}) async {
final futures = <Future<void>>[];
final localSyncFuture = _ref.read(backgroundSyncProvider).syncLocal().then((_) async {
if (_isCleanedUp) {
return;
}
var hashFuture = _ref.read(backgroundSyncProvider).hashAssets();
if (hashTimeout != null) {
hashFuture = hashFuture.timeout(
hashTimeout,
onTimeout: () {
// Consume cancellation errors as we want to continue processing
},
);
}
return hashFuture;
});
futures.add(localSyncFuture);
if (syncRemote) {
final remoteSyncFuture = _ref.read(backgroundSyncProvider).syncRemote();
futures.add(remoteSyncFuture);
}
await Future.wait(futures);
}
}
@pragma('vm:entry-point')
Future<void> _backgroundSyncNativeEntrypoint() async {
WidgetsFlutterBinding.ensureInitialized();
DartPluginRegistrant.ensureInitialized();
final (isar, drift, logDB) = await Bootstrap.initDB();
await Bootstrap.initDomain(isar, drift, logDB, shouldBufferLogs: false);
await BackgroundWorkerBgService(isar: isar, drift: drift, driftLogger: logDB).init();
}

View File

@@ -15,6 +15,7 @@ class HashService {
final DriftLocalAssetRepository _localAssetRepository;
final StorageRepository _storageRepository;
final NativeSyncApi _nativeSyncApi;
final bool Function()? _cancelChecker;
final _log = Logger('HashService');
HashService({
@@ -22,13 +23,17 @@ class HashService {
required DriftLocalAssetRepository localAssetRepository,
required StorageRepository storageRepository,
required NativeSyncApi nativeSyncApi,
bool Function()? cancelChecker,
this.batchSizeLimit = kBatchHashSizeLimit,
this.batchFileLimit = kBatchHashFileLimit,
}) : _localAlbumRepository = localAlbumRepository,
_localAssetRepository = localAssetRepository,
_storageRepository = storageRepository,
_cancelChecker = cancelChecker,
_nativeSyncApi = nativeSyncApi;
bool get isCancelled => _cancelChecker?.call() ?? false;
Future<void> hashAssets() async {
final Stopwatch stopwatch = Stopwatch()..start();
// Sorted by backupSelection followed by isCloud
@@ -37,6 +42,11 @@ class HashService {
);
for (final album in localAlbums) {
if (isCancelled) {
_log.warning("Hashing cancelled. Stopped processing albums.");
break;
}
final assetsToHash = await _localAlbumRepository.getAssetsToHash(album.id);
if (assetsToHash.isNotEmpty) {
await _hashAssets(assetsToHash);
@@ -55,6 +65,11 @@ class HashService {
final toHash = <_AssetToPath>[];
for (final asset in assetsToHash) {
if (isCancelled) {
_log.warning("Hashing cancelled. Stopped processing assets.");
return;
}
final file = await _storageRepository.getFileForAsset(asset.id);
if (file == null) {
continue;
@@ -89,6 +104,11 @@ class HashService {
);
for (int i = 0; i < hashes.length; i++) {
if (isCancelled) {
_log.warning("Hashing cancelled. Stopped processing batch.");
return;
}
final hash = hashes[i];
final asset = toHash[i].asset;
if (hash?.length == 20) {

View File

@@ -123,6 +123,11 @@ class LogService {
_flushTimer = null;
final buffer = [..._msgBuffer];
_msgBuffer.clear();
if (buffer.isEmpty) {
return;
}
await _logRepository.insertAll(buffer);
}
}

View File

@@ -59,6 +59,28 @@ class BackgroundSyncManager {
}
}
Future<void> cancelLocal() async {
final futures = <Future>[];
if (_hashTask != null) {
futures.add(_hashTask!.future);
}
_hashTask?.cancel();
_hashTask = null;
if (_deviceAlbumSyncTask != null) {
futures.add(_deviceAlbumSyncTask!.future);
}
_deviceAlbumSyncTask?.cancel();
_deviceAlbumSyncTask = null;
try {
await Future.wait(futures);
} on CanceledError {
// Ignore cancellation errors
}
}
// No need to cancel the task, as it can also be run when the user logs out
Future<void> syncLocal({bool full = false}) {
if (_deviceAlbumSyncTask != null) {

View File

@@ -54,6 +54,8 @@ class User {
avatarColor: dto.avatarColor,
memoryEnabled: dto.memoryEnabled,
inTimeline: dto.inTimeline,
quotaUsageInBytes: dto.quotaUsageInBytes,
quotaSizeInBytes: dto.quotaSizeInBytes,
);
UserDto toDto() => UserDto(

View File

@@ -1,15 +1,16 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:ui' as ui;
import 'package:cronet_http/cronet_http.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:ffi/ffi.dart';
import 'package:http/http.dart' as http;
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/providers/image/cache/remote_image_cache_manager.dart';
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
import 'package:logging/logging.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
part 'local_image_request.dart';
part 'thumbhash_image_request.dart';

View File

@@ -1,14 +1,18 @@
part of 'image_request.dart';
class RemoteImageRequest extends ImageRequest {
static final log = Logger('RemoteImageRequest');
static final client = HttpClient()..maxConnectionsPerHost = 16;
final RemoteCacheManager? cacheManager;
static final _client = const NetworkRepository().getHttpClient(
'thumbnails',
diskCapacity: kThumbnailDiskCacheSize,
memoryCapacity: 0,
maxConnections: 16,
cacheMode: CacheMode.disk,
);
final String uri;
final Map<String, String> headers;
HttpClientRequest? _request;
final abortTrigger = Completer<void>();
RemoteImageRequest({required this.uri, required this.headers, this.cacheManager});
RemoteImageRequest({required this.uri, required this.headers});
@override
Future<ImageInfo?> load(ImageDecoderCallback decode, {double scale = 1.0}) async {
@@ -16,15 +20,8 @@ class RemoteImageRequest extends ImageRequest {
return null;
}
// TODO: the cache manager makes everything sequential with its DB calls and its operations cannot be cancelled,
// so it ends up being a bottleneck. We only prefer fetching from it when it can skip the DB call.
final cachedFileImage = await _loadCachedFile(uri, decode, scale, inMemoryOnly: true);
if (cachedFileImage != null) {
return cachedFileImage;
}
try {
final buffer = await _downloadImage(uri);
final buffer = await _downloadImage();
if (buffer == null) {
return null;
}
@@ -35,82 +32,75 @@ class RemoteImageRequest extends ImageRequest {
return null;
}
final cachedFileImage = await _loadCachedFile(uri, decode, scale, inMemoryOnly: false);
if (cachedFileImage != null) {
return cachedFileImage;
}
rethrow;
} finally {
_request = null;
}
}
Future<ImmutableBuffer?> _downloadImage(String url) async {
Future<ImmutableBuffer?> _downloadImage() async {
if (_isCancelled) {
return null;
}
final request = _request = await client.getUrl(Uri.parse(url));
if (_isCancelled) {
request.abort();
return _request = null;
}
for (final entry in headers.entries) {
request.headers.set(entry.key, entry.value);
}
final response = await request.close();
final req = http.AbortableRequest('GET', Uri.parse(uri), abortTrigger: abortTrigger.future);
req.headers.addAll(headers);
final res = await _client.send(req);
if (_isCancelled) {
_onCancelled();
return null;
}
final bytes = Uint8List(response.contentLength);
int offset = 0;
final subscription = response.listen((List<int> chunk) {
// this is important to break the response stream if the request is cancelled
if (res.statusCode != 200) {
throw Exception('Failed to download $uri: ${res.statusCode}');
}
final stream = res.stream.map((chunk) {
if (_isCancelled) {
throw StateError('Cancelled request');
}
bytes.setAll(offset, chunk);
offset += chunk.length;
}, cancelOnError: true);
cacheManager?.putStreamedFile(url, response);
await subscription.asFuture();
return await ImmutableBuffer.fromUint8List(bytes);
}
Future<ImageInfo?> _loadCachedFile(
String url,
ImageDecoderCallback decode,
double scale, {
required bool inMemoryOnly,
}) async {
final cacheManager = this.cacheManager;
if (_isCancelled || cacheManager == null) {
return null;
}
final file = await (inMemoryOnly ? cacheManager.getFileFromMemory(url) : cacheManager.getFileFromCache(url));
if (_isCancelled || file == null) {
return null;
}
return chunk;
});
try {
final buffer = await ImmutableBuffer.fromFilePath(file.file.path);
return await _decodeBuffer(buffer, decode, scale);
final Uint8List bytes = await _downloadBytes(stream, res.contentLength ?? -1);
if (_isCancelled) {
return null;
}
return await ImmutableBuffer.fromUint8List(bytes);
} catch (e) {
log.severe('Failed to decode cached image', e);
_evictFile(url);
return null;
if (_isCancelled) {
return null;
}
rethrow;
}
}
Future<void> _evictFile(String url) async {
try {
await cacheManager?.removeFile(url);
} catch (e) {
log.severe('Failed to remove cached image', e);
Future<Uint8List> _downloadBytes(Stream<List<int>> stream, int length) async {
final Uint8List bytes;
int offset = 0;
if (length > 0) {
// Known content length - use pre-allocated buffer
bytes = Uint8List(length);
await stream.listen((chunk) {
bytes.setAll(offset, chunk);
offset += chunk.length;
}, cancelOnError: true).asFuture();
} else {
// Unknown content length - collect chunks dynamically
final chunks = <List<int>>[];
int totalLength = 0;
await stream.listen((chunk) {
chunks.add(chunk);
totalLength += chunk.length;
}, cancelOnError: true).asFuture();
bytes = Uint8List(totalLength);
for (final chunk in chunks) {
bytes.setAll(offset, chunk);
offset += chunk.length;
}
}
return bytes;
}
Future<ImageInfo?> _decodeBuffer(ImmutableBuffer buffer, ImageDecoderCallback decode, scale) async {
@@ -130,7 +120,6 @@ class RemoteImageRequest extends ImageRequest {
@override
void _onCancelled() {
_request?.abort();
_request = null;
abortTrigger.complete();
}
}

View File

@@ -9,7 +9,7 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftLocalAssetRepository(this._db) : super(_db);
Stream<LocalAsset?> watchAsset(String id) {
SingleOrNullSelectable<LocalAsset?> _assetSelectable(String id) {
final query = _db.localAssetEntity.select().addColumns([_db.remoteAssetEntity.id]).join([
leftOuterJoin(
_db.remoteAssetEntity,
@@ -21,9 +21,13 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
return query.map((row) {
final asset = row.readTable(_db.localAssetEntity).toDto();
return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id));
}).watchSingleOrNull();
});
}
Future<LocalAsset?> get(String id) => _assetSelectable(id).getSingleOrNull();
Stream<LocalAsset?> watch(String id) => _assetSelectable(id).watchSingleOrNull();
Future<void> updateHashes(Iterable<LocalAsset> hashes) {
if (hashes.isEmpty) {
return Future.value();

View File

@@ -15,8 +15,8 @@ class DriftMemoryRepository extends DriftDatabaseRepository {
final query =
_db.select(_db.memoryEntity).join([
leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)),
leftOuterJoin(
innerJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)),
innerJoin(
_db.remoteAssetEntity,
_db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) &
_db.remoteAssetEntity.deletedAt.isNull() &
@@ -30,6 +30,9 @@ class DriftMemoryRepository extends DriftDatabaseRepository {
..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
final rows = await query.get();
if (rows.isEmpty) {
return const [];
}
final Map<String, DriftMemory> memoriesMap = {};
@@ -46,7 +49,7 @@ class DriftMemoryRepository extends DriftDatabaseRepository {
}
}
return memoriesMap.values.toList();
return memoriesMap.values.toList(growable: false);
}
Future<DriftMemory?> get(String memoryId) async {

View File

@@ -0,0 +1,65 @@
import 'dart:io';
import 'package:cronet_http/cronet_http.dart';
import 'package:cupertino_http/cupertino_http.dart';
import 'package:http/http.dart' as http;
import 'package:immich_mobile/utils/user_agent.dart';
import 'package:path_provider/path_provider.dart';
class NetworkRepository {
static late Directory _cachePath;
static late String _userAgent;
static final _clients = <String, http.Client>{};
static Future<void> init() {
return (
getTemporaryDirectory().then((cachePath) => _cachePath = cachePath),
getUserAgentString().then((userAgent) => _userAgent = userAgent),
).wait;
}
static void reset() {
Future.microtask(init);
for (final client in _clients.values) {
client.close();
}
_clients.clear();
}
const NetworkRepository();
http.Client getHttpClient(
String directoryName, {
int diskCapacity = 100 << 20,
int memoryCapacity = 10 << 20,
int maxConnections = 6,
CacheMode cacheMode = CacheMode.disk,
}) {
final cachedClient = _clients[directoryName];
if (cachedClient != null) {
return cachedClient;
}
final directory = Directory('${_cachePath.path}/$directoryName');
directory.createSync(recursive: true);
if (Platform.isAndroid) {
final engine = CronetEngine.build(
cacheMode: cacheMode,
cacheMaxSize: diskCapacity,
storagePath: directory.path,
userAgent: _userAgent,
);
return _clients[directoryName] = CronetClient.fromCronetEngine(engine, closeEngine: true);
}
final config = URLSessionConfiguration.defaultSessionConfiguration()
..httpMaximumConnectionsPerHost = maxConnections
..cache = URLCache.withCapacity(
diskCapacity: diskCapacity,
memoryCapacity: memoryCapacity,
directory: directory.uri,
)
..httpAdditionalHeaders = {'User-Agent': _userAgent};
return _clients[directoryName] = CupertinoClient.fromSessionConfiguration(config);
}
}

View File

@@ -17,7 +17,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
const DriftRemoteAlbumRepository(this._db) : super(_db);
Future<List<RemoteAlbum>> getAll({Set<SortRemoteAlbumsBy> sortBy = const {SortRemoteAlbumsBy.updatedAt}}) {
final assetCount = _db.remoteAlbumAssetEntity.assetId.count();
final assetCount = _db.remoteAlbumAssetEntity.assetId.count(distinct: true);
final query = _db.remoteAlbumEntity.select().join([
leftOuterJoin(
@@ -41,7 +41,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
..where(_db.remoteAssetEntity.deletedAt.isNull())
..addColumns([assetCount])
..addColumns([_db.userEntity.name])
..addColumns([_db.remoteAlbumUserEntity.userId.count()])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..groupBy([_db.remoteAlbumEntity.id]);
if (sortBy.isNotEmpty) {
@@ -62,14 +62,14 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
.toDto(
assetCount: row.read(assetCount) ?? 0,
ownerName: row.read(_db.userEntity.name)!,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count())! > 2,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 2,
),
)
.get();
}
Future<RemoteAlbum?> get(String albumId) {
final assetCount = _db.remoteAlbumAssetEntity.assetId.count();
final assetCount = _db.remoteAlbumAssetEntity.assetId.count(distinct: true);
final query =
_db.remoteAlbumEntity.select().join([
@@ -97,7 +97,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull())
..addColumns([assetCount])
..addColumns([_db.userEntity.name])
..addColumns([_db.remoteAlbumUserEntity.userId.count()])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..groupBy([_db.remoteAlbumEntity.id]);
return query
@@ -107,7 +107,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
.toDto(
assetCount: row.read(assetCount) ?? 0,
ownerName: row.read(_db.userEntity.name)!,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count())! > 2,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 2,
),
)
.getSingleOrNull();
@@ -282,7 +282,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
])
..where(_db.remoteAlbumEntity.id.equals(albumId))
..addColumns([_db.userEntity.name])
..addColumns([_db.remoteAlbumUserEntity.userId.count()])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..groupBy([_db.remoteAlbumEntity.id]);
return query.map((row) {
@@ -290,7 +290,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
.readTable(_db.remoteAlbumEntity)
.toDto(
ownerName: row.read(_db.userEntity.name)!,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count())! > 2,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 2,
);
return album;
}).watchSingleOrNull();

View File

@@ -55,24 +55,6 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
return _assetSelectable(id).getSingleOrNull();
}
Stream<RemoteAsset?> watchAsset(String id) {
final query =
_db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([
leftOuterJoin(
_db.localAssetEntity,
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
useColumns: false,
),
])
..where(_db.remoteAssetEntity.id.equals(id))
..limit(1);
return query.map((row) {
final asset = row.readTable(_db.remoteAssetEntity).toDto();
return asset.copyWith(localId: row.read(_db.localAssetEntity.id));
}).watchSingleOrNull();
}
Future<List<RemoteAsset>> getStackChildren(RemoteAsset asset) {
if (asset.stackId == null) {
return Future.value([]);

View File

@@ -4,11 +4,13 @@ import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/sync_event.model.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
class SyncApiRepository {
static final _client = const NetworkRepository().getHttpClient('api');
final Logger _logger = Logger('SyncApiRepository');
final ApiService _api;
SyncApiRepository(this._api);
@@ -20,10 +22,8 @@ class SyncApiRepository {
Future<void> streamChanges(
Function(List<SyncEvent>, Function() abort) onData, {
int batchSize = kSyncEventBatchSize,
http.Client? httpClient,
}) async {
final stopwatch = Stopwatch()..start();
final client = httpClient ?? http.Client();
final endpoint = "${_api.apiClient.basePath}/sync/stream";
final headers = {'Content-Type': 'application/json', 'Accept': 'application/jsonlines+json'};
@@ -70,7 +70,7 @@ class SyncApiRepository {
}
try {
final response = await client.send(request);
final response = await _client.send(request);
if (response.statusCode != 200) {
final errorBody = await response.stream.bytesToString();
@@ -101,8 +101,6 @@ class SyncApiRepository {
} catch (error, stack) {
_logger.severe("Error processing stream", error, stack);
return Future.error(error, stack);
} finally {
client.close();
}
stopwatch.stop();
_logger.info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms");

View File

@@ -1,9 +1,11 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/domain/models/user_metadata.model.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity;
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/user_metadata.repository.dart';
import 'package:isar/isar.dart';
class IsarUserRepository extends IsarDatabaseRepository {
@@ -70,8 +72,16 @@ class DriftUserRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftUserRepository(super.db) : _db = db;
Future<UserDto?> get(String id) =>
_db.managers.userEntity.filter((user) => user.id.equals(id)).getSingleOrNull().then((user) => user?.toDto());
Future<UserDto?> get(String id) async {
final user = await _db.managers.userEntity.filter((user) => user.id.equals(id)).getSingleOrNull();
if (user == null) return null;
final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(id));
final metadata = await query.map((row) => row.toDto()).get();
return user.toDto(metadata);
}
Future<UserDto> upsert(UserDto user) async {
await _db.userEntity.insertOnConflictUpdate(
@@ -87,10 +97,35 @@ class DriftUserRepository extends DriftDatabaseRepository {
);
return user;
}
Future<List<UserDto>> getAll() async {
final users = await _db.userEntity.select().get();
final List<UserDto> result = [];
for (final user in users) {
final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(user.id));
final metadata = await query.map((row) => row.toDto()).get();
result.add(user.toDto(metadata));
}
return result;
}
}
extension on UserEntityData {
UserDto toDto() {
UserDto toDto([List<UserMetadata>? metadata]) {
AvatarColor avatarColor = AvatarColor.primary;
bool memoryEnabled = true;
if (metadata != null) {
for (final meta in metadata) {
if (meta.key == UserMetadataKey.preferences && meta.preferences != null) {
avatarColor = meta.preferences?.userAvatarColor ?? AvatarColor.primary;
memoryEnabled = meta.preferences?.memoriesEnabled ?? true;
}
}
}
return UserDto(
id: id,
email: email,
@@ -99,6 +134,8 @@ extension on UserEntityData {
updatedAt: updatedAt,
profileChangedAt: profileChangedAt,
hasProfileImage: hasProfileImage,
avatarColor: avatarColor,
memoryEnabled: memoryEnabled,
);
}
}

View File

@@ -16,7 +16,7 @@ class DriftUserMetadataRepository extends DriftDatabaseRepository {
}
}
extension on UserMetadataEntityData {
extension UserMetadataDataExtension on UserMetadataEntityData {
UserMetadata toDto() => switch (key) {
UserMetadataKey.onboarding => UserMetadata(userId: userId, key: key, onboarding: Onboarding.fromMap(value)),
UserMetadataKey.preferences => UserMetadata(userId: userId, key: key, preferences: Preferences.fromMap(value)),

View File

@@ -29,6 +29,8 @@ abstract final class UserConverter {
isPartnerSharedWith: false,
profileChangedAt: adminDto.profileChangedAt,
hasProfileImage: adminDto.profileImagePath.isNotEmpty,
quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0,
quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0,
);
static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto(

View File

@@ -12,10 +12,14 @@ import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/constants/locales.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/codegen_loader.g.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/locale_provider.dart';
@@ -23,6 +27,7 @@ import 'package:immich_mobile/providers/routes.provider.dart';
import 'package:immich_mobile/providers/theme.provider.dart';
import 'package:immich_mobile/routing/app_navigation_observer.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/services/background.service.dart';
import 'package:immich_mobile/services/deep_link.service.dart';
import 'package:immich_mobile/services/local_notification.service.dart';
@@ -110,6 +115,8 @@ Future<void> initApp() async {
yield LicenseEntryWithLineBreaks([license.key], license.value);
}
});
await NetworkRepository.init();
}
class ImmichApp extends ConsumerStatefulWidget {
@@ -165,36 +172,6 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
await ref.read(localNotificationService).setup();
}
void _configureFileDownloaderNotifications() {
FileDownloader().configureNotificationForGroup(
kDownloadGroupImage,
running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'),
complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'),
progressBar: true,
);
FileDownloader().configureNotificationForGroup(
kDownloadGroupVideo,
running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'),
complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'),
progressBar: true,
);
FileDownloader().configureNotificationForGroup(
kManualUploadGroup,
running: TaskNotification('uploading_media'.tr(), '${'file_name'.tr()}: {displayName}'),
complete: TaskNotification('upload_finished'.tr(), '${'file_name'.tr()}: {displayName}'),
progressBar: true,
);
FileDownloader().configureNotificationForGroup(
kBackupGroup,
running: TaskNotification('uploading_media'.tr(), '${'file_name'.tr()}: {displayName}'),
complete: TaskNotification('upload_finished'.tr(), '${'file_name'.tr()}: {displayName}'),
progressBar: true,
);
}
Future<DeepLink> _deepLinkBuilder(PlatformDeepLink deepLink) async {
final deepLinkHandler = ref.read(deepLinkServiceProvider);
final currentRouteName = ref.read(currentRouteNameProvider.notifier).state;
@@ -202,13 +179,13 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
final isColdStart = currentRouteName == null || currentRouteName == SplashScreenRoute.name;
if (deepLink.uri.scheme == "immich") {
final proposedRoute = await deepLinkHandler.handleScheme(deepLink, isColdStart);
final proposedRoute = await deepLinkHandler.handleScheme(deepLink, ref, isColdStart);
return proposedRoute;
}
if (deepLink.uri.host == "my.immich.app") {
final proposedRoute = await deepLinkHandler.handleMyImmichApp(deepLink, isColdStart);
final proposedRoute = await deepLinkHandler.handleMyImmichApp(deepLink, ref, isColdStart);
return proposedRoute;
}
@@ -221,7 +198,7 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
super.didChangeDependencies();
Intl.defaultLocale = context.locale.toLanguageTag();
WidgetsBinding.instance.addPostFrameCallback((_) {
_configureFileDownloaderNotifications();
configureFileDownloaderNotifications();
});
}
@@ -231,7 +208,16 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
initApp().then((_) => debugPrint("App Init Completed"));
WidgetsBinding.instance.addPostFrameCallback((_) {
// needs to be delayed so that EasyLocalization is working
ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
if (Store.isBetaTimelineEnabled) {
ref.read(driftBackgroundUploadFgService).enableSyncService();
if (ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup)) {
ref.read(backgroundServiceProvider).disableService();
ref.read(driftBackgroundUploadFgService).enableUploadService();
}
} else {
ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
ref.read(driftBackgroundUploadFgService).disableUploadService();
}
});
ref.read(shareIntentUploadProvider.notifier).init();
@@ -243,6 +229,14 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
super.dispose();
}
@override
void reassemble() {
if (kDebugMode) {
NetworkRepository.reset();
}
super.reassemble();
}
@override
Widget build(BuildContext context) {
final router = ref.watch(appRouterProvider);

View File

@@ -8,6 +8,7 @@ import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
@@ -42,10 +43,12 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
await ref.read(backgroundSyncProvider).syncRemote();
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
await ref.read(driftBackgroundUploadFgService).enableUploadService();
await ref.read(driftBackupProvider.notifier).startBackup(currentUser.id);
}
Future<void> stopBackup() async {
await ref.read(driftBackgroundUploadFgService).disableUploadService();
await ref.read(driftBackupProvider.notifier).cancel();
}

View File

@@ -13,7 +13,9 @@ import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/backup/manual_upload.provider.dart';
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/websocket.provider.dart';
import 'package:immich_mobile/services/background.service.dart';
import 'package:immich_mobile/utils/migration.dart';
import 'package:logging/logging.dart';
import 'package:permission_handler/permission_handler.dart';
@@ -68,12 +70,16 @@ class _ChangeExperiencePageState extends ConsumerState<ChangeExperiencePage> {
await migrateDeviceAssetToSqlite(ref.read(isarProvider), ref.read(driftProvider));
await migrateBackupAlbumsToSqlite(ref.read(isarProvider), ref.read(driftProvider));
await migrateStoreToSqlite(ref.read(isarProvider), ref.read(driftProvider));
await ref.read(backgroundServiceProvider).disableService();
}
} else {
await ref.read(backgroundSyncProvider).cancel();
ref.read(websocketProvider.notifier).stopListeningToBetaEvents();
ref.read(websocketProvider.notifier).startListeningToOldEvents();
ref.read(readonlyModeProvider.notifier).setReadonlyMode(false);
await migrateStoreToIsar(ref.read(isarProvider), ref.read(driftProvider));
await ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
await ref.read(driftBackgroundUploadFgService).disableUploadService();
}
await IsarStoreRepository(ref.read(isarProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta);

View File

@@ -11,6 +11,7 @@ import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/search/search_input_focus.provider.dart';
import 'package:immich_mobile/providers/tab.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
@@ -54,6 +55,7 @@ class _TabShellPageState extends ConsumerState<TabShellPage> {
@override
Widget build(BuildContext context) {
final isScreenLandscape = context.orientation == Orientation.landscape;
final isReadonlyModeEnabled = ref.watch(readonlyModeProvider);
final navigationDestinations = [
NavigationDestination(
@@ -65,23 +67,33 @@ class _TabShellPageState extends ConsumerState<TabShellPage> {
label: 'search'.tr(),
icon: const Icon(Icons.search_rounded),
selectedIcon: Icon(Icons.search, color: context.primaryColor),
enabled: !isReadonlyModeEnabled,
),
NavigationDestination(
label: 'albums'.tr(),
icon: const Icon(Icons.photo_album_outlined),
selectedIcon: Icon(Icons.photo_album_rounded, color: context.primaryColor),
enabled: !isReadonlyModeEnabled,
),
NavigationDestination(
label: 'library'.tr(),
icon: const Icon(Icons.space_dashboard_outlined),
selectedIcon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor),
enabled: !isReadonlyModeEnabled,
),
];
Widget navigationRail(TabsRouter tabsRouter) {
return NavigationRail(
destinations: navigationDestinations
.map((e) => NavigationRailDestination(icon: e.icon, label: Text(e.label), selectedIcon: e.selectedIcon))
.map(
(e) => NavigationRailDestination(
icon: e.icon,
label: Text(e.label),
selectedIcon: e.selectedIcon,
disabled: !e.enabled,
),
)
.toList(),
onDestinationSelected: (index) => _onNavigationSelected(tabsRouter, index, ref),
selectedIndex: tabsRouter.activeIndex,

View File

@@ -0,0 +1,296 @@
// Autogenerated from Pigeon (v26.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
import 'package:flutter/services.dart';
PlatformException _createConnectionError(String channelName) {
return PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel: "$channelName".',
);
}
List<Object?> wrapResponse({Object? result, PlatformException? error, bool empty = false}) {
if (empty) {
return <Object?>[];
}
if (error == null) {
return <Object?>[result];
}
return <Object?>[error.code, error.message, error.details];
}
class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is int) {
buffer.putUint8(4);
buffer.putInt64(value);
} else {
super.writeValue(buffer, value);
}
}
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
default:
return super.readValueOfType(type, buffer);
}
}
}
class BackgroundWorkerFgHostApi {
/// Constructor for [BackgroundWorkerFgHostApi]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
/// BinaryMessenger will be used which routes to the host platform.
BackgroundWorkerFgHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
: pigeonVar_binaryMessenger = binaryMessenger,
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
final BinaryMessenger? pigeonVar_binaryMessenger;
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
final String pigeonVar_messageChannelSuffix;
Future<void> enableSyncWorker() async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableSyncWorker$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}
Future<void> enableUploadWorker(int callbackHandle) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableUploadWorker$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[callbackHandle]);
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}
Future<void> disableUploadWorker() async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disableUploadWorker$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}
}
class BackgroundWorkerBgHostApi {
/// Constructor for [BackgroundWorkerBgHostApi]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
/// BinaryMessenger will be used which routes to the host platform.
BackgroundWorkerBgHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
: pigeonVar_binaryMessenger = binaryMessenger,
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
final BinaryMessenger? pigeonVar_binaryMessenger;
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
final String pigeonVar_messageChannelSuffix;
Future<void> onInitialized() async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.onInitialized$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}
}
abstract class BackgroundWorkerFlutterApi {
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
Future<void> onLocalSync(int? maxSeconds);
Future<void> onIosUpload(bool isRefresh, int? maxSeconds);
Future<void> onAndroidUpload();
Future<void> cancel();
static void setUp(
BackgroundWorkerFlutterApi? api, {
BinaryMessenger? binaryMessenger,
String messageChannelSuffix = '',
}) {
messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onLocalSync$messageChannelSuffix',
pigeonChannelCodec,
binaryMessenger: binaryMessenger,
);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
assert(
message != null,
'Argument for dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onLocalSync was null.',
);
final List<Object?> args = (message as List<Object?>?)!;
final int? arg_maxSeconds = (args[0] as int?);
try {
await api.onLocalSync(arg_maxSeconds);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()),
);
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload$messageChannelSuffix',
pigeonChannelCodec,
binaryMessenger: binaryMessenger,
);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
assert(
message != null,
'Argument for dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload was null.',
);
final List<Object?> args = (message as List<Object?>?)!;
final bool? arg_isRefresh = (args[0] as bool?);
assert(
arg_isRefresh != null,
'Argument for dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload was null, expected non-null bool.',
);
final int? arg_maxSeconds = (args[1] as int?);
try {
await api.onIosUpload(arg_isRefresh!, arg_maxSeconds);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()),
);
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onAndroidUpload$messageChannelSuffix',
pigeonChannelCodec,
binaryMessenger: binaryMessenger,
);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
try {
await api.onAndroidUpload();
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()),
);
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.cancel$messageChannelSuffix',
pigeonChannelCodec,
binaryMessenger: binaryMessenger,
);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
try {
await api.cancel();
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()),
);
}
});
}
}
}
}

View File

@@ -4,7 +4,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/presentation/widgets/memory/memory_lane.widget.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
@RoutePage()
class MainTimelinePage extends ConsumerWidget {
@@ -12,25 +11,10 @@ class MainTimelinePage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final memoryLaneProvider = ref.watch(driftMemoryFutureProvider);
final memoriesEnabled = ref.watch(currentUserProvider.select((user) => user?.memoryEnabled ?? true));
// TODO: the user preferences need to be updated
// from the server to get live hiding/showing of memory lane
return memoryLaneProvider.maybeWhen(
data: (memories) {
return memories.isEmpty || !memoriesEnabled
? const Timeline()
: Timeline(
topSliverWidget: SliverToBoxAdapter(
key: Key('memory-lane-${memories.first.assets.first.id}'),
child: DriftMemoryLane(memories: memories),
),
topSliverWidgetHeight: 200,
);
},
orElse: () => const Timeline(),
final hasMemories = ref.watch(driftMemoryFutureProvider.select((state) => state.value?.isNotEmpty ?? false));
return Timeline(
topSliverWidget: const SliverToBoxAdapter(child: DriftMemoryLane()),
topSliverWidgetHeight: hasMemories ? 200 : 0,
);
}
}

View File

@@ -1,15 +1,34 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart';
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
class _SharePreparingDialog extends StatelessWidget {
const _SharePreparingDialog();
@override
Widget build(BuildContext context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(),
Container(margin: const EdgeInsets.only(top: 12), child: const Text('share_dialog_preparing').tr()),
],
),
);
}
}
class ShareActionButton extends ConsumerWidget {
final ActionSource source;
@@ -20,28 +39,34 @@ class ShareActionButton extends ConsumerWidget {
return;
}
final result = await ref.read(actionProvider.notifier).shareAssets(source);
ref.read(multiSelectProvider.notifier).reset();
showDialog(
context: context,
builder: (BuildContext buildContext) {
ref.read(actionProvider.notifier).shareAssets(source).then((ActionResult result) {
ref.read(multiSelectProvider.notifier).reset();
if (!context.mounted) {
return;
}
if (!context.mounted) {
return;
}
if (!result.success) {
ImmichToast.show(
context: context,
msg: 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: ToastType.error,
);
} else if (result.count > 0) {
ImmichToast.show(
context: context,
msg: 'share_action_prompt'.t(context: context, args: {'count': result.count.toString()}),
gravity: ToastGravity.BOTTOM,
toastType: ToastType.success,
);
}
if (!result.success) {
ImmichToast.show(
context: context,
msg: 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: ToastType.error,
);
}
buildContext.pop();
});
// show a loading spinner with a "Preparing" message
return const _SharePreparingDialog();
},
barrierDismissible: false,
useRootNavigator: false,
);
}
@override

View File

@@ -24,6 +24,7 @@ import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provi
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
import 'package:immich_mobile/providers/cast.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
import 'package:immich_mobile/widgets/photo_view/photo_view.dart';
@@ -58,6 +59,18 @@ class AssetViewer extends ConsumerStatefulWidget {
@override
ConsumerState createState() => _AssetViewerState();
static void setAsset(WidgetRef ref, BaseAsset asset) {
// Always holds the current asset from the timeline
ref.read(assetViewerProvider.notifier).setAsset(asset);
// The currentAssetNotifier actually holds the current asset that is displayed
// which could be stack children as well
ref.read(currentAssetNotifier.notifier).setAsset(asset);
if (asset.isVideo || asset.isMotionPhoto) {
ref.read(videoPlaybackValueProvider.notifier).reset();
ref.read(videoPlayerControlsProvider.notifier).pause();
}
}
}
const double _kBottomSheetMinimumExtent = 0.4;
@@ -98,13 +111,12 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
@override
void initState() {
super.initState();
assert(ref.read(currentAssetNotifier) != null, "Current asset should not be null when opening the AssetViewer");
pageController = PageController(initialPage: widget.initialIndex);
platform = widget.platform ?? const LocalPlatform();
totalAssets = ref.read(timelineServiceProvider).totalAssets;
bottomSheetController = DraggableScrollableController();
WidgetsBinding.instance.addPostFrameCallback((_) {
_onAssetChanged(widget.initialIndex);
});
WidgetsBinding.instance.addPostFrameCallback(_onAssetInit);
reloadSubscription = EventStream.shared.listen(_onEvent);
heroOffset = widget.heroOffset ?? TabsRouterScope.of(context)?.controller.activeIndex ?? 0;
}
@@ -142,26 +154,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
return provider.resolve(ImageConfiguration.empty)..addListener(_dummyListener);
}
void _onAssetChanged(int index) async {
// Validate index bounds and try to get asset, loading buffer if needed
void _precacheAssets(int index) {
final timelineService = ref.read(timelineServiceProvider);
final asset = await timelineService.getAssetAsync(index);
if (asset == null) {
return;
}
// Always holds the current asset from the timeline
ref.read(assetViewerProvider.notifier).setAsset(asset);
// The currentAssetNotifier actually holds the current asset that is displayed
// which could be stack children as well
ref.read(currentAssetNotifier.notifier).setAsset(asset);
if (asset.isVideo || asset.isMotionPhoto) {
ref.read(videoPlaybackValueProvider.notifier).reset();
ref.read(videoPlayerControlsProvider.notifier).pause();
}
unawaited(ref.read(timelineServiceProvider).preCacheAssets(index));
unawaited(timelineService.preCacheAssets(index));
_cancelTimers();
// This will trigger the pre-caching of adjacent assets ensuring
// that they are ready when the user navigates to them.
@@ -180,12 +175,29 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
_nextPreCacheStream = nextAsset != null ? _precacheImage(nextAsset) : null;
});
_delayedOperations.add(timer);
_handleCasting(asset);
}
void _handleCasting(BaseAsset asset) {
void _onAssetInit(Duration _) {
_precacheAssets(widget.initialIndex);
_handleCasting();
}
void _onAssetChanged(int index) async {
final timelineService = ref.read(timelineServiceProvider);
final asset = await timelineService.getAssetAsync(index);
if (asset == null) {
return;
}
AssetViewer.setAsset(ref, asset);
_precacheAssets(index);
_handleCasting();
}
void _handleCasting() {
if (!ref.read(castProvider).isCasting) return;
final asset = ref.read(currentAssetNotifier);
if (asset == null) return;
// hide any casting snackbars if they exist
context.scaffoldMessenger.hideCurrentSnackBar();
@@ -308,7 +320,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
bottomSheetController.jumpTo((centre + distanceToOrigin) / ctx.height);
}
if (distanceToOrigin > openThreshold && !showingBottomSheet) {
if (distanceToOrigin > openThreshold && !showingBottomSheet && !ref.read(readonlyModeProvider)) {
_openBottomSheet(ctx);
}
}
@@ -596,7 +608,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
if (asset == null) return;
WidgetsBinding.instance.addPostFrameCallback((_) {
_handleCasting(asset);
_handleCasting();
});
});

View File

@@ -75,14 +75,23 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier<AssetViewerState> {
}
void setAsset(BaseAsset? asset) {
if (asset == state.currentAsset) {
return;
}
state = state.copyWith(currentAsset: asset, stackIndex: 0);
}
void setOpacity(int opacity) {
if (opacity == state.backgroundOpacity) {
return;
}
state = state.copyWith(backgroundOpacity: opacity, showingControls: opacity == 255 ? true : state.showingControls);
}
void setBottomSheet(bool showing) {
if (showing == state.showingBottomSheet) {
return;
}
state = state.copyWith(showingBottomSheet: showing, showingControls: showing ? true : state.showingControls);
if (showing) {
ref.read(videoPlayerControlsProvider.notifier).pause();
@@ -90,6 +99,9 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier<AssetViewerState> {
}
void setControls(bool isShowing) {
if (isShowing == state.showingControls) {
return;
}
state = state.copyWith(showingControls: isShowing);
}
@@ -98,6 +110,9 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier<AssetViewerState> {
}
void setStackIndex(int index) {
if (index == state.stackIndex) {
return;
}
state = state.copyWith(stackIndex: index);
}
}

View File

@@ -12,6 +12,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_acti
import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/routes.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart';
@@ -26,6 +27,7 @@ class ViewerBottomBar extends ConsumerWidget {
return const SizedBox.shrink();
}
final isReadonlyModeEnabled = ref.watch(readonlyModeProvider);
final user = ref.watch(currentUserProvider);
final isOwner = asset is RemoteAsset && asset.ownerId == user?.id;
final isSheetOpen = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet));
@@ -60,7 +62,7 @@ class ViewerBottomBar extends ConsumerWidget {
duration: Durations.short2,
child: AnimatedSwitcher(
duration: Durations.short4,
child: isSheetOpen
child: isSheetOpen || isReadonlyModeEnabled
? const SizedBox.shrink()
: Theme(
data: context.themeData.copyWith(

View File

@@ -1,5 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
@@ -9,6 +10,7 @@ import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart';
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
@@ -184,7 +186,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget {
}
}
class _SheetTile extends StatelessWidget {
class _SheetTile extends ConsumerWidget {
final String title;
final Widget? leading;
final Widget? trailing;
@@ -203,8 +205,18 @@ class _SheetTile extends StatelessWidget {
this.onTap,
});
void copyTitle(BuildContext context, WidgetRef ref) {
Clipboard.setData(ClipboardData(text: title));
ImmichToast.show(
context: context,
msg: 'copied_to_clipboard'.t(context: context),
toastType: ToastType.info,
);
ref.read(hapticFeedbackProvider.notifier).selectionClick();
}
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final Widget titleWidget;
if (leading == null) {
titleWidget = LimitedBox(
@@ -234,7 +246,7 @@ class _SheetTile extends StatelessWidget {
return ListTile(
dense: true,
visualDensity: VisualDensity.compact,
title: titleWidget,
title: GestureDetector(onLongPress: () => copyTitle(context, ref), child: titleWidget),
titleAlignment: ListTileTitleAlignment.center,
leading: leading,
trailing: trailing,

View File

@@ -14,6 +14,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_act
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
import 'package:immich_mobile/providers/cast.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
import 'package:immich_mobile/providers/routes.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
@@ -34,6 +35,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
final user = ref.watch(currentUserProvider);
final isOwner = asset is RemoteAsset && asset.ownerId == user?.id;
final isInLockedView = ref.watch(inLockedViewProvider);
final isReadonlyModeEnabled = ref.watch(readonlyModeProvider);
final previousRouteName = ref.watch(previousRouteNameProvider);
final showViewInTimelineButton =
@@ -94,7 +96,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
iconTheme: const IconThemeData(size: 22, color: Colors.white),
actionsIconTheme: const IconThemeData(size: 22, color: Colors.white),
shape: const Border(),
actions: isShowingSheet
actions: isShowingSheet || isReadonlyModeEnabled
? null
: isInLockedView
? lockedViewActions

View File

@@ -87,9 +87,10 @@ class NativeVideoViewer extends HookConsumerWidget {
return null;
}
final videoAsset = await ref.read(assetServiceProvider).getAsset(asset) ?? asset;
try {
if (asset.hasLocal && asset.livePhotoVideoId == null) {
final id = asset is LocalAsset ? (asset as LocalAsset).id : (asset as RemoteAsset).localId!;
if (videoAsset.hasLocal && videoAsset.livePhotoVideoId == null) {
final id = videoAsset is LocalAsset ? videoAsset.id : (videoAsset as RemoteAsset).localId!;
final file = await const StorageRepository().getFileForAsset(id);
if (file == null) {
throw Exception('No file found for the video');
@@ -99,14 +100,14 @@ class NativeVideoViewer extends HookConsumerWidget {
return source;
}
final remoteId = (asset as RemoteAsset).id;
final remoteId = (videoAsset as RemoteAsset).id;
// Use a network URL for the video player controller
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
final isOriginalVideo = ref.read(settingsProvider).get<bool>(Setting.loadOriginalVideo);
final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback';
final String videoUrl = asset.livePhotoVideoId != null
? '$serverEndpoint/assets/${asset.livePhotoVideoId}/$postfixUrl'
final String videoUrl = videoAsset.livePhotoVideoId != null
? '$serverEndpoint/assets/${videoAsset.livePhotoVideoId}/$postfixUrl'
: '$serverEndpoint/assets/$remoteId/$postfixUrl';
final source = await VideoSource.init(
@@ -116,7 +117,7 @@ class NativeVideoViewer extends HookConsumerWidget {
);
return source;
} catch (error) {
log.severe('Error creating video source for asset ${asset.name}: $error');
log.severe('Error creating video source for asset ${videoAsset.name}: $error');
return null;
}
}

Some files were not shown because too many files have changed in this diff Show More