New: Add translations for managing bulk indexers, lists and clients

This commit is contained in:
Bogdan 2023-07-11 10:17:38 +03:00 committed by Mark McDowall
parent 45336389e6
commit e641c57ad9
19 changed files with 258 additions and 149 deletions

View File

@ -235,7 +235,7 @@ function EditSeriesModalContent(props: EditSeriesModalContentProps) {
<Button onPress={onModalClose}>{translate('Cancel')}</Button> <Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button onPress={onSavePressWrapper}> <Button onPress={onSavePressWrapper}>
{translate('Apply Changes')} {translate('ApplyChanges')}
</Button> </Button>
</div> </div>
</ModalFooter> </ModalFooter>

View File

@ -6,6 +6,7 @@ import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector'; import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate';
import DownloadClientsConnector from './DownloadClients/DownloadClientsConnector'; import DownloadClientsConnector from './DownloadClients/DownloadClientsConnector';
import ManageDownloadClientsModal from './DownloadClients/Manage/ManageDownloadClientsModal'; import ManageDownloadClientsModal from './DownloadClients/Manage/ManageDownloadClientsModal';
import DownloadClientOptionsConnector from './Options/DownloadClientOptionsConnector'; import DownloadClientOptionsConnector from './Options/DownloadClientOptionsConnector';
@ -85,7 +86,7 @@ class DownloadClientSettings extends Component {
/> />
<PageToolbarButton <PageToolbarButton
label="Manage Clients" label={translate('ManageClients')}
iconName={icons.MANAGE} iconName={icons.MANAGE}
onPress={this.onManageDownloadClientsPress} onPress={this.onManageDownloadClientsPress}
/> />

View File

@ -27,9 +27,9 @@ interface ManageDownloadClientsEditModalContentProps {
const NO_CHANGE = 'noChange'; const NO_CHANGE = 'noChange';
const enableOptions = [ const enableOptions = [
{ key: NO_CHANGE, value: 'No Change', disabled: true }, { key: NO_CHANGE, value: translate('NoChange'), disabled: true },
{ key: 'enabled', value: 'Enabled' }, { key: 'enabled', value: translate('Enabled') },
{ key: 'disabled', value: 'Disabled' }, { key: 'disabled', value: translate('Disabled') },
]; ];
function ManageDownloadClientsEditModalContent( function ManageDownloadClientsEditModalContent(
@ -97,7 +97,9 @@ function ManageDownloadClientsEditModalContent(
setRemoveFailedDownloads(value); setRemoveFailedDownloads(value);
break; break;
default: default:
console.warn('EditDownloadClientsModalContent Unknown Input'); console.warn(
`EditDownloadClientsModalContent Unknown Input: '${name}'`
);
} }
}, },
[] []
@ -162,7 +164,7 @@ function ManageDownloadClientsEditModalContent(
<ModalFooter className={styles.modalFooter}> <ModalFooter className={styles.modalFooter}>
<div className={styles.selected}> <div className={styles.selected}>
{translate('{count} download clients selected', { {translate('CountDownloadClientsSelected', {
count: selectedCount, count: selectedCount,
})} })}
</div> </div>
@ -170,7 +172,7 @@ function ManageDownloadClientsEditModalContent(
<div> <div>
<Button onPress={onModalClose}>{translate('Cancel')}</Button> <Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button onPress={save}>{translate('Apply Changes')}</Button> <Button onPress={save}>{translate('ApplyChanges')}</Button>
</div> </div>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>

View File

@ -1,6 +1,7 @@
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { DownloadClientAppState } from 'App/State/SettingsAppState'; import { DownloadClientAppState } from 'App/State/SettingsAppState';
import Alert from 'Components/Alert';
import Button from 'Components/Link/Button'; import Button from 'Components/Link/Button';
import SpinnerButton from 'Components/Link/SpinnerButton'; import SpinnerButton from 'Components/Link/SpinnerButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
@ -20,6 +21,7 @@ import {
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector'; import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import { SelectStateInputProps } from 'typings/props'; import { SelectStateInputProps } from 'typings/props';
import getErrorMessage from 'Utilities/Object/getErrorMessage'; import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds'; import getSelectedIds from 'Utilities/Table/getSelectedIds';
import ManageDownloadClientsEditModal from './Edit/ManageDownloadClientsEditModal'; import ManageDownloadClientsEditModal from './Edit/ManageDownloadClientsEditModal';
import ManageDownloadClientsModalRow from './ManageDownloadClientsModalRow'; import ManageDownloadClientsModalRow from './ManageDownloadClientsModalRow';
@ -34,37 +36,37 @@ type OnSelectedChangeCallback = React.ComponentProps<
const COLUMNS = [ const COLUMNS = [
{ {
name: 'name', name: 'name',
label: 'Name', label: translate('Name'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'implementation', name: 'implementation',
label: 'Implementation', label: translate('Implementation'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'enable', name: 'enable',
label: 'Enabled', label: translate('Enabled'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'priority', name: 'priority',
label: 'Priority', label: translate('Priority'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'removeCompletedDownloads', name: 'removeCompletedDownloads',
label: 'Remove Completed', label: translate('RemoveCompleted'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'removeFailedDownloads', name: 'removeFailedDownloads',
label: 'Remove Failed', label: translate('RemoveFailed'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
@ -191,17 +193,24 @@ function ManageDownloadClientsModalContent(
[items, setSelectState] [items, setSelectState]
); );
const errorMessage = getErrorMessage(error, 'Unable to load import lists.'); const errorMessage = getErrorMessage(
error,
'Unable to load download clients.'
);
const anySelected = selectedCount > 0; const anySelected = selectedCount > 0;
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader>Manage Import Lists</ModalHeader> <ModalHeader>{translate('ManageDownloadClients')}</ModalHeader>
<ModalBody> <ModalBody>
{isFetching ? <LoadingIndicator /> : null} {isFetching ? <LoadingIndicator /> : null}
{error ? <div>{errorMessage}</div> : null} {error ? <div>{errorMessage}</div> : null}
{isPopulated && !error && !items.length && (
<Alert kind={kinds.INFO}>{translate('NoDownloadClientsFound')}</Alert>
)}
{isPopulated && !!items.length && !isFetching && !isFetching ? ( {isPopulated && !!items.length && !isFetching && !isFetching ? (
<Table <Table
columns={COLUMNS} columns={COLUMNS}
@ -236,7 +245,7 @@ function ManageDownloadClientsModalContent(
isDisabled={!anySelected} isDisabled={!anySelected}
onPress={onDeletePress} onPress={onDeletePress}
> >
Delete {translate('Delete')}
</SpinnerButton> </SpinnerButton>
<SpinnerButton <SpinnerButton
@ -244,7 +253,7 @@ function ManageDownloadClientsModalContent(
isDisabled={!anySelected} isDisabled={!anySelected}
onPress={onEditPress} onPress={onEditPress}
> >
Edit {translate('Edit')}
</SpinnerButton> </SpinnerButton>
<SpinnerButton <SpinnerButton
@ -256,7 +265,7 @@ function ManageDownloadClientsModalContent(
</SpinnerButton> </SpinnerButton>
</div> </div>
<Button onPress={onModalClose}>Close</Button> <Button onPress={onModalClose}>{translate('Close')}</Button>
</ModalFooter> </ModalFooter>
<ManageDownloadClientsEditModal <ManageDownloadClientsEditModal
@ -276,9 +285,11 @@ function ManageDownloadClientsModalContent(
<ConfirmModal <ConfirmModal
isOpen={isDeleteModalOpen} isOpen={isDeleteModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title="Delete Download Clients(s)" title={translate('DeleteSelectedDownloadClients')}
message={`Are you sure you want to delete ${selectedIds.length} download clients(s)?`} message={translate('DeleteSelectedDownloadClientsMessageText', {
confirmLabel="Delete" count: selectedIds.length,
})}
confirmLabel={translate('Delete')}
onConfirm={onConfirmDelete} onConfirm={onConfirmDelete}
onCancel={onDeleteModalClose} onCancel={onDeleteModalClose}
/> />

View File

@ -1,10 +1,13 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import Label from 'Components/Label';
import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell'; import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import Column from 'Components/Table/Column'; import Column from 'Components/Table/Column';
import TableRow from 'Components/Table/TableRow'; import TableRow from 'Components/Table/TableRow';
import TagListConnector from 'Components/TagListConnector'; import TagListConnector from 'Components/TagListConnector';
import { kinds } from 'Helpers/Props';
import { SelectStateInputProps } from 'typings/props'; import { SelectStateInputProps } from 'typings/props';
import translate from 'Utilities/String/translate';
import styles from './ManageDownloadClientsModalRow.css'; import styles from './ManageDownloadClientsModalRow.css';
interface ManageDownloadClientsModalRowProps { interface ManageDownloadClientsModalRowProps {
@ -61,17 +64,19 @@ function ManageDownloadClientsModalRow(
</TableRowCell> </TableRowCell>
<TableRowCell className={styles.enable}> <TableRowCell className={styles.enable}>
{enable ? 'Yes' : 'No'} <Label kind={enable ? kinds.SUCCESS : kinds.DISABLED} outline={!enable}>
{enable ? translate('Yes') : translate('No')}
</Label>
</TableRowCell> </TableRowCell>
<TableRowCell className={styles.priority}>{priority}</TableRowCell> <TableRowCell className={styles.priority}>{priority}</TableRowCell>
<TableRowCell className={styles.removeCompletedDownloads}> <TableRowCell className={styles.removeCompletedDownloads}>
{removeCompletedDownloads ? 'Yes' : 'No'} {removeCompletedDownloads ? translate('Yes') : translate('No')}
</TableRowCell> </TableRowCell>
<TableRowCell className={styles.removeFailedDownloads}> <TableRowCell className={styles.removeFailedDownloads}>
{removeFailedDownloads ? 'Yes' : 'No'} {removeFailedDownloads ? translate('Yes') : translate('No')}
</TableRowCell> </TableRowCell>
<TableRowCell className={styles.tags}> <TableRowCell className={styles.tags}>

View File

@ -17,6 +17,7 @@ import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds, sizes } from 'Helpers/Props'; import { inputTypes, kinds, sizes } from 'Helpers/Props';
import createTagsSelector from 'Store/Selectors/createTagsSelector'; import createTagsSelector from 'Store/Selectors/createTagsSelector';
import DownloadClient from 'typings/DownloadClient'; import DownloadClient from 'typings/DownloadClient';
import translate from 'Utilities/String/translate';
import styles from './TagsModalContent.css'; import styles from './TagsModalContent.css';
interface TagsModalContentProps { interface TagsModalContentProps {
@ -36,7 +37,7 @@ function TagsModalContent(props: TagsModalContentProps) {
const [tags, setTags] = useState<number[]>([]); const [tags, setTags] = useState<number[]>([]);
const [applyTags, setApplyTags] = useState('add'); const [applyTags, setApplyTags] = useState('add');
const seriesTags = useMemo(() => { const downloadClientsTags = useMemo(() => {
const tags = ids.reduce((acc: number[], id) => { const tags = ids.reduce((acc: number[], id) => {
const s = allDownloadClients.items.find( const s = allDownloadClients.items.find(
(s: DownloadClient) => s.id === id (s: DownloadClient) => s.id === id
@ -71,19 +72,19 @@ function TagsModalContent(props: TagsModalContentProps) {
}, [tags, applyTags, onApplyTagsPress]); }, [tags, applyTags, onApplyTagsPress]);
const applyTagsOptions = [ const applyTagsOptions = [
{ key: 'add', value: 'Add' }, { key: 'add', value: translate('Add') },
{ key: 'remove', value: 'Remove' }, { key: 'remove', value: translate('Remove') },
{ key: 'replace', value: 'Replace' }, { key: 'replace', value: translate('Replace') },
]; ];
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader>Tags</ModalHeader> <ModalHeader>{translate('Tags')}</ModalHeader>
<ModalBody> <ModalBody>
<Form> <Form>
<FormGroup> <FormGroup>
<FormLabel>Tags</FormLabel> <FormLabel>{translate('Tags')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.TAG} type={inputTypes.TAG}
@ -94,7 +95,7 @@ function TagsModalContent(props: TagsModalContentProps) {
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<FormLabel>Apply Tags</FormLabel> <FormLabel>{translate('ApplyTags')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.SELECT} type={inputTypes.SELECT}
@ -102,20 +103,20 @@ function TagsModalContent(props: TagsModalContentProps) {
value={applyTags} value={applyTags}
values={applyTagsOptions} values={applyTagsOptions}
helpTexts={[ helpTexts={[
'How to apply tags to the selected download clients(s)', translate('ApplyTagsHelpTexts1'),
'Add: Add the tags the existing list of tags', translate('ApplyTagsHelpTexts2'),
'Remove: Remove the entered tags', translate('ApplyTagsHelpTexts3'),
'Replace: Replace the tags with the entered tags (enter no tags to clear all tags)', translate('ApplyTagsHelpTexts4'),
]} ]}
onChange={onApplyTagsChange} onChange={onApplyTagsChange}
/> />
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<FormLabel>Result</FormLabel> <FormLabel>{translate('Result')}</FormLabel>
<div className={styles.result}> <div className={styles.result}>
{seriesTags.map((id) => { {downloadClientsTags.map((id) => {
const tag = tagList.find((t) => t.id === id); const tag = tagList.find((t) => t.id === id);
if (!tag) { if (!tag) {
@ -129,7 +130,11 @@ function TagsModalContent(props: TagsModalContentProps) {
return ( return (
<Label <Label
key={tag.id} key={tag.id}
title={removeTag ? 'Removing tag' : 'Existing tag'} title={
removeTag
? translate('RemovingTag')
: translate('ExistingTag')
}
kind={removeTag ? kinds.INVERSE : kinds.INFO} kind={removeTag ? kinds.INVERSE : kinds.INFO}
size={sizes.LARGE} size={sizes.LARGE}
> >
@ -146,14 +151,14 @@ function TagsModalContent(props: TagsModalContentProps) {
return null; return null;
} }
if (seriesTags.indexOf(id) > -1) { if (downloadClientsTags.indexOf(id) > -1) {
return null; return null;
} }
return ( return (
<Label <Label
key={tag.id} key={tag.id}
title={'Adding tag'} title={translate('AddingTag')}
kind={kinds.SUCCESS} kind={kinds.SUCCESS}
size={sizes.LARGE} size={sizes.LARGE}
> >
@ -167,10 +172,10 @@ function TagsModalContent(props: TagsModalContentProps) {
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button onPress={onModalClose}>Cancel</Button> <Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button kind={kinds.PRIMARY} onPress={onApplyPress}> <Button kind={kinds.PRIMARY} onPress={onApplyPress}>
Apply {translate('Apply')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>

View File

@ -6,6 +6,7 @@ import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector'; import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate';
import ImportListsExclusionsConnector from './ImportListExclusions/ImportListExclusionsConnector'; import ImportListsExclusionsConnector from './ImportListExclusions/ImportListExclusionsConnector';
import ImportListsConnector from './ImportLists/ImportListsConnector'; import ImportListsConnector from './ImportLists/ImportListsConnector';
import ManageImportListsModal from './ImportLists/Manage/ManageImportListsModal'; import ManageImportListsModal from './ImportLists/Manage/ManageImportListsModal';
@ -81,7 +82,7 @@ class ImportListSettings extends Component {
/> />
<PageToolbarButton <PageToolbarButton
label="Manage Lists" label={translate('ManageLists')}
iconName={icons.MANAGE} iconName={icons.MANAGE}
onPress={this.onManageImportListsPress} onPress={this.onManageImportListsPress}
/> />

View File

@ -26,9 +26,9 @@ interface ManageImportListsEditModalContentProps {
const NO_CHANGE = 'noChange'; const NO_CHANGE = 'noChange';
const autoAddOptions = [ const autoAddOptions = [
{ key: NO_CHANGE, value: 'No Change', disabled: true }, { key: NO_CHANGE, value: translate('NoChange'), disabled: true },
{ key: 'enabled', value: 'Enabled' }, { key: 'enabled', value: translate('Enabled') },
{ key: 'disabled', value: 'Disabled' }, { key: 'disabled', value: translate('Disabled') },
]; ];
function ManageImportListsEditModalContent( function ManageImportListsEditModalContent(
@ -87,7 +87,7 @@ function ManageImportListsEditModalContent(
setRootFolderPath(value); setRootFolderPath(value);
break; break;
default: default:
console.warn('EditImportListModalContent Unknown Input'); console.warn(`EditImportListModalContent Unknown Input: '${name}'`);
} }
}, },
[] []
@ -142,7 +142,9 @@ function ManageImportListsEditModalContent(
<ModalFooter className={styles.modalFooter}> <ModalFooter className={styles.modalFooter}>
<div className={styles.selected}> <div className={styles.selected}>
{translate('{count} import lists selected', { count: selectedCount })} {translate('CountImportListsSelected', {
count: selectedCount,
})}
</div> </div>
<div> <div>

View File

@ -1,6 +1,7 @@
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { ImportListAppState } from 'App/State/SettingsAppState'; import { ImportListAppState } from 'App/State/SettingsAppState';
import Alert from 'Components/Alert';
import Button from 'Components/Link/Button'; import Button from 'Components/Link/Button';
import SpinnerButton from 'Components/Link/SpinnerButton'; import SpinnerButton from 'Components/Link/SpinnerButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
@ -20,6 +21,7 @@ import {
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector'; import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import { SelectStateInputProps } from 'typings/props'; import { SelectStateInputProps } from 'typings/props';
import getErrorMessage from 'Utilities/Object/getErrorMessage'; import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds'; import getSelectedIds from 'Utilities/Table/getSelectedIds';
import ManageImportListsEditModal from './Edit/ManageImportListsEditModal'; import ManageImportListsEditModal from './Edit/ManageImportListsEditModal';
import ManageImportListsModalRow from './ManageImportListsModalRow'; import ManageImportListsModalRow from './ManageImportListsModalRow';
@ -34,37 +36,37 @@ type OnSelectedChangeCallback = React.ComponentProps<
const COLUMNS = [ const COLUMNS = [
{ {
name: 'name', name: 'name',
label: 'Name', label: translate('Name'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'implementation', name: 'implementation',
label: 'Implementation', label: translate('Implementation'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'qualityProfileId', name: 'qualityProfileId',
label: 'Quality Profile', label: translate('QualityProfile'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'rootFolderPath', name: 'rootFolderPath',
label: 'Root Folder', label: translate('RootFolder'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'enableAutomaticAdd', name: 'enableAutomaticAdd',
label: 'Auto Add', label: translate('AutoAdd'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'tags', name: 'tags',
label: 'Tags', label: translate('Tags'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
@ -190,12 +192,16 @@ function ManageImportListsModalContent(
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader>Manage Import Lists</ModalHeader> <ModalHeader>{translate('ManageImportLists')}</ModalHeader>
<ModalBody> <ModalBody>
{isFetching ? <LoadingIndicator /> : null} {isFetching ? <LoadingIndicator /> : null}
{error ? <div>{errorMessage}</div> : null} {error ? <div>{errorMessage}</div> : null}
{isPopulated && !error && !items.length && (
<Alert kind={kinds.INFO}>{translate('NoImportListsFound')}</Alert>
)}
{isPopulated && !!items.length && !isFetching && !isFetching ? ( {isPopulated && !!items.length && !isFetching && !isFetching ? (
<Table <Table
columns={COLUMNS} columns={COLUMNS}
@ -230,7 +236,7 @@ function ManageImportListsModalContent(
isDisabled={!anySelected} isDisabled={!anySelected}
onPress={onDeletePress} onPress={onDeletePress}
> >
Delete {translate('Delete')}
</SpinnerButton> </SpinnerButton>
<SpinnerButton <SpinnerButton
@ -238,7 +244,7 @@ function ManageImportListsModalContent(
isDisabled={!anySelected} isDisabled={!anySelected}
onPress={onEditPress} onPress={onEditPress}
> >
Edit {translate('Edit')}
</SpinnerButton> </SpinnerButton>
<SpinnerButton <SpinnerButton
@ -246,11 +252,11 @@ function ManageImportListsModalContent(
isDisabled={!anySelected} isDisabled={!anySelected}
onPress={onTagsPress} onPress={onTagsPress}
> >
Set Tags {translate('SetTags')}
</SpinnerButton> </SpinnerButton>
</div> </div>
<Button onPress={onModalClose}>Close</Button> <Button onPress={onModalClose}>{translate('Close')}</Button>
</ModalFooter> </ModalFooter>
<ManageImportListsEditModal <ManageImportListsEditModal
@ -270,9 +276,11 @@ function ManageImportListsModalContent(
<ConfirmModal <ConfirmModal
isOpen={isDeleteModalOpen} isOpen={isDeleteModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title="Delete Import List(s)" title={translate('DeleteSelectedImportLists')}
message={`Are you sure you want to delete ${selectedIds.length} import list(s)?`} message={translate('DeleteSelectedImportListsMessageText', {
confirmLabel="Delete" count: selectedIds.length,
})}
confirmLabel={translate('Delete')}
onConfirm={onConfirmDelete} onConfirm={onConfirmDelete}
onCancel={onDeleteModalClose} onCancel={onDeleteModalClose}
/> />

View File

@ -17,6 +17,7 @@ import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds, sizes } from 'Helpers/Props'; import { inputTypes, kinds, sizes } from 'Helpers/Props';
import createTagsSelector from 'Store/Selectors/createTagsSelector'; import createTagsSelector from 'Store/Selectors/createTagsSelector';
import ImportList from 'typings/ImportList'; import ImportList from 'typings/ImportList';
import translate from 'Utilities/String/translate';
import styles from './TagsModalContent.css'; import styles from './TagsModalContent.css';
interface TagsModalContentProps { interface TagsModalContentProps {
@ -36,7 +37,7 @@ function TagsModalContent(props: TagsModalContentProps) {
const [tags, setTags] = useState<number[]>([]); const [tags, setTags] = useState<number[]>([]);
const [applyTags, setApplyTags] = useState('add'); const [applyTags, setApplyTags] = useState('add');
const seriesTags = useMemo(() => { const importListsTags = useMemo(() => {
const tags = ids.reduce((acc: number[], id) => { const tags = ids.reduce((acc: number[], id) => {
const s = allImportLists.items.find((s: ImportList) => s.id === id); const s = allImportLists.items.find((s: ImportList) => s.id === id);
@ -69,19 +70,19 @@ function TagsModalContent(props: TagsModalContentProps) {
}, [tags, applyTags, onApplyTagsPress]); }, [tags, applyTags, onApplyTagsPress]);
const applyTagsOptions = [ const applyTagsOptions = [
{ key: 'add', value: 'Add' }, { key: 'add', value: translate('Add') },
{ key: 'remove', value: 'Remove' }, { key: 'remove', value: translate('Remove') },
{ key: 'replace', value: 'Replace' }, { key: 'replace', value: translate('Replace') },
]; ];
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader>Tags</ModalHeader> <ModalHeader>{translate('Tags')}</ModalHeader>
<ModalBody> <ModalBody>
<Form> <Form>
<FormGroup> <FormGroup>
<FormLabel>Tags</FormLabel> <FormLabel>{translate('Tags')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.TAG} type={inputTypes.TAG}
@ -92,7 +93,7 @@ function TagsModalContent(props: TagsModalContentProps) {
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<FormLabel>Apply Tags</FormLabel> <FormLabel>{translate('ApplyTags')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.SELECT} type={inputTypes.SELECT}
@ -100,20 +101,20 @@ function TagsModalContent(props: TagsModalContentProps) {
value={applyTags} value={applyTags}
values={applyTagsOptions} values={applyTagsOptions}
helpTexts={[ helpTexts={[
'How to apply tags to the selected list', translate('ApplyTagsHelpTexts1'),
'Add: Add the tags the existing list of tags', translate('ApplyTagsHelpTexts2'),
'Remove: Remove the entered tags', translate('ApplyTagsHelpTexts3'),
'Replace: Replace the tags with the entered tags (enter no tags to clear all tags)', translate('ApplyTagsHelpTexts4'),
]} ]}
onChange={onApplyTagsChange} onChange={onApplyTagsChange}
/> />
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<FormLabel>Result</FormLabel> <FormLabel>{translate('Result')}</FormLabel>
<div className={styles.result}> <div className={styles.result}>
{seriesTags.map((id) => { {importListsTags.map((id) => {
const tag = tagList.find((t) => t.id === id); const tag = tagList.find((t) => t.id === id);
if (!tag) { if (!tag) {
@ -127,7 +128,11 @@ function TagsModalContent(props: TagsModalContentProps) {
return ( return (
<Label <Label
key={tag.id} key={tag.id}
title={removeTag ? 'Removing tag' : 'Existing tag'} title={
removeTag
? translate('RemovingTag')
: translate('ExistingTag')
}
kind={removeTag ? kinds.INVERSE : kinds.INFO} kind={removeTag ? kinds.INVERSE : kinds.INFO}
size={sizes.LARGE} size={sizes.LARGE}
> >
@ -144,14 +149,14 @@ function TagsModalContent(props: TagsModalContentProps) {
return null; return null;
} }
if (seriesTags.indexOf(id) > -1) { if (importListsTags.indexOf(id) > -1) {
return null; return null;
} }
return ( return (
<Label <Label
key={tag.id} key={tag.id}
title={'Adding tag'} title={translate('AddingTag')}
kind={kinds.SUCCESS} kind={kinds.SUCCESS}
size={sizes.LARGE} size={sizes.LARGE}
> >
@ -165,10 +170,10 @@ function TagsModalContent(props: TagsModalContentProps) {
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button onPress={onModalClose}>Cancel</Button> <Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button kind={kinds.PRIMARY} onPress={onApplyPress}> <Button kind={kinds.PRIMARY} onPress={onApplyPress}>
Apply {translate('Apply')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>

View File

@ -6,6 +6,7 @@ import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector'; import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate';
import IndexersConnector from './Indexers/IndexersConnector'; import IndexersConnector from './Indexers/IndexersConnector';
import ManageIndexersModal from './Indexers/Manage/ManageIndexersModal'; import ManageIndexersModal from './Indexers/Manage/ManageIndexersModal';
import IndexerOptionsConnector from './Options/IndexerOptionsConnector'; import IndexerOptionsConnector from './Options/IndexerOptionsConnector';
@ -84,7 +85,7 @@ class IndexerSettings extends Component {
/> />
<PageToolbarButton <PageToolbarButton
label="Manage Indexers" label={translate('ManageIndexers')}
iconName={icons.MANAGE} iconName={icons.MANAGE}
onPress={this.onManageIndexersPress} onPress={this.onManageIndexersPress}
/> />

View File

@ -27,9 +27,9 @@ interface ManageIndexersEditModalContentProps {
const NO_CHANGE = 'noChange'; const NO_CHANGE = 'noChange';
const enableOptions = [ const enableOptions = [
{ key: NO_CHANGE, value: 'No Change', disabled: true }, { key: NO_CHANGE, value: translate('NoChange'), disabled: true },
{ key: 'enabled', value: 'Enabled' }, { key: 'enabled', value: translate('Enabled') },
{ key: 'disabled', value: 'Disabled' }, { key: 'disabled', value: translate('Disabled') },
]; ];
function ManageIndexersEditModalContent( function ManageIndexersEditModalContent(
@ -97,7 +97,7 @@ function ManageIndexersEditModalContent(
setPriority(value); setPriority(value);
break; break;
default: default:
console.warn('EditIndexersModalContent Unknown Input'); console.warn(`EditIndexersModalContent Unknown Input: '${name}'`);
} }
}, },
[] []
@ -111,7 +111,7 @@ function ManageIndexersEditModalContent(
<ModalBody> <ModalBody>
<FormGroup> <FormGroup>
<FormLabel>{translate('EnableRss')}</FormLabel> <FormLabel>{translate('EnableRSS')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.SELECT} type={inputTypes.SELECT}
@ -162,13 +162,15 @@ function ManageIndexersEditModalContent(
<ModalFooter className={styles.modalFooter}> <ModalFooter className={styles.modalFooter}>
<div className={styles.selected}> <div className={styles.selected}>
{translate('{count} indexers selected', { count: selectedCount })} {translate('CountIndexersSelected', {
count: selectedCount,
})}
</div> </div>
<div> <div>
<Button onPress={onModalClose}>{translate('Cancel')}</Button> <Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button onPress={save}>{translate('Apply Changes')}</Button> <Button onPress={save}>{translate('ApplyChanges')}</Button>
</div> </div>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>

View File

@ -1,6 +1,7 @@
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { IndexerAppState } from 'App/State/SettingsAppState'; import { IndexerAppState } from 'App/State/SettingsAppState';
import Alert from 'Components/Alert';
import Button from 'Components/Link/Button'; import Button from 'Components/Link/Button';
import SpinnerButton from 'Components/Link/SpinnerButton'; import SpinnerButton from 'Components/Link/SpinnerButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
@ -20,6 +21,7 @@ import {
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector'; import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import { SelectStateInputProps } from 'typings/props'; import { SelectStateInputProps } from 'typings/props';
import getErrorMessage from 'Utilities/Object/getErrorMessage'; import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds'; import getSelectedIds from 'Utilities/Table/getSelectedIds';
import ManageIndexersEditModal from './Edit/ManageIndexersEditModal'; import ManageIndexersEditModal from './Edit/ManageIndexersEditModal';
import ManageIndexersModalRow from './ManageIndexersModalRow'; import ManageIndexersModalRow from './ManageIndexersModalRow';
@ -34,43 +36,43 @@ type OnSelectedChangeCallback = React.ComponentProps<
const COLUMNS = [ const COLUMNS = [
{ {
name: 'name', name: 'name',
label: 'Name', label: translate('Name'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'implementation', name: 'implementation',
label: 'Implementation', label: translate('Implementation'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'enableRss', name: 'enableRss',
label: 'Enable RSS', label: translate('EnableRSS'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'enableAutomaticSearch', name: 'enableAutomaticSearch',
label: 'Enable Automatic Search', label: translate('EnableAutomaticSearch'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'enableInteractiveSearch', name: 'enableInteractiveSearch',
label: 'Enable Interactive Search', label: translate('EnableInteractiveSearch'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'priority', name: 'priority',
label: 'Priority', label: translate('Priority'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
{ {
name: 'tags', name: 'tags',
label: 'Tags', label: translate('Tags'),
isSortable: true, isSortable: true,
isVisible: true, isVisible: true,
}, },
@ -189,17 +191,21 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
[items, setSelectState] [items, setSelectState]
); );
const errorMessage = getErrorMessage(error, 'Unable to load import lists.'); const errorMessage = getErrorMessage(error, 'Unable to load indexers.');
const anySelected = selectedCount > 0; const anySelected = selectedCount > 0;
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader>Manage Import Lists</ModalHeader> <ModalHeader>{translate('ManageIndexers')}</ModalHeader>
<ModalBody> <ModalBody>
{isFetching ? <LoadingIndicator /> : null} {isFetching ? <LoadingIndicator /> : null}
{error ? <div>{errorMessage}</div> : null} {error ? <div>{errorMessage}</div> : null}
{isPopulated && !error && !items.length && (
<Alert kind={kinds.INFO}>{translate('NoIndexersFound')}</Alert>
)}
{isPopulated && !!items.length && !isFetching && !isFetching ? ( {isPopulated && !!items.length && !isFetching && !isFetching ? (
<Table <Table
columns={COLUMNS} columns={COLUMNS}
@ -234,7 +240,7 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
isDisabled={!anySelected} isDisabled={!anySelected}
onPress={onDeletePress} onPress={onDeletePress}
> >
Delete {translate('Delete')}
</SpinnerButton> </SpinnerButton>
<SpinnerButton <SpinnerButton
@ -242,7 +248,7 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
isDisabled={!anySelected} isDisabled={!anySelected}
onPress={onEditPress} onPress={onEditPress}
> >
Edit {translate('Edit')}
</SpinnerButton> </SpinnerButton>
<SpinnerButton <SpinnerButton
@ -250,11 +256,11 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
isDisabled={!anySelected} isDisabled={!anySelected}
onPress={onTagsPress} onPress={onTagsPress}
> >
Set Tags {translate('SetTags')}
</SpinnerButton> </SpinnerButton>
</div> </div>
<Button onPress={onModalClose}>Close</Button> <Button onPress={onModalClose}>{translate('Close')}</Button>
</ModalFooter> </ModalFooter>
<ManageIndexersEditModal <ManageIndexersEditModal
@ -274,9 +280,11 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
<ConfirmModal <ConfirmModal
isOpen={isDeleteModalOpen} isOpen={isDeleteModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title="Delete Import List(s)" title={translate('DeleteSelectedIndexers')}
message={`Are you sure you want to delete ${selectedIds.length} import list(s)?`} message={translate('DeleteSelectedIndexersMessageText', {
confirmLabel="Delete" count: selectedIds.length,
})}
confirmLabel={translate('Delete')}
onConfirm={onConfirmDelete} onConfirm={onConfirmDelete}
onCancel={onDeleteModalClose} onCancel={onDeleteModalClose}
/> />

View File

@ -1,10 +1,13 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import Label from 'Components/Label';
import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell'; import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import Column from 'Components/Table/Column'; import Column from 'Components/Table/Column';
import TableRow from 'Components/Table/TableRow'; import TableRow from 'Components/Table/TableRow';
import TagListConnector from 'Components/TagListConnector'; import TagListConnector from 'Components/TagListConnector';
import { kinds } from 'Helpers/Props';
import { SelectStateInputProps } from 'typings/props'; import { SelectStateInputProps } from 'typings/props';
import translate from 'Utilities/String/translate';
import styles from './ManageIndexersModalRow.css'; import styles from './ManageIndexersModalRow.css';
interface ManageIndexersModalRowProps { interface ManageIndexersModalRowProps {
@ -59,15 +62,30 @@ function ManageIndexersModalRow(props: ManageIndexersModalRowProps) {
</TableRowCell> </TableRowCell>
<TableRowCell className={styles.enableRss}> <TableRowCell className={styles.enableRss}>
{enableRss ? 'Yes' : 'No'} <Label
kind={enableRss ? kinds.SUCCESS : kinds.DISABLED}
outline={!enableRss}
>
{enableRss ? translate('Yes') : translate('No')}
</Label>
</TableRowCell> </TableRowCell>
<TableRowCell className={styles.enableAutomaticSearch}> <TableRowCell className={styles.enableAutomaticSearch}>
{enableAutomaticSearch ? 'Yes' : 'No'} <Label
kind={enableAutomaticSearch ? kinds.SUCCESS : kinds.DISABLED}
outline={!enableAutomaticSearch}
>
{enableAutomaticSearch ? translate('Yes') : translate('No')}
</Label>
</TableRowCell> </TableRowCell>
<TableRowCell className={styles.enableInteractiveSearch}> <TableRowCell className={styles.enableInteractiveSearch}>
{enableInteractiveSearch ? 'Yes' : 'No'} <Label
kind={enableInteractiveSearch ? kinds.SUCCESS : kinds.DISABLED}
outline={!enableInteractiveSearch}
>
{enableInteractiveSearch ? translate('Yes') : translate('No')}
</Label>
</TableRowCell> </TableRowCell>
<TableRowCell className={styles.priority}>{priority}</TableRowCell> <TableRowCell className={styles.priority}>{priority}</TableRowCell>

View File

@ -17,6 +17,7 @@ import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds, sizes } from 'Helpers/Props'; import { inputTypes, kinds, sizes } from 'Helpers/Props';
import createTagsSelector from 'Store/Selectors/createTagsSelector'; import createTagsSelector from 'Store/Selectors/createTagsSelector';
import Indexer from 'typings/Indexer'; import Indexer from 'typings/Indexer';
import translate from 'Utilities/String/translate';
import styles from './TagsModalContent.css'; import styles from './TagsModalContent.css';
interface TagsModalContentProps { interface TagsModalContentProps {
@ -36,7 +37,7 @@ function TagsModalContent(props: TagsModalContentProps) {
const [tags, setTags] = useState<number[]>([]); const [tags, setTags] = useState<number[]>([]);
const [applyTags, setApplyTags] = useState('add'); const [applyTags, setApplyTags] = useState('add');
const seriesTags = useMemo(() => { const indexersTags = useMemo(() => {
const tags = ids.reduce((acc: number[], id) => { const tags = ids.reduce((acc: number[], id) => {
const s = allIndexers.items.find((s: Indexer) => s.id === id); const s = allIndexers.items.find((s: Indexer) => s.id === id);
@ -69,19 +70,19 @@ function TagsModalContent(props: TagsModalContentProps) {
}, [tags, applyTags, onApplyTagsPress]); }, [tags, applyTags, onApplyTagsPress]);
const applyTagsOptions = [ const applyTagsOptions = [
{ key: 'add', value: 'Add' }, { key: 'add', value: translate('Add') },
{ key: 'remove', value: 'Remove' }, { key: 'remove', value: translate('Remove') },
{ key: 'replace', value: 'Replace' }, { key: 'replace', value: translate('Replace') },
]; ];
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader>Tags</ModalHeader> <ModalHeader>{translate('Tags')}</ModalHeader>
<ModalBody> <ModalBody>
<Form> <Form>
<FormGroup> <FormGroup>
<FormLabel>Tags</FormLabel> <FormLabel>{translate('Tags')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.TAG} type={inputTypes.TAG}
@ -92,7 +93,7 @@ function TagsModalContent(props: TagsModalContentProps) {
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<FormLabel>Apply Tags</FormLabel> <FormLabel>{translate('ApplyTags')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.SELECT} type={inputTypes.SELECT}
@ -100,20 +101,20 @@ function TagsModalContent(props: TagsModalContentProps) {
value={applyTags} value={applyTags}
values={applyTagsOptions} values={applyTagsOptions}
helpTexts={[ helpTexts={[
'How to apply tags to the selected indexer(s)', translate('ApplyTagsHelpTexts1'),
'Add: Add the tags the existing list of tags', translate('ApplyTagsHelpTexts2'),
'Remove: Remove the entered tags', translate('ApplyTagsHelpTexts3'),
'Replace: Replace the tags with the entered tags (enter no tags to clear all tags)', translate('ApplyTagsHelpTexts4'),
]} ]}
onChange={onApplyTagsChange} onChange={onApplyTagsChange}
/> />
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<FormLabel>Result</FormLabel> <FormLabel>{translate('Result')}</FormLabel>
<div className={styles.result}> <div className={styles.result}>
{seriesTags.map((id) => { {indexersTags.map((id) => {
const tag = tagList.find((t) => t.id === id); const tag = tagList.find((t) => t.id === id);
if (!tag) { if (!tag) {
@ -127,7 +128,11 @@ function TagsModalContent(props: TagsModalContentProps) {
return ( return (
<Label <Label
key={tag.id} key={tag.id}
title={removeTag ? 'Removing tag' : 'Existing tag'} title={
removeTag
? translate('RemovingTag')
: translate('ExistingTag')
}
kind={removeTag ? kinds.INVERSE : kinds.INFO} kind={removeTag ? kinds.INVERSE : kinds.INFO}
size={sizes.LARGE} size={sizes.LARGE}
> >
@ -144,14 +149,14 @@ function TagsModalContent(props: TagsModalContentProps) {
return null; return null;
} }
if (seriesTags.indexOf(id) > -1) { if (indexersTags.indexOf(id) > -1) {
return null; return null;
} }
return ( return (
<Label <Label
key={tag.id} key={tag.id}
title={'Adding tag'} title={translate('AddingTag')}
kind={kinds.SUCCESS} kind={kinds.SUCCESS}
size={sizes.LARGE} size={sizes.LARGE}
> >
@ -165,10 +170,10 @@ function TagsModalContent(props: TagsModalContentProps) {
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button onPress={onModalClose}>Cancel</Button> <Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button kind={kinds.PRIMARY} onPress={onApplyPress}> <Button kind={kinds.PRIMARY} onPress={onApplyPress}>
Apply {translate('Apply')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>

View File

@ -31,9 +31,8 @@ export const DELETE_DOWNLOAD_CLIENT = 'settings/downloadClients/deleteDownloadCl
export const TEST_DOWNLOAD_CLIENT = 'settings/downloadClients/testDownloadClient'; export const TEST_DOWNLOAD_CLIENT = 'settings/downloadClients/testDownloadClient';
export const CANCEL_TEST_DOWNLOAD_CLIENT = 'settings/downloadClients/cancelTestDownloadClient'; export const CANCEL_TEST_DOWNLOAD_CLIENT = 'settings/downloadClients/cancelTestDownloadClient';
export const TEST_ALL_DOWNLOAD_CLIENTS = 'settings/downloadClients/testAllDownloadClients'; export const TEST_ALL_DOWNLOAD_CLIENTS = 'settings/downloadClients/testAllDownloadClients';
export const BULK_DELETE_DOWNLOAD_CLIENTS = 'settings/downloadClients/bulkDeleteDownloadClients';
export const BULK_EDIT_DOWNLOAD_CLIENTS = 'settings/downloadClients/bulkEditDownloadClients'; export const BULK_EDIT_DOWNLOAD_CLIENTS = 'settings/downloadClients/bulkEditDownloadClients';
export const BULK_DELETE_DOWNLOAD_CLIENTS = 'settings/downloadClients/bulkDeleteDownloadClients';
// //
// Action Creators // Action Creators
@ -48,9 +47,8 @@ export const deleteDownloadClient = createThunk(DELETE_DOWNLOAD_CLIENT);
export const testDownloadClient = createThunk(TEST_DOWNLOAD_CLIENT); export const testDownloadClient = createThunk(TEST_DOWNLOAD_CLIENT);
export const cancelTestDownloadClient = createThunk(CANCEL_TEST_DOWNLOAD_CLIENT); export const cancelTestDownloadClient = createThunk(CANCEL_TEST_DOWNLOAD_CLIENT);
export const testAllDownloadClients = createThunk(TEST_ALL_DOWNLOAD_CLIENTS); export const testAllDownloadClients = createThunk(TEST_ALL_DOWNLOAD_CLIENTS);
export const bulkDeleteDownloadClients = createThunk(BULK_DELETE_DOWNLOAD_CLIENTS);
export const bulkEditDownloadClients = createThunk(BULK_EDIT_DOWNLOAD_CLIENTS); export const bulkEditDownloadClients = createThunk(BULK_EDIT_DOWNLOAD_CLIENTS);
export const bulkDeleteDownloadClients = createThunk(BULK_DELETE_DOWNLOAD_CLIENTS);
export const setDownloadClientValue = createAction(SET_DOWNLOAD_CLIENT_VALUE, (payload) => { export const setDownloadClientValue = createAction(SET_DOWNLOAD_CLIENT_VALUE, (payload) => {
return { return {
@ -106,8 +104,8 @@ export default {
[TEST_DOWNLOAD_CLIENT]: createTestProviderHandler(section, '/downloadclient'), [TEST_DOWNLOAD_CLIENT]: createTestProviderHandler(section, '/downloadclient'),
[CANCEL_TEST_DOWNLOAD_CLIENT]: createCancelTestProviderHandler(section), [CANCEL_TEST_DOWNLOAD_CLIENT]: createCancelTestProviderHandler(section),
[TEST_ALL_DOWNLOAD_CLIENTS]: createTestAllProvidersHandler(section, '/downloadclient'), [TEST_ALL_DOWNLOAD_CLIENTS]: createTestAllProvidersHandler(section, '/downloadclient'),
[BULK_DELETE_DOWNLOAD_CLIENTS]: createBulkRemoveItemHandler(section, '/downloadclient/bulk'), [BULK_EDIT_DOWNLOAD_CLIENTS]: createBulkEditItemHandler(section, '/downloadclient/bulk'),
[BULK_EDIT_DOWNLOAD_CLIENTS]: createBulkEditItemHandler(section, '/downloadclient/bulk') [BULK_DELETE_DOWNLOAD_CLIENTS]: createBulkRemoveItemHandler(section, '/downloadclient/bulk')
}, },
// //

View File

@ -31,9 +31,8 @@ export const DELETE_IMPORT_LIST = 'settings/importlists/deleteImportList';
export const TEST_IMPORT_LIST = 'settings/importlists/testImportList'; export const TEST_IMPORT_LIST = 'settings/importlists/testImportList';
export const CANCEL_TEST_IMPORT_LIST = 'settings/importlists/cancelTestImportList'; export const CANCEL_TEST_IMPORT_LIST = 'settings/importlists/cancelTestImportList';
export const TEST_ALL_IMPORT_LISTS = 'settings/importlists/testAllImportLists'; export const TEST_ALL_IMPORT_LISTS = 'settings/importlists/testAllImportLists';
export const BULK_DELETE_IMPORT_LISTS = 'settings/importlists/bulkDeleteImportLists';
export const BULK_EDIT_IMPORT_LISTS = 'settings/importlists/bulkEditImportLists'; export const BULK_EDIT_IMPORT_LISTS = 'settings/importlists/bulkEditImportLists';
export const BULK_DELETE_IMPORT_LISTS = 'settings/importlists/bulkDeleteImportLists';
// //
// Action Creators // Action Creators
@ -48,9 +47,8 @@ export const deleteImportList = createThunk(DELETE_IMPORT_LIST);
export const testImportList = createThunk(TEST_IMPORT_LIST); export const testImportList = createThunk(TEST_IMPORT_LIST);
export const cancelTestImportList = createThunk(CANCEL_TEST_IMPORT_LIST); export const cancelTestImportList = createThunk(CANCEL_TEST_IMPORT_LIST);
export const testAllImportLists = createThunk(TEST_ALL_IMPORT_LISTS); export const testAllImportLists = createThunk(TEST_ALL_IMPORT_LISTS);
export const bulkDeleteImportLists = createThunk(BULK_DELETE_IMPORT_LISTS);
export const bulkEditImportLists = createThunk(BULK_EDIT_IMPORT_LISTS); export const bulkEditImportLists = createThunk(BULK_EDIT_IMPORT_LISTS);
export const bulkDeleteImportLists = createThunk(BULK_DELETE_IMPORT_LISTS);
export const setImportListValue = createAction(SET_IMPORT_LIST_VALUE, (payload) => { export const setImportListValue = createAction(SET_IMPORT_LIST_VALUE, (payload) => {
return { return {
@ -105,8 +103,8 @@ export default {
[TEST_IMPORT_LIST]: createTestProviderHandler(section, '/importlist'), [TEST_IMPORT_LIST]: createTestProviderHandler(section, '/importlist'),
[CANCEL_TEST_IMPORT_LIST]: createCancelTestProviderHandler(section), [CANCEL_TEST_IMPORT_LIST]: createCancelTestProviderHandler(section),
[TEST_ALL_IMPORT_LISTS]: createTestAllProvidersHandler(section, '/importlist'), [TEST_ALL_IMPORT_LISTS]: createTestAllProvidersHandler(section, '/importlist'),
[BULK_DELETE_IMPORT_LISTS]: createBulkRemoveItemHandler(section, '/importlist/bulk'), [BULK_EDIT_IMPORT_LISTS]: createBulkEditItemHandler(section, '/importlist/bulk'),
[BULK_EDIT_IMPORT_LISTS]: createBulkEditItemHandler(section, '/importlist/bulk') [BULK_DELETE_IMPORT_LISTS]: createBulkRemoveItemHandler(section, '/importlist/bulk')
}, },
// //

View File

@ -1,4 +1,6 @@
import { createAction } from 'redux-actions'; import { createAction } from 'redux-actions';
import createBulkEditItemHandler from 'Store/Actions/Creators/createBulkEditItemHandler';
import createBulkRemoveItemHandler from 'Store/Actions/Creators/createBulkRemoveItemHandler';
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler'; import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
import createFetchSchemaHandler from 'Store/Actions/Creators/createFetchSchemaHandler'; import createFetchSchemaHandler from 'Store/Actions/Creators/createFetchSchemaHandler';
import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler'; import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler';
@ -11,8 +13,6 @@ import { createThunk } from 'Store/thunks';
import getSectionState from 'Utilities/State/getSectionState'; import getSectionState from 'Utilities/State/getSectionState';
import selectProviderSchema from 'Utilities/State/selectProviderSchema'; import selectProviderSchema from 'Utilities/State/selectProviderSchema';
import updateSectionState from 'Utilities/State/updateSectionState'; import updateSectionState from 'Utilities/State/updateSectionState';
import createBulkEditItemHandler from '../Creators/createBulkEditItemHandler';
import createBulkRemoveItemHandler from '../Creators/createBulkRemoveItemHandler';
// //
// Variables // Variables
@ -34,9 +34,8 @@ export const DELETE_INDEXER = 'settings/indexers/deleteIndexer';
export const TEST_INDEXER = 'settings/indexers/testIndexer'; export const TEST_INDEXER = 'settings/indexers/testIndexer';
export const CANCEL_TEST_INDEXER = 'settings/indexers/cancelTestIndexer'; export const CANCEL_TEST_INDEXER = 'settings/indexers/cancelTestIndexer';
export const TEST_ALL_INDEXERS = 'settings/indexers/testAllIndexers'; export const TEST_ALL_INDEXERS = 'settings/indexers/testAllIndexers';
export const BULK_DELETE_INDEXERS = 'settings/indexers/bulkDeleteIndexers';
export const BULK_EDIT_INDEXERS = 'settings/indexers/bulkEditIndexers'; export const BULK_EDIT_INDEXERS = 'settings/indexers/bulkEditIndexers';
export const BULK_DELETE_INDEXERS = 'settings/indexers/bulkDeleteIndexers';
// //
// Action Creators // Action Creators
@ -52,9 +51,8 @@ export const deleteIndexer = createThunk(DELETE_INDEXER);
export const testIndexer = createThunk(TEST_INDEXER); export const testIndexer = createThunk(TEST_INDEXER);
export const cancelTestIndexer = createThunk(CANCEL_TEST_INDEXER); export const cancelTestIndexer = createThunk(CANCEL_TEST_INDEXER);
export const testAllIndexers = createThunk(TEST_ALL_INDEXERS); export const testAllIndexers = createThunk(TEST_ALL_INDEXERS);
export const bulkDeleteIndexers = createThunk(BULK_DELETE_INDEXERS);
export const bulkEditIndexers = createThunk(BULK_EDIT_INDEXERS); export const bulkEditIndexers = createThunk(BULK_EDIT_INDEXERS);
export const bulkDeleteIndexers = createThunk(BULK_DELETE_INDEXERS);
export const setIndexerValue = createAction(SET_INDEXER_VALUE, (payload) => { export const setIndexerValue = createAction(SET_INDEXER_VALUE, (payload) => {
return { return {
@ -110,9 +108,8 @@ export default {
[TEST_INDEXER]: createTestProviderHandler(section, '/indexer'), [TEST_INDEXER]: createTestProviderHandler(section, '/indexer'),
[CANCEL_TEST_INDEXER]: createCancelTestProviderHandler(section), [CANCEL_TEST_INDEXER]: createCancelTestProviderHandler(section),
[TEST_ALL_INDEXERS]: createTestAllProvidersHandler(section, '/indexer'), [TEST_ALL_INDEXERS]: createTestAllProvidersHandler(section, '/indexer'),
[BULK_EDIT_INDEXERS]: createBulkEditItemHandler(section, '/indexer/bulk'),
[BULK_DELETE_INDEXERS]: createBulkRemoveItemHandler(section, '/indexer/bulk'), [BULK_DELETE_INDEXERS]: createBulkRemoveItemHandler(section, '/indexer/bulk')
[BULK_EDIT_INDEXERS]: createBulkEditItemHandler(section, '/indexer/bulk')
}, },
// //

View File

@ -1,40 +1,63 @@
{ {
"Add": "Add",
"Added": "Added", "Added": "Added",
"AddingTag": "Adding tag",
"ApiKeyValidationHealthCheckMessage": "Please update your API key to be at least {0} characters long. You can do this via settings or the config file", "ApiKeyValidationHealthCheckMessage": "Please update your API key to be at least {0} characters long. You can do this via settings or the config file",
"AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update", "AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update",
"Apply": "Apply",
"ApplyChanges": "Apply Changes", "ApplyChanges": "Apply Changes",
"ApplyTags": "Apply Tags",
"ApplyTagsHelpTexts1": "How to apply tags to the selected indexers",
"ApplyTagsHelpTexts2": "Add: Add the tags the existing list of tags",
"ApplyTagsHelpTexts3": "Remove: Remove the entered tags",
"ApplyTagsHelpTexts4": "Replace: Replace the tags with the entered tags (enter no tags to clear all tags)",
"AutoAdd": "Auto Add",
"AutomaticAdd": "Automatic Add", "AutomaticAdd": "Automatic Add",
"BlocklistRelease": "Blocklist Release", "BlocklistRelease": "Blocklist Release",
"BlocklistReleaseHelpText": "Prevents Sonarr from automatically grabbing this release again", "BlocklistReleaseHelpText": "Prevents Sonarr from automatically grabbing this release again",
"BlocklistReleases": "Blocklist Releases", "BlocklistReleases": "Blocklist Releases",
"Browser Reload Required": "Browser Reload Required", "Browser Reload Required": "Browser Reload Required",
"Cancel": "Cancel",
"CloneCondition": "Clone Condition", "CloneCondition": "Clone Condition",
"CloneCustomFormat": "Clone Custom Format", "CloneCustomFormat": "Clone Custom Format",
"Close": "Close", "Close": "Close",
"CountDownloadClientsSelected": "{count} download client(s) selected",
"CountImportListsSelected": "{count} import list(s) selected",
"CountIndexersSelected": "{count} indexer(s) selected",
"CountSeasons": "{count} seasons", "CountSeasons": "{count} seasons",
"Delete": "Delete", "Delete": "Delete",
"DeleteCondition": "Delete Condition", "DeleteCondition": "Delete Condition",
"DeleteConditionMessageText": "Are you sure you want to delete the condition '{0}'?", "DeleteConditionMessageText": "Are you sure you want to delete the condition '{0}'?",
"DeleteCustomFormat": "Delete Custom Format", "DeleteCustomFormat": "Delete Custom Format",
"DeleteCustomFormatMessageText": "Are you sure you want to delete the custom format '{0}'?", "DeleteCustomFormatMessageText": "Are you sure you want to delete the custom format '{0}'?",
"DeleteSelectedDownloadClients": "Delete Download Client(s)",
"DeleteSelectedDownloadClientsMessageText": "Are you sure you want to delete {count} selected download client(s)?",
"DeleteSelectedImportLists": "Delete Import List(s)",
"DeleteSelectedImportListsMessageText": "Are you sure you want to delete {count} selected import list(s)?",
"DeleteSelectedIndexers": "Delete Indexer(s)",
"DeleteSelectedIndexersMessageText": "Are you sure you want to delete {count} selected indexer(s)?",
"Disabled": "Disabled",
"DownloadClientCheckNoneAvailableHealthCheckMessage": "No download client is available", "DownloadClientCheckNoneAvailableHealthCheckMessage": "No download client is available",
"DownloadClientCheckUnableToCommunicateWithHealthCheckMessage": "Unable to communicate with {0}.", "DownloadClientCheckUnableToCommunicateWithHealthCheckMessage": "Unable to communicate with {0}.",
"DownloadClientRootFolderHealthCheckMessage": "Download client {0} places downloads in the root folder {1}. You should not download to a root folder.", "DownloadClientRootFolderHealthCheckMessage": "Download client {0} places downloads in the root folder {1}. You should not download to a root folder.",
"DownloadClientSortingHealthCheckMessage": "Download client {0} has {1} sorting enabled for Sonarr's category. You should disable sorting in your download client to avoid import issues.", "DownloadClientSortingHealthCheckMessage": "Download client {0} has {1} sorting enabled for Sonarr's category. You should disable sorting in your download client to avoid import issues.",
"DownloadClientStatusAllClientHealthCheckMessage": "All download clients are unavailable due to failures", "DownloadClientStatusAllClientHealthCheckMessage": "All download clients are unavailable due to failures",
"DownloadClientStatusSingleClientHealthCheckMessage": "Download clients unavailable due to failures: {0}", "DownloadClientStatusSingleClientHealthCheckMessage": "Download clients unavailable due to failures: {0}",
"Edit": "Edit",
"EditSelectedDownloadClients": "Edit Selected Download Clients", "EditSelectedDownloadClients": "Edit Selected Download Clients",
"EditSelectedImportLists": "Edit Selected Import Lists", "EditSelectedImportLists": "Edit Selected Import Lists",
"EditSelectedIndexers": "Edit Selected Indexers", "EditSelectedIndexers": "Edit Selected Indexers",
"EditSeries": "Edit Series", "EditSeries": "Edit Series",
"EnableAutomaticSearch": "Enable Automatic Search", "EnableAutomaticSearch": "Enable Automatic Search",
"EnableInteractiveSearch": "Enable Interactive Search", "EnableInteractiveSearch": "Enable Interactive Search",
"EnableRss": "Enable Rss", "EnableRSS": "Enable RSS",
"Enabled": "Enabled", "Enabled": "Enabled",
"Ended": "Ended", "Ended": "Ended",
"ExistingTag": "Existing tag",
"ExportCustomFormat": "Export Custom Format", "ExportCustomFormat": "Export Custom Format",
"HiddenClickToShow": "Hidden, click to show", "HiddenClickToShow": "Hidden, click to show",
"HideAdvanced": "Hide Advanced", "HideAdvanced": "Hide Advanced",
"Implementation": "Implementation",
"ImportListRootFolderMissingRootHealthCheckMessage": "Missing root folder for import list(s): {0}", "ImportListRootFolderMissingRootHealthCheckMessage": "Missing root folder for import list(s): {0}",
"ImportListRootFolderMultipleMissingRootsHealthCheckMessage": "Multiple root folders are missing for import lists: {0}", "ImportListRootFolderMultipleMissingRootsHealthCheckMessage": "Multiple root folders are missing for import lists: {0}",
"ImportListStatusAllUnavailableHealthCheckMessage": "All lists are unavailable due to failures", "ImportListStatusAllUnavailableHealthCheckMessage": "All lists are unavailable due to failures",
@ -54,11 +77,22 @@
"IndexerStatusUnavailableHealthCheckMessage": "Indexers unavailable due to failures: {0}", "IndexerStatusUnavailableHealthCheckMessage": "Indexers unavailable due to failures: {0}",
"Language": "Language", "Language": "Language",
"Language that Sonarr will use for UI": "Language that Sonarr will use for UI", "Language that Sonarr will use for UI": "Language that Sonarr will use for UI",
"ManageClients": "Manage Clients",
"ManageDownloadClients": "Manage Download Clients",
"ManageImportLists": "Manage Import Lists",
"ManageIndexers": "Manage Indexers",
"ManageLists": "Manage Lists",
"Monitored": "Monitored", "Monitored": "Monitored",
"MountHealthCheckMessage": "Mount containing a series path is mounted read-only: ", "MountHealthCheckMessage": "Mount containing a series path is mounted read-only: ",
"Name": "Name",
"Negated": "Negated", "Negated": "Negated",
"Network": "Network", "Network": "Network",
"NextAiring": "Next Airing", "NextAiring": "Next Airing",
"No": "No",
"NoChange": "No Change",
"NoDownloadClientsFound": "No download clients found",
"NoImportListsFound": "No import lists found",
"NoIndexersFound": "No indexers found",
"NoSeasons": "No seasons", "NoSeasons": "No seasons",
"OneSeason": "1 season", "OneSeason": "1 season",
"OriginalLanguage": "Original Language", "OriginalLanguage": "Original Language",
@ -87,7 +121,9 @@
"RemotePathMappingRemoteDownloadClientHealthCheckMessage": "Remote download client {0} reported files in {1} but this directory does not appear to exist. Likely missing remote path mapping.", "RemotePathMappingRemoteDownloadClientHealthCheckMessage": "Remote download client {0} reported files in {1} but this directory does not appear to exist. Likely missing remote path mapping.",
"RemotePathMappingWrongOSPathHealthCheckMessage": "Remote download client {0} places downloads in {1} but this is not a valid {2} path. Review your remote path mappings and download client settings.", "RemotePathMappingWrongOSPathHealthCheckMessage": "Remote download client {0} places downloads in {1} but this is not a valid {2} path. Review your remote path mappings and download client settings.",
"Remove": "Remove", "Remove": "Remove",
"RemoveCompleted": "Remove Completed",
"RemoveCompletedDownloads": "Remove Completed Downloads", "RemoveCompletedDownloads": "Remove Completed Downloads",
"RemoveFailed": "Remove Failed",
"RemoveFailedDownloads": "Remove Failed Downloads", "RemoveFailedDownloads": "Remove Failed Downloads",
"RemoveFromDownloadClient": "Remove From Download Client", "RemoveFromDownloadClient": "Remove From Download Client",
"RemoveFromDownloadClientHelpTextWarning": "Removing will remove the download and the file(s) from the download client.", "RemoveFromDownloadClientHelpTextWarning": "Removing will remove the download and the file(s) from the download client.",
@ -97,19 +133,25 @@
"RemoveSelectedItemsQueueMessageText": "Are you sure you want to remove {0} items from the queue?", "RemoveSelectedItemsQueueMessageText": "Are you sure you want to remove {0} items from the queue?",
"RemovedSeriesMultipleRemovedHealthCheckMessage": "Series {0} were removed from TheTVDB", "RemovedSeriesMultipleRemovedHealthCheckMessage": "Series {0} were removed from TheTVDB",
"RemovedSeriesSingleRemovedHealthCheckMessage": "Series {0} was removed from TheTVDB", "RemovedSeriesSingleRemovedHealthCheckMessage": "Series {0} was removed from TheTVDB",
"RemovingTag": "Removing tag",
"Replace": "Replace",
"Required": "Required", "Required": "Required",
"Result": "Result",
"RootFolder": "Root Folder", "RootFolder": "Root Folder",
"RootFolderMissingHealthCheckMessage": "Missing root folder: {0}", "RootFolderMissingHealthCheckMessage": "Missing root folder: {0}",
"RootFolderMultipleMissingHealthCheckMessage": "Multiple root folders are missing: {0}", "RootFolderMultipleMissingHealthCheckMessage": "Multiple root folders are missing: {0}",
"SearchForMonitoredEpisodes": "Search for monitored episodes", "SearchForMonitoredEpisodes": "Search for monitored episodes",
"SetTags": "Set Tags",
"ShowAdvanced": "Show Advanced", "ShowAdvanced": "Show Advanced",
"ShownClickToHide": "Shown, click to hide", "ShownClickToHide": "Shown, click to hide",
"SizeOnDisk": "Size on disk", "SizeOnDisk": "Size on disk",
"SystemTimeHealthCheckMessage": "System time is off by more than 1 day. Scheduled tasks may not run correctly until the time is corrected", "SystemTimeHealthCheckMessage": "System time is off by more than 1 day. Scheduled tasks may not run correctly until the time is corrected",
"Tags": "Tags",
"UI Language": "UI Language", "UI Language": "UI Language",
"Unmonitored": "Unmonitored", "Unmonitored": "Unmonitored",
"UpdateAvailableHealthCheckMessage": "New update is available", "UpdateAvailableHealthCheckMessage": "New update is available",
"UpdateStartupNotWritableHealthCheckMessage": "Cannot install update because startup folder '{0}' is not writable by the user '{1}'.", "UpdateStartupNotWritableHealthCheckMessage": "Cannot install update because startup folder '{0}' is not writable by the user '{1}'.",
"UpdateStartupTranslocationHealthCheckMessage": "Cannot install update because startup folder '{0}' is in an App Translocation folder.", "UpdateStartupTranslocationHealthCheckMessage": "Cannot install update because startup folder '{0}' is in an App Translocation folder.",
"UpdateUINotWritableHealthCheckMessage": "Cannot install update because UI folder '{0}' is not writable by the user '{1}'." "UpdateUINotWritableHealthCheckMessage": "Cannot install update because UI folder '{0}' is not writable by the user '{1}'.",
"Yes": "Yes"
} }