mirror of
https://github.com/Sonarr/Sonarr
synced 2024-12-21 23:33:00 +00:00
Add Translations to Settings Pages
This commit is contained in:
parent
8008610d47
commit
f2c31e92ce
102 changed files with 1697 additions and 802 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -128,8 +128,6 @@ coverage*.json
|
|||
setup/Output/
|
||||
*.~is
|
||||
|
||||
UI/
|
||||
|
||||
#VS outout folders
|
||||
bin
|
||||
obj
|
||||
|
|
|
@ -133,7 +133,7 @@ const links = [
|
|||
to: '/settings/general'
|
||||
},
|
||||
{
|
||||
title: () => translate('UI'),
|
||||
title: () => translate('Ui'),
|
||||
to: '/settings/ui'
|
||||
}
|
||||
]
|
||||
|
|
|
@ -11,7 +11,8 @@ 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 { authenticationMethodOptions, authenticationRequiredOptions, authenticationRequiredWarning } from 'Settings/General/SecuritySettings';
|
||||
import { authenticationMethodOptions, authenticationRequiredOptions } from 'Settings/General/SecuritySettings';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './AuthenticationRequiredModalContent.css';
|
||||
|
||||
function onModalClose() {
|
||||
|
@ -54,7 +55,7 @@ function AuthenticationRequiredModalContent(props) {
|
|||
onModalClose={onModalClose}
|
||||
>
|
||||
<ModalHeader>
|
||||
Authentication Required
|
||||
{translate('AuthenticationRequired')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -62,40 +63,40 @@ function AuthenticationRequiredModalContent(props) {
|
|||
className={styles.authRequiredAlert}
|
||||
kind={kinds.WARNING}
|
||||
>
|
||||
{authenticationRequiredWarning}
|
||||
{translate('AuthenticationRequiredWarning')}
|
||||
</Alert>
|
||||
|
||||
{
|
||||
isPopulated && !error ?
|
||||
<div>
|
||||
<FormGroup>
|
||||
<FormLabel>Authentication</FormLabel>
|
||||
<FormLabel>{translate('Authentication')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="authenticationMethod"
|
||||
values={authenticationMethodOptions}
|
||||
helpText="Require Username and Password to access Sonarr"
|
||||
helpText={translate('AuthenticationMethodHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...authenticationMethod}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Authentication Required</FormLabel>
|
||||
<FormLabel>{translate('AuthenticationRequired')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="authenticationRequired"
|
||||
values={authenticationRequiredOptions}
|
||||
helpText="Change which requests authentication is required for. Do not change unless you understand the risks."
|
||||
helpText={translate('AuthenticationRequiredHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...authenticationRequired}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Username</FormLabel>
|
||||
<FormLabel>{translate('Username')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
|
@ -106,7 +107,7 @@ function AuthenticationRequiredModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormLabel>{translate('Password')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.PASSWORD}
|
||||
|
@ -131,7 +132,7 @@ function AuthenticationRequiredModalContent(props) {
|
|||
isDisabled={!authenticationEnabled}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -6,11 +6,12 @@ import PageContentBody from 'Components/Page/PageContentBody';
|
|||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import ParseToolbarButton from 'Parse/ParseToolbarButton';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import CustomFormatsConnector from './CustomFormats/CustomFormatsConnector';
|
||||
|
||||
function CustomFormatSettingsPage() {
|
||||
return (
|
||||
<PageContent title="Custom Format Settings">
|
||||
<PageContent title={translate('CustomFormatsSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
|
|
|
@ -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 CustomFormat from './CustomFormat';
|
||||
import EditCustomFormatModalConnector from './EditCustomFormatModalConnector';
|
||||
import styles from './CustomFormats.css';
|
||||
|
@ -58,9 +59,9 @@ class CustomFormats extends Component {
|
|||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldSet legend="Custom Formats">
|
||||
<FieldSet legend={translate('CustomFormats')}>
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load custom formats"
|
||||
errorMessage={translate('CustomFormatsLoadError')}
|
||||
{...otherProps}c={true}
|
||||
>
|
||||
<div className={styles.customFormats}>
|
||||
|
|
|
@ -15,6 +15,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { icons, inputTypes, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import ImportCustomFormatModal from './ImportCustomFormatModal';
|
||||
import AddSpecificationModal from './Specifications/AddSpecificationModal';
|
||||
import EditSpecificationModalConnector from './Specifications/EditSpecificationModalConnector';
|
||||
|
@ -99,7 +100,7 @@ class EditCustomFormatModalContent extends Component {
|
|||
<ModalContent onModalClose={onModalClose}>
|
||||
|
||||
<ModalHeader>
|
||||
{id ? 'Edit Custom Format' : 'Add Custom Format'}
|
||||
{id ? translate('EditCustomFormat') : translate('AddCustomFormat')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -112,7 +113,7 @@ class EditCustomFormatModalContent extends Component {
|
|||
{
|
||||
!isFetching && !!error &&
|
||||
<div>
|
||||
{'Unable to add a new custom format, please try again.'}
|
||||
{translate('AddCustomFormatError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -124,7 +125,7 @@ class EditCustomFormatModalContent extends Component {
|
|||
>
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Name
|
||||
{translate('Name')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
|
@ -136,19 +137,19 @@ class EditCustomFormatModalContent extends Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{'Include Custom Format when Renaming'}</FormLabel>
|
||||
<FormLabel>{translate('IncludeCustomFormatWhenRenaming')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="includeCustomFormatWhenRenaming"
|
||||
helpText={'Include in {Custom Formats} renaming format'}
|
||||
helpText={translate('IncludeCustomFormatWhenRenamingHelpText')}
|
||||
{...includeCustomFormatWhenRenaming}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
|
||||
<FieldSet legend={'Conditions'}>
|
||||
<FieldSet legend={translate('Conditions')}>
|
||||
<div className={styles.customFormats}>
|
||||
{
|
||||
specifications.map((tag) => {
|
||||
|
@ -205,7 +206,7 @@ class EditCustomFormatModalContent extends Component {
|
|||
kind={kinds.DANGER}
|
||||
onPress={onDeleteCustomFormatPress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
|
@ -213,14 +214,14 @@ class EditCustomFormatModalContent extends Component {
|
|||
className={styles.deleteButton}
|
||||
onPress={this.onImportPress}
|
||||
>
|
||||
Import
|
||||
{translate('Import')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
|
@ -228,7 +229,7 @@ class EditCustomFormatModalContent extends Component {
|
|||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -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 styles from './ExportCustomFormatModalContent.css';
|
||||
|
||||
class ExportCustomFormatModalContent extends Component {
|
||||
|
@ -28,7 +29,7 @@ class ExportCustomFormatModalContent extends Component {
|
|||
<ModalContent onModalClose={onModalClose}>
|
||||
|
||||
<ModalHeader>
|
||||
Export Custom Format
|
||||
{translate('ExportCustomFormat')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -41,7 +42,7 @@ class ExportCustomFormatModalContent extends Component {
|
|||
{
|
||||
!isFetching && !!error &&
|
||||
<div>
|
||||
Unable to load custom formats
|
||||
{translate('CustomFormatsLoadError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -59,13 +60,13 @@ class ExportCustomFormatModalContent extends Component {
|
|||
<ClipboardButton
|
||||
className={styles.button}
|
||||
value={json}
|
||||
title="Copy to clipboard"
|
||||
title={translate('CopyToClipboard')}
|
||||
kind={kinds.DEFAULT}
|
||||
/>
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Close
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -12,6 +12,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ImportCustomFormatModalContent.css';
|
||||
|
||||
class ImportCustomFormatModalContent extends Component {
|
||||
|
@ -82,7 +83,7 @@ class ImportCustomFormatModalContent extends Component {
|
|||
<ModalContent onModalClose={onModalClose}>
|
||||
|
||||
<ModalHeader>
|
||||
Import Custom Format
|
||||
{translate('ImportCustomFormat')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -95,7 +96,7 @@ class ImportCustomFormatModalContent extends Component {
|
|||
{
|
||||
!isFetching && !!error &&
|
||||
<div>
|
||||
Unable to load custom formats
|
||||
{translate('CustomFormatsLoadError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -104,7 +105,7 @@ class ImportCustomFormatModalContent extends Component {
|
|||
<Form>
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>
|
||||
Custom Format JSON
|
||||
{translate('CustomFormatJSON')}
|
||||
</FormLabel>
|
||||
<FormInputGroup
|
||||
key={0}
|
||||
|
@ -125,14 +126,14 @@ class ImportCustomFormatModalContent extends Component {
|
|||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
<SpinnerErrorButton
|
||||
onPress={this.onImportPress}
|
||||
isSpinning={isSpinning}
|
||||
error={parseError}
|
||||
>
|
||||
Import
|
||||
{translate('Import')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -6,6 +6,7 @@ import { createSelector } from 'reselect';
|
|||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
||||
import { clearCustomFormatSpecificationPending, deleteAllCustomFormatSpecification, fetchCustomFormatSpecificationSchema, saveCustomFormatSpecification, selectCustomFormatSpecificationSchema, setCustomFormatSpecificationFieldValue, setCustomFormatSpecificationValue, setCustomFormatValue } from 'Store/Actions/settingsActions';
|
||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import ImportCustomFormatModalContent from './ImportCustomFormatModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
|
@ -88,7 +89,9 @@ class ImportCustomFormatModalContentConnector extends Component {
|
|||
const selectedImplementation = _.find(this.props.specificationSchema, { implementation: spec.implementation });
|
||||
|
||||
if (!selectedImplementation) {
|
||||
throw new Error(`Unknown Custom Format condition '${spec.implementation}'`);
|
||||
throw new Error(translate('CustomFormatUnknownCondition', {
|
||||
implementation: spec.implementation
|
||||
}));
|
||||
}
|
||||
|
||||
this.props.selectCustomFormatSpecificationSchema({ implementation: spec.implementation });
|
||||
|
@ -108,7 +111,10 @@ class ImportCustomFormatModalContentConnector extends Component {
|
|||
for (const [key, value] of Object.entries(fields)) {
|
||||
const field = _.find(schema.fields, { name: key });
|
||||
if (!field) {
|
||||
throw new Error(`Unknown option '${key}' for condition '${schema.implementationName}'`);
|
||||
throw new Error(translate('CustomFormatUnknownConditionOption', {
|
||||
key,
|
||||
implementation: schema.implementationName
|
||||
}));
|
||||
}
|
||||
|
||||
this.props.setCustomFormatSpecificationFieldValue({ name: key, value });
|
||||
|
|
|
@ -5,6 +5,7 @@ import Link from 'Components/Link/Link';
|
|||
import Menu from 'Components/Menu/Menu';
|
||||
import MenuContent from 'Components/Menu/MenuContent';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AddSpecificationPresetMenuItem from './AddSpecificationPresetMenuItem';
|
||||
import styles from './AddSpecificationItem.css';
|
||||
|
||||
|
@ -57,7 +58,7 @@ class AddSpecificationItem extends Component {
|
|||
size={sizes.SMALL}
|
||||
onPress={this.onSpecificationSelect}
|
||||
>
|
||||
Custom
|
||||
{translate('Custom')}
|
||||
</Button>
|
||||
|
||||
<Menu className={styles.presetsMenu}>
|
||||
|
@ -65,7 +66,7 @@ class AddSpecificationItem extends Component {
|
|||
className={styles.presetsMenuButton}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
Presets
|
||||
{translate('Presets')}
|
||||
</Button>
|
||||
|
||||
<MenuContent>
|
||||
|
@ -90,7 +91,7 @@ class AddSpecificationItem extends Component {
|
|||
to={infoLink}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
More Info
|
||||
{translate('MoreInfo')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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 AddSpecificationItem from './AddSpecificationItem';
|
||||
import styles from './AddSpecificationModalContent.css';
|
||||
|
||||
|
@ -42,7 +43,7 @@ class AddSpecificationModalContent extends Component {
|
|||
{
|
||||
!isSchemaFetching && !!schemaError &&
|
||||
<div>
|
||||
{'Unable to add a new condition, please try again.'}
|
||||
{translate('AddConditionError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -52,11 +53,11 @@ class AddSpecificationModalContent extends Component {
|
|||
|
||||
<Alert kind={kinds.INFO}>
|
||||
<div>
|
||||
{'Sonarr supports custom conditions against the release properties below.'}
|
||||
{translate('SupportedCustomConditions')}
|
||||
</div>
|
||||
<div>
|
||||
{'Visit the wiki for more details: '}
|
||||
<Link to="https://wiki.servarr.com/sonarr/settings#custom-formats-2">{'Wiki'}</Link>
|
||||
{translate('VisitTheWikiForMoreDetails')}
|
||||
<Link to="https://wiki.servarr.com/sonarr/settings#custom-formats-2">{translate('Wiki')}</Link>
|
||||
</div>
|
||||
</Alert>
|
||||
|
||||
|
@ -81,7 +82,7 @@ class AddSpecificationModalContent extends Component {
|
|||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Close
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -7,13 +7,14 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
|
|||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import ProviderFieldFormGroup from 'Components/Form/ProviderFieldFormGroup';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Link from 'Components/Link/Link';
|
||||
import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton';
|
||||
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 { inputTypes, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import InlineMarkdown from '../../../../Components/Markdown/InlineMarkdown';
|
||||
import styles from './EditSpecificationModalContent.css';
|
||||
|
||||
function EditSpecificationModalContent(props) {
|
||||
|
@ -40,7 +41,7 @@ function EditSpecificationModalContent(props) {
|
|||
return (
|
||||
<ModalContent onModalClose={onCancelPress}>
|
||||
<ModalHeader>
|
||||
{`${id ? 'Edit' : 'Add'} Condition - ${implementationName}`}
|
||||
{`${id ? translate('EditCondition') : translate('AddCondition')} - ${implementationName}`}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -48,22 +49,23 @@ function EditSpecificationModalContent(props) {
|
|||
{...otherProps}
|
||||
>
|
||||
{
|
||||
fields && fields.some((x) => x.label === 'Regular Expression') &&
|
||||
fields && fields.some((x) => x.label === translate('RegularExpression')) &&
|
||||
<Alert kind={kinds.INFO}>
|
||||
<div>
|
||||
<div dangerouslySetInnerHTML={{ __html: 'This condition matches using Regular Expressions. Note that the characters <code>\\^$.|?*+()[{</code> have special meanings and need escaping with a <code>\\</code>' }} />
|
||||
{'More details'} <Link to="https://www.regular-expressions.info/tutorial.html">{'Here'}</Link>
|
||||
<InlineMarkdown data={translate('ConditionUsingRegularExpressions')} />
|
||||
</div>
|
||||
<div>
|
||||
{'Regular expressions can be tested '}
|
||||
<Link to="http://regexstorm.net/tester">Here</Link>
|
||||
<InlineMarkdown data={translate('RegularExpressionsTutorialLink')} />
|
||||
</div>
|
||||
<div>
|
||||
<InlineMarkdown data={translate('RegularExpressionsCanBeTested')} />
|
||||
</div>
|
||||
</Alert>
|
||||
}
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Name
|
||||
{translate('Name')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
|
@ -91,28 +93,28 @@ function EditSpecificationModalContent(props) {
|
|||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Negate
|
||||
{translate('Negate')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="negate"
|
||||
{...negate}
|
||||
helpText={`If checked, the custom format will not apply if this ${implementationName} condition matches.`}
|
||||
helpText={translate('NegateHelpText', { implementationName })}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Required
|
||||
{translate('Required')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="required"
|
||||
{...required}
|
||||
helpText={`This ${implementationName} condition must match for the custom format to apply. Otherwise a single ${implementationName} match is sufficient.`}
|
||||
helpText={translate('RequiredHelpText', { implementationName })}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
@ -126,21 +128,21 @@ function EditSpecificationModalContent(props) {
|
|||
kind={kinds.DANGER}
|
||||
onPress={onDeleteSpecificationPress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
<Button
|
||||
onPress={onCancelPress}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
isSpinning={false}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -115,7 +115,7 @@ class Specification extends Component {
|
|||
isOpen={this.state.isDeleteSpecificationModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteCondition')}
|
||||
message={translate('DeleteConditionMessageText', [name])}
|
||||
message={translate('DeleteConditionMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteSpecification}
|
||||
onCancel={this.onDeleteSpecificationModalClose}
|
||||
|
|
|
@ -70,7 +70,7 @@ class DownloadClientSettings extends Component {
|
|||
} = this.state;
|
||||
|
||||
return (
|
||||
<PageContent title="Download Client Settings">
|
||||
<PageContent title={translate('DownloadClientSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
|
@ -79,7 +79,7 @@ class DownloadClientSettings extends Component {
|
|||
<PageToolbarSeparator />
|
||||
|
||||
<PageToolbarButton
|
||||
label="Test All Clients"
|
||||
label={translate('TestAllClients')}
|
||||
iconName={icons.TEST}
|
||||
isSpinning={isTestingAll}
|
||||
onPress={dispatchTestAllDownloadClients}
|
||||
|
|
|
@ -5,6 +5,7 @@ import Link from 'Components/Link/Link';
|
|||
import Menu from 'Components/Menu/Menu';
|
||||
import MenuContent from 'Components/Menu/MenuContent';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AddDownloadClientPresetMenuItem from './AddDownloadClientPresetMenuItem';
|
||||
import styles from './AddDownloadClientItem.css';
|
||||
|
||||
|
@ -57,7 +58,7 @@ class AddDownloadClientItem extends Component {
|
|||
size={sizes.SMALL}
|
||||
onPress={this.onDownloadClientSelect}
|
||||
>
|
||||
Custom
|
||||
{translate('Custom')}
|
||||
</Button>
|
||||
|
||||
<Menu className={styles.presetsMenu}>
|
||||
|
@ -65,7 +66,7 @@ class AddDownloadClientItem extends Component {
|
|||
className={styles.presetsMenuButton}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
Presets
|
||||
{translate('Presets')}
|
||||
</Button>
|
||||
|
||||
<MenuContent>
|
||||
|
@ -90,7 +91,7 @@ class AddDownloadClientItem extends Component {
|
|||
to={infoLink}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
More info
|
||||
{translate('MoreInfo')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
@ -31,7 +32,7 @@ class AddDownloadClientModalContent extends Component {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Add Download Client
|
||||
{translate('AddDownloadClient')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -42,7 +43,9 @@ class AddDownloadClientModalContent extends Component {
|
|||
|
||||
{
|
||||
!isSchemaFetching && !!schemaError &&
|
||||
<div>Unable to add a new downloadClient, please try again.</div>
|
||||
<div>
|
||||
{translate('AddDownloadClientError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -50,11 +53,15 @@ class AddDownloadClientModalContent extends Component {
|
|||
<div>
|
||||
|
||||
<Alert kind={kinds.INFO}>
|
||||
<div>Sonarr supports many popular torrent and usenet download clients.</div>
|
||||
<div>For more information on the individual download clients, click the more info buttons.</div>
|
||||
<div>
|
||||
{translate('SupportedDownloadClients')}
|
||||
</div>
|
||||
<div>
|
||||
{translate('SupportedDownloadClientsMoreInfo')}
|
||||
</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) => {
|
||||
|
@ -94,7 +101,7 @@ class AddDownloadClientModalContent extends Component {
|
|||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Close
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -5,6 +5,7 @@ import Label from 'Components/Label';
|
|||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import TagList from 'Components/TagList';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditDownloadClientModalConnector from './EditDownloadClientModalConnector';
|
||||
import styles from './DownloadClient.css';
|
||||
|
||||
|
@ -75,13 +76,13 @@ class DownloadClient extends Component {
|
|||
{
|
||||
enable ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
Enabled
|
||||
{translate('Enabled')}
|
||||
</Label> :
|
||||
<Label
|
||||
kind={kinds.DISABLED}
|
||||
outline={true}
|
||||
>
|
||||
Disabled
|
||||
{translate('Disabled')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
|
@ -91,7 +92,7 @@ class DownloadClient extends Component {
|
|||
kind={kinds.DISABLED}
|
||||
outline={true}
|
||||
>
|
||||
Priority: {priority}
|
||||
{translate('PrioritySettings', { priority })}
|
||||
</Label>
|
||||
}
|
||||
</div>
|
||||
|
@ -111,9 +112,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';
|
||||
|
@ -59,9 +60,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('DownloadClientsLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.downloadClients}>
|
||||
|
|
|
@ -15,6 +15,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 './EditDownloadClientModalContent.css';
|
||||
|
||||
class EditDownloadClientModalContent extends Component {
|
||||
|
@ -57,7 +58,7 @@ class EditDownloadClientModalContent extends Component {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{`${id ? 'Edit' : 'Add'} Download Client - ${implementationName}`}
|
||||
{`${id ? translate('Edit') : translate('Add')} ${translate('DownloadClient')} - ${implementationName}`}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -68,7 +69,9 @@ class EditDownloadClientModalContent extends Component {
|
|||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<div>Unable to add a new download client, please try again.</div>
|
||||
<div>
|
||||
{translate('AddDownloadClientError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -85,7 +88,7 @@ class EditDownloadClientModalContent extends Component {
|
|||
}
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{translate('Name')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
|
@ -96,7 +99,7 @@ class EditDownloadClientModalContent extends Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Enable</FormLabel>
|
||||
<FormLabel>{translate('Enable')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
|
@ -125,12 +128,12 @@ 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}
|
||||
|
@ -139,12 +142,12 @@ class EditDownloadClientModalContent extends Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Tags</FormLabel>
|
||||
<FormLabel>{translate('Tags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
name="tags"
|
||||
helpText="Only use this download client for series with at least one matching tag. Leave blank to use with all series."
|
||||
helpText={translate('DownloadClientTagHelpText')}
|
||||
{...tags}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -152,15 +155,15 @@ class EditDownloadClientModalContent extends Component {
|
|||
|
||||
<FieldSet
|
||||
size={sizes.SMALL}
|
||||
legend="Completed Download Handling"
|
||||
legend={translate('CompletedDownloadHandling')}
|
||||
>
|
||||
<FormGroup>
|
||||
<FormLabel>Remove Completed</FormLabel>
|
||||
<FormLabel>{translate('RemoveCompleted')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="removeCompletedDownloads"
|
||||
helpText="Remove imported downloads from download client history (when finished seeding for torrents)"
|
||||
helpText={translate('RemoveCompletedDownloadsHelpText')}
|
||||
{...removeCompletedDownloads}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -169,12 +172,12 @@ class EditDownloadClientModalContent extends Component {
|
|||
{
|
||||
protocol.value !== 'torrent' &&
|
||||
<FormGroup>
|
||||
<FormLabel>Remove Failed</FormLabel>
|
||||
<FormLabel>{translate('RemoveFailed')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="removeFailedDownloads"
|
||||
helpText="Remove failed downloads from download client history"
|
||||
helpText={translate('RemoveFailedDownloadsHelpText')}
|
||||
{...removeFailedDownloads}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -192,7 +195,7 @@ class EditDownloadClientModalContent extends Component {
|
|||
kind={kinds.DANGER}
|
||||
onPress={onDeleteDownloadClientPress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
|
@ -201,13 +204,13 @@ class EditDownloadClientModalContent extends Component {
|
|||
error={saveError}
|
||||
onPress={onTestPress}
|
||||
>
|
||||
Test
|
||||
{translate('Test')}
|
||||
</SpinnerErrorButton>
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
|
@ -215,7 +218,7 @@ class EditDownloadClientModalContent extends Component {
|
|||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -8,6 +8,7 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
|
|||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function DownloadClientOptions(props) {
|
||||
const {
|
||||
|
@ -28,13 +29,15 @@ function DownloadClientOptions(props) {
|
|||
|
||||
{
|
||||
!isFetching && error &&
|
||||
<Alert kind={kinds.DANGER}>Unable to load download client options</Alert>
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('DownloadClientOptionsLoadError')}
|
||||
</Alert>
|
||||
}
|
||||
|
||||
{
|
||||
hasSettings && !isFetching && !error && advancedSettings &&
|
||||
<div>
|
||||
<FieldSet legend="Completed Download Handling">
|
||||
<FieldSet legend={translate('CompletedDownloadHandling')}>
|
||||
|
||||
<Form>
|
||||
<FormGroup
|
||||
|
@ -42,12 +45,12 @@ function DownloadClientOptions(props) {
|
|||
isAdvanced={true}
|
||||
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}
|
||||
/>
|
||||
|
@ -58,12 +61,12 @@ function DownloadClientOptions(props) {
|
|||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>Redownload Failed</FormLabel>
|
||||
<FormLabel>{translate('RedownloadFailed')}</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}
|
||||
/>
|
||||
|
@ -71,7 +74,7 @@ function DownloadClientOptions(props) {
|
|||
</Form>
|
||||
|
||||
<Alert kind={kinds.INFO}>
|
||||
The Remove settings were moved to the individual Download Client settings in the table above.
|
||||
{translate('RemoveDownloadsAlert')}
|
||||
</Alert>
|
||||
</FieldSet>
|
||||
</div>
|
||||
|
|
|
@ -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) {
|
||||
|
@ -40,7 +41,7 @@ function EditRemotePathMappingModalContent(props) {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{id ? 'Edit Remote Path Mapping' : 'Add Remote Path Mapping'}
|
||||
{id ? translate('EditRemotePathMapping') : translate('AddRemotePathMapping')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody className={styles.body}>
|
||||
|
@ -51,19 +52,21 @@ function EditRemotePathMappingModalContent(props) {
|
|||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<div>Unable to add a new remote path mapping, please try again.</div>
|
||||
<div>
|
||||
{translate('AddRemotePathMappingError')}
|
||||
</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('RemotePathMappingHostHelpText')}
|
||||
{...host}
|
||||
values={downloadClientHosts}
|
||||
onChange={onInputChange}
|
||||
|
@ -71,24 +74,24 @@ 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('RemotePathMappingRemotePathHelpText')}
|
||||
{...remotePath}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Local Path</FormLabel>
|
||||
<FormLabel>{translate('LocalPath')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.PATH}
|
||||
name="localPath"
|
||||
helpText="Path that Sonarr should use to access the remote path locally"
|
||||
helpText={translate('RemotePathMappingLocalPathHelpText')}
|
||||
{...localPath}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -105,14 +108,14 @@ function EditRemotePathMappingModalContent(props) {
|
|||
kind={kinds.DANGER}
|
||||
onPress={onDeleteRemotePathMappingPress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
|
@ -120,7 +123,7 @@ function EditRemotePathMappingModalContent(props) {
|
|||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -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 Remote Path Mapping"
|
||||
message="Are you sure you want to delete this remote path mapping?"
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteRemotePathMapping')}
|
||||
message={translate('DeleteRemotePathMappingMessageText')}
|
||||
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,15 +45,21 @@ 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('RemotePathMappingsLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.remotePathMappingsHeader}>
|
||||
<div className={styles.host}>Host</div>
|
||||
<div className={styles.path}>Remote Path</div>
|
||||
<div className={styles.path}>Local Path</div>
|
||||
<div className={styles.host}>
|
||||
{translate('Host')}
|
||||
</div>
|
||||
<div className={styles.path}>
|
||||
{translate('RemotePath')}
|
||||
</div>
|
||||
<div className={styles.path}>
|
||||
{translate('LocalPath')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
|
@ -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,15 @@ 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 Sonarr's servers. This includes information on your browser, which Sonarr 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('RestartRequiredHelpTextWarning')}
|
||||
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,17 @@ 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 Sonarr's AppData directory"
|
||||
helpText={translate('BackupFolderHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...backupFolder}
|
||||
/>
|
||||
|
@ -44,13 +45,13 @@ function BackupSettings(props) {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Interval</FormLabel>
|
||||
<FormLabel>{translate('Interval')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="backupInterval"
|
||||
unit="days"
|
||||
helpText="Interval between automatic backups"
|
||||
helpText={translate('BackupIntervalHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...backupInterval}
|
||||
/>
|
||||
|
@ -60,13 +61,13 @@ 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}
|
||||
/>
|
||||
|
|
|
@ -9,6 +9,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';
|
||||
|
@ -111,7 +112,7 @@ class GeneralSettings extends Component {
|
|||
} = this.props;
|
||||
|
||||
return (
|
||||
<PageContent title="General Settings">
|
||||
<PageContent title={translate('GeneralSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
{...otherProps}
|
||||
/>
|
||||
|
@ -124,7 +125,9 @@ class GeneralSettings extends Component {
|
|||
|
||||
{
|
||||
!isFetching && error &&
|
||||
<Alert kind={kinds.DANGER}>Unable to load General settings</Alert>
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('GeneralSettingsLoadError')}
|
||||
</Alert>
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -183,12 +186,10 @@ class GeneralSettings extends Component {
|
|||
<ConfirmModal
|
||||
isOpen={this.state.isRestartRequiredModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Restart Sonarr"
|
||||
message={
|
||||
`Sonarr requires a restart to apply changes, do you want to restart now? ${isWindowsService ? 'Depending which user is running the Sonarr service you may need to restart Sonarr as admin once before the service will start automatically.' : ''}`
|
||||
}
|
||||
cancelLabel="I'll restart later"
|
||||
confirmLabel="Restart Now"
|
||||
title={translate('RestartSonarr')}
|
||||
message={`${translate('RestartRequiredToApplyChanges')} ${isWindowsService ? translate('RestartRequiredWindowsService') : ''}`}
|
||||
cancelLabel={translate('RestartLater')}
|
||||
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 {
|
||||
|
@ -29,25 +30,25 @@ 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 IP address, localhost or '*' for all interfaces"
|
||||
helpTextWarning="Requires restart to take effect"
|
||||
helpText={translate('BindAddressHelpText')}
|
||||
helpTextWarning={translate('RestartRequiredHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...bindAddress}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Port Number</FormLabel>
|
||||
<FormLabel>{translate('PortNumber')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
|
@ -55,20 +56,20 @@ function HostSettings(props) {
|
|||
min={1}
|
||||
max={65535}
|
||||
autocomplete="off"
|
||||
helpTextWarning="Requires restart to take effect"
|
||||
helpTextWarning={translate('RestartRequiredHelpTextWarning')}
|
||||
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('RestartRequiredHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...urlBase}
|
||||
/>
|
||||
|
@ -78,13 +79,13 @@ function HostSettings(props) {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Instance Name</FormLabel>
|
||||
<FormLabel>{translate('InstanceName')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="instanceName"
|
||||
helpText="Instance name in tab and for Syslog app name"
|
||||
helpTextWarning="Requires restart to take effect"
|
||||
helpText={translate('InstanceNameHelpText')}
|
||||
helpTextWarning={translate('RestartRequiredHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...instanceName}
|
||||
/>
|
||||
|
@ -94,12 +95,12 @@ function HostSettings(props) {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Application URL</FormLabel>
|
||||
<FormLabel>{translate('ApplicationURL')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="applicationUrl"
|
||||
helpText="This application's external URL including http(s)://, port and URL base"
|
||||
helpText={translate('ApplicationUrlHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...applicationUrl}
|
||||
/>
|
||||
|
@ -110,12 +111,12 @@ 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}
|
||||
/>
|
||||
|
@ -127,14 +128,14 @@ 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('RestartRequiredHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...sslPort}
|
||||
/>
|
||||
|
@ -148,13 +149,13 @@ 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('RestartRequiredHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...sslCertPath}
|
||||
/>
|
||||
|
@ -168,13 +169,13 @@ 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('RestartRequiredHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...sslCertPassword}
|
||||
/>
|
||||
|
@ -185,12 +186,12 @@ 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 Sonarr homepage on app start."
|
||||
helpText={translate('OpenBrowserOnStartHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...launchBrowser}
|
||||
/>
|
||||
|
|
|
@ -5,11 +5,27 @@ 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' },
|
||||
{ key: 'debug', value: 'Debug' },
|
||||
{ key: 'trace', value: 'Trace' }
|
||||
{
|
||||
key: 'info',
|
||||
get value() {
|
||||
return translate('Info');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
get value() {
|
||||
return translate('Debug');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'trace',
|
||||
get value() {
|
||||
return translate('Trace');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
function LoggingSettings(props) {
|
||||
|
@ -23,15 +39,15 @@ 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('LogLevelTraceHelpTextWarning') : 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 {
|
||||
|
@ -24,15 +25,24 @@ function ProxySettings(props) {
|
|||
} = settings;
|
||||
|
||||
const proxyTypeOptions = [
|
||||
{ key: 'http', value: 'HTTP(S)' },
|
||||
{ key: 'socks4', value: 'Socks4' },
|
||||
{ key: 'socks5', value: 'Socks5 (Support TOR)' }
|
||||
{
|
||||
key: 'http',
|
||||
value: translate('HttpHttps')
|
||||
},
|
||||
{
|
||||
key: 'socks4',
|
||||
value: translate('Socks4')
|
||||
},
|
||||
{
|
||||
key: 'socks5',
|
||||
value: translate('Socks5')
|
||||
}
|
||||
];
|
||||
|
||||
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 +56,7 @@ function ProxySettings(props) {
|
|||
proxyEnabled.value &&
|
||||
<div>
|
||||
<FormGroup>
|
||||
<FormLabel>Proxy Type</FormLabel>
|
||||
<FormLabel>{translate('ProxyType')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
|
@ -58,7 +68,7 @@ function ProxySettings(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Hostname</FormLabel>
|
||||
<FormLabel>{translate('Hostname')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
|
@ -70,7 +80,7 @@ function ProxySettings(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Port</FormLabel>
|
||||
<FormLabel>{translate('Port')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
|
@ -83,43 +93,43 @@ 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,25 +9,71 @@ import Icon from 'Components/Icon';
|
|||
import ClipboardButton from 'Components/Link/ClipboardButton';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import { icons, inputTypes, kinds } from 'Helpers/Props';
|
||||
|
||||
export const authenticationRequiredWarning = 'To prevent remote access without authentication, Sonarr now requires authentication to be enabled. You can optionally disable authentication from local addresses.';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
export const authenticationMethodOptions = [
|
||||
{ key: 'none', value: 'None', isDisabled: true },
|
||||
{ key: 'external', value: 'External', isHidden: true },
|
||||
{ key: 'basic', value: 'Basic (Browser Popup)' },
|
||||
{ key: 'forms', value: 'Forms (Login Page)' }
|
||||
{
|
||||
key: 'none',
|
||||
get value() {
|
||||
return translate('None');
|
||||
},
|
||||
isDisabled: true
|
||||
},
|
||||
{
|
||||
key: 'external',
|
||||
get value() {
|
||||
return translate('External');
|
||||
},
|
||||
isHidden: true
|
||||
},
|
||||
{
|
||||
key: 'basic',
|
||||
get value() {
|
||||
return translate('AuthBasic');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'forms',
|
||||
get value() {
|
||||
return translate('AuthForm');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export const authenticationRequiredOptions = [
|
||||
{ key: 'enabled', value: 'Enabled' },
|
||||
{ key: 'disabledForLocalAddresses', value: 'Disabled for Local Addresses' }
|
||||
{
|
||||
key: 'enabled',
|
||||
get value() {
|
||||
return translate('Enabled');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'disabledForLocalAddresses',
|
||||
get value() {
|
||||
return translate('DisabledForLocalAddresses');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const certificateValidationOptions = [
|
||||
{ key: 'enabled', value: 'Enabled' },
|
||||
{ key: 'disabledForLocalAddresses', value: 'Disabled for Local Addresses' },
|
||||
{ key: 'disabled', value: 'Disabled' }
|
||||
{
|
||||
key: 'enabled',
|
||||
get value() {
|
||||
return translate('Enabled');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'disabledForLocalAddresses',
|
||||
get value() {
|
||||
return translate('DisabledForLocalAddresses');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'disabled',
|
||||
get value() {
|
||||
return translate('Disabled');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
class SecuritySettings extends Component {
|
||||
|
@ -85,16 +131,16 @@ 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 Sonarr"
|
||||
helpTextWarning={authenticationRequiredWarning}
|
||||
helpText={translate('AuthenticationMethodHelpText')}
|
||||
helpTextWarning={translate('AuthenticationRequiredWarning')}
|
||||
onChange={onInputChange}
|
||||
{...authenticationMethod}
|
||||
/>
|
||||
|
@ -103,13 +149,13 @@ class SecuritySettings extends Component {
|
|||
{
|
||||
authenticationEnabled ?
|
||||
<FormGroup>
|
||||
<FormLabel>Authentication Required</FormLabel>
|
||||
<FormLabel>{translate('AuthenticationRequired')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="authenticationRequired"
|
||||
values={authenticationRequiredOptions}
|
||||
helpText="Change which requests authentication is required for. Do not change unless you understand the risks."
|
||||
helpText={translate('AuthenticationRequiredHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...authenticationRequired}
|
||||
/>
|
||||
|
@ -120,7 +166,7 @@ class SecuritySettings extends Component {
|
|||
{
|
||||
authenticationEnabled ?
|
||||
<FormGroup>
|
||||
<FormLabel>Username</FormLabel>
|
||||
<FormLabel>{translate('Username')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
|
@ -135,7 +181,7 @@ class SecuritySettings extends Component {
|
|||
{
|
||||
authenticationEnabled ?
|
||||
<FormGroup>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormLabel>{translate('Password')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.PASSWORD}
|
||||
|
@ -148,13 +194,13 @@ 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('RestartRequiredHelpTextWarning')}
|
||||
buttons={[
|
||||
<ClipboardButton
|
||||
key="copy"
|
||||
|
@ -180,13 +226,13 @@ 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. Do not change unless you understand the risks."
|
||||
helpText={translate('CertificateValidationHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...certificateValidation}
|
||||
/>
|
||||
|
@ -195,9 +241,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';
|
||||
|
||||
const branchValues = [
|
||||
'master',
|
||||
|
@ -42,23 +43,23 @@ function UpdateSettings(props) {
|
|||
value: titleCase(packageUpdateMechanism)
|
||||
});
|
||||
} else {
|
||||
updateOptions.push({ key: 'builtIn', value: 'Built-In' });
|
||||
updateOptions.push({ key: 'builtIn', value: translate('BuiltIn') });
|
||||
}
|
||||
|
||||
updateOptions.push({ key: 'script', value: 'Script' });
|
||||
updateOptions.push({ key: 'script', value: translate('Script') });
|
||||
|
||||
return (
|
||||
<FieldSet legend="Updates">
|
||||
<FieldSet legend={translate('Updates')}>
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Branch</FormLabel>
|
||||
<FormLabel>{translate('Branch')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.AUTO_COMPLETE}
|
||||
name="branch"
|
||||
helpText={usingExternalUpdateMechanism ? 'Branch used by external update mechanism' : 'Branch to use to update Sonarr'}
|
||||
helpText={usingExternalUpdateMechanism ? translate('BranchUpdateMechanism') : translate('BranchUpdate')}
|
||||
helpLink="https://wiki.servarr.com/sonarr/settings#updates"
|
||||
{...branch}
|
||||
values={branchValues}
|
||||
|
@ -76,12 +77,12 @@ 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}
|
||||
/>
|
||||
|
@ -91,13 +92,13 @@ function UpdateSettings(props) {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Mechanism</FormLabel>
|
||||
<FormLabel>{translate('Mechanism')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="updateMechanism"
|
||||
values={updateOptions}
|
||||
helpText="Use Sonarr's built-in updater or a script"
|
||||
helpText={translate('UpdateMechanismHelpText')}
|
||||
helpLink="https://wiki.servarr.com/sonarr/settings#updates"
|
||||
onChange={onInputChange}
|
||||
{...updateMechanism}
|
||||
|
@ -110,12 +111,12 @@ 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 { numberSettingShape, stringSettingShape } from 'Helpers/Props/Shapes/settingShape';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './EditImportListExclusionModalContent.css';
|
||||
|
||||
function EditImportListExclusionModalContent(props) {
|
||||
|
@ -38,7 +39,7 @@ function EditImportListExclusionModalContent(props) {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{id ? 'Edit Import List Exclusion' : 'Add Import List Exclusion'}
|
||||
{id ? translate('EditImportListExclusion') : translate('AddImportListExclusion')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody className={styles.body}>
|
||||
|
@ -49,7 +50,9 @@ function EditImportListExclusionModalContent(props) {
|
|||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<div>Unable to add a new import list exclusion, please try again.</div>
|
||||
<div>
|
||||
{translate('AddImportListExclusionError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -58,24 +61,24 @@ function EditImportListExclusionModalContent(props) {
|
|||
{...otherProps}
|
||||
>
|
||||
<FormGroup>
|
||||
<FormLabel>Title</FormLabel>
|
||||
<FormLabel>{translate('Title')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="title"
|
||||
helpText="The name of the series to exclude"
|
||||
helpText={translate('SeriesTitleToExcludeHelpText')}
|
||||
{...title}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>TVDB ID</FormLabel>
|
||||
<FormLabel>{translate('TvdbId')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="tvdbId"
|
||||
helpText="The TVDB ID of the series to exclude"
|
||||
helpText={translate('TvdbIdExcludeHelpText')}
|
||||
{...tvdbId}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -92,14 +95,14 @@ function EditImportListExclusionModalContent(props) {
|
|||
kind={kinds.DANGER}
|
||||
onPress={onDeleteImportListExclusionPress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
|
@ -107,7 +110,7 @@ function EditImportListExclusionModalContent(props) {
|
|||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -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 EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector';
|
||||
import styles from './ImportListExclusion.css';
|
||||
|
||||
|
@ -85,9 +86,9 @@ class ImportListExclusion extends Component {
|
|||
<ConfirmModal
|
||||
isOpen={this.state.isDeleteImportListExclusionModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete Import List Exclusion"
|
||||
message="Are you sure you want to delete this import list exclusion?"
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteImportListExclusion')}
|
||||
message={translate('DeleteImportListExclusionMessageText')}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteImportListExclusion}
|
||||
onCancel={this.onDeleteImportListExclusionModalClose}
|
||||
/>
|
||||
|
|
|
@ -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 EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector';
|
||||
import ImportListExclusion from './ImportListExclusion';
|
||||
import styles from './ImportListExclusions.css';
|
||||
|
@ -44,14 +45,18 @@ class ImportListExclusions extends Component {
|
|||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldSet legend="Import List Exclusions">
|
||||
<FieldSet legend={translate('ImportListExclusions')}>
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load Import List Exclusions"
|
||||
errorMessage={translate('ImportListExclusionsLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.importListExclusionsHeader}>
|
||||
<div className={styles.title}>Title</div>
|
||||
<div className={styles.tvdbId}>TVDB ID</div>
|
||||
<div className={styles.title}>
|
||||
{translate('Title')}
|
||||
</div>
|
||||
<div className={styles.tvdbId}>
|
||||
{translate('TvdbId')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
|
@ -66,7 +66,7 @@ class ImportListSettings extends Component {
|
|||
} = this.state;
|
||||
|
||||
return (
|
||||
<PageContent title="Import List Settings">
|
||||
<PageContent title={translate('ImportListSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
|
@ -75,7 +75,7 @@ class ImportListSettings extends Component {
|
|||
<PageToolbarSeparator />
|
||||
|
||||
<PageToolbarButton
|
||||
label="Test All Lists"
|
||||
label={translate('TestAllLists')}
|
||||
iconName={icons.TEST}
|
||||
isSpinning={isTestingAll}
|
||||
onPress={dispatchTestAllImportLists}
|
||||
|
|
|
@ -5,6 +5,7 @@ import Link from 'Components/Link/Link';
|
|||
import Menu from 'Components/Menu/Menu';
|
||||
import MenuContent from 'Components/Menu/MenuContent';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AddImportListPresetMenuItem from './AddImportListPresetMenuItem';
|
||||
import styles from './AddImportListItem.css';
|
||||
|
||||
|
@ -57,7 +58,7 @@ class AddImportListItem extends Component {
|
|||
size={sizes.SMALL}
|
||||
onPress={this.onListSelect}
|
||||
>
|
||||
Custom
|
||||
{translate('Custom')}
|
||||
</Button>
|
||||
|
||||
<Menu className={styles.presetsMenu}>
|
||||
|
@ -65,7 +66,7 @@ class AddImportListItem extends Component {
|
|||
className={styles.presetsMenuButton}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
Presets
|
||||
{translate('Presets')}
|
||||
</Button>
|
||||
|
||||
<MenuContent>
|
||||
|
@ -90,7 +91,7 @@ class AddImportListItem extends Component {
|
|||
to={infoLink}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
More info
|
||||
{translate('MoreInfo')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,6 +10,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
|
|||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import titleCase from 'Utilities/String/titleCase';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AddImportListItem from './AddImportListItem';
|
||||
import styles from './AddImportListModalContent.css';
|
||||
|
||||
|
@ -31,7 +32,7 @@ class AddImportListModalContent extends Component {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Add List
|
||||
{translate('AddImportList')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -43,7 +44,9 @@ class AddImportListModalContent extends Component {
|
|||
|
||||
{
|
||||
!isSchemaFetching && !!schemaError ?
|
||||
<div>Unable to add a new list, please try again.</div> :
|
||||
<div>
|
||||
{translate('AddListError')}
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
||||
|
@ -52,13 +55,20 @@ class AddImportListModalContent extends Component {
|
|||
<div>
|
||||
|
||||
<Alert kind={kinds.INFO}>
|
||||
<div>Sonarr supports multiple lists for importing Series into the database.</div>
|
||||
<div>For more information on the individual lists, click on the info buttons.</div>
|
||||
<div>
|
||||
{translate('SupportedLists')}
|
||||
</div>
|
||||
<div>
|
||||
{translate('SupportedListsMoreInfo')}
|
||||
</div>
|
||||
</Alert>
|
||||
{
|
||||
Object.keys(listGroups).map((key) => {
|
||||
return (
|
||||
<FieldSet legend={`${titleCase(key)} List`} key={key}>
|
||||
<FieldSet key={key} legend={translate('TypeOfList', {
|
||||
typeOfList: titleCase(key)
|
||||
})} // `${titleCase(key)} ${translate('List')}`}
|
||||
>
|
||||
<div className={styles.lists}>
|
||||
{
|
||||
listGroups[key].map((list) => {
|
||||
|
@ -85,7 +95,7 @@ class AddImportListModalContent extends Component {
|
|||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Close
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -19,6 +19,7 @@ import ModalHeader from 'Components/Modal/ModalHeader';
|
|||
import Popover from 'Components/Tooltip/Popover';
|
||||
import { icons, inputTypes, kinds, tooltipPositions } from 'Helpers/Props';
|
||||
import formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './EditImportListModalContent.css';
|
||||
|
||||
function EditImportListModalContent(props) {
|
||||
|
@ -57,7 +58,7 @@ function EditImportListModalContent(props) {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{id ? 'Edit List' : 'Add List'}
|
||||
{id ? translate('EditImportList') : translate('AddImportList')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -69,7 +70,9 @@ function EditImportListModalContent(props) {
|
|||
|
||||
{
|
||||
!isFetching && !!error ?
|
||||
<div>Unable to add a new list, please try again.</div> :
|
||||
<div>
|
||||
{translate('AddListError')}
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
||||
|
@ -81,11 +84,13 @@ function EditImportListModalContent(props) {
|
|||
kind={kinds.INFO}
|
||||
className={styles.message}
|
||||
>
|
||||
{`List will refresh every ${formatShortTimeSpan(minRefreshInterval.value)}`}
|
||||
{translate('ListWillRefreshEveryInterval', {
|
||||
refreshInterval: formatShortTimeSpan(minRefreshInterval.value)
|
||||
})}
|
||||
</Alert>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{translate('Name')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
|
@ -96,12 +101,12 @@ function EditImportListModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Enable Automatic Add</FormLabel>
|
||||
<FormLabel>{translate('EnableAutomaticAdd')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableAutomaticAdd"
|
||||
helpText={'Add series to Sonarr when syncs are performed via the UI or by Sonarr'}
|
||||
helpText={translate('EnableAutomaticAddHelpText')}
|
||||
{...enableAutomaticAdd}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -109,7 +114,7 @@ function EditImportListModalContent(props) {
|
|||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Monitor
|
||||
{translate('Monitor')}
|
||||
|
||||
<Popover
|
||||
anchor={
|
||||
|
@ -118,7 +123,7 @@ function EditImportListModalContent(props) {
|
|||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title="Monitoring Options"
|
||||
title={translate('MonitoringOptions')}
|
||||
body={<SeriesMonitoringOptionsPopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
/>
|
||||
|
@ -133,12 +138,12 @@ function EditImportListModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Root Folder</FormLabel>
|
||||
<FormLabel>{translate('RootFolder')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.ROOT_FOLDER_SELECT}
|
||||
name="rootFolderPath"
|
||||
helpText={'Root Folder list items will be added to'}
|
||||
helpText={translate('ListRootFolderHelpText')}
|
||||
{...rootFolderPath}
|
||||
includeMissingValue={true}
|
||||
onChange={onInputChange}
|
||||
|
@ -146,12 +151,12 @@ function EditImportListModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Quality Profile</FormLabel>
|
||||
<FormLabel>{translate('QualityProfile')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.QUALITY_PROFILE_SELECT}
|
||||
name="qualityProfileId"
|
||||
helpText={'Quality Profile list items will be added with'}
|
||||
helpText={translate('ListQualityProfileHelpText')}
|
||||
{...qualityProfileId}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -159,7 +164,7 @@ function EditImportListModalContent(props) {
|
|||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Series Type
|
||||
{translate('SeriesType')}
|
||||
|
||||
<Popover
|
||||
anchor={
|
||||
|
@ -168,7 +173,7 @@ function EditImportListModalContent(props) {
|
|||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title="Series Types"
|
||||
title={translate('SeriesTypes')}
|
||||
body={<SeriesTypePopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
/>
|
||||
|
@ -183,7 +188,7 @@ function EditImportListModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Season Folder</FormLabel>
|
||||
<FormLabel>{translate('SeasonFolder')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
|
@ -194,12 +199,12 @@ function EditImportListModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Sonarr Tags</FormLabel>
|
||||
<FormLabel>{translate('SonarrTags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
name="tags"
|
||||
helpText="Tags list items will be added with"
|
||||
helpText={translate('ListTagsHelpText')}
|
||||
{...tags}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -238,7 +243,7 @@ function EditImportListModalContent(props) {
|
|||
kind={kinds.DANGER}
|
||||
onPress={onDeleteImportListPress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
|
@ -247,13 +252,13 @@ function EditImportListModalContent(props) {
|
|||
error={saveError}
|
||||
onPress={onTestPress}
|
||||
>
|
||||
Test
|
||||
{translate('Test')}
|
||||
</SpinnerErrorButton>
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
|
@ -261,7 +266,7 @@ function EditImportListModalContent(props) {
|
|||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -5,6 +5,7 @@ import Label from 'Components/Label';
|
|||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditImportListModalConnector from './EditImportListModalConnector';
|
||||
import styles from './ImportList.css';
|
||||
|
||||
|
@ -73,7 +74,7 @@ class ImportList extends Component {
|
|||
{
|
||||
enableAutomaticAdd &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
Automatic Add
|
||||
{translate('AutomaticAdd')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
|
@ -81,7 +82,7 @@ class ImportList extends Component {
|
|||
|
||||
<div className={styles.enabled}>
|
||||
<Label kind={kinds.INFO} title='List Refresh Interval'>
|
||||
{`Refresh: ${formatShortTimeSpan(minRefreshInterval)}`}
|
||||
{`${translate('Refresh')}: ${formatShortTimeSpan(minRefreshInterval)}`}
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
|
@ -95,9 +96,9 @@ class ImportList extends Component {
|
|||
<ConfirmModal
|
||||
isOpen={this.state.isDeleteImportListModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete Import List"
|
||||
message={`Are you sure you want to delete the list '${name}'?`}
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteImportList')}
|
||||
message={translate('DeleteImportListMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteImportList}
|
||||
onCancel={this.onDeleteImportListModalClose}
|
||||
/>
|
||||
|
|
|
@ -6,6 +6,7 @@ import Icon from 'Components/Icon';
|
|||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import sortByName from 'Utilities/Array/sortByName';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AddImportListModal from './AddImportListModal';
|
||||
import EditImportListModalConnector from './EditImportListModalConnector';
|
||||
import ImportList from './ImportList';
|
||||
|
@ -59,11 +60,9 @@ class ImportLists extends Component {
|
|||
} = this.state;
|
||||
|
||||
return (
|
||||
<FieldSet
|
||||
legend="Import Lists"
|
||||
>
|
||||
<FieldSet legend={translate('ImportLists')} >
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load Lists"
|
||||
errorMessage={translate('ImportListsLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.lists}>
|
||||
|
|
|
@ -7,6 +7,7 @@ import TableRow from 'Components/Table/TableRow';
|
|||
import TagListConnector from 'Components/TagListConnector';
|
||||
import { createQualityProfileSelectorForHook } from 'Store/Selectors/createQualityProfileSelector';
|
||||
import { SelectStateInputProps } from 'typings/props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ManageImportListsModalRow.css';
|
||||
|
||||
interface ManageImportListsModalRowProps {
|
||||
|
@ -63,7 +64,7 @@ function ManageImportListsModalRow(props: ManageImportListsModalRowProps) {
|
|||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.qualityProfileId}>
|
||||
{qualityProfile?.name ?? 'None'}
|
||||
{qualityProfile?.name ?? translate('None')}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.rootFolderPath}>
|
||||
|
@ -71,7 +72,7 @@ function ManageImportListsModalRow(props: ManageImportListsModalRowProps) {
|
|||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.enableAutomaticAdd}>
|
||||
{enableAutomaticAdd ? 'Yes' : 'No'}
|
||||
{enableAutomaticAdd ? translate('Yes') : translate('No')}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.tags}>
|
||||
|
|
|
@ -69,7 +69,7 @@ class IndexerSettings extends Component {
|
|||
} = this.state;
|
||||
|
||||
return (
|
||||
<PageContent title="Indexer Settings">
|
||||
<PageContent title={translate('IndexerSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
|
@ -78,7 +78,7 @@ class IndexerSettings extends Component {
|
|||
<PageToolbarSeparator />
|
||||
|
||||
<PageToolbarButton
|
||||
label="Test All Indexers"
|
||||
label={translate('TestAllIndexers')}
|
||||
iconName={icons.TEST}
|
||||
isSpinning={isTestingAll}
|
||||
onPress={dispatchTestAllIndexers}
|
||||
|
|
|
@ -5,6 +5,7 @@ import Link from 'Components/Link/Link';
|
|||
import Menu from 'Components/Menu/Menu';
|
||||
import MenuContent from 'Components/Menu/MenuContent';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AddIndexerPresetMenuItem from './AddIndexerPresetMenuItem';
|
||||
import styles from './AddIndexerItem.css';
|
||||
|
||||
|
@ -57,7 +58,7 @@ class AddIndexerItem extends Component {
|
|||
size={sizes.SMALL}
|
||||
onPress={this.onIndexerSelect}
|
||||
>
|
||||
Custom
|
||||
{translate('Custom')}
|
||||
</Button>
|
||||
|
||||
<Menu className={styles.presetsMenu}>
|
||||
|
@ -65,7 +66,7 @@ class AddIndexerItem extends Component {
|
|||
className={styles.presetsMenuButton}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
Presets
|
||||
{translate('Presets')}
|
||||
</Button>
|
||||
|
||||
<MenuContent>
|
||||
|
@ -90,7 +91,7 @@ class AddIndexerItem extends Component {
|
|||
to={infoLink}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
More info
|
||||
{translate('MoreInfo')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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 AddIndexerItem from './AddIndexerItem';
|
||||
import styles from './AddIndexerModalContent.css';
|
||||
|
||||
|
@ -31,7 +32,7 @@ class AddIndexerModalContent extends Component {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Add Indexer
|
||||
{translate('AddIndexer')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -42,7 +43,9 @@ class AddIndexerModalContent extends Component {
|
|||
|
||||
{
|
||||
!isSchemaFetching && !!schemaError &&
|
||||
<div>Unable to add a new indexer, please try again.</div>
|
||||
<div>
|
||||
{translate('AddIndexerError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -50,11 +53,15 @@ class AddIndexerModalContent extends Component {
|
|||
<div>
|
||||
|
||||
<Alert kind={kinds.INFO}>
|
||||
<div>Sonarr supports any indexer that uses the Newznab standard, as well as other indexers listed below.</div>
|
||||
<div>For more information on the individual indexers, click on the info buttons.</div>
|
||||
<div>
|
||||
{translate('SupportedIndexers')}
|
||||
</div>
|
||||
<div>
|
||||
{translate('SupportedIndexersMoreInfo')}
|
||||
</div>
|
||||
</Alert>
|
||||
|
||||
<FieldSet legend="Usenet">
|
||||
<FieldSet legend={translate('Usenet')}>
|
||||
<div className={styles.indexers}>
|
||||
{
|
||||
usenetIndexers.map((indexer) => {
|
||||
|
@ -71,7 +78,7 @@ class AddIndexerModalContent extends Component {
|
|||
</div>
|
||||
</FieldSet>
|
||||
|
||||
<FieldSet legend="Torrents">
|
||||
<FieldSet legend={translate('Torrents')}>
|
||||
<div className={styles.indexers}>
|
||||
{
|
||||
torrentIndexers.map((indexer) => {
|
||||
|
@ -94,7 +101,7 @@ class AddIndexerModalContent extends Component {
|
|||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Close
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -14,6 +14,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
|
|||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import AdvancedSettingsButton from 'Settings/AdvancedSettingsButton';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './EditIndexerModalContent.css';
|
||||
|
||||
function EditIndexerModalContent(props) {
|
||||
|
@ -55,7 +56,7 @@ function EditIndexerModalContent(props) {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{`${id ? 'Edit' : 'Add'} Indexer - ${implementationName}`}
|
||||
{`${id ? translate('EditIndexer') : translate('AddIndexer')} - ${implementationName}`}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -66,14 +67,16 @@ function EditIndexerModalContent(props) {
|
|||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<div>Unable to add a new indexer, please try again.</div>
|
||||
<div>
|
||||
{translate('AddIndexerError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && !error &&
|
||||
<Form {...otherProps}>
|
||||
<FormGroup>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{translate('Name')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
|
@ -84,13 +87,13 @@ function EditIndexerModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Enable RSS</FormLabel>
|
||||
<FormLabel>{translate('EnableRss')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableRss"
|
||||
helpText={supportsRss.value ? 'Will be used when Sonarr periodically looks for releases via RSS Sync' : undefined}
|
||||
helpTextWarning={supportsRss.value ? undefined : 'RSS is not supported with this indexer'}
|
||||
helpText={supportsRss.value ? translate('EnableRssHelpText') : undefined}
|
||||
helpTextWarning={supportsRss.value ? undefined : translate('RssIsNotSupportedWithThisIndexer')}
|
||||
isDisabled={!supportsRss.value}
|
||||
{...enableRss}
|
||||
onChange={onInputChange}
|
||||
|
@ -98,13 +101,13 @@ function EditIndexerModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Enable Automatic Search</FormLabel>
|
||||
<FormLabel>{translate('EnableAutomaticSearch')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableAutomaticSearch"
|
||||
helpText={supportsSearch.value ? 'Will be used when automatic searches are performed via the UI or by Sonarr' : undefined}
|
||||
helpTextWarning={supportsSearch.value ? undefined : 'Search is not supported with this indexer'}
|
||||
helpText={supportsSearch.value ? translate('EnableAutomaticSearchHelpText') : undefined}
|
||||
helpTextWarning={supportsSearch.value ? undefined : translate('SearchIsNotSupportedWithThisIndexer')}
|
||||
isDisabled={!supportsSearch.value}
|
||||
{...enableAutomaticSearch}
|
||||
onChange={onInputChange}
|
||||
|
@ -112,13 +115,13 @@ function EditIndexerModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Enable Interactive Search</FormLabel>
|
||||
<FormLabel>{translate('EnableInteractiveSearch')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableInteractiveSearch"
|
||||
helpText={supportsSearch.value ? 'Will be used when interactive search is used' : undefined}
|
||||
helpTextWarning={supportsSearch.value ? undefined : 'Search is not supported with this indexer'}
|
||||
helpText={supportsSearch.value ? translate('EnableInteractiveSearchHelpText') : undefined}
|
||||
helpTextWarning={supportsSearch.value ? undefined : translate('SearchIsNotSupportedWithThisIndexer')}
|
||||
isDisabled={!supportsSearch.value}
|
||||
{...enableInteractiveSearch}
|
||||
onChange={onInputChange}
|
||||
|
@ -144,12 +147,12 @@ function EditIndexerModalContent(props) {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Indexer Priority</FormLabel>
|
||||
<FormLabel>{translate('IndexerPriority')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="priority"
|
||||
helpText="Indexer Priority from 1 (Highest) to 50 (Lowest). Default: 25. Used when grabbing releases as a tiebreaker for otherwise equal releases, Sonarr will still use all enabled indexers for RSS Sync and Searching."
|
||||
helpText={translate('IndexerPriorityHelpText')}
|
||||
min={1}
|
||||
max={50}
|
||||
{...priority}
|
||||
|
@ -161,12 +164,12 @@ function EditIndexerModalContent(props) {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Maximum Single Episode Age</FormLabel>
|
||||
<FormLabel>{translate('MaximumSingleEpisodeAge')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="seasonSearchMaximumSingleEpisodeAge"
|
||||
helpText="During a full season search only season packs will be allowed when the season's last episode is older than this setting. Standard series only. Use 0 to disable."
|
||||
helpText={translate('MaximumSingleEpisodeAgeHelpText')}
|
||||
min={0}
|
||||
unit="days"
|
||||
{...seasonSearchMaximumSingleEpisodeAge}
|
||||
|
@ -178,12 +181,12 @@ function EditIndexerModalContent(props) {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>DownloadClient</FormLabel>
|
||||
<FormLabel>{translate('DownloadClient')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.DOWNLOAD_CLIENT_SELECT}
|
||||
name="downloadClientId"
|
||||
helpText={'Specify which download client is used for grabs from this indexer'}
|
||||
helpText={translate('IndexerDownloadClientHelpText')}
|
||||
{...downloadClientId}
|
||||
includeAny={true}
|
||||
protocol={protocol.value}
|
||||
|
@ -192,12 +195,12 @@ function EditIndexerModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Tags</FormLabel>
|
||||
<FormLabel>{translate('Tags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
name="tags"
|
||||
helpText="Only use this indexer for series with at least one matching tag. Leave blank to use with all series."
|
||||
helpText={translate('IndexerTagHelpText')}
|
||||
{...tags}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -213,7 +216,7 @@ function EditIndexerModalContent(props) {
|
|||
kind={kinds.DANGER}
|
||||
onPress={onDeleteIndexerPress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
|
@ -228,13 +231,13 @@ function EditIndexerModalContent(props) {
|
|||
error={saveError}
|
||||
onPress={onTestPress}
|
||||
>
|
||||
Test
|
||||
{translate('Test')}
|
||||
</SpinnerErrorButton>
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
|
@ -242,7 +245,7 @@ function EditIndexerModalContent(props) {
|
|||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -6,6 +6,7 @@ import IconButton from 'Components/Link/IconButton';
|
|||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import TagList from 'Components/TagList';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditIndexerModalConnector from './EditIndexerModalConnector';
|
||||
import styles from './Indexer.css';
|
||||
|
||||
|
@ -89,7 +90,7 @@ class Indexer extends Component {
|
|||
|
||||
<IconButton
|
||||
className={styles.cloneButton}
|
||||
title="Clone Indexer"
|
||||
title={translate('CloneIndexer')}
|
||||
name={icons.CLONE}
|
||||
onPress={this.onCloneIndexerPress}
|
||||
/>
|
||||
|
@ -100,28 +101,28 @@ class Indexer extends Component {
|
|||
{
|
||||
supportsRss && enableRss &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
RSS
|
||||
{translate('Rss')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
supportsSearch && enableAutomaticSearch &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
Automatic Search
|
||||
{translate('AutomaticSearch')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
supportsSearch && enableInteractiveSearch &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
Interactive Search
|
||||
{translate('InteractiveSearch')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
showPriority &&
|
||||
<Label kind={kinds.DEFAULT}>
|
||||
Priority: {priority}
|
||||
{translate('Priority')}: {priority}
|
||||
</Label>
|
||||
}
|
||||
{
|
||||
|
@ -130,7 +131,7 @@ class Indexer extends Component {
|
|||
kind={kinds.DISABLED}
|
||||
outline={true}
|
||||
>
|
||||
Disabled
|
||||
{translate('Disabled')}
|
||||
</Label>
|
||||
}
|
||||
</div>
|
||||
|
@ -150,9 +151,9 @@ class Indexer extends Component {
|
|||
<ConfirmModal
|
||||
isOpen={this.state.isDeleteIndexerModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete Indexer"
|
||||
message={`Are you sure you want to delete the indexer '${name}'?`}
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteIndexer')}
|
||||
message={translate('DeleteIndexerMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteIndexer}
|
||||
onCancel={this.onDeleteIndexerModalClose}
|
||||
/>
|
||||
|
|
|
@ -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 AddIndexerModal from './AddIndexerModal';
|
||||
import EditIndexerModalConnector from './EditIndexerModalConnector';
|
||||
import Indexer from './Indexer';
|
||||
|
@ -67,9 +68,9 @@ class Indexers extends Component {
|
|||
const showPriority = items.some((index) => index.priority !== 25);
|
||||
|
||||
return (
|
||||
<FieldSet legend="Indexers">
|
||||
<FieldSet legend={translate('Indexers')}>
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load Indexers"
|
||||
errorMessage={translate('IndexersLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.indexers}>
|
||||
|
|
|
@ -127,7 +127,7 @@ function ManageIndexersEditModalContent(
|
|||
|
||||
<ModalBody>
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('EnableRSS')}</FormLabel>
|
||||
<FormLabel>{translate('EnableRss')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
|
|
|
@ -48,7 +48,7 @@ const COLUMNS = [
|
|||
},
|
||||
{
|
||||
name: 'enableRss',
|
||||
label: () => translate('EnableRSS'),
|
||||
label: () => translate('EnableRss'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
|
|
|
@ -8,6 +8,7 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
|
|||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function IndexerOptions(props) {
|
||||
const {
|
||||
|
@ -20,7 +21,7 @@ function IndexerOptions(props) {
|
|||
} = props;
|
||||
|
||||
return (
|
||||
<FieldSet legend="Options">
|
||||
<FieldSet legend={translate('Options')}>
|
||||
{
|
||||
isFetching &&
|
||||
<LoadingIndicator />
|
||||
|
@ -28,49 +29,51 @@ function IndexerOptions(props) {
|
|||
|
||||
{
|
||||
!isFetching && error &&
|
||||
<Alert kind={kinds.DANGER}>Unable to load indexer options</Alert>
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('IndexerOptionsLoadError')}
|
||||
</Alert>
|
||||
}
|
||||
|
||||
{
|
||||
hasSettings && !isFetching && !error &&
|
||||
<Form>
|
||||
<FormGroup>
|
||||
<FormLabel>Minimum Age</FormLabel>
|
||||
<FormLabel>{translate('MinimumAge')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="minimumAge"
|
||||
min={0}
|
||||
unit="minutes"
|
||||
helpText="Usenet only: Minimum age in minutes of NZBs before they are grabbed. Use this to give new releases time to propagate to your usenet provider."
|
||||
helpText={translate('MinimumAgeHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.minimumAge}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Retention</FormLabel>
|
||||
<FormLabel>{translate('Retention')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="retention"
|
||||
min={0}
|
||||
unit="days"
|
||||
helpText="Usenet only: Set to zero to set for unlimited retention"
|
||||
helpText={translate('RetentionHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.retention}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Maximum Size</FormLabel>
|
||||
<FormLabel>{translate('MaximumSize')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="maximumSize"
|
||||
min={0}
|
||||
unit="MB"
|
||||
helpText="Maximum size for a release to be grabbed in MB. Set to zero to set to unlimited"
|
||||
helpText={translate('MaximumSizeHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.maximumSize}
|
||||
/>
|
||||
|
@ -80,7 +83,7 @@ function IndexerOptions(props) {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>RSS Sync Interval</FormLabel>
|
||||
<FormLabel>{translate('RssSyncInterval')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
|
@ -88,8 +91,8 @@ function IndexerOptions(props) {
|
|||
min={0}
|
||||
max={120}
|
||||
unit="minutes"
|
||||
helpText="Interval in minutes. Set to zero to disable (this will stop all automatic release grabbing)"
|
||||
helpTextWarning="This will apply to all indexers, please follow the rules set forth by them"
|
||||
helpText={translate('RssSyncIntervalHelpText')}
|
||||
helpTextWarning={translate('RssSyncIntervalHelpTextWarning')}
|
||||
helpLink="https://wiki.servarr.com/sonarr/faq#how-does-sonarr-find-episodes"
|
||||
onChange={onInputChange}
|
||||
{...settings.rssSyncInterval}
|
||||
|
|
|
@ -17,27 +17,87 @@ import NamingConnector from './Naming/NamingConnector';
|
|||
import AddRootFolder from './RootFolder/AddRootFolder';
|
||||
|
||||
const episodeTitleRequiredOptions = [
|
||||
{ key: 'always', value: 'Always' },
|
||||
{ key: 'bulkSeasonReleases', value: 'Only for Bulk Season Releases' },
|
||||
{ key: 'never', value: 'Never' }
|
||||
{
|
||||
key: 'always',
|
||||
get value() {
|
||||
return translate('Always');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'bulkSeasonReleases',
|
||||
get value() {
|
||||
return translate('OnlyForBulkSeasonReleases');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'never',
|
||||
get value() {
|
||||
return translate('Never');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const rescanAfterRefreshOptions = [
|
||||
{ key: 'always', value: 'Always' },
|
||||
{ key: 'afterManual', value: 'After Manual Refresh' },
|
||||
{ key: 'never', value: 'Never' }
|
||||
{
|
||||
key: 'always',
|
||||
get value() {
|
||||
return translate('Always');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'afterManual',
|
||||
get value() {
|
||||
return translate('AfterManualRefresh');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'never',
|
||||
get value() {
|
||||
return translate('Never');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const downloadPropersAndRepacksOptions = [
|
||||
{ key: 'preferAndUpgrade', value: 'Prefer and Upgrade' },
|
||||
{ key: 'doNotUpgrade', value: 'Do not Upgrade Automatically' },
|
||||
{ key: 'doNotPrefer', value: 'Do not Prefer' }
|
||||
{
|
||||
key: 'preferAndUpgrade',
|
||||
get value() {
|
||||
return translate('PreferAndUpgrade');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'doNotUpgrade',
|
||||
get value() {
|
||||
return translate('DoNotUpgradeAutomatically');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'doNotPrefer',
|
||||
get value() {
|
||||
return translate('DoNotPrefer');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const fileDateOptions = [
|
||||
{ key: 'none', value: 'None' },
|
||||
{ key: 'localAirDate', value: 'Local Air Date' },
|
||||
{ key: 'utcAirDate', value: 'UTC Air Date' }
|
||||
{
|
||||
key: 'none',
|
||||
get value() {
|
||||
return translate('None');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'localAirDate',
|
||||
get value() {
|
||||
return translate('LocalAirDate');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'utcAirDate',
|
||||
get value() {
|
||||
return translate('UtcAirDate');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
class MediaManagement extends Component {
|
||||
|
@ -59,7 +119,7 @@ class MediaManagement extends Component {
|
|||
} = this.props;
|
||||
|
||||
return (
|
||||
<PageContent title="Media Management Settings">
|
||||
<PageContent title={translate('MediaManagementSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
advancedSettings={advancedSettings}
|
||||
{...otherProps}
|
||||
|
@ -71,15 +131,17 @@ class MediaManagement extends Component {
|
|||
|
||||
{
|
||||
isFetching ?
|
||||
<FieldSet legend="Naming Settings">
|
||||
<FieldSet legend={translate('NamingSettings')}>
|
||||
<LoadingIndicator />
|
||||
</FieldSet> : null
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && error ?
|
||||
<FieldSet legend="Naming Settings">
|
||||
<Alert kind={kinds.DANGER}>Unable to load Media Management settings</Alert>
|
||||
<FieldSet legend={translate('NamingSettings')}>
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('MediaManagementSettingsLoadError')}
|
||||
</Alert>
|
||||
</FieldSet> : null
|
||||
}
|
||||
|
||||
|
@ -91,18 +153,18 @@ class MediaManagement extends Component {
|
|||
>
|
||||
{
|
||||
advancedSettings ?
|
||||
<FieldSet legend="Folders">
|
||||
<FieldSet legend={translate('Folders')}>
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>Create empty series folders</FormLabel>
|
||||
<FormLabel>{translate('CreateEmptySeriesFolders')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="createEmptySeriesFolders"
|
||||
helpText="Create missing series folders during disk scan"
|
||||
helpText={translate('CreateEmptySeriesFoldersHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.createEmptySeriesFolders}
|
||||
/>
|
||||
|
@ -113,12 +175,12 @@ class MediaManagement extends Component {
|
|||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>Delete empty folders</FormLabel>
|
||||
<FormLabel>{translate('DeleteEmptyFolders')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="deleteEmptyFolders"
|
||||
helpText="Delete empty series and season folders during disk scan and when episode files are deleted"
|
||||
helpText={translate('DeleteEmptyFoldersHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.deleteEmptyFolders}
|
||||
/>
|
||||
|
@ -129,19 +191,19 @@ class MediaManagement extends Component {
|
|||
{
|
||||
advancedSettings ?
|
||||
<FieldSet
|
||||
legend="Importing"
|
||||
legend={translate('Importing')}
|
||||
>
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
<FormLabel>Episode Title Required</FormLabel>
|
||||
<FormLabel>{translate('EpisodeTitleRequired')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="episodeTitleRequired"
|
||||
helpText="Prevent importing for up to 48 hours if the episode title is in the naming format and the episode title is TBA"
|
||||
helpText={translate('EpisodeTitleRequiredHelpText')}
|
||||
values={episodeTitleRequiredOptions}
|
||||
onChange={onInputChange}
|
||||
{...settings.episodeTitleRequired}
|
||||
|
@ -156,12 +218,12 @@ class MediaManagement extends Component {
|
|||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>Skip Free Space Check</FormLabel>
|
||||
<FormLabel>{translate('SkipFreeSpaceCheck')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="skipFreeSpaceCheckWhenImporting"
|
||||
helpText="Use when Sonarr is unable to detect free space from your series root folder"
|
||||
helpText={translate('SkipFreeSpaceCheckWhenImportingHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.skipFreeSpaceCheckWhenImporting}
|
||||
/>
|
||||
|
@ -173,13 +235,13 @@ class MediaManagement extends Component {
|
|||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>Minimum Free Space</FormLabel>
|
||||
<FormLabel>{translate('MinimumFreeSpace')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
unit='MB'
|
||||
name="minimumFreeSpaceWhenImporting"
|
||||
helpText="Prevent import if it would leave less than this amount of disk space available"
|
||||
helpText={translate('MinimumFreeSpaceHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.minimumFreeSpaceWhenImporting}
|
||||
/>
|
||||
|
@ -190,13 +252,13 @@ class MediaManagement extends Component {
|
|||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>Use Hardlinks instead of Copy</FormLabel>
|
||||
<FormLabel>{translate('UseHardlinksInsteadOfCopy')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="copyUsingHardlinks"
|
||||
helpText="Hardlinks allow Sonarr to import seeding torrents to the the series folder without taking extra disk space or copying the entire contents of the file. Hardlinks will only work if the source and destination are on the same volume"
|
||||
helpTextWarning="Occasionally, file locks may prevent renaming files that are being seeded. You may temporarily disable seeding and use Sonarr's rename function as a work around."
|
||||
helpText={translate('CopyUsingHardlinksHelpText')}
|
||||
helpTextWarning={translate('CopyUsingHardlinksHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...settings.copyUsingHardlinks}
|
||||
/>
|
||||
|
@ -207,12 +269,12 @@ class MediaManagement extends Component {
|
|||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>Import Using Script</FormLabel>
|
||||
<FormLabel>{translate('ImportUsingScript')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="useScriptImport"
|
||||
helpText="Copy files for importing using a script (ex. for transcoding)"
|
||||
helpText={translate('ImportUsingScriptHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.useScriptImport}
|
||||
/>
|
||||
|
@ -224,13 +286,13 @@ class MediaManagement extends Component {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Import Script Path</FormLabel>
|
||||
<FormLabel>{translate('ImportScriptPath')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.PATH}
|
||||
includeFiles={true}
|
||||
name="scriptImportPath"
|
||||
helpText="The path to the script to use for importing"
|
||||
helpText={translate('ImportScriptPathHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.scriptImportPath}
|
||||
/>
|
||||
|
@ -238,12 +300,12 @@ class MediaManagement extends Component {
|
|||
}
|
||||
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>Import Extra Files</FormLabel>
|
||||
<FormLabel>{translate('ImportExtraFiles')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="importExtraFiles"
|
||||
helpText="Import matching extra files (subtitles, nfo, etc) after importing an episode file"
|
||||
helpText={translate('ImportExtraFilesHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.importExtraFiles}
|
||||
/>
|
||||
|
@ -255,14 +317,14 @@ class MediaManagement extends Component {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Import Extra Files</FormLabel>
|
||||
<FormLabel>{translate('ImportExtraFiles')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="extraFileExtensions"
|
||||
helpTexts={[
|
||||
'Comma separated list of extra files to import (.nfo will be imported as .nfo-orig)',
|
||||
'Examples: ".sub, .nfo" or "sub,nfo"'
|
||||
translate('ExtraFileExtensionsHelpText'),
|
||||
translate('ExtraFileExtensionsHelpTextsExamples')
|
||||
]}
|
||||
onChange={onInputChange}
|
||||
{...settings.extraFileExtensions}
|
||||
|
@ -273,15 +335,15 @@ class MediaManagement extends Component {
|
|||
}
|
||||
|
||||
<FieldSet
|
||||
legend="File Management"
|
||||
legend={translate('FileManagement')}
|
||||
>
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>Unmonitor Deleted Episodes</FormLabel>
|
||||
<FormLabel>{translate('UnmonitorDeletedEpisodes')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="autoUnmonitorPreviouslyDownloadedEpisodes"
|
||||
helpText="Episodes deleted from disk are automatically unmonitored in Sonarr"
|
||||
helpText={translate('UnmonitorDeletedEpisodesHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.autoUnmonitorPreviouslyDownloadedEpisodes}
|
||||
/>
|
||||
|
@ -292,18 +354,18 @@ class MediaManagement extends Component {
|
|||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>Propers and Repacks</FormLabel>
|
||||
<FormLabel>{translate('DownloadPropersAndRepacks')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="downloadPropersAndRepacks"
|
||||
helpTexts={[
|
||||
'Whether or not to automatically upgrade to Propers/Repacks',
|
||||
'Use \'Do not Prefer\' to sort by custom format score over propers/repacks'
|
||||
translate('DownloadPropersAndRepacksHelpText'),
|
||||
translate('DownloadPropersAndRepacksHelpTextCustomFormat')
|
||||
]}
|
||||
helpTextWarning={
|
||||
settings.downloadPropersAndRepacks.value === 'doNotPrefer' ?
|
||||
'Use custom formats for automatic upgrades to propers/repacks' :
|
||||
translate('DownloadPropersAndRepacksHelpTextWarning') :
|
||||
undefined
|
||||
}
|
||||
values={downloadPropersAndRepacksOptions}
|
||||
|
@ -317,12 +379,12 @@ class MediaManagement extends Component {
|
|||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>Analyse video files</FormLabel>
|
||||
<FormLabel>{translate('AnalyseVideoFiles')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableMediaInfo"
|
||||
helpText="Extract video information such as resolution, runtime and codec information from files. This requires Sonarr to read parts of the file which may cause high disk or network activity during scans."
|
||||
helpText={translate('AnalyseVideoFilesHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.enableMediaInfo}
|
||||
/>
|
||||
|
@ -332,13 +394,13 @@ class MediaManagement extends Component {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Rescan Series Folder after Refresh</FormLabel>
|
||||
<FormLabel>{translate('RescanSeriesFolderAfterRefresh')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="rescanAfterRefresh"
|
||||
helpText="Rescan the series folder after refreshing the series"
|
||||
helpTextWarning="Sonarr will not automatically detect changes to files when not set to 'Always'"
|
||||
helpText={translate('RescanAfterRefreshHelpText')}
|
||||
helpTextWarning={translate('RescanAfterRefreshHelpTextWarning')}
|
||||
values={rescanAfterRefreshOptions}
|
||||
onChange={onInputChange}
|
||||
{...settings.rescanAfterRefresh}
|
||||
|
@ -349,12 +411,12 @@ class MediaManagement extends Component {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Change File Date</FormLabel>
|
||||
<FormLabel>{translate('ChangeFileDate')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="fileDate"
|
||||
helpText="Change file date on import/rescan"
|
||||
helpText={translate('ChangeFileDateHelpText')}
|
||||
values={fileDateOptions}
|
||||
onChange={onInputChange}
|
||||
{...settings.fileDate}
|
||||
|
@ -365,12 +427,12 @@ class MediaManagement extends Component {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Recycling Bin</FormLabel>
|
||||
<FormLabel>{translate('RecyclingBin')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.PATH}
|
||||
name="recycleBin"
|
||||
helpText="Episode files will go here when deleted instead of being permanently deleted"
|
||||
helpText={translate('RecyclingBinHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.recycleBin}
|
||||
/>
|
||||
|
@ -380,13 +442,13 @@ class MediaManagement extends Component {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Recycling Bin Cleanup</FormLabel>
|
||||
<FormLabel>{translate('RecyclingBinCleanup')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="recycleBinCleanupDays"
|
||||
helpText="Set to 0 to disable automatic cleanup"
|
||||
helpTextWarning="Files in the recycle bin older than the selected number of days will be cleaned up automatically"
|
||||
helpText={translate('RecyclingBinCleanupHelpText')}
|
||||
helpTextWarning={translate('RecyclingBinCleanupHelpTextWarning')}
|
||||
min={0}
|
||||
onChange={onInputChange}
|
||||
{...settings.recycleBinCleanupDays}
|
||||
|
@ -397,20 +459,20 @@ class MediaManagement extends Component {
|
|||
{
|
||||
advancedSettings && !isWindows ?
|
||||
<FieldSet
|
||||
legend="Permissions"
|
||||
legend={translate('Permissions')}
|
||||
>
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>Set Permissions</FormLabel>
|
||||
<FormLabel>{translate('SetPermissions')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="setPermissionsLinux"
|
||||
helpText="Should chmod be run when files are imported/renamed?"
|
||||
helpTextWarning="If you're unsure what these settings do, do not alter them."
|
||||
helpText={translate('SetPermissionsLinuxHelpText')}
|
||||
helpTextWarning={translate('SetPermissionsLinuxHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...settings.setPermissionsLinux}
|
||||
/>
|
||||
|
@ -420,13 +482,13 @@ class MediaManagement extends Component {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>chmod Folder</FormLabel>
|
||||
<FormLabel>{translate('ChmodFolder')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.UMASK}
|
||||
name="chmodFolder"
|
||||
helpText="Octal, applied during import/rename to media folders and files (without execute bits)"
|
||||
helpTextWarning="This only works if the user running sonarr is the owner of the file. It's better to ensure the download client sets the permissions properly."
|
||||
helpText={translate('ChmodFolderHelpText')}
|
||||
helpTextWarning={translate('ChmodFolderHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...settings.chmodFolder}
|
||||
/>
|
||||
|
@ -436,13 +498,13 @@ class MediaManagement extends Component {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>chown Group</FormLabel>
|
||||
<FormLabel>{translate('ChownGroup')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="chownGroup"
|
||||
helpText="Group name or gid. Use gid for remote file systems."
|
||||
helpTextWarning="This only works if the user running sonarr is the owner of the file. It's better to ensure the download client uses the same group as sonarr."
|
||||
helpText={translate('ChownGroupHelpText')}
|
||||
helpTextWarning={translate('ChownGroupHelpTextWarning')}
|
||||
values={fileDateOptions}
|
||||
onChange={onInputChange}
|
||||
{...settings.chownGroup}
|
||||
|
|
|
@ -9,6 +9,7 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
|
|||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import NamingModal from './NamingModal';
|
||||
import styles from './Naming.css';
|
||||
|
||||
|
@ -124,20 +125,20 @@ class Naming extends Component {
|
|||
const replaceIllegalCharacters = hasSettings && settings.replaceIllegalCharacters.value;
|
||||
|
||||
const multiEpisodeStyleOptions = [
|
||||
{ key: 0, value: 'Extend', hint: 'S01E01-02-03' },
|
||||
{ key: 1, value: 'Duplicate', hint: 'S01E01.S01E02' },
|
||||
{ key: 2, value: 'Repeat', hint: 'S01E01E02E03' },
|
||||
{ key: 3, value: 'Scene', hint: 'S01E01-E02-E03' },
|
||||
{ key: 4, value: 'Range', hint: 'S01E01-03' },
|
||||
{ key: 5, value: 'Prefixed Range', hint: 'S01E01-E03' }
|
||||
{ key: 0, value: translate('Extend'), hint: 'S01E01-02-03' },
|
||||
{ key: 1, value: translate('Duplicate'), hint: 'S01E01.S01E02' },
|
||||
{ key: 2, value: translate('Repeat'), hint: 'S01E01E02E03' },
|
||||
{ key: 3, value: translate('Scene'), hint: 'S01E01-E02-E03' },
|
||||
{ key: 4, value: translate('Range'), hint: 'S01E01-03' },
|
||||
{ key: 5, value: translate('PrefixedRange'), hint: 'S01E01-E03' }
|
||||
];
|
||||
|
||||
const colonReplacementOptions = [
|
||||
{ key: 0, value: 'Delete' },
|
||||
{ key: 1, value: 'Replace with Dash' },
|
||||
{ key: 2, value: 'Replace with Space Dash' },
|
||||
{ key: 3, value: 'Replace with Space Dash Space' },
|
||||
{ key: 4, value: 'Smart Replace', hint: 'Dash or Space Dash depending on name' }
|
||||
{ key: 0, value: translate('Delete') },
|
||||
{ key: 1, value: translate('ReplaceWithDash') },
|
||||
{ key: 2, value: translate('ReplaceWithSpaceDash') },
|
||||
{ key: 3, value: translate('ReplaceWithSpaceDashSpace') },
|
||||
{ key: 4, value: translate('SmartReplace'), hint: translate('SmartReplaceHint') }
|
||||
];
|
||||
|
||||
const standardEpisodeFormatHelpTexts = [];
|
||||
|
@ -155,56 +156,56 @@ class Naming extends Component {
|
|||
|
||||
if (examplesPopulated) {
|
||||
if (examples.singleEpisodeExample) {
|
||||
standardEpisodeFormatHelpTexts.push(`Single Episode: ${examples.singleEpisodeExample}`);
|
||||
standardEpisodeFormatHelpTexts.push(`${translate('SingleEpisode')}: ${examples.singleEpisodeExample}`);
|
||||
} else {
|
||||
standardEpisodeFormatErrors.push({ message: 'Single Episode: Invalid Format' });
|
||||
standardEpisodeFormatErrors.push({ message: translate('SingleEpisodeInvalidFormat') });
|
||||
}
|
||||
|
||||
if (examples.multiEpisodeExample) {
|
||||
standardEpisodeFormatHelpTexts.push(`Multi Episode: ${examples.multiEpisodeExample}`);
|
||||
standardEpisodeFormatHelpTexts.push(`${translate('MultiEpisode')}: ${examples.multiEpisodeExample}`);
|
||||
} else {
|
||||
standardEpisodeFormatErrors.push({ message: 'Multi Episode: Invalid Format' });
|
||||
standardEpisodeFormatErrors.push({ message: translate('MultiEpisodeInvalidFormat') });
|
||||
}
|
||||
|
||||
if (examples.dailyEpisodeExample) {
|
||||
dailyEpisodeFormatHelpTexts.push(`Example: ${examples.dailyEpisodeExample}`);
|
||||
dailyEpisodeFormatHelpTexts.push(`${translate('Example')}: ${examples.dailyEpisodeExample}`);
|
||||
} else {
|
||||
dailyEpisodeFormatErrors.push({ message: 'Invalid Format' });
|
||||
dailyEpisodeFormatErrors.push({ message: translate('InvalidFormat') });
|
||||
}
|
||||
|
||||
if (examples.animeEpisodeExample) {
|
||||
animeEpisodeFormatHelpTexts.push(`Single Episode: ${examples.animeEpisodeExample}`);
|
||||
animeEpisodeFormatHelpTexts.push(`${translate('SingleEpisode')}: ${examples.animeEpisodeExample}`);
|
||||
} else {
|
||||
animeEpisodeFormatErrors.push({ message: 'Single Episode: Invalid Format' });
|
||||
animeEpisodeFormatErrors.push({ message: translate('SingleEpisodeInvalidFormat') });
|
||||
}
|
||||
|
||||
if (examples.animeMultiEpisodeExample) {
|
||||
animeEpisodeFormatHelpTexts.push(`Multi Episode: ${examples.animeMultiEpisodeExample}`);
|
||||
animeEpisodeFormatHelpTexts.push(`${translate('MultiEpisode')}: ${examples.animeMultiEpisodeExample}`);
|
||||
} else {
|
||||
animeEpisodeFormatErrors.push({ message: 'Multi Episode: Invalid Format' });
|
||||
animeEpisodeFormatErrors.push({ message: translate('MultiEpisodeInvalidFormat') });
|
||||
}
|
||||
|
||||
if (examples.seriesFolderExample) {
|
||||
seriesFolderFormatHelpTexts.push(`Example: ${examples.seriesFolderExample}`);
|
||||
seriesFolderFormatHelpTexts.push(`${translate('Example')}: ${examples.seriesFolderExample}`);
|
||||
} else {
|
||||
seriesFolderFormatErrors.push({ message: 'Invalid Format' });
|
||||
seriesFolderFormatErrors.push({ message: translate('InvalidFormat') });
|
||||
}
|
||||
|
||||
if (examples.seasonFolderExample) {
|
||||
seasonFolderFormatHelpTexts.push(`Example: ${examples.seasonFolderExample}`);
|
||||
seasonFolderFormatHelpTexts.push(`${translate('Example')}: ${examples.seasonFolderExample}`);
|
||||
} else {
|
||||
seasonFolderFormatErrors.push({ message: 'Invalid Format' });
|
||||
seasonFolderFormatErrors.push({ message: translate('InvalidFormat') });
|
||||
}
|
||||
|
||||
if (examples.specialsFolderExample) {
|
||||
specialsFolderFormatHelpTexts.push(`Example: ${examples.specialsFolderExample}`);
|
||||
specialsFolderFormatHelpTexts.push(`${translate('Example')}: ${examples.specialsFolderExample}`);
|
||||
} else {
|
||||
specialsFolderFormatErrors.push({ message: 'Invalid Format' });
|
||||
specialsFolderFormatErrors.push({ message: translate('InvalidFormat') });
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<FieldSet legend="Episode Naming">
|
||||
<FieldSet legend={translate('EpisodeNaming')}>
|
||||
{
|
||||
isFetching &&
|
||||
<LoadingIndicator />
|
||||
|
@ -212,31 +213,33 @@ class Naming extends Component {
|
|||
|
||||
{
|
||||
!isFetching && error &&
|
||||
<Alert kind={kinds.DANGER}>Unable to load Naming settings</Alert>
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('NamingSettingsLoadError')}
|
||||
</Alert>
|
||||
}
|
||||
|
||||
{
|
||||
hasSettings && !isFetching && !error &&
|
||||
<Form>
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>Rename Episodes</FormLabel>
|
||||
<FormLabel>{translate('RenameEpisodes')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="renameEpisodes"
|
||||
helpText="Sonarr will use the existing file name if renaming is disabled"
|
||||
helpText={translate('RenameEpisodesHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.renameEpisodes}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>Replace Illegal Characters</FormLabel>
|
||||
<FormLabel>{translate('ReplaceIllegalCharacters')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="replaceIllegalCharacters"
|
||||
helpText="Replace illegal characters. If unchecked, Sonarr will remove them instead"
|
||||
helpText={translate('ReplaceIllegalCharactersHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.replaceIllegalCharacters}
|
||||
/>
|
||||
|
@ -245,12 +248,13 @@ class Naming extends Component {
|
|||
{
|
||||
replaceIllegalCharacters ?
|
||||
<FormGroup>
|
||||
<FormLabel>Colon Replacement</FormLabel>
|
||||
<FormLabel>{translate('ColonReplacement')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="colonReplacementFormat"
|
||||
values={colonReplacementOptions}
|
||||
helpText={translate('ColonReplacementFormatHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.colonReplacementFormat}
|
||||
/>
|
||||
|
@ -262,7 +266,7 @@ class Naming extends Component {
|
|||
renameEpisodes &&
|
||||
<div>
|
||||
<FormGroup size={sizes.LARGE}>
|
||||
<FormLabel>Standard Episode Format</FormLabel>
|
||||
<FormLabel>{translate('StandardEpisodeFormat')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
inputClassName={styles.namingInput}
|
||||
|
@ -277,7 +281,7 @@ class Naming extends Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup size={sizes.LARGE}>
|
||||
<FormLabel>Daily Episode Format</FormLabel>
|
||||
<FormLabel>{translate('DailyEpisodeFormat')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
inputClassName={styles.namingInput}
|
||||
|
@ -292,7 +296,7 @@ class Naming extends Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup size={sizes.LARGE}>
|
||||
<FormLabel>Anime Episode Format</FormLabel>
|
||||
<FormLabel>{translate('DailyEpisodeFormat')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
inputClassName={styles.namingInput}
|
||||
|
@ -312,7 +316,7 @@ class Naming extends Component {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Series Folder Format</FormLabel>
|
||||
<FormLabel>{translate('SeriesFolderFormat')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
inputClassName={styles.namingInput}
|
||||
|
@ -321,13 +325,13 @@ class Naming extends Component {
|
|||
buttons={<FormInputButton onPress={this.onSeriesFolderNamingModalOpenClick}>?</FormInputButton>}
|
||||
onChange={onInputChange}
|
||||
{...settings.seriesFolderFormat}
|
||||
helpTexts={['Used when adding a new series or moving series via the series editor', ...seriesFolderFormatHelpTexts]}
|
||||
helpTexts={[translate('SeriesFolderFormatHelpText'), ...seriesFolderFormatHelpTexts]}
|
||||
errors={[...seriesFolderFormatErrors, ...settings.seriesFolderFormat.errors]}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Season Folder Format</FormLabel>
|
||||
<FormLabel>{translate('SeasonFolderFormat')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
inputClassName={styles.namingInput}
|
||||
|
@ -345,7 +349,7 @@ class Naming extends Component {
|
|||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Specials Folder Format</FormLabel>
|
||||
<FormLabel>{translate('SpecialsFolderFormat')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
inputClassName={styles.namingInput}
|
||||
|
@ -360,7 +364,7 @@ class Naming extends Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Multi-Episode Style</FormLabel>
|
||||
<FormLabel>{translate('MultiEpisodeStyle')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
|
|
|
@ -11,20 +11,57 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { icons, sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import InlineMarkdown from '../../../Components/Markdown/InlineMarkdown';
|
||||
import NamingOption from './NamingOption';
|
||||
import styles from './NamingModal.css';
|
||||
|
||||
const separatorOptions = [
|
||||
{ key: ' ', value: 'Space ( )' },
|
||||
{ key: '.', value: 'Period (.)' },
|
||||
{ key: '_', value: 'Underscore (_)' },
|
||||
{ key: '-', value: 'Dash (-)' }
|
||||
{
|
||||
key: ' ',
|
||||
get value() {
|
||||
return `${translate('Space')} ( )`;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: '.',
|
||||
get value() {
|
||||
return `${translate('Period')} (.)`;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: '_',
|
||||
get value() {
|
||||
return `${translate('Underscore')} (_)`;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: '-',
|
||||
get value() {
|
||||
return `${translate('Dash')} (-)`;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const caseOptions = [
|
||||
{ key: 'title', value: 'Default Case' },
|
||||
{ key: 'lower', value: 'Lowercase' },
|
||||
{ key: 'upper', value: 'Uppercase' }
|
||||
{
|
||||
key: 'title',
|
||||
get value() {
|
||||
return translate('DefaultCase');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'lower',
|
||||
get value() {
|
||||
return translate('Lowercase');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'upper',
|
||||
get value() {
|
||||
return translate('Uppercase');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const fileNameTokens = [
|
||||
|
@ -208,7 +245,7 @@ class NamingModal extends Component {
|
|||
>
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
File Name Tokens
|
||||
{translate('FileNameTokens')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -232,7 +269,7 @@ class NamingModal extends Component {
|
|||
|
||||
{
|
||||
!advancedSettings &&
|
||||
<FieldSet legend="File Names">
|
||||
<FieldSet legend={translate('FileNames')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
fileNameTokens.map(({ token, example }) => {
|
||||
|
@ -257,7 +294,7 @@ class NamingModal extends Component {
|
|||
</FieldSet>
|
||||
}
|
||||
|
||||
<FieldSet legend="Series">
|
||||
<FieldSet legend={translate('Series')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
seriesTokens.map(({ token, example }) => {
|
||||
|
@ -279,7 +316,7 @@ class NamingModal extends Component {
|
|||
</div>
|
||||
</FieldSet>
|
||||
|
||||
<FieldSet legend="Series ID">
|
||||
<FieldSet legend={translate('SeriesID')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
seriesIdTokens.map(({ token, example }) => {
|
||||
|
@ -303,7 +340,7 @@ class NamingModal extends Component {
|
|||
|
||||
{
|
||||
season &&
|
||||
<FieldSet legend="Season">
|
||||
<FieldSet legend={translate('Season')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
seasonTokens.map(({ token, example }) => {
|
||||
|
@ -329,7 +366,7 @@ class NamingModal extends Component {
|
|||
{
|
||||
episode &&
|
||||
<div>
|
||||
<FieldSet legend="Episode">
|
||||
<FieldSet legend={translate('Episode')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
episodeTokens.map(({ token, example }) => {
|
||||
|
@ -351,7 +388,7 @@ class NamingModal extends Component {
|
|||
</div>
|
||||
</FieldSet>
|
||||
|
||||
<FieldSet legend="Air-Date">
|
||||
<FieldSet legend={translate('AirDate')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
airDateTokens.map(({ token, example }) => {
|
||||
|
@ -375,7 +412,7 @@ class NamingModal extends Component {
|
|||
|
||||
{
|
||||
anime &&
|
||||
<FieldSet legend="Absolute Episode Number">
|
||||
<FieldSet legend={translate('AbsoluteEpisodeNumber')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
absoluteTokens.map(({ token, example }) => {
|
||||
|
@ -403,7 +440,7 @@ class NamingModal extends Component {
|
|||
{
|
||||
additional &&
|
||||
<div>
|
||||
<FieldSet legend="Episode Title">
|
||||
<FieldSet legend={translate('EpisodeTitle')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
episodeTitleTokens.map(({ token, example }) => {
|
||||
|
@ -425,7 +462,7 @@ class NamingModal extends Component {
|
|||
</div>
|
||||
</FieldSet>
|
||||
|
||||
<FieldSet legend="Quality">
|
||||
<FieldSet legend={translate('Quality')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
qualityTokens.map(({ token, example }) => {
|
||||
|
@ -447,7 +484,7 @@ class NamingModal extends Component {
|
|||
</div>
|
||||
</FieldSet>
|
||||
|
||||
<FieldSet legend="Media Info">
|
||||
<FieldSet legend={translate('MediaInfo')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
mediaInfoTokens.map(({ token, example, footNote }) => {
|
||||
|
@ -471,14 +508,11 @@ class NamingModal extends Component {
|
|||
|
||||
<div className={styles.footNote}>
|
||||
<Icon className={styles.icon} name={icons.FOOTNOTE} />
|
||||
<div>
|
||||
MediaInfo Full/AudioLanguages/SubtitleLanguages support a <code>:EN+DE</code> suffix allowing you to filter the languages included in the filename. Use <code>-DE</code> to exclude specific languages.
|
||||
Appending <code>+</code> (eg <code>:EN+</code>) will output <code>[EN]</code>/<code>[EN+--]</code>/<code>[--]</code> depending on excluded languages. For example <code>{'{'}MediaInfo Full:EN+DE{'}'}</code>.
|
||||
</div>
|
||||
<InlineMarkdown data={translate('MediaInfoFootNote')} />
|
||||
</div>
|
||||
</FieldSet>
|
||||
|
||||
<FieldSet legend="Other">
|
||||
<FieldSet legend={translate('Other')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
otherTokens.map(({ token, example }) => {
|
||||
|
@ -500,7 +534,7 @@ class NamingModal extends Component {
|
|||
</div>
|
||||
</FieldSet>
|
||||
|
||||
<FieldSet legend="Original">
|
||||
<FieldSet legend={translate('Original')}>
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
originalTokens.map(({ token, example }) => {
|
||||
|
@ -534,7 +568,7 @@ class NamingModal extends Component {
|
|||
onSelectionChange={this.onInputSelectionChange}
|
||||
/>
|
||||
<Button onPress={onModalClose}>
|
||||
Close
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -12,6 +12,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';
|
||||
|
||||
function EditMetadataModalContent(props) {
|
||||
const {
|
||||
|
@ -35,18 +36,18 @@ function EditMetadataModalContent(props) {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Edit {name.value} Metadata
|
||||
{translate('EditMetadata', { metadataType: name.value })}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<Form {...otherProps}>
|
||||
<FormGroup>
|
||||
<FormLabel>Enable</FormLabel>
|
||||
<FormLabel>{translate('Enable')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enable"
|
||||
helpText="Enable metadata file creation for this metadata type"
|
||||
helpText={translate('EnableMetadataHelpText')}
|
||||
{...enable}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -74,7 +75,7 @@ function EditMetadataModalContent(props) {
|
|||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
|
@ -82,7 +83,7 @@ function EditMetadataModalContent(props) {
|
|||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -3,6 +3,7 @@ import React, { Component } from 'react';
|
|||
import Card from 'Components/Card';
|
||||
import Label from 'Components/Label';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditMetadataModalConnector from './EditMetadataModalConnector';
|
||||
import styles from './Metadata.css';
|
||||
|
||||
|
@ -66,13 +67,13 @@ class Metadata extends Component {
|
|||
{
|
||||
enable ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
Enabled
|
||||
{translate('Enabled')}
|
||||
</Label> :
|
||||
<Label
|
||||
kind={kinds.DISABLED}
|
||||
outline={true}
|
||||
>
|
||||
Disabled
|
||||
{translate('Disabled')}
|
||||
</Label>
|
||||
}
|
||||
</div>
|
||||
|
@ -81,7 +82,7 @@ class Metadata extends Component {
|
|||
enable && !!metadataFields.length &&
|
||||
<div>
|
||||
<div className={styles.section}>
|
||||
Metadata
|
||||
{translate('Metadata')}
|
||||
</div>
|
||||
|
||||
{
|
||||
|
@ -107,7 +108,7 @@ class Metadata extends Component {
|
|||
enable && !!imageFields.length &&
|
||||
<div>
|
||||
<div className={styles.section}>
|
||||
Images
|
||||
{translate('Images')}
|
||||
</div>
|
||||
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|||
import React from 'react';
|
||||
import FieldSet from 'Components/FieldSet';
|
||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import Metadata from './Metadata';
|
||||
import styles from './Metadatas.css';
|
||||
|
||||
|
@ -12,9 +13,9 @@ function Metadatas(props) {
|
|||
} = props;
|
||||
|
||||
return (
|
||||
<FieldSet legend="Metadata">
|
||||
<FieldSet legend={translate('Metadata')}>
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load Metadata"
|
||||
errorMessage={translate('MetadataLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.metadatas}>
|
||||
|
|
|
@ -2,11 +2,12 @@ import React from 'react';
|
|||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import MetadatasConnector from './Metadata/MetadatasConnector';
|
||||
|
||||
function MetadataSettings() {
|
||||
return (
|
||||
<PageContent title="Metadata Settings">
|
||||
<PageContent title={translate('MetadataSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
showSave={false}
|
||||
/>
|
||||
|
|
|
@ -2,11 +2,12 @@ import React from 'react';
|
|||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import TheTvdb from './TheTvdb';
|
||||
|
||||
function MetadataSourceSettings() {
|
||||
return (
|
||||
<PageContent title="Metadata Source Settings">
|
||||
<PageContent title={translate('MetadataSourceSettings')} >
|
||||
<SettingsToolbarConnector
|
||||
showSave={false}
|
||||
/>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import Link from 'Components/Link/Link';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import InlineMarkdown from '../../Components/Markdown/InlineMarkdown';
|
||||
import styles from './TheTvdb.css';
|
||||
|
||||
function TheTvdb(props) {
|
||||
|
@ -12,12 +13,10 @@ function TheTvdb(props) {
|
|||
|
||||
<div className={styles.info}>
|
||||
<div className={styles.title}>
|
||||
TheTVDB
|
||||
{translate('TheTvdb')}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Series and episode information is provided by TheTVDB.com. <Link to="https://www.thetvdb.com/subscribe">Please consider supporting them.</Link>
|
||||
</div>
|
||||
<InlineMarkdown data={translate('SeriesAndEpisodeInformationIsProvidedByTheTVDB')} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -2,11 +2,12 @@ import React from 'react';
|
|||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import NotificationsConnector from './Notifications/NotificationsConnector';
|
||||
|
||||
function NotificationSettings() {
|
||||
return (
|
||||
<PageContent title="Connect Settings">
|
||||
<PageContent title={translate('ConnectSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
showSave={false}
|
||||
/>
|
||||
|
|
|
@ -5,6 +5,7 @@ import Link from 'Components/Link/Link';
|
|||
import Menu from 'Components/Menu/Menu';
|
||||
import MenuContent from 'Components/Menu/MenuContent';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AddNotificationPresetMenuItem from './AddNotificationPresetMenuItem';
|
||||
import styles from './AddNotificationItem.css';
|
||||
|
||||
|
@ -57,7 +58,7 @@ class AddNotificationItem extends Component {
|
|||
size={sizes.SMALL}
|
||||
onPress={this.onNotificationSelect}
|
||||
>
|
||||
Custom
|
||||
{translate('Custom')}
|
||||
</Button>
|
||||
|
||||
<Menu className={styles.presetsMenu}>
|
||||
|
@ -65,7 +66,7 @@ class AddNotificationItem extends Component {
|
|||
className={styles.presetsMenuButton}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
Presets
|
||||
{translate('Presets')}
|
||||
</Button>
|
||||
|
||||
<MenuContent>
|
||||
|
@ -90,7 +91,7 @@ class AddNotificationItem extends Component {
|
|||
to={infoLink}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
More Info
|
||||
{translate('MoreInfo')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,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';
|
||||
import AddNotificationItem from './AddNotificationItem';
|
||||
import styles from './AddNotificationModalContent.css';
|
||||
|
||||
|
@ -27,7 +28,7 @@ class AddNotificationModalContent extends Component {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Add Notification
|
||||
{translate('AddNotification')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -38,7 +39,9 @@ class AddNotificationModalContent extends Component {
|
|||
|
||||
{
|
||||
!isSchemaFetching && !!schemaError &&
|
||||
<div>Unable to add a new notification, please try again.</div>
|
||||
<div>
|
||||
{translate('AddNotificationError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -65,7 +68,7 @@ class AddNotificationModalContent extends Component {
|
|||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Close
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -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 NotificationEventItems from './NotificationEventItems';
|
||||
import styles from './EditNotificationModalContent.css';
|
||||
|
||||
|
@ -47,7 +48,7 @@ function EditNotificationModalContent(props) {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{`${id ? 'Edit' : 'Add'} Connection - ${implementationName}`}
|
||||
{`${id ? translate('EditConnection') : translate('AddConnection')} - ${implementationName}`}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -59,7 +60,7 @@ function EditNotificationModalContent(props) {
|
|||
{
|
||||
!isFetching && !!error &&
|
||||
<div>
|
||||
Unable to add a new notification, please try again.
|
||||
{translate('AddNotificationError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -77,7 +78,7 @@ function EditNotificationModalContent(props) {
|
|||
}
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{translate('Name')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
|
@ -93,12 +94,12 @@ function EditNotificationModalContent(props) {
|
|||
/>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Tags</FormLabel>
|
||||
<FormLabel>{translate('Tags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
name="tags"
|
||||
helpText="Only send notifications for series with at least one matching tag"
|
||||
helpText={translate('NotificationsTagsHelpText')}
|
||||
{...tags}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -131,7 +132,7 @@ function EditNotificationModalContent(props) {
|
|||
kind={kinds.DANGER}
|
||||
onPress={onDeleteNotificationPress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
|
@ -140,13 +141,13 @@ function EditNotificationModalContent(props) {
|
|||
error={saveError}
|
||||
onPress={onTestPress}
|
||||
>
|
||||
Test
|
||||
{translate('Test')}
|
||||
</SpinnerErrorButton>
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
|
@ -154,7 +155,7 @@ function EditNotificationModalContent(props) {
|
|||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -5,6 +5,7 @@ import Label from 'Components/Label';
|
|||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import TagList from 'Components/TagList';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditNotificationModalConnector from './EditNotificationModalConnector';
|
||||
import styles from './Notification.css';
|
||||
|
||||
|
@ -96,7 +97,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnGrab && onGrab ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Grab
|
||||
{translate('OnGrab')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -104,7 +105,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnDownload && onDownload ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Import
|
||||
{translate('OnImport')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -112,7 +113,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnUpgrade && onDownload && onUpgrade ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Upgrade
|
||||
{translate('OnUpgrade')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -120,7 +121,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnRename && onRename ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Rename
|
||||
{translate('OnRename')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -128,7 +129,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnHealthIssue && onHealthIssue ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Health Issue
|
||||
{translate('OnHealthIssue')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -136,7 +137,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnHealthRestored && onHealthRestored ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Health Restored
|
||||
{translate('OnHealthRestored')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -144,7 +145,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnApplicationUpdate && onApplicationUpdate ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Application Update
|
||||
{translate('OnApplicationUpdate')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -152,7 +153,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnSeriesAdd && onSeriesAdd ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Series Add
|
||||
{translate('OnSeriesAdd')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -160,7 +161,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnSeriesDelete && onSeriesDelete ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Series Delete
|
||||
{translate('OnSeriesDelete')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -168,7 +169,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnEpisodeFileDelete && onEpisodeFileDelete ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Episode File Delete
|
||||
{translate('OnEpisodeFileDelete')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -176,7 +177,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnEpisodeFileDeleteForUpgrade && onEpisodeFileDelete && onEpisodeFileDeleteForUpgrade ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Episode File Delete For Upgrade
|
||||
{translate('OnEpisodeFileDeleteForUpgrade')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -184,7 +185,7 @@ class Notification extends Component {
|
|||
{
|
||||
supportsOnManualInteractionRequired && onManualInteractionRequired ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Manual Interaction Required
|
||||
{translate('OnManualInteractionRequired')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -195,7 +196,7 @@ class Notification extends Component {
|
|||
kind={kinds.DISABLED}
|
||||
outline={true}
|
||||
>
|
||||
Disabled
|
||||
{translate('Disabled')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -215,9 +216,9 @@ class Notification extends Component {
|
|||
<ConfirmModal
|
||||
isOpen={this.state.isDeleteNotificationModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete Notification"
|
||||
message={`Are you sure you want to delete the notification '${name}'?`}
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteNotification')}
|
||||
message={translate('DeleteNotificationMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteNotification}
|
||||
onCancel={this.onDeleteNotificationModalClose}
|
||||
/>
|
||||
|
|
|
@ -5,6 +5,7 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
|
|||
import FormInputHelpText from 'Components/Form/FormInputHelpText';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import { inputTypes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './NotificationEventItems.css';
|
||||
|
||||
function NotificationEventItems(props) {
|
||||
|
@ -43,10 +44,10 @@ function NotificationEventItems(props) {
|
|||
|
||||
return (
|
||||
<FormGroup>
|
||||
<FormLabel>Notification Triggers</FormLabel>
|
||||
<FormLabel>{translate('NotificationTriggers')}</FormLabel>
|
||||
<div>
|
||||
<FormInputHelpText
|
||||
text="Select which events should trigger this notification"
|
||||
text={translate('NotificationTriggersHelpText')}
|
||||
link="https://wiki.servarr.com/sonarr/settings#connections"
|
||||
/>
|
||||
<div className={styles.events}>
|
||||
|
@ -54,7 +55,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onGrab"
|
||||
helpText="On Grab"
|
||||
helpText={translate('OnGrab')}
|
||||
isDisabled={!supportsOnGrab.value}
|
||||
{...onGrab}
|
||||
onChange={onInputChange}
|
||||
|
@ -65,7 +66,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onDownload"
|
||||
helpText="On Import"
|
||||
helpText={translate('OnImport')}
|
||||
isDisabled={!supportsOnDownload.value}
|
||||
{...onDownload}
|
||||
onChange={onInputChange}
|
||||
|
@ -78,7 +79,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onUpgrade"
|
||||
helpText="On Upgrade"
|
||||
helpText={translate('OnUpgrade')}
|
||||
isDisabled={!supportsOnUpgrade.value}
|
||||
{...onUpgrade}
|
||||
onChange={onInputChange}
|
||||
|
@ -90,7 +91,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onRename"
|
||||
helpText="On Rename"
|
||||
helpText={translate('OnRename')}
|
||||
isDisabled={!supportsOnRename.value}
|
||||
{...onRename}
|
||||
onChange={onInputChange}
|
||||
|
@ -101,7 +102,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onSeriesAdd"
|
||||
helpText="On Series Add"
|
||||
helpText={translate('OnSeriesAdd')}
|
||||
isDisabled={!supportsOnSeriesAdd.value}
|
||||
{...onSeriesAdd}
|
||||
onChange={onInputChange}
|
||||
|
@ -112,7 +113,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onSeriesDelete"
|
||||
helpText="On Series Delete"
|
||||
helpText={translate('OnSeriesDelete')}
|
||||
isDisabled={!supportsOnSeriesDelete.value}
|
||||
{...onSeriesDelete}
|
||||
onChange={onInputChange}
|
||||
|
@ -123,7 +124,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onEpisodeFileDelete"
|
||||
helpText="On Episode File Delete"
|
||||
helpText={translate('OnEpisodeFileDelete')}
|
||||
isDisabled={!supportsOnEpisodeFileDelete.value}
|
||||
{...onEpisodeFileDelete}
|
||||
onChange={onInputChange}
|
||||
|
@ -136,7 +137,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onEpisodeFileDeleteForUpgrade"
|
||||
helpText="On Episode File Delete For Upgrade"
|
||||
helpText={translate('OnEpisodeFileDeleteForUpgrade')}
|
||||
isDisabled={!supportsOnEpisodeFileDeleteForUpgrade.value}
|
||||
{...onEpisodeFileDeleteForUpgrade}
|
||||
onChange={onInputChange}
|
||||
|
@ -148,7 +149,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onHealthIssue"
|
||||
helpText="On Health Issue"
|
||||
helpText={translate('OnHealthIssue')}
|
||||
isDisabled={!supportsOnHealthIssue.value}
|
||||
{...onHealthIssue}
|
||||
onChange={onInputChange}
|
||||
|
@ -159,7 +160,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onHealthRestored"
|
||||
helpText="On Health Restored"
|
||||
helpText={translate('OnHealthRestored')}
|
||||
isDisabled={!supportsOnHealthRestored.value}
|
||||
{...onHealthRestored}
|
||||
onChange={onInputChange}
|
||||
|
@ -172,7 +173,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="includeHealthWarnings"
|
||||
helpText="Include Health Warnings"
|
||||
helpText={translate('IncludeHealthWarnings')}
|
||||
isDisabled={!supportsOnHealthIssue.value}
|
||||
{...includeHealthWarnings}
|
||||
onChange={onInputChange}
|
||||
|
@ -184,7 +185,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onApplicationUpdate"
|
||||
helpText="On Application Update"
|
||||
helpText={translate('OnApplicationUpdate')}
|
||||
isDisabled={!supportsOnApplicationUpdate.value}
|
||||
{...onApplicationUpdate}
|
||||
onChange={onInputChange}
|
||||
|
@ -195,7 +196,7 @@ function NotificationEventItems(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onManualInteractionRequired"
|
||||
helpText="On Manual Interaction Required"
|
||||
helpText={translate('OnManualInteractionRequired')}
|
||||
isDisabled={!supportsOnManualInteractionRequired.value}
|
||||
{...onManualInteractionRequired}
|
||||
onChange={onInputChange}
|
||||
|
|
|
@ -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 AddNotificationModal from './AddNotificationModal';
|
||||
import EditNotificationModalConnector from './EditNotificationModalConnector';
|
||||
import Notification from './Notification';
|
||||
|
@ -59,9 +60,9 @@ class Notifications extends Component {
|
|||
} = this.state;
|
||||
|
||||
return (
|
||||
<FieldSet legend="Connections">
|
||||
<FieldSet legend={translate('Connections')}>
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load Notifications"
|
||||
errorMessage={translate('NotificationsLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.notifications}>
|
||||
|
|
|
@ -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';
|
||||
|
||||
function PendingChangesModal(props) {
|
||||
const {
|
||||
|
@ -27,10 +28,10 @@ function PendingChangesModal(props) {
|
|||
onModalClose={onCancel}
|
||||
>
|
||||
<ModalContent onModalClose={onCancel}>
|
||||
<ModalHeader>Unsaved Changes</ModalHeader>
|
||||
<ModalHeader>{translate('UnsavedChanges')}</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
You have unsaved changes, are you sure you want to leave this page?
|
||||
{translate('PendingChangesMessage')}
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
|
@ -38,7 +39,7 @@ function PendingChangesModal(props) {
|
|||
kind={kinds.DEFAULT}
|
||||
onPress={onCancel}
|
||||
>
|
||||
Stay and review changes
|
||||
{translate('PendingChangesStayReview')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
|
@ -46,7 +47,7 @@ function PendingChangesModal(props) {
|
|||
kind={kinds.DANGER}
|
||||
onPress={onConfirm}
|
||||
>
|
||||
Discard changes and leave
|
||||
{translate('PendingChangesDiscardChanges')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -7,6 +7,7 @@ import ConfirmModal from 'Components/Modal/ConfirmModal';
|
|||
import TagList from 'Components/TagList';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import titleCase from 'Utilities/String/titleCase';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditDelayProfileModalConnector from './EditDelayProfileModalConnector';
|
||||
import styles from './DelayProfile.css';
|
||||
|
||||
|
@ -16,15 +17,15 @@ function getDelay(enabled, delay) {
|
|||
}
|
||||
|
||||
if (!delay) {
|
||||
return 'No Delay';
|
||||
return translate('NoDelay');
|
||||
}
|
||||
|
||||
if (delay === 1) {
|
||||
return '1 Minute';
|
||||
return translate('OneMinute');
|
||||
}
|
||||
|
||||
// TODO: use better units of time than just minutes
|
||||
return `${delay} Minutes`;
|
||||
return translate('DelayMinutes', { delay });
|
||||
}
|
||||
|
||||
class DelayProfile extends Component {
|
||||
|
@ -84,12 +85,12 @@ class DelayProfile extends Component {
|
|||
connectDragSource
|
||||
} = this.props;
|
||||
|
||||
let preferred = `Prefer ${titleCase(preferredProtocol)}`;
|
||||
let preferred = titleCase(translate('PreferProtocol', { preferredProtocol }));
|
||||
|
||||
if (!enableUsenet) {
|
||||
preferred = 'Only Torrent';
|
||||
preferred = translate('OnlyTorrent');
|
||||
} else if (!enableTorrent) {
|
||||
preferred = 'Only Usenet';
|
||||
preferred = translate('OnlyUsenet');
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -139,9 +140,9 @@ class DelayProfile extends Component {
|
|||
<ConfirmModal
|
||||
isOpen={this.state.isDeleteDelayProfileModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete Delay Profile"
|
||||
message="Are you sure you want to delete this delay profile?"
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteDelayProfile')}
|
||||
message={translate('DeleteDelayProfileMessageText')}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteDelayProfile}
|
||||
onCancel={this.onDeleteDelayProfileModalClose}
|
||||
/>
|
||||
|
|
|
@ -7,6 +7,7 @@ import Measure from 'Components/Measure';
|
|||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||
import Scroller from 'Components/Scroller/Scroller';
|
||||
import { icons, scrollDirections } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import DelayProfile from './DelayProfile';
|
||||
import DelayProfileDragPreview from './DelayProfileDragPreview';
|
||||
import DelayProfileDragSource from './DelayProfileDragSource';
|
||||
|
@ -67,9 +68,9 @@ class DelayProfiles extends Component {
|
|||
|
||||
return (
|
||||
<Measure onMeasure={this.onMeasure}>
|
||||
<FieldSet legend="Delay Profiles">
|
||||
<FieldSet legend={translate('DelayProfiles')}>
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load Delay Profiles"
|
||||
errorMessage={translate('DelayProfilesLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<Scroller
|
||||
|
@ -81,10 +82,18 @@ class DelayProfiles extends Component {
|
|||
>
|
||||
<div>
|
||||
<div className={styles.delayProfilesHeader}>
|
||||
<div className={styles.column}>Preferred Protocol</div>
|
||||
<div className={styles.column}>Usenet Delay</div>
|
||||
<div className={styles.column}>Torrent Delay</div>
|
||||
<div className={styles.tags}>Tags</div>
|
||||
<div className={styles.column}>
|
||||
{translate('PreferredProtocol')}
|
||||
</div>
|
||||
<div className={styles.column}>
|
||||
{translate('UsenetDelay')}
|
||||
</div>
|
||||
<div className={styles.column}>
|
||||
{translate('TorrentDelay')}
|
||||
</div>
|
||||
<div className={styles.tags}>
|
||||
{translate('Tags')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.delayProfiles}>
|
||||
|
|
|
@ -14,13 +14,34 @@ import ModalFooter from 'Components/Modal/ModalFooter';
|
|||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import { boolSettingShape, numberSettingShape, tagSettingShape } from 'Helpers/Props/Shapes/settingShape';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './EditDelayProfileModalContent.css';
|
||||
|
||||
const protocolOptions = [
|
||||
{ key: 'preferUsenet', value: 'Prefer Usenet' },
|
||||
{ key: 'preferTorrent', value: 'Prefer Torrent' },
|
||||
{ key: 'onlyUsenet', value: 'Only Usenet' },
|
||||
{ key: 'onlyTorrent', value: 'Only Torrent' }
|
||||
{
|
||||
key: 'preferUsenet',
|
||||
get value() {
|
||||
return translate('PreferUsenet');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'preferTorrent',
|
||||
get value() {
|
||||
return translate('PreferTorrent');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'onlyUsenet',
|
||||
get value() {
|
||||
return translate('OnlyUsenet');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'onlyTorrent',
|
||||
get value() {
|
||||
return translate('OnlyTorrent');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
function EditDelayProfileModalContent(props) {
|
||||
|
@ -54,7 +75,7 @@ function EditDelayProfileModalContent(props) {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{id ? 'Edit Delay Profile' : 'Add Delay Profile'}
|
||||
{id ? translate('EditDelayProfile') : translate('AddDelayProfile')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -66,7 +87,9 @@ function EditDelayProfileModalContent(props) {
|
|||
|
||||
{
|
||||
!isFetching && !!error ?
|
||||
<div>Unable to add a new quality profile, please try again.</div> :
|
||||
<div>
|
||||
{translate('AddQualityProfileError')}
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
||||
|
@ -74,14 +97,14 @@ function EditDelayProfileModalContent(props) {
|
|||
!isFetching && !error ?
|
||||
<Form {...otherProps}>
|
||||
<FormGroup>
|
||||
<FormLabel>Preferred Protocol</FormLabel>
|
||||
<FormLabel>{translate('PreferredProtocol')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="protocol"
|
||||
value={protocol}
|
||||
values={protocolOptions}
|
||||
helpText="Choose which protocol(s) to use and which one is preferred when choosing between otherwise equal releases"
|
||||
helpText={translate('ProtocolHelpText')}
|
||||
onChange={onProtocolChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
@ -89,14 +112,14 @@ function EditDelayProfileModalContent(props) {
|
|||
{
|
||||
enableUsenet.value ?
|
||||
<FormGroup>
|
||||
<FormLabel>Usenet Delay</FormLabel>
|
||||
<FormLabel>{translate('UsenetDelay')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="usenetDelay"
|
||||
unit="minutes"
|
||||
{...usenetDelay}
|
||||
helpText="Delay in minutes to wait before grabbing a release from Usenet"
|
||||
helpText={translate('UsenetDelayHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup> :
|
||||
|
@ -106,14 +129,14 @@ function EditDelayProfileModalContent(props) {
|
|||
{
|
||||
enableTorrent.value ?
|
||||
<FormGroup>
|
||||
<FormLabel>Torrent Delay</FormLabel>
|
||||
<FormLabel>{translate('TorrentDelay')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="torrentDelay"
|
||||
unit="minutes"
|
||||
{...torrentDelay}
|
||||
helpText="Delay in minutes to wait before grabbing a torrent"
|
||||
helpText={translate('TorrentDelayHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup> :
|
||||
|
@ -121,25 +144,25 @@ function EditDelayProfileModalContent(props) {
|
|||
}
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Bypass if Highest Quality</FormLabel>
|
||||
<FormLabel>{translate('BypassDelayIfHighestQuality')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="bypassIfHighestQuality"
|
||||
{...bypassIfHighestQuality}
|
||||
helpText="Bypass delay when release has the highest enabled quality in the quality profile with the preferred protocol"
|
||||
helpText={translate('BypassDelayIfHighestQualityHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Bypass if Above Custom Format Score</FormLabel>
|
||||
<FormLabel>{translate('BypassDelayIfAboveCustomFormatScore')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="bypassIfAboveCustomFormatScore"
|
||||
{...bypassIfAboveCustomFormatScore}
|
||||
helpText="Enable bypass when release has a score higher than the configured minimum custom format score"
|
||||
helpText={translate('BypassDelayIfAboveCustomFormatScoreHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
@ -147,13 +170,13 @@ function EditDelayProfileModalContent(props) {
|
|||
{
|
||||
bypassIfAboveCustomFormatScore.value ?
|
||||
<FormGroup>
|
||||
<FormLabel>Minimum Custom Format Score</FormLabel>
|
||||
<FormLabel>{translate('BypassDelayIfAboveCustomFormatScoreMinimumScore')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="minimumCustomFormatScore"
|
||||
{...minimumCustomFormatScore}
|
||||
helpText="Minimum Custom Format Score required to bypass delay for the preferred protocol"
|
||||
helpText={translate('BypassDelayIfAboveCustomFormatScoreMinimumScoreHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup> :
|
||||
|
@ -163,17 +186,17 @@ function EditDelayProfileModalContent(props) {
|
|||
{
|
||||
id === 1 ?
|
||||
<Alert>
|
||||
This is the default profile. It applies to all series that don't have an explicit profile.
|
||||
{translate('DefaultDelayProfile')}
|
||||
</Alert> :
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Tags</FormLabel>
|
||||
<FormLabel>{translate('Tags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
name="tags"
|
||||
{...tags}
|
||||
helpText="Applies to series with at least one matching tag"
|
||||
helpText={translate('DelayProfileTagsHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
@ -190,7 +213,7 @@ function EditDelayProfileModalContent(props) {
|
|||
kind={kinds.DANGER}
|
||||
onPress={onDeleteDelayProfilePress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button> :
|
||||
null
|
||||
}
|
||||
|
@ -198,7 +221,7 @@ function EditDelayProfileModalContent(props) {
|
|||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
|
@ -206,7 +229,7 @@ function EditDelayProfileModalContent(props) {
|
|||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -4,6 +4,7 @@ import HTML5toTouch from 'react-dnd-multi-backend/dist/esm/HTML5toTouch';
|
|||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import DelayProfilesConnector from './Delay/DelayProfilesConnector';
|
||||
import QualityProfilesConnector from './Quality/QualityProfilesConnector';
|
||||
import ReleaseProfilesConnector from './Release/ReleaseProfilesConnector';
|
||||
|
@ -18,7 +19,7 @@ class Profiles extends Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<PageContent title="Profiles">
|
||||
<PageContent title={translate('Profiles')}>
|
||||
<SettingsToolbarConnector showSave={false} />
|
||||
|
||||
<PageContentBody>
|
||||
|
|
|
@ -14,6 +14,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
|
|||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import dimensions from 'Styles/Variables/dimensions';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import QualityProfileFormatItems from './QualityProfileFormatItems';
|
||||
import QualityProfileItems from './QualityProfileItems';
|
||||
import styles from './EditQualityProfileModalContent.css';
|
||||
|
@ -134,7 +135,7 @@ class EditQualityProfileModalContent extends Component {
|
|||
onMeasure={this.onHeaderMeasure}
|
||||
>
|
||||
<ModalHeader>
|
||||
{id ? 'Edit Quality Profile' : 'Add Quality Profile'}
|
||||
{id ? translate('EditQualityProfile') : translate('AddQualityProfile')}
|
||||
</ModalHeader>
|
||||
</Measure>
|
||||
|
||||
|
@ -151,7 +152,9 @@ class EditQualityProfileModalContent extends Component {
|
|||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<div>Unable to add a new quality profile, please try again.</div>
|
||||
<div>
|
||||
{translate('AddQualityProfileError')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -163,7 +166,7 @@ class EditQualityProfileModalContent extends Component {
|
|||
<div className={styles.formGroupWrapper}>
|
||||
<FormGroup size={sizes.EXTRA_SMALL}>
|
||||
<FormLabel size={sizes.SMALL}>
|
||||
Name
|
||||
{translate('Name')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
|
@ -176,14 +179,14 @@ class EditQualityProfileModalContent extends Component {
|
|||
|
||||
<FormGroup size={sizes.EXTRA_SMALL}>
|
||||
<FormLabel size={sizes.SMALL}>
|
||||
Upgrades Allowed
|
||||
{translate('UpgradesAllowed')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="upgradeAllowed"
|
||||
{...upgradeAllowed}
|
||||
helpText="If disabled qualities will not be upgraded"
|
||||
helpText={translate('UpgradesAllowedHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
@ -192,7 +195,7 @@ class EditQualityProfileModalContent extends Component {
|
|||
upgradeAllowed.value &&
|
||||
<FormGroup size={sizes.EXTRA_SMALL}>
|
||||
<FormLabel size={sizes.SMALL}>
|
||||
Upgrade Until
|
||||
{translate('UpgradeUntil')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
|
@ -200,7 +203,7 @@ class EditQualityProfileModalContent extends Component {
|
|||
name="cutoff"
|
||||
{...cutoff}
|
||||
values={qualities}
|
||||
helpText="Once this quality is reached Sonarr will no longer download episodes"
|
||||
helpText={translate('UpgradeUntilHelpText')}
|
||||
onChange={onCutoffChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
@ -210,14 +213,14 @@ class EditQualityProfileModalContent extends Component {
|
|||
formatItems.value.length > 0 &&
|
||||
<FormGroup size={sizes.EXTRA_SMALL}>
|
||||
<FormLabel size={sizes.SMALL}>
|
||||
Minimum Custom Format Score
|
||||
{translate('MinimumCustomFormatScore')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="minFormatScore"
|
||||
{...minFormatScore}
|
||||
helpText="Minimum custom format score allowed to download"
|
||||
helpText={translate('MinimumCustomFormatScoreHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
@ -227,14 +230,14 @@ class EditQualityProfileModalContent extends Component {
|
|||
upgradeAllowed.value && formatItems.value.length > 0 &&
|
||||
<FormGroup size={sizes.EXTRA_SMALL}>
|
||||
<FormLabel size={sizes.SMALL}>
|
||||
Upgrade Until Custom Format Score
|
||||
{translate('UpgradeUntilCustomFormatScore')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="cutoffFormatScore"
|
||||
{...cutoffFormatScore}
|
||||
helpText="Once this custom format score is reached Sonarr will no longer grab episode releases"
|
||||
helpText={translate('UpgradeUntilCustomFormatScoreHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
@ -278,7 +281,7 @@ class EditQualityProfileModalContent extends Component {
|
|||
className={styles.deleteButtonContainer}
|
||||
title={
|
||||
isInUse ?
|
||||
'Can\'t delete a quality profile that is attached to a series' :
|
||||
translate('QualityProfileInUse') :
|
||||
undefined
|
||||
}
|
||||
>
|
||||
|
@ -287,7 +290,7 @@ class EditQualityProfileModalContent extends Component {
|
|||
isDisabled={isInUse}
|
||||
onPress={onDeleteQualityProfilePress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
</div> :
|
||||
null
|
||||
|
@ -296,7 +299,7 @@ class EditQualityProfileModalContent extends Component {
|
|||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
|
@ -304,7 +307,7 @@ class EditQualityProfileModalContent extends Component {
|
|||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</Measure>
|
||||
|
|
|
@ -6,6 +6,7 @@ import IconButton from 'Components/Link/IconButton';
|
|||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditQualityProfileModalConnector from './EditQualityProfileModalConnector';
|
||||
import styles from './QualityProfile.css';
|
||||
|
||||
|
@ -84,7 +85,7 @@ class QualityProfile extends Component {
|
|||
|
||||
<IconButton
|
||||
className={styles.cloneButton}
|
||||
title="Clone Profile"
|
||||
title={translate('CloneProfile')}
|
||||
name={icons.CLONE}
|
||||
onPress={this.onCloneQualityProfilePress}
|
||||
/>
|
||||
|
@ -104,7 +105,7 @@ class QualityProfile extends Component {
|
|||
<Label
|
||||
key={item.quality.id}
|
||||
kind={isCutoff ? kinds.INFO : kinds.DEFAULT}
|
||||
title={isCutoff ? 'Upgrade until this quality is met or exceeded' : null}
|
||||
title={isCutoff ? translate('UpgradeUntilThisQualityIsMetOrExceeded') : null}
|
||||
>
|
||||
{item.quality.name}
|
||||
</Label>
|
||||
|
@ -120,7 +121,7 @@ class QualityProfile extends Component {
|
|||
anchor={
|
||||
<Label
|
||||
kind={isCutoff ? kinds.INFO : kinds.DEFAULT}
|
||||
title={isCutoff ? 'Cutoff' : null}
|
||||
title={isCutoff ? translate('Cutoff') : null}
|
||||
>
|
||||
{item.name}
|
||||
</Label>
|
||||
|
@ -133,7 +134,7 @@ class QualityProfile extends Component {
|
|||
<Label
|
||||
key={groupItem.quality.id}
|
||||
kind={isCutoff ? kinds.INFO : kinds.DEFAULT}
|
||||
title={isCutoff ? 'Cutoff' : null}
|
||||
title={isCutoff ? translate('Cutoff') : null}
|
||||
>
|
||||
{groupItem.quality.name}
|
||||
</Label>
|
||||
|
@ -160,9 +161,9 @@ class QualityProfile extends Component {
|
|||
<ConfirmModal
|
||||
isOpen={this.state.isDeleteQualityProfileModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete Quality Profile"
|
||||
message={`Are you sure you want to delete the quality profile '${name}'?`}
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteQualityProfile')}
|
||||
message={translate('DeleteQualityProfileMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
isSpinning={isDeleting}
|
||||
onConfirm={this.onConfirmDeleteQualityProfile}
|
||||
onCancel={this.onDeleteQualityProfileModalClose}
|
||||
|
|
|
@ -4,8 +4,9 @@ import React, { Component } from 'react';
|
|||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputHelpText from 'Components/Form/FormInputHelpText';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import Link from 'Components/Link/Link';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import InlineMarkdown from '../../../Components/Markdown/InlineMarkdown';
|
||||
import QualityProfileFormatItem from './QualityProfileFormatItem';
|
||||
import styles from './QualityProfileFormatItems.css';
|
||||
|
||||
|
@ -66,22 +67,19 @@ class QualityProfileFormatItems extends Component {
|
|||
|
||||
if (profileFormatItems.length < 1) {
|
||||
return (
|
||||
<div className={styles.addCustomFormatMessage}>
|
||||
{'Want more control over which downloads are preferred? Add a'}
|
||||
<Link to='/settings/customformats'> Custom Format </Link>
|
||||
</div>
|
||||
<InlineMarkdown className={styles.addCustomFormatMessage} data={translate('WantMoreControlAddACustomFormat')} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormGroup size={sizes.EXTRA_SMALL}>
|
||||
<FormLabel size={sizes.SMALL}>
|
||||
Custom Formats
|
||||
{translate('CustomFormats')}
|
||||
</FormLabel>
|
||||
|
||||
<div>
|
||||
<FormInputHelpText
|
||||
text="Sonarr scores each release using the sum of scores for matching custom formats. If a new release would improve the score, at the same or better quality, then Sonarr will grab it."
|
||||
text={translate('CustomFormatHelpText')}
|
||||
/>
|
||||
|
||||
{
|
||||
|
@ -113,10 +111,10 @@ class QualityProfileFormatItems extends Component {
|
|||
<div className={styles.formats}>
|
||||
<div className={styles.headerContainer}>
|
||||
<div className={styles.headerTitle}>
|
||||
Custom Format
|
||||
{translate('CustomFormat')}
|
||||
</div>
|
||||
<div className={styles.headerScore}>
|
||||
Score
|
||||
{translate('Score')}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ import CheckInput from 'Components/Form/CheckInput';
|
|||
import Icon from 'Components/Icon';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './QualityProfileItem.css';
|
||||
|
||||
class QualityProfileItem extends Component {
|
||||
|
@ -63,7 +64,7 @@ class QualityProfileItem extends Component {
|
|||
<IconButton
|
||||
className={styles.createGroupButton}
|
||||
name={icons.GROUP}
|
||||
title="Group"
|
||||
title={translate('Group')}
|
||||
onPress={this.onCreateGroupPress}
|
||||
/>
|
||||
}
|
||||
|
@ -95,7 +96,7 @@ class QualityProfileItem extends Component {
|
|||
<div className={styles.dragHandle}>
|
||||
<Icon
|
||||
className={styles.dragIcon}
|
||||
title="Create group"
|
||||
title={translate('CreateGroup')}
|
||||
name={icons.REORDER}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -7,6 +7,7 @@ import Icon from 'Components/Icon';
|
|||
import Label from 'Components/Label';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import QualityProfileItemDragSource from './QualityProfileItemDragSource';
|
||||
import styles from './QualityProfileItemGroup.css';
|
||||
|
||||
|
@ -77,7 +78,7 @@ class QualityProfileItemGroup extends Component {
|
|||
<IconButton
|
||||
className={styles.deleteGroupButton}
|
||||
name={icons.UNGROUP}
|
||||
title="Ungroup"
|
||||
title={translate('Ungroup')}
|
||||
onPress={this.onDeleteGroupPress}
|
||||
/>
|
||||
|
||||
|
@ -133,7 +134,7 @@ class QualityProfileItemGroup extends Component {
|
|||
<Icon
|
||||
className={styles.dragIcon}
|
||||
name={icons.REORDER}
|
||||
title="Reorder"
|
||||
title={translate('Reorder')}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -7,6 +7,7 @@ import Icon from 'Components/Icon';
|
|||
import Button from 'Components/Link/Button';
|
||||
import Measure from 'Components/Measure';
|
||||
import { icons, kinds, sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import QualityProfileItemDragPreview from './QualityProfileItemDragPreview';
|
||||
import QualityProfileItemDragSource from './QualityProfileItemDragSource';
|
||||
import styles from './QualityProfileItems.css';
|
||||
|
@ -69,12 +70,12 @@ class QualityProfileItems extends Component {
|
|||
return (
|
||||
<FormGroup size={sizes.EXTRA_SMALL}>
|
||||
<FormLabel size={sizes.SMALL}>
|
||||
Qualities
|
||||
{translate('Qualities')}
|
||||
</FormLabel>
|
||||
|
||||
<div>
|
||||
<FormInputHelpText
|
||||
text="Qualities higher in the list are more preferred. Qualities within the same group are equal. Only checked qualities are wanted"
|
||||
text={translate('QualitiesHelpText')}
|
||||
/>
|
||||
|
||||
{
|
||||
|
@ -115,7 +116,7 @@ class QualityProfileItems extends Component {
|
|||
/>
|
||||
|
||||
{
|
||||
editGroups ? 'Done Editing Groups' : 'Edit Groups'
|
||||
editGroups ? translate('DoneEditingGroups') : translate('EditGroups')
|
||||
}
|
||||
</div>
|
||||
</Button>
|
||||
|
|
|
@ -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 EditQualityProfileModalConnector from './EditQualityProfileModalConnector';
|
||||
import QualityProfile from './QualityProfile';
|
||||
import styles from './QualityProfiles.css';
|
||||
|
@ -51,9 +52,9 @@ class QualityProfiles extends Component {
|
|||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldSet legend="Quality Profiles">
|
||||
<FieldSet legend={translate('QualityProfiles')}>
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load Quality Profiles"
|
||||
errorMessage={translate('QualityProfilesLoadError')}
|
||||
{...otherProps}c={true}
|
||||
>
|
||||
<div className={styles.qualityProfiles}>
|
||||
|
|
|
@ -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';
|
||||
import styles from './EditReleaseProfileModalContent.css';
|
||||
|
||||
const tagInputDelimiters = ['Tab', 'Enter'];
|
||||
|
@ -40,7 +41,7 @@ function EditReleaseProfileModalContent(props) {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{id ? 'Edit Release Profile' : 'Add Release Profile'}
|
||||
{id ? translate('EditReleaseProfile') : translate('AddReleaseProfile')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -53,35 +54,35 @@ function EditReleaseProfileModalContent(props) {
|
|||
type={inputTypes.TEXT}
|
||||
name="name"
|
||||
{...name}
|
||||
placeholder="Optional name"
|
||||
placeholder={translate('OptionalName')}
|
||||
canEdit={true}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Enable Profile</FormLabel>
|
||||
<FormLabel>{translate('EnableProfile')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enabled"
|
||||
helpText="Check to enable release profile"
|
||||
helpText={translate('EnableProfileHelpText')}
|
||||
{...enabled}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Must Contain</FormLabel>
|
||||
<FormLabel>{translate('MustContain')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
{...required}
|
||||
inputClassName={styles.tagInternalInput}
|
||||
type={inputTypes.TEXT_TAG}
|
||||
name="required"
|
||||
helpText="The release must contain at least one of these terms (case insensitive)"
|
||||
helpText={translate('MustContainHelpText')}
|
||||
kind={kinds.SUCCESS}
|
||||
placeholder="Add new restriction"
|
||||
placeholder={translate('AddNewRestriction')}
|
||||
delimiters={tagInputDelimiters}
|
||||
canEdit={true}
|
||||
onChange={onInputChange}
|
||||
|
@ -89,16 +90,16 @@ function EditReleaseProfileModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Must Not Contain</FormLabel>
|
||||
<FormLabel>{translate('MustNotContain')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
{...ignored}
|
||||
inputClassName={styles.tagInternalInput}
|
||||
type={inputTypes.TEXT_TAG}
|
||||
name="ignored"
|
||||
helpText="The release will be rejected if it contains one or more of terms (case insensitive)"
|
||||
helpText={translate('MustNotContainHelpText')}
|
||||
kind={kinds.DANGER}
|
||||
placeholder="Add new restriction"
|
||||
placeholder={translate('AddNewRestriction')}
|
||||
delimiters={tagInputDelimiters}
|
||||
canEdit={true}
|
||||
onChange={onInputChange}
|
||||
|
@ -106,13 +107,13 @@ function EditReleaseProfileModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Indexer</FormLabel>
|
||||
<FormLabel>{translate('Indexer')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.INDEXER_SELECT}
|
||||
name="indexerId"
|
||||
helpText="Specify what indexer the profile applies to"
|
||||
helpTextWarning="Using a specific indexer with release profiles can lead to duplicate releases being grabbed"
|
||||
helpText={translate('ReleaseProfileIndexerHelpText')}
|
||||
helpTextWarning={translate('ReleaseProfileIndexerHelpTextWarning')}
|
||||
{...indexerId}
|
||||
includeAny={true}
|
||||
onChange={onInputChange}
|
||||
|
@ -120,12 +121,12 @@ function EditReleaseProfileModalContent(props) {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Tags</FormLabel>
|
||||
<FormLabel>{translate('Tags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
name="tags"
|
||||
helpText="Release profiles will apply to series with at least one matching tag. Leave blank to apply to all series"
|
||||
helpText={translate('ReleaseProfileTagHelpText')}
|
||||
{...tags}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
@ -140,14 +141,14 @@ function EditReleaseProfileModalContent(props) {
|
|||
kind={kinds.DANGER}
|
||||
onPress={onDeleteReleaseProfilePress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
|
@ -155,7 +156,7 @@ function EditReleaseProfileModalContent(props) {
|
|||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -7,6 +7,7 @@ import Label from 'Components/Label';
|
|||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import TagList from 'Components/TagList';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditReleaseProfileModalConnector from './EditReleaseProfileModalConnector';
|
||||
import styles from './ReleaseProfile.css';
|
||||
|
||||
|
@ -147,7 +148,7 @@ class ReleaseProfile extends Component {
|
|||
kind={kinds.DISABLED}
|
||||
outline={true}
|
||||
>
|
||||
Disabled
|
||||
{translate('Disabled')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
|
@ -172,9 +173,9 @@ class ReleaseProfile extends Component {
|
|||
<ConfirmModal
|
||||
isOpen={isDeleteReleaseProfileModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete ReleaseProfile"
|
||||
message={'Are you sure you want to delete this releaseProfile?'}
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteReleaseProfile')}
|
||||
message={translate('DeleteReleaseProfileMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteReleaseProfile}
|
||||
onCancel={this.onDeleteReleaseProfileModalClose}
|
||||
/>
|
||||
|
|
|
@ -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 EditReleaseProfileModalConnector from './EditReleaseProfileModalConnector';
|
||||
import ReleaseProfile from './ReleaseProfile';
|
||||
import styles from './ReleaseProfiles.css';
|
||||
|
@ -46,9 +47,9 @@ class ReleaseProfiles extends Component {
|
|||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldSet legend="Release Profiles">
|
||||
<FieldSet legend={translate('Release Profiles')}>
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load ReleaseProfiles"
|
||||
errorMessage={translate('ReleaseProfilesLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.releaseProfiles}>
|
||||
|
|
|
@ -8,6 +8,7 @@ import Popover from 'Components/Tooltip/Popover';
|
|||
import { kinds, tooltipPositions } from 'Helpers/Props';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import roundNumber from 'Utilities/Number/roundNumber';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import QualityDefinitionLimits from './QualityDefinitionLimits';
|
||||
import styles from './QualityDefinition.css';
|
||||
|
||||
|
@ -186,13 +187,13 @@ class QualityDefinition extends Component {
|
|||
} = this.state;
|
||||
|
||||
const minBytes = minSize * 1024 * 1024;
|
||||
const minSixty = `${formatBytes(minBytes * 60)}/h`;
|
||||
const minSixty = `${formatBytes(minBytes * 60)}/${translate('HourShorthand')}`;
|
||||
|
||||
const preferredBytes = preferredSize * 1024 * 1024;
|
||||
const preferredSixty = preferredBytes ? `${formatBytes(preferredBytes * 60)}/h` : 'Unlimited';
|
||||
const preferredSixty = preferredBytes ? `${formatBytes(preferredBytes * 60)}/${translate('HourShorthand')}` : translate('Unlimited');
|
||||
|
||||
const maxBytes = maxSize && maxSize * 1024 * 1024;
|
||||
const maxSixty = maxBytes ? `${formatBytes(maxBytes * 60)}/h` : 'Unlimited';
|
||||
const maxSixty = maxBytes ? `${formatBytes(maxBytes * 60)}/${translate('HourShorthand')}` : translate('Unlimited');
|
||||
|
||||
return (
|
||||
<div className={styles.qualityDefinition}>
|
||||
|
@ -231,11 +232,11 @@ class QualityDefinition extends Component {
|
|||
anchor={
|
||||
<Label kind={kinds.INFO}>{minSixty}</Label>
|
||||
}
|
||||
title="Minimum Limits"
|
||||
title={translate('MinimumLimits')}
|
||||
body={
|
||||
<QualityDefinitionLimits
|
||||
bytes={minBytes}
|
||||
message="No minimum for any runtime"
|
||||
message={translate('NoMinimumForAnyRuntime')}
|
||||
/>
|
||||
}
|
||||
position={tooltipPositions.BOTTOM}
|
||||
|
@ -247,11 +248,11 @@ class QualityDefinition extends Component {
|
|||
anchor={
|
||||
<Label kind={kinds.SUCCESS}>{preferredSixty}</Label>
|
||||
}
|
||||
title="Preferred Size"
|
||||
title={translate('PreferredSize')}
|
||||
body={
|
||||
<QualityDefinitionLimits
|
||||
bytes={preferredBytes}
|
||||
message="No limit for any runtime"
|
||||
message={translate('NoLimitForAnyRuntime')}
|
||||
/>
|
||||
}
|
||||
position={tooltipPositions.BOTTOM}
|
||||
|
@ -263,11 +264,11 @@ class QualityDefinition extends Component {
|
|||
anchor={
|
||||
<Label kind={kinds.WARNING}>{maxSixty}</Label>
|
||||
}
|
||||
title="Maximum Limits"
|
||||
title={translate('MaximumLimits')}
|
||||
body={
|
||||
<QualityDefinitionLimits
|
||||
bytes={maxBytes}
|
||||
message="No limit for any runtime"
|
||||
message={translate('NoLimitForAnyRuntime')}
|
||||
/>
|
||||
}
|
||||
position={tooltipPositions.BOTTOM}
|
||||
|
@ -280,7 +281,7 @@ class QualityDefinition extends Component {
|
|||
advancedSettings &&
|
||||
<div className={styles.megabytesPerMinute}>
|
||||
<div>
|
||||
Min
|
||||
{translate('Min')}
|
||||
|
||||
<NumberInput
|
||||
className={styles.sizeInput}
|
||||
|
@ -295,7 +296,7 @@ class QualityDefinition extends Component {
|
|||
</div>
|
||||
|
||||
<div>
|
||||
Preferred
|
||||
{translate('Preferred')}
|
||||
|
||||
<NumberInput
|
||||
className={styles.sizeInput}
|
||||
|
@ -310,7 +311,7 @@ class QualityDefinition extends Component {
|
|||
</div>
|
||||
|
||||
<div>
|
||||
Max
|
||||
{translate('Max')}
|
||||
|
||||
<NumberInput
|
||||
className={styles.sizeInput}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function QualityDefinitionLimits(props) {
|
||||
const {
|
||||
|
@ -13,14 +14,20 @@ function QualityDefinitionLimits(props) {
|
|||
}
|
||||
|
||||
const thirty = formatBytes(bytes * 30);
|
||||
const fourtyFive = formatBytes(bytes * 45);
|
||||
const fortyFive = formatBytes(bytes * 45);
|
||||
const sixty = formatBytes(bytes * 60);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>30 Minutes: {thirty}</div>
|
||||
<div>45 Minutes: {fourtyFive}</div>
|
||||
<div>60 Minutes: {sixty}</div>
|
||||
<div>
|
||||
{translate('MinutesThirty', { thirty })}
|
||||
</div>
|
||||
<div>
|
||||
{translate('MinutesFortyFive', { fortyFive })}
|
||||
</div>
|
||||
<div>
|
||||
{translate('MinutesSixty', { sixty })}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import FieldSet from 'Components/FieldSet';
|
||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import QualityDefinitionConnector from './QualityDefinitionConnector';
|
||||
import styles from './QualityDefinitions.css';
|
||||
|
||||
|
@ -18,20 +19,26 @@ class QualityDefinitions extends Component {
|
|||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldSet legend="Quality Definitions">
|
||||
<FieldSet legend={translate('QualityDefinitions')}>
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load Quality Definitions"
|
||||
errorMessage={translate('QualityDefinitionsLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.quality}>Quality</div>
|
||||
<div className={styles.title}>Title</div>
|
||||
<div className={styles.sizeLimit}>Size Limit</div>
|
||||
<div className={styles.quality}>
|
||||
{translate('Quality')}
|
||||
</div>
|
||||
<div className={styles.title}>
|
||||
{translate('Title')}
|
||||
</div>
|
||||
<div className={styles.sizeLimit}>
|
||||
{translate('SizeLimit')}
|
||||
</div>
|
||||
|
||||
{
|
||||
advancedSettings ?
|
||||
<div className={styles.megabytesPerMinute}>
|
||||
Megabytes Per Minute
|
||||
{translate('MegabytesPerMinute')}
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
@ -53,7 +60,7 @@ class QualityDefinitions extends Component {
|
|||
|
||||
<div className={styles.sizeLimitHelpTextContainer}>
|
||||
<div className={styles.sizeLimitHelpText}>
|
||||
Limits are automatically adjusted for the series runtime and number of episodes in the file.
|
||||
{translate('QualityLimitsHelpText')}
|
||||
</div>
|
||||
</div>
|
||||
</PageSectionContent>
|
||||
|
|
|
@ -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 QualityDefinitionsConnector from './Definition/QualityDefinitionsConnector';
|
||||
import ResetQualityDefinitionsModal from './Reset/ResetQualityDefinitionsModal';
|
||||
|
||||
|
@ -62,7 +63,7 @@ class Quality extends Component {
|
|||
} = this.state;
|
||||
|
||||
return (
|
||||
<PageContent title="Quality Settings">
|
||||
<PageContent title={translate('QualitySettings')}>
|
||||
<SettingsToolbarConnector
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
|
@ -71,7 +72,7 @@ class Quality extends Component {
|
|||
<PageToolbarSeparator />
|
||||
|
||||
<PageToolbarButton
|
||||
label="Reset Definitions"
|
||||
label={translate('ResetDefinitions')}
|
||||
iconName={icons.REFRESH}
|
||||
isSpinning={isResettingQualityDefinitions}
|
||||
onPress={this.onResetQualityDefinitionsPress}
|
||||
|
|
|
@ -2,12 +2,13 @@ import React from 'react';
|
|||
import Link from 'Components/Link/Link';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import SettingsToolbarConnector from './SettingsToolbarConnector';
|
||||
import styles from './Settings.css';
|
||||
|
||||
function Settings() {
|
||||
return (
|
||||
<PageContent title="Settings">
|
||||
<PageContent title={translate('Settings')}>
|
||||
<SettingsToolbarConnector
|
||||
hasPendingChanges={false}
|
||||
/>
|
||||
|
@ -17,143 +18,143 @@ function Settings() {
|
|||
className={styles.link}
|
||||
to="/settings/mediamanagement"
|
||||
>
|
||||
Media Management
|
||||
{translate('MediaManagement')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Naming, file management settings and root folders
|
||||
{translate('MediaManagementSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/profiles"
|
||||
>
|
||||
Profiles
|
||||
{translate('Profiles')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Quality, Language, Delay and Release profiles
|
||||
{translate('ProfilesSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/quality"
|
||||
>
|
||||
Quality
|
||||
{translate('Quality')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Quality sizes and naming
|
||||
{translate('QualitySettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/customformats"
|
||||
>
|
||||
Custom Formats
|
||||
{translate('CustomFormats')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Custom Formats and Settings
|
||||
{translate('CustomFormatsSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/indexers"
|
||||
>
|
||||
Indexers
|
||||
{translate('Indexers')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Indexers and indexer options
|
||||
{translate('IndexersSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/downloadclients"
|
||||
>
|
||||
Download Clients
|
||||
{translate('DownloadClients')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Download clients, download handling and remote path mappings
|
||||
{translate('DownloadClientsSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/importlists"
|
||||
>
|
||||
Import Lists
|
||||
{translate('ImportLists')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Import from another Sonarr instance or Trakt lists and manage list exclusions
|
||||
{translate('ImportListsSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/connect"
|
||||
>
|
||||
Connect
|
||||
{translate('Connect')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Notifications, connections to media servers/players and custom scripts
|
||||
{translate('ConnectSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/metadata"
|
||||
>
|
||||
Metadata
|
||||
{translate('Metadata')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Create metadata files when episodes are imported or series are refreshed
|
||||
{translate('MetadataSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/metadatasource"
|
||||
>
|
||||
Metadata Source
|
||||
{translate('MetadataSource')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Information on where Sonarr gets Series and Episode information
|
||||
{translate('MetadataSourceSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/tags"
|
||||
>
|
||||
Tags
|
||||
{translate('Tags')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
See all tags and how they are used. Unused tags can be removed
|
||||
{translate('TagsSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/general"
|
||||
>
|
||||
General
|
||||
{translate('General')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Port, SSL, username/password, proxy, analytics and updates
|
||||
{translate('GeneralSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/ui"
|
||||
>
|
||||
UI
|
||||
{translate('Ui')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
Calendar, date and color impaired options
|
||||
{translate('UiSettingsSummary')}
|
||||
</div>
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
|
|
|
@ -5,6 +5,7 @@ import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
|||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AdvancedSettingsButton from './AdvancedSettingsButton';
|
||||
import PendingChangesModal from './PendingChangesModal';
|
||||
|
||||
|
@ -61,7 +62,7 @@ class SettingsToolbar extends Component {
|
|||
{
|
||||
showSave &&
|
||||
<PageToolbarButton
|
||||
label={hasPendingChanges ? 'Save Changes' : 'No Changes'}
|
||||
label={hasPendingChanges ? translate('SaveChanges') : translate('NoChanges')}
|
||||
iconName={icons.SAVE}
|
||||
isSpinning={isSaving}
|
||||
isDisabled={!hasPendingChanges}
|
||||
|
|
|
@ -58,7 +58,7 @@ export default function AutoTaggings() {
|
|||
return (
|
||||
<FieldSet legend={translate('AutoTagging')}>
|
||||
<PageSectionContent
|
||||
errorMessage={translate('UnableToLoadAutoTagging')}
|
||||
errorMessage={translate('AutoTaggingLoadError')}
|
||||
error={error}
|
||||
isFetching={isFetching}
|
||||
isPopulated={isPopulated}
|
||||
|
|
|
@ -123,7 +123,7 @@ export default function EditAutoTaggingModalContent(props) {
|
|||
{
|
||||
!isFetching && !!error ?
|
||||
<div>
|
||||
{'Unable to add a new auto tag, please try again.'}
|
||||
{translate('AddAutoTagError')}
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ export default function AddSpecificationModalContent(props) {
|
|||
{
|
||||
!isSchemaFetching && !!schemaError ?
|
||||
<div>
|
||||
{'Unable to add a new condition, please try again.'}
|
||||
{translate('AddConditionError')}
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ export default function AddSpecificationModalContent(props) {
|
|||
|
||||
<Alert kind={kinds.INFO}>
|
||||
<div>
|
||||
{'Sonarr supports the follow properties for auto tagging rules'}
|
||||
{translate('SupportedAutoTaggingProperties')}
|
||||
</div>
|
||||
</Alert>
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
|
|||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import ProviderFieldFormGroup from 'Components/Form/ProviderFieldFormGroup';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Link from 'Components/Link/Link';
|
||||
import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton';
|
||||
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
|
@ -83,12 +83,13 @@ function EditSpecificationModalContent(props) {
|
|||
fields && fields.some((x) => x.label === 'Regular Expression') &&
|
||||
<Alert kind={kinds.INFO}>
|
||||
<div>
|
||||
<div dangerouslySetInnerHTML={{ __html: 'This condition matches using Regular Expressions. Note that the characters <code>\\^$.|?*+()[{</code> have special meanings and need escaping with a <code>\\</code>' }} />
|
||||
{'More details'} <Link to="https://www.regular-expressions.info/tutorial.html">{'Here'}</Link>
|
||||
<InlineMarkdown data={translate('ConditionUsingRegularExpressions')} />
|
||||
</div>
|
||||
<div>
|
||||
{'Regular expressions can be tested '}
|
||||
<Link to="http://regexstorm.net/tester">Here</Link>
|
||||
<InlineMarkdown data={translate('RegularExpressionsTutorialLink')} />
|
||||
</div>
|
||||
<div>
|
||||
<InlineMarkdown data={translate('RegularExpressionsCanBeTested')} />
|
||||
</div>
|
||||
</Alert>
|
||||
}
|
||||
|
@ -130,7 +131,7 @@ function EditSpecificationModalContent(props) {
|
|||
type={inputTypes.CHECK}
|
||||
name="negate"
|
||||
{...negate}
|
||||
helpText={translate('AutoTaggingNegateHelpText', { name: implementationName })}
|
||||
helpText={translate('AutoTaggingNegateHelpText', { implementationName })}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
@ -144,7 +145,7 @@ function EditSpecificationModalContent(props) {
|
|||
type={inputTypes.CHECK}
|
||||
name="required"
|
||||
{...required}
|
||||
helpText={translate('AutoTaggingRequiredHelpText', { name: implementationName })}
|
||||
helpText={translate('AutoTaggingRequiredHelpText', { implementationName })}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
|
|
@ -5,6 +5,7 @@ import Label from 'Components/Label';
|
|||
import IconButton from 'Components/Link/IconButton';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditSpecificationModal from './EditSpecificationModal';
|
||||
import styles from './Specification.css';
|
||||
|
||||
|
@ -60,7 +61,7 @@ export default function Specification(props) {
|
|||
|
||||
<IconButton
|
||||
className={styles.cloneButton}
|
||||
title="Clone"
|
||||
title={translate('Clone')}
|
||||
name={icons.CLONE}
|
||||
onPress={onClonePress}
|
||||
/>
|
||||
|
@ -74,7 +75,7 @@ export default function Specification(props) {
|
|||
{
|
||||
negate ?
|
||||
<Label kind={kinds.DANGER}>
|
||||
Negated
|
||||
{translate('Negated')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -82,7 +83,7 @@ export default function Specification(props) {
|
|||
{
|
||||
required ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
Required
|
||||
{translate('Required')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
@ -98,9 +99,9 @@ export default function Specification(props) {
|
|||
<ConfirmModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete Specification"
|
||||
message={`Are you sure you want to delete specification ${name} ?`}
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteSpecification')}
|
||||
message={translate('DeleteSpecificationHelpText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={onDeleteModalClose}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import titleCase from 'Utilities/String/titleCase';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function TagDetailsDelayProfile(props) {
|
||||
const {
|
||||
|
@ -14,22 +15,22 @@ function TagDetailsDelayProfile(props) {
|
|||
return (
|
||||
<div>
|
||||
<div>
|
||||
Protocol: {titleCase(preferredProtocol)}
|
||||
{titleCase(translate('DelayProfileProtocol', { preferredProtocol }))}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{
|
||||
enableUsenet ?
|
||||
`Usenet Delay: ${usenetDelay}` :
|
||||
'Usenet disabled'
|
||||
translate('UsenetDelayTime', { usenetDelay }) :
|
||||
translate('UsenetDisabled')
|
||||
}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{
|
||||
enableTorrent ?
|
||||
`Torrent Delay: ${torrentDelay}` :
|
||||
'Torrents disabled'
|
||||
translate('TorrentDelayTime', { torrentDelay }) :
|
||||
translate('TorrentsDisabled')
|
||||
}
|
||||
</div>
|
||||
</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 TagDetailsDelayProfile from './TagDetailsDelayProfile';
|
||||
import styles from './TagDetailsModalContent.css';
|
||||
|
||||
|
@ -30,18 +31,20 @@ function TagDetailsModalContent(props) {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Tag Details - {label}
|
||||
{translate('TagDetails', { label })}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
{
|
||||
!isTagUsed &&
|
||||
<div>Tag is not used and can be deleted</div>
|
||||
<div>
|
||||
{translate('TagIsNotUsedAndCanBeDeleted')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
series.length ?
|
||||
<FieldSet legend="Series">
|
||||
<FieldSet legend={translate('Series')}>
|
||||
{
|
||||
series.map((item) => {
|
||||
return (
|
||||
|
@ -57,7 +60,7 @@ function TagDetailsModalContent(props) {
|
|||
|
||||
{
|
||||
delayProfiles.length ?
|
||||
<FieldSet legend="Delay Profile">
|
||||
<FieldSet legend={translate('DelayProfile')}>
|
||||
{
|
||||
delayProfiles.map((item) => {
|
||||
const {
|
||||
|
@ -87,7 +90,7 @@ function TagDetailsModalContent(props) {
|
|||
|
||||
{
|
||||
notifications.length ?
|
||||
<FieldSet legend="Connections">
|
||||
<FieldSet legend={translate('Connections')}>
|
||||
{
|
||||
notifications.map((item) => {
|
||||
return (
|
||||
|
@ -103,7 +106,7 @@ function TagDetailsModalContent(props) {
|
|||
|
||||
{
|
||||
importLists.length ?
|
||||
<FieldSet legend="Import Lists">
|
||||
<FieldSet legend={translate('ImportLists')}>
|
||||
{
|
||||
importLists.map((item) => {
|
||||
return (
|
||||
|
@ -119,7 +122,7 @@ function TagDetailsModalContent(props) {
|
|||
|
||||
{
|
||||
releaseProfiles.length ?
|
||||
<FieldSet legend="Release Profiles">
|
||||
<FieldSet legend={translate('ReleaseProfiles')}>
|
||||
{
|
||||
releaseProfiles.map((item) => {
|
||||
return (
|
||||
|
@ -166,7 +169,7 @@ function TagDetailsModalContent(props) {
|
|||
|
||||
{
|
||||
indexers.length ?
|
||||
<FieldSet legend="Indexers">
|
||||
<FieldSet legend={translate('Indexers')}>
|
||||
{
|
||||
indexers.map((item) => {
|
||||
return (
|
||||
|
@ -182,7 +185,7 @@ function TagDetailsModalContent(props) {
|
|||
|
||||
{
|
||||
downloadClients.length ?
|
||||
<FieldSet legend="Download Clients">
|
||||
<FieldSet legend={translate('DownloadClients')}>
|
||||
{
|
||||
downloadClients.map((item) => {
|
||||
return (
|
||||
|
@ -198,7 +201,7 @@ function TagDetailsModalContent(props) {
|
|||
|
||||
{
|
||||
autoTags.length ?
|
||||
<FieldSet legend="Auto Tagging">
|
||||
<FieldSet legend={translate('AutoTagging')}>
|
||||
{
|
||||
autoTags.map((item) => {
|
||||
return (
|
||||
|
@ -218,18 +221,18 @@ function TagDetailsModalContent(props) {
|
|||
<Button
|
||||
className={styles.deleteButton}
|
||||
kind={kinds.DANGER}
|
||||
title={isTagUsed ? 'Cannot be deleted while in use' : undefined}
|
||||
title={isTagUsed ? translate('TagCannotBeDeletedWhileInUse') : undefined}
|
||||
isDisabled={isTagUsed}
|
||||
onPress={onDeleteTagPress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Close
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
|
|
@ -3,6 +3,7 @@ import React, { Component } from 'react';
|
|||
import Card from 'Components/Card';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import TagDetailsModal from './Details/TagDetailsModal';
|
||||
import TagInUse from './TagInUse';
|
||||
import styles from './Tag.css';
|
||||
|
@ -93,45 +94,49 @@ class Tag extends Component {
|
|||
isTagUsed ?
|
||||
<div>
|
||||
<TagInUse
|
||||
label="series"
|
||||
label={translate('Series')}
|
||||
count={seriesIds.length}
|
||||
shouldPluralize={false}
|
||||
/>
|
||||
|
||||
<TagInUse
|
||||
label="delay profile"
|
||||
label={translate('DelayProfile')}
|
||||
labelPlural={translate('DelayProfiles')}
|
||||
count={delayProfileIds.length}
|
||||
/>
|
||||
|
||||
<TagInUse
|
||||
label="import list"
|
||||
label={translate('ImportList')}
|
||||
labelPlural={translate('ImportLists')}
|
||||
count={importListIds.length}
|
||||
/>
|
||||
|
||||
<TagInUse
|
||||
label="connection"
|
||||
label={translate('Connection')}
|
||||
labelPlural={translate('Connections')}
|
||||
count={notificationIds.length}
|
||||
/>
|
||||
|
||||
<TagInUse
|
||||
label="release profile"
|
||||
label={translate('ReleaseProfile')}
|
||||
labelPlural={translate('ReleaseProfiles')}
|
||||
count={restrictionIds.length}
|
||||
/>
|
||||
|
||||
<TagInUse
|
||||
label="indexer"
|
||||
label={translate('Indexer')}
|
||||
labelPlural={translate('Indexers')}
|
||||
count={indexerIds.length}
|
||||
/>
|
||||
|
||||
<TagInUse
|
||||
label="download client"
|
||||
label={translate('DownloadClient')}
|
||||
labelPlural={translate('DownloadClients')}
|
||||
count={downloadClientIds.length}
|
||||
/>
|
||||
|
||||
<TagInUse
|
||||
label="auto tagging"
|
||||
label={translate('AutoTagging')}
|
||||
count={autoTagIds.length}
|
||||
shouldPluralize={false}
|
||||
/>
|
||||
</div> :
|
||||
null
|
||||
|
@ -140,7 +145,7 @@ class Tag extends Component {
|
|||
{
|
||||
!isTagUsed &&
|
||||
<div>
|
||||
No links
|
||||
{translate('NoLinks')}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -163,9 +168,9 @@ class Tag extends Component {
|
|||
<ConfirmModal
|
||||
isOpen={isDeleteTagModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete Tag"
|
||||
message={`Are you sure you want to delete the tag '${label}'?`}
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteTag')}
|
||||
message={translate('DeleteTagMessageText', { label })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteTag}
|
||||
onCancel={this.onDeleteTagModalClose}
|
||||
/>
|
||||
|
|
|
@ -4,31 +4,31 @@ import React from 'react';
|
|||
export default function TagInUse(props) {
|
||||
const {
|
||||
label,
|
||||
count,
|
||||
shouldPluralize = true
|
||||
labelPlural,
|
||||
count
|
||||
} = props;
|
||||
|
||||
if (count === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (count > 1 && shouldPluralize) {
|
||||
if (count > 1 && labelPlural ) {
|
||||
return (
|
||||
<div>
|
||||
{count} {label}s
|
||||
{count} {labelPlural.toLowerCase()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{count} {label}
|
||||
{count} {label.toLowerCase()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
TagInUse.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
count: PropTypes.number.isRequired,
|
||||
shouldPluralize: PropTypes.bool
|
||||
labelPlural: PropTypes.string,
|
||||
count: PropTypes.number.isRequired
|
||||
};
|
||||
|
|
|
@ -2,12 +2,13 @@ import React from 'react';
|
|||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AutoTaggings from './AutoTagging/AutoTaggings';
|
||||
import TagsConnector from './TagsConnector';
|
||||
|
||||
function TagSettings() {
|
||||
return (
|
||||
<PageContent title="Tags">
|
||||
<PageContent title={translate('Tags')}>
|
||||
<SettingsToolbarConnector
|
||||
showSave={false}
|
||||
/>
|
||||
|
|
|
@ -4,6 +4,7 @@ import Alert from 'Components/Alert';
|
|||
import FieldSet from 'Components/FieldSet';
|
||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import TagConnector from './TagConnector';
|
||||
import styles from './Tags.css';
|
||||
|
||||
|
@ -16,17 +17,17 @@ function Tags(props) {
|
|||
if (!items.length) {
|
||||
return (
|
||||
<Alert kind={kinds.INFO}>
|
||||
No tags have been added yet
|
||||
{translate('NoTagsHaveBeenAddedYet')}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FieldSet
|
||||
legend="Tags"
|
||||
legend={translate('Tags')}
|
||||
>
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load Tags"
|
||||
errorMessage={translate('TagsLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.tags}>
|
||||
|
|
|
@ -16,8 +16,18 @@ import titleCase from 'Utilities/String/titleCase';
|
|||
import translate from 'Utilities/String/translate';
|
||||
|
||||
export const firstDayOfWeekOptions = [
|
||||
{ key: 0, value: 'Sunday' },
|
||||
{ key: 1, value: 'Monday' }
|
||||
{
|
||||
key: 0,
|
||||
get value() {
|
||||
return translate('Sunday');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
get value() {
|
||||
return translate('Monday');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export const weekColumnOptions = [
|
||||
|
@ -67,7 +77,7 @@ class UISettings extends Component {
|
|||
.map((theme) => ({ key: theme, value: titleCase(theme) }));
|
||||
|
||||
return (
|
||||
<PageContent title="UI Settings">
|
||||
<PageContent title={translate('UiSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
{...otherProps}
|
||||
onSavePress={onSavePress}
|
||||
|
@ -82,7 +92,9 @@ class UISettings extends Component {
|
|||
|
||||
{
|
||||
!isFetching && error ?
|
||||
<Alert kind={kinds.DANGER}>Unable to load UI settings</Alert> :
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('UiSettingsLoadError')}
|
||||
</Alert> :
|
||||
null
|
||||
}
|
||||
|
||||
|
@ -92,9 +104,9 @@ class UISettings extends Component {
|
|||
id="uiSettings"
|
||||
{...otherProps}
|
||||
>
|
||||
<FieldSet legend="Calendar">
|
||||
<FieldSet legend={translate('Calendar')}>
|
||||
<FormGroup>
|
||||
<FormLabel>First Day of Week</FormLabel>
|
||||
<FormLabel>{translate('FirstDayOfWeek')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
|
@ -106,24 +118,24 @@ class UISettings extends Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Week Column Header</FormLabel>
|
||||
<FormLabel>{translate('WeekColumnHeader')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="calendarWeekColumnHeader"
|
||||
values={weekColumnOptions}
|
||||
onChange={onInputChange}
|
||||
helpText="Shown above each column when week is the active view"
|
||||
helpText={translate('WeekColumnHeaderHelpText')}
|
||||
{...settings.calendarWeekColumnHeader}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FieldSet>
|
||||
|
||||
<FieldSet
|
||||
legend="Dates"
|
||||
legend={translate('Dates')}
|
||||
>
|
||||
<FormGroup>
|
||||
<FormLabel>Short Date Format</FormLabel>
|
||||
<FormLabel>{translate('ShortDateFormat')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
|
@ -135,7 +147,7 @@ class UISettings extends Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Long Date Format</FormLabel>
|
||||
<FormLabel>{translate('LongDateFormat')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
|
@ -147,7 +159,7 @@ class UISettings extends Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Time Format</FormLabel>
|
||||
<FormLabel>{translate('TimeFormat')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
|
@ -159,11 +171,11 @@ class UISettings extends Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Show Relative Dates</FormLabel>
|
||||
<FormLabel>{translate('ShowRelativeDates')}</FormLabel>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="showRelativeDates"
|
||||
helpText="Show relative (Today/Yesterday/etc) or absolute dates"
|
||||
helpText={translate('ShowRelativeDatesHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.showRelativeDates}
|
||||
/>
|
||||
|
@ -171,14 +183,14 @@ class UISettings extends Component {
|
|||
</FieldSet>
|
||||
|
||||
<FieldSet
|
||||
legend="Style"
|
||||
legend={translate('Style')}
|
||||
>
|
||||
<FormGroup>
|
||||
<FormLabel>Theme</FormLabel>
|
||||
<FormLabel>{translate('Theme')}</FormLabel>
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="theme"
|
||||
helpText="Change Application UI Theme, 'Auto' Theme will use your OS Theme to set Light or Dark mode. Inspired by Theme.Park"
|
||||
helpText={translate('ThemeHelpText')}
|
||||
values={themeOptions}
|
||||
onChange={onInputChange}
|
||||
{...settings.theme}
|
||||
|
@ -186,11 +198,11 @@ class UISettings extends Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Enable Color-Impaired Mode</FormLabel>
|
||||
<FormLabel>{translate('EnableColorImpairedMode')}</FormLabel>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableColorImpairedMode"
|
||||
helpText="Altered style to allow color-impaired users to better distinguish color coded information"
|
||||
helpText={translate('EnableColorImpairedModeHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.enableColorImpairedMode}
|
||||
/>
|
||||
|
@ -199,13 +211,13 @@ class UISettings extends Component {
|
|||
|
||||
<FieldSet legend={translate('Language')}>
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('UI Language')}</FormLabel>
|
||||
<FormLabel>{translate('UiLanguage')}</FormLabel>
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="uiLanguage"
|
||||
values={languages}
|
||||
helpText={translate('Language that Sonarr will use for UI')}
|
||||
helpTextWarning={translate('Browser Reload Required')}
|
||||
helpText={translate('UiLanguageHelpText')}
|
||||
helpTextWarning={translate('BrowserReloadRequired')}
|
||||
onChange={onInputChange}
|
||||
{...settings.uiLanguage}
|
||||
/>
|
||||
|
|
|
@ -109,7 +109,7 @@ class Backups extends Component {
|
|||
{
|
||||
!isFetching && !!error &&
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('UnableToLoadBackups')}
|
||||
{translate('BackupsLoadError')}
|
||||
</Alert>
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue