Translate Frontend InteractiveSearch

This commit is contained in:
Stevie Robinson 2023-08-15 09:18:35 +02:00 committed by Mark McDowall
parent 78d4dee461
commit efca704388
7 changed files with 76 additions and 37 deletions

View File

@ -139,10 +139,9 @@ function InteractiveSearch(props) {
{ {
errorMessage ? errorMessage ?
<Fragment> <Fragment>
Search failed because its {errorMessage.charAt(0).toLowerCase() + errorMessage.slice(1)}. {translate('InteractiveSearchResultsFailedErrorMessage', { message: errorMessage.charAt(0).toLowerCase() + errorMessage.slice(1) })}
Try refreshing the series info and verify the necessary information is present before searching again
</Fragment> : </Fragment> :
'Unable to load results for this episode search. Try again later' translate('EpisodeSearchResultsLoadError')
} }
</div> : </div> :
null null

View File

@ -20,6 +20,7 @@ import formatDateTime from 'Utilities/Date/formatDateTime';
import formatAge from 'Utilities/Number/formatAge'; import formatAge from 'Utilities/Number/formatAge';
import formatBytes from 'Utilities/Number/formatBytes'; import formatBytes from 'Utilities/Number/formatBytes';
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore'; import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
import translate from 'Utilities/String/translate';
import OverrideMatchModal from './OverrideMatch/OverrideMatchModal'; import OverrideMatchModal from './OverrideMatch/OverrideMatchModal';
import Peers from './Peers'; import Peers from './Peers';
import ReleaseEpisode from './ReleaseEpisode'; import ReleaseEpisode from './ReleaseEpisode';
@ -62,12 +63,12 @@ function getDownloadTooltip(
if (isGrabbing) { if (isGrabbing) {
return ''; return '';
} else if (isGrabbed) { } else if (isGrabbed) {
return 'Added to download queue'; return translate('AddToDownloadQueue');
} else if (grabError) { } else if (grabError) {
return grabError; return grabError;
} }
return 'Add to download queue'; return translate('AddedToDownloadQueue');
} }
interface InteractiveSearchRowProps { interface InteractiveSearchRowProps {
@ -261,7 +262,7 @@ function InteractiveSearchRow(props: InteractiveSearchRowProps) {
{rejections.length ? ( {rejections.length ? (
<Popover <Popover
anchor={<Icon name={icons.DANGER} kind={kinds.DANGER} />} anchor={<Icon name={icons.DANGER} kind={kinds.DANGER} />}
title="Release Rejected" title={translate('ReleaseRejected')}
body={ body={
<ul> <ul>
{rejections.map((rejection, index) => { {rejections.map((rejection, index) => {
@ -285,7 +286,7 @@ function InteractiveSearchRow(props: InteractiveSearchRowProps) {
<Link <Link
className={styles.manualDownloadContent} className={styles.manualDownloadContent}
title="Override and add to download queue" title={translate('OverrideAndAddToDownloadQueue')}
onPress={onOverridePress} onPress={onOverridePress}
> >
<div className={styles.manualDownloadContent}> <div className={styles.manualDownloadContent}>
@ -307,9 +308,9 @@ function InteractiveSearchRow(props: InteractiveSearchRowProps) {
<ConfirmModal <ConfirmModal
isOpen={isConfirmGrabModalOpen} isOpen={isConfirmGrabModalOpen}
kind={kinds.WARNING} kind={kinds.WARNING}
title="Grab Release" title={translate('GrabRelease')}
message={`Sonarr was unable to determine which series and episode this release was for. Sonarr may be unable to automatically import this release. Do you want to grab '${title}'?`} message={translate('GrabReleaseMessageText', { title })}
confirmLabel="Grab" confirmLabel={translate('Grab')}
onConfirm={onGrabConfirm} onConfirm={onGrabConfirm}
onCancel={onGrabCancel} onCancel={onGrabCancel}
/> />

View File

@ -32,13 +32,17 @@ function SelectDownloadClientModalContent(
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader>{modalTitle} - Select Download Client</ModalHeader> <ModalHeader>
{translate('SelectDownloadClientModalTitle', { modalTitle })}
</ModalHeader>
<ModalBody> <ModalBody>
{isFetching ? <LoadingIndicator /> : null} {isFetching ? <LoadingIndicator /> : null}
{!isFetching && error ? ( {!isFetching && error ? (
<Alert kind={kinds.DANGER}>Unable to load download clients</Alert> <Alert kind={kinds.DANGER}>
{translate('DownloadClientsLoadError')}
</Alert>
) : null} ) : null}
{isPopulated && !error ? ( {isPopulated && !error ? (

View File

@ -1,5 +1,6 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import Link from 'Components/Link/Link'; import Link from 'Components/Link/Link';
import translate from 'Utilities/String/translate';
import styles from './SelectDownloadClientRow.css'; import styles from './SelectDownloadClientRow.css';
interface SelectSeasonRowProps { interface SelectSeasonRowProps {
@ -23,7 +24,7 @@ function SelectDownloadClientRow(props: SelectSeasonRowProps) {
onPress={onSeasonSelectWrapper} onPress={onSeasonSelectWrapper}
> >
<div>{name}</div> <div>{name}</div>
<div>Priority: {priority}</div> <div>{translate('PrioritySettings', { priority })}</div>
</Link> </Link>
); );
} }

View File

@ -56,7 +56,7 @@ interface OverrideMatchModalContentProps {
} }
function OverrideMatchModalContent(props: OverrideMatchModalContentProps) { function OverrideMatchModalContent(props: OverrideMatchModalContentProps) {
const modalTitle = 'Manual Grab'; const modalTitle = translate('ManualGrab');
const { const {
indexerId, indexerId,
title, title,
@ -185,16 +185,16 @@ function OverrideMatchModalContent(props: OverrideMatchModalContentProps) {
const onGrabPress = useCallback(() => { const onGrabPress = useCallback(() => {
if (!seriesId) { if (!seriesId) {
setError('Series must be selected'); setError(translate('OverrideGrabNoSeries'));
return; return;
} else if (!episodes.length) { } else if (!episodes.length) {
setError('At least one episode must be selected'); setError(translate('OverrideGrabNoEpisode'));
return; return;
} else if (!quality) { } else if (!quality) {
setError('Quality must be selected'); setError(translate('OverrideGrabNoQuality'));
return; return;
} else if (!languages.length) { } else if (!languages.length) {
setError('At least one language must be selected'); setError(translate('OverrideGrabNoLanguage'));
return; return;
} }
@ -239,7 +239,7 @@ function OverrideMatchModalContent(props: OverrideMatchModalContentProps) {
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader> <ModalHeader>
{translate('Override and Grab - {title}', { title })} {translate('OverrideGrabModalTitle', { title })}
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>
@ -317,7 +317,7 @@ function OverrideMatchModalContent(props: OverrideMatchModalContentProps) {
value={ value={
downloadClients.find( downloadClients.find(
(downloadClient) => downloadClient.id === downloadClientId (downloadClient) => downloadClient.id === downloadClientId
)?.name ?? 'Default' )?.name ?? translate('Default')
} }
onPress={onSelectDownloadClientPress} onPress={onSelectDownloadClientPress}
/> />

View File

@ -6,6 +6,7 @@ import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem'
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import Popover from 'Components/Tooltip/Popover'; import Popover from 'Components/Tooltip/Popover';
import { icons, tooltipPositions } from 'Helpers/Props'; import { icons, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './ReleaseSceneIndicator.css'; import styles from './ReleaseSceneIndicator.css';
function formatReleaseNumber( function formatReleaseNumber(
@ -32,7 +33,7 @@ function formatReleaseNumber(
} }
if (seasonNumber !== undefined) { if (seasonNumber !== undefined) {
return `Season ${seasonNumber}`; return translate('SeasonNumberToken', { seasonNumber });
} }
return null; return null;
@ -112,22 +113,28 @@ function ReleaseSceneIndicator(props: ReleaseSceneIndicatorProps) {
level = styles.levelMixed; level = styles.levelMixed;
messages.push( messages.push(
<div key="source"> <div key="source">
{comment ?? 'Source'} releases exist with ambiguous numbering, unable to {translate('ReleaseSceneIndicatorSourceMessage', {
reliably identify episode. message: comment ?? 'Source',
})}
</div> </div>
); );
} else if (isUnknown) { } else if (isUnknown) {
level = styles.levelUnknown; level = styles.levelUnknown;
messages.push( messages.push(
<div key="unknown"> <div key="unknown">
Numbering varies for this episode and release does not match any known {translate('ReleaseSceneIndicatorUnknownMessage')}
mappings.
</div> </div>
); );
if (sceneOrigin === 'unknown') { if (sceneOrigin === 'unknown') {
messages.push(<div key="origin">Assuming Scene numbering.</div>); messages.push(
<div key="origin">
{translate('ReleaseSceneIndicatorAssumingScene')}.
</div>
);
} else if (sceneOrigin === 'unknown:tvdb') { } else if (sceneOrigin === 'unknown:tvdb') {
messages.push(<div key="origin">Assuming TheTVDB numbering.</div>); messages.push(
<div key="origin">{translate('ReleaseSceneIndicatorAssumingTvdb')}</div>
);
} }
} else if (mappingDifferent) { } else if (mappingDifferent) {
level = styles.levelMapped; level = styles.levelMapped;
@ -142,11 +149,15 @@ function ReleaseSceneIndicator(props: ReleaseSceneIndicatorProps) {
if (mappedNumber) { if (mappedNumber) {
messages.push( messages.push(
<div key="not-requested"> <div key="not-requested">
Mapped episode wasn't requested in this search. {translate('ReleaseSceneIndicatorMappedNotRequested')}
</div> </div>
); );
} else { } else {
messages.push(<div key="unknown-series">Unknown episode or series.</div>); messages.push(
<div key="unknown-series">
{translate('ReleaseSceneIndicatorUnknownSeries')}
</div>
);
} }
} }
@ -156,7 +167,7 @@ function ReleaseSceneIndicator(props: ReleaseSceneIndicatorProps) {
<DescriptionListItem <DescriptionListItem
titleClassName={styles.title} titleClassName={styles.title}
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Mapping" title={translate('Mapping')}
data={comment} data={comment}
/> />
)} )}
@ -165,7 +176,7 @@ function ReleaseSceneIndicator(props: ReleaseSceneIndicatorProps) {
<DescriptionListItem <DescriptionListItem
titleClassName={styles.title} titleClassName={styles.title}
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Title" title={translate('Title')}
data={title} data={title}
/> />
)} )}
@ -174,7 +185,7 @@ function ReleaseSceneIndicator(props: ReleaseSceneIndicatorProps) {
<DescriptionListItem <DescriptionListItem
titleClassName={styles.title} titleClassName={styles.title}
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="Release" title={translate('Release')}
data={releaseNumber ?? 'unknown'} data={releaseNumber ?? 'unknown'}
/> />
)} )}
@ -183,7 +194,7 @@ function ReleaseSceneIndicator(props: ReleaseSceneIndicatorProps) {
<DescriptionListItem <DescriptionListItem
titleClassName={styles.title} titleClassName={styles.title}
descriptionClassName={styles.description} descriptionClassName={styles.description}
title="TheTVDB" title={translate('TheTvdb')}
data={mappedNumber ?? 'unknown'} data={mappedNumber ?? 'unknown'}
/> />
)} )}
@ -197,7 +208,7 @@ function ReleaseSceneIndicator(props: ReleaseSceneIndicatorProps) {
<Icon name={icons.SCENE_MAPPING} /> <Icon name={icons.SCENE_MAPPING} />
</div> </div>
} }
title="Scene Info" title={translate('SceneInfo')}
body={ body={
<div> <div>
{table} {table}

View File

@ -48,7 +48,9 @@
"AddRemotePathMappingError": "Unable to add a new remote path mapping, please try again.", "AddRemotePathMappingError": "Unable to add a new remote path mapping, please try again.",
"AddRootFolder": "Add Root Folder", "AddRootFolder": "Add Root Folder",
"AddSeriesWithTitle": "Add {title}", "AddSeriesWithTitle": "Add {title}",
"AddToDownloadQueue": "Add to download queue",
"Added": "Added", "Added": "Added",
"AddedToDownloadQueue": "Added to download queue",
"AddingTag": "Adding tag", "AddingTag": "Adding tag",
"AfterManualRefresh": "After Manual Refresh", "AfterManualRefresh": "After Manual Refresh",
"Age": "Age", "Age": "Age",
@ -241,6 +243,7 @@
"Dates": "Dates", "Dates": "Dates",
"Day": "Day", "Day": "Day",
"Debug": "Debug", "Debug": "Debug",
"Default": "Default",
"DefaultCase": "Default Case", "DefaultCase": "Default Case",
"DefaultDelayProfile": "This is the default profile. It applies to all series that don't have an explicit profile.", "DefaultDelayProfile": "This is the default profile. It applies to all series that don't have an explicit profile.",
"DefaultNotFoundMessage": "You must be lost, nothing to see here.", "DefaultNotFoundMessage": "You must be lost, nothing to see here.",
@ -494,7 +497,10 @@
"GeneralSettingsSummary": "Port, SSL, username/password, proxy, analytics and updates", "GeneralSettingsSummary": "Port, SSL, username/password, proxy, analytics and updates",
"Genres": "Genres", "Genres": "Genres",
"Global": "Global", "Global": "Global",
"Grab": "Grab",
"GrabId": "Grab ID", "GrabId": "Grab ID",
"GrabRelease": "GrabRelease",
"GrabReleaseMessageText": "Sonarr was unable to determine which series and episode this release was for. Sonarr may be unable to automatically import this release. Do you want to grab '{title}'?",
"GrabSelected": "Grab Selected", "GrabSelected": "Grab Selected",
"Grabbed": "Grabbed", "Grabbed": "Grabbed",
"GrabbedHistoryTooltip": "Episode grabbed from {indexer} and sent to {downloadClient}", "GrabbedHistoryTooltip": "Episode grabbed from {indexer} and sent to {downloadClient}",
@ -602,6 +608,7 @@
"InteractiveImportNoSeason": "Season must be chosen for each selected file", "InteractiveImportNoSeason": "Season must be chosen for each selected file",
"InteractiveImportNoSeries": "Series must be chosen for each selected file", "InteractiveImportNoSeries": "Series must be chosen for each selected file",
"InteractiveSearch": "Interactive Search", "InteractiveSearch": "Interactive Search",
"InteractiveSearchResultsFailedErrorMessage": "Search failed because its {message}. Try refreshing the series info and verify the necessary information is present before searching again.",
"Interval": "Interval", "Interval": "Interval",
"InvalidFormat": "Invalid Format", "InvalidFormat": "Invalid Format",
"KeyboardShortcuts": "Keyboard Shortcuts", "KeyboardShortcuts": "Keyboard Shortcuts",
@ -653,9 +660,11 @@
"ManageIndexers": "Manage Indexers", "ManageIndexers": "Manage Indexers",
"ManageLists": "Manage Lists", "ManageLists": "Manage Lists",
"Manual": "Manual", "Manual": "Manual",
"ManualGrab": "Manual Grab",
"ManualImport": "Manual Import", "ManualImport": "Manual Import",
"ManualImportItemsLoadError": "Unable to load manual import items", "ManualImportItemsLoadError": "Unable to load manual import items",
"MappedNetworkDrivesWindowsService": "Mapped network drives are not available when running as a Windows Service, see the [FAQ](https://wiki.servarr.com/sonarr/faq#why-cant-sonarr-see-my-files-on-a-remote-server) for more information.", "MappedNetworkDrivesWindowsService": "Mapped network drives are not available when running as a Windows Service, see the [FAQ](https://wiki.servarr.com/sonarr/faq#why-cant-sonarr-see-my-files-on-a-remote-server) for more information.",
"Mapping": "Mapping",
"MarkAsFailed": "Mark as Failed", "MarkAsFailed": "Mark as Failed",
"MarkAsFailedConfirmation": "Are you sure you want to mark '{sourceTitle}' as failed?", "MarkAsFailedConfirmation": "Are you sure you want to mark '{sourceTitle}' as failed?",
"MatchedToEpisodes": "Matched to Episodes", "MatchedToEpisodes": "Matched to Episodes",
@ -811,6 +820,12 @@
"OriginalLanguage": "Original Language", "OriginalLanguage": "Original Language",
"Other": "Other", "Other": "Other",
"OutputPath": "Output Path", "OutputPath": "Output Path",
"OverrideAndAddToDownloadQueue": "Override and add to download queue",
"OverrideGrabModalTitle": "Override and Grab - {title}",
"OverrideGrabNoEpisode": "At least one episode must be selected",
"OverrideGrabNoLanguage": "At least one language must be selected",
"OverrideGrabNoQuality": "Quality must be selected",
"OverrideGrabNoSeries": "Series must be selected",
"PackageVersion": "Package Version", "PackageVersion": "Package Version",
"PackageVersionInfo": "{packageVersion} by {packageAuthor}", "PackageVersionInfo": "{packageVersion} by {packageAuthor}",
"PartialSeason": "Partial Season", "PartialSeason": "Partial Season",
@ -907,6 +922,12 @@
"ReleaseProfiles": "Release Profiles", "ReleaseProfiles": "Release Profiles",
"ReleaseProfilesLoadError": "Unable to load Release Profiles", "ReleaseProfilesLoadError": "Unable to load Release Profiles",
"ReleaseRejected": "Release Rejected", "ReleaseRejected": "Release Rejected",
"ReleaseSceneIndicatorAssumingScene": "Assuming Scene numbering.",
"ReleaseSceneIndicatorAssumingTvdb": "Assuming TVDB numbering.",
"ReleaseSceneIndicatorMappedNotRequested": "Mapped episode wasn't requested in this search.",
"ReleaseSceneIndicatorSourceMessage": "{message} releases exist with ambiguous numbering, unable to reliably identify episode.",
"ReleaseSceneIndicatorUnknownMessage": "Numbering varies for this episode and release does not match any known mappings.",
"ReleaseSceneIndicatorUnknownSeries": "Unknown episode or series.",
"ReleaseTitle": "Release Title", "ReleaseTitle": "Release Title",
"Reload": "Reload", "Reload": "Reload",
"RemotePath": "Remote Path", "RemotePath": "Remote Path",
@ -1017,6 +1038,7 @@
"SaveChanges": "Save Changes", "SaveChanges": "Save Changes",
"SaveSettings": "Save Settings", "SaveSettings": "Save Settings",
"Scene": "Scene", "Scene": "Scene",
"SceneInfo": "Scene Info",
"SceneInformation": "Scene Information", "SceneInformation": "Scene Information",
"SceneNumberNotVerified": "Scene number hasn't been verified yet", "SceneNumberNotVerified": "Scene number hasn't been verified yet",
"SceneNumbering": "Scene Numbering", "SceneNumbering": "Scene Numbering",
@ -1044,6 +1066,7 @@
"Seasons": "Seasons", "Seasons": "Seasons",
"Security": "Security", "Security": "Security",
"Seeders": "Seeders", "Seeders": "Seeders",
"SelectDownloadClientModalTitle": "{modalTitle} - Select Download Client",
"SelectDropdown": "Select...", "SelectDropdown": "Select...",
"SelectEpisodes": "Select Episode(s)", "SelectEpisodes": "Select Episode(s)",
"SelectEpisodesModalTitle": "{modalTitle} - Select Episode(s)", "SelectEpisodesModalTitle": "{modalTitle} - Select Episode(s)",