diff --git a/build/debian/DEBIAN/preinst b/build/debian/DEBIAN/preinst index e30bc490c..241a47010 100644 --- a/build/debian/DEBIAN/preinst +++ b/build/debian/DEBIAN/preinst @@ -22,7 +22,7 @@ add_user() { declare -r descr="${4:-No description}" declare -r shell="${5:-/bin/false}" - if ! getent passwd | grep -q "^$user:"; then + if ! getent passwd "$user" 2>&1 >/dev/null; then echo "Creating system user: $user in $group with $descr and shell $shell" useradd $uid_flags --gid $group --no-create-home --system --shell $shell -c "$descr" $user fi @@ -39,7 +39,7 @@ add_group() { declare -r gid_flags="--gid $gid" fi - if ! getent group | grep -q "^$group:" ; then + if ! getent group "$group" 2>&1 >/dev/null; then echo "Creating system group: $group" groupadd $gid_flags --system $group fi diff --git a/client/components/app/Appbar.vue b/client/components/app/Appbar.vue index f74134041..428ae6ebf 100644 --- a/client/components/app/Appbar.vue +++ b/client/components/app/Appbar.vue @@ -196,6 +196,7 @@ export default { requestBatchQuickEmbed() { const payload = { message: this.$strings.MessageConfirmQuickEmbed, + allowHtml: true, callback: (confirmed) => { if (confirmed) { this.$axios diff --git a/client/components/app/BookShelfCategorized.vue b/client/components/app/BookShelfCategorized.vue index 4bf8cfbbf..a8e8ae469 100644 --- a/client/components/app/BookShelfCategorized.vue +++ b/client/components/app/BookShelfCategorized.vue @@ -300,6 +300,8 @@ export default { }) }, userUpdated(user) { + if (user.id !== this.$store.state.user.user.id) return + if (user.seriesHideFromContinueListening && user.seriesHideFromContinueListening.length) { this.removeAllSeriesFromContinueSeries(user.seriesHideFromContinueListening) } diff --git a/client/components/cards/ItemUploadCard.vue b/client/components/cards/ItemUploadCard.vue index 40836b8e4..adbf3adbe 100644 --- a/client/components/cards/ItemUploadCard.vue +++ b/client/components/cards/ItemUploadCard.vue @@ -62,7 +62,24 @@
{{ displayTitle }}
{{ displaySubtitle }}
{{ displayLineTwo || ' ' }}
diff --git a/client/components/controls/LibraryFilterSelect.vue b/client/components/controls/LibraryFilterSelect.vue index 62a9b8037..4834a1a25 100644 --- a/client/components/controls/LibraryFilterSelect.vue +++ b/client/components/controls/LibraryFilterSelect.vue @@ -338,6 +338,18 @@ export default { const series = this.series.find((se) => se.id == decoded) if (series) filterValue = series.name } + } else if (parts[0] === 'progress') { + const item = this.progress.find((p) => p.id == decoded) + if (item) filterValue = item.name + } else if (parts[0] === 'tracks') { + const item = this.tracks.find((t) => t.id == decoded) + if (item) filterValue = item.name + } else if (parts[0] === 'ebooks') { + const item = this.ebooks.find((e) => e.id == decoded) + if (item) filterValue = item.name + } else if (parts[0] === 'missing') { + const item = this.missing.find((m) => m.id == decoded) + if (item) filterValue = item.name } else { filterValue = decoded } diff --git a/client/components/modals/collections/AddCreateModal.vue b/client/components/modals/collections/AddCreateModal.vue index 24e8695d6..f0d43c14f 100644 --- a/client/components/modals/collections/AddCreateModal.vue +++ b/client/components/modals/collections/AddCreateModal.vue @@ -227,7 +227,7 @@ export default { .catch((error) => { console.error('Failed to create collection', error) var errMsg = error.response ? error.response.data || '' : '' - this.$toast.error(this.$strings.ToastCollectionCreateFailed + ': ' + errMsg) + this.$toast.error(errMsg) this.processing = false }) } diff --git a/client/components/modals/item/tabs/Match.vue b/client/components/modals/item/tabs/Match.vue index 27300ebdc..0205c5dc3 100644 --- a/client/components/modals/item/tabs/Match.vue +++ b/client/components/modals/item/tabs/Match.vue @@ -78,7 +78,7 @@- {{ $strings.LabelCurrently }} {{ isPodcast ? mediaMetadata.author : mediaMetadata.authorName }} + {{ $strings.LabelCurrently }} {{ isPodcast ? mediaMetadata.author : mediaMetadata.authorName }}
- {{ $strings.LabelCurrently }} {{ mediaMetadata.narratorName }} + {{ $strings.LabelCurrently }} {{ mediaMetadata.narratorName }}
- {{ $strings.LabelCurrently }} {{ mediaMetadata.descriptionPlain.substr(0, 100) + (mediaMetadata.descriptionPlain.length > 100 ? '...' : '') }} + {{ $strings.LabelCurrently }} {{ mediaMetadata.descriptionPlain.substr(0, 100) + (mediaMetadata.descriptionPlain.length > 100 ? '...' : '') }}
- {{ $strings.LabelCurrently }} {{ mediaMetadata.publisher }} + {{ $strings.LabelCurrently }} {{ mediaMetadata.publisher }}
- {{ $strings.LabelCurrently }} {{ mediaMetadata.publishedYear }} + {{ $strings.LabelCurrently }} {{ mediaMetadata.publishedYear }}
{{ message }}
')
+ .replace(/<\/code>/gi, '')
+ },
callback() {
return this.confirmPromptOptions.callback
},
@@ -103,6 +115,14 @@ export default {
if (this.callback) this.callback(true, this.checkboxValue)
this.show = false
},
+ escapeHtml(value) {
+ return String(value)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+ },
setShow() {
this.checkboxValue = this.checkboxDefaultValue
this.$eventBus.$emit('showing-prompt', true)
diff --git a/client/components/stats/DailyListeningChart.vue b/client/components/stats/DailyListeningChart.vue
index c0f3acb32..55752fc5d 100644
--- a/client/components/stats/DailyListeningChart.vue
+++ b/client/components/stats/DailyListeningChart.vue
@@ -14,7 +14,7 @@
/metadata/cache/items.{{ getPlayMethodName(session.playMethod) }}
+
+
{{ line }}
+
+
{{ $elapsedPrettyLocalized(session.timeListening) }}
@@ -130,7 +134,11 @@{{ getPlayMethodName(session.playMethod) }}
+
+
{{ line }}
+
+
{{ $elapsedPretty(session.timeListening) }}
@@ -172,7 +180,11 @@{{ getPlayMethodName(session.playMethod) }}
+
+
{{ line }}
+
+
{{ $secondsToTimestamp(session.currentTime) }}
@@ -433,16 +445,16 @@ export default { this.selectedSession = session this.showSessionModal = true }, - getDeviceInfoString(deviceInfo) { - if (!deviceInfo) return '' - var lines = [] + getDeviceInfoLines(deviceInfo) { + if (!deviceInfo) return [] + const lines = [] if (deviceInfo.clientName) lines.push(`${deviceInfo.clientName} ${deviceInfo.clientVersion || ''}`) if (deviceInfo.osName) lines.push(`${deviceInfo.osName} ${deviceInfo.osVersion}`) if (deviceInfo.browserName) lines.push(deviceInfo.browserName) if (deviceInfo.manufacturer && deviceInfo.model) lines.push(`${deviceInfo.manufacturer} ${deviceInfo.model}`) if (deviceInfo.sdkVersion) lines.push(`SDK Version: ${deviceInfo.sdkVersion}`) - return lines.join('{{ getPlayMethodName(session.playMethod) }}
+
+
{{ line }}
+
+
{{ $elapsedPrettyLocalized(session.timeListening) }}
/login?autoLaunch=0)",
+ "LabelAutoLaunchDescription": "Аўтаматычна перанакіроўваць да пастаўшчыка аўтэнтыфікацыі пры пераходзе на старонку ўваходу (ручное пераключэнне праз шлях /login?autoLaunch=0)",
"LabelAutoRegister": "Аўтарэгістрацыя",
"LabelAutoRegisterDescription": "Аўтаматычна ствараць новых карыстальнікаў пасля ўваходу ў сістэму",
"LabelBackToUser": "Вярнуцца да карыстальніка",
- "LabelBackupAudioFiles": "Рэзервовае капіраванне аўдыёфайлаў",
- "LabelBackupLocation": "Месцазнаходжанне рэзервовых копій",
+ "LabelBackupAudioFiles": "Рэзервовае капіраванне аўдыяфайлаў",
+ "LabelBackupLocation": "Размяшчэнне рэзервовых копій",
"LabelBackupsEnableAutomaticBackups": "Аўтаматычнае рэзервовае капіраванне",
"LabelBackupsEnableAutomaticBackupsHelp": "Рэзервовыя копіі захаваныя ў /metadata/backups",
"LabelBackupsMaxBackupSize": "Максімальны памер рэзервовай копіі (у ГБ) (0 — неабмежавана)",
@@ -273,11 +275,11 @@
"LabelBonus": "Бонус",
"LabelBooks": "Кнігі",
"LabelButtonText": "Тэкст кнопкі",
- "LabelByAuthor": "ад {0}",
+ "LabelByAuthor": "– {0}",
"LabelChangePassword": "Змяніць пароль",
"LabelChannels": "Каналы",
"LabelChapterCount": "{0} раздзелаў",
- "LabelChapterTitle": "Назва раздзела",
+ "LabelChapterTitle": "Загаловак раздзела",
"LabelChapters": "Раздзелы",
"LabelChaptersFound": "раздзелаў знойдзена",
"LabelClickForMoreInfo": "Націсніце для больш падрабязнай інфармацыі",
@@ -288,14 +290,15 @@
"LabelCollapseSubSeries": "Згарнуць падсерыі",
"LabelCollection": "Калекцыя",
"LabelCollections": "Калекцыі",
- "LabelComplete": "Завершана",
+ "LabelComplete": "Завяршыць",
"LabelConfirmPassword": "Пацвердзіце пароль",
- "LabelContinueListening": "Працягваць слухаць",
+ "LabelContinueListening": "Працягнуць праслухоўванне",
"LabelContinueReading": "Працягнуць чытанне",
"LabelContinueSeries": "Працягнуць серыі",
+ "LabelCorsAllowed": "Дазволеныя крыніцы CORS",
"LabelCover": "Вокладка",
- "LabelCoverImageURL": "URL выявы вокладкі",
- "LabelCoverProvider": "Крыніца вокладак",
+ "LabelCoverImageURL": "URL-адрас відарыса вокладкі",
+ "LabelCoverProvider": "Пастаўшчык вокладак",
"LabelCreatedAt": "Дата стварэння",
"LabelCronExpression": "Запіс Cron",
"LabelCurrent": "Бягучы",
@@ -306,15 +309,16 @@
"LabelDeleteFromFileSystemCheckbox": "Выдаліць з файлавай сістэмы (зніміце галачку, каб выдаліць толькі з базы даных)",
"LabelDescription": "Апісанне",
"LabelDeselectAll": "Скасаваць выбар усяго",
+ "LabelDetectedPattern": "Выяўлены ўзор:",
"LabelDevice": "Прылада",
"LabelDeviceInfo": "Інфармацыя пра прыладу",
"LabelDeviceIsAvailableTo": "Прылада даступная для...",
"LabelDirectory": "Каталог",
- "LabelDiscFromFilename": "Дыск з імя файла",
- "LabelDiscFromMetadata": "Дыск па метададзеных",
- "LabelDiscover": "Знайсці",
+ "LabelDiscFromFilename": "Дыск з файла",
+ "LabelDiscFromMetadata": "Дыск з метаданых",
+ "LabelDiscover": "Знаходкі",
"LabelDownload": "Спампаваць",
- "LabelDownloadNEpisodes": "Спампована {0} эпізодаў",
+ "LabelDownloadNEpisodes": "Спампавана {0} выпускаў",
"LabelDownloadable": "Спампоўваецца",
"LabelDuration": "Працягласць",
"LabelDurationComparisonExactMatch": "(дакладнае супадзенне)",
@@ -328,23 +332,29 @@
"LabelEmailSettingsFromAddress": "Адрас адпраўніка",
"LabelEmailSettingsRejectUnauthorized": "Адхіляць неаўтарызаваныя сертыфікаты",
"LabelEmailSettingsRejectUnauthorizedHelp": "Адключэнне праверкі SSL-сертыфіката можа зрабіць ваша злучэнне ўразлівым перад пагрозамі бяспекі, такімі як атакі \"чалавек пасярэдзіне\". Адключайце гэтую опцыю толькі калі цалкам разумееце наступствы і ўпэўнены ў надзейнасці паштовага сервера.",
- "LabelEmailSettingsSecure": "Бяспечныя",
+ "LabelEmailSettingsSecure": "Бяспечна",
"LabelEmailSettingsSecureHelp": "Калі ўключана, злучэнне будзе выкарыстоўваць TLS пры падключэнні да сервера. Калі выключана, TLS будзе выкарыстоўвацца толькі ў выпадку падтрымкі пашырэння STARTTLS на серверы. У большасці выпадкаў усталюйце значэнне true пры падключэнні да порта 465. Для партоў 587 або 25 не ўключайце яго. (інфармацыя з nodemailer.com/smtp/#authentication)",
"LabelEmailSettingsTestAddress": "Тэставы адрас",
"LabelEmbeddedCover": "Убудаваная вокладка",
"LabelEnable": "Уключыць",
- "LabelEncodingBackupLocation": "Рэзервовая копія вашых арыгінальных аўдыёфайлаў будзе захавана ў:",
- "LabelEncodingChaptersNotEmbedded": "Раздзелы не ўбудаваны ў шматдарожкавыя аўдыякнігі.",
+ "LabelEncodingBackupLocation": "Рэзервовая копія арыгінальных аўдыяфайлаў будзе захавана ў:",
+ "LabelEncodingChaptersNotEmbedded": "Раздзелы не ўбудоўваюцца ў аўдыякнігі з некалькімі трэкамі.",
"LabelEncodingClearItemCache": "Пераканайцеся, што перыядычна ачышчаеце кэш элементаў.",
- "LabelEncodingFinishedM4B": "Гатовы файл M4B будзе змешчаны ў вашу тэчку з аўдыякнігамі па адрасе:",
- "LabelEncodingInfoEmbedded": "Метададзеныя будуць убудаваны ў аўдыядарожкі ўнутры вашай тэчкі з аўдыякнігамі.",
+ "LabelEncodingFinishedM4B": "Гатовы файл M4B будзе змешчаны ў вашу папку з аўдыякнігамі ў:",
+ "LabelEncodingInfoEmbedded": "Метаданыя будуць убудаваны ў аўдыятрэкі ўнутры папкі з аўдыякнігамі.",
"LabelEncodingStartedNavigation": "Пасля запуску задачы вы можаце перайсці на іншую старонку.",
"LabelEncodingTimeWarning": "Кадаванне можа заняць да 30 хвілін.",
+ "LabelEncodingWarningAdvancedSettings": "Увага: Не абнаўляйце гэтыя налады, калі вы не знаёмыя з параметрамі кадавання ffmpeg.",
+ "LabelEncodingWatcherDisabled": "Калі ў вас адключана адсочванне змен у папцы, пасля трэба будзе паўторна сканіраваць гэту аўдыякнігу.",
"LabelEnd": "Канец",
"LabelEndOfChapter": "Канец раздзела",
- "LabelEpisode": "Эпізод",
- "LabelEpisodeNotLinkedToRssFeed": "Эпізод не звязаны з RSS-стужкай",
- "LabelEpisodeUrlFromRssFeed": "URL эпізоду з RSS-стужкі",
+ "LabelEpisode": "Выпуск",
+ "LabelEpisodeNotLinkedToRssFeed": "Выпуск не звязаны з RSS-стужкай",
+ "LabelEpisodeNumber": "Выпуск №{0}",
+ "LabelEpisodeTitle": "Загаловак выпуску",
+ "LabelEpisodeType": "Тып выпуску",
+ "LabelEpisodeUrlFromRssFeed": "URL-адрас выпуску з RSS-стужкі",
+ "LabelEpisodes": "Выпускі",
"LabelEpisodic": "Эпізадычны",
"LabelExample": "Прыклад",
"LabelExpandSeries": "Разгарнуць серыю",
@@ -353,27 +363,48 @@
"LabelExpiresAt": "Тэрмін дзеяння заканчваецца ў",
"LabelExpiresInSeconds": "Тэрмін дзеяння заканчваецца праз (секунд)",
"LabelExpiresNever": "Ніколі",
- "LabelExplicit": "Відверты",
+ "LabelExplicit": "Непрыстойнае",
+ "LabelExplicitChecked": "Непрыстойнае (пазначана)",
+ "LabelExplicitUnchecked": "Прыстойнае (не пазначана)",
"LabelExportOPML": "Экспарт OPML",
"LabelFeedURL": "URL стужкі",
- "LabelFetchingMetadata": "Атрыманне метададзеных",
+ "LabelFetchingMetadata": "Атрыманне метаданых",
"LabelFile": "Файл",
"LabelFileBirthtime": "Час стварэння файла",
+ "LabelFileBornDate": "Створаны {0}",
"LabelFileModified": "Час змянення файла",
- "LabelFilename": "Імя файла",
- "LabelFinished": "Скончана",
- "LabelFolder": "Тэчка",
+ "LabelFileModifiedDate": "Зменены {0}",
+ "LabelFilename": "Назва файла",
+ "LabelFilterByUser": "Фільтраваць па карыстальніку",
+ "LabelFindEpisodes": "Знайсці выпускі",
+ "LabelFinished": "Завершана",
+ "LabelFinishedDate": "Завершана {0}",
+ "LabelFolder": "Папка",
+ "LabelFolders": "Папкі",
+ "LabelFontBold": "Тоўсты",
"LabelFontBoldness": "Таўшчыня шрыфта",
+ "LabelFontFamily": "Сямейства шрыфтоў",
+ "LabelFontItalic": "Курсіў",
"LabelFontScale": "Памер шрыфту",
+ "LabelFontStrikethrough": "Перакрэслены",
+ "LabelFormat": "Фармат",
+ "LabelFull": "Поўны",
"LabelGenre": "Жанр",
"LabelGenres": "Жанры",
+ "LabelHardDeleteFile": "Жорстка выдаляць файл",
"LabelHasEbook": "Мае электронную кнігу",
"LabelHasSupplementaryEbook": "Мае дадатковую электронную кнігу",
"LabelHideSubtitles": "Схаваць падзагалоўкі",
+ "LabelHighestPriority": "Найвышэйшы прыярытэт",
"LabelHost": "Хост",
- "LabelImageURLFromTheWeb": "URL выявы з інтэрнэту",
+ "LabelHour": "Гадзіна",
+ "LabelHours": "Гадзіны",
+ "LabelIcon": "Значок",
+ "LabelImageURLFromTheWeb": "URL-адрас відарыса з інтэрнэту",
"LabelInProgress": "У працэсе",
+ "LabelIncludeInTracklist": "Уключыць у спіс трэкаў",
"LabelIncomplete": "Незавершана",
+ "LabelInterval": "Інтэрвал",
"LabelIntervalCustomDailyWeekly": "Карыстальніцкі штодзённы/штотыднёвы",
"LabelIntervalEvery12Hours": "Кожныя 12 гадзін",
"LabelIntervalEvery15Minutes": "Кожныя 15 хвілін",
@@ -385,11 +416,14 @@
"LabelIntervalEveryMinute": "Кожную хвіліну",
"LabelInvert": "Інвертаваць",
"LabelItem": "Элемент",
+ "LabelJumpBackwardAmount": "Час пераходу назад",
+ "LabelJumpForwardAmount": "Час пераходу наперад",
"LabelLanguage": "Мова",
- "LabelLanguageDefaultServer": "Мова сервера па змаўчанні",
+ "LabelLanguageDefaultServer": "Прадвызначаная мова сервера",
"LabelLanguages": "Мовы",
"LabelLastBookAdded": "Апошняя дададзеная кніга",
"LabelLastBookUpdated": "Апошняя абноўленая кніга",
+ "LabelLastProgressDate": "Апошні прагрэс: {0}",
"LabelLastSeen": "Апошні прагляд",
"LabelLastTime": "Апошні раз",
"LabelLastUpdate": "Апошняе абнаўленне",
@@ -401,43 +435,109 @@
"LabelLibrary": "Бібліятэка",
"LabelLibraryFilterSublistEmpty": "Не {0}",
"LabelLibraryItem": "Элемент бібліятэкі",
- "LabelLibraryName": "Імя бібліятэкі",
- "LabelLibrarySortByProgress": "Прагрэс абноўлены",
+ "LabelLibraryName": "Назва бібліятэкі",
+ "LabelLibrarySortByProgress": "Прагрэс: апошняе абнаўленне",
+ "LabelLibrarySortByProgressFinished": "Прагрэс: завершана",
+ "LabelLibrarySortByProgressStarted": "Прагрэс: пачата",
"LabelLimit": "Абмежаванне",
"LabelLineSpacing": "Міжрадковы інтэрвал",
"LabelListenAgain": "Паслухаць зноў",
- "LabelMaxEpisodesToDownload": "Максімальная колькасць эпізодаў для спампоўкі. Выкарыстоўвайце 0 для неабмежаванай колькасці.",
- "LabelMaxEpisodesToDownloadPerCheck": "Максімальная колькасць новых эпізодаў для спампоўкі за праверку",
- "LabelMaxEpisodesToKeepHelp": "Значэнне 0 не ўстанаўлівае максімальнага абмежавання. Пасля аўтаматычнай спампоўкі новага эпізоду будзе выдалены самы стары эпізод, калі ў вас больш за X эпізодаў. Пры кожнай новай спампоўцы будзе выдаляцца толькі 1 эпізод.",
+ "LabelLogLevelDebug": "Debug",
+ "LabelLogLevelInfo": "Info",
+ "LabelLogLevelWarn": "Warn",
+ "LabelLookForNewEpisodesAfterDate": "Шукаць новыя выпускі пасля гэтай даты",
+ "LabelLowestPriority": "Найніжэйшы прыярытэт",
+ "LabelMatchConfidence": "Упэўненасць",
+ "LabelMatchExistingUsersBy": "Параўноўваць існуючых карыстальнікаў па",
+ "LabelMatchExistingUsersByDescription": "Выкарыстоўваецца для падключэння існуючых карыстальнікаў. Пасля падключэння карыстальнікі будуць супастаўляцца з дапамогай унікальнага ідэнтыфікатара ад пастаўшчыка SSO",
+ "LabelMaxEpisodesToDownload": "Максімальная колькасць выпускаў для спампоўвання. 0 – неабмежаваная колькасць.",
+ "LabelMaxEpisodesToDownloadPerCheck": "Максімальная колькасць новых выпускаў для спампоўвання за праверку",
+ "LabelMaxEpisodesToKeep": "Максімальная колькасць выпускаў, якія будуць захоўвацца",
+ "LabelMaxEpisodesToKeepHelp": "Значэнне 0 не задае максімальнага абмежавання. Пасля аўтаматычнага спампоўвання новага выпуску будзе выдалены самы стары выпуск, калі ў вас больш за X выпускаў. Пры кожным новым спампоўванні будзе выдаляцца толькі 1 выпуск.",
"LabelMediaPlayer": "Медыяпрайгравальнік",
"LabelMediaType": "Тып медыя",
- "LabelMetadataOrderOfPrecedenceDescription": "Крыніцы метададзеных з вышэйшым прыярытэтам будуць замяняць крыніцы з ніжэйшым прыярытэтам",
- "LabelMetadataProvider": "Пастаўшчык метададзеных",
+ "LabelMetaTag": "Метатэг",
+ "LabelMetaTags": "Метатэгі",
+ "LabelMetadataOrderOfPrecedenceDescription": "Крыніцы метаданых з вышэйшым прыярытэтам будуць замяняць крыніцы з ніжэйшым прыярытэтам",
+ "LabelMetadataProvider": "Пастаўшчык метаданых",
+ "LabelMinute": "Хвіліна",
+ "LabelMinutes": "Хвіліны",
"LabelMissing": "Адсутнічае",
+ "LabelMissingEbook": "Няма электроннай кнігі",
+ "LabelMissingSupplementaryEbook": "Няма дадатковай электроннай кнігі",
+ "LabelMobileRedirectURIs": "Дазволеныя URI перанакіравання для мабільных прылад",
+ "LabelMobileRedirectURIsDescription": "Гэта белы спіс дапушчальных URI перанакіравання для мабільных праграм. Стандартным з'яўляецца audiobookshelf://oauth, які вы можаце выдаліць або дапоўніць дадатковымі URI для інтэграцыі са староннімі праграмамі. Выкарыстанне зорачкі (*) у якасці адзінага запісу дазваляе любы URI.",
"LabelMore": "Больш",
"LabelMoreInfo": "Больш інфармацыі",
- "LabelName": "Імя",
- "LabelNarrator": "Чытальнік",
- "LabelNarrators": "Чытальнікі",
+ "LabelName": "Назва",
+ "LabelNarrator": "Дыктар",
+ "LabelNarrators": "Дыктары",
+ "LabelNew": "Новы",
+ "LabelNewPassword": "Новы пароль",
"LabelNewestAuthors": "Новыя аўтары",
- "LabelNewestEpisodes": "Новыя эпізоды",
- "LabelNoCustomMetadataProviders": "Няма карыстацкіх пастаўшчыкоў метададзеных",
- "LabelNotFinished": "Не скончана",
+ "LabelNewestEpisodes": "Найноўшыя выпускі",
+ "LabelNextBackupDate": "Дата наступнага рэзервовага капіравання",
+ "LabelNextChapters": "Наступныя раздзелы:",
+ "LabelNextScheduledRun": "Наступны запланаваны запуск",
+ "LabelNoApiKeys": "Няма ключоў API",
+ "LabelNoCustomMetadataProviders": "Няма карыстальніцкіх пастаўшчыкоў метаданых",
+ "LabelNoEpisodesSelected": "Не выбрана ніводнага выпуску",
+ "LabelNotFinished": "Незавершана",
"LabelNotStarted": "Не пачата",
+ "LabelNotes": "Заўвагі",
+ "LabelNotificationAppriseURL": "URL-адрасы Apprise",
+ "LabelNotificationAvailableVariables": "Даступныя пераменныя",
+ "LabelNotificationBodyTemplate": "Шаблон зместу",
+ "LabelNotificationEvent": "Падзея апавяшчэння",
+ "LabelNotificationTitleTemplate": "Шаблон загалоўка",
+ "LabelNotificationsMaxFailedAttempts": "Максімальная колькасць няўдалых спроб",
"LabelNotificationsMaxFailedAttemptsHelp": "Апавяшчэнні адключаюцца пасля таго, як не ўдаецца іх адправіць гэтулькі разоў",
- "LabelNumberOfEpisodes": "# з эпізодаў",
+ "LabelNotificationsMaxQueueSize": "Максімальны памер чаргі для падзей апавяшчэнняў",
+ "LabelNotificationsMaxQueueSizeHelp": "Падзеі могуць спрацоўваць толькі адзін раз у секунду. Падзеі будуць ігнаравацца пры дасягненні максімальнага памеру чаргі. Гэта прадухіляе рассылку спаму.",
+ "LabelNumberOfBooks": "Колькасць кніг",
+ "LabelNumberOfChapters": "Колькасць раздзелаў:",
+ "LabelNumberOfEpisodes": "Колькасць выпускаў",
+ "LabelOpenIDAdvancedPermsClaimDescription": "Назва прэтэнзіі OpenID, якая змяшчае пашыраныя дазволы для дзеянняў карыстальніка ў праграме, якія будуць прымяняцца да роляў, якія не з'яўляюцца адміністратарамі (калі наладжана). Калі прэтэнзія адсутнічае ў адказе, доступ да ABS будзе забаронены. Калі адсутнічае адзін параметр, ён будзе разглядацца як false. Пераканайцеся, што прэтэнзія пастаўшчыка ідэнтыфікацыі адпавядае чаканай структуры:",
+ "LabelOpenIDClaims": "Пакіньце наступныя параметры пустымі, каб адключыць пашыранае прызначэнне груп і дазволаў, аўтаматычна прызначаючы групу \"Карыстальнік\".",
+ "LabelOpenIDGroupClaimDescription": "Назва прэтэнзіі OpenID, якая змяшчае спіс груп карыстальніка. Звычайна іх называюць групамі. Калі наладжана, праграма будзе аўтаматычна прызначаць ролі на аснове членства карыстальніка ў групах, пры ўмове, што ў прэтэнзіі гэтыя групы названы без уліку рэгістра: \"адміністратар\", \"карыстальнік або \"госць\". Прэтэнзія павінна ўтрымліваць спіс, і калі карыстальнік належыць да некалькіх груп, праграма прызначыць ролю, якая адпавядае найвышэйшаму ўзроўню доступу. Калі ніводная група не супадае, доступ будзе забаронены.",
"LabelOpenRSSFeed": "Адкрыць RSS-стужку",
+ "LabelOverwrite": "Перазапісаць",
+ "LabelPaginationPageXOfY": "Старонка {0} з {1}",
"LabelPassword": "Пароль",
"LabelPath": "Шлях",
- "LabelPermissionsDownload": "Можна спампаваць",
- "LabelPlaylists": "Cпісs прайгравання",
+ "LabelPermanent": "Пастаянны",
+ "LabelPermissionsAccessAllLibraries": "Мае доступ да ўсіх бібліятэк",
+ "LabelPermissionsAccessAllTags": "Мае доступ да ўсіх тэгаў",
+ "LabelPermissionsAccessExplicitContent": "Мае доступ да непрыстойнага змесціва",
+ "LabelPermissionsCreateEreader": "Можа ствараць прыладу для чытання",
+ "LabelPermissionsDelete": "Можа выдаляць",
+ "LabelPermissionsDownload": "Можа спампоўваць",
+ "LabelPermissionsUpdate": "Можа абнаўляць",
+ "LabelPermissionsUpload": "Можа запампоўваць",
+ "LabelPersonalYearReview": "Вынікі года ({0})",
+ "LabelPhotoPathURL": "Шлях/URL-адрас фота",
+ "LabelPlayMethod": "Метад прайгравання",
+ "LabelPlaybackRateIncrementDecrement": "Павелічэнне/памяншэнне хуткасці прайгравання",
+ "LabelPlayerChapterNumberMarker": "{0} з {1}",
+ "LabelPlaylists": "Плэй-лісты",
"LabelPodcast": "Падкаст",
+ "LabelPodcastSearchRegion": "Рэгіён пошуку падкастаў",
+ "LabelPodcastType": "Тып падкаста",
"LabelPodcasts": "Падкасты",
+ "LabelPort": "Порт",
+ "LabelPrefixesToIgnore": "Прэфіксы, якія трэба ігнараваць (без уліку рэгістра)",
"LabelPreventIndexing": "Прадухіліць індэксацыю вашай стужкі каталогамі падкастаў iTunes і Google",
+ "LabelPrimaryEbook": "Асноўная электронная кніга",
"LabelProgress": "Прагрэс",
+ "LabelProvider": "Пастаўшчык",
+ "LabelProviderAuthorizationValue": "Значэнне загалоўка аўтарызацыі",
"LabelPubDate": "Дата публікацыі",
"LabelPublishYear": "Год публікацыі",
"LabelPublishedDate": "Апублікавана {0}",
+ "LabelPublishedDecade": "Дзесяцігоддзе публікацыі",
+ "LabelPublishedDecades": "Дзесяцігоддзі публікацыі",
+ "LabelPublisher": "Выдавец",
+ "LabelPublishers": "Выдаўцы",
"LabelRSSFeedCustomOwnerEmail": "Карыстальніцкая электронная пошта ўладальніка",
"LabelRSSFeedCustomOwnerName": "Карыстальніцкае імя ўладальніка",
"LabelRSSFeedOpen": "RSS-стужка адкрыта",
@@ -445,16 +545,22 @@
"LabelRSSFeedSlug": "Ідэнтыфікатар RSS-стужкі",
"LabelRSSFeedURL": "URL RSS-стужкі",
"LabelRandomly": "Выпадкова",
- "LabelReAddSeriesToContinueListening": "Дадаць серыю зноў у Працягваць слухаць",
+ "LabelReAddSeriesToContinueListening": "Дадаць серыю зноў у Працягнуць праслухоўванне",
"LabelRead": "Чытаць",
"LabelReadAgain": "Чытаць зноў",
+ "LabelReadEbookWithoutProgress": "Чытаць электронную кнігу без захавання прагрэсу",
"LabelRecentSeries": "Апошнія серыі",
"LabelRecentlyAdded": "Нядаўна дададзеныя",
+ "LabelRecommended": "Рэкамендаваныя",
+ "LabelRedo": "Узнавіць",
+ "LabelRegion": "Рэгіён",
+ "LabelReleaseDate": "Дата выпуску",
"LabelRemoveAllMetadataAbs": "Выдаліць усе файлы metadata.abs",
"LabelRemoveAllMetadataJson": "Выдаліць усе файлы metadata.json",
+ "LabelRemoveAudibleBranding": "Выдаляць уступленне і завяршэнне Audible з раздзелаў",
"LabelRemoveCover": "Выдаліць вокладку",
- "LabelRemoveMetadataFile": "Выдаліць файлы метададзеных у тэчках элементаў бібліятэкі",
- "LabelRemoveMetadataFileHelp": "Выдаліць усе файлы metadata.json і metadata.abs у вашых {0} тэчках.",
+ "LabelRemoveMetadataFile": "Выдаліць файлы метаданых у папках элементаў бібліятэкі",
+ "LabelRemoveMetadataFileHelp": "Выдаліць усе файлы metadata.json і metadata.abs у {0} папках.",
"LabelRowsPerPage": "Радкоў на старонку",
"LabelSearchTerm": "Пошукавы запыт",
"LabelSearchTitle": "Пошук па загалоўку",
@@ -462,8 +568,9 @@
"LabelSeason": "Сезон",
"LabelSeasonNumber": "Сезон #{0}",
"LabelSelectAll": "Выбраць усё",
- "LabelSelectAllEpisodes": "Выбраць усе эпізоды",
- "LabelSelectEpisodesShowing": "Выбраць {0} эпізодаў для паказу",
+ "LabelSelectAllEpisodes": "Выбраць усе выпускі",
+ "LabelSelectEpisodesShowing": "Выбраць {0} выпускаў для паказу",
+ "LabelSelectUser": "Выберыце карыстальніка",
"LabelSelectUsers": "Выбраць карыстальнікаў",
"LabelSendEbookToDevice": "Адправіць электронную кнігу на...",
"LabelSequence": "Паслядоўнасць",
@@ -477,48 +584,70 @@
"LabelSetEbookAsSupplementary": "Зрабіць дадатковым",
"LabelSettingsAllowIframe": "Дазволіць убудоўванне ў iframe",
"LabelSettingsAudiobooksOnly": "Толькі аўдыякнігі",
- "LabelSettingsAudiobooksOnlyHelp": "Уключэнне гэтай налады будзе ігнараваць файлы электронных кніг, калі толькі яны не знаходзяцца ў тэчцы з аўдыякнігамі. У такім выпадку яны будуць пазначаны як дадатковыя электронныя кнігі.",
+ "LabelSettingsAudiobooksOnlyHelp": "Пры ўключэнні гэтай налады файлы электронных кніг будуць ігнаравацца, калі толькі яны не знаходзяцца ў папцы з аўдыякнігамі. У такім выпадку яны будуць пазначаны як дадатковыя электронныя кнігі.",
"LabelSettingsBookshelfViewHelp": "Рэалістычны дызайн з драўлянымі паліцамі",
- "LabelSettingsEnableWatcherForLibrary": "Аўтаматычна правяраць бібліятэку на змены",
+ "LabelSettingsChromecastSupport": "Падтрымка Chromecast",
+ "LabelSettingsDateFormat": "Фарматы даты",
+ "LabelSettingsEnableWatcher": "Аўтаматычна сачыць за зменамі ў бібліятэках",
+ "LabelSettingsEnableWatcherForLibrary": "Аўтаматычна сачыць за зменамі ў бібліятэцы",
"LabelSettingsEnableWatcherHelp": "Адключае аўтаматычнае дадаванне/абнаўленне элементаў пры выяўленні змен у файлах. *Патрабуецца перазапуск сервера",
- "LabelSettingsEpubsAllowScriptedContent": "Дазволіць скрыптавы кантэнт у EPUB",
- "LabelSettingsEpubsAllowScriptedContentHelp": "Дазволіць EPUB-файлам выконваць скрыпты. Рэкамендуецца пакінуць гэтую наладу выключанай, калі вы не давяраеце крыніцы EPUB-файлаў.",
+ "LabelSettingsEpubsAllowScriptedContent": "Дазваляць скрыпты ў файлах EPUB",
+ "LabelSettingsEpubsAllowScriptedContentHelp": "Дазволіць файлам EPUB выконваць скрыпты. Рэкамендуецца пакінуць гэтую наладу выключанай, калі вы не давяраеце крыніцы файлаў EPUB.",
"LabelSettingsExperimentalFeatures": "Эксперыментальныя функцыі",
"LabelSettingsExperimentalFeaturesHelp": "Функцыі ў распрацоўцы, для якіх вашы водгукі і дапамога ў тэставанні будуць карыснымі. Націсніце, каб адкрыць абмеркаванне на GitHub.",
- "LabelSettingsFindCovers": "Знайсці вокладкі",
- "LabelSettingsFindCoversHelp": "Калі ў вашай аўдыякнізе няма ўбудаванай вокладкі або выявы вокладкі ў тэчцы, сканер паспрабуе знайсці вокладку.http://192.168.1.1:8337, то вы павінны ўвесці http://192.168.1.1:8337/notify.",
- "MessageBackupsDescription": "Рэзервовыя копіі ўключаюць карыстальнікаў, іх прагрэс, падрабязнасці элементаў бібліятэкі, налады сервера і выявы, якія захоўваюцца ў /metadata/items і /metadata/authors. Рэзервовыя копіі не ўключаюць файлы, якія захоўваюцца ў вашых тэчках бібліятэкі.",
+ "MessageAsinCheck": "Пераканайцеся, што выкарыстоўваеце ASIN з правільнага рэгіёна Audible, а не Amazon.",
+ "MessageAuthenticationLegacyTokenWarning": "Састарэлыя токены API будуць выдалены ў будучыні. Замест іх выкарыстоўвайце ключы API.",
+ "MessageAuthenticationOIDCChangesRestart": "Перазапусціце сервер пасля захавання, каб прымяніць змены OIDC.",
+ "MessageAuthenticationSecurityMessage": "Дзеля бяспекі была палепшана аўтэнтыфікацыя. Усім карыстальнікам трэба паўторна ўвайсці ў сістэму.",
+ "MessageBackupsDescription": "Рэзервовыя копіі ўключаюць карыстальнікаў, іх прагрэс, падрабязнасці элементаў бібліятэкі, налады сервера і відарысы, якія захоўваюцца ў /metadata/items і /metadata/authors. Рэзервовыя копіі не ўключаюць файлы, якія захоўваюцца ў папках бібліятэкі.",
"MessageBackupsLocationEditNote": "Заўвага: Абнаўленне месцазнаходжання рэзервовых копій не перамяшчае і не змяняе існуючыя рэзервовыя копіі",
- "MessageBackupsLocationNoEditNote": "Заўвага: Месцазнаходжанне рэзервовых копій задаецца праз зменную асяроддзя і не можа быць зменена тут.",
+ "MessageBackupsLocationNoEditNote": "Заўвага: Размяшчэнне рэзервовых копій задаецца праз зменную асяроддзя і не можа быць зменена тут.",
"MessageBackupsLocationPathEmpty": "Шлях да месцазнаходжання рэзервовых копій не можа быць пустым",
- "MessageBatchEditPopulateMapDetailsAllHelp": "Запоўніце ўключаныя палі дадзенымі з усіх элементаў. Палі з некалькімі значэннямі будуць аб'яднаны",
- "MessageBatchEditPopulateMapDetailsItemHelp": "Запоўніце ўключаныя палі падрабязнасцей карты дадзенымі з гэтага элемента",
- "MessageBatchQuickMatchDescription": "Хуткі пошук паспрабуе дадаць адсутныя вокладкі і метададзеныя для выбраных элементаў. Уключыце ніжэй выкладзеныя опцыі, каб дазволіць Хуткаму пошуку замяняць існуючыя вокладкі і/або метададзеныя.",
+ "MessageBatchEditPopulateMapDetailsAllHelp": "Запоўніце ўключаныя палі данымі з усіх элементаў. Палі з некалькімі значэннямі будуць аб'яднаны",
+ "MessageBatchEditPopulateMapDetailsItemHelp": "Запоўніце ўключаныя палі падрабязнасцей карты данымі з гэтага элемента",
+ "MessageBatchQuickMatchDescription": "Хуткі пошук паспрабуе дадаць адсутныя вокладкі і метаданыя для выбраных элементаў. Уключыце параметры ніжэй, каб дазволіць Хуткаму пошуку замяняць існуючыя вокладкі і/або метаданыя.",
+ "MessageBookshelfNoCollections": "Вы пакуль не стварылі ніводнай калекцый",
+ "MessageBookshelfNoCollectionsHelp": "Калекцыі публічныя. Усе карыстальнікі, якія маюць доступ да бібліятэкі, могуць іх бачыць.",
"MessageBookshelfNoRSSFeeds": "Няма адкрытых RSS-стужак",
+ "MessageBookshelfNoResultsForFilter": "Няма вынікаў для фільтра \"{0}: {1}\"",
+ "MessageBookshelfNoResultsForQuery": "Няма вынікаў па запыце",
+ "MessageBookshelfNoSeries": "У вас няма серый",
+ "MessageBulkChapterPattern": "Колькі раздзелаў вы хочаце дадаць з дапамогай гэтага ўзору нумарацыі?",
+ "MessageChapterEndIsAfter": "Канец раздзела ідзе пасля канца аўдыякнігі",
+ "MessageChapterErrorFirstNotZero": "Першы раздзел павінен пачынацца з 0",
"MessageChapterErrorStartGteDuration": "Няправільны час пачатку: ён павінен быць меншым за працягласць аўдыякнігі",
"MessageChapterErrorStartLtPrev": "Няправільны час пачатку: ён павінен быць большым або роўным часу пачатку папярэдняга раздзела",
- "MessageConfirmCloseFeed": "Вы ўпэўнены, што жадаеце закрыць гэтую стужку?",
- "MessageConfirmDeleteMetadataProvider": "Ці ўпэўненыя вы, што жадаеце выдаліць карыстацкага пастаўшчыка метададзеных \"{0}\"?",
- "MessageConfirmEmbedMetadataInAudioFiles": "Ці ўпэўненыя вы, што жадаеце ўбудаваць метададзеныя ў {0} аўдыёфайлаў?",
- "MessageConfirmPurgeCache": "Ачышчэнне кэша выдаліць увесь каталог па адрасе /metadata/cache. /metadata/cache/items. /metadata/cache. /metadata/cache/items./metadata/logs у фармаце JSON. Журналы памылак захоўваюцца ў файле /metadata/logs/crashlogs.txt.",
- "MessageMapChapterTitles": "Супаставіць назвы раздзелаў з вашымі існуючымі раздзеламі аўдыякнігі без змянення часовых метак",
- "MessageMarkAsFinished": "Пазначыць як скончана",
+ "MessageM4BFailed": "Памылка M4B!",
+ "MessageM4BFinished": "M4B завершана!",
+ "MessageMapChapterTitles": "Супаставіць загалоўкі раздзелаў з існуючымі раздзеламі аўдыякнігі без змянення пазнак часу",
+ "MessageMarkAllEpisodesFinished": "Пазначыць усе выпускі як завершаныя",
+ "MessageMarkAllEpisodesNotFinished": "Пазначыць усе выпускі як незавершаныя",
+ "MessageMarkAsFinished": "Пазначыць як завершаную",
+ "MessageMarkAsNotFinished": "Пазначыць як незавершаную",
+ "MessageMatchBooksDescription": "паспрабуе параўнаць кнігі ў бібліятэцы з кнігай ад выбранай пошукавай сістэмы і запоўніць пустыя палі і вокладку. Не перазапісвае звесткі.",
+ "MessageNoAudioTracks": "Няма аўдыятрэкаў",
+ "MessageNoAuthors": "Няма аўтараў",
+ "MessageNoBackups": "Няма рэзервовых копій",
"MessageNoBookmarks": "Няма закладак",
"MessageNoChapters": "Няма раздзелаў",
"MessageNoCollections": "Няма калекцый",
- "MessageNoDownloadsInProgress": "Зараз няма актыўных спамповак",
+ "MessageNoCoversFound": "Не знойдзена вокладак",
+ "MessageNoDescription": "Няма апісання",
+ "MessageNoDevices": "Няма прылад",
+ "MessageNoDownloadsInProgress": "Зараз няма актыўных спампованняў",
"MessageNoDownloadsQueued": "Няма спамповак у чарзе",
+ "MessageNoEpisodeMatchesFound": "Адпаведных выпускаў не знойдзена",
+ "MessageNoEpisodes": "Няма выпускаў",
+ "MessageNoFoldersAvailable": "Няма даступных папак",
+ "MessageNoGenres": "Няма жанраў",
+ "MessageNoIssues": "Няма праблем",
"MessageNoItems": "Няма элементаў",
"MessageNoItemsFound": "Элементы не знойдзены",
"MessageNoListeningSessions": "Няма сеансаў праслухоўвання",
+ "MessageNoLogs": "Няма журналаў",
"MessageNoMediaProgress": "Няма прагрэсу медыя",
+ "MessageNoNotifications": "Няма апавяшчэнняў",
"MessageNoPodcastFeed": "Няправільны падкаст: Няма стужкі",
"MessageNoPodcastsFound": "Падкасты не знойдзены",
+ "MessageNoResults": "Няма вынікаў",
+ "MessageNoSearchResultsFor": "Няма вынікаў пошуку па запыце \"{0}\"",
+ "MessageNoSeries": "Няма серый",
+ "MessageNoTags": "Няма тэгаў",
"MessageNoTasksRunning": "Няма запушчаных задач",
"MessageNoUpdatesWereNecessary": "Абнаўленні не патрабаваліся",
- "MessageNoUserPlaylists": "У вас няма спісаў прайгравання",
- "MessageNoUserPlaylistsHelp": "Спісы прайгравання прыватныя. Толькі карыстальнік, які іх стварыў, можа іх бачыць.",
- "MessageOpmlPreviewNote": "Заўвага: гэта папярэдні прагляд разабранага файла OPML. Фактычная назва падкаста будзе ўзятая з RSS-стужкі.",
- "MessagePlaylistCreateFromCollection": "Стварыць спіс прайгравання з калекцыі",
+ "MessageNoUserPlaylists": "У вас няма плэй-лістоў",
+ "MessageNoUserPlaylistsHelp": "Плэй-лісты прыватныя. Толькі карыстальнік, які іх стварыў, можа іх бачыць.",
+ "MessageNotYetImplemented": "Пакуль не рэалізавана",
+ "MessageOpmlPreviewNote": "Заўвага: гэта перадпрагляд прааналізаванага файла OPML. Фактычны загаловак падкаста будзе ўзяты з RSS-стужкі.",
+ "MessageOr": "або",
+ "MessagePauseChapter": "Прыпыніць прайграванне раздзела",
+ "MessagePlayChapter": "Паслухаць пачатак раздзела",
+ "MessagePlaylistCreateFromCollection": "Стварыць плэй-ліст з калекцыі",
+ "MessagePleaseWait": "Пачакайце...",
"MessagePodcastHasNoRSSFeedForMatching": "У падкаста няма URL RSS-стужкі для супадзення",
"MessagePodcastSearchField": "Увядзіце пошукавы запыт або URL RSS-стужкі",
- "MessageQuickMatchDescription": "Запоўніць пустыя дэталі элемента і вокладку першым вынікам супадзення з '{0}'. Не замяняе дэталі, калі опцыя «Аддаваць перавагу супадаючым метададзеным» на серверы не ўключана.",
+ "MessageQuickEmbedInProgress": "Выконваецца хуткае ўбудаванне",
+ "MessageQuickEmbedQueue": "Пастаўлена ў чаргу для хуткага ўбудавання ({0} у чарзе)",
+ "MessageQuickMatchAllEpisodes": "Хуткае параўнанне ўсіх выпускаў",
+ "MessageQuickMatchDescription": "Запоўніць пустыя звесткі элемента і вокладку першым вынікам супадзення з '{0}'. Не замяняе звесткіі, калі параметр \"Аддаваць перавагу супадаючым метаданым\" на серверы не ўключана.",
+ "MessageRemoveChapter": "Выдаліць раздзел",
+ "MessageRemoveEpisodes": "Выдаліць выпускі ({0})",
+ "MessageRemoveFromPlayerQueue": "Выдаліць з чаргі прагравання",
+ "MessageRemoveUserWarning": "Вы ўпэўнены, што хочаце назаўжды выдаліць карыстальніка \"{0}\"?",
"MessageReportBugsAndContribute": "Паведамляйце пра памылкі, прапануйце новыя функцыі і ўдзельнічайце на",
- "MessageRestoreBackupWarning": "Аднаўленне рэзервовай копіі перазапіша ўсю базу даных, размешчаную ў /config, а таксама выявы вокладкі ў /metadata/items і /metadata/authors. /metadata/cache. /metadata/cache/items. false. Sørg for at identity provider's claim matcher den forventede struktur:",
"LabelOpenIDClaims": "Efterlad de følgende indstillinger tomme for at deaktivere avanceret gruppe og adgangsindstilling, ved automatisk at assigne 'Bruger' grupper.",
@@ -576,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Skeumorfisk design med træhylder",
"LabelSettingsChromecastSupport": "Chromecast-understøttelse",
"LabelSettingsDateFormat": "Datoformat",
- "LabelSettingsEnableWatcher": "Scan automatisk bibliotek for ændringer",
- "LabelSettingsEnableWatcherForLibrary": "Scan automatisk bibliotek for ændringer",
+ "LabelSettingsEnableWatcher": "Automatisk biblioteksovervåger",
+ "LabelSettingsEnableWatcherForLibrary": "Automatisk biblioteksovervåger",
"LabelSettingsEnableWatcherHelp": "Aktiverer automatisk tilføjelse/opdatering af elementer, når filændringer registreres. *Kræver servergenstart",
"LabelSettingsEpubsAllowScriptedContent": "Tillad scriptet indhold i epub",
"LabelSettingsEpubsAllowScriptedContentHelp": "Tillad epub filer at køre scripts. Det anbefales at holde denne indstilling deaktiveret med mindre du stoler på kilderne af epub filerne.",
@@ -626,6 +638,7 @@
"LabelStartTime": "Starttid",
"LabelStarted": "Startet",
"LabelStartedAt": "Startet klokken",
+ "LabelStartedDate": "Startet {0}",
"LabelStatsAudioTracks": "Lydspor",
"LabelStatsAuthors": "Forfattere",
"LabelStatsBestDay": "Bedste dag",
@@ -655,6 +668,7 @@
"LabelTheme": "Tema",
"LabelThemeDark": "Mørk",
"LabelThemeLight": "Lys",
+ "LabelThemeSepia": "Sepia",
"LabelTimeBase": "Tidsbase",
"LabelTimeDurationXHours": "{0} timer",
"LabelTimeDurationXMinutes": "{0} minutter",
@@ -739,6 +753,7 @@
"MessageBookshelfNoResultsForFilter": "Ingen resultater for filter \"{0}: {1}\"",
"MessageBookshelfNoResultsForQuery": "Intet resultat for query",
"MessageBookshelfNoSeries": "Du har ingen serier",
+ "MessageBulkChapterPattern": "Hvor mange kapitler vil du tilføje med dette nummereringsmønster?",
"MessageChapterEndIsAfter": "Kapitelslutningen er efter slutningen af din lydbog",
"MessageChapterErrorFirstNotZero": "Første kapitel skal starte ved 0",
"MessageChapterErrorStartGteDuration": "Ugyldig starttid skal være mindre end lydbogens varighed",
@@ -775,6 +790,7 @@
"MessageConfirmRemoveAuthor": "Er du sikker på, at du vil fjerne forfatteren \"{0}\"?",
"MessageConfirmRemoveCollection": "Er du sikker på, at du vil fjerne samlingen \"{0}\"?",
"MessageConfirmRemoveEpisode": "Er du sikker på, at du vil fjerne episoden \"{0}\"?",
+ "MessageConfirmRemoveEpisodeNote": "Obs: Dette sletter ikke lydfilen medmindre \"Permanent sletning af fil\" er aktiveret",
"MessageConfirmRemoveEpisodes": "Er du sikker på, at du vil fjerne {0} episoder?",
"MessageConfirmRemoveListeningSessions": "Er du sikker på at du vil fjerne {0} lytte sessioner?",
"MessageConfirmRemoveMetadataFiles": "Er du sikker på at du vil fjerne alle metadata.{0} filer i dine biblioteksfoldere?",
@@ -800,6 +816,8 @@
"MessageFeedURLWillBe": "Feed-URL vil være {0}",
"MessageFetching": "Henter...",
"MessageForceReScanDescription": "vil scanne alle filer igen som en frisk scanning. Lydfilens ID3-tags, OPF-filer og tekstfiler scannes som nye.",
+ "MessageHeatmapListeningTimeTooltip": "{0} lytter på {1}",
+ "MessageHeatmapNoListeningSessions": "Ingen lyttesessioner på {0}",
"MessageImportantNotice": "Vigtig besked!",
"MessageInsertChapterBelow": "Indsæt kapitel nedenfor",
"MessageInvalidAsin": "Ugyldig ASIN",
@@ -870,7 +888,7 @@
"MessageResetChaptersConfirm": "Er du sikker på, at du vil nulstille kapitler og annullere ændringerne, du har foretaget?",
"MessageRestoreBackupConfirm": "Er du sikker på, at du vil gendanne sikkerhedskopien oprettet den",
"MessageRestoreBackupWarning": "Gendannelse af en sikkerhedskopi vil overskrive hele databasen, som er placeret på /config, og omslagsbilleder i /metadata/items & /metadata/authors.http://192.168.1.1:8337 läuft, würdest du http://192.168.1.1:8337/notify eingeben.",
"MessageAsinCheck": "Stelle sicher, dass die ASIN aus der richtigen Audible Region verwendet wird, nicht Amazon.",
- "MessageAuthenticationLegacyTokenWarning": "Alte API tokens werden in Zukunft entfernt. Benutze stattdessen API Keys.",
+ "MessageAuthenticationLegacyTokenWarning": "Alte API-Token werden in Zukunft entfernt. Benutze stattdessen API Keys.",
"MessageAuthenticationOIDCChangesRestart": "Nach dem Speichern muss der Server neugestartet werden um die OIDC Änderungen zu übernehmen.",
"MessageAuthenticationSecurityMessage": "Die Anmeldung wurde abgesichert. Benutzersitzungen werden getrennt, alle Benutzer müssen sich erneut anmelden.",
"MessageBackupsDescription": "In einer Sicherung werden Benutzer, Benutzerfortschritte, Details zu den Bibliotheksobjekten, Servereinstellungen und Bilder welche in /metadata/items & /metadata/authors gespeichert sind gespeichert. Sicherungen enthalten keine Dateien welche in den einzelnen Bibliotheksordnern (Medien-Ordnern) gespeichert sind.",
@@ -888,7 +888,7 @@
"MessageResetChaptersConfirm": "Kapitel und vorgenommenen Änderungen werden zurückgesetzt und rückgängig gemacht! Bist du dir sicher?",
"MessageRestoreBackupConfirm": "Bist du dir sicher, dass du die Sicherung wiederherstellen willst, welche am",
"MessageRestoreBackupWarning": "Bei der Wiederherstellung einer Sicherung wird die gesamte Datenbank unter /config und die Titelbilder in /metadata/items und /metadata/authors überschrieben. audiobookshelf , que puede eliminar o complementar con URI adicionales para la integración de aplicaciones de terceros. Usando un asterisco ( * ) como única entrada que permite cualquier URI.",
+ "LabelMobileRedirectURIsDescription": "Esta es una lista en blanco de las URI de re‐direccionamiento válidos para aplicaciones móviles. El predeterminado es audiobookshelf , que puede retirar o sustituir con las URI adicionales para la integración de aplicaciones de terceros. Usando un asterisco (* ) como única entrada que permite cualquier URI.",
"LabelMore": "Más",
"LabelMoreInfo": "Más información",
"LabelName": "Nombre",
@@ -464,9 +479,10 @@
"LabelNextBackupDate": "Fecha del siguiente respaldo",
"LabelNextChapters": "Los próximos capítulos serán:",
"LabelNextScheduledRun": "Próxima ejecución programada",
+ "LabelNoApiKeys": "Sin claves API",
"LabelNoCustomMetadataProviders": "Sin proveedores de metadatos personalizados",
"LabelNoEpisodesSelected": "Ningún Episodio Seleccionado",
- "LabelNotFinished": "No terminado",
+ "LabelNotFinished": "No finalizado",
"LabelNotStarted": "Sin iniciar",
"LabelNotes": "Notas",
"LabelNotificationAppriseURL": "URL(s) de Apprise",
@@ -480,7 +496,7 @@
"LabelNotificationsMaxQueueSizeHelp": "Las notificaciones están limitadas a 1 por segundo. Las notificaciones serán ignoradas si llegan al numero máximo de cola para prevenir spam de eventos.",
"LabelNumberOfBooks": "Número de libros",
"LabelNumberOfChapters": "Número de capítulos:",
- "LabelNumberOfEpisodes": "N.º de episodios",
+ "LabelNumberOfEpisodes": "Nº de episodios",
"LabelOpenIDAdvancedPermsClaimDescription": "Nombre de la notificación de OpenID que contiene permisos avanzados para acciones de usuario dentro de la aplicación que se aplicarán a roles que no sean de administrador (si están configurados). Si el reclamo no aparece en la respuesta, se denegará el acceso a ABS. Si falta una sola opción, se tratará como falsa. Asegúrese de que la notificación del proveedor de identidades coincida con la estructura esperada:",
"LabelOpenIDClaims": "Deje las siguientes opciones vacías para desactivar la asignación avanzada de grupos y permisos, lo que asignaría de manera automática al grupo «Usuario».",
"LabelOpenIDGroupClaimDescription": "Nombre de la declaración OpenID que contiene una lista de grupos del usuario. Comúnmente conocidos como grupos. Si se configura, la aplicación asignará automáticamente roles en función de la pertenencia a grupos del usuario, siempre que estos grupos se denominen \"admin\", \"user\" o \"guest\" en la notificación. La solicitud debe contener una lista, y si un usuario pertenece a varios grupos, la aplicación asignará el rol correspondiente al mayor nivel de acceso. Si ningún grupo coincide, se denegará el acceso.",
@@ -510,7 +526,7 @@
"LabelPodcasts": "Pódcast",
"LabelPort": "Puerto",
"LabelPrefixesToIgnore": "Prefijos para ignorar (no distingue entre mayúsculas y minúsculas)",
- "LabelPreventIndexing": "Evite que los directorios de pódcast de iTunes y Google indicen su suministro",
+ "LabelPreventIndexing": "Evite que los directorios de pódcast de iTunes y Google indexen su suministro",
"LabelPrimaryEbook": "Libro electrónico principal",
"LabelProgress": "Progreso",
"LabelProvider": "Proveedor",
@@ -522,11 +538,11 @@
"LabelPublishedDecades": "Décadas publicadas",
"LabelPublisher": "Editor",
"LabelPublishers": "Editores",
- "LabelRSSFeedCustomOwnerEmail": "Correo electrónico de dueño personalizado",
- "LabelRSSFeedCustomOwnerName": "Nombre de dueño personalizado",
- "LabelRSSFeedOpen": "Suministro RSS abierto",
+ "LabelRSSFeedCustomOwnerEmail": "Correo-e de propietario personalizado",
+ "LabelRSSFeedCustomOwnerName": "Nombre de propietario personalizado",
+ "LabelRSSFeedOpen": "Fuente RSS Abierta",
"LabelRSSFeedPreventIndexing": "Evitar indización",
- "LabelRSSFeedSlug": "«Slug» de suministro RSS",
+ "LabelRSSFeedSlug": "Ficha de suministro RSS",
"LabelRSSFeedURL": "URL de suministro RSS",
"LabelRandomly": "Aleatorio",
"LabelReAddSeriesToContinueListening": "Volver a agregar la serie para continuar escuchándola",
@@ -554,11 +570,12 @@
"LabelSelectAll": "Seleccionar todo",
"LabelSelectAllEpisodes": "Seleccionar todos los episodios",
"LabelSelectEpisodesShowing": "Seleccionar los {0} episodios visibles",
+ "LabelSelectUser": "Seleccionar usuario",
"LabelSelectUsers": "Seleccionar usuarios",
"LabelSendEbookToDevice": "Enviar libro electrónico a...",
"LabelSequence": "Secuencia",
"LabelSerial": "En serie",
- "LabelSeries": "Serie",
+ "LabelSeries": "Series",
"LabelSeriesName": "Nombre de la serie",
"LabelSeriesProgress": "Progreso de la serie",
"LabelServerLogLevel": "Nivel de registro del servidor",
@@ -571,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Diseño Esqueuomorfo con Estantes de Madera",
"LabelSettingsChromecastSupport": "Compatibilidad con Chromecast",
"LabelSettingsDateFormat": "Formato de Fecha",
- "LabelSettingsEnableWatcher": "Buscar cambios automáticamente en las bibliotecas",
- "LabelSettingsEnableWatcherForLibrary": "Buscar cambios automáticamente en la biblioteca",
+ "LabelSettingsEnableWatcher": "Vigilar automáticamente los cambios en bibliotecas",
+ "LabelSettingsEnableWatcherForLibrary": "Vigilar automáticamente los cambios de biblioteca",
"LabelSettingsEnableWatcherHelp": "Permite agregar/actualizar elementos automáticamente cuando se detectan cambios en los archivos. *Requiere reiniciar el servidor",
"LabelSettingsEpubsAllowScriptedContent": "Permitir scripts en epubs",
"LabelSettingsEpubsAllowScriptedContentHelp": "Permitir que los archivos epub ejecuten scripts. Se recomienda mantener esta opción desactivada a menos que confíe en el origen de los archivos epub.",
@@ -605,14 +622,14 @@
"LabelSettingsStoreMetadataWithItemHelp": "Por defecto, los archivos de metadatos se almacenan en /metadata/items. Si habilita esta opción, los archivos de metadatos se guardarán en la carpeta de elementos de su biblioteca",
"LabelSettingsTimeFormat": "Formato de Tiempo",
"LabelShare": "Compartir",
- "LabelShareDownloadableHelp": "Permite a quienes posean el enlace de compartición descargar un archivo ZIP del elemento de la biblioteca.",
+ "LabelShareDownloadableHelp": "Permite a quienes posean el enlace de compartición descargar un archivo zip del elemento de la biblioteca.",
"LabelShareOpen": "abrir un recurso compartido",
"LabelShareURL": "Compartir la URL",
"LabelShowAll": "Mostrar todo",
"LabelShowSeconds": "Mostrar segundos",
"LabelShowSubtitles": "Mostrar subtítulos",
"LabelSize": "Tamaño",
- "LabelSleepTimer": "Temporizador de apagado",
+ "LabelSleepTimer": "Temporizador de dormida",
"LabelSlug": "Slug",
"LabelSortAscending": "Ascendente",
"LabelSortDescending": "Descendente",
@@ -621,6 +638,7 @@
"LabelStartTime": "Tiempo de Inicio",
"LabelStarted": "Iniciado",
"LabelStartedAt": "Iniciado En",
+ "LabelStartedDate": "Iniciado {0}",
"LabelStatsAudioTracks": "Pistas de Audio",
"LabelStatsAuthors": "Autores",
"LabelStatsBestDay": "Mejor día",
@@ -650,6 +668,7 @@
"LabelTheme": "Tema",
"LabelThemeDark": "Oscuro",
"LabelThemeLight": "Claro",
+ "LabelThemeSepia": "Sepia",
"LabelTimeBase": "Tiempo Base",
"LabelTimeDurationXHours": "{0} horas",
"LabelTimeDurationXMinutes": "{0} minutos",
@@ -718,8 +737,10 @@
"MessageAddToPlayerQueue": "Agregar a fila del Reproductor",
"MessageAppriseDescription": "Para usar esta función deberás tener la API de Apprise corriendo o una API que maneje los mismos resultados. http://192.168.1.1:8337 entonces pondría http://192.168.1.1:8337/notify.",
"MessageAsinCheck": "Cerciórese de usar el ASIN de la región correcta de Audible, no de Amazon.",
+ "MessageAuthenticationLegacyTokenWarning": "Los vales de API heredados serán retirados en el futuro. Utilice las claves de API en su lugar.",
"MessageAuthenticationOIDCChangesRestart": "Reinicie el servidor tras el guardado para aplicar los cambios de OIDC.",
- "MessageBackupsDescription": "Los respaldos incluyen: usuarios, el progreso del los usuarios, los detalles de los elementos de la biblioteca, la configuración del servidor y las imágenes en /metadata/items y /metadata/authors. Los Respaldos NO incluyen ningún archivo guardado en la carpeta de tu biblioteca.",
+ "MessageAuthenticationSecurityMessage": "La autenticación ha sido mejorada para seguridad. Todos los usuarios requieren reiniciar sesión.",
+ "MessageBackupsDescription": "Los respaldos incluyen: usuarios, el progreso del los usuarios, los detalles de los elementos de la biblioteca, la configuración del servidor y las imágenes en /metadata/items y /metadata/authors. Los Respaldos no incluyen ningún archivo guardado en la carpeta de tu biblioteca.",
"MessageBackupsLocationEditNote": "Nota: actualizar la ubicación de la copia de respaldo no moverá ni modificará los respaldos existentes",
"MessageBackupsLocationNoEditNote": "Nota: la ubicación de la copia de respaldo se establece a través de una variable de entorno y no se puede cambiar aquí.",
"MessageBackupsLocationPathEmpty": "La ruta de la copia de seguridad no puede estar vacía",
@@ -741,6 +762,7 @@
"MessageChaptersNotFound": "Capítulos no encontrados",
"MessageCheckingCron": "Revisando cron...",
"MessageConfirmCloseFeed": "¿Confirma que quiere cerrar este suministro?",
+ "MessageConfirmDeleteApiKey": "¿Está seguro que desea eliminar la clave API «{0}»?",
"MessageConfirmDeleteBackup": "¿Confirma que quiere eliminar el respaldo de {0}?",
"MessageConfirmDeleteDevice": "¿Confirma que quiere eliminar el lector electrónico «{0}»?",
"MessageConfirmDeleteFile": "Esto eliminará el archivo del sistema de archivos. ¿Quiere continuar?",
@@ -759,8 +781,8 @@
"MessageConfirmMarkSeriesFinished": "¿Confirma que quiere marcar todos los libros de esta serie como terminados?",
"MessageConfirmMarkSeriesNotFinished": "¿Confirma que quiere marcar todos los libros de esta serie como no terminados?",
"MessageConfirmNotificationTestTrigger": "¿Activar esta notificación con datos de prueba?",
- "MessageConfirmPurgeCache": "Purgar la antememoria eliminará el directorio completo ubicado en /metadata/cache. /metadata/cache/items./metadata/cache. /metadata/cache/items.http://192.168.1.1:8337, siis peaksite sisestama http://192.168.1.1:8337/notify.",
"MessageBackupsDescription": "Varukoopiad hõlmavad kasutajaid, kasutajate edenemist, raamatukogu üksikasju, serveri seadeid ja kaustades /metadata/items ja /metadata/authors salvestatud pilte. Varukoopiad ei hõlma ühtegi teie raamatukogu kaustades olevat faili.",
"MessageBatchQuickMatchDescription": "Kiire sobitamine üritab lisada valitud üksustele puuduvad kaaned ja metaandmed. Luba allpool olevad valikud, et lubada Kiire sobitamine'il üle kirjutada olemasolevaid kaasi ja/või metaandmeid.",
- "MessageBookshelfNoCollections": "Te pole veel ühtegi kogumit teinud",
+ "MessageBookshelfNoCollections": "Te pole veel ühtegi kollektsiooni teinud",
+ "MessageBookshelfNoCollectionsHelp": "Kollektsioonid on avalikud. Kõik kasutajad kellel on olemas ligipääs raamatukogule saavad neid näha.",
"MessageBookshelfNoRSSFeeds": "Ühtegi RSS-i voogu pole avatud",
"MessageBookshelfNoResultsForFilter": "Filtrile \"{0}: {1}\" pole tulemusi",
"MessageBookshelfNoSeries": "Teil pole ühtegi seeriat",
@@ -600,6 +601,7 @@
"MessageConfirmRenameTagMergeNote": "Märkus: See silt on juba olemas, nii et need ühendatakse.",
"MessageConfirmRenameTagWarning": "Hoiatus! Sarnane silt erineva puhvriga on juba olemas \"{0}\".",
"MessageConfirmSendEbookToDevice": "Olete kindel, et soovite saata {0} e-raamatu \"{1}\" seadmesse \"{2}\"?",
+ "MessageDaysListenedInTheLastYear": "{0} päeva kuuldud viimase aasta jooksul",
"MessageDownloadingEpisode": "Episoodi allalaadimine",
"MessageDragFilesIntoTrackOrder": "Lohistage failid õigesse järjekorda",
"MessageEmbedFinished": "Manustamine lõpetatud!",
diff --git a/client/strings/fi.json b/client/strings/fi.json
index 8d224d81c..dde7009ea 100644
--- a/client/strings/fi.json
+++ b/client/strings/fi.json
@@ -182,6 +182,7 @@
"HeaderPlaylist": "Soittolista",
"HeaderPlaylistItems": "Soittolistan kohteet",
"HeaderPodcastsToAdd": "Lisättävät podcastit",
+ "HeaderPresets": "Esivalinnat",
"HeaderPreviewCover": "Esikatsele kansikuvaa",
"HeaderRSSFeedGeneral": "RSS yksityiskohdat",
"HeaderRSSFeedIsOpen": "RSS syöte on avoinna",
@@ -221,7 +222,7 @@
"LabelAbridged": "Lyhennetty",
"LabelAbridgedChecked": "Lyhennetty (tarkistettu)",
"LabelAbridgedUnchecked": "Lyhentämätön (tarkistamaton)",
- "LabelAccessibleBy": "Saavutettavissa:",
+ "LabelAccessibleBy": "Saavutettavissa",
"LabelAccountType": "Tilin tyyppi",
"LabelAccountTypeAdmin": "Järjestelmänvalvoja",
"LabelAccountTypeGuest": "Vieras",
@@ -243,6 +244,8 @@
"LabelAlreadyInYourLibrary": "Jo kirjastossasi",
"LabelApiKeyCreated": "API avain \"{0}\" luotu onnistuneesti.",
"LabelApiKeyCreatedDescription": "Varmista, että kopioit API avaimen. Sitä ei näytetä enää tämän jälkeen.",
+ "LabelApiKeyUser": "Toimi käyttäjän puolesta",
+ "LabelApiKeyUserDescription": "Tällä API-avaimella on samat käyttöoikeudet kuin käyttäjällä, jonka puolesta se toimii. Tämä näkyy lokeissa samalla tavalla kuin jos käyttäjä itse tekisi pyynnön.",
"LabelApiToken": "Sovellusliittymätunnus",
"LabelAppend": "Lisää loppuun",
"LabelAudioBitrate": "Äänen bittinopeus (esim. 128k)",
@@ -272,7 +275,7 @@
"LabelBonus": "Bonus",
"LabelBooks": "Kirjat",
"LabelButtonText": "Painikkeen teksti",
- "LabelByAuthor": "tekijältä {0}",
+ "LabelByAuthor": "Tekijältä: {0}",
"LabelChangePassword": "Vaihda salasana",
"LabelChannels": "Kanavat",
"LabelChapterCount": "{0} lukua",
@@ -306,6 +309,7 @@
"LabelDeleteFromFileSystemCheckbox": "Poista tiedostojärjestelmästä (poista merkintä, jos haluat poistaa vain tietokannasta)",
"LabelDescription": "Kuvaus",
"LabelDeselectAll": "Poista valinta kaikista",
+ "LabelDetectedPattern": "Tunnista malli:",
"LabelDevice": "Laite",
"LabelDeviceInfo": "Laitteen tiedot",
"LabelDeviceIsAvailableTo": "Laite on saatavilla...",
@@ -355,6 +359,10 @@
"LabelExample": "Esimerkki",
"LabelExpandSeries": "Laajenna sarja",
"LabelExpandSubSeries": "Laajenna alisarja",
+ "LabelExpired": "Vanhentunut",
+ "LabelExpiresAt": "Vanhentuu",
+ "LabelExpiresInSeconds": "Vanhentuu (sekunnissa)",
+ "LabelExpiresNever": "Ei koskaan",
"LabelExplicit": "Sopimaton",
"LabelExplicitChecked": "Yksiselitteinen (valittu)",
"LabelExplicitUnchecked": "Ei yksiselitteinen (ei valittu)",
@@ -370,11 +378,12 @@
"LabelFilterByUser": "Suodata käyttäjien perusteella",
"LabelFindEpisodes": "Etsi jaksoja",
"LabelFinished": "Valmis",
+ "LabelFinishedDate": "Valmis {0}",
"LabelFolder": "Kansio",
"LabelFolders": "Kansiot",
"LabelFontBold": "Lihavoitu",
"LabelFontBoldness": "Kirjasintyyppien lihavointi",
- "LabelFontFamily": "Kirjasinperhe",
+ "LabelFontFamily": "Fonttiperhe",
"LabelFontItalic": "Kursiivi",
"LabelFontScale": "Kirjasintyyppien skaalautuminen",
"LabelFontStrikethrough": "Yliviivattu",
@@ -414,6 +423,7 @@
"LabelLanguages": "Kielet",
"LabelLastBookAdded": "Viimeisin lisätty kirja",
"LabelLastBookUpdated": "Viimeisin päivitetty kirja",
+ "LabelLastProgressDate": "Viimeisin edistyminen {0}",
"LabelLastSeen": "Nähty viimeksi",
"LabelLastTime": "Viimeinen kerta",
"LabelLastUpdate": "Viimeisin päivitys",
@@ -426,6 +436,9 @@
"LabelLibraryFilterSublistEmpty": "Ei {0}",
"LabelLibraryItem": "Kirjaston kohde",
"LabelLibraryName": "Kirjaston nimi",
+ "LabelLibrarySortByProgress": "Edistyminen: Viimeksi päivitetty",
+ "LabelLibrarySortByProgressFinished": "Edistyminen: Valmis",
+ "LabelLibrarySortByProgressStarted": "Edistyminen: Aloitettu",
"LabelLimit": "Raja",
"LabelLineSpacing": "Riviväli",
"LabelListenAgain": "Kuuntele uudelleen",
@@ -434,6 +447,7 @@
"LabelLogLevelWarn": "Varoitus",
"LabelLookForNewEpisodesAfterDate": "Etsi uusia jaksoja tämän päivämäärän jälkeen",
"LabelLowestPriority": "Vähiten tärkeä",
+ "LabelMatchConfidence": "Varmuus",
"LabelMatchExistingUsersBy": "Vastaa olemassa olevia käyttäjiä mukaan",
"LabelMatchExistingUsersByDescription": "Käytetään olemassa olevien käyttäjien yhdistämiseen. Kun yhteys on muodostettu, käyttäjät saavat yksilöllisen tunnuksen SSO-palveluntarjoajaltasi",
"LabelMaxEpisodesToDownload": "Jaksojen maksimilatausmäärä. 0 poistaa rajoituksen.",
@@ -463,7 +477,9 @@
"LabelNewestAuthors": "Uusimmat tekijät",
"LabelNewestEpisodes": "Uusimmat jaksot",
"LabelNextBackupDate": "Seuraava varmuuskopiointipäivämäärä",
+ "LabelNextChapters": "Seuraavat luvut:",
"LabelNextScheduledRun": "Seuraava ajastettu suorittaminen",
+ "LabelNoApiKeys": "Ei API-avaimia",
"LabelNoCustomMetadataProviders": "Ei mukautettuja kuvailutietojen toimittajia",
"LabelNoEpisodesSelected": "Jaksoja ei ole valittu",
"LabelNotFinished": "Ei valmis",
@@ -479,6 +495,7 @@
"LabelNotificationsMaxQueueSize": "Ilmoitustapahtumajonon enimmäispituus",
"LabelNotificationsMaxQueueSizeHelp": "Tapahtumat on rajoitettu ampumaan yksi sekunnissa. Tapahtumat ohitetaan, jos jono on enimmäiskoko. Tämä estää ilmoitusten roskapostin.",
"LabelNumberOfBooks": "Kirjojen määrä",
+ "LabelNumberOfChapters": "Lukujen lukumäärä:",
"LabelNumberOfEpisodes": "# jaksoja",
"LabelOpenIDAdvancedPermsClaimDescription": "OpenID-vaatimuksen nimi, joka sisältää lisäoikeudet sovelluksen käyttäjän toimiin, joita sovelletaan muihin kuin järjestelmänvalvojan rooleihin (jos määritetty). Jos vaatimus puuttuu vastauksesta, pääsy ABS:iin evätään. Jos yksittäinen vaihtoehto puuttuu, sitä käsitellään false-arvona. Varmista, että identiteetin tarjoajan vaatimus vastaa odotettua rakennetta:",
"LabelOpenIDClaims": "Jätä seuraavat vaihtoehdot tyhjiksi, jos haluat poistaa edistyneen ryhmän ja lupien määrityksen käytöstä ja määrittää sitten automaattisesti käyttäjäryhmän.",
@@ -523,7 +540,7 @@
"LabelPublishers": "Julkaisijat",
"LabelRSSFeedCustomOwnerEmail": "Mukautettu omistajan sähköposti",
"LabelRSSFeedCustomOwnerName": "Mukautettu omistajan nimi",
- "LabelRSSFeedOpen": "RSS-syöte avoin",
+ "LabelRSSFeedOpen": "RSS Syöte Avoin",
"LabelRSSFeedPreventIndexing": "Estä indeksointi",
"LabelRSSFeedSlug": "RSS-syöte Slug",
"LabelRSSFeedURL": "RSS-syötteen URL-osoite",
@@ -533,7 +550,7 @@
"LabelReadAgain": "Lue uudelleen",
"LabelReadEbookWithoutProgress": "Lue s-kirja tallentamatta edistymistietoja",
"LabelRecentSeries": "Viimeisimmät sarjat",
- "LabelRecentlyAdded": "Viimeeksi lisätyt",
+ "LabelRecentlyAdded": "Viimeksi lisätyt",
"LabelRecommended": "Suositeltu",
"LabelRedo": "Tee uudelleen",
"LabelRegion": "Alue",
@@ -553,6 +570,7 @@
"LabelSelectAll": "Valitse kaikki",
"LabelSelectAllEpisodes": "Valitse kaikki jaksot",
"LabelSelectEpisodesShowing": "Valitse {0} näytettävää jaksoa",
+ "LabelSelectUser": "Valitse käyttäjä",
"LabelSelectUsers": "Valitse käyttäjät",
"LabelSendEbookToDevice": "Lähetä s-kirja kohteeseen...",
"LabelSequence": "Sekvenssi",
@@ -570,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Skeuomorfinen muotoilu puisilla hyllyillä",
"LabelSettingsChromecastSupport": "Chromecast-tuki",
"LabelSettingsDateFormat": "Päivämäärän muoto",
- "LabelSettingsEnableWatcher": "Skannaa kirjastot automaattisesti muutoksien varalta",
- "LabelSettingsEnableWatcherForLibrary": "Skannaa kirjastot automaattisesti muutoksien varalta",
+ "LabelSettingsEnableWatcher": "Vahdi kirjastoja automaattisesti muutoksien varalta",
+ "LabelSettingsEnableWatcherForLibrary": "Vahdi kirjastoja automaattisesti muutoksien varalta",
"LabelSettingsEnableWatcherHelp": "Ottaa käyttöön kohteiden automaattisen lisäämisen ja päivityksen kun tiedostomuutoksia havaitaan. *Tarvitsee palvelimen uudelleenkäynnistyksen",
"LabelSettingsEpubsAllowScriptedContent": "Salli komentosarjamuotoinen sisältö epubissa",
"LabelSettingsEpubsAllowScriptedContentHelp": "Salli epub-tiedostojen suorittaa komentosarjoja. On suositeltavaa pitää tämä asetus pois käytöstä, ellet luota epub-tiedostojen lähteeseen.",
@@ -620,6 +638,7 @@
"LabelStartTime": "Aloitusaika",
"LabelStarted": "Aloitettu",
"LabelStartedAt": "Aloitettu",
+ "LabelStartedDate": "Aloitettu {0}",
"LabelStatsAudioTracks": "Ääniraidat",
"LabelStatsAuthors": "Tekijät",
"LabelStatsBestDay": "Paras päivä",
@@ -649,6 +668,7 @@
"LabelTheme": "Teema",
"LabelThemeDark": "Tumma",
"LabelThemeLight": "Kirkas",
+ "LabelThemeSepia": "Seepia",
"LabelTimeBase": "Aika-alusta",
"LabelTimeDurationXHours": "{0} tuntia",
"LabelTimeDurationXMinutes": "{0} minuuttia",
@@ -716,6 +736,10 @@
"LabelYourProgress": "Edistymisesi",
"MessageAddToPlayerQueue": "Lisää soittimen jonoon",
"MessageAppriseDescription": "Käyttääksesi tätä toimintoa tarvitset Apprise API -instanssin tai rajapinnan joka käsittelee samoja pyyntöjä. http://192.168.1.1:8337,niin arvoksi tulee antaa http://192.168.1.1:8337/notify.",
+ "MessageAsinCheck": "Varmista, että käytät ASIN-tunnusta oikealta Audible-alueelta, ei Amazonista.",
+ "MessageAuthenticationLegacyTokenWarning": "Vanhat API-tunnukset poistetaan tulevaisuudessa. Käytä sen sijaan API-avaimia.",
+ "MessageAuthenticationOIDCChangesRestart": "Käynnistä palvelin uudelleen tallennuksen jälkeen ottaaksesi OIDC-muutokset käyttöön.",
+ "MessageAuthenticationSecurityMessage": "Tunnistautumisen tietoturvaa on parannettu. Kaikkien käyttäjien tulee kirjautua sisään uudelleen.",
"MessageBackupsDescription": "Varmuuskopiot sisältävät käyttäjät, käyttäjien edistymisen, kirjastokohteiden tiedot, palvelinasetukset ja /metadata/items- ja /metadata/authors -kansioihin tallennetut kuvat. Varmuuskopiot eivät sisällä kirjastosi kansioihin tallennettuja tiedostoja.",
"MessageBackupsLocationEditNote": "Huomautus: Varmuuskopion sijainnin päivittäminen ei siirrä tai muokkaa olemassa olevia varmuuskopioita",
"MessageBackupsLocationNoEditNote": "Huomautus: Varmuuskopion sijainti asetetaan ympäristömuuttujan kautta, eikä sitä voi muuttaa tässä.",
@@ -729,6 +753,7 @@
"MessageBookshelfNoResultsForFilter": "Ei tuloksia suodattimelle \"{0}: {1}\"",
"MessageBookshelfNoResultsForQuery": "Ei tuloksia kyselylle",
"MessageBookshelfNoSeries": "Sinulla ei ole sarjoja",
+ "MessageBulkChapterPattern": "Kuinka monta lukua haluaisit lisätä tällä numerointimallilla?",
"MessageChapterEndIsAfter": "Luvun loppu sijaitsee äänikirjan lopun jälkeen",
"MessageChapterErrorFirstNotZero": "Ensimmäisen luvun tulee alkaa nollasta",
"MessageChapterErrorStartGteDuration": "Epäkelvollinen aloitusaika; on oltava lyhyempi kuin äänikirjan kesto",
@@ -737,6 +762,7 @@
"MessageChaptersNotFound": "Kappaleita ei löydy",
"MessageCheckingCron": "Tarkistetaan cronia...",
"MessageConfirmCloseFeed": "Oletko varma, että haluat sulkea tämän syötteen?",
+ "MessageConfirmDeleteApiKey": "Haluatko varmasti poistaa API-avaimen \"{0}\"?",
"MessageConfirmDeleteBackup": "Oletko varma, että haluat poistaa varmuuskopion {0}:lle?",
"MessageConfirmDeleteDevice": "Oletko varma, että haluat poistaa s-lukulaitteen \"{0}\"?",
"MessageConfirmDeleteFile": "Tämä poistaa tiedoston tiedostojärjestelmästäsi. Oletko varma?",
@@ -764,6 +790,7 @@
"MessageConfirmRemoveAuthor": "Oletko varma, että haluat poistaa tekijän \"{0}\"?",
"MessageConfirmRemoveCollection": "Oletko varma, että haluat poistaa kokoelman \"{0}\"?",
"MessageConfirmRemoveEpisode": "Oletko varma, että haluat poistaa jakson \"{0}\"?",
+ "MessageConfirmRemoveEpisodeNote": "Huomioi: Tämä ei poista äänitiedostoa, ellei \"Poista tiedosto pysyvästi\" -asetusta ole valittuna",
"MessageConfirmRemoveEpisodes": "Oletko varma, että haluat poistaa {0} jaksoa?",
"MessageConfirmRemoveListeningSessions": "Oletko varma, että haluat poistaa {0} kuuntelukertaa?",
"MessageConfirmRemoveMetadataFiles": "Oletko varma, että haluat poistaa kaikki metadata.{0}-tiedostot kirjaston kohdekansioista?",
@@ -789,6 +816,8 @@
"MessageFeedURLWillBe": "Syötteen URL tulee olemaan {0}",
"MessageFetching": "Haetaan...",
"MessageForceReScanDescription": "skannaa kaikki tiedostot uudelleen kuten uusi tarkistus. Äänitiedoston ID3-tunnisteet, OPF-tiedostot ja tekstitiedostot skannataan uusina.",
+ "MessageHeatmapListeningTimeTooltip": "{0} kuunnellaan on {1}",
+ "MessageHeatmapNoListeningSessions": "Ei kuuntelujaksoja {0}",
"MessageImportantNotice": "Tärkeä huomautus!",
"MessageInsertChapterBelow": "Syötä luku alle",
"MessageInvalidAsin": "Virheellinen ASIN",
@@ -859,10 +888,11 @@
"MessageResetChaptersConfirm": "Oletko varma, että haluat nollata luvut ja kumota tekemäsi muutokset?",
"MessageRestoreBackupConfirm": "Oletko varma, että haluat palauttaa varmuuskopion, joka on luotu",
"MessageRestoreBackupWarning": "Varmuuskopion palauttaminen korvaa koko /config:ssa sijaitsevan tietokannan, ja kansikuvat /metadata/items & /metadata/authors:ssa.false יש לוודא שטענת ספק הזהויות תואמת את המבנה הצפוי:",
"LabelOpenIDClaims": "השאר את האפשרויות הבאות ריקות כדי להשבית הקצאת קבוצות והרשאות מתקדמת, ולאחר מכן להקצות אוטומטית את קבוצת 'משתמש'.",
+ "LabelOpenIDGroupClaimDescription": "שם ה־OpenID claim המכיל את רשימת הקבוצות של המשתמש. בדרך כלל נקרא groups. אם הוגדרה, האפליקציה תקצה תפקידים באופן אוטומטי על סמך השיוך לקבוצות, בתנאי ששמות הקבוצות ב־claim הם 'admin', 'user' או 'guest' (ללא רגישות לרישיות - Case-insensitive). ה־claim צריך להכיל רשימה; אם המשתמש משויך למספר קבוצות, האפליקציה תקצה את התפקיד בעל רמת הגישה הגבוהה ביותר. במידה ולא נמצאה קבוצה תואמת, הגישה תיחסם.",
"LabelOpenRSSFeed": "פתח ערוץ RSS",
"LabelOverwrite": "לשכפל",
+ "LabelPaginationPageXOfY": "עמוד {0} מתוך {1}",
"LabelPassword": "סיסמה",
"LabelPath": "נתיב",
+ "LabelPermanent": "קבוע",
"LabelPermissionsAccessAllLibraries": "ניתן לגשת לכל הספריות",
"LabelPermissionsAccessAllTags": "ניתן לגשת לכל התגיות",
"LabelPermissionsAccessExplicitContent": "ניתן לגשת לתוכן בוטה",
+ "LabelPermissionsCreateEreader": "ניתן ליצור קורא ספרים דיגיטלי",
"LabelPermissionsDelete": "מותר למחוק",
"LabelPermissionsDownload": "מותר להוריד",
"LabelPermissionsUpdate": "מותר לעדכן",
@@ -489,6 +517,8 @@
"LabelPersonalYearReview": "השנה שלך בסקירה ({0})",
"LabelPhotoPathURL": "נתיב/URL לתמונה",
"LabelPlayMethod": "שיטת הפעלה",
+ "LabelPlaybackRateIncrementDecrement": "שיעור הגדלה/הפחתה של מהירות ההשמעה",
+ "LabelPlayerChapterNumberMarker": "{0} מתוך {1}",
"LabelPlaylists": "רשימות השמעה",
"LabelPodcast": "פודקאסט",
"LabelPodcastSearchRegion": "אזור חיפוש פודקאסט",
@@ -500,10 +530,14 @@
"LabelPrimaryEbook": "ספר אלקטרוני ראשי",
"LabelProgress": "התקדמות",
"LabelProvider": "ספק",
+ "LabelProviderAuthorizationValue": "ערך כותרת האימות (Authorization Header)",
"LabelPubDate": "תאריך פרסום",
"LabelPublishYear": "שנת הפרסום",
"LabelPublishedDate": "פורסם {0}",
+ "LabelPublishedDecade": "עשור פרסום",
+ "LabelPublishedDecades": "עשורי פרסום",
"LabelPublisher": "מוציא לאור",
+ "LabelPublishers": "מוצאים לאור",
"LabelRSSFeedCustomOwnerEmail": "אימייל בעלים מותאם אישית",
"LabelRSSFeedCustomOwnerName": "שם בעלים מותאם אישית",
"LabelRSSFeedOpen": "ערוץ RSS פתוח",
@@ -511,6 +545,7 @@
"LabelRSSFeedSlug": "Slug של ערוץ ה-RSS",
"LabelRSSFeedURL": "כתובת ערוץ ה-RSS",
"LabelRandomly": "באופן אקראי",
+ "LabelReAddSeriesToContinueListening": "הוסף סדרה בחזרה אל ״המשך האזנה״",
"LabelRead": "קריאה",
"LabelReadAgain": "קרא שוב",
"LabelReadEbookWithoutProgress": "קרא/י ספר אלקטרוני ללא שמירת התקדמות",
@@ -520,29 +555,44 @@
"LabelRedo": "עשה שוב",
"LabelRegion": "אזור",
"LabelReleaseDate": "תאריך הוצאה לאור",
+ "LabelRemoveAllMetadataAbs": "הסר את כל קבצי metadata.abs",
+ "LabelRemoveAllMetadataJson": "הסר את כל קבצי metadata.json",
+ "LabelRemoveAudibleBranding": "הסר פתיח וסיום של Audible מהפרקים",
"LabelRemoveCover": "הסר כריכה",
+ "LabelRemoveMetadataFile": "הסר קבצי מטא־נתונים מתיקיות הפריטים בספרייה",
+ "LabelRemoveMetadataFileHelp": "הסר את כל קבצי metadata.json ו־metadata.abs מתיקיות {0}.",
"LabelRowsPerPage": "שורות לעמוד",
"LabelSearchTerm": "מונח חיפוש",
"LabelSearchTitle": "כותרת חיפוש",
"LabelSearchTitleOrASIN": "כותרת חיפוש או ASIN",
"LabelSeason": "עונה",
+ "LabelSeasonNumber": "עונה #{0}",
+ "LabelSelectAll": "בחר הכל",
"LabelSelectAllEpisodes": "בחר את כל הפרקים",
"LabelSelectEpisodesShowing": "בחר {0} פרקים המוצגים",
+ "LabelSelectUser": "בחר משתמש",
"LabelSelectUsers": "בחר משתמשים",
"LabelSendEbookToDevice": "שלח ספר אלקטרוני ל...",
"LabelSequence": "רצף",
+ "LabelSerial": "מספר סידורי",
"LabelSeries": "סדרה",
"LabelSeriesName": "שם הסדרה",
"LabelSeriesProgress": "התקדמות בסדרה",
+ "LabelServerLogLevel": "רמת פירוט יומני הרישום",
"LabelServerYearReview": "השנה בסקירה של השרת ({0})",
"LabelSetEbookAsPrimary": "קבע כראשי",
"LabelSetEbookAsSupplementary": "קבע כמשלים",
+ "LabelSettingsAllowIframe": "אפשר הטמעה בתוך iframe",
"LabelSettingsAudiobooksOnly": "רק ספרי קול",
"LabelSettingsAudiobooksOnlyHelp": "הפעלת ההגדרה הזו תתעלם מקבצי ספרים אלקטרוניים אלא אם כן הם נמצאים בתיקיית ספרי קול, שבמקרה זה יקבעו כספרים אלקטרוניים נלווים",
"LabelSettingsBookshelfViewHelp": "עיצוב סקאומורפי עם מדפי עץ",
"LabelSettingsChromecastSupport": "תמיכה ב-Chromecast",
"LabelSettingsDateFormat": "פורמט תאריך",
+ "LabelSettingsEnableWatcher": "הפעל מעקב שינויים בספריות",
+ "LabelSettingsEnableWatcherForLibrary": "הפעל מעקב שינויים בספרייה",
"LabelSettingsEnableWatcherHelp": "מאפשר הוספת/עדכון אוטומטי של פריטים כאשר שינויי קבצים זוהים. *דורש איתחול שרת",
+ "LabelSettingsEpubsAllowScriptedContent": "אפשור תוכן הכולל סקריפטים ב־ePubs",
+ "LabelSettingsEpubsAllowScriptedContentHelp": "אפשר לקובצי EPUB להריץ סקריפטים. מומלץ להשאיר את ההגדרה כבויה, אלא אם כן מקור קובצי ה־ePub מהימן.",
"LabelSettingsExperimentalFeatures": "תכונות ניסיוניות",
"LabelSettingsExperimentalFeaturesHelp": "תכונות בפיתוח שדורשות משובך ובדיקה. לחץ לפתיחת דיון ב-GitHub.",
"LabelSettingsFindCovers": "מצא כריכות",
@@ -551,7 +601,8 @@
"LabelSettingsHideSingleBookSeriesHelp": "סדרות הכוללות ספר אחד יוסתרו מדף הסדרות ומדף הבית.",
"LabelSettingsHomePageBookshelfView": "השתמש בתצוגת מדף בדף הבית",
"LabelSettingsLibraryBookshelfView": "השתמש בתצוגת מדף בספרייה",
- "LabelSettingsOnlyShowLaterBooksInContinueSeries": "דלג על ספרים קודמים ב-המשך סדרה",
+ "LabelSettingsLibraryMarkAsFinishedWhen": "סמן פריט מדיה כהושלם כאשר",
+ "LabelSettingsOnlyShowLaterBooksInContinueSeries": "דלג על ספרים קודמים ב״המשך סדרה״",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "מדף המשך סדרות מציג את הספר הראשון שלא הושמע בסדרה שיש בה לפחות ספר אחד שהושלם ואין ספרים שכבר באמצע שמיעה. הפעלת הגדרה זו תמשיך סדרות מהספר שהושלם הכי מתקדם בסדרה במקום מהספר הראשון שלא הושמע.",
"LabelSettingsParseSubtitles": "פענח כתוביות",
"LabelSettingsParseSubtitlesHelp": "העתק כותרת משנה משם תיקיית הספר.http://192.168.1.1:8337 אז עליך לשים http://192.168.1.1:8337/notify.",
+ "MessageAsinCheck": "יש לוודא שימוש ב־ASIN מאזור ה־Audible הנכון, ולא מ־Amazon.",
+ "MessageAuthenticationLegacyTokenWarning": "אסימוני API ישנים יוסרו בעתיד. יש להשתמש ב מפתחות API במקום.",
+ "MessageAuthenticationOIDCChangesRestart": "יש להפעיל מחדש את השרת לאחר השמירה כדי להחיל את שינויי ה־OIDC.",
+ "MessageAuthenticationSecurityMessage": "האימות שופר מטעמי אבטחה. כל המשתמשים נדרשים להתחבר מחדש.",
"MessageBackupsDescription": "גיבויים כוללים משתמשים, התקדמות משתמש, פרטי פריטי ספרייה, הגדרות שרת ותמונות השמורות ב-/metadata/items & /metadata/authors. גיבויים לא כוללים קבצים שמורים בתיקיות הספרייה שלך.",
+ "MessageBackupsLocationEditNote": "הערה: שינוי מיקום הגיבוי לא יגרום להעברה או לשינוי של גיבויים קיימים",
+ "MessageBackupsLocationNoEditNote": "הערה: מיקום הגיבוי מוגדר באמצעות משתנה סביבה ולא ניתן לשנותו כאן.",
+ "MessageBackupsLocationPathEmpty": "נתיב מיקום הגיבוי אינו יכול להיות ריק",
+ "MessageBatchEditPopulateMapDetailsAllHelp": "מלא את השדות הפעילים בנתונים מכל הפריטים. שדות בעלי ערכים מרובים ימוזגו",
+ "MessageBatchEditPopulateMapDetailsItemHelp": "מלא את שדות פרטי המיפוי הפעילים בנתונים מפריט זה",
"MessageBatchQuickMatchDescription": "התאמה מהירה תנסה להוסיף כריכות ומטה-נתונים חסרים עבור הפריטים הנבחרים. הפעל את האפשרויות למטה כדי לאפשר להתאמה מהירה להחליף כריכות קיימות ו/או מטה-נתונים.",
"MessageBookshelfNoCollections": "עדיין לא יצרת אוספים",
+ "MessageBookshelfNoCollectionsHelp": "האוספים ציבוריים. כל המשתמשים בעלי גישה לספרייה יכולים לראות אותם.",
"MessageBookshelfNoRSSFeeds": "אין ערוצי RSS פתוחים",
"MessageBookshelfNoResultsForFilter": "אין תוצאות עבור סינון \"{0}: {1}\"",
+ "MessageBookshelfNoResultsForQuery": "אין תוצאות עבור השאילתה",
"MessageBookshelfNoSeries": "אין לך סדרות",
+ "MessageBulkChapterPattern": "כמה פרקים להוסיף לפי תבנית מספור זו?",
"MessageChapterEndIsAfter": "זמן סיום הפרק אחרי סיום הספר הקולי שלך",
"MessageChapterErrorFirstNotZero": "הפרק הראשון חייב להתחיל ב-0",
"MessageChapterErrorStartGteDuration": "זמן התחלה לא תקין, חייב להיות פחות ממשך הספר הקולי",
"MessageChapterErrorStartLtPrev": "זמן התחלה לא תקין, חייב להיות גדול או שווה לזמן ההתחלה של הפרק הקודם",
"MessageChapterStartIsAfter": "התחלת הפרק אחרי סיום הספר הקולי שלך",
+ "MessageChaptersNotFound": "לא נמצאו פרקים",
"MessageCheckingCron": "בודק את תזמון העבודה...",
"MessageConfirmCloseFeed": "האם אתה בטוח שאתה רוצה לסגור את הערוץ הזה?",
+ "MessageConfirmDeleteApiKey": "האם למחוק את מפתח ה־API \"{0}\"?",
"MessageConfirmDeleteBackup": "האם אתה בטוח שברצונך למחוק גיבוי עבור {0}?",
+ "MessageConfirmDeleteDevice": "האם למחוק את הקורא האלקטרוני \"{0}\"?",
"MessageConfirmDeleteFile": "הקובץ ימחק לצמיתות מהמערכת שלך. האם אתה בטוח?",
"MessageConfirmDeleteLibrary": "האם אתה בטוח שברצונך למחוק לצמיתות את הספרייה \"{0}\"?",
"MessageConfirmDeleteLibraryItem": "פריט הספרייה יימחק לצמיתות ממסד הנתונים ומהמערכת שלך. האם אתה בטוח?",
"MessageConfirmDeleteLibraryItems": "פריטי הספרייה {0} יימחקו ממסד הנתונים ומהמערכת שלך. האם אתה בטוח?",
+ "MessageConfirmDeleteMetadataProvider": "האם למחוק את ספק המטא־נתונים המותאם \"{0}\"?",
+ "MessageConfirmDeleteNotification": "האם למחוק התראה זו?",
"MessageConfirmDeleteSession": "האם אתה בטוח שאתה רוצה למחוק את ההפעלה הזו?",
+ "MessageConfirmEmbedMetadataInAudioFiles": "האם להטמיע מטא־נתונים ב־{0} קובצי שמע?",
"MessageConfirmForceReScan": "האם אתה בטוח שאתה רוצה להכריח סריקה מחדש?",
"MessageConfirmMarkAllEpisodesFinished": "האם אתה בטוח שברצונך לסמן את כל הפרקים כהסתיימו?",
"MessageConfirmMarkAllEpisodesNotFinished": "האם אתה בטוח שברצונך לסמן את כל הפרקים כלא הסתיימו?",
+ "MessageConfirmMarkItemFinished": "האם לסמן את \"{0}\" כהושלם?",
+ "MessageConfirmMarkItemNotFinished": "האם לסמן את \"{0}\" כלא הושלם?",
"MessageConfirmMarkSeriesFinished": "האם אתה בטוח שברצונך לסמן את כל הספרים בסדרה זו כהסתיימו?",
"MessageConfirmMarkSeriesNotFinished": "האם אתה בטוח שברצונך לסמן את כל הספרים בסדרה זו כלא הסתיימו?",
+ "MessageConfirmNotificationTestTrigger": "האם להפעיל התראה זו עם נתוני בדיקה?",
+ "MessageConfirmPurgeCache": "ניקוי המטמון ימחק את כל התיקייה ב־/metadata/cache.metadata/cache/items/./login?autoLaunch=0)",
"LabelAutoRegister": "Automatska registracija",
@@ -286,8 +286,8 @@
"LabelClickToUseCurrentValue": "Kliknite za trenutnu vrijednost",
"LabelClosePlayer": "Zatvori reproduktor",
"LabelCodec": "Kodek",
- "LabelCollapseSeries": "Serijale prikaži sažeto",
- "LabelCollapseSubSeries": "Podserijale prikaži sažeto",
+ "LabelCollapseSeries": "Sažmi serijal",
+ "LabelCollapseSubSeries": "Sažmi podserijale",
"LabelCollection": "Zbirka",
"LabelCollections": "Zbirke",
"LabelComplete": "Potpuno",
@@ -303,7 +303,7 @@
"LabelCronExpression": "Cron izraz",
"LabelCurrent": "Trenutan",
"LabelCurrently": "Trenutno:",
- "LabelCustomCronExpression": "Prilagođeni CRON izraz:",
+ "LabelCustomCronExpression": "Prilagođeni cron izraz:",
"LabelDatetime": "Datum i vrijeme",
"LabelDays": "Dani",
"LabelDeleteFromFileSystemCheckbox": "Izbriši datoteke (uklonite kvačicu ako stavku želite izbrisati samo iz baze podataka)",
@@ -312,7 +312,7 @@
"LabelDetectedPattern": "Prepoznat obrazac:",
"LabelDevice": "Uređaj",
"LabelDeviceInfo": "O uređaju",
- "LabelDeviceIsAvailableTo": "Uređaj je dostupan...",
+ "LabelDeviceIsAvailableTo": "Uređaj je dostupan za...",
"LabelDirectory": "Direktorij",
"LabelDiscFromFilename": "Disk iz imena datoteke",
"LabelDiscFromMetadata": "Disk iz metapodataka",
@@ -340,7 +340,7 @@
"LabelEncodingBackupLocation": "Sigurnosna kopija vaših izvornih zvučnih datoteka čuvat će se u mapi:",
"LabelEncodingChaptersNotEmbedded": "Poglavlja se ne ugrađuju u zvučne knjige koje se sastoje od više zvučnih zapisa.",
"LabelEncodingClearItemCache": "Svakako redovito praznite predmemoriju stavki.",
- "LabelEncodingFinishedM4B": "Stvorene M4B datoteke spremit će se u vašu mapu sa zvučnim knjigama:",
+ "LabelEncodingFinishedM4B": "Gotove M4B datoteke spremit će se u vašu mapu sa zvučnim knjigama:",
"LabelEncodingInfoEmbedded": "Meta-podatci će se ugraditi u zvučne zapise u vašoj mapi sa zvučnim knjigama.",
"LabelEncodingStartedNavigation": "Nakon pokretanja zadatka možete napustiti ovu stranicu.",
"LabelEncodingTimeWarning": "Kodiranje može potrajati do 30 minuta.",
@@ -357,8 +357,8 @@
"LabelEpisodes": "Nastavci",
"LabelEpisodic": "U nastavcima",
"LabelExample": "Primjer",
- "LabelExpandSeries": "Serijal prikaži prošireno",
- "LabelExpandSubSeries": "Podserijal prikaži prošireno",
+ "LabelExpandSeries": "Proširi serijal",
+ "LabelExpandSubSeries": "Proširi podserijal",
"LabelExpired": "Istekao",
"LabelExpiresAt": "Istječe",
"LabelExpiresInSeconds": "Istječe za (sekundi)",
@@ -374,7 +374,7 @@
"LabelFileBornDate": "Stvoreno {0}",
"LabelFileModified": "Datoteka izmijenjena",
"LabelFileModifiedDate": "Izmijenjeno {0}",
- "LabelFilename": "Naziv datoteke",
+ "LabelFilename": "Ime datoteke",
"LabelFilterByUser": "Filtriraj po korisniku",
"LabelFindEpisodes": "Pronađi nastavke",
"LabelFinished": "Završeno",
@@ -383,7 +383,7 @@
"LabelFolders": "Mape",
"LabelFontBold": "Podebljano",
"LabelFontBoldness": "Debljina slova",
- "LabelFontFamily": "Skupina fontova",
+ "LabelFontFamily": "Skup pisma",
"LabelFontItalic": "Kurziv",
"LabelFontScale": "Veličina slova",
"LabelFontStrikethrough": "Precrtano",
@@ -394,7 +394,7 @@
"LabelHardDeleteFile": "Izbriši datoteku zauvijek",
"LabelHasEbook": "Ima e-knjigu",
"LabelHasSupplementaryEbook": "Ima dopunsku e-knjigu",
- "LabelHideSubtitles": "Skrij podnaslove",
+ "LabelHideSubtitles": "Sakrij podnaslove",
"LabelHighestPriority": "Najviši prioritet",
"LabelHost": "Poslužitelj",
"LabelHour": "Sat",
@@ -402,7 +402,7 @@
"LabelIcon": "Ikona",
"LabelImageURLFromTheWeb": "URL slike s weba",
"LabelInProgress": "U tijeku",
- "LabelIncludeInTracklist": "Uključi u popisu zvučnih zapisa",
+ "LabelIncludeInTracklist": "Uključi u popis zvučnih zapisa",
"LabelIncomplete": "Nepotpuno",
"LabelInterval": "Interval",
"LabelIntervalCustomDailyWeekly": "Prilagođeno dnevno/tjedno",
@@ -436,23 +436,23 @@
"LabelLibraryFilterSublistEmpty": "Br {0}",
"LabelLibraryItem": "Stavka knjižnice",
"LabelLibraryName": "Ime knjižnice",
- "LabelLibrarySortByProgress": "Napredak: zadnje ažurirano",
- "LabelLibrarySortByProgressFinished": "Napredak: dovršeno",
- "LabelLibrarySortByProgressStarted": "Napredak: započeto",
+ "LabelLibrarySortByProgress": "Napredak: Zadnje ažuriranje",
+ "LabelLibrarySortByProgressFinished": "Napredak: Završeno",
+ "LabelLibrarySortByProgressStarted": "Napredak: Započeto",
"LabelLimit": "Ograničenje",
"LabelLineSpacing": "Razmak između redaka",
"LabelListenAgain": "Ponovno poslušaj",
"LabelLogLevelDebug": "Debug",
"LabelLogLevelInfo": "Info",
- "LabelLogLevelWarn": "Warn",
+ "LabelLogLevelWarn": "Upozorenje",
"LabelLookForNewEpisodesAfterDate": "Traži nove nastavke nakon ovog datuma",
"LabelLowestPriority": "Najniži prioritet",
"LabelMatchConfidence": "Pouzdanost",
"LabelMatchExistingUsersBy": "Prepoznaj postojeće korisnike pomoću",
"LabelMatchExistingUsersByDescription": "Rabi se za povezivanje postojećih korisnika. Nakon što se spoje, korisnike se prepoznaje temeljem jedinstvene oznake vašeg pružatelja SSO usluga",
"LabelMaxEpisodesToDownload": "Najveći broj nastavaka za preuzimanje. 0 za neograničeno.",
- "LabelMaxEpisodesToDownloadPerCheck": "Najviše novih nastavaka za preuzimanje po provjeri",
- "LabelMaxEpisodesToKeep": "Najviše nastavaka za čuvanje",
+ "LabelMaxEpisodesToDownloadPerCheck": "Najveći broj novih nastavaka za preuzimanje po provjeri",
+ "LabelMaxEpisodesToKeep": "Najveći # nastavaka za čuvanje",
"LabelMaxEpisodesToKeepHelp": "Ako je vrijednost 0, nema ograničenja broja. Nakon automatskog preuzimanja novog nastavka ova funkcija briše najstariji nastavak ako ih ima više od zadanog broja. Ovo briše samo jedan nastavak po novom preuzetom nastavku.",
"LabelMediaPlayer": "Reproduktor medijskih sadržaja",
"LabelMediaType": "Vrsta medija",
@@ -482,7 +482,7 @@
"LabelNoApiKeys": "Nema API ključeva",
"LabelNoCustomMetadataProviders": "Nema prilagođenih pružatelja meta-podataka",
"LabelNoEpisodesSelected": "Nema odabranih nastavaka",
- "LabelNotFinished": "Nije dovršeno",
+ "LabelNotFinished": "Nezavršeno",
"LabelNotStarted": "Nije započeto",
"LabelNotes": "Bilješke",
"LabelNotificationAppriseURL": "Apprise URL(ovi)",
@@ -497,9 +497,9 @@
"LabelNumberOfBooks": "Broj knjiga",
"LabelNumberOfChapters": "Broj poglavljâ:",
"LabelNumberOfEpisodes": "broj nastavaka",
- "LabelOpenIDAdvancedPermsClaimDescription": "Naziv OpenID zahtjeva koji sadrži napredna dopuštenja za korisničke radnje u aplikaciji koje će se primijeniti na ne-administratorske uloge (ako su konfigurirane). Ako zahtjev nedostaje u odgovoru, pristup ABS-u neće se odobriti. Ako i jedna opcija nedostaje, smatrat će se da je false. Pripazite da zahtjev pružatelja identiteta uvijek odgovara očekivanoj strukturi:",
+ "LabelOpenIDAdvancedPermsClaimDescription": "Ime OpenID zahtjeva koji sadrži napredna dopuštenja za korisničke radnje u aplikaciji koje će se primijeniti na ne-administratorske uloge (ako su konfigurirane). Ako zahtjev nedostaje u odgovoru, pristup ABS-u će se odbiti. Ako jedna opcija nedostaje, smatrat će se da je false. Pripazite da zahtjev pružatelja identiteta uvijek odgovara očekivanoj strukturi:",
"LabelOpenIDClaims": "Sljedeće opcije ostavite praznima ako želite onemogućiti napredno dodjeljivanje grupa i dozvola, odnosno ako želite automatski dodijeliti grupu 'korisnik'.",
- "LabelOpenIDGroupClaimDescription": "Naziv OpenID zahtjeva koji sadrži popis korisnikovih grupa. Često se naziva groups. Ako se konfigurira, aplikacija će automatski dodijeliti uloge temeljem korisnikovih članstava u grupama, pod uvjetom da se iste zovu 'admin', 'user' ili 'guest' u zahtjevu (ne razlikuju se velika i mala slova). Zahtjev treba sadržavati popis i ako je korisnik član više grupa, aplikacija će dodijeliti ulogu koja odgovara najvišoj razini pristupa. Ukoliko se niti jedna grupa ne podudara, pristup će biti onemogućen.",
+ "LabelOpenIDGroupClaimDescription": "Ime OpenID zahtjeva koji sadrži popis grupa korisnika. Često se zove groups. Ako se konfigurira, aplikacija će automatski dodijeliti uloge temeljem korisničkih članstava u grupama, pod uvjetom da se iste zovu 'admin', 'user' ili 'guest' u zahtjevu (ne razlikuju se velika i mala slova). Zahtjev treba sadržavati popis i ako je korisnik član više grupa, aplikacija će dodijeliti ulogu koja odgovara najvišoj razini pristupa. Ukoliko se niti jedna grupa ne podudara, pristup će se odbiti.",
"LabelOpenRSSFeed": "Otvori RSS Feed",
"LabelOverwrite": "Prepiši",
"LabelPaginationPageXOfY": "Stranica {0} od {1}",
@@ -513,7 +513,7 @@
"LabelPermissionsDelete": "Smije brisati",
"LabelPermissionsDownload": "Smije preuzimati",
"LabelPermissionsUpdate": "Smije ažurirati",
- "LabelPermissionsUpload": "Smije učitavati",
+ "LabelPermissionsUpload": "Smije prenositi",
"LabelPersonalYearReview": "Vaš godišnji pregled ({0})",
"LabelPhotoPathURL": "Putanja ili URL fotografije",
"LabelPlayMethod": "Način reprodukcije",
@@ -545,11 +545,11 @@
"LabelRSSFeedSlug": "Slug RSS izvora",
"LabelRSSFeedURL": "URL RSS izvora",
"LabelRandomly": "Nasumično",
- "LabelReAddSeriesToContinueListening": "Ponovno dodaj serijal u Nastavi slušati",
+ "LabelReAddSeriesToContinueListening": "Ponovo dodaj serijal u „Nastavi slušati”",
"LabelRead": "Čitaj",
"LabelReadAgain": "Ponovno čitaj",
"LabelReadEbookWithoutProgress": "Čitaj e-knjige bez praćenja napretka",
- "LabelRecentSeries": "Najnoviji serijali",
+ "LabelRecentSeries": "Nedavni serijali",
"LabelRecentlyAdded": "Nedavno dodano",
"LabelRecommended": "Preporučeno",
"LabelRedo": "Ponovi",
@@ -572,7 +572,7 @@
"LabelSelectEpisodesShowing": "Prikazujem {0} odabranih nastavaka",
"LabelSelectUser": "Odaberite korisnika",
"LabelSelectUsers": "Označi korisnike",
- "LabelSendEbookToDevice": "Pošalji e-knjigu …",
+ "LabelSendEbookToDevice": "Pošalji e-knjigu na…",
"LabelSequence": "Slijed",
"LabelSerial": "Serijal",
"LabelSeries": "Serijal",
@@ -588,36 +588,36 @@
"LabelSettingsBookshelfViewHelp": "Skeumorfni dizajn sa drvenim policama",
"LabelSettingsChromecastSupport": "Podrška za Chromecast",
"LabelSettingsDateFormat": "Format datuma",
- "LabelSettingsEnableWatcher": "Automatski pretražuj ima li promjena u knjižnicama",
- "LabelSettingsEnableWatcherForLibrary": "Automatski traži promjene u knjižnicama",
+ "LabelSettingsEnableWatcher": "Automatski prati promjene u knjižnicama",
+ "LabelSettingsEnableWatcherForLibrary": "Automatski prati promjene u knjižnici",
"LabelSettingsEnableWatcherHelp": "Omogućuje automatsko dodavanje/ažuriranje stavki kada se uoče izmjene datoteka. *Potrebno je ponovno pokretanje poslužitelja",
"LabelSettingsEpubsAllowScriptedContent": "Omogući skripte u epub datotekama",
"LabelSettingsEpubsAllowScriptedContentHelp": "Omogućuje epub datotekama izvođenje skripti. Preporučamo isključiti ovu mogućnost ukoliko nemate povjerenja u izvore epub datoteka.",
- "LabelSettingsExperimentalFeatures": "Eksperimentalne značajke",
- "LabelSettingsExperimentalFeaturesHelp": "Značajke u razvoju za koje trebamo vaše povratne informacije i pomoć u testiranju. Kliknite za otvaranje rasprave na githubu.",
+ "LabelSettingsExperimentalFeatures": "Eksperimentalne funkcije",
+ "LabelSettingsExperimentalFeaturesHelp": "Funkcije u razvoju za koje trebamo vaše povratne informacije i pomoć u testiranju. Kliknite za otvaranje rasprave na githubu.",
"LabelSettingsFindCovers": "Pronađi naslovnice",
"LabelSettingsFindCoversHelp": "Ako vaša zvučna knjiga nema ugrađenu naslovnicu ili sliku naslovnice u mapi, skener će pokušati pronaći naslovnicu.http://192.168.1.1:8337 trebate upisati http://192.168.1.1:8337/notify.",
+ "MessageAppriseDescription": "Za korištnje ove funkcije, treba vam instanca Apprise API-ja ili API koji može rukovati istom vrstom zahtjeva.http://192.168.1.1:8337 morate upisati http://192.168.1.1:8337/notify.",
"MessageAsinCheck": "Upišite ASIN iz odgovarajuće Audibleove regije, ne s Amazonov.",
"MessageAuthenticationLegacyTokenWarning": "Starije API tokene ćemo ukloniti. Umjesto njih, koristite se API ključevima .",
"MessageAuthenticationOIDCChangesRestart": "Ponovno pokrenite poslužitelj da biste primijenili OIDC promjene.",
"MessageAuthenticationSecurityMessage": "Provjera autentičnosti poboljšana je radi sigurnosti. Svi se korisnici moraju ponovno prijaviti.",
- "MessageBackupsDescription": "Sigurnosne kopije sadrže korisnike, korisnikov napredak medija, pojedinosti knjižničke građe, postavke poslužitelja i slike koje se spremaju u /metadata/items & /metadata/authors. Sigurnosne kopije ne sadrže niti jednu datoteku iz mapa knjižnice.",
+ "MessageBackupsDescription": "Sigurnosne kopije sadrže korisnike, napredak korisnika, pojedinosti knjižničke građe, postavke poslužitelja i slike koje se spremaju u /metadata/items i /metadata/authors. Sigurnosne kopije ne sadrže niti jednu datoteku iz mapa knjižnice.",
"MessageBackupsLocationEditNote": "Napomena: Uređivanje lokacije za sigurnosne kopije ne premješta ili mijenja postojeće sigurnosne kopije",
"MessageBackupsLocationNoEditNote": "Napomena: Lokacija za sigurnosne kopije zadana je kroz varijablu okoline i ovdje se ne može izmijeniti.",
"MessageBackupsLocationPathEmpty": "Putanja do lokacije za sigurnosne kopije ne može ostati prazna",
@@ -760,7 +760,7 @@
"MessageChapterErrorStartLtPrev": "Netočno vrijeme početka, mora biti veće ili jednako vremenu početka prethodnog poglavlja",
"MessageChapterStartIsAfter": "Početak poglavlja je nakon kraja zvučne knjige",
"MessageChaptersNotFound": "Poglavlja nisu pronađena",
- "MessageCheckingCron": "Provjeravam cron...",
+ "MessageCheckingCron": "Provjeravanje cron izraza...",
"MessageConfirmCloseFeed": "Sigurno želite zatvoriti ovaj izvor?",
"MessageConfirmDeleteApiKey": "Sigurno želite izbrisati API ključ \"{0}\"?",
"MessageConfirmDeleteBackup": "Sigurno želite izbrisati sigurnosnu kopiju za {0}?",
@@ -774,12 +774,12 @@
"MessageConfirmDeleteSession": "Sigurno želite izbrisati ovu sesiju?",
"MessageConfirmEmbedMetadataInAudioFiles": "Sigurno želite ugraditi meta-podatke u {0} zvučnih datoteka?",
"MessageConfirmForceReScan": "Sigurno želite ponovno pokrenuti skeniranje?",
- "MessageConfirmMarkAllEpisodesFinished": "Sigurno želite označiti sve nastavke dovršenima?",
- "MessageConfirmMarkAllEpisodesNotFinished": "Sigurno želite označiti sve nastavke nedovršenima?",
- "MessageConfirmMarkItemFinished": "Sigurno želite označiti \"{0}\" dovršenim?",
- "MessageConfirmMarkItemNotFinished": "Sigurno želite označiti \"{0}\" nedovršenim?",
- "MessageConfirmMarkSeriesFinished": "Sigurno želite označiti sve knjige u ovom serijalu dovršenima?",
- "MessageConfirmMarkSeriesNotFinished": "Sigurno želite označiti sve knjige u ovom serijalu nedovršenima?",
+ "MessageConfirmMarkAllEpisodesFinished": "Sigurno želite označiti sve nastavke kao završene?",
+ "MessageConfirmMarkAllEpisodesNotFinished": "Sigurno želite označiti sve nastavke kao nezavršene?",
+ "MessageConfirmMarkItemFinished": "Sigurno želite označiti stavku \"{0}\" kao završenu?",
+ "MessageConfirmMarkItemNotFinished": "Sigurno želite označiti stavku \"{0}\" kao nezavršenu?",
+ "MessageConfirmMarkSeriesFinished": "Sigurno želite označiti sve knjige u ovom serijalu kao završene?",
+ "MessageConfirmMarkSeriesNotFinished": "Sigurno želite označiti sve knjige u ovom serijalu kao nezavršene?",
"MessageConfirmNotificationTestTrigger": "Želite li okinuti ovu obavijest s probnim podatcima?",
"MessageConfirmPurgeCache": "Brisanje predmemorije izbrisat će cijelu mapu /metadata/cache. /metadata/cache/items./metadata/logs u obliku JSON datoteka. Zapisnici pada sustava čuvaju se u datoteci /metadata/logs/crash_logs.txt.",
- "MessageM4BFailed": "M4B neuspješan!",
- "MessageM4BFinished": "M4B završio!",
- "MessageMapChapterTitles": "Mapiraj nazive poglavlja postojećim poglavljima zvučne knjige bez uređivanja vremenskih identifikatora",
- "MessageMarkAllEpisodesFinished": "Označi sve nastavke dovršenima",
- "MessageMarkAllEpisodesNotFinished": "Označi sve nastavke nedovršenima",
- "MessageMarkAsFinished": "Označi kao dovršeno",
- "MessageMarkAsNotFinished": "Označi kao nedovršeno",
- "MessageMatchBooksDescription": "će pokušati prepoznati knjige iz knjižnice u katalogu odabranog pružatelja podatka te nadopuniti podatke koji nedostaju i naslovnice. Ne prepisuje preko postojećih podataka.",
+ "MessageM4BFailed": "M4B datoteka neuspjela!",
+ "MessageM4BFinished": "M4B datoteka je završena!",
+ "MessageMapChapterTitles": "Mapiraj imana poglavlja s vašim postojećim poglavljima zvučne knjige bez podešavanja vremenskih identifikatora",
+ "MessageMarkAllEpisodesFinished": "Označi sve nastavke kao završene",
+ "MessageMarkAllEpisodesNotFinished": "Označi sve nastavke kao nezavršene",
+ "MessageMarkAsFinished": "Označi kao završeno",
+ "MessageMarkAsNotFinished": "Označi kao nezavršeno",
+ "MessageMatchBooksDescription": "će pokušati prepoznati knjige iz knjižnice u katalogu odabranog pružatelja podatka te nadopuniti podatke koji nedostaju i naslovnice. Ne prepisuje postojeće podatke.",
"MessageNoAudioTracks": "Nema zvučnih zapisa",
"MessageNoAuthors": "Nema autora",
"MessageNoBackups": "Nema sigurnosnih kopija",
"MessageNoBookmarks": "Nema knjižnih oznaka",
"MessageNoChapters": "Nema poglavlja",
"MessageNoCollections": "Nema zbirki",
- "MessageNoCoversFound": "Naslovnice nisu pronađene",
+ "MessageNoCoversFound": "Nije pronađena nijedna naslovnica",
"MessageNoDescription": "Nema opisa",
"MessageNoDevices": "Nema uređaja",
- "MessageNoDownloadsInProgress": "Nema preuzimanja u tijeku",
+ "MessageNoDownloadsInProgress": "Trenutačno nema preuzimanja u tijeku",
"MessageNoDownloadsQueued": "Nema preuzimanja u redu",
"MessageNoEpisodeMatchesFound": "Nije pronađen ni jedan odgovarajući nastavak",
"MessageNoEpisodes": "Nema nastavaka",
@@ -873,28 +873,28 @@
"MessagePauseChapter": "Pauziraj reprodukciju poglavlja",
"MessagePlayChapter": "Slušaj početak poglavlja",
"MessagePlaylistCreateFromCollection": "Stvori popis za izvođenje od zbirke",
- "MessagePleaseWait": "Molimo pričekajte...",
+ "MessagePleaseWait": "Pričekajte...",
"MessagePodcastHasNoRSSFeedForMatching": "Podcast nema adresu RSS izvora za prepoznavanje",
"MessagePodcastSearchField": "Upišite izraz za pretraživanje ili URL RSS izvora",
"MessageQuickEmbedInProgress": "Brzo ugrađivanje u tijeku",
"MessageQuickEmbedQueue": "Dodano u red za brzo ugrađivanje ({0} u redu izvođenja)",
"MessageQuickMatchAllEpisodes": "Brzo prepoznavanje svih nastavaka",
- "MessageQuickMatchDescription": "Popuni pojedinosti i naslovnice koji nedostaju prvim pronađenim rezultatom za '{0}'. Ne prepisuje podatke osim ako ne uključite mogućnost 'Daj prednost meta-podatcima prepoznatih stavki'.",
+ "MessageQuickMatchDescription": "Popuni pojedinosti i naslovnice koji nedostaju prvim pronađenim rezultatom za '{0}'. Ne prepisuje podatke osim ako je postavka poslužitelja „Daj prednost meta-podatcima prepoznatih stavki” uključena.",
"MessageRemoveChapter": "Ukloni poglavlje",
"MessageRemoveEpisodes": "Ukloni {0} nastavaka",
"MessageRemoveFromPlayerQueue": "Ukloni iz redoslijeda izvođenja",
"MessageRemoveUserWarning": "Sigurno želite trajno izbrisati korisnika \"{0}\"?",
- "MessageReportBugsAndContribute": "Prijavite pogreške, zatražite značajke i doprinesite na",
+ "MessageReportBugsAndContribute": "Prijavite pogreške, zatražite funkcije i doprinesite na",
"MessageResetChaptersConfirm": "Sigurno želite vratiti poglavlja na prethodno stanje i poništiti učinjene promjene?",
"MessageRestoreBackupConfirm": "Sigurno želite vratiti sigurnosnu kopiju izrađenu",
"MessageRestoreBackupWarning": "Vraćanjem sigurnosne kopije prepisat ćete cijelu bazu podataka koja se nalazi u /config i slike naslovnice u /metadata/items i /metadata/authors.false-ként fogja kezelni. Győződj meg arról, hogy az identitásszolgáltató igénye megfelel a várt struktúrának:",
"LabelOpenIDClaims": "Hagyd üresen a következő opciókat, hogy letiltsd a haladó csoport- és jogosultság-hozzárendelést, ekkor automatikusan a ‘Felhasználó’ csoport kerül hozzárendelésre.",
- "LabelOpenIDGroupClaimDescription": "Az OpenID-igény neve, amely a felhasználó csoportjainak listáját tartalmazza. Általában groups néven hivatkoznak rá. Ha konfigurálva van, az alkalmazás automatikusan hozzárendeli a szerepköröket a felhasználó csoporttagságai alapján, feltéve, hogy ezek a csoportok az igényben kis- és nagybetűkre érzéketlenül ‘admin’, ‘user’ vagy ‘guest’ néven szerepelnek. Az igénynek egy listát kell tartalmaznia, és ha egy felhasználó több csoport tagja, az alkalmazás a legmagasabb szintű hozzáféréssel rendelkező szerepkört rendeli hozzá. Ha egyetlen csoport sem felel meg, a hozzáférés megtagadásra kerül.",
+ "LabelOpenIDGroupClaimDescription": "Az OpenID-igény neve, amely a felhasználó csoportjainak listáját tartalmazza. Általában groups néven hivatkoznak rá. Ha konfigurálva van, az alkalmazás automatikusan hozzárendeli a szerepköröket a felhasználó csoporttagságai alapján, feltéve, hogy ezek a csoportok az igényben kis- és nagybetűkre érzéketlenül ‘admin’, ‘user’ vagy ‘guest’ néven szerepelnek. Az igénynek egy listát kell tartalmaznia, és ha egy felhasználó több csoport tagja, az alkalmazás a legmagasabb szintű hozzáféréssel rendelkező szerepkört rendeli hozzá. Ha egyetlen csoport sem felel meg, a hozzáférés megtagadásra kerül.",
"LabelOpenRSSFeed": "RSS hírcsatorna megnyitása",
"LabelOverwrite": "Felülírás",
"LabelPaginationPageXOfY": "{0} oldal {1}-ból/ből",
@@ -588,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Skeuomorfikus dizájn fa polcokkal",
"LabelSettingsChromecastSupport": "Chromecast támogatás",
"LabelSettingsDateFormat": "Dátumformátum",
- "LabelSettingsEnableWatcher": "Változások automatikus vizsgálata a könyvtárakban",
- "LabelSettingsEnableWatcherForLibrary": "Változások automatikus vizsgálata a könyvtárban",
+ "LabelSettingsEnableWatcher": "Változások automatikus figyelése a könyvtárakban",
+ "LabelSettingsEnableWatcherForLibrary": "Változások automatikus figyelése a könyvtárban",
"LabelSettingsEnableWatcherHelp": "Engedélyezi az automatikus elem hozzáadás/frissítés funkciót, amikor fájlváltozásokat észlel. *Szerver újraindítása szükséges",
"LabelSettingsEpubsAllowScriptedContent": "Szkriptelt tartalmak engedélyezése epub-okban",
"LabelSettingsEpubsAllowScriptedContentHelp": "Megengedi, hogy az epub fájlok szkripteket hajtsanak végre. Ezt a beállítást kikapcsolva ajánlott tartani, kivéve, ha megbízik az epub fájlok forrásában.",
@@ -643,8 +643,8 @@
"LabelStatsAuthors": "Szerző",
"LabelStatsBestDay": "Legjobb nap",
"LabelStatsDailyAverage": "Napi átlag",
- "LabelStatsDays": "Napok",
- "LabelStatsDaysListened": "Hallgatással töltött napok",
+ "LabelStatsDays": "Nap",
+ "LabelStatsDaysListened": "Hallgatással töltött nap",
"LabelStatsHours": "Órák",
"LabelStatsInARow": "egymás után",
"LabelStatsItemsFinished": "Befejezett elem",
@@ -888,7 +888,7 @@
"MessageResetChaptersConfirm": "Biztosan alaphelyzetbe szeretné állítani a fejezeteket és visszavonni a módosításokat?",
"MessageRestoreBackupConfirm": "Biztosan vissza szeretné állítani a biztonsági másolatot, amely ekkor készült:",
"MessageRestoreBackupWarning": "A biztonsági mentés visszaállítása felülírja az egész adatbázist, amely a /config mappában található, valamint a borítóképeket a /metadata/items és /metadata/authors mappákban.
A biztonsági mentések nem módosítják a könyvtár mappáiban található fájlokat. Ha engedélyezte a szerverbeállításokat a borítóképek és a metaadatok könyvtármappákban való tárolására, akkor ezek nem kerülnek biztonsági mentésre vagy felülírásra.
A szerver használó összes kliens automatikusan frissül.",
- "MessageScheduleLibraryScanNote": "A legtöbb felhasználó számára ajánlott ezt a funkciót kikapcsolva hagyni, és engedélyezni a mappafigyelő beállítást. A mappafigyelő automatikusan észleli a könyvtári mappák változásait. A mappafigyelő nem működik minden fájlrendszernél (mint például az NFS), ezért helyette ütemezett könyvtárellenőrzéseket lehet használni.",
+ "MessageScheduleLibraryScanNote": "A legtöbb felhasználó számára ajánlott ezt a funkciót kikapcsolva hagyni, és engedélyezni a mappafigyelő beállítást. A mappafigyelő automatikusan észleli a könyvtári mappák változásait. Kapcsolja be ezt a funkciót, ha az „Automatikus könyvtárfigyelés” nem működik a fájlrendszerén (például NFS).",
"MessageScheduleRunEveryWeekdayAtTime": "Futás minden {1} óra {0}-kor",
"MessageSearchResultsFor": "Keresési eredmények",
"MessageSelected": "{0} kiválasztva",
diff --git a/client/strings/is.json b/client/strings/is.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/client/strings/is.json
@@ -0,0 +1 @@
+{}
diff --git a/client/strings/it.json b/client/strings/it.json
index 2354bc0ae..d2178cbf6 100644
--- a/client/strings/it.json
+++ b/client/strings/it.json
@@ -275,7 +275,7 @@
"LabelBonus": "Bonus",
"LabelBooks": "Libri",
"LabelButtonText": "Buttone Testo",
- "LabelByAuthor": "da {0}",
+ "LabelByAuthor": "di {0}",
"LabelChangePassword": "Cambia Password",
"LabelChannels": "Canali",
"LabelChapterCount": "{0} Capitoli",
@@ -383,7 +383,7 @@
"LabelFolders": "Cartelle",
"LabelFontBold": "Grassetto",
"LabelFontBoldness": "Grassetto",
- "LabelFontFamily": "Famiglia di caratteri",
+ "LabelFontFamily": "Famiglia caratteri",
"LabelFontItalic": "Corsivo",
"LabelFontScale": "Dimensione font",
"LabelFontStrikethrough": "Barrato",
@@ -588,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Design con scaffali in legno",
"LabelSettingsChromecastSupport": "Supporto a Chromecast",
"LabelSettingsDateFormat": "Formato Data",
- "LabelSettingsEnableWatcher": "Scansiona le librerie Automaticamente per trovare modifiche",
- "LabelSettingsEnableWatcherForLibrary": "Scansiona la libreria Automaticamente per trovare modifiche",
+ "LabelSettingsEnableWatcher": "Controlla automaticamente le modifiche alle librerie",
+ "LabelSettingsEnableWatcherForLibrary": "Controlla automaticamente le modifiche alle librerie",
"LabelSettingsEnableWatcherHelp": "Abilita l'aggiunta/aggiornamento automatico degli elementi quando vengono rilevate modifiche ai file. *Richiede il riavvio del Server",
"LabelSettingsEpubsAllowScriptedContent": "Consenti contenuti con script negli epub",
"LabelSettingsEpubsAllowScriptedContentHelp": "Consenti ai file epub di eseguire script. Si consiglia di mantenere questa impostazione disabilitata a meno che non si ritenga attendibile l'origine dei file epub.",
@@ -888,7 +888,7 @@
"MessageResetChaptersConfirm": "Sei sicuro di voler reimpostare i capitoli e annullare le modifiche ?",
"MessageRestoreBackupConfirm": "Sei sicuro di voler ripristinare il backup creato su",
"MessageRestoreBackupWarning": "Il ripristino di un backup sovrascriverà l'intero database situato in /config e sovrascrive le immagini in /metadata/items & /metadata/authors.
I backup non modificano alcun file nelle cartelle della libreria. Se hai abilitato le impostazioni del server per archiviare copertine e metadati nelle cartelle della libreria, questi non vengono sottoposti a backup o sovrascritti.
Tutti i client che utilizzano il tuo server verranno aggiornati automaticamente.",
- "MessageScheduleLibraryScanNote": "Per la maggior parte degli utenti, si consiglia di lasciare questa funzionalità disabilitata e di mantenere abilitata l'impostazione di folder watcher. Il folder watcher rileverà automaticamente le modifiche nelle cartelle della libreria. Il folder watcher non funziona per ogni file system (come NFS), quindi è possibile utilizzare le scansioni pianificate della libreria.",
+ "MessageScheduleLibraryScanNote": "Per la maggior parte degli utenti, si consiglia di lasciare questa funzione disabilitata e mantenere abilitata l'impostazione “Controlla automaticamente le modifiche nella libreria”: in questo modo verranno rilevate automaticamente le modifiche nelle cartelle della libreria. Abilita questa funzione se “Controlla automaticamente le modifiche nella libreria” non funziona con il tuo file system (come NFS).",
"MessageScheduleRunEveryWeekdayAtTime": "Esegui ogni {0} alle {1}",
"MessageSearchResultsFor": "cerca risultati per",
"MessageSelected": "{0} selezionati",
diff --git a/client/strings/ja.json b/client/strings/ja.json
index 7c2acb889..0621614f4 100644
--- a/client/strings/ja.json
+++ b/client/strings/ja.json
@@ -72,6 +72,7 @@
"ButtonQueueRemoveItem": "次に再生から削除",
"ButtonQuickEmbed": "クイック埋め込み",
"ButtonQuickEmbedMetadata": "メタデータの埋め込み",
+ "ButtonQuickMatch": "クイックマッチ",
"ButtonReScan": "再スキャン",
"ButtonRead": "読む",
"ButtonReadLess": "閉じる",
@@ -80,71 +81,159 @@
"ButtonRemove": "削除",
"ButtonRemoveAll": "全て削除",
"ButtonRemoveAllLibraryItems": "ライブラリーの項目を全て削除",
+ "ButtonRemoveFromContinueListening": "「続きを聴く」から削除",
+ "ButtonRemoveFromContinueReading": "「続きを読む」から削除",
+ "ButtonRemoveSeriesFromContinueSeries": "「シリーズを続く」からシリーズを削除",
"ButtonReset": "元に戻す",
"ButtonResetToDefault": "デフォルトに戻す",
"ButtonRestore": "復元",
"ButtonSave": "保存",
"ButtonSaveAndClose": "保存して閉じる",
+ "ButtonSaveTracklist": "トラックリストを保存",
"ButtonScan": "スキャン",
"ButtonScanLibrary": "ライブラリーをスキャン",
"ButtonScrollLeft": "左にスクロール",
"ButtonScrollRight": "右にスクロール",
"ButtonSearch": "検索",
+ "ButtonSelectFolderPath": "保存先フォルダを選択",
"ButtonSeries": "シリーズ",
+ "ButtonSetChaptersFromTracks": "トラックからチャプターを設定する",
+ "ButtonShare": "共有",
+ "ButtonShiftTimes": "再生時間の移動",
+ "ButtonShow": "表示",
+ "ButtonStartM4BEncode": "M4Bエンコード開始",
+ "ButtonStartMetadataEmbed": "メタデータ埋め込み開始",
+ "ButtonStats": "統計",
"ButtonSubmit": "送信",
+ "ButtonTest": "テスト",
+ "ButtonUnlinkOpenId": "OpenID 連携解除",
+ "ButtonUpload": "アップロード",
+ "ButtonUploadBackup": "バックアップのアップロード",
+ "ButtonUploadCover": "カバー画像をアップロード",
+ "ButtonUploadOPMLFile": "OPMLファイルをアップロード",
+ "ButtonUserDelete": "ユーザーを削除 {0}",
+ "ButtonUserEdit": "ユーザを編集 {0}",
+ "ButtonViewAll": "すべて表示",
"ButtonYes": "はい",
+ "ErrorUploadFetchMetadataAPI": "メタデータの取得中にエラーが発生しました",
+ "ErrorUploadFetchMetadataNoResults": "メタデータ取得に失敗しました。タイトルや著者名を更新してください",
+ "ErrorUploadLacksTitle": "タイトルは必須です",
"HeaderAccount": "アカウント",
+ "HeaderAddCustomMetadataProvider": "カスタムメタデータプロバイダーを追加",
"HeaderAdvanced": "上級者向け",
+ "HeaderApiKeys": "APIキー",
+ "HeaderAppriseNotificationSettings": "Apprise 通知設定",
"HeaderAudioTracks": "オーディオトラック",
+ "HeaderAudiobookTools": "オーディオブックのファイル管理ツール",
+ "HeaderAuthentication": "認証",
+ "HeaderBackups": "バックアップ",
+ "HeaderBulkChapterModal": "チャプターをまとめて追加",
+ "HeaderChangePassword": "パスワードを変更",
"HeaderChapters": "チャプター",
+ "HeaderChooseAFolder": "フォルダを選択",
"HeaderCollection": "コレクション",
"HeaderCollectionItems": "コレクションの項目",
+ "HeaderCover": "カバー",
+ "HeaderCurrentDownloads": "現在のダウンロード",
+ "HeaderCustomMessageOnLogin": "ログイン時のカスタムメッセージ",
+ "HeaderCustomMetadataProviders": "カスタムメタデータプロバイダー",
"HeaderDetails": "詳細",
+ "HeaderDownloadQueue": "ダウンロード待ち",
"HeaderEbookFiles": "電子書籍ファイル",
+ "HeaderEmail": "メール",
+ "HeaderEmailSettings": "メール設定",
"HeaderEpisodes": "エピソード",
+ "HeaderEreaderDevices": "電子書籍リーダー端末",
"HeaderEreaderSettings": "電子書籍リーダーの設定",
+ "HeaderFiles": "ファイル",
+ "HeaderFindChapters": "チャプターを検索",
+ "HeaderIgnoredFiles": "無視されたファイル",
+ "HeaderItemFiles": "アイテムファイル",
+ "HeaderItemMetadataUtils": "アイテムメタデータユーティリティ",
+ "HeaderLastListeningSession": "直近の再生セッション",
"HeaderLatestEpisodes": "最新のエピソード",
"HeaderLibraries": "ライブラリー",
+ "HeaderLibraryFiles": "ライブラリファイル",
+ "HeaderLibraryStats": "ライブラリ統計",
+ "HeaderListeningSessions": "再生セッション",
+ "HeaderListeningStats": "再生統計",
+ "HeaderLogin": "ログイン",
+ "HeaderLogs": "ログ",
+ "HeaderManageGenres": "ジャンルを管理",
+ "HeaderManageTags": "タグを管理",
+ "HeaderMapDetails": "マップの詳細",
+ "HeaderMatch": "マッチ",
+ "HeaderMetadataOrderOfPrecedence": "メタデータの優先順",
+ "HeaderMetadataToEmbed": "埋め込むメタデータ",
+ "HeaderNewAccount": "新規アカウント",
+ "HeaderNewApiKey": "新規APIキー",
+ "HeaderNewLibrary": "新規ライブラリー",
+ "HeaderNotificationCreate": "通知を作成",
+ "HeaderNotificationUpdate": "通知を更新",
+ "HeaderNotifications": "通知",
+ "HeaderOpenIDConnectAuthentication": "OpenID Connect 認証",
"HeaderOpenRSSFeed": "RSS Feedを開く",
"HeaderPlayerSettings": "プレーヤーの設定",
"HeaderPlaylist": "プレイリスト",
"HeaderPlaylistItems": "プレイリストアイテム",
"HeaderRSSFeedGeneral": "RSS 詳細",
+ "HeaderRSSFeedIsOpen": "RSSフィードが開いています",
"HeaderSettings": "設定",
"HeaderSettingsGeneral": "一般",
"HeaderSettingsScanner": "スキャナー",
"HeaderSleepTimer": "スリープタイマー",
"HeaderStatsMinutesListeningChart": "過去7日間の視聴時間(分)",
+ "HeaderStatsRecentSessions": "最近の再生履歴",
+ "HeaderTableOfContents": "目次",
+ "HeaderYourStats": "再生統計",
"LabelAddToPlaylist": "プレイリストの追加",
+ "LabelAddedAt": "追加日時",
+ "LabelAddedDate": "追加日時 {0}",
+ "LabelAll": "すべて",
"LabelAuthor": "著者",
"LabelAuthorFirstLast": "著者(名 氏)",
"LabelAuthorLastFirst": "著者(氏 名)",
"LabelAuthors": "著者",
"LabelAutoDownloadEpisodes": "エピソードの自動ダウンロード",
"LabelBooks": "ほん",
+ "LabelByAuthor": "著 {0}",
"LabelChapters": "チャプター",
"LabelClosePlayer": "プレイヤーを閉じる",
+ "LabelCollapseSeries": "シリーズを折りたたむ",
"LabelComplete": "完了",
"LabelContinueListening": "続きから聞く",
+ "LabelContinueReading": "続きを読む",
+ "LabelContinueSeries": "シリーズを続く",
"LabelDescription": "説明",
+ "LabelDiscover": "おすすめ",
"LabelDownload": "ダウンロード",
"LabelDuration": "長さ",
"LabelEbook": "Eブック",
"LabelEbooks": "Eブック",
"LabelEnable": "有効",
+ "LabelEnd": "終了",
"LabelEndOfChapter": "チャプターの最後",
"LabelEpisode": "エピソード",
+ "LabelEpisodes": "エピソード",
+ "LabelEpisodic": "エピソード",
+ "LabelExplicit": "露骨な表現",
"LabelFeedURL": "Feed URL",
"LabelFile": "ファイル",
+ "LabelFileBirthtime": "ファイル作成日時",
+ "LabelFileModified": "ファイル更新日時",
"LabelFilename": "ファイル名",
"LabelFinished": "完了",
"LabelFolder": "フォルダ",
"LabelFontBoldness": "フォントの太さ",
+ "LabelFontFamily": "フォントファミリー",
"LabelFontScale": "フォントサイズ",
"LabelGenre": "ジャンル",
"LabelGenres": "ジャンル",
+ "LabelHasEbook": "eBookあり",
+ "LabelHasSupplementaryEbook": "付属eBookあり",
"LabelHost": "ホスト",
"LabelInProgress": "進行中",
+ "LabelIncomplete": "未完了",
"LabelLanguage": "言語",
"LabelLanguages": "言語",
"LabelLayout": "レイアウト",
diff --git a/client/strings/ko.json b/client/strings/ko.json
new file mode 100644
index 000000000..a2a4dc92a
--- /dev/null
+++ b/client/strings/ko.json
@@ -0,0 +1,1165 @@
+{
+ "ButtonAdd": "추가",
+ "ButtonAddApiKey": "API Key 추가",
+ "ButtonAddChapters": "챕터 추가",
+ "ButtonAddDevice": "기기 추가",
+ "ButtonAddLibrary": "라이브러리 추가",
+ "ButtonAddPodcasts": "팟케스트 추가",
+ "ButtonAddUser": "사용자 추가",
+ "ButtonAddYourFirstLibrary": "첫 라이브러리 추가",
+ "ButtonApply": "적용",
+ "ButtonApplyChapters": "챕터 적용",
+ "ButtonAuthors": "작가",
+ "ButtonBack": "뒤로",
+ "ButtonBatchEditPopulateFromExisting": "기존에서 채우기",
+ "ButtonBatchEditPopulateMapDetails": "지도 세부 정보 채우기",
+ "ButtonBrowseForFolder": "폴더 찾아보기",
+ "ButtonCancel": "취소",
+ "ButtonCancelEncode": "인코딩 취소",
+ "ButtonChangeRootPassword": "루트 비밀번호 변경",
+ "ButtonCheckAndDownloadNewEpisodes": "새로운 에피소드 확인 및 다운로드",
+ "ButtonChooseAFolder": "폴더를 선택",
+ "ButtonChooseFiles": "파일 선택",
+ "ButtonClearFilter": "필터 지우기",
+ "ButtonClose": "닫기",
+ "ButtonCloseFeed": "피드 닫기",
+ "ButtonCloseSession": "열린 세션 닫기",
+ "ButtonCollections": "컬렉션",
+ "ButtonConfigureScanner": "스캐너 구성",
+ "ButtonCreate": "만들기",
+ "ButtonCreateBackup": "백업 만들기",
+ "ButtonDelete": "삭제",
+ "ButtonDownloadQueue": "대기줄",
+ "ButtonEdit": "편집",
+ "ButtonEditChapters": "챕터 편집",
+ "ButtonEditPodcast": "팟캐스트 편집",
+ "ButtonEnable": "활성화",
+ "ButtonFireAndFail": "Fire and Fail",
+ "ButtonFireOnTest": "Fire onTest 이벤트",
+ "ButtonForceReScan": "강제 재스캔",
+ "ButtonFullPath": "전체 경로",
+ "ButtonHide": "숨기기",
+ "ButtonHome": "홈",
+ "ButtonIssues": "이슈",
+ "ButtonJumpBackward": "뒤로 점프",
+ "ButtonJumpForward": "앞으로 점프",
+ "ButtonLatest": "최신",
+ "ButtonLibrary": "라이브러리",
+ "ButtonLogout": "로그아웃",
+ "ButtonLookup": "Lookup",
+ "ButtonManageTracks": "트랙 관리",
+ "ButtonMapChapterTitles": "지도 장 제목",
+ "ButtonMatchAllAuthors": "모든 저자와 일치",
+ "ButtonMatchBooks": "일치한 책",
+ "ButtonNevermind": "괜찮아요",
+ "ButtonNext": "다음",
+ "ButtonNextChapter": "다음 장",
+ "ButtonNextItemInQueue": "대기열의 다음 항목",
+ "ButtonOk": "확인",
+ "ButtonOpenFeed": "피드 열기",
+ "ButtonOpenManager": "매니저 열기",
+ "ButtonPause": "일시정지",
+ "ButtonPlay": "재생",
+ "ButtonPlayAll": "모두 재생",
+ "ButtonPlaying": "재생중",
+ "ButtonPlaylists": "재생목록",
+ "ButtonPrevious": "이전",
+ "ButtonPreviousChapter": "이전 챕터",
+ "ButtonProbeAudioFile": "Probe 오디오 파일",
+ "ButtonPurgeAllCache": "모든 캐시 삭제",
+ "ButtonPurgeItemsCache": "항목 캐시 삭제",
+ "ButtonQueueAddItem": "대기열에 추가",
+ "ButtonQueueRemoveItem": "대기열에서 제거",
+ "ButtonQuickEmbed": "빠른 임베드",
+ "ButtonQuickEmbedMetadata": "빠른 임베드 메타데이터",
+ "ButtonQuickMatch": "퀵 매치",
+ "ButtonReScan": "재스캔",
+ "ButtonRead": "읽기",
+ "ButtonReadLess": "덜 읽기",
+ "ButtonReadMore": "더 읽기",
+ "ButtonRefresh": "새로고침",
+ "ButtonRemove": "제거",
+ "ButtonRemoveAll": "모두 제거",
+ "ButtonRemoveAllLibraryItems": "모든 라이브러리 항목 제거",
+ "ButtonRemoveFromContinueListening": "계속 듣기에서 제거",
+ "ButtonRemoveFromContinueReading": "계속 읽기에서 제거",
+ "ButtonRemoveSeriesFromContinueSeries": "계속 시리즈에서 시리즈 제거",
+ "ButtonReset": "리셋",
+ "ButtonResetToDefault": "기본값으로 재설정",
+ "ButtonRestore": "복구",
+ "ButtonSave": "저장",
+ "ButtonSaveAndClose": "저장 후 닫기",
+ "ButtonSaveTracklist": "트랙리스트 저장",
+ "ButtonScan": "스캔",
+ "ButtonScanLibrary": "라이브러리 스캔",
+ "ButtonScrollLeft": "왼쪽 스크롤",
+ "ButtonScrollRight": "오른쪽 스크롤",
+ "ButtonSearch": "검색",
+ "ButtonSelectFolderPath": "폴더 경로 선택",
+ "ButtonSeries": "시리즈",
+ "ButtonSetChaptersFromTracks": "트랙에서 챕터 설정",
+ "ButtonShare": "공유",
+ "ButtonShiftTimes": "시간 이동",
+ "ButtonShow": "보기",
+ "ButtonStartM4BEncode": "M4B 인코딩 시작",
+ "ButtonStartMetadataEmbed": "메타데이터 삽입 시작",
+ "ButtonStats": "통계",
+ "ButtonSubmit": "제출",
+ "ButtonTest": "테스트",
+ "ButtonUnlinkOpenId": "OpenID 연결 해제",
+ "ButtonUpload": "업로드",
+ "ButtonUploadBackup": "백업 업로드",
+ "ButtonUploadCover": "커버 업로드",
+ "ButtonUploadOPMLFile": "OPML 파일 업로드",
+ "ButtonUserDelete": "사용자 {0} 삭제",
+ "ButtonUserEdit": "사용자 {0} 편집",
+ "ButtonViewAll": "모두 보기",
+ "ButtonYes": "예",
+ "ErrorUploadFetchMetadataAPI": "메타데이터를 가져오는 중 오류가 발생했습니다.",
+ "ErrorUploadFetchMetadataNoResults": "메타데이터를 가져올 수 없습니다. 제목 및/또는 작성자를 업데이트해 보세요.",
+ "ErrorUploadLacksTitle": "제목이 있어야 합니다",
+ "HeaderAccount": "계정",
+ "HeaderAddCustomMetadataProvider": "사용자 정의 메타데이터 공급자 추가",
+ "HeaderAdvanced": "고급",
+ "HeaderApiKeys": "API 키",
+ "HeaderAppriseNotificationSettings": "Apprise 알림 설정",
+ "HeaderAudioTracks": "오디오 트랙",
+ "HeaderAudiobookTools": "오디오북 파일 관리 도구",
+ "HeaderAuthentication": "인증",
+ "HeaderBackups": "백업",
+ "HeaderBulkChapterModal": "여러 챕터 추가",
+ "HeaderChangePassword": "비밀번호 변경",
+ "HeaderChapters": "챕터",
+ "HeaderChooseAFolder": "폴더 선택",
+ "HeaderCollection": "컬렉션",
+ "HeaderCollectionItems": "컬렉션 항목",
+ "HeaderCover": "커버",
+ "HeaderCurrentDownloads": "현재 다운로드",
+ "HeaderCustomMessageOnLogin": "로그인시 사용자 정의 메시지",
+ "HeaderCustomMetadataProviders": "사용자 정의 메타데이터 공급자",
+ "HeaderDetails": "세부사항",
+ "HeaderDownloadQueue": "다운로드 대기열",
+ "HeaderEbookFiles": "전자책 파일",
+ "HeaderEmail": "이메일",
+ "HeaderEmailSettings": "이메일 설정",
+ "HeaderEpisodes": "에피소드",
+ "HeaderEreaderDevices": "전자책 리더기",
+ "HeaderEreaderSettings": "전자책 리더 설정",
+ "HeaderFiles": "파일",
+ "HeaderFindChapters": "챕터 찾기",
+ "HeaderIgnoredFiles": "무시된 파일",
+ "HeaderItemFiles": "항목 파일",
+ "HeaderItemMetadataUtils": "항목 메타데이터 유틸리티",
+ "HeaderLastListeningSession": "마지막 청취 세션",
+ "HeaderLatestEpisodes": "최신 에피소드",
+ "HeaderLibraries": "라이브러리",
+ "HeaderLibraryFiles": "라이브러리 파일",
+ "HeaderLibraryStats": "라이브러리 통계",
+ "HeaderListeningSessions": "청취 세션",
+ "HeaderListeningStats": "청취 통계",
+ "HeaderLogin": "로그인",
+ "HeaderLogs": "로그",
+ "HeaderManageGenres": "장르 관리",
+ "HeaderManageTags": "태그 관리",
+ "HeaderMapDetails": "지도 세부 정보",
+ "HeaderMatch": "일치",
+ "HeaderMetadataOrderOfPrecedence": "메타데이터 우선 순위",
+ "HeaderMetadataToEmbed": "포함할 메타데이터",
+ "HeaderNewAccount": "새 계정",
+ "HeaderNewApiKey": "새로운 API 키",
+ "HeaderNewLibrary": "새 라이브러리",
+ "HeaderNotificationCreate": "알림 생성",
+ "HeaderNotificationUpdate": "업데이트 알림",
+ "HeaderNotifications": "알림",
+ "HeaderOpenIDConnectAuthentication": "OpenID Connect 인증",
+ "HeaderOpenListeningSessions": "청취 세션 열기",
+ "HeaderOpenRSSFeed": "RSS 피드 열기",
+ "HeaderOtherFiles": "기타 파일",
+ "HeaderPasswordAuthentication": "비밀번호 인증",
+ "HeaderPermissions": "권한",
+ "HeaderPlayerQueue": "플레이어 대기열",
+ "HeaderPlayerSettings": "플레이어 설정",
+ "HeaderPlaylist": "재생목록",
+ "HeaderPlaylistItems": "재생 목록 항목",
+ "HeaderPodcastsToAdd": "추가할 팟캐스트",
+ "HeaderPresets": "사전 설정",
+ "HeaderPreviewCover": "커버 미리보기",
+ "HeaderRSSFeedGeneral": "RSS 자세히",
+ "HeaderRSSFeedIsOpen": "RSS 피드가 열려 있습니다",
+ "HeaderRSSFeeds": "RSS 피드",
+ "HeaderRemoveEpisode": "에피소드 제거",
+ "HeaderRemoveEpisodes": "{0}개 에피소드 제거",
+ "HeaderSavedMediaProgress": "저장된 미디어 진행 상황",
+ "HeaderSchedule": "일정",
+ "HeaderScheduleEpisodeDownloads": "에피소드 자동 다운로드 일정 설정",
+ "HeaderScheduleLibraryScans": "라이브러리 자동 스캔 일정",
+ "HeaderSession": "세션",
+ "HeaderSetBackupSchedule": "백업 일정 설정",
+ "HeaderSettings": "설정",
+ "HeaderSettingsDisplay": "디스플레이",
+ "HeaderSettingsExperimental": "실험적 특징",
+ "HeaderSettingsGeneral": "일반",
+ "HeaderSettingsScanner": "스캐너",
+ "HeaderSettingsSecurity": "보안",
+ "HeaderSettingsWebClient": "웹 클라이언트",
+ "HeaderSleepTimer": "슬립 타이머",
+ "HeaderStatsLargestItems": "가장 큰 항목",
+ "HeaderStatsLongestItems": "가장 긴 항목(시간)",
+ "HeaderStatsMinutesListeningChart": "청취 시간(지난 7일)",
+ "HeaderStatsRecentSessions": "최근 세션",
+ "HeaderStatsTop10Authors": "상위 10명의 작가",
+ "HeaderStatsTop5Genres": "상위 5개 장르",
+ "HeaderTableOfContents": "목차",
+ "HeaderTools": "도구",
+ "HeaderUpdateAccount": "계정 업데이트",
+ "HeaderUpdateApiKey": "API 키 업데이트",
+ "HeaderUpdateAuthor": "작가 업데이트",
+ "HeaderUpdateDetails": "업데이트 세부 정보",
+ "HeaderUpdateLibrary": "라이브러리 업데이트y",
+ "HeaderUsers": "사용자",
+ "HeaderYearReview": "{0}년안의 리뷰",
+ "HeaderYourStats": "내 통계",
+ "LabelAbridged": "요약",
+ "LabelAbridgedChecked": "요약(확인됨)",
+ "LabelAbridgedUnchecked": "요약되지 않음(검토되지 않음)",
+ "LabelAccessibleBy": "접근 가능한",
+ "LabelAccountType": "계정 유형",
+ "LabelAccountTypeAdmin": "관리자",
+ "LabelAccountTypeGuest": "손님",
+ "LabelAccountTypeUser": "사용자",
+ "LabelActivities": "활동",
+ "LabelActivity": "활동적인",
+ "LabelAddToCollection": "컬렉션에 추가",
+ "LabelAddToCollectionBatch": "컬렉션에 {0}권의 책 추가",
+ "LabelAddToPlaylist": "재생목록에 추가",
+ "LabelAddToPlaylistBatch": "재생 목록에 {0}개 항목 추가",
+ "LabelAddedAt": "추가됨",
+ "LabelAddedDate": "{0} 추가됨",
+ "LabelAdminUsersOnly": "관리자 사용자만",
+ "LabelAll": "모두",
+ "LabelAllEpisodesDownloaded": "모든 에피소드 다운로드됨",
+ "LabelAllUsers": "모든 사용자",
+ "LabelAllUsersExcludingGuests": "게스트를 제외한 모든 사용자",
+ "LabelAllUsersIncludingGuests": "게스트를 포함한 모든 사용자",
+ "LabelAlreadyInYourLibrary": "이미 내 라이브러리에 있습니다",
+ "LabelApiKeyCreated": "API 키 \"{0}\"이(가) 성공적으로 생성되었습니다.",
+ "LabelApiKeyCreatedDescription": "API 키를 지금 복사해 두세요. 다시 볼 수 없게 됩니다.",
+ "LabelApiKeyUser": "사용자를 대신하여 행동",
+ "LabelApiKeyUserDescription": "이 API 키는 해당 API 키를 대신하는 사용자와 동일한 권한을 갖습니다. 이는 사용자가 요청을 하는 것과 동일하게 로그에 기록됩니다.",
+ "LabelApiToken": "API 토큰",
+ "LabelAppend": "첨부",
+ "LabelAudioBitrate": "오디오 비트레이트(예: 128k)",
+ "LabelAudioChannels": "오디오 채널(1 또는 2)",
+ "LabelAudioCodec": "오디오 코덱",
+ "LabelAuthor": "작가",
+ "LabelAuthorFirstLast": "작가 (이름 성)",
+ "LabelAuthorLastFirst": "작가 (성, 이름)",
+ "LabelAuthors": "작가",
+ "LabelAutoDownloadEpisodes": "에피소드 자동 다운로드",
+ "LabelAutoFetchMetadata": "메타데이터 자동 가져오기",
+ "LabelAutoFetchMetadataHelp": "업로드를 간소화하기 위해 제목, 저자, 시리즈에 대한 메타데이터를 가져옵니다. 업로드 후 추가 메타데이터를 일치시켜야 할 수도 있습니다.",
+ "LabelAutoLaunch": "자동 실행",
+ "LabelAutoLaunchDescription": "로그인 페이지로 이동할 때 자동으로 인증 공급자로 리디렉션합니다(수동 재정의 경로 /login?autoLaunch=0)",
+ "LabelAutoRegister": "자동 등록",
+ "LabelAutoRegisterDescription": "로그인 후 자동으로 새 사용자를 생성합니다.",
+ "LabelBackToUser": "사용자에게 돌아가기",
+ "LabelBackupAudioFiles": "오디오 파일 백업",
+ "LabelBackupLocation": "백업 위치",
+ "LabelBackupsEnableAutomaticBackups": "자동 백업",
+ "LabelBackupsEnableAutomaticBackupsHelp": "/metadata/backups에 저장된 백업",
+ "LabelBackupsMaxBackupSize": "최대 백업 크기(GB) (무제한의 경우 0)",
+ "LabelBackupsMaxBackupSizeHelp": "잘못된 구성에 대한 보호 조치로, 구성된 크기를 초과하면 백업이 실패합니다.",
+ "LabelBackupsNumberToKeep": "보관할 백업 수",
+ "LabelBackupsNumberToKeepHelp": "한 번에 1개의 백업만 제거되므로 이미 이보다 많은 백업이 있는 경우 수동으로 제거해야 합니다.",
+ "LabelBitrate": "비트레이트",
+ "LabelBonus": "보너스",
+ "LabelBooks": "전자책",
+ "LabelButtonText": "버튼 텍스트",
+ "LabelByAuthor": "by {0}",
+ "LabelChangePassword": "비밀번호 변경",
+ "LabelChannels": "채널",
+ "LabelChapterCount": "{0} 챕터",
+ "LabelChapterTitle": "챕터 제목",
+ "LabelChapters": "챕터",
+ "LabelChaptersFound": "챕터 발견됨",
+ "LabelClickForMoreInfo": "자세한 내용을 보려면 클릭",
+ "LabelClickToUseCurrentValue": "현재 값을 사용하려면 클릭",
+ "LabelClosePlayer": "플레이어 닫기",
+ "LabelCodec": "코덱",
+ "LabelCollapseSeries": "시리즈 펴기",
+ "LabelCollapseSubSeries": "하위 시리즈 축소",
+ "LabelCollection": "컬렉션",
+ "LabelCollections": "컬렉션",
+ "LabelComplete": "완료",
+ "LabelConfirmPassword": "비밀번호 확인",
+ "LabelContinueListening": "계속 듣기",
+ "LabelContinueReading": "계속 읽기",
+ "LabelContinueSeries": "시리즈 계속하기",
+ "LabelCorsAllowed": "허용된 CORS Origins",
+ "LabelCover": "커버",
+ "LabelCoverImageURL": "커버 이미지 URL",
+ "LabelCoverProvider": "커버 제공자",
+ "LabelCreatedAt": "생성일",
+ "LabelCronExpression": "Cron Expression",
+ "LabelCurrent": "현재",
+ "LabelCurrently": "현재:",
+ "LabelCustomCronExpression": "사용자 정의 Cron 표현식:",
+ "LabelDatetime": "일시",
+ "LabelDays": "일",
+ "LabelDeleteFromFileSystemCheckbox": "파일 시스템에서 삭제(데이터베이스에서만 제거하려면 선택 취소)",
+ "LabelDescription": "설명",
+ "LabelDeselectAll": "모두 선택 해제",
+ "LabelDetectedPattern": "감지된 패턴:",
+ "LabelDevice": "장치",
+ "LabelDeviceInfo": "장치 정보",
+ "LabelDeviceIsAvailableTo": "사용할 수 있는 장치...",
+ "LabelDirectory": "디렉토리",
+ "LabelDiscFromFilename": "파일 이름의 디스크",
+ "LabelDiscFromMetadata": "메타데이터의 디스크",
+ "LabelDiscover": "발견",
+ "LabelDownload": "다운로드",
+ "LabelDownloadNEpisodes": "{0}개 에피소드 다운로드",
+ "LabelDownloadable": "다운로드 가능",
+ "LabelDuration": "기간",
+ "LabelDurationComparisonExactMatch": "(정확히 일치)",
+ "LabelDurationComparisonLonger": "({0} 더 길음)",
+ "LabelDurationComparisonShorter": "({0} 더 짧음)",
+ "LabelDurationFound": "찾은 기간:",
+ "LabelEbook": "전자책",
+ "LabelEbooks": "전자책",
+ "LabelEdit": "편집",
+ "LabelEmail": "이메일",
+ "LabelEmailSettingsFromAddress": "보낸 사람 주소",
+ "LabelEmailSettingsRejectUnauthorized": "승인되지 않은 인증서 거부",
+ "LabelEmailSettingsRejectUnauthorizedHelp": "SSL 인증서 유효성 검사를 비활성화하면 중간자 공격과 같은 보안 위험에 노출될 수 있습니다. 이 옵션의 의미를 이해하고 연결하려는 메일 서버를 신뢰할 수 있는 경우에만 비활성화하세요.",
+ "LabelEmailSettingsSecure": "보안",
+ "LabelEmailSettingsSecureHelp": "true이면 서버에 연결할 때 TLS를 사용합니다. false이면 서버가 STARTTLS 확장을 지원하는 경우 TLS가 사용됩니다. 대부분의 경우 포트 465에 연결하는 경우 이 값을 true로 설정하고, 포트 587 또는 25를 사용하는 경우 false로 설정합니다. (nodemailer.com/smtp/#authentication에서 발췌)",
+ "LabelEmailSettingsTestAddress": "테스트 주소",
+ "LabelEmbeddedCover": "내장형 커버",
+ "LabelEnable": "활성화",
+ "LabelEncodingBackupLocation": "원본 오디오 파일의 백업이 저장되는 위치 :",
+ "LabelEncodingChaptersNotEmbedded": "다중 트랙 오디오북에는 챕터가 포함되지 않습니다..",
+ "LabelEncodingClearItemCache": "주기적으로 항목 캐시를 삭제하세요.",
+ "LabelEncodingFinishedM4B": "완성된 M4B는 오디오북 저장 폴더 위치:",
+ "LabelEncodingInfoEmbedded": "메타데이터는 오디오북 폴더 내의 오디오 트랙에 포함됩니다.",
+ "LabelEncodingStartedNavigation": "작업이 시작되면 이 페이지에서 벗어날 수 있습니다.",
+ "LabelEncodingTimeWarning": "인코딩은 최대 30분이 걸릴 수 있습니다.",
+ "LabelEncodingWarningAdvancedSettings": "경고: ffmpeg 인코딩 옵션에 익숙하지 않은 경우 이러한 설정을 업데이트하지 마세요.",
+ "LabelEncodingWatcherDisabled": "시청 기능을 비활성화한 경우 나중에 오디오북을 다시 스캔해야 합니다.",
+ "LabelEnd": "마지막",
+ "LabelEndOfChapter": "챕터의 마지작",
+ "LabelEpisode": "에피소드",
+ "LabelEpisodeNotLinkedToRssFeed": "에피소드가 RSS 피드에 연결되지 않았습니다.",
+ "LabelEpisodeNumber": "에피소드 #{0}",
+ "LabelEpisodeTitle": "에피소드 제목",
+ "LabelEpisodeType": "에피소드 유형",
+ "LabelEpisodeUrlFromRssFeed": "RSS 피드의 에피소드 URL",
+ "LabelEpisodes": "에피소드",
+ "LabelEpisodic": "에피소드식",
+ "LabelExample": "예제",
+ "LabelExpandSeries": "시리즈 확장",
+ "LabelExpandSubSeries": "하위 시리즈 확장",
+ "LabelExpired": "만료됨",
+ "LabelExpiresAt": "만료일",
+ "LabelExpiresInSeconds": "(초) 후에 만료됩니다",
+ "LabelExpiresNever": "Never",
+ "LabelExplicit": "명백한",
+ "LabelExplicitChecked": "명시적(확인됨)",
+ "LabelExplicitUnchecked": "명시적이지 않음(체크되지 않음)",
+ "LabelExportOPML": "OPML 내보내기",
+ "LabelFeedURL": "피드 URL",
+ "LabelFetchingMetadata": "메타데이터 가져오는중",
+ "LabelFile": "파일",
+ "LabelFileBirthtime": "파일 생년월일",
+ "LabelFileBornDate": "생성된 {0}",
+ "LabelFileModified": "파일 수정됨",
+ "LabelFileModifiedDate": "수정된 {0}",
+ "LabelFilename": "파일 이름",
+ "LabelFilterByUser": "사용자별 필터링",
+ "LabelFindEpisodes": "에피소드 찾기",
+ "LabelFinished": "완료됨",
+ "LabelFinishedDate": "완료 {0}",
+ "LabelFolder": "폴더",
+ "LabelFolders": "폴더",
+ "LabelFontBold": "진하게",
+ "LabelFontBoldness": "글꼴 굵기",
+ "LabelFontFamily": "글꼴 모양",
+ "LabelFontItalic": "이탤릭체",
+ "LabelFontScale": "글꼴 크기",
+ "LabelFontStrikethrough": "취소선",
+ "LabelFormat": "형식",
+ "LabelFull": "전체",
+ "LabelGenre": "장르",
+ "LabelGenres": "장르",
+ "LabelHardDeleteFile": "파일 하드 삭제",
+ "LabelHasEbook": "보유한 전자책",
+ "LabelHasSupplementaryEbook": "보유한 보충 전자책",
+ "LabelHideSubtitles": "자막 숨기기",
+ "LabelHighestPriority": "가장 높은 우선순위",
+ "LabelHost": "호스트",
+ "LabelHour": "시간",
+ "LabelHours": "시간",
+ "LabelIcon": "아이콘",
+ "LabelImageURLFromTheWeb": "웹의 이미지 URL",
+ "LabelInProgress": "진행 중",
+ "LabelIncludeInTracklist": "트랙리스트에 포함",
+ "LabelIncomplete": "불완전한",
+ "LabelInterval": "간격",
+ "LabelIntervalCustomDailyWeekly": "사용자 정의 일일/주간",
+ "LabelIntervalEvery12Hours": "12시간마다",
+ "LabelIntervalEvery15Minutes": "15분마다",
+ "LabelIntervalEvery2Hours": "2시간마다",
+ "LabelIntervalEvery30Minutes": "30분마다",
+ "LabelIntervalEvery6Hours": "6시간마다",
+ "LabelIntervalEveryDay": "매일",
+ "LabelIntervalEveryHour": "매 시간마다",
+ "LabelIntervalEveryMinute": "매 분마다",
+ "LabelInvert": "반전",
+ "LabelItem": "항목",
+ "LabelJumpBackwardAmount": "뒤로 점프",
+ "LabelJumpForwardAmount": "앞으로 점프",
+ "LabelLanguage": "언어",
+ "LabelLanguageDefaultServer": "기본 서버 언어",
+ "LabelLanguages": "언어",
+ "LabelLastBookAdded": "마지막으로 추가된 책",
+ "LabelLastBookUpdated": "마지막 책 업데이트됨",
+ "LabelLastProgressDate": "마지막 진행 상황: {0}",
+ "LabelLastSeen": "마지막으로 본",
+ "LabelLastTime": "지난번",
+ "LabelLastUpdate": "마지막 업데이트",
+ "LabelLayout": "레이아웃",
+ "LabelLayoutSinglePage": "단일 페이지",
+ "LabelLayoutSplitPage": "분할 페이지",
+ "LabelLess": "Less",
+ "LabelLibrariesAccessibleToUser": "사용자가 접근 가능한 라이브러리",
+ "LabelLibrary": "라이브러리",
+ "LabelLibraryFilterSublistEmpty": "{0} 없음",
+ "LabelLibraryItem": "라이브러리 항목",
+ "LabelLibraryName": "라이브러리 이름",
+ "LabelLibrarySortByProgress": "진행상황: 마지막 업데이트",
+ "LabelLibrarySortByProgressFinished": "진행 상황: 완료",
+ "LabelLibrarySortByProgressStarted": "진행 상황: 시작됨",
+ "LabelLimit": "한계",
+ "LabelLineSpacing": "줄 간격",
+ "LabelListenAgain": "다시 듣기",
+ "LabelLogLevelDebug": "디버그",
+ "LabelLogLevelInfo": "정보",
+ "LabelLogLevelWarn": "경고",
+ "LabelLookForNewEpisodesAfterDate": "이 날짜 이후의 새로운 에피소드를 찾아보세요",
+ "LabelLowestPriority": "가장 낮은 우선순위",
+ "LabelMatchConfidence": "신뢰",
+ "LabelMatchExistingUsersBy": "기존 사용자와 일치시키기",
+ "LabelMatchExistingUsersByDescription": "기존 사용자 연결에 사용됩니다. 연결되면 사용자는 SSO 제공업체의 고유 ID와 매칭됩니다.",
+ "LabelMaxEpisodesToDownload": "다운로드 가능한 최대 # 에피소드 수. 무제한을 원하시면 0을 사용하세요.",
+ "LabelMaxEpisodesToDownloadPerCheck": "체크당 다운로드 가능한 최대 신규 # 에피소드 수",
+ "LabelMaxEpisodesToKeep": "보관할 최대 # 에피소드 수",
+ "LabelMaxEpisodesToKeepHelp": "값이 0이면 최대 제한이 없습니다. 새 에피소드가 자동으로 다운로드된 후, 에피소드가 X개 이상인 경우 가장 오래된 에피소드부터 삭제됩니다. 새로 다운로드할 때마다 에피소드 1개만 삭제됩니다.",
+ "LabelMediaPlayer": "미디어 플레이어",
+ "LabelMediaType": "미디어 유형",
+ "LabelMetaTag": "메타 태그",
+ "LabelMetaTags": "메타 태그",
+ "LabelMetadataOrderOfPrecedenceDescription": "우선순위가 높은 메타데이터 소스는 우선순위가 낮은 메타데이터 소스를 재정의합니다.",
+ "LabelMetadataProvider": "메타데이터 공급자",
+ "LabelMinute": "분",
+ "LabelMinutes": "분",
+ "LabelMissing": "누락",
+ "LabelMissingEbook": "전자책이 없습니다",
+ "LabelMissingSupplementaryEbook": "보충 전자책이 없습니다",
+ "LabelMobileRedirectURIs": "허용된 모바일 리디렉션 URI",
+ "LabelMobileRedirectURIsDescription": "모바일 앱에 유효한 리디렉션 URI 허용 목록입니다. 기본 허용 목록은 audiobookshelf://oauth이며, 타사 앱 통합을 위해 추가 URI로 보완하거나 삭제할 수 있습니다. 별표(*)만 사용하면 모든 URI가 허용됩니다.",
+ "LabelMore": "더보기",
+ "LabelMoreInfo": "정보 더 보기",
+ "LabelName": "이름",
+ "LabelNarrator": "내레이터",
+ "LabelNarrators": "내레이터",
+ "LabelNew": "신규",
+ "LabelNewPassword": "새 비밀번호",
+ "LabelNewestAuthors": "최신 작가",
+ "LabelNewestEpisodes": "최신 에피소드",
+ "LabelNextBackupDate": "다음 백업 날짜",
+ "LabelNextChapters": "다음장은 다음과 같음:",
+ "LabelNextScheduledRun": "다음 예정된 실행",
+ "LabelNoApiKeys": "API 키 없음",
+ "LabelNoCustomMetadataProviders": "사용자 정의 메타데이터 공급자 없음",
+ "LabelNoEpisodesSelected": "선택된 에피소드가 없습니다",
+ "LabelNotFinished": "완료되지 않음",
+ "LabelNotStarted": "시작되지 않음",
+ "LabelNotes": "노트",
+ "LabelNotificationAppriseURL": "Apprise URL(s)",
+ "LabelNotificationAvailableVariables": "사용 가능한 변수",
+ "LabelNotificationBodyTemplate": "본문 템플릿",
+ "LabelNotificationEvent": "알림 이벤트",
+ "LabelNotificationTitleTemplate": "제목 템플릿",
+ "LabelNotificationsMaxFailedAttempts": "최대 실패 시도 횟수",
+ "LabelNotificationsMaxFailedAttemptsHelp": "이 횟수만큼 알림을 보내지 못하면 알림이 비활성화됩니다.",
+ "LabelNotificationsMaxQueueSize": "알림 이벤트에 대한 최대 대기열 크기",
+ "LabelNotificationsMaxQueueSizeHelp": "이벤트는 초당 1회로 제한됩니다. 대기열 크기가 최대치에 도달하면 이벤트가 무시됩니다. 이를 통해 알림 스팸을 방지할 수 있습니다.",
+ "LabelNumberOfBooks": "전자책의 수",
+ "LabelNumberOfChapters": "챕터의 수:",
+ "LabelNumberOfEpisodes": "# 의 에피소드",
+ "LabelOpenIDAdvancedPermsClaimDescription": "애플리케이션 내 사용자 작업에 대한 고급 권한을 포함하는 OpenID 클레임의 이름입니다. 이 권한은 관리자가 아닌 역할(구성된 경우)에 적용됩니다. 응답에서 클레임이 누락되면 ABS에 대한 액세스가 거부됩니다. 단일 옵션이 누락되면 false로 처리됩니다. ID 공급자의 클레임이 예상 구조와 일치하는지 확인하세요.",
+ "LabelOpenIDClaims": "고급 그룹 및 권한 할당을 비활성화하고 자동으로 '사용자' 그룹을 할당하려면 다음 옵션을 비워 두세요.",
+ "LabelOpenIDGroupClaimDescription": "사용자 그룹 목록이 포함된 OpenID 클레임의 이름입니다. 일반적으로 그룹이라고 합니다. 구성된 경우, 애플리케이션은 사용자의 그룹 멤버십을 기반으로 역할을 자동으로 할당합니다. 단, 클레임에서 그룹 이름이 대소문자를 구분하지 않고 'admin', 'user' 또는 'guest'로 지정되어야 합니다. 클레임에는 목록이 포함되어야 하며, 사용자가 여러 그룹에 속하는 경우 애플리케이션은 가장 높은 수준의 액세스 권한에 해당하는 역할을 할당합니다. 일치하는 그룹이 없으면 액세스가 거부됩니다.",
+ "LabelOpenRSSFeed": "RSS 피드 열기",
+ "LabelOverwrite": "덮어쓰기",
+ "LabelPaginationPageXOfY": "{1} 중 {0}페이지",
+ "LabelPassword": "비밀번호",
+ "LabelPath": "경로",
+ "LabelPermanent": "영구적인",
+ "LabelPermissionsAccessAllLibraries": "모든 라이브러리에 접속 가능",
+ "LabelPermissionsAccessAllTags": "모든 태그에 액세스 가능",
+ "LabelPermissionsAccessExplicitContent": "명시적 콘텐츠에 액세스할 수 있음",
+ "LabelPermissionsCreateEreader": "전자책 리더기를 만들기 가능",
+ "LabelPermissionsDelete": "삭제 가능",
+ "LabelPermissionsDownload": "다운로드 가능",
+ "LabelPermissionsUpdate": "엡데이트 가능",
+ "LabelPermissionsUpload": "업로드 가능",
+ "LabelPersonalYearReview": "올해의 리뷰 ({0})",
+ "LabelPhotoPathURL": "사진 경로/URL",
+ "LabelPlayMethod": "플레이 방법",
+ "LabelPlaybackRateIncrementDecrement": "재생 속도 증가/감소량",
+ "LabelPlayerChapterNumberMarker": "{0} of {1}",
+ "LabelPlaylists": "재생목록",
+ "LabelPodcast": "팟케스트",
+ "LabelPodcastSearchRegion": "팟캐스트 검색 영역",
+ "LabelPodcastType": "팟캐스트 유형",
+ "LabelPodcasts": "팟케스트",
+ "LabelPort": "포트",
+ "LabelPrefixesToIgnore": "무시할 접두사(대소문자 구분 없음)",
+ "LabelPreventIndexing": "iTunes 및 Google Podcast 디렉토리에서 피드가 인덱싱되는 것을 방지합니다.",
+ "LabelPrimaryEbook": "기본 전자책",
+ "LabelProgress": "진행",
+ "LabelProvider": "공급자",
+ "LabelProviderAuthorizationValue": "권한 헤더 값",
+ "LabelPubDate": "출판 날짜",
+ "LabelPublishYear": "출판 연도",
+ "LabelPublishedDate": "출판됨 {0}",
+ "LabelPublishedDecade": "출판된지 10년",
+ "LabelPublishedDecades": "출판된지 10년이상",
+ "LabelPublisher": "출판사",
+ "LabelPublishers": "출판사",
+ "LabelRSSFeedCustomOwnerEmail": "사용자 정의 이메일",
+ "LabelRSSFeedCustomOwnerName": "사용자 정의 이름",
+ "LabelRSSFeedOpen": "RSS 피드 열기",
+ "LabelRSSFeedPreventIndexing": "인덱싱 방지",
+ "LabelRSSFeedSlug": "RSS 피드 슬러그",
+ "LabelRSSFeedURL": "RSS 피드 URL",
+ "LabelRandomly": "무작위로",
+ "LabelReAddSeriesToContinueListening": "계속 듣기 위해 시리즈를 다시 추가하세요",
+ "LabelRead": "읽기",
+ "LabelReadAgain": "다시 읽기",
+ "LabelReadEbookWithoutProgress": "진행 상황을 유지하지 않고 전자책을 읽으세요",
+ "LabelRecentSeries": "최근 시리즈",
+ "LabelRecentlyAdded": "최근 추가됨",
+ "LabelRecommended": "추천",
+ "LabelRedo": "되돌리기",
+ "LabelRegion": "지역",
+ "LabelReleaseDate": "출시일",
+ "LabelRemoveAllMetadataAbs": "모든 metadata.abs 파일을 제거합니다.",
+ "LabelRemoveAllMetadataJson": "모든 metadata.json 파일을 제거합니다.",
+ "LabelRemoveAudibleBranding": "챕터에서 Audible 인트로와 아웃트로 제거",
+ "LabelRemoveCover": "커버 제거",
+ "LabelRemoveMetadataFile": "라이브러리 항목 폴더에서 메타데이터 파일 제거",
+ "LabelRemoveMetadataFileHelp": "{0} 폴더에서 모든 metadata.json 및 metadata.abs 파일을 제거합니다.",
+ "LabelRowsPerPage": "페이지당 행 수",
+ "LabelSearchTerm": "검색어",
+ "LabelSearchTitle": "제목 검색",
+ "LabelSearchTitleOrASIN": "제목 또는 ASIN 검색",
+ "LabelSeason": "시즌",
+ "LabelSeasonNumber": "시즌 #{0}",
+ "LabelSelectAll": "모두 선택",
+ "LabelSelectAllEpisodes": "모든 에피소드 선택",
+ "LabelSelectEpisodesShowing": "{0}개의 에피소드를 선택",
+ "LabelSelectUser": "사용자 선택",
+ "LabelSelectUsers": "사용자 선택",
+ "LabelSendEbookToDevice": "...에 전자책을 보내기",
+ "LabelSequence": "순서",
+ "LabelSerial": "일련번호",
+ "LabelSeries": "시리즈",
+ "LabelSeriesName": "시리즈 이름",
+ "LabelSeriesProgress": "시리즈 진행 상황",
+ "LabelServerLogLevel": "서버 로그 수준",
+ "LabelServerYearReview": "서버 연간 리뷰 ({0})",
+ "LabelSetEbookAsPrimary": "기본으로 설정",
+ "LabelSetEbookAsSupplementary": "보충으로 설정",
+ "LabelSettingsAllowIframe": "iframe에 포함 허용",
+ "LabelSettingsAudiobooksOnly": "오디오북만",
+ "LabelSettingsAudiobooksOnlyHelp": "이 설정을 활성화하면 오디오북 폴더 내부에 있는 경우를 제외하고 전자책 파일이 무시되며, 오디오북 폴더 내부에 있는 경우 해당 파일은 보충 전자책으로 설정됩니다.",
+ "LabelSettingsBookshelfViewHelp": "나무 선반을 활용한 스큐어모픽 디자인",
+ "LabelSettingsChromecastSupport": "Chromecast 지원",
+ "LabelSettingsDateFormat": "날짜 형식",
+ "LabelSettingsEnableWatcher": "라이브러리의 변경 사항을 자동으로 감시",
+ "LabelSettingsEnableWatcherForLibrary": "라이브러리의 변경 사항을 자동으로 감시",
+ "LabelSettingsEnableWatcherHelp": "파일 변경 사항이 감지되면 항목을 자동으로 추가/업데이트합니다. *서버 재시작이 필요합니다.",
+ "LabelSettingsEpubsAllowScriptedContent": "epub에서 스크립트 콘텐츠 허용",
+ "LabelSettingsEpubsAllowScriptedContentHelp": "epub 파일에서 스크립트를 실행하도록 허용합니다. epub 파일 출처를 신뢰하지 않는 한 이 설정을 비활성화하는 것이 좋습니다.",
+ "LabelSettingsExperimentalFeatures": "실험적 특징",
+ "LabelSettingsExperimentalFeaturesHelp": "여러분의 피드백과 테스트에 도움이 될 만한 개발 중인 기능입니다. 클릭하여 Github 토론을 시작하세요.",
+ "LabelSettingsFindCovers": "커버 찾기",
+ "LabelSettingsFindCoversHelp": "오디오북에 내장된 표지나 폴더 내에 표지 이미지가 없는 경우 스캐너는 표지를 찾으려고 시도합니다.
참고: 이렇게 하면 스캔 시간이 길어집니다.",
+ "LabelSettingsHideSingleBookSeries": "단일 책 시리즈 숨기기",
+ "LabelSettingsHideSingleBookSeriesHelp": "단일 책으로 구성된 시리즈는 시리즈 페이지와 홈페이지 선반에서 숨겨집니다.",
+ "LabelSettingsHomePageBookshelfView": "홈페이지 책꽂이 보기 사용",
+ "LabelSettingsLibraryBookshelfView": "라이브러리에 bookshelf view 사용",
+ "LabelSettingsLibraryMarkAsFinishedPercentComplete": "완료율이 다음보다 큼",
+ "LabelSettingsLibraryMarkAsFinishedTimeRemaining": "남은 시간은 (초) 미만입니다.",
+ "LabelSettingsLibraryMarkAsFinishedWhen": "미디어 항목을 완료된 것으로 표시",
+ "LabelSettingsOnlyShowLaterBooksInContinueSeries": "Continue Series의 이전 책 건너뛰기",
+ "LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "시리즈 계속하기 홈페이지 선반에는 시리즈 중 아직 시작하지 않은 첫 번째 책이 표시됩니다. 시리즈 중 최소 한 권은 완료되었고 진행 중인 책은 없습니다. 이 설정을 활성화하면 시작하지 않은 첫 번째 책 대신 가장 늦게 완료된 책부터 시리즈가 이어집니다.",
+ "LabelSettingsParseSubtitles": "자막 파싱",
+ "LabelSettingsParseSubtitlesHelp": "오디오북 폴더 이름에서 자막을 추출합니다.
자막은 \" - \"로 구분해야 합니다.
즉, \"책 제목 - 여기에 자막이 있습니다\"에는 \"여기에 자막이 있습니다\"라는 자막이 있습니다.",
+ "LabelSettingsPreferMatchedMetadata": "일치하는 메타데이터를 선호",
+ "LabelSettingsPreferMatchedMetadataHelp": "빠른 매칭을 사용하면 매칭된 데이터가 항목 세부 정보보다 우선합니다. 기본적으로 빠른 매칭은 누락된 세부 정보만 채웁니다.",
+ "LabelSettingsSkipMatchingBooksWithASIN": "이미 ASIN이 있는 일치하는 책을 건너뜁니다.",
+ "LabelSettingsSkipMatchingBooksWithISBN": "이미 ISBN이 있는 일치하는 책을 건너뜁니다.",
+ "LabelSettingsSortingIgnorePrefixes": "정렬 시 접두사 무시",
+ "LabelSettingsSortingIgnorePrefixesHelp": "즉, 접두사 \"the\"의 경우 책 제목 \"The Book Title\"은 \"Book Title, The\"로 정렬됩니다.",
+ "LabelSettingsSquareBookCovers": "정사각형 책 표지를 사용",
+ "LabelSettingsSquareBookCoversHelp": "표준 1.6:1 책 표지보다 정사각형 표지를 사용하는 것을 선호합니다.",
+ "LabelSettingsStoreCoversWithItem": "항목에 있는 커버로 저장",
+ "LabelSettingsStoreCoversWithItemHelp": "기본적으로 표지는 /metadata/items에 저장됩니다. 이 설정을 활성화하면 표지가 라이브러리 항목 폴더에 저장됩니다. \"cover\"라는 이름의 파일 하나만 저장됩니다.",
+ "LabelSettingsStoreMetadataWithItem": "항목과 함께 메타데이터 저장",
+ "LabelSettingsStoreMetadataWithItemHelp": "기본적으로 메타데이터 파일은 /metadata/items에 저장되며 이 설정을 활성화하면 라이브러리 항목 폴더에 메타데이터 파일이 저장됩니다.",
+ "LabelSettingsTimeFormat": "시간 형식",
+ "LabelShare": "공유",
+ "LabelShareDownloadableHelp": "공유 링크가 있는 사용자는 라이브러리 항목의 zip 파일을 다운로드할 수 있습니다.",
+ "LabelShareOpen": "공유 열기",
+ "LabelShareURL": "URL 공유",
+ "LabelShowAll": "모두 표시",
+ "LabelShowSeconds": "초 표시",
+ "LabelShowSubtitles": "자막 표시",
+ "LabelSize": "크기",
+ "LabelSleepTimer": "슬립 타이머",
+ "LabelSlug": "Slug",
+ "LabelSortAscending": "오름차순",
+ "LabelSortDescending": "내림차순",
+ "LabelSortPubDate": "게시 날짜 정렬",
+ "LabelStart": "시작",
+ "LabelStartTime": "시작 시간",
+ "LabelStarted": "시작됨",
+ "LabelStartedAt": "시작일",
+ "LabelStartedDate": "{0} 시작됨",
+ "LabelStatsAudioTracks": "오디오 트랙",
+ "LabelStatsAuthors": "작가",
+ "LabelStatsBestDay": "최고의 날",
+ "LabelStatsDailyAverage": "일일 평균",
+ "LabelStatsDays": "일",
+ "LabelStatsDaysListened": "청취 일수",
+ "LabelStatsHours": "시간",
+ "LabelStatsInARow": "in a row",
+ "LabelStatsItemsFinished": "완료된 항목",
+ "LabelStatsItemsInLibrary": "라이브러리의 항목",
+ "LabelStatsMinutes": "분",
+ "LabelStatsMinutesListening": "청취 분수",
+ "LabelStatsOverallDays": "전체 일수",
+ "LabelStatsOverallHours": "전체 시간",
+ "LabelStatsWeekListening": "주간 청취",
+ "LabelSubtitle": "자막",
+ "LabelSupportedFileTypes": "지원되는 파일 유형",
+ "LabelTag": "태그",
+ "LabelTags": "태그",
+ "LabelTagsAccessibleToUser": "사용자가 접근 가능한 태그",
+ "LabelTagsNotAccessibleToUser": "사용자가 액세스할 수 없는 태그",
+ "LabelTasks": "실행 중인 작업",
+ "LabelTextEditorBulletedList": "글머리 기호 목록",
+ "LabelTextEditorLink": "링크",
+ "LabelTextEditorNumberedList": "번호가 매겨진 목록",
+ "LabelTextEditorUnlink": "언링크",
+ "LabelTheme": "테마",
+ "LabelThemeDark": "Dark",
+ "LabelThemeLight": "Light",
+ "LabelThemeSepia": "Sepia",
+ "LabelTimeBase": "시간 기준",
+ "LabelTimeDurationXHours": "{0}시간",
+ "LabelTimeDurationXMinutes": "{0}분",
+ "LabelTimeDurationXSeconds": "{0}초",
+ "LabelTimeInMinutes": "시간(분)",
+ "LabelTimeLeft": "{0}개 남음",
+ "LabelTimeListened": "들은 시간",
+ "LabelTimeListenedToday": "오늘 청취 시간",
+ "LabelTimeRemaining": "{0} 남음",
+ "LabelTimeToShift": "이동 시간(초)",
+ "LabelTitle": "제목",
+ "LabelToolsEmbedMetadata": "메타데이터 삽입",
+ "LabelToolsEmbedMetadataDescription": "오디오 파일에 커버 이미지와 챕터를 포함한 메타데이터를 포함합니다.",
+ "LabelToolsM4bEncoder": "M4B 인코더",
+ "LabelToolsMakeM4b": "M4B 오디오북 파일 만들기",
+ "LabelToolsMakeM4bDescription": "내장된 메타데이터, 표지 이미지, 챕터가 포함된 .M4B 오디오북 파일을 생성합니다.",
+ "LabelToolsSplitM4b": "M4B를 MP3로 분할",
+ "LabelToolsSplitM4bDescription": "챕터별로 분할된 M4B에서 내장된 메타데이터, 표지 이미지, 챕터를 사용하여 MP3를 만듭니다.",
+ "LabelTotalDuration": "총 기간",
+ "LabelTotalTimeListened": "총 청취 시간",
+ "LabelTrackFromFilename": "파일 이름에서 추적",
+ "LabelTrackFromMetadata": "메타데이터에서 추적",
+ "LabelTracks": "트랙",
+ "LabelTracksMultiTrack": "멀티트랙",
+ "LabelTracksNone": "트랙 없음",
+ "LabelTracksSingleTrack": "싱글트랙",
+ "LabelTrailer": "트레일러",
+ "LabelType": "유형",
+ "LabelUnabridged": "완본",
+ "LabelUndo": "실행 취소",
+ "LabelUnknown": "미상",
+ "LabelUnknownPublishDate": "알 수 없는 게시 날짜",
+ "LabelUpdateCover": "커버 업데이트",
+ "LabelUpdateCoverHelp": "일치하는 항목이 발견되면 선택한 책의 기존 표지를 덮어쓸 수 있습니다.",
+ "LabelUpdateDetails": "세부 정보 업데이트",
+ "LabelUpdateDetailsHelp": "일치하는 항목이 발견되면 선택한 책에 대한 기존 세부 정보를 덮어쓸 수 있습니다.",
+ "LabelUpdatedAt": "업데이트됨",
+ "LabelUploaderDragAndDrop": "파일 또는 폴더 드래그 앤 드롭",
+ "LabelUploaderDragAndDropFilesOnly": "드래그 앤 드롭 파일",
+ "LabelUploaderDropFiles": "파일 드롭",
+ "LabelUploaderItemFetchMetadataHelp": "제목, 저자 및 시리즈를 자동으로 가져옵니다.",
+ "LabelUseAdvancedOptions": "고급 옵션 사용",
+ "LabelUseChapterTrack": "챕터 트랙 사용",
+ "LabelUseFullTrack": "전체 트랙 사용",
+ "LabelUseZeroForUnlimited": "무제한을 원하시면 0을 사용",
+ "LabelUser": "사용자",
+ "LabelUsername": "사용자 이름",
+ "LabelValue": "값",
+ "LabelVersion": "버전",
+ "LabelViewBookmarks": "북마크 보기",
+ "LabelViewChapters": "챕터 보기",
+ "LabelViewPlayerSettings": "플레이어 설정 보기",
+ "LabelViewQueue": "플레이어 대기열 보기",
+ "LabelVolume": "볼륨",
+ "LabelWebRedirectURLsDescription": "로그인 후 웹 앱으로 다시 리디렉션할 수 있도록 OAuth 공급자에서 다음 URL을 승인하세요.",
+ "LabelWebRedirectURLsSubfolder": "리디렉션 URL의 하위 폴더",
+ "LabelWeekdaysToRun": "주중에 실행",
+ "LabelXBooks": "{0}권의 책",
+ "LabelXItems": "{0}개 항목",
+ "LabelYearReviewHide": "연간 리뷰 숨기기",
+ "LabelYearReviewShow": "올해의 리뷰 보기",
+ "LabelYourAudiobookDuration": "오디오북 길이",
+ "LabelYourBookmarks": "내 북마크",
+ "LabelYourPlaylists": "내 재생목록",
+ "LabelYourProgress": "내 진행 상황",
+ "MessageAddToPlayerQueue": "플레이어 대기열에 추가",
+ "MessageAppriseDescription": "이 기능을 사용하려면 Apprise API 인스턴스를 실행 중이거나 동일한 요청을 처리할 API가 필요합니다.
Apprise API URL은 알림을 보낼 전체 URL 경로여야 합니다. 예를 들어, API 인스턴스가 http://192.168.1.1:8337에서 제공되는 경우 http://192.168.1.1:8337/notify를 입력합니다.",
+ "MessageAsinCheck": "Amazon이 아닌 올바른 Audible 지역의 ASIN을 사용하고 있는지 확인하세요.",
+ "MessageAuthenticationLegacyTokenWarning": "기존 API 토큰은 향후 제거될 예정입니다. 대신 API 키를 사용하세요.",
+ "MessageAuthenticationOIDCChangesRestart": "OIDC 변경 사항을 적용하려면 저장한 후 서버를 다시 시작하세요.",
+ "MessageAuthenticationSecurityMessage": "보안을 위해 인증 방식이 개선되었습니다. 모든 사용자는 다시 로그인해야 합니다.",
+ "MessageBackupsDescription": "백업에는 사용자, 사용자 진행 상황, 라이브러리 항목 세부 정보, 서버 설정, 그리고 /metadata/items 및 /metadata/authors에 저장된 이미지가 포함됩니다. 라이브러리 폴더에 저장된 파일은 백업에 포함되지 않습니다.",
+ "MessageBackupsLocationEditNote": "참고: 백업 위치를 업데이트해도 기존 백업은 이동되거나 수정되지 않습니다.",
+ "MessageBackupsLocationNoEditNote": "참고: 백업 위치는 환경 변수를 통해 설정되며 여기서는 변경할 수 없습니다.",
+ "MessageBackupsLocationPathEmpty": "백업 위치 경로는 비워둘 수 없습니다.",
+ "MessageBatchEditPopulateMapDetailsAllHelp": "활성화된 필드에 모든 항목의 데이터를 채웁니다. 여러 값이 있는 필드는 병합됩니다.",
+ "MessageBatchEditPopulateMapDetailsItemHelp": "이 항목의 데이터로 활성화된 지도 세부 정보 필드를 채웁니다.",
+ "MessageBatchQuickMatchDescription": "빠른 매칭은 선택한 항목에 대해 누락된 표지와 메타데이터를 추가하려고 시도합니다. 빠른 매칭이 기존 표지 및/또는 메타데이터를 덮어쓰도록 허용하려면 아래 옵션을 활성화하세요.",
+ "MessageBookshelfNoCollections": "아직 컬렉션을 만들지 않았습니다.",
+ "MessageBookshelfNoCollectionsHelp": "소장 자료는 공개되어 있습니다. 라이브러리에 접속할 수 있는 모든 이용자가 열람할 수 있습니다.",
+ "MessageBookshelfNoRSSFeeds": "RSS 피드가 열려 있지 않습니다",
+ "MessageBookshelfNoResultsForFilter": "필터 \"{0}: {1}\"에 대한 결과가 없습니다.",
+ "MessageBookshelfNoResultsForQuery": "쿼리에 대한 결과가 없습니다",
+ "MessageBookshelfNoSeries": "시리즈가 없습니다",
+ "MessageBulkChapterPattern": "이 번호 매기기 패턴을 사용하여 몇 개의 챕터를 추가하시겠습니까?",
+ "MessageChapterEndIsAfter": "오디오북이 끝난 후 챕터가 끝납니다.",
+ "MessageChapterErrorFirstNotZero": "첫 번째 챕터는 0에서 시작해야 합니다.",
+ "MessageChapterErrorStartGteDuration": "잘못된 시작 시간은 오디오북 길이보다 짧아야 합니다.",
+ "MessageChapterErrorStartLtPrev": "잘못된 시작 시간은 이전 챕터 시작 시간보다 크거나 같아야 합니다.",
+ "MessageChapterStartIsAfter": "챕터 시작은 오디오북이 끝난 후입니다.",
+ "MessageChaptersNotFound": "챕터를 찾을 수 없습니다",
+ "MessageCheckingCron": "cron을 확인 중입니다...",
+ "MessageConfirmCloseFeed": "이 피드를 닫으시겠습니까?",
+ "MessageConfirmDeleteApiKey": "API 키 \"{0}\"을 삭제하시겠습니까?",
+ "MessageConfirmDeleteBackup": "{0}에 대한 백업을 삭제하시겠습니까?",
+ "MessageConfirmDeleteDevice": "e-reader 장치 \"{0}\"을(를) 삭제하시겠습니까?",
+ "MessageConfirmDeleteFile": "이렇게 하면 파일 시스템에서 파일이 삭제됩니다. 정말 삭제하시겠습니까?",
+ "MessageConfirmDeleteLibrary": "라이브러리 \"{0}\"을 영구적으로 삭제하시겠습니까?",
+ "MessageConfirmDeleteLibraryItem": "이렇게 하면 라이브러리 항목이 데이터베이스와 파일 시스템에서 삭제됩니다. 정말 삭제하시겠습니까?",
+ "MessageConfirmDeleteLibraryItems": "이렇게 하면 데이터베이스와 파일 시스템에서 {0}개의 라이브러리 항목이 삭제됩니다. 정말 삭제하시겠습니까?",
+ "MessageConfirmDeleteMetadataProvider": "사용자 지정 메타데이터 공급자 \"{0}\"을(를) 삭제하시겠습니까?",
+ "MessageConfirmDeleteNotification": "이 알림을 삭제하시겠습니까?",
+ "MessageConfirmDeleteSession": "이 세션을 삭제하시겠습니까?",
+ "MessageConfirmEmbedMetadataInAudioFiles": "{0} 오디오 파일에 메타데이터를 포함하시겠습니까?",
+ "MessageConfirmForceReScan": "강제로 다시 스캔하시겠습니까?",
+ "MessageConfirmMarkAllEpisodesFinished": "모든 에피소드를 완료로 표시하시겠습니까?",
+ "MessageConfirmMarkAllEpisodesNotFinished": "모든 에피소드를 완료되지 않은 것으로 표시하시겠습니까?",
+ "MessageConfirmMarkItemFinished": "\"{0}\"을(를) 완료로 표시하시겠습니까?",
+ "MessageConfirmMarkItemNotFinished": "\"{0}\"을(를) 완료되지 않은 것으로 표시하시겠습니까?",
+ "MessageConfirmMarkSeriesFinished": "이 시리즈의 모든 책을 완료로 표시하시겠습니까?",
+ "MessageConfirmMarkSeriesNotFinished": "이 시리즈의 모든 책을 미완료로 표시하시겠습니까?",
+ "MessageConfirmNotificationTestTrigger": "테스트 데이터로 이 알림을 트리거할까요?",
+ "MessageConfirmPurgeCache": "캐시를 제거하면 /metadata/cache에 있는 전체 디렉토리가 삭제됩니다.
캐시 디렉토리를 제거하시겠습니까?",
+ "MessageConfirmPurgeItemsCache": "항목 캐시를 삭제하면 /metadata/cache/items에 있는 전체 디렉터리가 삭제됩니다.
정말입니까?",
+ "MessageConfirmQuickEmbed": "경고! 빠른 삽입 기능은 오디오 파일을 백업하지 않습니다. 오디오 파일을 백업해 두세요.
계속하시겠습니까?",
+ "MessageConfirmQuickMatchEpisodes": "빠른 매칭 에피소드는 일치하는 에피소드가 발견되면 세부 정보를 덮어씁니다. 매칭되지 않은 에피소드만 업데이트됩니다. 계속하시겠습니까?",
+ "MessageConfirmReScanLibraryItems": "{0}개 항목을 다시 스캔하시겠습니까?",
+ "MessageConfirmRemoveAllChapters": "모든 챕터를 제거하시겠습니까?",
+ "MessageConfirmRemoveAuthor": "작가 \"{0}\"을(를) 삭제하시겠습니까?",
+ "MessageConfirmRemoveCollection": "컬렉션 \"{0}\"을 제거하시겠습니까?",
+ "MessageConfirmRemoveEpisode": "에피소드 \"{0}\"을(를) 삭제하시겠습니까?",
+ "MessageConfirmRemoveEpisodeNote": "참고: \"Hard delete file\"를 전환하지 않는 한 오디오 파일은 삭제되지 않습니다.",
+ "MessageConfirmRemoveEpisodes": "{0}개의 에피소드를 삭제하시겠습니까?",
+ "MessageConfirmRemoveListeningSessions": "{0}개의 청취 세션을 제거하시겠습니까?",
+ "MessageConfirmRemoveMetadataFiles": "라이브러리 항목 폴더에서 모든 metadata.{0} 파일을 제거하시겠습니까?",
+ "MessageConfirmRemoveNarrator": "내레이터 \"{0}\"을(를) 제거하시겠습니까?",
+ "MessageConfirmRemovePlaylist": "재생목록 \"{0}\"을(를) 제거하시겠습니까?",
+ "MessageConfirmRenameGenre": "모든 항목의 장르 이름을 \"{0}\"에서 \"{1}\"로 바꾸시겠습니까?",
+ "MessageConfirmRenameGenreMergeNote": "참고: 이 장르는 이미 존재하므로 병합됩니다.",
+ "MessageConfirmRenameGenreWarning": "경고! 대소문자가 다른 유사한 장르 \"{0}\"이 이미 존재합니다.",
+ "MessageConfirmRenameTag": "모든 항목에 대해 태그 \"{0}\"의 이름을 \"{1}\"로 바꾸시겠습니까?",
+ "MessageConfirmRenameTagMergeNote": "참고: 이 태그는 이미 존재하므로 병합됩니다.",
+ "MessageConfirmRenameTagWarning": "경고! 대소문자가 다른 유사한 태그 \"{0}\"이 이미 존재합니다.",
+ "MessageConfirmResetProgress": "진행 상황을 재설정하시겠습니까?",
+ "MessageConfirmSendEbookToDevice": "{0}개의 전자책 \"{1}\"을 장치 \"{2}\"로 보내시겠습니까?",
+ "MessageConfirmUnlinkOpenId": "이 사용자를 OpenID에서 연결 해제하시겠습니까?",
+ "MessageDaysListenedInTheLastYear": "지난 1년 동안 {0}일 동안 청취됨",
+ "MessageDownloadingEpisode": "에피소드 다운로드 중",
+ "MessageDragFilesIntoTrackOrder": "파일을 올바른 트랙 순서로 끌어다 놓으세요",
+ "MessageEmbedFailed": "Embed 실패했습니다!",
+ "MessageEmbedFinished": "Embed 완료!",
+ "MessageEmbedQueue": "메타데이터 임베드 대기 중(대기열에 {0} 있음)",
+ "MessageEpisodesQueuedForDownload": "{0}개의 에피소드가 다운로드 대기 중입니다.",
+ "MessageEreaderDevices": "전자책을 안전하게 전달하려면 아래 나열된 각 기기에 대해 위의 이메일 주소를 유효한 발신자로 추가해야 할 수도 있습니다.",
+ "MessageFeedURLWillBe": "피드 URL은 {0}입니다.",
+ "MessageFetching": "가져오는 중...",
+ "MessageForceReScanDescription": "모든 파일을 새로 스캔하는 것처럼 다시 스캔합니다. 오디오 파일 ID3 태그, OPF 파일, 텍스트 파일은 새 파일처럼 스캔됩니다.",
+ "MessageHeatmapListeningTimeTooltip": "{0} {1}에서 청취 중",
+ "MessageHeatmapNoListeningSessions": "{0}에 대한 청취 세션이 없습니다.",
+ "MessageImportantNotice": "중요 공지!",
+ "MessageInsertChapterBelow": "아래에 챕터를 삽입하세요",
+ "MessageInvalidAsin": "잘못된 ASIN",
+ "MessageItemsSelected": "{0}개 항목이 선택됨",
+ "MessageItemsUpdated": "{0}개 항목이 업데이트됨.",
+ "MessageJoinUsOn": "가입하기",
+ "MessageLoading": "로딩중...",
+ "MessageLoadingFolders": "폴더를 로딩 중...",
+ "MessageLogsDescription": "로그는 /metadata/logs에 JSON 파일로 저장됩니다. 충돌 로그는 /metadata/logs/crash_logs.txt에 저장됩니다.",
+ "MessageM4BFailed": "M4B 실패!",
+ "MessageM4BFinished": "M4B 완료!",
+ "MessageMapChapterTitles": "타임스탬프를 조정하지 않고 기존 오디오북 장에 챕터 제목을 매핑합니다.",
+ "MessageMarkAllEpisodesFinished": "모든 에피소드를 완료로 표시",
+ "MessageMarkAllEpisodesNotFinished": "모든 에피소드를 완료되지 않은 것으로 표시",
+ "MessageMarkAsFinished": "완료로 표시",
+ "MessageMarkAsNotFinished": "완료되지 않음으로 표시",
+ "MessageMatchBooksDescription": "라이브러리에 있는 책과 선택한 검색 제공업체의 책을 비교하고, 빈 세부 정보와 표지 그림을 채웁니다. 세부 정보를 덮어쓰지 않습니다.",
+ "MessageNoAudioTracks": "오디오 트랙 없음",
+ "MessageNoAuthors": "작가 없음",
+ "MessageNoBackups": "백업 없음",
+ "MessageNoBookmarks": "북마크 없음",
+ "MessageNoChapters": "챕터 없음",
+ "MessageNoCollections": "컬렉션 없음",
+ "MessageNoCoversFound": "커버를 찾을 수 없음",
+ "MessageNoDescription": "설명 없음",
+ "MessageNoDevices": "장치가 없음",
+ "MessageNoDownloadsInProgress": "현재 진행 중인 다운로드가 없습니다.",
+ "MessageNoDownloadsQueued": "대기 중인 다운로드가 없습니다",
+ "MessageNoEpisodeMatchesFound": "일치하는 에피소드가 없습니다.",
+ "MessageNoEpisodes": "에피소드 없음",
+ "MessageNoFoldersAvailable": "사용 가능한 폴더가 없습니다",
+ "MessageNoGenres": "장르 없음",
+ "MessageNoIssues": "이슈 없음",
+ "MessageNoItems": "항목 없음",
+ "MessageNoItemsFound": "항목을 찾을 수 없음",
+ "MessageNoListeningSessions": "청취 세션 없음",
+ "MessageNoLogs": "로그 없음",
+ "MessageNoMediaProgress": "진행중인 미디어 없음",
+ "MessageNoNotifications": "알림 없음",
+ "MessageNoPodcastFeed": "잘못된 팟캐스트: 피드 없음",
+ "MessageNoPodcastsFound": "팟캐스트를 찾을 수 없음",
+ "MessageNoResults": "결과 없음",
+ "MessageNoSearchResultsFor": "\"{0}\"에 대한 검색 결과가 없습니다.",
+ "MessageNoSeries": "시리즈 없음",
+ "MessageNoTags": "태그 없음",
+ "MessageNoTasksRunning": "실행 중인 작업이 없음",
+ "MessageNoUpdatesWereNecessary": "업데이트가 필요하지 않음",
+ "MessageNoUserPlaylists": "재생목록이 없음",
+ "MessageNoUserPlaylistsHelp": "재생목록은 비공개입니다. 재생목록을 만든 사용자만 볼 수 있습니다.",
+ "MessageNotYetImplemented": "아직 구현되지 않음",
+ "MessageOpmlPreviewNote": "참고: 이는 파싱된 OPML 파일의 미리보기입니다. 실제 팟캐스트 제목은 RSS 피드에서 가져옵니다.",
+ "MessageOr": "또는",
+ "MessagePauseChapter": "챕터 재생 일시 정지",
+ "MessagePlayChapter": "챕터의 시작 부분을 들어보세요",
+ "MessagePlaylistCreateFromCollection": "컬렉션에서 재생 목록 만들기",
+ "MessagePleaseWait": "기다리세요...",
+ "MessagePodcastHasNoRSSFeedForMatching": "팟캐스트에 매칭에 사용할 RSS 피드 URL이 없습니다.",
+ "MessagePodcastSearchField": "검색어 또는 RSS 피드 URL을 입력하세요",
+ "MessageQuickEmbedInProgress": "빠른 임베드 진행 중",
+ "MessageQuickEmbedQueue": "빠른 임베드 대기 중(대기 중인 {0}개)",
+ "MessageQuickMatchAllEpisodes": "모든 에피소드 퀵 매치",
+ "MessageQuickMatchDescription": "빈 항목 세부 정보를 채우고 '{0}'의 첫 번째 일치 결과로 덮습니다. '일치하는 메타데이터 선호' 서버 설정이 활성화되어 있지 않으면 세부 정보를 덮어쓰지 않습니다.",
+ "MessageRemoveChapter": "챕터 제거",
+ "MessageRemoveEpisodes": "{0}개의 에피소드를 제거합니다.",
+ "MessageRemoveFromPlayerQueue": "플레이어 대기열에서 제거",
+ "MessageRemoveUserWarning": "사용자 \"{0}\"을 영구적으로 삭제하시겠습니까?",
+ "MessageReportBugsAndContribute": "버그 보고, 기능 요청 및 기여",
+ "MessageResetChaptersConfirm": "챕터를 재설정하고 변경 사항을 취소하시겠습니까?",
+ "MessageRestoreBackupConfirm": "생성된 백업을 복원하시겠습니까?",
+ "MessageRestoreBackupWarning": "백업을 복원하면 /config에 있는 전체 데이터베이스와 /metadata/items 및 /metadata/authors에 있는 표지 이미지가 덮어씌워집니다.
백업은 라이브러리 폴더의 어떤 파일도 수정하지 않습니다. 라이브러리 폴더에 표지 그림과 메타데이터를 저장하도록 서버 설정을 활성화한 경우, 해당 내용은 백업되거나 덮어씌워지지 않습니다.
서버를 사용하는 모든 클라이언트는 자동으로 새로 고쳐집니다.",
+ "MessageScheduleLibraryScanNote": "대부분의 사용자는 이 기능을 비활성화하고 라이브러리 변경 사항 자동 감시 설정을 활성화하는 것이 좋습니다. 라이브러리 폴더의 변경 사항을 자동으로 감지합니다. 파일 시스템(예: NFS)에서 라이브러리 변경 사항 자동 감시 기능이 작동하지 않는 경우 이 기능을 활성화하세요.",
+ "MessageScheduleRunEveryWeekdayAtTime": "{1}에서 {0}마다 실행",
+ "MessageSearchResultsFor": "검색 결과",
+ "MessageSelected": "{0}개 선택됨",
+ "MessageSeriesSequenceCannotContainSpaces": "시리즈 시퀀스에는 공백을 포함할 수 없습니다.",
+ "MessageServerCouldNotBeReached": "서버에 접속할 수 없습니다",
+ "MessageSetChaptersFromTracksDescription": "각 오디오 파일을 장으로 사용하고 챕터 제목을 오디오 파일 이름으로 사용하여 챕터를 설정합니다.",
+ "MessageShareExpirationWillBe": "만료일은 {0}입니다.",
+ "MessageShareExpiresIn": "{0}에 만료됨",
+ "MessageShareURLWillBe": "공유 URL은 {0}입니다.",
+ "MessageStartPlaybackAtTime": "{1}에서 \"{0}\"의 재생을 시작하시겠습니까?",
+ "MessageTaskAudioFileNotWritable": "오디오 파일 \"{0}\"은 쓸 수 없습니다.",
+ "MessageTaskCanceledByUser": "사용자가 작업을 취소했습니다.",
+ "MessageTaskDownloadingEpisodeDescription": "에피소드 \"{0}\" 다운로드 중",
+ "MessageTaskEmbeddingMetadata": "메타데이터 임베딩",
+ "MessageTaskEmbeddingMetadataDescription": "오디오북 \"{0}\"에 메타데이터 삽입",
+ "MessageTaskEncodingM4b": "M4B 인코딩",
+ "MessageTaskEncodingM4bDescription": "오디오북 \"{0}\"을 단일 m4b 파일로 인코딩",
+ "MessageTaskFailed": "실패함",
+ "MessageTaskFailedToBackupAudioFile": "오디오 파일 \"{0}\"을 백업하지 못했습니다.",
+ "MessageTaskFailedToCreateCacheDirectory": "캐시 디렉토리를 생성하지 못했습니다.",
+ "MessageTaskFailedToEmbedMetadataInFile": "파일 \"{0}\"에 메타데이터를 삽입하는 데 실패했습니다.",
+ "MessageTaskFailedToMergeAudioFiles": "오디오 파일을 병합하지 못했습니다.",
+ "MessageTaskFailedToMoveM4bFile": "m4b 파일을 이동하지 못했습니다.",
+ "MessageTaskFailedToWriteMetadataFile": "메타데이터 파일을 쓰지 못했습니다.",
+ "MessageTaskMatchingBooksInLibrary": "라이브러리 \"{0}\"에서 일치하는 책",
+ "MessageTaskNoFilesToScan": "검사할 파일이 없음",
+ "MessageTaskOpmlImport": "OPML 가져오기",
+ "MessageTaskOpmlImportDescription": "{0} RSS 피드에서 팟캐스트 만들기",
+ "MessageTaskOpmlImportFeed": "OPML 가져오기 피드",
+ "MessageTaskOpmlImportFeedDescription": "RSS 피드 \"{0}\"을(를) 가져오는 중",
+ "MessageTaskOpmlImportFeedFailed": "팟캐스트 피드를 가져오지 못했습니다.",
+ "MessageTaskOpmlImportFeedPodcastDescription": "팟캐스트 \"{0}\" 생성 중",
+ "MessageTaskOpmlImportFeedPodcastExists": "팟캐스트가 이미 경로에 존재합니다.",
+ "MessageTaskOpmlImportFeedPodcastFailed": "팟캐스트를 만들지 못했습니다",
+ "MessageTaskOpmlImportFinished": "{0}개의 팟캐스트가 추가되었습니다.",
+ "MessageTaskOpmlParseFailed": "OPML 파일을 구문 분석하지 못했습니다.",
+ "MessageTaskOpmlParseFastFail": "잘못된 OPML 파일 태그를 찾을 수 없거나 태그를 찾을 수 없습니다.",
+ "MessageTaskOpmlParseNoneFound": "OPML 파일에서 피드를 찾을 수 없습니다.",
+ "MessageTaskScanItemsAdded": "{0} 추가됨",
+ "MessageTaskScanItemsMissing": "{0} 누락됨",
+ "MessageTaskScanItemsUpdated": "{0} 업데이트됨",
+ "MessageTaskScanNoChangesNeeded": "변경이 필요하지 않습니다",
+ "MessageTaskScanningFileChanges": "\"{0}\"의 파일 변경 사항 스캔 중",
+ "MessageTaskScanningLibrary": "\"{0}\" 라이브러리 스캔 중",
+ "MessageTaskTargetDirectoryNotWritable": "대상 디렉토리에 쓸 수 없습니다",
+ "MessageThinking": "생각중...",
+ "MessageUploaderItemFailed": "업로드에 실패했습니다",
+ "MessageUploaderItemSuccess": "성공적으로 업로드되었습니다!",
+ "MessageUploading": "업로드 중...",
+ "MessageValidCronExpression": "유효한 cron 표현식",
+ "MessageWatcherIsDisabledGlobally": "서버 설정에서 Watcher가 전역적으로 비활성화되었습니다.",
+ "MessageXLibraryIsEmpty": "{0} 라이브러리가 비어 있습니다!",
+ "MessageYourAudiobookDurationIsLonger": "오디오북 길이가 검색된 길이보다 깁니다.",
+ "MessageYourAudiobookDurationIsShorter": "오디오북 길이가 검색된 길이보다 짧습니다.",
+ "NoteChangeRootPassword": "루트 사용자는 빈 비밀번호를 가질 수 있는 유일한 사용자입니다.",
+ "NoteChapterEditorTimes": "참고: 첫 번째 장의 시작 시간은 0:00으로 유지되어야 하며 마지막 장의 시작 시간은 이 오디오북 길이를 초과할 수 없습니다.",
+ "NoteFolderPicker": "참고: 이미 매핑된 폴더는 표시되지 않습니다.",
+ "NoteRSSFeedPodcastAppsHttps": "경고: 대부분의 팟캐스트 앱에서는 RSS 피드 URL이 HTTPS를 사용해야 합니다.",
+ "NoteRSSFeedPodcastAppsPubDate": "경고: 1개 이상의 에피소드에 게시 날짜가 없습니다. 일부 팟캐스트 앱에서는 게시 날짜가 필요합니다.",
+ "NoteUploaderFoldersWithMediaFiles": "미디어 파일이 있는 폴더는 별도의 라이브러리 항목으로 처리됩니다.",
+ "NoteUploaderOnlyAudioFiles": "오디오 파일만 업로드하는 경우 각 오디오 파일은 별도의 오디오북으로 처리됩니다.",
+ "NoteUploaderUnsupportedFiles": "지원되지 않는 파일은 무시됩니다. 폴더를 선택하거나 삭제할 때 항목 폴더에 없는 다른 파일은 무시됩니다.",
+ "NotificationOnBackupCompletedDescription": "백업이 완료되면 트리거됩니다.",
+ "NotificationOnBackupFailedDescription": "백업이 실패하면 트리거됨",
+ "NotificationOnEpisodeDownloadedDescription": "팟캐스트 에피소드가 자동으로 다운로드될 때 트리거됩니다.",
+ "NotificationOnRSSFeedDisabledDescription": "실패한 시도가 너무 많아 자동 에피소드 다운로드가 비활성화되면 트리거됩니다.",
+ "NotificationOnRSSFeedFailedDescription": "자동 에피소드 다운로드에 대한 RSS 피드 요청이 실패하면 트리거됩니다.",
+ "NotificationOnTestDescription": "알림 시스템 테스트를 위한 이벤트",
+ "PlaceholderBulkChapterInput": "챕터 제목을 입력하거나 번호를 사용하세요(예: '에피소드 1', '챕터 10', '1.')",
+ "PlaceholderNewCollection": "새로운 컬렉션 이름",
+ "PlaceholderNewFolderPath": "새 폴더 경로",
+ "PlaceholderNewPlaylist": "새로운 재생 목록 이름",
+ "PlaceholderSearch": "검색..",
+ "PlaceholderSearchEpisode": "에피소드 검색..",
+ "StatsAuthorsAdded": "작가가 추가되었습니다",
+ "StatsBooksAdded": "추가된 책",
+ "StatsBooksAdditional": "추가된 내용은 포함…",
+ "StatsBooksFinished": "책 완성",
+ "StatsBooksFinishedThisYear": "올해 읽은 책이 몇 권 있어요…",
+ "StatsBooksListenedTo": "듣는 책",
+ "StatsCollectionGrewTo": "나의 책 컬렉션이 늘어난…",
+ "StatsSessions": "세션",
+ "StatsSpentListening": "보낸 청취시간",
+ "StatsTopAuthor": "인기 작가",
+ "StatsTopAuthors": "인기 작가들",
+ "StatsTopGenre": "인기 장르",
+ "StatsTopGenres": "인기 장르",
+ "StatsTopMonth": "월간 인기",
+ "StatsTopNarrator": "인기 내레이터",
+ "StatsTopNarrators": "인기 내레이터들",
+ "StatsTotalDuration": "총 기간은…",
+ "StatsYearInReview": "올해 리뷰",
+ "ToastAccountUpdateSuccess": "계정이 업데이트되었습니다",
+ "ToastAppriseUrlRequired": "Apprise URL을 입력해야 합니다.",
+ "ToastAsinRequired": "ASIN이 필요합니다",
+ "ToastAuthorImageRemoveSuccess": "작가 이미지가 삭제되었습니다.",
+ "ToastAuthorNotFound": "작가 \"{0}\"을(를) 찾을 수 없습니다.",
+ "ToastAuthorRemoveSuccess": "작가 삭제됨",
+ "ToastAuthorSearchNotFound": "작가를 찾을 수 없습니다",
+ "ToastAuthorUpdateMerged": "작가 병합됨",
+ "ToastAuthorUpdateSuccess": "작가 업데이트됨",
+ "ToastAuthorUpdateSuccessNoImageFound": "작가 업데이트됨(이미지 없음)",
+ "ToastBackupAppliedSuccess": "백업이 적용되었음",
+ "ToastBackupCreateFailed": "백업을 생성하지 못했습니다",
+ "ToastBackupCreateSuccess": "백업이 생성되었습니다",
+ "ToastBackupDeleteFailed": "백업을 삭제하지 못했습니다.",
+ "ToastBackupDeleteSuccess": "백업이 삭제되었습니다",
+ "ToastBackupInvalidMaxKeep": "보관할 백업 수가 올바르지 않습니다.",
+ "ToastBackupInvalidMaxSize": "잘못된 최대 백업 크기",
+ "ToastBackupRestoreFailed": "백업을 복원하지 못했습니다",
+ "ToastBackupUploadFailed": "백업 업로드에 실패했습니다",
+ "ToastBackupUploadSuccess": "백업 업로드됨",
+ "ToastBatchApplyDetailsToItemsSuccess": "항목에 적용된 세부 정보",
+ "ToastBatchDeleteFailed": "일괄 삭제에 실패했습니다",
+ "ToastBatchDeleteSuccess": "일괄 삭제 성공",
+ "ToastBatchQuickMatchFailed": "일괄 퀵 매치에 실패했습니다!",
+ "ToastBatchQuickMatchStarted": "{0}권의 책에 대한 일괄 퀵 매치가 시작되었습니다!",
+ "ToastBatchUpdateFailed": "일괄 업데이트에 실패했습니다",
+ "ToastBatchUpdateSuccess": "일괄 업데이트 성공",
+ "ToastBookmarkCreateFailed": "북마크를 생성하지 못했습니다.",
+ "ToastBookmarkCreateSuccess": "북마크 추가됨",
+ "ToastBookmarkRemoveSuccess": "북마크가 삭제되었습니다",
+ "ToastBulkChapterInvalidCount": "1~150 사이의 숫자를 입력하세요",
+ "ToastCachePurgeFailed": "캐시를 정리하지 못했습니다.",
+ "ToastCachePurgeSuccess": "캐시가 성공적으로 삭제되었습니다.",
+ "ToastChapterLocked": "챕터가 잠겼습니다.",
+ "ToastChapterStartTimeAdjusted": "챕터 시작 시간이 {0}초로 조정되었습니다.",
+ "ToastChaptersAllLocked": "모든 챕터는 잠겨 있습니다. 일부 챕터를 잠금 해제하여 시간을 이동하세요.",
+ "ToastChaptersHaveErrors": "챕터에 오류가 있습니다",
+ "ToastChaptersInvalidShiftAmountLast": "잘못된 이동량입니다. 마지막 장의 시작 시간이 이 오디오북의 길이를 초과합니다.",
+ "ToastChaptersInvalidShiftAmountStart": "잘못된 이동량입니다. 첫 번째 챕터의 길이는 0 또는 음수이며 두 번째 챕터로 덮어쓰여집니다. 두 번째 챕터의 시작 길이를 늘리세요.",
+ "ToastChaptersMustHaveTitles": "챕터에는 제목이 있어야 합니다.",
+ "ToastChaptersRemoved": "챕터가 삭제되었습니다",
+ "ToastChaptersUpdated": "챕터 업데이트됨",
+ "ToastCollectionItemsAddFailed": "컬렉션에 항목을 추가하는 데 실패했습니다.",
+ "ToastCollectionRemoveSuccess": "컬렉션이 제거되었습니다",
+ "ToastCollectionUpdateSuccess": "컬렉션이 업데이트되었습니다",
+ "ToastConnectionNotAvailable": "연결할 수 없습니다. 나중에 다시 시도해 주세요.",
+ "ToastCoverSearchFailed": "커버 검색에 실패했습니다",
+ "ToastCoverUpdateFailed": "커버 업데이트에 실패했습니다",
+ "ToastDateTimeInvalidOrIncomplete": "날짜와 시간이 잘못되었거나 불완전합니다.",
+ "ToastDeleteFileFailed": "파일을 삭제하지 못했습니다",
+ "ToastDeleteFileSuccess": "파일이 삭제되었습니다",
+ "ToastDeviceAddFailed": "장치를 추가하지 못했습니다",
+ "ToastDeviceNameAlreadyExists": "해당 이름의 전자책 단말기가 이미 존재합니다.",
+ "ToastDeviceTestEmailFailed": "테스트 이메일을 보내지 못했습니다.",
+ "ToastDeviceTestEmailSuccess": "테스트 이메일이 전송되었습니다.",
+ "ToastEmailSettingsUpdateSuccess": "이메일 설정이 업데이트되었습니다.",
+ "ToastEncodeCancelFailed": "인코딩 취소에 실패했습니다",
+ "ToastEncodeCancelSucces": "인코딩 취소됨",
+ "ToastEpisodeDownloadQueueClearFailed": "대기열을 지우는 데 실패했습니다.",
+ "ToastEpisodeDownloadQueueClearSuccess": "에피소드 다운로드 대기열이 지워졌습니다.",
+ "ToastEpisodeUpdateSuccess": "{0}개 에피소드 업데이트됨",
+ "ToastErrorCannotShare": "이 기기에서는 기본적으로 공유할 수 없습니다.",
+ "ToastFailedToCreate": "생성에 실패했습니다",
+ "ToastFailedToDelete": "삭제에 실패했습니다",
+ "ToastFailedToLoadData": "데이터를 로드하는 데 실패했습니다",
+ "ToastFailedToMatch": "매칭에 실패했습니다",
+ "ToastFailedToShare": "공유에 실패했습니다",
+ "ToastFailedToUpdate": "업데이트에 실패했습니다",
+ "ToastInvalidImageUrl": "잘못된 이미지 URL",
+ "ToastInvalidMaxEpisodesToDownload": "다운로드할 수 있는 최대 에피소드 수가 올바르지 않습니다.",
+ "ToastInvalidUrl": "잘못된 URL",
+ "ToastInvalidUrls": "하나 이상의 URL이 잘못되었습니다.",
+ "ToastItemCoverUpdateSuccess": "아이템 커버 업데이트됨",
+ "ToastItemDeletedFailed": "항목을 삭제하지 못했습니다.",
+ "ToastItemDeletedSuccess": "삭제된 항목",
+ "ToastItemDetailsUpdateSuccess": "항목 상세정보 업데이트됨",
+ "ToastItemMarkedAsFinishedFailed": "완료로 표시하지 못했습니다.",
+ "ToastItemMarkedAsFinishedSuccess": "완료로 표시된 항목",
+ "ToastItemMarkedAsNotFinishedFailed": "완료되지 않음으로 표시하는 데 실패했습니다.",
+ "ToastItemMarkedAsNotFinishedSuccess": "완료되지 않음으로 표시된 항목",
+ "ToastItemUpdateSuccess": "항목이 업데이트되었습니다",
+ "ToastLibraryCreateFailed": "라이브러리를 생성하지 못했습니다.",
+ "ToastLibraryCreateSuccess": "라이브러리 \"{0}\"이 생성되었습니다.",
+ "ToastLibraryDeleteFailed": "라이브러리 삭제에 실패했습니다",
+ "ToastLibraryDeleteSuccess": "라이브러리 삭제됨",
+ "ToastLibraryScanFailedToStart": "스캔을 시작하지 못했습니다",
+ "ToastLibraryScanStarted": "라이브러리 스캔이 시작되었습니다",
+ "ToastLibraryUpdateSuccess": "라이브러리 \"{0}\"이(가) 업데이트되었습니다.",
+ "ToastMatchAllAuthorsFailed": "모든 작가를 일치시키지 못했습니다.",
+ "ToastMetadataFilesRemovedError": "메타데이터를 제거하는 중 오류가 발생했습니다.{0} 파일",
+ "ToastMetadataFilesRemovedNoneFound": "메타데이터가 없습니다.{0} 라이브러리에서 파일을 찾았습니다.",
+ "ToastMetadataFilesRemovedNoneRemoved": "메타데이터가 없습니다.{0} 파일이 제거되었습니다.",
+ "ToastMetadataFilesRemovedSuccess": "{0} 메타데이터.{1} 파일 제거됨",
+ "ToastMustHaveAtLeastOnePath": "최소한 하나의 경로가 있어야 합니다",
+ "ToastNameEmailRequired": "이름과 이메일이 필요합니다",
+ "ToastNameRequired": "이름이 필요합니다",
+ "ToastNewApiKeyUserError": "사용자를 선택해야 합니다",
+ "ToastNewEpisodesFound": "{0}개의 새로운 에피소드가 발견되었습니다.",
+ "ToastNewUserCreatedFailed": "계정 생성에 실패했습니다: \"{0}\"",
+ "ToastNewUserCreatedSuccess": "새로운 계정이 생성되었습니다",
+ "ToastNewUserLibraryError": "최소한 하나의 라이브러리를 선택해야 합니다.",
+ "ToastNewUserPasswordError": "비밀번호가 있어야 합니다. 루트 사용자만 빈 비밀번호를 가질 수 있습니다.",
+ "ToastNewUserTagError": "최소한 하나의 태그를 선택해야 합니다.",
+ "ToastNewUserUsernameError": "사용자 이름을 입력하세요",
+ "ToastNoNewEpisodesFound": "새로운 에피소드가 없습니다",
+ "ToastNoRSSFeed": "팟캐스트에 RSS 피드가 없습니다",
+ "ToastNoUpdatesNecessary": "업데이트가 필요하지 않습니다",
+ "ToastNotificationCreateFailed": "알림을 생성하지 못했습니다.",
+ "ToastNotificationDeleteFailed": "알림 삭제에 실패했습니다",
+ "ToastNotificationFailedMaximum": "실패한 최대 시도 횟수는 0 이상이어야 합니다.",
+ "ToastNotificationQueueMaximum": "최대 알림 대기열은 0 이상이어야 합니다.",
+ "ToastNotificationSettingsUpdateSuccess": "알림 설정이 업데이트되었습니다.",
+ "ToastNotificationTestTriggerFailed": "테스트 알림을 트리거하지 못했습니다.",
+ "ToastNotificationTestTriggerSuccess": "테스트 알림이 트리거되었습니다.",
+ "ToastNotificationUpdateSuccess": "알림이 업데이트되었습니다",
+ "ToastPlaylistCreateFailed": "플레이리스트를 생성하지 못했습니다",
+ "ToastPlaylistCreateSuccess": "플레이리스트가 생성되었습니다",
+ "ToastPlaylistRemoveSuccess": "재생목록이 삭제되었습니다",
+ "ToastPlaylistUpdateSuccess": "플레이리스트가 업데이트되었습니다",
+ "ToastPodcastCreateFailed": "팟캐스트를 만들지 못했습니다",
+ "ToastPodcastCreateSuccess": "팟캐스트가 성공적으로 생성되었습니다.",
+ "ToastPodcastEpisodeUpdated": "에피소드 업데이트됨",
+ "ToastPodcastGetFeedFailed": "팟캐스트 피드를 가져오지 못했습니다.",
+ "ToastPodcastNoEpisodesInFeed": "RSS 피드에서 에피소드를 찾을 수 없습니다.",
+ "ToastPodcastNoRssFeed": "팟캐스트에 RSS 피드가 없습니다",
+ "ToastProgressIsNotBeingSynced": "진행 상황이 동기화되지 않습니다. 재생을 다시 시작하세요.",
+ "ToastProviderCreatedFailed": "공급자를 추가하지 못했습니다",
+ "ToastProviderCreatedSuccess": "새로운 공급자가 추가되었습니다",
+ "ToastProviderNameAndUrlRequired": "이름과 URL이 필요합니다",
+ "ToastProviderRemoveSuccess": "제공자 제거됨",
+ "ToastRSSFeedCloseFailed": "RSS 피드를 닫지 못했습니다.",
+ "ToastRSSFeedCloseSuccess": "RSS 피드가 닫혔습니다",
+ "ToastRemoveFailed": "제거에 실패했습니다",
+ "ToastRemoveItemFromCollectionFailed": "컬렉션에서 항목을 제거하지 못했습니다.",
+ "ToastRemoveItemFromCollectionSuccess": "컬렉션에서 항목이 제거되었습니다.",
+ "ToastRemoveItemsWithIssuesFailed": "이슈가 있는 라이브러리 항목을 제거하지 못했습니다.",
+ "ToastRemoveItemsWithIssuesSuccess": "이슈가 있는 라이브러리 항목을 제거했습니다.",
+ "ToastRenameFailed": "이름 바꾸기에 실패했습니다",
+ "ToastRescanFailed": "{0}에 대한 재스캔에 실패했습니다.",
+ "ToastRescanRemoved": "재스캔 완료 항목이 제거되었습니다.",
+ "ToastRescanUpToDate": "재스캔 완료 항목이 최신 상태였습니다.",
+ "ToastRescanUpdated": "재스캔 완료 항목이 업데이트되었습니다.",
+ "ToastScanFailed": "라이브러리 항목 스캔에 실패했습니다.",
+ "ToastSelectAtLeastOneUser": "최소한 한 명의 사용자를 선택하세요",
+ "ToastSendEbookToDeviceFailed": "기기로 전자책을 보내지 못했습니다.",
+ "ToastSendEbookToDeviceSuccess": "전자책이 기기 \"{0}\"로 전송되었습니다.",
+ "ToastSeriesSubmitFailedSameName": "같은 이름의 두 시리즈를 추가할 수 없습니다.",
+ "ToastSeriesUpdateFailed": "시리즈 업데이트에 실패했습니다",
+ "ToastSeriesUpdateSuccess": "시리즈 업데이트 성공",
+ "ToastServerSettingsUpdateSuccess": "서버 설정이 업데이트되었습니다.",
+ "ToastSessionCloseFailed": "세션을 닫지 못했습니다",
+ "ToastSessionDeleteFailed": "세션 삭제에 실패했습니다",
+ "ToastSessionDeleteSuccess": "세션이 삭제되었습니다",
+ "ToastSleepTimerDone": "슬립 타이머 완료... zZzzZz",
+ "ToastSlugMustChange": "슬러그에 잘못된 문자가 포함되어 있습니다.",
+ "ToastSlugRequired": "슬러그가 필요합니다",
+ "ToastSocketConnected": "소켓 연결됨",
+ "ToastSocketDisconnected": "소켓이 연결해제",
+ "ToastSocketFailedToConnect": "소켓 연결에 실패했습니다",
+ "ToastSortingPrefixesEmptyError": "최소 1개의 정렬 접두사가 있어야 합니다.",
+ "ToastSortingPrefixesUpdateSuccess": "정렬 접두사가 업데이트되었습니다({0}개 항목)",
+ "ToastTitleRequired": "제목은 필수입니다",
+ "ToastUnknownError": "알 수 없는 오류",
+ "ToastUnlinkOpenIdFailed": "OpenID에서 사용자 연결을 해제하지 못했습니다.",
+ "ToastUnlinkOpenIdSuccess": "OpenID에서 사용자 연결이 해제되었습니다.",
+ "ToastUploaderFilepathExistsError": "파일 경로 \"{0}\"이(가) 서버에 이미 있습니다.",
+ "ToastUploaderItemExistsInSubdirectoryError": "항목 \"{0}\"이(가) 업로드 경로의 하위 디렉토리를 사용하고 있습니다.",
+ "ToastUserDeleteFailed": "사용자 삭제에 실패했습니다",
+ "ToastUserDeleteSuccess": "사용자가 삭제되었습니다",
+ "ToastUserPasswordChangeSuccess": "비밀번호가 성공적으로 변경되었습니다",
+ "ToastUserPasswordMismatch": "비밀번호가 일치하지 않습니다",
+ "ToastUserPasswordMustChange": "새 비밀번호는 이전 비밀번호와 일치할 수 없습니다.",
+ "ToastUserRootRequireName": "루트 사용자 이름을 입력해야 합니다",
+ "TooltipAddChapters": "챕터 추가",
+ "TooltipAddOneSecond": "1초 추가",
+ "TooltipAdjustChapterStart": "시작 시간을 조정하려면 클릭하세요",
+ "TooltipLockAllChapters": "모든 챕터 잠금",
+ "TooltipLockChapter": "장 잠금(범위를 위해 Shift+클릭)",
+ "TooltipSubtractOneSecond": "1초 빼기",
+ "TooltipUnlockAllChapters": "모든 챕터 잠금 해제",
+ "TooltipUnlockChapter": "챕터 잠금 해제(범위를 보려면 Shift+클릭)"
+}
diff --git a/client/strings/lt.json b/client/strings/lt.json
index 9ebec5190..2480cdf35 100644
--- a/client/strings/lt.json
+++ b/client/strings/lt.json
@@ -1,5 +1,6 @@
{
"ButtonAdd": "Pridėti",
+ "ButtonAddApiKey": "Pridėti API raktą",
"ButtonAddChapters": "Pridėti skyrius",
"ButtonAddDevice": "Pridėti įrenginį",
"ButtonAddLibrary": "Pridėti Biblioteką",
@@ -18,6 +19,7 @@
"ButtonChooseAFolder": "Pasirinkite aplanką",
"ButtonChooseFiles": "Pasirinkite failus",
"ButtonClearFilter": "Valyti filtrą",
+ "ButtonClose": "Uždaryti",
"ButtonCloseFeed": "Uždaryti srautą",
"ButtonCloseSession": "Uždaryti Atidarytą sesiją",
"ButtonCollections": "Kolekcijos",
@@ -251,7 +253,7 @@
"LabelDuration": "Trukmė",
"LabelDurationFound": "Rasta trukmė:",
"LabelEbook": "Elektroninė knyga",
- "LabelEbooks": "Elektroninės knygos",
+ "LabelEbooks": "El. knygos",
"LabelEdit": "Redaguoti",
"LabelEmail": "El. paštas",
"LabelEmailSettingsFromAddress": "Siuntėjo adresas",
@@ -261,6 +263,7 @@
"LabelEmbeddedCover": "Įterptas viršelis",
"LabelEnable": "Įjungti",
"LabelEnd": "Pabaiga",
+ "LabelEndOfChapter": "Skyriaus pabaiga",
"LabelEpisode": "Epizodas",
"LabelEpisodeTitle": "Epizodo pavadinimas",
"LabelEpisodeType": "Epizodo tipas",
@@ -282,8 +285,8 @@
"LabelGenre": "Žanras",
"LabelGenres": "Žanrai",
"LabelHardDeleteFile": "Galutinai ištrinti failą",
- "LabelHasEbook": "Turi e-knygą",
- "LabelHasSupplementaryEbook": "Turi papildomą e-knygą",
+ "LabelHasEbook": "Turi el. knygą",
+ "LabelHasSupplementaryEbook": "Turi papildomą el. knygą",
"LabelHost": "Serveris",
"LabelHour": "Valanda",
"LabelIcon": "Piktograma",
diff --git a/client/strings/no.json b/client/strings/no.json
index 1a19cfa1e..8e1c9cfbb 100644
--- a/client/strings/no.json
+++ b/client/strings/no.json
@@ -34,7 +34,7 @@
"ButtonEditChapters": "Rediger kapittel",
"ButtonEditPodcast": "Rediger podcast",
"ButtonEnable": "Aktiver",
- "ButtonFireAndFail": "Kjør ved feil",
+ "ButtonFireAndFail": "Utfør og feil",
"ButtonFireOnTest": "Kjør onTest-kommando",
"ButtonForceReScan": "Tving skann",
"ButtonFullPath": "Full sti",
@@ -113,7 +113,7 @@
"ButtonUploadOPMLFile": "Last opp OPML fil",
"ButtonUserDelete": "Slett bruker {0}",
"ButtonUserEdit": "Rediger bruker {0}",
- "ButtonViewAll": "Vis alle",
+ "ButtonViewAll": "Vis alt",
"ButtonYes": "Ja",
"ErrorUploadFetchMetadataAPI": "Feil ved innhenting av metadata",
"ErrorUploadFetchMetadataNoResults": "Kunne ikke hente metadata - forsøk å oppdatere tittel og/eller forfatter",
@@ -309,6 +309,7 @@
"LabelDeleteFromFileSystemCheckbox": "Slett fra filsystemet (fjern haken for kun å ta bort fra databasen)",
"LabelDescription": "Beskrivelse",
"LabelDeselectAll": "Fjern valg",
+ "LabelDetectedPattern": "Oppdaget mønster:",
"LabelDevice": "Enhet",
"LabelDeviceInfo": "Enhetsinformasjon",
"LabelDeviceIsAvailableTo": "Enheten er tilgjengelig for...",
@@ -377,11 +378,12 @@
"LabelFilterByUser": "Filtrer etter bruker",
"LabelFindEpisodes": "Finn episoder",
"LabelFinished": "Fullført",
+ "LabelFinishedDate": "Fullført {0}",
"LabelFolder": "Mappe",
"LabelFolders": "Mapper",
"LabelFontBold": "Fet",
"LabelFontBoldness": "Skrifttykkelse",
- "LabelFontFamily": "Fontfamilie",
+ "LabelFontFamily": "Skriftfamilie",
"LabelFontItalic": "Kursiv",
"LabelFontScale": "Font størrelse",
"LabelFontStrikethrough": "Gjennomstreking",
@@ -434,7 +436,9 @@
"LabelLibraryFilterSublistEmpty": "Ingen {0}",
"LabelLibraryItem": "Bibliotek enhet",
"LabelLibraryName": "Bibliotek navn",
- "LabelLibrarySortByProgress": "Fremgang: Sist oppdatert",
+ "LabelLibrarySortByProgress": "Fremdrift: Sist oppdatert",
+ "LabelLibrarySortByProgressFinished": "Fremdrift: Fullført",
+ "LabelLibrarySortByProgressStarted": "Fremdrift: Startet",
"LabelLimit": "Begrensning",
"LabelLineSpacing": "Linjemellomrom",
"LabelListenAgain": "Lytt igjen",
@@ -443,8 +447,9 @@
"LabelLogLevelWarn": "Varsel",
"LabelLookForNewEpisodesAfterDate": "Se etter nye episoder etter denne datoen",
"LabelLowestPriority": "Laveste prioritet",
+ "LabelMatchConfidence": "Konfidens",
"LabelMatchExistingUsersBy": "Knytt sammen eksisterende brukere basert på",
- "LabelMatchExistingUsersByDescription": "Brukes for å koble til eksisterende brukere. Når koblingen er i orden vil brukerne bli identifisert med en unik id fra SSO-tilbyderen.",
+ "LabelMatchExistingUsersByDescription": "Brukes for å koble til eksisterende brukere. Når koblingen er i orden vil brukerne bli identifisert med en unik id fra SSO-tilbyderen",
"LabelMaxEpisodesToDownload": "Maksimalt antall episoder som skal lastes ned. Bruk 0 for ubegrenset.",
"LabelMaxEpisodesToDownloadPerCheck": "Maksimalt antall nye episoder som skal lastes ned per sjekk",
"LabelMaxEpisodesToKeep": "Maksimalt antall episoder som skal beholdes",
@@ -453,7 +458,7 @@
"LabelMediaType": "Medie type",
"LabelMetaTag": "Meta tag",
"LabelMetaTags": "Meta tags",
- "LabelMetadataOrderOfPrecedenceDescription": "Høyere prioritert kilder for metadata overstyrer laverer prioriterte kilder for metadata.",
+ "LabelMetadataOrderOfPrecedenceDescription": "Høyere prioritert kilder for metadata overstyrer laverer prioriterte kilder for metadata",
"LabelMetadataProvider": "Metadata Leverandør",
"LabelMinute": "Minutt",
"LabelMinutes": "Minutter",
@@ -472,7 +477,9 @@
"LabelNewestAuthors": "Nyeste forfattere",
"LabelNewestEpisodes": "Nyeste episoder",
"LabelNextBackupDate": "Neste sikkerhetskopi dato",
+ "LabelNextChapters": "Neste kapitler blir:",
"LabelNextScheduledRun": "Neste planlagte kjøring",
+ "LabelNoApiKeys": "Ingen API-nøkler",
"LabelNoCustomMetadataProviders": "Ingen egendefinerte tilbydere for metadata",
"LabelNoEpisodesSelected": "Ingen episoder valgt",
"LabelNotFinished": "Ikke fullført",
@@ -488,10 +495,11 @@
"LabelNotificationsMaxQueueSize": "Maksimalt antall varslinger i kø",
"LabelNotificationsMaxQueueSizeHelp": "Hendelser er begrenset til avfyre én gang per sekund. Hendelser blir ignorert om køen er full. Dette forhindrer overflod av varslinger.",
"LabelNumberOfBooks": "Antall bøker",
+ "LabelNumberOfChapters": "Antall kapitler:",
"LabelNumberOfEpisodes": "# episoder",
"LabelOpenIDAdvancedPermsClaimDescription": "Navnet på OpenID claim'et som inneholder avanserte tilganger for brukerhandlinger i applikasjonen som vil brukes for ikke-administratorroller (hvis konfigurert). Hvis claim'et mangler fra responsen, nektes tilgang til ABS. Hvis en enkelt opsjon mangler, blir behandlet som false. Påse at identitetstilbyderens claim stemmer overens med den forventede strukturen:",
"LabelOpenIDClaims": "La følge valg være tomme for å slå av avanserte gruppe og tillatelser. Gruppen \"Bruker\" vil da også automatisk legges til.",
- "LabelOpenIDGroupClaimDescription": "Navn på OpenID-forespørsel som inneholder en lite over brukerens grupper. Vanligvis kalt grupper. Om konfigurert, vil applikasjonen tildele roller baseret på brukerens gruppemedlemsskaper, gitt disse grupper er navngitt (uten forbehold for store og små bokstaver) 'admin', 'user' eller 'guest' i forespørsel. Forespørselen burde inneholde en liste (og hvis brukeren tilhører flere grupper), applikasjonen vil tildele rolle med høyeste adgangsnivå. Hvis ingen grupper matcher vil adgang bli nektet.",
+ "LabelOpenIDGroupClaimDescription": "Navn på OpenID-forespørsel som inneholder en lite over brukerens grupper. Vanligvis kalt grupper. Om konfigurert, vil applikasjonen tildele roller baseret på brukerens gruppemedlemsskaper, gitt disse grupper er navngitt (uten forbehold for store og små bokstaver) 'admin', 'user' eller 'guest' i forespørsel. Forespørselen burde inneholde en liste (og hvis brukeren tilhører flere grupper), applikasjonen vil tildele rolle med høyeste adgangsnivå. Hvis ingen grupper matcher vil adgang bli nektet.",
"LabelOpenRSSFeed": "Åpne RSS Feed",
"LabelOverwrite": "Overskriv",
"LabelPaginationPageXOfY": "Side {0} av {1}",
@@ -509,6 +517,7 @@
"LabelPersonalYearReview": "Oppsummering av året ditt ({0})",
"LabelPhotoPathURL": "Bilde sti/URL",
"LabelPlayMethod": "Avspillingsmetode",
+ "LabelPlaybackRateIncrementDecrement": "Trinnstørrelse for økning/senking av avspillingshastighet",
"LabelPlayerChapterNumberMarker": "{0} av {1}",
"LabelPlaylists": "Spilleliste",
"LabelPodcast": "Podcast",
@@ -561,6 +570,7 @@
"LabelSelectAll": "Velg alt",
"LabelSelectAllEpisodes": "Velg alle episoder",
"LabelSelectEpisodesShowing": "Velg {0} episoder vist",
+ "LabelSelectUser": "Velg bruker",
"LabelSelectUsers": "Velg brukere",
"LabelSendEbookToDevice": "Send Ebok til...",
"LabelSequence": "Sekvens",
@@ -628,6 +638,7 @@
"LabelStartTime": "Start Tid",
"LabelStarted": "Startet",
"LabelStartedAt": "Startet",
+ "LabelStartedDate": "Startet {0}",
"LabelStatsAudioTracks": "Lydspor",
"LabelStatsAuthors": "Forfattere",
"LabelStatsBestDay": "Beste dag",
@@ -657,6 +668,7 @@
"LabelTheme": "Tema",
"LabelThemeDark": "Mørk",
"LabelThemeLight": "Lys",
+ "LabelThemeSepia": "Sepia",
"LabelTimeBase": "Tidsbase",
"LabelTimeDurationXHours": "{0} timer",
"LabelTimeDurationXMinutes": "{0} minutter",
@@ -725,24 +737,32 @@
"MessageAddToPlayerQueue": "Legg til i kø",
"MessageAppriseDescription": "For å bruke denne funksjonen trenger du en instans av Apprise API kjørende eller et API som håndterer disse forespørslene.
Apprise API URL skal være hele URL-en til varslingen, f.eks., hvis din API-instans er på http://192.168.1.1:8337 så skal du bruke http://192.168.1.1:8337/notify.",
"MessageAsinCheck": "Påse at du bruker ASIN fra den riktige Audible-regionen, ikke Amazon.",
+ "MessageAuthenticationLegacyTokenWarning": "Eldre API-tokener vil bli fjernet i fremtiden. Bruk API-nøkler i stedet.",
"MessageAuthenticationOIDCChangesRestart": "Etter å ha lagret, start serveren din på nytt for at OIDC-endringene skal tre i kraft.",
+ "MessageAuthenticationSecurityMessage": "Autentisering er forbedret av sikkerhetshensyn. Alle brukere må logge inn på nytt.",
"MessageBackupsDescription": "Sikkerhetskopier inkluderer, brukerfremgang, detaljer om bibliotekgjenstander, tjener instillinger og bilder lagret under /metadata/items og /metadata/authors. Sikkerhetskopier vil ikke inkludere filer som er lagret i bibliotek mappene.",
"MessageBackupsLocationEditNote": "Viktig: Endring av mappen for sikkerhetskopi hverken endrer eller flytter eksisterende sikkerhetskopier!",
- "MessageBackupsLocationNoEditNote": "NB: Mappen for sikkerhetskopi settes i en miljøvariabel og kan ikke endres her.",
+ "MessageBackupsLocationNoEditNote": "Viktig: Mappen for sikkerhetskopi satt i en miljøvariabel og kan ikke endres her.",
"MessageBackupsLocationPathEmpty": "Mappen for sikkerhetskopiering må angis",
+ "MessageBatchEditPopulateMapDetailsAllHelp": "Fyll aktiverte felt med data fra alle elementer. Felt med flere verdier blir slått sammen",
+ "MessageBatchEditPopulateMapDetailsItemHelp": "Fyll aktiverte kartdetaljfelt med data fra dette elementet",
"MessageBatchQuickMatchDescription": "Kjapt søk vil forsøke å legge til manglende omslag og metadata for de valgte gjenstandene. Aktiver dette valget for å tillate Kjapt søk til å overskrive eksisterende omslag og/eller metadata.",
"MessageBookshelfNoCollections": "Du har ikke laget noen samlinger ennå",
+ "MessageBookshelfNoCollectionsHelp": "Samlinger er offentlige. Alle brukere med tilgang til biblioteket kan se dem.",
"MessageBookshelfNoRSSFeeds": "Ingen RSS feed er åpen",
"MessageBookshelfNoResultsForFilter": "Ingen resultat for filter \"{0}: {1}\"",
"MessageBookshelfNoResultsForQuery": "Ingen resultater for søket",
"MessageBookshelfNoSeries": "Du har ingen serier",
+ "MessageBulkChapterPattern": "Hvor mange kapitler vil du legge til med dette nummereringsmønsteret?",
"MessageChapterEndIsAfter": "Kapittel slutt er etter slutt av lydboken",
"MessageChapterErrorFirstNotZero": "Første kapittel starter på 0",
"MessageChapterErrorStartGteDuration": "Feil start tid, må være mindre enn lengde på lydbok",
"MessageChapterErrorStartLtPrev": "Feil start tid, må være større eller det samme som forrige kapittel start tid",
"MessageChapterStartIsAfter": "Kapittel start er etter slutten av din lydbok",
+ "MessageChaptersNotFound": "Fant ikke kapitler",
"MessageCheckingCron": "Sjekker cron...",
"MessageConfirmCloseFeed": "Er du sikker på at du vil lukke denne feeden?",
+ "MessageConfirmDeleteApiKey": "Er du sikker på at du vil slette API-nøkkelen \"{0}\"?",
"MessageConfirmDeleteBackup": "Er du sikker på at du vil slette sikkerhetskopi for {0}?",
"MessageConfirmDeleteDevice": "Er du sikker på at du vil slette e-leser enheten \"{0}\"?",
"MessageConfirmDeleteFile": "Dette vil slette filen fra filsystemet. Er du sikker?",
@@ -761,7 +781,7 @@
"MessageConfirmMarkSeriesFinished": "Er du sikker på at du vil markere alle bøkene i serien som fullført?",
"MessageConfirmMarkSeriesNotFinished": "Er du sikker på at du vil markere alle bøkene i serien som ikke fullført?",
"MessageConfirmNotificationTestTrigger": "Utløs dette varselet med test-data?",
- "MessageConfirmPurgeCache": "Tømming av mellomlagring vil slette hele mappen /metadata/cache.
Er du sikker på at du du vil slette mappen?",
+ "MessageConfirmPurgeCache": "Tømming av mellomlagring vil slette hele mappen /metadata/cache.
Er du sikker på at du vil slette mappen?",
"MessageConfirmPurgeItemsCache": "(Purge items cache) Dette vil sletter hele mappen /metadata/cache/items.
Er du sikker?",
"MessageConfirmQuickEmbed": "Advarsel! Rask innbygging av metadata tar ikke backup av lyd-filene først. Forsikre deg om at du har sikkerhetskopi av filene.
Fortsett?",
"MessageConfirmQuickMatchEpisodes": "Hurtig gjenkjenning av episoder overskriver detaljene hvis en match blir funnet. Kun episoder som ikke allerede er matchet blir oppdatert. Er du sikker?",
@@ -770,6 +790,7 @@
"MessageConfirmRemoveAuthor": "Er du sikker på at du vil fjerne forfatteren \"{0}\"?",
"MessageConfirmRemoveCollection": "Er du sikker på at du vil fjerne samling\"{0}\"?",
"MessageConfirmRemoveEpisode": "Er du sikker på at du vil fjerne episode \"{0}\"?",
+ "MessageConfirmRemoveEpisodeNote": "Merk: Dette sletter ikke lydfilen med mindre du slår på \"Hard delete file\"",
"MessageConfirmRemoveEpisodes": "Er du sikker på at du vil fjerne {0} episoder?",
"MessageConfirmRemoveListeningSessions": "Er du sikker på at du vil fjerne {0} lytte-sesjoner?",
"MessageConfirmRemoveMetadataFiles": "Er du sikker på at du vil fjerne alle metadata.{0}-filer i mappene for biblioteks-elementer?",
@@ -795,8 +816,11 @@
"MessageFeedURLWillBe": "Feed URL vil bli {0}",
"MessageFetching": "Henter...",
"MessageForceReScanDescription": "vil skanne alle filene igjen som en ny skann. Lyd fil ID3 tagger, OPF filer og tekstfiler vil bli skannet som nye.",
+ "MessageHeatmapListeningTimeTooltip": "{0} lytter på {1}",
+ "MessageHeatmapNoListeningSessions": "Ingen lytteøkter på {0}",
"MessageImportantNotice": "Viktig varsel!",
"MessageInsertChapterBelow": "Sett inn kapittel under",
+ "MessageInvalidAsin": "Ugyldig ASIN",
"MessageItemsSelected": "{0} Gjenstander valgt",
"MessageItemsUpdated": "{0} Gjenstander oppdatert",
"MessageJoinUsOn": "Følg oss nå",
@@ -842,6 +866,7 @@
"MessageNoTasksRunning": "Ingen oppgaver kjører",
"MessageNoUpdatesWereNecessary": "Ingen oppdatering var nødvendig",
"MessageNoUserPlaylists": "Du har ingen spillelister",
+ "MessageNoUserPlaylistsHelp": "Spillelister er private. Bare brukeren som oppretter dem kan se dem.",
"MessageNotYetImplemented": "Ikke implementert ennå",
"MessageOpmlPreviewNote": "PS: Dette er en forhåndvisning av en OPML-fil. Den faktiske podcast-tittelen hentes direkte fra RSS-feeden.",
"MessageOr": "eller",
@@ -864,8 +889,10 @@
"MessageRestoreBackupConfirm": "Er du sikker på at du vil gjenopprette sikkerhetskopien som var laget",
"MessageRestoreBackupWarning": "gjenoppretting av sikkerhetskopi vil overskrive hele databasen under /config og omslagsbilde under /metadata/items og /metadata/authors.
Sikkerhetskopier endrer ikke noen filer under dine bibliotekmapper. Hvis du har aktivert tjenerinstillingen for å lagre omslagsbilder og metadata i bibliotekmapper så vil ikke de filene bli tatt sikkerhetskopi eller overskrevet.
Alle klientene som bruker din tjener vil bli fornyet automatisk.",
"MessageScheduleLibraryScanNote": "For de fleste brukere er det anbefalt å la denne funksjonen være slått av, og la mappeovervåkeren stå på. Mappeovervåkeren oppdager automatisk endringer i biblioteksmappene. Mappeovervåkeren fungerer ikke med alle filsystemer (f.eks. NFS) og da kan planlagt skanning av bibliotekene brukes i steden for.",
+ "MessageScheduleRunEveryWeekdayAtTime": "Kjør hver {0} kl. {1}",
"MessageSearchResultsFor": "Søk resultat for",
"MessageSelected": "{0} valgt",
+ "MessageSeriesSequenceCannotContainSpaces": "Serienummer kan ikke inneholde mellomrom",
"MessageServerCouldNotBeReached": "Tjener kunne ikke bli nådd",
"MessageSetChaptersFromTracksDescription": "Sett kapitler ved å bruke hver lydfil som kapittel og kapitteltittel som lydfilnavnet",
"MessageShareExpirationWillBe": "Utløp vil være {0}",
@@ -886,6 +913,27 @@
"MessageTaskFailedToMergeAudioFiles": "Kunne ikke slå sammen lydfiler",
"MessageTaskFailedToMoveM4bFile": "Kunne ikke flytte M4B-fil",
"MessageTaskFailedToWriteMetadataFile": "Kunne ikke lagre metadata-fil",
+ "MessageTaskMatchingBooksInLibrary": "Samsvarende bøker i biblioteket \"{0}\"",
+ "MessageTaskNoFilesToScan": "Ingen filer å skanne",
+ "MessageTaskOpmlImport": "OPML-import",
+ "MessageTaskOpmlImportDescription": "Oppretter podkaster fra {0} RSS-feeder",
+ "MessageTaskOpmlImportFeed": "OPML-importfeed",
+ "MessageTaskOpmlImportFeedDescription": "Importerer RSS-feed \"{0}\"",
+ "MessageTaskOpmlImportFeedFailed": "Kunne ikke hente podcast-feed",
+ "MessageTaskOpmlImportFeedPodcastDescription": "Oppretter podkast \"{0}\"",
+ "MessageTaskOpmlImportFeedPodcastExists": "Podkast finnes allerede på stien",
+ "MessageTaskOpmlImportFeedPodcastFailed": "Misslykkes å opprette podcast",
+ "MessageTaskOpmlImportFinished": "La til {0} podkaster",
+ "MessageTaskOpmlParseFailed": "Klarte ikke å tolke OPML-fil",
+ "MessageTaskOpmlParseFastFail": "Ugyldig OPML-fil: -tagg ble ikke funnet ELLER en -tagg ble ikke funnet",
+ "MessageTaskOpmlParseNoneFound": "Fant ingen feeder i OPML-filen",
+ "MessageTaskScanItemsAdded": "{0} lagt til",
+ "MessageTaskScanItemsMissing": "{0} mangler",
+ "MessageTaskScanItemsUpdated": "{0} oppdatert",
+ "MessageTaskScanNoChangesNeeded": "Ingen endringer nødvendig",
+ "MessageTaskScanningFileChanges": "Skanner filendringer i \"{0}\"",
+ "MessageTaskScanningLibrary": "Skanner biblioteket \"{0}\"",
+ "MessageTaskTargetDirectoryNotWritable": "Målkatalogen er ikke skrivbar",
"MessageThinking": "Tenker...",
"MessageUploaderItemFailed": "Opplastning mislykkes",
"MessageUploaderItemSuccess": "Opplastning fullført!",
@@ -903,13 +951,43 @@
"NoteUploaderFoldersWithMediaFiles": "Mapper med mediefiler vil bli behandlet som separate bibliotekgjenstander.",
"NoteUploaderOnlyAudioFiles": "Om man laster opp kun lydfiler så vil hver lydfil bli behandlet som en separat lydbok.",
"NoteUploaderUnsupportedFiles": "Filer som ikke er støttet vil bli ignorert. Når man velger eller slipper en mappe, filer som ikke er en mappe vil bli ignorert.",
+ "NotificationOnBackupCompletedDescription": "Utløses når en sikkerhetskopi er fullført",
+ "NotificationOnBackupFailedDescription": "Utløses når en sikkerhetskopi mislykkes",
+ "NotificationOnEpisodeDownloadedDescription": "Utløses når en podkastepisode lastes ned automatisk",
+ "NotificationOnRSSFeedDisabledDescription": "Utløses når automatiske episodenedlastinger deaktiveres på grunn av for mange mislykkede forsøk",
+ "NotificationOnRSSFeedFailedDescription": "Utløses når RSS-feedforespørselen mislykkes for en automatisk episodenedlasting",
+ "NotificationOnTestDescription": "Hendelse for testing av varslingssystemet",
+ "PlaceholderBulkChapterInput": "Skriv inn kapitteltittel eller bruk nummerering (f.eks. 'Episode 1', 'Kapittel 10', '1.')",
"PlaceholderNewCollection": "Ny samlingsnavn",
"PlaceholderNewFolderPath": "Ny mappesti",
"PlaceholderNewPlaylist": "Ny spillelistenavn",
"PlaceholderSearch": "Søk..",
"PlaceholderSearchEpisode": "Søk episode..",
+ "StatsAuthorsAdded": "forfattere lagt til",
+ "StatsBooksAdded": "bøker lagt til",
+ "StatsBooksAdditional": "Noen av tilleggene inkluderer…",
+ "StatsBooksFinished": "bøker fullført",
+ "StatsBooksFinishedThisYear": "Noen bøker fullført i år…",
+ "StatsBooksListenedTo": "bøker lyttet til",
+ "StatsCollectionGrewTo": "Boksamlingen din vokste til…",
+ "StatsSessions": "økter",
+ "StatsSpentListening": "brukt på lytting",
+ "StatsTopAuthor": "BESTE FORFATTER",
+ "StatsTopAuthors": "BESTE FORFATTERE",
+ "StatsTopGenre": "BESTE SJANGER",
+ "StatsTopGenres": "BESTE SJANGRE",
+ "StatsTopMonth": "BESTE MÅNED",
+ "StatsTopNarrator": "BESTE FORTELLER",
+ "StatsTopNarrators": "BESTE FORTELLERE",
+ "StatsTotalDuration": "Med en total varighet på…",
+ "StatsYearInReview": "ÅRET SOM GIKK",
"ToastAccountUpdateSuccess": "Konto oppdatert",
+ "ToastAppriseUrlRequired": "Du må angi en Apprise-URL",
+ "ToastAsinRequired": "ASIN er påkrevd",
"ToastAuthorImageRemoveSuccess": "Forfatter bilde fjernet",
+ "ToastAuthorNotFound": "Fant ikke forfatter \"{0}\"",
+ "ToastAuthorRemoveSuccess": "Forfatter fjernet",
+ "ToastAuthorSearchNotFound": "Fant ikke forfatter",
"ToastAuthorUpdateMerged": "Forfatter slått sammen",
"ToastAuthorUpdateSuccess": "Forfatter oppdatert",
"ToastAuthorUpdateSuccessNoImageFound": "Forfatter oppdater (ingen bilde funnet)",
@@ -923,6 +1001,7 @@
"ToastBackupRestoreFailed": "Misslykkes å gjenopprette sikkerhetskopi",
"ToastBackupUploadFailed": "Misslykkes å laste opp sikkerhetskopi",
"ToastBackupUploadSuccess": "Sikkerhetskopi lastet opp",
+ "ToastBatchApplyDetailsToItemsSuccess": "Detaljer brukt på elementene",
"ToastBatchDeleteFailed": "Sletting feilet på utvalget",
"ToastBatchDeleteSuccess": "Sletting av samling utført",
"ToastBatchQuickMatchFailed": "Feil ved rask integrering av metadata!",
@@ -932,17 +1011,25 @@
"ToastBookmarkCreateFailed": "Misslykkes å opprette bokmerke",
"ToastBookmarkCreateSuccess": "Bokmerke lagt til",
"ToastBookmarkRemoveSuccess": "Bokmerke fjernet",
+ "ToastBulkChapterInvalidCount": "Skriv inn et tall mellom 1 og 150",
"ToastCachePurgeFailed": "Kunne ikke å slette mellomlager",
"ToastCachePurgeSuccess": "Mellomlager slettet",
+ "ToastChapterLocked": "Kapittelet er låst.",
+ "ToastChapterStartTimeAdjusted": "Kapittelstart ble justert med {0} sekunder",
+ "ToastChaptersAllLocked": "Alle kapitler er låst. Lås opp noen kapitler for å flytte tidene.",
"ToastChaptersHaveErrors": "Kapittel har feil",
+ "ToastChaptersInvalidShiftAmountLast": "Ugyldig forskyvningsverdi. Starttid for siste kapittel vil gå utover varigheten til denne lydboken.",
+ "ToastChaptersInvalidShiftAmountStart": "Ugyldig forskyvningsverdi. Det første kapitlet ville fått null eller negativ lengde og blitt overskrevet av det andre kapitlet. Øk starttiden til det andre kapitlet.",
"ToastChaptersMustHaveTitles": "Kapittel må ha titler",
"ToastChaptersRemoved": "Kapitler fjernet",
"ToastChaptersUpdated": "Kapitler oppdatert",
"ToastCollectionItemsAddFailed": "Feil med å legge til element(er)",
"ToastCollectionRemoveSuccess": "Samling fjernet",
"ToastCollectionUpdateSuccess": "samlingupdated",
+ "ToastConnectionNotAvailable": "Tilkobling er ikke tilgjengelig. Prøv igjen senere",
"ToastCoverSearchFailed": "Finner ikke bokomslag",
"ToastCoverUpdateFailed": "Oppdatering av bilde feilet",
+ "ToastDateTimeInvalidOrIncomplete": "Dato og klokkeslett er ugyldig eller ufullstendig",
"ToastDeleteFileFailed": "Kunne ikke slette fil",
"ToastDeleteFileSuccess": "Fil slettet",
"ToastDeviceAddFailed": "Kunne ikke legge til enhet",
@@ -955,6 +1042,9 @@
"ToastEpisodeDownloadQueueClearFailed": "Kunne ikke tømme køen",
"ToastEpisodeDownloadQueueClearSuccess": "Nedlastingskø for eposider tømt",
"ToastEpisodeUpdateSuccess": "{0} episoder oppdatert",
+ "ToastErrorCannotShare": "Kan ikke dele direkte på denne enheten",
+ "ToastFailedToCreate": "Kunne ikke opprette",
+ "ToastFailedToDelete": "Kunne ikke slette",
"ToastFailedToLoadData": "Kunne ikke laste inn data",
"ToastFailedToMatch": "Kunne ikke matche",
"ToastFailedToShare": "Deling feilet",
@@ -962,6 +1052,7 @@
"ToastInvalidImageUrl": "Ugyldig URL for bilde",
"ToastInvalidMaxEpisodesToDownload": "Ugyldig maksimalt antall for nedlasting av episoder",
"ToastInvalidUrl": "Ugyldig URL",
+ "ToastInvalidUrls": "Én eller flere URL-er er ugyldige",
"ToastItemCoverUpdateSuccess": "Omslag oppdatert",
"ToastItemDeletedFailed": "Kunne ikke slette element",
"ToastItemDeletedSuccess": "Element slettet",
@@ -986,6 +1077,7 @@
"ToastMustHaveAtLeastOnePath": "Påkrevd med minst én mappe",
"ToastNameEmailRequired": "Navn og e-post påkrevd",
"ToastNameRequired": "Navn er påkrevd",
+ "ToastNewApiKeyUserError": "Du må velge en bruker",
"ToastNewEpisodesFound": "{0} nye episoder funnet",
"ToastNewUserCreatedFailed": "Kunne ikke opprette konto: \"{0}\"",
"ToastNewUserCreatedSuccess": "Ny konto opprettet",
@@ -994,6 +1086,7 @@
"ToastNewUserTagError": "Velg minst en tag",
"ToastNewUserUsernameError": "Skriv inn brukernavn",
"ToastNoNewEpisodesFound": "Ingen nye episoder funnet",
+ "ToastNoRSSFeed": "Podkasten har ikke en RSS-feed",
"ToastNoUpdatesNecessary": "Ingen oppdateringer nødvendig",
"ToastNotificationCreateFailed": "Kunne ikke opprette varsling",
"ToastNotificationDeleteFailed": "Kunne ikke slette varsling",
@@ -1009,6 +1102,7 @@
"ToastPlaylistUpdateSuccess": "Spilleliste oppdatert",
"ToastPodcastCreateFailed": "Misslykkes å opprette podcast",
"ToastPodcastCreateSuccess": "Podcast opprettet",
+ "ToastPodcastEpisodeUpdated": "Episode oppdatert",
"ToastPodcastGetFeedFailed": "Kunne ikke hente podcast-feed",
"ToastPodcastNoEpisodesInFeed": "Ingen episoder funnet i RSS-feed",
"ToastPodcastNoRssFeed": "Podcast har ingen RSS-feed",
@@ -1033,6 +1127,7 @@
"ToastSelectAtLeastOneUser": "Velg minst én bruker",
"ToastSendEbookToDeviceFailed": "Misslykkes å sende ebok",
"ToastSendEbookToDeviceSuccess": "Ebok sendt til \"{0}\"",
+ "ToastSeriesSubmitFailedSameName": "Kan ikke legge til to serier med samme navn",
"ToastSeriesUpdateFailed": "Misslykkes å oppdatere serie",
"ToastSeriesUpdateSuccess": "Serie oppdatert",
"ToastServerSettingsUpdateSuccess": "Server-innstillinger oppdatert",
@@ -1051,10 +1146,20 @@
"ToastUnknownError": "Ukjent feil",
"ToastUnlinkOpenIdFailed": "Kunne ikke koble bruker fra OpenID",
"ToastUnlinkOpenIdSuccess": "Bruker koblet fra OpenID",
+ "ToastUploaderFilepathExistsError": "Filstien \"{0}\" finnes allerede på serveren",
+ "ToastUploaderItemExistsInSubdirectoryError": "Elementet \"{0}\" bruker en underkatalog av opplastingsstien.",
"ToastUserDeleteFailed": "Misslykkes å slette bruker",
"ToastUserDeleteSuccess": "Bruker slettet",
"ToastUserPasswordChangeSuccess": "Passord ble endret",
"ToastUserPasswordMismatch": "Passord må stemme overens",
"ToastUserPasswordMustChange": "Nytt passord kan ikke være identisk med gammelt passord",
- "ToastUserRootRequireName": "Root-brukernavn er påkrevd"
+ "ToastUserRootRequireName": "Root-brukernavn er påkrevd",
+ "TooltipAddChapters": "Legg til kapittel(er)",
+ "TooltipAddOneSecond": "Legg til 1 sekund",
+ "TooltipAdjustChapterStart": "Klikk for å justere starttid",
+ "TooltipLockAllChapters": "Lås alle kapitler",
+ "TooltipLockChapter": "Lås kapittel (Shift+klikk for område)",
+ "TooltipSubtractOneSecond": "Trekk fra 1 sekund",
+ "TooltipUnlockAllChapters": "Lås opp alle kapitler",
+ "TooltipUnlockChapter": "Lås opp kapittel (Shift+klikk for område)"
}
diff --git a/client/strings/pl.json b/client/strings/pl.json
index 0b92dc89c..d1bc6c062 100644
--- a/client/strings/pl.json
+++ b/client/strings/pl.json
@@ -11,7 +11,7 @@
"ButtonApplyChapters": "Zatwierdź rozdziały",
"ButtonAuthors": "Autorzy",
"ButtonBack": "Wstecz",
- "ButtonBatchEditPopulateFromExisting": "Powiel z poprzednich",
+ "ButtonBatchEditPopulateFromExisting": "Uzupełnij na podstawie istniejących",
"ButtonBatchEditPopulateMapDetails": "Powiel szczegóły mapy",
"ButtonBrowseForFolder": "Wyszukaj folder",
"ButtonCancel": "Anuluj",
@@ -55,7 +55,7 @@
"ButtonNext": "Następny",
"ButtonNextChapter": "Następny rozdział",
"ButtonNextItemInQueue": "Następny element w kolejce",
- "ButtonOk": "Ok",
+ "ButtonOk": "OK",
"ButtonOpenFeed": "Otwórz feed",
"ButtonOpenManager": "Otwórz menadżera",
"ButtonPause": "Wstrzymaj",
@@ -76,7 +76,7 @@
"ButtonReScan": "Ponowne skanowanie",
"ButtonRead": "Czytaj",
"ButtonReadLess": "Pokaż mniej",
- "ButtonReadMore": "Pokaż więcej",
+ "ButtonReadMore": "Czytaj więcej",
"ButtonRefresh": "Odśwież",
"ButtonRemove": "Usuń",
"ButtonRemoveAll": "Usuń wszystko",
@@ -96,7 +96,7 @@
"ButtonScrollRight": "Przewiń w prawo",
"ButtonSearch": "Szukaj",
"ButtonSelectFolderPath": "Wybierz ścieżkę folderu",
- "ButtonSeries": "Seria",
+ "ButtonSeries": "Serie",
"ButtonSetChaptersFromTracks": "Ustawiaj rozdziały na podstawie utworów",
"ButtonShare": "Udostępnij",
"ButtonShiftTimes": "Przesunięcie czasowe",
@@ -127,7 +127,7 @@
"HeaderAudiobookTools": "Narzędzia do zarządzania audiobookami",
"HeaderAuthentication": "Uwierzytelnianie",
"HeaderBackups": "Kopie zapasowe",
- "HeaderBulkChapterModal": "Dodaj wiele rozdziałów",
+ "HeaderBulkChapterModal": "Dodaj kilka rozdziałów",
"HeaderChangePassword": "Zmień hasło",
"HeaderChapters": "Rozdziały",
"HeaderChooseAFolder": "Wybierz folder",
@@ -275,7 +275,7 @@
"LabelBonus": "Bonus",
"LabelBooks": "Książki",
"LabelButtonText": "Tekst przycisku",
- "LabelByAuthor": "autorstwa {0}",
+ "LabelByAuthor": "Autor {0}",
"LabelChangePassword": "Zmień hasło",
"LabelChannels": "Kanały",
"LabelChapterCount": "{0} rozdziałów",
@@ -286,7 +286,7 @@
"LabelClickToUseCurrentValue": "Kliknij by zastosować aktualną wartość",
"LabelClosePlayer": "Zamknij odtwarzacz",
"LabelCodec": "Kodek",
- "LabelCollapseSeries": "Podsumuj serię",
+ "LabelCollapseSeries": "Zwiń serię",
"LabelCollapseSubSeries": "Zwiń podserie",
"LabelCollection": "Kolekcja",
"LabelCollections": "Kolekcje",
@@ -295,11 +295,12 @@
"LabelContinueListening": "Kontynuuj słuchanie",
"LabelContinueReading": "Kontynuuj czytanie",
"LabelContinueSeries": "Kontynuuj serię",
+ "LabelCorsAllowed": "Dozwolone źródła CORS",
"LabelCover": "Okładka",
"LabelCoverImageURL": "URL okładki",
"LabelCoverProvider": "Dostawca okładki",
"LabelCreatedAt": "Utworzone",
- "LabelCronExpression": "Wyrażenie CRON",
+ "LabelCronExpression": "Wyrażenie harmonogramowania zadań cron",
"LabelCurrent": "Aktualny",
"LabelCurrently": "Obecnie:",
"LabelCustomCronExpression": "Niestandardowe wyrażenie Cron:",
@@ -338,11 +339,11 @@
"LabelEnable": "Włącz",
"LabelEncodingBackupLocation": "Kopia zapasowa twoich oryginalnych plików audio będzie się znajdować w:",
"LabelEncodingChaptersNotEmbedded": "W audiobookach wielościeżkowych rozdziały nie są osadzone.",
- "LabelEncodingClearItemCache": "Pamiętaj o okresowym czyszczeniu pamięci podręcznej elementów.",
+ "LabelEncodingClearItemCache": "Pamiętaj, aby okresowo czyścić pamięć podręczną elementów.",
"LabelEncodingFinishedM4B": "Ukończony plik M4B zostanie umieszczony w folderze audiobooka pod adresem:",
"LabelEncodingInfoEmbedded": "Metadane zostaną osadzone w ścieżkach audio w folderze z audiobookiem.",
"LabelEncodingStartedNavigation": "Po uruchomieniu zadania możesz opuścić tę stronę.",
- "LabelEncodingTimeWarning": "Konwersja może potrwać do 30 minut.",
+ "LabelEncodingTimeWarning": "Kodowanie może potrwać do 30 minut.",
"LabelEncodingWarningAdvancedSettings": "Ostrzeżenie: Nie aktualizuj tych ustawień, jeśli nie jesteś zaznajomiony ze sposobem działania ffmpeg i opcji konwersji.",
"LabelEncodingWatcherDisabled": "Jeśli monitorowanie folderów jest wyłączone, należy ponownie przeskanować audiobooka.",
"LabelEnd": "Zakończ",
@@ -358,11 +359,11 @@
"LabelExample": "Przykład",
"LabelExpandSeries": "Rozwiń serie",
"LabelExpandSubSeries": "Rozwiń podserie",
- "LabelExpired": "Przeterminowane",
- "LabelExpiresAt": "Wygasa w",
+ "LabelExpired": "Wygasły",
+ "LabelExpiresAt": "Wygasa o",
"LabelExpiresInSeconds": "Wygasa za (sekund)",
"LabelExpiresNever": "Nigdy",
- "LabelExplicit": "Nieprzyzwoite",
+ "LabelExplicit": "18+",
"LabelExplicitChecked": "Nieprzyzwoite (sprawdzone)",
"LabelExplicitUnchecked": "Przyzwoite (niesprawdzone)",
"LabelExportOPML": "Wyeksportuj OPML",
@@ -382,7 +383,7 @@
"LabelFolders": "Foldery",
"LabelFontBold": "Pogrubiony",
"LabelFontBoldness": "Grubość czcionki",
- "LabelFontFamily": "Rodzina czcionek",
+ "LabelFontFamily": "Krój pisma",
"LabelFontItalic": "Kursywa",
"LabelFontScale": "Rozmiar czcionki",
"LabelFontStrikethrough": "Przekreślony",
@@ -435,9 +436,9 @@
"LabelLibraryFilterSublistEmpty": "Brak {0}",
"LabelLibraryItem": "Element biblioteki",
"LabelLibraryName": "Nazwa biblioteki",
- "LabelLibrarySortByProgress": "Postęp: Ostatnio zaktualizowane",
- "LabelLibrarySortByProgressFinished": "Postęp: Ukończone",
- "LabelLibrarySortByProgressStarted": "Postęp: Rozpoczęte",
+ "LabelLibrarySortByProgress": "Postęp: Ostatnia aktualizacja",
+ "LabelLibrarySortByProgressFinished": "Postęp: Ukończony",
+ "LabelLibrarySortByProgressStarted": "Postęp: Rozpoczęty",
"LabelLimit": "Limit",
"LabelLineSpacing": "Odstęp między wierszami",
"LabelListenAgain": "Słuchaj ponownie",
@@ -449,7 +450,7 @@
"LabelMatchConfidence": "Zaufanie",
"LabelMatchExistingUsersBy": "Dopasuje istniejących użytkowników poprzez",
"LabelMatchExistingUsersByDescription": "Służy do łączenia istniejących użytkowników. Po połączeniu użytkownicy zostaną dopasowani za pomocą unikalnego identyfikatora od dostawcy SSO",
- "LabelMaxEpisodesToDownload": "Maksymalna liczba odcinków do pobrania. Użyj 0, aby wyłączyć ograniczenie.",
+ "LabelMaxEpisodesToDownload": "Maksymalna liczba odcinków do pobrania. Użyj 0, aby uzyskać nieograniczoną liczbę.",
"LabelMaxEpisodesToDownloadPerCheck": "Maksymalna liczba nowych odcinków do pobrania na jedno sprawdzenie",
"LabelMaxEpisodesToKeep": "Maksymalna liczba odcinków do zachowania",
"LabelMaxEpisodesToKeepHelp": "Wartość 0 wyłącza maksymalny limit. Po automatycznym pobraniu nowego odcinka, najstarszy odcinek zostanie usunięty, jeśli masz ich więcej niż X. Spowoduje to usunięcie tylko 1 odcinka na nowe pobieranie.",
@@ -497,7 +498,8 @@
"LabelNumberOfChapters": "Liczba rozdziałów:",
"LabelNumberOfEpisodes": "# Odcinków",
"LabelOpenIDAdvancedPermsClaimDescription": "Nazwa deklaracji OpenID zawierającej zaawansowane uprawnienia do działań użytkownika w aplikacji, które będą miały zastosowanie do ról innych niż administracyjne (jeśli skonfigurowano). Jeśli deklaracja nie zostanie uwzględniona w odpowiedzi, dostęp do ABS zostanie zablokowany. Brak jednej opcji zostanie uznany za fałsz. Upewnij się, że deklaracja dostawcy tożsamości jest zgodna z oczekiwaną strukturą:",
- "LabelOpenIDClaims": "Pozostaw poniższe opcje puste, aby wyłączyć zaawansowane przypisywanie grup i uprawnień. Automatycznie zostanie przypisana grupa „Użytkownik”.",
+ "LabelOpenIDClaims": "Pozostaw poniższe opcje puste, aby wyłączyć zaawansowane przypisywanie grup i uprawnień. Wówczas automatycznie zostanie przypisana grupa „Użytkownik”.",
+ "LabelOpenIDGroupClaimDescription": "Nazwa roszczenia OpenID zawierającego listę grup użytkownika. Powszechnie nazywane grupami. Jeśli skonfigurowano, aplikacja automatycznie przypisze role na podstawie przynależności użytkownika do grup, pod warunkiem, że te grupy są nazwane bez uwzględniania wielkości liter „admin”, „user” lub „guest” w roszczeniu. Roszczenie powinno zawierać listę, a jeśli użytkownik należy do wielu grup, aplikacja przypisze rolę odpowiadającą najwyższemu poziomowi dostępu. Jeśli żadna grupa nie będzie pasować, dostęp zostanie odrzucony.",
"LabelOpenRSSFeed": "Otwórz kanał RSS",
"LabelOverwrite": "Nadpisz",
"LabelPaginationPageXOfY": "Strona {0} z {1}",
@@ -572,11 +574,11 @@
"LabelSelectUsers": "Wybór użytkowników",
"LabelSendEbookToDevice": "Wyślij ebook do...",
"LabelSequence": "Kolejność",
- "LabelSerial": "Seria",
+ "LabelSerial": "Numer serii",
"LabelSeries": "Serie",
"LabelSeriesName": "Nazwy serii",
"LabelSeriesProgress": "Postęp w serii",
- "LabelServerLogLevel": "Poziom logowania servera",
+ "LabelServerLogLevel": "Poziom logów servera",
"LabelServerYearReview": "Podsumowanie serwera w roku ({0})",
"LabelSetEbookAsPrimary": "Ustaw jako pierwszy",
"LabelSetEbookAsSupplementary": "Ustaw jako dodatkowy",
@@ -586,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Widok półki z książkami",
"LabelSettingsChromecastSupport": "Wsparcie Chromecast",
"LabelSettingsDateFormat": "Format daty",
- "LabelSettingsEnableWatcher": "Automatyczne skanowanie bibliotek w poszukiwaniu zmian",
- "LabelSettingsEnableWatcherForLibrary": "Automatyczne skanowanie biblioteki w poszukiwaniu zmian",
+ "LabelSettingsEnableWatcher": "Automatyczne monitorowanie bibliotek w poszukiwaniu zmian",
+ "LabelSettingsEnableWatcherForLibrary": "Automatyczne monitorowanie biblioteki w poszukiwaniu zmian",
"LabelSettingsEnableWatcherHelp": "Włącza automatyczne dodawanie/aktualizację pozycji gdy wykryte zostaną zmiany w plikach. Wymaga restartu serwera",
"LabelSettingsEpubsAllowScriptedContent": "Zezwalanie na skrypty w plikach epub",
"LabelSettingsEpubsAllowScriptedContentHelp": "Zezwala plikom epub na wykonywanie skryptów. Zaleca się mieć to ustawienie wyłączone, chyba że ma się zaufanie do źródła plików epub.",
@@ -620,7 +622,7 @@
"LabelSettingsStoreMetadataWithItemHelp": "Domyślnie metadane są przechowywane w folderze /metadata/items, włączenie tej opcji spowoduje, że okładka będzie przechowywana w folderze ksiązki. Tylko jedna okładka o nazwie pliku \"cover\" będzie przechowywana",
"LabelSettingsTimeFormat": "Format czasu",
"LabelShare": "Udostępnij",
- "LabelShareDownloadableHelp": "Użytkownicy mogą przy pomocy linka ściągnąć archiwum ZIP pozycji biblioteki",
+ "LabelShareDownloadableHelp": "Zezwala użytkownikom z linkiem udostępniania na pobranie pliku zip elementu biblioteki.",
"LabelShareOpen": "Otwórz udział",
"LabelShareURL": "Link do udziału",
"LabelShowAll": "Pokaż wszystko",
@@ -628,6 +630,7 @@
"LabelShowSubtitles": "Pokaż Napisy",
"LabelSize": "Rozmiar",
"LabelSleepTimer": "Wyłącznik czasowy",
+ "LabelSlug": "slug (części URL która identyfikuje konkretny zasób)",
"LabelSortAscending": "Rosnąco",
"LabelSortDescending": "Malejąco",
"LabelSortPubDate": "Sortuj według daty publikacji",
@@ -658,14 +661,15 @@
"LabelTagsAccessibleToUser": "Tagi dostępne dla użytkownika",
"LabelTagsNotAccessibleToUser": "Znaczniki niedostępne dla użytkownika",
"LabelTasks": "Uruchomione zadania",
- "LabelTextEditorBulletedList": "Lista punktowana",
+ "LabelTextEditorBulletedList": "Lista wypunktowana",
"LabelTextEditorLink": "Link",
"LabelTextEditorNumberedList": "Lista numerowana",
"LabelTextEditorUnlink": "Usuń link",
- "LabelTheme": "Kompozycja",
+ "LabelTheme": "Motyw",
"LabelThemeDark": "Ciemny",
"LabelThemeLight": "Jasny",
"LabelThemeSepia": "Sepia",
+ "LabelTimeBase": "Podstawa czasu",
"LabelTimeDurationXHours": "{0} godzin",
"LabelTimeDurationXMinutes": "{0} minuty",
"LabelTimeDurationXSeconds": "{0} sekundy",
@@ -683,13 +687,13 @@
"LabelToolsMakeM4bDescription": "Tworzy plik w formacie .M4B, który zawiera metadane, okładkę oraz rozdziały.",
"LabelToolsSplitM4b": "Podziel plik .M4B na pliki .MP3",
"LabelToolsSplitM4bDescription": "Podziel plik .M4B na pliki .MP3 na rozdziały z załączonymi metadanymi oraz okładką.",
- "LabelTotalDuration": "TCałkowita długość",
+ "LabelTotalDuration": "Całkowita długość",
"LabelTotalTimeListened": "Całkowity czas odtwarzania",
"LabelTrackFromFilename": "Ścieżka z nazwy pliku",
"LabelTrackFromMetadata": "Ścieżka z metadanych",
"LabelTracks": "Ścieżki",
"LabelTracksMultiTrack": "Wielościeżkowy",
- "LabelTracksNone": "Brak ścieżek",
+ "LabelTracksNone": "Brak utworów",
"LabelTracksSingleTrack": "Pojedyncza ścieżka",
"LabelTrailer": "Zwiastun",
"LabelType": "Typ",
@@ -706,7 +710,7 @@
"LabelUploaderDragAndDropFilesOnly": "Przeciągnij i upuść pliki",
"LabelUploaderDropFiles": "Puść pliki",
"LabelUploaderItemFetchMetadataHelp": "Automatycznie pobierz tytuł, autora i serie",
- "LabelUseAdvancedOptions": "Opcje zaawansowane",
+ "LabelUseAdvancedOptions": "Użyj ustawień zaawansowanych",
"LabelUseChapterTrack": "Użyj ścieżki rozdziału",
"LabelUseFullTrack": "Użycie ścieżki rozdziału",
"LabelUseZeroForUnlimited": "Użyj 0, aby wyłączyć ograniczenia",
@@ -719,6 +723,8 @@
"LabelViewPlayerSettings": "Zobacz ustawienia odtwarzacza",
"LabelViewQueue": "Wyświetlaj kolejkę odtwarzania",
"LabelVolume": "Głośność",
+ "LabelWebRedirectURLsDescription": "Zezwól na te adresy URL w swoim dostawcy OAuth, aby umożliwić przekierowanie z powrotem do aplikacji internetowej po zalogowaniu:",
+ "LabelWebRedirectURLsSubfolder": "Podfolder dla adresów URL przekierowań",
"LabelWeekdaysToRun": "Dni tygodnia",
"LabelXBooks": "{0} książek",
"LabelXItems": "{0} elementów",
@@ -739,6 +745,7 @@
"MessageBackupsLocationNoEditNote": "Uwaga: Lokalizacja kopii zapasowej jest ustawiona poprzez zmienną środowiskową i nie może być tutaj zmieniona.",
"MessageBackupsLocationPathEmpty": "Ścieżka do kopii zapasowej nie może być pusta",
"MessageBatchEditPopulateMapDetailsAllHelp": "Wypełnij włączone pola danymi ze wszystkich elementów. Pola z wieloma wartościami zostaną scalone.",
+ "MessageBatchEditPopulateMapDetailsItemHelp": "Wypełnia włączone pola szczegółów mapy danymi z tego elementu",
"MessageBatchQuickMatchDescription": "Quick Match będzie próbował dodać brakujące okładki i metadane dla wybranych elementów. Włącz poniższe opcje, aby umożliwić Quick Match nadpisanie istniejących okładek i/lub metadanych.",
"MessageBookshelfNoCollections": "Nie posiadasz jeszcze żadnych kolekcji",
"MessageBookshelfNoCollectionsHelp": "Kolekcje są publiczne. Wszyscy użytkownicy mający dostęp do biblioteki mogą je zobaczyć.",
@@ -810,6 +817,7 @@
"MessageFetching": "Pobieranie...",
"MessageForceReScanDescription": "przeskanuje wszystkie pliki ponownie, jak przy świeżym skanowaniu. Tagi ID3 plików audio, pliki OPF i pliki tekstowe będą skanowane jak nowe.",
"MessageHeatmapListeningTimeTooltip": "{0} słucha na {1}",
+ "MessageHeatmapNoListeningSessions": "Brak sesji słuchania na {0}",
"MessageImportantNotice": "Ważna informacja!",
"MessageInsertChapterBelow": "Wstaw rozdział poniżej",
"MessageInvalidAsin": "Nieprawidłowy ASIN",
@@ -884,6 +892,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Uruchom w każdy {0} o {1}",
"MessageSearchResultsFor": "Wyniki wyszukiwania dla",
"MessageSelected": "{0} wybranych",
+ "MessageSeriesSequenceCannotContainSpaces": "Sekwencja serii nie może zawierać spacji",
"MessageServerCouldNotBeReached": "Nie udało się uzyskać połączenia z serwerem",
"MessageSetChaptersFromTracksDescription": "Ustaw rozdziały, używając każdego pliku audio jako rozdziału, a tytuł rozdziału jako nazwy pliku audio.",
"MessageShareExpirationWillBe": "Czas udostępniania {0}",
@@ -901,12 +910,29 @@
"MessageTaskFailedToBackupAudioFile": "Nieudana próba wykonania kopii zapasowego pliku audio \"{0}\"",
"MessageTaskFailedToCreateCacheDirectory": "Nie udało się utworzyć katalogu cache",
"MessageTaskFailedToEmbedMetadataInFile": "Nie udało się wbudować metadanych do pliku \"{0}\"",
+ "MessageTaskFailedToMergeAudioFiles": "Nie udało się połączyć plików audio",
+ "MessageTaskFailedToMoveM4bFile": "Nie udało się przenieść pliku m4b",
"MessageTaskFailedToWriteMetadataFile": "Niepowodzenie zapisania pliku metadanych",
+ "MessageTaskMatchingBooksInLibrary": "Pasujące książki w bibliotece \"{0}\"",
"MessageTaskNoFilesToScan": "Brak plików do skanowania",
+ "MessageTaskOpmlImport": "Importuj OPML",
+ "MessageTaskOpmlImportDescription": "Tworzenie {0} podcastów z kanałów RSS",
+ "MessageTaskOpmlImportFeed": "Importuje plik OPML",
+ "MessageTaskOpmlImportFeedDescription": "Importowanie kanału RSS „{0}”",
+ "MessageTaskOpmlImportFeedFailed": "Nie udało się pobrać kanału podcastowego",
+ "MessageTaskOpmlImportFeedPodcastDescription": "Tworzenie podcastu \"{0}\"",
+ "MessageTaskOpmlImportFeedPodcastExists": "Podcast już istnieje pod podaną ścieżką",
+ "MessageTaskOpmlImportFeedPodcastFailed": "Nie udało się utworzyć podcastu",
+ "MessageTaskOpmlImportFinished": "Dodano {0} podcastów",
+ "MessageTaskOpmlParseFailed": "Błąd parsowania pliku OPML",
+ "MessageTaskOpmlParseFastFail": "Nieprawidłowy plik OPML. Nie znaleziono tagu LUB nie znaleziono tagu .",
+ "MessageTaskOpmlParseNoneFound": "Nie znaleziono kanałów w pliku OPML",
"MessageTaskScanItemsAdded": "Dodano {0}",
"MessageTaskScanItemsMissing": "Brakuje {0}",
"MessageTaskScanItemsUpdated": "Zaktualizowano {0}",
"MessageTaskScanNoChangesNeeded": "Brak zmian",
+ "MessageTaskScanningFileChanges": "Skanowanie zmian w plikach w „{0}”",
+ "MessageTaskScanningLibrary": "Skanowanie biblioteki \"{0}\"",
"MessageTaskTargetDirectoryNotWritable": "Brak prawa zapisu do folderu docelowego",
"MessageThinking": "Myślę...",
"MessageUploaderItemFailed": "Nie udało się przesłać",
@@ -925,6 +951,8 @@
"NoteUploaderFoldersWithMediaFiles": "Foldery z plikami multimedialnymi będą traktowane jako osobne elementy w bibliotece.",
"NoteUploaderOnlyAudioFiles": "Jeśli przesyłasz tylko pliki audio, każdy plik audio będzie traktowany jako osobny audiobook.",
"NoteUploaderUnsupportedFiles": "Nieobsługiwane pliki są ignorowane. Podczas dodawania folderu, inne pliki, które nie znajdują się w folderze elementu, są ignorowane.",
+ "NotificationOnTestDescription": "Zdarzenie używane do testowania systemu powiadomień",
+ "PlaceholderBulkChapterInput": "Wpisz tytuł rozdziału lub użyj numeracji (np. „Odcinek 1”, „Rozdział 10”, „1.”)",
"PlaceholderNewCollection": "Nowa nazwa kolekcji",
"PlaceholderNewFolderPath": "Nowa ścieżka folderu",
"PlaceholderNewPlaylist": "Nowa nazwa playlisty",
@@ -945,26 +973,50 @@
"StatsTopMonth": "TOPOWY MIESIĄC",
"StatsTopNarrator": "TOPOWY NARRATOR",
"StatsTopNarrators": "TOPOWI NARRATORZY",
+ "StatsTotalDuration": "O sumarycznej długości…",
"StatsYearInReview": "PRZEGLĄD ROKU",
"ToastAccountUpdateSuccess": "Zaktualizowano konto",
+ "ToastAsinRequired": "ASIN jest wymagany",
"ToastAuthorImageRemoveSuccess": "Zdjęcie autora usunięte",
+ "ToastAuthorNotFound": "Autor \"{0}\" nie został znaleziony",
+ "ToastAuthorRemoveSuccess": "Autor usunięty",
+ "ToastAuthorSearchNotFound": "Autor nie odnaleziony",
"ToastAuthorUpdateMerged": "Autor scalony",
"ToastAuthorUpdateSuccess": "Autor zaktualizowany",
"ToastAuthorUpdateSuccessNoImageFound": "Autor zaktualizowany (nie znaleziono obrazu)",
+ "ToastBackupAppliedSuccess": "Kopia zapasowa została przywrócona",
"ToastBackupCreateFailed": "Nie udało się utworzyć kopii zapasowej",
"ToastBackupCreateSuccess": "Utworzono kopię zapasową",
"ToastBackupDeleteFailed": "Nie udało się usunąć kopii zapasowej",
"ToastBackupDeleteSuccess": "Udało się usunąć kopie zapasowej",
+ "ToastBackupInvalidMaxKeep": "Nieprawidłowa ilość kopii zapasowych do przechowania",
+ "ToastBackupInvalidMaxSize": "Nieprawidłowy rozmiar maksymalny kopii zapasowej",
"ToastBackupRestoreFailed": "Nie udało się przywrócić kopii zapasowej",
"ToastBackupUploadFailed": "Nie udało się przesłać kopii zapasowej",
"ToastBackupUploadSuccess": "Kopia zapasowa została przesłana",
- "ToastBatchUpdateFailed": "Aktualizacja wsadowa nie powiodła się",
- "ToastBatchUpdateSuccess": "Aktualizacja wsadowa powiodła się",
+ "ToastBatchDeleteFailed": "Usuwanie zbiorcze nie powiodło się",
+ "ToastBatchDeleteSuccess": "Usuwanie zbiorcze powiodło się",
+ "ToastBatchUpdateFailed": "Aktualizacja zbiorcza nie powiodła się",
+ "ToastBatchUpdateSuccess": "Aktualizacja zbiorcza powiodła się",
"ToastBookmarkCreateFailed": "Nie udało się utworzyć zakładki",
"ToastBookmarkCreateSuccess": "Dodano zakładkę",
"ToastBookmarkRemoveSuccess": "Zakładka została usunięta",
+ "ToastBulkChapterInvalidCount": "Wprowadź liczbę z przedziału od 1 do 150",
+ "ToastCachePurgeFailed": "Nie udało się wyczyścić pamięci cache",
+ "ToastCachePurgeSuccess": "Wyczyszczono pamięć cache",
+ "ToastChapterLocked": "Rozdział jest zablokowany.",
+ "ToastChapterStartTimeAdjusted": "Czas rozpoczęcia rozdziału przesunięty o \"{0}\" sekund",
+ "ToastChaptersAllLocked": "Wszystkie rozdziały są zablokowane. Odblokuj edycję, aby użyć przesunięcia czasowego.",
+ "ToastChaptersHaveErrors": "Rozdziały posiadają błędy",
+ "ToastChaptersInvalidShiftAmountLast": "Niepoprawna wartość przesunięcia. Czas rozpoczęcia ostatniego rozdziału wykroczyłby poza długość tego audiobooka.",
+ "ToastChaptersInvalidShiftAmountStart": "Niepoprawna wartość przesunięcia. Pierwszy rozdział miałby długość mniejszą lub równą zeru oraz on zostałby nadpisany przez rozdział drugi. Ustaw późniejszy czas rozpoczęcia drugiego rozdziału.",
+ "ToastChaptersMustHaveTitles": "Rozdziały muszą posiadać tytuł",
+ "ToastChaptersRemoved": "Rozdziały usunięte",
+ "ToastChaptersUpdated": "Rozdziały zaktualizowane",
+ "ToastCollectionItemsAddFailed": "Dodanie elementów do kolekcji nie powiodło się",
"ToastCollectionRemoveSuccess": "Kolekcja usunięta",
"ToastCollectionUpdateSuccess": "Zaktualizowano kolekcję",
+ "ToastConnectionNotAvailable": "Brak połączenia. Spróbuj ponownie później",
"ToastCoverSearchFailed": "Nieudane wyszukiwanie okładki",
"ToastCoverUpdateFailed": "Nieudana aktualizacja okładki",
"ToastDateTimeInvalidOrIncomplete": "Niepoprawna data i czas",
@@ -975,12 +1027,18 @@
"ToastDeviceTestEmailFailed": "NIeudana próba wysłania testowego maila",
"ToastDeviceTestEmailSuccess": "Testowy email został wysłany",
"ToastEmailSettingsUpdateSuccess": "Ustawienia email zaktualizowane",
+ "ToastEncodeCancelFailed": "Nie udało się anulować kodowania",
+ "ToastEncodeCancelSucces": "Kodowanie anulowane",
+ "ToastEpisodeDownloadQueueClearFailed": "Nie udało się wyczyścić kolejki",
"ToastEpisodeDownloadQueueClearSuccess": "Wyczyszczono kolejkę epizodów do ściągnięcia",
"ToastEpisodeUpdateSuccess": "Zaktualizowano {0} odcinków",
+ "ToastErrorCannotShare": "Nie można udostępniać natywnie na tym urządzeniu.",
"ToastInvalidImageUrl": "Nieprawidłowy URL obrazu",
"ToastInvalidUrl": "Nieprawidłowy URL",
"ToastInvalidUrls": "Jeden lub więcej URL-i są nieprawidłowe",
"ToastItemCoverUpdateSuccess": "Zaktualizowano okładkę",
+ "ToastItemDeletedFailed": "Nie udało się usunąć elementu",
+ "ToastItemDeletedSuccess": "Element usunięty",
"ToastItemDetailsUpdateSuccess": "Zaktualizowano szczegóły",
"ToastItemMarkedAsFinishedFailed": "Nie udało się oznaczyć jako ukończone",
"ToastItemMarkedAsFinishedSuccess": "Pozycja oznaczona jako ukończona",
@@ -1019,12 +1077,41 @@
"ToastRSSFeedCloseSuccess": "Zamknięcie kanału RSS powiodło się",
"ToastRemoveItemFromCollectionFailed": "Nie udało się usunąć elementu z kolekcji",
"ToastRemoveItemFromCollectionSuccess": "Pozycja usunięta z kolekcji",
+ "ToastRemoveItemsWithIssuesFailed": "Nie udało się usunąć wadliwych elementów z biblioteki",
+ "ToastRemoveItemsWithIssuesSuccess": "Usunięto wadliwe elementy z biblioteki",
+ "ToastRenameFailed": "Nie udało się zmienić nazwy",
+ "ToastRescanFailed": "Ponowne skanowanie nie powiodło się dla {0}",
+ "ToastRescanRemoved": "Ponowne skanowanie powiodło się – element został usunięty",
+ "ToastRescanUpToDate": "Ponowne skanowanie powiodło się – element był aktualny",
+ "ToastRescanUpdated": "Ponowne skanowanie powiodło się – element został zaktualizowany",
+ "ToastScanFailed": "Nie powiódł się skan elementu biblioteki",
+ "ToastSelectAtLeastOneUser": "Zaznacz co najmniej jednego użytkownika",
"ToastSendEbookToDeviceFailed": "Failed to Send Ebook to device",
+ "ToastSendEbookToDeviceSuccess": "Ebook wysłany na urządzenie \"{0}\"",
+ "ToastSeriesSubmitFailedSameName": "Nie można dodać dwóch serii pod tą samą nazwą",
+ "ToastSeriesUpdateFailed": "Aktualizacja serii nie powiodła się",
+ "ToastSeriesUpdateSuccess": "Aktualizacja serii powiodła się",
+ "ToastServerSettingsUpdateSuccess": "Zaktualizowano ustawienia serwera",
+ "ToastSessionCloseFailed": "Nie udało się zamknąć sesji",
"ToastSessionDeleteFailed": "Nie udało się usunąć sesji",
"ToastSessionDeleteSuccess": "Sesja usunięta",
+ "ToastSleepTimerDone": "Słodkich snów... zZzzZz",
"ToastSocketConnected": "Nawiązano połączenie z serwerem",
"ToastSocketDisconnected": "Połączenie z serwerem zostało zamknięte",
"ToastSocketFailedToConnect": "Poączenie z serwerem nie powiodło się",
+ "ToastTitleRequired": "Tytuł jest wymagany",
+ "ToastUnknownError": "Nieznany błąd",
+ "ToastUnlinkOpenIdFailed": "Nie udało się odpiąć użytkownika z OpenID",
+ "ToastUnlinkOpenIdSuccess": "Użytkownik odpięty z OpenID",
+ "ToastUploaderFilepathExistsError": "Ścieżka \"{0}\" już istnieje na serwerze",
"ToastUserDeleteFailed": "Nie udało się usunąć użytkownika",
- "ToastUserDeleteSuccess": "Użytkownik usunięty"
+ "ToastUserDeleteSuccess": "Użytkownik usunięty",
+ "TooltipAddChapters": "Dodaj rozdział(y)",
+ "TooltipAddOneSecond": "Dodaj sekundę",
+ "TooltipAdjustChapterStart": "Kliknij, aby skorygować czas początkowy",
+ "TooltipLockAllChapters": "Zablokuj wszystkie rozdziały",
+ "TooltipLockChapter": "Zablokuj rozdział (przytrzymaj Shift i kliknij, aby zaznaczyć zakres)",
+ "TooltipSubtractOneSecond": "Odejmij sekundę",
+ "TooltipUnlockAllChapters": "Odblokuj wszystkie rozdziały",
+ "TooltipUnlockChapter": "Odblokuj rozdział (przytrzymaj Shift i kliknij, aby zaznaczyć zakres)"
}
diff --git a/client/strings/pt-br.json b/client/strings/pt-br.json
index 53b9199e2..d2189698c 100644
--- a/client/strings/pt-br.json
+++ b/client/strings/pt-br.json
@@ -143,8 +143,8 @@
"HeaderEmail": "Email",
"HeaderEmailSettings": "Configurações de Email",
"HeaderEpisodes": "Episódios",
- "HeaderEreaderDevices": "Dispositivos Ereader",
- "HeaderEreaderSettings": "Configurações Ereader",
+ "HeaderEreaderDevices": "Dispositivos leitores de ebook",
+ "HeaderEreaderSettings": "Configurações do leitor de ebook",
"HeaderFiles": "Arquivos",
"HeaderFindChapters": "Localizar Capítulos",
"HeaderIgnoredFiles": "Arquivos Ignorados",
@@ -349,33 +349,46 @@
"LabelEnd": "Fim",
"LabelEndOfChapter": "Fim do Capítulo",
"LabelEpisode": "Episódio",
+ "LabelEpisodeNotLinkedToRssFeed": "Episódio não vinculado ao feed RSS",
+ "LabelEpisodeNumber": "Episódio #{0}",
"LabelEpisodeTitle": "Título do Episódio",
"LabelEpisodeType": "Tipo do Episódio",
+ "LabelEpisodeUrlFromRssFeed": "URL do episódio a partir do feed RSS",
"LabelEpisodes": "Episódios",
+ "LabelEpisodic": "Episódico",
"LabelExample": "Exemplo",
+ "LabelExpandSeries": "Expandir Série",
+ "LabelExpandSubSeries": "Expandir Subséries",
"LabelExpired": "Expirado",
+ "LabelExpiresAt": "Expira às",
+ "LabelExpiresInSeconds": "Expira em (segundos)",
"LabelExpiresNever": "Nunca",
"LabelExplicit": "Explícito",
"LabelExplicitChecked": "Explícito (verificado)",
"LabelExplicitUnchecked": "Não explícito (não verificado)",
+ "LabelExportOPML": "Exportar OPML",
"LabelFeedURL": "URL do Feed",
"LabelFetchingMetadata": "Buscando Metadados",
"LabelFile": "Arquivo",
"LabelFileBirthtime": "Criação do Arquivo",
+ "LabelFileBornDate": "Criado {0}",
"LabelFileModified": "Modificação do Arquivo",
+ "LabelFileModifiedDate": "Modificado {0}",
"LabelFilename": "Nome do Arquivo",
"LabelFilterByUser": "Filtrar por Usuário",
"LabelFindEpisodes": "Localizar Episódios",
"LabelFinished": "Concluído",
+ "LabelFinishedDate": "Concluído {0}",
"LabelFolder": "Pasta",
"LabelFolders": "Pastas",
"LabelFontBold": "Negrito",
"LabelFontBoldness": "Intensidade do negrito",
- "LabelFontFamily": "Família de fonte",
+ "LabelFontFamily": "Família de fontes",
"LabelFontItalic": "Itálico",
"LabelFontScale": "Escala de fonte",
"LabelFontStrikethrough": "Tachado",
"LabelFormat": "Formato",
+ "LabelFull": "Cheio",
"LabelGenre": "Gênero",
"LabelGenres": "Gêneros",
"LabelHardDeleteFile": "Apagar definitivamente",
@@ -403,11 +416,14 @@
"LabelIntervalEveryMinute": "A cada minuto",
"LabelInvert": "Inverter",
"LabelItem": "Item",
+ "LabelJumpBackwardAmount": "Tempo de retrocesso",
+ "LabelJumpForwardAmount": "Tempo de avanço",
"LabelLanguage": "Idioma",
"LabelLanguageDefaultServer": "Idioma Padrão do Servidor",
"LabelLanguages": "Idiomas",
"LabelLastBookAdded": "Último Livro Acrescentado",
"LabelLastBookUpdated": "Último Livro Atualizado",
+ "LabelLastProgressDate": "Último progresso: {0}",
"LabelLastSeen": "Visto pela Última Vez",
"LabelLastTime": "Progresso",
"LabelLastUpdate": "Última Atualização",
@@ -420,8 +436,9 @@
"LabelLibraryFilterSublistEmpty": "Sem {0}",
"LabelLibraryItem": "Item da Biblioteca",
"LabelLibraryName": "Nome da Biblioteca",
- "LabelLibrarySortByProgress": "Última Atualização",
- "LabelLibrarySortByProgressFinished": "Concluído",
+ "LabelLibrarySortByProgress": "Progresso: Ultima Atualização",
+ "LabelLibrarySortByProgressFinished": "Progresso: Terminado",
+ "LabelLibrarySortByProgressStarted": "Progresso: Iniciado",
"LabelLimit": "Limite",
"LabelLineSpacing": "Espaçamento entre linhas",
"LabelListenAgain": "Escutar novamente",
@@ -430,8 +447,13 @@
"LabelLogLevelWarn": "Atenção",
"LabelLookForNewEpisodesAfterDate": "Procurar por novos Episódios após essa data",
"LabelLowestPriority": "Prioridade mais baixa",
+ "LabelMatchConfidence": "Confiabilidade",
"LabelMatchExistingUsersBy": "Consultar usuários existentes usando",
"LabelMatchExistingUsersByDescription": "Utilizado para conectar usuários já existentes. Uma vez conectados, usuários serão consultados utilizando uma identificação única do seu provedor de SSO",
+ "LabelMaxEpisodesToDownload": "Número máximo de episódios para baixar. Use 0 para download ilimitado.",
+ "LabelMaxEpisodesToDownloadPerCheck": "Número máximo de novos episódios para baixar por verificação",
+ "LabelMaxEpisodesToKeep": "Número máximo de episódios para manter",
+ "LabelMaxEpisodesToKeepHelp": "O valor 0 define que não há limite máximo. Após um novo episódio ser baixado automaticamente, o episódio mais antigo será excluído caso você tenha mais de X episódios. Apenas um episódio será excluído a cada novo download.",
"LabelMediaPlayer": "Reprodutor de mídia",
"LabelMediaType": "Tipo de Mídia",
"LabelMetaTag": "Etiqueta Meta",
@@ -457,6 +479,7 @@
"LabelNextBackupDate": "Data do próximo backup",
"LabelNextChapters": "Próximo capítulo será:",
"LabelNextScheduledRun": "Próxima execução programada",
+ "LabelNoApiKeys": "Sem chaves de API",
"LabelNoCustomMetadataProviders": "Não existem fontes de metadados customizados",
"LabelNoEpisodesSelected": "Nenhum episódio selecionado",
"LabelNotFinished": "Não concluído",
@@ -472,6 +495,7 @@
"LabelNotificationsMaxQueueSize": "Tamanho máximo da fila de eventos de notificação",
"LabelNotificationsMaxQueueSizeHelp": "Eventos estão limitados a um disparo por segundo. Eventos serão ignorados se a fila estiver no tamanho máximo. Isso evita o excesso de notificações.",
"LabelNumberOfBooks": "Número de Livros",
+ "LabelNumberOfChapters": "Número de capítulos:",
"LabelNumberOfEpisodes": "# de Episódios",
"LabelOpenIDAdvancedPermsClaimDescription": "Nome do claim OpenID contendo as permissões avançadas para ações do usuário na aplicação para serem aplicadas aos perfis não-administradores (se configurados). Se o claim não estiver presente na resposta, acesso ao ABS será negado. Se apenas uma opção estiver ausente, ela será tratada como false. Garanta que o claim do provedor de identidade segue a estrutura esperada:",
"LabelOpenIDClaims": "Deixe as opções a seguir em branco para desativar a atribuição de grupos e permissões avançadas; nesse caso, o grupo 'Usuário' será atribuído automaticamente.",
@@ -485,6 +509,7 @@
"LabelPermissionsAccessAllLibraries": "Pode Acessar Todas Bibliotecas",
"LabelPermissionsAccessAllTags": "Pode Acessar Todas as Etiquetas",
"LabelPermissionsAccessExplicitContent": "Pode Acessar Conteúdos Explícitos",
+ "LabelPermissionsCreateEreader": "Pode criar leitor de ebooks",
"LabelPermissionsDelete": "Pode Apagar",
"LabelPermissionsDownload": "Pode Fazer Download",
"LabelPermissionsUpdate": "Pode Atualizar",
@@ -492,6 +517,7 @@
"LabelPersonalYearReview": "Sua Retrospectiva Anual ({0})",
"LabelPhotoPathURL": "Caminho/URL para Foto",
"LabelPlayMethod": "Método de Reprodução",
+ "LabelPlaybackRateIncrementDecrement": "Valor de incremento/decremento da taxa de reprodução",
"LabelPlayerChapterNumberMarker": "{0} de {1}",
"LabelPlaylists": "Listas de Reprodução",
"LabelPodcast": "Podcast",
@@ -504,9 +530,12 @@
"LabelPrimaryEbook": "Ebook principal",
"LabelProgress": "Progresso",
"LabelProvider": "Fonte",
+ "LabelProviderAuthorizationValue": "Valor do Cabeçalho de Autorização",
"LabelPubDate": "Data de Publicação",
"LabelPublishYear": "Ano de Publicação",
"LabelPublishedDate": "Publicado {0}",
+ "LabelPublishedDecade": "Década de Publicação",
+ "LabelPublishedDecades": "Décadas de Publicação",
"LabelPublisher": "Editora",
"LabelPublishers": "Editoras",
"LabelRSSFeedCustomOwnerEmail": "E-mail do dono personalizado",
@@ -516,6 +545,7 @@
"LabelRSSFeedSlug": "Slug do Feed RSS",
"LabelRSSFeedURL": "URL do Feed RSS",
"LabelRandomly": "Aleatoriamente",
+ "LabelReAddSeriesToContinueListening": "Adicionar novamente a série à \"Continuar ouvindo\"",
"LabelRead": "Lido",
"LabelReadAgain": "Ler novamente",
"LabelReadEbookWithoutProgress": "Ler ebook sem armazenar progresso",
@@ -525,24 +555,34 @@
"LabelRedo": "Refazer",
"LabelRegion": "Região",
"LabelReleaseDate": "Data de Lançamento",
+ "LabelRemoveAllMetadataAbs": "Remover todos os arquivos metadata.abs",
+ "LabelRemoveAllMetadataJson": "Remover todos os arquivos metadata.json",
+ "LabelRemoveAudibleBranding": "Remover introdução e encerramento da Audible dos capítulos",
"LabelRemoveCover": "Remover capa",
+ "LabelRemoveMetadataFile": "Remover arquivos de metadados nas pastas da biblioteca",
+ "LabelRemoveMetadataFileHelp": "Remover todos os arquivos metadata.json e metadata.abs em suas {0} pastas.",
"LabelRowsPerPage": "Linhas por Página",
"LabelSearchTerm": "Busca por Termo",
"LabelSearchTitle": "Busca por Título",
"LabelSearchTitleOrASIN": "Busca por Título ou ASIN",
"LabelSeason": "Temporada",
+ "LabelSeasonNumber": "Temporada #{0}",
"LabelSelectAll": "Selecionar todos",
"LabelSelectAllEpisodes": "Selecionar todos os Episódios",
"LabelSelectEpisodesShowing": "Selecionar os {0} Episódios Visíveis",
+ "LabelSelectUser": "Selecionar usuário",
"LabelSelectUsers": "Selecionar usuários",
"LabelSendEbookToDevice": "Enviar Ebook para...",
"LabelSequence": "Sequência",
+ "LabelSerial": "Serial",
"LabelSeries": "Série",
"LabelSeriesName": "Nome da Série",
"LabelSeriesProgress": "Progresso da Série",
+ "LabelServerLogLevel": "Nível de Logs do Servidor",
"LabelServerYearReview": "Retrospectiva Anual do Servidor ({0})",
"LabelSetEbookAsPrimary": "Definir como principal",
"LabelSetEbookAsSupplementary": "Definir como complementar",
+ "LabelSettingsAllowIframe": "Permitir incorporação em iframe",
"LabelSettingsAudiobooksOnly": "Apenas Audiobooks",
"LabelSettingsAudiobooksOnlyHelp": "Ao ativar essa configuração os arquivos de ebooks serão ignorados a não ser que estejam dentro de uma pasta com um audiobook. Nesse caso eles serão definidos como ebooks complementares",
"LabelSettingsBookshelfViewHelp": "Aparência esqueomorfa com prateleiras de madeira",
@@ -561,6 +601,9 @@
"LabelSettingsHideSingleBookSeriesHelp": "Séries com um só livro serão ocultadas na página de séries e na prateleira de séries na página principal.",
"LabelSettingsHomePageBookshelfView": "Usar visão estante na página principal",
"LabelSettingsLibraryBookshelfView": "Usar visão estante na página da biblioteca",
+ "LabelSettingsLibraryMarkAsFinishedPercentComplete": "O percentual de conclusão é maior que",
+ "LabelSettingsLibraryMarkAsFinishedTimeRemaining": "O tempo restante é inferior a (segundos)",
+ "LabelSettingsLibraryMarkAsFinishedWhen": "Marcar o item de mídia como concluído quando",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Pular livros anteriores em Continuar Série",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "A prateleira Continuar Série na página principal de exibe o primeiro livro não iniciado em uma série que tem pelo menos um livro concluído e nenhum livro em andamento. Ativar essa configuração irá continuar a série a partir do livro mais recentemente concluído ao invés do primeiro livro não iniciado.",
"LabelSettingsParseSubtitles": "Analisar subtítulos",
@@ -579,6 +622,8 @@
"LabelSettingsStoreMetadataWithItemHelp": "Por padrão os arquivos de metadados são armazenados em /metadata/items. Ao ativar essa configuração os arquivos de metadados serão armazenadas nas pastas dos itens na sua biblioteca",
"LabelSettingsTimeFormat": "Formato da Tempo",
"LabelShare": "Compartilhar",
+ "LabelShareDownloadableHelp": "Permitir que os usuários com o link de compartilhamento baixem um arquivo zip do item da biblioteca.",
+ "LabelShareOpen": "Abrir compartilhamento",
"LabelShareURL": "Compartilhar URL",
"LabelShowAll": "Exibir Todos",
"LabelShowSeconds": "Exibir segundos",
@@ -586,10 +631,14 @@
"LabelSize": "Tamanho",
"LabelSleepTimer": "Timer",
"LabelSlug": "Slug",
+ "LabelSortAscending": "Crescente",
+ "LabelSortDescending": "Decrescente",
+ "LabelSortPubDate": "Ordenar por data de publicação",
"LabelStart": "Iniciar",
"LabelStartTime": "Horário do Início",
"LabelStarted": "Iniciado",
"LabelStartedAt": "Iniciado Em",
+ "LabelStartedDate": "Iniciado {0}",
"LabelStatsAudioTracks": "Trilhas de Áudio",
"LabelStatsAuthors": "Autores",
"LabelStatsBestDay": "Melhor Dia",
@@ -624,6 +673,8 @@
"LabelTimeDurationXHours": "{0} horas",
"LabelTimeDurationXMinutes": "{0} minutos",
"LabelTimeDurationXSeconds": "{0} segundos",
+ "LabelTimeInMinutes": "Tempo em minutos",
+ "LabelTimeLeft": "{0} restante",
"LabelTimeListened": "Tempo de escuta",
"LabelTimeListenedToday": "Tempo de escuta hoje",
"LabelTimeRemaining": "{0} restantes",
@@ -631,6 +682,7 @@
"LabelTitle": "Título",
"LabelToolsEmbedMetadata": "Incluir Metadados",
"LabelToolsEmbedMetadataDescription": "Incluir metadados no arquivo de áudio, com imagem da capa e capítulos.",
+ "LabelToolsM4bEncoder": "Codificador M4B",
"LabelToolsMakeM4b": "Gerar audiobook no formato M4B",
"LabelToolsMakeM4bDescription": "Gerar um arquivo de audiobook no formato .M4B com metadados, imagem da capa e capítulos.",
"LabelToolsSplitM4b": "Dividir um M4B em MP3s",
@@ -648,24 +700,31 @@
"LabelUnabridged": "Não Abreviada",
"LabelUndo": "Desfazer",
"LabelUnknown": "Desconhecido",
+ "LabelUnknownPublishDate": "Data de publicação desconhecida",
"LabelUpdateCover": "Atualizar Capa",
"LabelUpdateCoverHelp": "Permite sobrescrever capas existentes para os livros selecionados quando uma consulta for localizada",
"LabelUpdateDetails": "Atualizar Detalhes",
"LabelUpdateDetailsHelp": "Permite sobrescrever detalhes existentes para os livros selecionados quando uma consulta for localizada",
"LabelUpdatedAt": "Atualizado em",
"LabelUploaderDragAndDrop": "Arraste e solte arquivos ou pastas",
+ "LabelUploaderDragAndDropFilesOnly": "Arraste e solte arquivos",
"LabelUploaderDropFiles": "Solte os arquivos",
"LabelUploaderItemFetchMetadataHelp": "Busca título, autor e série automaticamente",
+ "LabelUseAdvancedOptions": "Usar Opções Avançadas",
"LabelUseChapterTrack": "Usar a trilha do capítulo",
"LabelUseFullTrack": "Usar a trilha toda",
+ "LabelUseZeroForUnlimited": "Use 0 para ilimitado",
"LabelUser": "Usuário",
"LabelUsername": "Nome do usuário",
"LabelValue": "Valor",
"LabelVersion": "Versão",
"LabelViewBookmarks": "Ver marcadores",
"LabelViewChapters": "Ver capítulos",
+ "LabelViewPlayerSettings": "Ver configurações do reprodutor",
"LabelViewQueue": "Ver fila do reprodutor",
"LabelVolume": "Volume",
+ "LabelWebRedirectURLsDescription": "Autorize esses URLs em seu provedor OAuth para permitir o redirecionamento de volta ao aplicativo web após o login:",
+ "LabelWebRedirectURLsSubfolder": "Subpasta de URLs de redirecionamento",
"LabelWeekdaysToRun": "Dias da semana para executar",
"LabelXBooks": "{0} livros",
"LabelXItems": "{0} itens",
@@ -677,41 +736,64 @@
"LabelYourProgress": "Seu Progresso",
"MessageAddToPlayerQueue": "Adicionar à lista do reprodutor",
"MessageAppriseDescription": "Para utilizar essa funcionalidade é preciso ter uma instância da API do Apprise em execução ou uma API que possa tratar esses mesmos chamados.
A URL da API do Apprise deve conter o caminho completo da URL para enviar as notificações. Ex: se a sua instância da API estiver em http://192.168.1.1:8337 você deve utilizar http://192.168.1.1:8337/notify.",
+ "MessageAsinCheck": "Certifique-se de estar usando o ASIN da região correta da Audible, e não da Amazon.",
+ "MessageAuthenticationLegacyTokenWarning": "Os tokens de API legados serão removidos no futuro. Use Chaves de API em vez disso.",
+ "MessageAuthenticationOIDCChangesRestart": "Reinicie o servidor após salvar para aplicar as alterações do OIDC.",
+ "MessageAuthenticationSecurityMessage": "A autenticação foi aprimorada para maior segurança. Todos os usuários precisam fazer login novamente.",
"MessageBackupsDescription": "Backups incluem usuários, progresso dos usuários, detalhes dos itens da biblioteca, configurações do servidor e imagens armazenadas em /metadata/items & /metadata/authors. Backups não incluem quaisquer arquivos armazenados nas pastas da sua biblioteca.",
+ "MessageBackupsLocationEditNote": "Observação: Atualizar o local de backup não moverá nem modificará os backups existentes",
+ "MessageBackupsLocationNoEditNote": "Observação: o local de backup é definido por meio de uma variável de ambiente e não pode ser alterado aqui.",
+ "MessageBackupsLocationPathEmpty": "O caminho do local de backup não pode ser vazio",
+ "MessageBatchEditPopulateMapDetailsAllHelp": "Preencha os campos habilitados com dados de todos os itens. Os campos com múltiplos valores serão mesclados",
+ "MessageBatchEditPopulateMapDetailsItemHelp": "Preencha os campos de detalhes do mapa habilitados com dados deste item",
"MessageBatchQuickMatchDescription": "Consulta Rápida tentará adicionar capas e metadados ausentes para os itens selecionados. Ative as opções abaixo para permitir que a Consulta Rápida sobrescreva capas e/ou metadados existentes.",
"MessageBookshelfNoCollections": "Você ainda não criou coleções",
+ "MessageBookshelfNoCollectionsHelp": "As coleções são públicas. Todos os usuários com acesso à biblioteca podem vê-las.",
"MessageBookshelfNoRSSFeeds": "Não existem feeds RSS abertos",
"MessageBookshelfNoResultsForFilter": "Sem Resultados para o filtro \"{0}: {1}\"",
"MessageBookshelfNoResultsForQuery": "Sem resultados para a consulta",
"MessageBookshelfNoSeries": "Você não tem séries",
+ "MessageBulkChapterPattern": "Quantos capítulos você gostaria de adicionar seguindo esse padrão de numeração?",
"MessageChapterEndIsAfter": "O final do capítulo está além do final do seu audiobook",
"MessageChapterErrorFirstNotZero": "O primeiro capítulo precisa começar no 0",
"MessageChapterErrorStartGteDuration": "Tempo de início não é válido pois precisa ser menor do que a duração do audioboook",
"MessageChapterErrorStartLtPrev": "Tempo de início não é válido pois precisa ser igual ou maior que o tempo de início do capítulo anterior",
"MessageChapterStartIsAfter": "Início do capítulo está além do final do seu audiobook",
+ "MessageChaptersNotFound": "Capítulos não encontrados",
"MessageCheckingCron": "Verificando o cron...",
"MessageConfirmCloseFeed": "Tem certeza de que deseja fechar esse feed?",
+ "MessageConfirmDeleteApiKey": "Tem certeza de que deseja excluir a chave de API \"{0}\"?",
"MessageConfirmDeleteBackup": "Tem certeza de que deseja apagar o backup {0}?",
+ "MessageConfirmDeleteDevice": "Tem certeza de que deseja excluir o leitor de ebook \"{0}\"?",
"MessageConfirmDeleteFile": "Essa ação apagará o arquivo do seu sistema de arquivos. Tem certeza?",
"MessageConfirmDeleteLibrary": "Tem certeza de que deseja apagar a biblioteca \"{0}\" definitivamente?",
"MessageConfirmDeleteLibraryItem": "Essa ação apagará o item da biblioteca do banco de dados e do seu sistema de arquivos. Tem certeza?",
"MessageConfirmDeleteLibraryItems": "Essa ação apagará {0} itens da biblioteca do banco de dados e do seu sistema de arquivos. Tem certeza?",
+ "MessageConfirmDeleteMetadataProvider": "Tem certeza de que deseja excluir o provedor de metadados personalizado \"{0}\"?",
+ "MessageConfirmDeleteNotification": "Tem certeza de que deseja excluir esta notificação?",
"MessageConfirmDeleteSession": "Tem certeza de que deseja apagar essa sessão?",
+ "MessageConfirmEmbedMetadataInAudioFiles": "Tem certeza de que deseja incorporar metadados em arquivos de áudio {0}?",
"MessageConfirmForceReScan": "Tem certeza de que deseja forçar a nova verificação?",
"MessageConfirmMarkAllEpisodesFinished": "Tem certeza de que deseja marcar todos os episódios como concluídos?",
"MessageConfirmMarkAllEpisodesNotFinished": "Tem certeza de que deseja marcar todos os episódios como não concluídos?",
+ "MessageConfirmMarkItemFinished": "Tem certeza de que deseja marcar \"{0}\" como concluído?",
+ "MessageConfirmMarkItemNotFinished": "Tem certeza de que deseja marcar \"{0}\" como não concluído?",
"MessageConfirmMarkSeriesFinished": "Tem certeza de que deseja marcar todos os livros nesta série como concluídos?",
"MessageConfirmMarkSeriesNotFinished": "Tem certeza de que deseja marcar todos os livros nesta série como não concluídos?",
+ "MessageConfirmNotificationTestTrigger": "Acionar esta notificação com dados de teste?",
"MessageConfirmPurgeCache": "Apagar o cache irá apagar o diretório todo localizado em /metadata/cache.
Tem certeza que deseja apagar o diretório de cache?",
"MessageConfirmPurgeItemsCache": "Apagar o cache de itens irá apagar todo conteúdo da pasta /metadata/cache/items.
Tem certeza?",
"MessageConfirmQuickEmbed": "Aviso! Inclusão rápida não fará backup dos seus arquivos de áudio. Verifique se tem um backup dos seus arquivos de áudio.
Quer continuar?",
+ "MessageConfirmQuickMatchEpisodes": "A correspondência rápida de episódios sobrescreverá os detalhes se uma correspondência for encontrada. Somente os episódios sem correspondência serão atualizados. Tem certeza?",
"MessageConfirmReScanLibraryItems": "Tem certeza de que deseja uma nova verificação de {0} itens?",
"MessageConfirmRemoveAllChapters": "Tem certeza de que deseja remover todos os capítulos?",
"MessageConfirmRemoveAuthor": "Tem certeza de que deseja remover o autor \"{0}\"?",
"MessageConfirmRemoveCollection": "Tem certeza de que deseja remover a coleção \"{0}\"?",
"MessageConfirmRemoveEpisode": "Tem certeza de que deseja remover o episódio \"{0}\"?",
+ "MessageConfirmRemoveEpisodeNote": "Observação: Isso não exclui o arquivo de áudio, a menos que você ative a opção \"Excluir arquivo permanentemente\"",
"MessageConfirmRemoveEpisodes": "Tem certeza de que deseja remover os {0} episódios?",
"MessageConfirmRemoveListeningSessions": "Tem certeza de que deseja remover as {0} sessões de escuta?",
+ "MessageConfirmRemoveMetadataFiles": "Tem certeza de que deseja remover todos os arquivos metadata.{0} nas pastas dos itens da sua biblioteca?",
"MessageConfirmRemoveNarrator": "Tem certeza de que deseja remover o narrador \"{0}\"?",
"MessageConfirmRemovePlaylist": "Tem certeza de que deseja remover a sua lista de reprodução \"{0}\"?",
"MessageConfirmRenameGenre": "Tem certeza de que deseja renomear o gênero \"{0}\" para \"{1}\" em todos os itens?",
@@ -720,17 +802,25 @@
"MessageConfirmRenameTag": "Tem certeza de que deseja renomear a etiqueta \"{0}\" para \"{1}\" em todos os itens?",
"MessageConfirmRenameTagMergeNote": "Aviso: Esta etiqueta já existe então elas serão combinadas.",
"MessageConfirmRenameTagWarning": "Atenção! Uma etiqueta com um nome semelhante já existe \"{0}\".",
+ "MessageConfirmResetProgress": "Tem certeza de que deseja redefinir seu progresso?",
"MessageConfirmSendEbookToDevice": "Tem certeza de que deseja enviar {0} ebook(s) \"{1}\" para o dispositivo \"{2}\"?",
+ "MessageConfirmUnlinkOpenId": "Tem certeza de que deseja desvincular este usuário do OpenID?",
+ "MessageDaysListenedInTheLastYear": "{0} dias ouvidos no último ano",
"MessageDownloadingEpisode": "Realizando o download do episódio",
"MessageDragFilesIntoTrackOrder": "Arraste os arquivos para ordenar as trilhas corretamente",
+ "MessageEmbedFailed": "Falha ao incorporar!",
"MessageEmbedFinished": "Inclusão Concluída!",
+ "MessageEmbedQueue": "Enfileirado para incorporação de metadados ({0} na fila)",
"MessageEpisodesQueuedForDownload": "{0} Episódio(s) na fila de download",
"MessageEreaderDevices": "Para garantir a entrega dos ebooks, você pode precisar adicionar o endereço de email acima como um remetente válido para cada um dos dispositivos listados abaixo.",
"MessageFeedURLWillBe": "URL do Feed será {0}",
"MessageFetching": "Buscando...",
"MessageForceReScanDescription": "verificará todos os arquivos, como uma verificação nova. Etiquetas ID3 de arquivos de áudio, arquivos OPF e arquivos de texto serão tratados como novos.",
+ "MessageHeatmapListeningTimeTooltip": "{0} ouvindo em {1}",
+ "MessageHeatmapNoListeningSessions": "Nenhuma sessão de reprodução em {0}",
"MessageImportantNotice": "Aviso Importante!",
"MessageInsertChapterBelow": "Inserir capítulo abaixo",
+ "MessageInvalidAsin": "ASIN inválido",
"MessageItemsSelected": "{0} itens selecionados",
"MessageItemsUpdated": "{0} itens atualizados",
"MessageJoinUsOn": "Junte-se a nós",
@@ -753,6 +843,7 @@
"MessageNoCollections": "Sem Coleções",
"MessageNoCoversFound": "Nenhuma Capa Encontrada",
"MessageNoDescription": "Sem Descrições",
+ "MessageNoDevices": "Nenhum dispositivo",
"MessageNoDownloadsInProgress": "Não existem downloads em andamento",
"MessageNoDownloadsQueued": "Não existem itens na fila de download",
"MessageNoEpisodeMatchesFound": "Não existem episódios correspondentes",
@@ -766,6 +857,7 @@
"MessageNoLogs": "Sem Logs",
"MessageNoMediaProgress": "Sem Progresso de Mídia",
"MessageNoNotifications": "Sem Notificações",
+ "MessageNoPodcastFeed": "Podcast inválido: Sem feed",
"MessageNoPodcastsFound": "Nenhum podcast encontrado",
"MessageNoResults": "Sem resultados",
"MessageNoSearchResultsFor": "Sem resultados para \"{0}\"",
@@ -774,13 +866,19 @@
"MessageNoTasksRunning": "Sem Tarefas em Execução",
"MessageNoUpdatesWereNecessary": "Nenhuma atualização é necessária",
"MessageNoUserPlaylists": "Você não tem listas de reprodução",
+ "MessageNoUserPlaylistsHelp": "As listas de reprodução são privadas. Somente o usuário que as criou pode vê-las.",
"MessageNotYetImplemented": "Ainda não implementado",
+ "MessageOpmlPreviewNote": "Nota: esta é uma visualização do arquivo OPML processado. O título verdadeiro do podcast será recuperado do feed RSS.",
"MessageOr": "ou",
"MessagePauseChapter": "Pausar reprodução do capítulo",
"MessagePlayChapter": "Escutar o início do capítulo",
"MessagePlaylistCreateFromCollection": "Criar uma lista de reprodução a partir da coleção",
+ "MessagePleaseWait": "Por favor, aguarde...",
"MessagePodcastHasNoRSSFeedForMatching": "Podcast não tem uma URL do feed RSS para ser usada na consulta",
"MessagePodcastSearchField": "Digite um termo para a busca ou a URL de um feed RSS",
+ "MessageQuickEmbedInProgress": "Incorporação rápida em andamento",
+ "MessageQuickEmbedQueue": "Enfileirado para incorporação rápida ({0} na fila)",
+ "MessageQuickMatchAllEpisodes": "Correspondência Rápida de Todos os Episódios",
"MessageQuickMatchDescription": "Preenche detalhes vazios do item & capa com o primeiro resultado de '{0}'. Não sobrescreve detalhes a não ser que a configuração 'Preferir metadados consultados' do servidor esteja ativa.",
"MessageRemoveChapter": "Remover capítulo",
"MessageRemoveEpisodes": "Remover {0} episódio(s)",
@@ -790,12 +888,52 @@
"MessageResetChaptersConfirm": "Tem certeza de que deseja resetar os capítulos e desfazer as alterações realizadas?",
"MessageRestoreBackupConfirm": "Tem certeza de que deseja restaurar o backup criado em",
"MessageRestoreBackupWarning": "Restaurar um backup sobrescreverá totalmente o banco de dados localizado em /config e as imagens de capa em /metadata/items & /metadata/authors.
Backups não alteram quaisquer arquivos nas pastas da sua biblioteca. Se a configuração do servidor de armazenar a arte da capa e os metadados nas pastas da sua biblioteca estiver ativa, esses itens não estão no backup e não serão sobrescritos.
Todos os clientes usando o seu servidor serão atualizados automaticamente.",
+ "MessageScheduleLibraryScanNote": "Para a maioria dos usuários, é recomendável deixar este recurso desativado e manter a configuração \"Monitorar automaticamente alterações na biblioteca\" ativada. Dessa forma alterações nas pastas da sua biblioteca serão automáticamente detectadas. Habilite essa opção se \"Monitorar automaticamente alterações na biblioteca\" não fucnionara para o seu sistema de arquivos (Como NFS).",
+ "MessageScheduleRunEveryWeekdayAtTime": "Executar a cada {0} às {1}",
"MessageSearchResultsFor": "Resultado da busca por",
"MessageSelected": "{0} selecionado(s)",
+ "MessageSeriesSequenceCannotContainSpaces": "O identificador da série não pode conter espaços",
"MessageServerCouldNotBeReached": "Não foi possível estabelecer conexão com o servidor",
"MessageSetChaptersFromTracksDescription": "Definir os capítulos usando cada arquivo de áudio como um capítulo e o nome do arquivo como o título do capítulo",
+ "MessageShareExpirationWillBe": "Válido até {0}",
+ "MessageShareExpiresIn": "Expira em {0}",
+ "MessageShareURLWillBe": "A URL de compartilhamento será {0}",
"MessageStartPlaybackAtTime": "Iniciar a reprodução de \"{0}\" em {1}?",
+ "MessageTaskAudioFileNotWritable": "O arquivo de áudio \"{0}\" não pode ser gravado",
+ "MessageTaskCanceledByUser": "Tarefa cancelada pelo usuário",
+ "MessageTaskDownloadingEpisodeDescription": "Baixando episódio \"{0}\"",
+ "MessageTaskEmbeddingMetadata": "Incorporação de metadados",
+ "MessageTaskEmbeddingMetadataDescription": "Incorporando metadados no audiolivro \"{0}\"",
+ "MessageTaskEncodingM4b": "Codificação M4B",
+ "MessageTaskEncodingM4bDescription": "Codificação do audiolivro \"{0}\" em um único arquivo m4b",
"MessageTaskFailed": "Falhou",
+ "MessageTaskFailedToBackupAudioFile": "Falha ao fazer backup do arquivo de áudio \"{0}\"",
+ "MessageTaskFailedToCreateCacheDirectory": "Falha ao criar o diretório de cache",
+ "MessageTaskFailedToEmbedMetadataInFile": "Falha ao incorporar metadados no arquivo \"{0}\"",
+ "MessageTaskFailedToMergeAudioFiles": "Falha ao mesclar arquivos de áudio",
+ "MessageTaskFailedToMoveM4bFile": "Falha ao mover o arquivo m4b",
+ "MessageTaskFailedToWriteMetadataFile": "Falha ao gravar o arquivo de metadados",
+ "MessageTaskMatchingBooksInLibrary": "Livros correspondentes na biblioteca \"{0}\"",
+ "MessageTaskNoFilesToScan": "Não há arquivos para verificar",
+ "MessageTaskOpmlImport": "Importação OPML",
+ "MessageTaskOpmlImportDescription": "Criando podcasts a partir de feeds RSS {0}",
+ "MessageTaskOpmlImportFeed": "Feed de importação OPML",
+ "MessageTaskOpmlImportFeedDescription": "Importando o feed RSS \"{0}\"",
+ "MessageTaskOpmlImportFeedFailed": "Não foi possível obter o feed do podcast",
+ "MessageTaskOpmlImportFeedPodcastDescription": "Criando podcast \"{0}\"",
+ "MessageTaskOpmlImportFeedPodcastExists": "O podcast já existe no caminho",
+ "MessageTaskOpmlImportFeedPodcastFailed": "Falha ao criar o podcast",
+ "MessageTaskOpmlImportFinished": "Adicionados {0} podcasts",
+ "MessageTaskOpmlParseFailed": "Falha ao analisar o arquivo OPML",
+ "MessageTaskOpmlParseFastFail": "Arquivo OPML inválido. Tag não encontrada OU tag não encontrada",
+ "MessageTaskOpmlParseNoneFound": "Nenhum feed encontrado no arquivo OPML",
+ "MessageTaskScanItemsAdded": "{0} adicionado",
+ "MessageTaskScanItemsMissing": "{0} não encontrado",
+ "MessageTaskScanItemsUpdated": "{0} atualizado",
+ "MessageTaskScanNoChangesNeeded": "Nenhuma alteração necessária",
+ "MessageTaskScanningFileChanges": "Verificando alterações de arquivo em \"{0}\"",
+ "MessageTaskScanningLibrary": "Analisando a biblioteca \"{0}\"",
+ "MessageTaskTargetDirectoryNotWritable": "Diretório de destino sem permissão de escrita",
"MessageThinking": "Pensando...",
"MessageUploaderItemFailed": "Falha no upload",
"MessageUploaderItemSuccess": "Upload realizado!",
@@ -813,6 +951,12 @@
"NoteUploaderFoldersWithMediaFiles": "Pastas com arquivos de mídia serão tratadas como itens de biblioteca distintos.",
"NoteUploaderOnlyAudioFiles": "Ao subir apenas arquivos de áudio, cada arquivo será tratado como um audiobook distinto.",
"NoteUploaderUnsupportedFiles": "Arquivos não suportados serão ignorados. Ao escolher ou arrastar uma pasta, outros arquivos que não estão em uma pasta dentro do item serão ignorados.",
+ "NotificationOnBackupCompletedDescription": "Acionado quando um backup é concluído",
+ "NotificationOnBackupFailedDescription": "Acionado quando um backup falha",
+ "NotificationOnEpisodeDownloadedDescription": "Acionado quando um episódio de podcast é baixado automaticamente",
+ "NotificationOnRSSFeedDisabledDescription": "Acionado quando os downloads automáticos de episódios são desativados devido a muitas tentativas falhas",
+ "NotificationOnRSSFeedFailedDescription": "Acionado quando a solicitação do feed RSS falha durante o download automático de um episódio",
+ "NotificationOnTestDescription": "Evento para testar o sistema de notificação",
"PlaceholderBulkChapterInput": "Digite o título de um capítulo ou use uma numeração (por exemplo, 'Episódio 1', 'Capítulo 10', '1.')",
"PlaceholderNewCollection": "Novo nome da coleção",
"PlaceholderNewFolderPath": "Novo caminho para a pasta",
@@ -821,22 +965,47 @@
"PlaceholderSearchEpisode": "Buscar Episódio..",
"StatsAuthorsAdded": "autores adicionados",
"StatsBooksAdded": "livros adicionados",
+ "StatsBooksAdditional": "Algumas adições incluem…",
"StatsBooksFinished": "livros concluídos",
+ "StatsBooksFinishedThisYear": "Alguns livros foram finalizados este ano…",
+ "StatsBooksListenedTo": "livros ouvidos",
+ "StatsCollectionGrewTo": "Sua coleção de livros cresceu para…",
+ "StatsSessions": "sessões",
+ "StatsSpentListening": "tempo gasto ouvindo",
"StatsTopAuthor": "TOP AUTOR",
"StatsTopAuthors": "TOP AUTORES",
+ "StatsTopGenre": "PRINCIPAL GÊNERO",
+ "StatsTopGenres": "PRINCIPAL GÊNEROS",
+ "StatsTopMonth": "PRINCIPAL MÊS",
+ "StatsTopNarrator": "PRINCIPAL NARRADOR",
+ "StatsTopNarrators": "PRINCIPAIS NARRADORES",
+ "StatsTotalDuration": "Com duração total de…",
+ "StatsYearInReview": "RESUMO DO ANO",
"ToastAccountUpdateSuccess": "Conta atualizada",
"ToastAppriseUrlRequired": "É preciso digitar uma URL Apprise",
+ "ToastAsinRequired": "É necessário o ASIN",
"ToastAuthorImageRemoveSuccess": "Imagem do autor removida",
+ "ToastAuthorNotFound": "Autor \"{0}\" não encontrado",
+ "ToastAuthorRemoveSuccess": "Autor removido",
+ "ToastAuthorSearchNotFound": "Autor não encontrado",
"ToastAuthorUpdateMerged": "Autor combinado",
"ToastAuthorUpdateSuccess": "Autor atualizado",
"ToastAuthorUpdateSuccessNoImageFound": "Autor atualizado (nenhuma imagem encontrada)",
+ "ToastBackupAppliedSuccess": "Backup aplicado",
"ToastBackupCreateFailed": "Falha ao criar backup",
"ToastBackupCreateSuccess": "Backup criado",
"ToastBackupDeleteFailed": "Falha ao apagar backup",
"ToastBackupDeleteSuccess": "Backup apagado",
+ "ToastBackupInvalidMaxKeep": "Número inválido de backups a serem mantidos",
+ "ToastBackupInvalidMaxSize": "Tamanho máximo de backup inválido",
"ToastBackupRestoreFailed": "Falha ao restaurar backup",
"ToastBackupUploadFailed": "Falha no upload do backup",
"ToastBackupUploadSuccess": "Upload do backup realizado",
+ "ToastBatchApplyDetailsToItemsSuccess": "Detalhes aplicados aos itens",
+ "ToastBatchDeleteFailed": "A exclusão em lote falhou",
+ "ToastBatchDeleteSuccess": "Exclusão em lote concluída com sucesso",
+ "ToastBatchQuickMatchFailed": "A correspondência rápida em lote falhou!",
+ "ToastBatchQuickMatchStarted": "Correspondência rápida em lote de {0} livros iniciada!",
"ToastBatchUpdateFailed": "Falha na atualização em lote",
"ToastBatchUpdateSuccess": "Atualização em lote realizada",
"ToastBookmarkCreateFailed": "Falha ao criar marcador",
@@ -845,19 +1014,54 @@
"ToastBulkChapterInvalidCount": "Digite um número entre 1 e 150",
"ToastCachePurgeFailed": "Falha ao apagar o cache",
"ToastCachePurgeSuccess": "Cache apagado com sucesso",
+ "ToastChapterLocked": "Capítulo bloqueado.",
+ "ToastChapterStartTimeAdjusted": "O horário de início do capítulo foi ajustado em {0} segundos",
+ "ToastChaptersAllLocked": "Todos os capítulos estão bloqueados. Desbloqueie alguns capítulos para alterar seus horários.",
"ToastChaptersHaveErrors": "Capítulos com erro",
+ "ToastChaptersInvalidShiftAmountLast": "Quantidade de deslocamento inválida. O horário de início do último capítulo ultrapassaria a duração deste audiolivro.",
+ "ToastChaptersInvalidShiftAmountStart": "Valor de deslocamento inválido. O primeiro capítulo teria duração zero ou negativa e seria sobrescrito pelo segundo capítulo. Aumente a duração inicial do segundo capítulo.",
"ToastChaptersMustHaveTitles": "Capítulos precisam ter títulos",
+ "ToastChaptersRemoved": "Capítulos removidos",
+ "ToastChaptersUpdated": "Capítulos atualizados",
+ "ToastCollectionItemsAddFailed": "Falha ao adicionar itens à coleção",
"ToastCollectionRemoveSuccess": "Coleção removida",
"ToastCollectionUpdateSuccess": "Coleção atualizada",
+ "ToastConnectionNotAvailable": "Conexão indisponível. Tente novamente mais tarde",
+ "ToastCoverSearchFailed": "A busca pela capa falhou",
+ "ToastCoverUpdateFailed": "Falha na atualização da capa",
+ "ToastDateTimeInvalidOrIncomplete": "A data e a hora são inválidas ou estão incompletas",
"ToastDeleteFileFailed": "Falha ao apagar arquivo",
"ToastDeleteFileSuccess": "Arquivo apagado",
+ "ToastDeviceAddFailed": "Falha ao adicionar dispositivo",
+ "ToastDeviceNameAlreadyExists": "Já existe um leitor de ebook com esse nome",
+ "ToastDeviceTestEmailFailed": "Falha ao enviar o e-mail de teste",
+ "ToastDeviceTestEmailSuccess": "E-mail de teste enviado",
+ "ToastEmailSettingsUpdateSuccess": "Configurações de e-mail atualizadas",
+ "ToastEncodeCancelFailed": "Falha ao cancelar a codificação",
+ "ToastEncodeCancelSucces": "Codificação cancelada",
+ "ToastEpisodeDownloadQueueClearFailed": "Falha ao limpar a fila",
+ "ToastEpisodeDownloadQueueClearSuccess": "Fila de downloads de episódios limpa",
+ "ToastEpisodeUpdateSuccess": "{0} episódios atualizados",
+ "ToastErrorCannotShare": "Não é possível compartilhar nativamente neste dispositivo",
+ "ToastFailedToCreate": "Falha ao criar",
+ "ToastFailedToDelete": "Falha ao excluir",
"ToastFailedToLoadData": "Falha ao carregar dados",
+ "ToastFailedToMatch": "Não foi possível encontrar correspondência",
+ "ToastFailedToShare": "Não foi possível compartilhar",
+ "ToastFailedToUpdate": "Falha ao atualizar",
+ "ToastInvalidImageUrl": "URL de imagem inválida",
+ "ToastInvalidMaxEpisodesToDownload": "Número máximo de episódios para baixar inválido",
+ "ToastInvalidUrl": "URL inválida",
+ "ToastInvalidUrls": "Um ou mais URLs são inválidos",
"ToastItemCoverUpdateSuccess": "Capa do item atualizada",
+ "ToastItemDeletedFailed": "Falha ao excluir o item",
+ "ToastItemDeletedSuccess": "Item excluído",
"ToastItemDetailsUpdateSuccess": "Detalhes do item atualizados",
"ToastItemMarkedAsFinishedFailed": "Falha ao marcar como Concluído",
"ToastItemMarkedAsFinishedSuccess": "Item marcado como Concluído",
"ToastItemMarkedAsNotFinishedFailed": "Falha ao marcar como Não Concluído",
"ToastItemMarkedAsNotFinishedSuccess": "Item marcado como Não Concluído",
+ "ToastItemUpdateSuccess": "Item atualizado",
"ToastLibraryCreateFailed": "Falha ao criar biblioteca",
"ToastLibraryCreateSuccess": "Biblioteca \"{0}\" criada",
"ToastLibraryDeleteFailed": "Falha ao apagar biblioteca",
@@ -865,30 +1069,97 @@
"ToastLibraryScanFailedToStart": "Falha ao iniciar verificação",
"ToastLibraryScanStarted": "Verificação da biblioteca iniciada",
"ToastLibraryUpdateSuccess": "Biblioteca \"{0}\" atualizada",
+ "ToastMatchAllAuthorsFailed": "Não foi possível encontrar correspondência para todos os autores",
+ "ToastMetadataFilesRemovedError": "Erro ao remover arquivos de metadados.{0}",
+ "ToastMetadataFilesRemovedNoneFound": "Nenhum arquivo metadados.{0} encontrado na biblioteca",
+ "ToastMetadataFilesRemovedNoneRemoved": "Nenhum arquivo metadados.{0} removido",
+ "ToastMetadataFilesRemovedSuccess": "{0} arquivos metadados.{1} removidos",
+ "ToastMustHaveAtLeastOnePath": "Deve ter pelo menos um caminho",
+ "ToastNameEmailRequired": "Nome e e-mail são obrigatórios",
+ "ToastNameRequired": "O nome é obrigatório",
+ "ToastNewApiKeyUserError": "É necessário selecionar um usuário",
+ "ToastNewEpisodesFound": "{0} novos episódios encontrados",
+ "ToastNewUserCreatedFailed": "Falha ao criar a conta: \"{0}\"",
+ "ToastNewUserCreatedSuccess": "Nova conta criada",
+ "ToastNewUserLibraryError": "É necessário selecionar pelo menos uma biblioteca",
+ "ToastNewUserPasswordError": "É obrigatória a criação de uma senha; somente o usuário root pode ter uma senha vazia",
+ "ToastNewUserTagError": "É necessário selecionar pelo menos uma etiqueta",
"ToastNewUserUsernameError": "Digite um nome de usuário",
+ "ToastNoNewEpisodesFound": "Nenhum episódio novo encontrado",
+ "ToastNoRSSFeed": "O Podcast não possui um feed RSS",
+ "ToastNoUpdatesNecessary": "Nenhuma atualização necessária",
+ "ToastNotificationCreateFailed": "Falha ao criar notificação",
+ "ToastNotificationDeleteFailed": "Falha ao excluir a notificação",
+ "ToastNotificationFailedMaximum": "O número máximo de tentativas falhas deve ser >= 0",
+ "ToastNotificationQueueMaximum": "A fila máxima de notificações deve ser >= 0",
+ "ToastNotificationSettingsUpdateSuccess": "Configurações de notificação atualizadas",
+ "ToastNotificationTestTriggerFailed": "Falha ao acionar a notificação de teste",
+ "ToastNotificationTestTriggerSuccess": "Notificação de teste acionada",
+ "ToastNotificationUpdateSuccess": "Notificação atualizada",
"ToastPlaylistCreateFailed": "Falha ao criar lista de reprodução",
"ToastPlaylistCreateSuccess": "Lista de reprodução criada",
"ToastPlaylistRemoveSuccess": "Lista de reprodução removida",
"ToastPlaylistUpdateSuccess": "Lista de reprodução atualizada",
"ToastPodcastCreateFailed": "Falha ao criar podcast",
"ToastPodcastCreateSuccess": "Podcast criado",
+ "ToastPodcastEpisodeUpdated": "Episódio atualizado",
+ "ToastPodcastGetFeedFailed": "Não foi possível obter o feed do podcast",
+ "ToastPodcastNoEpisodesInFeed": "Nenhum episódio encontrado no feed RSS",
+ "ToastPodcastNoRssFeed": "O podcast não possui um feed RSS",
+ "ToastProgressIsNotBeingSynced": "O progresso não está sendo sincronizado, reinicie a reprodução",
+ "ToastProviderCreatedFailed": "Falha ao adicionar o provedor",
+ "ToastProviderCreatedSuccess": "Novo provedor adicionado",
+ "ToastProviderNameAndUrlRequired": "Nome e URL obrigatórios",
+ "ToastProviderRemoveSuccess": "Fornecedor removido",
"ToastRSSFeedCloseFailed": "Falha ao fechar feed RSS",
"ToastRSSFeedCloseSuccess": "Feed RSS fechado",
+ "ToastRemoveFailed": "Falha ao remover",
"ToastRemoveItemFromCollectionFailed": "Falha ao remover item da coleção",
"ToastRemoveItemFromCollectionSuccess": "Item removido da coleção",
+ "ToastRemoveItemsWithIssuesFailed": "Falha ao remover itens da biblioteca com problemas",
+ "ToastRemoveItemsWithIssuesSuccess": "Itens da biblioteca com problemas foram removidos",
+ "ToastRenameFailed": "Falha ao renomear",
+ "ToastRescanFailed": "Falha na nova verificação para {0}",
+ "ToastRescanRemoved": "O item foi removido após a digitalização ser concluída",
+ "ToastRescanUpToDate": "A nova verificação completou o item, que estava atualizado",
+ "ToastRescanUpdated": "O item foi atualizado após a digitalização",
+ "ToastScanFailed": "Falha ao digitalizar o item da biblioteca",
+ "ToastSelectAtLeastOneUser": "Selecione pelo menos um usuário",
"ToastSendEbookToDeviceFailed": "Falha ao enviar ebook para dispositivo",
"ToastSendEbookToDeviceSuccess": "Ebook enviado para o dispositivo \"{0}\"",
+ "ToastSeriesSubmitFailedSameName": "Não é possível adicionar duas séries com o mesmo nome",
"ToastSeriesUpdateFailed": "Falha ao atualizar série",
"ToastSeriesUpdateSuccess": "Série atualizada",
"ToastServerSettingsUpdateSuccess": "Configurações do servidor atualizadas",
+ "ToastSessionCloseFailed": "Falha ao encerrar a sessão",
"ToastSessionDeleteFailed": "Falha ao apagar sessão",
"ToastSessionDeleteSuccess": "Sessão apagada",
+ "ToastSleepTimerDone": "Temporizador de sono concluído... zZzzZz",
+ "ToastSlugMustChange": "O slug possui caracteres inválidos",
+ "ToastSlugRequired": "Slug é necessário",
"ToastSocketConnected": "Socket conectado",
"ToastSocketDisconnected": "Socket desconectado",
"ToastSocketFailedToConnect": "Falha na conexão do socket",
"ToastSortingPrefixesEmptyError": "É preciso ter pelo menos um prefixo de ordenação",
"ToastSortingPrefixesUpdateSuccess": "Prefixos de ordenação atualizados ({0} item(ns))",
+ "ToastTitleRequired": "É necessário ter um título",
+ "ToastUnknownError": "Erro desconhecido",
+ "ToastUnlinkOpenIdFailed": "Falha ao desvincular o usuário do OpenID",
+ "ToastUnlinkOpenIdSuccess": "Usuário desvinculado do OpenID",
+ "ToastUploaderFilepathExistsError": "O caminho de arquivo \"{0}\" já existe no servidor",
+ "ToastUploaderItemExistsInSubdirectoryError": "O item \"{0}\" está usando um subdiretório do caminho de upload.",
"ToastUserDeleteFailed": "Falha ao apagar usuário",
"ToastUserDeleteSuccess": "Usuário apagado",
- "ToastUserRootRequireName": "É preciso entrar com um nome de usuário root"
+ "ToastUserPasswordChangeSuccess": "Senha alterada com sucesso",
+ "ToastUserPasswordMismatch": "As senhas não coincidem",
+ "ToastUserPasswordMustChange": "A nova senha não pode ser igual à senha antiga",
+ "ToastUserRootRequireName": "É preciso entrar com um nome de usuário root",
+ "TooltipAddChapters": "Adicionar capítulo(s)",
+ "TooltipAddOneSecond": "Adicionar 1 segundo",
+ "TooltipAdjustChapterStart": "Clique para ajustar a hora de início",
+ "TooltipLockAllChapters": "Bloquear todos os capítulos",
+ "TooltipLockChapter": "Bloquear capítulo (Shift+clique para selecionar o intervalo)",
+ "TooltipSubtractOneSecond": "Subtrair 1 segundo",
+ "TooltipUnlockAllChapters": "Desbloqueie todos os capítulos",
+ "TooltipUnlockChapter": "Desbloquear capítulo (Shift + clique para selecionar o intervalo)"
}
diff --git a/client/strings/ro.json b/client/strings/ro.json
index c6628bd7d..441ccc400 100644
--- a/client/strings/ro.json
+++ b/client/strings/ro.json
@@ -4,143 +4,588 @@
"ButtonAddChapters": "Adaugă Capitole",
"ButtonAddDevice": "Adaugă Dispozitiv",
"ButtonAddLibrary": "Adaugă Librărie",
+ "ButtonAddPodcasts": "Adaugă Podcasturi",
"ButtonAddUser": "Adaugă Utilizator",
+ "ButtonAddYourFirstLibrary": "Adaugă prima librărie",
+ "ButtonApply": "Aplică",
+ "ButtonApplyChapters": "Aplică Capitole",
"ButtonAuthors": "Autori",
"ButtonBack": "Înapoi",
+ "ButtonBatchEditPopulateFromExisting": "Populează din existente",
+ "ButtonBatchEditPopulateMapDetails": "Populează detaliile hărții",
+ "ButtonBrowseForFolder": "Caută un dosar",
"ButtonCancel": "Anulează",
+ "ButtonCancelEncode": "Anulare codificare",
+ "ButtonChangeRootPassword": "Schimbare parolă de root",
+ "ButtonCheckAndDownloadNewEpisodes": "Verifică și descarcă episoade noi",
+ "ButtonChooseAFolder": "Alege un dosar",
+ "ButtonChooseFiles": "Alege fișiere",
"ButtonClearFilter": "Șterge filtrul",
"ButtonClose": "Închide",
"ButtonCloseFeed": "Închide sursa",
"ButtonCloseSession": "Închide Sesiunea Curentă",
"ButtonCollections": "Colecții",
+ "ButtonConfigureScanner": "Configurare scaner",
"ButtonCreate": "Creează",
+ "ButtonCreateBackup": "Creează backup",
"ButtonDelete": "Șterge",
+ "ButtonDownloadQueue": "Coadă",
+ "ButtonEdit": "Editare",
+ "ButtonEditChapters": "Editare capitole",
+ "ButtonEditPodcast": "Editare podcast",
+ "ButtonEnable": "Activează",
+ "ButtonForceReScan": "Forțează rescanare",
+ "ButtonFullPath": "Calea completă",
"ButtonHide": "Ascunde",
"ButtonHome": "Acasă",
- "ButtonIssues": "Erori",
+ "ButtonIssues": "Probleme",
+ "ButtonJumpBackward": "Sari înapoi",
+ "ButtonJumpForward": "Sari înainte",
"ButtonLatest": "Noutăți",
"ButtonLibrary": "Bibliotecă",
+ "ButtonLogout": "Deconectare",
+ "ButtonLookup": "Căutare",
+ "ButtonManageTracks": "Gestionează pista",
+ "ButtonMapChapterTitles": "Maparea titlurilor capitolelor",
+ "ButtonMatchAllAuthors": "Potriviește toți autorii",
+ "ButtonMatchBooks": "Potrivește Cărți",
+ "ButtonNevermind": "Anulează",
+ "ButtonNext": "Următorul",
+ "ButtonNextChapter": "Următorul Capitol",
+ "ButtonNextItemInQueue": "Următorul Articol în Coadă",
"ButtonOk": "OK",
"ButtonOpenFeed": "Vezi noutățile",
+ "ButtonOpenManager": "Deschide Managerul",
"ButtonPause": "Pauză",
"ButtonPlay": "Redă",
+ "ButtonPlayAll": "Redă tot",
+ "ButtonPlaying": "Redare",
"ButtonPlaylists": "Liste",
+ "ButtonPrevious": "Anterior",
+ "ButtonPreviousChapter": "Capitolul Anterior",
+ "ButtonProbeAudioFile": "Analizare Fișier Audio",
+ "ButtonPurgeAllCache": "Golire Cache Completă",
+ "ButtonPurgeItemsCache": "Golire Cache Articole",
+ "ButtonQueueAddItem": "Adaugă la Coadă",
+ "ButtonQueueRemoveItem": "Sterge din Coadă",
+ "ButtonQuickEmbed": "Încorporare Rapidă",
+ "ButtonQuickEmbedMetadata": "Metadate pentru Încorporare Rapidă",
+ "ButtonQuickMatch": "Potrivire Rapidă",
+ "ButtonReScan": "Rescanare",
"ButtonRead": "Citește",
- "ButtonReadLess": "Mai puțin",
+ "ButtonReadLess": "Citește Mai Puțin",
"ButtonReadMore": "Afișează mai mult",
+ "ButtonRefresh": "Reîmprospătare",
"ButtonRemove": "Elimină",
+ "ButtonRemoveAll": "Eliminați Tot",
+ "ButtonRemoveAllLibraryItems": "Ștergerea tuturor Articolelor din Librărie",
+ "ButtonRemoveFromContinueListening": "Ștergere din \"Continuă să Asculți\"",
+ "ButtonRemoveFromContinueReading": "Ștergere din \"Continuă să citești\"",
+ "ButtonRemoveSeriesFromContinueSeries": "Ștergere Serie din \"Continuă Seria\"",
+ "ButtonReset": "Resetează",
+ "ButtonResetToDefault": "Resetează la valorile implicite",
+ "ButtonRestore": "Restaurare",
"ButtonSave": "Salvează",
+ "ButtonSaveAndClose": "Salvează și Închide",
+ "ButtonSaveTracklist": "Salvare Pistă",
+ "ButtonScan": "Scanează",
+ "ButtonScanLibrary": "Scanează Librăria",
+ "ButtonScrollLeft": "Derulează spre stânga",
+ "ButtonScrollRight": "Derulează spre Dreapta",
"ButtonSearch": "Caută",
+ "ButtonSelectFolderPath": "Selectează Calea către Dosar",
"ButtonSeries": "Serii",
+ "ButtonSetChaptersFromTracks": "Setează capitole din piste",
+ "ButtonShare": "Distribuie",
+ "ButtonShiftTimes": "Aliniează timpi",
+ "ButtonShow": "Arată",
+ "ButtonStartM4BEncode": "Începe Codarea M4B",
+ "ButtonStartMetadataEmbed": "Începe Încorporarea Metadatelor",
+ "ButtonStats": "Statistici",
"ButtonSubmit": "Trimite",
+ "ButtonTest": "Testează",
+ "ButtonUnlinkOpenId": "Deconectare OpenID",
+ "ButtonUpload": "Încarcă",
+ "ButtonUploadBackup": "Încarcă Backup",
+ "ButtonUploadCover": "Încarcă Copertă",
+ "ButtonUploadOPMLFile": "Încarcă Fișier OPML",
+ "ButtonUserDelete": "Șterge userul {0}",
+ "ButtonUserEdit": "Editează userul {0}",
+ "ButtonViewAll": "Vizualizează tot",
"ButtonYes": "Da",
+ "ErrorUploadFetchMetadataAPI": "Eroare în descărcarea metadatelor",
+ "ErrorUploadFetchMetadataNoResults": "Nu s-au putut prelua metadatele - încearcă să editezi titlul și/sau autorul",
+ "ErrorUploadLacksTitle": "Trebuie să aibă un titlu",
"HeaderAccount": "Cont",
+ "HeaderAddCustomMetadataProvider": "Adaugă Furnizor de Metadate Personalizat",
"HeaderAdvanced": "Avansat",
+ "HeaderApiKeys": "Chei API",
+ "HeaderAppriseNotificationSettings": "Setări Notificări Apprise",
"HeaderAudioTracks": "Înregistrări audio",
+ "HeaderAudiobookTools": "Instrumente pentru Gestionarea Fișierelor Audiobook",
+ "HeaderAuthentication": "Autentificare",
+ "HeaderBackups": "Copii de siguranță",
+ "HeaderBulkChapterModal": "Adaugă Multiple Capitole",
+ "HeaderChangePassword": "Schimbă Parola",
"HeaderChapters": "Capitole",
+ "HeaderChooseAFolder": "Alege Dosar",
"HeaderCollection": "Colecție",
"HeaderCollectionItems": "Conținutul colecției",
+ "HeaderCover": "Copertă",
+ "HeaderCurrentDownloads": "Descărcări Curente",
+ "HeaderCustomMessageOnLogin": "Mesaj Personalizat la Autentificare",
+ "HeaderCustomMetadataProviders": "Furnizor de Metadate Personalizat",
"HeaderDetails": "Detalii",
+ "HeaderDownloadQueue": "Coadă de Descărcare",
"HeaderEbookFiles": "Ebook-uri",
+ "HeaderEmail": "Email",
+ "HeaderEmailSettings": "Setări Email",
"HeaderEpisodes": "Episoade",
+ "HeaderEreaderDevices": "Dispozitive eReader",
"HeaderEreaderSettings": "Setări eReader",
+ "HeaderFiles": "Fișiere",
+ "HeaderFindChapters": "Caută Capitol",
+ "HeaderIgnoredFiles": "Fișiere Ignorate",
+ "HeaderItemFiles": "Fișiere Articol",
+ "HeaderLastListeningSession": "Ultima Sesiune de Ascultare",
"HeaderLatestEpisodes": "Episoade recente",
"HeaderLibraries": "Biblioteci",
+ "HeaderLibraryFiles": "Fișiere in Librărie",
+ "HeaderLibraryStats": "Statistici Librărie",
+ "HeaderListeningSessions": "Sesiuni de Ascultare",
+ "HeaderListeningStats": "Statistici Ascultare",
+ "HeaderLogin": "Autentifică",
+ "HeaderLogs": "Loguri",
+ "HeaderManageGenres": "Gestionează Genuri",
+ "HeaderManageTags": "Gestionează Etichete",
+ "HeaderMapDetails": "Detaliile Hărții",
+ "HeaderMatch": "Potrivește",
+ "HeaderMetadataOrderOfPrecedence": "Prioritatea Metadatelor",
+ "HeaderMetadataToEmbed": "Metadate pentru Încorporare",
+ "HeaderNewAccount": "Cont nou",
+ "HeaderNewApiKey": "Cheie API Nouă",
+ "HeaderNewLibrary": "Librărie Nouă",
+ "HeaderNotificationCreate": "Creează Notificare",
+ "HeaderNotificationUpdate": "Actualizare Notificare",
+ "HeaderNotifications": "Notificări",
+ "HeaderOpenIDConnectAuthentication": "Autentificare prin OpenID",
+ "HeaderOpenListeningSessions": "Deschide Sesiuni de Ascultare",
"HeaderOpenRSSFeed": "Deschide flux RSS",
+ "HeaderOtherFiles": "Alte Fișiere",
+ "HeaderPasswordAuthentication": "Autentificare cu Parolă",
+ "HeaderPermissions": "Permisiuni",
+ "HeaderPlayerQueue": "Coadă Player",
+ "HeaderPlayerSettings": "Setări Player",
"HeaderPlaylist": "Listă de redare",
"HeaderPlaylistItems": "Conținut listă",
+ "HeaderPodcastsToAdd": "Podcast de Adăugat",
+ "HeaderPresets": "Presetări",
+ "HeaderPreviewCover": "Previzualizare Copertă",
"HeaderRSSFeedGeneral": "Date RSS",
"HeaderRSSFeedIsOpen": "RSS activ",
+ "HeaderRSSFeeds": "Fluxuri RSS",
+ "HeaderRemoveEpisode": "Elimină Episod",
+ "HeaderRemoveEpisodes": "Elimină {0} Episoade",
+ "HeaderSavedMediaProgress": "Progres Media Salvat",
+ "HeaderSchedule": "Planifică",
+ "HeaderScheduleEpisodeDownloads": "Planifică Descărcare Automată a Episoadelor",
+ "HeaderScheduleLibraryScans": "Planifică Scanarea Automată a Librăriei",
+ "HeaderSession": "Sesiuni",
+ "HeaderSetBackupSchedule": "Planifică Backup",
"HeaderSettings": "Setări",
+ "HeaderSettingsDisplay": "Afișaj",
+ "HeaderSettingsExperimental": "Caracteristici Experimentale",
+ "HeaderSettingsGeneral": "General",
+ "HeaderSettingsScanner": "Scaner",
+ "HeaderSettingsSecurity": "Securitate",
+ "HeaderSettingsWebClient": "Client Web",
"HeaderSleepTimer": "Timer de somn",
+ "HeaderStatsLargestItems": "Cele mai mari articole",
+ "HeaderStatsLongestItems": "Cele mai lungi articole (ore)",
"HeaderStatsMinutesListeningChart": "Minute ascultate (ultimele 7 zile)",
"HeaderStatsRecentSessions": "Sesiuni recente",
+ "HeaderStatsTop10Authors": "Top 10 Autori",
+ "HeaderStatsTop5Genres": "Top 5 Genuri",
"HeaderTableOfContents": "Cuprins",
+ "HeaderTools": "Unelte",
+ "HeaderUpdateAccount": "Actualizare Cont",
+ "HeaderUpdateApiKey": "Actualizare Cheie API",
+ "HeaderUpdateAuthor": "Actualizare Autor",
+ "HeaderUpdateDetails": "Actualizare Detalii",
+ "HeaderUpdateLibrary": "Actualizare Librărie",
+ "HeaderUsers": "Utilizatori",
+ "HeaderYearReview": "Trecere în revistă a anului {0}",
"HeaderYourStats": "Progresul tău",
+ "LabelAbridged": "Abreviat",
+ "LabelAbridgedChecked": "Abreviat (verificat)",
+ "LabelAbridgedUnchecked": "Neprescurtat (neverificat)",
+ "LabelAccessibleBy": "Accesibil prin",
+ "LabelAccountType": "Tip de Cont",
+ "LabelAccountTypeAdmin": "Administrator",
+ "LabelAccountTypeGuest": "Oaspete",
+ "LabelAccountTypeUser": "Utilizator",
+ "LabelActivities": "Activități",
+ "LabelActivity": "Activitate",
+ "LabelAddToCollection": "Adaugă la Colecție",
+ "LabelAddToCollectionBatch": "Adaugare {0} Cărți la Colecție",
"LabelAddToPlaylist": "Adaugă în listă",
+ "LabelAddToPlaylistBatch": "Adaugare {0} Articole la Listă",
"LabelAddedAt": "Adăugat la",
"LabelAddedDate": "Adăugat {0}",
+ "LabelAdminUsersOnly": "Doar Administratori",
"LabelAll": "Toate",
+ "LabelAllEpisodesDownloaded": "Toate episoadele descărcate",
+ "LabelAllUsers": "Toți Utilizatorii",
+ "LabelAllUsersExcludingGuests": "Toți utilizatorii cu excepția oaspeților",
+ "LabelAllUsersIncludingGuests": "Toți utilizatorii inclusiv oaspeții",
+ "LabelAlreadyInYourLibrary": "Deja în bibliotecă",
+ "LabelApiKeyCreated": "Cheia API \"{0}\" creată cu succes.",
+ "LabelApiKeyCreatedDescription": "Copiază cheia API acum deoarece nu va mai fi disponibilă pentru vizualizare.",
+ "LabelApiKeyUser": "Acționează în numele utilizatorului",
+ "LabelApiKeyUserDescription": "Această cheie API va avea aceleași permisiuni ca utilizatorul în numele căruia acționează. In loguri va părea că utilizatorul lansa cererile.",
+ "LabelApiToken": "Token API",
+ "LabelAppend": "Atașează",
+ "LabelAudioBitrate": "Rata de Biți Audio (e.g. 128k)",
+ "LabelAudioChannels": "Canale Audio (1 sau 2)",
+ "LabelAudioCodec": "Codec Audio",
"LabelAuthor": "Autor",
"LabelAuthorFirstLast": "Autor (Prenume Nume)",
"LabelAuthorLastFirst": "Autor (Nume, Prenume)",
"LabelAuthors": "Autori",
"LabelAutoDownloadEpisodes": "Descarcă automat episoadele",
+ "LabelAutoFetchMetadata": "Descarcă Automat Metadate",
+ "LabelAutoFetchMetadataHelp": "Descarcă metadate pentru titlu, autor si serii pentru eficientizarea încărcării. Metadatele suplimentare s-ar putea să trebuiască potrivite după încărcare.",
+ "LabelAutoLaunch": "Lansare automată",
+ "LabelAutoLaunchDescription": "Redirecționează automat către furnizorul de autentificare când navighez la pagina de autentificare (cale de suprascriere manuală /login?autoLaunch=0)",
+ "LabelAutoRegister": "Înregistrare Automată",
+ "LabelAutoRegisterDescription": "Creează utilizatori automat dupa autentificare",
+ "LabelBackToUser": "Înapoi la Utilizator",
+ "LabelBackupAudioFiles": "Copii de rezervă a Fișierelor Audio",
+ "LabelBackupLocation": "Locația Copiilor de Rezervă",
+ "LabelBackupsEnableAutomaticBackups": "Copii de Rezervă Automate",
+ "LabelBackupsEnableAutomaticBackupsHelp": "Copiile de Rezervă au fost salvate în /metadata/backups",
+ "LabelBackupsMaxBackupSize": "Dimensiunea maximă a copiilor de rezervă (în GB) (0 pentru nelimitat)",
+ "LabelBackupsMaxBackupSizeHelp": "Ca protecție împotriva configurațiilor greșite, backup-ul va eșua dacă trece de limita de dimensiune configurată.",
+ "LabelBackupsNumberToKeep": "Numărul copiilor de siguranță de păstrat",
+ "LabelBackupsNumberToKeepHelp": "Doar 1 copie de siguranță va fi ștearsă odata deci dacă există mai multe copii de siguranță vor trebui șterse manual.",
+ "LabelBitrate": "Rată de biți",
+ "LabelBonus": "Bonus",
"LabelBooks": "Cărți",
+ "LabelButtonText": "Textul Butonului",
+ "LabelByAuthor": "de {0}",
+ "LabelChangePassword": "Schimbare Parolă",
+ "LabelChannels": "Canale",
+ "LabelChapterCount": "{0} Capitole",
+ "LabelChapterTitle": "Titlul Capitolului",
"LabelChapters": "Capitole",
+ "LabelChaptersFound": "capitole găsite",
+ "LabelClickForMoreInfo": "Click pentru mai multe informații",
+ "LabelClickToUseCurrentValue": "Click pentru a folosi valoarea curentă",
"LabelClosePlayer": "Închide playerul",
+ "LabelCodec": "Codec",
"LabelCollapseSeries": "Restrânge seriile",
+ "LabelCollapseSubSeries": "Restrânge Sub-Seriile",
+ "LabelCollection": "Colecție",
+ "LabelCollections": "Colecții",
"LabelComplete": "Finalizat",
+ "LabelConfirmPassword": "Confirmare Parolă",
"LabelContinueListening": "Ascultă în continuare",
"LabelContinueReading": "Continuă lectura",
"LabelContinueSeries": "Continuă seria",
+ "LabelCorsAllowed": "Origini CORS Permise",
+ "LabelCover": "Copertă",
+ "LabelCoverImageURL": "URL-ul Imaginii de Copertă",
+ "LabelCoverProvider": "Furnizor Copertă",
+ "LabelCreatedAt": "Creat la",
+ "LabelCronExpression": "Expresie Cron",
+ "LabelCurrent": "Curent",
+ "LabelCurrently": "Acum:",
+ "LabelCustomCronExpression": "Expresie Cron Personalizată:",
+ "LabelDatetime": "Data și ora",
+ "LabelDays": "Zile",
+ "LabelDeleteFromFileSystemCheckbox": "Șterge fișierele din sistem (debifeaza pentru a șterge doar din baza de date)",
"LabelDescription": "Descriere",
+ "LabelDeselectAll": "Deselectați Tot",
+ "LabelDetectedPattern": "Tipar Identificat:",
+ "LabelDevice": "Dispozitiv",
+ "LabelDeviceInfo": "Informații Dispozitiv",
+ "LabelDeviceIsAvailableTo": "Dispozitiv accesibil lui...",
+ "LabelDirectory": "Dosar",
+ "LabelDiscFromFilename": "Disc din Numele Fișierului",
+ "LabelDiscFromMetadata": "Disc din Metadate",
"LabelDiscover": "Descoperă",
"LabelDownload": "Descarcă",
+ "LabelDownloadNEpisodes": "Descarcă {0} episoade",
+ "LabelDownloadable": "Descărcabil",
"LabelDuration": "Durată",
+ "LabelDurationComparisonExactMatch": "(potrivire exactă)",
+ "LabelDurationComparisonLonger": "({0} mai lung)",
+ "LabelDurationComparisonShorter": "({0} mai scurt)",
+ "LabelDurationFound": "Durată identificată:",
"LabelEbook": "Carte electronică",
"LabelEbooks": "Cărți electronice",
+ "LabelEdit": "Editare",
+ "LabelEmail": "Email",
+ "LabelEmailSettingsFromAddress": "De la Adresa",
+ "LabelEmailSettingsRejectUnauthorized": "Respingere certificate neautorizate",
+ "LabelEmailSettingsRejectUnauthorizedHelp": "Dezactivarea verificării certificatelor SSL vă poate expune conexiunea la riscuri de securitate, cum ar fi atacuri de tip man-in-the-middle. Dezactivați această opțiune dacă înțelegeti implicațiile și aveți încredere în serverul de mail la care vă conectați.",
+ "LabelEmailSettingsSecure": "Sigur",
+ "LabelEmailSettingsSecureHelp": "Dacă e adevărat, conexiunea se va realiza prin TLS către server. Dacă e fals, TLS este folosit dacă serverul suporta extensia STARTTLS. În majoritatea cazurilor setati adevărat dacă folosiți portul 465. Pentru portul 587 sau 25 setati fals. (referinta nodemailer.com/smtp/#authentication)",
+ "LabelEmailSettingsTestAddress": "Adresă de Test",
+ "LabelEmbeddedCover": "Încorporează Copertă",
"LabelEnable": "Activează",
+ "LabelEncodingBackupLocation": "O copie de siguranță a fișierului audio original va fi salvată în:",
+ "LabelEncodingChaptersNotEmbedded": "Capitolele nu sunt încorporate în cărțile audio cu mai multe track-uri.",
+ "LabelEncodingClearItemCache": "Asigurați-vă că ștergeți articolele din cache periodic.",
+ "LabelEncodingFinishedM4B": "Fișierul M4B va fi adaugat în dosarul dvs. de cărți audio când codificarea e terminată:",
+ "LabelEncodingInfoEmbedded": "Metadatele vor fi încorporate în fișierele audio din interiorul dosarului dvs. cu cărți audio.",
+ "LabelEncodingStartedNavigation": "Odată pornită sarcina poti naviga din această pagină.",
+ "LabelEncodingTimeWarning": "Codificarea poate dura până la 30 de minute.",
+ "LabelEncodingWarningAdvancedSettings": "Avertizare: Nu modificați aceste setări dacă nu sunteți familiar cu opțiunile de codare ffmpeg .",
+ "LabelEncodingWatcherDisabled": "Dacă ați dezactivat funcția de urmările va trebui sa rescanați acestă carte audio la ulterior.",
"LabelEnd": "Sfârșit",
"LabelEndOfChapter": "Sfârșitul capitolului",
"LabelEpisode": "Episod",
+ "LabelEpisodeNotLinkedToRssFeed": "Episoade nelegate de un flux RSS",
+ "LabelEpisodeNumber": "Episodul #{0}",
+ "LabelEpisodeTitle": "Titlul Episodului",
+ "LabelEpisodeType": "Tipul Episodului",
+ "LabelEpisodeUrlFromRssFeed": "URL-ul Episodului din Fluxul RSS",
+ "LabelEpisodes": "Episoade",
+ "LabelEpisodic": "Episodic",
+ "LabelExample": "Exemplu",
+ "LabelExpandSeries": "Extinde Seriile",
+ "LabelExpandSubSeries": "Extinde Sub-Seriile",
+ "LabelExpired": "Expirat",
+ "LabelExpiresAt": "Expiră La",
+ "LabelExpiresInSeconds": "Expiră în (secunde)",
+ "LabelExpiresNever": "Niciodată",
"LabelExplicit": "Explicit",
- "LabelFeedURL": "URL flux",
+ "LabelExplicitChecked": "Explicit (verificat)",
+ "LabelExplicitUnchecked": "Neexplicit (neverificat)",
+ "LabelExportOPML": "Exportă OPML",
+ "LabelFeedURL": "Flux URL",
+ "LabelFetchingMetadata": "Aducere Metadate",
"LabelFile": "Fișier",
"LabelFileBirthtime": "Data creării fișierului",
+ "LabelFileBornDate": "Creat {0}",
"LabelFileModified": "Fișier modificat",
+ "LabelFileModifiedDate": "Modificat {0}",
"LabelFilename": "Nume fișier",
+ "LabelFilterByUser": "Filtrare după Utilizator",
+ "LabelFindEpisodes": "Găsire Episoade",
"LabelFinished": "Finalizat",
+ "LabelFinishedDate": "Finalizat {0}",
"LabelFolder": "Dosar",
+ "LabelFolders": "Dosare",
+ "LabelFontBold": "Îngroșat",
"LabelFontBoldness": "Grosimea fontului",
+ "LabelFontFamily": "Familia Fontului",
+ "LabelFontItalic": "Cursiv",
"LabelFontScale": "Mărimea fontului",
+ "LabelFontStrikethrough": "Tăiat cu o linie",
+ "LabelFormat": "Format",
+ "LabelFull": "Întreg",
"LabelGenre": "Gen",
"LabelGenres": "Genuri",
+ "LabelHardDeleteFile": "Ștergere definitivă a fișierului",
"LabelHasEbook": "Are carte electronică",
"LabelHasSupplementaryEbook": "Are carte electronică suplimentară",
+ "LabelHideSubtitles": "Ascunde Subtitrări",
+ "LabelHighestPriority": "Prioritatea cea mai ridicată",
"LabelHost": "Gazdă",
+ "LabelHour": "Ora",
+ "LabelHours": "Ore",
+ "LabelIcon": "Pictogramă",
+ "LabelImageURLFromTheWeb": "URL-ul imaginii de pe web",
"LabelInProgress": "În desfășurare",
+ "LabelIncludeInTracklist": "Include în Lista de Melodii",
"LabelIncomplete": "Incomplet",
+ "LabelInterval": "Interval",
+ "LabelIntervalCustomDailyWeekly": "Personalizat zilnic/saptămânal",
+ "LabelIntervalEvery12Hours": "La fiecare 12 ore",
+ "LabelIntervalEvery15Minutes": "La fiecare 15 minute",
+ "LabelIntervalEvery2Hours": "La fiecare 2 ore",
+ "LabelIntervalEvery30Minutes": "La fiecare 30 minute",
+ "LabelIntervalEvery6Hours": "La fiecare 6 ore",
+ "LabelIntervalEveryDay": "În fiecare zi",
+ "LabelIntervalEveryHour": "În fiecare oră",
+ "LabelIntervalEveryMinute": "La fiecare minut",
+ "LabelInvert": "Inversează",
+ "LabelItem": "Articol",
+ "LabelJumpBackwardAmount": "Sari înapoi cu",
+ "LabelJumpForwardAmount": "Sari înainte cu",
"LabelLanguage": "Limbă",
+ "LabelLanguageDefaultServer": "Limba Prestabilită a Serverului",
+ "LabelLanguages": "Limbi",
+ "LabelLastBookAdded": "Ultima Carte Adăugată",
+ "LabelLastBookUpdated": "Ultima Carte Actualizată",
+ "LabelLastProgressDate": "Ultimul progres: {0}",
+ "LabelLastSeen": "Ultima dată văzut",
+ "LabelLastTime": "Ultima dată",
+ "LabelLastUpdate": "Ultima actualizare",
"LabelLayout": "Aspect",
"LabelLayoutSinglePage": "Pagină unică",
+ "LabelLayoutSplitPage": "Pagină împărțită",
+ "LabelLess": "Mai puțin",
+ "LabelLibrariesAccessibleToUser": "Biblioteci Accesibile Utilizatorului",
+ "LabelLibrary": "Bibliotecă",
+ "LabelLibraryFilterSublistEmpty": "Numărul {0}",
+ "LabelLibraryItem": "Articol din Bibliotecă",
+ "LabelLibraryName": "Numele Bibliotecii",
+ "LabelLibrarySortByProgress": "Progres: Ultima Actualizare",
+ "LabelLibrarySortByProgressFinished": "Progres: Finalizat",
+ "LabelLibrarySortByProgressStarted": "Progres: Început",
+ "LabelLimit": "Limită",
"LabelLineSpacing": "Spațiere între rânduri",
"LabelListenAgain": "Ascultă din nou",
+ "LabelLogLevelDebug": "Depanare",
+ "LabelLogLevelInfo": "Informații",
+ "LabelLogLevelWarn": "Avertizare",
+ "LabelLookForNewEpisodesAfterDate": "Caută episoade noi după această dată",
+ "LabelLowestPriority": "Cea Mai Scăzută Prioritate",
+ "LabelMatchConfidence": "Încredere",
+ "LabelMatchExistingUsersBy": "Potrivire utilizatori existenți prin",
+ "LabelMatchExistingUsersByDescription": "Folosit pentru a conecta utilizatorii existenți. Odata conectați, utilizatorii vor fi potriviți după un ID unic trimis de furnizorul SSO",
+ "LabelMaxEpisodesToDownload": "Numarul maxim # de episoade de descărcat. Folosiți 0 pentru nelimitat.",
+ "LabelMaxEpisodesToDownloadPerCheck": "Numărul maxim # de episoade de descărcat per verificare",
+ "LabelMaxEpisodesToKeep": "Numarul maxim # de episoade păstrate",
+ "LabelMaxEpisodesToKeepHelp": "Valorea 0 nu stabilește o limită maximă. După ce un episod nou a fost descărcat automat, cel mai vechi episod va fi șters dacă aveți mai mult de X episoade. Se va șterge câte un episod vechi pentru fiecare episod nou descărcat.",
+ "LabelMediaPlayer": "Player Media",
"LabelMediaType": "Tip media",
+ "LabelMetaTag": "Etichetă Meta",
+ "LabelMetaTags": "Etichete Meta",
+ "LabelMetadataOrderOfPrecedenceDescription": "Sursele de metadate cu prioritate mai mare o să suprascrie sursele de metadate cu prioritate mai mică",
+ "LabelMetadataProvider": "Furnizor Metadate",
+ "LabelMinute": "Minut",
+ "LabelMinutes": "Minute",
"LabelMissing": "Lipsă",
+ "LabelMissingEbook": "Nu are carte electronică",
+ "LabelMissingSupplementaryEbook": "Nu are carte electronică adițională",
+ "LabelMobileRedirectURIs": "URL-uri de redirecționare Mobile Permise",
+ "LabelMobileRedirectURIsDescription": "Aceasta este o listă cu URI-uri valide pentru redirectionare a aplicațiilor mobile. URI-ul predefinit este audiobookshelf://oauth,, care poate fi sters sau suplimentat cu URI-uri adiționale pentru integrarea cu alte aplicații. Folosirea unui asterisc (*) ca singur element permite orice URI.",
"LabelMore": "Mai multe",
"LabelMoreInfo": "Mai multe informații",
"LabelName": "Nume",
"LabelNarrator": "Narator",
"LabelNarrators": "Naratori",
+ "LabelNew": "Nou",
+ "LabelNewPassword": "Parolă Nouă",
"LabelNewestAuthors": "Autori noi",
"LabelNewestEpisodes": "Episoade noi",
+ "LabelNextBackupDate": "Următoarea dată a copiilor de siguranță",
+ "LabelNextChapters": "Următoarele capitole vor fi:",
+ "LabelNextScheduledRun": "Urmatoarea rulare programată",
+ "LabelNoApiKeys": "Nu exista chei API",
+ "LabelNoCustomMetadataProviders": "Nu există furnizori de metadate personalizați",
+ "LabelNoEpisodesSelected": "Nici un episod selectat",
"LabelNotFinished": "Nefinalizat",
"LabelNotStarted": "Neînceput",
+ "LabelNotes": "Note",
+ "LabelNotificationAppriseURL": "URL-ul(urile) Apprise",
+ "LabelNotificationAvailableVariables": "Variabile disponibile",
+ "LabelNotificationBodyTemplate": "Corpul Șablonului",
+ "LabelNotificationEvent": "Eveniment de notificare",
+ "LabelNotificationTitleTemplate": "Titlul Șablonului",
+ "LabelNotificationsMaxFailedAttempts": "Număr de încercări eșuate maxim",
+ "LabelNotificationsMaxFailedAttemptsHelp": "Notificările sunt dezactivate dacă nu reușesc să fie trimise de acest număr de ori",
+ "LabelNotificationsMaxQueueSize": "Dimensiunea maximă a cozii pentru evenimentele de notificare",
+ "LabelNotificationsMaxQueueSizeHelp": "Evenimentele sunt limitate la 1 per secunda. Evenimentele vor fi ignorate dacă coada este plină. Previne spamarea cu notificări.",
+ "LabelNumberOfBooks": "Numărul de Cărți",
+ "LabelNumberOfChapters": "Număr de capitole:",
"LabelNumberOfEpisodes": "# de Episoade",
+ "LabelOpenIDAdvancedPermsClaimDescription": "Numele revendicării OpenID care conține permisiuni avansate pentru acțiunile utilizatorului în interiorul aplicației care vor fi aplicate rolurilor non-administrator (dacă e configurat). Dacă revendicarea nu e prezentă în răspunsul primit, accesul către ABS e refuzat. Dacă o singură opțiune lipsește, va fi tratată ca falsă. Asigurați-vă că revendicările furnizorului de securitate corespund structurii așteptate:",
+ "LabelOpenIDClaims": "Lăsați urmatoarele opțiuni goale pentru a dezactiva atribuirea avansată de grupuri și permisiuni, asignând grupul \"Utilizatori\" automat.",
+ "LabelOpenRSSFeed": "Flux Open RSS",
+ "LabelOverwrite": "Suprascrie",
+ "LabelPaginationPageXOfY": "Pagina {0} din {1}",
"LabelPassword": "Parolă",
"LabelPath": "Cale",
+ "LabelPermanent": "Permanent",
+ "LabelPermissionsAccessAllLibraries": "Poate accesa toate bibliotecile",
+ "LabelPermissionsAccessAllTags": "Poate accesa toate etichetele",
+ "LabelPermissionsAccessExplicitContent": "Poate Accesa Conținut Explicit",
+ "LabelPermissionsCreateEreader": "Poate Crea Cititoare Electronice",
+ "LabelPermissionsDelete": "Poate Șterge",
+ "LabelPermissionsDownload": "Poate Descărca",
+ "LabelPermissionsUpdate": "Poate Actualiza",
+ "LabelPermissionsUpload": "Poate Încărca",
+ "LabelPersonalYearReview": "Recapitularea Anului tău ({0})",
+ "LabelPhotoPathURL": "Calea/URL-ul Fotografiei",
+ "LabelPlayMethod": "Metoda de Redare",
+ "LabelPlaybackRateIncrementDecrement": "Incrementare/Decrementare a Ratei de Redare cu",
+ "LabelPlayerChapterNumberMarker": "{0} din {1}",
+ "LabelPlaylists": "Liste de redare",
"LabelPodcast": "Podcast",
+ "LabelPodcastSearchRegion": "Regiunea căutării podcastului",
+ "LabelPodcastType": "Tipul Podcastului",
"LabelPodcasts": "Podcasturi",
+ "LabelPort": "Portul",
+ "LabelPrefixesToIgnore": "Prefix de ignorat (fără a ține cont de majuscule)",
"LabelPreventIndexing": "Împiedică indexarea fluxului în directoarele iTunes și Google Podcasts",
+ "LabelPrimaryEbook": "eCarte Principală",
"LabelProgress": "Progres",
+ "LabelProvider": "Furnizor",
+ "LabelProviderAuthorizationValue": "Valoarea Antetului de Autorizare",
"LabelPubDate": "Data publicării",
"LabelPublishYear": "Anul publicării",
"LabelPublishedDate": "Publicat la {0}",
+ "LabelPublishedDecade": "Deceniul Publicării",
+ "LabelPublishedDecades": "Deceniile Publicării",
+ "LabelPublisher": "Editor",
+ "LabelPublishers": "Editori",
"LabelRSSFeedCustomOwnerEmail": "Email personalizat al proprietarului",
"LabelRSSFeedCustomOwnerName": "Nume personalizat al proprietarului",
"LabelRSSFeedOpen": "Flux RSS deschis",
"LabelRSSFeedPreventIndexing": "Previne indexarea",
"LabelRSSFeedSlug": "Identificator flux RSS",
+ "LabelRSSFeedURL": "URL-ul Fluxului RSS",
"LabelRandomly": "Aleatoriu",
+ "LabelReAddSeriesToContinueListening": "Readăugare serie la \"Continuă să asculți\"",
"LabelRead": "Citește",
"LabelReadAgain": "Citește din nou",
+ "LabelReadEbookWithoutProgress": "Citire eCarte fără a memora progresul",
"LabelRecentSeries": "Serii recente",
"LabelRecentlyAdded": "Adăugate recent",
+ "LabelRecommended": "Recomandat",
+ "LabelRedo": "Refă",
+ "LabelRegion": "Regiune",
+ "LabelReleaseDate": "Data Lansării",
+ "LabelRemoveAllMetadataAbs": "Ștergerea tuturor fișierelor metadata.abs",
+ "LabelRemoveAllMetadataJson": "Ștergerea tuturor fișierelor metadata.json",
+ "LabelRemoveAudibleBranding": "Ștergerea Audible intro și outro din capitole",
+ "LabelRemoveCover": "Șterge coperta",
+ "LabelRemoveMetadataFile": "Șterge fisierele metadate din dosarele bibliotecii",
+ "LabelRemoveMetadataFileHelp": "Șterge toate fișierele metadata.json și metadata.abs din {0} dosare.",
+ "LabelRowsPerPage": "Rânduri pe pagină",
+ "LabelSearchTerm": "Termen de căutat",
+ "LabelSearchTitle": "Titlu de căutat",
+ "LabelSearchTitleOrASIN": "Titlu de căutat sau ASN",
"LabelSeason": "Sezon",
+ "LabelSeasonNumber": "Sezonul #{0}",
+ "LabelSelectAll": "Selectează tot",
+ "LabelSelectAllEpisodes": "Selectează toate episoadele",
+ "LabelSelectEpisodesShowing": "Selectează {0} episoade dintre cele afișate",
+ "LabelSelectUser": "Selectare utilizator",
+ "LabelSelectUsers": "Selectare utilizatori",
+ "LabelSendEbookToDevice": "Trimite eCarte către...",
+ "LabelSequence": "Secvență",
+ "LabelSerial": "Serie",
"LabelSeries": "Serii",
+ "LabelSeriesName": "Numele Seriilor",
+ "LabelSeriesProgress": "Progresul Seriilor",
+ "LabelServerLogLevel": "Nivelul de Jurnal al Serverului",
+ "LabelServerYearReview": "Anul Serverului în Retrospectivă ({0})",
"LabelSetEbookAsPrimary": "Setează ca principală",
"LabelSetEbookAsSupplementary": "Setează ca suplimentară",
+ "LabelSettingsAllowIframe": "Permite încorporarea intr-un iframe",
+ "LabelSettingsAudiobooksOnly": "Doar cărți audio",
+ "LabelSettingsAudiobooksOnlyHelp": "Activarea acestei set[ri va ignora fișierele eBook daca acestea nu se află într-un dosar al unei cărți audio, caz în care vor fi setate ca eBook suplimentar",
+ "LabelSettingsBookshelfViewHelp": "Design scheumorf cu rafturi de lemn",
+ "LabelSettingsChromecastSupport": "Suport Chromecast",
+ "LabelSettingsDateFormat": "Formatul Datei",
+ "LabelSettingsEnableWatcher": "Urmărește în mod automat bibliotecile pentru schimbări",
+ "LabelSettingsEnableWatcherForLibrary": "Urmărește în mod automat biblioteca pentru schimbări",
"LabelShowAll": "Afișează tot",
"LabelSize": "Dimensiune",
"LabelSleepTimer": "Timer de somn",
diff --git a/client/strings/ru.json b/client/strings/ru.json
index 8bebb2835..c84fe9dcf 100644
--- a/client/strings/ru.json
+++ b/client/strings/ru.json
@@ -275,7 +275,7 @@
"LabelBonus": "Бонус",
"LabelBooks": "Книги",
"LabelButtonText": "Текст кнопки",
- "LabelByAuthor": "{0}",
+ "LabelByAuthor": "от {0}",
"LabelChangePassword": "Изменить пароль",
"LabelChannels": "Ленты",
"LabelChapterCount": "{0} Главы",
@@ -383,7 +383,7 @@
"LabelFolders": "Папки",
"LabelFontBold": "Жирный",
"LabelFontBoldness": "Жирность шрифта",
- "LabelFontFamily": "Семейство шрифтов",
+ "LabelFontFamily": "Семейство шрифта",
"LabelFontItalic": "Курсив",
"LabelFontScale": "Масштаб шрифта",
"LabelFontStrikethrough": "Зачеркнутый",
@@ -588,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Конструкция с деревянными полками",
"LabelSettingsChromecastSupport": "Поддержка Chromecast",
"LabelSettingsDateFormat": "Формат даты",
- "LabelSettingsEnableWatcher": "Автоматическое сканирование библиотек на изменение",
- "LabelSettingsEnableWatcherForLibrary": "Автоматическое сканирование библиотеки на изменение",
+ "LabelSettingsEnableWatcher": "Автоматически отслеживать изменения в библиотеках",
+ "LabelSettingsEnableWatcherForLibrary": "Автоматический просмотр библиотеки на изменение",
"LabelSettingsEnableWatcherHelp": "Включает автоматическое добавление/обновление элементов при обнаружении изменений файлов. *Требуется перезапуск сервера",
"LabelSettingsEpubsAllowScriptedContent": "Разрешение содержимого epub с скриптами",
"LabelSettingsEpubsAllowScriptedContentHelp": "Разрешить файлам epub выполнять скрипты. Рекомендуется отключать этот параметр, если вы не доверяете источнику файлов epub.",
@@ -888,7 +888,7 @@
"MessageResetChaptersConfirm": "Вы уверены, что хотите сбросить главы и отменить внесенные изменения?",
"MessageRestoreBackupConfirm": "Вы уверены, что хотите восстановить резервную копию, созданную",
"MessageRestoreBackupWarning": "Восстановление резервной копии перезапишет всю базу данных, расположенную в /config, и обложки изображений в /metadata/items и /metadata/authors.
Бэкапы не изменяют файлы в папках библиотеки. Если вы включили параметры сервера для хранения обложек и метаданных в папках библиотеки, то они не резервируются и не перезаписываются.
Все клиенты, использующие ваш сервер, будут автоматически обновлены.",
- "MessageScheduleLibraryScanNote": "Большинству пользователей рекомендуется отключить эту функцию и включить функцию просмотра папок. Программа просмотра папок автоматически обнаружит изменения в папках вашей библиотеки. Программа просмотра папок работает не для каждой файловой системы (например, NFS), поэтому вместо этого можно использовать запланированные проверки библиотеки.",
+ "MessageScheduleLibraryScanNote": "Большинству пользователей рекомендуется отключить эту функцию и оставить включённой функцию \"Автоматически отслеживать изменения в библиотеках\" - она будет автоматически обнаруживать изменения в папках вашей библиотеки. Включите эту функцию если \"Автоматически отслеживать изменения в библиотеках\" не работает для вашей файловой системы (например, NFS).",
"MessageScheduleRunEveryWeekdayAtTime": "Запуск каждые {0} по {1}",
"MessageSearchResultsFor": "Результаты поиска для",
"MessageSelected": "{0} выбрано",
diff --git a/client/strings/sk.json b/client/strings/sk.json
index e0d02898c..6101eba7d 100644
--- a/client/strings/sk.json
+++ b/client/strings/sk.json
@@ -23,7 +23,7 @@
"ButtonClearFilter": "Zrušiť filter",
"ButtonClose": "Uzavrieť",
"ButtonCloseFeed": "Zatvoriť zdroj",
- "ButtonCloseSession": "Ukončiť otvorené pripojenie",
+ "ButtonCloseSession": "Ukončiť aktívne relácie",
"ButtonCollections": "Kolekcie",
"ButtonConfigureScanner": "Nastaviť skener",
"ButtonCreate": "Vytvoriť",
@@ -150,12 +150,12 @@
"HeaderIgnoredFiles": "Ignorované súbory",
"HeaderItemFiles": "Položka Súbory",
"HeaderItemMetadataUtils": "Položka Nástroje metadát",
- "HeaderLastListeningSession": "Posledné pripojenie",
+ "HeaderLastListeningSession": "Posledná relácia",
"HeaderLatestEpisodes": "Posledné epizódy",
"HeaderLibraries": "Knižnice",
"HeaderLibraryFiles": "Súbory knižnice",
"HeaderLibraryStats": "Štatistiky knižnice",
- "HeaderListeningSessions": "Pripojenia",
+ "HeaderListeningSessions": "Relácie",
"HeaderListeningStats": "Štatistiky počúvania",
"HeaderLogin": "Prihlásenie",
"HeaderLogs": "Záznamy udalostí",
@@ -172,7 +172,7 @@
"HeaderNotificationUpdate": "Aktualizovať notifikáciu",
"HeaderNotifications": "Notifikácie",
"HeaderOpenIDConnectAuthentication": "Overenie pripojenia OpenID",
- "HeaderOpenListeningSessions": "Aktívne pripojenia",
+ "HeaderOpenListeningSessions": "Aktívne relácie",
"HeaderOpenRSSFeed": "Otvoriť RSS zdroj",
"HeaderOtherFiles": "Ostatné súbory",
"HeaderPasswordAuthentication": "Overenie heslom",
@@ -189,7 +189,7 @@
"HeaderRSSFeeds": "RSS zdroje",
"HeaderRemoveEpisode": "Odstrániť epizódu",
"HeaderRemoveEpisodes": "Odstrániť {0} epizód",
- "HeaderSavedMediaProgress": "Priebeh uložených médií",
+ "HeaderSavedMediaProgress": "Stav uložených médií",
"HeaderSchedule": "Plán",
"HeaderScheduleEpisodeDownloads": "Naplánovať automatické sťahovanie epizód",
"HeaderScheduleLibraryScans": "Naplánovanovať automatické skenovanie knižnice",
@@ -222,7 +222,7 @@
"LabelAbridged": "Skrátená verzia",
"LabelAbridgedChecked": "Skrátená verzia (zaškrtnuté)",
"LabelAbridgedUnchecked": "Neskrátená verzia (nezaškrtnuté)",
- "LabelAccessibleBy": "Prístupné pre",
+ "LabelAccessibleBy": "Dostupné pre",
"LabelAccountType": "Typ účtu",
"LabelAccountTypeAdmin": "Administrátor",
"LabelAccountTypeGuest": "Hosť",
@@ -230,7 +230,7 @@
"LabelActivities": "Aktivity",
"LabelActivity": "Aktivita",
"LabelAddToCollection": "Pridať do zbierky",
- "LabelAddToCollectionBatch": "Pridať {0} kníh do kolekcie",
+ "LabelAddToCollectionBatch": "Pridať {0} kníh do zbierky",
"LabelAddToPlaylist": "Pridať do playlistu",
"LabelAddToPlaylistBatch": "Pridať {0} položie do playlistu",
"LabelAddedAt": "Pridané",
@@ -275,7 +275,7 @@
"LabelBonus": "Bonus",
"LabelBooks": "Knihy",
"LabelButtonText": "Text tlačidla",
- "LabelByAuthor": "od {0}",
+ "LabelByAuthor": "od",
"LabelChangePassword": "Zmeniť heslo",
"LabelChannels": "Kanály",
"LabelChapterCount": "{0} kapitol",
@@ -288,8 +288,8 @@
"LabelCodec": "Kodek",
"LabelCollapseSeries": "Zbaliť série",
"LabelCollapseSubSeries": "Zbaliť podsérie",
- "LabelCollection": "Kolekcia",
- "LabelCollections": "Kolekcie",
+ "LabelCollection": "Zbierka",
+ "LabelCollections": "Zbierky",
"LabelComplete": "Hotovo",
"LabelConfirmPassword": "Potvrdiť heslo",
"LabelContinueListening": "Pokračovať v počúvaní",
@@ -383,7 +383,7 @@
"LabelFolders": "Priečinky",
"LabelFontBold": "Tučné",
"LabelFontBoldness": "Hrúbka písma",
- "LabelFontFamily": "Rodina písiem",
+ "LabelFontFamily": "písmo",
"LabelFontItalic": "Kurzíva",
"LabelFontScale": "Veľkosť písma",
"LabelFontStrikethrough": "Preškrtnuté",
@@ -436,9 +436,9 @@
"LabelLibraryFilterSublistEmpty": "Žiadne {0}",
"LabelLibraryItem": "Položka knižnice",
"LabelLibraryName": "Názov knižnice",
- "LabelLibrarySortByProgress": "Pokrok: Aktualizované",
- "LabelLibrarySortByProgressFinished": "Pokrok: Dokončené",
- "LabelLibrarySortByProgressStarted": "Pokrok: Začiatok",
+ "LabelLibrarySortByProgress": "Stav: Naposledy aktualizované",
+ "LabelLibrarySortByProgressFinished": "Stav: Dokončené",
+ "LabelLibrarySortByProgressStarted": "Stav: Začal",
"LabelLimit": "Limit",
"LabelLineSpacing": "Riadkovanie",
"LabelListenAgain": "Počúvať znova",
@@ -528,7 +528,7 @@
"LabelPrefixesToIgnore": "Ignorované predpony (bez ohľadu na veľkosť písmen)",
"LabelPreventIndexing": "Zabráni indexácii vašich zdrojov službami iTunes a Google podcast directories",
"LabelPrimaryEbook": "Primárny e-book",
- "LabelProgress": "Stav",
+ "LabelProgress": "Aktuálny stav",
"LabelProvider": "Poskytovateľ",
"LabelProviderAuthorizationValue": "Obsah hlavičky autorizácie",
"LabelPubDate": "Dátum publikovania",
@@ -548,7 +548,7 @@
"LabelReAddSeriesToContinueListening": "Znova pridať série do pokračujúceho počúvania",
"LabelRead": "Načítať",
"LabelReadAgain": "Čítať znova",
- "LabelReadEbookWithoutProgress": "Čítať e-knihu bez sledovania pokroku",
+ "LabelReadEbookWithoutProgress": "Čítať e-knihu bez zmeny stavu",
"LabelRecentSeries": "Posledné série",
"LabelRecentlyAdded": "Posledné pridané",
"LabelRecommended": "Odporúčané",
@@ -577,7 +577,7 @@
"LabelSerial": "Na pokračovanie",
"LabelSeries": "Série",
"LabelSeriesName": "Názov série",
- "LabelSeriesProgress": "Pokrok série",
+ "LabelSeriesProgress": "Aktuálny stav série",
"LabelServerLogLevel": "Úroveň logovania servera",
"LabelServerYearReview": "Rok servera v prehľade ({0})",
"LabelSetEbookAsPrimary": "Nastaviť ako primárny",
@@ -588,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Skeuomorfný dizajn s drevenými poličkami",
"LabelSettingsChromecastSupport": "Podpora chromecastu",
"LabelSettingsDateFormat": "Formát dátumu",
- "LabelSettingsEnableWatcher": "Automatické skenovanie knižníc pre zmeny",
- "LabelSettingsEnableWatcherForLibrary": "Automaticky skenovať knižnicu pre zmeny",
+ "LabelSettingsEnableWatcher": "Automatické sledovanie zmien v knižniciach",
+ "LabelSettingsEnableWatcherForLibrary": "Automatické sledovanie zmien v knižnici",
"LabelSettingsEnableWatcherHelp": "Povoliť automatické pridávanie/aktualizácie položiek pri zmene súborov. *Vyžaduje reštart servera",
"LabelSettingsEpubsAllowScriptedContent": "Povoliť v e-knihách skriptovaný obsah",
"LabelSettingsEpubsAllowScriptedContentHelp": "Povoliť e-knihám spúšťanie skriptov. Odporúča sa túto voľbu nepovolovať, pokiaľ plne nedôverujete zdrojom súborov e-kníh.",
@@ -888,7 +888,7 @@
"MessageResetChaptersConfirm": "Ste si istý, že chcete resetnúť kapitoly a zahodiť zmeny, ktoré ste vykonali?",
"MessageRestoreBackupConfirm": "Ste si istí, že chcete obnoviť zálohu vytvorenú",
"MessageRestoreBackupWarning": "Obnovenie zálohy spôsobí kompletný prepis databázy umiestnenej v /config a obrázkov prebalov a autorov v /metadata/items a /metadata/authors.
Zálohy nemenia žiadne súbory v priečinkoch vašej knižnice. Ak ste povolili v nastaveniach servera ukladanie obrázkov prebalov a metadát v priečinkoch knižnice, tieto nie sú zálohované a teda ani prepisované.
Všetky klienti používajúci váš server budú automaticky obnovené.",
- "MessageScheduleLibraryScanNote": "Pre väčšinu používateľov sa odporúča ponechať túto funkciu vypnutú a povoliť nastavenia funkcie sledovania obsahu priečinku. Funkcia sledovania priečinku bude automaticky detekovať zmeny v priečinkoch knižnice. Táto funkcia však nefunguje pre všetky súborové systémy (ako napr. NFS), v tom prípade využite funkciu plánovaného skenovania knižnice.",
+ "MessageScheduleLibraryScanNote": "Pre väčšinu používateľov sa odporúča nechať túto funkciu vypnutú a ponechať zapnuté nastavenie „Automatické sledovanie zmien v knižnici“ – táto funkcia automaticky zistí zmeny vo vašich priečinkoch knižnice. Túto funkciu zapnite, ak „Automatické sledovanie zmien v knižnici“ nefunguje vo vašom súborovom systéme (napr. NFS).",
"MessageScheduleRunEveryWeekdayAtTime": "Spustiť každú {0} o {1}",
"MessageSearchResultsFor": "Výsledky vyhľadávania pre",
"MessageSelected": "{0} vybrané",
@@ -1015,7 +1015,7 @@
"ToastCachePurgeFailed": "Vyčistenie vyrovnávacej pamäte zlyhalo",
"ToastCachePurgeSuccess": "Vyrovnávacia pamäť vyčistená",
"ToastChapterLocked": "Kapitola je zamknutá.",
- "ToastChapterStartTimeAdjusted": "Čas začiatku kapitoly upravený o {0} sek.",
+ "ToastChapterStartTimeAdjusted": "Čas začiatku kapitoly upravený o {0} sekúnd",
"ToastChaptersAllLocked": "Všetky kapitoly sú zamknuté. Odomknite niektoré kapitoly, aby ste posunuli ich časy.",
"ToastChaptersHaveErrors": "Kapitoly obsahujú chyby",
"ToastChaptersInvalidShiftAmountLast": "Neplatná hodnota veľkosti posunutia. Začiatok poslednej kapitoly by ležal za koncom audioknihy.",
@@ -1026,6 +1026,8 @@
"ToastCollectionItemsAddFailed": "Pridanie položky/-iek do zbierky zlyhalo",
"ToastCollectionRemoveSuccess": "Zbierka odstránená",
"ToastCollectionUpdateSuccess": "Zbierka aktualizovaná",
+ "ToastConnectionNotAvailable": "Pripojenie je nedostupné. Skúste to neskôr",
+ "ToastCoverSearchFailed": "Vyhľadanie obalu sa nepodarilo",
"ToastCoverUpdateFailed": "Aktualizácia prebalu zlyhala",
"ToastDateTimeInvalidOrIncomplete": "Dátum a čas sú neplatné alebo neúplné",
"ToastDeleteFileFailed": "Odstránenie súboru zlyhalo",
diff --git a/client/strings/sl.json b/client/strings/sl.json
index adcccb80c..c88be4ba1 100644
--- a/client/strings/sl.json
+++ b/client/strings/sl.json
@@ -104,7 +104,7 @@
"ButtonStartM4BEncode": "Zaženi M4B prekodiranje",
"ButtonStartMetadataEmbed": "Začni vdelavo metapodatkov",
"ButtonStats": "Statistika",
- "ButtonSubmit": "Potrdi",
+ "ButtonSubmit": "Pošlji",
"ButtonTest": "Test",
"ButtonUnlinkOpenId": "Prekini povezavo OpenID",
"ButtonUpload": "Naloži",
@@ -383,7 +383,7 @@
"LabelFolders": "Mape",
"LabelFontBold": "Krepko",
"LabelFontBoldness": "Krepkost pisave",
- "LabelFontFamily": "Družina pisave",
+ "LabelFontFamily": "Družina pisav",
"LabelFontItalic": "Ležeče",
"LabelFontScale": "Merilo pisave",
"LabelFontStrikethrough": "Prečrtano",
@@ -436,9 +436,9 @@
"LabelLibraryFilterSublistEmpty": "Ne {0}",
"LabelLibraryItem": "Element knjižnice",
"LabelLibraryName": "Ime knjižnice",
- "LabelLibrarySortByProgress": "Napredek: Nazadnje posodobljen",
- "LabelLibrarySortByProgressFinished": "Napredej: Končano",
- "LabelLibrarySortByProgressStarted": "Napredek: Začeto",
+ "LabelLibrarySortByProgress": "Napredek: Zadnja posodobitev",
+ "LabelLibrarySortByProgressFinished": "Napredek: Končano",
+ "LabelLibrarySortByProgressStarted": "Napredek: Začelo se je",
"LabelLimit": "Omejitev",
"LabelLineSpacing": "Vrstični razmak",
"LabelListenAgain": "Poslušaj znova",
@@ -588,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Skeuomorfna oblika z lesenimi policami",
"LabelSettingsChromecastSupport": "Podpora za Chromecast",
"LabelSettingsDateFormat": "Oblika datuma",
- "LabelSettingsEnableWatcher": "Samodejno preglej knjižnice za spremembe",
- "LabelSettingsEnableWatcherForLibrary": "Samodejno preglej knjižnico za spremembe",
+ "LabelSettingsEnableWatcher": "Samodejno preišči knjižnice za spremembe",
+ "LabelSettingsEnableWatcherForLibrary": "Samodejno preišči knjižnico za spremembe",
"LabelSettingsEnableWatcherHelp": "Omogoča samodejno dodajanje/posodabljanje elementov, ko so zaznane spremembe datoteke. *Potreben je ponovni zagon strežnika",
"LabelSettingsEpubsAllowScriptedContent": "Dovoli skriptirano vsebino v epubih",
"LabelSettingsEpubsAllowScriptedContentHelp": "Dovoli datotekam epub izvajanje skript. Priporočljivo je, da to nastavitev pustite onemogočeno, razen če zaupate viru datotek epub.",
@@ -888,7 +888,7 @@
"MessageResetChaptersConfirm": "Ali ste prepričani, da želite ponastaviti poglavja in razveljaviti spremembe, ki ste jih naredili?",
"MessageRestoreBackupConfirm": "Ali ste prepričani, da želite obnoviti varnostno kopijo, ustvarjeno ob",
"MessageRestoreBackupWarning": "Obnovitev varnostne kopije bo prepisala celotno zbirko podatkov, ki se nahaja v /config, in zajema slike v /metadata/items in /metadata/authors.
Varnostne kopije ne spreminjajo nobenih datotek v mapah vaše knjižnice. Če ste omogočili nastavitve strežnika za shranjevanje naslovnic in metapodatkov v mapah vaše knjižnice, potem ti niso varnostno kopirani ali prepisani.
Vsi odjemalci, ki uporabljajo vaš strežnik, bodo samodejno osveženi.",
- "MessageScheduleLibraryScanNote": "Za večino uporabnikov je priporočljivo, da to funkcijo pustite onemogočeno in ohranite nastavitev pregledovalnika map omogočeno. Pregledovalnik map bo samodejno zaznal spremembe v mapah vaše knjižnice. Pregledovalnik map ne deluje za vse datotečne sisteme (na primer NFS), zato lahko namesto tega uporabite načrtovane preglede knjižnic.",
+ "MessageScheduleLibraryScanNote": "Za večino uporabnikov je priporočljivo, da to funkcijo pustijo onemogočeno in ohranijo nastavitev »Samodejno spremljaj knjižnico za spremembe« omogočeno – samodejno bo zaznala spremembe v mapah vaše knjižnice. Omogočite to funkcijo, če »Samodejno spremljaj knjižnico za spremembe« ne deluje za vaš datotečni sistem (kot je NFS).",
"MessageScheduleRunEveryWeekdayAtTime": "Zaženi vsakih {0} ob {1}",
"MessageSearchResultsFor": "Rezultati iskanja za",
"MessageSelected": "{0} izbrano",
@@ -1026,6 +1026,8 @@
"ToastCollectionItemsAddFailed": "Dodajanje elementov v zbirko ni uspelo",
"ToastCollectionRemoveSuccess": "Zbirka je bila odstranjena",
"ToastCollectionUpdateSuccess": "Zbirka je bila posodobljena",
+ "ToastConnectionNotAvailable": "Povezava ni na voljo. Poskusite znova pozneje",
+ "ToastCoverSearchFailed": "Iskanje naslovnice ni uspelo",
"ToastCoverUpdateFailed": "Posodobitev naslovnice ni uspela",
"ToastDateTimeInvalidOrIncomplete": "Datum in čas sta neveljavna ali nepopolna",
"ToastDeleteFileFailed": "Brisanje datoteke ni uspelo",
diff --git a/client/strings/sv.json b/client/strings/sv.json
index e41dd8e2b..fa0267e82 100644
--- a/client/strings/sv.json
+++ b/client/strings/sv.json
@@ -1,6 +1,6 @@
{
"ButtonAdd": "Lägg till",
- "ButtonAddApiKey": "Addera API-nyckel",
+ "ButtonAddApiKey": "Lägg till API-nyckel",
"ButtonAddChapters": "Lägg till kapitel",
"ButtonAddDevice": "Lägg till enhet",
"ButtonAddLibrary": "Lägg till bibliotek",
@@ -48,7 +48,7 @@
"ButtonLogout": "Logga ut",
"ButtonLookup": "Sök",
"ButtonManageTracks": "Hantera spår",
- "ButtonMapChapterTitles": "Karta kapitelrubriker",
+ "ButtonMapChapterTitles": "Mappa kapitelrubriker",
"ButtonMatchAllAuthors": "Matcha alla författare",
"ButtonMatchBooks": "Matcha böcker",
"ButtonNevermind": "Glöm det",
@@ -104,7 +104,7 @@
"ButtonStartM4BEncode": "Starta M4B-omkodning",
"ButtonStartMetadataEmbed": "Infoga metadata",
"ButtonStats": "Statistik",
- "ButtonSubmit": "Spara",
+ "ButtonSubmit": "Skicka",
"ButtonTest": "Testa",
"ButtonUnlinkOpenId": "Koppla ifrån OpenID",
"ButtonUpload": "Ladda upp",
@@ -123,7 +123,7 @@
"HeaderAdvanced": "Avancerad",
"HeaderApiKeys": "API-nyckel",
"HeaderAppriseNotificationSettings": "Inställningar av meddelanden med Apprise",
- "HeaderAudioTracks": "Ljudfiler",
+ "HeaderAudioTracks": "Ljudspår",
"HeaderAudiobookTools": "Hantering av ljudboksfiler",
"HeaderAuthentication": "Autentisering",
"HeaderBackups": "Säkerhetskopior",
@@ -202,7 +202,7 @@
"HeaderSettingsScanner": "Skanner",
"HeaderSettingsSecurity": "Säkerhet",
"HeaderSettingsWebClient": "Webklient",
- "HeaderSleepTimer": "Timer för att sova",
+ "HeaderSleepTimer": "Insomningstimer",
"HeaderStatsLargestItems": "Största objekten",
"HeaderStatsLongestItems": "Längsta objekten (timmar)",
"HeaderStatsMinutesListeningChart": "Minuters lyssning (senaste 7 dagarna)",
@@ -231,10 +231,10 @@
"LabelActivity": "Aktivitet",
"LabelAddToCollection": "Lägg till i en samling",
"LabelAddToCollectionBatch": "Lägg till {0} böcker i samlingen",
- "LabelAddToPlaylist": "Lägg till i en spellista",
+ "LabelAddToPlaylist": "Lägg till i spellista",
"LabelAddToPlaylistBatch": "Lägg till {0} objekt i Spellistan",
"LabelAddedAt": "Datum adderad",
- "LabelAddedDate": "Adderad {0}",
+ "LabelAddedDate": "Tillagd {0}",
"LabelAdminUsersOnly": "Endast administratörer",
"LabelAll": "Alla",
"LabelAllEpisodesDownloaded": "Alla avsnitt är nedladdade",
@@ -363,14 +363,14 @@
"LabelExpiresAt": "Gäller till och med",
"LabelExpiresInSeconds": "Upphör om (sekunder)",
"LabelExpiresNever": "Aldrig",
- "LabelExplicit": "Bestämd",
+ "LabelExplicit": "Vuxeninnehåll",
"LabelExplicitChecked": "Explicit version (markerad)",
"LabelExplicitUnchecked": "Ej Explicit version (ej markerad)",
"LabelExportOPML": "Exportera OPML-information",
"LabelFeedURL": "URL-adress för flödet",
"LabelFetchingMetadata": "Hämtar metadata",
"LabelFile": "Fil",
- "LabelFileBirthtime": "Tidpunkt, adderad",
+ "LabelFileBirthtime": "Tidpunkt, tillagd",
"LabelFileBornDate": "Skapad {0}",
"LabelFileModified": "Tidpunkt, ändrad",
"LabelFileModifiedDate": "Ändrad {0}",
@@ -385,13 +385,13 @@
"LabelFontBoldness": "Fetstil",
"LabelFontFamily": "Typsnittsfamilj",
"LabelFontItalic": "Kursiv",
- "LabelFontScale": "Skala på typsnitt",
+ "LabelFontScale": "Storlek på typsnitt",
"LabelFontStrikethrough": "Genomstruken",
"LabelFormat": "Format",
"LabelFull": "Komplett",
"LabelGenre": "Kategori",
"LabelGenres": "Kategorier",
- "LabelHardDeleteFile": "Hård radering av fil",
+ "LabelHardDeleteFile": "Permanent radering av fil",
"LabelHasEbook": "Har e-bok",
"LabelHasSupplementaryEbook": "Har kompletterande e-bok",
"LabelHideSubtitles": "Dölj underrubriker",
@@ -436,9 +436,9 @@
"LabelLibraryFilterSublistEmpty": "Ingen {0}",
"LabelLibraryItem": "Objekt",
"LabelLibraryName": "Biblioteksnamn",
- "LabelLibrarySortByProgress": "Framsteg uppdaterat",
- "LabelLibrarySortByProgressFinished": "Avslutsdag",
- "LabelLibrarySortByProgressStarted": "Startdag",
+ "LabelLibrarySortByProgress": "Status: Senast uppdaterad",
+ "LabelLibrarySortByProgressFinished": "Status: Avslutad",
+ "LabelLibrarySortByProgressStarted": "Status: Startad",
"LabelLimit": "Begränsning",
"LabelLineSpacing": "Radavstånd",
"LabelListenAgain": "Lyssna igen",
@@ -588,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Bakgrund med ett utseende liknande en bokhylla i trä",
"LabelSettingsChromecastSupport": "Stöd för Chromecast",
"LabelSettingsDateFormat": "Datumformat",
- "LabelSettingsEnableWatcher": "Upptäck automatiskt förändringar i biblioteket",
- "LabelSettingsEnableWatcherForLibrary": "Upptäck automatiskt förändringar i biblioteket",
+ "LabelSettingsEnableWatcher": "Bevaka automatiskt förändringar i biblioteken",
+ "LabelSettingsEnableWatcherForLibrary": "Bevaka biblioteket automatiskt efter ändringar",
"LabelSettingsEnableWatcherHelp": "Aktiverar automatik att upptäcka när objekt
adderas, uppdateras eller raderas.
OBS: Kräver en omstart av servern",
"LabelSettingsEpubsAllowScriptedContent": "Tillåt e-böcker i epubs-format som innehåller script",
"LabelSettingsEpubsAllowScriptedContentHelp": "Tillåt att epub-filer får innehålla script.
Det rekommenderas att denna inställning är
avstängd när du inte litar på källan för epub-filerna.",
@@ -629,7 +629,7 @@
"LabelShowSeconds": "Visa i sekunder",
"LabelShowSubtitles": "Visa underrubriker",
"LabelSize": "Storlek",
- "LabelSleepTimer": "Sovtimer",
+ "LabelSleepTimer": "Insomningstimer",
"LabelSlug": "Kortnamn",
"LabelSortAscending": "Stigande",
"LabelSortDescending": "Fallande",
@@ -724,6 +724,7 @@
"LabelViewQueue": "Visa spellista",
"LabelVolume": "Volym",
"LabelWebRedirectURLsDescription": "Auktorisera dessa URLer i din OAuth-leverantör för att tillåta omdirigering tillbaka till webbappen efter inloggning:",
+ "LabelWebRedirectURLsSubfolder": "Undermappar för omdirigeringslänkar",
"LabelWeekdaysToRun": "Veckodagar att köra skanning",
"LabelXBooks": "{0} böcker",
"LabelXItems": "{0} objekt",
@@ -752,6 +753,7 @@
"MessageBookshelfNoResultsForFilter": "Inga resultat för filter \"{0}: {1}\"",
"MessageBookshelfNoResultsForQuery": "Sökningen gav inget resultat",
"MessageBookshelfNoSeries": "Du har inga serier",
+ "MessageBulkChapterPattern": "Hur många kapitel vill du lägga till med detta numreringsmönster?",
"MessageChapterEndIsAfter": "Kapitelns slut är efter din ljudboks slut",
"MessageChapterErrorFirstNotZero": "Första kapitlet måste börja vid 0",
"MessageChapterErrorStartGteDuration": "Ogiltig starttid, måste vara mindre än ljudbokens längd",
@@ -778,6 +780,7 @@
"MessageConfirmMarkItemNotFinished": "Är du säker på att du vill markera \"{0}\" som ej avslutad?",
"MessageConfirmMarkSeriesFinished": "Är du säker på att du vill markera alla böcker i denna serie som avslutade?",
"MessageConfirmMarkSeriesNotFinished": "Är du säker på att du vill markera alla böcker i denna serie som ej avslutade?",
+ "MessageConfirmNotificationTestTrigger": "Trigga denna avisering med testdata?",
"MessageConfirmPurgeCache": "När du rensar cashen kommer katalogen /metadata/cache att raderas.
Är du säker på att du vill radera katalogen?",
"MessageConfirmPurgeItemsCache": "När du rensar cashen för objekten kommer katalogen /metadata/cache/items att raderas.
Är du säker på att du vill radera katalogen?",
"MessageConfirmQuickEmbed": "VARNING! När du infogar metadata i dina ljudfiler kommer INGEN SÄKERHETSKOPIA av filerna att göras. Se därför till att först säkerhetskopiera ljudfilerna.
Vill du fortsätta?",
@@ -787,6 +790,7 @@
"MessageConfirmRemoveAuthor": "Är du säker på att du vill ta bort författaren \"{0}\"?",
"MessageConfirmRemoveCollection": "Är du säker på att du vill ta bort samlingen \"{0}\"?",
"MessageConfirmRemoveEpisode": "Är du säker på att du vill radera avsnittet \"{0}\"?",
+ "MessageConfirmRemoveEpisodeNote": "OBSERERA: Detta tar inte bort ljudfilen om inte \"Permanent radering av fil\" är aktiverad",
"MessageConfirmRemoveEpisodes": "Är du säker på att du vill radera {0} avsnitt?",
"MessageConfirmRemoveListeningSessions": "Är du säker på att du vill radera {0} lyssningstillfällen?",
"MessageConfirmRemoveMetadataFiles": "Är du säker på att du vill radera filerna 'metadata.{0}' i alla mappar i ditt bibliotek?",
@@ -804,16 +808,20 @@
"MessageDaysListenedInTheLastYear": "{0} dagars lyssnande det senaste året",
"MessageDownloadingEpisode": "Laddar ner avsnitt",
"MessageDragFilesIntoTrackOrder": "Ändra ordningen genom att klicka och dra filerna till rätt plats",
+ "MessageEmbedFailed": "Inbäddning misslyckades!",
"MessageEmbedFinished": "Inbäddning genomförd!",
+ "MessageEmbedQueue": "Köad för inbäddning av metadata plats ({0} i kön)",
"MessageEpisodesQueuedForDownload": "{0} avsnitt i kö för nedladdning",
"MessageEreaderDevices": "För att säkerställa överföring av e-böcker kan du bli tvungen
att addera ovanstående e-postadress som godkänd avsändare
för varje enhet angiven nedan.",
"MessageFeedURLWillBe": "Flödes-URL kommer att vara {0}",
"MessageFetching": "Hämtar...",
"MessageForceReScanDescription": "kommer att göra en omgångssökning av alla filer som en färsk sökning. ID3-taggar för ljudfiler, OPF-filer och textfiler kommer att sökas som nya.",
+ "MessageHeatmapListeningTimeTooltip": "{0} lyssnar på {1}",
+ "MessageHeatmapNoListeningSessions": "Inga lyssningssessioner på {0}",
"MessageImportantNotice": "Viktig meddelande!",
"MessageInsertChapterBelow": "Infoga kapitel nedanför",
"MessageInvalidAsin": "Felaktig ASIN-kod",
- "MessageItemsSelected": "{0} objekt markerade",
+ "MessageItemsSelected": "{0} objekt valda",
"MessageItemsUpdated": "{0} objekt uppdaterade",
"MessageJoinUsOn": "Anslut dig till oss på",
"MessageLoading": "Laddar...",
@@ -849,6 +857,7 @@
"MessageNoLogs": "Inga loggningsinformation finns",
"MessageNoMediaProgress": "Ingen medieförlopp",
"MessageNoNotifications": "Inga aviseringar",
+ "MessageNoPodcastFeed": "Felaktig podcast: ingen ström",
"MessageNoPodcastsFound": "Inga podcasts hittade",
"MessageNoResults": "Inga resultat",
"MessageNoSearchResultsFor": "Inga sökresultat för \"{0}\"",
@@ -859,6 +868,7 @@
"MessageNoUserPlaylists": "Du har inga spellistor",
"MessageNoUserPlaylistsHelp": "Spellistor är privata. Endast den användare som skapat listan kan se den.",
"MessageNotYetImplemented": "Ännu inte implementerad",
+ "MessageOpmlPreviewNote": "Obs: Detta är en förhandsvisning av den analyserade OPML-filen. Den faktiska podcasttiteln kommer att hämtas från RSS-flödet.",
"MessageOr": "eller",
"MessagePauseChapter": "Pausa kapiteluppspelning",
"MessagePlayChapter": "Lyssna på kapitlets början",
@@ -878,7 +888,7 @@
"MessageResetChaptersConfirm": "Är du säker på att du vill återställa alla kapitel och ångra de ändringarna du gjort?",
"MessageRestoreBackupConfirm": "Är du säker på att du vill läsa in säkerhetskopian som skapades den",
"MessageRestoreBackupWarning": "Att återställa en säkerhetskopia kommer att skriva över hela databasen som finns i /config och omslagsbilder i /metadata/items & /metadata/authors.
Säkerhetskopior ändrar inte några filer i dina biblioteksmappar. Om du har aktiverat serverinställningar för att lagra omslagskonst och metadata i dina biblioteksmappar säkerhetskopieras eller skrivs de inte över.
Alla klienter som använder din server kommer att uppdateras automatiskt.",
- "MessageScheduleLibraryScanNote": "För de flesta användare rekommenderas att denna funktion ej aktiveras. Istället bör funktionen som automatisk upptäcker ändringar i biblioteket vara aktiverad. För vissa filsystem (som t.ex. NFS) fungerar inte denna funktion. Då kan schemalagda skanningar av biblioteken användas istället.",
+ "MessageScheduleLibraryScanNote": "För de flesta användare rekommenderas det att låta den här funktionen vara inaktiverad och att inställningen \"Bevaka biblioteket automatiskt efter ändringar\" är aktiverad – den kommer automatiskt att upptäcka ändringar i dina biblioteksmappar. Aktivera den här funktionen om \"Bevaka biblioteket automatiskt efter ändringar\" inte fungerar för ditt filsystem (som NFS).",
"MessageScheduleRunEveryWeekdayAtTime": "Startar varje {0} klockan {1}",
"MessageSearchResultsFor": "Sökresultat för",
"MessageSelected": "{0} valda",
@@ -887,12 +897,14 @@
"MessageSetChaptersFromTracksDescription": "Använd varje ljudfil som ett kapitel och ljudfilens namn som kapitlets rubrik",
"MessageShareExpirationWillBe": "Giltig till kommer att bli {0}",
"MessageShareExpiresIn": "Upphör om {0}",
+ "MessageShareURLWillBe": "Delningslänken kommer att vara {0}",
"MessageStartPlaybackAtTime": "Starta uppspelning av \"{0}\" från tidpunkt {1}?",
"MessageTaskAudioFileNotWritable": "Det går inte att skriva till ljudfilen \"{0}\"",
"MessageTaskCanceledByUser": "Uppgiften avslutades av användaren",
"MessageTaskDownloadingEpisodeDescription": "Laddar ner avsnitt \"{0}\"",
"MessageTaskEmbeddingMetadata": "Infogar metadata",
"MessageTaskEmbeddingMetadataDescription": "Infogar metadata i ljudboken \"{0}\"",
+ "MessageTaskEncodingM4b": "Kodar M4B",
"MessageTaskEncodingM4bDescription": "Omkodning av ljudbok \"{0}\" till en M4B-fil",
"MessageTaskFailed": "Misslyckades",
"MessageTaskFailedToBackupAudioFile": "Misslyckades med att göra backup på ljudfil \"{0}\"",
@@ -903,8 +915,11 @@
"MessageTaskFailedToWriteMetadataFile": "Misslyckades med att skapa filen med metadata",
"MessageTaskMatchingBooksInLibrary": "Matchar böcker i biblioteket \"{0}\"",
"MessageTaskNoFilesToScan": "Inga filer finns tillgängliga för skanning",
+ "MessageTaskOpmlImport": "OPML-import",
"MessageTaskOpmlImportDescription": "Skapar podcasts från {0} RSS-flöden",
+ "MessageTaskOpmlImportFeed": "OPML importflöde",
"MessageTaskOpmlImportFeedDescription": "Importerar RSS-flödet \"{0}\"",
+ "MessageTaskOpmlImportFeedFailed": "Det gick inte att hämta poddflödet",
"MessageTaskOpmlImportFeedPodcastDescription": "Skapar podcast \"{0}\"",
"MessageTaskOpmlImportFeedPodcastExists": "En podcast finns redan med den adressen",
"MessageTaskOpmlImportFeedPodcastFailed": "Misslyckades med att skapa podcast",
@@ -987,6 +1002,9 @@
"ToastBackupUploadFailed": "Det gick inte att ladda upp säkerhetskopian",
"ToastBackupUploadSuccess": "Säkerhetskopian har laddats upp",
"ToastBatchApplyDetailsToItemsSuccess": "Informationen har adderats till alla objekt",
+ "ToastBatchDeleteFailed": "Det gick inte att radera batch",
+ "ToastBatchDeleteSuccess": "Batch borttagning lyckades",
+ "ToastBatchQuickMatchFailed": "Snabbmatchning av batch misslyckades!",
"ToastBatchQuickMatchStarted": "Snabbmatchning av {0} böcker har påbörjats!",
"ToastBatchUpdateFailed": "Batchuppdateringen misslyckades",
"ToastBatchUpdateSuccess": "Batchuppdateringen lyckades",
@@ -1024,6 +1042,7 @@
"ToastEpisodeDownloadQueueClearFailed": "Misslyckades med att tömma kön",
"ToastEpisodeDownloadQueueClearSuccess": "Kö för nedladdning av avsnitt har tömts",
"ToastEpisodeUpdateSuccess": "{0} avsnitt uppdaterades",
+ "ToastErrorCannotShare": "Kan inte dela direkt på den här enheten",
"ToastFailedToCreate": "Misslyckades med att addera",
"ToastFailedToDelete": "Misslyckades med att radera",
"ToastFailedToLoadData": "Misslyckades med att ladda data",
@@ -1055,6 +1074,7 @@
"ToastMetadataFilesRemovedNoneFound": "Inga 'metadata.{0}' filer hittades i biblioteket",
"ToastMetadataFilesRemovedNoneRemoved": "Inga 'metadata.{0}' filer raderades",
"ToastMetadataFilesRemovedSuccess": "{0} 'metadata.{1}' raderades",
+ "ToastMustHaveAtLeastOnePath": "Måste ha minst en sökväg",
"ToastNameEmailRequired": "Ett namn och en e-postadress måste anges",
"ToastNameRequired": "Ett namn måste anges",
"ToastNewApiKeyUserError": "En användare måste väljas",
@@ -1071,8 +1091,10 @@
"ToastNotificationCreateFailed": "Misslyckades med att skapa meddelandet",
"ToastNotificationDeleteFailed": "Misslyckades med att radera meddelandet",
"ToastNotificationFailedMaximum": "Maximalt antal misslyckade försök måste vara större än eller lika med 0",
+ "ToastNotificationQueueMaximum": "Maximala antalet aviseringsköer måste vara >= 0",
"ToastNotificationSettingsUpdateSuccess": "Inställningarna för meddelanden har ändrats",
"ToastNotificationTestTriggerFailed": "Misslyckades med att skicka testmeddelandet",
+ "ToastNotificationTestTriggerSuccess": "Triggade testavisering",
"ToastNotificationUpdateSuccess": "Meddelandet har uppdaterats",
"ToastPlaylistCreateFailed": "Det gick inte att skapa spellistan",
"ToastPlaylistCreateSuccess": "Spellistan skapad",
@@ -1081,8 +1103,10 @@
"ToastPodcastCreateFailed": "Misslyckades med att skapa podcasten",
"ToastPodcastCreateSuccess": "Podcasten skapades framgångsrikt",
"ToastPodcastEpisodeUpdated": "Avsnittet har uppdaterats",
+ "ToastPodcastGetFeedFailed": "Det gick inte att hämta poddflödet",
"ToastPodcastNoEpisodesInFeed": "Inga avsnitt finns i RSS-flödet",
"ToastPodcastNoRssFeed": "Denna podcast har ingen RSS-flöde",
+ "ToastProgressIsNotBeingSynced": "Förloppet synkroniseras inte, starta om uppspelningen",
"ToastProviderCreatedFailed": "Misslyckades med att addera en källa",
"ToastProviderCreatedSuccess": "En ny källa har adderats",
"ToastProviderNameAndUrlRequired": "Ett namn och en URL-adress krävs",
@@ -1107,9 +1131,12 @@
"ToastSeriesUpdateFailed": "Misslyckades med att uppdatera serien",
"ToastSeriesUpdateSuccess": "Uppdateringen av serierna lyckades",
"ToastServerSettingsUpdateSuccess": "Inställningarna för servern har uppdaterats",
+ "ToastSessionCloseFailed": "Misslyckades med att avsluta sessionen",
"ToastSessionDeleteFailed": "Misslyckades med att ta bort sessionen",
"ToastSessionDeleteSuccess": "Sessionen borttagen",
"ToastSleepTimerDone": "Timer har stängt av lyssning. Sov gott... zZzzZz",
+ "ToastSlugMustChange": "Slug innehåller ogiltiga tecken",
+ "ToastSlugRequired": "Slug krävs",
"ToastSocketConnected": "Socket ansluten",
"ToastSocketDisconnected": "Socket frånkopplad",
"ToastSocketFailedToConnect": "Socket misslyckades med att ansluta",
@@ -1120,6 +1147,7 @@
"ToastUnlinkOpenIdFailed": "Misslyckades med att koppla bort användaren från OpenID",
"ToastUnlinkOpenIdSuccess": "Användaren har kopplats bort från OpenID",
"ToastUploaderFilepathExistsError": "En fil med namnet \"{0}\" finns redan på servern",
+ "ToastUploaderItemExistsInSubdirectoryError": "Objektet \"{0}\" använder en underkatalog till uppladdningssökvägen.",
"ToastUserDeleteFailed": "Misslyckades med att ta bort användaren",
"ToastUserDeleteSuccess": "Användaren borttagen",
"ToastUserPasswordChangeSuccess": "Lösenordet har ändrats",
diff --git a/client/strings/tr.json b/client/strings/tr.json
index af64a618d..770521cb8 100644
--- a/client/strings/tr.json
+++ b/client/strings/tr.json
@@ -275,7 +275,7 @@
"LabelBonus": "Bonus",
"LabelBooks": "Kitaplar",
"LabelButtonText": "Buton Metni",
- "LabelByAuthor": "Yazar: {0}",
+ "LabelByAuthor": "{0} tarafından",
"LabelChangePassword": "Şifreyi Değiştir",
"LabelChannels": "Kanallar",
"LabelChapterCount": "{0} Bölüm",
@@ -383,7 +383,7 @@
"LabelFolders": "Klasörler",
"LabelFontBold": "Kalın",
"LabelFontBoldness": "Yazı Tipi Kalınlığı",
- "LabelFontFamily": "Yazı Tipi Ailesi",
+ "LabelFontFamily": "Yazı tipi ailesi",
"LabelFontItalic": "İtalik",
"LabelFontScale": "Yazı Tipi Ölçeği",
"LabelFontStrikethrough": "Üstü Çizili",
@@ -588,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Ahşap raflı skeuomorfik tasarım",
"LabelSettingsChromecastSupport": "Chromecast desteği",
"LabelSettingsDateFormat": "Tarih Formatı",
- "LabelSettingsEnableWatcher": "Değişiklikler için kütüphaneleri otomatik olarak tara",
- "LabelSettingsEnableWatcherForLibrary": "Değişiklikler için kütüphaneyi otomatik olarak tara",
+ "LabelSettingsEnableWatcher": "Kütüphanelerdeki değişiklikleri otomatik olarak izle",
+ "LabelSettingsEnableWatcherForLibrary": "Kütüphanedeki değişiklikleri otomatik olarak izle",
"LabelSettingsEnableWatcherHelp": "Dosya değişiklikleri algılandığında öğelerin otomatik olarak eklenmesini/güncellenmesini sağlar. *Sunucunun yeniden başlatılmasını gerektirir",
"LabelSettingsEpubsAllowScriptedContent": "Epub'larda betiklenmiş içeriğe izin ver",
"LabelSettingsEpubsAllowScriptedContentHelp": "Epub dosyalarının betik çalıştırmasına izin verin. Epub dosyalarının kaynağına güvenmiyorsanız bu ayarı devre dışı bırakmanız önerilir.",
@@ -888,7 +888,7 @@
"MessageResetChaptersConfirm": "Bölümleri sıfırlamak ve yaptığınız değişiklikleri geri almak istediğinizden emin misiniz?",
"MessageRestoreBackupConfirm": "Şu tarihte oluşturulan yedeği geri yüklemek istediğinizden emin misiniz",
"MessageRestoreBackupWarning": "Bir yedeği geri yüklemek, /config konumundaki tüm veritabanının ve /metadata/items & /metadata/authors içindeki kapak resimlerinin üzerine yazacaktır.
Yedekler, kütüphane klasörlerinizdeki hiçbir dosyayı değiştirmez. Sunucu ayarlarını kütüphane klasörlerinizde kapak resmi ve üst veri saklamak için etkinleştirdiyseniz, bunlar yedeklenmez veya üzerine yazılmaz.
Sunucunuzu kullanan tüm istemciler otomatik olarak yenilenecektir.",
- "MessageScheduleLibraryScanNote": "Çoğu kullanıcı için, bu özelliği devre dışı bırakıp klasör izleyici ayarını etkin tutmaları önerilir. Klasör izleyici, kütüphane klasörlerinizdeki değişiklikleri otomatik olarak algılayacaktır. Klasör izleyici her dosya sistemi için (NFS gibi) çalışmaz, bu nedenle bunun yerine zamanlanmış kütüphane taramaları kullanılabilir.",
+ "MessageScheduleLibraryScanNote": "Çoğu kullanıcı için bu ayarı pasif bırakması ve \"Kütüphanedeki değişiklikleri otomatik olarak izle\" seçeneğini aktif etmesi önerilir. O seçenek kütüphane dizinlerindeki herhangi bir değişikliği otomatik olarak tespit edecektir. Eğer dosya sisteminiz \"Kütüphanedeki değişiklikleri otomatik olarak izle\" yöntemini desteklemiyorsa (örn; NFS dosya sistemi) bu özelliği aktif edebilirsiniz.",
"MessageScheduleRunEveryWeekdayAtTime": "Her {0} günü saat {1}'de çalıştır",
"MessageSearchResultsFor": "Arama sonuçları",
"MessageSelected": "{0} seçildi",
diff --git a/client/strings/uk.json b/client/strings/uk.json
index fbb736f6d..1db23322f 100644
--- a/client/strings/uk.json
+++ b/client/strings/uk.json
@@ -383,7 +383,7 @@
"LabelFolders": "Теки",
"LabelFontBold": "Жирний",
"LabelFontBoldness": "Товщина шрифту",
- "LabelFontFamily": "Гарнітура",
+ "LabelFontFamily": "Сімейство шрифтів",
"LabelFontItalic": "Курсив",
"LabelFontScale": "Розмір шрифту",
"LabelFontStrikethrough": "Закреслений",
@@ -588,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "Імітує вигляд дерев'яних полиць",
"LabelSettingsChromecastSupport": "Підтримка Chromecast",
"LabelSettingsDateFormat": "Формат дати",
- "LabelSettingsEnableWatcher": "Автоматично сканувати бібліотеки на наявність змін",
- "LabelSettingsEnableWatcherForLibrary": "Автоматично сканувати бібліотеку на наявність змін",
+ "LabelSettingsEnableWatcher": "Автоматично відстежувати зміни в бібліотеках",
+ "LabelSettingsEnableWatcherForLibrary": "Автоматично відстежувати зміни в бібліотеці",
"LabelSettingsEnableWatcherHelp": "Вмикає автоматичне додавання/оновлення елементів, коли спостерігаються зміни файлів. *Потребує перезавантаження сервера",
"LabelSettingsEpubsAllowScriptedContent": "Дозволити JavaScript-вміст у epub",
"LabelSettingsEpubsAllowScriptedContentHelp": "Дозволяти epub-файлам виконувати код. Вмикайте цей параметр лише якщо ви довіряєте джерелу epub-файлів.",
@@ -888,7 +888,7 @@
"MessageResetChaptersConfirm": "Ви впевнені, що хочете скинути глави та скасувати внесені зміни?",
"MessageRestoreBackupConfirm": "Ви впевнені, що хочете відновити резервну копію, створену",
"MessageRestoreBackupWarning": "Відновлення резервної копії перезапише всю базу даних у /config і зображення обкладинок у /metadata/items та /metadata/authors.
Резервні копії не змінюють файли у теках бібліотеки. Якщо у налаштуваннях сервера увімкнено збереження обкладинок і метаданих у теках бібліотеки, вони не створюються під час резервного копіювання і не перезаписуються.
Всі клієнти, що користуються вашим сервером, будуть автоматично оновлені.",
- "MessageScheduleLibraryScanNote": "Для більшості користувачів рекомендується залишити цю функцію вимкненою та залишити параметр перегляду папок увімкненим. Засіб спостереження за папками автоматично виявить зміни в папках вашої бібліотеки. Засіб спостереження за папками не працює для кожної файлової системи (наприклад, NFS), тому замість нього можна використовувати сканування бібліотек за розкладом.",
+ "MessageScheduleLibraryScanNote": "Для більшості користувачів рекомендується відключити цю функцію та підтримувати налаштування \"Автоматично переглядати бібліотеку для змін\" - вона автоматично виявить зміни в ваших папках бібліотеки. Звісніть цю функцію, якщо для вашої файлової системи (наприклад, NFS) не працює \"Автоматично переглядати бібліотеку.",
"MessageScheduleRunEveryWeekdayAtTime": "Запуск кожні {0} о {1}",
"MessageSearchResultsFor": "Результати пошуку для",
"MessageSelected": "Вибрано: {0}",
diff --git a/client/strings/zh-cn.json b/client/strings/zh-cn.json
index 361a74613..14c70cb10 100644
--- a/client/strings/zh-cn.json
+++ b/client/strings/zh-cn.json
@@ -275,7 +275,7 @@
"LabelBonus": "额外",
"LabelBooks": "图书",
"LabelButtonText": "按钮文本",
- "LabelByAuthor": "由 {0}",
+ "LabelByAuthor": "作者: {0}",
"LabelChangePassword": "修改密码",
"LabelChannels": "声道",
"LabelChapterCount": "{0} 章节",
@@ -383,7 +383,7 @@
"LabelFolders": "文件夹",
"LabelFontBold": "加粗",
"LabelFontBoldness": "字体粗细",
- "LabelFontFamily": "字体系列",
+ "LabelFontFamily": "字体",
"LabelFontItalic": "斜体",
"LabelFontScale": "字体比例",
"LabelFontStrikethrough": "删除线",
@@ -436,9 +436,9 @@
"LabelLibraryFilterSublistEmpty": "没有 {0}",
"LabelLibraryItem": "媒体库项目",
"LabelLibraryName": "媒体库名称",
- "LabelLibrarySortByProgress": "收听进度: 上次收听时间",
- "LabelLibrarySortByProgressFinished": "收听进度: 已完成的",
- "LabelLibrarySortByProgressStarted": "收听进度: 已开始的",
+ "LabelLibrarySortByProgress": "进度: 上次更新",
+ "LabelLibrarySortByProgressFinished": "进度: 已完成",
+ "LabelLibrarySortByProgressStarted": "进度: 已开始",
"LabelLimit": "限制",
"LabelLineSpacing": "行间距",
"LabelListenAgain": "再次收听",
@@ -588,8 +588,8 @@
"LabelSettingsBookshelfViewHelp": "带有木架子的拟物化设计",
"LabelSettingsChromecastSupport": "Chromecast 支持",
"LabelSettingsDateFormat": "日期格式",
- "LabelSettingsEnableWatcher": "自动扫描库以查找更改",
- "LabelSettingsEnableWatcherForLibrary": "自动扫描库以查找更改",
+ "LabelSettingsEnableWatcher": "自动检测媒体库变化",
+ "LabelSettingsEnableWatcherForLibrary": "自动检测媒体库变化",
"LabelSettingsEnableWatcherHelp": "当检测到文件更改时, 启用项目的自动添加/更新. *需要重新启动服务器",
"LabelSettingsEpubsAllowScriptedContent": "允许 epubs 中包含脚本内容",
"LabelSettingsEpubsAllowScriptedContentHelp": "允许 epub 文件执行脚本. 建议将此设置保持禁用, 除非你信任 epub 文件的来源.",
@@ -888,7 +888,7 @@
"MessageResetChaptersConfirm": "你确定要重置章节并撤消你所做的更改吗?",
"MessageRestoreBackupConfirm": "你确定要恢复创建的这个备份",
"MessageRestoreBackupWarning": "恢复备份将覆盖位于 /config 的整个数据库并覆盖 /metadata/items & /metadata/authors 中的图像.
备份不会修改媒体库文件夹中的任何文件. 如果你已启用服务器设置将封面和元数据存储在库文件夹中,则不会备份或覆盖这些内容.
将自动刷新使用服务器的所有客户端.",
- "MessageScheduleLibraryScanNote": "对于大多数用户, 建议禁用此功能并保持文件夹监视程序设置启用. 文件夹监视程序将自动检测库文件夹中的更改. 文件夹监视程序不适用于每个文件系统 (如 NFS), 因此可以使用计划库扫描.",
+ "MessageScheduleLibraryScanNote": "对于大多数用户, 建议保持此功能关闭并启用 \"自动检测媒体库变化\" 选项, 它会自动检测媒体库文件夹中的更改. 如果 \"自动检测媒体库变化\" 在你的文件系统 (例如 NFS) 上无法正常工作, 请启用此功能.",
"MessageScheduleRunEveryWeekdayAtTime": "每 {0} 的 {1} 执行",
"MessageSearchResultsFor": "搜索结果",
"MessageSelected": "{0} 已选择",
diff --git a/custom-metadata-provider-specification.yaml b/custom-metadata-provider-specification.yaml
index d85e4a895..0b68c74d9 100644
--- a/custom-metadata-provider-specification.yaml
+++ b/custom-metadata-provider-specification.yaml
@@ -127,7 +127,7 @@ components:
duration:
type: integer
format: int64
- description: Duration in seconds
+ description: Duration in minutes
rating:
type: number
format: float
diff --git a/package-lock.json b/package-lock.json
index 8eea3abbd..d8cc43bc8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "audiobookshelf",
- "version": "2.30.0",
+ "version": "2.33.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "audiobookshelf",
- "version": "2.30.0",
+ "version": "2.33.1",
"license": "GPL-3.0",
"dependencies": {
"axios": "^0.27.2",
diff --git a/package.json b/package.json
index 0b4ee4137..d44df1165 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf",
- "version": "2.30.0",
+ "version": "2.33.1",
"buildNumber": 1,
"description": "Self-hosted audiobook and podcast server",
"main": "index.js",
diff --git a/server/auth/TokenManager.js b/server/auth/TokenManager.js
index faa6774a3..5efeb7a64 100644
--- a/server/auth/TokenManager.js
+++ b/server/auth/TokenManager.js
@@ -234,6 +234,13 @@ class TokenManager {
}
const user = await Database.userModel.getUserById(apiKey.userId)
+
+ if (!user?.isActive) {
+ // deny login
+ done(null, null)
+ return
+ }
+
done(null, user)
} else {
// JWT based authentication
diff --git a/server/controllers/AuthorController.js b/server/controllers/AuthorController.js
index 50eeda31a..82ed3e50a 100644
--- a/server/controllers/AuthorController.js
+++ b/server/controllers/AuthorController.js
@@ -113,7 +113,7 @@ class AuthorController {
payload.lastFirst = Database.authorModel.getLastFirst(payload.name)
}
- // Check if author name matches another author and merge the authors
+ // Check if author name matches another author in the same library and merge the authors
let existingAuthor = null
if (authorNameUpdate) {
existingAuthor = await Database.authorModel.findOne({
@@ -121,7 +121,8 @@ class AuthorController {
id: {
[sequelize.Op.not]: req.author.id
},
- name: payload.name
+ name: payload.name,
+ libraryId: req.author.libraryId
}
})
}
diff --git a/server/controllers/CollectionController.js b/server/controllers/CollectionController.js
index 475adfe0f..1476b0f81 100644
--- a/server/controllers/CollectionController.js
+++ b/server/controllers/CollectionController.js
@@ -3,6 +3,7 @@ const Sequelize = require('sequelize')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const Database = require('../Database')
+const htmlSanitizer = require('../utils/htmlSanitizer')
const RssFeedManager = require('../managers/RssFeedManager')
@@ -31,8 +32,10 @@ class CollectionController {
async create(req, res) {
const reqBody = req.body || {}
+ const nameCleaned = htmlSanitizer.stripAllTags(reqBody.name)
+
// Validation
- if (!reqBody.name || !reqBody.libraryId) {
+ if (!nameCleaned || !reqBody.libraryId) {
return res.status(400).send('Invalid collection data')
}
if (reqBody.description && typeof reqBody.description !== 'string') {
@@ -65,7 +68,7 @@ class CollectionController {
newCollection = await Database.collectionModel.create(
{
libraryId: reqBody.libraryId,
- name: reqBody.name,
+ name: nameCleaned,
description: reqBody.description || null
},
{ transaction }
@@ -145,9 +148,12 @@ class CollectionController {
collectionUpdatePayload.description = req.body.description
wasUpdated = true
}
- if (req.body.name !== undefined && req.body.name !== req.collection.name) {
- collectionUpdatePayload.name = req.body.name
- wasUpdated = true
+ if (req.body.name !== undefined && typeof req.body.name === 'string') {
+ const nameCleaned = htmlSanitizer.stripAllTags(req.body.name)
+ if (nameCleaned !== req.collection.name) {
+ collectionUpdatePayload.name = nameCleaned
+ wasUpdated = true
+ }
}
if (wasUpdated) {
diff --git a/server/controllers/MeController.js b/server/controllers/MeController.js
index 51773a5ad..c5968f521 100644
--- a/server/controllers/MeController.js
+++ b/server/controllers/MeController.js
@@ -63,7 +63,7 @@ class MeController {
* @param {Response} res
*/
async getItemListeningSessions(req, res) {
- const libraryItem = await Database.libraryItemModel.findByPk(req.params.libraryItemId)
+ const libraryItem = await Database.libraryItemModel.getExpandedById(req.params.libraryItemId)
const episode = await Database.podcastEpisodeModel.findByPk(req.params.episodeId)
if (!libraryItem || (libraryItem.isPodcast && !episode)) {
@@ -71,6 +71,12 @@ class MeController {
return res.sendStatus(404)
}
+ // Check if user has access to this library item
+ if (!req.user.checkCanAccessLibraryItem(libraryItem)) {
+ Logger.error(`[MeController] User "${req.user.username}" attempted to access listening sessions for library item "${req.params.libraryItemId}" without access`)
+ return res.sendStatus(403)
+ }
+
const mediaItemId = episode?.id || libraryItem.mediaId
let listeningSessions = await this.getUserItemListeningSessionsHelper(req.user.id, mediaItemId)
@@ -125,6 +131,13 @@ class MeController {
* @param {Response} res
*/
async removeMediaProgress(req, res) {
+ // Verify the media progress belongs to the current user
+ const mediaProgress = req.user.mediaProgresses.find((mp) => mp.id === req.params.id)
+ if (!mediaProgress) {
+ Logger.error(`[MeController] Media progress not found or does not belong to user "${req.user.username}"`)
+ return res.sendStatus(404)
+ }
+
await Database.mediaProgressModel.removeById(req.params.id)
req.user.mediaProgresses = req.user.mediaProgresses.filter((mp) => mp.id !== req.params.id)
@@ -192,7 +205,16 @@ class MeController {
* @param {Response} res
*/
async createBookmark(req, res) {
- if (!(await Database.libraryItemModel.checkExistsById(req.params.id))) return res.sendStatus(404)
+ const libraryItem = await Database.libraryItemModel.getExpandedById(req.params.id)
+ if (!libraryItem) {
+ return res.sendStatus(404)
+ }
+
+ // Check if user has access to this library item
+ if (!req.user.checkCanAccessLibraryItem(libraryItem)) {
+ Logger.error(`[MeController] User "${req.user.username}" attempted to create bookmark for library item "${req.params.id}" without access`)
+ return res.sendStatus(403)
+ }
const { time, title } = req.body
if (isNullOrNaN(time)) {
@@ -216,7 +238,16 @@ class MeController {
* @param {Response} res
*/
async updateBookmark(req, res) {
- if (!(await Database.libraryItemModel.checkExistsById(req.params.id))) return res.sendStatus(404)
+ const libraryItem = await Database.libraryItemModel.getExpandedById(req.params.id)
+ if (!libraryItem) {
+ return res.sendStatus(404)
+ }
+
+ // Check if user has access to this library item
+ if (!req.user.checkCanAccessLibraryItem(libraryItem)) {
+ Logger.error(`[MeController] User "${req.user.username}" attempted to update bookmark for library item "${req.params.id}" without access`)
+ return res.sendStatus(403)
+ }
const { time, title } = req.body
if (isNullOrNaN(time)) {
@@ -245,7 +276,16 @@ class MeController {
* @param {Response} res
*/
async removeBookmark(req, res) {
- if (!(await Database.libraryItemModel.checkExistsById(req.params.id))) return res.sendStatus(404)
+ const libraryItem = await Database.libraryItemModel.getExpandedById(req.params.id)
+ if (!libraryItem) {
+ return res.sendStatus(404)
+ }
+
+ // Check if user has access to this library item
+ if (!req.user.checkCanAccessLibraryItem(libraryItem)) {
+ Logger.error(`[MeController] User "${req.user.username}" attempted to remove bookmark for library item "${req.params.id}" without access`)
+ return res.sendStatus(403)
+ }
const time = Number(req.params.time)
if (isNaN(time)) {
diff --git a/server/controllers/PlaylistController.js b/server/controllers/PlaylistController.js
index 972c352a4..bc1a7a455 100644
--- a/server/controllers/PlaylistController.js
+++ b/server/controllers/PlaylistController.js
@@ -2,6 +2,7 @@ const { Request, Response, NextFunction } = require('express')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const Database = require('../Database')
+const htmlSanitizer = require('../utils/htmlSanitizer')
/**
* @typedef RequestUserObject
@@ -29,7 +30,8 @@ class PlaylistController {
const reqBody = req.body || {}
// Validation
- if (!reqBody.name || !reqBody.libraryId) {
+ const nameCleaned = htmlSanitizer.stripAllTags(reqBody.name)
+ if (!nameCleaned || !reqBody.libraryId) {
return res.status(400).send('Invalid playlist data')
}
if (reqBody.description && typeof reqBody.description !== 'string') {
@@ -84,7 +86,7 @@ class PlaylistController {
{
libraryId: reqBody.libraryId,
userId: req.user.id,
- name: reqBody.name,
+ name: nameCleaned,
description: reqBody.description || null
},
{ transaction }
@@ -174,7 +176,11 @@ class PlaylistController {
}
const playlistUpdatePayload = {}
- if (reqBody.name) playlistUpdatePayload.name = reqBody.name
+
+ const nameCleaned = htmlSanitizer.stripAllTags(reqBody.name)
+ if (nameCleaned) {
+ playlistUpdatePayload.name = nameCleaned
+ }
if (reqBody.description) playlistUpdatePayload.description = reqBody.description
// Update name and description
diff --git a/server/controllers/PodcastController.js b/server/controllers/PodcastController.js
index 1ebe1d110..c70287600 100644
--- a/server/controllers/PodcastController.js
+++ b/server/controllers/PodcastController.js
@@ -412,6 +412,12 @@ class PodcastController {
Logger.debug(`[PodcastController] Sanitized description from "${req.body[key]}" to "${sanitizedDescription}"`)
req.body[key] = sanitizedDescription
}
+ } else if (key === 'subtitle' && req.body[key]) {
+ const sanitizedSubtitle = htmlSanitizer.sanitize(req.body[key])
+ if (sanitizedSubtitle !== req.body[key]) {
+ Logger.debug(`[PodcastController] Sanitized subtitle from "${req.body[key]}" to "${sanitizedSubtitle}"`)
+ req.body[key] = sanitizedSubtitle
+ }
}
updatePayload[key] = req.body[key]
diff --git a/server/controllers/SearchController.js b/server/controllers/SearchController.js
index f6f0ba475..ecd9a41c0 100644
--- a/server/controllers/SearchController.js
+++ b/server/controllers/SearchController.js
@@ -88,10 +88,10 @@ class SearchController {
const provider = getQueryParamAsString(query, 'provider', 'google')
const title = getQueryParamAsString(query, 'title', '')
const author = getQueryParamAsString(query, 'author', '')
- const id = getQueryParamAsString(query, 'id', '', true)
+ const id = getQueryParamAsString(query, 'id', undefined)
// Fetch library item
- const libraryItem = await SearchController.fetchLibraryItem(id)
+ const libraryItem = id ? await SearchController.fetchLibraryItem(id) : null
const results = await BookFinder.search(libraryItem, provider, title, author)
res.json(results)
@@ -187,7 +187,7 @@ class SearchController {
try {
const query = req.query
const asin = getQueryParamAsString(query, 'asin', '', true)
- const region = getQueryParamAsString(req.query.region, 'us').toLowerCase()
+ const region = getQueryParamAsString(query, 'region', 'us').toLowerCase()
if (!isValidASIN(asin.toUpperCase())) throw new ValidationError('asin', 'is invalid')
diff --git a/server/finders/BookFinder.js b/server/finders/BookFinder.js
index fe1a61027..90fed951f 100644
--- a/server/finders/BookFinder.js
+++ b/server/finders/BookFinder.js
@@ -227,7 +227,7 @@ class BookFinder {
title = this.#removeAuthorFromTitle(title)
const titleTransformers = [
- [/([,:;_]| by ).*/g, ''], // Remove subtitle
+ [/(: |[,;_]| by ).*/g, ''], // Remove subtitle
[/(^| )\d+k(bps)?( |$)/, ' '], // Remove bitrate
[/ (2nd|3rd|\d+th)\s+ed(\.|ition)?/g, ''], // Remove edition
[/(^| |\.)(m4b|m4a|mp3)( |$)/g, ''], // Remove file-type
@@ -428,7 +428,7 @@ class BookFinder {
}
}
- if (books.length) {
+ if (books.length && libraryItem) {
const isAudibleProvider = provider.startsWith('audible')
const libraryItemDurationMinutes = libraryItem?.media?.duration ? libraryItem.media.duration / 60 : null
@@ -646,11 +646,11 @@ class BookFinder {
module.exports = new BookFinder()
function hasSubtitle(title) {
- return title.includes(':') || title.includes(' - ')
+ return title.includes(': ') || title.includes(' - ')
}
function stripSubtitle(title) {
- if (title.includes(':')) {
- return title.split(':')[0].trim()
+ if (title.includes(': ')) {
+ return title.split(': ')[0].trim()
} else if (title.includes(' - ')) {
return title.split(' - ')[0].trim()
}
diff --git a/server/managers/ApiCacheManager.js b/server/managers/ApiCacheManager.js
index 2d8eece85..8a67e4898 100644
--- a/server/managers/ApiCacheManager.js
+++ b/server/managers/ApiCacheManager.js
@@ -5,6 +5,9 @@ const Database = require('../Database')
class ApiCacheManager {
defaultCacheOptions = { max: 1000, maxSize: 10 * 1000 * 1000, sizeCalculation: (item) => item.body.length + JSON.stringify(item.headers).length }
defaultTtlOptions = { ttl: 30 * 60 * 1000 }
+ highChurnModels = new Set(['session', 'mediaProgress', 'playbackSession', 'device'])
+ modelsInvalidatingPersonalized = new Set(['mediaProgress'])
+ modelsInvalidatingMe = new Set(['session', 'mediaProgress', 'playbackSession', 'device'])
constructor(cache = new LRUCache(this.defaultCacheOptions), ttlOptions = this.defaultTtlOptions) {
this.cache = cache
@@ -16,8 +19,44 @@ class ApiCacheManager {
hooks.forEach((hook) => database.sequelize.addHook(hook, (model) => this.clear(model, hook)))
}
+ getModelName(model) {
+ if (typeof model?.name === 'string') return model.name
+ if (typeof model?.model?.name === 'string') return model.model.name
+ if (typeof model?.constructor?.name === 'string' && model.constructor.name !== 'Object') return model.constructor.name
+ return 'unknown'
+ }
+
+ clearByUrlPattern(urlPattern) {
+ let removed = 0
+ for (const key of this.cache.keys()) {
+ try {
+ const parsed = JSON.parse(key)
+ if (typeof parsed?.url === 'string' && urlPattern.test(parsed.url)) {
+ if (this.cache.delete(key)) removed++
+ }
+ } catch {
+ if (this.cache.delete(key)) removed++
+ }
+ }
+ return removed
+ }
+
+ clearUserProgressSlices(modelName, hook) {
+ const removedPersonalized = this.modelsInvalidatingPersonalized.has(modelName) ? this.clearByUrlPattern(/^\/libraries\/[^/]+\/personalized/) : 0
+ const removedMe = this.modelsInvalidatingMe.has(modelName) ? this.clearByUrlPattern(/^\/me(\/|\?|$)/) : 0
+ Logger.debug(
+ `[ApiCacheManager] ${modelName}.${hook}: cleared user-progress cache slices (personalized=${removedPersonalized}, me=${removedMe})`
+ )
+ }
+
clear(model, hook) {
- Logger.debug(`[ApiCacheManager] ${model.constructor.name}.${hook}: Clearing cache`)
+ const modelName = this.getModelName(model)
+ if (this.highChurnModels.has(modelName)) {
+ this.clearUserProgressSlices(modelName, hook)
+ return
+ }
+
+ Logger.debug(`[ApiCacheManager] ${modelName}.${hook}: Clearing cache`)
this.cache.clear()
}
diff --git a/server/managers/BackupManager.js b/server/managers/BackupManager.js
index 102521b71..2697f94ea 100644
--- a/server/managers/BackupManager.js
+++ b/server/managers/BackupManager.js
@@ -48,9 +48,14 @@ class BackupManager {
}
async init() {
- const backupsDirExists = await fs.pathExists(this.backupPath)
- if (!backupsDirExists) {
- await fs.ensureDir(this.backupPath)
+ try {
+ const backupsDirExists = await fs.pathExists(this.backupPath)
+ if (!backupsDirExists) {
+ await fs.ensureDir(this.backupPath)
+ }
+ } catch (error) {
+ Logger.error(`[BackupManager] Failed to ensure backup directory at "${this.backupPath}": ${error.message}`)
+ throw new Error(`[BackupManager] Failed to ensure backup directory at "${this.backupPath}"`, { cause: error })
}
await this.loadBackups()
diff --git a/server/managers/CacheManager.js b/server/managers/CacheManager.js
index b44b65de3..38c0415e9 100644
--- a/server/managers/CacheManager.js
+++ b/server/managers/CacheManager.js
@@ -24,10 +24,15 @@ class CacheManager {
this.ImageCachePath = Path.join(this.CachePath, 'images')
this.ItemCachePath = Path.join(this.CachePath, 'items')
- await fs.ensureDir(this.CachePath)
- await fs.ensureDir(this.CoverCachePath)
- await fs.ensureDir(this.ImageCachePath)
- await fs.ensureDir(this.ItemCachePath)
+ try {
+ await fs.ensureDir(this.CachePath)
+ await fs.ensureDir(this.CoverCachePath)
+ await fs.ensureDir(this.ImageCachePath)
+ await fs.ensureDir(this.ItemCachePath)
+ } catch (error) {
+ Logger.error(`[CacheManager] Failed to create cache directories at "${this.CachePath}": ${error.message}`)
+ throw new Error(`[CacheManager] Failed to create cache directories at "${this.CachePath}"`, { cause: error })
+ }
}
async handleCoverCache(res, libraryItemId, options = {}) {
diff --git a/server/managers/LogManager.js b/server/managers/LogManager.js
index 731dfe70a..07bb7d401 100644
--- a/server/managers/LogManager.js
+++ b/server/managers/LogManager.js
@@ -37,8 +37,13 @@ class LogManager {
}
async ensureLogDirs() {
- await fs.ensureDir(this.DailyLogPath)
- await fs.ensureDir(this.ScanLogPath)
+ try {
+ await fs.ensureDir(this.DailyLogPath)
+ await fs.ensureDir(this.ScanLogPath)
+ } catch (error) {
+ console.error(`[LogManager] Failed to create log directories at "${this.DailyLogPath}": ${error.message}`)
+ throw new Error(`[LogManager] Failed to create log directories at "${this.DailyLogPath}"`, { cause: error })
+ }
}
/**
@@ -102,20 +107,20 @@ class LogManager {
}
/**
- *
- * @param {string} filename
+ *
+ * @param {string} filename
*/
async removeLogFile(filename) {
const fullPath = Path.join(this.DailyLogPath, filename)
const exists = await fs.pathExists(fullPath)
if (!exists) {
Logger.error(TAG, 'Invalid log dne ' + fullPath)
- this.dailyLogFiles = this.dailyLogFiles.filter(dlf => dlf !== filename)
+ this.dailyLogFiles = this.dailyLogFiles.filter((dlf) => dlf !== filename)
} else {
try {
await fs.unlink(fullPath)
Logger.info(TAG, 'Removed daily log: ' + filename)
- this.dailyLogFiles = this.dailyLogFiles.filter(dlf => dlf !== filename)
+ this.dailyLogFiles = this.dailyLogFiles.filter((dlf) => dlf !== filename)
} catch (error) {
Logger.error(TAG, 'Failed to unlink log file ' + fullPath)
}
@@ -123,8 +128,8 @@ class LogManager {
}
/**
- *
- * @param {LogObject} logObj
+ *
+ * @param {LogObject} logObj
*/
async logToFile(logObj) {
// Fatal crashes get logged to a separate file
@@ -152,8 +157,8 @@ class LogManager {
}
/**
- *
- * @param {LogObject} logObj
+ *
+ * @param {LogObject} logObj
*/
async logCrashToFile(logObj) {
const line = JSON.stringify(logObj) + '\n'
@@ -161,18 +166,18 @@ class LogManager {
const logsDir = Path.join(global.MetadataPath, 'logs')
await fs.ensureDir(logsDir)
const crashLogPath = Path.join(logsDir, 'crash_logs.txt')
- return fs.writeFile(crashLogPath, line, { flag: "a+" }).catch((error) => {
+ return fs.writeFile(crashLogPath, line, { flag: 'a+' }).catch((error) => {
console.log('[LogManager] Appended crash log', error)
})
}
/**
* Most recent 5000 daily logs
- *
+ *
* @returns {string}
*/
getMostRecentCurrentDailyLogs() {
return this.currentDailyLog?.logs.slice(-5000) || ''
}
}
-module.exports = LogManager
\ No newline at end of file
+module.exports = LogManager
diff --git a/server/managers/MigrationManager.js b/server/managers/MigrationManager.js
index 8635def10..e302038ff 100644
--- a/server/managers/MigrationManager.js
+++ b/server/managers/MigrationManager.js
@@ -38,7 +38,12 @@ class MigrationManager {
if (!(await fs.pathExists(this.configPath))) throw new Error(`Config path does not exist: ${this.configPath}`)
this.migrationsDir = path.join(this.configPath, 'migrations')
- await fs.ensureDir(this.migrationsDir)
+ try {
+ await fs.ensureDir(this.migrationsDir)
+ } catch (error) {
+ Logger.error(`[MigrationManager] Failed to create migrations directory at "${this.migrationsDir}": ${error.message}`)
+ throw new Error(`[MigrationManager] Failed to create migrations directory at "${this.migrationsDir}"`, { cause: error })
+ }
this.serverVersion = this.extractVersionFromTag(serverVersion)
if (!this.serverVersion) throw new Error(`Invalid server version: ${serverVersion}. Expected a version tag like v1.2.3.`)
diff --git a/server/managers/PlaybackSessionManager.js b/server/managers/PlaybackSessionManager.js
index fb12fedaa..1e95727f1 100644
--- a/server/managers/PlaybackSessionManager.js
+++ b/server/managers/PlaybackSessionManager.js
@@ -459,7 +459,12 @@ class PlaybackSessionManager {
* Remove all stream folders in `/metadata/streams`
*/
async removeOrphanStreams() {
- await fs.ensureDir(this.StreamsPath)
+ try {
+ await fs.ensureDir(this.StreamsPath)
+ } catch (error) {
+ Logger.error(`[PlaybackSessionManager] Failed to create streams directory at "${this.StreamsPath}": ${error.message}`)
+ throw new Error(`[PlaybackSessionManager] Failed to create streams directory at "${this.StreamsPath}"`, { cause: error })
+ }
try {
const streamsInPath = await fs.readdir(this.StreamsPath)
for (const streamId of streamsInPath) {
diff --git a/server/migrations/v2.33.0-add-discover-query-indexes.js b/server/migrations/v2.33.0-add-discover-query-indexes.js
new file mode 100644
index 000000000..ebd92bbaa
--- /dev/null
+++ b/server/migrations/v2.33.0-add-discover-query-indexes.js
@@ -0,0 +1,74 @@
+/**
+ * @typedef MigrationContext
+ * @property {import('sequelize').QueryInterface} queryInterface
+ * @property {import('../Logger')} logger
+ *
+ * @typedef MigrationOptions
+ * @property {MigrationContext} context
+ */
+
+const migrationVersion = '2.33.0'
+const migrationName = `${migrationVersion}-add-discover-query-indexes`
+const loggerPrefix = `[${migrationVersion} migration]`
+
+const indexes = [
+ {
+ table: 'mediaProgresses',
+ name: 'media_progresses_user_item_finished_time',
+ fields: ['userId', 'mediaItemId', 'isFinished', 'currentTime']
+ },
+ {
+ table: 'bookSeries',
+ name: 'book_series_series_book',
+ fields: ['seriesId', 'bookId']
+ }
+]
+
+async function up({ context: { queryInterface, logger } }) {
+ logger.info(`${loggerPrefix} UPGRADE BEGIN: ${migrationName}`)
+
+ for (const index of indexes) {
+ await addIndexIfMissing(queryInterface, logger, index)
+ }
+
+ logger.info(`${loggerPrefix} UPGRADE END: ${migrationName}`)
+}
+
+async function down({ context: { queryInterface, logger } }) {
+ logger.info(`${loggerPrefix} DOWNGRADE BEGIN: ${migrationName}`)
+
+ for (const index of indexes) {
+ await removeIndexIfExists(queryInterface, logger, index)
+ }
+
+ logger.info(`${loggerPrefix} DOWNGRADE END: ${migrationName}`)
+}
+
+async function addIndexIfMissing(queryInterface, logger, index) {
+ const existing = await queryInterface.showIndex(index.table)
+ if (existing.some((i) => i.name === index.name)) {
+ logger.info(`${loggerPrefix} index ${index.name} already exists on ${index.table}`)
+ return
+ }
+
+ logger.info(`${loggerPrefix} adding index ${index.name} on ${index.table}(${index.fields.join(', ')})`)
+ await queryInterface.addIndex(index.table, {
+ name: index.name,
+ fields: index.fields
+ })
+ logger.info(`${loggerPrefix} added index ${index.name}`)
+}
+
+async function removeIndexIfExists(queryInterface, logger, index) {
+ const existing = await queryInterface.showIndex(index.table)
+ if (!existing.some((i) => i.name === index.name)) {
+ logger.info(`${loggerPrefix} index ${index.name} does not exist on ${index.table}`)
+ return
+ }
+
+ logger.info(`${loggerPrefix} removing index ${index.name}`)
+ await queryInterface.removeIndex(index.table, index.name)
+ logger.info(`${loggerPrefix} removed index ${index.name}`)
+}
+
+module.exports = { up, down }
diff --git a/server/models/BookSeries.js b/server/models/BookSeries.js
index 31eccb9fa..e71f28a8e 100644
--- a/server/models/BookSeries.js
+++ b/server/models/BookSeries.js
@@ -48,6 +48,10 @@ class BookSeries extends Model {
{
name: 'bookSeries_seriesId',
fields: ['seriesId']
+ },
+ {
+ name: 'book_series_series_book',
+ fields: ['seriesId', 'bookId']
}
]
}
diff --git a/server/models/LibraryItem.js b/server/models/LibraryItem.js
index f4b031fe9..a39aceb12 100644
--- a/server/models/LibraryItem.js
+++ b/server/models/LibraryItem.js
@@ -340,6 +340,15 @@ class LibraryItem extends Model {
const shelves = []
+ const timed = async (loader) => {
+ const start = Date.now()
+ const payload = await loader()
+ return {
+ payload,
+ elapsedSeconds: ((Date.now() - start) / 1000).toFixed(2)
+ }
+ }
+
// "Continue Listening" shelf
const itemsInProgressPayload = await libraryFilters.getMediaItemsInProgress(library, user, include, limit, false)
if (itemsInProgressPayload.items.length) {
@@ -371,11 +380,18 @@ class LibraryItem extends Model {
}
Logger.debug(`Loaded ${itemsInProgressPayload.items.length} of ${itemsInProgressPayload.count} items for "Continue Listening/Reading" in ${((Date.now() - fullStart) / 1000).toFixed(2)}s`)
- let start = Date.now()
if (library.isBook) {
- start = Date.now()
+ const [continueSeriesResult, mostRecentResult, seriesMostRecentResult, discoverResult, mediaFinishedResult, newestAuthorsResult] = await Promise.all([
+ timed(() => libraryFilters.getLibraryItemsContinueSeries(library, user, include, limit)),
+ timed(() => libraryFilters.getLibraryItemsMostRecentlyAdded(library, user, include, limit)),
+ timed(() => libraryFilters.getSeriesMostRecentlyAdded(library, user, include, 5)),
+ timed(() => libraryFilters.getLibraryItemsToDiscover(library, user, include, limit)),
+ timed(() => libraryFilters.getMediaFinished(library, user, include, limit)),
+ timed(() => libraryFilters.getNewestAuthors(library, user, limit))
+ ])
+
+ const continueSeriesPayload = continueSeriesResult.payload
// "Continue Series" shelf
- const continueSeriesPayload = await libraryFilters.getLibraryItemsContinueSeries(library, user, include, limit)
if (continueSeriesPayload.libraryItems.length) {
shelves.push({
id: 'continue-series',
@@ -386,42 +402,24 @@ class LibraryItem extends Model {
total: continueSeriesPayload.count
})
}
- Logger.debug(`Loaded ${continueSeriesPayload.libraryItems.length} of ${continueSeriesPayload.count} items for "Continue Series" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
- } else if (library.isPodcast) {
- // "Newest Episodes" shelf
- const newestEpisodesPayload = await libraryFilters.getNewestPodcastEpisodes(library, user, limit)
- if (newestEpisodesPayload.libraryItems.length) {
+ Logger.debug(`Loaded ${continueSeriesPayload.libraryItems.length} of ${continueSeriesPayload.count} items for "Continue Series" in ${continueSeriesResult.elapsedSeconds}s`)
+
+ const mostRecentPayload = mostRecentResult.payload
+ // "Recently Added" shelf
+ if (mostRecentPayload.libraryItems.length) {
shelves.push({
- id: 'newest-episodes',
- label: 'Newest Episodes',
- labelStringKey: 'LabelNewestEpisodes',
- type: 'episode',
- entities: newestEpisodesPayload.libraryItems,
- total: newestEpisodesPayload.count
+ id: 'recently-added',
+ label: 'Recently Added',
+ labelStringKey: 'LabelRecentlyAdded',
+ type: library.mediaType,
+ entities: mostRecentPayload.libraryItems,
+ total: mostRecentPayload.count
})
}
- Logger.debug(`Loaded ${newestEpisodesPayload.libraryItems.length} of ${newestEpisodesPayload.count} episodes for "Newest Episodes" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
- }
+ Logger.debug(`Loaded ${mostRecentPayload.libraryItems.length} of ${mostRecentPayload.count} items for "Recently Added" in ${mostRecentResult.elapsedSeconds}s`)
- start = Date.now()
- // "Recently Added" shelf
- const mostRecentPayload = await libraryFilters.getLibraryItemsMostRecentlyAdded(library, user, include, limit)
- if (mostRecentPayload.libraryItems.length) {
- shelves.push({
- id: 'recently-added',
- label: 'Recently Added',
- labelStringKey: 'LabelRecentlyAdded',
- type: library.mediaType,
- entities: mostRecentPayload.libraryItems,
- total: mostRecentPayload.count
- })
- }
- Logger.debug(`Loaded ${mostRecentPayload.libraryItems.length} of ${mostRecentPayload.count} items for "Recently Added" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
-
- if (library.isBook) {
- start = Date.now()
+ const seriesMostRecentPayload = seriesMostRecentResult.payload
// "Recent Series" shelf
- const seriesMostRecentPayload = await libraryFilters.getSeriesMostRecentlyAdded(library, user, include, 5)
if (seriesMostRecentPayload.series.length) {
shelves.push({
id: 'recent-series',
@@ -432,11 +430,10 @@ class LibraryItem extends Model {
total: seriesMostRecentPayload.count
})
}
- Logger.debug(`Loaded ${seriesMostRecentPayload.series.length} of ${seriesMostRecentPayload.count} series for "Recent Series" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
+ Logger.debug(`Loaded ${seriesMostRecentPayload.series.length} of ${seriesMostRecentPayload.count} series for "Recent Series" in ${seriesMostRecentResult.elapsedSeconds}s`)
- start = Date.now()
+ const discoverLibraryItemsPayload = discoverResult.payload
// "Discover" shelf
- const discoverLibraryItemsPayload = await libraryFilters.getLibraryItemsToDiscover(library, user, include, limit)
if (discoverLibraryItemsPayload.libraryItems.length) {
shelves.push({
id: 'discover',
@@ -447,45 +444,41 @@ class LibraryItem extends Model {
total: discoverLibraryItemsPayload.count
})
}
- Logger.debug(`Loaded ${discoverLibraryItemsPayload.libraryItems.length} of ${discoverLibraryItemsPayload.count} items for "Discover" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
- }
+ Logger.debug(`Loaded ${discoverLibraryItemsPayload.libraryItems.length} of ${discoverLibraryItemsPayload.count} items for "Discover" in ${discoverResult.elapsedSeconds}s`)
- start = Date.now()
- // "Listen Again" shelf
- const mediaFinishedPayload = await libraryFilters.getMediaFinished(library, user, include, limit)
- if (mediaFinishedPayload.items.length) {
- const ebookOnlyItemsInProgress = mediaFinishedPayload.items.filter((li) => li.media.ebookFormat && !li.media.numTracks)
- const audioItemsInProgress = mediaFinishedPayload.items.filter((li) => li.media.numTracks || li.mediaType === 'podcast')
+ const mediaFinishedPayload = mediaFinishedResult.payload
+ // "Listen Again" shelf
+ if (mediaFinishedPayload.items.length) {
+ const ebookOnlyItemsInProgress = mediaFinishedPayload.items.filter((li) => li.media.ebookFormat && !li.media.numTracks)
+ const audioItemsInProgress = mediaFinishedPayload.items.filter((li) => li.media.numTracks || li.mediaType === 'podcast')
- if (audioItemsInProgress.length) {
- shelves.push({
- id: 'listen-again',
- label: 'Listen Again',
- labelStringKey: 'LabelListenAgain',
- type: library.isPodcast ? 'episode' : 'book',
- entities: audioItemsInProgress,
- total: mediaFinishedPayload.count
- })
+ if (audioItemsInProgress.length) {
+ shelves.push({
+ id: 'listen-again',
+ label: 'Listen Again',
+ labelStringKey: 'LabelListenAgain',
+ type: library.isPodcast ? 'episode' : 'book',
+ entities: audioItemsInProgress,
+ total: mediaFinishedPayload.count
+ })
+ }
+
+ if (ebookOnlyItemsInProgress.length) {
+ // "Read Again" shelf
+ shelves.push({
+ id: 'read-again',
+ label: 'Read Again',
+ labelStringKey: 'LabelReadAgain',
+ type: 'book',
+ entities: ebookOnlyItemsInProgress,
+ total: mediaFinishedPayload.count
+ })
+ }
}
+ Logger.debug(`Loaded ${mediaFinishedPayload.items.length} of ${mediaFinishedPayload.count} items for "Listen/Read Again" in ${mediaFinishedResult.elapsedSeconds}s`)
- // "Read Again" shelf
- if (ebookOnlyItemsInProgress.length) {
- shelves.push({
- id: 'read-again',
- label: 'Read Again',
- labelStringKey: 'LabelReadAgain',
- type: 'book',
- entities: ebookOnlyItemsInProgress,
- total: mediaFinishedPayload.count
- })
- }
- }
- Logger.debug(`Loaded ${mediaFinishedPayload.items.length} of ${mediaFinishedPayload.count} items for "Listen/Read Again" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
-
- if (library.isBook) {
- start = Date.now()
+ const newestAuthorsPayload = newestAuthorsResult.payload
// "Newest Authors" shelf
- const newestAuthorsPayload = await libraryFilters.getNewestAuthors(library, user, limit)
if (newestAuthorsPayload.authors.length) {
shelves.push({
id: 'newest-authors',
@@ -496,7 +489,72 @@ class LibraryItem extends Model {
total: newestAuthorsPayload.count
})
}
- Logger.debug(`Loaded ${newestAuthorsPayload.authors.length} of ${newestAuthorsPayload.count} authors for "Newest Authors" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
+ Logger.debug(`Loaded ${newestAuthorsPayload.authors.length} of ${newestAuthorsPayload.count} authors for "Newest Authors" in ${newestAuthorsResult.elapsedSeconds}s`)
+ } else if (library.isPodcast) {
+ const [newestEpisodesResult, mostRecentResult, mediaFinishedResult] = await Promise.all([
+ timed(() => libraryFilters.getNewestPodcastEpisodes(library, user, limit)),
+ timed(() => libraryFilters.getLibraryItemsMostRecentlyAdded(library, user, include, limit)),
+ timed(() => libraryFilters.getMediaFinished(library, user, include, limit))
+ ])
+
+ const newestEpisodesPayload = newestEpisodesResult.payload
+ // "Newest Episodes" shelf
+ if (newestEpisodesPayload.libraryItems.length) {
+ shelves.push({
+ id: 'newest-episodes',
+ label: 'Newest Episodes',
+ labelStringKey: 'LabelNewestEpisodes',
+ type: 'episode',
+ entities: newestEpisodesPayload.libraryItems,
+ total: newestEpisodesPayload.count
+ })
+ }
+ Logger.debug(`Loaded ${newestEpisodesPayload.libraryItems.length} of ${newestEpisodesPayload.count} episodes for "Newest Episodes" in ${newestEpisodesResult.elapsedSeconds}s`)
+
+ const mostRecentPayload = mostRecentResult.payload
+ // "Recently Added" shelf
+ if (mostRecentPayload.libraryItems.length) {
+ shelves.push({
+ id: 'recently-added',
+ label: 'Recently Added',
+ labelStringKey: 'LabelRecentlyAdded',
+ type: library.mediaType,
+ entities: mostRecentPayload.libraryItems,
+ total: mostRecentPayload.count
+ })
+ }
+ Logger.debug(`Loaded ${mostRecentPayload.libraryItems.length} of ${mostRecentPayload.count} items for "Recently Added" in ${mostRecentResult.elapsedSeconds}s`)
+
+ const mediaFinishedPayload = mediaFinishedResult.payload
+ // "Listen Again" shelf
+ if (mediaFinishedPayload.items.length) {
+ const ebookOnlyItemsInProgress = mediaFinishedPayload.items.filter((li) => li.media.ebookFormat && !li.media.numTracks)
+ const audioItemsInProgress = mediaFinishedPayload.items.filter((li) => li.media.numTracks || li.mediaType === 'podcast')
+
+ if (audioItemsInProgress.length) {
+ shelves.push({
+ id: 'listen-again',
+ label: 'Listen Again',
+ labelStringKey: 'LabelListenAgain',
+ type: 'episode',
+ entities: audioItemsInProgress,
+ total: mediaFinishedPayload.count
+ })
+ }
+
+ if (ebookOnlyItemsInProgress.length) {
+ // "Read Again" shelf
+ shelves.push({
+ id: 'read-again',
+ label: 'Read Again',
+ labelStringKey: 'LabelReadAgain',
+ type: 'book',
+ entities: ebookOnlyItemsInProgress,
+ total: mediaFinishedPayload.count
+ })
+ }
+ }
+ Logger.debug(`Loaded ${mediaFinishedPayload.items.length} of ${mediaFinishedPayload.count} items for "Listen/Read Again" in ${mediaFinishedResult.elapsedSeconds}s`)
}
Logger.debug(`Loaded ${shelves.length} personalized shelves in ${((Date.now() - fullStart) / 1000).toFixed(2)}s`)
diff --git a/server/models/MediaProgress.js b/server/models/MediaProgress.js
index 0ebe2f598..9c0269a9e 100644
--- a/server/models/MediaProgress.js
+++ b/server/models/MediaProgress.js
@@ -80,6 +80,10 @@ class MediaProgress extends Model {
indexes: [
{
fields: ['updatedAt']
+ },
+ {
+ name: 'media_progresses_user_item_finished_time',
+ fields: ['userId', 'mediaItemId', 'isFinished', 'currentTime']
}
]
}
diff --git a/server/models/User.js b/server/models/User.js
index 36b2eca98..936efde13 100644
--- a/server/models/User.js
+++ b/server/models/User.js
@@ -782,7 +782,14 @@ class User extends Model {
error: 'Library item not found',
statusCode: 404
}
+ } else if (libraryItem.mediaType !== 'book') {
+ Logger.error(`[User] createUpdateMediaProgress: library item ${progressPayload.libraryItemId} is not a book`)
+ return {
+ error: 'Library item is not a book',
+ statusCode: 400
+ }
}
+
mediaItemId = libraryItem.media.id
mediaProgress = libraryItem.media.mediaProgresses?.[0]
}
diff --git a/server/objects/DeviceInfo.js b/server/objects/DeviceInfo.js
index ceff6c32e..22ebfbea4 100644
--- a/server/objects/DeviceInfo.js
+++ b/server/objects/DeviceInfo.js
@@ -1,6 +1,10 @@
-const uuidv4 = require("uuid").v4
+const uuidv4 = require('uuid').v4
+const { stripAllTags } = require('../utils/htmlSanitizer')
class DeviceInfo {
+ /** @type {string[]} Fields to sanitize when loading from stored data */
+ static stringFields = ['deviceId', 'clientVersion', 'manufacturer', 'model', 'sdkVersion', 'clientName', 'deviceName']
+
constructor(deviceInfo = null) {
this.id = null
this.userId = null
@@ -31,7 +35,7 @@ class DeviceInfo {
construct(deviceInfo) {
for (const key in deviceInfo) {
if (deviceInfo[key] !== undefined && this[key] !== undefined) {
- this[key] = deviceInfo[key]
+ this[key] = DeviceInfo.stringFields.includes(key) ? stripAllTags(deviceInfo[key]) : deviceInfo[key]
}
}
}
@@ -63,7 +67,8 @@ class DeviceInfo {
}
get deviceDescription() {
- if (this.model) { // Set from mobile apps
+ if (this.model) {
+ // Set from mobile apps
if (this.sdkVersion) return `${this.model} SDK ${this.sdkVersion} / v${this.clientVersion}`
return `${this.model} / v${this.clientVersion}`
}
@@ -72,18 +77,7 @@ class DeviceInfo {
// When client doesn't send a device id
getTempDeviceId() {
- const keys = [
- this.userId,
- this.browserName,
- this.browserVersion,
- this.osName,
- this.osVersion,
- this.clientVersion,
- this.manufacturer,
- this.model,
- this.sdkVersion,
- this.ipAddress
- ].map(k => k || '')
+ const keys = [this.userId, this.browserName, this.browserVersion, this.osName, this.osVersion, this.clientVersion, this.manufacturer, this.model, this.sdkVersion, this.ipAddress].map((k) => k || '')
return 'temp-' + Buffer.from(keys.join('-'), 'utf-8').toString('base64')
}
@@ -99,12 +93,12 @@ class DeviceInfo {
this.osVersion = ua?.os.version || null
this.deviceType = ua?.device.type || null
- this.clientVersion = clientDeviceInfo?.clientVersion || serverVersion
- this.manufacturer = clientDeviceInfo?.manufacturer || null
- this.model = clientDeviceInfo?.model || null
- this.sdkVersion = clientDeviceInfo?.sdkVersion || null
+ this.clientVersion = stripAllTags(clientDeviceInfo?.clientVersion) || serverVersion
+ this.manufacturer = stripAllTags(clientDeviceInfo?.manufacturer) || null
+ this.model = stripAllTags(clientDeviceInfo?.model) || null
+ this.sdkVersion = stripAllTags(clientDeviceInfo?.sdkVersion) || null
- this.clientName = clientDeviceInfo?.clientName || null
+ this.clientName = stripAllTags(clientDeviceInfo?.clientName) || null
if (this.sdkVersion) {
if (!this.clientName) this.clientName = 'Abs Android'
this.deviceName = `${this.manufacturer || 'Unknown'} ${this.model || ''}`
@@ -149,4 +143,4 @@ class DeviceInfo {
return hasUpdates
}
}
-module.exports = DeviceInfo
\ No newline at end of file
+module.exports = DeviceInfo
diff --git a/server/objects/Stream.js b/server/objects/Stream.js
index d9bdd583c..5aa013e8e 100644
--- a/server/objects/Stream.js
+++ b/server/objects/Stream.js
@@ -73,7 +73,7 @@ class Stream extends EventEmitter {
return [AudioMimeType.FLAC, AudioMimeType.OPUS, AudioMimeType.WMA, AudioMimeType.AIFF, AudioMimeType.WEBM, AudioMimeType.WEBMA, AudioMimeType.AWB, AudioMimeType.CAF]
}
get codecsToForceAAC() {
- return ['alac']
+ return ['alac', 'ac3', 'eac3']
}
get userToken() {
return this.user.token
@@ -273,7 +273,16 @@ class Stream extends EventEmitter {
audioCodec = 'aac'
}
- this.ffmpeg.addOption([`-loglevel ${logLevel}`, '-map 0:a', `-c:a ${audioCodec}`])
+ const codecOptions = [`-loglevel ${logLevel}`, '-map 0:a']
+
+ if (['ac3', 'eac3'].includes(this.tracksCodec) && this.tracks.length > 0 && this.tracks[0].bitRate && this.tracks[0].channels) {
+ // In case for ac3/eac3 it needs to be passed the bitrate and channels to avoid ffmpeg errors
+ codecOptions.push(`-c:a ${audioCodec}`, `-b:a ${this.tracks[0].bitRate}`, `-ac ${this.tracks[0].channels}`)
+ } else {
+ codecOptions.push(`-c:a ${audioCodec}`)
+ }
+
+ this.ffmpeg.addOption(codecOptions)
const hlsOptions = ['-f hls', '-copyts', '-avoid_negative_ts make_non_negative', '-max_delay 5000000', '-max_muxing_queue_size 2048', `-hls_time 6`, `-hls_segment_type ${this.hlsSegmentType}`, `-start_number ${this.segmentStartNumber}`, '-hls_playlist_type vod', '-hls_list_size 0', '-hls_allow_cache 0']
this.ffmpeg.addOption(hlsOptions)
if (this.hlsSegmentType === 'fmp4') {
diff --git a/server/objects/settings/ServerSettings.js b/server/objects/settings/ServerSettings.js
index cc8099931..f4680ad0e 100644
--- a/server/objects/settings/ServerSettings.js
+++ b/server/objects/settings/ServerSettings.js
@@ -3,6 +3,7 @@ const packageJson = require('../../../package.json')
const { BookshelfView } = require('../../utils/constants')
const Logger = require('../../Logger')
const User = require('../../models/User')
+const { sanitize } = require('../../utils/htmlSanitizer')
class ServerSettings {
constructor(settings) {
@@ -128,7 +129,7 @@ class ServerSettings {
this.version = settings.version || null
this.buildNumber = settings.buildNumber || 0 // Added v2.4.5
- this.authLoginCustomMessage = settings.authLoginCustomMessage || null // Added v2.8.0
+ this.authLoginCustomMessage = sanitize(settings.authLoginCustomMessage) || null // Added v2.8.0
this.authActiveAuthMethods = settings.authActiveAuthMethods || ['local']
this.authOpenIDIssuerURL = settings.authOpenIDIssuerURL || null
@@ -312,7 +313,7 @@ class ServerSettings {
get authFormData() {
const clientFormData = {
- authLoginCustomMessage: this.authLoginCustomMessage
+ authLoginCustomMessage: sanitize(this.authLoginCustomMessage)
}
if (this.authActiveAuthMethods.includes('openid')) {
clientFormData.authOpenIDButtonText = this.authOpenIDButtonText
@@ -330,6 +331,9 @@ class ServerSettings {
update(payload) {
let hasUpdates = false
for (const key in payload) {
+ if (key === 'authLoginCustomMessage') {
+ payload[key] = sanitize(payload[key])
+ }
if (key === 'sortingPrefixes') {
// Sorting prefixes are updated with the /api/sorting-prefixes endpoint
continue
diff --git a/server/providers/Audible.js b/server/providers/Audible.js
index 2c12ffc1a..133d3c0d8 100644
--- a/server/providers/Audible.js
+++ b/server/providers/Audible.js
@@ -57,8 +57,13 @@ class Audible {
})
}
- const genresFiltered = genres ? genres.filter((g) => g.type == 'genre').map((g) => g.name) : []
- const tagsFiltered = genres ? genres.filter((g) => g.type == 'tag').map((g) => g.name) : []
+ let genresCleaned = []
+ let tagsCleaned = []
+
+ if (genres && Array.isArray(genres)) {
+ genresCleaned = [...new Set(genres.filter((g) => g.type == 'genre').map((g) => g.name))]
+ tagsCleaned = [...new Set(genres.filter((g) => g.type == 'tag').map((g) => g.name))]
+ }
return {
title,
@@ -71,8 +76,8 @@ class Audible {
cover: image,
asin,
isbn,
- genres: genresFiltered.length ? genresFiltered : null,
- tags: tagsFiltered.length ? tagsFiltered.join(', ') : null,
+ genres: genresCleaned.length ? genresCleaned : null,
+ tags: tagsCleaned.length ? tagsCleaned : null,
series: series.length ? series : null,
language: language ? language.charAt(0).toUpperCase() + language.slice(1) : null,
duration: runtimeLengthMin && !isNaN(runtimeLengthMin) ? Number(runtimeLengthMin) : 0,
diff --git a/server/providers/CustomProviderAdapter.js b/server/providers/CustomProviderAdapter.js
index b4837f181..0c099f9e8 100644
--- a/server/providers/CustomProviderAdapter.js
+++ b/server/providers/CustomProviderAdapter.js
@@ -89,6 +89,27 @@ class CustomProviderAdapter {
})
.filter((s) => s !== undefined)
}
+ /**
+ * Validates and dedupes tags/genres array
+ * Can be comma separated string or array of strings
+ * @param {string|string[]} tagsGenres
+ * @returns {string[]}
+ */
+ const validateTagsGenresArray = (tagsGenres) => {
+ if (!tagsGenres || (typeof tagsGenres !== 'string' && !Array.isArray(tagsGenres))) return undefined
+
+ // If string, split by comma and trim each item
+ if (typeof tagsGenres === 'string') tagsGenres = tagsGenres.split(',')
+ // If array, ensure all items are strings
+ else if (!tagsGenres.every((t) => typeof t === 'string')) return undefined
+
+ // Trim and filter out empty strings
+ tagsGenres = tagsGenres.map((t) => t.trim()).filter(Boolean)
+ if (!tagsGenres.length) return undefined
+
+ // Dedup
+ return [...new Set(tagsGenres)]
+ }
// re-map keys to throw out
return matches.map((match) => {
@@ -105,8 +126,8 @@ class CustomProviderAdapter {
cover: toStringOrUndefined(cover),
isbn: toStringOrUndefined(isbn),
asin: toStringOrUndefined(asin),
- genres: Array.isArray(genres) && genres.every((g) => typeof g === 'string') ? genres : undefined,
- tags: toStringOrUndefined(tags),
+ genres: validateTagsGenresArray(genres),
+ tags: validateTagsGenresArray(tags),
series: validateSeriesArray(series),
language: toStringOrUndefined(language),
duration: !isNaN(duration) && duration !== null ? Number(duration) : undefined,
diff --git a/server/scanner/Scanner.js b/server/scanner/Scanner.js
index bd1d4091a..c1f6ec529 100644
--- a/server/scanner/Scanner.js
+++ b/server/scanner/Scanner.js
@@ -275,18 +275,17 @@ class Scanner {
SocketAuthority.emitter('author_added', author.toOldJSON())
// Update filter data
Database.addAuthorToFilterData(libraryItem.libraryId, author.name, author.id)
-
- await Database.bookAuthorModel
- .create({
- authorId: author.id,
- bookId: libraryItem.media.id
- })
- .then(() => {
- Logger.info(`[Scanner] quickMatchBookBuildUpdatePayload: Added author "${author.name}" to "${libraryItem.media.title}"`)
- libraryItem.media.authors.push(author)
- hasAuthorUpdates = true
- })
}
+ await Database.bookAuthorModel
+ .create({
+ authorId: author.id,
+ bookId: libraryItem.media.id
+ })
+ .then(() => {
+ Logger.info(`[Scanner] quickMatchBookBuildUpdatePayload: Added author "${author.name}" to "${libraryItem.media.title}"`)
+ libraryItem.media.authors.push(author)
+ hasAuthorUpdates = true
+ })
}
const authorsRemoved = libraryItem.media.authors.filter((a) => !matchData.author.find((ma) => ma.toLowerCase() === a.name.toLowerCase()))
if (authorsRemoved.length) {
diff --git a/server/utils/htmlSanitizer.js b/server/utils/htmlSanitizer.js
index 4ed30e727..be839b7c2 100644
--- a/server/utils/htmlSanitizer.js
+++ b/server/utils/htmlSanitizer.js
@@ -5,11 +5,10 @@ const { entities } = require('./htmlEntities')
*
* @param {string} html
* @returns {string}
- * @throws {Error} if input is not a string
*/
function sanitize(html) {
if (typeof html !== 'string') {
- throw new Error('sanitizeHtml: input must be a string')
+ return ''
}
const sanitizerOptions = {
@@ -27,6 +26,8 @@ function sanitize(html) {
module.exports.sanitize = sanitize
function stripAllTags(html, shouldDecodeEntities = true) {
+ if (typeof html !== 'string') return ''
+
const sanitizerOptions = {
allowedTags: [],
disallowedTagsMode: 'discard'
diff --git a/server/utils/parsers/parseNfoMetadata.js b/server/utils/parsers/parseNfoMetadata.js
index 6682a0078..4276443d0 100644
--- a/server/utils/parsers/parseNfoMetadata.js
+++ b/server/utils/parsers/parseNfoMetadata.js
@@ -22,7 +22,7 @@ function parseNfoMetadata(nfoText) {
switch (key) {
case 'title':
{
- const titleMatch = value.match(/^(.*?):(.*)$/)
+ const titleMatch = value.match(/^(.*?): (.*)$/)
if (titleMatch) {
metadata.title = titleMatch[1].trim()
metadata.subtitle = titleMatch[2].trim()
diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js
index d97a3acc5..e2df87539 100644
--- a/server/utils/queries/libraryItemsBookFilters.js
+++ b/server/utils/queries/libraryItemsBookFilters.js
@@ -236,7 +236,7 @@ module.exports = {
} else if (group === 'publishedDecades') {
const startYear = parseInt(value)
const endYear = parseInt(value, 10) + 9
- mediaWhere = Sequelize.where(Sequelize.literal('CAST(`book`.`publishedYear` AS INTEGER)'), {
+ mediaWhere = Sequelize.where(Sequelize.literal('CAST(publishedYear AS INTEGER)'), {
[Sequelize.Op.between]: [startYear, endYear]
})
}
@@ -891,28 +891,79 @@ module.exports = {
})
}
- // Step 2: Get books not started and not in a series OR is the first book of a series not started (ordered randomly)
- const { rows: books, count } = await Database.bookModel.findAndCountAll({
- where: [
- {
- '$mediaProgresses.isFinished$': {
- [Sequelize.Op.or]: [null, 0]
- },
- '$mediaProgresses.currentTime$': {
- [Sequelize.Op.or]: [null, 0]
- },
- [Sequelize.Op.or]: [
- Sequelize.where(Sequelize.literal(`(SELECT COUNT(*) FROM bookSeries bs where bs.bookId = book.id)`), 0),
- {
- id: {
- [Sequelize.Op.in]: booksFromSeriesToInclude
- }
- }
- ]
+ const discoverWhere = [
+ {
+ '$mediaProgresses.isFinished$': {
+ [Sequelize.Op.or]: [null, 0]
},
- ...userPermissionBookWhere.bookWhere
- ],
+ '$mediaProgresses.currentTime$': {
+ [Sequelize.Op.or]: [null, 0]
+ },
+ [Sequelize.Op.or]: [
+ Sequelize.where(Sequelize.literal(`(SELECT COUNT(*) FROM bookSeries bs where bs.bookId = book.id)`), 0),
+ {
+ id: {
+ [Sequelize.Op.in]: booksFromSeriesToInclude
+ }
+ }
+ ]
+ },
+ ...userPermissionBookWhere.bookWhere
+ ]
+
+ const baseDiscoverInclude = [
+ {
+ model: Database.libraryItemModel,
+ where: {
+ libraryId
+ }
+ },
+ {
+ model: Database.mediaProgressModel,
+ where: {
+ userId: user.id
+ },
+ required: false
+ }
+ ]
+
+ // Step 2a: Count with lightweight includes only
+ const count = await Database.bookModel.count({
+ where: discoverWhere,
replacements: userPermissionBookWhere.replacements,
+ include: baseDiscoverInclude,
+ distinct: true,
+ col: 'id',
+ subQuery: false
+ })
+
+ // Step 2b: Select random IDs with lightweight includes only
+ const randomSelection = await Database.bookModel.findAll({
+ attributes: ['id'],
+ where: discoverWhere,
+ replacements: userPermissionBookWhere.replacements,
+ include: baseDiscoverInclude,
+ subQuery: false,
+ distinct: true,
+ limit,
+ order: Database.sequelize.random()
+ })
+
+ const selectedIds = randomSelection.map((b) => b.id).filter(Boolean)
+ if (!selectedIds.length) {
+ return {
+ libraryItems: [],
+ count
+ }
+ }
+
+ // Step 2c: Hydrate selected IDs with full metadata for API response
+ const books = await Database.bookModel.findAll({
+ where: {
+ id: {
+ [Sequelize.Op.in]: selectedIds
+ }
+ },
include: [
{
model: Database.libraryItemModel,
@@ -921,13 +972,6 @@ module.exports = {
},
include: libraryItemIncludes
},
- {
- model: Database.mediaProgressModel,
- where: {
- userId: user.id
- },
- required: false
- },
{
model: Database.bookAuthorModel,
attributes: ['authorId'],
@@ -945,14 +989,14 @@ module.exports = {
separate: true
}
],
- subQuery: false,
- distinct: true,
- limit,
- order: Database.sequelize.random()
+ subQuery: false
})
+ const booksById = new Map(books.map((b) => [b.id, b]))
+ const orderedBooks = selectedIds.map((id) => booksById.get(id)).filter(Boolean)
+
// Step 3: Map books to library items
- const libraryItems = books.map((bookExpanded) => {
+ const libraryItems = orderedBooks.map((bookExpanded) => {
const libraryItem = bookExpanded.libraryItem
const book = bookExpanded
delete book.libraryItem
diff --git a/test/server/controllers/MeController.test.js b/test/server/controllers/MeController.test.js
new file mode 100644
index 000000000..3cc5496d9
--- /dev/null
+++ b/test/server/controllers/MeController.test.js
@@ -0,0 +1,636 @@
+const { expect } = require('chai')
+const { Sequelize } = require('sequelize')
+const sinon = require('sinon')
+
+const Database = require('../../../server/Database')
+const ApiRouter = require('../../../server/routers/ApiRouter')
+const MeController = require('../../../server/controllers/MeController')
+const Auth = require('../../../server/Auth')
+const Logger = require('../../../server/Logger')
+const SocketAuthority = require('../../../server/SocketAuthority')
+
+describe('MeController - IDOR Security Tests', () => {
+ /** @type {ApiRouter} */
+ let apiRouter
+
+ beforeEach(async () => {
+ global.ServerSettings = {}
+ Database.sequelize = new Sequelize({ dialect: 'sqlite', storage: ':memory:', logging: false })
+ Database.sequelize.uppercaseFirst = (str) => (str ? `${str[0].toUpperCase()}${str.substr(1)}` : '')
+ await Database.buildModels()
+
+ // Create mock server object with required dependencies
+ const mockServer = {
+ auth: new Auth(),
+ playbackSessionManager: { sessions: [] },
+ abMergeManager: {},
+ backupManager: {},
+ podcastManager: {},
+ audioMetadataManager: {},
+ cronManager: {},
+ emailManager: {},
+ apiCacheManager: { middleware: (req, res, next) => next() }
+ }
+
+ apiRouter = new ApiRouter(mockServer)
+
+ sinon.stub(Logger, 'info')
+ sinon.stub(Logger, 'error')
+ sinon.stub(SocketAuthority, 'clientEmitter')
+ })
+
+ afterEach(async () => {
+ sinon.restore()
+
+ // Clear all tables
+ await Database.sequelize.sync({ force: true })
+ })
+
+ describe('removeMediaProgress - IDOR Protection', () => {
+ let user1, user2
+ let mediaProgress1, mediaProgress2
+
+ beforeEach(async () => {
+ // Create two users
+ user1 = await Database.userModel.create({
+ username: 'user1',
+ pash: 'hashed_password_1',
+ type: 'user',
+ isActive: true
+ })
+
+ user2 = await Database.userModel.create({
+ username: 'user2',
+ pash: 'hashed_password_2',
+ type: 'user',
+ isActive: true
+ })
+
+ // Create library and book
+ const library = await Database.libraryModel.create({ name: 'Test Library', mediaType: 'book' })
+ const libraryFolder = await Database.libraryFolderModel.create({ path: '/test', libraryId: library.id })
+ const book = await Database.bookModel.create({ title: 'Test Book', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
+ const libraryItem = await Database.libraryItemModel.create({
+ libraryFiles: [],
+ mediaId: book.id,
+ mediaType: 'book',
+ libraryId: library.id,
+ libraryFolderId: libraryFolder.id
+ })
+
+ // Create media progress for each user
+ mediaProgress1 = await Database.mediaProgressModel.create({
+ userId: user1.id,
+ mediaItemId: book.id,
+ mediaItemType: 'book',
+ duration: 1000,
+ currentTime: 500,
+ isFinished: false
+ })
+
+ mediaProgress2 = await Database.mediaProgressModel.create({
+ userId: user2.id,
+ mediaItemId: book.id,
+ mediaItemType: 'book',
+ duration: 1000,
+ currentTime: 300,
+ isFinished: false
+ })
+
+ // Load media progresses into users
+ user1.mediaProgresses = await user1.getMediaProgresses()
+ user2.mediaProgresses = await user2.getMediaProgresses()
+ })
+
+ it('should allow user to delete their own media progress', async () => {
+ const fakeReq = {
+ user: user1,
+ params: { id: mediaProgress1.id }
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy()
+ }
+
+ await MeController.removeMediaProgress(fakeReq, fakeRes)
+
+ expect(fakeRes.sendStatus.calledWith(200)).to.be.true
+
+ // Verify media progress was deleted
+ const deletedProgress = await Database.mediaProgressModel.findByPk(mediaProgress1.id)
+ expect(deletedProgress).to.be.null
+ })
+
+ it('should prevent user from deleting another users media progress (IDOR)', async () => {
+ const fakeReq = {
+ user: user1,
+ params: { id: mediaProgress2.id } // Trying to delete user2's progress
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy()
+ }
+
+ await MeController.removeMediaProgress(fakeReq, fakeRes)
+
+ expect(fakeRes.sendStatus.calledWith(404)).to.be.true
+
+ // Verify media progress was NOT deleted
+ const existingProgress = await Database.mediaProgressModel.findByPk(mediaProgress2.id)
+ expect(existingProgress).to.not.be.null
+ expect(existingProgress.userId).to.equal(user2.id)
+ })
+
+ it('should return 404 for non-existent media progress', async () => {
+ const fakeReq = {
+ user: user1,
+ params: { id: 'non-existent-id' }
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy()
+ }
+
+ await MeController.removeMediaProgress(fakeReq, fakeRes)
+
+ expect(fakeRes.sendStatus.calledWith(404)).to.be.true
+ })
+ })
+
+ describe('Bookmark Operations - Authorization Checks', () => {
+ let user1, user2
+ let library1, library2
+ let libraryItem1, libraryItem2
+
+ beforeEach(async () => {
+ // Create two users with different library access
+ user1 = await Database.userModel.create({
+ username: 'user1',
+ pash: 'hashed_password_1',
+ type: 'user',
+ isActive: true,
+ librariesAccessible: null // Access to all libraries
+ })
+
+ user2 = await Database.userModel.create({
+ username: 'user2',
+ pash: 'hashed_password_2',
+ type: 'user',
+ isActive: true,
+ librariesAccessible: [] // Will be set to specific library
+ })
+
+ // Create two libraries
+ library1 = await Database.libraryModel.create({ name: 'Library 1', mediaType: 'book' })
+ library2 = await Database.libraryModel.create({ name: 'Library 2', mediaType: 'book' })
+
+ // User2 only has access to library1
+ user2.librariesAccessible = [library1.id]
+ await user2.save()
+
+ const libraryFolder1 = await Database.libraryFolderModel.create({ path: '/test1', libraryId: library1.id })
+ const libraryFolder2 = await Database.libraryFolderModel.create({ path: '/test2', libraryId: library2.id })
+
+ const book1 = await Database.bookModel.create({ title: 'Book 1', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
+ const book2 = await Database.bookModel.create({ title: 'Book 2', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
+
+ libraryItem1 = await Database.libraryItemModel.create({
+ libraryFiles: [],
+ mediaId: book1.id,
+ mediaType: 'book',
+ libraryId: library1.id,
+ libraryFolderId: libraryFolder1.id
+ })
+
+ libraryItem2 = await Database.libraryItemModel.create({
+ libraryFiles: [],
+ mediaId: book2.id,
+ mediaType: 'book',
+ libraryId: library2.id,
+ libraryFolderId: libraryFolder2.id
+ })
+
+ // Initialize bookmarks
+ user1.bookmarks = []
+ user2.bookmarks = []
+ })
+
+ describe('createBookmark', () => {
+ it('should allow user to create bookmark for accessible library item', async () => {
+ const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
+
+ const bookmark = { libraryItemId: libraryItem1.id, time: 100, title: 'Test Bookmark', createdAt: Date.now() }
+
+ const fakeReq = {
+ user: {
+ ...user2.toJSON(),
+ id: user2.id,
+ username: user2.username,
+ checkCanAccessLibraryItem: () => true,
+ createBookmark: sinon.stub().resolves(bookmark),
+ toOldJSONForBrowser: () => ({ id: user2.id, username: user2.username })
+ },
+ params: { id: libraryItem1.id },
+ body: { time: 100, title: 'Test Bookmark' }
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy(),
+ json: sinon.spy()
+ }
+
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
+
+ await MeController.createBookmark(fakeReq, fakeRes)
+
+ expect(fakeRes.json.calledOnce).to.be.true
+ expect(fakeRes.json.calledWith(bookmark)).to.be.true
+
+ Database.libraryItemModel.getExpandedById.restore()
+ })
+
+ it('should prevent user from creating bookmark for inaccessible library item (IDOR)', async () => {
+ const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem2.id)
+
+ const fakeReq = {
+ user: user2, // user2 doesn't have access to library2
+ params: { id: libraryItem2.id },
+ body: { time: 100, title: 'Test Bookmark' }
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy(),
+ json: sinon.spy()
+ }
+
+ // Mock getExpandedById
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
+
+ await MeController.createBookmark(fakeReq, fakeRes)
+
+ expect(fakeRes.sendStatus.calledWith(403)).to.be.true
+ expect(fakeRes.json.called).to.be.false
+
+ Database.libraryItemModel.getExpandedById.restore()
+ })
+
+ it('should return 404 for non-existent library item', async () => {
+ const fakeReq = {
+ user: user1,
+ params: { id: 'non-existent-id' },
+ body: { time: 100, title: 'Test Bookmark' }
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy(),
+ json: sinon.spy()
+ }
+
+ // Mock getExpandedById to return null
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(null)
+
+ await MeController.createBookmark(fakeReq, fakeRes)
+
+ expect(fakeRes.sendStatus.calledWith(404)).to.be.true
+
+ Database.libraryItemModel.getExpandedById.restore()
+ })
+
+ it('should validate bookmark time parameter', async () => {
+ const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
+
+ const fakeReq = {
+ user: {
+ ...user1.toJSON(),
+ id: user1.id,
+ username: user1.username,
+ checkCanAccessLibraryItem: () => true
+ },
+ params: { id: libraryItem1.id },
+ body: { time: null, title: 'Test Bookmark' } // null time is invalid
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy(),
+ json: sinon.spy()
+ }
+
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
+
+ await MeController.createBookmark(fakeReq, fakeRes)
+
+ expect(fakeRes.status.calledWith(400)).to.be.true
+ expect(fakeRes.send.calledWith('Invalid time')).to.be.true
+
+ Database.libraryItemModel.getExpandedById.restore()
+ })
+ })
+
+ describe('updateBookmark', () => {
+ beforeEach(async () => {
+ // Add existing bookmark to user1
+ user1.bookmarks = [{ libraryItemId: libraryItem1.id, time: 100, title: 'Original Title' }]
+ await user1.save()
+ })
+
+ it('should allow user to update bookmark for accessible library item', async () => {
+ const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
+
+ const bookmark = { libraryItemId: libraryItem1.id, time: 100, title: 'Updated Title' }
+
+ const fakeReq = {
+ user: {
+ ...user1.toJSON(),
+ id: user1.id,
+ username: user1.username,
+ checkCanAccessLibraryItem: () => true,
+ updateBookmark: sinon.stub().resolves(bookmark),
+ toOldJSONForBrowser: () => ({ id: user1.id, username: user1.username })
+ },
+ params: { id: libraryItem1.id },
+ body: { time: 100, title: 'Updated Title' }
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy(),
+ json: sinon.spy()
+ }
+
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
+
+ await MeController.updateBookmark(fakeReq, fakeRes)
+
+ expect(fakeRes.json.calledOnce).to.be.true
+ expect(fakeRes.json.calledWith(bookmark)).to.be.true
+
+ Database.libraryItemModel.getExpandedById.restore()
+ })
+
+ it('should prevent user from updating bookmark for inaccessible library item (IDOR)', async () => {
+ const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem2.id)
+
+ const fakeReq = {
+ user: user2, // user2 doesn't have access to library2
+ params: { id: libraryItem2.id },
+ body: { time: 100, title: 'Updated Title' }
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy(),
+ json: sinon.spy()
+ }
+
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
+
+ await MeController.updateBookmark(fakeReq, fakeRes)
+
+ expect(fakeRes.sendStatus.calledWith(403)).to.be.true
+
+ Database.libraryItemModel.getExpandedById.restore()
+ })
+ })
+
+ describe('removeBookmark', () => {
+ beforeEach(async () => {
+ // Add existing bookmark to user1
+ user1.bookmarks = [{ libraryItemId: libraryItem1.id, time: 100, title: 'Test Bookmark' }]
+ await user1.save()
+ })
+
+ it('should allow user to remove bookmark for accessible library item', async () => {
+ const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
+
+ const fakeReq = {
+ user: {
+ ...user1.toJSON(),
+ id: user1.id,
+ username: user1.username,
+ checkCanAccessLibraryItem: () => true,
+ findBookmark: sinon.stub().returns({ libraryItemId: libraryItem1.id, time: 100, title: 'Test Bookmark' }),
+ removeBookmark: sinon.stub().resolves(true),
+ toOldJSONForBrowser: () => ({ id: user1.id, username: user1.username })
+ },
+ params: { id: libraryItem1.id, time: '100' }
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy()
+ }
+
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
+
+ await MeController.removeBookmark(fakeReq, fakeRes)
+
+ expect(fakeRes.sendStatus.calledWith(200)).to.be.true
+
+ Database.libraryItemModel.getExpandedById.restore()
+ })
+
+ it('should prevent user from removing bookmark for inaccessible library item (IDOR)', async () => {
+ const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem2.id)
+
+ const fakeReq = {
+ user: user2, // user2 doesn't have access to library2
+ params: { id: libraryItem2.id, time: '100' }
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy()
+ }
+
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
+
+ await MeController.removeBookmark(fakeReq, fakeRes)
+
+ expect(fakeRes.sendStatus.calledWith(403)).to.be.true
+
+ Database.libraryItemModel.getExpandedById.restore()
+ })
+
+ it('should validate time parameter is a number', async () => {
+ const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
+
+ const fakeReq = {
+ user: {
+ ...user1.toJSON(),
+ id: user1.id,
+ username: user1.username,
+ checkCanAccessLibraryItem: () => true
+ },
+ params: { id: libraryItem1.id, time: 'not-a-number' }
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy()
+ }
+
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
+
+ await MeController.removeBookmark(fakeReq, fakeRes)
+
+ expect(fakeRes.status.calledWith(400)).to.be.true
+ expect(fakeRes.send.calledWith('Invalid time')).to.be.true
+
+ Database.libraryItemModel.getExpandedById.restore()
+ })
+ })
+ })
+
+ describe('getItemListeningSessions - Authorization Check', () => {
+ let user1, user2
+ let library1, library2
+ let libraryItem1, libraryItem2
+
+ beforeEach(async () => {
+ // Create two users with different library access
+ user1 = await Database.userModel.create({
+ username: 'user1',
+ pash: 'hashed_password_1',
+ type: 'user',
+ isActive: true,
+ librariesAccessible: null // Access to all libraries
+ })
+
+ user2 = await Database.userModel.create({
+ username: 'user2',
+ pash: 'hashed_password_2',
+ type: 'user',
+ isActive: true,
+ librariesAccessible: [] // Will be set to specific library
+ })
+
+ // Create two libraries
+ library1 = await Database.libraryModel.create({ name: 'Library 1', mediaType: 'book' })
+ library2 = await Database.libraryModel.create({ name: 'Library 2', mediaType: 'book' })
+
+ // User2 only has access to library1
+ user2.librariesAccessible = [library1.id]
+ await user2.save()
+
+ const libraryFolder1 = await Database.libraryFolderModel.create({ path: '/test1', libraryId: library1.id })
+ const libraryFolder2 = await Database.libraryFolderModel.create({ path: '/test2', libraryId: library2.id })
+
+ const book1 = await Database.bookModel.create({ title: 'Book 1', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
+ const book2 = await Database.bookModel.create({ title: 'Book 2', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
+
+ libraryItem1 = await Database.libraryItemModel.create({
+ libraryFiles: [],
+ mediaId: book1.id,
+ mediaType: 'book',
+ libraryId: library1.id,
+ libraryFolderId: libraryFolder1.id
+ })
+
+ libraryItem2 = await Database.libraryItemModel.create({
+ libraryFiles: [],
+ mediaId: book2.id,
+ mediaType: 'book',
+ libraryId: library2.id,
+ libraryFolderId: libraryFolder2.id
+ })
+ })
+
+ it('should allow user to view listening sessions for accessible library item', async () => {
+ const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
+
+ // Create mock context with getUserItemListeningSessionsHelper
+ const mockContext = {
+ getUserItemListeningSessionsHelper: sinon.stub().resolves([{ id: 'session1', timeListening: 300, startedAt: Date.now() }])
+ }
+
+ const fakeReq = {
+ user: {
+ ...user1.toJSON(),
+ id: user1.id,
+ username: user1.username,
+ checkCanAccessLibraryItem: () => true
+ },
+ params: { libraryItemId: libraryItem1.id },
+ query: {}
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy(),
+ json: sinon.spy()
+ }
+
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
+ sinon.stub(Database.podcastEpisodeModel, 'findByPk').resolves(null)
+
+ await MeController.getItemListeningSessions.bind(mockContext)(fakeReq, fakeRes)
+
+ expect(fakeRes.json.calledOnce).to.be.true
+ expect(fakeRes.sendStatus.called).to.be.false
+
+ // Verify the payload structure
+ const payload = fakeRes.json.firstCall.args[0]
+ expect(payload).to.have.property('total')
+ expect(payload).to.have.property('sessions')
+
+ Database.libraryItemModel.getExpandedById.restore()
+ Database.podcastEpisodeModel.findByPk.restore()
+ })
+
+ it('should prevent user from viewing listening sessions for inaccessible library item (IDOR)', async () => {
+ const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem2.id)
+
+ const fakeReq = {
+ user: user2, // user2 doesn't have access to library2
+ params: { libraryItemId: libraryItem2.id },
+ query: {}
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy(),
+ json: sinon.spy()
+ }
+
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
+ sinon.stub(Database.podcastEpisodeModel, 'findByPk').resolves(null)
+
+ await MeController.getItemListeningSessions.bind(apiRouter)(fakeReq, fakeRes)
+
+ expect(fakeRes.sendStatus.calledWith(403)).to.be.true
+ expect(fakeRes.json.called).to.be.false
+
+ Database.libraryItemModel.getExpandedById.restore()
+ Database.podcastEpisodeModel.findByPk.restore()
+ })
+
+ it('should return 404 for non-existent library item', async () => {
+ const fakeReq = {
+ user: user1,
+ params: { libraryItemId: 'non-existent-id' },
+ query: {}
+ }
+ const fakeRes = {
+ sendStatus: sinon.spy(),
+ status: sinon.stub().returnsThis(),
+ send: sinon.spy(),
+ json: sinon.spy()
+ }
+
+ sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(null)
+
+ await MeController.getItemListeningSessions.bind(apiRouter)(fakeReq, fakeRes)
+
+ expect(fakeRes.sendStatus.calledWith(404)).to.be.true
+
+ Database.libraryItemModel.getExpandedById.restore()
+ })
+ })
+})
diff --git a/test/server/finders/BookFinder.test.js b/test/server/finders/BookFinder.test.js
index 6578ca82a..cb697b81a 100644
--- a/test/server/finders/BookFinder.test.js
+++ b/test/server/finders/BookFinder.test.js
@@ -35,7 +35,10 @@ describe('TitleCandidates', () => {
['adds candidate, removing author', `anna karenina by ${cleanAuthor}`, ['anna karenina']],
['does not add empty candidate after removing author', cleanAuthor, []],
['adds candidate, removing subtitle', 'anna karenina: subtitle', ['anna karenina']],
+ ['adds candidate, not stripping subtitle for bare colon in title', '10:04', ['10:04']],
+ ['adds candidate, not stripping subtitle for colon between words without space', 'making the mission:impossible movies', ['making the mission:impossible movies']],
['adds candidate + variant, removing "by ..."', 'anna karenina by arnold schwarzenegger', ['anna karenina', 'anna karenina by arnold schwarzenegger']],
+ ['adds candidate + variant, removing "by ..." when title has bare colon', '10:04 by ben lerner', ['10:04', '10:04 by ben lerner']],
['adds candidate + variant, removing bitrate', 'anna karenina 64kbps', ['anna karenina', 'anna karenina 64kbps']],
['adds candidate + variant, removing edition 1', 'anna karenina 2nd edition', ['anna karenina', 'anna karenina 2nd edition']],
['adds candidate + variant, removing edition 2', 'anna karenina 4th ed.', ['anna karenina', 'anna karenina 4th ed.']],
diff --git a/test/server/utils/parsers/parseNfoMetadata.test.js b/test/server/utils/parsers/parseNfoMetadata.test.js
index 9ff51fbe5..ba11ddff2 100644
--- a/test/server/utils/parsers/parseNfoMetadata.test.js
+++ b/test/server/utils/parsers/parseNfoMetadata.test.js
@@ -21,6 +21,20 @@ describe('parseNfoMetadata', () => {
expect(result.subtitle).to.equal('A Novel')
})
+ it('does not split title on bare colon without space', () => {
+ const nfoText = 'Title: 10:04'
+ const result = parseNfoMetadata(nfoText)
+ expect(result.title).to.equal('10:04')
+ expect(result.subtitle).to.be.undefined
+ })
+
+ it('does not split title on colon between words without space', () => {
+ const nfoText = 'Title: Making the Mission:Impossible Movies'
+ const result = parseNfoMetadata(nfoText)
+ expect(result.title).to.equal('Making the Mission:Impossible Movies')
+ expect(result.subtitle).to.be.undefined
+ })
+
it('parses authors', () => {
const nfoText = 'Author: F. Scott Fitzgerald'
const result = parseNfoMetadata(nfoText)