diff --git a/API.Tests/Helpers/SmartFilterHelperTests.cs b/API.Tests/Helpers/SmartFilterHelperTests.cs index 5508ab1a7..974cb0ba6 100644 --- a/API.Tests/Helpers/SmartFilterHelperTests.cs +++ b/API.Tests/Helpers/SmartFilterHelperTests.cs @@ -44,6 +44,17 @@ public class SmartFilterHelperTests AssertStatementSame(list[0], FilterField.Genres, FilterComparison.Equal, "95"); } + [Fact] + public void Test_Decode2() + { + const string encoded = """ + name=Test%202&stmts=comparison%253D10%25C2%25A6field%253D1%25C2%25A6value%253DA%EF%BF%BDcomparison%253D0%25C2%25A6field%253D19%25C2%25A6value%253D11&sortOptions=sortField%3D1%C2%A6isAscending%3DTrue&limitTo=0&combination=1 + """; + + var filter = SmartFilterHelper.Decode(encoded); + Assert.True(filter.SortOptions.IsAscending); + } + [Fact] public void Test_EncodeDecode() { diff --git a/API.Tests/Parser/MangaParserTests.cs b/API.Tests/Parser/MangaParserTests.cs index f89a411e6..5ba6a35b7 100644 --- a/API.Tests/Parser/MangaParserTests.cs +++ b/API.Tests/Parser/MangaParserTests.cs @@ -83,6 +83,7 @@ public class MangaParserTests [InlineData("시즌34삽화2", "34")] [InlineData("Accel World Chapter 001 Volume 002", "2")] [InlineData("Accel World Volume 2", "2")] + [InlineData("Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.31 Omake", "30")] public void ParseVolumeTest(string filename, string expected) { Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseVolume(filename)); @@ -204,6 +205,7 @@ public class MangaParserTests [InlineData("죠시라쿠! 2년 후 1권", "죠시라쿠! 2년 후")] [InlineData("test 2 years 1권", "test 2 years")] [InlineData("test 2 years 1화", "test 2 years")] + [InlineData("Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.30 Omake", "Nagasarete Airantou")] public void ParseSeriesTest(string filename, string expected) { Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseSeries(filename)); diff --git a/API.Tests/Services/ScrobblingServiceTests.cs b/API.Tests/Services/ScrobblingServiceTests.cs index 2d40c5211..d460ee4e5 100644 --- a/API.Tests/Services/ScrobblingServiceTests.cs +++ b/API.Tests/Services/ScrobblingServiceTests.cs @@ -2,13 +2,23 @@ using Xunit; namespace API.Tests.Services; +#nullable enable public class ScrobblingServiceTests { [Theory] [InlineData("https://anilist.co/manga/35851/Byeontaega-Doeja/", 35851)] - public void CanParseWeblink(string link, long expectedId) + [InlineData("https://anilist.co/manga/30105", 30105)] + [InlineData("https://anilist.co/manga/30105/Kekkaishi/", 30105)] + public void CanParseWeblink_AniList(string link, int? expectedId) { - Assert.Equal(ScrobblingService.ExtractId(link, ScrobblingService.AniListWeblinkWebsite), expectedId); + Assert.Equal(ScrobblingService.ExtractId(link, ScrobblingService.AniListWeblinkWebsite), expectedId); + } + + [Theory] + [InlineData("https://mangadex.org/title/316d3d09-bb83-49da-9d90-11dc7ce40967/honzuki-no-gekokujou-shisho-ni-naru-tame-ni-wa-shudan-wo-erandeiraremasen-dai-3-bu-ryouchi-ni-hon-o", "316d3d09-bb83-49da-9d90-11dc7ce40967")] + public void CanParseWeblink_MangaDex(string link, string expectedId) + { + Assert.Equal(ScrobblingService.ExtractId(link, ScrobblingService.MangaDexWeblinkWebsite), expectedId); } } diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs index 576011b91..dce22c979 100644 --- a/API/Controllers/AccountController.cs +++ b/API/Controllers/AccountController.cs @@ -26,6 +26,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.RateLimiting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using SharpCompress; namespace API.Controllers; @@ -137,8 +138,7 @@ public class AccountController : BaseApiController if (!result.Succeeded) return BadRequest(result.Errors); // Assign default streams - user.DashboardStreams = Seed.DefaultStreams.ToList(); - user.SideNavStreams = Seed.DefaultSideNavStreams.ToList(); + AddDefaultStreamsToUser(user); var token = await _userManager.GenerateEmailConfirmationTokenAsync(user); if (string.IsNullOrEmpty(token)) return BadRequest(await _localizationService.Get("en", "confirm-token-gen")); @@ -608,7 +608,8 @@ public class AccountController : BaseApiController } // Create a new user - var user = new AppUserBuilder(dto.Email, dto.Email, await _unitOfWork.SiteThemeRepository.GetDefaultTheme()).Build(); + var user = new AppUserBuilder(dto.Email, dto.Email, + await _unitOfWork.SiteThemeRepository.GetDefaultTheme()).Build(); _unitOfWork.UserRepository.Add(user); try { @@ -616,9 +617,7 @@ public class AccountController : BaseApiController if (!result.Succeeded) return BadRequest(result.Errors); // Assign default streams - user.DashboardStreams = Seed.DefaultStreams.ToList(); - user.SideNavStreams = Seed.DefaultSideNavStreams.ToList(); - + AddDefaultStreamsToUser(user); // Assign Roles var roles = dto.Roles; @@ -657,7 +656,6 @@ public class AccountController : BaseApiController user.CreateSideNavFromLibrary(lib); } - _unitOfWork.UserRepository.Update(user); user.AgeRestriction = hasAdminRole ? AgeRating.NotApplicable : dto.AgeRestriction.AgeRating; user.AgeRestrictionIncludeUnknowns = hasAdminRole || dto.AgeRestriction.IncludeUnknowns; @@ -669,6 +667,7 @@ public class AccountController : BaseApiController } user.ConfirmationToken = token; + _unitOfWork.UserRepository.Update(user); await _unitOfWork.CommitAsync(); } catch (Exception ex) @@ -702,7 +701,7 @@ public class AccountController : BaseApiController BackgroundJob.Enqueue(() => _emailService.SendConfirmationEmail(new ConfirmationEmailDto() { EmailAddress = dto.Email, - InvitingUser = adminUser.UserName!, + InvitingUser = adminUser.UserName, ServerConfirmationLink = emailLink })); } @@ -721,6 +720,19 @@ public class AccountController : BaseApiController return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-invite-user")); } + private void AddDefaultStreamsToUser(AppUser user) + { + foreach (var newStream in Seed.DefaultStreams.Select(stream => _mapper.Map(stream))) + { + user.DashboardStreams.Add(newStream); + } + + foreach (var stream in Seed.DefaultSideNavStreams.Select(stream => _mapper.Map(stream))) + { + user.SideNavStreams.Add(stream); + } + } + /// /// Last step in authentication flow, confirms the email token for email /// diff --git a/API/Controllers/ReaderController.cs b/API/Controllers/ReaderController.cs index dcee9b62d..89078487d 100644 --- a/API/Controllers/ReaderController.cs +++ b/API/Controllers/ReaderController.cs @@ -115,10 +115,6 @@ public class ReaderController : BaseApiController try { - if (new Random().Next(1, 10) > 5) - { - await Task.Delay(1000); - } var chapter = await _cacheService.Ensure(chapterId, extractPdf); if (chapter == null) return NoContent(); _logger.LogInformation("Fetching Page {PageNum} on Chapter {ChapterId}", page, chapterId); diff --git a/API/Data/Seed.cs b/API/Data/Seed.cs index b807e4dcd..c5fe643ea 100644 --- a/API/Data/Seed.cs +++ b/API/Data/Seed.cs @@ -76,48 +76,42 @@ public static class Seed }, }.ToArray()); - public static readonly ImmutableArray DefaultSideNavStreams = ImmutableArray.Create(new[] + public static readonly ImmutableArray DefaultSideNavStreams = ImmutableArray.Create( + new AppUserSideNavStream() { - new AppUserSideNavStream() - { - Name = "want-to-read", - StreamType = SideNavStreamType.WantToRead, - Order = 1, - IsProvided = true, - Visible = true - }, - new AppUserSideNavStream() - { - Name = "collections", - StreamType = SideNavStreamType.Collections, - Order = 2, - IsProvided = true, - Visible = true - }, - new AppUserSideNavStream() - { - Name = "reading-lists", - StreamType = SideNavStreamType.ReadingLists, - Order = 3, - IsProvided = true, - Visible = true - }, - new AppUserSideNavStream() - { - Name = "bookmarks", - StreamType = SideNavStreamType.Bookmarks, - Order = 4, - IsProvided = true, - Visible = true - }, - new AppUserSideNavStream() - { - Name = "all-series", - StreamType = SideNavStreamType.AllSeries, - Order = 5, - IsProvided = true, - Visible = true - } + Name = "want-to-read", + StreamType = SideNavStreamType.WantToRead, + Order = 1, + IsProvided = true, + Visible = true + }, new AppUserSideNavStream() + { + Name = "collections", + StreamType = SideNavStreamType.Collections, + Order = 2, + IsProvided = true, + Visible = true + }, new AppUserSideNavStream() + { + Name = "reading-lists", + StreamType = SideNavStreamType.ReadingLists, + Order = 3, + IsProvided = true, + Visible = true + }, new AppUserSideNavStream() + { + Name = "bookmarks", + StreamType = SideNavStreamType.Bookmarks, + Order = 4, + IsProvided = true, + Visible = true + }, new AppUserSideNavStream() + { + Name = "all-series", + StreamType = SideNavStreamType.AllSeries, + Order = 5, + IsProvided = true, + Visible = true }); diff --git a/API/Helpers/AutoMapperProfiles.cs b/API/Helpers/AutoMapperProfiles.cs index 77ab29396..8d0a4fa43 100644 --- a/API/Helpers/AutoMapperProfiles.cs +++ b/API/Helpers/AutoMapperProfiles.cs @@ -240,9 +240,10 @@ public class AutoMapperProfiles : Profile CreateMap(); CreateMap(); - // CreateMap() - // .ForMember(dest => dest.SmartFilterEncoded, - // opt => opt.MapFrom(src => src.SmartFilter)); + + // This is for cloning to ensure the records don't get overwritten when setting from SeedData + CreateMap(); + CreateMap(); } } diff --git a/API/Helpers/Builders/PlusSeriesDtoBuilder.cs b/API/Helpers/Builders/PlusSeriesDtoBuilder.cs index b379242ac..f1c43df84 100644 --- a/API/Helpers/Builders/PlusSeriesDtoBuilder.cs +++ b/API/Helpers/Builders/PlusSeriesDtoBuilder.cs @@ -27,6 +27,8 @@ public class PlusSeriesDtoBuilder : IEntityBuilder ScrobblingService.MalWeblinkWebsite), GoogleBooksId = ScrobblingService.ExtractId(series.Metadata.WebLinks, ScrobblingService.GoogleBooksWeblinkWebsite), + MangaDexId = ScrobblingService.ExtractId(series.Metadata.WebLinks, + ScrobblingService.MangaDexWeblinkWebsite), VolumeCount = series.Volumes.Count, ChapterCount = series.Volumes.SelectMany(v => v.Chapters).Count(c => !c.IsSpecial), Year = series.Metadata.ReleaseYear diff --git a/API/Helpers/SmartFilterHelper.cs b/API/Helpers/SmartFilterHelper.cs index 0749cb29e..4b8e0c8f6 100644 --- a/API/Helpers/SmartFilterHelper.cs +++ b/API/Helpers/SmartFilterHelper.cs @@ -133,7 +133,7 @@ public static class SmartFilterHelper var sortFieldPart = parts.FirstOrDefault(part => part.StartsWith(SortFieldKey)); var isAscendingPart = parts.FirstOrDefault(part => part.StartsWith(IsAscendingKey)); - var isAscending = isAscendingPart?.Substring(11).Equals("true", StringComparison.OrdinalIgnoreCase) ?? false; + var isAscending = isAscendingPart?.Trim().Replace(IsAscendingKey, string.Empty).Equals("true", StringComparison.OrdinalIgnoreCase) ?? false; if (sortFieldPart == null) { return new SortOptions(); diff --git a/API/I18N/fr.json b/API/I18N/fr.json index e4aef00c1..ded3f9fb0 100644 --- a/API/I18N/fr.json +++ b/API/I18N/fr.json @@ -1,6 +1,6 @@ { "register-user": "Quelque chose s'est mal passé lors de l'enregistrement de l'utilisateur", - "denied": "Non autorisé", + "denied": "Interdit", "permission-denied": "Vous n'êtes pas autorisé à cette opération", "disabled-account": "Votre compte a été désactivé. Veuillez contacter un administrateur.", "confirm-email": "Vous devez d'abord confirmer votre email", diff --git a/API/I18N/it.json b/API/I18N/it.json index 329086d1f..35795157a 100644 --- a/API/I18N/it.json +++ b/API/I18N/it.json @@ -170,5 +170,7 @@ "external-sources": "Sorgenti Esterne", "external-source-required": "ApiKey e Host sono obbligatori", "external-source-already-in-use": "Esiste uno stream esistente con questa Sorgente Esterna", - "smart-filter-already-in-use": "Esiste uno stream esistente con questo Filtro Intelligente" + "smart-filter-already-in-use": "Esiste uno stream esistente con questo Filtro Intelligente", + "dashboard-stream-doesnt-exist": "Dashboard Stream non esiste", + "sidenav-stream-doesnt-exist": "SideNav Stream non esiste" } diff --git a/API/Services/Plus/RecommendationService.cs b/API/Services/Plus/RecommendationService.cs index d5dd67231..1a6a1b315 100644 --- a/API/Services/Plus/RecommendationService.cs +++ b/API/Services/Plus/RecommendationService.cs @@ -25,6 +25,7 @@ public record PlusSeriesDto public int? AniListId { get; set; } public long? MalId { get; set; } public string? GoogleBooksId { get; set; } + public string? MangaDexId { get; set; } public string SeriesName { get; set; } public string? AltSeriesName { get; set; } public MediaFormat MediaFormat { get; set; } diff --git a/API/Services/Plus/ScrobblingService.cs b/API/Services/Plus/ScrobblingService.cs index 4f679fb2b..d33ccfb19 100644 --- a/API/Services/Plus/ScrobblingService.cs +++ b/API/Services/Plus/ScrobblingService.cs @@ -67,13 +67,14 @@ public class ScrobblingService : IScrobblingService public const string AniListWeblinkWebsite = "https://anilist.co/manga/"; public const string MalWeblinkWebsite = "https://myanimelist.net/manga/"; public const string GoogleBooksWeblinkWebsite = "https://books.google.com/books?id="; + public const string MangaDexWeblinkWebsite = "https://mangadex.org/title/"; private static readonly IDictionary WeblinkExtractionMap = new Dictionary() { {AniListWeblinkWebsite, 0}, {MalWeblinkWebsite, 0}, {GoogleBooksWeblinkWebsite, 0}, - + {MangaDexWeblinkWebsite, 0}, }; private const int ScrobbleSleepTime = 700; // We can likely tie this to AniList's 90 rate / min ((60 * 1000) / 90) @@ -829,12 +830,12 @@ public class ScrobblingService : IScrobblingService if (!webLink.StartsWith(website)) continue; var tokens = webLink.Split(website)[1].Split('/'); var value = tokens[index]; - if (typeof(T) == typeof(int)) + if (typeof(T) == typeof(int?)) { if (int.TryParse(value, out var intValue)) return (T)(object)intValue; } - else if (typeof(T) == typeof(long)) + else if (typeof(T) == typeof(long?)) { if (long.TryParse(value, out var longValue)) return (T)(object)longValue; diff --git a/API/Services/TaskScheduler.cs b/API/Services/TaskScheduler.cs index a7a309844..14f24b30e 100644 --- a/API/Services/TaskScheduler.cs +++ b/API/Services/TaskScheduler.cs @@ -34,7 +34,6 @@ public interface ITaskScheduler void ScanSiteThemes(); void CovertAllCoversToEncoding(); Task CleanupDbEntries(); - Task ScrobbleUpdates(int userId); } public class TaskScheduler : ITaskScheduler @@ -141,7 +140,6 @@ public class TaskScheduler : ITaskScheduler } RecurringJob.AddOrUpdate(CleanupTaskId, () => _cleanupService.Cleanup(), Cron.Daily, RecurringJobOptions); - RecurringJob.AddOrUpdate(CleanupDbTaskId, () => _cleanupService.CleanupDbEntries(), Cron.Daily, RecurringJobOptions); RecurringJob.AddOrUpdate(RemoveFromWantToReadTaskId, () => _cleanupService.CleanupWantToRead(), Cron.Daily, RecurringJobOptions); RecurringJob.AddOrUpdate(UpdateYearlyStatsTaskId, () => _statisticService.UpdateServerStatistics(), Cron.Monthly, RecurringJobOptions); @@ -272,16 +270,6 @@ public class TaskScheduler : ITaskScheduler await _cleanupService.CleanupDbEntries(); } - /// - /// TODO: Remove this for Release - /// - /// - public async Task ScrobbleUpdates(int userId) - { - if (!await _licenseService.HasActiveLicense()) return; - BackgroundJob.Enqueue(() => _scrobblingService.ProcessUpdatesSinceLastSync()); - } - /// /// Attempts to call ScanLibraries on ScannerService, but if another scan task is in progress, will reschedule the invocation for 3 hours in future. /// diff --git a/API/Services/Tasks/BackupService.cs b/API/Services/Tasks/BackupService.cs index 99e921c50..a95b9f108 100644 --- a/API/Services/Tasks/BackupService.cs +++ b/API/Services/Tasks/BackupService.cs @@ -9,6 +9,7 @@ using API.Entities.Enums; using API.Logging; using API.SignalR; using Hangfire; +using Kavita.Common.EnvironmentInfo; using Microsoft.Extensions.Logging; namespace API.Services.Tasks; @@ -91,7 +92,7 @@ public class BackupService : IBackupService await SendProgress(0.1F, "Copying core files"); var dateString = $"{DateTime.UtcNow.ToShortDateString()}_{DateTime.UtcNow.ToLongTimeString()}".Replace("/", "_").Replace(":", "_"); - var zipPath = _directoryService.FileSystem.Path.Join(backupDirectory, $"kavita_backup_{dateString}.zip"); + var zipPath = _directoryService.FileSystem.Path.Join(backupDirectory, $"kavita_backup_{dateString}_v{BuildInfo.Version}.zip"); if (File.Exists(zipPath)) { diff --git a/API/Services/Tasks/CleanupService.cs b/API/Services/Tasks/CleanupService.cs index c5b908f73..3d657a929 100644 --- a/API/Services/Tasks/CleanupService.cs +++ b/API/Services/Tasks/CleanupService.cs @@ -92,6 +92,8 @@ public class CleanupService : ICleanupService await CleanupLogs(); await SendProgress(0.9F, "Cleaning progress events that exceed 100%"); await EnsureChapterProgressIsCapped(); + await SendProgress(0.95F, "Cleaning abandoned database rows"); + await CleanupDbEntries(); await SendProgress(1F, "Cleanup finished"); _logger.LogInformation("Cleanup finished"); } diff --git a/API/Services/Tasks/Scanner/Parser/Parser.cs b/API/Services/Tasks/Scanner/Parser/Parser.cs index 8753cd2ca..0b71ec67b 100644 --- a/API/Services/Tasks/Scanner/Parser/Parser.cs +++ b/API/Services/Tasks/Scanner/Parser/Parser.cs @@ -109,9 +109,9 @@ public static class Parser new Regex( @"(?.*)(\b|_)v(?\d+-?\d+)( |_)", MatchOptions, RegexTimeout), - // NEEDLESS_Vol.4_-Simeon_6_v2[SugoiSugoi].rar + // Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.31 Omake new Regex( - @"(?.*)(\b|_)(?!\[)(vol\.?)(?\d+(-\d+)?)(?!\])", + @"^(?.+?)(\s*Chapter\s*\d+)?(\s|_|\-\s)+(Vol(ume)?\.?(\s|_)?)(?\d+(\.\d+)?)(.+?|$)", MatchOptions, RegexTimeout), // Historys Strongest Disciple Kenichi_v11_c90-98.zip or Dance in the Vampire Bund v16-17 new Regex( @@ -137,6 +137,7 @@ public static class Parser new Regex( @"(vol_)(?\d+(\.\d)?)", MatchOptions, RegexTimeout), + // Chinese Volume: 第n卷 -> Volume n, 第n册 -> Volume n, 幽游白书完全版 第03卷 天下 or 阿衰online 第1册 new Regex( @"第(?\d+)(卷|册)", @@ -197,16 +198,17 @@ public static class Parser new Regex( @"(?.*)(\b|_|-|\s)(?:sp)\d", MatchOptions, RegexTimeout), - // [SugoiSugoi]_NEEDLESS_Vol.2_-_Disk_The_Informant_5_[ENG].rar, Yuusha Ga Shinda! - Vol.tbd Chapter 27.001 V2 Infection ①.cbz - new Regex( - @"^(?.*)( |_)Vol\.?(\d+|tbd)", - MatchOptions, RegexTimeout), // Mad Chimera World - Volume 005 - Chapter 026.cbz (couldn't figure out how to get Volume negative lookaround working on below regex), // The Duke of Death and His Black Maid - Vol. 04 Ch. 054.5 - V4 Omake new Regex( @"(?.+?)(\s|_|-)+(?:Vol(ume|\.)?(\s|_|-)+\d+)(\s|_|-)+(?:(Ch|Chapter|Ch)\.?)(\s|_|-)+(?\d+)", MatchOptions, RegexTimeout), + // [SugoiSugoi]_NEEDLESS_Vol.2_-_Disk_The_Informant_5_[ENG].rar, Yuusha Ga Shinda! - Vol.tbd Chapter 27.001 V2 Infection ①.cbz, + // Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.30 Omake + new Regex( + @"^(?.+?)(\s*Chapter\s*\d+)?(\s|_|\-\s)+Vol(ume)?\.?(\d+|tbd|\s\d).+?", + MatchOptions, RegexTimeout), // Ichiban_Ushiro_no_Daimaou_v04_ch34_[VISCANS].zip, VanDread-v01-c01.zip new Regex( @"(?.*)(\b|_)v(?\d+-?\d*)(\s|_|-)", @@ -233,6 +235,7 @@ public static class Parser @"(?.+?):?(\s|\b|_|-)Chapter(\s|\b|_|-)\d+(\s|\b|_|-)(vol)(ume)", MatchOptions, RegexTimeout), + // [xPearse] Kyochuu Rettou Volume 1 [English] [Manga] [Volume Scans] new Regex( @"(?.+?):? (\b|_|-)(vol)(ume)", diff --git a/API/config/config.7z b/API/config/config.7z deleted file mode 100644 index 7415012b2..000000000 Binary files a/API/config/config.7z and /dev/null differ diff --git a/Kavita.Common/Kavita.Common.csproj b/Kavita.Common/Kavita.Common.csproj index 0179e5a54..9890a323b 100644 --- a/Kavita.Common/Kavita.Common.csproj +++ b/Kavita.Common/Kavita.Common.csproj @@ -4,7 +4,7 @@ net7.0 kavitareader.com Kavita - 0.7.11.1 + 0.7.11.2 en true @@ -20,5 +20,4 @@ - \ No newline at end of file diff --git a/README.md b/README.md index 5cbd43675..5380a52bf 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ install methods and platforms. **Note: Kavita is under heavy development and is being updated all the time, so the tag for bleeding edge builds is `:nightly`. The `:latest` tag will be the latest stable release.** ## Feature Requests -Got a great idea? Throw it up on our [Feature Request site](https://feats.kavitareader.com/) or vote on another idea. Please check the [Project Board](https://github.com/Kareadita/Kavita/projects) first for a list of planned features before you submit an idea. +Got a great idea? Throw it up on our [Feature Request site](https://feats.kavitareader.com/) or vote on another idea. Please check the [Project Board](https://github.com/Kareadita/Kavita/projects?type=classic) first for a list of planned features before you submit an idea. ## Notice Kavita is being actively developed and should be considered beta software until the 1.0 release. diff --git a/UI/Web/src/app/_pipes/utc-to-local-time.pipe.ts b/UI/Web/src/app/_pipes/utc-to-local-time.pipe.ts index a9be8e300..22c8c4639 100644 --- a/UI/Web/src/app/_pipes/utc-to-local-time.pipe.ts +++ b/UI/Web/src/app/_pipes/utc-to-local-time.pipe.ts @@ -15,7 +15,8 @@ type UtcToLocalTimeFormat = 'full' | 'short' | 'shortDate' | 'shortTime'; }) export class UtcToLocalTimePipe implements PipeTransform { - transform(utcDate: string, format: UtcToLocalTimeFormat = 'short'): string { + transform(utcDate: string | undefined | null, format: UtcToLocalTimeFormat = 'short'): string { + if (utcDate === undefined || utcDate === null) return ''; const browserLanguage = navigator.language; const dateTime = DateTime.fromISO(utcDate, { zone: 'utc' }).toLocal().setLocale(browserLanguage); diff --git a/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html b/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html index 76017a435..5816a1c28 100644 --- a/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html +++ b/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html @@ -58,7 +58,9 @@ {{task.title | titlecase}} - {{task.lastExecutionUtc | utcToLocalTime | defaultValue }} + + {{task.lastExecutionUtc | utcToLocalTime | defaultValue }} + {{task.cron}} diff --git a/UI/Web/src/app/manga-reader/_components/infinite-scroller/infinite-scroller.component.html b/UI/Web/src/app/manga-reader/_components/infinite-scroller/infinite-scroller.component.html index ceba55c95..72f94d2aa 100644 --- a/UI/Web/src/app/manga-reader/_components/infinite-scroller/infinite-scroller.component.html +++ b/UI/Web/src/app/manga-reader/_components/infinite-scroller/infinite-scroller.component.html @@ -31,7 +31,7 @@ image + (load)="onImageLoad($event)" (touchstart)="onTouchStart(item.page)" (click)="onTouchStart(item.page)" id="page-{{item.page}}" [attr.page]="item.page" ondragstart="return false;" onselectstart="return false;"> - +
diff --git a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.html b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.html index 267f5e09b..7d1976340 100644 --- a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.html +++ b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.html @@ -51,7 +51,7 @@
-
+
diff --git a/UI/Web/src/assets/langs/fr.json b/UI/Web/src/assets/langs/fr.json index ec10f0228..c00dd029f 100644 --- a/UI/Web/src/assets/langs/fr.json +++ b/UI/Web/src/assets/langs/fr.json @@ -254,7 +254,8 @@ "edit": "{{common.edit}}", "cancel": "{{common.cancel}}", "save": "{{common.save}}", - "token-input-label": "{{service}} Le token vient ici" + "token-input-label": "{{service}} Le token vient ici", + "title": "Fournisseur d'analyse de contenu" }, "typeahead": { "locked-field": "Ce champ est vérouillé", @@ -372,7 +373,8 @@ "cover-artist": "Artiste de la couverture", "character": "Personnage", "artist": "Artiste", - "inker": "Encreur" + "inker": "Encreur", + "publisher": "Éditeur" }, "manga-format-pipe": { "epub": "EPUB", @@ -388,15 +390,35 @@ }, "reset-password": { "description": "Entrez l'email de votre compte. Kavita vous enverra un e-mail s'il est valide dans le dossier, sinon demandez à l'administrateur le lien des journaux.", - "title": "Réinitialisation de mot de passe" + "title": "Réinitialisation de mot de passe", + "submit": "{{common.submit}}", + "valid-email": "{{validation.valid-email}}", + "email-label": "{{common.email}}", + "required-field": "{{validation.required-field}}" }, "all-series": { - "title": "Toutes les Séries" + "title": "Toutes les Séries", + "series-count": "{{common.series-count}}" }, "series-metadata-detail": { "collections-title": "{{side-nav.collections}}", "tags-title": "Étiquettes", - "characters-title": "Personnages" + "characters-title": "Personnages", + "reading-lists-title": "{{side-nav.reading-lists}}", + "colorists-title": "Coloristes", + "writers-title": "Écrivains", + "publishers-title": "Éditeurs", + "see-less": "Voir moins", + "see-more": "Voir plus", + "genres-title": "Genres", + "inkers-title": "Encreurs", + "pencillers-title": "Crayonneurs", + "links-title": "Liens", + "editors-title": "Éditeurs", + "promoted": "{{common.promoted}}", + "cover-artists-title": "Artistes de couverture", + "letterers-title": "Lettreurs", + "translators-title": "Traducteurs" }, "side-nav": { "home": "Accueil", @@ -404,7 +426,12 @@ "collections": "Collections", "reading-lists": "Listes de lecture", "bookmarks": "Marque-pages", - "all-series": "Toutes les Séries" + "all-series": "Toutes les Séries", + "back": "Retour", + "more": "Plus", + "clear": "{{common.clear}}", + "filter-label": "{{common.filter}}", + "donate": "Donner" }, "all-collections": { "title": "Collections" @@ -514,7 +541,9 @@ "r18-plus": "Interdit aux moins de 18 ans", "kids-to-adults": "Enfants aux adultes", "rating-pending": "Classification en attente", - "everyone": "Tous publics" + "everyone": "Tous publics", + "ma15-plus": "15+", + "mature-17-plus": "17+" }, "server-stats": { "tags": "Étiquettes", @@ -543,10 +572,23 @@ "reset-password-modal": { "title": "Réinitialiser le mot de passe de {nom}", "error-label": "Erreur : ", - "new-password-label": "Nouveau mot de passe" + "new-password-label": "Nouveau mot de passe", + "close": "{{common.close}}", + "cancel": "{{common.cancel}}", + "save": "{{common.save}}" }, "invite-user": { - "title": "Inviter un utilisateur" + "title": "Inviter un utilisateur", + "required-field": "{{common.required-field}}", + "setup-user-account": "Configuration du compte utilisateur", + "invite-url-label": "URL d'invitation", + "close": "{{common.close}}", + "cancel": "{{common.cancel}}", + "email": "{{common.email}}", + "inviting": "Invitation en cours…", + "setup-user-account-tooltip": "Copier et coller dans un nouvel onglet. Vous devrait peut-être vous déconnecter.", + "setup-user-title": "Utilisateurs invités", + "invite": "Inviter" }, "announcements": { "title": "Annonces" @@ -557,10 +599,21 @@ "activate-delete": "Supprimer", "no-license-key": "Aucune clé de licence", "license-not-valid": "Licence non valide", - "activate-description": "Entrez la clé de licence et l'email utilisé pour s'inscrire avec Stripe" + "activate-description": "Entrez la clé de licence et l'email utilisé pour s'inscrire avec Stripe", + "manage": "Gérer", + "activate": "Activer", + "buy": "Acheter", + "loading": "{{common.loading}}", + "cancel": "{{common.cancel}}", + "title": "Licence Kavita+", + "renew": "Renouveler", + "check": "Vérifier", + "activate-save": "{{common.save}}", + "activate-email-label": "{{common.email}}", + "edit": "{{common.edit}}" }, "book-reader": { - "bookmarks-header": "Signets", + "bookmarks-header": "{{side-nav.bookmarks}}", "incognito-mode-label": "Navigation privée", "page-label": "Page", "prev-page": "Page précédente", @@ -581,15 +634,213 @@ "virtual-pages": "pages virtuelles", "toc-header": "TdM", "pagination-header": "Section", - "prev-chapter": "Chapitre/Volume précédent" + "prev-chapter": "Chapitre/Volume précédent", + "close-reader": "Fermer le lecteur" }, "book-line-overlay": { "bookmark": "Signet", "copy": "Copier", - "bookmark-label": "Nom du signet" + "bookmark-label": "Nom du signet", + "close": "{{common.close}}", + "required-field": "{{common.required-field}}", + "save": "{{common.save}}" }, "register": { "description": "Remplissez le formulaire pour enregistrer un compte administrateur", - "title": "S'inscrire" + "title": "S'inscrire", + "email-label": "{{common.email}}", + "required-field": "{{validation.required-field}}", + "password-validation": "{{validation.password-validation}}", + "register": "S'enregistrer", + "username-label": "{{common.username}}", + "valid-email": "{{validation.valid-email}}", + "password-label": "{{common.password}}", + "email-tooltip": "L'e-mail ne doit pas nécessairement être une véritable adresse, mais donne accès au mot de passe oublié. Il n'est pas envoyé en dehors du serveur, sauf si mot de passe oublié est utilisé sans un hôte de service de messagerie personnalisé." + }, + "update-notification-modal": { + "title": "Nouvelle mise à jour disponible !", + "download": "Télécharger", + "help": "Comment mettre à jour", + "close": "{{common.close}}" + }, + "library-settings-modal": { + "folder-tab": "Dossier", + "name-label": "Nom", + "include-in-search-tooltip": "Inclure les séries et informations dérivées (genres, personnes, fichiers) dans les résultats de recherche.", + "include-in-recommendation-label": "Inclure dans les recommandations", + "naming-conventions-part-2": "dossier requis.", + "type-label": "Type", + "help-us-part-1": "Aidez-nous en suivant ", + "reset": "{{common.reset}}", + "manage-collection-label": "Gérer les collections", + "type-tooltip": "Le type de bibliothèque détermine la manière dont les noms de fichiers sont analysés et si l'interface utilisateur affiche des chapitres (Manga) ou des numéros (Bandes dessinées). Les livres fonctionnent de la même manière que les mangas mais ont un nom différent dans l'interface utilisateur.", + "cover-tab": "Couverture", + "manage-reading-list-label": "Gérer les listes de lecture", + "include-in-search-label": "Inclure dans la recherche", + "force-scan-tooltip": "Ceci forcera l'analyse de la bibliothèque, c", + "edit-title": "Modifier le {{name}}", + "close": "{{common.close}}", + "required-field": "{{validation.required-field}}", + "general-tab": "Général", + "cancel": "{{common.cancel}}", + "exclude-patterns-label": "Exclure les modèles", + "help-us-part-3": "pour nommer et organiser vos médias.", + "naming-conventions-part-1": "Kavita a ", + "cover-description": "Les icônes des bibliothèques personnalisées sont optionnelles", + "naming-conventions-part-3": "Vérifiez ce lien pour vous assurer que vous suivez, sinon les fichiers ne s'afficheront pas lors de l'analyse.", + "folder-watching-label": "Surveillance de dossiers", + "library-name-unique": "Le nom de la bibliothèque doit être unique", + "browse": "Rechercher des dossiers multimédias", + "folder-description": "Ajouter des dossier dans votre bibliothèque", + "help-us-part-2": "notre guide", + "allow-scrobbling-label": "Autoriser le scrobbling", + "advanced-tab": "Avancée", + "help": "{{common.help}}", + "include-in-recommendation-tooltip": "Inclure les séries de la bibliothèque dans la page des recommandations.", + "last-scanned-label": "Dernière analyse :", + "add-title": "Ajouter Bibliothèque", + "file-type-group-label": "Types de fichier", + "save": "{{common.save}}", + "force-scan": "Forcer l'analyse", + "include-in-dashboard-label": "Inclure dans le tableau de bord", + "next": "Suivant" + }, + "series-detail": { + "close": "{{common.close}}", + "volumes-tab": "Volumes", + "layout-mode-option-card": "Carte", + "continue": "Continuer", + "layout-mode-label": "{{user-preferences.layout-mode-book-label}}", + "cover-change": "L'actualisation de l'image par votre navigateur peut prendre jusqu'à une minute. En attendant, l'ancienne image peut être affichée sur certaines pages.", + "read": "{{common.read}}", + "incognito": "Mode privé", + "remove-from-want-to-read": "{{actionable.remove-from-want-to-read}}", + "continue-from": "Continuer {{title}}", + "read-options-alt": "Options de lecture", + "books-tab": "Livres", + "downloading-status": "Téléchargement…", + "send-to": "Fichier envoyé par e-mail à {{deviceName}}", + "storyline-tab": "Scénario", + "no-chapters": "Il n'y a aucun chapitre dans ce volume. Il ne peut être lu.", + "continue-incognito": "Continuer en mode privé", + "page-settings-title": "Paramètres des pages", + "user-reviews-alt": "Critiques d'utilisateurs", + "related-tab": "Relations", + "read-incognito": "Lire en mode privé", + "specials-tab": "Spéciales", + "recommendations-tab": "Recommandations", + "download-series--tooltip": "Télécharger la série", + "edit-series-alt": "Modifier les informations sur la série", + "layout-mode-option-list": "Liste", + "add-to-want-to-read": "{{actionable.add-to-want-to-read}}", + "no-pages": "{{toasts.no-pages}}" + }, + "reader-settings": { + "theme-paper": "Papier", + "immersive-mode-label": "{{user-preferences.immersive-mode-label}}", + "left-to-right": "De gauche à droite", + "layout-mode-option-2col": "2 Colonnes", + "reading-direction-label": "{{user-preferences.reading-direction-book-label}}", + "on": "Activé", + "writing-style-tooltip": "Change la direction du texte. L'horizontale est de gauche à droite, la verticale de haut en bas.", + "off": "Désactivé", + "margin-label": "{{user-preferences.margin-book-label}}", + "general-settings-title": "Réglages généraux", + "reader-settings-title": "Paramètres de lecture", + "fullscreen-label": "Plein écran", + "layout-mode-option-1col": "1 Colonne", + "fullscreen-tooltip": "Mettre le lecteur en plein écran", + "theme-dark": "Sombre", + "writing-style-label": "{{user-preferences.writing-style-label}}", + "vertical": "Vertical", + "layout-mode-label": "{{user-preferences.layout-mode-book-label}}", + "tap-to-paginate-label": "Appuyez sur Pagination", + "exit": "Sortir", + "line-spacing-label": "{{user-preferences.line-height-book-label}}", + "color-theme-title": "Couleur du thème", + "theme-white": "Blanc", + "font-size-label": "{{user-preferences.font-size-book-label}}", + "theme-black": "Noir", + "font-family-label": "{{user-preferences.font-family-label}}", + "reset-to-defaults": "Réinitialiser les paramètres par défaut", + "tap-to-paginate-tooltip": "Cliquez sur les bords de l'écran pour paginer", + "enter": "Entrer", + "right-to-left": "De droite à gauche", + "layout-mode-option-scroll": "Faire défiler" + }, + "read-more": { + "read-less": "Lire moins", + "read-more": "Lire plus" + }, + "confirm-email": { + "title": "S'enregistrer", + "password-validation": "{{validation.password-validation}}", + "valid-email": "{{common.valid-email}}", + "email-label": "{{common.email}}", + "description": "Compléter le formulaire pour terminer la création du compte", + "password-label": "{{common.password}}", + "register": "S'enregistrer", + "username-label": "{{common.username}}", + "required-field": "{{common.required-field}}", + "error-label": "Erreurs : " + }, + "bookmarks": { + "delete-success": "Les favoris ont été supprimés", + "confirm-single-delete": "Êtes-vous sûr de vouloir effacer tous les favoris de {{seriesName}}. Ceci ne peut pas être annulé.", + "confirm-delete": "Êtes-vous sûr de vouloir effacer tous les favoris de plusieurs séries ? Ceci ne peut pas être annulé.", + "no-data-2": "un.", + "no-data": "Il n'y a pas de favoris. Essayez de créer", + "title": "{{side-nav.bookmarks}}", + "series-count": "{{common.series-count}}" + }, + "side-nav-companion-bar": { + "open-filter-and-sort": "Ouvrir Filtres et Tris", + "close-filter-and-sort": "Fermer Filtres et Tris", + "filter-and-sort-alt": "Trier / Filtrer", + "page-settings-title": "{{series-detail.page-settings-title}}" + }, + "confirm-email-change": { + "non-confirm-description": "Merci d'attendre la validation de la mise à jour de votre mail.", + "title": "Valider la modification du mail", + "confirm-description": "Votre mail a été validé et est maintenant modifié dans Kavita. Vous allez être redirigé vers la fenêtre de connexion.", + "success": "Succès !" + }, + "confirm-reset-password": { + "password-validation": "{{validation.password-validation}}", + "required-field": "{{validation.required-field}}", + "password-label": "{{common.password}}", + "title": "Réinitialisation du mot de passe", + "description": "Saisir un nouveau mot de passe", + "submit": "{{common.submit}}" + }, + "file-type-group-pipe": { + "epub": "Epub", + "archive": "Archive", + "pdf": "Pdf", + "image": "Image" + }, + "library-selector": { + "deselect-all": "{{common.deselect-all}}", + "select-all": "{{common.select-all}}", + "title": "Bibliothèques", + "no-data": "Aucune bibliothèque configurée." + }, + "all-filters": { + "title": "Tous les filtres intelligents", + "count": "{{count}} {{customize-dashboard-modal.title-smart-filters}}" + }, + "personal-table-of-contents": { + "delete": "Supprimer {{bookmarkName}}", + "no-data": "Rien n'a encore été mis en favoris", + "page": "Page {{value}}" + }, + "table-of-contents": { + "no-data": "Ce livre n'a pas de table des matières définie dans les métadonnées ou dans un fichier toc" + }, + "badge-expander": { + "more-items": "et {{count}} plus" + }, + "user-holds": { + "description": "C'est une liste de séries gérée par l'utilisateur qui ne sera pas soumis aux fournisseurs de contenu. Vous pouvez enlever une série à n'importe quel moment et le prochain événement de soumission (progression de lecture, notation, statut \"à lire\") déclenchera l'événement." } } diff --git a/UI/Web/src/assets/langs/it.json b/UI/Web/src/assets/langs/it.json index 4315e497a..98aee133d 100644 --- a/UI/Web/src/assets/langs/it.json +++ b/UI/Web/src/assets/langs/it.json @@ -556,7 +556,8 @@ "incognito-mode-label": "Modalità Incognito", "next": "Prossimo", "previous": "Precedente", - "go-to-page-prompt": "Ci sono {{totalPages}} pagine. A quale pagina vuoi andare?" + "go-to-page-prompt": "Ci sono {{totalPages}} pagine. A quale pagina vuoi andare?", + "close-reader": "Chiudi Lettore" }, "personal-table-of-contents": { "no-data": "Nessun preferito ancora", @@ -726,7 +727,12 @@ "cancel": "{{common.cancel}}", "next": "Prossimo", "save": "{{common.save}}", - "required-field": "{{validation.required-field}}" + "required-field": "{{validation.required-field}}", + "exclude-patterns-label": "Escludi modelli", + "help": "{{common.help}}", + "file-type-group-label": "Tipi di file", + "file-type-group-tooltip": "Quali tipi di file dovrebbe scansionare Kavita. Ad esempio, Archivio includerà tutti i file cb*, zip, rar, ecc.", + "exclude-patterns-tooltip": "Configura una serie di modelli (sintassi Glob) che Kavita abbinerà durante la scansione delle directory ed escluderà dai risultati dello scanner." }, "reader-settings": { "general-settings-title": "Impostazioni Generali", @@ -1081,7 +1087,10 @@ "analyze-files-task-desc": "Esegue un'attività di lunga durata che analizzerà i file per generare estensione e dimensione. Dovrebbe essere eseguito solo una volta per la versione v0.7. Non necessario se hai installato la versione successiva alla v0.7.", "analyze-files-task-success": "L'analisi del file è stata messa in coda", "check-for-updates-task": "Cerca Aggiornamenti", - "check-for-updates-task-desc": "Verifica se sono disponibili versioni stabili prima della tua versione." + "check-for-updates-task-desc": "Verifica se sono disponibili versioni stabili prima della tua versione.", + "bust-locale-task-desc": "Blocca la cache delle impostazioni locali. Ciò può risolvere i problemi relativi alle stringhe che non vengono visualizzate correttamente dopo un aggiornamento", + "bust-locale-task-success": "Cache locale cancellata", + "bust-locale-task": "Busta cache delle impostazioni locali" }, "manage-users": { "title": "Utenti Attivi", @@ -1234,7 +1243,8 @@ "reading-lists": "Liste Lettura", "collections": "Collezioni", "close": "{{common.close}}", - "loading": "{{common.loading}}" + "loading": "{{common.loading}}", + "bookmarks": "{{side-nav.bookmarks}}" }, "nav-header": { "skip-alt": "Passa al contenuto principale", @@ -1337,7 +1347,10 @@ "no-prev-chapter": "Nessun capitolo precedente", "user-preferences-updated": "Preferenze utente aggiornate", "emulate-comic-book-label": "{{user-preferences.emulate-comic-book-label}}", - "bookmarks-title": "Segnalibri" + "bookmarks-title": "Segnalibri", + "series-progress": "Avanzamento della serie: {{percentage}}", + "unbookmark-page-tooltip": "Annulla pagina segnalibri", + "bookmark-page-tooltip": "Pagina dei segnalibri" }, "metadata-filter": { "filter-title": "{{common.filter}}", @@ -1384,7 +1397,8 @@ "last-modified": "Ultima modifica", "last-chapter-added": "Elemento aggiunto", "time-to-read": "Tempo di leggere", - "release-year": "Anno di pubblicazione" + "release-year": "Anno di pubblicazione", + "read-progress": "Ultimo Letto" }, "edit-series-modal": { "title": "{{seriesName}} Dettagli", @@ -1449,7 +1463,8 @@ "day-breakdown": { "title": "Ripartizione del giorno", "x-axis-label": "Giorno della settimana", - "y-axis-label": "Eventi di lettura" + "y-axis-label": "Eventi di lettura", + "no-data": "Nessun progresso, inizia a leggere" }, "file-breakdown-stats": { "format-title": "Formato", @@ -1712,7 +1727,8 @@ "chapter-num": "Capitolo", "volume-num": "Volume", "clear": "Pulisci", - "filter": "Filtro" + "filter": "Filtro", + "remove": "Rimuovere" }, "infinite-scroller": { "continuous-reading-prev-chapter-alt": "Scorri verso l'alto per passare al capitolo precedente", @@ -1758,7 +1774,9 @@ "languages": "Lingue", "libraries": "Librerie", "path": "Percorso", - "file-path": "Percorso File" + "file-path": "Percorso File", + "read-date": "Data di lettura", + "want-to-read": "Voler leggere" }, "filter-comparison-pipe": { "not-equal": "Non uguale", @@ -1820,7 +1838,9 @@ "remove-from-want-to-read": "{{actionable.remove-from-want-to-read}}", "vols-and-chapters": "{{volCount}} Volumi / {{chpCount}} Capitoli", "tags-label": "{{filter-field-pipe.tags}}", - "add-to-want-to-read": "{{actionable.add-to-want-to-read}}" + "add-to-want-to-read": "{{actionable.add-to-want-to-read}}", + "view-series": "Visualizza serie", + "staff-label": "Personale" }, "customize-dashboard-modal": { "smart-filters": "Filtri Intelligenti", @@ -1829,7 +1849,10 @@ "help": "{{common.help}}", "external-sources": "Fonti esterne", "title-dashboard": "Personalizza la Dashboard", - "title-smart-filters": "Filtri Intelligenti" + "title-smart-filters": "Filtri Intelligenti", + "dashboard": "Dashboard", + "sidenav": "Nav laterale", + "title-sidenav": "Personalizza la navigazione laterale" }, "stream-list-item": { "external-source": "Fonti Esterne", @@ -1857,7 +1880,8 @@ "filter": "{{common.filter}}", "clear": "{{common.clear}}", "no-data": "Non esistono fonti esterne", - "help-link": "Maggiori informazioni" + "help-link": "Maggiori informazioni", + "description": "Migliora la tua esperienza aggiungendo server esterni e includili comodamente nel tuo Side Nav per un accesso rapido sia al tuo server che a quello del tuo amico." }, "next-expected-card": { "title": "~{{date}}" @@ -1869,5 +1893,11 @@ "metadata-filter-row": { "unit-reading-progress": "Percentuale", "unit-reading-date": "Data" + }, + "file-type-group-pipe": { + "epub": "Epub", + "archive": "Archivio", + "pdf": "Pdf", + "image": "Immagine" } } diff --git a/UI/Web/src/assets/langs/ja.json b/UI/Web/src/assets/langs/ja.json index f78551fca..ad0ed40b5 100644 --- a/UI/Web/src/assets/langs/ja.json +++ b/UI/Web/src/assets/langs/ja.json @@ -10,7 +10,8 @@ "dashboard": { "server-settings-link": "サーバー設定", "recently-updated-title": "最近更新されたシリーズ", - "recently-added-title": "最近追加されたシリーズ" + "recently-added-title": "最近追加されたシリーズ", + "on-deck-title": "最近読んだ本" }, "edit-user": { "edit": "{{common.edit}}", @@ -25,7 +26,8 @@ }, "user-scrobble-history": { "series-header": "シリーズ", - "no-data": "データなし" + "no-data": "データなし", + "filter-label": "{{common.filter}}" }, "scrobble-event-type-pipe": { "want-to-read-add": "読みたい:追加", @@ -160,5 +162,10 @@ }, "card-detail-layout": { "jumpkey-count": "{{count}} シリーズ" + }, + "user-preferences": { + "account-tab": "アカウント", + "theme-tab": "テーマ", + "devices-tab": "デバイス" } } diff --git a/UI/Web/src/assets/langs/pt_BR.json b/UI/Web/src/assets/langs/pt_BR.json index a53d0a7c3..10ac121e8 100644 --- a/UI/Web/src/assets/langs/pt_BR.json +++ b/UI/Web/src/assets/langs/pt_BR.json @@ -30,7 +30,7 @@ "user-scrobble-history": { "title": "Histórico de scrobble", "description": "Aqui você encontrará todos os eventos scrobble vinculados à sua conta. Para que os eventos existam, você deve ter um provedor scrobble ativo configurado. Todos os eventos processados serão apagados após um mês. Se houver eventos não processados, é provável que eles não possam formar correspondências upstream. Entre em contato com seu administrador para corrigi-los.", - "filter-label": "Filtro", + "filter-label": "{{common.filter}}", "created-header": "Criado", "last-modified-header": "Última Modificação", "type-header": "Tipo", @@ -150,7 +150,7 @@ }, "user-holds": { "title": "Segurar Scrobble", - "description": "Esta é uma lista de séries gerenciada pelo usuário que não será scrobbled para provedores upstream. Você pode remover uma série a qualquer momento e o próximo evento compatível com Scrobble (progresso de leitura, classificação, status de desejo de leitura) acionará eventos." + "description": "Esta é uma lista de séries gerenciada pelo usuário que não será transferida para provedores upstream. Você pode remover uma série a qualquer momento e o próximo evento passível de scrobble (leitura de progresso, classificação, desejo de ler status) acionará eventos." }, "theme-manager": { "title": "Gerenciador de Temas", @@ -233,7 +233,7 @@ "save": "{{common.save}}" }, "change-age-restriction": { - "age-restriction-label": "Classificação Etária", + "age-restriction-label": "Restrição de Etária", "unknowns": "Desconhecidos", "reset": "{{common.reset}}", "edit": "{{common.edit}}", @@ -340,7 +340,7 @@ "all-time": "Todo o Tempo" }, "device-platform-pipe": { - "custom": "Personalizar" + "custom": "Personalizado" }, "day-of-week-pipe": { "monday": "Segunda-feira", diff --git a/openapi.json b/openapi.json index 3fb292b83..377e11e1e 100644 --- a/openapi.json +++ b/openapi.json @@ -7,7 +7,7 @@ "name": "GPL-3.0", "url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE" }, - "version": "0.7.11.0" + "version": "0.7.11.1" }, "servers": [ {