From ea1863ac3a5d3051e07815d07df0d3f2abd9166f Mon Sep 17 00:00:00 2001 From: TheCatLady <52870424+TheCatLady@users.noreply.github.com> Date: Wed, 17 Mar 2021 22:28:44 -0400 Subject: [PATCH] fix(lang): UI string edits, round 2 (#1202) --- src/components/CollectionDetails/index.tsx | 2 +- .../Layout/LanguagePicker/index.tsx | 5 +- src/components/Layout/Sidebar/index.tsx | 13 +- src/components/Layout/index.tsx | 7 +- src/components/Login/index.tsx | 6 +- .../MovieDetails/MovieRecommendations.tsx | 16 +- src/components/MovieDetails/MovieSimilar.tsx | 16 +- src/components/MovieDetails/index.tsx | 20 ++- .../RequestModal/MovieRequestModal.tsx | 5 +- .../RequestModal/SearchByNameModal/index.tsx | 3 +- .../RequestModal/TvRequestModal.tsx | 3 +- .../ResetPassword/RequestResetLink.tsx | 17 +-- src/components/ResetPassword/index.tsx | 16 +- .../Notifications/NotificationsEmail.tsx | 10 +- src/components/Settings/RadarrModal/index.tsx | 14 +- .../Settings/SettingsAbout/Releases/index.tsx | 8 +- .../Settings/SettingsAbout/index.tsx | 24 ++- .../Settings/SettingsJobsCache/index.tsx | 9 ++ src/components/Settings/SettingsLayout.tsx | 6 +- .../Settings/SettingsLogs/index.tsx | 8 + src/components/Settings/SettingsMain.tsx | 24 +-- .../Settings/SettingsNotifications.tsx | 9 ++ src/components/Settings/SettingsPlex.tsx | 95 ++++++------ src/components/Settings/SettingsServices.tsx | 9 ++ .../Settings/SettingsUsers/index.tsx | 13 +- src/components/Settings/SonarrModal/index.tsx | 14 +- src/components/Setup/LoginWithPlex.tsx | 7 +- src/components/Setup/index.tsx | 18 +-- src/components/Slider/index.tsx | 11 +- src/components/StatusChacker/index.tsx | 6 +- .../TvDetails/TvRecommendations.tsx | 16 +- src/components/TvDetails/TvSimilar.tsx | 16 +- src/components/UserList/BulkEditModal.tsx | 4 +- .../UserGeneralSettings/index.tsx | 17 ++- .../UserNotificationSettings/index.tsx | 11 +- .../UserSettings/UserPasswordChange/index.tsx | 50 +++++-- .../UserSettings/UserPermissions/index.tsx | 11 +- .../UserProfile/UserSettings/index.tsx | 18 ++- src/i18n/globalMessages.ts | 2 + src/i18n/locale/en.json | 138 +++++++++--------- src/pages/404.tsx | 15 +- src/pages/_error.tsx | 36 +++-- 42 files changed, 435 insertions(+), 313 deletions(-) diff --git a/src/components/CollectionDetails/index.tsx b/src/components/CollectionDetails/index.tsx index 5953df1e..5f7cbcee 100644 --- a/src/components/CollectionDetails/index.tsx +++ b/src/components/CollectionDetails/index.tsx @@ -36,7 +36,7 @@ const messages = defineMessages({ requestcollection4k: 'Request Collection in 4K', requestswillbecreated4k: 'The following titles will have 4K requests created for them:', - requestSuccess: '{title} successfully requested!', + requestSuccess: '{title} requested successfully!', }); interface CollectionDetailsProps { diff --git a/src/components/Layout/LanguagePicker/index.tsx b/src/components/Layout/LanguagePicker/index.tsx index 77377ff9..e1aa8bc8 100644 --- a/src/components/Layout/LanguagePicker/index.tsx +++ b/src/components/Layout/LanguagePicker/index.tsx @@ -5,7 +5,7 @@ import { LanguageContext, AvailableLocales, } from '../../../context/LanguageContext'; -import { FormattedMessage, defineMessages } from 'react-intl'; +import { useIntl, defineMessages } from 'react-intl'; const messages = defineMessages({ changelanguage: 'Change Language', @@ -80,6 +80,7 @@ const availableLanguages: AvailableLanguageObject = { }; const LanguagePicker: React.FC = () => { + const intl = useIntl(); const dropdownRef = useRef(null); const { locale, setLocale } = useContext(LanguageContext); const [isDropdownOpen, setDropdownOpen] = useState(false); @@ -128,7 +129,7 @@ const LanguagePicker: React.FC = () => { htmlFor="language" className="block pb-2 text-sm font-medium leading-5 text-gray-300" > - + {intl.formatMessage(messages.changelanguage)} { return ( <> +

{intl.formatMessage(messages.logs)}

diff --git a/src/components/Settings/SettingsMain.tsx b/src/components/Settings/SettingsMain.tsx index 06c80092..db6d753a 100644 --- a/src/components/Settings/SettingsMain.tsx +++ b/src/components/Settings/SettingsMain.tsx @@ -13,8 +13,10 @@ import Badge from '../Common/Badge'; import globalMessages from '../../i18n/globalMessages'; import * as Yup from 'yup'; import RegionSelector from '../RegionSelector'; +import PageTitle from '../Common/PageTitle'; const messages = defineMessages({ + general: 'General', generalsettings: 'General Settings', generalsettingsDescription: 'Configure global and default settings for Overseerr.', @@ -24,21 +26,19 @@ const messages = defineMessages({ applicationTitle: 'Application Title', applicationurl: 'Application URL', region: 'Discover Region', - regionTip: - 'Filter content by region (only applies to the "Popular" and "Upcoming" categories)', + regionTip: 'Filter content by regional availability', originallanguage: 'Discover Language', - originallanguageTip: - 'Filter content by original language (only applies to the "Popular" and "Upcoming" categories)', - toastApiKeySuccess: 'New API key generated!', + originallanguageTip: 'Filter content by original language', + toastApiKeySuccess: 'New API key generated successfully!', toastApiKeyFailure: 'Something went wrong while generating a new API key.', - toastSettingsSuccess: 'Settings successfully saved!', + toastSettingsSuccess: 'Settings saved successfully!', toastSettingsFailure: 'Something went wrong while saving settings.', hideAvailable: 'Hide Available Media', csrfProtection: 'Enable CSRF Protection', csrfProtectionTip: - 'Sets external API access to read-only (requires HTTPS and Overseerr must be reloaded for changes to take effect)', + 'Sets external API access to read-only (requires HTTPS, and Overseerr must be reloaded for changes to take effect)', csrfProtectionHoverTip: - 'Do NOT enable this unless you understand what you are doing!', + 'Do NOT enable this setting unless you understand what you are doing!', trustProxy: 'Enable Proxy Support', trustProxyTip: 'Allows Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be reloaded for changes to take effect)', @@ -46,7 +46,7 @@ const messages = defineMessages({ validationApplicationUrl: 'You must provide a valid URL', validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash', originalLanguageDefault: 'All Languages', - partialRequestsEnabled: 'Enable Partial Series Requests', + partialRequestsEnabled: 'Allow Partial Series Requests', }); const SettingsMain: React.FC = () => { @@ -119,6 +119,12 @@ const SettingsMain: React.FC = () => { return ( <> +

{intl.formatMessage(messages.generalsettings)} diff --git a/src/components/Settings/SettingsNotifications.tsx b/src/components/Settings/SettingsNotifications.tsx index 64b9ba2f..ac2416b3 100644 --- a/src/components/Settings/SettingsNotifications.tsx +++ b/src/components/Settings/SettingsNotifications.tsx @@ -15,8 +15,11 @@ import LoadingSpinner from '../Common/LoadingSpinner'; import axios from 'axios'; import { useToasts } from 'react-toast-notifications'; import Button from '../Common/Button'; +import PageTitle from '../Common/PageTitle'; +import globalMessages from '../../i18n/globalMessages'; const messages = defineMessages({ + notifications: 'Notifications', save: 'Save Changes', saving: 'Saving…', notificationsettings: 'Notification Settings', @@ -174,6 +177,12 @@ const SettingsNotifications: React.FC = ({ children }) => { return ( <> +

{intl.formatMessage(messages.notificationsettings)} diff --git a/src/components/Settings/SettingsPlex.tsx b/src/components/Settings/SettingsPlex.tsx index be0eb40e..3161e3c9 100644 --- a/src/components/Settings/SettingsPlex.tsx +++ b/src/components/Settings/SettingsPlex.tsx @@ -9,12 +9,15 @@ import Button from '../Common/Button'; import axios from 'axios'; import LibraryItem from './LibraryItem'; import Badge from '../Common/Badge'; -import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; import * as Yup from 'yup'; import Alert from '../Common/Alert'; import Spinner from '../../assets/spinner.svg'; +import PageTitle from '../Common/PageTitle'; +import globalMessages from '../../i18n/globalMessages'; const messages = defineMessages({ + plex: 'Plex', plexsettings: 'Plex Settings', plexsettingsDescription: 'Configure the settings for your Plex server. Overseerr scans your Plex libraries to see what content is available.', @@ -30,17 +33,17 @@ const messages = defineMessages({ serverpresetRefreshing: 'Retrieving servers…', serverpresetLoad: 'Press the button to load available servers', toastPlexRefresh: 'Retrieving server list from Plex', - toastPlexRefreshSuccess: 'Retrieved server list from Plex', - toastPlexRefreshFailure: 'Unable to retrieve server list from Plex', - toastPlexConnecting: 'Attempting to connect to Plex server', - toastPlexConnectingSuccess: 'Connected to Plex server', - toastPlexConnectingFailure: 'Unable to connect to Plex server', + toastPlexRefreshSuccess: 'Plex server list retrieved successfully!', + toastPlexRefreshFailure: 'Failed to retrieve Plex server list.', + toastPlexConnecting: 'Attempting to connect to Plex…', + toastPlexConnectingSuccess: 'Plex connection established successfully!', + toastPlexConnectingFailure: 'Failed to connect to Plex.', settingUpPlex: 'Setting Up Plex', settingUpPlexDescription: 'To set up Plex, you can either enter your details manually \ or select a server retrieved from plex.tv.\ Press the button to the right of the dropdown to check connectivity and retrieve available servers.', - hostname: 'Hostname/IP', + hostname: 'Hostname or IP Address', port: 'Port', ssl: 'SSL', timeout: 'Timeout', @@ -59,8 +62,8 @@ const messages = defineMessages({ librariesRemaining: 'Libraries Remaining: {count}', startscan: 'Start Scan', cancelscan: 'Cancel Scan', - validationHostnameRequired: 'You must provide a hostname/IP', - validationPortRequired: 'You must provide a port', + validationHostnameRequired: 'You must provide a hostname or IP address', + validationPortRequired: 'You must provide a valid port number', }); interface Library { @@ -120,9 +123,9 @@ const SettingsPlex: React.FC = ({ onComplete }) => { /^(([a-z]|\d|_|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*)?([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])$/i, intl.formatMessage(messages.validationHostnameRequired) ), - port: Yup.number().required( - intl.formatMessage(messages.validationPortRequired) - ), + port: Yup.number() + .typeError(intl.formatMessage(messages.validationPortRequired)) + .required(intl.formatMessage(messages.validationPortRequired)), }); const activeLibraries = @@ -259,12 +262,16 @@ const SettingsPlex: React.FC = ({ onComplete }) => { } return ( <> +
-

- -

+

{intl.formatMessage(messages.plexsettings)}

- + {intl.formatMessage(messages.plexsettingsDescription)}

@@ -350,11 +357,9 @@ const SettingsPlex: React.FC = ({ onComplete }) => {
@@ -376,7 +381,7 @@ const SettingsPlex: React.FC = ({ onComplete }) => {
@@ -460,7 +465,7 @@ const SettingsPlex: React.FC = ({ onComplete }) => {
@@ -482,7 +487,7 @@ const SettingsPlex: React.FC = ({ onComplete }) => {
= ({ onComplete }) => {

- + {intl.formatMessage(messages.plexlibraries)}

- + {intl.formatMessage(messages.plexlibrariesDescription)}

@@ -581,11 +586,9 @@ const SettingsPlex: React.FC = ({ onComplete }) => {
-

- -

+

{intl.formatMessage(messages.manualscan)}

- + {intl.formatMessage(messages.manualscanDescription)}

@@ -615,28 +618,24 @@ const SettingsPlex: React.FC = ({ onComplete }) => { {dataSync.currentLibrary && (
- + {intl.formatMessage(messages.currentlibrary, { + name: dataSync.currentLibrary.name, + })}
)}
- - library.id === dataSync.currentLibrary?.id - ) + 1 - ).length - : 0, - }} - /> + {intl.formatMessage(messages.librariesRemaining, { + count: dataSync.currentLibrary + ? dataSync.libraries.slice( + dataSync.libraries.findIndex( + (library) => + library.id === dataSync.currentLibrary?.id + ) + 1 + ).length + : 0, + })}
@@ -658,7 +657,7 @@ const SettingsPlex: React.FC = ({ onComplete }) => { d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> - + {intl.formatMessage(messages.startscan)} )} @@ -678,7 +677,7 @@ const SettingsPlex: React.FC = ({ onComplete }) => { d="M6 18L18 6M6 6l12 12" /> - + {intl.formatMessage(messages.cancelscan)} )}
diff --git a/src/components/Settings/SettingsServices.tsx b/src/components/Settings/SettingsServices.tsx index bcccf8a5..faac3268 100644 --- a/src/components/Settings/SettingsServices.tsx +++ b/src/components/Settings/SettingsServices.tsx @@ -14,8 +14,11 @@ import Transition from '../Transition'; import axios from 'axios'; import SonarrModal from './SonarrModal'; import Alert from '../Common/Alert'; +import PageTitle from '../Common/PageTitle'; +import globalMessages from '../../i18n/globalMessages'; const messages = defineMessages({ + services: 'Services', radarrsettings: 'Radarr Settings', radarrSettingsDescription: 'Configure your Radarr connection below. You can have multiple Radarr configurations, but only two can be active as defaults at any time (one for standard HD and one for 4K). Administrators can override the server which is used for new requests.', @@ -214,6 +217,12 @@ const SettingsServices: React.FC = () => { return ( <> +

{intl.formatMessage(messages.radarrsettings)} diff --git a/src/components/Settings/SettingsUsers/index.tsx b/src/components/Settings/SettingsUsers/index.tsx index 35dfeea8..7986b700 100644 --- a/src/components/Settings/SettingsUsers/index.tsx +++ b/src/components/Settings/SettingsUsers/index.tsx @@ -8,13 +8,16 @@ import Button from '../../Common/Button'; import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import PermissionEdit from '../../PermissionEdit'; +import PageTitle from '../../Common/PageTitle'; +import globalMessages from '../../../i18n/globalMessages'; const messages = defineMessages({ - userSettings: 'Users', + users: 'Users', + userSettings: 'User Settings', userSettingsDescription: 'Configure global and default user settings.', save: 'Save Changes', saving: 'Saving…', - toastSettingsSuccess: 'Settings successfully saved!', + toastSettingsSuccess: 'User settings saved successfully!', toastSettingsFailure: 'Something went wrong while saving settings.', localLogin: 'Enable Local User Sign-In', defaultPermissions: 'Default User Permissions', @@ -33,6 +36,12 @@ const SettingsUsers: React.FC = () => { return ( <> +

{intl.formatMessage(messages.userSettings)}

diff --git a/src/components/Settings/SonarrModal/index.tsx b/src/components/Settings/SonarrModal/index.tsx index a9411f5d..709dc01d 100644 --- a/src/components/Settings/SonarrModal/index.tsx +++ b/src/components/Settings/SonarrModal/index.tsx @@ -12,13 +12,13 @@ const messages = defineMessages({ createsonarr: 'Add New Sonarr Server', editsonarr: 'Edit Sonarr Server', validationNameRequired: 'You must provide a server name', - validationHostnameRequired: 'You must provide a hostname/IP', - validationPortRequired: 'You must provide a port', + validationHostnameRequired: 'You must provide a hostname or IP address', + validationPortRequired: 'You must provide a valid port number', validationApiKeyRequired: 'You must provide an API key', validationRootFolderRequired: 'You must select a root folder', validationProfileRequired: 'You must select a quality profile', validationLanguageProfileRequired: 'You must select a language profile', - toastSonarrTestSuccess: 'Sonarr connection established!', + toastSonarrTestSuccess: 'Sonarr connection established successfully!', toastSonarrTestFailure: 'Failed to connect to Sonarr.', saving: 'Saving…', save: 'Save Changes', @@ -28,7 +28,7 @@ const messages = defineMessages({ defaultserver: 'Default Server', servername: 'Server Name', servernamePlaceholder: 'A Sonarr Server', - hostname: 'Hostname', + hostname: 'Hostname or IP Address', port: 'Port', ssl: 'SSL', apiKey: 'API Key', @@ -109,9 +109,9 @@ const SonarrModal: React.FC = ({ /^(([a-z]|\d|_|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*)?([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])$/i, intl.formatMessage(messages.validationHostnameRequired) ), - port: Yup.number().required( - intl.formatMessage(messages.validationPortRequired) - ), + port: Yup.number() + .typeError(intl.formatMessage(messages.validationPortRequired)) + .required(intl.formatMessage(messages.validationPortRequired)), apiKey: Yup.string().required( intl.formatMessage(messages.validationApiKeyRequired) ), diff --git a/src/components/Setup/LoginWithPlex.tsx b/src/components/Setup/LoginWithPlex.tsx index d62db786..30a49a7a 100644 --- a/src/components/Setup/LoginWithPlex.tsx +++ b/src/components/Setup/LoginWithPlex.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import { useUser } from '../../hooks/useUser'; import PlexLoginButton from '../PlexLoginButton'; import axios from 'axios'; -import { defineMessages, FormattedMessage } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; const messages = defineMessages({ welcome: 'Welcome to Overseerr', @@ -14,6 +14,7 @@ interface LoginWithPlexProps { } const LoginWithPlex: React.FC = ({ onComplete }) => { + const intl = useIntl(); const [authToken, setAuthToken] = useState(undefined); const { user, revalidate } = useUser(); @@ -45,10 +46,10 @@ const LoginWithPlex: React.FC = ({ onComplete }) => { return (

- + {intl.formatMessage(messages.welcome)}
- + {intl.formatMessage(messages.signinMessage)}
setAuthToken(authToken)} /> diff --git a/src/components/Setup/index.tsx b/src/components/Setup/index.tsx index b174887f..9a4d363e 100644 --- a/src/components/Setup/index.tsx +++ b/src/components/Setup/index.tsx @@ -7,7 +7,7 @@ import SettingsServices from '../Settings/SettingsServices'; import LoginWithPlex from './LoginWithPlex'; import SetupSteps from './SetupSteps'; import axios from 'axios'; -import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; import Badge from '../Common/Badge'; import LanguagePicker from '../Layout/LanguagePicker'; import PageTitle from '../Common/PageTitle'; @@ -63,11 +63,7 @@ const Setup: React.FC = () => {
- Logo + Logo
@@ -133,11 +129,9 @@ const Setup: React.FC = () => { onClick={() => finishSetup()} disabled={isUpdating} > - {isUpdating ? ( - - ) : ( - - )} + {isUpdating + ? intl.formatMessage(messages.finishing) + : intl.formatMessage(messages.finish)}
diff --git a/src/components/Slider/index.tsx b/src/components/Slider/index.tsx index d764d991..787c4ff4 100644 --- a/src/components/Slider/index.tsx +++ b/src/components/Slider/index.tsx @@ -8,7 +8,7 @@ import React, { } from 'react'; import { useSpring } from 'react-spring'; import TitleCard from '../TitleCard'; -import { defineMessages, FormattedMessage } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; const messages = defineMessages({ noresults: 'No results.', @@ -36,6 +36,7 @@ const Slider: React.FC = ({ emptyMessage, placeholder = , }) => { + const intl = useIntl(); const containerRef = useRef(null); const [scrollPos, setScrollPos] = useState({ isStart: true, isEnd: false }); @@ -230,11 +231,9 @@ const Slider: React.FC = ({ ))} {isEmpty && (
- {emptyMessage ? ( - emptyMessage - ) : ( - - )} + {emptyMessage + ? emptyMessage + : intl.formatMessage(messages.noresults)}
)}

diff --git a/src/components/StatusChacker/index.tsx b/src/components/StatusChacker/index.tsx index ce9b7341..e63fc98a 100644 --- a/src/components/StatusChacker/index.tsx +++ b/src/components/StatusChacker/index.tsx @@ -5,10 +5,10 @@ import Modal from '../Common/Modal'; import Transition from '../Transition'; const messages = defineMessages({ - newversionavailable: 'New Version Available', + newversionavailable: 'Application Update', newversionDescription: - 'An update is now available. Click the button below to reload the application.', - reloadOverseerr: 'Reload Overseerr', + 'Overseerr has been updated! Please click the button below to reload the page.', + reloadOverseerr: 'Reload', }); const StatusChecker: React.FC = () => { diff --git a/src/components/TvDetails/TvRecommendations.tsx b/src/components/TvDetails/TvRecommendations.tsx index cd6d4afa..c5aa7b04 100644 --- a/src/components/TvDetails/TvRecommendations.tsx +++ b/src/components/TvDetails/TvRecommendations.tsx @@ -5,22 +5,22 @@ import ListView from '../Common/ListView'; import { useRouter } from 'next/router'; import { LanguageContext } from '../../context/LanguageContext'; import Header from '../Common/Header'; -import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; import { TvDetails } from '../../../server/models/Tv'; import PageTitle from '../Common/PageTitle'; import Error from '../../pages/_error'; import useDiscover from '../../hooks/useDiscover'; +import Link from 'next/link'; const messages = defineMessages({ recommendations: 'Recommendations', - recommendationssubtext: 'If you liked {title}, you might also like…', }); const TvRecommendations: React.FC = () => { const router = useRouter(); const intl = useIntl(); const { locale } = useContext(LanguageContext); - const { data: tvData, error: tvError } = useSWR( + const { data: tvData } = useSWR( `/api/v1/tv/${router.query.tvId}?language=${locale}` ); const { @@ -45,14 +45,12 @@ const TvRecommendations: React.FC = () => {
+ {tvData?.name} + } > - + {intl.formatMessage(messages.recommendations)}
{ const router = useRouter(); const intl = useIntl(); const { locale } = useContext(LanguageContext); - const { data: tvData, error: tvError } = useSWR( + const { data: tvData } = useSWR( `/api/v1/tv/${router.query.tvId}?language=${locale}` ); const { @@ -43,14 +43,12 @@ const TvSimilar: React.FC = () => {
+ {tvData?.name} + } > - + {intl.formatMessage(messages.similar)}
{ return ( <> +

{intl.formatMessage(messages.generalsettings)} diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx index 677724b2..386e5e6d 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx @@ -14,8 +14,10 @@ import * as Yup from 'yup'; import Badge from '../../../Common/Badge'; import globalMessages from '../../../../i18n/globalMessages'; import { PgpLink } from '../../../Settings/Notifications/NotificationsEmail'; +import PageTitle from '../../../Common/PageTitle'; const messages = defineMessages({ + notifications: 'Notifications', notificationsettings: 'Notification Settings', enableNotifications: 'Enable Notifications', discordId: 'Discord User ID', @@ -33,7 +35,7 @@ const messages = defineMessages({ saving: 'Saving…', plexuser: 'Plex User', localuser: 'Local User', - toastSettingsSuccess: 'Settings successfully saved!', + toastSettingsSuccess: 'Notification settings saved successfully!', toastSettingsFailure: 'Something went wrong while saving settings.', pgpKey: 'PGP Public Key', pgpKeyTip: 'Encrypt email messages', @@ -70,6 +72,13 @@ const UserNotificationSettings: React.FC = () => { return ( <> +

{intl.formatMessage(messages.notificationsettings)} diff --git a/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx b/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx index 860d1ca1..98913b20 100644 --- a/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx +++ b/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx @@ -12,6 +12,8 @@ import Button from '../../../Common/Button'; import LoadingSpinner from '../../../Common/LoadingSpinner'; import * as Yup from 'yup'; import useSettings from '../../../../hooks/useSettings'; +import PageTitle from '../../../Common/PageTitle'; +import globalMessages from '../../../../i18n/globalMessages'; const messages = defineMessages({ password: 'Password', @@ -20,19 +22,23 @@ const messages = defineMessages({ confirmpassword: 'Confirm Password', save: 'Save Changes', saving: 'Saving…', - toastSettingsSuccess: 'Password changed!', - toastSettingsFailure: - 'Something went wrong while changing the password. Is your current password correct?', + toastSettingsSuccess: 'Password saved successfully!', + toastSettingsFailure: 'Something went wrong while saving the password.', + toastSettingsFailureVerifyCurrent: + 'Something went wrong while saving the password. Was your current password entered correctly?', validationCurrentPassword: 'You must provide your current password', validationNewPassword: 'You must provide a new password', validationNewPasswordLength: 'Password is too short; should be a minimum of 8 characters', - validationConfirmPassword: 'You must confirm your new password', - validationConfirmPasswordSame: 'Password must match', + validationConfirmPassword: 'You must confirm the new password', + validationConfirmPasswordSame: 'Passwords must match', nopasswordset: 'No Password Set', nopasswordsetDescription: 'This user account currently does not have a password specifically for {applicationTitle}.\ Configure a password below to enable this account to sign in as a "local user."', + nopasswordsetDescriptionOwnAccount: + 'Your account currently does not have a password specifically for {applicationTitle}.\ + Configure a password below to enable sign in as a "local user" using your email address.', nopermission: 'Unauthorized', nopermissionDescription: "You do not have permission to modify this user's password.", @@ -95,6 +101,13 @@ const UserPasswordChange: React.FC = () => { return ( <> +

{intl.formatMessage(messages.password)}

@@ -119,10 +132,17 @@ const UserPasswordChange: React.FC = () => { appearance: 'success', }); } catch (e) { - addToast(intl.formatMessage(messages.toastSettingsFailure), { - autoDismiss: true, - appearance: 'error', - }); + addToast( + intl.formatMessage( + data.hasPassword && user?.id === currentUser?.id + ? messages.toastSettingsFailureVerifyCurrent + : messages.toastSettingsFailure + ), + { + autoDismiss: true, + appearance: 'error', + } + ); } finally { revalidate(); resetForm(); @@ -137,9 +157,15 @@ const UserPasswordChange: React.FC = () => { type="warning" title={intl.formatMessage(messages.nopasswordset)} > - {intl.formatMessage(messages.nopasswordsetDescription, { - applicationTitle: settings.currentSettings.applicationTitle, - })} + {intl.formatMessage( + user?.id === currentUser?.id + ? messages.nopasswordsetDescriptionOwnAccount + : messages.nopasswordsetDescription, + { + applicationTitle: + settings.currentSettings.applicationTitle, + } + )} )} {data.hasPassword && user?.id === currentUser?.id && ( diff --git a/src/components/UserProfile/UserSettings/UserPermissions/index.tsx b/src/components/UserProfile/UserSettings/UserPermissions/index.tsx index ce4d2dc8..d7dc2778 100644 --- a/src/components/UserProfile/UserSettings/UserPermissions/index.tsx +++ b/src/components/UserProfile/UserSettings/UserPermissions/index.tsx @@ -11,6 +11,8 @@ import Button from '../../../Common/Button'; import LoadingSpinner from '../../../Common/LoadingSpinner'; import PermissionEdit from '../../../PermissionEdit'; import Alert from '../../../Common/Alert'; +import PageTitle from '../../../Common/PageTitle'; +import globalMessages from '../../../../i18n/globalMessages'; const messages = defineMessages({ displayName: 'Display Name', @@ -18,7 +20,7 @@ const messages = defineMessages({ saving: 'Saving…', plexuser: 'Plex User', localuser: 'Local User', - toastSettingsSuccess: 'Settings successfully saved!', + toastSettingsSuccess: 'Permissions saved successfully!', toastSettingsFailure: 'Something went wrong while saving settings.', permissions: 'Permissions', unauthorized: 'Unauthorized', @@ -60,6 +62,13 @@ const UserPermissions: React.FC = () => { return ( <> +

{intl.formatMessage(messages.permissions)}

diff --git a/src/components/UserProfile/UserSettings/index.tsx b/src/components/UserProfile/UserSettings/index.tsx index 392029d2..0ef74908 100644 --- a/src/components/UserProfile/UserSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/index.tsx @@ -10,10 +10,10 @@ import PageTitle from '../../Common/PageTitle'; import ProfileHeader from '../ProfileHeader'; import useSettings from '../../../hooks/useSettings'; import Alert from '../../Common/Alert'; +import globalMessages from '../../../i18n/globalMessages'; const messages = defineMessages({ - settings: 'User Settings', - menuGeneralSettings: 'General Settings', + menuGeneralSettings: 'General', menuChangePass: 'Password', menuNotifications: 'Notifications', menuPermissions: 'Permissions', @@ -115,7 +115,12 @@ const UserSettings: React.FC = ({ children }) => { if (currentUser?.id !== 1 && user.id === 1) { return ( <> - +
@@ -136,7 +141,12 @@ const UserSettings: React.FC = ({ children }) => { return ( <> - +
diff --git a/src/i18n/globalMessages.ts b/src/i18n/globalMessages.ts index daaace5e..7322994e 100644 --- a/src/i18n/globalMessages.ts +++ b/src/i18n/globalMessages.ts @@ -24,6 +24,8 @@ const globalMessages = defineMessages({ experimental: 'Experimental', advanced: 'Advanced', loading: 'Loading…', + settings: 'Settings', + usersettings: 'User Settings', }); export default globalMessages; diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 335c5b77..eb22316a 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -7,7 +7,7 @@ "components.CollectionDetails.overviewunavailable": "Overview unavailable.", "components.CollectionDetails.request": "Request", "components.CollectionDetails.request4k": "Request 4K", - "components.CollectionDetails.requestSuccess": "{title} successfully requested!", + "components.CollectionDetails.requestSuccess": "{title} requested successfully!", "components.CollectionDetails.requestcollection": "Request Collection", "components.CollectionDetails.requestcollection4k": "Request Collection in 4K", "components.CollectionDetails.requesting": "Requesting…", @@ -47,7 +47,7 @@ "components.Layout.UserDropdown.myprofile": "Profile", "components.Layout.UserDropdown.settings": "Settings", "components.Layout.UserDropdown.signout": "Sign Out", - "components.Layout.alphawarning": "This is ALPHA software. Features may be broken and/or unstable. Please report issues on GitHub!", + "components.Layout.alphawarning": "This is ALPHA software. Features may be broken and/or unstable. Please report any issues on GitHub!", "components.Login.email": "Email Address", "components.Login.forgotpassword": "Forgot Password?", "components.Login.loginerror": "Something went wrong while trying to sign in.", @@ -86,12 +86,10 @@ "components.MovieDetails.play4konplex": "Play 4K on Plex", "components.MovieDetails.playonplex": "Play on Plex", "components.MovieDetails.recommendations": "Recommendations", - "components.MovieDetails.recommendationssubtext": "If you liked {title}, you might also like…", "components.MovieDetails.releasedate": "Release Date", "components.MovieDetails.revenue": "Revenue", "components.MovieDetails.runtime": "{minutes} minutes", "components.MovieDetails.similar": "Similar Titles", - "components.MovieDetails.similarsubtext": "Other movies similar to {title}", "components.MovieDetails.status": "Status", "components.MovieDetails.studio": "{studioCount, plural, one {Studio} other {Studios}}", "components.MovieDetails.unavailable": "Unavailable", @@ -212,7 +210,7 @@ "components.RequestModal.SearchByNameModal.next": "Next", "components.RequestModal.SearchByNameModal.nosummary": "No summary for this title was found.", "components.RequestModal.SearchByNameModal.notvdbid": "Manual Match Required", - "components.RequestModal.SearchByNameModal.notvdbiddescription": "We couldn't automatically match your request. Please select the correct match from the list below:", + "components.RequestModal.SearchByNameModal.notvdbiddescription": "We couldn't automatically match your request. Please select the correct match from the list below.", "components.RequestModal.alreadyrequested": "Already Requested", "components.RequestModal.autoapproval": "Automatic Approval", "components.RequestModal.backbutton": "Back", @@ -222,7 +220,6 @@ "components.RequestModal.close": "Close", "components.RequestModal.errorediting": "Something went wrong while editing the request.", "components.RequestModal.extras": "Extras", - "components.RequestModal.next": "Next", "components.RequestModal.notrequested": "Not Requested", "components.RequestModal.numberofepisodes": "# of Episodes", "components.RequestModal.pending4krequest": "Pending Request for {title} in 4K", @@ -238,7 +235,7 @@ "components.RequestModal.requestcancelled": "Request canceled.", "components.RequestModal.requestedited": "Request edited.", "components.RequestModal.requesterror": "Something went wrong while submitting the request.", - "components.RequestModal.requestfrom": "There is currently a pending request from {username}", + "components.RequestModal.requestfrom": "There is currently a pending request from {username}.", "components.RequestModal.requesting": "Requesting…", "components.RequestModal.requestseasons": "Request {seasonCount} {seasonCount, plural, one {Season} other {Seasons}}", "components.RequestModal.requesttitle": "Request {title}", @@ -248,15 +245,15 @@ "components.RequestModal.status": "Status", "components.ResetPassword.confirmpassword": "Confirm Password", "components.ResetPassword.email": "Email Address", - "components.ResetPassword.emailresetlink": "Email Me a Recovery Link", - "components.ResetPassword.forgotpassword": "Reset your password", - "components.ResetPassword.gobacklogin": "Go Back to Sign-In Page", + "components.ResetPassword.emailresetlink": "Email a Recovery Link", + "components.ResetPassword.gobacklogin": "Return to Sign-In Page", "components.ResetPassword.password": "Password", + "components.ResetPassword.passwordreset": "Password Reset", "components.ResetPassword.requestresetlinksuccessmessage": "A password reset link will be sent to the provided email address if it is associated with a valid user.", "components.ResetPassword.resetpassword": "Reset your password", - "components.ResetPassword.resetpasswordsuccessmessage": "Password successfully reset, if the link was valid and associated with an existing user.", + "components.ResetPassword.resetpasswordsuccessmessage": "Password reset successfully!", "components.ResetPassword.validationemailrequired": "You must provide a valid email address", - "components.ResetPassword.validationpasswordmatch": "Password must match", + "components.ResetPassword.validationpasswordmatch": "Passwords must match", "components.ResetPassword.validationpasswordminchars": "Password is too short; should be a minimum of 8 characters", "components.ResetPassword.validationpasswordrequired": "You must provide a password", "components.Search.search": "Search", @@ -354,8 +351,8 @@ "components.Settings.Notifications.validationBotAPIRequired": "You must provide a bot authentication token", "components.Settings.Notifications.validationChatIdRequired": "You must provide a valid chat ID", "components.Settings.Notifications.validationEmail": "You must provide a valid email address", - "components.Settings.Notifications.validationSmtpHostRequired": "You must provide an SMTP host", - "components.Settings.Notifications.validationSmtpPortRequired": "You must provide an SMTP port", + "components.Settings.Notifications.validationSmtpHostRequired": "You must provide a hostname or IP address", + "components.Settings.Notifications.validationSmtpPortRequired": "You must provide a valid port number", "components.Settings.Notifications.validationUrl": "You must provide a valid URL", "components.Settings.Notifications.webhookUrl": "Webhook URL", "components.Settings.Notifications.webhookUrlPlaceholder": "Server Settings → Integrations → Webhooks", @@ -369,7 +366,7 @@ "components.Settings.RadarrModal.editradarr": "Edit Radarr Server", "components.Settings.RadarrModal.externalUrl": "External URL", "components.Settings.RadarrModal.externalUrlPlaceholder": "External URL pointing to your Radarr server", - "components.Settings.RadarrModal.hostname": "Hostname", + "components.Settings.RadarrModal.hostname": "Hostname or IP Address", "components.Settings.RadarrModal.loadingprofiles": "Loading quality profiles…", "components.Settings.RadarrModal.loadingrootfolders": "Loading root folders…", "components.Settings.RadarrModal.minimumAvailability": "Minimum Availability", @@ -392,28 +389,28 @@ "components.Settings.RadarrModal.testFirstRootFolders": "Test connection to load root folders", "components.Settings.RadarrModal.testing": "Testing…", "components.Settings.RadarrModal.toastRadarrTestFailure": "Failed to connect to Radarr.", - "components.Settings.RadarrModal.toastRadarrTestSuccess": "Radarr connection established!", + "components.Settings.RadarrModal.toastRadarrTestSuccess": "Radarr connection established successfully!", "components.Settings.RadarrModal.validationApiKeyRequired": "You must provide an API key", "components.Settings.RadarrModal.validationApplicationUrl": "You must provide a valid URL", "components.Settings.RadarrModal.validationApplicationUrlTrailingSlash": "URL must not end in a trailing slash", "components.Settings.RadarrModal.validationBaseUrlLeadingSlash": "Base URL must have a leading slash", "components.Settings.RadarrModal.validationBaseUrlTrailingSlash": "Base URL must not end in a trailing slash", - "components.Settings.RadarrModal.validationHostnameRequired": "You must provide a hostname/IP", + "components.Settings.RadarrModal.validationHostnameRequired": "You must provide a hostname or IP address", "components.Settings.RadarrModal.validationMinimumAvailabilityRequired": "You must select a minimum availability", "components.Settings.RadarrModal.validationNameRequired": "You must provide a server name", - "components.Settings.RadarrModal.validationPortRequired": "You must provide a port", + "components.Settings.RadarrModal.validationPortRequired": "You must provide a valid port number", "components.Settings.RadarrModal.validationProfileRequired": "You must select a quality profile", "components.Settings.RadarrModal.validationRootFolderRequired": "You must select a root folder", "components.Settings.SettingsAbout.Releases.currentversion": "Current Version", "components.Settings.SettingsAbout.Releases.latestversion": "Latest", "components.Settings.SettingsAbout.Releases.releasedataMissing": "Release data unavailable. Is GitHub down?", "components.Settings.SettingsAbout.Releases.releases": "Releases", - "components.Settings.SettingsAbout.Releases.runningDevelop": "You are running a develop version of Overseerr!", - "components.Settings.SettingsAbout.Releases.runningDevelopMessage": "The changes in your version will not be available below. Please see the GitHub repository for latest updates.", + "components.Settings.SettingsAbout.Releases.runningDevelop": "Development Version", + "components.Settings.SettingsAbout.Releases.runningDevelopMessage": "The latest changes to the develop branch of Overseerr are not shown below. Please see the commit history for this branch on GitHub for details.", "components.Settings.SettingsAbout.Releases.versionChangelog": "Version Changelog", "components.Settings.SettingsAbout.Releases.viewchangelog": "View Changelog", "components.Settings.SettingsAbout.Releases.viewongithub": "View on GitHub", - "components.Settings.SettingsAbout.clickheretojoindiscord": "Click here to join our Discord server!", + "components.Settings.SettingsAbout.about": "About", "components.Settings.SettingsAbout.documentation": "Documentation", "components.Settings.SettingsAbout.gettingsupport": "Getting Support", "components.Settings.SettingsAbout.githubdiscussions": "GitHub Discussions", @@ -421,7 +418,7 @@ "components.Settings.SettingsAbout.overseerrinformation": "Overseerr Information", "components.Settings.SettingsAbout.preferredmethod": "Preferred", "components.Settings.SettingsAbout.supportoverseerr": "Support Overseerr", - "components.Settings.SettingsAbout.timezone": "Timezone", + "components.Settings.SettingsAbout.timezone": "Time Zone", "components.Settings.SettingsAbout.totalmedia": "Total Media", "components.Settings.SettingsAbout.totalrequests": "Total Requests", "components.Settings.SettingsAbout.version": "Version", @@ -443,6 +440,7 @@ "components.Settings.SettingsJobsCache.jobname": "Job Name", "components.Settings.SettingsJobsCache.jobs": "Jobs", "components.Settings.SettingsJobsCache.jobsDescription": "Overseerr performs certain maintenance tasks as regularly-scheduled jobs, but they can also be manually triggered below. Manually running a job will not alter its schedule.", + "components.Settings.SettingsJobsCache.jobsandcache": "Jobs & Cache", "components.Settings.SettingsJobsCache.jobstarted": "{jobname} started.", "components.Settings.SettingsJobsCache.jobtype": "Type", "components.Settings.SettingsJobsCache.nextexecution": "Next Execution", @@ -476,9 +474,10 @@ "components.Settings.SettingsUsers.save": "Save Changes", "components.Settings.SettingsUsers.saving": "Saving…", "components.Settings.SettingsUsers.toastSettingsFailure": "Something went wrong while saving settings.", - "components.Settings.SettingsUsers.toastSettingsSuccess": "Settings successfully saved!", - "components.Settings.SettingsUsers.userSettings": "Users", + "components.Settings.SettingsUsers.toastSettingsSuccess": "User settings saved successfully!", + "components.Settings.SettingsUsers.userSettings": "User Settings", "components.Settings.SettingsUsers.userSettingsDescription": "Configure global and default user settings.", + "components.Settings.SettingsUsers.users": "Users", "components.Settings.SonarrModal.add": "Add Server", "components.Settings.SonarrModal.animelanguageprofile": "Anime Language Profile", "components.Settings.SonarrModal.animequalityprofile": "Anime Quality Profile", @@ -492,7 +491,7 @@ "components.Settings.SonarrModal.editsonarr": "Edit Sonarr Server", "components.Settings.SonarrModal.externalUrl": "External URL", "components.Settings.SonarrModal.externalUrlPlaceholder": "External URL pointing to your Sonarr server", - "components.Settings.SonarrModal.hostname": "Hostname", + "components.Settings.SonarrModal.hostname": "Hostname or IP Address", "components.Settings.SonarrModal.languageprofile": "Language Profile", "components.Settings.SonarrModal.loadinglanguageprofiles": "Loading language profiles…", "components.Settings.SonarrModal.loadingprofiles": "Loading quality profiles…", @@ -518,16 +517,16 @@ "components.Settings.SonarrModal.testFirstRootFolders": "Test connection to load root folders", "components.Settings.SonarrModal.testing": "Testing…", "components.Settings.SonarrModal.toastSonarrTestFailure": "Failed to connect to Sonarr.", - "components.Settings.SonarrModal.toastSonarrTestSuccess": "Sonarr connection established!", + "components.Settings.SonarrModal.toastSonarrTestSuccess": "Sonarr connection established successfully!", "components.Settings.SonarrModal.validationApiKeyRequired": "You must provide an API key", "components.Settings.SonarrModal.validationApplicationUrl": "You must provide a valid URL", "components.Settings.SonarrModal.validationApplicationUrlTrailingSlash": "URL must not end in a trailing slash", "components.Settings.SonarrModal.validationBaseUrlLeadingSlash": "Base URL must have a leading slash", "components.Settings.SonarrModal.validationBaseUrlTrailingSlash": "Base URL must not end in a trailing slash", - "components.Settings.SonarrModal.validationHostnameRequired": "You must provide a hostname/IP", + "components.Settings.SonarrModal.validationHostnameRequired": "You must provide a hostname or IP address", "components.Settings.SonarrModal.validationLanguageProfileRequired": "You must select a language profile", "components.Settings.SonarrModal.validationNameRequired": "You must provide a server name", - "components.Settings.SonarrModal.validationPortRequired": "You must provide a port", + "components.Settings.SonarrModal.validationPortRequired": "You must provide a valid port number", "components.Settings.SonarrModal.validationProfileRequired": "You must select a quality profile", "components.Settings.SonarrModal.validationRootFolderRequired": "You must select a root folder", "components.Settings.activeProfile": "Active Profile", @@ -540,8 +539,8 @@ "components.Settings.cancelscan": "Cancel Scan", "components.Settings.copied": "Copied API key to clipboard.", "components.Settings.csrfProtection": "Enable CSRF Protection", - "components.Settings.csrfProtectionHoverTip": "Do NOT enable this unless you understand what you are doing!", - "components.Settings.csrfProtectionTip": "Sets external API access to read-only (requires HTTPS and Overseerr must be reloaded for changes to take effect)", + "components.Settings.csrfProtectionHoverTip": "Do NOT enable this setting unless you understand what you are doing!", + "components.Settings.csrfProtectionTip": "Sets external API access to read-only (requires HTTPS, and Overseerr must be reloaded for changes to take effect)", "components.Settings.currentlibrary": "Current Library: {name}", "components.Settings.default": "Default", "components.Settings.default4k": "Default 4K", @@ -550,15 +549,16 @@ "components.Settings.edit": "Edit", "components.Settings.email": "Email", "components.Settings.enablenotifications": "Enable Notifications", + "components.Settings.general": "General", "components.Settings.generalsettings": "General Settings", "components.Settings.generalsettingsDescription": "Configure global and default settings for Overseerr.", "components.Settings.hideAvailable": "Hide Available Media", - "components.Settings.hostname": "Hostname/IP", + "components.Settings.hostname": "Hostname or IP Address", "components.Settings.librariesRemaining": "Libraries Remaining: {count}", "components.Settings.manualscan": "Manual Library Scan", "components.Settings.manualscanDescription": "Normally, this will only be run once every 24 hours. Overseerr will check your Plex server's recently added more aggressively. If this is your first time configuring Plex, a one-time full manual library scan is recommended!", "components.Settings.menuAbout": "About", - "components.Settings.menuGeneralSettings": "General Settings", + "components.Settings.menuGeneralSettings": "General", "components.Settings.menuJobs": "Jobs & Cache", "components.Settings.menuLogs": "Logs", "components.Settings.menuNotifications": "Notifications", @@ -569,6 +569,7 @@ "components.Settings.nodefaultdescription": "At least one server must be marked as default before any requests will make it to your services.", "components.Settings.notificationAgentSettingsDescription": "Choose the types of notifications to send, and which notification agents to use.", "components.Settings.notificationAgentsSettings": "Notification Agents", + "components.Settings.notifications": "Notifications", "components.Settings.notificationsettings": "Notification Settings", "components.Settings.notificationsettingsDescription": "Configure global notification settings. The options below will apply to all notification agents.", "components.Settings.notificationsettingsfailed": "Notification settings failed to save.", @@ -576,8 +577,9 @@ "components.Settings.notrunning": "Not Running", "components.Settings.originalLanguageDefault": "All Languages", "components.Settings.originallanguage": "Discover Language", - "components.Settings.originallanguageTip": "Filter content by original language (only applies to the \"Popular\" and \"Upcoming\" categories)", - "components.Settings.partialRequestsEnabled": "Enable Partial Series Requests", + "components.Settings.originallanguageTip": "Filter content by original language", + "components.Settings.partialRequestsEnabled": "Allow Partial Series Requests", + "components.Settings.plex": "Plex", "components.Settings.plexlibraries": "Plex Libraries", "components.Settings.plexlibrariesDescription": "The libraries Overseerr scans for titles. Set up and save your Plex connection settings, then click the button below if no libraries are listed.", "components.Settings.plexsettings": "Plex Settings", @@ -586,7 +588,7 @@ "components.Settings.radarrSettingsDescription": "Configure your Radarr connection below. You can have multiple Radarr configurations, but only two can be active as defaults at any time (one for standard HD and one for 4K). Administrators can override the server which is used for new requests.", "components.Settings.radarrsettings": "Radarr Settings", "components.Settings.region": "Discover Region", - "components.Settings.regionTip": "Filter content by region (only applies to the \"Popular\" and \"Upcoming\" categories)", + "components.Settings.regionTip": "Filter content by regional availability", "components.Settings.save": "Save Changes", "components.Settings.saving": "Saving…", "components.Settings.scan": "Scan Plex Libraries", @@ -602,31 +604,31 @@ "components.Settings.serverpresetManualMessage": "Manual configuration", "components.Settings.serverpresetPlaceholder": "Plex Server", "components.Settings.serverpresetRefreshing": "Retrieving servers…", + "components.Settings.services": "Services", "components.Settings.settingUpPlex": "Setting Up Plex", "components.Settings.settingUpPlexDescription": "To set up Plex, you can either enter your details manually or select a server retrieved from plex.tv. Press the button to the right of the dropdown to check connectivity and retrieve available servers.", - "components.Settings.settings": "Settings", "components.Settings.sonarrSettingsDescription": "Configure your Sonarr connection below. You can have multiple Sonarr configurations, but only two can be active as defaults at any time (one for standard HD and one for 4K). Administrators can override the server which is used for new requests.", "components.Settings.sonarrsettings": "Sonarr Settings", "components.Settings.ssl": "SSL", "components.Settings.startscan": "Start Scan", "components.Settings.timeout": "Timeout", "components.Settings.toastApiKeyFailure": "Something went wrong while generating a new API key.", - "components.Settings.toastApiKeySuccess": "New API key generated!", + "components.Settings.toastApiKeySuccess": "New API key generated successfully!", "components.Settings.toastPlexConnecting": "Attempting to connect to Plex…", - "components.Settings.toastPlexConnectingFailure": "Unable to connect to Plex!", - "components.Settings.toastPlexConnectingSuccess": "Connected to Plex server.", + "components.Settings.toastPlexConnectingFailure": "Failed to connect to Plex.", + "components.Settings.toastPlexConnectingSuccess": "Plex connection established successfully!", "components.Settings.toastPlexRefresh": "Retrieving server list from Plex…", - "components.Settings.toastPlexRefreshFailure": "Unable to retrieve Plex server list!", - "components.Settings.toastPlexRefreshSuccess": "Retrieved Plex server list.", + "components.Settings.toastPlexRefreshFailure": "Failed to retrieve Plex server list.", + "components.Settings.toastPlexRefreshSuccess": "Plex server list retrieved successfully!", "components.Settings.toastSettingsFailure": "Something went wrong while saving settings.", - "components.Settings.toastSettingsSuccess": "Settings successfully saved!", + "components.Settings.toastSettingsSuccess": "Settings saved successfully!", "components.Settings.trustProxy": "Enable Proxy Support", "components.Settings.trustProxyTip": "Allows Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be reloaded for changes to take effect)", "components.Settings.validationApplicationTitle": "You must provide an application title", "components.Settings.validationApplicationUrl": "You must provide a valid URL", "components.Settings.validationApplicationUrlTrailingSlash": "URL must not end in a trailing slash", - "components.Settings.validationHostnameRequired": "You must provide a hostname/IP", - "components.Settings.validationPortRequired": "You must provide a port", + "components.Settings.validationHostnameRequired": "You must provide a hostname or IP address", + "components.Settings.validationPortRequired": "You must provide a valid port number", "components.Settings.webhook": "Webhook", "components.Setup.configureplex": "Configure Plex", "components.Setup.configureservices": "Configure Services", @@ -641,9 +643,9 @@ "components.Setup.welcome": "Welcome to Overseerr", "components.Slider.noresults": "No results.", "components.StatusBadge.status4k": "4K {status}", - "components.StatusChacker.newversionDescription": "An update is now available. Click the button below to reload the application.", - "components.StatusChacker.newversionavailable": "New Version Available", - "components.StatusChacker.reloadOverseerr": "Reload Overseerr", + "components.StatusChacker.newversionDescription": "Overseerr has been updated! Please click the button below to reload the page.", + "components.StatusChacker.newversionavailable": "Application Update", + "components.StatusChacker.reloadOverseerr": "Reload", "components.TitleCard.movie": "Movie", "components.TitleCard.tvshow": "Series", "components.TvDetails.TvCast.fullseriescast": "Full Series Cast", @@ -678,11 +680,9 @@ "components.TvDetails.play4konplex": "Play 4K on Plex", "components.TvDetails.playonplex": "Play on Plex", "components.TvDetails.recommendations": "Recommendations", - "components.TvDetails.recommendationssubtext": "If you liked {title}, you might also like…", "components.TvDetails.seasons": "{seasonCount, plural, one {# Season} other {# Seasons}}", "components.TvDetails.showtype": "Series Type", "components.TvDetails.similar": "Similar Series", - "components.TvDetails.similarsubtext": "Other series similar to {title}", "components.TvDetails.status": "Status", "components.TvDetails.unavailable": "Unavailable", "components.TvDetails.userrating": "User Rating", @@ -730,10 +730,10 @@ "components.UserList.usercreatedsuccess": "User created successfully!", "components.UserList.userdeleted": "User deleted.", "components.UserList.userdeleteerror": "Something went wrong while deleting the user.", - "components.UserList.userfail": "Something went wrong while saving the user.", + "components.UserList.userfail": "Something went wrong while saving user permissions.", "components.UserList.userlist": "User List", "components.UserList.users": "Users", - "components.UserList.userssaved": "Users saved!", + "components.UserList.userssaved": "User permissions saved successfully!", "components.UserList.validationEmail": "You must provide a valid email address", "components.UserList.validationpasswordminchars": "Password is too short; should be a minimum of 8 characters", "components.UserProfile.ProfileHeader.joindate": "Joined {joindate}", @@ -744,26 +744,28 @@ "components.UserProfile.UserSettings.UserGeneralSettings.accounttype": "Account Type", "components.UserProfile.UserSettings.UserGeneralSettings.admin": "Admin", "components.UserProfile.UserSettings.UserGeneralSettings.displayName": "Display Name", + "components.UserProfile.UserSettings.UserGeneralSettings.general": "General", "components.UserProfile.UserSettings.UserGeneralSettings.generalsettings": "General Settings", "components.UserProfile.UserSettings.UserGeneralSettings.languageServerDefault": "Default ({language})", "components.UserProfile.UserSettings.UserGeneralSettings.localuser": "Local User", "components.UserProfile.UserSettings.UserGeneralSettings.originalLanguageDefault": "All Languages", "components.UserProfile.UserSettings.UserGeneralSettings.originallanguage": "Discover Language", - "components.UserProfile.UserSettings.UserGeneralSettings.originallanguageTip": "Filter content by original language (only applies to the \"Popular\" and \"Upcoming\" categories)", + "components.UserProfile.UserSettings.UserGeneralSettings.originallanguageTip": "Filter content by original language", "components.UserProfile.UserSettings.UserGeneralSettings.owner": "Owner", "components.UserProfile.UserSettings.UserGeneralSettings.plexuser": "Plex User", "components.UserProfile.UserSettings.UserGeneralSettings.region": "Discover Region", - "components.UserProfile.UserSettings.UserGeneralSettings.regionTip": "Filter content by region (only applies to the \"Popular\" and \"Upcoming\" categories)", + "components.UserProfile.UserSettings.UserGeneralSettings.regionTip": "Filter content by regional availability", "components.UserProfile.UserSettings.UserGeneralSettings.role": "Role", "components.UserProfile.UserSettings.UserGeneralSettings.save": "Save Changes", "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Saving…", "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsFailure": "Something went wrong while saving settings.", - "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Settings successfully saved!", + "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Settings saved successfully!", "components.UserProfile.UserSettings.UserGeneralSettings.user": "User", "components.UserProfile.UserSettings.UserNotificationSettings.discordId": "Discord ID", "components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "The ID number for your Discord user account", "components.UserProfile.UserSettings.UserNotificationSettings.enableNotifications": "Enable Notifications", "components.UserProfile.UserSettings.UserNotificationSettings.localuser": "Local User", + "components.UserProfile.UserSettings.UserNotificationSettings.notifications": "Notifications", "components.UserProfile.UserSettings.UserNotificationSettings.notificationsettings": "Notification Settings", "components.UserProfile.UserSettings.UserNotificationSettings.pgpKey": "PGP Public Key", "components.UserProfile.UserSettings.UserNotificationSettings.pgpKeyTip": "Encrypt email messages", @@ -776,7 +778,7 @@ "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTip": "Add @get_id_bot to the chat", "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Start a chat, add @get_id_bot, and issue the /my_id command", "components.UserProfile.UserSettings.UserNotificationSettings.toastSettingsFailure": "Something went wrong while saving settings.", - "components.UserProfile.UserSettings.UserNotificationSettings.toastSettingsSuccess": "Settings successfully saved!", + "components.UserProfile.UserSettings.UserNotificationSettings.toastSettingsSuccess": "Notification settings saved successfully!", "components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "You must provide a valid Discord user ID", "components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "You must provide a valid Telegram chat ID", "components.UserProfile.UserSettings.UserPasswordChange.confirmpassword": "Confirm Password", @@ -784,15 +786,17 @@ "components.UserProfile.UserSettings.UserPasswordChange.newpassword": "New Password", "components.UserProfile.UserSettings.UserPasswordChange.nopasswordset": "No Password Set", "components.UserProfile.UserSettings.UserPasswordChange.nopasswordsetDescription": "This user account currently does not have a password specifically for {applicationTitle}. Configure a password below to enable this account to sign in as a \"local user.\"", + "components.UserProfile.UserSettings.UserPasswordChange.nopasswordsetDescriptionOwnAccount": "Your account currently does not have a password specifically for {applicationTitle}. Configure a password below to enable sign in as a \"local user\" using your email address.", "components.UserProfile.UserSettings.UserPasswordChange.nopermission": "Unauthorized", "components.UserProfile.UserSettings.UserPasswordChange.nopermissionDescription": "You do not have permission to modify this user's password.", "components.UserProfile.UserSettings.UserPasswordChange.password": "Password", "components.UserProfile.UserSettings.UserPasswordChange.save": "Save Changes", "components.UserProfile.UserSettings.UserPasswordChange.saving": "Saving…", - "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailure": "Something went wrong while changing the password. Is your current password correct?", - "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsSuccess": "Password changed!", - "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPassword": "You must confirm your new password", - "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPasswordSame": "Password must match", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailure": "Something went wrong while saving the password.", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailureVerifyCurrent": "Something went wrong while saving the password. Was your current password entered correctly?", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsSuccess": "Password saved successfully!", + "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPassword": "You must confirm the new password", + "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPasswordSame": "Passwords must match", "components.UserProfile.UserSettings.UserPasswordChange.validationCurrentPassword": "You must provide your current password", "components.UserProfile.UserSettings.UserPasswordChange.validationNewPassword": "You must provide a new password", "components.UserProfile.UserSettings.UserPasswordChange.validationNewPasswordLength": "Password is too short; should be a minimum of 8 characters", @@ -803,14 +807,13 @@ "components.UserProfile.UserSettings.UserPermissions.save": "Save Changes", "components.UserProfile.UserSettings.UserPermissions.saving": "Saving…", "components.UserProfile.UserSettings.UserPermissions.toastSettingsFailure": "Something went wrong while saving settings.", - "components.UserProfile.UserSettings.UserPermissions.toastSettingsSuccess": "Settings successfully saved!", + "components.UserProfile.UserSettings.UserPermissions.toastSettingsSuccess": "Permissions saved successfully!", "components.UserProfile.UserSettings.UserPermissions.unauthorized": "Unauthorized", "components.UserProfile.UserSettings.UserPermissions.unauthorizedDescription": "You cannot modify your own permissions.", "components.UserProfile.UserSettings.menuChangePass": "Password", - "components.UserProfile.UserSettings.menuGeneralSettings": "General Settings", + "components.UserProfile.UserSettings.menuGeneralSettings": "General", "components.UserProfile.UserSettings.menuNotifications": "Notifications", "components.UserProfile.UserSettings.menuPermissions": "Permissions", - "components.UserProfile.UserSettings.settings": "User Settings", "components.UserProfile.UserSettings.unauthorized": "Unauthorized", "components.UserProfile.UserSettings.unauthorizedDescription": "You do not have permission to modify this user's settings.", "components.UserProfile.norequests": "No Requests", @@ -836,12 +839,15 @@ "i18n.request": "Request", "i18n.requested": "Requested", "i18n.retry": "Retry", + "i18n.settings": "Settings", "i18n.tvshows": "Series", "i18n.unavailable": "Unavailable", - "pages.internalServerError": "{statusCode} - Internal server error", + "i18n.usersettings": "User Settings", + "pages.errormessagewithcode": "404 - {error}", + "pages.internalservererror": "Internal Server Error", "pages.oops": "Oops", - "pages.pageNotFound": "404 - Page Not Found", + "pages.pagenotfound": "Page Not Found", "pages.returnHome": "Return Home", - "pages.serviceUnavailable": "{statusCode} - Service unavailable", - "pages.somethingWentWrong": "{statusCode} - Something went wrong" + "pages.serviceunavailable": "Service Unavailable", + "pages.somethingwentwrong": "Something Went Wrong" } diff --git a/src/pages/404.tsx b/src/pages/404.tsx index deabedb7..5ec79d8b 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -1,21 +1,28 @@ import Link from 'next/link'; import React from 'react'; -import { defineMessages, FormattedMessage } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; +import PageTitle from '../components/Common/PageTitle'; const messages = defineMessages({ - pageNotFound: '404 - Page Not Found', + errormessagewithcode: '404 - {error}', + pagenotfound: 'Page Not Found', returnHome: 'Return Home', }); const Custom404: React.FC = () => { + const intl = useIntl(); + return (
+
- + {intl.formatMessage(messages.errormessagewithcode, { + error: intl.formatMessage(messages.pagenotfound), + })}
- + {intl.formatMessage(messages.returnHome)} = ({ statusCode }) => { const getErrorMessage = (statusCode?: number) => { switch (statusCode) { case 500: - return intl.formatMessage(messages.internalServerError, { - statusCode: 500, - }); + return intl.formatMessage(messages.internalservererror); case 503: - return intl.formatMessage(messages.serviceUnavailable, { - statusCode: 503, - }); + return intl.formatMessage(messages.serviceunavailable); default: - return intl.formatMessage(messages.somethingWentWrong, { - statusCode: statusCode ?? intl.formatMessage(messages.oops), - }); + return statusCode + ? intl.formatMessage(messages.somethingwentwrong) + : intl.formatMessage(messages.oops); } }; return (