diff --git a/API/Controllers/ServerController.cs b/API/Controllers/ServerController.cs index 6a74e8b30..2ac68ca61 100644 --- a/API/Controllers/ServerController.cs +++ b/API/Controllers/ServerController.cs @@ -222,8 +222,7 @@ public class ServerController : BaseApiController Id = dto.Id, Title = dto.Id.Replace('-', ' '), Cron = dto.Cron, - CreatedAt = dto.CreatedAt, - LastExecution = dto.LastExecution, + LastExecutionUtc = dto.LastExecution.HasValue ? new DateTime(dto.LastExecution.Value.Ticks, DateTimeKind.Utc) : null }); return Ok(recurringJobs); diff --git a/API/DTOs/ChapterDto.cs b/API/DTOs/ChapterDto.cs index 843dabde4..b70a472de 100644 --- a/API/DTOs/ChapterDto.cs +++ b/API/DTOs/ChapterDto.cs @@ -9,7 +9,7 @@ namespace API.DTOs; /// A Chapter is the lowest grouping of a reading medium. A Chapter contains a set of MangaFiles which represents the underlying /// file (abstracted from type). /// -public class ChapterDto : IHasReadTimeEstimate, IEntityDate +public class ChapterDto : IHasReadTimeEstimate { public int Id { get; init; } /// @@ -59,8 +59,6 @@ public class ChapterDto : IHasReadTimeEstimate, IEntityDate /// /// When chapter was created /// - public DateTime Created { get; set; } - public DateTime LastModified { get; set; } public DateTime CreatedUtc { get; set; } public DateTime LastModifiedUtc { get; set; } /// diff --git a/API/DTOs/Device/DeviceDto.cs b/API/DTOs/Device/DeviceDto.cs index 069a7a4d2..b2e83e6fc 100644 --- a/API/DTOs/Device/DeviceDto.cs +++ b/API/DTOs/Device/DeviceDto.cs @@ -26,12 +26,4 @@ public class DeviceDto /// Platform (ie) Windows 10 /// public DevicePlatform Platform { get; set; } - /// - /// Last time this device was used to send a file - /// - public DateTime LastUsed { get; set; } - /// - /// Last time this device was used to send a file - /// - public DateTime LastUsedUtc { get; set; } } diff --git a/API/DTOs/Jobs/JobDto.cs b/API/DTOs/Jobs/JobDto.cs index 89d7b30f9..648765a34 100644 --- a/API/DTOs/Jobs/JobDto.cs +++ b/API/DTOs/Jobs/JobDto.cs @@ -15,14 +15,6 @@ public class JobDto /// /// When the job was created /// - public DateTime? CreatedAt { get; set; } - /// - /// Last time the job was run - /// - public DateTime? LastExecution { get; set; } - /// - /// When the job was created - /// public DateTime? CreatedAtUtc { get; set; } /// /// Last time the job was run diff --git a/API/DTOs/MediaErrors/MediaErrorDto.cs b/API/DTOs/MediaErrors/MediaErrorDto.cs index 6a6111e4e..d890108d2 100644 --- a/API/DTOs/MediaErrors/MediaErrorDto.cs +++ b/API/DTOs/MediaErrors/MediaErrorDto.cs @@ -20,6 +20,4 @@ public class MediaErrorDto /// Exception message /// public string Details { get; set; } - public DateTime Created { get; set; } - public DateTime CreatedUtc { get; set; } } diff --git a/API/DTOs/Scrobbling/ScrobbleEventDto.cs b/API/DTOs/Scrobbling/ScrobbleEventDto.cs index 3ed0cd569..25690da82 100644 --- a/API/DTOs/Scrobbling/ScrobbleEventDto.cs +++ b/API/DTOs/Scrobbling/ScrobbleEventDto.cs @@ -10,8 +10,8 @@ public class ScrobbleEventDto public bool IsProcessed { get; set; } public int? VolumeNumber { get; set; } public int? ChapterNumber { get; set; } - public DateTime LastModified { get; set; } - public DateTime Created { get; set; } + public DateTime LastModifiedUtc { get; set; } + public DateTime CreatedUtc { get; set; } public float? Rating { get; set; } public ScrobbleEventType ScrobbleEventType { get; set; } diff --git a/API/DTOs/Theme/SiteThemeDto.cs b/API/DTOs/Theme/SiteThemeDto.cs index b3d7659ea..18a281b56 100644 --- a/API/DTOs/Theme/SiteThemeDto.cs +++ b/API/DTOs/Theme/SiteThemeDto.cs @@ -1,6 +1,4 @@ -using System; using API.Entities.Enums.Theme; -using API.Entities.Interfaces; using API.Services; namespace API.DTOs.Theme; @@ -8,7 +6,7 @@ namespace API.DTOs.Theme; /// /// Represents a set of css overrides the user can upload to Kavita and will load into webui /// -public class SiteThemeDto : IEntityDate +public class SiteThemeDto { public int Id { get; set; } /// @@ -32,9 +30,5 @@ public class SiteThemeDto : IEntityDate /// Where did the theme come from /// public ThemeProvider Provider { get; set; } - public DateTime Created { get; set; } - public DateTime LastModified { get; set; } - public DateTime CreatedUtc { get; set; } - public DateTime LastModifiedUtc { get; set; } public string Selector => "bg-" + Name.ToLower(); } diff --git a/UI/Web/src/app/_models/chapter.ts b/UI/Web/src/app/_models/chapter.ts index ca95de81b..67c5cbc54 100644 --- a/UI/Web/src/app/_models/chapter.ts +++ b/UI/Web/src/app/_models/chapter.ts @@ -21,7 +21,7 @@ export interface Chapter { pagesRead: number; // Attached for the given user when requesting from API isSpecial: boolean; title: string; - created: string; + createdUtc: string; /** * Actual name of the Chapter if populated in underlying metadata */ diff --git a/UI/Web/src/app/_models/device/device.ts b/UI/Web/src/app/_models/device/device.ts index 435be4937..72d3d57e2 100644 --- a/UI/Web/src/app/_models/device/device.ts +++ b/UI/Web/src/app/_models/device/device.ts @@ -6,4 +6,5 @@ export interface Device { platform: DevicePlatform; emailAddress: string; lastUsed: string; -} \ No newline at end of file + lastUsedUtc: string; +} diff --git a/UI/Web/src/app/_models/job/job.ts b/UI/Web/src/app/_models/job/job.ts index 00ce0dffa..ef32e9263 100644 --- a/UI/Web/src/app/_models/job/job.ts +++ b/UI/Web/src/app/_models/job/job.ts @@ -2,6 +2,5 @@ export interface Job { id: string; title: string; cron: string; - createdAt: string; - lastExecution: string; -} \ No newline at end of file + lastExecutionUtc: string; +} diff --git a/UI/Web/src/app/_models/scrobbling/scrobble-event.ts b/UI/Web/src/app/_models/scrobbling/scrobble-event.ts index 4122ab69c..41113e5d0 100644 --- a/UI/Web/src/app/_models/scrobbling/scrobble-event.ts +++ b/UI/Web/src/app/_models/scrobbling/scrobble-event.ts @@ -14,8 +14,8 @@ export interface ScrobbleEvent { scrobbleEventType: ScrobbleEventType; rating: number | null; processedDateUtc: string; - lastModified: string; - created: string; + lastModifiedUtc: string; + createdUtc: string; volumeNumber: number | null; chapterNumber: number | null; } diff --git a/UI/Web/src/app/_services/account.service.ts b/UI/Web/src/app/_services/account.service.ts index 9ccb0a8dc..86cf72618 100644 --- a/UI/Web/src/app/_services/account.service.ts +++ b/UI/Web/src/app/_services/account.service.ts @@ -32,8 +32,8 @@ export class AccountService { private readonly destroyRef = inject(DestroyRef); baseUrl = environment.apiUrl; userKey = 'kavita-user'; - public lastLoginKey = 'kavita-lastlogin'; - public localeKey = 'kavita-locale'; + public static lastLoginKey = 'kavita-lastlogin'; + public static localeKey = 'kavita-locale'; private currentUser: User | undefined; // Stores values, when someone subscribes gives (1) of last values seen. @@ -135,7 +135,7 @@ export class AccountService { Array.isArray(roles) ? user.roles = roles : user.roles.push(roles); localStorage.setItem(this.userKey, JSON.stringify(user)); - localStorage.setItem(this.lastLoginKey, user.username); + localStorage.setItem(AccountService.lastLoginKey, user.username); if (user.preferences && user.preferences.theme) { this.themeService.setTheme(user.preferences.theme.name); } else { @@ -268,8 +268,8 @@ export class AccountService { this.currentUser.preferences = settings; this.setCurrentUser(this.currentUser); - // Update the locale on disk (for logout only) - localStorage.setItem(this.localeKey, this.currentUser.preferences.locale); + // Update the locale on disk (for logout and compact-number pipe) + localStorage.setItem(AccountService.localeKey, this.currentUser.preferences.locale); } return settings; }), takeUntilDestroyed(this.destroyRef)); diff --git a/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.html b/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.html index 43c0f5c66..d3a544e23 100644 --- a/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.html +++ b/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.html @@ -22,10 +22,10 @@ - - - + diff --git a/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.ts b/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.ts index f1ff39fbf..f45aa5e2b 100644 --- a/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.ts +++ b/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.ts @@ -13,6 +13,7 @@ import {DownloadService} from 'src/app/shared/_services/download.service'; import {DefaultValuePipe} from '../../pipe/default-value.pipe'; import {AsyncPipe, DatePipe, NgFor, NgIf, NgTemplateOutlet, TitleCasePipe} from '@angular/common'; import {TranslocoModule, TranslocoService} from "@ngneat/transloco"; +import {TranslocoLocaleModule} from "@ngneat/transloco-locale"; interface AdhocTask { name: string; @@ -28,7 +29,7 @@ interface AdhocTask { styleUrls: ['./manage-tasks-settings.component.scss'], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [NgIf, ReactiveFormsModule, NgbTooltip, NgFor, AsyncPipe, TitleCasePipe, DatePipe, DefaultValuePipe, TranslocoModule, NgTemplateOutlet] + imports: [NgIf, ReactiveFormsModule, NgbTooltip, NgFor, AsyncPipe, TitleCasePipe, DatePipe, DefaultValuePipe, TranslocoModule, NgTemplateOutlet, TranslocoLocaleModule] }) export class ManageTasksSettingsComponent implements OnInit { diff --git a/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.html b/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.html index 3c1583a33..1892f6662 100644 --- a/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.html +++ b/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.html @@ -33,7 +33,7 @@
- + {{t('pages-count', {num: totalPages | compactNumber})}}
@@ -60,11 +60,11 @@
- +
- {{chapter.created | date:'short' | defaultDate}} + {{chapter.createdUtc | translocoDate: {dateStyle: 'short', timeStyle: 'short' } | defaultDate}}
diff --git a/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.ts b/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.ts index 91c35aeae..18bf71ed1 100644 --- a/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.ts +++ b/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.ts @@ -27,11 +27,12 @@ import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; import {MetadataDetailComponent} from "../../series-detail/_components/metadata-detail/metadata-detail.component"; import {FilterQueryParam} from "../../shared/_services/filter-utilities.service"; import {TranslocoModule} from "@ngneat/transloco"; +import {TranslocoLocaleModule} from "@ngneat/transloco-locale"; @Component({ selector: 'app-entity-info-cards', standalone: true, - imports: [CommonModule, IconAndTitleComponent, SafeHtmlPipe, DefaultDatePipe, BytesPipe, CompactNumberPipe, AgeRatingPipe, NgbTooltip, MetadataDetailComponent, TranslocoModule], + imports: [CommonModule, IconAndTitleComponent, SafeHtmlPipe, DefaultDatePipe, BytesPipe, CompactNumberPipe, AgeRatingPipe, NgbTooltip, MetadataDetailComponent, TranslocoModule, CompactNumberPipe, TranslocoLocaleModule], templateUrl: './entity-info-cards.component.html', styleUrls: ['./entity-info-cards.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush diff --git a/UI/Web/src/app/cards/series-card/series-card.component.ts b/UI/Web/src/app/cards/series-card/series-card.component.ts index e04bacd1a..29e5cf1de 100644 --- a/UI/Web/src/app/cards/series-card/series-card.component.ts +++ b/UI/Web/src/app/cards/series-card/series-card.component.ts @@ -91,7 +91,7 @@ export class SeriesCardComponent implements OnInit, OnChanges { if (this.actions[othersIndex].children.findIndex(o => o.action === Action.RemoveFromOnDeck) < 0) { this.actions[othersIndex].children.push({ action: Action.RemoveFromOnDeck, - title: this.translocoService.translate('actionable.remove-from-on-deck'), + title: 'remove-from-on-deck', callback: (action: ActionItem, series: Series) => this.handleSeriesActionCallback(action, series), class: 'danger', requiresAdmin: false, diff --git a/UI/Web/src/app/pipe/compact-number.pipe.ts b/UI/Web/src/app/pipe/compact-number.pipe.ts index 046926535..db5672078 100644 --- a/UI/Web/src/app/pipe/compact-number.pipe.ts +++ b/UI/Web/src/app/pipe/compact-number.pipe.ts @@ -1,17 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; - -// TODO: Figure out how to handle culture based on localization setting -const formatter = new Intl.NumberFormat('en-GB', { - //@ts-ignore - notation: 'compact', // https://github.com/microsoft/TypeScript/issues/36533 - maximumSignificantDigits: 3 -}); - -const formatterForDoublePercision = new Intl.NumberFormat('en-GB', { - //@ts-ignore - notation: 'compact', // https://github.com/microsoft/TypeScript/issues/36533 - maximumSignificantDigits: 2 -}); +import {AccountService} from "../_services/account.service"; const specialCases = [4, 7, 10, 13]; @@ -21,14 +9,35 @@ const specialCases = [4, 7, 10, 13]; }) export class CompactNumberPipe implements PipeTransform { + constructor() {} + transform(value: number): string { + // Weblate allows some non-standard languages, like 'zh_Hans', which should be just 'zh'. So we handle that here + const locale = localStorage.getItem(AccountService.localeKey)?.split('_')[0]; + return this.transformValue(locale || 'en', value); + } + + private transformValue(locale: string, value: number) { + const formatter = new Intl.NumberFormat(locale, { + //@ts-ignore + notation: 'compact', // https://github.com/microsoft/TypeScript/issues/36533 + maximumSignificantDigits: 3 + }); + + const formatterForDoublePrecision = new Intl.NumberFormat(locale, { + //@ts-ignore + notation: 'compact', // https://github.com/microsoft/TypeScript/issues/36533 + maximumSignificantDigits: 2 + }); if (value < 1000) return value + ''; if (specialCases.includes((value + '').length)) { // from 4, every 3 will have a case where we need to override - return formatterForDoublePercision.format(value); + return formatterForDoublePrecision.format(value); } return formatter.format(value); } + + } diff --git a/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.html b/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.html index 63358a507..331dc5f3f 100644 --- a/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.html +++ b/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.html @@ -3,7 +3,7 @@
-

{{t('reading-activity')}}

+

{{t('title')}}

diff --git a/UI/Web/src/app/statistics/_components/server-stats/server-stats.component.ts b/UI/Web/src/app/statistics/_components/server-stats/server-stats.component.ts index 037dddcdd..d8d46cce6 100644 --- a/UI/Web/src/app/statistics/_components/server-stats/server-stats.component.ts +++ b/UI/Web/src/app/statistics/_components/server-stats/server-stats.component.ts @@ -31,7 +31,9 @@ import {TranslocoModule, TranslocoService} from "@ngneat/transloco"; styleUrls: ['./server-stats.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [NgIf, IconAndTitleComponent, StatListComponent, TopReadersComponent, FileBreakdownStatsComponent, PublicationStatusStatsComponent, ReadingActivityComponent, DayBreakdownComponent, AsyncPipe, DecimalPipe, CompactNumberPipe, TimeDurationPipe, BytesPipe, TranslocoModule] + imports: [NgIf, IconAndTitleComponent, StatListComponent, TopReadersComponent, FileBreakdownStatsComponent, + PublicationStatusStatsComponent, ReadingActivityComponent, DayBreakdownComponent, AsyncPipe, DecimalPipe, + CompactNumberPipe, TimeDurationPipe, BytesPipe, TranslocoModule] }) export class ServerStatsComponent { diff --git a/UI/Web/src/app/statistics/_components/user-stats-info-cards/user-stats-info-cards.component.ts b/UI/Web/src/app/statistics/_components/user-stats-info-cards/user-stats-info-cards.component.ts index 759065efd..b4df09dc7 100644 --- a/UI/Web/src/app/statistics/_components/user-stats-info-cards/user-stats-info-cards.component.ts +++ b/UI/Web/src/app/statistics/_components/user-stats-info-cards/user-stats-info-cards.component.ts @@ -1,14 +1,14 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { CompactNumberPipe } from 'src/app/pipe/compact-number.pipe'; import { StatisticsService } from 'src/app/_services/statistics.service'; import { GenericListModalComponent } from '../_modals/generic-list-modal/generic-list-modal.component'; import { TimeAgoPipe } from '../../../pipe/time-ago.pipe'; import { TimeDurationPipe } from '../../../pipe/time-duration.pipe'; -import { CompactNumberPipe as CompactNumberPipe_1 } from '../../../pipe/compact-number.pipe'; import { DecimalPipe } from '@angular/common'; import { IconAndTitleComponent } from '../../../shared/icon-and-title/icon-and-title.component'; import {TranslocoModule} from "@ngneat/transloco"; +import {AccountService} from "../../../_services/account.service"; +import {CompactNumberPipe} from "../../../pipe/compact-number.pipe"; @Component({ selector: 'app-user-stats-info-cards', @@ -16,7 +16,7 @@ import {TranslocoModule} from "@ngneat/transloco"; styleUrls: ['./user-stats-info-cards.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [IconAndTitleComponent, DecimalPipe, CompactNumberPipe_1, TimeDurationPipe, TimeAgoPipe, TranslocoModule] + imports: [IconAndTitleComponent, DecimalPipe, CompactNumberPipe, TimeDurationPipe, TimeAgoPipe, TranslocoModule] }) export class UserStatsInfoCardsComponent { @@ -27,7 +27,7 @@ export class UserStatsInfoCardsComponent { @Input() lastActive: string = ''; @Input() avgHoursPerWeekSpentReading: number = 0; - constructor(private statsService: StatisticsService, private modalService: NgbModal) { } + constructor(private statsService: StatisticsService, private modalService: NgbModal, private accountService: AccountService) { } openPageByYearList() { const numberPipe = new CompactNumberPipe(); @@ -46,5 +46,4 @@ export class UserStatsInfoCardsComponent { ref.componentInstance.title = 'Words Read By Year'; }); } - } diff --git a/UI/Web/src/assets/langs/en.json b/UI/Web/src/assets/langs/en.json index 079e8ba93..095f1bc84 100644 --- a/UI/Web/src/assets/langs/en.json +++ b/UI/Web/src/assets/langs/en.json @@ -31,7 +31,7 @@ "user-scrobble-history": { "title": "Scrobble History", - "description": "Here you will find any scrobble events linked with your account. In order for events to exist, you must have an active\n scrobble provider configured. All events that have been processed will clear after a month. If there are non-processed events, it\n is likely these cannot form matches upstream. Please reach out to your admin to get them corrected.", + "description": "Here you will find any scrobble events linked with your account. In order for events to exist, you must have an active scrobble provider configured. All events that have been processed will clear after a month. If there are non-processed events, it is likely these cannot form matches upstream. Please reach out to your admin to get them corrected.", "filter-label": "Filter", "created-header": "Created", "last-modified-header": "Last Modified", @@ -164,7 +164,7 @@ "user-holds": { "title": "Scrobble Holds", - "description": "This is a user-managed list of Series that will not be scrobbled to upstream providers. You can remove a series at\n any time and the next Scrobble-able event (reading progress, rating, want to read status) will trigger events." + "description": "This is a user-managed list of Series that will not be scrobbled to upstream providers. You can remove a series at any time and the next Scrobble-able event (reading progress, rating, want to read status) will trigger events." }, "theme-manager": { @@ -243,7 +243,7 @@ "current-password-label": "Current Password", "email-not-confirmed": "This email is not confirmed", "email-updated-title": "Email Updated", - "email-updated-description": "You can use the following link below to confirm the email for your account.\n If your server is externally accessible, an email will have been sent to the email and the link can be used to confirm the email.", + "email-updated-description": "You can use the following link below to confirm the email for your account. If your server is externally accessible, an email will have been sent to the email and the link can be used to confirm the email.", "setup-user-account": "Setup user's account", "invite-url-label": "Invite Url", "invite-url-tooltip": "Copy this and paste in a new tab", @@ -280,7 +280,7 @@ "no-token-set": "No Token Set", "token-set": "Token Set", "generate": "Generate", - "instructions": "First time users should click on \"{{scrobbling-providers.generate}}\" below to allow Kavita+ to talk with {{service}}.\n Once you authorize the program, copy and paste the token in the input below. You can regenerate your token at any time.", + "instructions": "First time users should click on \"{{scrobbling-providers.generate}}\" below to allow Kavita+ to talk with {{service}}. Once you authorize the program, copy and paste the token in the input below. You can regenerate your token at any time.", "token-input-label": "{{service}} Token Goes Here", "edit": "{{common.edit}}", "cancel": "{{common.cancel}}", @@ -540,11 +540,11 @@ "invite-user": { "title": "Invite User", "close": "{{common.close}}", - "description": "Invite a user to your server. Enter their email in and we will send them an email to create an account. If you do not want to use our email service, you can host your own\n email service or use a fake email (Forgot User will not work). A link will be presented regardless and can be used to setup the account manually.", + "description": "Invite a user to your server. Enter their email in and we will send them an email to create an account. If you do not want to use our email service, you can host your own email service or use a fake email (Forgot User will not work). A link will be presented regardless and can be used to setup the account manually.", "email": "{{common.email}}", "required-field": "{{common.required-field}}", "setup-user-title": "User invited", - "setup-user-description": "You can use the following link below to setup the account for your user or use the copy button. You may need to log out before using the link to register a new user.\n If your server is externally accessible, an email will have been sent to the user and the links can be used by them to finish setting up their account.", + "setup-user-description": "You can use the following link below to setup the account for your user or use the copy button. You may need to log out before using the link to register a new user. If your server is externally accessible, an email will have been sent to the user and the links can be used by them to finish setting up their account.", "setup-user-account": "Setup user's account", "setup-user-account-tooltip": "Copy this and paste in a new tab. You may need to log out.", "invite-url-label": "Invite Url", @@ -659,7 +659,7 @@ "description": "Complete the form to register an admin account", "username-label": "{{common.username}}", "email-label": "{{common.email}}", - "email-tooltip": "Email does not need to be a real address, but provides access to forgot password.\n It is not sent outside the server unless forgot password is used without a custom email service host.", + "email-tooltip": "Email does not need to be a real address, but provides access to forgot password. It is not sent outside the server unless forgot password is used without a custom email service host.", "password-label": "{{common.password}}", "required-field": "{{validation.required-field}}", "valid-email": "{{validation.valid-email}}", @@ -941,6 +941,7 @@ "length-title": "Length", "pages-count": "{{num}} Pages", "words-count": "{{num}} Words", + "reading-time-title": "Read Time", "date-added-title": "Date Added", "size-title": "Size", @@ -952,7 +953,7 @@ "range-hours": "{{value}} {{hourWord}}", "hour": "Hour", "hours": "Hours", - "read-time-title": "{{series-info-cards.read-time-title}}" + "read-time-title": "{{series-info-cards.read-time-title}}", }, "series-info-cards": { @@ -1069,7 +1070,7 @@ }, "manage-scrobble-errors": { - "description": "This table contains issues found during scrobbling. This list is non-managed.\n You can clear it at any time and wait for the next scrobble upload to see. If there is an unknown series, you are best correcting the\nseries name or localized series name or adding a weblink for the providers.", + "description": "This table contains issues found during scrobbling. This list is non-managed. You can clear it at any time and wait for the next scrobble upload to see. If there is an unknown series, you are best correcting the series name or localized series name or adding a weblink for the providers.", "filter-label": "Filter", "clear-errors": "Clear Errors", "series-header": "Series", @@ -1614,7 +1615,13 @@ "x-axis-label": "Time", "y-axis-label": "Hours Read", "no-data": "No Reading Progress", - "time-frame-label": "Time Frame" + "time-frame-label": "Time Frame", + "this-week": "{{time-periods.this-week}}", + "last-7-days": "{{time-periods.last-7-days}}", + "last-30-days": "{{time-periods.last-30-days}}", + "last-90-days": "{{time-periods.last-90-days}}", + "last-year": "{{time-periods.last-year}}", + "all-time": "{{time-periods.all-time}}" }, "manga-format-stats": { @@ -1753,7 +1760,7 @@ "confirm-delete-series": "Are you sure you want to delete this series? It will not modify files on disk.", "alert-bad-theme": "There is invalid or unsafe css in the theme. Please reach out to your admin to have this corrected. Defaulting to dark theme.", "confirm-library-delete": "Are you sure you want to delete the {{name}} library? You cannot undo this action.", - "confirm-library-type-change": "Changing library type will trigger a new scan with different parsing rules and may lead to\n series being re-created and hence you may loose progress and bookmarks. You should backup before you do this. Are you sure you want to continue?", + "confirm-library-type-change": "Changing library type will trigger a new scan with different parsing rules and may lead to series being re-created and hence you may loose progress and bookmarks. You should backup before you do this. Are you sure you want to continue?", "confirm-download-size": "The {{entityType}} is {{size}}. Are you sure you want to continue?" }, diff --git a/UI/Web/src/main.ts b/UI/Web/src/main.ts index aa520734f..3e5ca1b15 100644 --- a/UI/Web/src/main.ts +++ b/UI/Web/src/main.ts @@ -33,7 +33,7 @@ export function preloadUser(userService: AccountService, transloco: TranslocoSer } // If no user or locale is available, fallback to the default language ('en') - const localStorageLocale = localStorage.getItem(userService.localeKey) || 'en'; + const localStorageLocale = localStorage.getItem(AccountService.localeKey) || 'en'; transloco.setActiveLang(localStorageLocale); return transloco.load(localStorageLocale) })).subscribe(); @@ -71,7 +71,7 @@ const languageCodes = [ 'syr', 'syr-SY', 'ta', 'ta-IN', 'te', 'te-IN', 'th', 'th-TH', 'tl', 'tl-PH', 'tn', 'tn-ZA', 'tr', 'tr-TR', 'tt', 'tt-RU', 'ts', 'uk', 'uk-UA', 'ur', 'ur-PK', 'uz', 'uz-UZ', 'uz-UZ', 'vi', 'vi-VN', 'xh', 'xh-ZA', 'zh', 'zh-CN', 'zh-HK', 'zh-MO', - 'zh-SG', 'zh-TW', 'zu', 'zu-ZA' + 'zh-SG', 'zh-TW', 'zu', 'zu-ZA', 'zh_Hans' ]; bootstrapApplication(AppComponent, { @@ -109,13 +109,13 @@ bootstrapApplication(AppComponent, { provide: TRANSLOCO_CONFIG, useValue: { reRenderOnLangChange: true, - availableLangs: languageCodes, // TODO: Derive this from the directory + availableLangs: languageCodes, prodMode: environment.production, defaultLang: 'en', fallbackLang: 'en', missingHandler: { useFallbackTranslation: true, - allowEmpty: true, + allowEmpty: false, }, flatten: { aot: !isDevMode() diff --git a/openapi.json b/openapi.json index b4cf725bd..3532700cf 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.6.5" + "version": "0.7.6.6" }, "servers": [ { @@ -12822,16 +12822,6 @@ "type": "integer", "description": "Platform (ie) Windows 10", "format": "int32" - }, - "lastUsed": { - "type": "string", - "description": "Last time this device was used to send a file", - "format": "date-time" - }, - "lastUsedUtc": { - "type": "string", - "description": "Last time this device was used to send a file", - "format": "date-time" } }, "additionalProperties": false, @@ -13391,18 +13381,6 @@ "description": "Human Readable title for the Job", "nullable": true }, - "createdAt": { - "type": "string", - "description": "When the job was created", - "format": "date-time", - "nullable": true - }, - "lastExecution": { - "type": "string", - "description": "Last time the job was run", - "format": "date-time", - "nullable": true - }, "createdAtUtc": { "type": "string", "description": "When the job was created", @@ -13879,14 +13857,6 @@ "type": "string", "description": "Exception message", "nullable": true - }, - "created": { - "type": "string", - "format": "date-time" - }, - "createdUtc": { - "type": "string", - "format": "date-time" } }, "additionalProperties": false @@ -14850,11 +14820,11 @@ "format": "int32", "nullable": true }, - "lastModified": { + "lastModifiedUtc": { "type": "string", "format": "date-time" }, - "created": { + "createdUtc": { "type": "string", "format": "date-time" }, @@ -16439,22 +16409,6 @@ "description": "Where did the theme come from", "format": "int32" }, - "created": { - "type": "string", - "format": "date-time" - }, - "lastModified": { - "type": "string", - "format": "date-time" - }, - "createdUtc": { - "type": "string", - "format": "date-time" - }, - "lastModifiedUtc": { - "type": "string", - "format": "date-time" - }, "selector": { "type": "string", "nullable": true,
+ {{t('created-header')}} + {{t('last-modified-header')}} @@ -48,10 +48,10 @@
- {{item.created | date:'MM/dd/yy h:mm a' }} + {{item.createdUtc | translocoDate: {dateStyle: 'short', timeStyle: 'medium', } | defaultValue }} - {{item.lastModified | date:'MM/dd/yy h:mm a' }} + {{item.lastModifiedUtc | translocoDate: {dateStyle: 'short', timeStyle: 'medium' } | defaultValue }} {{item.scrobbleEventType | scrobbleEventType}} diff --git a/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.ts b/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.ts index 41268d93c..2ce5c453d 100644 --- a/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.ts +++ b/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.ts @@ -12,11 +12,13 @@ import {PaginatedResult, Pagination} from "../../_models/pagination"; import {SortableHeader, SortEvent} from "../table/_directives/sortable-header.directive"; import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms"; import {TranslocoModule} from "@ngneat/transloco"; +import {DefaultValuePipe} from "../../pipe/default-value.pipe"; +import {TranslocoLocaleModule} from "@ngneat/transloco-locale"; @Component({ selector: 'app-user-scrobble-history', standalone: true, - imports: [CommonModule, ScrobbleEventTypePipe, NgbPagination, ReactiveFormsModule, SortableHeader, TranslocoModule], + imports: [CommonModule, ScrobbleEventTypePipe, NgbPagination, ReactiveFormsModule, SortableHeader, TranslocoModule, DefaultValuePipe, TranslocoLocaleModule], templateUrl: './user-scrobble-history.component.html', styleUrls: ['./user-scrobble-history.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush @@ -36,7 +38,7 @@ export class UserScrobbleHistoryComponent implements OnInit { get ScrobbleEventType() { return ScrobbleEventType; } ngOnInit() { - this.loadPage({column: 'created', direction: 'desc'}); + this.loadPage({column: 'createdUtc', direction: 'desc'}); this.formGroup.get('filter')?.valueChanges.pipe(debounceTime(200), takeUntilDestroyed(this.destroyRef)).subscribe(query => { this.loadPage(); @@ -81,9 +83,9 @@ export class UserScrobbleHistoryComponent implements OnInit { private mapSortColumnField(column: string | undefined) { switch (column) { - case 'created': return ScrobbleEventSortField.Created; + case 'createdUtc': return ScrobbleEventSortField.Created; case 'isProcessed': return ScrobbleEventSortField.IsProcessed; - case 'lastModified': return ScrobbleEventSortField.LastModified; + case 'lastModifiedUtc': return ScrobbleEventSortField.LastModified; case 'seriesName': return ScrobbleEventSortField.Series; } return ScrobbleEventSortField.None; diff --git a/UI/Web/src/app/admin/_models/media-error.ts b/UI/Web/src/app/admin/_models/media-error.ts index ee4ecd966..943994940 100644 --- a/UI/Web/src/app/admin/_models/media-error.ts +++ b/UI/Web/src/app/admin/_models/media-error.ts @@ -3,6 +3,4 @@ export interface KavitaMediaError { filePath: string; comment: string; details: string; - created: string; - createdUtc: string; -} \ No newline at end of file +} diff --git a/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.html b/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.html index b8378e951..6caa31dc8 100644 --- a/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.html +++ b/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.html @@ -38,7 +38,7 @@ {{item.details}} - {{item.created | date:'shortDate'}} + {{item.createdUtc | translocoDate: {dateStyle: 'short', timeStyle: 'medium' } | defaultValue }} {{item.comment}} diff --git a/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.ts b/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.ts index 28c1bb843..8c9ca86e8 100644 --- a/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.ts +++ b/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.ts @@ -26,11 +26,14 @@ import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import {FilterPipe} from "../../pipe/filter.pipe"; import {LoadingComponent} from "../../shared/loading/loading.component"; import {TranslocoModule} from "@ngneat/transloco"; +import {DefaultDatePipe} from "../../pipe/default-date.pipe"; +import {DefaultValuePipe} from "../../pipe/default-value.pipe"; +import {TranslocoLocaleModule} from "@ngneat/transloco-locale"; @Component({ selector: 'app-manage-scrobble-errors', standalone: true, - imports: [CommonModule, ReactiveFormsModule, FilterPipe, LoadingComponent, SortableHeader, TranslocoModule], + imports: [CommonModule, ReactiveFormsModule, FilterPipe, LoadingComponent, SortableHeader, TranslocoModule, DefaultDatePipe, DefaultValuePipe, TranslocoLocaleModule], templateUrl: './manage-scrobble-errors.component.html', styleUrls: ['./manage-scrobble-errors.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush 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 75b08a106..117377849 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,7 @@ {{task.title | titlecase}} {{task.lastExecution | date:'short' | defaultValue }}{{task.lastExecutionUtc | translocoDate: {dateStyle: 'short', timeStyle: 'medium' } | defaultValue }} {{task.cron}}