Localization framework

pull/1012/head
nitsua 3 years ago committed by ta264
parent 144134446d
commit d87bf5ae63

@ -14,6 +14,7 @@ import TablePager from 'Components/Table/TablePager';
import { align, icons, kinds } from 'Helpers/Props';
import getRemovedItems from 'Utilities/Object/getRemovedItems';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
import selectAll from 'Utilities/Table/selectAll';
@ -120,11 +121,11 @@ class Blacklist extends Component {
const selectedIds = this.getSelectedIds();
return (
<PageContent title="Blacklist">
<PageContent title={translate('Blacklist')}>
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label="Remove Selected"
label={translate('RemoveSelected')}
iconName={icons.REMOVE}
isDisabled={!selectedIds.length}
isSpinning={isRemoving}
@ -132,7 +133,7 @@ class Blacklist extends Component {
/>
<PageToolbarButton
label="Clear"
label={translate('Clear')}
iconName={icons.CLEAR}
isSpinning={isClearingBlacklistExecuting}
onPress={onClearBlacklistPress}
@ -145,7 +146,7 @@ class Blacklist extends Component {
columns={columns}
>
<PageToolbarButton
label="Options"
label={translate('Options')}
iconName={icons.TABLE}
/>
</TableOptionsModalWrapper>
@ -160,7 +161,9 @@ class Blacklist extends Component {
{
!isAnyFetching && !!error &&
<div>Unable to load blacklist</div>
<div>
{translate('UnableToLoadBlacklist')}
</div>
}
{
@ -210,9 +213,9 @@ class Blacklist extends Component {
<ConfirmModal
isOpen={isConfirmRemoveModalOpen}
kind={kinds.DANGER}
title="Remove Selected"
message={'Are you sure you want to remove the selected items from the blacklist?'}
confirmLabel="Remove Selected"
title={translate('RemoveSelected')}
message={translate('RemoveSelectedMessageText')}
confirmLabel={translate('RemoveSelected')}
onConfirm={this.onRemoveSelectedConfirmed}
onCancel={this.onConfirmRemoveModalClose}
/>

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

@ -8,6 +8,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import TableRow from 'Components/Table/TableRow';
import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import BlacklistDetailsModal from './BlacklistDetailsModal';
import styles from './BlacklistRow.css';
@ -141,7 +142,7 @@ class BlacklistRow extends Component {
/>
<IconButton
title="Remove from blacklist"
title={translate('RemoveFromBlacklist')}
name={icons.REMOVE}
kind={kinds.DANGER}
onPress={onRemovePress}

@ -9,6 +9,7 @@ import Link from 'Components/Link/Link';
import { icons } from 'Helpers/Props';
import formatDateTime from 'Utilities/Date/formatDateTime';
import formatAge from 'Utilities/Number/formatAge';
import translate from 'Utilities/String/translate';
import styles from './HistoryDetails.css';
function getDetailedList(statusMessages) {
@ -77,14 +78,14 @@ function HistoryDetails(props) {
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title="Name"
title={translate('Name')}
data={sourceTitle}
/>
{
!!indexer &&
<DescriptionListItem
title="Indexer"
title={translate('Indexer')}
data={indexer}
/>
}
@ -93,7 +94,7 @@ function HistoryDetails(props) {
!!releaseGroup &&
<DescriptionListItem
descriptionClassName={styles.description}
title="Release Group"
title={translate('ReleaseGroup')}
data={releaseGroup}
/>
}
@ -114,7 +115,7 @@ function HistoryDetails(props) {
{
!!downloadClient &&
<DescriptionListItem
title="Download Client"
title={translate('DownloadClient')}
data={downloadClient}
/>
}
@ -122,7 +123,7 @@ function HistoryDetails(props) {
{
!!downloadId &&
<DescriptionListItem
title="Grab ID"
title={translate('GrabID')}
data={downloadId}
/>
}
@ -130,7 +131,7 @@ function HistoryDetails(props) {
{
!!indexer &&
<DescriptionListItem
title="Age (when grabbed)"
title={translate('AgeWhenGrabbed')}
data={formatAge(age, ageHours, ageMinutes)}
/>
}
@ -138,7 +139,7 @@ function HistoryDetails(props) {
{
!!publishedDate &&
<DescriptionListItem
title="Published Date"
title={translate('PublishedDate')}
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
/>
}
@ -155,14 +156,14 @@ function HistoryDetails(props) {
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title="Name"
title={translate('Name')}
data={sourceTitle}
/>
{
!!message &&
<DescriptionListItem
title="Message"
title={translate('Message')}
data={message}
/>
}
@ -180,7 +181,7 @@ function HistoryDetails(props) {
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title="Name"
title={translate('Name')}
data={sourceTitle}
/>
@ -188,7 +189,7 @@ function HistoryDetails(props) {
!!droppedPath &&
<DescriptionListItem
descriptionClassName={styles.description}
title="Source"
title={translate('Source')}
data={droppedPath}
/>
}
@ -197,7 +198,7 @@ function HistoryDetails(props) {
!!importedPath &&
<DescriptionListItem
descriptionClassName={styles.description}
title="Imported To"
title={translate('ImportedTo')}
data={importedPath}
/>
}
@ -229,12 +230,12 @@ function HistoryDetails(props) {
return (
<DescriptionList>
<DescriptionListItem
title="Name"
title={translate('Name')}
data={sourceTitle}
/>
<DescriptionListItem
title="Reason"
title={translate('Reason')}
data={reasonMessage}
/>
</DescriptionList>
@ -250,12 +251,12 @@ function HistoryDetails(props) {
return (
<DescriptionList>
<DescriptionListItem
title="Source Path"
title={translate('SourcePath')}
data={sourcePath}
/>
<DescriptionListItem
title="Destination Path"
title={translate('DestinationPath')}
data={path}
/>
</DescriptionList>
@ -271,7 +272,7 @@ function HistoryDetails(props) {
return (
<DescriptionList>
<DescriptionListItem
title="Path"
title={translate('Path')}
data={sourceTitle}
/>
{
@ -286,7 +287,7 @@ function HistoryDetails(props) {
})
}
<DescriptionListItem
title="Existing tags scrubbed"
title={translate('ExistingTagsScrubbed')}
data={tagsScrubbed === 'True' ? <Icon name={icons.CHECK} /> : <Icon name={icons.REMOVE} />}
/>
</DescriptionList>
@ -301,14 +302,14 @@ function HistoryDetails(props) {
return (
<DescriptionList>
<DescriptionListItem
title="Name"
title={translate('Name')}
data={sourceTitle}
/>
{
!!statusMessages &&
<DescriptionListItem
title="Import failures"
title={translate('ImportFailures')}
data={getDetailedList(JSON.parse(statusMessages))}
/>
}
@ -332,14 +333,14 @@ function HistoryDetails(props) {
return (
<DescriptionList>
<DescriptionListItem
title="Name"
title={translate('Name')}
data={sourceTitle}
/>
{
!!indexer &&
<DescriptionListItem
title="Indexer"
title={translate('Indexer')}
data={indexer}
/>
}
@ -347,7 +348,7 @@ function HistoryDetails(props) {
{
!!releaseGroup &&
<DescriptionListItem
title="Release Group"
title={translate('ReleaseGroup')}
data={releaseGroup}
/>
}
@ -368,7 +369,7 @@ function HistoryDetails(props) {
{
!!downloadClient &&
<DescriptionListItem
title="Download Client"
title={translate('DownloadClient')}
data={downloadClient}
/>
}
@ -376,7 +377,7 @@ function HistoryDetails(props) {
{
!!downloadId &&
<DescriptionListItem
title="Grab ID"
title={translate('GrabID')}
data={downloadId}
/>
}
@ -384,7 +385,7 @@ function HistoryDetails(props) {
{
!!indexer &&
<DescriptionListItem
title="Age (when grabbed)"
title={translate('AgeWhenGrabbed')}
data={formatAge(age, ageHours, ageMinutes)}
/>
}
@ -392,7 +393,7 @@ function HistoryDetails(props) {
{
!!publishedDate &&
<DescriptionListItem
title="Published Date"
title={translate('PublishedDate')}
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
/>
}
@ -409,14 +410,14 @@ function HistoryDetails(props) {
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title="Name"
title={translate('Name')}
data={sourceTitle}
/>
{
!!message &&
<DescriptionListItem
title="Message"
title={translate('Message')}
data={message}
/>
}
@ -428,7 +429,7 @@ function HistoryDetails(props) {
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title="Name"
title={translate('Name')}
data={sourceTitle}
/>
</DescriptionList>

@ -13,6 +13,7 @@ import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptions
import TablePager from 'Components/Table/TablePager';
import { align, icons } from 'Helpers/Props';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import translate from 'Utilities/String/translate';
import HistoryRowConnector from './HistoryRowConnector';
class History extends Component {
@ -66,11 +67,11 @@ class History extends Component {
const hasError = error || booksError;
return (
<PageContent title="History">
<PageContent title={translate('History')}>
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label="Refresh"
label={translate('Refresh')}
iconName={icons.REFRESH}
isSpinning={isFetching}
onPress={onFirstPagePress}
@ -83,7 +84,7 @@ class History extends Component {
columns={columns}
>
<PageToolbarButton
label="Options"
label={translate('Options')}
iconName={icons.TABLE}
/>
</TableOptionsModalWrapper>
@ -106,7 +107,9 @@ class History extends Component {
{
!isFetchingAny && hasError &&
<div>Unable to load history</div>
<div>
{translate('UnableToLoadHistory')}
</div>
}
{

@ -15,6 +15,7 @@ import TablePager from 'Components/Table/TablePager';
import { align, icons } from 'Helpers/Props';
import getRemovedItems from 'Utilities/Object/getRemovedItems';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
import selectAll from 'Utilities/Table/selectAll';
@ -150,11 +151,11 @@ class Queue extends Component {
const disableSelectedActions = selectedCount === 0;
return (
<PageContent title="Queue">
<PageContent title={translate('Queue')}>
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label="Refresh"
label={translate('Refresh')}
iconName={icons.REFRESH}
isSpinning={isRefreshing}
onPress={onRefreshPress}
@ -163,7 +164,7 @@ class Queue extends Component {
<PageToolbarSeparator />
<PageToolbarButton
label="Grab Selected"
label={translate('GrabSelected')}
iconName={icons.DOWNLOAD}
isDisabled={disableSelectedActions || !isPendingSelected}
isSpinning={isGrabbing}
@ -171,7 +172,7 @@ class Queue extends Component {
/>
<PageToolbarButton
label="Remove Selected"
label={translate('RemoveSelected')}
iconName={icons.REMOVE}
isDisabled={disableSelectedActions}
isSpinning={isRemoving}
@ -188,7 +189,7 @@ class Queue extends Component {
optionsComponent={QueueOptionsConnector}
>
<PageToolbarButton
label="Options"
label={translate('Options')}
iconName={icons.TABLE}
/>
</TableOptionsModalWrapper>

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
function QueueDetails(props) {
const {
@ -23,7 +24,7 @@ function QueueDetails(props) {
return (
<Icon
name={icons.PENDING}
title={`Release will be processed ${moment(estimatedCompletionTime).fromNow()}`}
title={translate('ReleaseWillBeProcessedInterp', [moment(estimatedCompletionTime).fromNow()])}
/>
);
}
@ -34,7 +35,7 @@ function QueueDetails(props) {
<Icon
name={icons.DOWNLOAD}
kind={kinds.DANGER}
title={`Import failed: ${errorMessage}`}
title={translate('ImportFailedInterp', [errorMessage])}
/>
);
}
@ -47,7 +48,7 @@ function QueueDetails(props) {
<Icon
name={icons.DOWNLOADING}
kind={kinds.DANGER}
title={`Download failed: ${errorMessage}`}
title={translate('DownloadFailedInterp', [errorMessage])}
/>
);
}
@ -57,7 +58,7 @@ function QueueDetails(props) {
<Icon
name={icons.DOWNLOADING}
kind={kinds.DANGER}
title="Download failed: check download client for more details"
title={translate('DownloadFailedCheckDownloadClientForMoreDetails')}
/>
);
}
@ -67,7 +68,7 @@ function QueueDetails(props) {
<Icon
name={icons.DOWNLOADING}
kind={kinds.WARNING}
title="Download warning: check download client for more details"
title={translate('DownloadWarningCheckDownloadClientForMoreDetails')}
/>
);
}
@ -76,7 +77,7 @@ function QueueDetails(props) {
return (
<Icon
name={icons.DOWNLOADING}
title={`Book is downloading - ${progress.toFixed(1)}% ${title}`}
title={translate('BookIsDownloadingInterp', [progress.toFixed(1), title])}
/>
);
}

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

@ -15,6 +15,7 @@ import TableRow from 'Components/Table/TableRow';
import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
import translate from 'Utilities/String/translate';
import QueueStatusCell from './QueueStatusCell';
import RemoveQueueItemModal from './RemoveQueueItemModal';
import TimeleftCell from './TimeleftCell';
@ -285,7 +286,7 @@ class QueueRow extends Component {
kind={kinds.DANGER}
/>
}
title="Manual Download"
title={translate('ManualDownload')}
body="This release failed parsing checks and was manually downloaded from an interactive search. Import is likely to fail."
position={tooltipPositions.LEFT}
/>
@ -310,7 +311,7 @@ class QueueRow extends Component {
}
<SpinnerIconButton
title="Remove from queue"
title={translate('RemoveFromQueue')}
name={icons.REMOVE}
isSpinning={isRemoving}
onPress={this.onRemoveQueueItemPress}

@ -4,6 +4,7 @@ import Icon from 'Components/Icon';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './QueueStatusCell.css';
function getDetailedPopoverBody(statusMessages) {
@ -49,7 +50,7 @@ function QueueStatusCell(props) {
// status === 'downloading'
let iconName = icons.DOWNLOADING;
let iconKind = kinds.DEFAULT;
let title = 'Downloading';
let title = translate('Title');
if (hasWarning) {
iconKind = kinds.WARNING;

@ -10,6 +10,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
class RemoveQueueItemModal extends Component {
@ -95,26 +96,30 @@ class RemoveQueueItemModal extends Component {
</div>
<FormGroup>
<FormLabel>Remove From Download Client</FormLabel>
<FormLabel>
{translate('RemoveFromDownloadClient')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="remove"
value={remove}
helpTextWarning="Removing will remove the download and the file(s) from the download client."
helpTextWarning={translate('RemoveHelpTextWarning')}
isDisabled={!canIgnore}
onChange={this.onRemoveChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Blacklist Release</FormLabel>
<FormLabel>
{translate('BlacklistRelease')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="blacklist"
value={blacklist}
helpText="Prevents Readarr from automatically grabbing this release again"
helpText={translate('BlacklistHelpText')}
onChange={this.onBlacklistChange}
/>
</FormGroup>
@ -122,12 +127,14 @@ class RemoveQueueItemModal extends Component {
{
blacklist &&
<FormGroup>
<FormLabel>Skip Redownload</FormLabel>
<FormLabel>
{translate('SkipRedownload')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="skipredownload"
value={skipredownload}
helpText="Prevents Readarr from trying download an alternative release for this item"
helpText={translate('SkipredownloadHelpText')}
onChange={this.onSkipReDownloadChange}
/>
</FormGroup>

@ -10,6 +10,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './RemoveQueueItemsModal.css';
class RemoveQueueItemsModal extends Component {
@ -96,13 +97,15 @@ class RemoveQueueItemsModal extends Component {
</div>
<FormGroup>
<FormLabel>Remove From Download Client</FormLabel>
<FormLabel>
{translate('RemoveFromDownloadClient')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="remove"
value={remove}
helpTextWarning="Removing will remove the download and the file(s) from the download client."
helpTextWarning={translate('RemoveHelpTextWarning')}
isDisabled={!canIgnore}
onChange={this.onRemoveChange}
/>
@ -117,7 +120,7 @@ class RemoveQueueItemsModal extends Component {
type={inputTypes.CHECK}
name="blacklist"
value={blacklist}
helpText="Prevents Readarr from automatically grabbing these files again"
helpText={translate('BlacklistHelpText')}
onChange={this.onBlacklistChange}
/>
</FormGroup>
@ -125,12 +128,14 @@ class RemoveQueueItemsModal extends Component {
{
blacklist &&
<FormGroup>
<FormLabel>Skip Redownload</FormLabel>
<FormLabel>
{translate('SkipRedownload')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="skipredownload"
value={skipredownload}
helpText="Prevents Readarr from trying download alternative releases for the removed items"
helpText={translate('SkipredownloadHelpText')}
onChange={this.onSkipReDownloadChange}
/>
</FormGroup>

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

@ -1,42 +1,43 @@
import React from 'react';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
import translate from 'Utilities/String/translate';
function AuthorMonitoringOptionsPopoverContent() {
return (
<DescriptionList>
<DescriptionListItem
title="All Books"
title={translate('AllBooks')}
data="Monitor all books"
/>
<DescriptionListItem
title="Future Books"
title={translate('FutureBooks')}
data="Monitor books that have not released yet"
/>
<DescriptionListItem
title="Missing Books"
title={translate('MissingBooks')}
data="Monitor books that do not have files or have not released yet"
/>
<DescriptionListItem
title="Existing Books"
title={translate('ExistingBooks')}
data="Monitor books that have files or have not released yet"
/>
<DescriptionListItem
title="First Book"
title={translate('FirstBook')}
data="Monitor the first book. All other books will be ignored"
/>
<DescriptionListItem
title="Latest Book"
title={translate('LatestBook')}
data="Monitor the latest book and future books"
/>
<DescriptionListItem
title="None"
title={translate('None')}
data="No books will be monitored"
/>
</DescriptionList>

@ -8,6 +8,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { kinds } from 'Helpers/Props';
import UpdateChanges from 'System/Updates/UpdateChanges';
import translate from 'Utilities/String/translate';
import styles from './AppUpdatedModalContent.css';
function AppUpdatedModalContent(props) {
@ -49,12 +50,12 @@ function AppUpdatedModalContent(props) {
</div>
<UpdateChanges
title="New"
title={translate('New')}
changes={update.changes.new}
/>
<UpdateChanges
title="Fixed"
title={translate('Fixed')}
changes={update.changes.fixed}
/>
</div>

@ -11,6 +11,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { icons, inputTypes, kinds } from 'Helpers/Props';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
import styles from './DeleteAuthorModalContent.css';
class DeleteAuthorModalContent extends Component {
@ -67,7 +68,7 @@ class DeleteAuthorModalContent extends Component {
const addImportListExclusion = this.state.addImportListExclusion;
let deleteFilesLabel = `Delete ${bookFileCount} Book Files`;
let deleteFilesHelpText = 'Delete the book files and author folder';
let deleteFilesHelpText = translate('DeleteFilesHelpText');
if (bookFileCount === 0) {
deleteFilesLabel = 'Delete Author Folder';
@ -106,13 +107,15 @@ class DeleteAuthorModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Add List Exclusion</FormLabel>
<FormLabel>
{translate('AddListExclusion')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="addImportListExclusion"
value={addImportListExclusion}
helpText="Prevent author from being added to Readarr by Import lists"
helpText={translate('AddImportListExclusionHelpText')}
kind={kinds.DANGER}
onChange={this.onAddImportListExclusionChange}
/>
@ -121,7 +124,9 @@ class DeleteAuthorModalContent extends Component {
{
deleteFiles &&
<div className={styles.deleteFilesMessage}>
<div>The author folder <strong>{path}</strong> and all of its content will be deleted.</div>
<div>
{translate('TheAuthorFolderStrongpathstrongAndAllOfItsContentWillBeDeleted')}
</div>
{
!!bookFileCount &&

@ -20,6 +20,7 @@ import InteractiveSearchFilterMenuConnector from 'InteractiveSearch/InteractiveS
import InteractiveSearchTable from 'InteractiveSearch/InteractiveSearchTable';
import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector';
import RetagPreviewModalConnector from 'Retag/RetagPreviewModalConnector';
import translate from 'Utilities/String/translate';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
import InteractiveImportModal from '../../InteractiveImport/InteractiveImportModal';
@ -181,41 +182,41 @@ class AuthorDetails extends Component {
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label="Refresh & Scan"
label={translate('RefreshScan')}
iconName={icons.REFRESH}
spinningName={icons.REFRESH}
title="Refresh information and scan disk"
title={translate('RefreshInformationAndScanDisk')}
isSpinning={isRefreshing}
onPress={onRefreshPress}
/>
<PageToolbarButton
label="Search Monitored"
label={translate('SearchMonitored')}
iconName={icons.SEARCH}
isDisabled={!monitored || !hasMonitoredBooks || !hasBooks}
isSpinning={isSearching}
title={hasMonitoredBooks ? undefined : 'No monitored books for this author'}
title={hasMonitoredBooks ? undefined : translate('HasMonitoredBooksNoMonitoredBooksForThisAuthor')}
onPress={onSearchPress}
/>
<PageToolbarSeparator />
<PageToolbarButton
label="Preview Rename"
label={translate('PreviewRename')}
iconName={icons.ORGANIZE}
isDisabled={!hasBookFiles}
onPress={this.onOrganizePress}
/>
<PageToolbarButton
label="Preview Retag"
label={translate('PreviewRetag')}
iconName={icons.RETAG}
isDisabled={!hasBookFiles}
onPress={this.onRetagPress}
/>
<PageToolbarButton
label="Manual Import"
label={translate('ManualImport')}
iconName={icons.INTERACTIVE}
onPress={this.onInteractiveImportPress}
/>
@ -223,13 +224,13 @@ class AuthorDetails extends Component {
<PageToolbarSeparator />
<PageToolbarButton
label="Edit"
label={translate('Edit')}
iconName={icons.EDIT}
onPress={this.onEditAuthorPress}
/>
<PageToolbarButton
label="Delete"
label={translate('Delete')}
iconName={icons.DELETE}
onPress={this.onDeleteAuthorPress}
/>
@ -237,7 +238,7 @@ class AuthorDetails extends Component {
<PageToolbarSection alignContent={align.RIGHT}>
<PageToolbarButton
label={allExpanded ? 'Collapse All' : 'Expand All'}
label={allExpanded ? translate('AllExpandedCollapseAll') : translate('AllExpandedExpandAll')}
iconName={expandIcon}
onPress={this.onExpandAllPress}
/>
@ -258,7 +259,7 @@ class AuthorDetails extends Component {
className={styles.authorNavigationButton}
name={icons.ARROW_LEFT}
size={30}
title={`Go to ${previousAuthor.authorName}`}
title={translate('GoToInterp', [previousAuthor.authorName])}
to={`/author/${previousAuthor.titleSlug}`}
/>
@ -266,7 +267,7 @@ class AuthorDetails extends Component {
className={styles.authorUpButton}
name={icons.ARROW_UP}
size={30}
title={'Go to author listing'}
title={translate('GoToAuthorListing')}
to={'/'}
/>
@ -274,7 +275,7 @@ class AuthorDetails extends Component {
className={styles.authorNavigationButton}
name={icons.ARROW_RIGHT}
size={30}
title={`Go to ${nextAuthor.authorName}`}
title={translate('GoToInterp', [nextAuthor.authorName])}
to={`/author/${nextAuthor.titleSlug}`}
/>
</div>
@ -288,12 +289,16 @@ class AuthorDetails extends Component {
{
!isFetching && booksError &&
<div>Loading books failed</div>
<div>
{translate('LoadingBooksFailed')}
</div>
}
{
!isFetching && bookFilesError &&
<div>Loading book files failed</div>
<div>
{translate('LoadingBookFilesFailed')}
</div>
}
{

@ -15,6 +15,7 @@ import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfil
import fonts from 'Styles/Variables/fonts';
import formatBytes from 'Utilities/Number/formatBytes';
import stripHtml from 'Utilities/String/stripHtml';
import translate from 'Utilities/String/translate';
import AuthorAlternateTitles from './AuthorAlternateTitles';
import AuthorDetailsLinks from './AuthorDetailsLinks';
import AuthorTagsConnector from './AuthorTagsConnector';
@ -95,7 +96,7 @@ class AuthorDetailsHeader extends Component {
const continuing = status === 'continuing';
let bookFilesCountMessage = 'No book files';
let bookFilesCountMessage = translate('BookFilesCountMessage');
if (bookFileCount === 1) {
bookFilesCountMessage = '1 book file';
@ -152,7 +153,7 @@ class AuthorDetailsHeader extends Component {
size={20}
/>
}
title="Alternate Titles"
title={translate('AlternateTitles')}
body={<AuthorAlternateTitles alternateTitles={alternateTitles} />}
position={tooltipPositions.BOTTOM}
/>
@ -204,7 +205,7 @@ class AuthorDetailsHeader extends Component {
<Label
className={styles.detailsLabel}
title="Quality Profile"
title={translate('QualityProfile')}
size={sizes.LARGE}
>
<Icon
@ -237,7 +238,7 @@ class AuthorDetailsHeader extends Component {
<Label
className={styles.detailsLabel}
title={continuing ? 'More books are expected' : 'No additional books are expected'}
title={continuing ? translate('ContinuingMoreBooksAreExpected') : translate('ContinuingNoAdditionalBooksAreExpected')}
size={sizes.LARGE}
>
<Icon

@ -9,6 +9,7 @@ import NotFound from 'Components/NotFound';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import AuthorDetailsConnector from './AuthorDetailsConnector';
import styles from './AuthorDetails.css';
@ -92,7 +93,7 @@ class AuthorDetailsPageConnector extends Component {
if (!titleSlug) {
return (
<NotFound
message="Sorry, that author cannot be found."
message={translate('SorryThatAuthorCannotBeFound')}
/>
);
}

@ -8,6 +8,7 @@ import MonitorToggleButton from 'Components/MonitorToggleButton';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { icons, sortDirections } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import getToggledRange from 'Utilities/Table/getToggledRange';
import BookRowConnector from './BookRowConnector';
import styles from './AuthorDetailsSeries.css';
@ -152,7 +153,7 @@ class AuthorDetailsSeries extends Component {
<Icon
className={styles.expandButtonIcon}
name={isExpanded ? icons.COLLAPSE : icons.EXPAND}
title={isExpanded ? 'Hide books' : 'Show books'}
title={isExpanded ? translate('IsExpandedHideBooks') : translate('IsExpandedShowBooks')}
size={24}
/>
@ -198,7 +199,7 @@ class AuthorDetailsSeries extends Component {
iconClassName={styles.collapseButtonIcon}
name={icons.COLLAPSE}
size={20}
title="Hide books"
title={translate('HideBooks')}
onPress={this.onExpandPress}
/>
</div>

@ -9,6 +9,7 @@ import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellCo
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import { kinds, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './BookRow.css';
function getBookCountKind(monitored, bookFileCount, bookCount) {
@ -196,7 +197,7 @@ class BookRow extends Component {
className={styles.status}
>
<Label
title={`${totalBookCount} books total. ${bookFileCount} books with files.`}
title={translate('TotalBookCountBooksTotalBookFileCountBooksWithFilesInterp', [totalBookCount, bookFileCount])}
kind={getBookCountKind(monitored, bookFileCount, bookCount)}
size={sizes.MEDIUM}
>

@ -15,6 +15,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import Popover from 'Components/Tooltip/Popover';
import { icons, inputTypes, kinds, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './EditAuthorModalContent.css';
class EditAuthorModalContent extends Component {
@ -87,19 +88,23 @@ class EditAuthorModalContent extends Component {
<ModalBody>
<Form {...otherProps}>
<FormGroup>
<FormLabel>Monitored</FormLabel>
<FormLabel>
{translate('Monitored')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="monitored"
helpText="Download monitored books from this author"
helpText={translate('MonitoredHelpText')}
{...monitored}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Quality Profile</FormLabel>
<FormLabel>
{translate('QualityProfile')}
</FormLabel>
<FormInputGroup
type={inputTypes.QUALITY_PROFILE_SELECT}
@ -122,7 +127,7 @@ class EditAuthorModalContent extends Component {
name={icons.INFO}
/>
}
title="Metadata Profile"
title={translate('MetadataProfile')}
body={<AuthorMetadataProfilePopoverContent />}
position={tooltipPositions.RIGHT}
/>
@ -132,7 +137,7 @@ class EditAuthorModalContent extends Component {
<FormInputGroup
type={inputTypes.METADATA_PROFILE_SELECT}
name="metadataProfileId"
helpText="Changes will take place on next author refresh"
helpText={translate('MetadataProfileIdHelpText')}
includeNone={true}
{...metadataProfileId}
onChange={onInputChange}
@ -141,7 +146,9 @@ class EditAuthorModalContent extends Component {
}
<FormGroup>
<FormLabel>Path</FormLabel>
<FormLabel>
{translate('Path')}
</FormLabel>
<FormInputGroup
type={inputTypes.PATH}
@ -152,7 +159,9 @@ class EditAuthorModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Tags</FormLabel>
<FormLabel>
{translate('Tags')}
</FormLabel>
<FormInputGroup
type={inputTypes.TAG}

@ -14,6 +14,7 @@ import TableBody from 'Components/Table/TableBody';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import { align, icons, sortDirections } from 'Helpers/Props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
@ -143,7 +144,7 @@ class AuthorEditor extends Component {
const selectedAuthorIds = this.getSelectedIds();
return (
<PageContent title="Author Editor">
<PageContent title={translate('AuthorEditor')}>
<PageToolbar>
<PageToolbarSection />
<PageToolbarSection alignContent={align.RIGHT}>
@ -152,7 +153,7 @@ class AuthorEditor extends Component {
onTableOptionChange={onTableOptionChange}
>
<PageToolbarButton
label="Options"
label={translate('Options')}
iconName={icons.TABLE}
/>
</TableOptionsModalWrapper>

@ -8,6 +8,7 @@ import SelectInput from 'Components/Form/SelectInput';
import SpinnerButton from 'Components/Link/SpinnerButton';
import PageContentFooter from 'Components/Page/PageContentFooter';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import AuthorEditorFooterLabel from './AuthorEditorFooterLabel';
import DeleteAuthorModal from './Delete/DeleteAuthorModal';
import TagsModal from './Tags/TagsModal';
@ -165,7 +166,7 @@ class AuthorEditorFooter extends Component {
<PageContentFooter>
<div className={styles.inputContainer}>
<AuthorEditorFooterLabel
label="Monitor Author"
label={translate('MonitorAuthor')}
isSaving={isSaving && monitored !== NO_CHANGE}
/>
@ -196,7 +197,7 @@ class AuthorEditorFooter extends Component {
className={styles.inputContainer}
>
<AuthorEditorFooterLabel
label="Quality Profile"
label={translate('QualityProfile')}
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
/>
@ -218,7 +219,7 @@ class AuthorEditorFooter extends Component {
className={styles.inputContainer}
>
<AuthorEditorFooterLabel
label="Metadata Profile"
label={translate('MetadataProfile')}
isSaving={isSaving && metadataProfileId !== NO_CHANGE}
/>
@ -240,7 +241,7 @@ class AuthorEditorFooter extends Component {
className={styles.inputContainer}
>
<AuthorEditorFooterLabel
label="Root Folder"
label={translate('RootFolder')}
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
/>
@ -263,7 +264,7 @@ class AuthorEditorFooter extends Component {
<div className={styles.buttonContainer}>
<div className={styles.buttonContainerContent}>
<AuthorEditorFooterLabel
label={`${selectedCount} Author(s) Selected`}
label={translate('SelectedCountAuthorsSelectedInterp', [selectedCount])}
isSaving={false}
/>

@ -12,6 +12,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './TagsModalContent.css';
class TagsModalContent extends Component {
@ -74,7 +75,9 @@ class TagsModalContent extends Component {
<ModalBody>
<Form>
<FormGroup>
<FormLabel>Tags</FormLabel>
<FormLabel>
{translate('Tags')}
</FormLabel>
<FormInputGroup
type={inputTypes.TAG}
@ -85,7 +88,9 @@ class TagsModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Apply Tags</FormLabel>
<FormLabel>
{translate('ApplyTags')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@ -93,17 +98,19 @@ class TagsModalContent extends Component {
value={applyTags}
values={applyTagsOptions}
helpTexts={[
'How to apply tags to the selected author',
'Add: Add the tags the existing list of tags',
'Remove: Remove the entered tags',
'Replace: Replace the tags with the entered tags (enter no tags to clear all tags)'
translate('ApplyTagsHelpTexts1'),
translate('ApplyTagsHelpTexts2'),
translate('ApplyTagsHelpTexts3'),
translate('ApplyTagsHelpTexts4')
]}
onChange={this.onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Result</FormLabel>
<FormLabel>
{translate('Result')}
</FormLabel>
<div className={styles.result}>
{
@ -120,7 +127,7 @@ class TagsModalContent extends Component {
return (
<Label
key={tag.id}
title={removeTag ? 'Removing tag' : 'Existing tag'}
title={removeTag ? translate('RemoveTagRemovingTag') : translate('RemoveTagExistingTag')}
kind={removeTag ? kinds.INVERSE : kinds.INFO}
size={sizes.LARGE}
>
@ -146,7 +153,7 @@ class TagsModalContent extends Component {
return (
<Label
key={tag.id}
title={'Adding tag'}
title={translate('AddingTag')}
kind={kinds.SUCCESS}
size={sizes.LARGE}
>

@ -11,6 +11,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './AuthorHistoryRow.css';
function getTitle(eventType) {
@ -132,7 +133,7 @@ class AuthorHistoryRow extends Component {
{
eventType === 'grabbed' &&
<IconButton
title="Mark as failed"
title={translate('MarkAsFailed')}
name={icons.REMOVE}
onPress={this.onMarkAsFailedPress}
/>
@ -142,9 +143,9 @@ class AuthorHistoryRow extends Component {
<ConfirmModal
isOpen={isMarkAsFailedModalOpen}
kind={kinds.DANGER}
title="Mark as Failed"
message={`Are you sure you want to mark '${sourceTitle}' as failed?`}
confirmLabel="Mark as Failed"
title={translate('MarkAsFailed')}
message={translate('MarkAsFailedMessageText', [sourceTitle])}
confirmLabel={translate('MarkAsFailed')}
onConfirm={this.onConfirmMarkAsFailed}
onCancel={this.onMarkAsFailedModalClose}
/>

@ -3,6 +3,7 @@ import React, { Component } from 'react';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import translate from 'Utilities/String/translate';
import AuthorHistoryRowConnector from './AuthorHistoryRowConnector';
const columns = [
@ -69,12 +70,16 @@ class AuthorHistoryTableContent extends Component {
{
!isFetching && !!error &&
<div>Unable to load history.</div>
<div>
{translate('UnableToLoadHistory')}
</div>
}
{
isPopulated && !hasItems && !error &&
<div>No history.</div>
<div>
{translate('NoHistory')}
</div>
}
{

@ -14,6 +14,7 @@ import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptions
import { align, icons, sortDirections } from 'Helpers/Props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
import translate from 'Utilities/String/translate';
import AuthorIndexFooterConnector from './AuthorIndexFooterConnector';
import AuthorIndexBannersConnector from './Banners/AuthorIndexBannersConnector';
import AuthorIndexBannerOptionsModal from './Banners/Options/AuthorIndexBannerOptionsModal';
@ -213,7 +214,7 @@ class AuthorIndex extends Component {
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label="Update all"
label={translate('UpdateAll')}
iconName={icons.REFRESH}
spinningName={icons.REFRESH}
isSpinning={isRefreshingAuthor}
@ -221,7 +222,7 @@ class AuthorIndex extends Component {
/>
<PageToolbarButton
label="RSS Sync"
label={translate('RSSSync')}
iconName={icons.RSS}
isSpinning={isRssSyncExecuting}
isDisabled={hasNoAuthor}
@ -242,7 +243,7 @@ class AuthorIndex extends Component {
optionsComponent={AuthorIndexTableOptionsConnector}
>
<PageToolbarButton
label="Options"
label={translate('Options')}
iconName={icons.TABLE}
/>
</TableOptionsModalWrapper> :
@ -252,7 +253,7 @@ class AuthorIndex extends Component {
{
view === 'posters' ?
<PageToolbarButton
label="Options"
label={translate('Options')}
iconName={icons.POSTER}
isDisabled={hasNoAuthor}
onPress={this.onPosterOptionsPress}
@ -263,7 +264,7 @@ class AuthorIndex extends Component {
{
view === 'banners' ?
<PageToolbarButton
label="Options"
label={translate('Options')}
iconName={icons.POSTER}
isDisabled={hasNoAuthor}
onPress={this.onBannerOptionsPress}
@ -274,7 +275,7 @@ class AuthorIndex extends Component {
{
view === 'overview' ?
<PageToolbarButton
label="Options"
label={translate('Options')}
iconName={icons.OVERVIEW}
isDisabled={hasNoAuthor}
onPress={this.onOverviewOptionsPress}

@ -5,6 +5,7 @@ import { ColorImpairedConsumer } from 'App/ColorImpairedContext';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
import styles from './AuthorIndexFooter.css';
class AuthorIndexFooter extends PureComponent {
@ -60,7 +61,9 @@ class AuthorIndexFooter extends PureComponent {
enableColorImpairedMode && 'colorImpaired'
)}
/>
<div>Continuing (All books downloaded)</div>
<div>
{translate('ContinuingAllBooksDownloaded')}
</div>
</div>
<div className={styles.legendItem}>
@ -70,7 +73,9 @@ class AuthorIndexFooter extends PureComponent {
enableColorImpairedMode && 'colorImpaired'
)}
/>
<div>Ended (All books downloaded)</div>
<div>
{translate('EndedAllBooksDownloaded')}
</div>
</div>
<div className={styles.legendItem}>
@ -80,7 +85,9 @@ class AuthorIndexFooter extends PureComponent {
enableColorImpairedMode && 'colorImpaired'
)}
/>
<div>Missing Books (Author monitored)</div>
<div>
{translate('MissingBooksAuthorMonitored')}
</div>
</div>
<div className={styles.legendItem}>
@ -90,55 +97,57 @@ class AuthorIndexFooter extends PureComponent {
enableColorImpairedMode && 'colorImpaired'
)}
/>
<div>Missing Books (Author not monitored)</div>
<div>
{translate('MissingBooksAuthorNotMonitored')}
</div>
</div>
</div>
<div className={styles.statistics}>
<DescriptionList>
<DescriptionListItem
title="Authors"
title={translate('Authors')}
data={count}
/>
<DescriptionListItem
title="Ended"
title={translate('Ended')}
data={ended}
/>
<DescriptionListItem
title="Continuing"
title={translate('Continuing')}
data={continuing}
/>
</DescriptionList>
<DescriptionList>
<DescriptionListItem
title="Monitored"
title={translate('Monitored')}
data={monitored}
/>
<DescriptionListItem
title="Unmonitored"
title={translate('Unmonitored')}
data={count - monitored}
/>
</DescriptionList>
<DescriptionList>
<DescriptionListItem
title="Books"
title={translate('Books')}
data={books}
/>
<DescriptionListItem
title="Files"
title={translate('Files')}
data={bookFiles}
/>
</DescriptionList>
<DescriptionList>
<DescriptionListItem
title="Total File Size"
title={translate('TotalFileSize')}
data={formatBytes(totalFileSize)}
/>
</DescriptionList>

@ -10,6 +10,7 @@ import Link from 'Components/Link/Link';
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
import { icons } from 'Helpers/Props';
import getRelativeDate from 'Utilities/Date/getRelativeDate';
import translate from 'Utilities/String/translate';
import AuthorIndexBannerInfo from './AuthorIndexBannerInfo';
import styles from './AuthorIndexBanner.css';
@ -107,7 +108,7 @@ class AuthorIndexBanner extends Component {
<SpinnerIconButton
className={styles.action}
name={icons.REFRESH}
title="Refresh Author"
title={translate('RefreshAuthor')}
isSpinning={isRefreshingAuthor}
onPress={onRefreshAuthorPress}
/>
@ -117,7 +118,7 @@ class AuthorIndexBanner extends Component {
<SpinnerIconButton
className={styles.action}
name={icons.SEARCH}
title="Search for monitored books"
title={translate('SearchForMonitoredBooks')}
isSpinning={isSearchingAuthor}
onPress={onSearchPress}
/>
@ -126,7 +127,7 @@ class AuthorIndexBanner extends Component {
<IconButton
className={styles.action}
name={icons.EDIT}
title="Edit Author"
title={translate('EditAuthor')}
onPress={this.onEditAuthorPress}
/>
</Label>
@ -135,7 +136,7 @@ class AuthorIndexBanner extends Component {
status === 'ended' &&
<div
className={styles.ended}
title="Ended"
title={translate('Ended')}
/>
}

@ -11,6 +11,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
const bannerSizeOptions = [
{ key: 'small', value: 'Small' },
@ -114,7 +115,9 @@ class AuthorIndexBannerOptionsModalContent extends Component {
<ModalBody>
<Form>
<FormGroup>
<FormLabel> Size</FormLabel>
<FormLabel>
{translate('Size')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@ -126,61 +129,71 @@ class AuthorIndexBannerOptionsModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Detailed Progress Bar</FormLabel>
<FormLabel>
{translate('DetailedProgressBar')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="detailedProgressBar"
value={detailedProgressBar}
helpText="Show text on progess bar"
helpText={translate('DetailedProgressBarHelpText')}
onChange={this.onChangeBannerOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Name</FormLabel>
<FormLabel>
{translate('ShowName')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showTitle"
value={showTitle}
helpText="Show author name under banner"
helpText={translate('ShowTitleHelpText')}
onChange={this.onChangeBannerOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Monitored</FormLabel>
<FormLabel>
{translate('ShowMonitored')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showMonitored"
value={showMonitored}
helpText="Show monitored status under banner"
helpText={translate('ShowMonitoredHelpText')}
onChange={this.onChangeBannerOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Quality Profile</FormLabel>
<FormLabel>
{translate('ShowQualityProfile')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showQualityProfile"
value={showQualityProfile}
helpText="Show quality profile under banner"
helpText={translate('ShowQualityProfileHelpText')}
onChange={this.onChangeBannerOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Search</FormLabel>
<FormLabel>
{translate('ShowSearch')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showSearchAction"
value={showSearchAction}
helpText="Show search button on hover"
helpText={translate('ShowSearchActionHelpText')}
onChange={this.onChangeBannerOption}
/>
</FormGroup>

@ -12,6 +12,7 @@ import { icons } from 'Helpers/Props';
import dimensions from 'Styles/Variables/dimensions';
import fonts from 'Styles/Variables/fonts';
import stripHtml from 'Utilities/String/stripHtml';
import translate from 'Utilities/String/translate';
import AuthorIndexOverviewInfo from './AuthorIndexOverviewInfo';
import styles from './AuthorIndexOverview.css';
@ -130,7 +131,7 @@ class AuthorIndexOverview extends Component {
status === 'ended' &&
<div
className={styles.ended}
title="Ended"
title={translate('Ended')}
/>
}
@ -173,7 +174,7 @@ class AuthorIndexOverview extends Component {
<div className={styles.actions}>
<SpinnerIconButton
name={icons.REFRESH}
title="Refresh Author"
title={translate('RefreshAuthor')}
isSpinning={isRefreshingAuthor}
onPress={onRefreshAuthorPress}
/>
@ -183,7 +184,7 @@ class AuthorIndexOverview extends Component {
<SpinnerIconButton
className={styles.action}
name={icons.SEARCH}
title="Search for monitored books"
title={translate('SearchForMonitoredBooks')}
isSpinning={isSearchingAuthor}
onPress={onSearchPress}
/>
@ -191,7 +192,7 @@ class AuthorIndexOverview extends Component {
<IconButton
name={icons.EDIT}
title="Edit Author"
title={translate('EditAuthor')}
onPress={this.onEditAuthorPress}
/>
</div>

@ -11,6 +11,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
const posterSizeOptions = [
{ key: 'small', value: 'Small' },
@ -142,7 +143,9 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
<ModalBody>
<Form>
<FormGroup>
<FormLabel>Poster Size</FormLabel>
<FormLabel>
{translate('PosterSize')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@ -154,19 +157,23 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Detailed Progress Bar</FormLabel>
<FormLabel>
{translate('DetailedProgressBar')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="detailedProgressBar"
value={detailedProgressBar}
helpText="Show text on progess bar"
helpText={translate('DetailedProgressBarHelpText')}
onChange={this.onChangeOverviewOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Monitored</FormLabel>
<FormLabel>
{translate('ShowMonitored')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@ -178,7 +185,9 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
<FormGroup>
<FormLabel>Show Quality Profile</FormLabel>
<FormLabel>
{translate('ShowQualityProfile')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@ -189,7 +198,9 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Show Last Book</FormLabel>
<FormLabel>
{translate('ShowLastBook')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@ -200,7 +211,9 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Show Date Added</FormLabel>
<FormLabel>
{translate('ShowDateAdded')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@ -211,7 +224,9 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Show Book Count</FormLabel>
<FormLabel>
{translate('ShowBookCount')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@ -222,7 +237,9 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Show Path</FormLabel>
<FormLabel>
{translate('ShowPath')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@ -233,7 +250,9 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Show Size on Disk</FormLabel>
<FormLabel>
{translate('ShowSizeOnDisk')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@ -244,13 +263,15 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Show Search</FormLabel>
<FormLabel>
{translate('ShowSearch')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showSearchAction"
value={showSearchAction}
helpText="Show search button"
helpText={translate('ShowSearchActionHelpText')}
onChange={this.onChangeOverviewOption}
/>
</FormGroup>

@ -10,6 +10,7 @@ import Link from 'Components/Link/Link';
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
import { icons } from 'Helpers/Props';
import getRelativeDate from 'Utilities/Date/getRelativeDate';
import translate from 'Utilities/String/translate';
import AuthorIndexPosterInfo from './AuthorIndexPosterInfo';
import styles from './AuthorIndexPoster.css';
@ -122,7 +123,7 @@ class AuthorIndexPoster extends Component {
<SpinnerIconButton
className={styles.action}
name={icons.REFRESH}
title="Refresh Author"
title={translate('RefreshAuthor')}
isSpinning={isRefreshingAuthor}
onPress={onRefreshAuthorPress}
/>
@ -132,7 +133,7 @@ class AuthorIndexPoster extends Component {
<SpinnerIconButton
className={styles.action}
name={icons.SEARCH}
title="Search for monitored books"
title={translate('SearchForMonitoredBooks')}
isSpinning={isSearchingAuthor}
onPress={onSearchPress}
/>
@ -141,7 +142,7 @@ class AuthorIndexPoster extends Component {
<IconButton
className={styles.action}
name={icons.EDIT}
title="Edit Author"
title={translate('EditAuthor')}
onPress={this.onEditAuthorPress}
/>
</Label>
@ -150,7 +151,7 @@ class AuthorIndexPoster extends Component {
status === 'ended' &&
<div
className={styles.ended}
title="Ended"
title={translate('Ended')}
/>
}

@ -11,6 +11,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
const posterSizeOptions = [
{ key: 'small', value: 'Small' },
@ -114,7 +115,9 @@ class AuthorIndexPosterOptionsModalContent extends Component {
<ModalBody>
<Form>
<FormGroup>
<FormLabel>Poster Size</FormLabel>
<FormLabel>
{translate('PosterSize')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@ -126,61 +129,71 @@ class AuthorIndexPosterOptionsModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Detailed Progress Bar</FormLabel>
<FormLabel>
{translate('DetailedProgressBar')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="detailedProgressBar"
value={detailedProgressBar}
helpText="Show text on progess bar"
helpText={translate('DetailedProgressBarHelpText')}
onChange={this.onChangePosterOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Name</FormLabel>
<FormLabel>
{translate('ShowName')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showTitle"
value={showTitle}
helpText="Show author name under poster"
helpText={translate('ShowTitleHelpText')}
onChange={this.onChangePosterOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Monitored</FormLabel>
<FormLabel>
{translate('ShowMonitored')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showMonitored"
value={showMonitored}
helpText="Show monitored status under poster"
helpText={translate('ShowMonitoredHelpText')}
onChange={this.onChangePosterOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Quality Profile</FormLabel>
<FormLabel>
{translate('ShowQualityProfile')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showQualityProfile"
value={showQualityProfile}
helpText="Show quality profile under poster"
helpText={translate('ShowQualityProfileHelpText')}
onChange={this.onChangePosterOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Search</FormLabel>
<FormLabel>
{translate('ShowSearch')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showSearchAction"
value={showSearchAction}
helpText="Show search button on hover"
helpText={translate('ShowSearchActionHelpText')}
onChange={this.onChangePosterOption}
/>
</FormGroup>

@ -3,6 +3,7 @@ import React from 'react';
import ProgressBar from 'Components/ProgressBar';
import { sizes } from 'Helpers/Props';
import getProgressBarKind from 'Utilities/Author/getProgressBarKind';
import translate from 'Utilities/String/translate';
import styles from './AuthorIndexProgressBar.css';
function AuthorIndexProgressBar(props) {
@ -28,7 +29,7 @@ function AuthorIndexProgressBar(props) {
size={detailedProgressBar ? sizes.MEDIUM : sizes.SMALL}
showText={detailedProgressBar}
text={text}
title={`${bookFileCount} / ${bookCount} (Total: ${totalBookCount})`}
title={translate('BookFileCountBookCountTotalTotalBookCountInterp', [bookFileCount, bookCount, totalBookCount])}
width={posterWidth}
/>
);

@ -6,6 +6,7 @@ import IconButton from 'Components/Link/IconButton';
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
class AuthorIndexActionsCell extends Component {
@ -65,14 +66,14 @@ class AuthorIndexActionsCell extends Component {
>
<SpinnerIconButton
name={icons.REFRESH}
title="Refresh Author"
title={translate('RefreshAuthor')}
isSpinning={isRefreshingAuthor}
onPress={onRefreshAuthorPress}
/>
<IconButton
name={icons.EDIT}
title="Edit Author"
title={translate('EditAuthor')}
onPress={this.onEditAuthorPress}
/>

@ -17,6 +17,7 @@ import TagListConnector from 'Components/TagListConnector';
import { icons } from 'Helpers/Props';
import getProgressBarKind from 'Utilities/Author/getProgressBarKind';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
import AuthorStatusCell from './AuthorStatusCell';
import hasGrowableColumns from './hasGrowableColumns';
import styles from './AuthorIndexRow.css';
@ -278,7 +279,7 @@ class AuthorIndexRow extends Component {
kind={getProgressBarKind(status, monitored, progress)}
showText={true}
text={`${bookFileCount} / ${bookCount}`}
title={`${bookFileCount} / ${bookCount} (Total: ${totalBookCount})`}
title={translate('BookFileCountBookCountTotalTotalBookCountInterp', [bookFileCount, bookCount, totalBookCount])}
width={125}
/>
</VirtualTableRowCell>
@ -356,7 +357,7 @@ class AuthorIndexRow extends Component {
>
<SpinnerIconButton
name={icons.REFRESH}
title="Refresh Author"
title={translate('RefreshAuthor')}
isSpinning={isRefreshingAuthor}
onPress={onRefreshAuthorPress}
/>
@ -366,7 +367,7 @@ class AuthorIndexRow extends Component {
<SpinnerIconButton
className={styles.action}
name={icons.SEARCH}
title="Search for monitored books"
title={translate('SearchForMonitoredBooks')}
isSpinning={isSearchingAuthor}
onPress={onSearchPress}
/>
@ -374,7 +375,7 @@ class AuthorIndexRow extends Component {
<IconButton
name={icons.EDIT}
title="Edit Author"
title={translate('EditAuthor')}
onPress={this.onEditAuthorPress}
/>
</VirtualTableRowCell>

@ -4,6 +4,7 @@ import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
class AuthorIndexTableOptions extends Component {
@ -64,25 +65,29 @@ class AuthorIndexTableOptions extends Component {
return (
<Fragment>
<FormGroup>
<FormLabel>Show Banners</FormLabel>
<FormLabel>
{translate('ShowBanners')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showBanners"
value={showBanners}
helpText="Show banners instead of names"
helpText={translate('ShowBannersHelpText')}
onChange={this.onTableOptionChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Search</FormLabel>
<FormLabel>
{translate('ShowSearch')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showSearchAction"
value={showSearchAction}
helpText="Show search button on hover"
helpText={translate('ShowSearchActionHelpText')}
onChange={this.onTableOptionChange}
/>
</FormGroup>

@ -3,6 +3,7 @@ import React from 'react';
import Icon from 'Components/Icon';
import VirtualTableRowCell from 'Components/Table/Cells/TableRowCell';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './AuthorStatusCell.css';
function AuthorStatusCell(props) {
@ -22,13 +23,13 @@ function AuthorStatusCell(props) {
<Icon
className={styles.statusIcon}
name={monitored ? icons.MONITORED : icons.UNMONITORED}
title={monitored ? 'Author is monitored' : 'Author is unmonitored'}
title={monitored ? translate('MonitoredAuthorIsMonitored') : translate('MonitoredAuthorIsUnmonitored')}
/>
<Icon
className={styles.statusIcon}
name={status === 'ended' ? icons.AUTHOR_ENDED : icons.AUTHOR_CONTINUING}
title={status === 'ended' ? 'Deceased' : 'Continuing'}
title={status === 'ended' ? translate('StatusEndedDeceased') : translate('StatusEndedContinuing')}
/>
</Component>
);

@ -10,6 +10,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds } from 'Helpers/Props';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
import styles from './DeleteBookModalContent.css';
class DeleteBookModalContent extends Component {
@ -91,13 +92,15 @@ class DeleteBookModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Add List Exclusion</FormLabel>
<FormLabel>
{translate('AddListExclusion')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="addImportListExclusion"
value={addImportListExclusion}
helpText="Prevent book from being added to Readarr by Import Lists or Author Refresh"
helpText={translate('AddImportListExclusionHelpText')}
kind={kinds.DANGER}
onChange={this.onAddImportListExclusionChange}
/>
@ -106,14 +109,18 @@ class DeleteBookModalContent extends Component {
{
!addImportListExclusion &&
<div className={styles.deleteFilesMessage}>
<div>If you don't add an import list exclusion and the author has a metadata profile other than 'None' then this book may be re-added during the next author refresh.</div>
<div>
{translate('IfYouDontAddAnImportListExclusionAndTheAuthorHasAMetadataProfileOtherThanNoneThenThisBookMayBeReaddedDuringTheNextAuthorRefresh')}
</div>
</div>
}
{
deleteFiles &&
<div className={styles.deleteFilesMessage}>
<div>The book's files will be deleted.</div>
<div>
{translate('TheBooksFilesWillBeDeleted')}
</div>
{
!!bookFileCount &&

@ -19,6 +19,7 @@ import InteractiveSearchFilterMenuConnector from 'InteractiveSearch/InteractiveS
import InteractiveSearchTable from 'InteractiveSearch/InteractiveSearchTable';
import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector';
import RetagPreviewModalConnector from 'Retag/RetagPreviewModalConnector';
import translate from 'Utilities/String/translate';
import BookDetailsHeaderConnector from './BookDetailsHeaderConnector';
import styles from './BookDetails.css';
@ -114,16 +115,16 @@ class BookDetails extends Component {
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label="Refresh"
label={translate('Refresh')}
iconName={icons.REFRESH}
spinningName={icons.REFRESH}
title="Refresh information"
title={translate('RefreshInformation')}
isSpinning={isRefreshing}
onPress={onRefreshPress}
/>
<PageToolbarButton
label="Search Book"
label={translate('SearchBook')}
iconName={icons.SEARCH}
isSpinning={isSearching}
onPress={onSearchPress}
@ -132,14 +133,14 @@ class BookDetails extends Component {
<PageToolbarSeparator />
<PageToolbarButton
label="Preview Rename"
label={translate('PreviewRename')}
iconName={icons.ORGANIZE}
isDisabled={!hasBookFiles}
onPress={this.onOrganizePress}
/>
<PageToolbarButton
label="Preview Retag"
label={translate('PreviewRetag')}
iconName={icons.RETAG}
isDisabled={!hasBookFiles}
onPress={this.onRetagPress}
@ -148,13 +149,13 @@ class BookDetails extends Component {
<PageToolbarSeparator />
<PageToolbarButton
label="Edit"
label={translate('Edit')}
iconName={icons.EDIT}
onPress={this.onEditBookPress}
/>
<PageToolbarButton
label="Delete"
label={translate('Delete')}
iconName={icons.DELETE}
onPress={this.onDeleteBookPress}
/>
@ -194,7 +195,7 @@ class BookDetails extends Component {
className={styles.bookNavigationButton}
name={icons.ARROW_LEFT}
size={30}
title={`Go to ${previousBook.title}`}
title={translate('GoToInterp', [previousBook.title])}
to={`/book/${previousBook.titleSlug}`}
/>
@ -202,7 +203,7 @@ class BookDetails extends Component {
className={styles.bookUpButton}
name={icons.ARROW_UP}
size={30}
title={`Go to ${author.authorName}`}
title={translate('GoToInterp', [author.authorName])}
to={`/author/${author.titleSlug}`}
/>
@ -210,7 +211,7 @@ class BookDetails extends Component {
className={styles.bookNavigationButton}
name={icons.ARROW_RIGHT}
size={30}
title={`Go to ${nextBook.title}`}
title={translate('GoToInterp', [nextBook.title])}
to={`/book/${nextBook.titleSlug}`}
/>
</div>
@ -224,7 +225,9 @@ class BookDetails extends Component {
{
!isFetching && bookFilesError &&
<div>Loading book files failed</div>
<div>
{translate('LoadingBookFilesFailed')}
</div>
}
<Tabs selectedIndex={this.state.tabIndex} onSelect={this.onTabSelect}>

@ -9,6 +9,7 @@ import NotFound from 'Components/NotFound';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import { clearBooks, fetchBooks } from 'Store/Actions/bookActions';
import translate from 'Utilities/String/translate';
import BookDetailsConnector from './BookDetailsConnector';
function createMapStateToProps() {
@ -94,7 +95,7 @@ class BookDetailsPageConnector extends Component {
if (!titleSlug) {
return (
<NotFound
message="Sorry, that book cannot be found."
message={translate('SorryThatBookCannotBeFound')}
/>
);
}

@ -11,6 +11,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
class EditBookModalContent extends Component {
@ -60,36 +61,42 @@ class EditBookModalContent extends Component {
{...otherProps}
>
<FormGroup>
<FormLabel>Monitored</FormLabel>
<FormLabel>
{translate('Monitored')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="monitored"
helpText="Readarr will search for and download book"
helpText={translate('MonitoredHelpText')}
{...monitored}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Automatically Switch Edition</FormLabel>
<FormLabel>
{translate('AutomaticallySwitchEdition')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="anyEditionOk"
helpText="Readarr will automatically switch to the edition best matching downloaded files"
helpText={translate('AnyEditionOkHelpText')}
{...anyEditionOk}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Edition</FormLabel>
<FormLabel>
{translate('Edition')}
</FormLabel>
<FormInputGroup
type={inputTypes.BOOK_EDITION_SELECT}
name="editions"
helpText="Change edition for this book"
helpText={translate('EditionsHelpText')}
isDisabled={anyEditionOk.value && hasFile}
bookEditions={editions}
onChange={onInputChange}

@ -3,6 +3,7 @@ import React from 'react';
import Icon from 'Components/Icon';
import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import SceneInfo from './SceneInfo';
import styles from './EpisodeNumber.css';
@ -40,7 +41,7 @@ function EpisodeNumber(props) {
}
</span>
}
title="Scene Information"
title={translate('SceneInformation')}
body={
<SceneInfo
sceneSeasonNumber={sceneSeasonNumber}
@ -70,7 +71,7 @@ function EpisodeNumber(props) {
className={styles.warning}
name={icons.WARNING}
kind={kinds.WARNING}
title="Scene number hasn't been verified yet"
title={translate('SceneNumberHasntBeenVerifiedYet')}
/>
}
@ -80,7 +81,7 @@ function EpisodeNumber(props) {
className={styles.warning}
name={icons.WARNING}
kind={kinds.WARNING}
title="Episode does not have an absolute episode number"
title={translate('EpisodeDoesNotHaveAnAbsoluteEpisodeNumber')}
/>
}
</span>

@ -5,6 +5,7 @@ import Icon from 'Components/Icon';
import ProgressBar from 'Components/ProgressBar';
import { icons, kinds, sizes } from 'Helpers/Props';
import isBefore from 'Utilities/Date/isBefore';
import translate from 'Utilities/String/translate';
import BookQuality from './BookQuality';
import styles from './EpisodeStatus.css';
@ -35,7 +36,7 @@ function EpisodeStatus(props) {
{...queueItem}
progressBar={
<ProgressBar
title={`Book is downloading - ${progress.toFixed(1)}% ${queueItem.title}`}
title={translate('BookIsDownloadingInterp', [progress.toFixed(1), queueItem.title])}
progress={progress}
kind={kinds.PURPLE}
size={sizes.MEDIUM}
@ -51,7 +52,7 @@ function EpisodeStatus(props) {
<div className={styles.center}>
<Icon
name={icons.DOWNLOADING}
title="Book is downloading"
title={translate('BookIsDownloading')}
/>
</div>
);
@ -67,7 +68,7 @@ function EpisodeStatus(props) {
quality={quality}
size={bookFile.size}
isCutoffNotMet={isCutoffNotMet}
title="Book Downloaded"
title={translate('BookDownloaded')}
/>
</div>
);
@ -78,7 +79,7 @@ function EpisodeStatus(props) {
<div className={styles.center}>
<Icon
name={icons.TBA}
title="TBA"
title={translate('TBA')}
/>
</div>
);
@ -89,7 +90,7 @@ function EpisodeStatus(props) {
<div className={styles.center}>
<Icon
name={icons.UNMONITORED}
title="Book is not monitored"
title={translate('BookIsNotMonitored')}
/>
</div>
);
@ -100,7 +101,7 @@ function EpisodeStatus(props) {
<div className={styles.center}>
<Icon
name={icons.MISSING}
title="Book missing from disk"
title={translate('BookMissingFromDisk')}
/>
</div>
);
@ -110,7 +111,7 @@ function EpisodeStatus(props) {
<div className={styles.center}>
<Icon
name={icons.NOT_AIRED}
title="Book has not aired"
title={translate('BookHasNotAired')}
/>
</div>
);

@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
import translate from 'Utilities/String/translate';
import styles from './SceneInfo.css';
function SceneInfo(props) {
@ -20,7 +21,7 @@ function SceneInfo(props) {
<DescriptionListItem
titleClassName={styles.title}
descriptionClassName={styles.description}
title="Season"
title={translate('Season')}
data={sceneSeasonNumber}
/>
}
@ -30,7 +31,7 @@ function SceneInfo(props) {
<DescriptionListItem
titleClassName={styles.title}
descriptionClassName={styles.description}
title="Episode"
title={translate('Episode')}
data={sceneEpisodeNumber}
/>
}
@ -40,7 +41,7 @@ function SceneInfo(props) {
<DescriptionListItem
titleClassName={styles.title}
descriptionClassName={styles.description}
title="Absolute"
title={translate('Absolute')}
data={sceneAbsoluteEpisodeNumber}
/>
}
@ -50,7 +51,7 @@ function SceneInfo(props) {
<DescriptionListItem
titleClassName={styles.title}
descriptionClassName={styles.description}
title={alternateTitles.length === 1 ? 'Title' : 'Titles'}
title={alternateTitles.length === 1 ? translate('AlternateTitleslength1Title') : translate('AlternateTitleslength1Titles')}
data={
<div>
{

@ -5,6 +5,7 @@ import IconButton from 'Components/Link/IconButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './BookFileActionsCell.css';
class BookFileActionsCell extends Component {
@ -86,9 +87,9 @@ class BookFileActionsCell extends Component {
<ConfirmModal
isOpen={isConfirmDeleteModalOpen}
kind={kinds.DANGER}
title="Delete Book File"
message={`Are you sure you want to delete ${path}?`}
confirmLabel="Delete"
title={translate('DeleteBookFile')}
message={translate('DeleteBookFileMessageText', [path])}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDelete}
onCancel={this.onConfirmDeleteModalClose}
/>

@ -9,6 +9,7 @@ import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { kinds } from 'Helpers/Props';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
import selectAll from 'Utilities/Table/selectAll';
@ -218,9 +219,9 @@ class BookFileEditorTableContent extends Component {
<ConfirmModal
isOpen={isConfirmDeleteModalOpen}
kind={kinds.DANGER}
title="Delete Selected Book Files"
message={'Are you sure you want to delete the selected book files?'}
confirmLabel="Delete"
title={translate('DeleteSelectedBookFiles')}
message={translate('DeleteSelectedBookFilesMessageText')}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDelete}
onCancel={this.onConfirmDeleteModalClose}
/>

@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Icon from 'Components/Icon';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import FileDetails from './FileDetails';
import styles from './ExpandingFileDetails.css';
@ -55,7 +56,7 @@ class ExpandingFileDetails extends Component {
<Icon
className={styles.expandButtonIcon}
name={isExpanded ? icons.COLLAPSE : icons.EXPAND}
title={isExpanded ? 'Hide file info' : 'Show file info'}
title={isExpanded ? translate('IsExpandedHideFileInfo') : translate('IsExpandedShowFileInfo')}
size={24}
/>
</div>

@ -7,6 +7,7 @@ import DescriptionListItemDescription from 'Components/DescriptionList/Descripti
import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionListItemTitle';
import Link from 'Components/Link/Link';
import stripHtml from 'Utilities/String/stripHtml';
import translate from 'Utilities/String/translate';
import styles from './FileDetails.css';
function renderRejections(rejections) {
@ -43,7 +44,7 @@ function FileDetails(props) {
{
filename &&
<DescriptionListItem
title="Filename"
title={translate('Filename')}
data={filename}
descriptionClassName={styles.filename}
/>
@ -51,119 +52,119 @@ function FileDetails(props) {
{
audioTags.title !== undefined &&
<DescriptionListItem
title="Track Title"
title={translate('TrackTitle')}
data={audioTags.title}
/>
}
{
audioTags.trackNumbers[0] > 0 &&
<DescriptionListItem
title="Track Number"
title={translate('TrackNumber')}
data={audioTags.trackNumbers[0]}
/>
}
{
audioTags.discNumber > 0 &&
<DescriptionListItem
title="Disc Number"
title={translate('DiscNumber')}
data={audioTags.discNumber}
/>
}
{
audioTags.discCount > 0 &&
<DescriptionListItem
title="Disc Count"
title={translate('DiscCount')}
data={audioTags.discCount}
/>
}
{
audioTags.bookTitle !== undefined &&
<DescriptionListItem
title="Book"
title={translate('Book')}
data={audioTags.bookTitle}
/>
}
{
audioTags.authorTitle !== undefined &&
<DescriptionListItem
title="Author"
title={translate('Author')}
data={audioTags.authorTitle}
/>
}
{
audioTags.seriesTitle !== undefined &&
<DescriptionListItem
title="Series"
title={translate('Series')}
data={audioTags.seriesTitle}
/>
}
{
audioTags.seriesIndex !== undefined &&
<DescriptionListItem
title="Series Number"
title={translate('SeriesNumber')}
data={audioTags.seriesIndex}
/>
}
{
audioTags.country !== undefined &&
<DescriptionListItem
title="Country"
title={translate('Country')}
data={audioTags.country.name}
/>
}
{
audioTags.language !== undefined && audioTags.language !== 'UND' &&
<DescriptionListItem
title="Language"
title={translate('Language')}
data={audioTags.language}
/>
}
{
audioTags.year > 0 &&
<DescriptionListItem
title="Year"
title={translate('Year')}
data={audioTags.year}
/>
}
{
audioTags.label !== undefined &&
<DescriptionListItem
title="Label"
title={translate('Label')}
data={audioTags.label}
/>
}
{
audioTags.publisher !== undefined &&
<DescriptionListItem
title="Publisher"
title={translate('Publisher')}
data={audioTags.publisher}
/>
}
{
audioTags.catalogNumber !== undefined &&
<DescriptionListItem
title="Catalog Number"
title={translate('CatalogNumber')}
data={audioTags.catalogNumber}
/>
}
{
audioTags.disambiguation !== undefined &&
<DescriptionListItem
title="Overview"
title={translate('Overview')}
data={stripHtml(audioTags.disambiguation)}
/>
}
{
audioTags.isbn !== undefined &&
<DescriptionListItem
title="ISBN"
title={translate('ISBN')}
data={audioTags.isbn}
/>
}
{
audioTags.asin !== undefined &&
<DescriptionListItem
title="ASIN"
title={translate('ASIN')}
data={audioTags.asin}
/>
} {
@ -172,7 +173,7 @@ function FileDetails(props) {
to={`https://musicbrainz.org/author/${audioTags.authorMBId}`}
>
<DescriptionListItem
title="MusicBrainz Author ID"
title={translate('MusicBrainzAuthorID')}
data={audioTags.authorMBId}
/>
</Link>
@ -183,7 +184,7 @@ function FileDetails(props) {
to={`https://musicbrainz.org/release-group/${audioTags.bookMBId}`}
>
<DescriptionListItem
title="MusicBrainz Book ID"
title={translate('MusicBrainzBookID')}
data={audioTags.bookMBId}
/>
</Link>
@ -194,7 +195,7 @@ function FileDetails(props) {
to={`https://musicbrainz.org/release/${audioTags.releaseMBId}`}
>
<DescriptionListItem
title="MusicBrainz Release ID"
title={translate('MusicBrainzReleaseID')}
data={audioTags.releaseMBId}
/>
</Link>
@ -205,7 +206,7 @@ function FileDetails(props) {
to={`https://musicbrainz.org/recording/${audioTags.recordingMBId}`}
>
<DescriptionListItem
title="MusicBrainz Recording ID"
title={translate('MusicBrainzRecordingID')}
data={audioTags.recordingMBId}
/>
</Link>
@ -216,7 +217,7 @@ function FileDetails(props) {
to={`https://musicbrainz.org/track/${audioTags.trackMBId}`}
>
<DescriptionListItem
title="MusicBrainz Track ID"
title={translate('MusicBrainzTrackID')}
data={audioTags.trackMBId}
/>
</Link>

@ -15,6 +15,7 @@ import VirtualTableRow from 'Components/Table/VirtualTableRow';
import { align, sortDirections } from 'Helpers/Props';
import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
@ -347,7 +348,7 @@ class Bookshelf extends Component {
} = this.state;
return (
<PageContent title="Book Studio">
<PageContent title={translate('BookStudio')}>
<PageToolbar>
<PageToolbarSection />
<PageToolbarSection alignContent={align.RIGHT}>

@ -2,6 +2,7 @@ import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import MonitorToggleButton from 'Components/MonitorToggleButton';
import translate from 'Utilities/String/translate';
import styles from './BookshelfBook.css';
class BookshelfBook extends Component {
@ -58,7 +59,7 @@ class BookshelfBook extends Component {
percentOfBooks < 100 && monitored && styles.missingWanted,
percentOfBooks === 100 && styles.allBooks
)}
title={`${bookFileCount}/${totalBookCount} books downloaded`}
title={translate('BookFileCounttotalBookCountBooksDownloadedInterp', [bookFileCount, totalBookCount])}
>
{
totalBookCount === 0 ? '0/0' : `${bookFileCount}/${totalBookCount}`

@ -6,6 +6,7 @@ import MonitorToggleButton from 'Components/MonitorToggleButton';
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import BookshelfBook from './BookshelfBook';
import styles from './BookshelfRow.css';
@ -52,7 +53,7 @@ class BookshelfRow extends Component {
<Icon
className={styles.statusIcon}
name={status === 'ended' ? icons.AUTHOR_ENDED : icons.AUTHOR_CONTINUING}
title={status === 'ended' ? 'Ended' : 'Continuing'}
title={status === 'ended' ? translate('StatusEndedEnded') : translate('StatusEndedContinuing')}
/>
</VirtualTableRowCell>

@ -8,6 +8,7 @@ import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import { icons } from 'Helpers/Props';
import formatTime from 'Utilities/Date/formatTime';
import translate from 'Utilities/String/translate';
import styles from './AgendaEvent.css';
class AgendaEvent extends Component {
@ -110,7 +111,7 @@ class AgendaEvent extends Component {
!queueItem && grabbed &&
<Icon
name={icons.DOWNLOADING}
title="Book is downloading"
title={translate('BookIsDownloading')}
/>
}
</Link>

@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import translate from 'Utilities/String/translate';
import AgendaConnector from './Agenda/AgendaConnector';
import * as calendarViews from './calendarViews';
import CalendarDaysConnector from './Day/CalendarDaysConnector';
@ -30,7 +31,9 @@ class Calendar extends Component {
{
!isFetching && !!error &&
<div>Unable to load the calendar</div>
<div>
{translate('UnableToLoadTheCalendar')}
</div>
}
{

@ -11,6 +11,7 @@ import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import { align, icons } from 'Helpers/Props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import CalendarConnector from './CalendarConnector';
import CalendarLinkModal from './iCal/CalendarLinkModal';
import LegendConnector from './Legend/LegendConnector';
@ -94,17 +95,17 @@ class CalendarPage extends Component {
const isMeasured = this.state.width > 0;
return (
<PageContent title="Calendar">
<PageContent title={translate('Calendar')}>
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label="iCal Link"
label={translate('ICalLink')}
iconName={icons.CALENDAR}
onPress={this.onGetCalendarLinkPress}
/>
<PageToolbarButton
label="Search for Missing"
label={translate('SearchForMissing')}
iconName={icons.SEARCH}
isDisabled={!missingBookIds.length}
isSpinning={isSearchingForMissing}
@ -114,7 +115,7 @@ class CalendarPage extends Component {
<PageToolbarSection alignContent={align.RIGHT}>
<PageToolbarButton
label="Options"
label={translate('Options')}
iconName={icons.POSTER}
onPress={this.onOptionsPress}
/>

@ -6,6 +6,7 @@ import getStatusStyle from 'Calendar/getStatusStyle';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import CalendarEventQueueDetails from './CalendarEventQueueDetails';
import styles from './CalendarEvent.css';
@ -97,7 +98,7 @@ class CalendarEvent extends Component {
<Icon
className={styles.statusIcon}
name={icons.DOWNLOADING}
title="Book is downloading"
title={translate('BookIsDownloading')}
/>
}
</div>

@ -3,6 +3,7 @@ import React from 'react';
import QueueDetails from 'Activity/Queue/QueueDetails';
import CircularProgressBar from 'Components/CircularProgressBar';
import colors from 'Styles/Variables/colors';
import translate from 'Utilities/String/translate';
function CalendarEventQueueDetails(props) {
const {
@ -25,7 +26,7 @@ function CalendarEventQueueDetails(props) {
status={status}
errorMessage={errorMessage}
progressBar={
<div title={`Book is downloading - ${progress.toFixed(1)}% ${title}`}>
<div title={translate('BookIsDownloadingInterp', [progress.toFixed(1), title])}>
<CircularProgressBar
progress={progress}
size={20}

@ -12,6 +12,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes } from 'Helpers/Props';
import { firstDayOfWeekOptions, timeFormatOptions, weekColumnOptions } from 'Settings/UI/UISettings';
import translate from 'Utilities/String/translate';
class CalendarOptionsModalContent extends Component {
@ -110,38 +111,44 @@ class CalendarOptionsModalContent extends Component {
</ModalHeader>
<ModalBody>
<FieldSet legend="Local">
<FieldSet legend={translate('Local')}>
<Form>
<FormGroup>
<FormLabel>Collapse Multiple Books</FormLabel>
<FormLabel>
{translate('CollapseMultipleBooks')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="collapseMultipleBooks"
value={collapseMultipleBooks}
helpText="Collapse multiple books releasing on the same day"
helpText={translate('CollapseMultipleBooksHelpText')}
onChange={this.onOptionInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Icon for Cutoff Unmet</FormLabel>
<FormLabel>
{translate('IconForCutoffUnmet')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showCutoffUnmetIcon"
value={showCutoffUnmetIcon}
helpText="Show icon for files when the cutoff hasn't been met"
helpText={translate('ShowCutoffUnmetIconHelpText')}
onChange={this.onOptionInputChange}
/>
</FormGroup>
</Form>
</FieldSet>
<FieldSet legend="Global">
<FieldSet legend={translate('Global')}>
<Form>
<FormGroup>
<FormLabel>First Day of Week</FormLabel>
<FormLabel>
{translate('FirstDayOfWeek')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@ -153,7 +160,9 @@ class CalendarOptionsModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Week Column Header</FormLabel>
<FormLabel>
{translate('WeekColumnHeader')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@ -161,12 +170,14 @@ class CalendarOptionsModalContent extends Component {
values={weekColumnOptions}
value={calendarWeekColumnHeader}
onChange={this.onGlobalInputChange}
helpText="Shown above each column when week is the active view"
helpText={translate('ShownAboveEachColumnWhenWeekIsTheActiveView')}
/>
</FormGroup>
<FormGroup>
<FormLabel>Time Format</FormLabel>
<FormLabel>
{translate('TimeFormat')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@ -176,13 +187,15 @@ class CalendarOptionsModalContent extends Component {
onChange={this.onGlobalInputChange}
/>
</FormGroup><FormGroup>
<FormLabel>Enable Color-Impaired Mode</FormLabel>
<FormLabel>
{translate('EnableColorImpairedMode')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="enableColorImpairedMode"
value={enableColorImpairedMode}
helpText="Altered style to allow color-impaired users to better distinguish color coded information"
helpText={translate('EnableColorImpairedModeHelpText')}
onChange={this.onGlobalInputChange}
/>
</FormGroup>

@ -13,6 +13,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { icons, inputTypes, kinds, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
function getUrls(state) {
const {
@ -113,49 +114,57 @@ class CalendarLinkModalContent extends Component {
<ModalBody>
<Form>
<FormGroup>
<FormLabel>Include Unmonitored</FormLabel>
<FormLabel>
{translate('IncludeUnmonitored')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="unmonitored"
value={unmonitored}
helpText="Include unmonitored books in the iCal feed"
helpText={translate('UnmonitoredHelpText')}
onChange={this.onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Past Days</FormLabel>
<FormLabel>
{translate('PastDays')}
</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="pastDays"
value={pastDays}
helpText="Days for iCal feed to look into the past"
helpText={translate('PastDaysHelpText')}
onChange={this.onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Future Days</FormLabel>
<FormLabel>
{translate('FutureDays')}
</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="futureDays"
value={futureDays}
helpText="Days for iCal feed to look into the future"
helpText={translate('FutureDaysHelpText')}
onChange={this.onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Tags</FormLabel>
<FormLabel>
{translate('Tags')}
</FormLabel>
<FormInputGroup
type={inputTypes.TAG}
name="tags"
value={tags}
helpText="Feed will only contain authors with at least one matching tag"
helpText={translate('TagsHelpText')}
onChange={this.onInputChange}
/>
</FormGroup>
@ -163,14 +172,16 @@ class CalendarLinkModalContent extends Component {
<FormGroup
size={sizes.LARGE}
>
<FormLabel>iCal Feed</FormLabel>
<FormLabel>
{translate('ICalFeed')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="iCalHttpUrl"
value={iCalHttpUrl}
readOnly={true}
helpText="Copy this URL to your client(s) or click to subscribe if your browser supports webcal"
helpText={translate('ICalHttpUrlHelpText')}
buttons={[
<ClipboardButton
key="copy"

@ -14,6 +14,7 @@ import Scroller from 'Components/Scroller/Scroller';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { kinds, scrollDirections } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import FileBrowserRow from './FileBrowserRow';
import styles from './FileBrowserModalContent.css';
@ -134,7 +135,7 @@ class FileBrowserModalContent extends Component {
<PathInput
className={styles.pathInput}
placeholder="Start typing or select a path below"
placeholder={translate('StartTypingOrSelectAPathBelow')}
hasFileBrowser={false}
{...otherProps}
value={this.state.currentPath}
@ -148,7 +149,9 @@ class FileBrowserModalContent extends Component {
>
{
!!error &&
<div>Error loading contents</div>
<div>
{translate('ErrorLoadingContents')}
</div>
}
{

@ -3,6 +3,7 @@ import React, { Component } from 'react';
import IconButton from 'Components/Link/IconButton';
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './CustomFilter.css';
class CustomFilter extends Component {
@ -89,7 +90,7 @@ class CustomFilter extends Component {
/>
<SpinnerIconButton
title="Remove filter"
title={translate('RemoveFilter')}
name={icons.REMOVE}
isSpinning={this.state.isDeleting}
onPress={this.onRemovePress}

@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import Link from 'Components/Link/Link';
import { inputTypes } from 'Helpers/Props';
// import translate from 'Utilities/String/translate';
import AutoCompleteInput from './AutoCompleteInput';
import BookEditionSelectInputConnector from './BookEditionSelectInputConnector';
import BookshelfInputConnector from './BookshelfInputConnector';
@ -172,7 +173,7 @@ function FormInputGroup(props) {
<Icon
name={icons.UNSAVED_SETTING}
className={styles.pendingChangesIcon}
title="Change has not been saved yet"
title={translate('ChangeHasNotBeenSavedYet')}
/>
}
</div> */}

@ -1,11 +1,12 @@
import PropTypes from 'prop-types';
import React from 'react';
import PageContent from 'Components/Page/PageContent';
import translate from 'Utilities/String/translate';
import styles from './NotFound.css';
function NotFound({ message }) {
return (
<PageContent title="MIA">
<PageContent title={translate('MIA')}>
<div className={styles.container}>
<div className={styles.message}>
{message}

@ -6,7 +6,7 @@ import { createSelector } from 'reselect';
import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
import { fetchAuthor } from 'Store/Actions/authorActions';
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
import { fetchImportLists, fetchMetadataProfiles, fetchQualityProfiles, fetchUISettings } from 'Store/Actions/settingsActions';
import { fetchImportLists, fetchLanguages, fetchMetadataProfiles, fetchQualityProfiles, fetchUISettings } from 'Store/Actions/settingsActions';
import { fetchStatus } from 'Store/Actions/systemActions';
import { fetchTags } from 'Store/Actions/tagActions';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
@ -46,6 +46,7 @@ const selectIsPopulated = createSelector(
(state) => state.customFilters.isPopulated,
(state) => state.tags.isPopulated,
(state) => state.settings.ui.isPopulated,
(state) => state.settings.languages.isPopulated,
(state) => state.settings.qualityProfiles.isPopulated,
(state) => state.settings.metadataProfiles.isPopulated,
(state) => state.settings.importLists.isPopulated,
@ -54,6 +55,7 @@ const selectIsPopulated = createSelector(
customFiltersIsPopulated,
tagsIsPopulated,
uiSettingsIsPopulated,
languagesIsPopulated,
qualityProfilesIsPopulated,
metadataProfilesIsPopulated,
importListsIsPopulated,
@ -63,6 +65,7 @@ const selectIsPopulated = createSelector(
customFiltersIsPopulated &&
tagsIsPopulated &&
uiSettingsIsPopulated &&
languagesIsPopulated &&
qualityProfilesIsPopulated &&
metadataProfilesIsPopulated &&
importListsIsPopulated &&
@ -75,6 +78,7 @@ const selectErrors = createSelector(
(state) => state.customFilters.error,
(state) => state.tags.error,
(state) => state.settings.ui.error,
(state) => state.settings.languages.error,
(state) => state.settings.qualityProfiles.error,
(state) => state.settings.metadataProfiles.error,
(state) => state.settings.importLists.error,
@ -83,6 +87,7 @@ const selectErrors = createSelector(
customFiltersError,
tagsError,
uiSettingsError,
languagesError,
qualityProfilesError,
metadataProfilesError,
importListsError,
@ -92,6 +97,7 @@ const selectErrors = createSelector(
customFiltersError ||
tagsError ||
uiSettingsError ||
languagesError ||
qualityProfilesError ||
metadataProfilesError ||
importListsError ||
@ -103,6 +109,7 @@ const selectErrors = createSelector(
customFiltersError,
tagsError,
uiSettingsError,
languagesError,
qualityProfilesError,
metadataProfilesError,
importListsError,
@ -147,6 +154,9 @@ function createMapDispatchToProps(dispatch, props) {
dispatchFetchTags() {
dispatch(fetchTags());
},
dispatchFetchLanguages() {
dispatch(fetchLanguages());
},
dispatchFetchQualityProfiles() {
dispatch(fetchQualityProfiles());
},
@ -189,6 +199,7 @@ class PageConnector extends Component {
this.props.dispatchFetchAuthor();
this.props.dispatchFetchCustomFilters();
this.props.dispatchFetchTags();
this.props.dispatchFetchLanguages();
this.props.dispatchFetchQualityProfiles();
this.props.dispatchFetchMetadataProfiles();
this.props.dispatchFetchImportLists();
@ -213,6 +224,7 @@ class PageConnector extends Component {
hasError,
dispatchFetchAuthor,
dispatchFetchTags,
dispatchFetchLanguages,
dispatchFetchQualityProfiles,
dispatchFetchMetadataProfiles,
dispatchFetchImportLists,
@ -252,6 +264,7 @@ PageConnector.propTypes = {
dispatchFetchAuthor: PropTypes.func.isRequired,
dispatchFetchCustomFilters: PropTypes.func.isRequired,
dispatchFetchTags: PropTypes.func.isRequired,
dispatchFetchLanguages: PropTypes.func.isRequired,
dispatchFetchQualityProfiles: PropTypes.func.isRequired,
dispatchFetchMetadataProfiles: PropTypes.func.isRequired,
dispatchFetchImportLists: PropTypes.func.isRequired,

@ -15,6 +15,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import TableOptionsColumn from './TableOptionsColumn';
import TableOptionsColumnDragPreview from './TableOptionsColumnDragPreview';
import TableOptionsColumnDragSource from './TableOptionsColumnDragSource';
@ -144,13 +145,15 @@ class TableOptionsModal extends Component {
{
hasPageSize ?
<FormGroup>
<FormLabel>Page Size</FormLabel>
<FormLabel>
{translate('PageSize')}
</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="pageSize"
value={pageSize || 0}
helpText="Number of items to show on each page"
helpText={translate('PageSizeHelpText')}
errors={pageSizeError ? [{ message: pageSizeError }] : undefined}
onChange={this.onPageSizeChange}
/>
@ -168,7 +171,9 @@ class TableOptionsModal extends Component {
{
canModifyColumns ?
<FormGroup>
<FormLabel>Columns</FormLabel>
<FormLabel>
{translate('Columns')}
</FormLabel>
<div>
<FormInputHelpText

@ -8,6 +8,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import Scroller from 'Components/Scroller/Scroller';
import { scrollDirections } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import SelectAuthorRow from './SelectAuthorRow';
import styles from './SelectAuthorModalContent.css';
@ -55,7 +56,7 @@ class SelectAuthorModalContent extends Component {
>
<TextInput
className={styles.filterInput}
placeholder="Filter author"
placeholder={translate('FilterPlaceHolder')}
name="filter"
value={filter}
autoFocus={true}

@ -11,6 +11,7 @@ import Scroller from 'Components/Scroller/Scroller';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { scrollDirections } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import SelectBookRow from './SelectBookRow';
import styles from './SelectBookModalContent.css';
@ -84,7 +85,7 @@ class SelectBookModalContent extends Component {
}
<TextInput
className={styles.filterInput}
placeholder="Filter book"
placeholder={translate('FilterPlaceHolder')}
name="filter"
value={filter}
autoFocus={true}

@ -5,6 +5,7 @@ import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellCo
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import { kinds, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './SelectBookRow.css';
function getBookCountKind(monitored, bookFileCount, bookCount) {
@ -85,7 +86,7 @@ class SelectBookRow extends Component {
key={name}
>
<Label
title={`${totalBookCount} books total. ${bookFileCount} books with files.`}
title={translate('TotalBookCountBooksTotalBookFileCountBooksWithFilesInterp', [totalBookCount, bookFileCount])}
kind={getBookCountKind(monitored, bookFileCount, bookCount)}
size={sizes.MEDIUM}
>

@ -5,6 +5,7 @@ import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellCo
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRowButton from 'Components/Table/TableRowButton';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './RecentFolderRow.css';
class RecentFolderRow extends Component {
@ -44,7 +45,7 @@ class RecentFolderRow extends Component {
<TableRowCell className={styles.actions}>
<IconButton
title="Remove"
title={translate('Remove')}
name={icons.REMOVE}
onPress={this.onRemovePress}
/>

@ -15,6 +15,7 @@ import SelectAuthorModal from 'InteractiveImport/Author/SelectAuthorModal';
import SelectBookModal from 'InteractiveImport/Book/SelectBookModal';
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
import InteractiveImportRowCellPlaceholder from './InteractiveImportRowCellPlaceholder';
import styles from './InteractiveImportRow.css';
@ -211,7 +212,7 @@ class InteractiveImportRow extends Component {
<TableRowCellButton
isDisabled={!allowAuthorChange}
title={allowAuthorChange ? 'Click to change author' : undefined}
title={allowAuthorChange ? translate('AllowAuthorChangeClickToChangeAuthor') : undefined}
onPress={this.onSelectAuthorPress}
>
{
@ -221,7 +222,7 @@ class InteractiveImportRow extends Component {
<TableRowCellButton
isDisabled={!author}
title={author ? 'Click to change book' : undefined}
title={author ? translate('AuthorClickToChangeBook') : undefined}
onPress={this.onSelectBookPress}
>
{
@ -231,7 +232,7 @@ class InteractiveImportRow extends Component {
<TableRowCellButton
className={styles.quality}
title="Click to change quality"
title={translate('ClickToChangeQuality')}
onPress={this.onSelectQualityPress}
>
{
@ -262,7 +263,7 @@ class InteractiveImportRow extends Component {
kind={kinds.DANGER}
/>
}
title="Release Rejected"
title={translate('ReleaseRejected')}
body={
<ul>
{
@ -284,12 +285,12 @@ class InteractiveImportRow extends Component {
<ConfirmModal
isOpen={isDetailsModalOpen}
title="File Details"
title={translate('FileDetails')}
message={fileDetails}
size={sizes.LARGE}
kind={kinds.DEFAULT}
hideCancelButton={true}
confirmLabel="Close"
confirmLabel={translate('Close')}
onConfirm={this.onDetailsModalClose}
onCancel={this.onDetailsModalClose}
/>

@ -11,6 +11,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
class SelectQualityModalContent extends Component {
@ -91,14 +92,18 @@ class SelectQualityModalContent extends Component {
{
!isFetching && !!error &&
<div>Unable to load qualities</div>
<div>
{translate('UnableToLoadQualities')}
</div>
}
{
isPopulated && !error &&
<Form>
<FormGroup>
<FormLabel>Quality</FormLabel>
<FormLabel>
{translate('Quality')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@ -110,7 +115,9 @@ class SelectQualityModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Proper</FormLabel>
<FormLabel>
{translate('Proper')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@ -121,7 +128,9 @@ class SelectQualityModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Real</FormLabel>
<FormLabel>
{translate('Real')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}

@ -13,6 +13,7 @@ import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import formatDateTime from 'Utilities/Date/formatDateTime';
import formatAge from 'Utilities/Number/formatAge';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
import Peers from './Peers';
import styles from './InteractiveSearchRow.css';
@ -179,7 +180,7 @@ class InteractiveSearchRow extends Component {
kind={kinds.DANGER}
/>
}
title="Release Rejected"
title={translate('ReleaseRejected')}
body={
<ul>
{
@ -213,9 +214,9 @@ class InteractiveSearchRow extends Component {
<ConfirmModal
isOpen={this.state.isConfirmGrabModalOpen}
kind={kinds.WARNING}
title="Grab Release"
message={`Readarr was unable to determine which author and book this release was for. Readarr may be unable to automatically import this release. Do you want to grab '${title}'?`}
confirmLabel="Grab"
title={translate('GrabRelease')}
message={translate('GrabReleaseMessageText', [title])}
confirmLabel={translate('Grab')}
onConfirm={this.onGrabConfirm}
onCancel={this.onGrabCancel}
/>

@ -9,6 +9,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
@ -100,12 +101,16 @@ class OrganizePreviewModalContent extends Component {
{
!isFetching && error &&
<div>Error loading previews</div>
<div>
{translate('ErrorLoadingPreviews')}
</div>
}
{
!isFetching && isPopulated && !items.length &&
<div>Success! My work is done, no files to rename.</div>
<div>
{translate('SuccessMyWorkIsDoneNoFilesToRename')}
</div>
}
{

@ -8,6 +8,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
@ -104,12 +105,16 @@ class RetagPreviewModalContent extends Component {
{
!isFetching && error &&
<div>Error loading previews</div>
<div>
{translate('ErrorLoadingPreviews')}
</div>
}
{
!isFetching && ((isPopulated && !items.length)) &&
<div>Success! My work is done, no files to retag.</div>
<div>
{translate('SuccessMyWorkIsDoneNoFilesToRetag')}
</div>
}
{

@ -9,6 +9,7 @@ import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import { icons } from 'Helpers/Props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import AddNewAuthorSearchResultConnector from './Author/AddNewAuthorSearchResultConnector';
import AddNewBookSearchResultConnector from './Book/AddNewBookSearchResultConnector';
import styles from './AddNewItem.css';
@ -87,7 +88,7 @@ class AddNewItem extends Component {
const isFetching = this.state.isFetching;
return (
<PageContent title="Add New Item">
<PageContent title={translate('AddNewItem')}>
<PageContentBody>
<div className={styles.searchContainer}>
<div className={styles.searchIconContainer}>
@ -101,7 +102,7 @@ class AddNewItem extends Component {
className={styles.searchInput}
name="searchBox"
value={term}
placeholder="eg. War and Peace, goodreads:656, isbn:067003469X, asin:B00JCDK5ME"
placeholder={translate('SearchBoxPlaceHolder')}
autoFocus={true}
onChange={this.onSearchInputChange}
/>

@ -10,6 +10,7 @@ import { icons, kinds, sizes } from 'Helpers/Props';
import dimensions from 'Styles/Variables/dimensions';
import fonts from 'Styles/Variables/fonts';
import stripHtml from 'Utilities/String/stripHtml';
import translate from 'Utilities/String/translate';
import AddNewAuthorModal from './AddNewAuthorModal';
import styles from './AddNewAuthorSearchResult.css';
@ -139,7 +140,7 @@ class AddNewAuthorSearchResult extends Component {
className={styles.alreadyExistsIcon}
name={icons.CHECK_CIRCLE}
size={36}
title="Already in your library"
title={translate('AlreadyInYourLibrary')}
/> :
null
}

@ -11,6 +11,7 @@ import { icons, sizes } from 'Helpers/Props';
import dimensions from 'Styles/Variables/dimensions';
import fonts from 'Styles/Variables/fonts';
import stripHtml from 'Utilities/String/stripHtml';
import translate from 'Utilities/String/translate';
import AddNewBookModal from './AddNewBookModal';
import styles from './AddNewBookSearchResult.css';
@ -131,7 +132,7 @@ class AddNewBookSearchResult extends Component {
className={styles.alreadyExistsIcon}
name={icons.CHECK_CIRCLE}
size={36}
title="Already in your library"
title={translate('AlreadyInYourLibrary')}
/> :
null
}

@ -9,6 +9,7 @@ import FormLabel from 'Components/Form/FormLabel';
import Icon from 'Components/Icon';
import Popover from 'Components/Tooltip/Popover';
import { icons, inputTypes, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './AddAuthorOptionsForm.css';
class AddAuthorOptionsForm extends Component {
@ -43,7 +44,9 @@ class AddAuthorOptionsForm extends Component {
return (
<Form {...otherProps}>
<FormGroup>
<FormLabel>Root Folder</FormLabel>
<FormLabel>
{translate('RootFolder')}
</FormLabel>
<FormInputGroup
type={inputTypes.ROOT_FOLDER_SELECT}
@ -64,7 +67,7 @@ class AddAuthorOptionsForm extends Component {
name={icons.INFO}
/>
}
title="Monitoring Options"
title={translate('MonitoringOptions')}
body={<AuthorMonitoringOptionsPopoverContent />}
position={tooltipPositions.RIGHT}
/>
@ -79,7 +82,9 @@ class AddAuthorOptionsForm extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Quality Profile</FormLabel>
<FormLabel>
{translate('QualityProfile')}
</FormLabel>
<FormInputGroup
type={inputTypes.QUALITY_PROFILE_SELECT}
@ -102,7 +107,7 @@ class AddAuthorOptionsForm extends Component {
name={icons.INFO}
/>
}
title="Metadata Profile"
title={translate('MetadataProfile')}
body={<AuthorMetadataProfilePopoverContent />}
position={tooltipPositions.RIGHT}
/>
@ -119,7 +124,9 @@ class AddAuthorOptionsForm extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Tags</FormLabel>
<FormLabel>
{translate('Tags')}
</FormLabel>
<FormInputGroup
type={inputTypes.TAG}

@ -4,6 +4,7 @@ import React from 'react';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './AdvancedSettingsButton.css';
function AdvancedSettingsButton(props) {
@ -15,7 +16,7 @@ function AdvancedSettingsButton(props) {
return (
<Link
className={styles.button}
title={advancedSettings ? 'Shown, click to hide' : 'Hidden, click to show'}
title={advancedSettings ? translate('AdvancedSettingsShownClickToHide') : translate('AdvancedSettingsHiddenClickToShow')}
onPress={onAdvancedSettingsPress}
>
<Icon

@ -10,6 +10,7 @@ import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import { inputTypes } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate';
const logLevelOptions = [
{ key: 'info', value: 'Info' },
@ -34,7 +35,7 @@ class DevelopmentSettings extends Component {
} = this.props;
return (
<PageContent title="Development">
<PageContent title={translate('Development')}>
<SettingsToolbarConnector
{...otherProps}
onSavePress={onSavePress}
@ -59,14 +60,16 @@ class DevelopmentSettings extends Component {
id="developmentSettings"
{...otherProps}
>
<FieldSet legend="Metadata Provider Source">
<FieldSet legend={translate('MetadataProviderSource')}>
<FormGroup>
<FormLabel>Metadata Source</FormLabel>
<FormLabel>
{translate('MetadataSource')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="metadataSource"
helpText="Alternative Metadata Source (Leave blank for default)"
helpText={translate('MetadataSourceHelpText')}
helpLink="https://wiki.servarr.com/Readarr_Settings#Metadata"
onChange={onInputChange}
{...settings.metadataSource}
@ -74,21 +77,25 @@ class DevelopmentSettings extends Component {
</FormGroup>
</FieldSet>
<FieldSet legend="Logging">
<FieldSet legend={translate('Logging')}>
<FormGroup>
<FormLabel>Log Rotation</FormLabel>
<FormLabel>
{translate('LogRotation')}
</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="logRotate"
helpText="Max number of log files to keep saved in logs folder"
helpText={translate('LogRotateHelpText')}
onChange={onInputChange}
{...settings.logRotate}
/>
</FormGroup>
<FormGroup>
<FormLabel>Console Log Level</FormLabel>
<FormLabel>
{translate('ConsoleLogLevel')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="consoleLogLevel"
@ -99,26 +106,30 @@ class DevelopmentSettings extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Log SQL</FormLabel>
<FormLabel>
{translate('LogSQL')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="logSql"
helpText="Log all SQL queries from Readarr"
helpText={translate('LogSqlHelpText')}
onChange={onInputChange}
{...settings.logSql}
/>
</FormGroup>
</FieldSet>
<FieldSet legend="Analytics">
<FieldSet legend={translate('Analytics')}>
<FormGroup>
<FormLabel>Filter Analytics Events</FormLabel>
<FormLabel>
{translate('FilterAnalyticsEvents')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="filterSentryEvents"
helpText="Filter known user error events from being sent as Analytics"
helpText={translate('FilterSentryEventsHelpText')}
onChange={onInputChange}
{...settings.filterSentryEvents}
/>

@ -6,6 +6,7 @@ import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import { icons } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate';
import DownloadClientsConnector from './DownloadClients/DownloadClientsConnector';
import DownloadClientOptionsConnector from './Options/DownloadClientOptionsConnector';
import RemotePathMappingsConnector from './RemotePathMappings/RemotePathMappingsConnector';
@ -58,7 +59,7 @@ class DownloadClientSettings extends Component {
} = this.state;
return (
<PageContent title="Download Client Settings">
<PageContent title={translate('DownloadClientSettings')}>
<SettingsToolbarConnector
isSaving={isSaving}
hasPendingChanges={hasPendingChanges}
@ -67,7 +68,7 @@ class DownloadClientSettings extends Component {
<PageToolbarSeparator />
<PageToolbarButton
label="Test All Clients"
label={translate('TestAllClients')}
iconName={icons.TEST}
isSpinning={isTestingAll}
onPress={dispatchTestAllDownloadClients}

@ -9,6 +9,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import AddDownloadClientItem from './AddDownloadClientItem';
import styles from './AddDownloadClientModalContent.css';
@ -42,7 +43,9 @@ class AddDownloadClientModalContent extends Component {
{
!isSchemaFetching && !!schemaError &&
<div>Unable to add a new downloadClient, please try again.</div>
<div>
{translate('UnableToAddANewDownloadClientPleaseTryAgain')}
</div>
}
{
@ -50,11 +53,15 @@ class AddDownloadClientModalContent extends Component {
<div>
<Alert kind={kinds.INFO}>
<div>Readarr supports any downloadClient that uses the Newznab standard, as well as other downloadClients listed below.</div>
<div>For more information on the individual downloadClients, click on the info buttons.</div>
<div>
{translate('ReadarrSupportsAnyDownloadClientThatUsesTheNewznabStandardAsWellAsOtherDownloadClientsListedBelow')}
</div>
<div>
{translate('ForMoreInformationOnTheIndividualDownloadClientsClickOnTheInfoButtons')}
</div>
</Alert>
<FieldSet legend="Usenet">
<FieldSet legend={translate('Usenet')}>
<div className={styles.downloadClients}>
{
usenetDownloadClients.map((downloadClient) => {
@ -71,7 +78,7 @@ class AddDownloadClientModalContent extends Component {
</div>
</FieldSet>
<FieldSet legend="Torrents">
<FieldSet legend={translate('Torrents')}>
<div className={styles.downloadClients}>
{
torrentDownloadClients.map((downloadClient) => {

@ -4,6 +4,7 @@ import Card from 'Components/Card';
import Label from 'Components/Label';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import EditDownloadClientModalConnector from './EditDownloadClientModalConnector';
import styles from './DownloadClient.css';
@ -103,9 +104,9 @@ class DownloadClient extends Component {
<ConfirmModal
isOpen={this.state.isDeleteDownloadClientModalOpen}
kind={kinds.DANGER}
title="Delete Download Client"
message={`Are you sure you want to delete the download client '${name}'?`}
confirmLabel="Delete"
title={translate('DeleteDownloadClient')}
message={translate('DeleteDownloadClientMessageText', [name])}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteDownloadClient}
onCancel={this.onDeleteDownloadClientModalClose}
/>

@ -5,6 +5,7 @@ import FieldSet from 'Components/FieldSet';
import Icon from 'Components/Icon';
import PageSectionContent from 'Components/Page/PageSectionContent';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import AddDownloadClientModal from './AddDownloadClientModal';
import DownloadClient from './DownloadClient';
import EditDownloadClientModalConnector from './EditDownloadClientModalConnector';
@ -58,9 +59,9 @@ class DownloadClients extends Component {
} = this.state;
return (
<FieldSet legend="Download Clients">
<FieldSet legend={translate('DownloadClients')}>
<PageSectionContent
errorMessage="Unable to load download clients"
errorMessage={translate('UnableToLoadDownloadClients')}
{...otherProps}
>
<div className={styles.downloadClients}>

@ -14,6 +14,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './EditDownloadClientModalContent.css';
class EditDownloadClientModalContent extends Component {
@ -63,7 +64,9 @@ class EditDownloadClientModalContent extends Component {
{
!isFetching && !!error &&
<div>Unable to add a new download client, please try again.</div>
<div>
{translate('UnableToAddANewDownloadClientPleaseTryAgain')}
</div>
}
{
@ -80,7 +83,9 @@ class EditDownloadClientModalContent extends Component {
}
<FormGroup>
<FormLabel>Name</FormLabel>
<FormLabel>
{translate('Name')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
@ -91,7 +96,9 @@ class EditDownloadClientModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Enable</FormLabel>
<FormLabel>
{translate('Enable')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@ -120,12 +127,14 @@ class EditDownloadClientModalContent extends Component {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>Client Priority</FormLabel>
<FormLabel>
{translate('ClientPriority')}
</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="priority"
helpText="Prioritize multiple Download Clients. Round-Robin is used for clients with the same priority."
helpText={translate('PriorityHelpText')}
min={1}
max={50}
{...priority}

@ -7,6 +7,7 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import { inputTypes, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
function DownloadClientOptions(props) {
const {
@ -27,21 +28,25 @@ function DownloadClientOptions(props) {
{
!isFetching && error &&
<div>Unable to load download client options</div>
<div>
{translate('UnableToLoadDownloadClientOptions')}
</div>
}
{
hasSettings && !isFetching && !error &&
<div>
<FieldSet legend="Completed Download Handling">
<FieldSet legend={translate('CompletedDownloadHandling')}>
<Form>
<FormGroup size={sizes.MEDIUM}>
<FormLabel>Enable</FormLabel>
<FormLabel>
{translate('Enable')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="enableCompletedDownloadHandling"
helpText="Automatically import completed downloads from download client"
helpText={translate('EnableCompletedDownloadHandlingHelpText')}
onChange={onInputChange}
{...settings.enableCompletedDownloadHandling}
/>
@ -52,12 +57,14 @@ function DownloadClientOptions(props) {
isAdvanced={true}
size={sizes.MEDIUM}
>
<FormLabel>Remove</FormLabel>
<FormLabel>
{translate('Remove')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="removeCompletedDownloads"
helpText="Remove imported downloads from download client history"
helpText={translate('RemoveCompletedDownloadsHelpText')}
onChange={onInputChange}
{...settings.removeCompletedDownloads}
/>
@ -66,16 +73,18 @@ function DownloadClientOptions(props) {
</FieldSet>
<FieldSet
legend="Failed Download Handling"
legend={translate('FailedDownloadHandling')}
>
<Form>
<FormGroup size={sizes.MEDIUM}>
<FormLabel>Redownload</FormLabel>
<FormLabel>
{translate('Redownload')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="autoRedownloadFailed"
helpText="Automatically search for and attempt to download a different release"
helpText={translate('AutoRedownloadFailedHelpText')}
onChange={onInputChange}
{...settings.autoRedownloadFailed}
/>
@ -86,12 +95,14 @@ function DownloadClientOptions(props) {
isAdvanced={true}
size={sizes.MEDIUM}
>
<FormLabel>Remove</FormLabel>
<FormLabel>
{translate('Remove')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="removeFailedDownloads"
helpText="Remove failed downloads from download client history"
helpText={translate('RemoveFailedDownloadsHelpText')}
onChange={onInputChange}
{...settings.removeFailedDownloads}
/>

@ -13,6 +13,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds } from 'Helpers/Props';
import { stringSettingShape } from 'Helpers/Props/Shapes/settingShape';
import translate from 'Utilities/String/translate';
import styles from './EditRemotePathMappingModalContent.css';
function EditRemotePathMappingModalContent(props) {
@ -51,19 +52,23 @@ function EditRemotePathMappingModalContent(props) {
{
!isFetching && !!error &&
<div>Unable to add a new remote path mapping, please try again.</div>
<div>
{translate('UnableToAddANewRemotePathMappingPleaseTryAgain')}
</div>
}
{
!isFetching && !error &&
<Form {...otherProps}>
<FormGroup>
<FormLabel>Host</FormLabel>
<FormLabel>
{translate('Host')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="host"
helpText="The same host you specified for the remote Download Client"
helpText={translate('HostHelpText')}
{...host}
values={downloadClientHosts}
onChange={onInputChange}
@ -71,24 +76,28 @@ function EditRemotePathMappingModalContent(props) {
</FormGroup>
<FormGroup>
<FormLabel>Remote Path</FormLabel>
<FormLabel>
{translate('RemotePath')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="remotePath"
helpText="Root path to the directory that the Download Client accesses"
helpText={translate('RemotePathHelpText')}
{...remotePath}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Local Path</FormLabel>
<FormLabel>
{translate('LocalPath')}
</FormLabel>
<FormInputGroup
type={inputTypes.PATH}
name="localPath"
helpText="Path that Readarr should use to access the remote path locally"
helpText={translate('LocalPathHelpText')}
{...localPath}
onChange={onInputChange}
/>

@ -5,6 +5,7 @@ import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import EditRemotePathMappingModalConnector from './EditRemotePathMappingModalConnector';
import styles from './RemotePathMapping.css';
@ -87,9 +88,9 @@ class RemotePathMapping extends Component {
<ConfirmModal
isOpen={this.state.isDeleteRemotePathMappingModalOpen}
kind={kinds.DANGER}
title="Delete Delay Profile"
message="Are you sure you want to delete this remote path mapping?"
confirmLabel="Delete"
title={translate('DeleteDelayProfile')}
message={translate('DeleteDelayProfileMessageText')}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteRemotePathMapping}
onCancel={this.onDeleteRemotePathMappingModalClose}
/>

@ -5,6 +5,7 @@ import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import PageSectionContent from 'Components/Page/PageSectionContent';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import EditRemotePathMappingModalConnector from './EditRemotePathMappingModalConnector';
import RemotePathMapping from './RemotePathMapping';
import styles from './RemotePathMappings.css';
@ -44,9 +45,9 @@ class RemotePathMappings extends Component {
} = this.props;
return (
<FieldSet legend="Remote Path Mappings">
<FieldSet legend={translate('RemotePathMappings')}>
<PageSectionContent
errorMessage="Unable to load Remote Path Mappings"
errorMessage={translate('UnableToLoadRemotePathMappings')}
{...otherProps}
>
<div className={styles.remotePathMappingsHeader}>

@ -5,6 +5,7 @@ import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import { inputTypes, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
function AnalyticSettings(props) {
const {
@ -17,15 +18,17 @@ function AnalyticSettings(props) {
} = settings;
return (
<FieldSet legend="Analytics">
<FieldSet legend={translate('Analytics')}>
<FormGroup size={sizes.MEDIUM}>
<FormLabel>Send Anonymous Usage Data</FormLabel>
<FormLabel>
{translate('SendAnonymousUsageData')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="analyticsEnabled"
helpText="Send anonymous usage and error information to Readarr's servers. This includes information on your browser, which Readarr WebUI pages you use, error reporting as well as OS and runtime version. We will use this information to prioritize features and bug fixes."
helpTextWarning="Requires restart to take effect"
helpText={translate('AnalyticsEnabledHelpText')}
helpTextWarning={translate('AnalyticsEnabledHelpTextWarning')}
onChange={onInputChange}
{...analyticsEnabled}
/>

@ -5,6 +5,7 @@ import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
function BackupSettings(props) {
const {
@ -24,17 +25,19 @@ function BackupSettings(props) {
}
return (
<FieldSet legend="Backups">
<FieldSet legend={translate('Backups')}>
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>Folder</FormLabel>
<FormLabel>
{translate('Folder')}
</FormLabel>
<FormInputGroup
type={inputTypes.PATH}
name="backupFolder"
helpText="Relative paths will be under Readarr's AppData directory"
helpText={translate('BackupFolderHelpText')}
onChange={onInputChange}
{...backupFolder}
/>
@ -44,13 +47,15 @@ function BackupSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>Interval</FormLabel>
<FormLabel>
{translate('Interval')}
</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="backupInterval"
unit="days"
helpText="Interval to backup the Readarr DB and settings"
helpText={translate('BackupIntervalHelpText')}
onChange={onInputChange}
{...backupInterval}
/>
@ -60,13 +65,15 @@ function BackupSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>Retention</FormLabel>
<FormLabel>
{translate('Retention')}
</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="backupRetention"
unit="days"
helpText="Automatic backups older than the retention period will be cleaned up automatically"
helpText={translate('BackupRetentionHelpText')}
onChange={onInputChange}
{...backupRetention}
/>

@ -8,6 +8,7 @@ import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import { kinds } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate';
import AnalyticSettings from './AnalyticSettings';
import BackupSettings from './BackupSettings';
import HostSettings from './HostSettings';
@ -110,7 +111,7 @@ class GeneralSettings extends Component {
} = this.props;
return (
<PageContent title="General Settings">
<PageContent title={translate('GeneralSettings')}>
<SettingsToolbarConnector
{...otherProps}
/>
@ -123,7 +124,9 @@ class GeneralSettings extends Component {
{
!isFetching && error &&
<div>Unable to load General settings</div>
<div>
{translate('UnableToLoadGeneralSettings')}
</div>
}
{
@ -183,12 +186,12 @@ class GeneralSettings extends Component {
<ConfirmModal
isOpen={this.state.isRestartRequiredModalOpen}
kind={kinds.DANGER}
title="Restart Readarr"
title={translate('RestartReadarr')}
message={
`Readarr requires a restart to apply changes, do you want to restart now? ${isWindowsService ? 'Depending which user is running the Readarr service you may need to restart Readarr as admin once before the service will start automatically.' : ''}`
}
cancelLabel="I'll restart later"
confirmLabel="Restart Now"
cancelLabel={translate('IllRestartLater')}
confirmLabel={translate('RestartNow')}
onConfirm={this.onConfirmRestart}
onCancel={this.onCloseRestartRequiredModalOpen}
/>

@ -5,6 +5,7 @@ import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import { inputTypes, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
function HostSettings(props) {
const {
@ -27,45 +28,51 @@ function HostSettings(props) {
} = settings;
return (
<FieldSet legend="Host">
<FieldSet legend={translate('Host')}>
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>Bind Address</FormLabel>
<FormLabel>
{translate('BindAddress')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="bindAddress"
helpText="Valid IP4 address or '*' for all interfaces"
helpTextWarning="Requires restart to take effect"
helpText={translate('BindAddressHelpText')}
helpTextWarning={translate('BindAddressHelpTextWarning')}
onChange={onInputChange}
{...bindAddress}
/>
</FormGroup>
<FormGroup>
<FormLabel>Port Number</FormLabel>
<FormLabel>
{translate('PortNumber')}
</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="port"
min={1}
max={65535}
helpTextWarning="Requires restart to take effect"
helpTextWarning={translate('PortHelpTextWarning')}
onChange={onInputChange}
{...port}
/>
</FormGroup>
<FormGroup>
<FormLabel>URL Base</FormLabel>
<FormLabel>
{translate('URLBase')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="urlBase"
helpText="For reverse proxy support, default is empty"
helpTextWarning="Requires restart to take effect"
helpText={translate('UrlBaseHelpText')}
helpTextWarning={translate('UrlBaseHelpTextWarning')}
onChange={onInputChange}
{...urlBase}
/>
@ -76,12 +83,14 @@ function HostSettings(props) {
isAdvanced={true}
size={sizes.MEDIUM}
>
<FormLabel>Enable SSL</FormLabel>
<FormLabel>
{translate('EnableSSL')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="enableSsl"
helpText=" Requires restart running as administrator to take effect"
helpText={translate('EnableSslHelpText')}
onChange={onInputChange}
{...enableSsl}
/>
@ -93,14 +102,16 @@ function HostSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>SSL Port</FormLabel>
<FormLabel>
{translate('SSLPort')}
</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="sslPort"
min={1}
max={65535}
helpTextWarning="Requires restart to take effect"
helpTextWarning={translate('SslPortHelpTextWarning')}
onChange={onInputChange}
{...sslPort}
/>
@ -114,13 +125,15 @@ function HostSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>SSL Cert Path</FormLabel>
<FormLabel>
{translate('SSLCertPath')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="sslCertPath"
helpText="Path to pfx file"
helpTextWarning="Requires restart to take effect"
helpText={translate('SslCertPathHelpText')}
helpTextWarning={translate('SslCertPathHelpTextWarning')}
onChange={onInputChange}
{...sslCertPath}
/>
@ -134,13 +147,15 @@ function HostSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>SSL Cert Password</FormLabel>
<FormLabel>
{translate('SSLCertPassword')}
</FormLabel>
<FormInputGroup
type={inputTypes.PASSWORD}
name="sslCertPassword"
helpText="Password for pfx file"
helpTextWarning="Requires restart to take effect"
helpText={translate('SslCertPasswordHelpText')}
helpTextWarning={translate('SslCertPasswordHelpTextWarning')}
onChange={onInputChange}
{...sslCertPassword}
/>
@ -151,12 +166,14 @@ function HostSettings(props) {
{
isWindows && mode !== 'service' &&
<FormGroup size={sizes.MEDIUM}>
<FormLabel>Open browser on start</FormLabel>
<FormLabel>
{translate('OpenBrowserOnStart')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="launchBrowser"
helpText=" Open a web browser and navigate to Readarr homepage on app start."
helpText={translate('LaunchBrowserHelpText')}
onChange={onInputChange}
{...launchBrowser}
/>

@ -5,6 +5,7 @@ import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
const logLevelOptions = [
{ key: 'info', value: 'Info' },
@ -23,15 +24,17 @@ function LoggingSettings(props) {
} = settings;
return (
<FieldSet legend="Logging">
<FieldSet legend={translate('Logging')}>
<FormGroup>
<FormLabel>Log Level</FormLabel>
<FormLabel>
{translate('LogLevel')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="logLevel"
values={logLevelOptions}
helpTextWarning={logLevel.value === 'trace' ? 'Trace logging should only be enabled temporarily' : undefined}
helpTextWarning={logLevel.value === 'trace' ? translate('LogLevelvalueTraceTraceLoggingShouldOnlyBeEnabledTemporarily') : undefined}
onChange={onInputChange}
{...logLevel}
/>

@ -5,6 +5,7 @@ import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import { inputTypes, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
function ProxySettings(props) {
const {
@ -30,9 +31,11 @@ function ProxySettings(props) {
];
return (
<FieldSet legend="Proxy">
<FieldSet legend={translate('Proxy')}>
<FormGroup size={sizes.MEDIUM}>
<FormLabel>Use Proxy</FormLabel>
<FormLabel>
{translate('UseProxy')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@ -46,7 +49,9 @@ function ProxySettings(props) {
proxyEnabled.value &&
<div>
<FormGroup>
<FormLabel>Proxy Type</FormLabel>
<FormLabel>
{translate('ProxyType')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@ -58,7 +63,9 @@ function ProxySettings(props) {
</FormGroup>
<FormGroup>
<FormLabel>Hostname</FormLabel>
<FormLabel>
{translate('Hostname')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
@ -69,7 +76,9 @@ function ProxySettings(props) {
</FormGroup>
<FormGroup>
<FormLabel>Port</FormLabel>
<FormLabel>
{translate('Port')}
</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
@ -82,43 +91,51 @@ function ProxySettings(props) {
</FormGroup>
<FormGroup>
<FormLabel>Username</FormLabel>
<FormLabel>
{translate('Username')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="proxyUsername"
helpText="You only need to enter a username and password if one is required. Leave them blank otherwise."
helpText={translate('ProxyUsernameHelpText')}
onChange={onInputChange}
{...proxyUsername}
/>
</FormGroup>
<FormGroup>
<FormLabel>Password</FormLabel>
<FormLabel>
{translate('Password')}
</FormLabel>
<FormInputGroup
type={inputTypes.PASSWORD}
name="proxyPassword"
helpText="You only need to enter a username and password if one is required. Leave them blank otherwise."
helpText={translate('ProxyPasswordHelpText')}
onChange={onInputChange}
{...proxyPassword}
/>
</FormGroup>
<FormGroup>
<FormLabel>Ignored Addresses</FormLabel>
<FormLabel>
{translate('IgnoredAddresses')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="proxyBypassFilter"
helpText="Use ',' as a separator, and '*.' as a wildcard for subdomains"
helpText={translate('ProxyBypassFilterHelpText')}
onChange={onInputChange}
{...proxyBypassFilter}
/>
</FormGroup>
<FormGroup size={sizes.MEDIUM}>
<FormLabel>Bypass Proxy for Local Addresses</FormLabel>
<FormLabel>
{translate('BypassProxyForLocalAddresses')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}

@ -9,6 +9,7 @@ import Icon from 'Components/Icon';
import ClipboardButton from 'Components/Link/ClipboardButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import { icons, inputTypes, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
const authenticationMethodOptions = [
{ key: 'none', value: 'None' },
@ -76,15 +77,17 @@ class SecuritySettings extends Component {
const authenticationEnabled = authenticationMethod && authenticationMethod.value !== 'none';
return (
<FieldSet legend="Security">
<FieldSet legend={translate('Security')}>
<FormGroup>
<FormLabel>Authentication</FormLabel>
<FormLabel>
{translate('Authentication')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="authenticationMethod"
values={authenticationMethodOptions}
helpText="Require Username and Password to access Readarr"
helpText={translate('AuthenticationMethodHelpText')}
onChange={onInputChange}
{...authenticationMethod}
/>
@ -93,7 +96,9 @@ class SecuritySettings extends Component {
{
authenticationEnabled &&
<FormGroup>
<FormLabel>Username</FormLabel>
<FormLabel>
{translate('Username')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
@ -107,7 +112,9 @@ class SecuritySettings extends Component {
{
authenticationEnabled &&
<FormGroup>
<FormLabel>Password</FormLabel>
<FormLabel>
{translate('Password')}
</FormLabel>
<FormInputGroup
type={inputTypes.PASSWORD}
@ -119,13 +126,15 @@ class SecuritySettings extends Component {
}
<FormGroup>
<FormLabel>API Key</FormLabel>
<FormLabel>
{translate('APIKey')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="apiKey"
readOnly={true}
helpTextWarning="Requires restart to take effect"
helpTextWarning={translate('ApiKeyHelpTextWarning')}
buttons={[
<ClipboardButton
key="copy"
@ -151,13 +160,15 @@ class SecuritySettings extends Component {
</FormGroup>
<FormGroup>
<FormLabel>Certificate Validation</FormLabel>
<FormLabel>
{translate('CertificateValidation')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="certificateValidation"
values={certificateValidationOptions}
helpText="Change how strict HTTPS certification validation is"
helpText={translate('CertificateValidationHelpText')}
onChange={onInputChange}
{...certificateValidation}
/>
@ -166,9 +177,9 @@ class SecuritySettings extends Component {
<ConfirmModal
isOpen={this.state.isConfirmApiKeyResetModalOpen}
kind={kinds.DANGER}
title="Reset API Key"
message="Are you sure you want to reset your API Key?"
confirmLabel="Reset"
title={translate('ResetAPIKey')}
message={translate('ResetAPIKeyMessageText')}
confirmLabel={translate('Reset')}
onConfirm={this.onConfirmResetApiKey}
onCancel={this.onCloseResetApiKeyModal}
/>

@ -6,6 +6,7 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import { inputTypes, sizes } from 'Helpers/Props';
import titleCase from 'Utilities/String/titleCase';
import translate from 'Utilities/String/translate';
function UpdateSettings(props) {
const {
@ -45,24 +46,28 @@ function UpdateSettings(props) {
if (isDocker) {
return (
<FieldSet legend="Updates">
<div>Updating is disabled inside a docker container. Update the container image instead.</div>
<FieldSet legend={translate('Updates')}>
<div>
{translate('UpdatingIsDisabledInsideADockerContainerUpdateTheContainerImageInstead')}
</div>
</FieldSet>
);
}
return (
<FieldSet legend="Updates">
<FieldSet legend={translate('Updates')}>
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>Branch</FormLabel>
<FormLabel>
{translate('Branch')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="branch"
helpText={usingExternalUpdateMechanism ? 'Branch used by external update mechanism' : 'Branch to use to update Readarr'}
helpText={usingExternalUpdateMechanism ? translate('UsingExternalUpdateMechanismBranchUsedByExternalUpdateMechanism') : translate('UsingExternalUpdateMechanismBranchToUseToUpdateReadarr')}
helpLink="https://wiki.servarr.com/Readarr_FAQ#How_do_I_update_my_Readarr"
{...branch}
onChange={onInputChange}
@ -78,12 +83,14 @@ function UpdateSettings(props) {
isAdvanced={true}
size={sizes.MEDIUM}
>
<FormLabel>Automatic</FormLabel>
<FormLabel>
{translate('Automatic')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="updateAutomatically"
helpText="Automatically download and install updates. You will still be able to install from System: Updates"
helpText={translate('UpdateAutomaticallyHelpText')}
onChange={onInputChange}
{...updateAutomatically}
/>
@ -93,13 +100,15 @@ function UpdateSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>Mechanism</FormLabel>
<FormLabel>
{translate('Mechanism')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="updateMechanism"
values={updateOptions}
helpText="Use Readarr's built-in updater or a script"
helpText={translate('UpdateMechanismHelpText')}
helpLink="https://wiki.servarr.com/Readarr_FAQ#How_do_I_update_my_Readarr"
onChange={onInputChange}
{...updateMechanism}
@ -112,12 +121,14 @@ function UpdateSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>Script Path</FormLabel>
<FormLabel>
{translate('ScriptPath')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="updateScriptPath"
helpText="Path to a custom script that takes an extracted update package and handle the remainder of the update process"
helpText={translate('UpdateScriptPathHelpText')}
onChange={onInputChange}
{...updateScriptPath}
/>

@ -13,6 +13,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds } from 'Helpers/Props';
import { stringSettingShape } from 'Helpers/Props/Shapes/settingShape';
import translate from 'Utilities/String/translate';
import styles from './EditImportListExclusionModalContent.css';
function EditImportListExclusionModalContent(props) {
@ -49,7 +50,9 @@ function EditImportListExclusionModalContent(props) {
{
!isFetching && !!error &&
<div>Unable to add a new import list exclusion, please try again.</div>
<div>
{translate('UnableToAddANewImportListExclusionPleaseTryAgain')}
</div>
}
{
@ -58,24 +61,28 @@ function EditImportListExclusionModalContent(props) {
{...otherProps}
>
<FormGroup>
<FormLabel>Entity Name</FormLabel>
<FormLabel>
{translate('EntityName')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="authorName"
helpText="The name of the author/book to exclude (can be anything meaningful)"
helpText={translate('AuthorNameHelpText')}
{...authorName}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Musicbrainz Id</FormLabel>
<FormLabel>
{translate('MusicbrainzId')}
</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="foreignId"
helpText="The Musicbrainz Id of the author/book to exclude"
helpText={translate('ForeignIdHelpText')}
{...foreignId}
onChange={onInputChange}
/>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save