fix(i18n): align translation keys in metadata viewer, searcher, sidecar, and tabs components (EN/ES)

This commit is contained in:
ACX
2026-02-10 08:32:56 -07:00
committed by acx10
parent 0f60faa124
commit 92a86bf23a
8 changed files with 134 additions and 123 deletions

View File

@@ -14,7 +14,7 @@
<div class="search-field search-field-provider">
<label>
<i class="pi pi-globe"></i>
{{ t('providers') }}
{{ t('providersLabel') }}
</label>
<p-multiselect
[filter]="false"
@@ -29,7 +29,7 @@
<div class="search-field">
<label>
<i class="pi pi-barcode"></i>
{{ t('isbn') }}
{{ t('isbnLabel') }}
<i
class="pi pi-info-circle"
[pTooltip]="t('isbnTooltip')"
@@ -47,7 +47,7 @@
<div class="search-field">
<label>
<i class="pi pi-book"></i>
{{ t('title') }}
{{ t('titleLabel') }}
</label>
<input
pInputText
@@ -59,7 +59,7 @@
<div class="search-field">
<label>
<i class="pi pi-user"></i>
{{ t('author') }}
{{ t('authorLabel') }}
</label>
<input
pInputText
@@ -70,7 +70,7 @@
<div class="search-field search-button-field">
<p-button
[label]="t('search')"
[label]="t('searchBtn')"
type="submit"
[disabled]="!isSearchEnabled || loading"
icon="pi pi-search"
@@ -128,7 +128,7 @@
<i class="pi pi-search"></i>
</div>
<h3>{{ t('readyToSearch') }}</h3>
<p>{{ t('readyToSearchDescription') }}</p>
<p>{{ t('readyToSearchHint') }}</p>
</div>
}
@@ -138,7 +138,7 @@
<i class="pi pi-inbox"></i>
</div>
<h3>{{ t('noResultsFound') }}</h3>
<p>{{ t('noResultsDescription') }}</p>
<p>{{ t('noResultsHint') }}</p>
</div>
}
@@ -154,7 +154,7 @@
loading="lazy"/>
<div class="card-overlay">
<i class="pi pi-eye"></i>
<span>{{ t('select') }}</span>
<span>{{ t('selectOverlay') }}</span>
</div>
</div>

View File

@@ -114,7 +114,7 @@
@if (isPhysicalBook()) {
<div class="empty-state">
<i class="pi pi-book empty-state-icon"></i>
<h4 class="empty-state-title">{{ t('physicalBook') }}</h4>
<h4 class="empty-state-title">{{ t('physicalBookTitle') }}</h4>
<p class="empty-state-message">{{ t('physicalBookMessage') }}</p>
<p class="empty-state-hint">{{ t('physicalBookHint') }}</p>
</div>
@@ -151,7 +151,7 @@
size="small"
text
severity="primary"
[pTooltip]="t('read')"
[pTooltip]="t('readTooltip')"
tooltipPosition="top"
(onClick)="read(book.id)">
</p-button>
@@ -160,7 +160,7 @@
size="small"
text
severity="info"
[pTooltip]="t('streamingReader')"
[pTooltip]="t('streamingReaderTooltip')"
tooltipPosition="top"
(onClick)="read(book.id, 'pdf-streaming')">
</p-button>
@@ -170,7 +170,7 @@
size="small"
text
severity="primary"
[pTooltip]="t('read')"
[pTooltip]="t('readTooltip')"
tooltipPosition="top"
(onClick)="read(book.id)">
</p-button>
@@ -179,7 +179,7 @@
size="small"
text
severity="info"
[pTooltip]="t('streamingReader')"
[pTooltip]="t('streamingReaderTooltip')"
tooltipPosition="top"
(onClick)="read(book.id, 'epub-streaming')">
</p-button>
@@ -189,7 +189,7 @@
size="small"
text
severity="primary"
[pTooltip]="t('read')"
[pTooltip]="t('readTooltip')"
tooltipPosition="top"
(onClick)="read(book.id)">
</p-button>
@@ -199,7 +199,7 @@
size="small"
text
severity="primary"
[pTooltip]="t('play')"
[pTooltip]="t('playTooltip')"
tooltipPosition="top"
(onClick)="read(book.id)">
</p-button>
@@ -209,7 +209,7 @@
size="small"
text
severity="success"
[pTooltip]="t('download')"
[pTooltip]="t('downloadTooltip')"
tooltipPosition="top"
(onClick)="download(book)">
</p-button>
@@ -218,7 +218,7 @@
size="small"
text
severity="danger"
[pTooltip]="t('delete')"
[pTooltip]="t('deleteTooltip')"
tooltipPosition="top"
(onClick)="deleteFile(book, book.primaryFile!.id, book.primaryFile!.fileName || 'file', true)">
</p-button>
@@ -247,7 +247,7 @@
size="small"
text
severity="primary"
[pTooltip]="t('read')"
[pTooltip]="t('readTooltip')"
tooltipPosition="top"
(onClick)="read(book.id, undefined, format.bookType)">
</p-button>
@@ -256,7 +256,7 @@
size="small"
text
severity="info"
[pTooltip]="t('streamingReader')"
[pTooltip]="t('streamingReaderTooltip')"
tooltipPosition="top"
(onClick)="read(book.id, format.bookType === 'PDF' ? 'pdf-streaming' : 'epub-streaming', format.bookType)">
</p-button>
@@ -266,7 +266,7 @@
size="small"
text
severity="primary"
[pTooltip]="t('read')"
[pTooltip]="t('readTooltip')"
tooltipPosition="top"
(onClick)="read(book.id, undefined, format.bookType)">
</p-button>
@@ -276,7 +276,7 @@
size="small"
text
severity="primary"
[pTooltip]="t('play')"
[pTooltip]="t('playTooltip')"
tooltipPosition="top"
(onClick)="read(book.id, undefined, format.bookType)">
</p-button>
@@ -286,7 +286,7 @@
size="small"
text
severity="success"
[pTooltip]="t('download')"
[pTooltip]="t('downloadTooltip')"
tooltipPosition="top"
(onClick)="downloadAdditionalFile(book, format.id)">
</p-button>
@@ -295,7 +295,7 @@
size="small"
text
severity="danger"
[pTooltip]="t('delete')"
[pTooltip]="t('deleteTooltip')"
tooltipPosition="top"
(onClick)="deleteFile(book, format.id, format.fileName || 'file', false)">
</p-button>
@@ -322,7 +322,7 @@
size="small"
text
severity="success"
[pTooltip]="t('download')"
[pTooltip]="t('downloadTooltip')"
tooltipPosition="top"
(onClick)="downloadAdditionalFile(book, file.id)">
</p-button>
@@ -331,7 +331,7 @@
size="small"
text
severity="danger"
[pTooltip]="t('delete')"
[pTooltip]="t('deleteTooltip')"
tooltipPosition="top"
(onClick)="deleteSupplementary(book.id, file.id, file.fileName || 'file')">
</p-button>

View File

@@ -270,7 +270,7 @@
<div class="tags-section">
@if (book?.metadata?.categories?.length) {
<div class="tag-row">
<span class="tag-label">{{ t('genres') }}</span>
<span class="tag-label">{{ t('genresLabel') }}</span>
<div class="tag-scroll">
<div class="tag-container">
@for (category of book.metadata!.categories; track category) {
@@ -285,7 +285,7 @@
@if (book?.metadata?.moods?.length) {
<div class="tag-row">
<span class="tag-label">{{ t('moods') }}</span>
<span class="tag-label">{{ t('moodsLabel') }}</span>
<div class="tag-scroll">
<div class="tag-container">
@for (mood of book.metadata!.moods; track mood) {
@@ -300,7 +300,7 @@
@if (book?.metadata?.tags?.length) {
<div class="tag-row">
<span class="tag-label">{{ t('tags') }}</span>
<span class="tag-label">{{ t('tagsLabel') }}</span>
<div class="tag-scroll">
<div class="tag-container">
@for (tag of book.metadata!.tags; track tag) {
@@ -316,7 +316,7 @@
<div class="metadata-grid">
<div class="metadata-item">
<span class="metadata-key">{{ t('library') }}</span>
<span class="metadata-key">{{ t('libraryLabel') }}</span>
@if (book?.libraryName) {
<span class="metadata-value metadata-link" (click)="goToLibrary(book.libraryId!)">
{{ book?.libraryName }}
@@ -327,7 +327,7 @@
</div>
<div class="metadata-item">
<span class="metadata-key">{{ t('publisher') }}</span>
<span class="metadata-key">{{ t('publisherLabel') }}</span>
@if (book?.metadata?.publisher) {
<span class="metadata-value metadata-link" (click)="goToPublisher(book?.metadata?.publisher!)">
{{ book?.metadata?.publisher }}
@@ -338,7 +338,7 @@
</div>
<div class="metadata-item">
<span class="metadata-key">{{ t('published') }}</span>
<span class="metadata-key">{{ t('publishedLabel') }}</span>
@if (book?.metadata!.publishedDate) {
<span class="metadata-value metadata-link" (click)="goToPublishedYear(book?.metadata!.publishedDate!)">
{{ book?.metadata!.publishedDate }}
@@ -349,7 +349,7 @@
</div>
<div class="metadata-item">
<span class="metadata-key">{{ t('language') }}</span>
<span class="metadata-key">{{ t('languageLabel') }}</span>
@if (book?.metadata!.language) {
<span class="metadata-value metadata-link" (click)="goToLanguage(book?.metadata!.language!)">
{{ book?.metadata!.language }}
@@ -360,14 +360,14 @@
</div>
<div class="metadata-item">
<span class="metadata-key">{{ t('formats') }}</span>
<span class="metadata-key">{{ t('formatsLabel') }}</span>
<span class="metadata-value-group format-tags">
@if (getDisplayFormat(book?.primaryFile); as displayFormat) {
<span class="format-tag-wrapper primary-format" (click)="goToFileType(book?.primaryFile?.filePath)" [pTooltip]="book?.primaryFile?.fileName ?? ''" tooltipPosition="top">
<app-tag size="3xs" variant="pill" [customBgColor]="getFileTypeBgColor(displayFormat)" customTextColor="#fff">
{{ displayFormat }}
</app-tag>
<span class="format-badge">{{ t('primary') }}</span>
<span class="format-badge">{{ t('primaryBadge') }}</span>
</span>
}
@for (formatExt of getUniqueAlternativeFormats(book); track formatExt) {
@@ -378,13 +378,13 @@
</span>
}
@if (!book?.primaryFile?.filePath && !book?.alternativeFormats?.length) {
<app-tag size="3xs" variant="pill" [customBgColor]="'var(--book-type-physical-color)'" customTextColor="#fff">{{ t('physical') }}</app-tag>
<app-tag size="3xs" variant="pill" [customBgColor]="'var(--book-type-physical-color)'" customTextColor="#fff">{{ t('physicalBadge') }}</app-tag>
}
</span>
</div>
<div class="metadata-item">
<span class="metadata-key">{{ t('bookloreProgress') }}</span>
<span class="metadata-key">{{ t('bookloreProgressLabel') }}</span>
<span class="metadata-value-group">
<app-tag size="3xs" variant="pill" [color]="getProgressColor(getProgressPercent(book))">
{{ getProgressPercent(book) !== null ? getProgressPercent(book) + '%' : 'N/A' }}
@@ -405,7 +405,7 @@
</div>
<div class="metadata-item">
<span class="metadata-key">{{ t('metadataMatch') }}</span>
<span class="metadata-key">{{ t('metadataMatchLabel') }}</span>
@if (book?.metadataMatchScore != null) {
<span (click)="goToMatchScoreRange(book?.metadataMatchScore!)" style="cursor: pointer;">
<app-tag size="3xs" variant="pill" [color]="getMatchScoreColor(book?.metadataMatchScore!)">
@@ -433,7 +433,7 @@
</div>
<div class="metadata-item">
<span class="metadata-key">{{ t('readStatus') }}</span>
<span class="metadata-key">{{ t('readStatusLabel') }}</span>
<span class="metadata-value-group">
<span (click)="goToReadStatus(selectedReadStatus)" style="cursor: pointer;">
<app-tag size="3xs" variant="pill" [color]="getStatusColor(selectedReadStatus)">
@@ -447,7 +447,7 @@
@if (book?.koreaderProgress && book.koreaderProgress?.percentage != null) {
<div class="metadata-item">
<span class="metadata-key">{{ t('koreaderProgress') }}</span>
<span class="metadata-key">{{ t('koreaderProgressLabel') }}</span>
<span class="metadata-value-group">
<app-tag size="3xs" variant="pill" [color]="getKoProgressColor(getKOReaderPercentage(book))">
{{ getKOReaderPercentage(book) + '%' }}
@@ -468,7 +468,7 @@
@if (book?.koboProgress && book.koboProgress?.percentage != null) {
<div class="metadata-item">
<span class="metadata-key">{{ t('koboProgress') }}</span>
<span class="metadata-key">{{ t('koboProgressLabel') }}</span>
<span class="metadata-value-group">
<app-tag size="3xs" variant="pill" [color]="getKoProgressColor(book.koboProgress?.percentage)">
{{ book.koboProgress?.percentage | number:'1.0-1' }}%
@@ -490,7 +490,7 @@
@if (selectedReadStatus === ReadStatus.READ) {
<div class="metadata-item">
@if (book?.dateFinished) {
<span class="metadata-key">{{ t('finishedOn') }}</span>
<span class="metadata-key">{{ t('finishedOnLabel') }}</span>
@if (!isEditingDateFinished) {
<span class="metadata-value-group">
<span class="metadata-value">{{ formatDate(book.dateFinished) }}</span>
@@ -515,7 +515,7 @@
}
<div class="metadata-item">
<span class="metadata-key">{{ t('pageCount') }}</span>
<span class="metadata-key">{{ t('pageCountLabel') }}</span>
@if (book?.metadata!.pageCount) {
<span class="metadata-value metadata-link" (click)="goToPageCountRange(book?.metadata!.pageCount!)">
{{ book?.metadata!.pageCount }}
@@ -526,7 +526,7 @@
</div>
<div class="metadata-item">
<span class="metadata-key">{{ t('ageRating') }}</span>
<span class="metadata-key">{{ t('ageRatingLabel') }}</span>
@if (book?.metadata?.ageRating != null) {
<span class="metadata-value metadata-link" (click)="goToAgeRating(book?.metadata?.ageRating!)">
{{ getAgeRatingLabel(book?.metadata?.ageRating) }}
@@ -537,7 +537,7 @@
</div>
<div class="metadata-item">
<span class="metadata-key">{{ t('contentRating') }}</span>
<span class="metadata-key">{{ t('contentRatingLabel') }}</span>
@if (book?.metadata?.contentRating) {
<span class="metadata-value metadata-link" (click)="goToContentRating(book?.metadata?.contentRating!)">
{{ getContentRatingLabel(book?.metadata?.contentRating) }}
@@ -549,7 +549,7 @@
@if (hasDigitalFile(book)) {
<div class="metadata-item">
<span class="metadata-key">{{ t('fileSize') }}</span>
<span class="metadata-key">{{ t('fileSizeLabel') }}</span>
@if (book?.primaryFile?.fileSizeKb) {
<span class="metadata-value metadata-link" (click)="goToFileSizeRange(book?.primaryFile?.fileSizeKb!)">
{{ formatFileSize(book?.primaryFile) }}
@@ -563,7 +563,7 @@
@if (hasDigitalFile(book)) {
<div class="file-path-section">
<span class="metadata-key">{{ t('filePath') }}</span>
<span class="metadata-key">{{ t('filePathLabel') }}</span>
<i class="pi eye-icon" [ngClass]="showFilePath ? 'pi-eye-slash' : 'pi-eye'" (click)="showFilePath = !showFilePath"></i>
@if (showFilePath) {
<code class="file-path-code">{{ book?.primaryFile?.filePath }}</code>
@@ -628,7 +628,7 @@
}
@if (userState.user!.permissions.canEditMetadata || userState.user!.permissions.admin) {
<p-button
[label]="isAutoFetching ? t('fetchingMetadata') : t('fetchMetadata')"
[label]="isAutoFetching ? t('fetchingBtn') : t('fetchMetadataBtn')"
[icon]="isAutoFetching ? 'pi pi-spin pi-spinner' : 'pi pi-bolt'"
[outlined]="true"
severity="warn"
@@ -655,7 +655,7 @@
<div class="description-section">
<div class="section-header">
<i class="pi pi-align-left"></i>
<span>{{ t('synopsis') }}</span>
<span>{{ t('synopsisTitle') }}</span>
</div>
<div class="description-body">
<div [ngClass]="{ 'line-clamp-7': !isExpanded }" class="description-content">
@@ -679,7 +679,7 @@
<div class="section-header clickable" (click)="isComicSectionExpanded = !isComicSectionExpanded">
<div class="section-header-left">
<i class="pi pi-images"></i>
<span>{{ t('comicDetails') }}</span>
<span>{{ t('comicDetailsTitle') }}</span>
</div>
<i class="pi section-toggle-icon" [ngClass]="isComicSectionExpanded ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
</div>
@@ -690,13 +690,13 @@
<div class="comic-info-grid">
@if (comic.issueNumber) {
<div class="comic-info-item">
<span class="comic-info-label">{{ t('issue') }}</span>
<span class="comic-info-label">{{ t('comicIssue') }}</span>
<span class="comic-info-value">#{{ comic.issueNumber }}</span>
</div>
}
@if (comic.volumeName) {
<div class="comic-info-item">
<span class="comic-info-label">{{ t('volume') }}</span>
<span class="comic-info-label">{{ t('comicVolume') }}</span>
<span class="comic-info-value">
{{ comic.volumeName }}
@if (comic.volumeNumber) {
@@ -707,7 +707,7 @@
}
@if (comic.storyArc) {
<div class="comic-info-item">
<span class="comic-info-label">{{ t('storyArc') }}</span>
<span class="comic-info-label">{{ t('comicStoryArc') }}</span>
<span class="comic-info-value">
{{ comic.storyArc }}
@if (comic.storyArcNumber) {
@@ -718,7 +718,7 @@
}
@if (comic.alternateSeries) {
<div class="comic-info-item">
<span class="comic-info-label">{{ t('altSeries') }}</span>
<span class="comic-info-label">{{ t('comicAltSeries') }}</span>
<span class="comic-info-value">
{{ comic.alternateSeries }}
@if (comic.alternateIssue) {
@@ -729,13 +729,13 @@
}
@if (comic.format) {
<div class="comic-info-item">
<span class="comic-info-label">{{ t('format') }}</span>
<span class="comic-info-label">{{ t('comicFormat') }}</span>
<span class="comic-info-value">{{ comic.format }}</span>
</div>
}
@if (comic.imprint) {
<div class="comic-info-item">
<span class="comic-info-label">{{ t('imprint') }}</span>
<span class="comic-info-label">{{ t('comicImprint') }}</span>
<span class="comic-info-value">{{ comic.imprint }}</span>
</div>
}
@@ -745,7 +745,7 @@
<div class="comic-subsection">
<div class="comic-subsection-title">
<i class="pi pi-users"></i>
<span>{{ t('creators') }}</span>
<span>{{ t('creatorsTitle') }}</span>
</div>
<div class="comic-creators-grid">
@if (comic.pencillers?.length) {
@@ -816,7 +816,7 @@
<div class="comic-subsection">
<div class="comic-subsection-title">
<i class="pi pi-globe"></i>
<span>{{ t('universe') }}</span>
<span>{{ t('universeTitle') }}</span>
</div>
<div class="comic-tags-container">
@if (comic.characters?.length) {
@@ -868,12 +868,12 @@
}
@if (comic.blackAndWhite) {
<span class="comic-property-badge bw">
<i class="pi pi-palette"></i> {{ t('bw') }}
<i class="pi pi-palette"></i> {{ t('blackAndWhite') }}
</span>
}
@if (comic.readingDirection === 'rtl') {
<span class="comic-property-badge rtl">
<i class="pi pi-arrow-left"></i> {{ t('rtl') }}
<i class="pi pi-arrow-left"></i> {{ t('rightToLeft') }}
</span>
}
</div>
@@ -892,7 +892,7 @@
<div class="comic-notes">
<div class="comic-notes-label">
<i class="pi pi-file-edit"></i>
<span>{{ t('comicNotes') }}</span>
<span>{{ t('notesLabel') }}</span>
</div>
<p class="comic-notes-content">{{ comic.notes }}</p>
</div>
@@ -906,7 +906,7 @@
<div class="section-header clickable" (click)="isAudiobookSectionExpanded = !isAudiobookSectionExpanded">
<div class="section-header-left">
<i class="pi pi-headphones"></i>
<span>{{ t('audiobookDetails') }}</span>
<span>{{ t('audiobookDetailsTitle') }}</span>
</div>
<i class="pi section-toggle-icon" [ngClass]="isAudiobookSectionExpanded ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
</div>
@@ -917,13 +917,13 @@
<div class="audiobook-info-grid">
@if (audio.durationSeconds) {
<div class="audiobook-info-item">
<span class="audiobook-info-label">{{ t('duration') }}</span>
<span class="audiobook-info-label">{{ t('audiobookDuration') }}</span>
<span class="audiobook-info-value">{{ formatDuration(audio.durationSeconds) }}</span>
</div>
}
@if (book.metadata?.narrator) {
<div class="audiobook-info-item">
<span class="audiobook-info-label">{{ t('narrator') }}</span>
<span class="audiobook-info-label">{{ t('audiobookNarrator') }}</span>
<a class="audiobook-info-value audiobook-link" (click)="goToNarrator(book.metadata!.narrator!)">
{{ book.metadata?.narrator }}
</a>
@@ -931,31 +931,31 @@
}
@if (audio.chapterCount) {
<div class="audiobook-info-item">
<span class="audiobook-info-label">{{ t('chapters') }}</span>
<span class="audiobook-info-label">{{ t('audiobookChapters') }}</span>
<span class="audiobook-info-value">{{ audio.chapterCount }}</span>
</div>
}
@if (audio.bitrate) {
<div class="audiobook-info-item">
<span class="audiobook-info-label">{{ t('bitrate') }}</span>
<span class="audiobook-info-label">{{ t('audiobookBitrate') }}</span>
<span class="audiobook-info-value">{{ audio.bitrate }} kbps</span>
</div>
}
@if (audio.sampleRate) {
<div class="audiobook-info-item">
<span class="audiobook-info-label">{{ t('sampleRate') }}</span>
<span class="audiobook-info-label">{{ t('audiobookSampleRate') }}</span>
<span class="audiobook-info-value">{{ formatSampleRate(audio.sampleRate) }}</span>
</div>
}
@if (audio.channels) {
<div class="audiobook-info-item">
<span class="audiobook-info-label">{{ t('channels') }}</span>
<span class="audiobook-info-label">{{ t('audiobookChannels') }}</span>
<span class="audiobook-info-value">{{ getChannelLabel(audio.channels) }}</span>
</div>
}
@if (audio.codec) {
<div class="audiobook-info-item">
<span class="audiobook-info-label">{{ t('codec') }}</span>
<span class="audiobook-info-label">{{ t('audiobookCodec') }}</span>
<span class="audiobook-info-value">{{ audio.codec.toUpperCase() }}</span>
</div>
}

View File

@@ -78,16 +78,16 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
isEditingDateFinished = false;
editDateFinished: Date | null = null;
readStatusOptions: { value: ReadStatus, label: string }[] = [
{value: ReadStatus.UNREAD, label: 'Unread'},
{value: ReadStatus.PAUSED, label: 'Paused'},
{value: ReadStatus.READING, label: 'Reading'},
{value: ReadStatus.RE_READING, label: 'Re-reading'},
{value: ReadStatus.READ, label: 'Read'},
{value: ReadStatus.PARTIALLY_READ, label: 'Partially Read'},
{value: ReadStatus.ABANDONED, label: 'Abandoned'},
{value: ReadStatus.WONT_READ, label: 'Won\'t Read'},
{value: ReadStatus.UNSET, label: 'Unset'},
readStatusOptions: { value: ReadStatus, labelKey: string }[] = [
{value: ReadStatus.UNREAD, labelKey: 'metadata.viewer.readStatusUnread'},
{value: ReadStatus.PAUSED, labelKey: 'metadata.viewer.readStatusPaused'},
{value: ReadStatus.READING, labelKey: 'metadata.viewer.readStatusReading'},
{value: ReadStatus.RE_READING, labelKey: 'metadata.viewer.readStatusReReading'},
{value: ReadStatus.READ, labelKey: 'metadata.viewer.readStatusRead'},
{value: ReadStatus.PARTIALLY_READ, labelKey: 'metadata.viewer.readStatusPartiallyRead'},
{value: ReadStatus.ABANDONED, labelKey: 'metadata.viewer.readStatusAbandoned'},
{value: ReadStatus.WONT_READ, labelKey: 'metadata.viewer.readStatusWontRead'},
{value: ReadStatus.UNSET, labelKey: 'metadata.viewer.readStatusUnset'},
];
private bookNavigationService = inject(BookNavigationService);
@@ -105,13 +105,13 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
const primaryType = book.primaryFile?.bookType;
if (primaryType === 'PDF') {
items.push({
label: this.t.translate('metadata.viewer.streamingReader'),
label: this.t.translate('metadata.viewer.menuStreamingReader'),
icon: 'pi pi-play',
command: () => this.read(book.id, 'pdf-streaming')
});
} else if (primaryType === 'EPUB') {
items.push({
label: this.t.translate('metadata.viewer.streamingReader'),
label: this.t.translate('metadata.viewer.menuStreamingReader'),
icon: 'pi pi-play',
command: () => this.read(book.id, 'epub-streaming')
});
@@ -138,12 +138,12 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
icon: this.getFileIcon(formatType),
items: [
{
label: this.t.translate('metadata.viewer.standardReader'),
label: this.t.translate('metadata.viewer.menuStandardReader'),
icon: 'pi pi-book',
command: () => this.read(book.id, undefined, formatType)
},
{
label: this.t.translate('metadata.viewer.streamingReader'),
label: this.t.translate('metadata.viewer.menuStreamingReader'),
icon: 'pi pi-play',
command: () => this.read(book.id, formatType === 'PDF' ? 'pdf-streaming' : 'epub-streaming', formatType)
}
@@ -216,7 +216,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
const items: MenuItem[] = [];
items.push({
label: this.t.translate('metadata.viewer.shelf'),
label: this.t.translate('metadata.viewer.menuShelf'),
icon: 'pi pi-folder',
command: () => this.assignShelf(book.id)
});
@@ -225,7 +225,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
if (userState?.user?.permissions.canUpload || userState?.user?.permissions.admin) {
items.push({
label: this.t.translate('metadata.viewer.uploadFile'),
label: this.t.translate('metadata.viewer.menuUploadFile'),
icon: 'pi pi-upload',
command: () => {
this.bookDialogHelperService.openAdditionalFileUploaderDialog(book);
@@ -237,7 +237,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
if (hasFiles && (userState?.user?.permissions.canManageLibrary || userState?.user?.permissions.admin) && appSettings?.diskType === 'LOCAL') {
items.push({
label: this.t.translate('metadata.viewer.organizeFiles'),
label: this.t.translate('metadata.viewer.menuOrganizeFiles'),
icon: 'pi pi-arrows-h',
command: () => {
this.openFileMoverDialog(book.id);
@@ -247,16 +247,16 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
if (hasFiles && (userState?.user?.permissions.canEmailBook || userState?.user?.permissions.admin)) {
items.push({
label: this.t.translate('metadata.viewer.sendBook'),
label: this.t.translate('metadata.viewer.menuSendBook'),
icon: 'pi pi-send',
items: [
{
label: this.t.translate('metadata.viewer.quickSend'),
label: this.t.translate('metadata.viewer.menuQuickSend'),
icon: 'pi pi-bolt',
command: () => this.quickSend(book.id)
},
{
label: this.t.translate('metadata.viewer.customSend'),
label: this.t.translate('metadata.viewer.menuCustomSend'),
icon: 'pi pi-cog',
command: () => {
this.bookDialogHelperService.openCustomSendDialog(book);
@@ -270,7 +270,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
const isSingleFileBook = hasFiles && !book.alternativeFormats?.length;
if (isSingleFileBook && (userState?.user?.permissions.canManageLibrary || userState?.user?.permissions.admin)) {
items.push({
label: this.t.translate('metadata.viewer.attachToAnotherBook'),
label: this.t.translate('metadata.viewer.menuAttachToAnotherBook'),
icon: 'pi pi-link',
command: () => {
this.bookDialogHelperService.openBookFileAttacherDialog(book);
@@ -312,7 +312,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
if (deleteFormatItems.length > 0) {
items.push({
label: this.t.translate('metadata.viewer.deleteFileFormats'),
label: this.t.translate('metadata.viewer.menuDeleteFileFormats'),
icon: 'pi pi-file',
items: deleteFormatItems
});
@@ -333,7 +333,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
});
items.push({
label: this.t.translate('metadata.viewer.deleteSupplementaryFiles'),
label: this.t.translate('metadata.viewer.menuDeleteSupplementaryFiles'),
icon: 'pi pi-paperclip',
items: deleteSupplementaryItems
});
@@ -356,11 +356,11 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
? `\n\nThe following files will be permanently deleted:\n• ${allFormats.join('\n• ')}`
: '';
const deleteLabel = isPhysical ? this.t.translate('metadata.viewer.deleteBook') : this.t.translate('metadata.viewer.deleteBookAndAllFiles');
const deleteLabel = isPhysical ? this.t.translate('metadata.viewer.menuDeleteBook') : this.t.translate('metadata.viewer.menuDeleteBookAllFiles');
const deleteMessage = isPhysical
? this.t.translate('metadata.viewer.confirm.deleteBookMessage', { title: book.metadata?.title })
: this.t.translate('metadata.viewer.confirm.deleteBookAndFilesMessage', { title: book.metadata?.title, fileList: fileListMessage });
const deleteAcceptLabel = isPhysical ? this.t.translate('common.delete') : this.t.translate('metadata.viewer.deleteEverything');
: this.t.translate('metadata.viewer.confirm.deleteBookAllFilesMessage', { title: book.metadata?.title, fileList: fileListMessage });
const deleteAcceptLabel = isPhysical ? this.t.translate('common.delete') : this.t.translate('metadata.viewer.confirm.deleteEverythingBtn');
items.push({
label: deleteLabel,
@@ -480,9 +480,9 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
getReadButtonLabel(book: Book): string {
const isAudiobook = book.primaryFile?.bookType === 'AUDIOBOOK';
if (this.isInProgressStatus()) {
return isAudiobook ? this.t.translate('metadata.viewer.continue') : this.t.translate('metadata.viewer.continueReading');
return isAudiobook ? this.t.translate('metadata.viewer.continueBtn') : this.t.translate('metadata.viewer.continueReadingBtn');
}
return isAudiobook ? this.t.translate('metadata.viewer.playAction') : this.t.translate('metadata.viewer.readAction');
return isAudiobook ? this.t.translate('metadata.viewer.playBtn') : this.t.translate('metadata.viewer.readBtn');
}
getReadButtonIcon(book: Book): string {
@@ -564,8 +564,8 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
message = this.t.translate('metadata.viewer.confirm.deletePrimaryFormatMessage', { fileName });
header = this.t.translate('metadata.viewer.confirm.deletePrimaryFormatHeader');
} else {
message = this.t.translate('metadata.viewer.confirm.deleteAlternativeFormatMessage', { fileName });
header = this.t.translate('metadata.viewer.confirm.deleteAlternativeFormatHeader');
message = this.t.translate('metadata.viewer.confirm.deleteAltFormatMessage', { fileName });
header = this.t.translate('metadata.viewer.confirm.deleteAltFormatHeader');
}
this.confirmationService.confirm({
@@ -574,7 +574,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
icon: 'pi pi-exclamation-triangle',
acceptIcon: 'pi pi-trash',
rejectIcon: 'pi pi-times',
acceptLabel: this.t.translate('metadata.viewer.confirm.deleteFile'),
acceptLabel: this.t.translate('metadata.viewer.confirm.deleteFileBtn'),
rejectLabel: this.t.translate('common.cancel'),
rejectButtonStyleClass: 'p-button-secondary',
acceptButtonStyleClass: 'p-button-danger',
@@ -622,7 +622,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
error: (err) => this.messageService.add({
severity: 'error',
summary: this.t.translate('metadata.viewer.toast.quickSendErrorSummary'),
detail: err?.error?.message || this.t.translate('metadata.viewer.toast.quickSendErrorDetailFallback'),
detail: err?.error?.message || this.t.translate('metadata.viewer.toast.quickSendErrorDetail'),
})
});
}
@@ -1122,12 +1122,13 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
}
readStatusMenuItems = this.readStatusOptions.map(option => ({
label: option.label,
label: this.t.translate(option.labelKey),
command: () => this.updateReadStatus(option.value)
}));
getStatusLabel(value: string): string {
return this.readStatusOptions.find(o => o.value === value)?.label.toUpperCase() ?? 'UNSET';
const option = this.readStatusOptions.find(o => o.value === value);
return option ? this.t.translate(option.labelKey).toUpperCase() : 'UNSET';
}
@@ -1170,8 +1171,8 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
error: () => {
this.messageService.add({
severity: 'error',
summary: this.t.translate('metadata.viewer.toast.dateFailedSummary'),
detail: this.t.translate('metadata.viewer.toast.dateFailedDetail'),
summary: this.t.translate('metadata.viewer.toast.dateUpdateFailedSummary'),
detail: this.t.translate('metadata.viewer.toast.dateUpdateFailedDetail'),
life: 3000
});
}
@@ -1225,7 +1226,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
getNavigationPosition(): string {
const position = this.bookNavigationService.getCurrentPosition();
return position ? this.t.translate('metadata.viewer.navigationOf', { current: position.current, total: position.total }) : '';
return position ? this.t.translate('metadata.viewer.navigationPosition', { current: position.current, total: position.total }) : '';
}
hasDigitalFile(book: Book): boolean {
@@ -1353,11 +1354,11 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
getChannelLabel(channels: number): string {
switch (channels) {
case 1:
return 'Mono';
return this.t.translate('metadata.viewer.channelMono');
case 2:
return 'Stereo';
return this.t.translate('metadata.viewer.channelStereo');
default:
return `${channels} channels`;
return this.t.translate('metadata.viewer.channelMultiple', { count: channels });
}
}

View File

@@ -24,7 +24,7 @@
} @else {
<div class="sidecar-actions">
<p-button
[label]="t('exportToSidecar')"
[label]="t('exportBtn')"
icon="pi pi-upload"
[loading]="exporting"
(onClick)="exportToSidecar()"
@@ -34,7 +34,7 @@
tooltipPosition="top"
/>
<p-button
[label]="t('importFromSidecar')"
[label]="t('importBtn')"
icon="pi pi-download"
[loading]="importing"
[disabled]="syncStatus === 'MISSING'"
@@ -49,7 +49,7 @@
@if (syncStatus === 'MISSING') {
<div class="empty-state">
<i class="pi pi-file"></i>
<h4>{{ t('noSidecarFile') }}</h4>
<h4>{{ t('noSidecarTitle') }}</h4>
<p>
{{ t('noSidecarDescription') }}
</p>
@@ -60,15 +60,15 @@
<div class="content-meta">
<span class="meta-item">
<i class="pi pi-clock"></i>
{{ t('generated') }} {{ sidecarContent.generatedAt | date:'medium' }}
{{ t('generatedLabel') }} {{ sidecarContent.generatedAt | date:'medium' }}
</span>
<span class="meta-item">
<i class="pi pi-tag"></i>
{{ t('version') }} {{ sidecarContent.version }}
{{ t('versionLabel') }} {{ sidecarContent.version }}
</span>
<span class="meta-item">
<i class="pi pi-cog"></i>
{{ t('generatedBy') }} {{ sidecarContent.generatedBy }}
{{ t('byLabel') }} {{ sidecarContent.generatedBy }}
</span>
</div>
</div>
@@ -80,7 +80,7 @@
@if (sidecarContent.cover) {
<div class="cover-info">
<i class="pi pi-image"></i>
<span>{{ t('coverFile') }} {{ sidecarContent.cover.path }}</span>
<span>{{ t('coverFileLabel') }} {{ sidecarContent.cover.path }}</span>
</div>
}
</div>

View File

@@ -161,17 +161,17 @@ export class SidecarViewerComponent implements OnInit, OnDestroy {
getSyncStatusLabel(): string {
switch (this.syncStatus) {
case 'IN_SYNC':
return this.t.translate('metadata.sidecar.syncStatus.inSync');
return this.t.translate('metadata.sidecar.syncStatusInSync');
case 'OUTDATED':
return this.t.translate('metadata.sidecar.syncStatus.outdated');
return this.t.translate('metadata.sidecar.syncStatusOutdated');
case 'CONFLICT':
return this.t.translate('metadata.sidecar.syncStatus.conflict');
return this.t.translate('metadata.sidecar.syncStatusConflict');
case 'MISSING':
return this.t.translate('metadata.sidecar.syncStatus.missing');
return this.t.translate('metadata.sidecar.syncStatusMissing');
case 'NOT_APPLICABLE':
return this.t.translate('metadata.sidecar.syncStatus.notApplicable');
return this.t.translate('metadata.sidecar.syncStatusNA');
default:
return this.t.translate('metadata.sidecar.syncStatus.unknown');
return this.t.translate('metadata.sidecar.syncStatusUnknown');
}
}

View File

@@ -215,7 +215,8 @@
"playTooltip": "Play",
"downloadTooltip": "Download",
"deleteTooltip": "Delete",
"downloadAllTooltip": "Download all files as a ZIP archive"
"downloadAllTooltip": "Download all files as a ZIP archive",
"physicalBookHint": "You can upload a digital copy or add metadata manually."
},
"viewer": {
"metadataLocked": "This book metadata is locked.",
@@ -288,6 +289,10 @@
"continueBtn": "Continue",
"fetchMetadataBtn": "Fetch Metadata",
"fetchingBtn": "Fetching...",
"fetchMetadataTooltip": "Quickly refresh metadata using your default fetch settings",
"goToPreviousBook": "Go to Previous Book",
"goToNextBook": "Go to Next Book",
"personalRating": "Personal Rating",
"loadingBookDetails": "Loading book details...",
"readStatusUnread": "Unread",
"readStatusPaused": "Paused",

View File

@@ -215,7 +215,8 @@
"playTooltip": "Reproducir",
"downloadTooltip": "Descargar",
"deleteTooltip": "Eliminar",
"downloadAllTooltip": "Descargar todos los archivos como archivo ZIP"
"downloadAllTooltip": "Descargar todos los archivos como archivo ZIP",
"physicalBookHint": "Puede subir una copia digital o agregar metadatos manualmente."
},
"viewer": {
"metadataLocked": "Los metadatos de este libro están bloqueados.",
@@ -288,6 +289,10 @@
"continueBtn": "Continuar",
"fetchMetadataBtn": "Obtener metadatos",
"fetchingBtn": "Obteniendo...",
"fetchMetadataTooltip": "Actualizar rápidamente los metadatos usando la configuración predeterminada",
"goToPreviousBook": "Ir al libro anterior",
"goToNextBook": "Ir al siguiente libro",
"personalRating": "Calificación personal",
"loadingBookDetails": "Cargando detalles del libro...",
"readStatusUnread": "No leído",
"readStatusPaused": "Pausado",