From f2c31e92ceec7c6a8daffa78f30f15ab8684bef9 Mon Sep 17 00:00:00 2001 From: Stevie Robinson Date: Sat, 22 Jul 2023 21:14:33 +0200 Subject: [PATCH] Add Translations to Settings Pages --- .gitignore | 2 - .../Components/Page/Sidebar/PageSidebar.js | 2 +- .../AuthenticationRequiredModalContent.js | 21 +- .../CustomFormatSettingsPage.tsx | 3 +- .../CustomFormats/CustomFormats.js | 5 +- .../EditCustomFormatModalContent.js | 21 +- .../ExportCustomFormatModalContent.js | 9 +- .../ImportCustomFormatModalContent.js | 11 +- ...ImportCustomFormatModalContentConnector.js | 10 +- .../Specifications/AddSpecificationItem.js | 7 +- .../AddSpecificationModalContent.js | 11 +- .../EditSpecificationModalContent.js | 32 +- .../Specifications/Specification.js | 2 +- .../DownloadClients/DownloadClientSettings.js | 4 +- .../DownloadClients/AddDownloadClientItem.js | 7 +- .../AddDownloadClientModalContent.js | 21 +- .../DownloadClients/DownloadClient.js | 13 +- .../DownloadClients/DownloadClients.js | 5 +- .../EditDownloadClientModalContent.js | 37 +- .../Options/DownloadClientOptions.js | 17 +- .../EditRemotePathMappingModalContent.js | 25 +- .../RemotePathMappings/RemotePathMapping.js | 7 +- .../RemotePathMappings/RemotePathMappings.js | 17 +- .../src/Settings/General/AnalyticSettings.js | 9 +- .../src/Settings/General/BackupSettings.js | 15 +- .../src/Settings/General/GeneralSettings.js | 17 +- frontend/src/Settings/General/HostSettings.js | 53 +- .../src/Settings/General/LoggingSettings.js | 28 +- .../src/Settings/General/ProxySettings.js | 40 +- .../src/Settings/General/SecuritySettings.js | 98 +++- .../src/Settings/General/UpdateSettings.js | 23 +- .../EditImportListExclusionModalContent.js | 21 +- .../ImportListExclusion.js | 7 +- .../ImportListExclusions.js | 13 +- .../ImportLists/ImportListSettings.js | 4 +- .../ImportLists/AddImportListItem.js | 7 +- .../ImportLists/AddImportListModalContent.js | 22 +- .../ImportLists/EditImportListModalContent.js | 47 +- .../ImportLists/ImportLists/ImportList.js | 11 +- .../ImportLists/ImportLists/ImportLists.js | 7 +- .../Manage/ManageImportListsModalRow.tsx | 5 +- .../src/Settings/Indexers/IndexerSettings.js | 4 +- .../Indexers/Indexers/AddIndexerItem.js | 7 +- .../Indexers/AddIndexerModalContent.js | 21 +- .../Indexers/EditIndexerModalContent.js | 51 +- .../src/Settings/Indexers/Indexers/Indexer.js | 19 +- .../Settings/Indexers/Indexers/Indexers.js | 5 +- .../Edit/ManageIndexersEditModalContent.tsx | 2 +- .../Manage/ManageIndexersModalContent.tsx | 2 +- .../Indexers/Options/IndexerOptions.js | 25 +- .../MediaManagement/MediaManagement.js | 200 ++++--- .../Settings/MediaManagement/Naming/Naming.js | 88 +-- .../MediaManagement/Naming/NamingModal.js | 84 ++- .../Metadata/EditMetadataModalContent.js | 11 +- .../Settings/Metadata/Metadata/Metadata.js | 9 +- .../Settings/Metadata/Metadata/Metadatas.js | 5 +- .../src/Settings/Metadata/MetadataSettings.js | 3 +- .../MetadataSource/MetadataSourceSettings.js | 3 +- .../src/Settings/MetadataSource/TheTvdb.js | 9 +- .../Notifications/NotificationSettings.js | 3 +- .../Notifications/AddNotificationItem.js | 7 +- .../AddNotificationModalContent.js | 9 +- .../EditNotificationModalContent.js | 19 +- .../Notifications/Notification.js | 33 +- .../Notifications/NotificationEventItems.js | 31 +- .../Notifications/Notifications.js | 5 +- frontend/src/Settings/PendingChangesModal.js | 9 +- .../Settings/Profiles/Delay/DelayProfile.js | 19 +- .../Settings/Profiles/Delay/DelayProfiles.js | 21 +- .../Delay/EditDelayProfileModalContent.js | 71 ++- frontend/src/Settings/Profiles/Profiles.js | 3 +- .../Quality/EditQualityProfileModalContent.js | 33 +- .../Profiles/Quality/QualityProfile.js | 15 +- .../Quality/QualityProfileFormatItems.js | 16 +- .../Profiles/Quality/QualityProfileItem.js | 5 +- .../Quality/QualityProfileItemGroup.js | 5 +- .../Profiles/Quality/QualityProfileItems.js | 7 +- .../Profiles/Quality/QualityProfiles.js | 5 +- .../Release/EditReleaseProfileModalContent.js | 37 +- .../Profiles/Release/ReleaseProfile.js | 9 +- .../Profiles/Release/ReleaseProfiles.js | 5 +- .../Quality/Definition/QualityDefinition.js | 25 +- .../Definition/QualityDefinitionLimits.js | 15 +- .../Quality/Definition/QualityDefinitions.js | 21 +- frontend/src/Settings/Quality/Quality.js | 5 +- frontend/src/Settings/Settings.js | 55 +- frontend/src/Settings/SettingsToolbar.js | 3 +- .../Settings/Tags/AutoTagging/AutoTaggings.js | 2 +- .../EditAutoTaggingModalContent.js | 2 +- .../AddSpecificationModalContent.js | 4 +- .../EditSpecificationModalContent.js | 15 +- .../Specifications/Specification.js | 13 +- .../Tags/Details/TagDetailsDelayProfile.js | 11 +- .../Tags/Details/TagDetailsModalContent.js | 29 +- frontend/src/Settings/Tags/Tag.js | 33 +- frontend/src/Settings/Tags/TagInUse.js | 14 +- frontend/src/Settings/Tags/TagSettings.js | 3 +- frontend/src/Settings/Tags/Tags.js | 7 +- frontend/src/Settings/UI/UISettings.js | 56 +- frontend/src/System/Backup/Backups.js | 2 +- frontend/src/System/Updates/Updates.js | 2 +- src/NzbDrone.Core/Localization/Core/en.json | 551 +++++++++++++++++- 102 files changed, 1697 insertions(+), 802 deletions(-) diff --git a/.gitignore b/.gitignore index a46ab698d..73bd6ad62 100644 --- a/.gitignore +++ b/.gitignore @@ -128,8 +128,6 @@ coverage*.json setup/Output/ *.~is -UI/ - #VS outout folders bin obj diff --git a/frontend/src/Components/Page/Sidebar/PageSidebar.js b/frontend/src/Components/Page/Sidebar/PageSidebar.js index 9f42c7c99..614281c26 100644 --- a/frontend/src/Components/Page/Sidebar/PageSidebar.js +++ b/frontend/src/Components/Page/Sidebar/PageSidebar.js @@ -133,7 +133,7 @@ const links = [ to: '/settings/general' }, { - title: () => translate('UI'), + title: () => translate('Ui'), to: '/settings/ui' } ] diff --git a/frontend/src/FirstRun/AuthenticationRequiredModalContent.js b/frontend/src/FirstRun/AuthenticationRequiredModalContent.js index f0548397d..5b9141a7b 100644 --- a/frontend/src/FirstRun/AuthenticationRequiredModalContent.js +++ b/frontend/src/FirstRun/AuthenticationRequiredModalContent.js @@ -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} > - Authentication Required + {translate('AuthenticationRequired')} @@ -62,40 +63,40 @@ function AuthenticationRequiredModalContent(props) { className={styles.authRequiredAlert} kind={kinds.WARNING} > - {authenticationRequiredWarning} + {translate('AuthenticationRequiredWarning')} { isPopulated && !error ?
- Authentication + {translate('Authentication')} - Authentication Required + {translate('AuthenticationRequired')} - Username + {translate('Username')} - Password + {translate('Password')} - Save + {translate('Save')} diff --git a/frontend/src/Settings/CustomFormats/CustomFormatSettingsPage.tsx b/frontend/src/Settings/CustomFormats/CustomFormatSettingsPage.tsx index cb86066b1..fee176554 100644 --- a/frontend/src/Settings/CustomFormats/CustomFormatSettingsPage.tsx +++ b/frontend/src/Settings/CustomFormats/CustomFormatSettingsPage.tsx @@ -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 ( - + +
diff --git a/frontend/src/Settings/CustomFormats/CustomFormats/EditCustomFormatModalContent.js b/frontend/src/Settings/CustomFormats/CustomFormats/EditCustomFormatModalContent.js index 8e16ab54c..a57a38a7e 100644 --- a/frontend/src/Settings/CustomFormats/CustomFormats/EditCustomFormatModalContent.js +++ b/frontend/src/Settings/CustomFormats/CustomFormats/EditCustomFormatModalContent.js @@ -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 { - {id ? 'Edit Custom Format' : 'Add Custom Format'} + {id ? translate('EditCustomFormat') : translate('AddCustomFormat')} @@ -112,7 +113,7 @@ class EditCustomFormatModalContent extends Component { { !isFetching && !!error &&
- {'Unable to add a new custom format, please try again.'} + {translate('AddCustomFormatError')}
} @@ -124,7 +125,7 @@ class EditCustomFormatModalContent extends Component { > - Name + {translate('Name')} - {'Include Custom Format when Renaming'} + {translate('IncludeCustomFormatWhenRenaming')} -
+
{ specifications.map((tag) => { @@ -205,7 +206,7 @@ class EditCustomFormatModalContent extends Component { kind={kinds.DANGER} onPress={onDeleteCustomFormatPress} > - Delete + {translate('Delete')} } @@ -213,14 +214,14 @@ class EditCustomFormatModalContent extends Component { className={styles.deleteButton} onPress={this.onImportPress} > - Import + {translate('Import')}
- Save + {translate('Save')} diff --git a/frontend/src/Settings/CustomFormats/CustomFormats/ExportCustomFormatModalContent.js b/frontend/src/Settings/CustomFormats/CustomFormats/ExportCustomFormatModalContent.js index 96e93d3b6..c6f0a64c5 100644 --- a/frontend/src/Settings/CustomFormats/CustomFormats/ExportCustomFormatModalContent.js +++ b/frontend/src/Settings/CustomFormats/CustomFormats/ExportCustomFormatModalContent.js @@ -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 { - Export Custom Format + {translate('ExportCustomFormat')} @@ -41,7 +42,7 @@ class ExportCustomFormatModalContent extends Component { { !isFetching && !!error &&
- Unable to load custom formats + {translate('CustomFormatsLoadError')}
} @@ -59,13 +60,13 @@ class ExportCustomFormatModalContent extends Component {
diff --git a/frontend/src/Settings/CustomFormats/CustomFormats/ImportCustomFormatModalContent.js b/frontend/src/Settings/CustomFormats/CustomFormats/ImportCustomFormatModalContent.js index 5fc796ea6..a32de1316 100644 --- a/frontend/src/Settings/CustomFormats/CustomFormats/ImportCustomFormatModalContent.js +++ b/frontend/src/Settings/CustomFormats/CustomFormats/ImportCustomFormatModalContent.js @@ -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 { - Import Custom Format + {translate('ImportCustomFormat')} @@ -95,7 +96,7 @@ class ImportCustomFormatModalContent extends Component { { !isFetching && !!error &&
- Unable to load custom formats + {translate('CustomFormatsLoadError')}
} @@ -104,7 +105,7 @@ class ImportCustomFormatModalContent extends Component {
- Custom Format JSON + {translate('CustomFormatJSON')} - Cancel + {translate('Cancel')} - Import + {translate('Import')} diff --git a/frontend/src/Settings/CustomFormats/CustomFormats/ImportCustomFormatModalContentConnector.js b/frontend/src/Settings/CustomFormats/CustomFormats/ImportCustomFormatModalContentConnector.js index 3a92bd136..53124e9e9 100644 --- a/frontend/src/Settings/CustomFormats/CustomFormats/ImportCustomFormatModalContentConnector.js +++ b/frontend/src/Settings/CustomFormats/CustomFormats/ImportCustomFormatModalContentConnector.js @@ -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 }); diff --git a/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/AddSpecificationItem.js b/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/AddSpecificationItem.js index 3ca832977..e4c32750c 100644 --- a/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/AddSpecificationItem.js +++ b/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/AddSpecificationItem.js @@ -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')} @@ -65,7 +66,7 @@ class AddSpecificationItem extends Component { className={styles.presetsMenuButton} size={sizes.SMALL} > - Presets + {translate('Presets')} @@ -90,7 +91,7 @@ class AddSpecificationItem extends Component { to={infoLink} size={sizes.SMALL} > - More Info + {translate('MoreInfo')}
diff --git a/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/AddSpecificationModalContent.js b/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/AddSpecificationModalContent.js index e62593301..3b38e9666 100644 --- a/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/AddSpecificationModalContent.js +++ b/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/AddSpecificationModalContent.js @@ -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 &&
- {'Unable to add a new condition, please try again.'} + {translate('AddConditionError')}
} @@ -52,11 +53,11 @@ class AddSpecificationModalContent extends Component {
- {'Sonarr supports custom conditions against the release properties below.'} + {translate('SupportedCustomConditions')}
- {'Visit the wiki for more details: '} - {'Wiki'} + {translate('VisitTheWikiForMoreDetails')} + {translate('Wiki')}
@@ -81,7 +82,7 @@ class AddSpecificationModalContent extends Component { diff --git a/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/EditSpecificationModalContent.js b/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/EditSpecificationModalContent.js index 2364f5746..c2ed82aad 100644 --- a/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/EditSpecificationModalContent.js +++ b/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/EditSpecificationModalContent.js @@ -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 ( - {`${id ? 'Edit' : 'Add'} Condition - ${implementationName}`} + {`${id ? translate('EditCondition') : translate('AddCondition')} - ${implementationName}`} @@ -48,22 +49,23 @@ function EditSpecificationModalContent(props) { {...otherProps} > { - fields && fields.some((x) => x.label === 'Regular Expression') && + fields && fields.some((x) => x.label === translate('RegularExpression')) &&
-
\\^$.|?*+()[{ have special meanings and need escaping with a \\' }} /> - {'More details'} {'Here'} +
- {'Regular expressions can be tested '} - Here + +
+
+
} - Name + {translate('Name')} - Negate + {translate('Negate')} - Required + {translate('Required')} @@ -126,21 +128,21 @@ function EditSpecificationModalContent(props) { kind={kinds.DANGER} onPress={onDeleteSpecificationPress} > - Delete + {translate('Delete')} } - Save + {translate('Save')} diff --git a/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/Specification.js b/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/Specification.js index 5d06efcf2..0b59f09d5 100644 --- a/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/Specification.js +++ b/frontend/src/Settings/CustomFormats/CustomFormats/Specifications/Specification.js @@ -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} diff --git a/frontend/src/Settings/DownloadClients/DownloadClientSettings.js b/frontend/src/Settings/DownloadClients/DownloadClientSettings.js index aca9111b3..8eec91d37 100644 --- a/frontend/src/Settings/DownloadClients/DownloadClientSettings.js +++ b/frontend/src/Settings/DownloadClients/DownloadClientSettings.js @@ -70,7 +70,7 @@ class DownloadClientSettings extends Component { } = this.state; return ( - + - Custom + {translate('Custom')} @@ -65,7 +66,7 @@ class AddDownloadClientItem extends Component { className={styles.presetsMenuButton} size={sizes.SMALL} > - Presets + {translate('Presets')} @@ -90,7 +91,7 @@ class AddDownloadClientItem extends Component { to={infoLink} size={sizes.SMALL} > - More info + {translate('MoreInfo')}
diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/AddDownloadClientModalContent.js b/frontend/src/Settings/DownloadClients/DownloadClients/AddDownloadClientModalContent.js index 73d7a3f9e..40f78f35f 100644 --- a/frontend/src/Settings/DownloadClients/DownloadClients/AddDownloadClientModalContent.js +++ b/frontend/src/Settings/DownloadClients/DownloadClients/AddDownloadClientModalContent.js @@ -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 ( - Add Download Client + {translate('AddDownloadClient')} @@ -42,7 +43,9 @@ class AddDownloadClientModalContent extends Component { { !isSchemaFetching && !!schemaError && -
Unable to add a new downloadClient, please try again.
+
+ {translate('AddDownloadClientError')} +
} { @@ -50,11 +53,15 @@ class AddDownloadClientModalContent extends Component {
-
Sonarr supports many popular torrent and usenet download clients.
-
For more information on the individual download clients, click the more info buttons.
+
+ {translate('SupportedDownloadClients')} +
+
+ {translate('SupportedDownloadClientsMoreInfo')} +
-
+
{ usenetDownloadClients.map((downloadClient) => { @@ -71,7 +78,7 @@ class AddDownloadClientModalContent extends Component {
-
+
{ torrentDownloadClients.map((downloadClient) => { @@ -94,7 +101,7 @@ class AddDownloadClientModalContent extends Component { diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClient.js b/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClient.js index 4f0b7e3df..fceaeda65 100644 --- a/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClient.js +++ b/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClient.js @@ -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 ? : } @@ -91,7 +92,7 @@ class DownloadClient extends Component { kind={kinds.DISABLED} outline={true} > - Priority: {priority} + {translate('PrioritySettings', { priority })} }
@@ -111,9 +112,9 @@ class DownloadClient extends Component { diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClients.js b/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClients.js index e1e27b37c..5104065ea 100644 --- a/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClients.js +++ b/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClients.js @@ -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 ( -
+
diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/EditDownloadClientModalContent.js b/frontend/src/Settings/DownloadClients/DownloadClients/EditDownloadClientModalContent.js index f6ed3c118..1d613f018 100644 --- a/frontend/src/Settings/DownloadClients/DownloadClients/EditDownloadClientModalContent.js +++ b/frontend/src/Settings/DownloadClients/DownloadClients/EditDownloadClientModalContent.js @@ -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 ( - {`${id ? 'Edit' : 'Add'} Download Client - ${implementationName}`} + {`${id ? translate('Edit') : translate('Add')} ${translate('DownloadClient')} - ${implementationName}`} @@ -68,7 +69,9 @@ class EditDownloadClientModalContent extends Component { { !isFetching && !!error && -
Unable to add a new download client, please try again.
+
+ {translate('AddDownloadClientError')} +
} { @@ -85,7 +88,7 @@ class EditDownloadClientModalContent extends Component { } - Name + {translate('Name')} - Enable + {translate('Enable')} - Client Priority + {translate('ClientPriority')} - Tags + {translate('Tags')} @@ -152,15 +155,15 @@ class EditDownloadClientModalContent extends Component {
- Remove Completed + {translate('RemoveCompleted')} @@ -169,12 +172,12 @@ class EditDownloadClientModalContent extends Component { { protocol.value !== 'torrent' && - Remove Failed + {translate('RemoveFailed')} @@ -192,7 +195,7 @@ class EditDownloadClientModalContent extends Component { kind={kinds.DANGER} onPress={onDeleteDownloadClientPress} > - Delete + {translate('Delete')} } @@ -201,13 +204,13 @@ class EditDownloadClientModalContent extends Component { error={saveError} onPress={onTestPress} > - Test + {translate('Test')} - Save + {translate('Save')} diff --git a/frontend/src/Settings/DownloadClients/Options/DownloadClientOptions.js b/frontend/src/Settings/DownloadClients/Options/DownloadClientOptions.js index f3bb181d7..7de4febfd 100644 --- a/frontend/src/Settings/DownloadClients/Options/DownloadClientOptions.js +++ b/frontend/src/Settings/DownloadClients/Options/DownloadClientOptions.js @@ -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 && - Unable to load download client options + + {translate('DownloadClientOptionsLoadError')} + } { hasSettings && !isFetching && !error && advancedSettings &&
-
+
- Enable + {translate('Enable')} @@ -58,12 +61,12 @@ function DownloadClientOptions(props) { isAdvanced={true} size={sizes.MEDIUM} > - Redownload Failed + {translate('RedownloadFailed')} @@ -71,7 +74,7 @@ function DownloadClientOptions(props) { - The Remove settings were moved to the individual Download Client settings in the table above. + {translate('RemoveDownloadsAlert')}
diff --git a/frontend/src/Settings/DownloadClients/RemotePathMappings/EditRemotePathMappingModalContent.js b/frontend/src/Settings/DownloadClients/RemotePathMappings/EditRemotePathMappingModalContent.js index 204695623..5fc2ac757 100644 --- a/frontend/src/Settings/DownloadClients/RemotePathMappings/EditRemotePathMappingModalContent.js +++ b/frontend/src/Settings/DownloadClients/RemotePathMappings/EditRemotePathMappingModalContent.js @@ -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 ( - {id ? 'Edit Remote Path Mapping' : 'Add Remote Path Mapping'} + {id ? translate('EditRemotePathMapping') : translate('AddRemotePathMapping')} @@ -51,19 +52,21 @@ function EditRemotePathMappingModalContent(props) { { !isFetching && !!error && -
Unable to add a new remote path mapping, please try again.
+
+ {translate('AddRemotePathMappingError')} +
} { !isFetching && !error &&
- Host + {translate('Host')} - Remote Path + {translate('RemotePath')} - Local Path + {translate('LocalPath')} @@ -105,14 +108,14 @@ function EditRemotePathMappingModalContent(props) { kind={kinds.DANGER} onPress={onDeleteRemotePathMappingPress} > - Delete + {translate('Delete')} } - Save + {translate('Save')} diff --git a/frontend/src/Settings/DownloadClients/RemotePathMappings/RemotePathMapping.js b/frontend/src/Settings/DownloadClients/RemotePathMappings/RemotePathMapping.js index ae2d54a65..ad7daad9e 100644 --- a/frontend/src/Settings/DownloadClients/RemotePathMappings/RemotePathMapping.js +++ b/frontend/src/Settings/DownloadClients/RemotePathMappings/RemotePathMapping.js @@ -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 { diff --git a/frontend/src/Settings/DownloadClients/RemotePathMappings/RemotePathMappings.js b/frontend/src/Settings/DownloadClients/RemotePathMappings/RemotePathMappings.js index 57f215618..d45958d91 100644 --- a/frontend/src/Settings/DownloadClients/RemotePathMappings/RemotePathMappings.js +++ b/frontend/src/Settings/DownloadClients/RemotePathMappings/RemotePathMappings.js @@ -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 ( -
+
-
Host
-
Remote Path
-
Local Path
+
+ {translate('Host')} +
+
+ {translate('RemotePath')} +
+
+ {translate('LocalPath')} +
diff --git a/frontend/src/Settings/General/AnalyticSettings.js b/frontend/src/Settings/General/AnalyticSettings.js index 7deb14a9d..91a8c5c55 100644 --- a/frontend/src/Settings/General/AnalyticSettings.js +++ b/frontend/src/Settings/General/AnalyticSettings.js @@ -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 ( -
+
- Send Anonymous Usage Data + {translate('SendAnonymousUsageData')} diff --git a/frontend/src/Settings/General/BackupSettings.js b/frontend/src/Settings/General/BackupSettings.js index 3c9baf84f..5f0f79c27 100644 --- a/frontend/src/Settings/General/BackupSettings.js +++ b/frontend/src/Settings/General/BackupSettings.js @@ -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 ( -
+
- Folder + {translate('Folder')} @@ -44,13 +45,13 @@ function BackupSettings(props) { advancedSettings={advancedSettings} isAdvanced={true} > - Interval + {translate('Interval')} @@ -60,13 +61,13 @@ function BackupSettings(props) { advancedSettings={advancedSettings} isAdvanced={true} > - Retention + {translate('Retention')} diff --git a/frontend/src/Settings/General/GeneralSettings.js b/frontend/src/Settings/General/GeneralSettings.js index cd79ec767..3c8154538 100644 --- a/frontend/src/Settings/General/GeneralSettings.js +++ b/frontend/src/Settings/General/GeneralSettings.js @@ -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 ( - + @@ -124,7 +125,9 @@ class GeneralSettings extends Component { { !isFetching && error && - Unable to load General settings + + {translate('GeneralSettingsLoadError')} + } { @@ -183,12 +186,10 @@ class GeneralSettings extends Component { diff --git a/frontend/src/Settings/General/HostSettings.js b/frontend/src/Settings/General/HostSettings.js index bcde83670..3c0eb01e3 100644 --- a/frontend/src/Settings/General/HostSettings.js +++ b/frontend/src/Settings/General/HostSettings.js @@ -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 ( -
+
- Bind Address + {translate('BindAddress')} - Port Number + {translate('PortNumber')} - URL Base + {translate('UrlBase')} @@ -78,13 +79,13 @@ function HostSettings(props) { advancedSettings={advancedSettings} isAdvanced={true} > - Instance Name + {translate('InstanceName')} @@ -94,12 +95,12 @@ function HostSettings(props) { advancedSettings={advancedSettings} isAdvanced={true} > - Application URL + {translate('ApplicationURL')} @@ -110,12 +111,12 @@ function HostSettings(props) { isAdvanced={true} size={sizes.MEDIUM} > - Enable SSL + {translate('EnableSsl')} @@ -127,14 +128,14 @@ function HostSettings(props) { advancedSettings={advancedSettings} isAdvanced={true} > - SSL Port + {translate('SslPort')} @@ -148,13 +149,13 @@ function HostSettings(props) { advancedSettings={advancedSettings} isAdvanced={true} > - SSL Cert Path + {translate('SslCertPath')} @@ -168,13 +169,13 @@ function HostSettings(props) { advancedSettings={advancedSettings} isAdvanced={true} > - SSL Cert Password + {translate('SslCertPassword')} @@ -185,12 +186,12 @@ function HostSettings(props) { { isWindows && mode !== 'service' ? - Open browser on start + {translate('OpenBrowserOnStart')} diff --git a/frontend/src/Settings/General/LoggingSettings.js b/frontend/src/Settings/General/LoggingSettings.js index 3e421052b..60b651dca 100644 --- a/frontend/src/Settings/General/LoggingSettings.js +++ b/frontend/src/Settings/General/LoggingSettings.js @@ -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 ( -
+
- Log Level + {translate('LogLevel')} diff --git a/frontend/src/Settings/General/ProxySettings.js b/frontend/src/Settings/General/ProxySettings.js index 697201257..dfc5e55b5 100644 --- a/frontend/src/Settings/General/ProxySettings.js +++ b/frontend/src/Settings/General/ProxySettings.js @@ -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 ( -
+
- Use Proxy + {translate('UseProxy')} - Proxy Type + {translate('ProxyType')} - Hostname + {translate('Hostname')} - Port + {translate('Port')} - Username + {translate('Username')} - Password + {translate('Password')} - Ignored Addresses + {translate('IgnoredAddresses')} - Bypass Proxy for Local Addresses + {translate('BypassProxyForLocalAddresses')} +
- Authentication + {translate('Authentication')} @@ -103,13 +149,13 @@ class SecuritySettings extends Component { { authenticationEnabled ? - Authentication Required + {translate('AuthenticationRequired')} @@ -120,7 +166,7 @@ class SecuritySettings extends Component { { authenticationEnabled ? - Username + {translate('Username')} - Password + {translate('Password')} - API Key + {translate('ApiKey')} - Certificate Validation + {translate('CertificateValidation')} @@ -195,9 +241,9 @@ class SecuritySettings extends Component { diff --git a/frontend/src/Settings/General/UpdateSettings.js b/frontend/src/Settings/General/UpdateSettings.js index ae18dc987..c01974560 100644 --- a/frontend/src/Settings/General/UpdateSettings.js +++ b/frontend/src/Settings/General/UpdateSettings.js @@ -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 ( -
+
- Branch + {translate('Branch')} - Automatic + {translate('Automatic')} @@ -91,13 +92,13 @@ function UpdateSettings(props) { advancedSettings={advancedSettings} isAdvanced={true} > - Mechanism + {translate('Mechanism')} - Script Path + {translate('ScriptPath')} diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.js b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.js index 679bc449e..c48e24903 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.js +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.js @@ -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 ( - {id ? 'Edit Import List Exclusion' : 'Add Import List Exclusion'} + {id ? translate('EditImportListExclusion') : translate('AddImportListExclusion')} @@ -49,7 +50,9 @@ function EditImportListExclusionModalContent(props) { { !isFetching && !!error && -
Unable to add a new import list exclusion, please try again.
+
+ {translate('AddImportListExclusionError')} +
} { @@ -58,24 +61,24 @@ function EditImportListExclusionModalContent(props) { {...otherProps} > - Title + {translate('Title')} - TVDB ID + {translate('TvdbId')} @@ -92,14 +95,14 @@ function EditImportListExclusionModalContent(props) { kind={kinds.DANGER} onPress={onDeleteImportListExclusionPress} > - Delete + {translate('Delete')} } - Save + {translate('Save')}
diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.js b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.js index 105203a66..e95561b82 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.js +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.js @@ -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 { diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.js b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.js index e1de2af42..9bb7814d9 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.js +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.js @@ -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 ( -
+
-
Title
-
TVDB ID
+
+ {translate('Title')} +
+
+ {translate('TvdbId')} +
diff --git a/frontend/src/Settings/ImportLists/ImportListSettings.js b/frontend/src/Settings/ImportLists/ImportListSettings.js index 16fead466..32a365860 100644 --- a/frontend/src/Settings/ImportLists/ImportListSettings.js +++ b/frontend/src/Settings/ImportLists/ImportListSettings.js @@ -66,7 +66,7 @@ class ImportListSettings extends Component { } = this.state; return ( - + - Custom + {translate('Custom')} @@ -65,7 +66,7 @@ class AddImportListItem extends Component { className={styles.presetsMenuButton} size={sizes.SMALL} > - Presets + {translate('Presets')} @@ -90,7 +91,7 @@ class AddImportListItem extends Component { to={infoLink} size={sizes.SMALL} > - More info + {translate('MoreInfo')}
diff --git a/frontend/src/Settings/ImportLists/ImportLists/AddImportListModalContent.js b/frontend/src/Settings/ImportLists/ImportLists/AddImportListModalContent.js index b98e97bb4..2a7d39410 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/AddImportListModalContent.js +++ b/frontend/src/Settings/ImportLists/ImportLists/AddImportListModalContent.js @@ -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 ( - Add List + {translate('AddImportList')} @@ -43,7 +44,9 @@ class AddImportListModalContent extends Component { { !isSchemaFetching && !!schemaError ? -
Unable to add a new list, please try again.
: +
+ {translate('AddListError')} +
: null } @@ -52,13 +55,20 @@ class AddImportListModalContent extends Component {
-
Sonarr supports multiple lists for importing Series into the database.
-
For more information on the individual lists, click on the info buttons.
+
+ {translate('SupportedLists')} +
+
+ {translate('SupportedListsMoreInfo')} +
{ Object.keys(listGroups).map((key) => { return ( -
+
{ listGroups[key].map((list) => { @@ -85,7 +95,7 @@ class AddImportListModalContent extends Component { diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js index fc632d40b..464e76879 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js +++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js @@ -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 ( - {id ? 'Edit List' : 'Add List'} + {id ? translate('EditImportList') : translate('AddImportList')} @@ -69,7 +70,9 @@ function EditImportListModalContent(props) { { !isFetching && !!error ? -
Unable to add a new list, please try again.
: +
+ {translate('AddListError')} +
: 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) + })} - Name + {translate('Name')} - Enable Automatic Add + {translate('EnableAutomaticAdd')} @@ -109,7 +114,7 @@ function EditImportListModalContent(props) { - Monitor + {translate('Monitor')} } - title="Monitoring Options" + title={translate('MonitoringOptions')} body={} position={tooltipPositions.RIGHT} /> @@ -133,12 +138,12 @@ function EditImportListModalContent(props) { - Root Folder + {translate('RootFolder')} - Quality Profile + {translate('QualityProfile')} @@ -159,7 +164,7 @@ function EditImportListModalContent(props) { - Series Type + {translate('SeriesType')} } - title="Series Types" + title={translate('SeriesTypes')} body={} position={tooltipPositions.RIGHT} /> @@ -183,7 +188,7 @@ function EditImportListModalContent(props) { - Season Folder + {translate('SeasonFolder')} - Sonarr Tags + {translate('SonarrTags')} @@ -238,7 +243,7 @@ function EditImportListModalContent(props) { kind={kinds.DANGER} onPress={onDeleteImportListPress} > - Delete + {translate('Delete')} } @@ -247,13 +252,13 @@ function EditImportListModalContent(props) { error={saveError} onPress={onTestPress} > - Test + {translate('Test')} - Save + {translate('Save')}
diff --git a/frontend/src/Settings/ImportLists/ImportLists/ImportList.js b/frontend/src/Settings/ImportLists/ImportLists/ImportList.js index fb49c6345..df7e34b88 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/ImportList.js +++ b/frontend/src/Settings/ImportLists/ImportLists/ImportList.js @@ -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 && } @@ -81,7 +82,7 @@ class ImportList extends Component {
@@ -95,9 +96,9 @@ class ImportList extends Component { diff --git a/frontend/src/Settings/ImportLists/ImportLists/ImportLists.js b/frontend/src/Settings/ImportLists/ImportLists/ImportLists.js index accfc174f..346aae650 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/ImportLists.js +++ b/frontend/src/Settings/ImportLists/ImportLists/ImportLists.js @@ -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 ( -
+
diff --git a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.tsx b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.tsx index 410e626e2..095ac7c03 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.tsx +++ b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.tsx @@ -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) { - {qualityProfile?.name ?? 'None'} + {qualityProfile?.name ?? translate('None')} @@ -71,7 +72,7 @@ function ManageImportListsModalRow(props: ManageImportListsModalRowProps) { - {enableAutomaticAdd ? 'Yes' : 'No'} + {enableAutomaticAdd ? translate('Yes') : translate('No')} diff --git a/frontend/src/Settings/Indexers/IndexerSettings.js b/frontend/src/Settings/Indexers/IndexerSettings.js index e9675a993..aa18acdf9 100644 --- a/frontend/src/Settings/Indexers/IndexerSettings.js +++ b/frontend/src/Settings/Indexers/IndexerSettings.js @@ -69,7 +69,7 @@ class IndexerSettings extends Component { } = this.state; return ( - + - Custom + {translate('Custom')} @@ -65,7 +66,7 @@ class AddIndexerItem extends Component { className={styles.presetsMenuButton} size={sizes.SMALL} > - Presets + {translate('Presets')} @@ -90,7 +91,7 @@ class AddIndexerItem extends Component { to={infoLink} size={sizes.SMALL} > - More info + {translate('MoreInfo')}
diff --git a/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.js b/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.js index 37d721240..0eb46995e 100644 --- a/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.js +++ b/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.js @@ -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 ( - Add Indexer + {translate('AddIndexer')} @@ -42,7 +43,9 @@ class AddIndexerModalContent extends Component { { !isSchemaFetching && !!schemaError && -
Unable to add a new indexer, please try again.
+
+ {translate('AddIndexerError')} +
} { @@ -50,11 +53,15 @@ class AddIndexerModalContent extends Component {
-
Sonarr supports any indexer that uses the Newznab standard, as well as other indexers listed below.
-
For more information on the individual indexers, click on the info buttons.
+
+ {translate('SupportedIndexers')} +
+
+ {translate('SupportedIndexersMoreInfo')} +
-
+
{ usenetIndexers.map((indexer) => { @@ -71,7 +78,7 @@ class AddIndexerModalContent extends Component {
-
+
{ torrentIndexers.map((indexer) => { @@ -94,7 +101,7 @@ class AddIndexerModalContent extends Component { diff --git a/frontend/src/Settings/Indexers/Indexers/EditIndexerModalContent.js b/frontend/src/Settings/Indexers/Indexers/EditIndexerModalContent.js index 8e491f081..674e169ad 100644 --- a/frontend/src/Settings/Indexers/Indexers/EditIndexerModalContent.js +++ b/frontend/src/Settings/Indexers/Indexers/EditIndexerModalContent.js @@ -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 ( - {`${id ? 'Edit' : 'Add'} Indexer - ${implementationName}`} + {`${id ? translate('EditIndexer') : translate('AddIndexer')} - ${implementationName}`} @@ -66,14 +67,16 @@ function EditIndexerModalContent(props) { { !isFetching && !!error && -
Unable to add a new indexer, please try again.
+
+ {translate('AddIndexerError')} +
} { !isFetching && !error && - Name + {translate('Name')} - Enable RSS + {translate('EnableRss')} - Enable Automatic Search + {translate('EnableAutomaticSearch')} - Enable Interactive Search + {translate('EnableInteractiveSearch')} - Indexer Priority + {translate('IndexerPriority')} - Maximum Single Episode Age + {translate('MaximumSingleEpisodeAge')} - DownloadClient + {translate('DownloadClient')} - Tags + {translate('Tags')} @@ -213,7 +216,7 @@ function EditIndexerModalContent(props) { kind={kinds.DANGER} onPress={onDeleteIndexerPress} > - Delete + {translate('Delete')} } @@ -228,13 +231,13 @@ function EditIndexerModalContent(props) { error={saveError} onPress={onTestPress} > - Test + {translate('Test')} - Save + {translate('Save')}
diff --git a/frontend/src/Settings/Indexers/Indexers/Indexer.js b/frontend/src/Settings/Indexers/Indexers/Indexer.js index 500ff00f6..ab9560338 100644 --- a/frontend/src/Settings/Indexers/Indexers/Indexer.js +++ b/frontend/src/Settings/Indexers/Indexers/Indexer.js @@ -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 { @@ -100,28 +101,28 @@ class Indexer extends Component { { supportsRss && enableRss && } { supportsSearch && enableAutomaticSearch && } { supportsSearch && enableInteractiveSearch && } { showPriority && } { @@ -130,7 +131,7 @@ class Indexer extends Component { kind={kinds.DISABLED} outline={true} > - Disabled + {translate('Disabled')} }
@@ -150,9 +151,9 @@ class Indexer extends Component { diff --git a/frontend/src/Settings/Indexers/Indexers/Indexers.js b/frontend/src/Settings/Indexers/Indexers/Indexers.js index 69786bbe2..2cb01953a 100644 --- a/frontend/src/Settings/Indexers/Indexers/Indexers.js +++ b/frontend/src/Settings/Indexers/Indexers/Indexers.js @@ -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 ( -
+
diff --git a/frontend/src/Settings/Indexers/Indexers/Manage/Edit/ManageIndexersEditModalContent.tsx b/frontend/src/Settings/Indexers/Indexers/Manage/Edit/ManageIndexersEditModalContent.tsx index 8af789d93..a7b7187e3 100644 --- a/frontend/src/Settings/Indexers/Indexers/Manage/Edit/ManageIndexersEditModalContent.tsx +++ b/frontend/src/Settings/Indexers/Indexers/Manage/Edit/ManageIndexersEditModalContent.tsx @@ -127,7 +127,7 @@ function ManageIndexersEditModalContent( - {translate('EnableRSS')} + {translate('EnableRss')} translate('EnableRSS'), + label: () => translate('EnableRss'), isSortable: true, isVisible: true, }, diff --git a/frontend/src/Settings/Indexers/Options/IndexerOptions.js b/frontend/src/Settings/Indexers/Options/IndexerOptions.js index f92a843a3..c1211b075 100644 --- a/frontend/src/Settings/Indexers/Options/IndexerOptions.js +++ b/frontend/src/Settings/Indexers/Options/IndexerOptions.js @@ -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 ( -
+
{ isFetching && @@ -28,49 +29,51 @@ function IndexerOptions(props) { { !isFetching && error && - Unable to load indexer options + + {translate('IndexerOptionsLoadError')} + } { hasSettings && !isFetching && !error && - Minimum Age + {translate('MinimumAge')} - Retention + {translate('Retention')} - Maximum Size + {translate('MaximumSize')} @@ -80,7 +83,7 @@ function IndexerOptions(props) { advancedSettings={advancedSettings} isAdvanced={true} > - RSS Sync Interval + {translate('RssSyncInterval')} + +
: null } { !isFetching && error ? -
- Unable to load Media Management settings +
+ + {translate('MediaManagementSettingsLoadError')} +
: null } @@ -91,18 +153,18 @@ class MediaManagement extends Component { > { advancedSettings ? -
+
- Create empty series folders + {translate('CreateEmptySeriesFolders')} @@ -113,12 +175,12 @@ class MediaManagement extends Component { isAdvanced={true} size={sizes.MEDIUM} > - Delete empty folders + {translate('DeleteEmptyFolders')} @@ -129,19 +191,19 @@ class MediaManagement extends Component { { advancedSettings ?
- Episode Title Required + {translate('EpisodeTitleRequired')} - Skip Free Space Check + {translate('SkipFreeSpaceCheck')} @@ -173,13 +235,13 @@ class MediaManagement extends Component { isAdvanced={true} size={sizes.MEDIUM} > - Minimum Free Space + {translate('MinimumFreeSpace')} @@ -190,13 +252,13 @@ class MediaManagement extends Component { isAdvanced={true} size={sizes.MEDIUM} > - Use Hardlinks instead of Copy + {translate('UseHardlinksInsteadOfCopy')} @@ -207,12 +269,12 @@ class MediaManagement extends Component { isAdvanced={true} size={sizes.MEDIUM} > - Import Using Script + {translate('ImportUsingScript')} @@ -224,13 +286,13 @@ class MediaManagement extends Component { advancedSettings={advancedSettings} isAdvanced={true} > - Import Script Path + {translate('ImportScriptPath')} @@ -238,12 +300,12 @@ class MediaManagement extends Component { } - Import Extra Files + {translate('ImportExtraFiles')} @@ -255,14 +317,14 @@ class MediaManagement extends Component { advancedSettings={advancedSettings} isAdvanced={true} > - Import Extra Files + {translate('ImportExtraFiles')} - Unmonitor Deleted Episodes + {translate('UnmonitorDeletedEpisodes')} @@ -292,18 +354,18 @@ class MediaManagement extends Component { isAdvanced={true} size={sizes.MEDIUM} > - Propers and Repacks + {translate('DownloadPropersAndRepacks')} - Analyse video files + {translate('AnalyseVideoFiles')} @@ -332,13 +394,13 @@ class MediaManagement extends Component { advancedSettings={advancedSettings} isAdvanced={true} > - Rescan Series Folder after Refresh + {translate('RescanSeriesFolderAfterRefresh')} - Change File Date + {translate('ChangeFileDate')} - Recycling Bin + {translate('RecyclingBin')} @@ -380,13 +442,13 @@ class MediaManagement extends Component { advancedSettings={advancedSettings} isAdvanced={true} > - Recycling Bin Cleanup + {translate('RecyclingBinCleanup')} - Set Permissions + {translate('SetPermissions')} @@ -420,13 +482,13 @@ class MediaManagement extends Component { advancedSettings={advancedSettings} isAdvanced={true} > - chmod Folder + {translate('ChmodFolder')} @@ -436,13 +498,13 @@ class MediaManagement extends Component { advancedSettings={advancedSettings} isAdvanced={true} > - chown Group + {translate('ChownGroup')} +
{ isFetching && @@ -212,31 +213,33 @@ class Naming extends Component { { !isFetching && error && - Unable to load Naming settings + + {translate('NamingSettingsLoadError')} + } { hasSettings && !isFetching && !error && - Rename Episodes + {translate('RenameEpisodes')} - Replace Illegal Characters + {translate('ReplaceIllegalCharacters')} @@ -245,12 +248,13 @@ class Naming extends Component { { replaceIllegalCharacters ? - Colon Replacement + {translate('ColonReplacement')} @@ -262,7 +266,7 @@ class Naming extends Component { renameEpisodes &&
- Standard Episode Format + {translate('StandardEpisodeFormat')} - Daily Episode Format + {translate('DailyEpisodeFormat')} - Anime Episode Format + {translate('DailyEpisodeFormat')} - Series Folder Format + {translate('SeriesFolderFormat')} ?} 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]} /> - Season Folder Format + {translate('SeasonFolderFormat')} - Specials Folder Format + {translate('SpecialsFolderFormat')} - Multi-Episode Style + {translate('MultiEpisodeStyle')} - File Name Tokens + {translate('FileNameTokens')} @@ -232,7 +269,7 @@ class NamingModal extends Component { { !advancedSettings && -
+
{ fileNameTokens.map(({ token, example }) => { @@ -257,7 +294,7 @@ class NamingModal extends Component {
} -
+
{ seriesTokens.map(({ token, example }) => { @@ -279,7 +316,7 @@ class NamingModal extends Component {
-
+
{ seriesIdTokens.map(({ token, example }) => { @@ -303,7 +340,7 @@ class NamingModal extends Component { { season && -
+
{ seasonTokens.map(({ token, example }) => { @@ -329,7 +366,7 @@ class NamingModal extends Component { { episode &&
-
+
{ episodeTokens.map(({ token, example }) => { @@ -351,7 +388,7 @@ class NamingModal extends Component {
-
+
{ airDateTokens.map(({ token, example }) => { @@ -375,7 +412,7 @@ class NamingModal extends Component { { anime && -
+
{ absoluteTokens.map(({ token, example }) => { @@ -403,7 +440,7 @@ class NamingModal extends Component { { additional &&
-
+
{ episodeTitleTokens.map(({ token, example }) => { @@ -425,7 +462,7 @@ class NamingModal extends Component {
-
+
{ qualityTokens.map(({ token, example }) => { @@ -447,7 +484,7 @@ class NamingModal extends Component {
-
+
{ mediaInfoTokens.map(({ token, example, footNote }) => { @@ -471,14 +508,11 @@ class NamingModal extends Component {
-
- MediaInfo Full/AudioLanguages/SubtitleLanguages support a :EN+DE suffix allowing you to filter the languages included in the filename. Use -DE to exclude specific languages. - Appending + (eg :EN+) will output [EN]/[EN+--]/[--] depending on excluded languages. For example {'{'}MediaInfo Full:EN+DE{'}'}. -
+
-
+
{ otherTokens.map(({ token, example }) => { @@ -500,7 +534,7 @@ class NamingModal extends Component {
-
+
{ originalTokens.map(({ token, example }) => { @@ -534,7 +568,7 @@ class NamingModal extends Component { onSelectionChange={this.onInputSelectionChange} /> diff --git a/frontend/src/Settings/Metadata/Metadata/EditMetadataModalContent.js b/frontend/src/Settings/Metadata/Metadata/EditMetadataModalContent.js index 65f0da061..221c6bcaf 100644 --- a/frontend/src/Settings/Metadata/Metadata/EditMetadataModalContent.js +++ b/frontend/src/Settings/Metadata/Metadata/EditMetadataModalContent.js @@ -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 ( - Edit {name.value} Metadata + {translate('EditMetadata', { metadataType: name.value })} - Enable + {translate('Enable')} @@ -74,7 +75,7 @@ function EditMetadataModalContent(props) { - Save + {translate('Save')} diff --git a/frontend/src/Settings/Metadata/Metadata/Metadata.js b/frontend/src/Settings/Metadata/Metadata/Metadata.js index 7135c87bd..ffb0ab967 100644 --- a/frontend/src/Settings/Metadata/Metadata/Metadata.js +++ b/frontend/src/Settings/Metadata/Metadata/Metadata.js @@ -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 ? : }
@@ -81,7 +82,7 @@ class Metadata extends Component { enable && !!metadataFields.length &&
- Metadata + {translate('Metadata')}
{ @@ -107,7 +108,7 @@ class Metadata extends Component { enable && !!imageFields.length &&
- Images + {translate('Images')}
{ diff --git a/frontend/src/Settings/Metadata/Metadata/Metadatas.js b/frontend/src/Settings/Metadata/Metadata/Metadatas.js index afe33ab4c..a52275bcc 100644 --- a/frontend/src/Settings/Metadata/Metadata/Metadatas.js +++ b/frontend/src/Settings/Metadata/Metadata/Metadatas.js @@ -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 ( -
+
diff --git a/frontend/src/Settings/Metadata/MetadataSettings.js b/frontend/src/Settings/Metadata/MetadataSettings.js index 62daa3659..5c9f9ea82 100644 --- a/frontend/src/Settings/Metadata/MetadataSettings.js +++ b/frontend/src/Settings/Metadata/MetadataSettings.js @@ -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 ( - + diff --git a/frontend/src/Settings/MetadataSource/MetadataSourceSettings.js b/frontend/src/Settings/MetadataSource/MetadataSourceSettings.js index ef235393b..9f7dffc5d 100644 --- a/frontend/src/Settings/MetadataSource/MetadataSourceSettings.js +++ b/frontend/src/Settings/MetadataSource/MetadataSourceSettings.js @@ -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 ( - + diff --git a/frontend/src/Settings/MetadataSource/TheTvdb.js b/frontend/src/Settings/MetadataSource/TheTvdb.js index 7add9322c..152a02cae 100644 --- a/frontend/src/Settings/MetadataSource/TheTvdb.js +++ b/frontend/src/Settings/MetadataSource/TheTvdb.js @@ -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) {
- TheTVDB + {translate('TheTvdb')}
-
- Series and episode information is provided by TheTVDB.com. Please consider supporting them. -
+
diff --git a/frontend/src/Settings/Notifications/NotificationSettings.js b/frontend/src/Settings/Notifications/NotificationSettings.js index 1271238bb..991624463 100644 --- a/frontend/src/Settings/Notifications/NotificationSettings.js +++ b/frontend/src/Settings/Notifications/NotificationSettings.js @@ -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 ( - + diff --git a/frontend/src/Settings/Notifications/Notifications/AddNotificationItem.js b/frontend/src/Settings/Notifications/Notifications/AddNotificationItem.js index 3113cccad..4cf8a6d57 100644 --- a/frontend/src/Settings/Notifications/Notifications/AddNotificationItem.js +++ b/frontend/src/Settings/Notifications/Notifications/AddNotificationItem.js @@ -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')} @@ -65,7 +66,7 @@ class AddNotificationItem extends Component { className={styles.presetsMenuButton} size={sizes.SMALL} > - Presets + {translate('Presets')} @@ -90,7 +91,7 @@ class AddNotificationItem extends Component { to={infoLink} size={sizes.SMALL} > - More Info + {translate('MoreInfo')}
diff --git a/frontend/src/Settings/Notifications/Notifications/AddNotificationModalContent.js b/frontend/src/Settings/Notifications/Notifications/AddNotificationModalContent.js index ae1d5de7d..ff035e710 100644 --- a/frontend/src/Settings/Notifications/Notifications/AddNotificationModalContent.js +++ b/frontend/src/Settings/Notifications/Notifications/AddNotificationModalContent.js @@ -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 ( - Add Notification + {translate('AddNotification')} @@ -38,7 +39,9 @@ class AddNotificationModalContent extends Component { { !isSchemaFetching && !!schemaError && -
Unable to add a new notification, please try again.
+
+ {translate('AddNotificationError')} +
} { @@ -65,7 +68,7 @@ class AddNotificationModalContent extends Component {
diff --git a/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js b/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js index cfef4dc21..2e4b6afd5 100644 --- a/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js +++ b/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js @@ -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 ( - {`${id ? 'Edit' : 'Add'} Connection - ${implementationName}`} + {`${id ? translate('EditConnection') : translate('AddConnection')} - ${implementationName}`} @@ -59,7 +60,7 @@ function EditNotificationModalContent(props) { { !isFetching && !!error &&
- Unable to add a new notification, please try again. + {translate('AddNotificationError')}
} @@ -77,7 +78,7 @@ function EditNotificationModalContent(props) { } - Name + {translate('Name')} - Tags + {translate('Tags')} @@ -131,7 +132,7 @@ function EditNotificationModalContent(props) { kind={kinds.DANGER} onPress={onDeleteNotificationPress} > - Delete + {translate('Delete')} } @@ -140,13 +141,13 @@ function EditNotificationModalContent(props) { error={saveError} onPress={onTestPress} > - Test + {translate('Test')} - Save + {translate('Save')}
diff --git a/frontend/src/Settings/Notifications/Notifications/Notification.js b/frontend/src/Settings/Notifications/Notifications/Notification.js index 3b43c2b39..8fb4230e0 100644 --- a/frontend/src/Settings/Notifications/Notifications/Notification.js +++ b/frontend/src/Settings/Notifications/Notifications/Notification.js @@ -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 ? : null } @@ -104,7 +105,7 @@ class Notification extends Component { { supportsOnDownload && onDownload ? : null } @@ -112,7 +113,7 @@ class Notification extends Component { { supportsOnUpgrade && onDownload && onUpgrade ? : null } @@ -120,7 +121,7 @@ class Notification extends Component { { supportsOnRename && onRename ? : null } @@ -128,7 +129,7 @@ class Notification extends Component { { supportsOnHealthIssue && onHealthIssue ? : null } @@ -136,7 +137,7 @@ class Notification extends Component { { supportsOnHealthRestored && onHealthRestored ? : null } @@ -144,7 +145,7 @@ class Notification extends Component { { supportsOnApplicationUpdate && onApplicationUpdate ? : null } @@ -152,7 +153,7 @@ class Notification extends Component { { supportsOnSeriesAdd && onSeriesAdd ? : null } @@ -160,7 +161,7 @@ class Notification extends Component { { supportsOnSeriesDelete && onSeriesDelete ? : null } @@ -168,7 +169,7 @@ class Notification extends Component { { supportsOnEpisodeFileDelete && onEpisodeFileDelete ? : null } @@ -176,7 +177,7 @@ class Notification extends Component { { supportsOnEpisodeFileDeleteForUpgrade && onEpisodeFileDelete && onEpisodeFileDeleteForUpgrade ? : null } @@ -184,7 +185,7 @@ class Notification extends Component { { supportsOnManualInteractionRequired && onManualInteractionRequired ? : null } @@ -195,7 +196,7 @@ class Notification extends Component { kind={kinds.DISABLED} outline={true} > - Disabled + {translate('Disabled')} : null } @@ -215,9 +216,9 @@ class Notification extends Component { diff --git a/frontend/src/Settings/Notifications/Notifications/NotificationEventItems.js b/frontend/src/Settings/Notifications/Notifications/NotificationEventItems.js index 994613039..4e5958250 100644 --- a/frontend/src/Settings/Notifications/Notifications/NotificationEventItems.js +++ b/frontend/src/Settings/Notifications/Notifications/NotificationEventItems.js @@ -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 ( - Notification Triggers + {translate('NotificationTriggers')}
@@ -54,7 +55,7 @@ function NotificationEventItems(props) { +
diff --git a/frontend/src/Settings/PendingChangesModal.js b/frontend/src/Settings/PendingChangesModal.js index 478c7cafa..4cb83e8f6 100644 --- a/frontend/src/Settings/PendingChangesModal.js +++ b/frontend/src/Settings/PendingChangesModal.js @@ -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} > - Unsaved Changes + {translate('UnsavedChanges')} - You have unsaved changes, are you sure you want to leave this page? + {translate('PendingChangesMessage')} @@ -38,7 +39,7 @@ function PendingChangesModal(props) { kind={kinds.DEFAULT} onPress={onCancel} > - Stay and review changes + {translate('PendingChangesStayReview')} diff --git a/frontend/src/Settings/Profiles/Delay/DelayProfile.js b/frontend/src/Settings/Profiles/Delay/DelayProfile.js index c7b318e16..24ed6a29a 100644 --- a/frontend/src/Settings/Profiles/Delay/DelayProfile.js +++ b/frontend/src/Settings/Profiles/Delay/DelayProfile.js @@ -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 { diff --git a/frontend/src/Settings/Profiles/Delay/DelayProfiles.js b/frontend/src/Settings/Profiles/Delay/DelayProfiles.js index f602bbf9b..1a872c2fd 100644 --- a/frontend/src/Settings/Profiles/Delay/DelayProfiles.js +++ b/frontend/src/Settings/Profiles/Delay/DelayProfiles.js @@ -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 ( -
+
-
Preferred Protocol
-
Usenet Delay
-
Torrent Delay
-
Tags
+
+ {translate('PreferredProtocol')} +
+
+ {translate('UsenetDelay')} +
+
+ {translate('TorrentDelay')} +
+
+ {translate('Tags')} +
diff --git a/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js b/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js index 7a83b7311..f99f42038 100644 --- a/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js +++ b/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js @@ -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 ( - {id ? 'Edit Delay Profile' : 'Add Delay Profile'} + {id ? translate('EditDelayProfile') : translate('AddDelayProfile')} @@ -66,7 +87,9 @@ function EditDelayProfileModalContent(props) { { !isFetching && !!error ? -
Unable to add a new quality profile, please try again.
: +
+ {translate('AddQualityProfileError')} +
: null } @@ -74,14 +97,14 @@ function EditDelayProfileModalContent(props) { !isFetching && !error ? - Preferred Protocol + {translate('PreferredProtocol')} @@ -89,14 +112,14 @@ function EditDelayProfileModalContent(props) { { enableUsenet.value ? - Usenet Delay + {translate('UsenetDelay')} : @@ -106,14 +129,14 @@ function EditDelayProfileModalContent(props) { { enableTorrent.value ? - Torrent Delay + {translate('TorrentDelay')} : @@ -121,25 +144,25 @@ function EditDelayProfileModalContent(props) { } - Bypass if Highest Quality + {translate('BypassDelayIfHighestQuality')} - Bypass if Above Custom Format Score + {translate('BypassDelayIfAboveCustomFormatScore')} @@ -147,13 +170,13 @@ function EditDelayProfileModalContent(props) { { bypassIfAboveCustomFormatScore.value ? - Minimum Custom Format Score + {translate('BypassDelayIfAboveCustomFormatScoreMinimumScore')} : @@ -163,17 +186,17 @@ function EditDelayProfileModalContent(props) { { id === 1 ? - This is the default profile. It applies to all series that don't have an explicit profile. + {translate('DefaultDelayProfile')} : - Tags + {translate('Tags')} @@ -190,7 +213,7 @@ function EditDelayProfileModalContent(props) { kind={kinds.DANGER} onPress={onDeleteDelayProfilePress} > - Delete + {translate('Delete')} : null } @@ -198,7 +221,7 @@ function EditDelayProfileModalContent(props) { - Save + {translate('Save')}
diff --git a/frontend/src/Settings/Profiles/Profiles.js b/frontend/src/Settings/Profiles/Profiles.js index 642466aaa..330591ed6 100644 --- a/frontend/src/Settings/Profiles/Profiles.js +++ b/frontend/src/Settings/Profiles/Profiles.js @@ -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 ( - + diff --git a/frontend/src/Settings/Profiles/Quality/EditQualityProfileModalContent.js b/frontend/src/Settings/Profiles/Quality/EditQualityProfileModalContent.js index aaaaa9689..486aeec2f 100644 --- a/frontend/src/Settings/Profiles/Quality/EditQualityProfileModalContent.js +++ b/frontend/src/Settings/Profiles/Quality/EditQualityProfileModalContent.js @@ -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} > - {id ? 'Edit Quality Profile' : 'Add Quality Profile'} + {id ? translate('EditQualityProfile') : translate('AddQualityProfile')} @@ -151,7 +152,9 @@ class EditQualityProfileModalContent extends Component { { !isFetching && !!error && -
Unable to add a new quality profile, please try again.
+
+ {translate('AddQualityProfileError')} +
} { @@ -163,7 +166,7 @@ class EditQualityProfileModalContent extends Component {
- Name + {translate('Name')} - Upgrades Allowed + {translate('UpgradesAllowed')} @@ -192,7 +195,7 @@ class EditQualityProfileModalContent extends Component { upgradeAllowed.value && - Upgrade Until + {translate('UpgradeUntil')} @@ -210,14 +213,14 @@ class EditQualityProfileModalContent extends Component { formatItems.value.length > 0 && - Minimum Custom Format Score + {translate('MinimumCustomFormatScore')} @@ -227,14 +230,14 @@ class EditQualityProfileModalContent extends Component { upgradeAllowed.value && formatItems.value.length > 0 && - Upgrade Until Custom Format Score + {translate('UpgradeUntilCustomFormatScore')} @@ -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')}
: null @@ -296,7 +299,7 @@ class EditQualityProfileModalContent extends Component { - Save + {translate('Save')} diff --git a/frontend/src/Settings/Profiles/Quality/QualityProfile.js b/frontend/src/Settings/Profiles/Quality/QualityProfile.js index f10a5e1f0..55f0dbe75 100644 --- a/frontend/src/Settings/Profiles/Quality/QualityProfile.js +++ b/frontend/src/Settings/Profiles/Quality/QualityProfile.js @@ -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 { @@ -104,7 +105,7 @@ class QualityProfile extends Component { @@ -120,7 +121,7 @@ class QualityProfile extends Component { anchor={ @@ -133,7 +134,7 @@ class QualityProfile extends Component { @@ -160,9 +161,9 @@ class QualityProfile extends Component { - {'Want more control over which downloads are preferred? Add a'} - Custom Format -
+ ); } return ( - Custom Formats + {translate('CustomFormats')}
{ @@ -113,10 +111,10 @@ class QualityProfileFormatItems extends Component {
- Custom Format + {translate('CustomFormat')}
- Score + {translate('Score')}
{ diff --git a/frontend/src/Settings/Profiles/Quality/QualityProfileItem.js b/frontend/src/Settings/Profiles/Quality/QualityProfileItem.js index 2cecf8d05..5550464cb 100644 --- a/frontend/src/Settings/Profiles/Quality/QualityProfileItem.js +++ b/frontend/src/Settings/Profiles/Quality/QualityProfileItem.js @@ -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 { } @@ -95,7 +96,7 @@ class QualityProfileItem extends Component {
diff --git a/frontend/src/Settings/Profiles/Quality/QualityProfileItemGroup.js b/frontend/src/Settings/Profiles/Quality/QualityProfileItemGroup.js index 7bfc863af..499b62016 100644 --- a/frontend/src/Settings/Profiles/Quality/QualityProfileItemGroup.js +++ b/frontend/src/Settings/Profiles/Quality/QualityProfileItemGroup.js @@ -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 { @@ -133,7 +134,7 @@ class QualityProfileItemGroup extends Component {
) diff --git a/frontend/src/Settings/Profiles/Quality/QualityProfileItems.js b/frontend/src/Settings/Profiles/Quality/QualityProfileItems.js index 5526e479f..5166894da 100644 --- a/frontend/src/Settings/Profiles/Quality/QualityProfileItems.js +++ b/frontend/src/Settings/Profiles/Quality/QualityProfileItems.js @@ -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 ( - Qualities + {translate('Qualities')}
{ @@ -115,7 +116,7 @@ class QualityProfileItems extends Component { /> { - editGroups ? 'Done Editing Groups' : 'Edit Groups' + editGroups ? translate('DoneEditingGroups') : translate('EditGroups') }
diff --git a/frontend/src/Settings/Profiles/Quality/QualityProfiles.js b/frontend/src/Settings/Profiles/Quality/QualityProfiles.js index c7b87fb60..26740b468 100644 --- a/frontend/src/Settings/Profiles/Quality/QualityProfiles.js +++ b/frontend/src/Settings/Profiles/Quality/QualityProfiles.js @@ -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 ( -
+
diff --git a/frontend/src/Settings/Profiles/Release/EditReleaseProfileModalContent.js b/frontend/src/Settings/Profiles/Release/EditReleaseProfileModalContent.js index e544077af..025c8e48e 100644 --- a/frontend/src/Settings/Profiles/Release/EditReleaseProfileModalContent.js +++ b/frontend/src/Settings/Profiles/Release/EditReleaseProfileModalContent.js @@ -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 ( - {id ? 'Edit Release Profile' : 'Add Release Profile'} + {id ? translate('EditReleaseProfile') : translate('AddReleaseProfile')} @@ -53,35 +54,35 @@ function EditReleaseProfileModalContent(props) { type={inputTypes.TEXT} name="name" {...name} - placeholder="Optional name" + placeholder={translate('OptionalName')} canEdit={true} onChange={onInputChange} /> - Enable Profile + {translate('EnableProfile')} - Must Contain + {translate('MustContain')} - Must Not Contain + {translate('MustNotContain')} - Indexer + {translate('Indexer')} - Tags + {translate('Tags')} @@ -140,14 +141,14 @@ function EditReleaseProfileModalContent(props) { kind={kinds.DANGER} onPress={onDeleteReleaseProfilePress} > - Delete + {translate('Delete')} } - Save + {translate('Save')} diff --git a/frontend/src/Settings/Profiles/Release/ReleaseProfile.js b/frontend/src/Settings/Profiles/Release/ReleaseProfile.js index 90116ffd9..8c277e8d9 100644 --- a/frontend/src/Settings/Profiles/Release/ReleaseProfile.js +++ b/frontend/src/Settings/Profiles/Release/ReleaseProfile.js @@ -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')} } @@ -172,9 +173,9 @@ class ReleaseProfile extends Component { diff --git a/frontend/src/Settings/Profiles/Release/ReleaseProfiles.js b/frontend/src/Settings/Profiles/Release/ReleaseProfiles.js index 3876924b6..4e1ebde25 100644 --- a/frontend/src/Settings/Profiles/Release/ReleaseProfiles.js +++ b/frontend/src/Settings/Profiles/Release/ReleaseProfiles.js @@ -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 ( -
+
diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinition.js b/frontend/src/Settings/Quality/Definition/QualityDefinition.js index 3fc196e09..eb49d4ca4 100644 --- a/frontend/src/Settings/Quality/Definition/QualityDefinition.js +++ b/frontend/src/Settings/Quality/Definition/QualityDefinition.js @@ -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 (
@@ -231,11 +232,11 @@ class QualityDefinition extends Component { anchor={ } - title="Minimum Limits" + title={translate('MinimumLimits')} body={ } position={tooltipPositions.BOTTOM} @@ -247,11 +248,11 @@ class QualityDefinition extends Component { anchor={ } - title="Preferred Size" + title={translate('PreferredSize')} body={ } position={tooltipPositions.BOTTOM} @@ -263,11 +264,11 @@ class QualityDefinition extends Component { anchor={ } - title="Maximum Limits" + title={translate('MaximumLimits')} body={ } position={tooltipPositions.BOTTOM} @@ -280,7 +281,7 @@ class QualityDefinition extends Component { advancedSettings &&
- Min + {translate('Min')}
- Preferred + {translate('Preferred')}
- Max + {translate('Max')} -
30 Minutes: {thirty}
-
45 Minutes: {fourtyFive}
-
60 Minutes: {sixty}
+
+ {translate('MinutesThirty', { thirty })} +
+
+ {translate('MinutesFortyFive', { fortyFive })} +
+
+ {translate('MinutesSixty', { sixty })} +
); } diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinitions.js b/frontend/src/Settings/Quality/Definition/QualityDefinitions.js index c99056b0f..d7e9ed40c 100644 --- a/frontend/src/Settings/Quality/Definition/QualityDefinitions.js +++ b/frontend/src/Settings/Quality/Definition/QualityDefinitions.js @@ -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 ( -
+
-
Quality
-
Title
-
Size Limit
+
+ {translate('Quality')} +
+
+ {translate('Title')} +
+
+ {translate('SizeLimit')} +
{ advancedSettings ?
- Megabytes Per Minute + {translate('MegabytesPerMinute')}
: null } @@ -53,7 +60,7 @@ class QualityDefinitions extends Component {
- Limits are automatically adjusted for the series runtime and number of episodes in the file. + {translate('QualityLimitsHelpText')}
diff --git a/frontend/src/Settings/Quality/Quality.js b/frontend/src/Settings/Quality/Quality.js index aa92684de..49c9df5d0 100644 --- a/frontend/src/Settings/Quality/Quality.js +++ b/frontend/src/Settings/Quality/Quality.js @@ -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 ( - + + @@ -17,143 +18,143 @@ function Settings() { className={styles.link} to="/settings/mediamanagement" > - Media Management + {translate('MediaManagement')}
- Naming, file management settings and root folders + {translate('MediaManagementSettingsSummary')}
- Profiles + {translate('Profiles')}
- Quality, Language, Delay and Release profiles + {translate('ProfilesSettingsSummary')}
- Quality + {translate('Quality')}
- Quality sizes and naming + {translate('QualitySettingsSummary')}
- Custom Formats + {translate('CustomFormats')}
- Custom Formats and Settings + {translate('CustomFormatsSettingsSummary')}
- Indexers + {translate('Indexers')}
- Indexers and indexer options + {translate('IndexersSettingsSummary')}
- Download Clients + {translate('DownloadClients')}
- Download clients, download handling and remote path mappings + {translate('DownloadClientsSettingsSummary')}
- Import Lists + {translate('ImportLists')}
- Import from another Sonarr instance or Trakt lists and manage list exclusions + {translate('ImportListsSettingsSummary')}
- Connect + {translate('Connect')}
- Notifications, connections to media servers/players and custom scripts + {translate('ConnectSettingsSummary')}
- Metadata + {translate('Metadata')}
- Create metadata files when episodes are imported or series are refreshed + {translate('MetadataSettingsSummary')}
- Metadata Source + {translate('MetadataSource')}
- Information on where Sonarr gets Series and Episode information + {translate('MetadataSourceSettingsSummary')}
- Tags + {translate('Tags')}
- See all tags and how they are used. Unused tags can be removed + {translate('TagsSettingsSummary')}
- General + {translate('General')}
- Port, SSL, username/password, proxy, analytics and updates + {translate('GeneralSettingsSummary')}
- UI + {translate('Ui')}
- Calendar, date and color impaired options + {translate('UiSettingsSummary')}
diff --git a/frontend/src/Settings/SettingsToolbar.js b/frontend/src/Settings/SettingsToolbar.js index 80ac84563..048dcc66e 100644 --- a/frontend/src/Settings/SettingsToolbar.js +++ b/frontend/src/Settings/SettingsToolbar.js @@ -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 && - {'Unable to add a new auto tag, please try again.'} + {translate('AddAutoTagError')}
: null } diff --git a/frontend/src/Settings/Tags/AutoTagging/Specifications/AddSpecificationModalContent.js b/frontend/src/Settings/Tags/AutoTagging/Specifications/AddSpecificationModalContent.js index 9b6553cbc..454a2591a 100644 --- a/frontend/src/Settings/Tags/AutoTagging/Specifications/AddSpecificationModalContent.js +++ b/frontend/src/Settings/Tags/AutoTagging/Specifications/AddSpecificationModalContent.js @@ -56,7 +56,7 @@ export default function AddSpecificationModalContent(props) { { !isSchemaFetching && !!schemaError ?
- {'Unable to add a new condition, please try again.'} + {translate('AddConditionError')}
: null } @@ -67,7 +67,7 @@ export default function AddSpecificationModalContent(props) {
- {'Sonarr supports the follow properties for auto tagging rules'} + {translate('SupportedAutoTaggingProperties')}
diff --git a/frontend/src/Settings/Tags/AutoTagging/Specifications/EditSpecificationModalContent.js b/frontend/src/Settings/Tags/AutoTagging/Specifications/EditSpecificationModalContent.js index 292468292..872110daf 100644 --- a/frontend/src/Settings/Tags/AutoTagging/Specifications/EditSpecificationModalContent.js +++ b/frontend/src/Settings/Tags/AutoTagging/Specifications/EditSpecificationModalContent.js @@ -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') &&
-
\\^$.|?*+()[{ have special meanings and need escaping with a \\' }} /> - {'More details'} {'Here'} +
- {'Regular expressions can be tested '} - Here + +
+
+
} @@ -130,7 +131,7 @@ function EditSpecificationModalContent(props) { type={inputTypes.CHECK} name="negate" {...negate} - helpText={translate('AutoTaggingNegateHelpText', { name: implementationName })} + helpText={translate('AutoTaggingNegateHelpText', { implementationName })} onChange={onInputChange} /> @@ -144,7 +145,7 @@ function EditSpecificationModalContent(props) { type={inputTypes.CHECK} name="required" {...required} - helpText={translate('AutoTaggingRequiredHelpText', { name: implementationName })} + helpText={translate('AutoTaggingRequiredHelpText', { implementationName })} onChange={onInputChange} /> diff --git a/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.js b/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.js index b53bc74b6..21977e160 100644 --- a/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.js +++ b/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.js @@ -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) { @@ -74,7 +75,7 @@ export default function Specification(props) { { negate ? : null } @@ -82,7 +83,7 @@ export default function Specification(props) { { required ? : null } @@ -98,9 +99,9 @@ export default function Specification(props) { diff --git a/frontend/src/Settings/Tags/Details/TagDetailsDelayProfile.js b/frontend/src/Settings/Tags/Details/TagDetailsDelayProfile.js index ab670359b..d3482e94f 100644 --- a/frontend/src/Settings/Tags/Details/TagDetailsDelayProfile.js +++ b/frontend/src/Settings/Tags/Details/TagDetailsDelayProfile.js @@ -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 (
- Protocol: {titleCase(preferredProtocol)} + {titleCase(translate('DelayProfileProtocol', { preferredProtocol }))}
{ enableUsenet ? - `Usenet Delay: ${usenetDelay}` : - 'Usenet disabled' + translate('UsenetDelayTime', { usenetDelay }) : + translate('UsenetDisabled') }
{ enableTorrent ? - `Torrent Delay: ${torrentDelay}` : - 'Torrents disabled' + translate('TorrentDelayTime', { torrentDelay }) : + translate('TorrentsDisabled') }
diff --git a/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js b/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js index af05ce3de..41a14807c 100644 --- a/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js +++ b/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js @@ -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 ( - Tag Details - {label} + {translate('TagDetails', { label })} { !isTagUsed && -
Tag is not used and can be deleted
+
+ {translate('TagIsNotUsedAndCanBeDeleted')} +
} { series.length ? -
+
{ series.map((item) => { return ( @@ -57,7 +60,7 @@ function TagDetailsModalContent(props) { { delayProfiles.length ? -
+
{ delayProfiles.map((item) => { const { @@ -87,7 +90,7 @@ function TagDetailsModalContent(props) { { notifications.length ? -
+
{ notifications.map((item) => { return ( @@ -103,7 +106,7 @@ function TagDetailsModalContent(props) { { importLists.length ? -
+
{ importLists.map((item) => { return ( @@ -119,7 +122,7 @@ function TagDetailsModalContent(props) { { releaseProfiles.length ? -
+
{ releaseProfiles.map((item) => { return ( @@ -166,7 +169,7 @@ function TagDetailsModalContent(props) { { indexers.length ? -
+
{ indexers.map((item) => { return ( @@ -182,7 +185,7 @@ function TagDetailsModalContent(props) { { downloadClients.length ? -
+
{ downloadClients.map((item) => { return ( @@ -198,7 +201,7 @@ function TagDetailsModalContent(props) { { autoTags.length ? -
+
{ autoTags.map((item) => { return ( @@ -218,18 +221,18 @@ function TagDetailsModalContent(props) { } diff --git a/frontend/src/Settings/Tags/Tag.js b/frontend/src/Settings/Tags/Tag.js index 9764f6f05..e7715364c 100644 --- a/frontend/src/Settings/Tags/Tag.js +++ b/frontend/src/Settings/Tags/Tag.js @@ -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 ?
: null @@ -140,7 +145,7 @@ class Tag extends Component { { !isTagUsed &&
- No links + {translate('NoLinks')}
} @@ -163,9 +168,9 @@ class Tag extends Component { diff --git a/frontend/src/Settings/Tags/TagInUse.js b/frontend/src/Settings/Tags/TagInUse.js index cc0b968b5..9fb57d230 100644 --- a/frontend/src/Settings/Tags/TagInUse.js +++ b/frontend/src/Settings/Tags/TagInUse.js @@ -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 (
- {count} {label}s + {count} {labelPlural.toLowerCase()}
); } return (
- {count} {label} + {count} {label.toLowerCase()}
); } TagInUse.propTypes = { label: PropTypes.string.isRequired, - count: PropTypes.number.isRequired, - shouldPluralize: PropTypes.bool + labelPlural: PropTypes.string, + count: PropTypes.number.isRequired }; diff --git a/frontend/src/Settings/Tags/TagSettings.js b/frontend/src/Settings/Tags/TagSettings.js index 1aa555e67..ca8672603 100644 --- a/frontend/src/Settings/Tags/TagSettings.js +++ b/frontend/src/Settings/Tags/TagSettings.js @@ -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 ( - + diff --git a/frontend/src/Settings/Tags/Tags.js b/frontend/src/Settings/Tags/Tags.js index 04d8e91e5..8f5a9918a 100644 --- a/frontend/src/Settings/Tags/Tags.js +++ b/frontend/src/Settings/Tags/Tags.js @@ -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 ( - No tags have been added yet + {translate('NoTagsHaveBeenAddedYet')} ); } return (
diff --git a/frontend/src/Settings/UI/UISettings.js b/frontend/src/Settings/UI/UISettings.js index e9d4ce400..a9365c49a 100644 --- a/frontend/src/Settings/UI/UISettings.js +++ b/frontend/src/Settings/UI/UISettings.js @@ -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 ( - + Unable to load UI settings : + + {translate('UiSettingsLoadError')} + : null } @@ -92,9 +104,9 @@ class UISettings extends Component { id="uiSettings" {...otherProps} > -
+
- First Day of Week + {translate('FirstDayOfWeek')} - Week Column Header + {translate('WeekColumnHeader')}
- Short Date Format + {translate('ShortDateFormat')} - Long Date Format + {translate('LongDateFormat')} - Time Format + {translate('TimeFormat')} - Show Relative Dates + {translate('ShowRelativeDates')} @@ -171,14 +183,14 @@ class UISettings extends Component {
- Theme + {translate('Theme')} - Enable Color-Impaired Mode + {translate('EnableColorImpairedMode')} @@ -199,13 +211,13 @@ class UISettings extends Component {
- {translate('UI Language')} + {translate('UiLanguage')} diff --git a/frontend/src/System/Backup/Backups.js b/frontend/src/System/Backup/Backups.js index 8f7a5b0a5..ede2f97f6 100644 --- a/frontend/src/System/Backup/Backups.js +++ b/frontend/src/System/Backup/Backups.js @@ -109,7 +109,7 @@ class Backups extends Component { { !isFetching && !!error && - {translate('UnableToLoadBackups')} + {translate('BackupsLoadError')} } diff --git a/frontend/src/System/Updates/Updates.js b/frontend/src/System/Updates/Updates.js index bded2676d..e40276181 100644 --- a/frontend/src/System/Updates/Updates.js +++ b/frontend/src/System/Updates/Updates.js @@ -44,7 +44,7 @@ class Updates extends Component { const hasUpdateToInstall = hasUpdates && _.some(items, { installable: true, latest: true }); const noUpdateToInstall = hasUpdates && !hasUpdateToInstall; - const externalUpdaterPrefix = translate('UnableToUpdateSonarrDirectly'); + const externalUpdaterPrefix = translate('UpdateSonarrDirectlyLoadError'); const externalUpdaterMessages = { external: translate('ExternalUpdater'), apt: translate('AptUpdater'), diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 8e9a1030b..a11957c4b 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -1,23 +1,59 @@ { "About": "About", + "AbsoluteEpisodeNumber": "Absolute Episode Number", "AbsoluteEpisodeNumbers": "Absolute Episode Number(s)", "Actions": "Actions", "Activity": "Activity", "Add": "Add", "AddAutoTag": "Add Auto Tag", + "AddAutoTagError": "Unable to add a new auto tag, please try again.", "AddCondition": "Add Condition", + "AddConditionError": "Unable to add a new condition, please try again.", + "AddConnection": "Add Connection", + "AddCustomFormat": "Add Custom Format", + "AddCustomFormatError": "Unable to add a new custom format, please try again.", + "AddDelayProfile": "Add Delay Profile", + "AddDownloadClient": "Add Download Client", + "AddDownloadClientError": "Unable to add a new download client, please try again.", + "AddExclusion": "Add Exclusion", + "AddImportList": "Add Import List", + "AddImportListExclusion": "Add Import List Exclusion", + "AddImportListExclusionError": "Unable to add a new import list exclusion, please try again.", + "AddIndexer": "Add Indexer", + "AddIndexerError": "Unable to add a new indexer, please try again.", + "AddList": "Add List", + "AddListError": "Unable to add a new list, please try again.", + "AddListExclusionError": "Unable to add a new list exclusion, please try again.", "AddNew": "Add New", + "AddNewRestriction": "Add new restriction", + "AddNotification": "Add Notification", + "AddNotificationError": "Unable to add a new notification, please try again.", + "AddQualityProfile": "Add Quality Profile", + "AddQualityProfileError": "Unable to add a new quality profile, please try again.", + "AddReleaseProfile": "Add Release Profile", + "AddRemotePathMapping": "Add Remote Path Mapping", + "AddRemotePathMappingError": "Unable to add a new remote path mapping, please try again.", "AddRootFolder": "Add Root Folder", "Added": "Added", "AddingTag": "Adding tag", + "AfterManualRefresh": "After Manual Refresh", "Age": "Age", "AirDate": "Air Date", "All": "All", "AllResultsAreHiddenByTheAppliedFilter": "All results are hidden by the applied filter", "AllTitles": "All Titles", + "Always": "Always", + "AnalyseVideoFiles": "Analyse video files", + "AnalyseVideoFilesHelpText": "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.", + "Analytics": "Analytics", + "AnalyticsEnabledHelpText": "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.", + "AnimeEpisodeFormat": "Anime Episode Format", + "ApiKey": "API Key", "ApiKeyValidationHealthCheckMessage": "Please update your API key to be at least {0} characters long. You can do this via settings or the config file", "AppDataDirectory": "AppData directory", "AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update", + "ApplicationURL": "Application URL", + "ApplicationUrlHelpText": "This application's external URL including http(s)://, port and URL base", "Apply": "Apply", "ApplyChanges": "Apply Changes", "ApplyTags": "Apply Tags", @@ -31,52 +67,152 @@ "AptUpdater": "Use apt to install the update", "AudioInfo": "Audio Info", "AudioLanguages": "Audio Languages", + "AuthBasic": "Basic (Browser Popup)", + "AuthForm": "Forms (Login Page)", + "Authentication": "Authentication", + "AuthenticationMethodHelpText": "Require Username and Password to access Sonarr", + "AuthenticationRequired": "Authentication Required", + "AuthenticationRequiredHelpText": "Change which requests authentication is required for. Do not change unless you understand the risks.", + "AuthenticationRequiredWarning": "To prevent remote access without authentication, Sonarr now requires authentication to be enabled. You can optionally disable authentication from local addresses.", "AutoAdd": "Auto Add", + "AutoRedownloadFailedHelpText": "Automatically search for and attempt to download a different release", "AutoTagging": "Auto Tagging", - "AutoTaggingNegateHelpText": "If checked, the auto tagging rule will not apply if this {name} condition matches.", - "AutoTaggingRequiredHelpText": "This {name} condition must match for the auto tagging rule to apply. Otherwise a single {name} match is sufficient.", + "AutoTaggingLoadError": "Unable to load auto tagging", + "AutoTaggingNegateHelpText": "If checked, the auto tagging rule will not apply if this {implementationName} condition matches.", + "AutoTaggingRequiredHelpText": "This {implementationName} condition must match for the auto tagging rule to apply. Otherwise a single {implementationName} match is sufficient.", + "Automatic": "Automatic", "AutomaticAdd": "Automatic Add", + "AutomaticSearch": "Automatic Search", "Backup": "Backup", + "BackupFolderHelpText": "Relative paths will be under Sonarr's AppData directory", + "BackupIntervalHelpText": "Interval between automatic backups", "BackupNow": "Backup Now", + "BackupRetentionHelpText": "Automatic backups older than the retention period will be cleaned up automatically", "Backups": "Backups", + "BackupsLoadError": "Unable to load backups", "BeforeUpdate": "Before update", + "BindAddress": "Bind Address", + "BindAddressHelpText": "Valid IP address, localhost or '*' for all interfaces", "Blocklist": "Blocklist", + "BlocklistLoadError": "Unable to load blocklist", "BlocklistRelease": "Blocklist Release", "BlocklistReleaseHelpText": "Prevents Sonarr from automatically grabbing this release again", "BlocklistReleases": "Blocklist Releases", - "Browser Reload Required": "Browser Reload Required", + "Branch": "Branch", + "BranchUpdate": "Branch to use to update Sonarr", + "BranchUpdateMechanism": "Branch used by external update mechanism", + "BrowserReloadRequired": "Browser Reload Required", + "BuiltIn": "Built-In", + "BypassDelayIfAboveCustomFormatScore": "Bypass if Above Custom Format Score", + "BypassDelayIfAboveCustomFormatScoreHelpText": "Enable bypass when release has a score higher than the configured minimum custom format score", + "BypassDelayIfAboveCustomFormatScoreMinimumScore": "Minimum Custom Format Score", + "BypassDelayIfAboveCustomFormatScoreMinimumScoreHelpText": "Minimum Custom Format Score required to bypass delay for the preferred protocol", + "BypassDelayIfHighestQuality": "Bypass if Highest Quality", + "BypassDelayIfHighestQualityHelpText": "Bypass delay when release has the highest enabled quality in the quality profile with the preferred protocol", + "BypassProxyForLocalAddresses": "Bypass Proxy for Local Addresses", "Calendar": "Calendar", + "CalendarLoadError": "Unable to load the calendar", "Cancel": "Cancel", "CancelPendingTask": "Are you sure you want to cancel this pending task?", + "CertificateValidation": "Certificate Validation", + "CertificateValidationHelpText": "Change how strict HTTPS certification validation is. Do not change unless you understand the risks.", "Certification": "Certification", + "ChangeFileDate": "Change File Date", + "ChangeFileDateHelpText": "Change file date on import/rescan", + "ChmodFolder": "chmod Folder", + "ChmodFolderHelpText": "Octal, applied during import/rename to media folders and files (without execute bits)", + "ChmodFolderHelpTextWarning": "This only works if the user running sonarr is the owner of the file. It's better to ensure the download client sets the permissions properly.", + "ChownGroup": "chown Group", + "ChownGroupHelpText": "Group name or gid. Use gid for remote file systems.", + "ChownGroupHelpTextWarning": "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.", "Clear": "Clear", + "ClientPriority": "Client Priority", + "Clone": "Clone", "CloneAutoTag": "Clone Auto Tag", "CloneCondition": "Clone Condition", "CloneCustomFormat": "Clone Custom Format", + "CloneIndexer": "Clone Indexer", + "CloneProfile": "Clone Profile", "Close": "Close", + "CollectionsLoadError": "Unable to load collections", + "ColonReplacement": "Colon Replacement", + "ColonReplacementFormatHelpText": "Change how Sonarr handles colon replacement", + "CompletedDownloadHandling": "Completed Download Handling", "Component": "Component", + "Condition": "Condition", + "ConditionUsingRegularExpressions": "This condition matches using Regular Expressions. Note that the characters `\\^$.|?*+()[{` have special meanings and need escaping with a `\\`", "Conditions": "Conditions", "Connect": "Connect", + "ConnectSettings": "Connect Settings", + "ConnectSettingsSummary": "Notifications, connections to media servers/players, and custom scripts", + "Connections": "Connections", "ContinuingOnly": "Continuing Only", + "CopyToClipboard": "Copy to Clipboard", + "CopyUsingHardlinksHelpText": "Hardlinks allow Sonarr to import seeding torrents to 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", + "CopyUsingHardlinksHelpTextWarning": "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.", "CountDownloadClientsSelected": "{count} download client(s) selected", "CountImportListsSelected": "{count} import list(s) selected", "CountIndexersSelected": "{count} indexer(s) selected", "CountSeasons": "{count} seasons", + "CreateEmptySeriesFolders": "Create Empty Series Folders", + "CreateEmptySeriesFoldersHelpText": "Create missing series folders during disk scan", + "CreateGroup": "Create Group", "CurrentlyInstalled": "Currently Installed", + "Custom": "Custom", + "CustomFormat": "Custom Format", + "CustomFormatHelpText": "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.", "CustomFormatScore": "Custom Format Score", + "CustomFormatUnknownCondition": "Unknown Custom Format condition '{implementation}'", + "CustomFormatUnknownConditionOption": "Unknown option '{key}' for condition '{implementation}'", "CustomFormats": "Custom Formats", + "CustomFormatsLoadError": "Unable to load Custom Formats", + "CustomFormatsSettings": "Custom Formats Settings", + "CustomFormatsSettingsSummary": "Custom Formats and Settings", + "Cutoff": "Cutoff", "CutoffUnmet": "Cutoff Unmet", "Daily": "Daily", + "DailyEpisodeFormat": "Daily Episode Format", + "Dash": "Dash", "Date": "Date", + "Dates": "Dates", + "Debug": "Debug", + "DefaultCase": "Default Case", + "DefaultDelayProfile": "This is the default profile. It applies to all series that don't have an explicit profile.", + "DelayMinutes": "{delay} Minutes", + "DelayProfile": "Delay Profile", + "DelayProfileProtocol": "Protocol: {preferredProtocol}", + "DelayProfileTagsHelpText": "Applies to series with at least one matching tag", + "DelayProfiles": "Delay Profiles", + "DelayProfilesLoadError": "Unable to load Delay Profiles", "Delete": "Delete", "DeleteAutoTag": "Delete Auto Tag", "DeleteAutoTagHelpText": "Are you sure you want to delete the auto tag '{name}'?", "DeleteBackup": "Delete Backup", "DeleteBackupMessageText": "Are you sure you want to delete the backup '{name}'?", "DeleteCondition": "Delete Condition", - "DeleteConditionMessageText": "Are you sure you want to delete the condition '{0}'?", + "DeleteConditionMessageText": "Are you sure you want to delete the condition '{name}'?", "DeleteCustomFormat": "Delete Custom Format", "DeleteCustomFormatMessageText": "Are you sure you want to delete the custom format '{0}'?", + "DeleteDelayProfile": "Delete Delay Profile", + "DeleteDelayProfileMessageText": "Are you sure you want to delete this delay profile?", + "DeleteDownloadClient": "Delete Download Client", + "DeleteDownloadClientMessageText": "Are you sure you want to delete the download client '{name}'?", + "DeleteEmptyFolders": "Delete Empty Folders", + "DeleteEmptyFoldersHelpText": "Delete empty series and season folders during disk scan and when episode files are deleted", + "DeleteImportList": "Delete Import List", + "DeleteImportListExclusion": "Delete Import List Exclusion", + "DeleteImportListExclusionMessageText": "Are you sure you want to delete this import list exclusion?", + "DeleteImportListMessageText": "Are you sure you want to delete the list {name}", + "DeleteIndexer": "Delete Indexer", + "DeleteIndexerMessageText": "Delete Indexer", + "DeleteNotification": "Delete Notification", + "DeleteNotificationMessageText": "Are you sure you want to delete the notification {name}", + "DeleteQualityProfile": "Delete Quality Profile", + "DeleteQualityProfileMessageText": "Are you sure you want to delete the quality profile {name}", + "DeleteReleaseProfile": "Delete Release Profile", + "DeleteReleaseProfileMessageText": "Are you sure you want to delete this release profile {name}", + "DeleteRemotePathMapping": "Delete Remote Path Mapping", + "DeleteRemotePathMappingMessageText": "Are you sure you want to delete this remote path mapping?", "DeleteRootFolder": "Delete Root Folder", "DeleteRootFolderMessageText": "Are you sure you want to delete the root folder '{path}'?", "DeleteSelectedDownloadClients": "Delete Download Client(s)", @@ -85,35 +221,84 @@ "DeleteSelectedImportListsMessageText": "Are you sure you want to delete {count} selected import list(s)?", "DeleteSelectedIndexers": "Delete Indexer(s)", "DeleteSelectedIndexersMessageText": "Are you sure you want to delete {count} selected indexer(s)?", + "DeleteSpecification": "Delete Specification", + "DeleteSpecificationHelpText": "Are you sure you want to delete specification '{name}'?", + "DeleteTag": "DeleteTag", + "DeleteTagMessageText": "Are you sure you want to delete the tag '{label}'?", "Deleted": "Deleted", "Details": "Details", "Disabled": "Disabled", + "DisabledForLocalAddresses": "Disabled for Local Addresses", "Discord": "Discord", "DiskSpace": "Disk Space", + "DoNotPrefer": "Do not Prefer", + "DoNotUpgradeAutomatically": "Do not Upgrade Automatically", "Docker": "Docker", "DockerUpdater": "Update the docker container to receive the update", "Donations": "Donations", + "DoneEditingGroups": "Done Editing Groups", "DotNetVersion": ".NET", "Download": "Download", "DownloadClient": "Download Client", "DownloadClientCheckNoneAvailableHealthCheckMessage": "No download client is available", "DownloadClientCheckUnableToCommunicateWithHealthCheckMessage": "Unable to communicate with {0}.", + "DownloadClientOptionsLoadError": "Unable to load download client options", "DownloadClientRootFolderHealthCheckMessage": "Download client {0} places downloads in the root folder {1}. You should not download to a root folder.", + "DownloadClientSettings": "Download Client Settings", "DownloadClientSortingHealthCheckMessage": "Download client {0} has {1} sorting enabled for Sonarr's category. You should disable sorting in your download client to avoid import issues.", "DownloadClientStatusAllClientHealthCheckMessage": "All download clients are unavailable due to failures", "DownloadClientStatusSingleClientHealthCheckMessage": "Download clients unavailable due to failures: {0}", + "DownloadClientTagHelpText": "Only use this download client for series with at least one matching tag. Leave blank to use with all series.", "DownloadClients": "Download Clients", + "DownloadClientsLoadError": "Unable to load download clients", + "DownloadClientsSettingsSummary": "Download clients, download handling and remote path mappings", + "DownloadPropersAndRepacks": "Propers and Repacks", + "DownloadPropersAndRepacksHelpText": "Whether or not to automatically upgrade to Propers/Repacks", + "DownloadPropersAndRepacksHelpTextCustomFormat": "Use 'Do not Prefer' to sort by custom format score over Propers/Repacks", + "DownloadPropersAndRepacksHelpTextWarning": "Use custom formats for automatic upgrades to Propers/Repacks", + "Duplicate": "Duplicate", "Duration": "Duration", "Edit": "Edit", "EditAutoTag": "Edit Auto Tag", "EditCondition": "Edit Condition", + "EditConnection": "Edit Connection", + "EditCustomFormat": "Edit Custom Format", + "EditDelayProfile": "Edit Delay Profile", + "EditGroups": "Edit Groups", + "EditImportList": "Edit Import List", + "EditImportListExclusion": "Edit Import List Exclusion", + "EditIndexer": "Edit Indexer", + "EditListExclusion": "Edit List Exclusion", + "EditMetadata": "Edit {metadataType} Metadata", + "EditQualityProfile": "Edit Quality Profile", + "EditReleaseProfile": "Edit Release Profile", + "EditRemotePathMapping": "Edit Remote Path Mapping", + "EditRestriction": "Edit Restriction", "EditSelectedDownloadClients": "Edit Selected Download Clients", "EditSelectedImportLists": "Edit Selected Import Lists", "EditSelectedIndexers": "Edit Selected Indexers", "EditSeries": "Edit Series", + "Enable": "Enable", + "EnableAutomaticAdd": "Enable Automatic Add", + "EnableAutomaticAddHelpText": "Add series from this list to Sonarr when syncs are performed via the UI or by Sonarr", "EnableAutomaticSearch": "Enable Automatic Search", + "EnableAutomaticSearchHelpText": "Will be used when automatic searches are performed via the UI or by Sonarr", + "EnableAutomaticSearchHelpTextWarning": "Will be used when interactive search is used", + "EnableColorImpairedMode": "Enable Color-Impaired Mode", + "EnableColorImpairedModeHelpText": "Altered style to allow color-impaired users to better distinguish color coded information", + "EnableCompletedDownloadHandlingHelpText": "Automatically import completed downloads from download client", + "EnableHelpText": "Enable metadata file creation for this metadata type", "EnableInteractiveSearch": "Enable Interactive Search", - "EnableRSS": "Enable RSS", + "EnableInteractiveSearchHelpText": "Will be used when interactive search is used", + "EnableInteractiveSearchHelpTextWarning": "Search is not supported with this indexer", + "EnableMediaInfoHelpText": "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.", + "EnableMetadataHelpText": "Enable metadata file creation for this metadata type", + "EnableProfile": "Enable Profile", + "EnableProfileHelpText": "Check to enable release profile", + "EnableRss": "Enable RSS", + "EnableRssHelpText": "Will be used when Sonarr periodically looks for releases via RSS Sync", + "EnableSsl": "Enable SSL", + "EnableSslHelpText": "Requires restart running as administrator to take effect", "Enabled": "Enabled", "Ended": "Ended", "EndedOnly": "Ended Only", @@ -121,25 +306,39 @@ "EpisodeAirDate": "Episode Air Date", "EpisodeCount": "Episode Count", "EpisodeInfo": "Episode Info", + "EpisodeNaming": "Episode Naming", "EpisodeNumbers": "Episode Number(s)", "EpisodeProgress": "Episode Progress", + "EpisodeSearchResultsLoadError": "Unable to load results for this episode search. Try again later", "EpisodeTitle": "Episode Title", + "EpisodeTitleRequired": "Episode Title Required", + "EpisodeTitleRequiredHelpText": "Prevent importing for up to 48 hours if the episode title is in the naming format and the episode title is TBA", "Episodes": "Episodes", "Error": "Error", "ErrorRestoringBackup": "Error restoring backup", "EventType": "Event Type", "Events": "Events", + "Example": "Example", "Exception": "Exception", "ExistingTag": "Existing tag", "ExportCustomFormat": "Export Custom Format", + "Extend": "Extend", + "External": "External", "ExternalUpdater": "Sonarr is configured to use an external update mechanism", + "ExtraFileExtensionsHelpText": "Comma separated list of extra files to import (.nfo will be imported as .nfo-orig)", + "ExtraFileExtensionsHelpTextsExamples": "Examples: '.sub, .nfo' or 'sub,nfo'", "Failed": "Failed", "FailedToFetchUpdates": "Failed to fetch updates", "FailedToUpdateSettings": "Failed to update settings", "FeatureRequests": "Feature Requests", + "FileManagement": "File Management", + "FileNameTokens": "File Name Tokens", + "FileNames": "File Names", "Filename": "Filename", + "FirstDayOfWeek": "First Day of Week", "Fixed": "Fixed", "Folder": "Folder", + "Folders": "Folders", "Formats": "Formats", "Forums": "Forums", "FreeSpace": "Free Space", @@ -147,48 +346,89 @@ "FullSeason": "Full Season", "General": "General", "GeneralSettings": "General Settings", + "GeneralSettingsLoadError": "Unable to load General settings", + "GeneralSettingsSummary": "Port, SSL, username/password, proxy, analytics and updates", "Genres": "Genres", "Grabbed": "Grabbed", + "Group": "Group", "HasMissingSeason": "Has Missing Season", "Health": "Health", + "Here": "here", "HiddenClickToShow": "Hidden, click to show", "HideAdvanced": "Hide Advanced", "History": "History", + "HistoryLoadError": "Unable to load history", "HomePage": "Home Page", + "Host": "Host", + "Hostname": "Hostname", + "HourShorthand": "h", + "HttpHttps": "HTTP(S)", "IRC": "IRC", "IRCLinkText": "#sonarr on Libera", "Ignored": "Ignored", + "IgnoredAddresses": "Ignored Addresses", + "Images": "Images", "Implementation": "Implementation", + "Import": "Import", + "ImportCustomFormat": "Import Custom Format", + "ImportExtraFiles": "Import Extra Files", + "ImportExtraFilesHelpText": "Import matching extra files (subtitles, nfo, etc) after importing an episode file", + "ImportList": "Import List", + "ImportListExclusions": "Import List Exclusions", + "ImportListExclusionsLoadError": "Unable to load Import List Exclusions", "ImportListRootFolderMissingRootHealthCheckMessage": "Missing root folder for import list(s): {0}", "ImportListRootFolderMultipleMissingRootsHealthCheckMessage": "Multiple root folders are missing for import lists: {0}", + "ImportListSettings": "Import List Settings", "ImportListStatusAllUnavailableHealthCheckMessage": "All lists are unavailable due to failures", "ImportListStatusUnavailableHealthCheckMessage": "Lists unavailable due to failures: {0}", "ImportLists": "Import Lists", + "ImportListsLoadError": "Unable to load Import Lists", + "ImportListsSettingsSummary": "Import from another Sonarr instance or Trakt lists and manage list exclusions", "ImportMechanismEnableCompletedDownloadHandlingIfPossibleHealthCheckMessage": "Enable Completed Download Handling if possible", "ImportMechanismEnableCompletedDownloadHandlingIfPossibleMultiComputerHealthCheckMessage": "Enable Completed Download Handling if possible (Multi-Computer unsupported)", "ImportMechanismHandlingDisabledHealthCheckMessage": "Enable Completed Download Handling", + "ImportScriptPath": "Import Script Path", + "ImportScriptPathHelpText": "The path to the script to use for importing", + "ImportUsingScript": "Import Using Script", + "ImportUsingScriptHelpText": "Copy files for importing using a script (ex. for transcoding)", "Imported": "Imported", + "Importing": "Importing", + "IncludeCustomFormatWhenRenaming": "Include Custom Format when Renaming", + "IncludeCustomFormatWhenRenamingHelpText": "Include in {Custom Formats} renaming format", + "IncludeHealthWarnings": "Include Health Warnings", "IncludeUnmonitored": "Include Unmonitored", "Indexer": "Indexer", "IndexerDownloadClientHealthCheckMessage": "Indexers with invalid download clients: {0}.", + "IndexerDownloadClientHelpText": "Specify which download client is used for grabs from this indexer", "IndexerJackettAllHealthCheckMessage": "Indexers using the unsupported Jackett 'all' endpoint: {0}", "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "All indexers are unavailable due to failures for more than 6 hours", "IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexers unavailable due to failures for more than 6 hours: {0}", + "IndexerOptionsLoadError": "Unable to load indexer options", + "IndexerPriority": "Indexer Priority", + "IndexerPriorityHelpText": "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", "IndexerRssNoIndexersAvailableHealthCheckMessage": "All rss-capable indexers are temporarily unavailable due to recent indexer errors", "IndexerRssNoIndexersEnabledHealthCheckMessage": "No indexers available with RSS sync enabled, Sonarr will not grab new releases automatically", "IndexerSearchNoAutomaticHealthCheckMessage": "No indexers available with Automatic Search enabled, Sonarr will not provide any automatic search results", "IndexerSearchNoAvailableIndexersHealthCheckMessage": "All search-capable indexers are temporarily unavailable due to recent indexer errors", "IndexerSearchNoInteractiveHealthCheckMessage": "No indexers available with Interactive Search enabled, Sonarr will not provide any interactive search results", + "IndexerSettings": "Indexer Settings", "IndexerStatusAllUnavailableHealthCheckMessage": "All indexers are unavailable due to failures", "IndexerStatusUnavailableHealthCheckMessage": "Indexers unavailable due to failures: {0}", + "IndexerTagHelpText": "Only use this indexer for series with at least one matching tag. Leave blank to use with all series.", "Indexers": "Indexers", + "IndexersLoadError": "Unable to load Indexers", + "IndexersSettingsSummary": "Indexers and indexer options", "Info": "Info", "InstallLatest": "Install Latest", + "InstanceName": "Instance Name", + "InstanceNameHelpText": "Instance name in tab and for Syslog app name", "InteractiveImport": "Interactive Import", + "InteractiveSearch": "Interactive Search", "Interval": "Interval", + "InvalidFormat": "Invalid Format", "Language": "Language", - "Language that Sonarr will use for UI": "Language that Sonarr will use for UI", "Languages": "Languages", + "LanguagesLoadError": "Unable to load languages", "LastDuration": "Last Duration", "LastExecution": "Last Execution", "LastUsed": "Last Used", @@ -196,10 +436,24 @@ "LatestSeason": "Latest Season", "LiberaWebchat": "Libera Webchat", "LibraryImport": "Library Import", + "ListExclusionsLoadError": "Unable to load List Exclusions", + "ListOptionsLoadError": "Unable to load list options", + "ListQualityProfileHelpText": "Quality Profile list items will be added with", + "ListRootFolderHelpText": "Root Folder list items will be added to", + "ListTagsHelpText": "Tags that will be added on import from this list", + "ListWillRefreshEveryInterval": "List will refresh every {refreshInterval}", + "ListsLoadError": "Unable to load Lists", + "LocalAirDate": "Local Air Date", + "LocalPath": "Local Path", "Location": "Location", "LogFiles": "Log Files", "LogFilesLocation": "Log files are located in: {location}", + "LogLevel": "Log Level", + "LogLevelTraceHelpTextWarning": "Trace logging should only be enabled temporarily", + "Logging": "Logging", "Logs": "Logs", + "LongDateFormat": "Long Date Format", + "Lowercase": "Lowercase", "MaintenanceRelease": "Maintenance Release: bug fixes and other improvements. See Github Commit History for more details", "ManageClients": "Manage Clients", "ManageDownloadClients": "Manage Download Clients", @@ -207,81 +461,210 @@ "ManageIndexers": "Manage Indexers", "ManageLists": "Manage Lists", "Manual": "Manual", + "ManualImportItemsLoadError": "Unable to load manual import items", "MatchedToEpisodes": "Matched to Episodes", "MatchedToSeason": "Matched to Season", "MatchedToSeries": "Matched to Series", + "Max": "Max", + "MaximumLimits": "Maximum Limits", + "MaximumSingleEpisodeAge": "Maximum Single Episode Age", + "MaximumSingleEpisodeAgeHelpText": "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.", + "MaximumSize": "Maximum Size", + "MaximumSizeHelpText": "Maximum size for a release to be grabbed in MB. Set to zero to set to unlimited", + "Mechanism": "Mechanism", + "MediaInfo": "Media Info", + "MediaInfoFootNote": "MediaInfo Full/AudioLanguages/SubtitleLanguages support a `:EN+DE` suffix allowing you to filter the languages included in the filename. Use `-DE` to exclude specific languages. Appending `+` (eg `:EN+`) will output `[EN]`/`[EN+--]`/`[--]` depending on excluded languages. For example `{MediaInfo Full:EN+DE}`.", "MediaManagement": "Media Management", + "MediaManagementSettings": "Media Management Settings", + "MediaManagementSettingsLoadError": "Unable to load Media Management settings", + "MediaManagementSettingsSummary": "Naming, file management settings and root folders", + "MegabytesPerMinute": "Megabytes Per Minute", "Message": "Message", "Metadata": "Metadata", + "MetadataLoadError": "Unable to load Metadata", + "MetadataSettings": "Metadata Settings", + "MetadataSettingsSummary": "Create metadata files when episodes are imported or series are refreshed", "MetadataSource": "Metadata Source", + "MetadataSourceSettings": "Metadata Source Settings", + "MetadataSourceSettingsSummary": "Information on where Sonarr gets series and episode information", + "Min": "Min", + "MinimumAge": "Minimum Age", + "MinimumAgeHelpText": "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.", + "MinimumCustomFormatScore": "Minimum Custom Format Score", + "MinimumCustomFormatScoreHelpText": "Minimum custom format score allowed to download", + "MinimumFreeSpace": "Minimum Free Space", + "MinimumFreeSpaceHelpText": "Prevent import if it would leave less than this amount of disk space available", + "MinimumLimits": "Minimum Limits", + "MinutesFortyFive": "45 Minutes: {fortyFive}", + "MinutesSixty": "60 Minutes: {sixty}", + "MinutesThirty": "30 Minutes: {thirty}", "Missing": "Missing", "MissingEpisodes": "Missing Episodes", "Mode": "Mode", + "Monday": "Monday", "Monitored": "Monitored", "MonitoredOnly": "Monitored Only", + "MonitoringOptions": "Monitoring Options", + "MoreDetails": "More details", "MoreInfo": "More Info", "MountHealthCheckMessage": "Mount containing a series path is mounted read-only: ", "MoveAutomatically": "Move Automatically", + "MultiEpisode": "Multi Episode", + "MultiEpisodeInvalidFormat": "Multi Episode: Invalid Format", + "MultiEpisodeStyle": "Multi Episode Style", "MultiSeason": "Multi-Season", + "MustContain": "Must Contain", + "MustContainHelpText": "The release must contain at least one of these terms (case insensitive)", + "MustNotContain": "Must Not Contain", + "MustNotContainHelpText": "The release will be rejected if it contains one or more of terms (case insensitive)", "Name": "Name", + "NamingSettings": "Naming Settings", + "NamingSettingsLoadError": "Unable to load Naming settings", "Negate": "Negate", + "NegateHelpText": "If checked, the custom format will not apply if this {implementationName} condition matches.", "Negated": "Negated", "Network": "Network", + "Never": "Never", "New": "New", "NextAiring": "Next Airing", "NextExecution": "Next Execution", "No": "No", "NoBackupsAreAvailable": "No backups are available", "NoChange": "No Change", + "NoChanges": "No Changes", + "NoDelay": "No Delay", "NoDownloadClientsFound": "No download clients found", "NoEventsFound": "No events found", "NoImportListsFound": "No import lists found", "NoIndexersFound": "No indexers found", "NoIssuesWithYourConfiguration": "No issues with your configuration", "NoLeaveIt": "No, Leave It", + "NoLimitForAnyRuntime": "No limit for any runtime", + "NoLinks": "No links", "NoLogFiles": "No log files", + "NoMinimumForAnyRuntime": "No minimum for any runtime", "NoResultsFound": "No results found", "NoSeasons": "No seasons", + "NoTagsHaveBeenAddedYet": "No tags have been added yet", "NoUpdatesAreAvailable": "No updates are available", + "None": "None", "NotSeasonPack": "Not Season Pack", + "NotificationTriggers": "Notification Triggers", + "NotificationTriggersHelpText": "Select which events should trigger this notification", + "NotificationsLoadError": "Unable to load Notifications", + "NotificationsTagsHelpText": "Only send notifications for series with at least one matching tag", + "OnApplicationUpdate": "On Application Update", + "OnEpisodeFileDelete": "On Episode File Delete", + "OnEpisodeFileDeleteForUpgrade": "On Episode File Delete For Upgrade", + "OnGrab": "On Grab", + "OnHealthIssue": "On Health Issue", + "OnHealthRestored": "On Health Restored", + "OnImport": "On Import", "OnLatestVersion": "The latest version of Sonarr is already installed", + "OnManualInteractionRequired": "On Manual Interaction Required", + "OnRename": "On Rename", + "OnSeriesAdd": "On Series Add", + "OnSeriesDelete": "On Series Delete", + "OnUpgrade": "On Upgrade", + "OneMinute": "1 Minute", "OneSeason": "1 season", + "OnlyForBulkSeasonReleases": "Only for Bulk Season Releases", + "OnlyTorrent": "Only Torrent", + "OnlyUsenet": "Only Usenet", + "OpenBrowserOnStart": "Open browser on start", + "OpenBrowserOnStartHelpText": " Open a web browser and navigate to the Sonarr homepage on app start.", + "OptionalName": "Optional name", "Options": "Options", + "Original": "Original", "OriginalLanguage": "Original Language", + "Other": "Other", "OutputPath": "Output Path", "PackageVersion": "Package Version", "PackageVersionInfo": "{packageVersion} by {packageAuthor}", "PartialSeason": "Partial Season", + "Password": "Password", "Path": "Path", "Peers": "Peers", + "PendingChangesDiscardChanges": "Discard changes and leave", + "PendingChangesMessage": "You have unsaved changes, are you sure you want to leave this page?", + "PendingChangesStayReview": "Stay and review changes", + "Period": "Period", + "Permissions": "Permissions", + "PortNumber": "Port Number", + "PreferAndUpgrade": "Prefer and Upgrade", + "PreferProtocol": "Prefer {preferredProtocol}", + "PreferTorrent": "Prefer Torrent", + "PreferUsenet": "Prefer Usenet", + "Preferred": "Preferred", + "PreferredProtocol": "Preferred Protocol", + "PreferredSize": "Preferred Size", + "PrefixedRange": "Prefixed Range", + "Presets": "Presets", "PreviousAiring": "Previous Airing", "PreviouslyInstalled": "Previously Installed", "Priority": "Priority", + "PriorityHelpText": "Prioritize multiple Download Clients. Round-Robin is used for clients with the same priority.", + "PrioritySettings": "Priority: {priority}", "Profiles": "Profiles", + "ProfilesSettingsSummary": "Quality, Language Delay and Release profiles", "Progress": "Progress", "Proper": "Proper", "Protocol": "Protocol", + "ProtocolHelpText": "Choose which protocol(s) to use and which one is preferred when choosing between otherwise equal releases", + "Proxy": "Proxy", "ProxyBadRequestHealthCheckMessage": "Failed to test proxy. Status Code: {0}", + "ProxyBypassFilterHelpText": "Use ',' as a separator, and '*.' as a wildcard for subdomains", "ProxyFailedToTestHealthCheckMessage": "Failed to test proxy: {0}", + "ProxyPasswordHelpText": "You only need to enter a username and password if one is required. Leave them blank otherwise.", "ProxyResolveIpHealthCheckMessage": "Failed to resolve the IP Address for the Configured Proxy Host {0}", + "ProxyType": "Proxy Type", + "ProxyUsernameHelpText": "You only need to enter a username and password if one is required. Leave them blank otherwise.", + "Qualities": "Qualities", + "QualitiesHelpText": "Qualities higher in the list are more preferred. Qualities within the same group are equal. Only checked qualities are wanted", + "QualitiesLoadError": "Unable to load qualities", "Quality": "Quality", + "QualityDefinitions": "Quality Definitions", + "QualityDefinitionsLoadError": "Unable to load Quality Definitions", + "QualityLimitsHelpText": "Limits are automatically adjusted for the series runtime and number of episodes in the file.", "QualityProfile": "Quality Profile", + "QualityProfileInUse": "Can't delete a quality profile that is attached to a series, list, or collection", + "QualityProfiles": "Quality Profiles", + "QualityProfilesLoadError": "Unable to load Quality Profiles", + "QualitySettings": "Quality Settings", + "QualitySettingsSummary": "Quality sizes and naming", "Queue": "Queue", "Queued": "Queued", + "Range": "Range", "Rating": "Rating", "ReadTheWikiForMoreInformation": "Read the Wiki for more information", "Real": "Real", "RecycleBinUnableToWriteHealthCheckMessage": "Unable to write to configured recycling bin folder: {0}. Ensure this path exists and is writable by the user running Sonarr", + "RecyclingBin": "Recycling Bin", + "RecyclingBinCleanup": "Recycling Bin Cleanup", + "RecyclingBinCleanupHelpText": "Set to 0 to disable automatic cleanup", + "RecyclingBinCleanupHelpTextWarning": "Files in the recycle bin older than the selected number of days will be cleaned up automatically", + "RecyclingBinHelpText": "Episode files will go here when deleted instead of being permanently deleted", + "RedownloadFailed": "Redownload Failed", "Refresh": "Refresh", "RefreshSeries": "Refresh Series", + "RegularExpression": "Regular Expression", + "RegularExpressionsCanBeTested": "Regular expressions can be tested [here](http://regexstorm.net/tester).", + "RegularExpressionsTutorialLink": "More details on regular expressions can be found [here](https://www.regular-expressions.info/tutorial.html).", "RejectionCount": "Rejection Count", "RelativePath": "Relative Path", "Release": "Release", "ReleaseGroup": "Release Group", "ReleaseGroups": "Release Groups", "ReleaseHash": "Release Hash", + "ReleaseProfile": "Release Profile", + "ReleaseProfileIndexerHelpText": "Specify what indexer the profile applies to", + "ReleaseProfileIndexerHelpTextWarning": "Using a specific indexer with release profiles can lead to duplicate releases being grabbed", + "ReleaseProfileTagHelpText": "Release profiles will apply to series with at least one matching tag. Leave blank to apply to all series", + "ReleaseProfiles": "Release Profiles", + "ReleaseProfilesLoadError": "Unable to load Release Profiles", "ReleaseTitle": "Release Title", "Reload": "Reload", + "RemotePath": "Remote Path", "RemotePathMappingBadDockerPathHealthCheckMessage": "You are using docker; download client {0} places downloads in {1} but this is not a valid {2} path. Review your remote path mappings and download client settings.", "RemotePathMappingDockerFolderMissingHealthCheckMessage": "You are using docker; download client {0} places downloads in {1} but this directory does not appear to exist inside the container. Review your remote path mappings and container volume settings.", "RemotePathMappingDownloadPermissionsHealthCheckMessage": "Sonarr can see but not access downloaded episode {0}. Likely permissions error.", @@ -292,16 +675,24 @@ "RemotePathMappingFilesWrongOSPathHealthCheckMessage": "Remote download client {0} reported files in {1} but this is not a valid {2} path. Review your remote path mappings and download client settings.", "RemotePathMappingFolderPermissionsHealthCheckMessage": "Sonarr can see but not access download directory {0}. Likely permissions error.", "RemotePathMappingGenericPermissionsHealthCheckMessage": "Download client {0} places downloads in {1} but Sonarr cannot see this directory. You may need to adjust the folder's permissions.", + "RemotePathMappingHostHelpText": "The same host you specified for the remote Download Client", "RemotePathMappingImportFailedHealthCheckMessage": "Sonarr failed to import (an) episode(s). Check your logs for details.", "RemotePathMappingLocalFolderMissingHealthCheckMessage": "Remote download client {0} places downloads in {1} but this directory does not appear to exist. Likely missing or incorrect remote path mapping.", + "RemotePathMappingLocalPathHelpText": "Path that Sonarr should use to access the remote path locally", "RemotePathMappingLocalWrongOSPathHealthCheckMessage": "Local download client {0} places downloads in {1} but this is not a valid {2} path. Review your download client settings.", "RemotePathMappingRemoteDownloadClientHealthCheckMessage": "Remote download client {0} reported files in {1} but this directory does not appear to exist. Likely missing remote path mapping.", + "RemotePathMappingRemotePathHelpText": "Root path to the directory that the Download Client accesses", "RemotePathMappingWrongOSPathHealthCheckMessage": "Remote download client {0} places downloads in {1} but this is not a valid {2} path. Review your remote path mappings and download client settings.", + "RemotePathMappings": "Remote Path Mappings", + "RemotePathMappingsLoadError": "Unable to load Remote Path Mappings", "Remove": "Remove", "RemoveCompleted": "Remove Completed", "RemoveCompletedDownloads": "Remove Completed Downloads", + "RemoveCompletedDownloadsHelpText": "Remove imported downloads from download client history", + "RemoveDownloadsAlert": "The Remove settings were moved to the individual Download Client settings in the table above.", "RemoveFailed": "Remove Failed", "RemoveFailedDownloads": "Remove Failed Downloads", + "RemoveFailedDownloadsHelpText": "Remove failed downloads from download client history", "RemoveFromDownloadClient": "Remove From Download Client", "RemoveFromDownloadClientHelpTextWarning": "Removing will remove the download and the file(s) from the download client.", "RemoveRootFolder": "Remove root folder", @@ -315,93 +706,237 @@ "RemovedSeriesMultipleRemovedHealthCheckMessage": "Series {0} were removed from TheTVDB", "RemovedSeriesSingleRemovedHealthCheckMessage": "Series {0} was removed from TheTVDB", "RemovingTag": "Removing tag", + "RenameEpisodes": "Rename Episodes", + "RenameEpisodesHelpText": "Sonarr will use the existing file name if renaming is disabled", "Renamed": "Renamed", + "Reorder": "Reorder", "Repack": "Repack", + "Repeat": "Repeat", "Replace": "Replace", + "ReplaceIllegalCharacters": "Replace Illegal Characters", + "ReplaceIllegalCharactersHelpText": "Replace illegal characters. If unchecked, Sonarr will remove them instead", + "ReplaceWithDash": "Replace with Dash", + "ReplaceWithSpaceDash": "Replace with Space Dash", + "ReplaceWithSpaceDashSpace": "Replace with Space Dash Space", "Required": "Required", + "RequiredHelpText": "This {implementationName} condition must match for the custom format to apply. Otherwise a single {implementationName} match is sufficient.", + "RescanAfterRefreshHelpText": "Rescan the series folder after refreshing the series", + "RescanAfterRefreshHelpTextWarning": "Sonarr will not automatically detect changes to files when not set to 'Always'", + "RescanSeriesFolderAfterRefresh": "Rescan Series Folder after Refresh", "Reset": "Reset", + "ResetAPIKey": "Reset API Key", + "ResetAPIKeyMessageText": "Are you sure you want to reset your API Key?", "ResetDefinitionTitlesHelpText": "Reset definition titles as well as values", + "ResetDefinitions": "Reset Definitions", "ResetQualityDefinitions": "Reset Quality Definitions", "ResetQualityDefinitionsMessageText": "Are you sure you want to reset quality definitions?", "ResetTitles": "Reset Titles", "Restart": "Restart", + "RestartLater": "I'll restart later", + "RestartNow": "Restart Now", "RestartReloadNote": "Note: Sonarr will automatically restart and reload the UI during the restore process.", + "RestartRequiredHelpTextWarning": "Requires restart to take effect", + "RestartRequiredToApplyChanges": "Sonarr requires a restart to apply changes, do you want to restart now?", + "RestartRequiredWindowsService": "Depending which user is running the Sonarr service you may need to restart Sonarr as admin once before the service will start automatically.", + "RestartSonarr": "Restart Sonarr", "Restore": "Restore", "RestoreBackup": "Restore Backup", + "RestrictionsLoadError": "Unable to load Restrictions", "Result": "Result", + "Retention": "Retention", + "RetentionHelpText": "Usenet only: Set to zero to set for unlimited retention", "RootFolder": "Root Folder", + "RootFolderLoadError": "Unable to add root folder", "RootFolderMissingHealthCheckMessage": "Missing root folder: {0}", "RootFolderMultipleMissingHealthCheckMessage": "Multiple root folders are missing: {0}", "RootFolderPath": "Root Folder Path", "RootFolders": "Root Folders", + "RootFoldersLoadError": "Unable to load root folders", + "Rss": "RSS", + "RssIsNotSupportedWithThisIndexer": "RSS is not supported with this indexer", + "RssSyncInterval": "RSS Sync Interval", + "RssSyncIntervalHelpText": "Interval in minutes. Set to zero to disable (this will stop all automatic release grabbing)", + "RssSyncIntervalHelpTextWarning": "This will apply to all indexers, please follow the rules set forth by them", "Runtime": "Runtime", "Save": "Save", + "SaveChanges": "Save Changes", + "SaveSettings": "Save Settings", + "Scene": "Scene", "SceneNumbering": "Scene Numbering", "Scheduled": "Scheduled", + "Score": "Score", + "Script": "Script", + "ScriptPath": "Script Path", "SearchForMonitoredEpisodes": "Search for monitored episodes", + "SearchIsNotSupportedWithThisIndexer": "Search is not supported with this indexer", "Season": "Season", "SeasonCount": "Season Count", "SeasonFolder": "Season Folder", + "SeasonFolderFormat": "Season Folder Format", "SeasonNumber": "Season Number", "SeasonPack": "Season Pack", "Seasons": "Seasons", + "Security": "Security", "Seeders": "Seeders", "SelectFolder": "Select Folder", + "SendAnonymousUsageData": "Send Anonymous Usage Data", "Series": "Series", + "SeriesAndEpisodeInformationIsProvidedByTheTVDB": "Series and episode information is provided by TheTVDB.com. [Please consider supporting them](https://www.thetvdb.com/subscribe).", "SeriesEditor": "Series Editor", + "SeriesFolderFormat": "Series Folder Format", + "SeriesFolderFormatHelpText": "Used when adding a new series or moving series via the series editor", + "SeriesID": "Series ID", + "SeriesLoadError": "Unable to load Series", "SeriesTitle": "Series Title", + "SeriesTitleToExcludeHelpText": "The name of the series to exclude", + "SeriesType": "Series Type", + "SeriesTypes": "Series Types", + "SetPermissions": "Set Permissions", + "SetPermissionsLinuxHelpText": "Should chmod be run when files are imported/renamed?", + "SetPermissionsLinuxHelpTextWarning": "If you're unsure what these settings do, do not alter them.", "SetTags": "Set Tags", "Settings": "Settings", + "ShortDateFormat": "Short Date Format", "ShowAdvanced": "Show Advanced", + "ShowRelativeDates": "Show Relative Dates", + "ShowRelativeDatesHelpText": "Show relative (Today/Yesterday/etc) or absolute dates", "ShownClickToHide": "Shown, click to hide", + "SingleEpisode": "Single Episode", + "SingleEpisodeInvalidFormat": "Single Episode: Invalid Format", "Size": "Size", + "SizeLimit": "Size Limit", "SizeOnDisk": "Size on disk", + "SkipFreeSpaceCheck": "Skip Free Space Check", + "SkipFreeSpaceCheckWhenImportingHelpText": "Use when Sonarr is unable to detect free space from your series root folder", "SkipRedownload": "Skip Redownload", "SkipRedownloadHelpText": "Prevents Sonarr from trying to download an alternative release for this item", + "SmartReplace": "Smart Replace", + "SmartReplaceHint": "Dash or Space Dash depending on name", + "Socks4": "Socks4", + "Socks5": "Socks5 (Support TOR)", "SomeResultsAreHiddenByTheAppliedFilter": "Some results are hidden by the applied filter", + "SonarrTags": "Sonarr Tags", "Source": "Source", "SourceTitle": "Source Title", + "Space": "Space", "Special": "Special", + "SpecialsFolderFormat": "Specials Folder Format", + "SslCertPassword": "SSL Cert Password", + "SslCertPasswordHelpText": "Password for pfx file", + "SslCertPath": "SSL Cert Path", + "SslCertPathHelpText": "Path to pfx file", + "SslPort": "SSL Port", + "StandardEpisodeFormat": "Standard Episode Format", "Started": "Started", "StartupDirectory": "Startup directory", "Status": "Status", + "Style": "Style", "SubtitleLanguages": "Subtitle Languages", + "Sunday": "Sunday", + "SupportedAutoTaggingProperties": "Sonarr supports the follow properties for auto tagging rules", + "SupportedCustomConditions": "Sonarr supports custom conditions against the release properties below.", + "SupportedDownloadClients": "Sonarr supports many popular torrent and usenet download clients.", + "SupportedDownloadClientsMoreInfo": "For more information on the individual download clients, click the more info buttons.", + "SupportedImportListsMoreInfo": "For more information on the individual import lists, click on the more info buttons.", + "SupportedIndexers": "Sonarr supports any indexer that uses the Newznab standard, as well as other indexers listed below.", + "SupportedIndexersMoreInfo": "For more information on the individual indexers, click on the more info buttons.", + "SupportedLists": "Sonarr supports multiple lists for importing Series into the database.", + "SupportedListsMoreInfo": "For more information on the individual lists, click on the more info buttons.", "System": "System", "SystemTimeHealthCheckMessage": "System time is off by more than 1 day. Scheduled tasks may not run correctly until the time is corrected", + "TagCannotBeDeletedWhileInUse": "Tag cannot be deleted while in use", + "TagDetails": "Tag Details - {label}", + "TagIsNotUsedAndCanBeDeleted": "Tag is not used and can be deleted", "Tags": "Tags", + "TagsLoadError": "Unable to load Tags", + "TagsSettingsSummary": "See all tags and how they are used. Unused tags can be removed", "TaskUserAgentTooltip": "User-Agent provided by the app that called the API", "Tasks": "Tasks", "TestAll": "Test All", + "TestAllClients": "Test All Clients", + "TestAllIndexers": "Test All Indexers", + "TestAllLists": "Test All Lists", "TestParsing": "Test Parsing", "TheLogLevelDefault": "The log level defaults to 'Info' and can be changed in [General Settings](/settings/general)", + "TheTvdb": "TheTVDB", + "Theme": "Theme", + "ThemeHelpText": "Change Application UI Theme, 'Auto' Theme will use your OS Theme to set Light or Dark mode. Inspired by Theme.Park", "Time": "Time", + "TimeFormat": "Time Format", "TimeLeft": "Time Left", "Title": "Title", + "TorrentDelay": "Torrent Delay", + "TorrentDelayHelpText": "Delay in minutes to wait before grabbing a torrent", + "TorrentDelayTime": "Torrent Delay: {torrentDelay}", + "Torrents": "Torrents", + "TorrentsDisabled": "Torrents Disabled", "TotalSpace": "Total Space", + "Trace": "Trace", + "TvdbId": "TVDB ID", + "TvdbIdExcludeHelpText": "The TVDB ID of the series to exclude", "Twitter": "Twitter", "Type": "Type", - "UI": "UI", - "UI Language": "UI Language", + "TypeOfList": "{typeOfList} List", + "Ui": "UI", + "UiLanguage": "UI Language", + "UiLanguageHelpText": "Language that Sonarr will use for UI", + "UiSettings": "UI Settings", + "UiSettingsLoadError": "Unable to load UI settings", + "UiSettingsSummary": "Calendar, date and color impaired options", "UnableToLoadAutoTagging": "Unable to load auto tagging", "UnableToLoadBackups": "Unable to load backups", "UnableToLoadRootFolders": "Unable to load root folders", "UnableToUpdateSonarrDirectly": "Unable to update Sonarr directly,", "Unavailable": "Unavailable", + "Underscore": "Underscore", + "Ungroup": "Ungroup", + "Unlimited": "Unlimited", "UnmappedFolders": "Unmapped Folders", + "UnmonitorDeletedEpisodes": "Unmonitor Deleted Episodes", + "UnmonitorDeletedEpisodesHelpText": "Episodes deleted from disk are automatically unmonitored in Sonarr", "Unmonitored": "Unmonitored", "UnmonitoredOnly": "Unmonitored Only", + "UnsavedChanges": "Unsaved Changes", + "UpdateAutomaticallyHelpText": "Automatically download and install updates. You will still be able to install from System: Updates", "UpdateAvailableHealthCheckMessage": "New update is available", + "UpdateMechanismHelpText": "Use Sonarr's built-in updater or a script", + "UpdateScriptPathHelpText": "Path to a custom script that takes an extracted update package and handle the remainder of the update process", + "UpdateSonarrDirectlyLoadError": "Unable to update Sonarr directly,", "UpdateStartupNotWritableHealthCheckMessage": "Cannot install update because startup folder '{0}' is not writable by the user '{1}'.", "UpdateStartupTranslocationHealthCheckMessage": "Cannot install update because startup folder '{0}' is in an App Translocation folder.", "UpdateUINotWritableHealthCheckMessage": "Cannot install update because UI folder '{0}' is not writable by the user '{1}'.", + "UpdateUiNotWritableHealthCheckMessage": "Cannot install update because UI folder '{0}' is not writable by the user '{1}'.", "UpdaterLogFiles": "Updater Log Files", "Updates": "Updates", + "UpgradeUntil": "Upgrade Until", + "UpgradeUntilCustomFormatScore": "Upgrade Until Custom Format Score", + "UpgradeUntilCustomFormatScoreHelpText": "Once this custom format score is reached Sonarr will no longer grab episode releases", + "UpgradeUntilHelpText": "Once this quality is reached Sonarr will no longer download episodes", + "UpgradeUntilThisQualityIsMetOrExceeded": "Upgrade until this quality is met or exceeded", + "UpgradesAllowed": "Upgrades Allowed", + "UpgradesAllowedHelpText": "If disabled qualities will not be upgraded", + "Uppercase": "Uppercase", "Uptime": "Uptime", + "UrlBase": "URL Base", + "UrlBaseHelpText": "For reverse proxy support, default is empty", + "UseHardlinksInsteadOfCopy": "Use Hardlinks instead of Copy", + "UseProxy": "Use Proxy", + "Usenet": "Usenet", + "UsenetDelay": "Usenet Delay", + "UsenetDelayHelpText": "Delay in minutes to wait before grabbing a release from Usenet", + "UsenetDelayTime": "Usenet Delay: {usenetDelay}", + "UsenetDisabled": "Usenet Disabled", + "Username": "Username", + "UtcAirDate": "UTC Air Date", "Version": "Version", "VideoCodec": "Video Codec", "VideoDynamicRange": "Video Dynamic Range", + "VisitTheWikiForMoreDetails": "Visit the wiki for more details: ", + "WantMoreControlAddACustomFormat": "Want more control over which downloads are preferred? Add a [Custom Format](/settings/customformats)", "Wanted": "Wanted", "Warn": "Warn", + "WeekColumnHeader": "Week Column Header", + "WeekColumnHeaderHelpText": "Shown above each column when week is the active view", "Wiki": "Wiki", "WouldYouLikeToRestoreBackup": "Would you like to restore the backup '{name}'?", "Year": "Year",