Compare commits

...

11 Commits

Author SHA1 Message Date
fallenbagel
2f7cfa3533 revert: dns caching #810 (46ee8a4) 2024-06-21 00:29:49 +05:00
Fallenbagel
4d14a15fb6 chore: merge upstream/develop (#824)
* feat(pushover): attach image to pushover notification payload (#3701)

* fix: api language query parameter (#3720)

* docs: add j0srisk as a contributor for code (#3745) [skip ci]

* docs: update README.md

* docs: update .all-contributorsrc

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>

* feat(tooltip): add tooltip to display exact time on date hover (#3773)

Co-authored-by: Loetwiek <lodommerholtcm@gmail.com>

* docs: add Loetwiek as a contributor for code (#3776) [skip ci]

* docs: update README.md

* docs: update .all-contributorsrc

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>

* fix(ui): ensure title fits into the `view collection` box (#3696)

* fix(docs): correct openapi docs minor issues (#3648)

* docs: add Fuochi as a contributor for doc (#3826)

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>

* feat: translations update from Hosted Weblate (#3597)

* feat(lang): translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (1234 of 1234 strings)

feat(lang): translated using Weblate (Portuguese (Brazil))

Currently translated at 99.8% (1232 of 1234 strings)

Co-authored-by: Cleiton Carvalho <cleitonsilvacarvalho@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/pt_BR/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (German)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (German)

Currently translated at 100.0% (1234 of 1234 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Nandor Rusz <nandor.rusz@vodafone.de>
Co-authored-by: Thomas Schöneberg <ta.schoeneberg@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/de/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Danish)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Danish)

Currently translated at 100.0% (1236 of 1236 strings)

feat(lang): translated using Weblate (Danish)

Currently translated at 100.0% (1234 of 1234 strings)

Co-authored-by: Anders Ecklon <aecklon@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kenneth Hansen <erathor@live.dk>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/da/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Greek)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Greek)

Currently translated at 100.0% (1236 of 1236 strings)

Co-authored-by: BeardedWatermelon <BeardedWatermelon@users.noreply.hosted.weblate.org>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/el/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Russian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Russian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Russian)

Currently translated at 99.5% (1234 of 1240 strings)

feat(lang): translated using Weblate (Russian)

Currently translated at 100.0% (1234 of 1234 strings)

feat(lang): translated using Weblate (Russian)

Currently translated at 100.0% (1234 of 1234 strings)

feat(lang): translated using Weblate (Russian)

Currently translated at 100.0% (1234 of 1234 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: SoundwaveUwU <SoundwaveUwU@users.noreply.hosted.weblate.org>
Co-authored-by: SoundwaveUwU <noreply@1000-7.space>
Co-authored-by: Димитър Мазнеков (Topper) <d.maznekov@gmail.com>
Co-authored-by: Кирилл Тюрин <1337soundwave1337@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ru/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Romanian)

Currently translated at 37.1% (461 of 1240 strings)

feat(lang): translated using Weblate (Romanian)

Currently translated at 37.0% (459 of 1240 strings)

feat(lang): translated using Weblate (Romanian)

Currently translated at 34.8% (432 of 1240 strings)

Co-authored-by: Don Cezar <goldie.czr@gmail.com>
Co-authored-by: Dragos <themsk@yahoo.com>
Co-authored-by: Eduard Oancea <uberfly@420blaze.it>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ro/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Bulgarian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Bulgarian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Bulgarian)

Currently translated at 57.4% (712 of 1240 strings)

feat(lang): translated using Weblate (Bulgarian)

Currently translated at 13.2% (164 of 1240 strings)

feat(lang): translated using Weblate (Bulgarian)

Currently translated at 4.8% (60 of 1240 strings)

feat(lang): added translation using Weblate (Bulgarian)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: sct <sctsnipe@gmail.com>
Co-authored-by: Димитър Мазнеков (Topper) <d.maznekov@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/bg/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Ukrainian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 99.1% (1230 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 99.1% (1230 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 99.1% (1230 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 97.9% (1215 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 82.0% (1017 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 72.9% (905 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 72.9% (905 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 71.3% (885 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 64.9% (805 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 64.4% (799 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 63.8% (792 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 63.7% (791 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 57.5% (714 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 49.9% (619 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 35.9% (446 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 35.9% (446 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 32.1% (399 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 24.6% (306 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 18.9% (235 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 17.5% (217 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 17.3% (215 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 8.0% (100 of 1240 strings)

feat(lang): translated using Weblate (Ukrainian)

Currently translated at 3.3% (41 of 1240 strings)

feat(lang): added translation using Weblate (Ukrainian)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Michael Michael <michaelvelosk@gmail.com>
Co-authored-by: sct <sctsnipe@gmail.com>
Co-authored-by: Сергій <sergiy.goncharuk.1@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/uk/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Catalan)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Catalan)

Currently translated at 100.0% (1240 of 1240 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: dtalens <databio@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ca/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Czech)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Czech)

Currently translated at 99.6% (1236 of 1240 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Karel Krýda <karel.kryda@gmail.com>
Co-authored-by: Smexhy <roman.bartik@icloud.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/cs/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Croatian)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 99.8% (1238 of 1240 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 99.8% (1238 of 1240 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 99.6% (1236 of 1240 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 99.5% (1235 of 1240 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 99.5% (1235 of 1240 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 99.1% (1230 of 1240 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 97.5% (1210 of 1240 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 95.5% (1185 of 1240 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 95.6% (1182 of 1236 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 95.6% (1182 of 1236 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 95.2% (1177 of 1236 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 95.2% (1177 of 1236 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 94.3% (1166 of 1236 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 91.7% (1134 of 1236 strings)

feat(lang): translated using Weblate (Croatian)

Currently translated at 91.7% (1134 of 1236 strings)

Co-authored-by: Bruno Ševčenko <bs3vcenk@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Stjepan <stjepstjepanovic@gmail.com>
Co-authored-by: lpispek <lpispek@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/hr/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Hungarian)

Currently translated at 91.3% (1133 of 1240 strings)

feat(lang): translated using Weblate (Hungarian)

Currently translated at 89.3% (1108 of 1240 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Levente Szajkó <leviko112@gmail.com>
Co-authored-by: Nandor Rusz <nandor.rusz@vodafone.de>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/hu/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Hebrew)

Currently translated at 13.9% (172 of 1236 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: osh <osh@osh.cc>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/he/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Polish)

Currently translated at 99.1% (1225 of 1236 strings)

Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/pl/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Italian)

Currently translated at 92.8% (1148 of 1236 strings)

Co-authored-by: Francesco <francy.ammirati@hotmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/it/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Arabic)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Arabic)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Arabic)

Currently translated at 100.0% (1234 of 1234 strings)

Co-authored-by: Fhd-pro <juve.11@msn.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ar/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Dutch)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Dutch)

Currently translated at 100.0% (1234 of 1234 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kobe <kobaubarr@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/nl/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Spanish)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Spanish)

Currently translated at 100.0% (1236 of 1236 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/es/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (French)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (French)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (French)

Currently translated at 100.0% (1236 of 1236 strings)

feat(lang): translated using Weblate (French)

Currently translated at 99.9% (1235 of 1236 strings)

feat(lang): translated using Weblate (French)

Currently translated at 99.9% (1235 of 1236 strings)

Co-authored-by: Baptiste <baptiste.nee@me.com>
Co-authored-by: Dimitri <dimitridroeck@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Maxime Lafarie <maxime.lafarie@gmail.com>
Co-authored-by: Miguel <mig.mllr@gmail.com>
Co-authored-by: asurare <jonathan.biteau16@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/fr/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Swedish)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Swedish)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Swedish)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Swedish)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Swedish)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Swedish)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Swedish)

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Swedish)

Currently translated at 100.0% (1236 of 1236 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Per Erik <urbanlolface@gmail.com>
Co-authored-by: Shjosan <shjosan@kakmix.co>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/sv/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Finnish)

Currently translated at 2.6% (33 of 1240 strings)

feat(lang): added translation using Weblate (Finnish)

Co-authored-by: Eero Konttaniemi <eero.konttaniemi@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: sct <sctsnipe@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/fi/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Serbian)

Currently translated at 50.8% (630 of 1240 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Milan Smudja <smudja@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/sr/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Korean)

Currently translated at 100.0% (1234 of 1234 strings)

Co-authored-by: Developer J <jshsakura@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ko/
Translation: Overseerr/Overseerr Frontend

* feat(lang): translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1240 of 1240 strings)

feat(lang): translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1234 of 1234 strings)

Co-authored-by: Haohao Zhang <hyacz@foxmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: lkw123 <lkw20010211@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/zh_Hans/
Translation: Overseerr/Overseerr Frontend

---------

Co-authored-by: Cleiton Carvalho <cleitonsilvacarvalho@gmail.com>
Co-authored-by: Nandor Rusz <nandor.rusz@vodafone.de>
Co-authored-by: Thomas Schöneberg <ta.schoeneberg@gmail.com>
Co-authored-by: Anders Ecklon <aecklon@gmail.com>
Co-authored-by: Kenneth Hansen <erathor@live.dk>
Co-authored-by: BeardedWatermelon <BeardedWatermelon@users.noreply.hosted.weblate.org>
Co-authored-by: SoundwaveUwU <SoundwaveUwU@users.noreply.hosted.weblate.org>
Co-authored-by: SoundwaveUwU <noreply@1000-7.space>
Co-authored-by: Димитър Мазнеков (Topper) <d.maznekov@gmail.com>
Co-authored-by: Кирилл Тюрин <1337soundwave1337@gmail.com>
Co-authored-by: Don Cezar <goldie.czr@gmail.com>
Co-authored-by: Dragos <themsk@yahoo.com>
Co-authored-by: Eduard Oancea <uberfly@420blaze.it>
Co-authored-by: sct <sctsnipe@gmail.com>
Co-authored-by: Michael Michael <michaelvelosk@gmail.com>
Co-authored-by: Сергій <sergiy.goncharuk.1@gmail.com>
Co-authored-by: dtalens <databio@gmail.com>
Co-authored-by: Karel Krýda <karel.kryda@gmail.com>
Co-authored-by: Smexhy <roman.bartik@icloud.com>
Co-authored-by: Bruno Ševčenko <bs3vcenk@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Stjepan <stjepstjepanovic@gmail.com>
Co-authored-by: lpispek <lpispek@gmail.com>
Co-authored-by: Levente Szajkó <leviko112@gmail.com>
Co-authored-by: osh <osh@osh.cc>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: Francesco <francy.ammirati@hotmail.com>
Co-authored-by: Fhd-pro <juve.11@msn.com>
Co-authored-by: Kobe <kobaubarr@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: Baptiste <baptiste.nee@me.com>
Co-authored-by: Dimitri <dimitridroeck@gmail.com>
Co-authored-by: Maxime Lafarie <maxime.lafarie@gmail.com>
Co-authored-by: Miguel <mig.mllr@gmail.com>
Co-authored-by: asurare <jonathan.biteau16@gmail.com>
Co-authored-by: Per Erik <urbanlolface@gmail.com>
Co-authored-by: Shjosan <shjosan@kakmix.co>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: Eero Konttaniemi <eero.konttaniemi@gmail.com>
Co-authored-by: Milan Smudja <smudja@gmail.com>
Co-authored-by: Developer J <jshsakura@gmail.com>
Co-authored-by: Haohao Zhang <hyacz@foxmail.com>
Co-authored-by: lkw123 <lkw20010211@gmail.com>

* feat(lang): add lang config for Bulgarian, Finnish, Ukrainian, Indonesian, Slovak, Turkish and Maori (#3834)

---------

Co-authored-by: Isaac M <masesisaac@gmail.com>
Co-authored-by: Joseph Risk <j0srisk@gmail.com>
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
Co-authored-by: Loetwiek <79059734+Loetwiek@users.noreply.github.com>
Co-authored-by: Loetwiek <lodommerholtcm@gmail.com>
Co-authored-by: Fuochi <ffuochi@hotmail.com>
Co-authored-by: Weblate (bot) <hosted@weblate.org>
Co-authored-by: Cleiton Carvalho <cleitonsilvacarvalho@gmail.com>
Co-authored-by: Nandor Rusz <nandor.rusz@vodafone.de>
Co-authored-by: Thomas Schöneberg <ta.schoeneberg@gmail.com>
Co-authored-by: Anders Ecklon <aecklon@gmail.com>
Co-authored-by: Kenneth Hansen <erathor@live.dk>
Co-authored-by: BeardedWatermelon <BeardedWatermelon@users.noreply.hosted.weblate.org>
Co-authored-by: SoundwaveUwU <SoundwaveUwU@users.noreply.hosted.weblate.org>
Co-authored-by: SoundwaveUwU <noreply@1000-7.space>
Co-authored-by: Димитър Мазнеков (Topper) <d.maznekov@gmail.com>
Co-authored-by: Кирилл Тюрин <1337soundwave1337@gmail.com>
Co-authored-by: Don Cezar <goldie.czr@gmail.com>
Co-authored-by: Dragos <themsk@yahoo.com>
Co-authored-by: Eduard Oancea <uberfly@420blaze.it>
Co-authored-by: sct <sctsnipe@gmail.com>
Co-authored-by: Michael Michael <michaelvelosk@gmail.com>
Co-authored-by: Сергій <sergiy.goncharuk.1@gmail.com>
Co-authored-by: dtalens <databio@gmail.com>
Co-authored-by: Karel Krýda <karel.kryda@gmail.com>
Co-authored-by: Smexhy <roman.bartik@icloud.com>
Co-authored-by: Bruno Ševčenko <bs3vcenk@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Stjepan <stjepstjepanovic@gmail.com>
Co-authored-by: lpispek <lpispek@gmail.com>
Co-authored-by: Levente Szajkó <leviko112@gmail.com>
Co-authored-by: osh <osh@osh.cc>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: Francesco <francy.ammirati@hotmail.com>
Co-authored-by: Fhd-pro <juve.11@msn.com>
Co-authored-by: Kobe <kobaubarr@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: Baptiste <baptiste.nee@me.com>
Co-authored-by: Dimitri <dimitridroeck@gmail.com>
Co-authored-by: Maxime Lafarie <maxime.lafarie@gmail.com>
Co-authored-by: Miguel <mig.mllr@gmail.com>
Co-authored-by: asurare <jonathan.biteau16@gmail.com>
Co-authored-by: Per Erik <urbanlolface@gmail.com>
Co-authored-by: Shjosan <shjosan@kakmix.co>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: Eero Konttaniemi <eero.konttaniemi@gmail.com>
Co-authored-by: Milan Smudja <smudja@gmail.com>
Co-authored-by: Developer J <jshsakura@gmail.com>
Co-authored-by: Haohao Zhang <hyacz@foxmail.com>
Co-authored-by: lkw123 <lkw20010211@gmail.com>
Co-authored-by: Jordan Jones <me@jjones.tech>
2024-06-19 18:40:25 +05:00
Fallenbagel
38ad875dd7 refactor(jellyfin): abstract jellyfin hostname, updated ui to reflect it, better validation (#773)
* refactor(jellyfinsettings): abstract jellyfin hostname, updated ui to reflect it, better validation

This PR refactors and abstracts jellyfin hostname into, jellyfin ip, jellyfin port, jellyfin useSsl,
and jellyfin urlBase. This makes it more consistent with how plex settings are stored as well. In
addition, this improves validation as validation can be applied seperately to them instead of as one
whole regex doing the work to validate the url.
UI was updated to reflect this.

BREAKING CHANGE: Jellyfin settings now does not include a hostname. Instead it abstracted it to ip,
port, useSsl, and urlBase. However, migration of old settings to new settings should work
automatically.

* refactor: remove console logs and use getHostname and ApiErrorCodes

* fix: store req.body jellyfin settings temporarily and store only if valid

This should fix the issue where settings are saved even if the url
was invalid. Now the settings will only be saved if the url is
valid. Sort of like a test connection.

* refactor: clean up commented out code

* refactor(i18n): extract translation keys

* fix(auth): auth failing with jellyfin login is disabled

* fix(settings): jellyfin migrations replacing the rest of the settings

* fix(settings): jellyfin hostname should be carried out if hostname exists

* fix(settings): merging the wrong settings source

* refactor(settings): use migrator for dynamic settings migrations

* refactor(settingsmigrator): settings migration handler and the migrations

* test(cypress): fix cypress tests failing

cypress settings were lacking some of the jobs so when the startJobs() is called when the app
starts, it was failing to schedule the jobs where their cron timings were not specified in the
cypress settings. Therefore, this commit adds those jobs back. In addition, other setting options
were added to keep cypress settings consistent with a normal user.

* chore(prettierignore): ignore cypress/config/settings.cypress.json as it does not need prettier

* chore(prettier): ran formatter on cypress config to fix format check error

format check locally passes on this file. However, it fails during the github actions format check.
Therefore, json language features formatter was run instead of prettier to see if that fixes the
issue.

* test(cypress): add only missing jobs to the cypress settings

* ci: attempt at trying to get formatter to pass on cypress config json file

* refactor: revert the changes brought to try and fix formatter

added back the rest of the cypress settings and removed cypress settings from .prettierignore

* refactor(settings): better erorr logging when jellyfin connection test fails in settings page
2024-06-13 19:06:33 +02:00
Fallenbagel
a9741fa36d fix(auth): improve login resilience with headerless fallback authentication (#814)
adds fallback to authenticate without headers to ensure and improve resilience across different
browsers and client configurations.
2024-06-13 11:16:07 +02:00
Fallenbagel
b5a069901a fix: bypass cache-able lookups when resolving localhost (#813)
* fix: bypass cache-able lookups when resolving localhost

* fix: bypass cacheable-lookup when resolving localhost

---------

Co-authored-by: Gauthier <mail@gauthierth.fr>
2024-06-13 04:53:12 +05:00
Fallenbagel
9aeb3604e6 fix(auth): validation of ipv6/ipv4 (#812)
validation for ipv6 was sort of broken where for example `::1` was being sent as `1`, therefore,
logins were broken. This PR fixes it by using nodejs `net.isIPv4()` & `net.isIPv6` for ipv4 and ipv6
validation.

possibly related to and fixes #795
2024-06-12 18:50:00 +05:00
Fallenbagel
6eb88f8674 ci: temporarily disable snap release builds (#811) 2024-06-12 10:49:15 +05:00
Gauthier
46ee8a4ca1 fix(api): add DNS caching (#810)
fix #387 #657 #728
2024-06-12 02:56:10 +05:00
Gauthier
f52939e4cd fix: remove the settings button of media when useless (#809)
After the Media Availability Sync job rund on deleted media, the setting button is still visible
even if neither the media file nor the media request no longer exists. This PR hides this button
when it's no longer the case
2024-06-11 19:47:02 +05:00
Gauthier
d31a2c37e6 fix(jellyfinscanner): assign only 4k available badge for a 4k request instead of both badges (#805)
When you have a 4k server setup, and request a 4k item, when it becomes available it also sets the
normal item as available thus not allowing the user to request for the normal item
2024-06-11 17:58:48 +05:00
Gauthier
20863d4a8d fix: empty email in user settings (#807)
Email is mandatory for every user and required during the setup of Jellyseerr, but it is possible to
set it empty afterwards in the user settings. When the email is empty, users are not able to connect
to Jellyseer. This PR makes the email field mandatory in the user settings.

fix #803
2024-06-11 16:23:35 +05:00
55 changed files with 3286 additions and 626 deletions

View File

@@ -376,6 +376,33 @@
"contributions": [
"code"
]
},
{
"login": "j0srisk",
"name": "Joseph Risk",
"avatar_url": "https://avatars.githubusercontent.com/u/18372584?v=4",
"profile": "http://josephrisk.com",
"contributions": [
"code"
]
},
{
"login": "Loetwiek",
"name": "Loetwiek",
"avatar_url": "https://avatars.githubusercontent.com/u/79059734?v=4",
"profile": "https://github.com/Loetwiek",
"contributions": [
"code"
]
},
{
"login": "Fuochi",
"name": "Fuochi",
"avatar_url": "https://avatars.githubusercontent.com/u/4720478?v=4",
"profile": "https://github.com/Fuochi",
"contributions": [
"doc"
]
}
]
}

View File

@@ -35,60 +35,60 @@ jobs:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: npx semantic-release
build-snap:
name: Build Snap Package (${{ matrix.architecture }})
needs: semantic-release
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
architecture:
- amd64
- arm64
- armhf
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Switch to main branch
run: git checkout main
- name: Pull latest changes
run: git pull
- name: Prepare
id: prepare
run: |
git fetch --prune --tags
if [[ $GITHUB_REF == refs/tags/* || $GITHUB_REF == refs/heads/master ]]; then
echo "RELEASE=stable" >> $GITHUB_OUTPUT
else
echo "RELEASE=edge" >> $GITHUB_OUTPUT
fi
- name: Set Up QEMU
uses: docker/setup-qemu-action@v3
with:
image: tonistiigi/binfmt@sha256:df15403e06a03c2f461c1f7938b171fda34a5849eb63a70e2a2109ed5a778bde
- name: Build Snap Package
uses: diddlesnaps/snapcraft-multiarch-action@v1
id: build
with:
architecture: ${{ matrix.architecture }}
- name: Upload Snap Package
uses: actions/upload-artifact@v4
with:
name: jellyseerr-snap-package-${{ matrix.architecture }}
path: ${{ steps.build.outputs.snap }}
- name: Review Snap Package
uses: diddlesnaps/snapcraft-review-tools-action@v1
with:
snap: ${{ steps.build.outputs.snap }}
- name: Publish Snap Package
uses: snapcore/action-publish@v1
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_LOGIN }}
with:
snap: ${{ steps.build.outputs.snap }}
release: ${{ steps.prepare.outputs.RELEASE }}
# build-snap:
# name: Build Snap Package (${{ matrix.architecture }})
# needs: semantic-release
# runs-on: ubuntu-22.04
# strategy:
# fail-fast: false
# matrix:
# architecture:
# - amd64
# - arm64
# - armhf
# steps:
# - name: Checkout Code
# uses: actions/checkout@v4
# with:
# fetch-depth: 0
# - name: Switch to main branch
# run: git checkout main
# - name: Pull latest changes
# run: git pull
# - name: Prepare
# id: prepare
# run: |
# git fetch --prune --tags
# if [[ $GITHUB_REF == refs/tags/* || $GITHUB_REF == refs/heads/master ]]; then
# echo "RELEASE=stable" >> $GITHUB_OUTPUT
# else
# echo "RELEASE=edge" >> $GITHUB_OUTPUT
# fi
# - name: Set Up QEMU
# uses: docker/setup-qemu-action@v3
# with:
# image: tonistiigi/binfmt@sha256:df15403e06a03c2f461c1f7938b171fda34a5849eb63a70e2a2109ed5a778bde
# - name: Build Snap Package
# uses: diddlesnaps/snapcraft-multiarch-action@v1
# id: build
# with:
# architecture: ${{ matrix.architecture }}
# - name: Upload Snap Package
# uses: actions/upload-artifact@v4
# with:
# name: jellyseerr-snap-package-${{ matrix.architecture }}
# path: ${{ steps.build.outputs.snap }}
# - name: Review Snap Package
# uses: diddlesnaps/snapcraft-review-tools-action@v1
# with:
# snap: ${{ steps.build.outputs.snap }}
# - name: Publish Snap Package
# uses: snapcore/action-publish@v1
# env:
# SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_LOGIN }}
# with:
# snap: ${{ steps.build.outputs.snap }}
# release: ${{ steps.prepare.outputs.RELEASE }}
discord:
name: Send Discord Notification

View File

@@ -378,6 +378,9 @@ Thanks goes to these wonderful people from Overseerr ([emoji key](https://allcon
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RemiRigal"><img src="https://avatars.githubusercontent.com/u/19256051?v=4?s=100" width="100px;" alt="RemiRigal"/><br /><sub><b>RemiRigal</b></sub></a><br /><a href="https://github.com/sct/overseerr/commits?author=RemiRigal" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://josephrisk.com"><img src="https://avatars.githubusercontent.com/u/18372584?v=4?s=100" width="100px;" alt="Joseph Risk"/><br /><sub><b>Joseph Risk</b></sub></a><br /><a href="https://github.com/sct/overseerr/commits?author=j0srisk" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Loetwiek"><img src="https://avatars.githubusercontent.com/u/79059734?v=4?s=100" width="100px;" alt="Loetwiek"/><br /><sub><b>Loetwiek</b></sub></a><br /><a href="https://github.com/sct/overseerr/commits?author=Loetwiek" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Fuochi"><img src="https://avatars.githubusercontent.com/u/4720478?v=4?s=100" width="100px;" alt="Fuochi"/><br /><sub><b>Fuochi</b></sub></a><br /><a href="https://github.com/sct/overseerr/commits?author=Fuochi" title="Documentation">📖</a></td>
</tr>
</tbody>
</table>

View File

@@ -19,6 +19,7 @@
"region": "",
"originalLanguage": "",
"trustProxy": false,
"mediaServerType": 1,
"partialRequestsEnabled": true,
"locale": "en"
},
@@ -37,6 +38,17 @@
],
"machineId": "test"
},
"jellyfin": {
"name": "",
"ip": "",
"port": 8096,
"useSsl": false,
"urlBase": "",
"externalHostname": "",
"jellyfinForgotPasswordUrl": "",
"libraries": [],
"serverId": ""
},
"tautulli": {},
"radarr": [],
"sonarr": [],
@@ -139,11 +151,26 @@
"sonarr-scan": {
"schedule": "0 30 4 * * *"
},
"plex-watchlist-sync": {
"schedule": "0 */10 * * * *"
},
"availability-sync": {
"schedule": "0 0 5 * * *"
},
"download-sync": {
"schedule": "0 * * * * *"
},
"download-sync-reset": {
"schedule": "0 0 1 * * *"
},
"jellyfin-recently-added-scan": {
"schedule": "0 */5 * * * *"
},
"jellyfin-full-scan": {
"schedule": "0 0 3 * * *"
},
"image-cache-cleanup": {
"schedule": "0 0 5 * * *"
}
}
}

View File

@@ -126,25 +126,31 @@ class JellyfinAPI extends ExternalAPI {
Password?: string,
ClientIP?: string
): Promise<JellyfinLoginResponse> {
try {
const headers = ClientIP
? {
'X-Forwarded-For': ClientIP,
}
: {};
const authenticate = async (useHeaders: boolean) => {
const headers =
useHeaders && ClientIP ? { 'X-Forwarded-For': ClientIP } : {};
const authResponse = await this.post<JellyfinLoginResponse>(
return this.post<JellyfinLoginResponse>(
'/Users/AuthenticateByName',
{
Username: Username,
Username,
Pw: Password,
},
{
headers: headers,
}
{ headers }
);
};
return authResponse;
try {
return await authenticate(true);
} catch (e) {
logger.debug(`Failed to authenticate with headers: ${e.message}`, {
label: 'Jellyfin API',
ip: ClientIP,
});
}
try {
return await authenticate(false);
} catch (e) {
const status = e.response?.status;
@@ -178,6 +184,16 @@ class JellyfinAPI extends ExternalAPI {
return;
}
public async getSystemInfo(): Promise<any> {
try {
const systemInfoResponse = await this.get<any>('/System/Info');
return systemInfoResponse;
} catch (e) {
throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken);
}
}
public async getServerName(): Promise<string> {
try {
const serverResponse = await this.get<JellyfinUserResponse>(

View File

@@ -3,5 +3,7 @@ export enum ApiErrorCode {
InvalidCredentials = 'INVALID_CREDENTIALS',
InvalidAuthToken = 'INVALID_AUTH_TOKEN',
NotAdmin = 'NOT_ADMIN',
SyncErrorGroupedFolders = 'SYNC_ERROR_GROUPED_FOLDERS',
SyncErrorNoLibraries = 'SYNC_ERROR_NO_LIBRARIES',
Unknown = 'UNKNOWN',
}

View File

@@ -9,6 +9,7 @@ import type { DownloadingItem } from '@server/lib/downloadtracker';
import downloadTracker from '@server/lib/downloadtracker';
import { getSettings } from '@server/lib/settings';
import logger from '@server/logger';
import { getHostname } from '@server/utils/getHostname';
import {
AfterLoad,
Column,
@@ -211,15 +212,12 @@ class Media {
} else {
const pageName =
process.env.JELLYFIN_TYPE === 'emby' ? 'item' : 'details';
const { serverId, hostname, externalHostname } = getSettings().jellyfin;
let jellyfinHost =
const { serverId, externalHostname } = getSettings().jellyfin;
const jellyfinHost =
externalHostname && externalHostname.length > 0
? externalHostname
: hostname;
jellyfinHost = jellyfinHost.endsWith('/')
? jellyfinHost.slice(0, -1)
: jellyfinHost;
: getHostname();
if (this.jellyfinMediaId) {
this.mediaUrl = `${jellyfinHost}/web/index.html#!/${pageName}?id=${this.jellyfinMediaId}&context=home&serverId=${serverId}`;

View File

@@ -16,6 +16,7 @@ import { User } from '@server/entity/User';
import type { RadarrSettings, SonarrSettings } from '@server/lib/settings';
import { getSettings } from '@server/lib/settings';
import logger from '@server/logger';
import { getHostname } from '@server/utils/getHostname';
class AvailabilitySync {
public running = false;
@@ -84,7 +85,7 @@ class AvailabilitySync {
) {
if (admin) {
this.jellyfinClient = new JellyfinAPI(
settings.jellyfin.hostname ?? '',
getHostname(),
admin.jellyfinAuthToken,
admin.jellyfinDeviceId
);

View File

@@ -14,7 +14,12 @@ import {
import type { NotificationAgent, NotificationPayload } from './agent';
import { BaseAgent } from './agent';
interface PushoverPayload {
interface PushoverImagePayload {
attachment_base64: string;
attachment_type: string;
}
interface PushoverPayload extends PushoverImagePayload {
token: string;
user: string;
title: string;
@@ -43,10 +48,36 @@ class PushoverAgent
return true;
}
private getNotificationPayload(
private async getImagePayload(
imageUrl: string
): Promise<Partial<PushoverImagePayload>> {
try {
const response = await axios.get(imageUrl, {
responseType: 'arraybuffer',
});
const base64 = Buffer.from(response.data, 'binary').toString('base64');
const contentType = (
response.headers['Content-Type'] || response.headers['content-type']
)?.toString();
return {
attachment_base64: base64,
attachment_type: contentType,
};
} catch (e) {
logger.error('Error getting image payload', {
label: 'Notifications',
errorMessage: e.message,
response: e.response?.data,
});
return {};
}
}
private async getNotificationPayload(
type: Notification,
payload: NotificationPayload
): Partial<PushoverPayload> {
): Promise<Partial<PushoverPayload>> {
const { applicationUrl, applicationTitle } = getSettings().main;
const title = payload.event ?? payload.subject;
@@ -122,6 +153,16 @@ class PushoverAgent
? `View ${payload.issue ? 'Issue' : 'Media'} in ${applicationTitle}`
: undefined;
let attachment_base64;
let attachment_type;
if (payload.image) {
const imagePayload = await this.getImagePayload(payload.image);
if (imagePayload.attachment_base64 && imagePayload.attachment_type) {
attachment_base64 = imagePayload.attachment_base64;
attachment_type = imagePayload.attachment_type;
}
}
return {
title,
message,
@@ -129,6 +170,8 @@ class PushoverAgent
url_title,
priority,
html: 1,
attachment_base64,
attachment_type,
};
}
@@ -138,7 +181,10 @@ class PushoverAgent
): Promise<boolean> {
const settings = this.getSettings();
const endpoint = 'https://api.pushover.net/1/messages.json';
const notificationPayload = this.getNotificationPayload(type, payload);
const notificationPayload = await this.getNotificationPayload(
type,
payload
);
// Send system notification
if (

View File

@@ -12,6 +12,7 @@ import type { Library } from '@server/lib/settings';
import { getSettings } from '@server/lib/settings';
import logger from '@server/logger';
import AsyncLock from '@server/utils/asyncLock';
import { getHostname } from '@server/utils/getHostname';
import { randomUUID as uuid } from 'crypto';
import { uniqWith } from 'lodash';
@@ -83,13 +84,17 @@ class JellyfinScanner {
}
const has4k = metadata.MediaSources?.some((MediaSource) => {
return MediaSource.MediaStreams.some((MediaStream) => {
return MediaSource.MediaStreams.filter(
(MediaStream) => MediaStream.Type === 'Video'
).some((MediaStream) => {
return (MediaStream.Width ?? 0) > 2000;
});
});
const hasOtherResolution = metadata.MediaSources?.some((MediaSource) => {
return MediaSource.MediaStreams.some((MediaStream) => {
return MediaSource.MediaStreams.filter(
(MediaStream) => MediaStream.Type === 'Video'
).some((MediaStream) => {
return (MediaStream.Width ?? 0) <= 2000;
});
});
@@ -590,8 +595,10 @@ class JellyfinScanner {
return this.log('No admin configured. Jellyfin sync skipped.', 'warn');
}
const hostname = getHostname();
this.jfClient = new JellyfinAPI(
settings.jellyfin.hostname ?? '',
hostname,
admin.jellyfinAuthToken,
admin.jellyfinDeviceId
);

View File

@@ -1,10 +1,11 @@
import { MediaServerType } from '@server/constants/server';
import { Permission } from '@server/lib/permissions';
import { runMigrations } from '@server/lib/settings/migrator';
import { randomUUID } from 'crypto';
import fs from 'fs';
import { merge } from 'lodash';
import path from 'path';
import webpush from 'web-push';
import { Permission } from './permissions';
export interface Library {
id: string;
@@ -38,7 +39,10 @@ export interface PlexSettings {
export interface JellyfinSettings {
name: string;
hostname: string;
ip: string;
port: number;
useSsl?: boolean;
urlBase?: string;
externalHostname?: string;
jellyfinForgotPasswordUrl?: string;
libraries: Library[];
@@ -130,7 +134,6 @@ interface FullPublicSettings extends PublicSettings {
region: string;
originalLanguage: string;
mediaServerType: number;
jellyfinHost?: string;
jellyfinExternalHost?: string;
jellyfinForgotPasswordUrl?: string;
jellyfinServerName?: string;
@@ -274,7 +277,7 @@ export type JobId =
| 'image-cache-cleanup'
| 'availability-sync';
interface AllSettings {
export interface AllSettings {
clientId: string;
vapidPublic: string;
vapidPrivate: string;
@@ -291,7 +294,7 @@ interface AllSettings {
const SETTINGS_PATH = process.env.CONFIG_DIRECTORY
? `${process.env.CONFIG_DIRECTORY}/settings.json`
: path.join(__dirname, '../../config/settings.json');
: path.join(__dirname, '../../../config/settings.json');
class Settings {
private data: AllSettings;
@@ -331,7 +334,10 @@ class Settings {
},
jellyfin: {
name: '',
hostname: '',
ip: '',
port: 8096,
useSsl: false,
urlBase: '',
externalHostname: '',
jellyfinForgotPasswordUrl: '',
libraries: [],
@@ -547,8 +553,6 @@ class Settings {
region: this.data.main.region,
originalLanguage: this.data.main.originalLanguage,
mediaServerType: this.main.mediaServerType,
jellyfinHost: this.jellyfin.hostname,
jellyfinExternalHost: this.jellyfin.externalHostname,
partialRequestsEnabled: this.data.main.partialRequestsEnabled,
cacheImages: this.data.main.cacheImages,
vapidPublic: this.vapidPublic,
@@ -637,7 +641,11 @@ class Settings {
const data = fs.readFileSync(SETTINGS_PATH, 'utf-8');
if (data) {
this.data = merge(this.data, JSON.parse(data));
const parsedJson = JSON.parse(data);
this.data = runMigrations(parsedJson);
this.data = merge(this.data, parsedJson);
this.save();
}
return this;

View File

@@ -0,0 +1,30 @@
import type { AllSettings } from '@server/lib/settings';
const migrateHostname = (settings: any): AllSettings => {
const oldJellyfinSettings = settings.jellyfin;
if (oldJellyfinSettings && oldJellyfinSettings.hostname) {
const { hostname } = oldJellyfinSettings;
const protocolMatch = hostname.match(/^(https?):\/\//i);
const useSsl = protocolMatch && protocolMatch[1].toLowerCase() === 'https';
const remainingUrl = hostname.replace(/^(https?):\/\//i, '');
const urlMatch = remainingUrl.match(/^([^:]+)(:([0-9]+))?(\/.*)?$/);
delete oldJellyfinSettings.hostname;
if (urlMatch) {
const [, ip, , port, urlBase] = urlMatch;
settings.jellyfin = {
...settings.jellyfin,
ip,
port: port || (useSsl ? 443 : 80),
useSsl,
urlBase: urlBase ? urlBase.replace(/\/$/, '') : '',
};
}
}
if (settings.jellyfin && settings.jellyfin.hostname) {
delete settings.jellyfin.hostname;
}
return settings;
};
export default migrateHostname;

View File

@@ -0,0 +1,21 @@
import type { AllSettings } from '@server/lib/settings';
import fs from 'fs';
import path from 'path';
const migrationsDir = path.join(__dirname, 'migrations');
export const runMigrations = (settings: AllSettings): AllSettings => {
const migrations = fs
.readdirSync(migrationsDir)
.filter((file) => file.endsWith('.js') || file.endsWith('.ts'))
// eslint-disable-next-line @typescript-eslint/no-var-requires
.map((file) => require(path.join(migrationsDir, file)).default);
let migrated = settings;
for (const migration of migrations) {
migrated = migration(migrated);
}
return migrated;
};

View File

@@ -11,9 +11,11 @@ import { getSettings } from '@server/lib/settings';
import logger from '@server/logger';
import { isAuthenticated } from '@server/middleware/auth';
import { ApiError } from '@server/types/error';
import { getHostname } from '@server/utils/getHostname';
import * as EmailValidator from 'email-validator';
import { Router } from 'express';
import gravatarUrl from 'gravatar-url';
import net from 'net';
const authRoutes = Router();
@@ -221,30 +223,39 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
username?: string;
password?: string;
hostname?: string;
port?: number;
urlBase?: string;
useSsl?: boolean;
email?: string;
};
//Make sure jellyfin login is enabled, but only if jellyfin is not already configured
if (
settings.main.mediaServerType !== MediaServerType.JELLYFIN &&
settings.jellyfin.hostname !== ''
settings.main.mediaServerType != MediaServerType.NOT_CONFIGURED
) {
return res.status(500).json({ error: 'Jellyfin login is disabled' });
} else if (!body.username) {
return res.status(500).json({ error: 'You must provide an username' });
} else if (settings.jellyfin.hostname !== '' && body.hostname) {
} else if (settings.jellyfin.ip !== '' && body.hostname) {
return res
.status(500)
.json({ error: 'Jellyfin hostname already configured' });
} else if (settings.jellyfin.hostname === '' && !body.hostname) {
} else if (settings.jellyfin.ip === '' && !body.hostname) {
return res.status(500).json({ error: 'No hostname provided.' });
}
try {
const hostname =
settings.jellyfin.hostname !== ''
? settings.jellyfin.hostname
: body.hostname ?? '';
settings.jellyfin.ip !== ''
? getHostname()
: getHostname({
useSsl: body.useSsl,
ip: body.hostname,
port: body.port,
urlBase: body.urlBase,
});
const { externalHostname } = getSettings().jellyfin;
// Try to find deviceId that corresponds to jellyfin user, else generate a new one
@@ -260,22 +271,29 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
'base64'
);
}
// First we need to attempt to log the user in to jellyfin
const jellyfinserver = new JellyfinAPI(hostname ?? '', undefined, deviceId);
let jellyfinHost =
const jellyfinserver = new JellyfinAPI(hostname, undefined, deviceId);
const jellyfinHost =
externalHostname && externalHostname.length > 0
? externalHostname
: hostname;
jellyfinHost = jellyfinHost.endsWith('/')
? jellyfinHost.slice(0, -1)
: jellyfinHost;
const ip = req.ip;
let clientIp;
if (ip) {
if (net.isIPv4(ip)) {
clientIp = ip;
} else if (net.isIPv6(ip)) {
clientIp = ip.startsWith('::ffff:') ? ip.substring(7) : ip;
}
}
const ip = req.ip ? req.ip.split(':').reverse()[0] : undefined;
const account = await jellyfinserver.login(
body.username,
body.password,
ip
clientIp
);
// Next let's see if the user already exists
@@ -317,8 +335,11 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
const serverName = await jellyfinserver.getServerName();
settings.jellyfin.name = serverName;
settings.jellyfin.hostname = body.hostname ?? '';
settings.jellyfin.serverId = account.User.ServerId;
settings.jellyfin.ip = body.hostname ?? '';
settings.jellyfin.port = body.port ?? 8096;
settings.jellyfin.urlBase = body.urlBase ?? '';
settings.jellyfin.useSsl = body.useSsl ?? false;
settings.save();
startJobs();
@@ -433,7 +454,12 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
label: 'Auth',
error: e.errorCode,
status: e.statusCode,
hostname: body.hostname,
hostname: getHostname({
useSsl: body.useSsl,
ip: body.hostname,
port: body.port,
urlBase: body.urlBase,
}),
}
);
return next({

View File

@@ -12,7 +12,7 @@ collectionRoutes.get<{ id: string }>('/:id', async (req, res, next) => {
try {
const collection = await tmdb.getCollection({
collectionId: Number(req.params.id),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const media = await Media.getRelatedMedia(

View File

@@ -166,7 +166,7 @@ discoverRoutes.get<{ language: string }>(
const data = await tmdb.getDiscoverMovies({
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
originalLanguage: req.params.language,
});
@@ -211,7 +211,7 @@ discoverRoutes.get<{ genreId: string }>(
try {
const genres = await tmdb.getMovieGenres({
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const genre = genres.find(
@@ -224,7 +224,7 @@ discoverRoutes.get<{ genreId: string }>(
const data = await tmdb.getDiscoverMovies({
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
genre: req.params.genreId as string,
});
@@ -272,7 +272,7 @@ discoverRoutes.get<{ studioId: string }>(
const data = await tmdb.getDiscoverMovies({
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
studio: req.params.studioId as string,
});
@@ -322,7 +322,7 @@ discoverRoutes.get('/movies/upcoming', async (req, res, next) => {
try {
const data = await tmdb.getDiscoverMovies({
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
primaryReleaseDateGte: date,
});
@@ -447,7 +447,7 @@ discoverRoutes.get<{ language: string }>(
const data = await tmdb.getDiscoverTv({
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
originalLanguage: req.params.language,
});
@@ -492,7 +492,7 @@ discoverRoutes.get<{ genreId: string }>(
try {
const genres = await tmdb.getTvGenres({
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const genre = genres.find(
@@ -505,7 +505,7 @@ discoverRoutes.get<{ genreId: string }>(
const data = await tmdb.getDiscoverTv({
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
genre: req.params.genreId,
});
@@ -553,7 +553,7 @@ discoverRoutes.get<{ networkId: string }>(
const data = await tmdb.getDiscoverTv({
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
network: Number(req.params.networkId),
});
@@ -603,7 +603,7 @@ discoverRoutes.get('/tv/upcoming', async (req, res, next) => {
try {
const data = await tmdb.getDiscoverTv({
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
firstAirDateGte: date,
});
@@ -643,7 +643,7 @@ discoverRoutes.get('/trending', async (req, res, next) => {
try {
const data = await tmdb.getAllTrending({
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const media = await Media.getRelatedMedia(
@@ -698,7 +698,7 @@ discoverRoutes.get<{ keywordId: string }>(
const data = await tmdb.getMoviesByKeyword({
keywordId: Number(req.params.keywordId),
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const media = await Media.getRelatedMedia(
@@ -743,7 +743,7 @@ discoverRoutes.get<{ language: string }, GenreSliderItem[]>(
const mappedGenres: GenreSliderItem[] = [];
const genres = await tmdb.getMovieGenres({
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
await Promise.all(
@@ -787,7 +787,7 @@ discoverRoutes.get<{ language: string }, GenreSliderItem[]>(
const mappedGenres: GenreSliderItem[] = [];
const genres = await tmdb.getTvGenres({
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
await Promise.all(

View File

@@ -237,7 +237,7 @@ router.get('/genres/movie', isAuthenticated(), async (req, res, next) => {
try {
const genres = await tmdb.getMovieGenres({
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
return res.status(200).json(genres);
@@ -258,7 +258,7 @@ router.get('/genres/tv', isAuthenticated(), async (req, res, next) => {
try {
const genres = await tmdb.getTvGenres({
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
return res.status(200).json(genres);

View File

@@ -17,7 +17,7 @@ movieRoutes.get('/:id', async (req, res, next) => {
try {
const tmdbMovie = await tmdb.getMovie({
movieId: Number(req.params.id),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const media = await Media.getMedia(tmdbMovie.id, MediaType.MOVIE);
@@ -43,7 +43,7 @@ movieRoutes.get('/:id/recommendations', async (req, res, next) => {
const results = await tmdb.getMovieRecommendations({
movieId: Number(req.params.id),
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const media = await Media.getRelatedMedia(
@@ -85,7 +85,7 @@ movieRoutes.get('/:id/similar', async (req, res, next) => {
const results = await tmdb.getMovieSimilar({
movieId: Number(req.params.id),
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const media = await Media.getRelatedMedia(

View File

@@ -16,7 +16,7 @@ personRoutes.get('/:id', async (req, res, next) => {
try {
const person = await tmdb.getPerson({
personId: Number(req.params.id),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
return res.status(200).json(mapPersonDetails(person));
} catch (e) {
@@ -38,7 +38,7 @@ personRoutes.get('/:id/combined_credits', async (req, res, next) => {
try {
const combinedCredits = await tmdb.getPersonCombinedCredits({
personId: Number(req.params.id),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const castMedia = await Media.getRelatedMedia(

View File

@@ -20,7 +20,7 @@ searchRoutes.get('/', async (req, res, next) => {
.match(searchProvider.pattern) as RegExpMatchArray;
results = await searchProvider.search({
id,
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
query: queryString,
});
} else {
@@ -29,7 +29,7 @@ searchRoutes.get('/', async (req, res, next) => {
results = await tmdb.searchMulti({
query: queryString,
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
}

View File

@@ -2,6 +2,7 @@ import JellyfinAPI from '@server/api/jellyfin';
import PlexAPI from '@server/api/plexapi';
import PlexTvAPI from '@server/api/plextv';
import TautulliAPI from '@server/api/tautulli';
import { ApiErrorCode } from '@server/constants/error';
import { getRepository } from '@server/datasource';
import Media from '@server/entity/Media';
import { MediaRequest } from '@server/entity/MediaRequest';
@@ -24,8 +25,10 @@ import { getSettings } from '@server/lib/settings';
import logger from '@server/logger';
import { isAuthenticated } from '@server/middleware/auth';
import discoverSettingRoutes from '@server/routes/settings/discover';
import { ApiError } from '@server/types/error';
import { appDataPath } from '@server/utils/appDataVolume';
import { getAppVersion } from '@server/utils/appVersion';
import { getHostname } from '@server/utils/getHostname';
import { Router } from 'express';
import rateLimit from 'express-rate-limit';
import fs from 'fs';
@@ -252,11 +255,59 @@ settingsRoutes.get('/jellyfin', (_req, res) => {
res.status(200).json(settings.jellyfin);
});
settingsRoutes.post('/jellyfin', (req, res) => {
settingsRoutes.post('/jellyfin', async (req, res, next) => {
const userRepository = getRepository(User);
const settings = getSettings();
settings.jellyfin = merge(settings.jellyfin, req.body);
settings.save();
try {
const admin = await userRepository.findOneOrFail({
where: { id: 1 },
select: ['id', 'jellyfinAuthToken', 'jellyfinUserId', 'jellyfinDeviceId'],
order: { id: 'ASC' },
});
const tempJellyfinSettings = { ...settings.jellyfin, ...req.body };
const jellyfinClient = new JellyfinAPI(
getHostname(tempJellyfinSettings),
admin.jellyfinAuthToken ?? '',
admin.jellyfinDeviceId ?? ''
);
const result = await jellyfinClient.getSystemInfo();
if (!result?.Id) {
throw new ApiError(result?.status, ApiErrorCode.InvalidUrl);
}
Object.assign(settings.jellyfin, req.body);
settings.jellyfin.serverId = result.Id;
settings.jellyfin.name = result.ServerName;
settings.save();
} catch (e) {
if (e instanceof ApiError) {
logger.error('Something went wrong testing Jellyfin connection', {
label: 'API',
status: e.statusCode,
errorMessage: ApiErrorCode.InvalidUrl,
});
return next({
status: e.statusCode,
message: ApiErrorCode.InvalidUrl,
});
} else {
logger.error('Something went wrong', {
label: 'API',
errorMessage: e.message,
});
return next({
status: e.statusCode ?? 500,
message: ApiErrorCode.Unknown,
});
}
}
return res.status(200).json(settings.jellyfin);
});
@@ -272,7 +323,7 @@ settingsRoutes.get('/jellyfin/library', async (req, res, next) => {
order: { id: 'ASC' },
});
const jellyfinClient = new JellyfinAPI(
settings.jellyfin.hostname ?? '',
getHostname(),
admin.jellyfinAuthToken ?? '',
admin.jellyfinDeviceId ?? ''
);
@@ -288,10 +339,13 @@ settingsRoutes.get('/jellyfin/library', async (req, res, next) => {
// Automatic Library grouping is not supported when user views are used to get library
if (account.Configuration.GroupedFolders.length > 0) {
return next({ status: 501, message: 'SYNC_ERROR_GROUPED_FOLDERS' });
return next({
status: 501,
message: ApiErrorCode.SyncErrorGroupedFolders,
});
}
return next({ status: 404, message: 'SYNC_ERROR_NO_LIBRARIES' });
return next({ status: 404, message: ApiErrorCode.SyncErrorNoLibraries });
}
const newLibraries: Library[] = libraries.map((library) => {
@@ -322,16 +376,12 @@ settingsRoutes.get('/jellyfin/library', async (req, res, next) => {
});
settingsRoutes.get('/jellyfin/users', async (req, res) => {
const settings = getSettings();
const { hostname, externalHostname } = getSettings().jellyfin;
let jellyfinHost =
const { externalHostname } = getSettings().jellyfin;
const jellyfinHost =
externalHostname && externalHostname.length > 0
? externalHostname
: hostname;
: getHostname();
jellyfinHost = jellyfinHost.endsWith('/')
? jellyfinHost.slice(0, -1)
: jellyfinHost;
const userRepository = getRepository(User);
const admin = await userRepository.findOneOrFail({
select: ['id', 'jellyfinAuthToken', 'jellyfinDeviceId', 'jellyfinUserId'],
@@ -339,7 +389,6 @@ settingsRoutes.get('/jellyfin/users', async (req, res) => {
order: { id: 'ASC' },
});
const jellyfinClient = new JellyfinAPI(
settings.jellyfin.hostname ?? '',
admin.jellyfinAuthToken ?? '',
admin.jellyfinDeviceId ?? ''
);

View File

@@ -14,7 +14,7 @@ tvRoutes.get('/:id', async (req, res, next) => {
try {
const tv = await tmdb.getTvShow({
tvId: Number(req.params.id),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const media = await Media.getMedia(tv.id, MediaType.TV);
@@ -40,7 +40,7 @@ tvRoutes.get('/:id/season/:seasonNumber', async (req, res, next) => {
const season = await tmdb.getTvSeason({
tvId: Number(req.params.id),
seasonNumber: Number(req.params.seasonNumber),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
return res.status(200).json(mapSeasonWithEpisodes(season));
@@ -65,7 +65,7 @@ tvRoutes.get('/:id/recommendations', async (req, res, next) => {
const results = await tmdb.getTvRecommendations({
tvId: Number(req.params.id),
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const media = await Media.getRelatedMedia(
@@ -106,7 +106,7 @@ tvRoutes.get('/:id/similar', async (req, res, next) => {
const results = await tmdb.getTvSimilar({
tvId: Number(req.params.id),
page: Number(req.query.page),
language: req.locale ?? (req.query.language as string),
language: (req.query.language as string) ?? req.locale,
});
const media = await Media.getRelatedMedia(

View File

@@ -20,6 +20,7 @@ import { hasPermission, Permission } from '@server/lib/permissions';
import { getSettings } from '@server/lib/settings';
import logger from '@server/logger';
import { isAuthenticated } from '@server/middleware/auth';
import { getHostname } from '@server/utils/getHostname';
import { Router } from 'express';
import gravatarUrl from 'gravatar-url';
import { findIndex, sortBy } from 'lodash';
@@ -496,7 +497,6 @@ router.post(
order: { id: 'ASC' },
});
const jellyfinClient = new JellyfinAPI(
settings.jellyfin.hostname ?? '',
admin.jellyfinAuthToken ?? '',
admin.jellyfinDeviceId ?? ''
);
@@ -504,15 +504,14 @@ router.post(
//const jellyfinUsersResponse = await jellyfinClient.getUsers();
const createdUsers: User[] = [];
const { hostname, externalHostname } = getSettings().jellyfin;
let jellyfinHost =
const { externalHostname } = getSettings().jellyfin;
const hostname = getHostname();
const jellyfinHost =
externalHostname && externalHostname.length > 0
? externalHostname
: hostname;
jellyfinHost = jellyfinHost.endsWith('/')
? jellyfinHost.slice(0, -1)
: jellyfinHost;
jellyfinClient.setUserId(admin.jellyfinUserId ?? '');
const jellyfinUsers = await jellyfinClient.getUsers();

View File

@@ -0,0 +1,18 @@
import { getSettings } from '@server/lib/settings';
interface HostnameParams {
useSsl?: boolean;
ip?: string;
port?: number;
urlBase?: string;
}
export const getHostname = (params?: HostnameParams): string => {
const settings = params ? params : getSettings().jellyfin;
const { useSsl, ip, port, urlBase } = settings;
const hostname = `${useSsl ? 'https' : 'http'}://${ip}:${port}${urlBase}`;
return hostname;
};

View File

@@ -14,7 +14,10 @@ import * as Yup from 'yup';
const messages = defineMessages({
username: 'Username',
password: 'Password',
host: '{mediaServerName} URL',
hostname: '{mediaServerName} URL',
port: 'Port',
enablessl: 'Use SSL',
urlBase: 'URL Base',
email: 'Email',
emailtooltip:
'Address does not need to be associated with your {mediaServerName} instance.',
@@ -24,6 +27,11 @@ const messages = defineMessages({
validationemailformat: 'Valid email required',
validationusernamerequired: 'Username required',
validationpasswordrequired: 'Password required',
validationHostnameRequired: 'You must provide a valid hostname or IP address',
validationPortRequired: 'You must provide a valid port number',
validationUrlTrailingSlash: 'URL must not end in a trailing slash',
validationUrlBaseLeadingSlash: 'URL base must have a leading slash',
validationUrlBaseTrailingSlash: 'URL base must not end in a trailing slash',
loginerror: 'Something went wrong while trying to sign in.',
adminerror: 'You must use an admin account to sign in.',
credentialerror: 'The username or password is incorrect.',
@@ -51,16 +59,23 @@ const JellyfinLogin: React.FC<JellyfinLoginProps> = ({
if (initial) {
const LoginSchema = Yup.object().shape({
host: Yup.string()
hostname: Yup.string().required(
intl.formatMessage(messages.validationhostrequired, {
mediaServerName:
publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? 'Emby' : 'Jellyfin',
})
),
port: Yup.number().required(
intl.formatMessage(messages.validationPortRequired)
),
urlBase: Yup.string()
.matches(
/^(?:(?:(?:https?):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/,
intl.formatMessage(messages.validationhostformat)
/^(\/[^/].*[^/]$)/,
intl.formatMessage(messages.validationUrlBaseLeadingSlash)
)
.required(
intl.formatMessage(messages.validationhostrequired, {
mediaServerName:
publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? 'Emby' : 'Jellyfin',
})
.matches(
/^(.*[^/])$/,
intl.formatMessage(messages.validationUrlBaseTrailingSlash)
),
email: Yup.string()
.email(intl.formatMessage(messages.validationemailformat))
@@ -75,12 +90,16 @@ const JellyfinLogin: React.FC<JellyfinLoginProps> = ({
mediaServerName:
publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? 'Emby' : 'Jellyfin',
};
return (
<Formik
initialValues={{
username: '',
password: '',
host: '',
hostname: '',
port: 8096,
useSsl: false,
urlBase: '',
email: '',
}}
validationSchema={LoginSchema}
@@ -89,7 +108,10 @@ const JellyfinLogin: React.FC<JellyfinLoginProps> = ({
await axios.post('/api/v1/auth/jellyfin', {
username: values.username,
password: values.password,
hostname: values.host,
hostname: values.hostname,
port: values.port,
useSsl: values.useSsl,
urlBase: values.urlBase,
email: values.email,
});
} catch (e) {
@@ -121,32 +143,100 @@ const JellyfinLogin: React.FC<JellyfinLoginProps> = ({
}
}}
>
{({ errors, touched, isSubmitting, isValid }) => (
{({
errors,
touched,
values,
setFieldValue,
isSubmitting,
isValid,
}) => (
<Form>
<div className="sm:border-t sm:border-gray-800">
<label htmlFor="host" className="text-label">
{intl.formatMessage(messages.host, mediaServerFormatValues)}
<div className="flex flex-col sm:flex-row sm:gap-4">
<div className="w-full">
<label htmlFor="hostname" className="text-label">
{intl.formatMessage(
messages.hostname,
mediaServerFormatValues
)}
</label>
<div className="mt-1 mb-2 sm:col-span-2 sm:mb-0 sm:mt-0">
<div className="flex rounded-md shadow-sm">
<span className="inline-flex cursor-default items-center rounded-l-md border border-r-0 border-gray-500 bg-gray-800 px-3 text-gray-100 sm:text-sm">
{values.useSsl ? 'https://' : 'http://'}
</span>
<Field
id="hostname"
name="hostname"
type="text"
className="rounded-r-only flex-1"
placeholder={intl.formatMessage(
messages.hostname,
mediaServerFormatValues
)}
/>
</div>
{errors.hostname && touched.hostname && (
<div className="error">{errors.hostname}</div>
)}
</div>
</div>
<div className="flex-1">
<label htmlFor="port" className="text-label">
{intl.formatMessage(messages.port)}
</label>
<div className="mt-1 sm:mt-0">
<Field
id="port"
name="port"
inputMode="numeric"
type="text"
className="short flex-1"
placeholder={intl.formatMessage(messages.port)}
/>
{errors.port && touched.port && (
<div className="error">{errors.port}</div>
)}
</div>
</div>
</div>
<label htmlFor="useSsl" className="text-label mt-2">
{intl.formatMessage(messages.enablessl)}
</label>
<div className="mt-1 mb-2 sm:col-span-2">
<div className="flex rounded-md shadow-sm">
<Field
id="useSsl"
name="useSsl"
type="checkbox"
onChange={() => {
setFieldValue('useSsl', !values.useSsl);
setFieldValue('port', values.useSsl ? 8096 : 443);
}}
/>
</div>
</div>
<label htmlFor="urlBase" className="text-label mt-1">
{intl.formatMessage(messages.urlBase)}
</label>
<div className="mt-1 mb-2 sm:col-span-2 sm:mt-0">
<div className="flex rounded-md shadow-sm">
<Field
id="host"
name="host"
type="text"
placeholder={intl.formatMessage(
messages.host,
mediaServerFormatValues
)}
inputMode="url"
id="urlBase"
name="urlBase"
placeholder={intl.formatMessage(messages.urlBase)}
/>
</div>
{errors.host && touched.host && (
<div className="error">{errors.host}</div>
{errors.urlBase && touched.urlBase && (
<div className="error">{errors.urlBase}</div>
)}
</div>
<label
htmlFor="email"
className="text-label"
style={{ display: 'inline-flex' }}
className="text-label inline-flex gap-1 align-middle"
>
{intl.formatMessage(messages.email)}
<span className="label-tip">
@@ -162,7 +252,7 @@ const JellyfinLogin: React.FC<JellyfinLoginProps> = ({
</Tooltip>
</span>
</label>
<div className="mt-1 mb-2 sm:col-span-2 sm:mt-0">
<div className="mt-1 sm:col-span-2 sm:mb-2 sm:mt-0">
<div className="flex rounded-md shadow-sm">
<Field
id="email"

View File

@@ -434,33 +434,38 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
</Button>
</Tooltip>
)}
{hasPermission(Permission.MANAGE_REQUESTS) && data.mediaInfo && (
<Tooltip content={intl.formatMessage(messages.managemovie)}>
<Button
buttonType="ghost"
onClick={() => setShowManager(true)}
className="relative ml-2 first:ml-0"
>
<CogIcon className="!mr-0" />
{hasPermission(
[Permission.MANAGE_ISSUES, Permission.VIEW_ISSUES],
{
type: 'or',
}
) &&
(
data.mediaInfo?.issues.filter(
(issue) => issue.status === IssueStatus.OPEN
) ?? []
).length > 0 && (
<>
<div className="absolute -right-1 -top-1 h-3 w-3 rounded-full bg-red-600" />
<div className="absolute -right-1 -top-1 h-3 w-3 animate-ping rounded-full bg-red-600" />
</>
)}
</Button>
</Tooltip>
)}
{hasPermission(Permission.MANAGE_REQUESTS) &&
data.mediaInfo &&
(data.mediaInfo.jellyfinMediaId ||
data.mediaInfo.jellyfinMediaId4k ||
data.mediaInfo.status !== MediaStatus.UNKNOWN ||
data.mediaInfo.status4k !== MediaStatus.UNKNOWN) && (
<Tooltip content={intl.formatMessage(messages.managemovie)}>
<Button
buttonType="ghost"
onClick={() => setShowManager(true)}
className="relative ml-2 first:ml-0"
>
<CogIcon className="!mr-0" />
{hasPermission(
[Permission.MANAGE_ISSUES, Permission.VIEW_ISSUES],
{
type: 'or',
}
) &&
(
data.mediaInfo?.issues.filter(
(issue) => issue.status === IssueStatus.OPEN
) ?? []
).length > 0 && (
<>
<div className="absolute -right-1 -top-1 h-3 w-3 rounded-full bg-red-600" />
<div className="absolute -right-1 -top-1 h-3 w-3 animate-ping rounded-full bg-red-600" />
</>
)}
</Button>
</Tooltip>
)}
</div>
</div>
<div className="media-overview">
@@ -530,7 +535,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
}}
/>
</div>
<div className="relative z-10 flex h-14 items-center justify-between p-4 text-gray-200 transition duration-300 group-hover:text-white">
<div className="relative z-10 flex h-full items-center justify-between p-4 text-gray-200 transition duration-300 group-hover:text-white">
<div>{data.collection.name}</div>
<Button buttonSize="sm">
{intl.formatMessage(globalMessages.view)}

View File

@@ -210,13 +210,24 @@ const RequestBlock = ({ request, onUpdate }: RequestBlockProps) => {
<Tooltip content={intl.formatMessage(messages.requestdate)}>
<CalendarIcon className="mr-1.5 h-5 w-5 flex-shrink-0" />
</Tooltip>
<span>
{intl.formatDate(request.createdAt, {
<Tooltip
content={intl.formatDate(request.createdAt, {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
})}
</span>
>
<span>
{intl.formatDate(request.createdAt, {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</span>
</Tooltip>
</div>
</div>
{(request.seasons ?? []).length > 0 && (

View File

@@ -4,6 +4,7 @@ import LoadingSpinner from '@app/components/Common/LoadingSpinner';
import LibraryItem from '@app/components/Settings/LibraryItem';
import globalMessages from '@app/i18n/globalMessages';
import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline';
import { ApiErrorCode } from '@server/constants/error';
import type { JellyfinSettings } from '@server/lib/settings';
import axios from 'axios';
import { Field, Formik } from 'formik';
@@ -32,14 +33,17 @@ const messages = defineMessages({
jellyfinSettingsDescription:
'Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL. A custom password reset URL can also be set for {mediaServerName} login, in case you would like to redirect to a different password reset page.',
externalUrl: 'External URL',
internalUrl: 'Internal URL',
hostname: 'Hostname or IP Address',
port: 'Port',
enablessl: 'Use SSL',
urlBase: 'URL Base',
jellyfinForgotPasswordUrl: 'Forgot Password URL',
jellyfinSyncFailedNoLibrariesFound: 'No libraries were found',
jellyfinSyncFailedAutomaticGroupedFolders:
'Custom authentication with Automatic Library Grouping not supported',
jellyfinSyncFailedGenericError:
'Something went wrong while syncing libraries',
validationUrl: 'You must provide a valid URL',
invalidurlerror: 'Unable to connect to {mediaServerName} server.',
syncing: 'Syncing',
syncJellyfin: 'Sync Libraries',
manualscanJellyfin: 'Manual Library Scan',
@@ -50,6 +54,12 @@ const messages = defineMessages({
librariesRemaining: 'Libraries Remaining: {count}',
startscan: 'Start Scan',
cancelscan: 'Cancel Scan',
validationUrl: 'You must provide a valid URL',
validationHostnameRequired: 'You must provide a valid hostname or IP address',
validationPortRequired: 'You must provide a valid port number',
validationUrlTrailingSlash: 'URL must not end in a trailing slash',
validationUrlBaseLeadingSlash: 'URL base must have a leading slash',
validationUrlBaseTrailingSlash: 'URL base must not end in a trailing slash',
});
interface Library {
@@ -65,6 +75,7 @@ interface SyncStatus {
currentLibrary?: Library;
libraries: Library[];
}
interface SettingsJellyfinProps {
showAdvancedSettings?: boolean;
onComplete?: () => void;
@@ -93,18 +104,50 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
const { publicRuntimeConfig } = getConfig();
const JellyfinSettingsSchema = Yup.object().shape({
jellyfinExternalUrl: Yup.string().matches(
/^(https?:\/\/)?(?:[\w-]+\.)*[\w-]+(?::\d{2,5})?(?:\/[\w-]+)*(?:\/)?$/gm,
intl.formatMessage(messages.validationUrl)
),
jellyfinInternalUrl: Yup.string().matches(
/^(https?:\/\/)?(?:[\w-]+\.)*[\w-]+(?::\d{2,5})?(?:\/[\w-]+)*(?:\/)?$/gm,
intl.formatMessage(messages.validationUrl)
),
jellyfinForgotPasswordUrl: Yup.string().matches(
/^(https?:\/\/)?(?:[\w-]+\.)*[\w-]+(?::\d{2,5})?(?:\/[\w-]+)*(?:\/)?$/gm,
intl.formatMessage(messages.validationUrl)
),
hostname: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationHostnameRequired))
.matches(
/^(((([a-z]|\d|_|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*)?([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])):((([a-z]|\d|_|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*)?([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))@)?(([a-z]|\d|_|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*)?([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])$/i,
intl.formatMessage(messages.validationHostnameRequired)
),
port: Yup.number().when(['hostname'], {
is: (value: unknown) => !!value,
then: Yup.number()
.typeError(intl.formatMessage(messages.validationPortRequired))
.nullable()
.required(intl.formatMessage(messages.validationPortRequired)),
otherwise: Yup.number()
.typeError(intl.formatMessage(messages.validationPortRequired))
.nullable(),
}),
urlBase: Yup.string()
.test(
'leading-slash',
intl.formatMessage(messages.validationUrlBaseLeadingSlash),
(value) => !value || value.startsWith('/')
)
.test(
'trailing-slash',
intl.formatMessage(messages.validationUrlBaseTrailingSlash),
(value) => !value || !value.endsWith('/')
),
jellyfinExternalUrl: Yup.string()
.nullable()
.url(intl.formatMessage(messages.validationUrl))
.test(
'no-trailing-slash',
intl.formatMessage(messages.validationUrlTrailingSlash),
(value) => !value || !value.endsWith('/')
),
jellyfinForgotPasswordUrl: Yup.string()
.nullable()
.url(intl.formatMessage(messages.validationUrl))
.test(
'no-trailing-slash',
intl.formatMessage(messages.validationUrlTrailingSlash),
(value) => !value || !value.endsWith('/')
),
});
const activeLibraries =
@@ -394,7 +437,10 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
</div>
<Formik
initialValues={{
jellyfinInternalUrl: data?.hostname || '',
hostname: data?.ip,
port: data?.port ?? 8096,
useSsl: data?.useSsl,
urlBase: data?.urlBase || '',
jellyfinExternalUrl: data?.externalHostname || '',
jellyfinForgotPasswordUrl: data?.jellyfinForgotPasswordUrl || '',
}}
@@ -402,7 +448,10 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
onSubmit={async (values) => {
try {
await axios.post('/api/v1/settings/jellyfin', {
hostname: values.jellyfinInternalUrl,
ip: values.hostname,
port: Number(values.port),
useSsl: values.useSsl,
urlBase: values.urlBase,
externalHostname: values.jellyfinExternalUrl,
jellyfinForgotPasswordUrl: values.jellyfinForgotPasswordUrl,
} as JellyfinSettings);
@@ -420,44 +469,127 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
}
);
} catch (e) {
addToast(
intl.formatMessage(messages.jellyfinSettingsFailure, {
mediaServerName:
publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
? 'Emby'
: 'Jellyfin',
}),
{
autoDismiss: true,
appearance: 'error',
}
);
if (e.response?.data?.message === ApiErrorCode.InvalidUrl) {
addToast(
intl.formatMessage(messages.invalidurlerror, {
mediaServerName:
publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
? 'Emby'
: 'Jellyfin',
}),
{
autoDismiss: true,
appearance: 'error',
}
);
} else {
addToast(
intl.formatMessage(messages.jellyfinSettingsFailure, {
mediaServerName:
publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
? 'Emby'
: 'Jellyfin',
}),
{
autoDismiss: true,
appearance: 'error',
}
);
}
} finally {
revalidate();
}
}}
>
{({ errors, touched, handleSubmit, isSubmitting, isValid }) => {
{({
errors,
touched,
values,
setFieldValue,
handleSubmit,
isSubmitting,
isValid,
}) => {
return (
<form className="section" onSubmit={handleSubmit}>
<div className="form-row">
<label htmlFor="jellyfinInternalUrl" className="text-label">
{intl.formatMessage(messages.internalUrl)}
<label htmlFor="hostname" className="text-label">
{intl.formatMessage(messages.hostname)}
<span className="text-red-500">*</span>
</label>
<div className="form-input-area">
<div className="form-input-field">
<span className="inline-flex cursor-default items-center rounded-l-md border border-r-0 border-gray-500 bg-gray-800 px-3 text-gray-100 sm:text-sm">
{values.useSsl ? 'https://' : 'http://'}
</span>
<Field
type="text"
inputMode="url"
id="hostname"
name="hostname"
className="rounded-r-only"
/>
</div>
{errors.hostname &&
touched.hostname &&
typeof errors.hostname === 'string' && (
<div className="error">{errors.hostname}</div>
)}
</div>
</div>
<div className="form-row">
<label htmlFor="port" className="text-label">
{intl.formatMessage(messages.port)}
<span className="label-required">*</span>
</label>
<div className="form-input-area">
<Field
type="text"
inputMode="numeric"
id="port"
name="port"
className="short"
/>
{errors.port &&
touched.port &&
typeof errors.port === 'string' && (
<div className="error">{errors.port}</div>
)}
</div>
</div>
<div className="form-row">
<label htmlFor="useSsl" className="checkbox-label">
{intl.formatMessage(messages.enablessl)}
</label>
<div className="form-input-area">
<Field
type="checkbox"
id="useSsl"
name="useSsl"
onChange={() => {
setFieldValue('useSsl', !values.useSsl);
setFieldValue('port', values.useSsl ? 8096 : 443);
}}
/>
</div>
</div>
<div className="form-row">
<label htmlFor="urlBase" className="text-label">
{intl.formatMessage(messages.urlBase)}
</label>
<div className="form-input-area">
<div className="form-input-field">
<Field
type="text"
inputMode="url"
id="jellyfinInternalUrl"
name="jellyfinInternalUrl"
id="urlBase"
name="urlBase"
/>
</div>
{errors.jellyfinInternalUrl &&
touched.jellyfinInternalUrl && (
<div className="error">
{errors.jellyfinInternalUrl}
</div>
{errors.urlBase &&
touched.urlBase &&
typeof errors.urlBase === 'string' && (
<div className="error">{errors.urlBase}</div>
)}
</div>
</div>

View File

@@ -53,6 +53,8 @@ const messages = defineMessages({
discordId: 'Discord User ID',
discordIdTip:
'The <FindDiscordIdLink>multi-digit ID number</FindDiscordIdLink> associated with your Discord user account',
validationemailrequired: 'Email required',
validationemailformat: 'Valid email required',
validationDiscordId: 'You must provide a valid Discord user ID',
plexwatchlistsyncmovies: 'Auto-Request Movies',
plexwatchlistsyncmoviestip:
@@ -88,6 +90,9 @@ const UserGeneralSettings = () => {
);
const UserGeneralSettingsSchema = Yup.object().shape({
email: Yup.string()
.email(intl.formatMessage(messages.validationemailformat))
.required(intl.formatMessage(messages.validationemailrequired)),
discordId: Yup.string()
.nullable()
.matches(/^\d{17,19}$/, intl.formatMessage(messages.validationDiscordId)),

View File

@@ -2,6 +2,7 @@ import React from 'react';
export type AvailableLocale =
| 'ar'
| 'bg'
| 'ca'
| 'cs'
| 'da'
@@ -10,8 +11,11 @@ export type AvailableLocale =
| 'el'
| 'es'
| 'es-MX'
| 'fi'
| 'fr'
| 'hr'
| 'he'
| 'hi'
| 'hu'
| 'it'
| 'ja'
@@ -22,6 +26,7 @@ export type AvailableLocale =
| 'pl'
| 'pt-BR'
| 'pt-PT'
| 'ro'
| 'ru'
| 'sq'
| 'sr'
@@ -36,6 +41,10 @@ type AvailableLanguageObject = Record<
>;
export const availableLanguages: AvailableLanguageObject = {
bg: {
code: 'bg',
display: 'Bulgarian',
},
ca: {
code: 'ca',
display: 'Català',
@@ -64,10 +73,22 @@ export const availableLanguages: AvailableLanguageObject = {
code: 'es-MX',
display: 'Español (Latinoamérica)',
},
fi: {
code: 'fi',
display: 'Finnish',
},
fr: {
code: 'fr',
display: 'Français',
},
he: {
code: 'he',
display: 'Hebrew',
},
hi: {
code: 'hi',
display: 'Hindi',
},
hr: {
code: 'hr',
display: 'Hrvatski',
@@ -116,6 +137,10 @@ export const availableLanguages: AvailableLanguageObject = {
code: 'el',
display: 'Ελληνικά',
},
ro: {
code: 'ro',
display: 'Romanian',
},
ru: {
code: 'ru',
display: 'pусский',
@@ -132,14 +157,14 @@ export const availableLanguages: AvailableLanguageObject = {
code: 'ja',
display: '日本語',
},
uk: {
code: 'uk',
display: 'українська',
},
ko: {
code: 'ko',
display: '한국어',
},
uk: {
code: 'uk',
display: 'українська мова',
},
'zh-TW': {
code: 'zh-TW',
display: '繁體中文',

View File

@@ -253,7 +253,7 @@
"components.PermissionEdit.viewrequests": "الإطلاع على الطلبات",
"components.PermissionEdit.viewrequestsDescription": "إعطاء صلاحية بالإطلاع على جميع الطلبات المقدمة من قبل المستخدمين.",
"components.PersonDetails.alsoknownas": "أيضا يُعرف بإسم: {names}",
"components.PersonDetails.appearsin": "المظهر",
"components.PersonDetails.appearsin": "الظهور",
"components.PersonDetails.ascharacter": "{character} شارك بِدوْر",
"components.PersonDetails.birthdate": "ولد في {birthdate}",
"components.PersonDetails.crewmember": "عضو",
@@ -673,7 +673,7 @@
"components.Settings.plexlibraries": "مكتبات بليكس",
"components.Settings.noDefaultServer": "على الأقل {serverType} سيرفر واحد يجب أن يكون معدا كإفتراضي لإتاحة تنفيذ طلبات الـ {mediaType}.",
"components.Settings.plexsettings": "إعدادات بليكس",
"components.Settings.serviceSettingsDescription": "قم بإعداد {serverType} سيرفراتك بالإسفل. تستطيع الاتصال بأكثر من سيرفر {serverType} ولكن إثنان فقط يمكن إعدادهما كإفتراضيين واحد لمحتوى الفور كي والاخر لغير الفور كي. الذين يملكون إذونات مسؤول بإمكانهم تجاوز السيرفر المخصص لتنفيذ الطلبات الجديدة قبل الموافقة.",
"components.Settings.serviceSettingsDescription": "قم بإعداد {serverType} بالإسفل.تستطيع الاتصال بأكثر من سيرفر {serverType} ,ولكن إثنان فقط يمكن إعدادهما كإفتراضيين (واحد لجودة الفور كي والأخر لغير جودة الفور كي). أصحاب الصلاحيات الإدارية بإمكانهم تجاوز السيرفر المستخدم قبل تأكيدهم لأي طلب محتوى جديد.",
"components.Settings.serverpresetManualMessage": "ضبط يدوي",
"components.Settings.sonarrsettings": "إعدادات سونار",
"components.Settings.tautulliSettingsDescription": "بشكل إختياري أضبط إعدادات سيرفرك الخاص بـ Tautulli.أوفرسيرر سيقوم بجلب بيانات سجل المشاهدة لمحتوى بليكس من Tautulli.",
@@ -1232,5 +1232,11 @@
"components.Discover.CreateSlider.validationTitlerequired": "يجب تقديم عنوان.",
"components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} الأفلام",
"components.Discover.CreateSlider.providetmdbkeywordid": "قم بتوفير معرّف كلمات بحث رئيسية من (TMDB)",
"components.MovieDetails.imdbuserscore": "تقييم مستخدمين IMDB"
"components.MovieDetails.imdbuserscore": "تقييم مستخدمين IMDB",
"components.Settings.Notifications.NotificationsPushover.sound": "صوت التنبيه",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "الجهاز الإفتراضي",
"components.Settings.SonarrModal.animeSeriesType": "نوع مسلسل الإنمي",
"components.Settings.SonarrModal.seriesType": "نوع المسلسل",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "صوت التنبيه",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "الجهاز الإفتراضي"
}

1242
src/i18n/locale/bg.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -191,7 +191,7 @@
"components.Discover.TvGenreSlider.tvgenres": "Gèneres de Sèries",
"components.Discover.TvGenreList.seriesgenres": "Gèneres de Sèries",
"components.Discover.StudioSlider.studios": "Estudis",
"components.Discover.NetworkSlider.networks": "Emissors",
"components.Discover.NetworkSlider.networks": "Plataformes",
"components.Discover.MovieGenreSlider.moviegenres": "Gèneres de Pel·lícules",
"components.Discover.MovieGenreList.moviegenres": "Gèneres de Pel·lícules",
"components.Discover.DiscoverTvLanguage.languageSeries": "Sèries en {language}",
@@ -397,7 +397,7 @@
"components.TvDetails.originaltitle": "Títol original",
"components.TvDetails.originallanguage": "Idioma original",
"components.TvDetails.nextAirDate": "Pròxima data d'emissió",
"components.TvDetails.network": "{networkCount, plural, one {Emissor} other {Emissors}}",
"components.TvDetails.network": "{networkCount, plural, one {Plataforma} other {Plataformes}}",
"components.TvDetails.firstAirDate": "Primera data d'emissió",
"components.TvDetails.episodeRuntimeMinutes": "{runtime} minuts",
"components.TvDetails.episodeRuntime": "Duració de l'episodi",
@@ -494,7 +494,7 @@
"components.Settings.SonarrModal.validationNameRequired": "Heu de proporcionar un nom de servidor",
"components.Settings.SonarrModal.validationLanguageProfileRequired": "Heu de seleccionar un perfil d'idioma",
"components.Settings.SonarrModal.validationHostnameRequired": "Heu de proporcionar un nom damfitrió o una adreça IP vàlides",
"components.Settings.SonarrModal.validationBaseUrlTrailingSlash": "L'URL base no pot acabar amb una barra inclinada final",
"components.Settings.SonarrModal.validationBaseUrlTrailingSlash": "L'URL base no ha d'acabar amb una barra inclinada final",
"components.Settings.SonarrModal.validationBaseUrlLeadingSlash": "L'URL base ha de tenir una barra inclinada",
"components.Settings.SonarrModal.validationApplicationUrlTrailingSlash": "L'URL no pot acabar amb una barra inclinada final",
"components.Settings.SonarrModal.validationApplicationUrl": "Heu de proporcionar un URL vàlid",
@@ -1135,7 +1135,7 @@
"components.Discover.CreateSlider.needresults": "Cal tenir almenys 1 resultat.",
"components.Discover.CreateSlider.nooptions": "Sense resultats.",
"components.Discover.CreateSlider.providetmdbgenreid": "Proporciona un ID de categoria TMDB",
"components.Discover.CreateSlider.providetmdbnetwork": "Proporciona l'ID d'emissor TMDB",
"components.Discover.CreateSlider.providetmdbnetwork": "Proporciona l'ID de la plataforma TMDB",
"components.Discover.CreateSlider.providetmdbstudio": "Proporciona l'ID d'estudi TMDB",
"components.Discover.CreateSlider.searchGenres": "Cercar per gènere…",
"components.Discover.CreateSlider.searchKeywords": "Cercar per paraules clau…",
@@ -1167,7 +1167,7 @@
"components.Discover.networks": "Emissors",
"components.Discover.resetwarning": "Restablir tots els controls lliscants al valor predeterminat. Això també suprimirà els controls lliscants personalitzats!",
"components.Discover.tmdbmoviekeyword": "Paraula clau de pel·lícula TMDB",
"components.Discover.tmdbnetwork": "Emissors TMDB",
"components.Discover.tmdbnetwork": "Plataformes TMDB",
"components.Discover.FilterSlideover.tmdbuserscore": "Puntuació d'usuaris TMDB",
"components.Discover.tvgenres": "Gèneres de sèries",
"components.Discover.DiscoverTvKeyword.keywordSeries": "Sèries {keywordTitle}",
@@ -1240,5 +1240,19 @@
"components.StatusBadge.seasonepisodenumber": "S{seasonNumber}E{episodeNumber}",
"components.Settings.SettingsJobsCache.availability-sync": "Sincronització de disponibilitat de contingut",
"components.Discover.tmdbmoviestreamingservices": "Serveis de transmissió de pel·lícules TMDB",
"components.Discover.tmdbtvstreamingservices": "Serveis de transmissió de TV TMDB"
"components.Discover.tmdbtvstreamingservices": "Serveis de transmissió de TV TMDB",
"components.Discover.FilterSlideover.tmdbuservotecount": "Recompte de vots dels usuaris de TMDB",
"components.Discover.FilterSlideover.voteCount": "Número de vots entre {minValue} i {maxValue}",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "So per a les notificacions",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "Dispositiu per defecte",
"components.Settings.Notifications.NotificationsPushover.sound": "So per a les notificacions",
"components.Settings.SonarrModal.animeSeriesType": "Tipus d'Anime",
"components.Settings.SonarrModal.seriesType": "Tipus de sèrie",
"components.Settings.SonarrModal.tagRequests": "Sol·licituds d'etiquetes",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Dispositiu per defecte",
"i18n.collection": "Col·lecció",
"components.MovieDetails.imdbuserscore": "Puntuació dels usuaris de IMDB",
"components.Settings.RadarrModal.tagRequests": "Sol·licituds d'etiqueta",
"components.Settings.RadarrModal.tagRequestsInfo": "Automàticament afegeix una etiqueta addicional amb el nom d'usuari i nom complet del sol·licitant",
"components.Settings.SonarrModal.tagRequestsInfo": "Automàticament afegeix una etiqueta addicional amb el nom d'usuari i nom complet del sol·licitant"
}

View File

@@ -1227,7 +1227,7 @@
"components.Settings.SettingsMain.applicationurl": "Adresa URL aplikace",
"components.Settings.SettingsMain.cacheImages": "Povolení ukládání obrázků do mezipaměti",
"components.Settings.SettingsMain.cacheImagesTip": "Ukládání obrázků z externích zdrojů do mezipaměti (vyžaduje značné množství místa na disku)",
"components.Settings.SettingsMain.csrfProtection": "Povolení ochrany CSRF",
"components.Settings.SettingsMain.csrfProtection": "Povolit ochranu CSRF",
"components.Settings.SettingsMain.csrfProtectionHoverTip": "Toto nastavení NEPOVOLUJTE, pokud nerozumíte tomu, co děláte!",
"components.Settings.SettingsMain.csrfProtectionTip": "Nastavení externího přístupu k rozhraní API pouze pro čtení (vyžaduje protokol HTTPS)",
"components.Settings.SettingsMain.general": "Obecné",
@@ -1264,5 +1264,12 @@
"components.Discover.FilterSlideover.tmdbuservotecount": "Počet hlasů uživatelů TMDB",
"components.Discover.tmdbmoviestreamingservices": "Filmové streamovací služby TMDB",
"components.Settings.SonarrModal.tagRequestsInfo": "Automatické přidání dodatočné značky s ID uživatele a zobrazovaným jménem žadatele",
"components.Discover.tmdbtvstreamingservices": "Televizní streamovací služby TMDB"
"components.Discover.tmdbtvstreamingservices": "Televizní streamovací služby TMDB",
"components.Settings.Notifications.NotificationsPushover.sound": "Zvuk upozornění",
"components.MovieDetails.imdbuserscore": "Uživatelské skóre IMDB",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "Zvuk upozornění",
"components.Settings.SonarrModal.animeSeriesType": "Typ anime série",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "Výchozí zařízení",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Výchozí zařízení",
"components.Settings.SonarrModal.seriesType": "Typ série"
}

View File

@@ -60,7 +60,7 @@
"components.MovieDetails.markavailable": "Markér som Tilgængelig",
"components.MovieDetails.originallanguage": "Originalsprog",
"components.Login.signinwithoverseerr": "Brug din {applicationTitle} konto",
"components.MovieDetails.MovieCast.fullcast": "Medvirkende",
"components.MovieDetails.MovieCast.fullcast": "Fuld rolleliste",
"components.MovieDetails.MovieCrew.fullcrew": "Filmstab",
"components.MovieDetails.budget": "Budget",
"components.MovieDetails.overview": "Overblik",
@@ -543,7 +543,7 @@
"components.Settings.Notifications.validationSmtpPortRequired": "Du skal angive et gyldigt port-nummer",
"components.Settings.Notifications.webhookUrl": "Webhook URL",
"components.Settings.Notifications.webhookUrlTip": "Opret en<DiscordWebhookLink>webhook integration</DiscordWebhookLink> til din server",
"components.Settings.RadarrModal.baseUrl": "URL Base",
"components.Settings.RadarrModal.baseUrl": "URL Kilde",
"components.Settings.RadarrModal.create4kradarr": "Tilføj Ny 4K Radarr Server",
"components.Settings.RadarrModal.createradarr": "Tilføj Ny Radarr Server",
"components.Settings.RadarrModal.default4kserver": "Standard 4K Server",
@@ -643,7 +643,7 @@
"components.Settings.SettingsLogs.label": "Label",
"components.Settings.SettingsLogs.level": "Alvorlighed",
"components.Settings.SettingsLogs.logDetails": "Loginformation",
"components.Settings.SonarrModal.baseUrl": "URL Base",
"components.Settings.SonarrModal.baseUrl": "URL Kilde",
"components.Settings.SonarrModal.create4ksonarr": "Tilføj Ny 4K Sonarr Server",
"components.Settings.SonarrModal.loadingprofiles": "Indlæser kvalitetsprofiler…",
"components.Settings.cacheImages": "Aktivér billede-caching",
@@ -826,7 +826,7 @@
"components.Setup.configureplex": "Konfigurér Plex",
"components.Settings.validationPortRequired": "Du skal angive et gyldigt port-nummer",
"components.Setup.configureservices": "Konfigurér Tjenester",
"components.Setup.continue": "Fortsæt",
"components.Setup.continue": "Videre",
"components.Setup.finish": "Fuldfør Opsætning",
"components.Setup.finishing": "Færdiggører…",
"components.Setup.loginwithplex": "Log in med Plex",
@@ -1266,5 +1266,12 @@
"components.Settings.SonarrModal.tagRequests": "Tag forespørgsler",
"components.Settings.SonarrModal.tagRequestsInfo": "Tilføj automatisk et ekstra tag med forespørgerens bruger ID og viste navn",
"i18n.collection": "Samling",
"components.Settings.RadarrModal.tagRequestsInfo": "Tilføj automatisk et ekstra tag med forespørgerens bruger id og viste navn"
"components.Settings.RadarrModal.tagRequestsInfo": "Tilføj automatisk et ekstra tag med forespørgerens bruger id og viste navn",
"components.MovieDetails.imdbuserscore": "IMDB Bruger Score",
"components.Settings.SonarrModal.animeSeriesType": "Anime Serie Type",
"components.Settings.SonarrModal.seriesType": "Serie Type",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "Enhedsstandard",
"components.Settings.Notifications.NotificationsPushover.sound": "Notifikationslyd",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Enhedsstandard",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "Notifikationslyd"
}

View File

@@ -1269,5 +1269,11 @@
"components.UserProfile.UserSettings.UserGeneralSettings.discordId": "Αναγνωριστικό χρήστη Discord",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Ζητήστε αυτόματα σειρές που βρίσκονται στη <PlexWatchlistSupportLink>λίστα παρακολούθησης του Plex</PlexWatchlistSupportLink>",
"components.UserProfile.plexwatchlist": "Λίστα παρακολούθησης Plex",
"components.MovieDetails.imdbuserscore": "Βαθμολογία χρηστών IMDB"
"components.MovieDetails.imdbuserscore": "Βαθμολογία χρηστών IMDB",
"components.Settings.SonarrModal.animeSeriesType": "Τύπος σειράς άνιμε",
"components.Settings.SonarrModal.seriesType": "Τύπος σειράς",
"components.Settings.Notifications.NotificationsPushover.sound": "Ήχος ειδοποίησης",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Προεπιλογή συσκευής",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "Ήχος ειδοποίησης",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "Προεπιλογή συσκευής"
}

View File

@@ -220,17 +220,19 @@
"components.Layout.VersionStatus.streamdevelop": "Jellyseerr Develop",
"components.Layout.VersionStatus.streamstable": "Jellyseerr Stable",
"components.Login.adminerror": "You must use an admin account to sign in.",
"components.Login.invalidurlerror": "Unable to connect to {mediaServerName} server.",
"components.Login.credentialerror": "The username or password is incorrect.",
"components.Login.description": "Since this is your first time logging into {applicationName}, you are required to add a valid email address.",
"components.Login.email": "Email Address",
"components.Login.emailtooltip": "Address does not need to be associated with your {mediaServerName} instance.",
"components.Login.enablessl": "Use SSL",
"components.Login.forgotpassword": "Forgot Password?",
"components.Login.host": "{mediaServerName} URL",
"components.Login.hostname": "{mediaServerName} URL",
"components.Login.initialsignin": "Connect",
"components.Login.initialsigningin": "Connecting…",
"components.Login.invalidurlerror": "Unable to connect to {mediaServerName} server.",
"components.Login.loginerror": "Something went wrong while trying to sign in.",
"components.Login.password": "Password",
"components.Login.port": "Port",
"components.Login.save": "Add",
"components.Login.saving": "Adding…",
"components.Login.signin": "Sign In",
@@ -240,9 +242,15 @@
"components.Login.signinwithoverseerr": "Use your {applicationTitle} account",
"components.Login.signinwithplex": "Use your Plex account",
"components.Login.title": "Add Email",
"components.Login.urlBase": "URL Base",
"components.Login.username": "Username",
"components.Login.validationEmailFormat": "Invalid email",
"components.Login.validationEmailRequired": "You must provide an email",
"components.Login.validationHostnameRequired": "You must provide a valid hostname or IP address",
"components.Login.validationPortRequired": "You must provide a valid port number",
"components.Login.validationUrlBaseLeadingSlash": "URL base must have a leading slash",
"components.Login.validationUrlBaseTrailingSlash": "URL base must not end in a trailing slash",
"components.Login.validationUrlTrailingSlash": "URL must not end in a trailing slash",
"components.Login.validationemailformat": "Valid email required",
"components.Login.validationemailrequired": "You must provide a valid email address",
"components.Login.validationhostformat": "Valid URL required",
@@ -937,7 +945,7 @@
"components.Settings.experimentalTooltip": "Enabling this setting may result in unexpected application behavior",
"components.Settings.externalUrl": "External URL",
"components.Settings.hostname": "Hostname or IP Address",
"components.Settings.internalUrl": "Internal URL",
"components.Settings.invalidurlerror": "Unable to connect to {mediaServerName} server.",
"components.Settings.is4k": "4K",
"components.Settings.jellyfinForgotPasswordUrl": "Forgot Password URL",
"components.Settings.jellyfinSettings": "{mediaServerName} Settings",
@@ -1177,6 +1185,8 @@
"components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Settings saved successfully!",
"components.UserProfile.UserSettings.UserGeneralSettings.user": "User",
"components.UserProfile.UserSettings.UserGeneralSettings.validationDiscordId": "You must provide a valid Discord user ID",
"components.UserProfile.UserSettings.UserGeneralSettings.validationemailformat": "Valid email required",
"components.UserProfile.UserSettings.UserGeneralSettings.validationemailrequired": "Email required",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Device Default",
"components.UserProfile.UserSettings.UserNotificationSettings.discordId": "User ID",
"components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "The <FindDiscordIdLink>multi-digit ID number</FindDiscordIdLink> associated with your user account",

View File

@@ -1204,5 +1204,11 @@
"i18n.collection": "Colección",
"components.Settings.RadarrModal.tagRequestsInfo": "Añadir automáticamente una etiqueta adicional con el nombre de usuario y el nombre para mostrar del solicitante",
"components.Settings.SonarrModal.tagRequestsInfo": "Añadir automáticamente una etiqueta adicional con el nombre de usuario y el nombre para mostrar del solicitante",
"components.MovieDetails.imdbuserscore": "Puntuación de los usuarios de IMDB"
"components.MovieDetails.imdbuserscore": "Puntuación de los usuarios de IMDB",
"components.Settings.SonarrModal.animeSeriesType": "Tipo de anime",
"components.Settings.SonarrModal.seriesType": "Tipo de series",
"components.Settings.Notifications.NotificationsPushover.sound": "Sonido para las notificaciones",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Dispositivo predeterminado",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "Sonido para las notificaciones",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "Dispositivo predeterminado"
}

35
src/i18n/locale/fi.json Normal file
View File

@@ -0,0 +1,35 @@
{
"components.Discover.CreateSlider.editsuccess": "Liukusäädintä muokattu ja löytömuokkausasetukset tallennettu.",
"components.CollectionDetails.numberofmovies": "{count} elokuvaa",
"components.Discover.CreateSlider.slidernameplaceholder": "Liukusäätimen nimi",
"components.AppDataWarning.dockerVolumeMissingDescription": "<code>{appDataPath}</code> -levyn liittäminen ei onnistunut oikein. Kaikki tiedot poistetaan, kun säilö pysäytetään tai käynnistetään uudelleen.",
"components.Discover.DiscoverMovies.sortPopularityDesc": "Suosio laskevassa järjestyksessä",
"components.AirDateBadge.airsrelative": "Esitys {relativeTime}",
"components.CollectionDetails.overview": "Yleiskatsaus",
"components.AirDateBadge.airedrelative": "Esitetty {relativeTime}",
"components.Discover.CreateSlider.searchStudios": "Etsi studioita…",
"components.Discover.CreateSlider.providetmdbnetwork": "Anna TMDB Network ID",
"components.Discover.CreateSlider.addfail": "Uuden liukusäätimen luominen epäonnistui.",
"components.CollectionDetails.requestcollection": "Pyydä kokoelma",
"components.Discover.DiscoverMovieGenre.genreMovies": "Lajityypin {genre} elokuvat",
"components.Discover.DiscoverMovieLanguage.languageMovies": "Elokuvat kielellä {language}",
"components.Discover.DiscoverMovies.sortPopularityAsc": "Suosio nousevassa järjestyksessä",
"components.Discover.CreateSlider.needresults": "Tarvitaan vähintään 1 tulos.",
"components.Discover.CreateSlider.addcustomslider": "Luo mukautettu liukusäädin",
"components.Discover.CreateSlider.editSlider": "Muokkaa liukusäädintä",
"components.Discover.CreateSlider.validationDatarequired": "Sinun täytyy antaa datan arvo.",
"components.Discover.CreateSlider.providetmdbstudio": "Anna TMDB Studio ID",
"components.Discover.CreateSlider.searchGenres": "Etsi tyylilajeja…",
"components.Discover.CreateSlider.editfail": "Liukusäätimen muokkaus epäonnistui.",
"components.Discover.CreateSlider.starttyping": "Aloita kirjoittaminen etsiäksesi.",
"components.Discover.CreateSlider.addSlider": "Lisää liukusäädin",
"components.CollectionDetails.requestcollection4k": "Pyydä kokoelma 4K-laadulla",
"components.Discover.CreateSlider.providetmdbsearch": "Anna hakukysely",
"components.Discover.CreateSlider.providetmdbkeywordid": "Anna TMDB Keyword ID",
"components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} elokuvat",
"components.Discover.CreateSlider.validationTitlerequired": "Sinun tulee antaa nimi.",
"components.Discover.CreateSlider.nooptions": "Ei tuloksia.",
"components.Discover.CreateSlider.searchKeywords": "Etsi avainsanoja…",
"components.Discover.DiscoverMovies.discovermovies": "Elokuvat",
"components.Discover.CreateSlider.providetmdbgenreid": "Anna TMDB Genre ID"
}

View File

@@ -176,7 +176,7 @@
"components.Settings.RadarrModal.port": "Port",
"components.Settings.RadarrModal.qualityprofile": "Profil de qualité",
"components.Settings.RadarrModal.rootfolder": "Dossier racine",
"components.Settings.RadarrModal.selectMinimumAvailability": "Sélectionner une disponibilté minimale",
"components.Settings.RadarrModal.selectMinimumAvailability": "Sélectionner une disponibilité minimale",
"components.Settings.RadarrModal.selectQualityProfile": "Sélectionner un profil qualité",
"components.Settings.RadarrModal.selectRootFolder": "Sélectionner un dossier racine",
"components.Settings.RadarrModal.server4k": "Serveur 4K",
@@ -277,7 +277,7 @@
"i18n.tvshows": "Séries",
"i18n.unavailable": "Indisponible",
"pages.oops": "Oups",
"pages.returnHome": "Retourner à l'acceuil",
"pages.returnHome": "Retourner à l'accueil",
"components.TvDetails.TvCast.fullseriescast": "Casting complet de la série",
"components.MovieDetails.MovieCast.fullcast": "Casting complet",
"components.Settings.Notifications.emailsettingssaved": "Paramètres de notification par e-mail enregistrés avec succès !",
@@ -323,7 +323,7 @@
"components.Settings.SettingsAbout.Releases.viewchangelog": "Voir le journal des modifications",
"components.Settings.SettingsAbout.Releases.versionChangelog": "Journal des modifications de la version {version}",
"components.Settings.SettingsAbout.Releases.releases": "Versions",
"components.Settings.SettingsAbout.Releases.releasedataMissing": "Les données de version sont actuellement indisponible.",
"components.Settings.SettingsAbout.Releases.releasedataMissing": "Les données de version sont actuellement indisponibles.",
"components.Settings.SettingsAbout.Releases.latestversion": "Dernière version",
"components.Settings.SettingsAbout.Releases.currentversion": "Actuelle",
"components.UserList.importfromplexerror": "Une erreur s'est produite durant l'importation des utilisateurs de Plex.",
@@ -1071,7 +1071,7 @@
"components.ManageSlideOver.manageModalMedia4k": "Média(s) 4K",
"components.ManageSlideOver.markallseasons4kavailable": "Marquer toutes les saisons comme disponibles en 4K",
"components.ManageSlideOver.playedby": "Joué par",
"components.Settings.validationUrlTrailingSlash": "L'URL ne doit pas ce terminer par un slash",
"components.Settings.validationUrlTrailingSlash": "L'URL ne doit pas se terminer par un slash",
"components.Settings.externalUrl": "URL externe",
"components.Settings.tautulliApiKey": "Clé API",
"components.Settings.tautulliSettings": "Paramètres Tautulli",
@@ -1251,5 +1251,11 @@
"components.Settings.SonarrModal.tagRequests": "Tager les demandes",
"components.Settings.SonarrModal.tagRequestsInfo": "Ajouter automatiquement un tag supplémentaire avec l'ID utilisateur et le nom d'affichage du demandeur",
"i18n.collection": "Collection",
"components.Settings.RadarrModal.tagRequestsInfo": "Ajouter automatiquement un tag supplémentaire avec l'ID utilisateur et le nom d'affichage du demandeur"
"components.Settings.RadarrModal.tagRequestsInfo": "Ajouter automatiquement un tag supplémentaire avec l'ID utilisateur et le nom d'affichage du demandeur",
"components.Settings.SonarrModal.seriesType": "Type de série",
"components.Settings.SonarrModal.animeSeriesType": "Types d'anime",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "Appareil par défaut",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Appareil par défaut",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "Son de notification",
"components.Settings.Notifications.NotificationsPushover.sound": "Son de notification"
}

View File

@@ -2,7 +2,7 @@
"components.ManageSlideOver.alltime": "כל הזמנים",
"components.Login.validationemailrequired": "חובה לספק כתובת מייל חוקית",
"components.NotificationTypeSelector.userissuereopenedDescription": "קבל התראה כשבעיות שפתחת נפתחות מחדש.",
"components.AppDataWarning.dockerVolumeMissingDescription": "ה <code>{appDataPath}</code> אחסון לא הוגדר כראוי. כל המידע יוסר כאשר הקונטיינר יעצור או יותחל מחדש.",
"components.AppDataWarning.dockerVolumeMissingDescription": "רכיב האחסון <code>{appDataPath}</code> לא הוגדר כראוי. כל המידע יוסר כאשר הקונטיינר יעצור או יותחל מחדש.",
"components.CollectionDetails.overview": "תצוגה כללית",
"components.CollectionDetails.numberofmovies": "{כמות} סרטים",
"components.CollectionDetails.requestcollection": "אוסף בקשות",
@@ -135,5 +135,41 @@
"components.Login.loginerror": "משהו השתבש בזמן ההתחברות.",
"components.Login.password": "סיסמה",
"components.Login.signin": "התחברות",
"components.Login.validationpasswordrequired": "חובה לכתוב סיסמה"
"components.Login.validationpasswordrequired": "חובה לכתוב סיסמה",
"components.Discover.CreateSlider.editsuccess": "ערוך סליידרהגדרות התאמה אישית שמורות.",
"components.Discover.CreateSlider.slidernameplaceholder": "שם הסליידר",
"components.Discover.DiscoverMovies.sortPopularityDesc": "פופולאריות יורדץ",
"components.Discover.DiscoverMovies.sortTmdbRatingAsc": "דירוג TMDB עולה",
"components.Discover.CreateSlider.searchStudios": "חפש אולפנים…",
"components.Discover.DiscoverMovies.sortReleaseDateDesc": "תאריך שחרור יורד",
"components.Discover.CreateSlider.providetmdbnetwork": "הזמן מזהה רשת TMDB",
"components.Discover.CreateSlider.addfail": "שגיאה ביצירת סליידר.",
"components.Discover.DiscoverMovies.sortPopularityAsc": "פופולאריות עולה",
"components.Discover.CreateSlider.needresults": "חייב שתהיה לפחות תוצאה אחת.",
"components.Discover.CreateSlider.addcustomslider": "צור סליידר מותאם אישית",
"components.Discover.CreateSlider.editSlider": "ערוך סליידר",
"components.Discover.CreateSlider.validationDatarequired": "עליך לספק ערך.",
"components.Discover.DiscoverTv.discovertv": "סדרה",
"components.Discover.DiscoverSliderEdit.deletefail": "שגיאה במחיקת סליידר.",
"components.Discover.CreateSlider.providetmdbstudio": "הזן מזהה אולפן TMDB",
"components.Discover.DiscoverMovies.sortTitleDesc": "כותר (ת-א) יורד",
"components.Discover.CreateSlider.searchGenres": "חפש ז'אנרים…",
"components.Discover.CreateSlider.editfail": "שגיאה בעריכת סליידר.",
"components.Discover.CreateSlider.starttyping": "התחל לכתוב כדי לחפש.",
"components.Discover.DiscoverSliderEdit.enable": "שנה נראות",
"components.Discover.CreateSlider.addSlider": "הוסף סליידר",
"components.Discover.CreateSlider.providetmdbsearch": "הזן מילת חיפוש",
"components.Discover.CreateSlider.providetmdbkeywordid": "הזן מילת חיפוש TMDB",
"components.Discover.DiscoverMovieKeyword.keywordMovies": "סרטי {keywordTitle}",
"components.Discover.CreateSlider.validationTitlerequired": "עליך לספק כותרת.",
"components.Discover.DiscoverMovies.sortReleaseDateAsc": "תאריך שחרור עולה",
"components.Discover.CreateSlider.nooptions": "אין תוצאות.",
"components.Discover.DiscoverMovies.sortTmdbRatingDesc": "דירוג TMDB יורד",
"components.Discover.CreateSlider.searchKeywords": "חפש מילות מפתח…",
"components.Discover.CreateSlider.addsuccess": "צור סליידר חדש והגדרות התאמה אישית שמורות.",
"components.Discover.DiscoverSliderEdit.deletesuccess": "סליידר נמחק בהצלחה.",
"components.Discover.DiscoverMovies.discovermovies": "סרטים",
"components.Discover.DiscoverMovies.sortTitleAsc": "כותר (א-ת) עולה",
"components.Discover.CreateSlider.providetmdbgenreid": "הזן מזהה ז'אנר TMDB",
"components.Discover.DiscoverSliderEdit.remove": "הסר"
}

View File

@@ -95,7 +95,7 @@
"components.MediaSlider.ShowMoreCard.seemore": "Vidi više",
"components.MovieDetails.markavailable": "Označi kao dostupno",
"components.ManageSlideOver.tvshow": "serije",
"components.MovieDetails.productioncountries": "{countryCount, plural, one {zemlja produkcije} few {zemlje produkcije} other {zemalja produkcije}}",
"components.MovieDetails.productioncountries": "{countryCount, plural, one {Zemlja produkcije} few {Zemlje produkcije} other {Zemlje produkcije}}",
"components.MovieDetails.managemovie": "Upravljaj filmom",
"components.MovieDetails.playonplex": "Reproduciraj na {mediaServerName}u",
"components.MovieDetails.overviewunavailable": "Pregled nedostupan.",
@@ -188,7 +188,7 @@
"components.MovieDetails.originallanguage": "Izvorni jezik",
"components.MovieDetails.MovieCrew.fullcrew": "Filmska postava",
"components.MovieDetails.physicalrelease": "Fizičko izdanje",
"components.MovieDetails.play4konplex": "Reproduciraj u 4K na Plexu",
"components.MovieDetails.play4konplex": "Reproduciraj u 4K rezoluciji na Plexu",
"components.MovieDetails.recommendations": "Preporuke",
"components.MovieDetails.releasedate": "{releaseCount, plural, one {Datum Izlaska} other {Datumi izlaska}}",
"components.MovieDetails.rtcriticsscore": "Rotten Tomatoes Tomatometer",
@@ -260,7 +260,7 @@
"components.PermissionEdit.autoapproveMovies": "Automatsko odobravanje filmova",
"components.PermissionEdit.autoapproveSeries": "Automatsko odobravanje serija",
"components.RequestButton.declinerequests": "Odbij {requestCount, plural, one {zahtjev} few {{requestCount} zahtjeva} other {{requestCount} zahtjeva}}",
"components.RequestModal.QuotaDisplay.requestsremaining": "{remaining, plural, =0 {No} other {<strong>#</strong>}} {type} {remaining, plural, one {zahtjev preostalo} other {zahtjeva preostala}}",
"components.RequestModal.QuotaDisplay.requestsremaining": "{remaining, plural, =0 {0} other {<strong>#</strong>}} {type} {remaining, plural, one {zahtjev preostao} few {zahtjeva preostala} other {zahtjeva preostalo}}",
"components.Settings.SettingsJobsCache.editJobScheduleCurrent": "Aktualna učestalost",
"components.Settings.SettingsJobsCache.editJobSchedulePrompt": "Nova učestalost",
"components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "{jobScheduleHours, plural, one {Svaki sat} few {Svaka {jobScheduleHours} sata} other {Svakih {jobScheduleHours} sati}}",
@@ -366,7 +366,7 @@
"components.Settings.menuServices": "Usluge",
"components.Settings.menuUsers": "Korisnici",
"components.Settings.noDefault4kServer": "4K {serverType} poslužitelj mora biti označen kao zadani kako bi se korisnicima omogućilo slanje zahtjeva za 4K {mediaType}.",
"components.Settings.noDefaultServer": "Najmanje jedan {serverType} poslužitelj mora biti označen kao zadani kako bi se zahtjevi za {mediaType} mogli obraditi.",
"components.Settings.noDefaultServer": "Barem jedan {serverType} poslužitelj mora biti označen kao zadani kako bi se zahtjevi za {mediaType} mogli obraditi.",
"components.Settings.notificationAgentSettingsDescription": "Konfiguriraj i aktiviraj agente obavijesti.",
"components.Settings.notifications": "Obavijesti",
"components.Settings.notificationsettings": "Postavke obavijesti",
@@ -387,7 +387,7 @@
"components.Settings.serverpresetLoad": "Pritisni gumb za učitavanje dostupnih polsužitelja",
"components.Settings.serverpresetManualMessage": "Ručna konfiguracija",
"components.Settings.services": "Usluge",
"components.Settings.settingUpPlexDescription": "Za postavljanje Plexa, unesi detalje ručno ili odaberi poslužitelj preuzet s <RegisterPlexTVLink>plex.tv</RegisterPlexTVLink>. Pritisni gumb desno od padajućeg izbornika za dohvaćanje popisa dostupnih poslužitelja.",
"components.Settings.settingUpPlexDescription": "Za postavljanje Plexa unesi detalje ručno ili odaberi poslužitelj dohvaćen s <RegisterPlexTVLink>plex.tv</RegisterPlexTVLink>. Pritisni gumb desno od padajućeg izbornika za dohvaćanje popisa dostupnih poslužitelja.",
"components.Settings.sonarrsettings": "Sonarr postavke",
"components.Settings.ssl": "SSL",
"components.Settings.tautulliApiKey": "API ključ",
@@ -401,7 +401,7 @@
"components.Settings.urlBase": "Osnovni URL",
"components.Settings.validationApiKey": "Moraš navesti valjani API ključ",
"components.Settings.webpush": "Web Push",
"components.Setup.continue": "Nastavi",
"components.Setup.continue": "Nastaviti",
"components.Setup.finish": "Završi postavljanje",
"components.Setup.tip": "Savjet",
"components.Setup.welcome": "Jellyseerr dobrodošlica",
@@ -453,8 +453,8 @@
"components.UserProfile.UserSettings.UserGeneralSettings.general": "Opće",
"components.UserProfile.UserSettings.UserGeneralSettings.generalsettings": "Opće postavke",
"components.UserProfile.UserSettings.UserGeneralSettings.languageDefault": "Zadano ({language})",
"components.UserProfile.UserSettings.UserGeneralSettings.originallanguageTip": "Filtriraj sadržaj prema izvornom jeziku",
"components.UserProfile.UserSettings.UserGeneralSettings.originallanguage": "Otkrij jezik",
"components.UserProfile.UserSettings.UserGeneralSettings.originallanguageTip": "Filtriraj sadržaj po izvornom jeziku",
"components.UserProfile.UserSettings.UserGeneralSettings.originallanguage": "Jezik otkrivanja",
"components.UserProfile.UserSettings.UserGeneralSettings.plexuser": "Plex korisnik",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "Automatsko zahtijevanje filmova",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Automatski zatraži filmove na tvom <PlexWatchlistSupportLink>Plex popisu gledanja</PlexWatchlistSupportLink>",
@@ -484,7 +484,7 @@
"components.UserProfile.UserSettings.UserPasswordChange.nopermissionDescription": "Nemaš dozvole za mijenjenje lozinke ovog korisnika.",
"components.UserProfile.UserSettings.UserPasswordChange.password": "Lozinka",
"components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailure": "Dogodila se greška prilikom spremanja lozinke.",
"components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailureVerifyCurrent": "Dogodila se greška prilikom spremanja lozinke. Je li vaša aktualna lozinka bila ispravno upisana?",
"components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailureVerifyCurrent": "Dogodila se greška prilikom spremanja lozinke. Je li tvoja aktualna lozinka ispravno upisana?",
"components.UserProfile.UserSettings.UserPasswordChange.toastSettingsSuccess": "Lozinka je uspješno spremljena!",
"components.UserProfile.UserSettings.UserPasswordChange.validationCurrentPassword": "Moraš navesti tvoju aktualnu lozinku",
"components.UserProfile.UserSettings.UserPasswordChange.validationNewPassword": "Moraš navesti novu lozinku",
@@ -496,10 +496,10 @@
"components.UserProfile.UserSettings.menuNotifications": "Obavijesti",
"components.UserProfile.UserSettings.menuPermissions": "Dozvole",
"components.UserProfile.UserSettings.unauthorizedDescription": "Nemaš dozvole za mijenjenje postavki ovog korisnika.",
"components.UserProfile.emptywatchlist": "Mediji koji su dodani u tvoj <PlexWatchlistSupportLink>popis gledanja na Plexu</PlexWatchlistSupportLink> pojavit će se ovdje.",
"components.UserProfile.emptywatchlist": "Mediji koji su dodani u tvoj <PlexWatchlistSupportLink>Plex popis gledanja</PlexWatchlistSupportLink> pojavit će se ovdje.",
"components.UserProfile.movierequests": "Zahtjevi za filmove",
"components.UserProfile.pastdays": "{type} (zadnjih {days} dana)",
"components.UserProfile.plexwatchlist": "Popis gledanja na Plexu",
"components.UserProfile.plexwatchlist": "Plex popis gledanja",
"components.UserProfile.seriesrequest": "Zahtjevi za serije",
"components.UserProfile.totalrequests": "Ukupno zahtjeva",
"components.UserProfile.unlimited": "Neograničeno",
@@ -540,7 +540,7 @@
"components.PermissionEdit.viewissues": "Prikaz problema",
"components.PermissionEdit.viewrecent": "Prikaz nedavno dodanih",
"components.PermissionEdit.viewrequests": "Prikaz zahtjeva",
"components.PermissionEdit.viewwatchlists": "Prikaz Plex popisa gledanja",
"components.PermissionEdit.viewwatchlists": "Prikaži Plex popise gledanja",
"components.PersonDetails.alsoknownas": "Poznat/a i kao: {names}",
"components.PersonDetails.appearsin": "Nastupanja",
"components.PersonDetails.ascharacter": "kao {character}",
@@ -562,7 +562,7 @@
"components.RequestButton.viewrequest": "Prikaz zahtjeva",
"components.RequestButton.viewrequest4k": "Prikaz 4K zahtjeva",
"components.RequestCard.approverequest": "Odobri zahtjev",
"components.RequestCard.cancelrequest": "Prekini zahtjev",
"components.RequestCard.cancelrequest": "Otkaži zahtjev",
"components.RequestCard.declinerequest": "Odbij zahtjev",
"components.RequestCard.deleterequest": "Izbriši zahtjev",
"components.RequestCard.editrequest": "Uredi zahtjev",
@@ -580,19 +580,19 @@
"components.RequestModal.errorediting": "Dogodila se greška prilikom uređivanja zahtjeva.",
"components.RequestModal.extras": "Dodaci",
"components.RequestModal.numberofepisodes": "Broj epizoda",
"components.RequestModal.pending4krequest": "4K zahtjeva na čekanju",
"components.RequestModal.pending4krequest": "4K zahtjevi na čekanju",
"components.RequestModal.pendingapproval": "Tvoj zahtjev čeka na odobrenje.",
"components.RequestCard.tvdbid": "TVDB ID",
"components.RequestModal.pendingrequest": "Zahtjevai na čekanju",
"components.RequestModal.requestApproved": "Zahtjev za <strong>{title}</strong> je odobren!",
"components.RequestModal.requestcancelled": "Zahtjev za <strong>{title}</strong> je prekinut.",
"components.RequestModal.requestcollection4ktitle": "Zahtjev za zbirkom u 4K",
"components.RequestModal.requestcollectiontitle": "Zahtjev za zbirkom",
"components.RequestModal.requestcollection4ktitle": "Zatraži zbirku u 4K",
"components.RequestModal.requestcollectiontitle": "Zatraži zbirku",
"components.RequestModal.requesterror": "Dogodila se greška prilikom slanja zahtjeva.",
"components.RequestModal.requestfrom": "Zahtjev korisnika {username} čeka na odobrenje.",
"components.RequestModal.requestmovie4ktitle": "Zatraži film u 4K",
"components.RequestModal.requestseries4ktitle": "Zatraži serije u 4K",
"components.RequestModal.requestseriestitle": "Zatraži serije",
"components.RequestModal.requestseries4ktitle": "Zatraži seriju u 4K",
"components.RequestModal.requestseriestitle": "Zatraži seriju",
"components.RequestModal.season": "Sezona",
"components.RequestModal.seasonnumber": "Sezona {number}",
"components.RequestModal.selectmovies": "Odaberi filmove",
@@ -692,7 +692,7 @@
"components.Settings.RadarrModal.validationMinimumAvailabilityRequired": "Moraš odabrati najmanju dostupnost",
"components.Settings.RadarrModal.validationPortRequired": "Moraš navesti valjani broj priključka",
"components.Settings.RadarrModal.validationProfileRequired": "Moraš odabrati profil kvalitete",
"components.Settings.SettingsAbout.Releases.currentversion": "Aktualno",
"components.Settings.SettingsAbout.Releases.currentversion": "Trenutno",
"components.Settings.SettingsAbout.Releases.latestversion": "Najnovije",
"components.Settings.SettingsAbout.Releases.releasedataMissing": "Podaci izdanja trenutačno nisu nedostupni.",
"components.Settings.SettingsAbout.Releases.versionChangelog": "{version} zapisnik promjena",
@@ -720,7 +720,7 @@
"components.StatusChecker.restartRequiredDescription": "Ponovo pokreni poslužitelja kako bi se ažurirane postavke primijenile.",
"components.TvDetails.episodeRuntime": "Trajanje epizode",
"components.TvDetails.nextAirDate": "Datum sljedećeg emitiranja",
"components.TvDetails.rtaudiencescore": "Ocjena publike na Rotten Tomatoes",
"components.TvDetails.rtaudiencescore": "Ocjena Rotten Tomatoes publike",
"components.TvDetails.seasonnumber": "Sezona {seasonNumber}",
"components.UserList.createlocaluser": "Stvori lokalnog korisnika",
"components.UserList.deleteuser": "Izbriši korisnika",
@@ -738,11 +738,11 @@
"components.PermissionEdit.autorequestSeries": "Automatsko zahtijevanje serija",
"components.PermissionEdit.createissues": "Prijavljivanje problema",
"components.PermissionEdit.manageissues": "Upravljanje problemima",
"components.PermissionEdit.managerequests": "Upravljanje zahtjevima",
"components.PermissionEdit.managerequests": "Upravljaj zahtjevima",
"components.PermissionEdit.request": "Zahtjev",
"components.PermissionEdit.request4k": "Zahtjev za 4K",
"components.PermissionEdit.request4kMovies": "Zahtjev za 4K filmove",
"components.PermissionEdit.request4kTv": "Zahtjev za 4K serije",
"components.PermissionEdit.request4k": "Zahtjev za 4K rezoluciju",
"components.PermissionEdit.request4kMovies": "Zahtjev za filmove u 4K rezoluciji",
"components.PermissionEdit.request4kTv": "Zahtjev za serije u 4K rezoluciji",
"components.PermissionEdit.requestMovies": "Zahtjev za filmovima",
"components.PersonDetails.birthdate": "Datum rođenja {birthdate}",
"components.PersonDetails.crewmember": "Suradnik",
@@ -829,7 +829,7 @@
"components.Settings.Notifications.NotificationsWebhook.agentenabled": "Aktiviraj agenta",
"components.Settings.Notifications.NotificationsWebPush.webpushsettingssaved": "Web push postavke obavijesti su uspješno spremljene!",
"components.Settings.Notifications.NotificationsWebhook.authheader": "Zaglavlje autorizacije",
"components.Settings.Notifications.NotificationsWebhook.resetPayload": "Vrati na zadane vrijednosti",
"components.Settings.Notifications.NotificationsWebhook.resetPayload": "Resetiraj na zadane vrijednosti",
"components.Settings.Notifications.NotificationsWebhook.toastWebhookTestFailed": "Neuspjelo slanje Webhook obavijesti provjere.",
"components.Settings.Notifications.NotificationsWebhook.webhooksettingsfailed": "Neuspjelo spremanje Webhook postavki obavijesti.",
"components.Settings.Notifications.NotificationsWebhook.webhooksettingssaved": "Webhook postavke obavijesti su uspješno spremljene!",
@@ -940,7 +940,7 @@
"components.TitleCard.cleardata": "Izbriši podatke",
"components.TitleCard.mediaerror": "{mediaType} nije pronađen",
"components.TitleCard.tmdbid": "TMDB ID",
"components.TvDetails.Season.somethingwentwrong": "Dogodila se greška prilikom preuzimanja podataka sezona.",
"components.TvDetails.Season.somethingwentwrong": "Dogodila se greška prilikom dohvaćanja podataka sezona.",
"components.TvDetails.anime": "Anime",
"components.TvDetails.episodeRuntimeMinutes": "{runtime} min",
"components.TvDetails.firstAirDate": "Datum prvog emitiranja",
@@ -950,12 +950,12 @@
"components.TvDetails.originaltitle": "Izvorni naslov",
"components.TvDetails.overview": "Pregled",
"components.TvDetails.overviewunavailable": "Pregled nedostupan.",
"components.TvDetails.play4konplex": "Reproduciraj u 4K na Plexu",
"components.TvDetails.play4konplex": "Reproduciraj u 4K rezoluciji na Plexu",
"components.TvDetails.playonplex": "Reproduciraj na Plexu",
"components.TvDetails.productioncountries": "{countryCount, plural, one {zemlja produkcije} few {zemlje produkcije} other {zemalja produkcije}}",
"components.TvDetails.recommendations": "Preporuke",
"components.TvDetails.reportissue": "Prijavi problem",
"components.TvDetails.rtcriticsscore": "Tomatometer na Rotten Tomatoes",
"components.TvDetails.rtcriticsscore": "Rotten Tomatoes Tomatometar",
"components.TvDetails.showtype": "Vrste serija",
"components.TvDetails.similar": "Slične serije",
"components.TvDetails.status4k": "4K {status}",
@@ -985,7 +985,7 @@
"components.UserProfile.UserSettings.UserGeneralSettings.movierequestlimit": "Ograničenje zahtjeva za filmove",
"components.UserProfile.UserSettings.UserGeneralSettings.owner": "Vlasnik",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Automatski zatraži serije na tvom <PlexWatchlistSupportLink>Plex popisu gledanja</PlexWatchlistSupportLink>",
"components.UserProfile.UserSettings.UserGeneralSettings.region": "Otkrij regiju",
"components.UserProfile.UserSettings.UserGeneralSettings.region": "Regionalno otkrivanje",
"components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsFailure": "Dogodila se greška prilikom spremanja postavki.",
"components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Postavke su uspješno spremljene!",
"components.UserProfile.UserSettings.UserGeneralSettings.user": "Korisnik",
@@ -1044,7 +1044,7 @@
"i18n.status": "Stanje",
"pages.oops": "Ups",
"components.Settings.advancedTooltip": "Neispravno konfiguriranje ove postavke može pokvariti funkcionalnost",
"components.Settings.noDefaultNon4kServer": "Ako imate samo jedan {serverType} poslužitelj za ne-4K i za 4K sadržaj (ili ako preuzimaš samo 4K sadržaj), tvoj {serverType} poslužitelj <strong>NE </strong> bi trebao biti određen kao 4K poslužitelj.",
"components.Settings.noDefaultNon4kServer": "Ako imaš samo jedan {serverType} poslužitelj za ne-4K i za 4K sadržaj (ili ako preuzimaš samo 4K sadržaj), <strong>NEMOJ</strong> odrediti svoj {serverType} poslužitelj kao 4K poslužitelj.",
"components.PermissionEdit.requestMoviesDescription": "Dozvoli automatsko slanje zahtjeva za filmove koje nisu u 4K rezoluciji.",
"components.PermissionEdit.autorequestDescription": "Dozvoli automatsko slanje zahtjeva za medije koji nisu u 4K rezoluciji putem Plex popisa gledanja.",
"components.PermissionEdit.autorequestMoviesDescription": "Dozvoli automatsko slanje zahtjeva za filmove koji nisu u 4K rezoluciji putem Plex popisa gledanja.",
@@ -1059,16 +1059,16 @@
"components.PermissionEdit.request4kTvDescription": "Dozvoli slanje zahtjeva za serijama u 4K rezoluciji.",
"components.PermissionEdit.usersDescription": "Dozvoli upravljanje korisnicima. Korisnici s ovom dozvolom ne mogu mijenjati korisnike s administratorskom privilegijom ili dozvoliti administratorske privilegije.",
"components.PermissionEdit.viewissuesDescription": "Dozvoli prikaz problema s medijima koje su prijavili drugi korisnici.",
"components.PermissionEdit.viewrequestsDescription": "Dozvoli prikaz medijskih zahtjeva koje su prijavili drugi korisnici.",
"components.PermissionEdit.viewrequestsDescription": "Dozvoli prikaz zahtjeva za medije koje su poslali drugi korisnici.",
"components.PermissionEdit.viewwatchlistsDescription": "Dozvoli prikaz Plex popisa gledanja drugih korisnika.",
"components.Settings.notrunning": "Ne pokreće se",
"components.Settings.serverRemote": "udaljeni",
"components.RequestModal.QuotaDisplay.quotaLink": "Sažetak tvojih ograničenja zahtjeva možeš vidjeti na tvojoj <ProfileLink>stranici profila</ProfileLink>.",
"components.RequestModal.QuotaDisplay.quotaLinkUser": "Sažetak ograničenja zahtjeva korisničkih zahtjeva možeš vidjeti na njihovoj <ProfileLink>stranici profila</ProfileLink>.",
"components.RequestModal.QuotaDisplay.notenoughseasonrequests": "Nema dovoljno preostalih zahtjeva za sezonama",
"components.RequestModal.QuotaDisplay.notenoughseasonrequests": "Nema dovoljno preostalih zahtjeva za sezone",
"components.RequestModal.SearchByNameModal.nomatches": "Nismo uspjeli pronaći seriju koja odgovara ovoj seriji.",
"components.RequestModal.SearchByNameModal.notvdbiddescription": "Nismo uspjeli automatski naći odgovarajuću seriju. Dolje odaberi odgovarajuću seriju.",
"components.RequestModal.requestadmin": "Ovaj će se zahtjev automatski prihvatiti.",
"components.RequestModal.requestadmin": "Ovaj će se zahtjev automatski odobriti.",
"components.ResetPassword.emailresetlink": "E-mail poveznica za obnavljanje lozinke",
"components.Settings.Notifications.NotificationsLunaSea.webhookUrlTip": "Tvoj korisničke ili na osnovi uređaja <LunaSeaLink>webhook URL obavijesti</LunaSeaLink>",
"components.Settings.Notifications.NotificationsWebPush.httpsRequirement": "Za primanje web push obavijesti, Jellyseerr se mora posluživati putem HTTPS-a.",
@@ -1082,7 +1082,7 @@
"components.Settings.plexlibrariesDescription": "Biblioteke u kojima će Jellyseerr tražiti naslove. Postavi i spremi tvoje postavke Plex veze, a zatim pritisni gumb ispod ako je popis biblioteka prazan.",
"components.Settings.startscan": "Pokreni pretraživanje",
"components.Setup.scanbackground": "Pretraživanje se pokreće u pozadini. U međuvremenu možeš nastaviti s postupkom postavljanja.",
"components.PermissionEdit.managerequestsDescription": "Dozvoli upravljanje zahtjevima za medijima. Svi zahtjevi korisnika s ovom dozvolom će se automatski odobriti.",
"components.PermissionEdit.managerequestsDescription": "Dozvoli upravljanje zahtjevima za medije. Svi zahtjevi korisnika s ovom dozvolom će se automatski odobriti.",
"components.Settings.Notifications.NotificationsWebhook.customJson": "JSON sadržaj",
"components.Settings.SettingsJobsCache.plex-full-scan": "Pretraživanje cijele Plex biblioteke",
"components.RequestModal.QuotaDisplay.allowedRequests": "Smiješ zatražiti <strong>{limit}</strong> {type} svakih <strong>{days}</strong> dana.",
@@ -1102,6 +1102,143 @@
"components.RequestList.RequestItem.unknowntitle": "Nepoznat naslov",
"components.StatusBadge.seasonepisodenumber": "S{seasonNumber}E{episodeNumber}",
"components.Discover.DiscoverTv.discovertv": "Serije",
"components.Discover.RecentlyAddedSlider.recentlyAdded": "Nedavno dodano",
"components.Discover.DiscoverMovies.discovermovies": "Filmovi"
"components.Discover.RecentlyAddedSlider.recentlyAdded": "Nedavno dodani",
"components.Discover.DiscoverMovies.discovermovies": "Filmovi",
"components.Discover.moviegenres": "Žanrovi filmova",
"components.Discover.FilterSlideover.keywords": "Ključne riječi",
"components.Discover.FilterSlideover.activefilters": "{count, plural, one {# aktivni filtar} few {# aktivna filtra} other {# aktivnih filtara}}",
"components.Discover.FilterSlideover.firstAirDate": "Datum prvog emitiranja",
"components.Discover.PlexWatchlistSlider.plexwatchlist": "Tvoj Plex popis gledanja",
"components.Discover.FilterSlideover.genres": "Žanrovi",
"components.Discover.FilterSlideover.originalLanguage": "Izvorni jezik",
"components.Discover.CreateSlider.editsuccess": "Uređen klizač i spremljene postavke prilagodbe otkrivanja.",
"components.Discover.CreateSlider.slidernameplaceholder": "Naziv klizača",
"components.Discover.DiscoverMovies.sortPopularityDesc": "Popularnost (silazno)",
"components.Discover.CreateSlider.searchStudios": "Pretražite studije…",
"components.Discover.CreateSlider.providetmdbnetwork": "Navdeite TMDB ID mreže",
"components.Discover.CreateSlider.addfail": "Izrada novog klizača nije uspjela.",
"components.Discover.DiscoverMovies.sortPopularityAsc": "Popularnost (uzlazno)",
"components.Discover.CreateSlider.needresults": "Morate imati barem 1 rezultat.",
"components.Discover.CreateSlider.addcustomslider": "Stvorite prilagođeni klizač",
"components.Discover.CreateSlider.editSlider": "Uredi klizač",
"components.Discover.CreateSlider.validationDatarequired": "Morate navesti vrijednost.",
"components.Discover.CreateSlider.providetmdbstudio": "Navedite TMDB ID studija",
"components.Discover.CreateSlider.searchGenres": "Traži žanrove…",
"components.Discover.CreateSlider.editfail": "Uređivanje klizača nije uspjelo.",
"components.Discover.CreateSlider.starttyping": "Počinje tipkati za pretraživanje.",
"components.Discover.CreateSlider.addSlider": "Dodaj klizač",
"components.Discover.CreateSlider.providetmdbsearch": "Navedite upit za pretraživanje",
"components.Discover.CreateSlider.providetmdbkeywordid": "Navdeite TMDB ID ključne riječi",
"components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} filmovi",
"components.Discover.CreateSlider.validationTitlerequired": "Morate navesti naslov.",
"components.Discover.CreateSlider.nooptions": "Nema rezultata.",
"components.Discover.CreateSlider.searchKeywords": "Pretraži ključne riječi…",
"components.Discover.CreateSlider.addsuccess": "Stvoren je novi klizač i spremljene su postavke prilagodbe otkrivanja.",
"components.Discover.CreateSlider.providetmdbgenreid": "Navedite TMDB ID žanra",
"components.Discover.DiscoverTv.sortTmdbRatingAsc": "TMDB ocjena (uzlazno)",
"components.Discover.FilterSlideover.studio": "Studio",
"components.Discover.FilterSlideover.ratingText": "Ocjena od {minValue} do {maxValue}",
"components.Discover.DiscoverTv.sortFirstAirDateAsc": "Prvo emitiranje (uzlazno)",
"components.Discover.DiscoverTv.sortTmdbRatingDesc": "TMDB ocjena (silazno)",
"components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# aktivni filtar} few {# aktivna filtra} other {# aktivnih filtara}}",
"components.Discover.DiscoverMovies.sortTmdbRatingAsc": "TMDB ocjena (uzlazno)",
"components.Discover.FilterSlideover.clearfilters": "Obriši aktivne filtre",
"components.Discover.DiscoverMovies.sortReleaseDateDesc": "Datum izlaska (silazno)",
"components.Discover.DiscoverTv.activefilters": "{count, plural, one {# aktivni filtar} few {# aktivna filtra} other {# aktivnih filtara}}",
"components.Discover.FilterSlideover.streamingservices": "Streaming usluge",
"components.Discover.FilterSlideover.tmdbuserscore": "TMDB ocjena korisnika",
"components.Discover.DiscoverTv.sortPopularityAsc": "Popularnost (uzlazno)",
"components.Discover.DiscoverTv.sortTitleAsc": "Naslov (A-Z) (uzlazno)",
"components.Discover.DiscoverTv.sortFirstAirDateDesc": "Prvo emitiranje (silazno)",
"components.Discover.FilterSlideover.releaseDate": "Datum izlaska",
"components.Discover.DiscoverSliderEdit.deletefail": "Greška pri uklanjanju klizača.",
"components.Discover.FilterSlideover.runtime": "Trajanje",
"components.Discover.FilterSlideover.from": "Od",
"components.Discover.DiscoverMovies.sortTitleDesc": "Naslov (Z-A) (silazno)",
"components.Discover.DiscoverTv.sortPopularityDesc": "Popularnost (silazno)",
"components.Discover.FilterSlideover.to": "Do",
"components.Discover.FilterSlideover.filters": "Filtri",
"components.Discover.DiscoverSliderEdit.enable": "Vidljivost",
"components.Discover.FilterSlideover.runtimeText": "trajanje {minValue}-{maxValue} minuta",
"components.Discover.DiscoverMovies.sortReleaseDateAsc": "Datum izlaska (uzlazno)",
"components.Discover.DiscoverMovies.sortTmdbRatingDesc": "TMDB ocjena (silazno)",
"components.Discover.FilterSlideover.tmdbuservotecount": "TMDB broj glasova korisnika",
"components.Discover.DiscoverSliderEdit.deletesuccess": "Klizač uspješno uklonjen.",
"components.Discover.DiscoverMovies.sortTitleAsc": "Naslov (A-Z) (uzlazno)",
"components.Discover.DiscoverTv.sortTitleDesc": "Naslov (Z-A) (silazno)",
"components.Discover.DiscoverSliderEdit.remove": "Ukloni",
"components.Discover.tvgenres": "Žanrovi serija",
"components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} serije",
"components.Selector.showmore": "Pokaži više",
"components.Selector.searchGenres": "Odaberi žanrove …",
"components.Selector.searchStudios": "Traži studija …",
"components.Discover.tmdbtvgenre": "TMDB žanr serije",
"components.Selector.showless": "Pokaži manje",
"components.Selector.starttyping": "Počni tipkati za pretraživanje.",
"components.Selector.searchKeywords": "Traži ključne riječi …",
"components.Selector.nooptions": "Nema rezultata.",
"components.Discover.tmdbmoviegenre": "TMDB žanr filmova",
"components.Discover.FilterSlideover.voteCount": "Broj glasova između {minValue} i {maxValue}",
"components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "{jobScheduleHours, plural, one {Svake sekunde} few {Svake {jobScheduleHours} sekunde} other {Svakih {jobScheduleHours} sekundi}}",
"components.Layout.Sidebar.browsemovies": "Filmovi",
"components.Layout.Sidebar.browsetv": "Serije",
"components.Discover.updatesuccess": "Aktualiziraj postavke za prilagođavanje otkrivanja.",
"components.Discover.updatefailed": "Dogodila se greška tiijekom aktualiziranja postavki za prilagođavanje otkrivanja.",
"components.Settings.SettingsMain.toastApiKeyFailure": "Dogodila se greška prilikom generiranja novog API ključa.",
"components.Settings.SettingsMain.toastSettingsFailure": "Dogodila se greška prilikom spremanja postavki.",
"components.Discover.PlexWatchlistSlider.emptywatchlist": "Mediji koji su dodani u tvoj <PlexWatchlistSupportLink>Plex popis gledanja</PlexWatchlistSupportLink> pojavit će se ovdje.",
"components.Discover.networks": "Mreže",
"components.Discover.studios": "Studiji",
"components.Settings.SettingsMain.hideAvailable": "Sakrij dostupne medije",
"components.Settings.Notifications.NotificationsPushover.sound": "Zvuk obavijesti",
"components.Discover.tmdbmoviekeyword": "TMDB ključna riječ filmova",
"components.Discover.stopediting": "Prekini uređivanje",
"components.Discover.resetsuccess": "Uspješno resetiranje postavki prilagodbe otkrivanja.",
"components.Discover.customizediscover": "Prilagodi otkrivanje",
"components.Settings.SettingsMain.locale": "Jezik prikaza",
"components.Discover.tmdbtvkeyword": "TMDB ključna riječ serija",
"components.Discover.tmdbnetwork": "TMDB mreža",
"components.Settings.SettingsMain.applicationTitle": "Naslov programa",
"components.Settings.SettingsMain.originallanguage": "Jezik otkrivanja",
"components.Settings.SettingsMain.csrfProtection": "Aktiviraj CSRF zaštitu",
"components.Discover.tmdbstudio": "TMDB studio",
"components.Settings.SettingsMain.generalsettings": "Opće postavke",
"components.MovieDetails.imdbuserscore": "IMDB ocjena korisnika",
"components.Discover.tmdbtvstreamingservices": "TMDB streaming usluge za TV",
"components.Settings.SettingsMain.apikey": "API ključ",
"components.Settings.SettingsMain.cacheImages": "Aktiviraj predmemoriranje slika",
"components.Discover.tmdbmoviestreamingservices": "TMDB streaming usluge filmova",
"components.Discover.resetfailed": "Dogodila se greška prilikom resetiranja postavki prilagodbe otkrivanja.",
"components.Discover.tmdbsearch": "TMDB pretraga",
"components.Settings.SettingsMain.applicationurl": "URL programa",
"components.Settings.SettingsMain.general": "Opće",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "Zvuk obavijesti",
"components.Discover.resettodefault": "Resetiraj na zadane vrijednosti",
"components.Settings.SettingsMain.regionTip": "Filtriraj sadržaj prema regionalnoj dostupnosti",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Zadane postavke uređaja",
"components.Settings.SettingsMain.region": "Regionalno otkrivanje",
"components.Settings.SettingsMain.trustProxy": "Aktiviraj proxy podršku",
"components.Settings.SettingsMain.toastSettingsSuccess": "Postavke su uspješno spremljene!",
"i18n.collection": "Zbirka",
"components.Settings.SettingsMain.originallanguageTip": "Filtriraj sadržaj po izvornom jeziku",
"components.Settings.SettingsMain.cacheImagesTip": "Predmemoriraj slike eksternih izvora (zahtijeva značajnu količinu memorije na disku)",
"components.Settings.SettingsJobsCache.availability-sync": "Sinkronizacija dostupnosti medija",
"components.Settings.SettingsMain.trustProxyTip": "Dopusti Overseerru da ispravno registrira IP adrese klijenata iza proxyja",
"components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "URL ne smije završiti s kosom crtom",
"components.Settings.SonarrModal.animeSeriesType": "Vrsta anime serije",
"components.Settings.SettingsMain.generalsettingsDescription": "Konfiguriraj globalne i zadane postavke za Overseerr.",
"components.Settings.SettingsMain.toastApiKeySuccess": "Novi API ključ je uspješno generiran!",
"components.Settings.SonarrModal.seriesType": "Vrsta serije",
"components.Settings.SettingsMain.csrfProtectionHoverTip": "NEMOJ aktivirati ovu postavku ako ne razumiješ što radiš!",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "Zadane postavke uređaja",
"components.Settings.SettingsMain.validationApplicationUrl": "Moraš navesti valjani URL",
"components.Settings.SettingsMain.validationApplicationTitle": "Moraš navesti naslov programa",
"components.Settings.SettingsMain.csrfProtectionTip": "Postavi eksterni API pristup u samo-za-čitanje (zahtijeva HTTPS)",
"components.Discover.resetwarning": "Vrati sve klizače na zadane postavke. To će također izbrisati sve prilagođene klizače!",
"components.Settings.SonarrModal.tagRequestsInfo": "Automatski dodaj dodatnu oznaku s korisničkim ID-om i prikaznim imenom podnositelja zahtjeva",
"components.Settings.RadarrModal.tagRequestsInfo": "Automatski dodaj dodatnu oznaku s korisničkim ID-om i prikaznim imenom podnositelja zahtjeva",
"components.Discover.createnewslider": "Stvori novi klizač",
"components.Settings.SettingsMain.partialRequestsEnabled": "Dopusti djelomične zahtjeve za seriju",
"components.Settings.SonarrModal.tagRequests": "Označi zahtjeve",
"components.Settings.RadarrModal.tagRequests": "Označi zahtjeve"
}

View File

@@ -457,7 +457,7 @@
"components.Settings.SettingsJobsCache.jobname": "Feladat neve",
"components.Settings.SettingsJobsCache.jobcancelled": "{jobname} törölve.",
"components.Settings.SettingsJobsCache.flushcache": "Cache kiürítése",
"components.Settings.SettingsJobsCache.download-sync-reset": "Szinkronizálás \"reset\" letöltése",
"components.Settings.SettingsJobsCache.download-sync-reset": "Töltse le a Sync Reset",
"components.Settings.SettingsJobsCache.download-sync": "Szinkronizálás letöltése",
"components.Settings.SettingsJobsCache.command": "Parancs",
"components.Settings.SettingsJobsCache.canceljob": "Feladat megszakítása",
@@ -491,14 +491,14 @@
"components.Settings.SettingsAbout.Releases.releases": "Kiadások",
"components.Settings.SettingsAbout.Releases.releasedataMissing": "Kiadási adatok nem állnak rendelkezésre.",
"components.Settings.SettingsAbout.Releases.latestversion": "Legújabb",
"components.Settings.SettingsAbout.Releases.currentversion": "Jelenlegi verzió",
"components.Settings.SettingsAbout.Releases.currentversion": "Jelenlegi",
"components.Settings.RadarrModal.validationRootFolderRequired": "Ki kell választania egy root mappát",
"components.Settings.RadarrModal.validationProfileRequired": "Ki kell választania egy minőség profilt",
"components.Settings.RadarrModal.validationPortRequired": "Érvényes portszámot kell megadnia",
"components.Settings.RadarrModal.validationNameRequired": "Meg kell adnia a szerver nevét",
"components.Settings.RadarrModal.validationMinimumAvailabilityRequired": "Ki kell választania egy minimális elérhetőséget",
"components.Settings.RadarrModal.validationHostnameRequired": "Meg kell adnia egy hostnevet vagy IP-címet",
"components.Settings.RadarrModal.validationBaseUrlTrailingSlash": "Az alap URL-nek nem lehet vége perjel",
"components.Settings.RadarrModal.validationHostnameRequired": "Érvényes gazdagépnevet vagy IP-címet kell megadnia",
"components.Settings.RadarrModal.validationBaseUrlTrailingSlash": "Az URL alapja nem végződhet perjelre",
"components.Settings.RadarrModal.validationBaseUrlLeadingSlash": "Az alap URL-nek perjellel kell végződnie",
"components.Settings.RadarrModal.validationApplicationUrlTrailingSlash": "Az URL-nek nem lehet vége perjel",
"components.Settings.RadarrModal.validationApplicationUrl": "Érvényes URL-t kell megadnia",
@@ -697,7 +697,7 @@
"components.RequestList.RequestItem.requested": "Kérve",
"components.RequestList.RequestItem.modifieduserdate": "{date} {user} által",
"components.RequestList.RequestItem.modified": "Módosított",
"components.RequestList.RequestItem.mediaerror": "A kéréshez társított címe már nem érhető el.",
"components.RequestList.RequestItem.mediaerror": "{mediaType} Nem található",
"components.RequestList.RequestItem.editrequest": "Kérés szerkesztése",
"components.RequestList.RequestItem.deleterequest": "Kérés törlése",
"components.RequestList.RequestItem.cancelRequest": "Kérés visszavonása",
@@ -989,9 +989,9 @@
"i18n.resolved": "Megoldva",
"components.Settings.RadarrModal.inCinemas": "Mozikban",
"components.Settings.SettingsJobsCache.editJobSchedule": "Feladat Módosítása",
"components.Settings.SettingsJobsCache.editJobSchedulePrompt": "Gyakoriság",
"components.Settings.SettingsJobsCache.editJobSchedulePrompt": "Új frekvencia",
"components.RequestBlock.languageprofile": "Nyelv Profil",
"components.RequestModal.requestApproved": "Kérés elfogadva: <strong>{title}</strong>",
"components.RequestModal.requestApproved": "Kérés <strong>{title}</strong> elfogadva!",
"components.Settings.Notifications.NotificationsGotify.gotifysettingsfailed": "A Gotify értesítési beállításait nem sikerült elmenteni.",
"components.Settings.Notifications.NotificationsGotify.toastGotifyTestSuccess": "Gotify teszt értesítés elküldve!",
"components.Settings.Notifications.NotificationsGotify.token": "Alkalmazás Token",
@@ -1040,5 +1040,151 @@
"components.RequestCard.approverequest": "Kérelem jóváhagyása",
"components.Layout.UserDropdown.MiniQuotaDisplay.movierequests": "Filmkérések",
"components.Layout.UserDropdown.requests": "Kérések",
"components.RequestModal.requestcollectiontitle": "Gyűjtemény kérése"
"components.RequestModal.requestcollectiontitle": "Gyűjtemény kérése",
"components.Settings.SettingsJobsCache.editJobScheduleCurrent": "Aktuális frekvencia",
"components.Settings.restartrequiredTooltip": "Az Jellyseerr-t újra kell indítani, hogy a beállítás módosításai életbe lépjenek",
"components.StatusBadge.managemedia": "{mediaType} kezelése",
"components.StatusBadge.openinarr": "Nyitás itt: {arr}",
"components.StatusChecker.appUpdatedDescription": "Kérjük, kattintson az alábbi gombra az alkalmazás újratöltéséhez.",
"components.TitleCard.mediaerror": "{mediaType} Nem található",
"components.TvDetails.Season.somethingwentwrong": "Hiba történt az évadadatok lekérésekor.",
"components.StatusBadge.playonplex": "Lejátszás Plexen",
"components.TvDetails.rtaudiencescore": "Rotten Tomatoes közönségpontszám",
"components.TvDetails.seasonstitle": "Évadok",
"components.TvDetails.seasonnumber": "{seasonNumber} Évad",
"components.TvDetails.tmdbuserscore": "TMDB felhasználói pontszám",
"components.UserProfile.emptywatchlist": "Itt jelennek meg a <PlexWatchlistSupportLink>Plex figyelőlistájához</PlexWatchlistSupportLink> hozzáadott médiák.",
"components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKey": "Felhasználói vagy csoportkulcs",
"components.StatusChecker.appUpdated": "{applicationTitle} Frissítve",
"components.IssueModal.CreateIssueModal.toastSuccessCreate": "A(z) <strong>{title}</strong> problémajelentése sikeresen elküldve!",
"components.PermissionEdit.viewrecentDescription": "Adjon engedélyt a nemrég hozzáadott médialista megtekintéséhez.",
"components.MovieDetails.theatricalrelease": "Színházi kiadás",
"components.NotificationTypeSelector.mediaautorequestedDescription": "Értesítést kaphat, ha a rendszer automatikusan új médiakérelmeket küld a Plex figyelőlistáján szereplő elemekhez.",
"components.Settings.SettingsJobsCache.plex-watchlist-sync": "Plex figyelőlista szinkronizálása",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Filmek automatikus kérése a <PlexWatchlistSupportLink>Plex figyelőlistán</PlexWatchlistSupportLink>",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseries": "Automatikus sorozat kérés",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "Filmek automatikus kérése",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Sorozatok automatikus kérése a <PlexWatchlistSupportLink>Plex figyelőlistán</PlexWatchlistSupportLink>",
"components.PermissionEdit.viewwatchlists": "Plex figyelőlisták megtekintése",
"components.Settings.experimentalTooltip": "A beállítás engedélyezése váratlan alkalmazási viselkedést eredményezhet",
"components.Settings.deleteServer": "Törölje a {serverType} szervert",
"components.StatusChecker.reloadApp": "{applicationTitle} újratöltése",
"components.StatusChecker.restartRequired": "Szerver újraindítása szükséges",
"components.StatusChecker.restartRequiredDescription": "Kérjük, indítsa újra a szervert a frissített beállítások alkalmazásához.",
"components.TitleCard.cleardata": "Adatok törlése",
"components.UserProfile.plexwatchlist": "Plex figyelőlista",
"components.TvDetails.manageseries": "Sorozatok kezelése",
"components.Settings.advancedTooltip": "A beállítás helytelen konfigurálása a funkció meghibásodását eredményezheti",
"components.Discover.DiscoverWatchlist.watchlist": "Plex figyelőlista",
"components.Settings.SettingsLogs.viewdetails": "Részletek megtekintése",
"components.TvDetails.reportissue": "Probléma bejelentése",
"components.PermissionEdit.viewwatchlistsDescription": "Adjon engedélyt más felhasználók Plex figyelőlistájának megtekintéséhez.",
"components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKeyTip": "Az Ön 30 karakteres <UsersGroupsLink>felhasználó- vagy csoportazonosítója</UsersGroupsLink>",
"components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverUserKey": "Meg kell adnia egy érvényes felhasználói vagy csoportkulcsot",
"components.Discover.plexwatchlist": "Az Ön Plex figyelőlistája",
"components.RequestModal.SearchByNameModal.nomatches": "Nem találtunk megfelelőt ehhez a sorozathoz.",
"components.RequestModal.requestmovie4ktitle": "Film kérése 4K-ban",
"components.RequestModal.requestmovietitle": "Film kérése",
"components.RequestModal.requestseries4ktitle": "Sorozat kérése 4K-ban",
"components.Discover.emptywatchlist": "Itt jelennek meg a <PlexWatchlistSupportLink>Plex figyelőlistájához</PlexWatchlistSupportLink> hozzáadott médiák.",
"components.RequestModal.requestseriestitle": "Sorozat kérelem",
"components.PermissionEdit.autorequestDescription": "Adjon engedélyt a nem 4K-s médiára vonatkozó kérések automatikus benyújtására a Plex figyelőlistán keresztül.",
"components.PermissionEdit.autorequestMoviesDescription": "Adjon engedélyt nem 4K-s filmekre vonatkozó kérelmek automatikus benyújtására a Plex figyelőlistán keresztül.",
"components.AirDateBadge.airedrelative": "Adott {relativeTime}",
"components.AirDateBadge.airsrelative": "Sugárzott {relativeTime}",
"components.PermissionEdit.autorequestSeriesDescription": "Adjon engedélyt a nem 4K sorozatokra vonatkozó kérelmek automatikus benyújtására a Plex figyelőlistán keresztül.",
"components.RequestModal.requestseasons4k": "Kérés {seasonCount} {seasonCount, plural, one {Season} other {Seasons}} 4K-ban",
"components.TitleCard.tmdbid": "TMDB azonosító",
"components.TvDetails.episodeCount": "{episodeCount, plural, one {# Episode} other {# Episodes}}",
"components.TvDetails.rtcriticsscore": "Rotten Tomatoes Tomatomérő",
"components.TvDetails.status4k": "4K {status}",
"components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "Minden {jobScheduleMinutes, plural, one {minute} other {{jobScheduleMinutes} minutes}}",
"components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "Minden {jobScheduleHours, plural, one {hour} other {{jobScheduleHours} hours}}",
"components.PermissionEdit.viewrecent": "Nemrég hozzáadott megtekintése",
"components.TitleCard.tvdbid": "TheTVDB azonosító",
"components.MovieDetails.productioncountries": "Gyártás {countryCount, plural, one {Country} other {Countries}}",
"components.TvDetails.productioncountries": "Gyártás {countryCount, plural, one {Country} other {Countries}}",
"components.UserList.newplexsigninenabled": "Az <strong>Új Plex bejelentkezés engedélyezése</strong> beállítás jelenleg engedélyezve van. A könyvtár-hozzáféréssel rendelkező Plex-felhasználókat nem kell importálni a bejelentkezéshez.",
"components.MovieDetails.rtcriticsscore": "Rotten Tomatoes Tomatomérő",
"components.RequestBlock.requestedby": "Kérte",
"components.RequestModal.requestmovies": "Kérés {count} {count, plural, one {Movie} other {Movies}}",
"components.RequestModal.requestmovies4k": "Kérés {count} {count, plural, one {Movie} other {Movies}} 4K-ban",
"components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationTokenTip": "<ApplicationRegistrationLink>Alkalmazás regisztrálása</ApplicationRegistrationLink> a(z) {applicationTitle} alkalmazáshoz",
"components.RequestCard.tmdbid": "TMDB azonosító",
"components.RequestCard.tvdbid": "TheTVDB azonosító",
"components.RequestList.RequestItem.tmdbid": "TMDB azonosító",
"components.RequestList.RequestItem.tvdbid": "TheTVDB azonosító",
"components.RequestModal.requestcollection4ktitle": "Gyűjtemény kérés 4K-ban",
"components.Discover.CreateSlider.editsuccess": "Szerkesztett csúszka és mentett felfedezés testreszabási beállítások.",
"components.Settings.SettingsJobsCache.imagecache": "Képgyorsítótár",
"components.Settings.SettingsJobsCache.imagecachesize": "Teljes gyorsítótár mérete",
"components.Discover.CreateSlider.slidernameplaceholder": "Csúszka neve",
"components.Discover.DiscoverTv.sortFirstAirDateAsc": "Első adás dátuma növekvő",
"components.Discover.DiscoverMovies.sortPopularityDesc": "Népszerűség csökkenő",
"components.Settings.SettingsJobsCache.image-cache-cleanup": "Képgyorsítótár tisztítása",
"i18n.restartRequired": "Újraindítás szükséges",
"components.Discover.DiscoverMovies.sortTmdbRatingAsc": "TMDB minősítés növekvő",
"components.Discover.FilterSlideover.clearfilters": "Aktív szűrők törlése",
"components.Discover.CreateSlider.searchStudios": "Stúdiók keresése…",
"components.Discover.DiscoverMovies.sortReleaseDateDesc": "Megjelenés dátuma csökkenő sorrendben",
"components.Discover.CreateSlider.providetmdbnetwork": "Adja meg a TMDB hálózati azonosítót",
"components.Settings.SettingsMain.applicationTitle": "Pályázat címe",
"components.Discover.CreateSlider.addfail": "Nem sikerült új csúszkát létrehozni.",
"components.Discover.DiscoverMovies.sortPopularityAsc": "Növekvő népszerűség",
"components.Discover.CreateSlider.needresults": "Legalább 1 eredménynek kell lennie.",
"components.Discover.CreateSlider.addcustomslider": "Egyéni csúszka létrehozása",
"components.Discover.DiscoverTv.sortPopularityAsc": "Növekvő népszerűség",
"components.Discover.CreateSlider.editSlider": "Csúszka szerkesztése",
"components.Discover.CreateSlider.validationDatarequired": "Meg kell adnia egy adatértéket.",
"components.Discover.DiscoverTv.sortFirstAirDateDesc": "Első adási dátum csökkenő",
"components.Discover.DiscoverTv.discovertv": "Sorozat",
"components.Discover.DiscoverSliderEdit.deletefail": "Nem sikerült törölni a csúszkát.",
"components.Discover.CreateSlider.providetmdbstudio": "Adja meg a TMDB Studio azonosítóját",
"components.Settings.SettingsMain.cacheImages": "Képgyorsítótár engedélyezése",
"components.Discover.DiscoverTv.sortPopularityDesc": "Népszerűség csökkenő",
"components.Discover.CreateSlider.searchGenres": "Műfajok keresése…",
"components.Discover.CreateSlider.editfail": "Nem sikerült szerkeszteni a csúszkát.",
"components.Discover.CreateSlider.starttyping": "Gépelés megkezdése a kereséshez.",
"components.Discover.FilterSlideover.filters": "Szűrők",
"components.Discover.DiscoverSliderEdit.enable": "Kapcsolja be a láthatóságot",
"components.Discover.CreateSlider.addSlider": "Csúszka hozzáadása",
"components.Settings.SettingsJobsCache.imagecachecount": "Képek gyorsítótárban",
"components.Discover.CreateSlider.providetmdbsearch": "Adjon meg egy keresési lekérdezést",
"components.Discover.CreateSlider.providetmdbkeywordid": "Adjon meg egy TMDB kulcsszóazonosítót",
"components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} Filmek",
"components.Discover.CreateSlider.validationTitlerequired": "Meg kell adnia a címet.",
"components.Discover.DiscoverMovies.sortReleaseDateAsc": "Kiadási dátum Növekvő sorrendben",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "Értesítési hang",
"components.Discover.CreateSlider.nooptions": "Nincs eredmény.",
"components.Discover.CreateSlider.searchKeywords": "Kulcsszavak keresése…",
"components.Discover.CreateSlider.addsuccess": "Új csúszkát hozott létre, és elmentette a felfedezés testreszabási beállításait.",
"components.Discover.DiscoverSliderEdit.deletesuccess": "A csúszka sikeresen törölve.",
"components.Discover.DiscoverMovies.discovermovies": "Filmek",
"components.Discover.CreateSlider.providetmdbgenreid": "Adjon meg egy TMDB műfajazonosítót",
"components.Discover.DiscoverSliderEdit.remove": "Távolítsa el",
"components.Discover.PlexWatchlistSlider.emptywatchlist": "A hozzáadott média a <PlexWatchlistSupportLink>Plex figyelőlistáján</PlexWatchlistSupportLink> elérhető.",
"components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} Sorzoat",
"components.Discover.DiscoverMovies.sortTmdbRatingDesc": "TMDB Értékelés Csökkenő",
"components.Discover.DiscoverTv.sortTitleAsc": "Cím (A-Z) Növekvő",
"components.Discover.DiscoverTv.sortTitleDesc": "Cím (Z-A) Csökkenő",
"components.Discover.FilterSlideover.ratingText": "Értékelések {minValue} és {maxValue} között",
"components.Discover.FilterSlideover.tmdbuservotecount": "TMDB Felhasználók szavazat szám",
"components.Discover.FilterSlideover.releaseDate": "Megjelenési dátum",
"components.Discover.FilterSlideover.voteCount": "Értékelések száma {minValue} és {maxValue} között",
"components.Discover.FilterSlideover.originalLanguage": "Eredeti Nyelv",
"components.Discover.PlexWatchlistSlider.plexwatchlist": "A te Plex figyelőlistád",
"components.Discover.RecentlyAddedSlider.recentlyAdded": "Nemrég hozzáadott",
"components.Discover.moviegenres": "Film műfajok",
"components.Discover.DiscoverMovies.sortTitleAsc": "Cím (A-Z) Növekvő",
"components.Discover.DiscoverMovies.sortTitleDesc": "Cím (Z-A) Csökkenő",
"components.Discover.DiscoverTv.sortTmdbRatingAsc": "TMDB Értékelés Növekvő",
"components.Discover.DiscoverTv.sortTmdbRatingDesc": "TMDB Értékelés Csökkenő",
"components.Discover.FilterSlideover.firstAirDate": "Első adási dátum",
"components.Discover.FilterSlideover.genres": "Műfajok",
"components.Discover.FilterSlideover.keywords": "Kulcsszavak",
"components.Discover.FilterSlideover.runtime": "Músoridő",
"components.Discover.FilterSlideover.runtimeText": "{minValue}-{maxValue} percnyi műsoridő",
"components.Discover.FilterSlideover.studio": "Stúdió",
"components.Discover.FilterSlideover.tmdbuserscore": "TMDB Felhasználói Értékelés",
"components.Discover.FilterSlideover.streamingservices": "Streaming Szolgáltatók"
}

View File

@@ -1165,5 +1165,15 @@
"components.Settings.SettingsMain.toastSettingsSuccess": "Impostazioni salvate con successo!",
"components.UserProfile.plexwatchlist": "Plex Watchlist",
"components.TvDetails.seasonstitle": "Stagioni",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Richiedi automaticamente le serie presenti nella tua <PlexWatchlistSupportLink>Plex Watchlist</PlexWatchlistSupportLink>"
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Richiedi automaticamente le serie presenti nella tua <PlexWatchlistSupportLink>Plex Watchlist</PlexWatchlistSupportLink>",
"components.Discover.moviegenres": "Film per genere",
"components.RequestModal.requestseries4ktitle": "Richiedi serie in 4K",
"components.RequestModal.requestmovie4ktitle": "Richiedi film in 4K",
"components.Discover.FilterSlideover.tmdbuserscore": "Voto utenti TMDB",
"components.StatusBadge.openinarr": "Apri su {arr}",
"components.Settings.SonarrModal.animeSeriesType": "Tipo serie anime",
"components.Settings.SonarrModal.seriesType": "Tipo serie TV",
"components.RequestModal.requestmovietitle": "Richiedi film",
"components.Discover.FilterSlideover.tmdbuservotecount": "Numero voti utenti TMDB",
"components.RequestModal.requestseriestitle": "Richiedi serie"
}

View File

@@ -1232,5 +1232,6 @@
"components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# 선택한 필터} other {# 선택한 필터}}",
"components.QuotaSelector.seasons": "{count, plural, one {시즌} other {시즌}}",
"components.RequestModal.QuotaDisplay.requestsremaining": "{remaining, plural, =0 {없음} other {<strong>#</strong>개의}} {type} {remaining, plural, one {요청} other {요청}} 남음",
"components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "매 {jobScheduleHours, plural, one {시간} other {{jobScheduleHours} 시간}}"
"components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "매 {jobScheduleHours, plural, one {시간} other {{jobScheduleHours} 시간}}",
"components.MovieDetails.imdbuserscore": "IMDB 사용자 점수"
}

View File

@@ -1261,5 +1261,19 @@
"components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Elke {jobScheduleSeconds, plural, one {seconde} other {{jobScheduleSeconds} seconden}}",
"components.Settings.SettingsJobsCache.availability-sync": "Synchronisatie van mediabeschikbaarheid",
"components.Discover.tmdbmoviestreamingservices": "Streamingdiensten voor films TMDB",
"components.Discover.tmdbtvstreamingservices": "Streamingdiensten voor series TMDB"
"components.Discover.tmdbtvstreamingservices": "Streamingdiensten voor series TMDB",
"components.Discover.FilterSlideover.tmdbuservotecount": "Aantal stemmen TMDB-gebruikers",
"components.Discover.FilterSlideover.voteCount": "Aantal stemmen tussen {minValue} en {maxValue}",
"components.Settings.RadarrModal.tagRequests": "Tagverzoeken",
"components.Settings.RadarrModal.tagRequestsInfo": "Voeg automatisch een extra tag toe met de gebruikers-ID en weergavenaam van de aanvrager",
"components.MovieDetails.imdbuserscore": "Gebruikersscore IMDB",
"components.Settings.SonarrModal.tagRequests": "Tagverzoeken",
"components.Settings.SonarrModal.tagRequestsInfo": "Voeg automatisch een extra tag toe met de gebruikers-ID en weergavenaam van de aanvrager",
"i18n.collection": "Collectie",
"components.Settings.Notifications.NotificationsPushover.sound": "Meldingsgeluid",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Apparaatstandaard",
"components.Settings.SonarrModal.animeSeriesType": "Serietype anime",
"components.Settings.SonarrModal.seriesType": "Serietype",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "Meldingsgeluid",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "Apparaatstandaard"
}

View File

@@ -1060,5 +1060,197 @@
"components.Layout.UserDropdown.requests": "Prośby",
"components.MovieDetails.rtaudiencescore": "Ocena Rotten Tomatoes",
"components.MovieDetails.rtcriticsscore": "Tomatometer Rotten Tomatoes",
"components.MovieDetails.tmdbuserscore": "Ocena użytkowników TMDB"
"components.MovieDetails.tmdbuserscore": "Ocena użytkowników TMDB",
"components.Settings.SettingsJobsCache.editJobScheduleCurrent": "Bieżąca częstotliwość",
"components.TvDetails.seasonnumber": "Sezon {seasonNumber}",
"components.TvDetails.seasonstitle": "Sezony",
"components.Settings.SettingsJobsCache.imagecache": "Pamięć podręczna obrazów",
"components.PermissionEdit.viewrecent": "Wyświetl ostatnio dodane",
"components.PermissionEdit.viewrecentDescription": "Przyznaj uprawnienia do przeglądania listy ostatnio dodanych multimediów.",
"components.TitleCard.cleardata": "Wyczyść dane",
"components.RequestList.RequestItem.tmdbid": "Identyfikator TMDB",
"components.RequestList.RequestItem.tvdbid": "Identyfikator TVDB",
"components.TitleCard.tmdbid": "Identyfikator TMDB",
"components.Settings.SettingsJobsCache.plex-watchlist-sync": "Synchronizacja listy obserwowanych Plex",
"components.TitleCard.mediaerror": "Nie znaleziono {mediaType}",
"components.TitleCard.tvdbid": "Identyfikator TVDB",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Automatyczne zamawianie filmów z <PlexWatchlistSupportLink>listy obserwowanych Plex</PlexWatchlistSupportLink>",
"components.PermissionEdit.autorequestSeriesDescription": "Udziel zgody na automatyczne przesyłanie próśb dotyczących multimediów innych niż 4K za pośrednictwem listy obserwowanych Plex.",
"components.PermissionEdit.viewwatchlists": "Wyświetlanie list obserwacyjnych Plex",
"components.PermissionEdit.viewwatchlistsDescription": "Przyznaj uprawnienia do przeglądania list obserwowanych Plex innych użytkowników.",
"components.RequestCard.tmdbid": "Identyfikator TMDB",
"components.RequestCard.tvdbid": "Identyfikator TVDB",
"components.Settings.SettingsLogs.viewdetails": "Zobacz szczegóły",
"components.Settings.restartrequiredTooltip": "Overseerr musi zostać ponownie uruchomiony, aby zmiany tego ustawienia zaczęły obowiązywać",
"components.TvDetails.reportissue": "Zgłoś problem",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseries": "Automatyczna prośba o serial",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Automatyczne zamawianie filmów z <PlexWatchlistSupportLink>listy obserwowanych Plex</PlexWatchlistSupportLink>",
"components.UserProfile.plexwatchlist": "Lista obserwowanych Plex",
"components.RequestCard.cancelrequest": "Anuluj prośbę",
"components.RequestCard.declinerequest": "Odrzuć prośbę",
"components.RequestCard.approverequest": "Zatwierdź prośbę",
"components.DownloadBlock.formattedTitle": "{title}: Sezon {seasonNumber} Odcinek {episodeNumber}",
"components.RequestBlock.approve": "Zatwierdź prośbę",
"components.RequestBlock.decline": "Odrzuć prośbę",
"components.RequestBlock.delete": "Usuń prośbę",
"components.RequestBlock.edit": "Edytuj prośbę",
"components.RequestBlock.lastmodifiedby": "Ostatnio zmodyfikowane przez",
"components.RequestBlock.requestdate": "Data złożenia prośby",
"components.RequestBlock.requestedby": "Prośba zgłoszona przez",
"components.RequestCard.editrequest": "Edytuj prośbę",
"components.RequestModal.requestcollection4ktitle": "Poproś o kolekcję w 4K",
"components.RequestModal.requestcollectiontitle": "Poproś o kolekcję",
"components.RequestModal.requestmovie4ktitle": "Poproś o film w 4K",
"components.RequestModal.requestmovietitle": "Poproś o film",
"components.RequestModal.requestseries4ktitle": "Poproś o serial w 4K",
"components.RequestModal.requestseriestitle": "Poproś o serial",
"components.Settings.SettingsJobsCache.image-cache-cleanup": "Czyszczenie pamięci podręcznej obrazów",
"components.Settings.SettingsJobsCache.imagecacheDescription": "Po włączeniu w ustawieniach Overseerr będzie pośredniczyć i buforować obrazy ze wstępnie skonfigurowanych źródeł zewnętrznych. Obrazy z pamięci podręcznej są zapisywane w folderze konfiguracji. Możesz znaleźć pliki w <code>{appDataPath}/cache/images</code>.",
"components.Settings.SettingsJobsCache.imagecachecount": "Obrazy zapisane w pamięci podręcznej",
"components.Settings.SettingsJobsCache.imagecachesize": "Całkowity rozmiar pamięci podręcznej",
"components.Settings.advancedTooltip": "Nieprawidłowe skonfigurowanie tego ustawienia może spowodować nieprawidłowe działanie",
"components.Settings.experimentalTooltip": "Włączenie tego ustawienia może spowodować nieoczekiwane zachowanie aplikacji",
"components.TvDetails.rtaudiencescore": "Ocena publiczności Rotten Tomatoes",
"components.TvDetails.rtcriticsscore": "Rotten Tomatoes Tomatometr",
"components.TvDetails.status4k": "4K {status}",
"components.TvDetails.tmdbuserscore": "Ocena użytkowników TMDB",
"components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "Filmy z próśb automatycznych",
"components.UserProfile.emptywatchlist": "W tym miejscu pojawią się multimedia dodane do <PlexWatchlistSupportLink>listy obserwowanych Plex</PlexWatchlistSupportLink>.",
"components.RequestCard.unknowntitle": "Nieznany tytuł",
"components.RequestList.RequestItem.unknowntitle": "Nieznany tytuł",
"components.StatusBadge.playonplex": "Odtwórz na Plex",
"components.StatusBadge.seasonepisodenumber": "S{seasonNumber}E{episodeNumber}",
"components.TvDetails.Season.noepisodes": "Lista odcinków jest niedostępna.",
"components.TvDetails.Season.somethingwentwrong": "Coś poszło nie tak podczas pobierania danych o sezonie.",
"components.TvDetails.manageseries": "Zarządzaj serialem",
"components.Discover.emptywatchlist": "W tym miejscu pojawią się multimedia dodane do <PlexWatchlistSupportLink>listy obserwowanych Plex</PlexWatchlistSupportLink>.",
"components.RequestModal.SearchByNameModal.nomatches": "Nie udało nam się znaleźć dopasowania do tej serii.",
"components.StatusBadge.managemedia": "Zarządzaj {mediaType}",
"components.StatusBadge.openinarr": "Otwórz w {arr}",
"components.TvDetails.episodeCount": "{episodeCount, plural, one {# Odcinek} other {# Odcinki}}",
"components.Discover.CreateSlider.addSlider": "Dodaj Suwak",
"components.Discover.CreateSlider.addcustomslider": "Utwórz niestandardowy Suwak",
"components.Discover.CreateSlider.addfail": "Nie udało się utworzyć nowego Suwaka.",
"components.Discover.CreateSlider.addsuccess": "Utworzono nowy Suwaki zapisano ustawienia dostosowywania odnajdywania.",
"components.Discover.CreateSlider.editSlider": "Edytuj Suwak",
"components.Discover.CreateSlider.editfail": "Nie udało się edytować Suwaka.",
"components.Discover.CreateSlider.editsuccess": "Edytowano Suwak i zapisano ustawienia odkrywania.",
"components.Discover.CreateSlider.needresults": "Musisz mieć co najmniej 1 wynik.",
"components.Discover.CreateSlider.nooptions": "Brak wyników.",
"components.Discover.CreateSlider.providetmdbgenreid": "Podaj identyfikator gatunku TMDB",
"components.Discover.CreateSlider.providetmdbkeywordid": "Podaj identyfikator słowa kluczowego TMDB",
"components.Discover.CreateSlider.providetmdbnetwork": "Podaj identyfikator platformy TMDB",
"components.Discover.CreateSlider.providetmdbsearch": "Podaj zapytanie wyszukiwania",
"components.Discover.CreateSlider.providetmdbstudio": "Podaj identyfikator studia TMDB",
"components.Discover.CreateSlider.searchGenres": "Szukaj gatunków…",
"components.Discover.CreateSlider.searchKeywords": "Szukaj słów kluczowych…",
"components.Discover.CreateSlider.slidernameplaceholder": "Nazwa slidera",
"components.Discover.CreateSlider.starttyping": "Zacznij pisać, aby wyszukać.",
"components.Discover.CreateSlider.validationDatarequired": "Musisz uzyskać wartość danych.",
"components.Discover.CreateSlider.validationTitlerequired": "Musisz podać tytuł.",
"components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} Filmy",
"components.Discover.DiscoverSliderEdit.deletefail": "Nie udało się usunąć slidera.",
"components.Discover.DiscoverSliderEdit.remove": "Usuń",
"components.Discover.DiscoverTvKeyword.keywordSeries": "Serial {keywordTitle}",
"components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# aktywny filtr} other {# aktywnych filtrów}}",
"components.Discover.DiscoverMovies.discovermovies": "Filmy",
"components.Discover.DiscoverMovies.sortPopularityAsc": "Popularność rosnąco",
"components.Discover.DiscoverMovies.sortPopularityDesc": "Popularność malejąco",
"components.Discover.DiscoverMovies.sortReleaseDateAsc": "Data wydania rosnąco",
"components.Discover.DiscoverMovies.sortReleaseDateDesc": "Data wydania malejąco",
"components.Discover.DiscoverMovies.sortTitleAsc": "Tytuł (A-Z) rosnąco",
"components.Discover.DiscoverMovies.sortTitleDesc": "Tytuł (Z-A) malejąco",
"components.Discover.DiscoverMovies.sortTmdbRatingAsc": "Ocena TMDB rosnąco",
"components.Discover.DiscoverMovies.sortTmdbRatingDesc": "Ocena TMDB malejąco",
"components.Discover.DiscoverSliderEdit.deletesuccess": "Pomyślnie usunięto slider.",
"components.Discover.DiscoverSliderEdit.enable": "Przełącz widoczność",
"components.Discover.DiscoverTv.activefilters": "{count, plural, one {# aktywny filtr} other {# aktywnych filtrów}}",
"components.Discover.DiscoverTv.discovertv": "Seriale",
"components.Discover.DiscoverTv.sortFirstAirDateAsc": "Data pierwszej emisji rosnąco",
"components.Discover.DiscoverTv.sortFirstAirDateDesc": "Pierwsza data emisji malejąco",
"components.Discover.DiscoverTv.sortPopularityAsc": "Popularność rosnąco",
"components.Discover.DiscoverTv.sortPopularityDesc": "Popularność malejąco",
"components.Discover.DiscoverTv.sortTitleAsc": "Tytuł (A-Z) rosnąco",
"components.Discover.DiscoverTv.sortTitleDesc": "Tytuł (Z-A) malejąco",
"components.Discover.DiscoverTv.sortTmdbRatingAsc": "Ocena TMDB rosnąco",
"components.Discover.DiscoverTv.sortTmdbRatingDesc": "Ocena TMDB malejąco",
"components.Discover.CreateSlider.searchStudios": "Szukaj studiów…",
"components.Settings.SettingsMain.general": "Ogólne",
"components.Settings.SettingsMain.generalsettings": "Ustawienia ogólne",
"components.Settings.SettingsMain.originallanguageTip": "Filtruj zawartość według języka oryginału",
"components.Discover.PlexWatchlistSlider.emptywatchlist": "W tym miejscu pojawią się multimedia dodane do <PlexWatchlistSupportLink>listy obserwowanych Plex</PlexWatchlistSupportLink>.",
"components.Discover.networks": "Platformy",
"components.Discover.moviegenres": "Gatunki filmowe",
"components.Discover.tmdbnetwork": "Platforma TMDB",
"components.Discover.tmdbstudio": "Studio TMDB",
"components.Discover.PlexWatchlistSlider.plexwatchlist": "Twoja lista obserwowanych Plex",
"components.Discover.createnewslider": "Utwórz nowy suwak",
"components.Discover.customizediscover": "Dostosowywanie funkcji Odkryj",
"components.Discover.resetfailed": "Wystąpił problem podczas resetowania ustawień odnajdywania.",
"components.Discover.resetsuccess": "Pomyślnie zresetowano ustawienia odnajdywania.",
"components.Discover.resetwarning": "Przywróć wszystkie Suwaki do ustawień domyślnych. Spowoduje to również usunięcie wszystkich niestandardowych Suwaków!",
"components.Discover.resettodefault": "Przywróć ustawienia domyślne",
"components.Discover.studios": "Studia",
"components.Discover.tmdbmoviegenre": "Gatunek filmu TMDB",
"components.Discover.tvgenres": "Gatunki serialu",
"components.Settings.SettingsMain.csrfProtectionTip": "Ustaw zewnętrzny dostęp api na tylko do odczytu (wymaga HTTPS)",
"components.Settings.SettingsMain.csrfProtectionHoverTip": "NIE włączaj tego ustawienia, chyba że rozumiesz, co robisz!",
"components.Settings.SettingsMain.generalsettingsDescription": "Skonfiguruj globalne i domyślne ustawienia dla Overseerr.",
"components.Settings.SettingsMain.hideAvailable": "Ukryj dostępne multimedia",
"components.Settings.SettingsMain.trustProxyTip": "Pozwól Overseerr poprawnie rejestrować adresy IP klientów za serwerem proxy",
"components.Settings.SettingsMain.toastSettingsSuccess": "Ustawienia zostały zapisane pomyślnie!",
"components.Settings.SettingsMain.trustProxy": "Włącz obsługę proxy",
"components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "Adres URL nie może kończyć się ukośnikiem",
"components.Discover.FilterSlideover.activefilters": "{count, plural, one {# aktywny filtr} other {# aktywnych filtrów}}",
"components.Discover.FilterSlideover.clearfilters": "Wyczyść aktywne filtry",
"components.Discover.FilterSlideover.filters": "Filtry",
"components.Discover.FilterSlideover.firstAirDate": "Pierwsza data emisji",
"components.Discover.FilterSlideover.from": "Od",
"components.Discover.FilterSlideover.genres": "Gatunki",
"components.Discover.FilterSlideover.keywords": "Słowa kluczowe",
"components.Discover.FilterSlideover.originalLanguage": "Język oryginalny",
"components.Discover.FilterSlideover.ratingText": "Oceny pomiędzy {minValue} a {maxValue}",
"components.Discover.FilterSlideover.releaseDate": "Data wydania",
"components.Discover.FilterSlideover.runtimeText": "{minValue}-{maxValue} czas odtwarzania w minutach",
"components.Discover.FilterSlideover.runtime": "Czas odtwarzania",
"components.Discover.FilterSlideover.tmdbuserscore": "Ocena użytkowników TMDB",
"components.Discover.FilterSlideover.to": "Do",
"components.Discover.RecentlyAddedSlider.recentlyAdded": "Ostatnio dodane",
"components.Discover.stopediting": "Zatrzymaj edycję",
"components.Discover.tmdbmoviekeyword": "Słowo kluczowe filmu TMDB",
"components.Discover.tmdbsearch": "Wyszukiwanie TMDB",
"components.Layout.Sidebar.browsemovies": "Filmy",
"components.Layout.Sidebar.browsetv": "Seriale",
"components.Selector.searchGenres": "Wybierz gatunki…",
"components.Selector.searchKeywords": "Szukaj słów kluczowych…",
"components.Selector.showmore": "Pokaż więcej",
"components.Selector.starttyping": "Zacznij pisać, aby wyszukać.",
"components.Settings.SettingsMain.applicationTitle": "Tytuł aplikacji",
"components.Settings.SettingsMain.applicationurl": "Adres URL aplikacji",
"components.Settings.SettingsMain.cacheImages": "Włącz buforowanie obrazów",
"components.Settings.SettingsMain.csrfProtection": "Włącz ochronę CSRF",
"components.Settings.SettingsMain.locale": "Język wyświetlania",
"components.Settings.SettingsMain.originallanguage": "Odkryj język",
"components.Settings.SettingsMain.partialRequestsEnabled": "Zezwalaj na prośby o część serialu",
"components.Settings.SettingsMain.region": "Odkryj region",
"components.Settings.SettingsMain.regionTip": "Filtruj zawartość według dostępności regionalnej",
"components.Settings.SettingsMain.toastApiKeyFailure": "Coś poszło nie tak podczas generowania nowego klucza API.",
"components.Settings.SettingsMain.toastApiKeySuccess": "Nowy klucz API został pomyślnie wygenerowany!",
"components.Settings.SettingsMain.validationApplicationTitle": "Należy podać tytuł aplikacji",
"components.Settings.SettingsMain.validationApplicationUrl": "Musisz podać prawidłowy adres URL",
"components.Discover.FilterSlideover.streamingservices": "Usługi streamingowe",
"components.Discover.FilterSlideover.studio": "Studia",
"components.Discover.tmdbtvgenre": "Gatunek serialu TMDB",
"components.Discover.tmdbtvkeyword": "Słowo kluczowe serialu TMDB",
"components.Discover.updatesuccess": "Zaktualizowano ustawienia odnajdywania.",
"components.Selector.nooptions": "Brak wyników.",
"components.Selector.searchStudios": "Szukaj studiów…",
"components.Selector.showless": "Pokaż mniej",
"components.Discover.updatefailed": "Wystąpił problem podczas aktualizowania ustawień odnajdywania.",
"components.Settings.SettingsMain.apikey": "Klucz API",
"components.Settings.SettingsMain.cacheImagesTip": "Pamięć podręczna dla obrazów pochodzących z zewnętrznych źródeł (wymaga znacznej ilości miejsca na dysku)",
"components.Settings.SettingsMain.toastSettingsFailure": "Coś poszło nie tak podczas zapisywania ustawień.",
"i18n.collection": "Kolekcja",
"components.MovieDetails.imdbuserscore": "Ocena użytkowników IMDB",
"components.Settings.SonarrModal.seriesType": "Typ serialu"
}

View File

@@ -1256,5 +1256,10 @@
"components.Discover.tmdbmoviestreamingservices": "Serviços de Streaming de Filmes do TMDB",
"components.Discover.FilterSlideover.tmdbuservotecount": "Qtd de Votos de Usuários TMDB",
"components.Discover.FilterSlideover.voteCount": "Qtd the votos entre {minValue} e {maxValue}",
"components.Settings.RadarrModal.tagRequestsInfo": "Adicione automaticamente uma tag extra com o ID de usuário e o nome de exibição do solicitante"
"components.Settings.RadarrModal.tagRequestsInfo": "Adicione automaticamente uma tag extra com o ID de usuário e o nome de exibição do solicitante",
"i18n.collection": "Coleção",
"components.MovieDetails.imdbuserscore": "Pontuação de usuário IMDB",
"components.Settings.SonarrModal.tagRequestsInfo": "Adiciona automaticamente uma tag adicional com o ID de usuário e nome de exibição de quem pediu",
"components.Settings.SonarrModal.tagRequests": "Marcar Pedidos",
"components.Settings.RadarrModal.tagRequests": "Marcar Pedidos"
}

View File

@@ -370,7 +370,7 @@
"components.PlexLoginButton.signinwithplex": "Conectat",
"components.QuotaSelector.movieRequests": "{quotaLimit} <quotaUnits>{movies} per {quotaDays} {days}</quotaUnits>",
"components.PersonDetails.lifespan": "{birthdate} {deathdate}",
"components.RequestBlock.seasons": "{seasonCount, plural, un {Season} alte {Seasons}}",
"components.RequestBlock.seasons": "{seasonCount, plural, one {Sezon} other {Sezoane}}",
"components.PermissionEdit.requestMoviesDescription": "Acordați permisiunea de a trimite solicitări pentru filme non-4K.",
"components.PermissionEdit.viewissuesDescription": "Acordați permisiunea de a vizualiza problemele media raportate de alți utilizatori.",
"components.PermissionEdit.viewwatchlistsDescription": "Acordați permisiunea de a vizualiza listele de urmărire Plex ale altor utilizatori.",
@@ -403,8 +403,62 @@
"components.RequestBlock.approve": "Aprobă Solicitarea",
"components.RequestBlock.decline": "Respinge Solicitarea",
"components.RequestBlock.requestedby": "Solicitat de",
"components.RequestButton.approve4krequests": "Aprobă {requestCount, plural, o {4K Request} alte {{requestCount} 4K Requests}}",
"components.RequestButton.approve4krequests": "Aprobă {requestCount, plural, one {Cerere 4K} other {{requestCount} Cereri 4K}}",
"components.RequestBlock.lastmodifiedby": "Ultima Dată Modificat de",
"components.RequestBlock.profilechanged": "Profil Calitate",
"components.RequestBlock.requestdate": "Dată Solicitare"
"components.RequestBlock.requestdate": "Dată Solicitare",
"components.RequestButton.approverequests": "Aprobă {requestCount, plural, one {Cerere} other {{requestCount} Cereri}}",
"components.RequestButton.requestmore4k": "Cere mai mult în 4K",
"components.RequestButton.approverequest4k": "Aproba Cereri 4K",
"components.RequestCard.tmdbid": "ID TMDB",
"components.RequestCard.failedretry": "A apărut o eroare la reîncercarea solicitării.",
"components.RequestButton.declinerequest": "Respinge Cerere",
"components.RequestCard.seasons": "{seasonCount, plural, one {Sezon} other {Sezoane}}",
"components.RequestCard.declinerequest": "Respinge Cererea",
"components.RequestButton.viewrequest": "Vezi Cerere",
"components.RequestButton.declinerequests": "Respinge {requestCount, plural, one {Cererea} other {{requestCount} Cererile}}",
"components.RequestCard.mediaerror": "{mediaType} Nu a fost găsit",
"components.RequestCard.editrequest": "Editează Cererea",
"components.RequestButton.viewrequest4k": "Vezi Cerere 4K",
"components.RequestButton.decline4krequests": "Respinge {requestCount, plural, one {Cererea 4K} other {{requestCount} Cererile 4K}}",
"components.RequestButton.declinerequest4k": "Respinge Cerere 4K",
"components.RequestCard.approverequest": "Aprobă Cererea",
"components.RequestButton.approverequest": "Cereri Aprobate",
"components.RequestCard.deleterequest": "Șterge Cererea",
"components.RequestCard.unknowntitle": "Titlu necunoscut",
"components.RequestList.RequestItem.cancelRequest": "Anulează Cerere",
"components.RequestCard.tvdbid": "ID TheTVDB",
"components.RequestButton.requestmore": "Cere mai mult",
"components.RequestList.RequestItem.deleterequest": "Șterge Cerere",
"components.RequestCard.cancelrequest": "Anulează Cererea",
"components.RequestModal.AdvancedRequester.folder": "{path} ({space})",
"components.RequestList.RequestItem.modified": "Modificat",
"components.RequestList.RequestItem.editrequest": "Editează Cererea",
"components.RequestModal.AdvancedRequester.qualityprofile": "Profil de Calitate",
"components.RequestList.requests": "Cereri",
"components.RequestModal.AdvancedRequester.advancedoptions": "Avansat",
"components.RequestModal.AdvancedRequester.notagoptions": "Fără etichete.",
"components.RequestList.RequestItem.modifieduserdate": "{date} de {user}",
"components.RequestModal.AdvancedRequester.requestas": "Cere ca",
"components.RequestList.showallrequests": "Afișează toate cererile",
"components.RequestList.RequestItem.tmdbid": "ID-ul TMDB",
"components.RequestList.RequestItem.requesteddate": "Solicitat",
"components.RequestModal.QuotaDisplay.movie": "film",
"components.RequestList.RequestItem.failedretry": "Ceva a mers greșit în timpul reîncercării cererii.",
"components.RequestList.RequestItem.unknowntitle": "Titlu Necunoscut",
"components.RequestModal.AdvancedRequester.destinationserver": "Server Destinație",
"components.RequestModal.AdvancedRequester.rootfolder": "Folder Rădăcină",
"components.RequestList.sortAdded": "Cele Mai Recente",
"components.RequestModal.AdvancedRequester.tags": "Etichete",
"components.RequestList.RequestItem.mediaerror": "{mediaType} nu a fost găsit",
"components.RequestList.sortModified": "Ultima Modificată",
"components.RequestList.RequestItem.tvdbid": "ID-ul TheTVDB",
"components.RequestModal.AdvancedRequester.selecttags": "Selectați Etichetele",
"components.RequestList.RequestItem.requested": "Solicitat",
"components.RequestModal.QuotaDisplay.notenoughseasonrequests": "Nu sunt suficiente cereri de sezon rămase",
"components.RequestModal.AdvancedRequester.default": "{name} (Implicit)",
"components.RequestModal.AdvancedRequester.languageprofile": "Profil de Limbă",
"components.RequestModal.QuotaDisplay.allowedRequestsUser": "Acest utilizator are voie sa ceara <strong>{limit}</strong> {type} la fiecare <strong>{days}</strong> zile.",
"components.RequestList.RequestItem.seasons": "",
"components.RequestModal.QuotaDisplay.allowedRequests": "Aveți voie să cereți <strong>{limit}</strong> de {type} la fiecare <strong>{days}</strong> zile."
}

View File

@@ -170,7 +170,7 @@
"pages.oops": "Упс",
"pages.returnHome": "Вернуться домой",
"components.CollectionDetails.overview": "Обзор",
"components.CollectionDetails.numberofmovies": "{count} фильмов",
"components.CollectionDetails.numberofmovies": "{count} {count, plural, one {фильм} few {фильма} other {фильмов}}",
"components.CollectionDetails.requestcollection": "Запросить Коллекцию",
"components.Login.email": "Адрес электронной почты",
"components.UserList.users": "Пользователи",
@@ -1018,7 +1018,7 @@
"components.Discover.CreateSlider.addcustomslider": "Создать слайдер",
"components.Discover.CreateSlider.nooptions": "Нет результатов.",
"components.Discover.CreateSlider.providetmdbgenreid": "Введите TMDB ID жанра",
"components.Discover.CreateSlider.needresults": "Должен быть хотя-бы 1 результат.",
"components.Discover.CreateSlider.needresults": "Должен быть хотя бы 1 результат.",
"components.Discover.CreateSlider.providetmdbkeywordid": "Введите TMDB ID ключевого слова",
"components.Discover.CreateSlider.providetmdbnetwork": "Введите TMDB ID сети",
"components.Discover.CreateSlider.providetmdbsearch": "Введите поисковой запрос",
@@ -1027,7 +1027,7 @@
"components.Discover.CreateSlider.searchKeywords": "Поиск ключевых слов…",
"components.Discover.CreateSlider.searchStudios": "Поиск студий…",
"components.Discover.CreateSlider.slidernameplaceholder": "Название слайдера",
"components.Discover.CreateSlider.starttyping": "Начините писать для поиска.",
"components.Discover.CreateSlider.starttyping": "Начните писать для поиска.",
"components.Discover.CreateSlider.validationDatarequired": "Вы должны ввести дату.",
"components.Discover.CreateSlider.validationTitlerequired": "Вы должны ввести заголовок.",
"components.Discover.DiscoverMovieKeyword.keywordMovies": "Фильмы по ключевому слову \"{keywordTitle}\"",
@@ -1059,7 +1059,7 @@
"components.Discover.DiscoverSliderEdit.deletesuccess": "Слайдер успешно удален.",
"components.Discover.DiscoverSliderEdit.enable": "Изменить видимость",
"components.Discover.DiscoverSliderEdit.remove": "Удалить",
"components.Discover.DiscoverTv.activefilters": "{count, plural, one {# Активный фильтр} other {# Активные фильтры}}",
"components.Discover.DiscoverTv.activefilters": "{count, plural, one {# Активен фильтр} other {# Активные фильтры }}",
"components.Discover.DiscoverTv.discovertv": "Сериалы",
"components.Discover.DiscoverTv.sortPopularityAsc": "Популярность по возрастанию",
"components.Discover.DiscoverTv.sortPopularityDesc": "Популярность по убыванию",
@@ -1256,5 +1256,22 @@
"components.Selector.showless": "Показать меньше",
"components.Selector.showmore": "Показать больше",
"components.Settings.SettingsJobsCache.imagecachesize": "Размер кэша",
"components.Settings.validationUrlBaseLeadingSlash": "Базовый URL должен начинаться с косой черты"
"components.Settings.validationUrlBaseLeadingSlash": "Базовый URL должен начинаться с косой черты",
"components.Discover.FilterSlideover.tmdbuservotecount": "Количество голосов пользователей TMDB",
"components.Discover.FilterSlideover.voteCount": "Количество голосов между {minValue} и {maxValue}",
"components.Discover.tmdbmoviestreamingservices": "Стриминговые сервисы фильмов TMDB",
"components.Discover.tmdbtvstreamingservices": "Стриминговые сервисы сериалов TMDB",
"components.Settings.RadarrModal.tagRequestsInfo": "Автоматически добавлять дополнительный тег с ID и именем запросившего пользователя",
"components.Settings.RadarrModal.tagRequests": "Теги запросов",
"components.MovieDetails.imdbuserscore": "Оценка пользователей IMDB",
"components.Settings.SettingsJobsCache.availability-sync": "Синхронизация доступности медиа",
"components.Settings.SonarrModal.tagRequests": "Теги запросов",
"components.Settings.SonarrModal.tagRequestsInfo": "Автоматически добавлять тег с ID и именем запросившего пользователя",
"i18n.collection": "Коллекция",
"components.Settings.Notifications.NotificationsPushover.sound": "Звук уведомления",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Устройство по умолчанию",
"components.Settings.SonarrModal.animeSeriesType": "Тип аниме-сериала",
"components.Settings.SonarrModal.seriesType": "Тип сериала",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "Звук уведомления",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "Устройство по умолчанию"
}

View File

@@ -452,7 +452,7 @@
"components.Settings.Notifications.NotificationsWebhook.webhookUrl": "Webhook adresa",
"components.ManageSlideOver.manageModalAdvanced": "Napredno",
"components.ManageSlideOver.manageModalMedia": "Mediji",
"components.Settings.Notifications.NotificationsWebhook.customJson": "JSON Payload",
"components.Settings.Notifications.NotificationsWebhook.customJson": "JSON zadržaj",
"components.Settings.SettingsLogs.label": "Labela",
"components.Settings.SettingsLogs.level": "Ozbiljnost",
"components.Settings.SettingsAbout.Releases.versionChangelog": "{version} Dnevnik promena",

File diff suppressed because it is too large Load Diff

View File

@@ -1139,5 +1139,145 @@
"pages.somethingwentwrong": "Щось пішло не так",
"Components.PermissionEdit.requestMovies": "Запити фільмів",
"Components.PermissionEdit.autoapprove4kMovies": "Автоматичне схвалення 4К фільмів",
"Components.PermissionEdit.autoapproveMovies": "Автоматичне схвалення фільмів"
"Components.PermissionEdit.autoapproveMovies": "Автоматичне схвалення фільмів",
"components.Discover.FilterSlideover.studio": "Студія",
"components.Discover.RecentlyAddedSlider.recentlyAdded": "Нещодавно додані",
"components.Discover.FilterSlideover.keywords": "Ключові слова",
"components.Discover.FilterSlideover.ratingText": "Оцінки від {minValue} до {maxValue}",
"components.Discover.FilterSlideover.tmdbuserscore": "Оцінка користувачів TMDB",
"components.Discover.DiscoverTv.discovertv": "Серіали",
"components.Discover.FilterSlideover.runtime": "Тривалість",
"components.Discover.FilterSlideover.from": "Від",
"components.Discover.studios": "Студії",
"components.Discover.FilterSlideover.to": "До",
"components.Discover.FilterSlideover.filters": "Фільтри",
"components.Discover.CreateSlider.providetmdbsearch": "Введіть пошуковий запит",
"components.Discover.DiscoverMovieKeyword.keywordMovies": "Фільми {keywordTitle}",
"components.Discover.PlexWatchlistSlider.plexwatchlist": "Ваш список перегляду Plex",
"components.Discover.FilterSlideover.genres": "Жанри",
"components.Discover.FilterSlideover.originalLanguage": "Мова оригіналу",
"components.Discover.CreateSlider.nooptions": "Немає результатів.",
"components.Discover.FilterSlideover.tmdbuservotecount": "Кількість голосів користувачів TMDB",
"components.Discover.DiscoverMovies.discovermovies": "Фільми",
"components.Discover.FilterSlideover.clearfilters": "Очистити активні фільтри",
"components.Discover.CreateSlider.searchStudios": "Пошук студій…",
"components.Discover.FilterSlideover.releaseDate": "Дата релізу",
"components.Discover.CreateSlider.searchGenres": "Пошук жанрів…",
"components.Discover.FilterSlideover.runtimeText": "тривалість {minValue}-{maxValue} хвилин",
"components.Discover.FilterSlideover.voteCount": "Кількість голосів від {minValue} до {maxValue}",
"components.Discover.DiscoverSliderEdit.remove": "Видалити",
"components.Layout.Sidebar.browsemovies": "Фільми",
"components.MovieDetails.imdbuserscore": "Оцінка користувачів IMDB",
"components.Layout.Sidebar.browsetv": "Серіали",
"components.Discover.DiscoverTv.sortPopularityDesc": "Популярність за спаданням",
"components.Discover.moviegenres": "Жанри фільмів",
"components.Discover.resetwarning": "Скинути всі повзунки до стандартних. Це також видалить будь-які спеціальні повзунки!",
"components.Discover.stopediting": "Зупинити редагування",
"components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# Активний фільтр} other {# Активні фільтри}}",
"components.Discover.DiscoverTv.activefilters": "{count, plural, one {# Активний фільтр} other {# Активні фільтри}}",
"components.Discover.FilterSlideover.streamingservices": "Сервіси потокового передавання",
"components.Discover.FilterSlideover.activefilters": "{count, plural, one {# Активний фільтр} other {# Активні фільтри}}",
"components.Discover.CreateSlider.addSlider": "Додати повзунок",
"components.Discover.tvgenres": "Жанри серіалів",
"components.Discover.tmdbmoviekeyword": "Ключове слово фільму TMDB",
"components.Discover.tmdbtvkeyword": "Ключове слово серіала TMDB",
"components.Discover.tmdbnetwork": "Телеканал TMDB",
"components.Discover.networks": "Телеканали",
"components.Discover.tmdbtvgenre": "Жанр серіала TMDB",
"components.Discover.tmdbstudio": "Студія TMDB",
"components.Discover.tmdbtvstreamingservices": "Сервіси потокового передавання серіалів TMDB",
"components.Discover.tmdbmoviestreamingservices": "Сервіси потокової передачі фільмів TMDB",
"components.Discover.resetfailed": "Щось пішло не так під час скидання налаштувань Discover.",
"components.Discover.tmdbsearch": "Пошук TMDB",
"components.Discover.CreateSlider.searchKeywords": "Ключові слова пошуку…",
"components.Discover.tmdbmoviegenre": "Жанр фільму TMDB",
"components.Discover.updatesuccess": "Оновлено параметри налаштування Discover.",
"components.Discover.resetsuccess": "Успішно скинуто параметри налаштування.",
"components.Discover.updatefailed": "Під час оновлення налаштувань Discover сталася помилка.",
"components.Selector.showmore": "Показати більше",
"components.Selector.searchGenres": "Виберіть жанри…",
"components.Selector.searchStudios": "Пошук студій…",
"components.Discover.CreateSlider.addcustomslider": "Створити власний повзунок",
"components.Selector.showless": "Показати менше",
"components.Selector.starttyping": "Початок введення для пошуку.",
"components.Selector.searchKeywords": "Пошук за ключовими словами…",
"components.Selector.nooptions": "Немає результатів.",
"components.Discover.resettodefault": "Скинути за замовчуванням",
"components.Settings.SettingsJobsCache.availability-sync": "Синхронізація доступності медіа",
"components.Settings.RadarrModal.tagRequestsInfo": "Автоматично додавати додатковий тег з ID та іменем користувача, який запитує",
"components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Кожну {jobScheduleSeconds, plural, one {секунду} other {{jobScheduleSeconds} секунд}}",
"components.Settings.RadarrModal.tagRequests": "Теги запитів",
"components.Settings.SettingsMain.hideAvailable": "Приховати доступні медіа",
"components.Settings.SettingsMain.regionTip": "Фільтрувати вміст за регіональною доступністю",
"components.Settings.SettingsMain.region": "Регіон для пошуку фільмів та серіалів",
"components.Settings.SettingsMain.trustProxy": "Увімкнути підтримку проксі",
"components.Settings.SettingsMain.toastSettingsSuccess": "Налаштування успішно збережено!",
"components.Settings.SettingsMain.locale": "Мова інтерфейсу",
"components.Settings.SettingsMain.applicationTitle": "Назва програми",
"components.Settings.SettingsMain.originallanguage": "Мови для пошуку фільмів та серіалів",
"components.Settings.SettingsMain.csrfProtection": "Увімкнути захист CSRF",
"components.Settings.SettingsMain.toastApiKeyFailure": "Під час створення нового ключа API сталася помилка.",
"components.Settings.SettingsMain.originallanguageTip": "Фільтрувати вміст за мовою оригіналу",
"components.Settings.SettingsMain.cacheImagesTip": "Кешувати зображення із зовнішніх джерел (потрібний значний об'єм дискового простору)",
"components.Settings.SettingsMain.trustProxyTip": "Дозволити Overseerr правильно реєструвати IP-адреси клієнтів за проксі-сервером",
"components.Settings.SettingsMain.generalsettings": "Загальні налаштування",
"components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "URL-адреса не має закінчуватися скісною рискою",
"components.Settings.SettingsMain.apikey": "Ключ API",
"components.Settings.SettingsMain.generalsettingsDescription": "Налаштуйте глобальні параметри і параметри за замовчуванням для Overseerr.",
"components.Settings.SettingsMain.toastApiKeySuccess": "Новий ключ API успішно згенеровано!",
"components.Settings.SettingsMain.cacheImages": "Увімкнути кешування зображень",
"components.Settings.SettingsMain.applicationurl": "URL програми",
"components.Settings.SettingsMain.general": "Загальні",
"components.Settings.SettingsMain.csrfProtectionHoverTip": "НЕ вмикайте цей параметр, якщо ви не розумієте, що робите!",
"components.Settings.SettingsMain.partialRequestsEnabled": "Дозволити запитувати серіали частково",
"components.Settings.SettingsMain.toastSettingsFailure": "Під час збереження налаштувань сталася помилка.",
"components.Settings.SettingsMain.validationApplicationUrl": "Ви повинні вказати дійсну URL-адресу",
"components.Settings.SettingsMain.validationApplicationTitle": "Ви повинні вказати назву програми",
"components.Settings.SettingsMain.csrfProtectionTip": "Встановіть доступ до зовнішнього API лише для читання (потрібний HTTPS)",
"components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} Серіали",
"components.Discover.CreateSlider.editsuccess": "Відредаговано повзунок і збережено налаштування Discover.",
"components.Discover.CreateSlider.slidernameplaceholder": "Назва повзунка",
"components.Discover.CreateSlider.addfail": "Не вдалося створити новий повзунок.",
"components.Discover.CreateSlider.needresults": "Ви повинні мати принаймні 1 результат.",
"components.Discover.CreateSlider.editSlider": "Редагувати повзунок",
"components.Discover.CreateSlider.editfail": "Не вдалося відредагувати повзунок.",
"components.Discover.CreateSlider.addsuccess": "Створено новий повзунок і збережено параметри налаштування Discover.",
"components.Discover.PlexWatchlistSlider.emptywatchlist": "Медіа додано до вашого <PlexWatchlistSupportLink>списку перегляду Plex</PlexWatchlistSupportLink>.",
"components.Settings.Notifications.NotificationsPushover.sound": "Звук сповіщення",
"components.Discover.customizediscover": "Налаштувати Discover",
"components.Discover.createnewslider": "Створити новий повзунок",
"components.Discover.FilterSlideover.firstAirDate": "Дата виходу в ефір",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "Пристрій за замовчуванням",
"components.Settings.SonarrModal.tagRequests": "Теги запитів",
"components.Settings.SonarrModal.tagRequestsInfo": "Автоматично додавати додатковий тег з ID та іменем користувача, який запитує",
"components.Settings.SonarrModal.animeSeriesType": "Тип аніме-серіалу",
"components.Settings.SonarrModal.seriesType": "Тип серіалу",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Пристрій за замовчуванням",
"i18n.collection": "Колекція",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "Звук сповіщення",
"components.Discover.DiscoverMovies.sortPopularityDesc": "Популярність за спаданням",
"components.Discover.DiscoverMovies.sortTmdbRatingAsc": "Рейтинг TMDB за зростанням",
"components.Discover.DiscoverMovies.sortReleaseDateDesc": "Дата випуску за спаданням",
"components.Discover.CreateSlider.providetmdbnetwork": "Введіть TMDB ID мережі",
"components.Discover.DiscoverMovies.sortPopularityAsc": "Популярність за зростанням",
"components.Discover.CreateSlider.validationDatarequired": "Ви повинні надати доступний для пошуку вміст.",
"components.Discover.CreateSlider.providetmdbstudio": "Введіть TMDB ID студії",
"components.Discover.DiscoverMovies.sortTitleDesc": "Назва (Я-А) за спаданням",
"components.Discover.CreateSlider.starttyping": "Початок введення для пошуку.",
"components.Discover.CreateSlider.providetmdbkeywordid": "Введіть TMDB ID ключового слова",
"components.Discover.CreateSlider.validationTitlerequired": "Ви повинні вказати назву.",
"components.Discover.DiscoverMovies.sortReleaseDateAsc": "Дата випуску за зростанням",
"components.Discover.DiscoverMovies.sortTmdbRatingDesc": "Рейтинг TMDB за спаданням",
"components.Discover.DiscoverMovies.sortTitleAsc": "Назва (А-Я) за зростанням",
"components.Discover.CreateSlider.providetmdbgenreid": "Введіть TMDB ID жанру",
"components.Discover.DiscoverTv.sortTmdbRatingAsc": "Рейтинг TMDB за зростанням",
"components.Discover.DiscoverTv.sortFirstAirDateAsc": "Дата виходу в ефір за зростанням",
"components.Discover.DiscoverTv.sortTmdbRatingDesc": "Рейтинг TMDB за спаданням",
"components.Discover.DiscoverTv.sortPopularityAsc": "Популярність за зростанням",
"components.Discover.DiscoverTv.sortTitleAsc": "Назва (А-Я) за зростанням",
"components.Discover.DiscoverTv.sortFirstAirDateDesc": "Дата виходу в ефір за спаданням",
"components.Discover.DiscoverSliderEdit.deletefail": "Не вдалося видалити повзунок.",
"components.Discover.DiscoverSliderEdit.enable": "Перемкнути видимість",
"components.Discover.DiscoverSliderEdit.deletesuccess": "Повзунок успішно видалено.",
"components.Discover.DiscoverTv.sortTitleDesc": "Назва (Я-А) за спаданням"
}

View File

@@ -1203,7 +1203,7 @@
"components.Discover.PlexWatchlistSlider.emptywatchlist": "您的 <PlexWatchlistSupportLink>Plex 关注列表</PlexWatchlistSupportLink>中的媒体会显示在这里。",
"components.Selector.starttyping": "开始打字以进行搜索。",
"components.Discover.CreateSlider.starttyping": "开始打字以进行搜索。",
"components.Discover.CreateSlider.needresults": "需要至少有 1 个结果。",
"components.Discover.CreateSlider.needresults": "需要至少有 1 个结果。",
"components.Selector.showless": "显示更少",
"components.Discover.resetfailed": "重置探索媒体设置时出了点问题。",
"components.Settings.SettingsMain.validationApplicationTitle": "你必须提供一个应用程序标题",
@@ -1260,5 +1260,12 @@
"components.Settings.SonarrModal.tagRequests": "标记请求",
"i18n.collection": "合集",
"components.Discover.FilterSlideover.tmdbuservotecount": "TMDB 用户评分数",
"components.Settings.SonarrModal.tagRequestsInfo": "自动添加带有请求者的用户 ID 和显示名称的附加标签"
"components.Settings.SonarrModal.tagRequestsInfo": "自动添加带有请求者的用户 ID 和显示名称的附加标签",
"components.MovieDetails.imdbuserscore": "IMDB 用户评分",
"components.Settings.Notifications.NotificationsPushover.sound": "通知提示音",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "默认设备",
"components.Settings.SonarrModal.animeSeriesType": "动漫剧集类型",
"components.Settings.SonarrModal.seriesType": "剧集类型",
"components.UserProfile.UserSettings.UserNotificationSettings.sound": "通知提示音",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "默认设备"
}

View File

@@ -29,6 +29,8 @@ const loadLocaleData = (locale: AvailableLocale): Promise<any> => {
switch (locale) {
case 'ar':
return import('../i18n/locale/ar.json');
case 'bg':
return import('../i18n/locale/bg.json');
case 'ca':
return import('../i18n/locale/ca.json');
case 'cs':
@@ -43,8 +45,14 @@ const loadLocaleData = (locale: AvailableLocale): Promise<any> => {
return import('../i18n/locale/es.json');
case 'es-MX':
return import('../i18n/locale/es_MX.json');
case 'fi':
return import('../i18n/locale/fi.json');
case 'fr':
return import('../i18n/locale/fr.json');
case 'he':
return import('../i18n/locale/he.json');
case 'hi':
return import('../i18n/locale/hi.json');
case 'hr':
return import('../i18n/locale/hr.json');
case 'hu':
@@ -67,6 +75,8 @@ const loadLocaleData = (locale: AvailableLocale): Promise<any> => {
return import('../i18n/locale/pt_BR.json');
case 'pt-PT':
return import('../i18n/locale/pt_PT.json');
case 'ro':
return import('../i18n/locale/ro.json');
case 'ru':
return import('../i18n/locale/ru.json');
case 'sq':