Translate Activity pages

This commit is contained in:
Stevie Robinson 2023-08-05 22:59:07 +02:00 committed by Mark McDowall
parent 02b0710814
commit 322836e2b3
17 changed files with 180 additions and 106 deletions

View File

@ -15,6 +15,7 @@ import TablePager from 'Components/Table/TablePager';
import { align, icons, kinds } from 'Helpers/Props'; import { align, icons, kinds } from 'Helpers/Props';
import getRemovedItems from 'Utilities/Object/getRemovedItems'; import getRemovedItems from 'Utilities/Object/getRemovedItems';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems'; import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds'; import getSelectedIds from 'Utilities/Table/getSelectedIds';
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState'; import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
import selectAll from 'Utilities/Table/selectAll'; import selectAll from 'Utilities/Table/selectAll';
@ -116,11 +117,11 @@ class Blocklist extends Component {
const selectedIds = this.getSelectedIds(); const selectedIds = this.getSelectedIds();
return ( return (
<PageContent title="Blocklist"> <PageContent title={translate('Blocklist')}>
<PageToolbar> <PageToolbar>
<PageToolbarSection> <PageToolbarSection>
<PageToolbarButton <PageToolbarButton
label="Remove Selected" label={translate('RemoveSelected')}
iconName={icons.REMOVE} iconName={icons.REMOVE}
isDisabled={!selectedIds.length} isDisabled={!selectedIds.length}
isSpinning={isRemoving} isSpinning={isRemoving}
@ -128,7 +129,7 @@ class Blocklist extends Component {
/> />
<PageToolbarButton <PageToolbarButton
label="Clear" label={translate('Clear')}
iconName={icons.CLEAR} iconName={icons.CLEAR}
isSpinning={isClearingBlocklistExecuting} isSpinning={isClearingBlocklistExecuting}
onPress={onClearBlocklistPress} onPress={onClearBlocklistPress}
@ -141,7 +142,7 @@ class Blocklist extends Component {
columns={columns} columns={columns}
> >
<PageToolbarButton <PageToolbarButton
label="Options" label={translate('Options')}
iconName={icons.TABLE} iconName={icons.TABLE}
/> />
</TableOptionsModalWrapper> </TableOptionsModalWrapper>
@ -156,13 +157,15 @@ class Blocklist extends Component {
{ {
!isFetching && !!error && !isFetching && !!error &&
<Alert kind={kinds.DANGER}>Unable to load blocklist</Alert> <Alert kind={kinds.DANGER}>
{translate('BlocklistLoadError')}
</Alert>
} }
{ {
isPopulated && !error && !items.length && isPopulated && !error && !items.length &&
<Alert kind={kinds.INFO}> <Alert kind={kinds.INFO}>
No history blocklist {translate('NoHistoryBlocklist')}
</Alert> </Alert>
} }
@ -206,9 +209,9 @@ class Blocklist extends Component {
<ConfirmModal <ConfirmModal
isOpen={isConfirmRemoveModalOpen} isOpen={isConfirmRemoveModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title="Remove Selected" title={translate('RemoveSelected')}
message={'Are you sure you want to remove the selected items from the blocklist?'} message={translate('RemoveSelectedBlocklistMessageText')}
confirmLabel="Remove Selected" confirmLabel={translate('RemoveSelected')}
onConfirm={this.onRemoveSelectedConfirmed} onConfirm={this.onRemoveSelectedConfirmed}
onCancel={this.onConfirmRemoveModalClose} onCancel={this.onConfirmRemoveModalClose}
/> />

View File

@ -8,6 +8,7 @@ import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent'; import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter'; import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader'; import ModalHeader from 'Components/Modal/ModalHeader';
import translate from 'Utilities/String/translate';
class BlocklistDetailsModal extends Component { class BlocklistDetailsModal extends Component {
@ -39,19 +40,19 @@ class BlocklistDetailsModal extends Component {
<ModalBody> <ModalBody>
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
title="Name" title={translate('Name')}
data={sourceTitle} data={sourceTitle}
/> />
<DescriptionListItem <DescriptionListItem
title="Protocol" title={translate('Protocol')}
data={protocol} data={protocol}
/> />
{ {
!!message && !!message &&
<DescriptionListItem <DescriptionListItem
title="Indexer" title={translate('Indexer')}
data={indexer} data={indexer}
/> />
} }
@ -59,7 +60,7 @@ class BlocklistDetailsModal extends Component {
{ {
!!message && !!message &&
<DescriptionListItem <DescriptionListItem
title="Message" title={translate('Message')}
data={message} data={message}
/> />
} }
@ -68,7 +69,7 @@ class BlocklistDetailsModal extends Component {
<ModalFooter> <ModalFooter>
<Button onPress={onModalClose}> <Button onPress={onModalClose}>
Close {translate('Close')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>

View File

@ -10,6 +10,7 @@ import EpisodeLanguages from 'Episode/EpisodeLanguages';
import EpisodeQuality from 'Episode/EpisodeQuality'; import EpisodeQuality from 'Episode/EpisodeQuality';
import { icons, kinds } from 'Helpers/Props'; import { icons, kinds } from 'Helpers/Props';
import SeriesTitleLink from 'Series/SeriesTitleLink'; import SeriesTitleLink from 'Series/SeriesTitleLink';
import translate from 'Utilities/String/translate';
import BlocklistDetailsModal from './BlocklistDetailsModal'; import BlocklistDetailsModal from './BlocklistDetailsModal';
import styles from './BlocklistRow.css'; import styles from './BlocklistRow.css';
@ -164,7 +165,7 @@ class BlocklistRow extends Component {
/> />
<IconButton <IconButton
title="Remove from blocklist" title={translate('RemoveFromBlocklist')}
name={icons.REMOVE} name={icons.REMOVE}
kind={kinds.DANGER} kind={kinds.DANGER}
onPress={onRemovePress} onPress={onRemovePress}

View File

@ -8,6 +8,7 @@ import Link from 'Components/Link/Link';
import formatDateTime from 'Utilities/Date/formatDateTime'; import formatDateTime from 'Utilities/Date/formatDateTime';
import formatAge from 'Utilities/Number/formatAge'; import formatAge from 'Utilities/Number/formatAge';
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore'; import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
import translate from 'Utilities/String/translate';
import styles from './HistoryDetails.css'; import styles from './HistoryDetails.css';
function HistoryDetails(props) { function HistoryDetails(props) {
@ -41,14 +42,14 @@ function HistoryDetails(props) {
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Name" title={translate('Name')}
data={sourceTitle} data={sourceTitle}
/> />
{ {
indexer ? indexer ?
<DescriptionListItem <DescriptionListItem
title="Indexer" title={translate('Indexer')}
data={indexer} data={indexer}
/> : /> :
null null
@ -58,7 +59,7 @@ function HistoryDetails(props) {
releaseGroup ? releaseGroup ?
<DescriptionListItem <DescriptionListItem
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Release Group" title={translate('ReleaseGroup')}
data={releaseGroup} data={releaseGroup}
/> : /> :
null null
@ -67,7 +68,7 @@ function HistoryDetails(props) {
{ {
customFormatScore && customFormatScore !== '0' ? customFormatScore && customFormatScore !== '0' ?
<DescriptionListItem <DescriptionListItem
title="Custom Format Score" title={translate('CustomFormatScore')}
data={formatCustomFormatScore(customFormatScore)} data={formatCustomFormatScore(customFormatScore)}
/> : /> :
null null
@ -77,7 +78,7 @@ function HistoryDetails(props) {
seriesMatchType ? seriesMatchType ?
<DescriptionListItem <DescriptionListItem
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Series Match Type" title={translate('SeriesMatchType')}
data={seriesMatchType} data={seriesMatchType}
/> : /> :
null null
@ -87,7 +88,7 @@ function HistoryDetails(props) {
nzbInfoUrl ? nzbInfoUrl ?
<span> <span>
<DescriptionListItemTitle> <DescriptionListItemTitle>
Info URL {translate('InfoUrl')}
</DescriptionListItemTitle> </DescriptionListItemTitle>
<DescriptionListItemDescription> <DescriptionListItemDescription>
@ -100,7 +101,7 @@ function HistoryDetails(props) {
{ {
downloadClientNameInfo ? downloadClientNameInfo ?
<DescriptionListItem <DescriptionListItem
title="Download Client" title={translate('DownloadClient')}
data={downloadClientNameInfo} data={downloadClientNameInfo}
/> : /> :
null null
@ -109,7 +110,7 @@ function HistoryDetails(props) {
{ {
downloadId ? downloadId ?
<DescriptionListItem <DescriptionListItem
title="Grab ID" title={translate('GrabId')}
data={downloadId} data={downloadId}
/> : /> :
null null
@ -118,7 +119,7 @@ function HistoryDetails(props) {
{ {
age || ageHours || ageMinutes ? age || ageHours || ageMinutes ?
<DescriptionListItem <DescriptionListItem
title="Age (when grabbed)" title={translate('AgeWhenGrabbed')}
data={formatAge(age, ageHours, ageMinutes)} data={formatAge(age, ageHours, ageMinutes)}
/> : /> :
null null
@ -127,7 +128,7 @@ function HistoryDetails(props) {
{ {
publishedDate ? publishedDate ?
<DescriptionListItem <DescriptionListItem
title="Published Date" title={translate('PublishedDate')}
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })} data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
/> : /> :
null null
@ -145,14 +146,14 @@ function HistoryDetails(props) {
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Name" title={translate('Name')}
data={sourceTitle} data={sourceTitle}
/> />
{ {
message ? message ?
<DescriptionListItem <DescriptionListItem
title="Message" title={translate('Message')}
data={message} data={message}
/> : /> :
null null
@ -172,7 +173,7 @@ function HistoryDetails(props) {
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Name" title={translate('Name')}
data={sourceTitle} data={sourceTitle}
/> />
@ -180,7 +181,7 @@ function HistoryDetails(props) {
droppedPath ? droppedPath ?
<DescriptionListItem <DescriptionListItem
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Source" title={translate('Source')}
data={droppedPath} data={droppedPath}
/> : /> :
null null
@ -190,7 +191,7 @@ function HistoryDetails(props) {
importedPath ? importedPath ?
<DescriptionListItem <DescriptionListItem
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Imported To" title={translate('ImportedTo')}
data={importedPath} data={importedPath}
/> : /> :
null null
@ -199,7 +200,7 @@ function HistoryDetails(props) {
{ {
customFormatScore && customFormatScore !== '0' ? customFormatScore && customFormatScore !== '0' ?
<DescriptionListItem <DescriptionListItem
title="Custom Format Score" title={translate('CustomFormatScore')}
data={formatCustomFormatScore(customFormatScore)} data={formatCustomFormatScore(customFormatScore)}
/> : /> :
null null
@ -218,13 +219,13 @@ function HistoryDetails(props) {
switch (reason) { switch (reason) {
case 'Manual': case 'Manual':
reasonMessage = 'File was deleted by via UI'; reasonMessage = translate('DeletedReasonManual');
break; break;
case 'MissingFromDisk': case 'MissingFromDisk':
reasonMessage = 'Sonarr was unable to find the file on disk so the file was unlinked from the episode in the database'; reasonMessage = translate('DeletedReasonMissingFromDisk');
break; break;
case 'Upgrade': case 'Upgrade':
reasonMessage = 'File was deleted to import an upgrade'; reasonMessage = translate('DeletedReasonUpgrade');
break; break;
default: default:
reasonMessage = ''; reasonMessage = '';
@ -233,19 +234,19 @@ function HistoryDetails(props) {
return ( return (
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
title="Name" title={translate('Name')}
data={sourceTitle} data={sourceTitle}
/> />
<DescriptionListItem <DescriptionListItem
title="Reason" title={translate('Reason')}
data={reasonMessage} data={reasonMessage}
/> />
{ {
customFormatScore && customFormatScore !== '0' ? customFormatScore && customFormatScore !== '0' ?
<DescriptionListItem <DescriptionListItem
title="Custom Format Score" title={translate('CustomFormatScore')}
data={formatCustomFormatScore(customFormatScore)} data={formatCustomFormatScore(customFormatScore)}
/> : /> :
null null
@ -265,22 +266,22 @@ function HistoryDetails(props) {
return ( return (
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
title="Source Path" title={translate('SourcePath')}
data={sourcePath} data={sourcePath}
/> />
<DescriptionListItem <DescriptionListItem
title="Source Relative Path" title={translate('SourceRelativePath')}
data={sourceRelativePath} data={sourceRelativePath}
/> />
<DescriptionListItem <DescriptionListItem
title="Destination Path" title={translate('DestinationPath')}
data={path} data={path}
/> />
<DescriptionListItem <DescriptionListItem
title="Destination Relative Path" title={translate('DestinationRelativePath')}
data={relativePath} data={relativePath}
/> />
</DescriptionList> </DescriptionList>
@ -296,14 +297,14 @@ function HistoryDetails(props) {
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Name" title={translate('Name')}
data={sourceTitle} data={sourceTitle}
/> />
{ {
message ? message ?
<DescriptionListItem <DescriptionListItem
title="Message" title={translate('Message')}
data={message} data={message}
/> : /> :
null null
@ -316,7 +317,7 @@ function HistoryDetails(props) {
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Name" title={translate('Name')}
data={sourceTitle} data={sourceTitle}
/> />
</DescriptionList> </DescriptionList>

View File

@ -8,25 +8,26 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter'; import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader'; import ModalHeader from 'Components/Modal/ModalHeader';
import { kinds } from 'Helpers/Props'; import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import HistoryDetails from './HistoryDetails'; import HistoryDetails from './HistoryDetails';
import styles from './HistoryDetailsModal.css'; import styles from './HistoryDetailsModal.css';
function getHeaderTitle(eventType) { function getHeaderTitle(eventType) {
switch (eventType) { switch (eventType) {
case 'grabbed': case 'grabbed':
return 'Grabbed'; return translate('Grabbed');
case 'downloadFailed': case 'downloadFailed':
return 'Download Failed'; return translate('DownloadFailed');
case 'downloadFolderImported': case 'downloadFolderImported':
return 'Episode Imported'; return translate('EpisodeImported');
case 'episodeFileDeleted': case 'episodeFileDeleted':
return 'Episode File Deleted'; return translate('EpisodeFileDeleted');
case 'episodeFileRenamed': case 'episodeFileRenamed':
return 'Episode File Renamed'; return translate('EpisodeFileRenamed');
case 'downloadIgnored': case 'downloadIgnored':
return 'Download Ignored'; return translate('DownloadIgnored');
default: default:
return 'Unknown'; return translate('Unknown');
} }
} }
@ -72,14 +73,14 @@ function HistoryDetailsModal(props) {
isSpinning={isMarkingAsFailed} isSpinning={isMarkingAsFailed}
onPress={onMarkAsFailedPress} onPress={onMarkAsFailedPress}
> >
Mark as Failed {translate('MarkAsFailed')}
</SpinnerButton> </SpinnerButton>
} }
<Button <Button
onPress={onModalClose} onPress={onModalClose}
> >
Close {translate('Close')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>

View File

@ -14,6 +14,7 @@ import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptions
import TablePager from 'Components/Table/TablePager'; import TablePager from 'Components/Table/TablePager';
import { align, icons, kinds } from 'Helpers/Props'; import { align, icons, kinds } from 'Helpers/Props';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems'; import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import translate from 'Utilities/String/translate';
import HistoryRowConnector from './HistoryRowConnector'; import HistoryRowConnector from './HistoryRowConnector';
class History extends Component { class History extends Component {
@ -65,11 +66,11 @@ class History extends Component {
const hasError = error || episodesError; const hasError = error || episodesError;
return ( return (
<PageContent title="History"> <PageContent title={translate('History')}>
<PageToolbar> <PageToolbar>
<PageToolbarSection> <PageToolbarSection>
<PageToolbarButton <PageToolbarButton
label="Refresh" label={translate('Refresh')}
iconName={icons.REFRESH} iconName={icons.REFRESH}
isSpinning={isFetching} isSpinning={isFetching}
onPress={onFirstPagePress} onPress={onFirstPagePress}
@ -82,7 +83,7 @@ class History extends Component {
columns={columns} columns={columns}
> >
<PageToolbarButton <PageToolbarButton
label="Options" label={translate('Options')}
iconName={icons.TABLE} iconName={icons.TABLE}
/> />
</TableOptionsModalWrapper> </TableOptionsModalWrapper>
@ -105,7 +106,9 @@ class History extends Component {
{ {
!isFetchingAny && hasError && !isFetchingAny && hasError &&
<Alert kind={kinds.DANGER}>Unable to load history</Alert> <Alert kind={kinds.DANGER}>
{translate('HistoryLoadError')}
</Alert>
} }
{ {
@ -114,7 +117,7 @@ class History extends Component {
isPopulated && !hasError && !items.length && isPopulated && !hasError && !items.length &&
<Alert kind={kinds.INFO}> <Alert kind={kinds.INFO}>
No history found {translate('NoHistoryFound')}
</Alert> </Alert>
} }

View File

@ -3,6 +3,7 @@ import React from 'react';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableRowCell from 'Components/Table/Cells/TableRowCell';
import { icons, kinds } from 'Helpers/Props'; import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './HistoryEventTypeCell.css'; import styles from './HistoryEventTypeCell.css';
function getIconName(eventType) { function getIconName(eventType) {
@ -38,21 +39,21 @@ function getIconKind(eventType) {
function getTooltip(eventType, data) { function getTooltip(eventType, data) {
switch (eventType) { switch (eventType) {
case 'grabbed': case 'grabbed':
return `Episode grabbed from ${data.indexer} and sent to ${data.downloadClient}`; return translate('GrabbedHistoryTooltip', { indexer: data.indexer, downloadClient: data.downloadClient });
case 'seriesFolderImported': case 'seriesFolderImported':
return 'Episode imported from series folder'; return translate('SeriesFolderImportedTooltip');
case 'downloadFolderImported': case 'downloadFolderImported':
return 'Episode downloaded successfully and picked up from download client'; return translate('EpisodeImportedTooltip');
case 'downloadFailed': case 'downloadFailed':
return 'Episode download failed'; return translate('DownloadFailedTooltip');
case 'episodeFileDeleted': case 'episodeFileDeleted':
return 'Episode file deleted'; return translate('EpisodeFileDeletedTooltip');
case 'episodeFileRenamed': case 'episodeFileRenamed':
return 'Episode file renamed'; return translate('EpisodeFileRenamedTooltip');
case 'downloadIgnored': case 'downloadIgnored':
return 'Episode Download Ignored'; return translate('DownloadIgnoredTooltip');
default: default:
return 'Unknown event'; return translate('UnknownEventTooltip');
} }
} }

View File

@ -16,6 +16,7 @@ import TablePager from 'Components/Table/TablePager';
import { align, icons, kinds } from 'Helpers/Props'; import { align, icons, kinds } from 'Helpers/Props';
import getRemovedItems from 'Utilities/Object/getRemovedItems'; import getRemovedItems from 'Utilities/Object/getRemovedItems';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems'; import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds'; import getSelectedIds from 'Utilities/Table/getSelectedIds';
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState'; import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
import selectAll from 'Utilities/Table/selectAll'; import selectAll from 'Utilities/Table/selectAll';
@ -175,7 +176,7 @@ class Queue extends Component {
const disableSelectedActions = selectedCount === 0; const disableSelectedActions = selectedCount === 0;
return ( return (
<PageContent title="Queue"> <PageContent title={translate('Queue')}>
<PageToolbar> <PageToolbar>
<PageToolbarSection> <PageToolbarSection>
<PageToolbarButton <PageToolbarButton
@ -188,7 +189,7 @@ class Queue extends Component {
<PageToolbarSeparator /> <PageToolbarSeparator />
<PageToolbarButton <PageToolbarButton
label="Grab Selected" label={translate('GrabSelected')}
iconName={icons.DOWNLOAD} iconName={icons.DOWNLOAD}
isDisabled={disableSelectedActions || !isPendingSelected} isDisabled={disableSelectedActions || !isPendingSelected}
isSpinning={isGrabbing} isSpinning={isGrabbing}
@ -196,7 +197,7 @@ class Queue extends Component {
/> />
<PageToolbarButton <PageToolbarButton
label="Remove Selected" label={translate('RemoveSelected')}
iconName={icons.REMOVE} iconName={icons.REMOVE}
isDisabled={disableSelectedActions} isDisabled={disableSelectedActions}
isSpinning={isRemoving} isSpinning={isRemoving}
@ -213,7 +214,7 @@ class Queue extends Component {
optionsComponent={QueueOptionsConnector} optionsComponent={QueueOptionsConnector}
> >
<PageToolbarButton <PageToolbarButton
label="Options" label={translate('Options')}
iconName={icons.TABLE} iconName={icons.TABLE}
/> />
</TableOptionsModalWrapper> </TableOptionsModalWrapper>
@ -230,7 +231,7 @@ class Queue extends Component {
{ {
!isRefreshing && hasError ? !isRefreshing && hasError ?
<Alert kind={kinds.DANGER}> <Alert kind={kinds.DANGER}>
Failed to load Queue {translate('QueueLoadError')}
</Alert> : </Alert> :
null null
} }
@ -238,7 +239,7 @@ class Queue extends Component {
{ {
isAllPopulated && !hasError && !items.length ? isAllPopulated && !hasError && !items.length ?
<Alert kind={kinds.INFO}> <Alert kind={kinds.INFO}>
Queue is empty {translate('QueueIsEmpty')}
</Alert> : </Alert> :
null null
} }

View File

@ -3,6 +3,7 @@ import React from 'react';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import Popover from 'Components/Tooltip/Popover'; import Popover from 'Components/Tooltip/Popover';
import { icons, tooltipPositions } from 'Helpers/Props'; import { icons, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import QueueStatus from './QueueStatus'; import QueueStatus from './QueueStatus';
import styles from './QueueDetails.css'; import styles from './QueueDetails.css';
@ -30,7 +31,7 @@ function QueueDetails(props) {
!hasWarning && !hasWarning &&
!hasError !hasError
) { ) {
const state = isPaused ? 'Paused' : 'Downloading'; const state = isPaused ? translate('Paused') : translate('Downloading');
if (progress < 5) { if (progress < 5) {
return ( return (

View File

@ -4,6 +4,7 @@ import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup'; import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel'; import FormLabel from 'Components/Form/FormLabel';
import { inputTypes } from 'Helpers/Props'; import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
class QueueOptions extends Component { class QueueOptions extends Component {
@ -54,13 +55,13 @@ class QueueOptions extends Component {
return ( return (
<Fragment> <Fragment>
<FormGroup> <FormGroup>
<FormLabel>Show Unknown Series Items</FormLabel> <FormLabel>{translate('ShowUnknownSeriesItems')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="includeUnknownSeriesItems" name="includeUnknownSeriesItems"
value={includeUnknownSeriesItems} value={includeUnknownSeriesItems}
helpText="Show items without a series in the queue, this could include removed series, movies or anything else in Sonarr's category" helpText={translate('ShownUnknownSeriesItemsHelpText')}
onChange={this.onOptionChange} onChange={this.onOptionChange}
/> />
</FormGroup> </FormGroup>

View File

@ -19,6 +19,7 @@ import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
import SeriesTitleLink from 'Series/SeriesTitleLink'; import SeriesTitleLink from 'Series/SeriesTitleLink';
import formatBytes from 'Utilities/Number/formatBytes'; import formatBytes from 'Utilities/Number/formatBytes';
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore'; import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
import translate from 'Utilities/String/translate';
import QueueStatusCell from './QueueStatusCell'; import QueueStatusCell from './QueueStatusCell';
import RemoveQueueItemModal from './RemoveQueueItemModal'; import RemoveQueueItemModal from './RemoveQueueItemModal';
import TimeleftCell from './TimeleftCell'; import TimeleftCell from './TimeleftCell';
@ -386,7 +387,7 @@ class QueueRow extends Component {
} }
<SpinnerIconButton <SpinnerIconButton
title="Remove from queue" title={translate('RemoveFromQueue')}
name={icons.REMOVE} name={icons.REMOVE}
isSpinning={isRemoving} isSpinning={isRemoving}
onPress={this.onRemoveQueueItemPress} onPress={this.onRemoveQueueItemPress}

View File

@ -3,6 +3,7 @@ import React from 'react';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import Popover from 'Components/Tooltip/Popover'; import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, tooltipPositions } from 'Helpers/Props'; import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './QueueStatus.css'; import styles from './QueueStatus.css';
function getDetailedPopoverBody(statusMessages) { function getDetailedPopoverBody(statusMessages) {
@ -53,34 +54,34 @@ function QueueStatus(props) {
// status === 'downloading' // status === 'downloading'
let iconName = icons.DOWNLOADING; let iconName = icons.DOWNLOADING;
let iconKind = kinds.DEFAULT; let iconKind = kinds.DEFAULT;
let title = 'Downloading'; let title = translate('Downloading');
if (status === 'paused') { if (status === 'paused') {
iconName = icons.PAUSED; iconName = icons.PAUSED;
title = 'Paused'; title = translate('Paused');
} }
if (status === 'queued') { if (status === 'queued') {
iconName = icons.QUEUED; iconName = icons.QUEUED;
title = 'Queued'; title = translate('Queued');
} }
if (status === 'completed') { if (status === 'completed') {
iconName = icons.DOWNLOADED; iconName = icons.DOWNLOADED;
title = 'Downloaded'; title = translate('Downloaded');
if (trackedDownloadState === 'importPending') { if (trackedDownloadState === 'importPending') {
title += ' - Waiting to Import'; title += ` - ${translate('WaitingToImport')}`;
iconKind = kinds.PURPLE; iconKind = kinds.PURPLE;
} }
if (trackedDownloadState === 'importing') { if (trackedDownloadState === 'importing') {
title += ' - Importing'; title += ` - ${translate('Importing')}`;
iconKind = kinds.PURPLE; iconKind = kinds.PURPLE;
} }
if (trackedDownloadState === 'failedPending') { if (trackedDownloadState === 'failedPending') {
title += ' - Waiting to Process'; title += ` - ${translate('WaitingToProcess')}`;
iconKind = kinds.DANGER; iconKind = kinds.DANGER;
} }
} }
@ -91,36 +92,37 @@ function QueueStatus(props) {
if (status === 'delay') { if (status === 'delay') {
iconName = icons.PENDING; iconName = icons.PENDING;
title = 'Pending'; title = translate('Pending');
} }
if (status === 'downloadClientUnavailable') { if (status === 'downloadClientUnavailable') {
iconName = icons.PENDING; iconName = icons.PENDING;
iconKind = kinds.WARNING; iconKind = kinds.WARNING;
title = 'Pending - Download client is unavailable'; title = translate('PendingDownloadClientUnavailable');
} }
if (status === 'failed') { if (status === 'failed') {
iconName = icons.DOWNLOADING; iconName = icons.DOWNLOADING;
iconKind = kinds.DANGER; iconKind = kinds.DANGER;
title = 'Download failed'; title = translate('DownloadFailed');
} }
if (status === 'warning') { if (status === 'warning') {
iconName = icons.DOWNLOADING; iconName = icons.DOWNLOADING;
iconKind = kinds.WARNING; iconKind = kinds.WARNING;
title = `Download warning: ${errorMessage || 'check download client for more details'}`; const warningMessage = errorMessage || translate('CheckDownloadClientForDetails');
title = translate('DownloadWarning', { warningMessage });
} }
if (hasError) { if (hasError) {
if (status === 'completed') { if (status === 'completed') {
iconName = icons.DOWNLOAD; iconName = icons.DOWNLOAD;
iconKind = kinds.DANGER; iconKind = kinds.DANGER;
title = `Import failed: ${sourceTitle}`; title = translate('ImportFailed', { sourceTitle });
} else { } else {
iconName = icons.DOWNLOADING; iconName = icons.DOWNLOADING;
iconKind = kinds.DANGER; iconKind = kinds.DANGER;
title = 'Download failed'; title = translate('DownloadFailed');
} }
} }
@ -152,8 +154,8 @@ QueueStatus.propTypes = {
}; };
QueueStatus.defaultProps = { QueueStatus.defaultProps = {
trackedDownloadStatus: 'Ok', trackedDownloadStatus: translate('Ok'),
trackedDownloadState: 'Downloading', trackedDownloadState: translate('Downloading'),
canFlip: false canFlip: false
}; };

View File

@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableRowCell from 'Components/Table/Cells/TableRowCell';
import { tooltipPositions } from 'Helpers/Props'; import { tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import QueueStatus from './QueueStatus'; import QueueStatus from './QueueStatus';
import styles from './QueueStatusCell.css'; import styles from './QueueStatusCell.css';
@ -40,8 +41,8 @@ QueueStatusCell.propTypes = {
}; };
QueueStatusCell.defaultProps = { QueueStatusCell.defaultProps = {
trackedDownloadStatus: 'Ok', trackedDownloadStatus: translate('Ok'),
trackedDownloadState: 'Downloading' trackedDownloadState: translate('Downloading')
}; };
export default QueueStatusCell; export default QueueStatusCell;

View File

@ -88,25 +88,25 @@ class RemoveQueueItemModal extends Component {
onModalClose={this.onModalClose} onModalClose={this.onModalClose}
> >
<ModalHeader> <ModalHeader>
Remove - {sourceTitle} {translate('RemoveQueueItem', { sourceTitle })}
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>
<div> <div>
Are you sure you want to remove '{sourceTitle}' from the queue? {translate('RemoveQueueItemConfirmation', { sourceTitle })}
</div> </div>
{ {
isPending ? isPending ?
null : null :
<FormGroup> <FormGroup>
<FormLabel>Remove From Download Client</FormLabel> <FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="remove" name="remove"
value={remove} value={remove}
helpTextWarning="Removing will remove the download and the file(s) from the download client." helpTextWarning={translate('RemoveHelpTextWarning')}
isDisabled={!canIgnore} isDisabled={!canIgnore}
onChange={this.onRemoveChange} onChange={this.onRemoveChange}
/> />
@ -114,13 +114,13 @@ class RemoveQueueItemModal extends Component {
} }
<FormGroup> <FormGroup>
<FormLabel>Add Release To Blocklist</FormLabel> <FormLabel>{translate('BlocklistRelease')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="blocklist" name="blocklist"
value={blocklist} value={blocklist}
helpText="Starts a search for this episode again and prevents this release from being grabbed again" helpText={translate('BlocklistReleaseHelpText')}
onChange={this.onBlocklistChange} onChange={this.onBlocklistChange}
/> />
</FormGroup> </FormGroup>
@ -143,14 +143,14 @@ class RemoveQueueItemModal extends Component {
<ModalFooter> <ModalFooter>
<Button onPress={this.onModalClose}> <Button onPress={this.onModalClose}>
Close {translate('Close')}
</Button> </Button>
<Button <Button
kind={kinds.DANGER} kind={kinds.DANGER}
onPress={this.onRemoveConfirmed} onPress={this.onRemoveConfirmed}
> >
Remove {translate('Remove')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>

View File

@ -94,7 +94,7 @@ class RemoveQueueItemsModal extends Component {
<ModalBody> <ModalBody>
<div className={styles.message}> <div className={styles.message}>
{selectedCount > 1 ? translate('RemoveSelectedItemsQueueMessageText', selectedCount) : translate('RemoveSelectedItemQueueMessageText')} {selectedCount > 1 ? translate('RemoveSelectedItemsQueueMessageText', { selectedCount }) : translate('RemoveSelectedItemQueueMessageText')}
</div> </div>
{ {

View File

@ -5,6 +5,7 @@ import formatTime from 'Utilities/Date/formatTime';
import formatTimeSpan from 'Utilities/Date/formatTimeSpan'; import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
import getRelativeDate from 'Utilities/Date/getRelativeDate'; import getRelativeDate from 'Utilities/Date/getRelativeDate';
import formatBytes from 'Utilities/Number/formatBytes'; import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
import styles from './TimeleftCell.css'; import styles from './TimeleftCell.css';
function TimeleftCell(props) { function TimeleftCell(props) {
@ -26,7 +27,7 @@ function TimeleftCell(props) {
return ( return (
<TableRowCell <TableRowCell
className={styles.timeleft} className={styles.timeleft}
title={`Delaying download until ${date} at ${time}`} title={translate('DelayingDownloadUntil', { date, time })}
> >
- -
</TableRowCell> </TableRowCell>
@ -40,7 +41,7 @@ function TimeleftCell(props) {
return ( return (
<TableRowCell <TableRowCell
className={styles.timeleft} className={styles.timeleft}
title={`Retrying download ${date} at ${time}`} title={translate('RetryingDownload', { date, time })}
> >
- -
</TableRowCell> </TableRowCell>

View File

@ -45,6 +45,7 @@
"AddingTag": "Adding tag", "AddingTag": "Adding tag",
"AfterManualRefresh": "After Manual Refresh", "AfterManualRefresh": "After Manual Refresh",
"Age": "Age", "Age": "Age",
"AgeWhenGrabbed": "Age (when grabbed)",
"AirDate": "Air Date", "AirDate": "Air Date",
"All": "All", "All": "All",
"AllResultsAreHiddenByTheAppliedFilter": "All results are hidden by the applied filter", "AllResultsAreHiddenByTheAppliedFilter": "All results are hidden by the applied filter",
@ -107,7 +108,7 @@
"Blocklist": "Blocklist", "Blocklist": "Blocklist",
"BlocklistLoadError": "Unable to load blocklist", "BlocklistLoadError": "Unable to load blocklist",
"BlocklistRelease": "Blocklist Release", "BlocklistRelease": "Blocklist Release",
"BlocklistReleaseHelpText": "Prevents Sonarr from automatically grabbing this release again", "BlocklistReleaseHelpText": "Starts a search for this episode again and prevents this release from being grabbed again",
"BlocklistReleases": "Blocklist Releases", "BlocklistReleases": "Blocklist Releases",
"Branch": "Branch", "Branch": "Branch",
"BranchUpdate": "Branch to use to update Sonarr", "BranchUpdate": "Branch to use to update Sonarr",
@ -131,6 +132,7 @@
"Certification": "Certification", "Certification": "Certification",
"ChangeFileDate": "Change File Date", "ChangeFileDate": "Change File Date",
"ChangeFileDateHelpText": "Change file date on import/rescan", "ChangeFileDateHelpText": "Change file date on import/rescan",
"CheckDownloadClientForDetails": "check download client for more details",
"ChmodFolder": "chmod Folder", "ChmodFolder": "chmod Folder",
"ChmodFolderHelpText": "Octal, applied during import/rename to media folders and files (without execute bits)", "ChmodFolderHelpText": "Octal, applied during import/rename to media folders and files (without execute bits)",
"ChmodFolderHelpTextWarning": "This only works if the user running sonarr is the owner of the file. It's better to ensure the download client sets the permissions properly.", "ChmodFolderHelpTextWarning": "This only works if the user running sonarr is the owner of the file. It's better to ensure the download client sets the permissions properly.",
@ -199,6 +201,7 @@
"DelayProfileTagsHelpText": "Applies to series with at least one matching tag", "DelayProfileTagsHelpText": "Applies to series with at least one matching tag",
"DelayProfiles": "Delay Profiles", "DelayProfiles": "Delay Profiles",
"DelayProfilesLoadError": "Unable to load Delay Profiles", "DelayProfilesLoadError": "Unable to load Delay Profiles",
"DelayingDownloadUntil": "Delaying download until {date} at {time}",
"Delete": "Delete", "Delete": "Delete",
"DeleteAutoTag": "Delete Auto Tag", "DeleteAutoTag": "Delete Auto Tag",
"DeleteAutoTagHelpText": "Are you sure you want to delete the auto tag '{name}'?", "DeleteAutoTagHelpText": "Are you sure you want to delete the auto tag '{name}'?",
@ -241,6 +244,11 @@
"DeleteTag": "DeleteTag", "DeleteTag": "DeleteTag",
"DeleteTagMessageText": "Are you sure you want to delete the tag '{label}'?", "DeleteTagMessageText": "Are you sure you want to delete the tag '{label}'?",
"Deleted": "Deleted", "Deleted": "Deleted",
"DeletedReasonManual": "File was deleted by via UI",
"DeletedReasonMissingFromDisk": "Sonarr was unable to find the file on disk so the file was unlinked from the episode in the database",
"DeletedReasonUpgrade": "File was deleted to import an upgrade",
"DestinationPath": "Destination Path",
"DestinationRelativePath": "Destination Relative Path",
"Details": "Details", "Details": "Details",
"Disabled": "Disabled", "Disabled": "Disabled",
"DisabledForLocalAddresses": "Disabled for Local Addresses", "DisabledForLocalAddresses": "Disabled for Local Addresses",
@ -267,10 +275,17 @@
"DownloadClients": "Download Clients", "DownloadClients": "Download Clients",
"DownloadClientsLoadError": "Unable to load download clients", "DownloadClientsLoadError": "Unable to load download clients",
"DownloadClientsSettingsSummary": "Download clients, download handling and remote path mappings", "DownloadClientsSettingsSummary": "Download clients, download handling and remote path mappings",
"DownloadFailed": "Download Failed",
"DownloadFailedTooltip": "Episode download failed",
"DownloadIgnored": "Download Ignored",
"DownloadIgnoredTooltip": "Episode Download Ignored",
"DownloadPropersAndRepacks": "Propers and Repacks", "DownloadPropersAndRepacks": "Propers and Repacks",
"DownloadPropersAndRepacksHelpText": "Whether or not to automatically upgrade to Propers/Repacks", "DownloadPropersAndRepacksHelpText": "Whether or not to automatically upgrade to Propers/Repacks",
"DownloadPropersAndRepacksHelpTextCustomFormat": "Use 'Do not Prefer' to sort by custom format score over Propers/Repacks", "DownloadPropersAndRepacksHelpTextCustomFormat": "Use 'Do not Prefer' to sort by custom format score over Propers/Repacks",
"DownloadPropersAndRepacksHelpTextWarning": "Use custom formats for automatic upgrades to Propers/Repacks", "DownloadPropersAndRepacksHelpTextWarning": "Use custom formats for automatic upgrades to Propers/Repacks",
"DownloadWarning": "Download warning: {warningMessage}",
"Downloaded": "Downloaded",
"Downloading": "Downloading",
"Duplicate": "Duplicate", "Duplicate": "Duplicate",
"Duration": "Duration", "Duration": "Duration",
"Edit": "Edit", "Edit": "Edit",
@ -320,6 +335,12 @@
"Episode": "Episode", "Episode": "Episode",
"EpisodeAirDate": "Episode Air Date", "EpisodeAirDate": "Episode Air Date",
"EpisodeCount": "Episode Count", "EpisodeCount": "Episode Count",
"EpisodeFileDeleted": "Episode File Deleted",
"EpisodeFileDeletedTooltip": "Episode file deleted",
"EpisodeFileRenamed": "Episode File Renamed",
"EpisodeFileRenamedTooltip": "Episode file renamed",
"EpisodeImported": "Episode Imported",
"EpisodeImportedTooltip": "Episode downloaded successfully and picked up from download client",
"EpisodeInfo": "Episode Info", "EpisodeInfo": "Episode Info",
"EpisodeNaming": "Episode Naming", "EpisodeNaming": "Episode Naming",
"EpisodeNumbers": "Episode Number(s)", "EpisodeNumbers": "Episode Number(s)",
@ -365,7 +386,10 @@
"GeneralSettingsLoadError": "Unable to load General settings", "GeneralSettingsLoadError": "Unable to load General settings",
"GeneralSettingsSummary": "Port, SSL, username/password, proxy, analytics and updates", "GeneralSettingsSummary": "Port, SSL, username/password, proxy, analytics and updates",
"Genres": "Genres", "Genres": "Genres",
"GrabId": "Grab ID",
"GrabSelected": "Grab Selected",
"Grabbed": "Grabbed", "Grabbed": "Grabbed",
"GrabbedHistoryTooltip": "Episode grabbed from {indexer} and sent to {downloadClient}",
"Group": "Group", "Group": "Group",
"HasMissingSeason": "Has Missing Season", "HasMissingSeason": "Has Missing Season",
"Health": "Health", "Health": "Health",
@ -392,6 +416,7 @@
"ImportExistingSeries": "Import Existing Series", "ImportExistingSeries": "Import Existing Series",
"ImportExtraFiles": "Import Extra Files", "ImportExtraFiles": "Import Extra Files",
"ImportExtraFilesHelpText": "Import matching extra files (subtitles, nfo, etc) after importing an episode file", "ImportExtraFilesHelpText": "Import matching extra files (subtitles, nfo, etc) after importing an episode file",
"ImportFailed": "Import Failed: {sourceTitle}",
"ImportList": "Import List", "ImportList": "Import List",
"ImportListExclusions": "Import List Exclusions", "ImportListExclusions": "Import List Exclusions",
"ImportListExclusionsLoadError": "Unable to load Import List Exclusions", "ImportListExclusionsLoadError": "Unable to load Import List Exclusions",
@ -412,6 +437,7 @@
"ImportUsingScript": "Import Using Script", "ImportUsingScript": "Import Using Script",
"ImportUsingScriptHelpText": "Copy files for importing using a script (ex. for transcoding)", "ImportUsingScriptHelpText": "Copy files for importing using a script (ex. for transcoding)",
"Imported": "Imported", "Imported": "Imported",
"ImportedTo": "Imported To",
"Importing": "Importing", "Importing": "Importing",
"IncludeCustomFormatWhenRenaming": "Include Custom Format when Renaming", "IncludeCustomFormatWhenRenaming": "Include Custom Format when Renaming",
"IncludeCustomFormatWhenRenamingHelpText": "Include in {Custom Formats} renaming format", "IncludeCustomFormatWhenRenamingHelpText": "Include in {Custom Formats} renaming format",
@ -439,6 +465,7 @@
"IndexersLoadError": "Unable to load Indexers", "IndexersLoadError": "Unable to load Indexers",
"IndexersSettingsSummary": "Indexers and indexer options", "IndexersSettingsSummary": "Indexers and indexer options",
"Info": "Info", "Info": "Info",
"InfoUrl": "Info URL",
"InstallLatest": "Install Latest", "InstallLatest": "Install Latest",
"InstanceName": "Instance Name", "InstanceName": "Instance Name",
"InstanceNameHelpText": "Instance name in tab and for Syslog app name", "InstanceNameHelpText": "Instance name in tab and for Syslog app name",
@ -487,6 +514,7 @@
"ManageLists": "Manage Lists", "ManageLists": "Manage Lists",
"Manual": "Manual", "Manual": "Manual",
"ManualImportItemsLoadError": "Unable to load manual import items", "ManualImportItemsLoadError": "Unable to load manual import items",
"MarkAsFailed": "Mark as Failed",
"MatchedToEpisodes": "Matched to Episodes", "MatchedToEpisodes": "Matched to Episodes",
"MatchedToSeason": "Matched to Season", "MatchedToSeason": "Matched to Season",
"MatchedToSeries": "Matched to Series", "MatchedToSeries": "Matched to Series",
@ -577,6 +605,8 @@
"NoDelay": "No Delay", "NoDelay": "No Delay",
"NoDownloadClientsFound": "No download clients found", "NoDownloadClientsFound": "No download clients found",
"NoEventsFound": "No events found", "NoEventsFound": "No events found",
"NoHistoryBlocklist": "No history blocklist",
"NoHistoryFound": "No history found",
"NoImportListsFound": "No import lists found", "NoImportListsFound": "No import lists found",
"NoIndexersFound": "No indexers found", "NoIndexersFound": "No indexers found",
"NoIssuesWithYourConfiguration": "No issues with your configuration", "NoIssuesWithYourConfiguration": "No issues with your configuration",
@ -597,6 +627,7 @@
"NotificationTriggersHelpText": "Select which events should trigger this notification", "NotificationTriggersHelpText": "Select which events should trigger this notification",
"NotificationsLoadError": "Unable to load Notifications", "NotificationsLoadError": "Unable to load Notifications",
"NotificationsTagsHelpText": "Only send notifications for series with at least one matching tag", "NotificationsTagsHelpText": "Only send notifications for series with at least one matching tag",
"Ok": "Ok",
"OnApplicationUpdate": "On Application Update", "OnApplicationUpdate": "On Application Update",
"OnEpisodeFileDelete": "On Episode File Delete", "OnEpisodeFileDelete": "On Episode File Delete",
"OnEpisodeFileDeleteForUpgrade": "On Episode File Delete For Upgrade", "OnEpisodeFileDeleteForUpgrade": "On Episode File Delete For Upgrade",
@ -628,10 +659,13 @@
"PartialSeason": "Partial Season", "PartialSeason": "Partial Season",
"Password": "Password", "Password": "Password",
"Path": "Path", "Path": "Path",
"Paused": "Paused",
"Peers": "Peers", "Peers": "Peers",
"Pending": "Pending",
"PendingChangesDiscardChanges": "Discard changes and leave", "PendingChangesDiscardChanges": "Discard changes and leave",
"PendingChangesMessage": "You have unsaved changes, are you sure you want to leave this page?", "PendingChangesMessage": "You have unsaved changes, are you sure you want to leave this page?",
"PendingChangesStayReview": "Stay and review changes", "PendingChangesStayReview": "Stay and review changes",
"PendingDownloadClientUnavailable": "Pending - Download client is unavailable",
"Period": "Period", "Period": "Period",
"Permissions": "Permissions", "Permissions": "Permissions",
"PortNumber": "Port Number", "PortNumber": "Port Number",
@ -664,6 +698,7 @@
"ProxyResolveIpHealthCheckMessage": "Failed to resolve the IP Address for the Configured Proxy Host {0}", "ProxyResolveIpHealthCheckMessage": "Failed to resolve the IP Address for the Configured Proxy Host {0}",
"ProxyType": "Proxy Type", "ProxyType": "Proxy Type",
"ProxyUsernameHelpText": "You only need to enter a username and password if one is required. Leave them blank otherwise.", "ProxyUsernameHelpText": "You only need to enter a username and password if one is required. Leave them blank otherwise.",
"PublishedDate": "Published Date",
"Qualities": "Qualities", "Qualities": "Qualities",
"QualitiesHelpText": "Qualities higher in the list are more preferred. Qualities within the same group are equal. Only checked qualities are wanted", "QualitiesHelpText": "Qualities higher in the list are more preferred. Qualities within the same group are equal. Only checked qualities are wanted",
"QualitiesLoadError": "Unable to load qualities", "QualitiesLoadError": "Unable to load qualities",
@ -678,11 +713,14 @@
"QualitySettings": "Quality Settings", "QualitySettings": "Quality Settings",
"QualitySettingsSummary": "Quality sizes and naming", "QualitySettingsSummary": "Quality sizes and naming",
"Queue": "Queue", "Queue": "Queue",
"QueueIsEmpty": "Queue is empty",
"QueueLoadError": "Failed to load Queue",
"Queued": "Queued", "Queued": "Queued",
"Range": "Range", "Range": "Range",
"Rating": "Rating", "Rating": "Rating",
"ReadTheWikiForMoreInformation": "Read the Wiki for more information", "ReadTheWikiForMoreInformation": "Read the Wiki for more information",
"Real": "Real", "Real": "Real",
"Reason": "Reason",
"RecycleBinUnableToWriteHealthCheckMessage": "Unable to write to configured recycling bin folder: {0}. Ensure this path exists and is writable by the user running Sonarr", "RecycleBinUnableToWriteHealthCheckMessage": "Unable to write to configured recycling bin folder: {0}. Ensure this path exists and is writable by the user running Sonarr",
"RecyclingBin": "Recycling Bin", "RecyclingBin": "Recycling Bin",
"RecyclingBinCleanup": "Recycling Bin Cleanup", "RecyclingBinCleanup": "Recycling Bin Cleanup",
@ -738,13 +776,19 @@
"RemoveFailed": "Remove Failed", "RemoveFailed": "Remove Failed",
"RemoveFailedDownloads": "Remove Failed Downloads", "RemoveFailedDownloads": "Remove Failed Downloads",
"RemoveFailedDownloadsHelpText": "Remove failed downloads from download client history", "RemoveFailedDownloadsHelpText": "Remove failed downloads from download client history",
"RemoveFromBlocklist": "Remove from Blocklist",
"RemoveFromDownloadClient": "Remove From Download Client", "RemoveFromDownloadClient": "Remove From Download Client",
"RemoveFromDownloadClientHelpTextWarning": "Removing will remove the download and the file(s) from the download client.", "RemoveFromDownloadClientHelpTextWarning": "Removing will remove the download and the file(s) from the download client.",
"RemoveFromQueue": "Remove from queue",
"RemoveQueueItem": "Remove - {sourceTitle}",
"RemoveQueueItemConfirmation": "Are you sure you want to remove '{sourceTitle}' from the queue?",
"RemoveRootFolder": "Remove root folder", "RemoveRootFolder": "Remove root folder",
"RemoveSelected": "Remove Selected",
"RemoveSelectedBlocklistMessageText": "Are you sure you want to remove the selected items from the blocklist?",
"RemoveSelectedItem": "Remove Selected Item", "RemoveSelectedItem": "Remove Selected Item",
"RemoveSelectedItemQueueMessageText": "Are you sure you want to remove 1 item from the queue?", "RemoveSelectedItemQueueMessageText": "Are you sure you want to remove 1 item from the queue?",
"RemoveSelectedItems": "Remove Selected Items", "RemoveSelectedItems": "Remove Selected Items",
"RemoveSelectedItemsQueueMessageText": "Are you sure you want to remove {0} items from the queue?", "RemoveSelectedItemsQueueMessageText": "Are you sure you want to remove {selectedCount} items from the queue?",
"RemoveTagsAutomatically": "Remove Tags Automatically", "RemoveTagsAutomatically": "Remove Tags Automatically",
"RemoveTagsAutomaticallyHelpText": "Remove tags automatically if conditions are not met", "RemoveTagsAutomaticallyHelpText": "Remove tags automatically if conditions are not met",
"RemovedFromTaskQueue": "Removed from task queue", "RemovedFromTaskQueue": "Removed from task queue",
@ -790,6 +834,7 @@
"Result": "Result", "Result": "Result",
"Retention": "Retention", "Retention": "Retention",
"RetentionHelpText": "Usenet only: Set to zero to set for unlimited retention", "RetentionHelpText": "Usenet only: Set to zero to set for unlimited retention",
"RetryingDownloadOn": "Retrying download on {date} at {time}",
"RootFolder": "Root Folder", "RootFolder": "Root Folder",
"RootFolderLoadError": "Unable to add root folder", "RootFolderLoadError": "Unable to add root folder",
"RootFolderMissingHealthCheckMessage": "Missing root folder: {0}", "RootFolderMissingHealthCheckMessage": "Missing root folder: {0}",
@ -832,8 +877,10 @@
"SeriesEditor": "Series Editor", "SeriesEditor": "Series Editor",
"SeriesFolderFormat": "Series Folder Format", "SeriesFolderFormat": "Series Folder Format",
"SeriesFolderFormatHelpText": "Used when adding a new series or moving series via the series editor", "SeriesFolderFormatHelpText": "Used when adding a new series or moving series via the series editor",
"SeriesFolderImportedTooltip": "Episode imported from series folder",
"SeriesID": "Series ID", "SeriesID": "Series ID",
"SeriesLoadError": "Unable to load Series", "SeriesLoadError": "Unable to load Series",
"SeriesMatchType": "Series Match Type",
"SeriesTitle": "Series Title", "SeriesTitle": "Series Title",
"SeriesTitleToExcludeHelpText": "The name of the series to exclude", "SeriesTitleToExcludeHelpText": "The name of the series to exclude",
"SeriesType": "Series Type", "SeriesType": "Series Type",
@ -849,6 +896,8 @@
"ShowRelativeDates": "Show Relative Dates", "ShowRelativeDates": "Show Relative Dates",
"ShowRelativeDatesHelpText": "Show relative (Today/Yesterday/etc) or absolute dates", "ShowRelativeDatesHelpText": "Show relative (Today/Yesterday/etc) or absolute dates",
"ShownClickToHide": "Shown, click to hide", "ShownClickToHide": "Shown, click to hide",
"ShownUnknownSeriesItems": "Show Unknown Series Items",
"ShownUnknownSeriesItemsHelpText": "Show items without a series in the queue, this could include removed series, movies or anything else in Sonarr's category",
"SingleEpisode": "Single Episode", "SingleEpisode": "Single Episode",
"SingleEpisodeInvalidFormat": "Single Episode: Invalid Format", "SingleEpisodeInvalidFormat": "Single Episode: Invalid Format",
"Size": "Size", "Size": "Size",
@ -865,6 +914,8 @@
"SomeResultsAreHiddenByTheAppliedFilter": "Some results are hidden by the applied filter", "SomeResultsAreHiddenByTheAppliedFilter": "Some results are hidden by the applied filter",
"SonarrTags": "Sonarr Tags", "SonarrTags": "Sonarr Tags",
"Source": "Source", "Source": "Source",
"SourcePath": "Source Path",
"SourceRelativePath": "Source Relative Path",
"SourceTitle": "Source Title", "SourceTitle": "Source Title",
"Space": "Space", "Space": "Space",
"Special": "Special", "Special": "Special",
@ -942,6 +993,8 @@
"Unavailable": "Unavailable", "Unavailable": "Unavailable",
"Underscore": "Underscore", "Underscore": "Underscore",
"Ungroup": "Ungroup", "Ungroup": "Ungroup",
"Unknown": "Unknown",
"UnknownEventTooltip": "Unknown event",
"Unlimited": "Unlimited", "Unlimited": "Unlimited",
"UnmappedFolders": "Unmapped Folders", "UnmappedFolders": "Unmapped Folders",
"UnmonitorDeletedEpisodes": "Unmonitor Deleted Episodes", "UnmonitorDeletedEpisodes": "Unmonitor Deleted Episodes",
@ -987,6 +1040,8 @@
"VideoCodec": "Video Codec", "VideoCodec": "Video Codec",
"VideoDynamicRange": "Video Dynamic Range", "VideoDynamicRange": "Video Dynamic Range",
"VisitTheWikiForMoreDetails": "Visit the wiki for more details: ", "VisitTheWikiForMoreDetails": "Visit the wiki for more details: ",
"WaitingToImport": "Waiting to Import",
"WaitingToProcess": "Waiting to Process",
"WantMoreControlAddACustomFormat": "Want more control over which downloads are preferred? Add a [Custom Format](/settings/customformats)", "WantMoreControlAddACustomFormat": "Want more control over which downloads are preferred? Add a [Custom Format](/settings/customformats)",
"Wanted": "Wanted", "Wanted": "Wanted",
"Warn": "Warn", "Warn": "Warn",