1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-04-24 22:36:19 -04:00

Add translations to frontend/InteractiveImport

This commit is contained in:
Stevie Robinson
2023-08-14 01:28:16 +02:00
committed by GitHub
parent 16d95ea6bf
commit 060b66aa39
13 changed files with 161 additions and 63 deletions
@@ -71,7 +71,7 @@ interface SelectEpisodeModalContentProps {
isAnime: boolean;
sortKey?: string;
sortDirection?: string;
modalTitle?: string;
modalTitle: string;
onEpisodesSelect(selectedEpisodes: SelectedEpisode[]): unknown;
onModalClose(): unknown;
}
@@ -103,7 +103,7 @@ function SelectEpisodeModalContent(props: SelectEpisodeModalContentProps) {
const dispatch = useDispatch();
const filterEpisodeNumber = parseInt(filter);
const errorMessage = getErrorMessage(error, 'Unable to load episodes');
const errorMessage = getErrorMessage(error, translate('EpisodesLoadError'));
const selectedCount = selectedIds.length;
const selectedEpisodesCount = getSelectedIds(selectedState).length;
const selectionIsValid =
@@ -197,13 +197,15 @@ function SelectEpisodeModalContent(props: SelectEpisodeModalContentProps) {
if (!details) {
details =
selectedCount > 1
? `${selectedCount} selected files`
: `${selectedCount} selected file`;
? translate('CountSelectedFiles', { selectedCount })
: translate('CountSelectedFile', { selectedCount });
}
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{modalTitle} - Select Episode(s)</ModalHeader>
<ModalHeader>
{translate('SelectEpisodesModalTitle', { modalTitle })}
</ModalHeader>
<ModalBody
className={styles.modalBody}
@@ -211,7 +213,7 @@ function SelectEpisodeModalContent(props: SelectEpisodeModalContentProps) {
>
<TextInput
className={styles.filterInput}
placeholder="Filter episodes by title or number"
placeholder={translate('FilterEpisodesPlaceholder')}
name="filter"
value={filter}
autoFocus={true}
@@ -256,7 +258,7 @@ function SelectEpisodeModalContent(props: SelectEpisodeModalContentProps) {
) : null}
{isPopulated && !items.length
? 'No episodes were found for the selected season'
? translate('NoEpisodesFoundForSelectedSeason')
: null}
</Scroller>
</ModalBody>
@@ -265,14 +267,14 @@ function SelectEpisodeModalContent(props: SelectEpisodeModalContentProps) {
<div className={styles.details}>{details}</div>
<div className={styles.buttons}>
<Button onPress={onModalClose}>Cancel</Button>
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button
kind={kinds.SUCCESS}
isDisabled={!selectionIsValid}
onPress={onEpisodesSelectWrapper}
>
Select Episodes
{translate('SelectEpisodes')}
</Button>
</div>
</ModalFooter>
@@ -100,7 +100,7 @@ function InteractiveImportSelectFolderModalContent(
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{modalTitle} - {translate('SelectFolder')}
{translate('SelectFolderModalTitle', { modalTitle })}
</ModalHeader>
<ModalBody>
@@ -5,6 +5,7 @@ import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellCo
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRowButton from 'Components/Table/TableRowButton';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './RecentFolderRow.css';
class RecentFolderRow extends Component {
@@ -44,7 +45,7 @@ class RecentFolderRow extends Component {
<TableRowCell className={styles.actions}>
<IconButton
title="Remove"
title={translate('Remove')}
name={icons.REMOVE}
onPress={this.onRemovePress}
/>
@@ -147,9 +147,19 @@ const COLUMNS = [
];
const importModeOptions = [
{ key: 'chooseImportMode', value: 'Choose Import Mode', disabled: true },
{ key: 'move', value: 'Move Files' },
{ key: 'copy', value: 'Hardlink/Copy Files' },
{
key: 'chooseImportMode',
value: () => translate('ChooseImportMode'),
disabled: true,
},
{
key: 'move',
value: () => translate('MoveFiles'),
},
{
key: 'copy',
value: () => translate('HardlinkCopyFiles'),
},
];
function isSameEpisodeFile(
@@ -260,12 +270,31 @@ function InteractiveImportModalContent(
useState<string | null>(null);
const [selectState, setSelectState] = useSelectState();
const [bulkSelectOptions, setBulkSelectOptions] = useState([
{ key: 'select', value: 'Select...', disabled: true },
{ key: 'season', value: 'Select Season' },
{ key: 'episode', value: 'Select Episode(s)' },
{ key: 'quality', value: 'Select Quality' },
{ key: 'releaseGroup', value: 'Select Release Group' },
{ key: 'language', value: 'Select Language' },
{
key: 'select',
value: translate('SelectDropdown'),
disabled: true,
},
{
key: 'season',
value: translate('SelectSeason'),
},
{
key: 'episode',
value: translate('SelectEpisodes'),
},
{
key: 'quality',
value: translate('SelectQuality'),
},
{
key: 'releaseGroup',
value: translate('SelectReleaseGroup'),
},
{
key: 'language',
value: translate('SelectLanguage'),
},
]);
const { allSelected, allUnselected, selectedState } = selectState;
const previousIsDeleting = usePrevious(isDeleting);
@@ -296,7 +325,7 @@ function InteractiveImportModalContent(
newBulkSelectOptions.splice(1, 0, {
key: 'series',
value: 'Select Series',
value: translate('SelectSeries'),
});
setBulkSelectOptions(newBulkSelectOptions);
@@ -410,7 +439,9 @@ function InteractiveImportModalContent(
const files: InteractiveImportCommandOptions[] = [];
if (finalImportMode === 'chooseImportMode') {
setInteractiveImportErrorMessage('An import mode must be selected');
setInteractiveImportErrorMessage(
translate('InteractiveImportNoImportMode')
);
return;
}
@@ -431,35 +462,35 @@ function InteractiveImportModalContent(
if (!series) {
setInteractiveImportErrorMessage(
'Series must be chosen for each selected file'
translate('InteractiveImportNoSeries')
);
return;
}
if (isNaN(seasonNumber)) {
setInteractiveImportErrorMessage(
'Season must be chosen for each selected file'
translate('InteractiveImportNoSeason')
);
return;
}
if (!episodes || !episodes.length) {
setInteractiveImportErrorMessage(
'One or more episodes must be chosen for each selected file'
translate('InteractiveImportNoEpisode')
);
return;
}
if (!quality) {
setInteractiveImportErrorMessage(
'Quality must be chosen for each selected file'
translate('InteractiveImportNoQuality')
);
return;
}
if (!languages) {
setInteractiveImportErrorMessage(
'Language(s) must be chosen for each selected file'
translate('InteractiveImportNoLanguage')
);
return;
}
@@ -699,7 +730,7 @@ function InteractiveImportModalContent(
const errorMessage = getErrorMessage(
error,
'Unable to load manual import items'
translate('InteractiveImportLoadError')
);
return (
@@ -716,7 +747,9 @@ function InteractiveImportModalContent(
<Icon name={icons.FILTER} size={22} />
<div className={styles.filterText}>
{filterExistingFiles ? 'Unmapped Files Only' : 'All Files'}
{filterExistingFiles
? translate('UnmappedFilesOnly')
: translate('AllFiles')}
</div>
</MenuButton>
@@ -726,7 +759,7 @@ function InteractiveImportModalContent(
isSelected={!filterExistingFiles}
onPress={onFilterExistingFilesChange}
>
All Files
{translate('AllFiles')}
</SelectedMenuItem>
<SelectedMenuItem
@@ -734,7 +767,7 @@ function InteractiveImportModalContent(
isSelected={filterExistingFiles}
onPress={onFilterExistingFilesChange}
>
Unmapped Files Only
{translate('UnmappedFilesOnly')}
</SelectedMenuItem>
</MenuContent>
</Menu>
@@ -777,7 +810,7 @@ function InteractiveImportModalContent(
) : null}
{isPopulated && !items.length && !isFetching
? 'No video files were found in the selected folder'
? translate('InteractiveImportNoFilesFound')
: null}
</ModalBody>
@@ -793,7 +826,7 @@ function InteractiveImportModalContent(
}
onPress={onDeleteSelectedPress}
>
Delete
{translate('Delete')}
</SpinnerButton>
) : null}
@@ -831,7 +864,7 @@ function InteractiveImportModalContent(
isDisabled={!selectedIds.length || !!invalidRowsSelected.length}
onPress={onImportSelectedPress}
>
Import
{translate('Import')}
</Button>
</div>
</ModalFooter>
@@ -891,9 +924,9 @@ function InteractiveImportModalContent(
<ConfirmModal
isOpen={isConfirmDeleteModalOpen}
kind={kinds.DANGER}
title="Delete Selected Episode Files"
message={'Are you sure you want to delete the selected episode files?'}
confirmLabel="Delete"
title={translate('DeleteSelectedEpisodeFiles')}
message={translate('DeleteSelectedEpisodeFilesHelpText')}
confirmLabel={translate('Delete')}
onConfirm={onConfirmDelete}
onCancel={onConfirmDeleteModalClose}
/>
@@ -348,7 +348,9 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
{isSeriesColumnVisible ? (
<TableRowCellButton
isDisabled={!allowSeriesChange}
title={allowSeriesChange ? 'Click to change series' : undefined}
title={
allowSeriesChange ? translate('ClickToChangeSeries') : undefined
}
onPress={onSelectSeriesPress}
>
{showSeriesPlaceholder ? (
@@ -361,7 +363,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
<TableRowCellButton
isDisabled={!series}
title={series ? 'Click to change season' : undefined}
title={series ? translate('ClickToChangeSeason') : undefined}
onPress={onSelectSeasonPress}
>
{showSeasonNumberPlaceholder ? (
@@ -379,7 +381,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
isDisabled={!series || requiresSeasonNumber}
title={
series && !requiresSeasonNumber
? 'Click to change episode'
? translate('ClickToChangeEpisode')
: undefined
}
onPress={onSelectEpisodePress}
@@ -392,7 +394,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
</TableRowCellButton>
<TableRowCellButton
title="Click to change release group"
title={translate('ClickToChangeReleaseGroup')}
onPress={onSelectReleaseGroupPress}
>
{showReleaseGroupPlaceholder ? (
@@ -404,7 +406,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
<TableRowCellButton
className={styles.quality}
title="Click to change quality"
title={translate('ClickToChangeQuality')}
onPress={onSelectQualityPress}
>
{showQualityPlaceholder && <InteractiveImportRowCellPlaceholder />}
@@ -416,7 +418,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
<TableRowCellButton
className={styles.languages}
title="Click to change language"
title={translate('ClickToChangeLanguage')}
onPress={onSelectLanguagePress}
>
{showLanguagePlaceholder && <InteractiveImportRowCellPlaceholder />}
@@ -450,7 +452,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
{rejections.length ? (
<Popover
anchor={<Icon name={icons.DANGER} kind={kinds.DANGER} />}
title="Release Rejected"
title={translate('ReleaseRejected')}
body={
<ul>
{rejections.map((rejection, index) => {
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react';
import Modal from 'Components/Modal/Modal';
import usePrevious from 'Helpers/Hooks/usePrevious';
import { sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import InteractiveImportSelectFolderModalContent from './Folder/InteractiveImportSelectFolderModalContent';
import InteractiveImportModalContent from './Interactive/InteractiveImportModalContent';
@@ -18,7 +19,7 @@ function InteractiveImportModal(props: InteractiveImportModalProps) {
isOpen,
folder,
downloadId,
modalTitle = 'Manual Import',
modalTitle = translate('ManualImport'),
onModalClose,
...otherProps
} = props;
@@ -16,6 +16,7 @@ import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds, sizes } from 'Helpers/Props';
import Language from 'Language/Language';
import createLanguagesSelector from 'Store/Selectors/createLanguagesSelector';
import translate from 'Utilities/String/translate';
import styles from './SelectLanguageModalContent.css';
interface SelectLanguageModalContentProps {
@@ -78,13 +79,15 @@ function SelectLanguageModalContent(props: SelectLanguageModalContentProps) {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{modalTitle} - Select Language</ModalHeader>
<ModalHeader>
{translate('SelectLanguageModalTitle', { modalTitle })}
</ModalHeader>
<ModalBody>
{isFetching ? <LoadingIndicator /> : null}
{!isFetching && error ? (
<Alert kind={kinds.DANGER}>Unable to load Languages</Alert>
<Alert kind={kinds.DANGER}>{translate('LanguagesLoadError')}</Alert>
) : null}
{isPopulated && !error ? (
@@ -111,10 +114,10 @@ function SelectLanguageModalContent(props: SelectLanguageModalContentProps) {
</ModalBody>
<ModalFooter>
<Button onPress={onModalClose}>Cancel</Button>
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button kind={kinds.SUCCESS} onPress={onLanguagesSelectWrapper}>
Select Languages
{translate('SelectLanguages')}
</Button>
</ModalFooter>
</ModalContent>
@@ -19,6 +19,7 @@ import Quality, { QualityModel } from 'Quality/Quality';
import { fetchQualityProfileSchema } from 'Store/Actions/settingsActions';
import { CheckInputChanged } from 'typings/inputs';
import getQualities from 'Utilities/Quality/getQualities';
import translate from 'Utilities/String/translate';
interface QualitySchemaState {
isFetching: boolean;
@@ -128,13 +129,13 @@ function SelectQualityModalContent(props: SelectQualityModalContentProps) {
{isFetching && <LoadingIndicator />}
{!isFetching && error ? (
<Alert kind={kinds.DANGER}>Unable to load qualities</Alert>
<Alert kind={kinds.DANGER}>{translate('QualitiesLoadError')}</Alert>
) : null}
{isPopulated && !error ? (
<Form>
<FormGroup>
<FormLabel>Quality</FormLabel>
<FormLabel>{translate('Quality')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@@ -146,7 +147,7 @@ function SelectQualityModalContent(props: SelectQualityModalContentProps) {
</FormGroup>
<FormGroup>
<FormLabel>Proper</FormLabel>
<FormLabel>{translate('Proper')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@@ -157,7 +158,7 @@ function SelectQualityModalContent(props: SelectQualityModalContentProps) {
</FormGroup>
<FormGroup>
<FormLabel>Real</FormLabel>
<FormLabel>{translate('Real')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@@ -174,7 +175,7 @@ function SelectQualityModalContent(props: SelectQualityModalContentProps) {
<Button onPress={onModalClose}>Cancel</Button>
<Button kind={kinds.SUCCESS} onPress={onQualitySelectWrapper}>
Select Quality
{translate('SelectQuality')}
</Button>
</ModalFooter>
</ModalContent>
@@ -9,6 +9,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds, scrollDirections } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './SelectReleaseGroupModalContent.css';
interface SelectReleaseGroupModalContentProps {
@@ -37,7 +38,9 @@ function SelectReleaseGroupModalContent(
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{modalTitle} - Set Release Group</ModalHeader>
<ModalHeader>
{translate('SetReleaseGroupModalTitle', { modalTitle })}
</ModalHeader>
<ModalBody
className={styles.modalBody}
@@ -45,7 +48,7 @@ function SelectReleaseGroupModalContent(
>
<Form>
<FormGroup>
<FormLabel>Release Group</FormLabel>
<FormLabel>{translate('ReleaseGroup')}</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
@@ -59,10 +62,10 @@ function SelectReleaseGroupModalContent(
</ModalBody>
<ModalFooter>
<Button onPress={onModalClose}>Cancel</Button>
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button kind={kinds.SUCCESS} onPress={onReleaseGroupSelectWrapper}>
Set Release Group
{translate('SetReleaseGroup')}
</Button>
</ModalFooter>
</ModalContent>
@@ -7,6 +7,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { Season } from 'Series/Series';
import { createSeriesSelectorForHook } from 'Store/Selectors/createSeriesSelector';
import translate from 'Utilities/String/translate';
import SelectSeasonRow from './SelectSeasonRow';
interface SelectSeasonModalContentProps {
@@ -25,7 +26,9 @@ function SelectSeasonModalContent(props: SelectSeasonModalContentProps) {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{modalTitle} - Select Season</ModalHeader>
<ModalHeader>
{translate('SelectSeasonModalTitle', { modalTitle })}
</ModalHeader>
<ModalBody>
{seasons.map((item) => {
@@ -40,7 +43,7 @@ function SelectSeasonModalContent(props: SelectSeasonModalContentProps) {
</ModalBody>
<ModalFooter>
<Button onPress={onModalClose}>Cancel</Button>
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
</ModalFooter>
</ModalContent>
);
@@ -1,5 +1,6 @@
import React, { useCallback } from 'react';
import Link from 'Components/Link/Link';
import translate from 'Utilities/String/translate';
import styles from './SelectSeasonRow.css';
interface SelectSeasonRowProps {
@@ -20,7 +21,9 @@ function SelectSeasonRow(props: SelectSeasonRowProps) {
component="div"
onPress={onSeasonSelectWrapper}
>
{seasonNumber === 0 ? 'Specials' : `Season ${seasonNumber}`}
{seasonNumber === 0
? translate('Specials')
: translate('SeasonNumberToken', { seasonNumber })}
</Link>
);
}
@@ -10,6 +10,7 @@ import Scroller from 'Components/Scroller/Scroller';
import { scrollDirections } from 'Helpers/Props';
import Series from 'Series/Series';
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
import translate from 'Utilities/String/translate';
import SelectSeriesRow from './SelectSeriesRow';
import styles from './SelectSeriesModalContent.css';
@@ -61,7 +62,7 @@ function SelectSeriesModalContent(props: SelectSeriesModalContentProps) {
>
<TextInput
className={styles.filterInput}
placeholder="Filter series"
placeholder={translate('FilterSeriesPlaceholder')}
name="filter"
value={filter}
autoFocus={true}
@@ -83,7 +84,7 @@ function SelectSeriesModalContent(props: SelectSeriesModalContentProps) {
</ModalBody>
<ModalFooter>
<Button onPress={onModalClose}>Cancel</Button>
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
</ModalFooter>
</ModalContent>
);