mirror of
https://github.com/sct/overseerr.git
synced 2025-09-28 13:04:23 +02:00
feat(frontend): added more localized strings
This commit is contained in:
@@ -1,8 +1,14 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import useClipboard from 'react-use-clipboard';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
copied: 'Copied API key to clipboard',
|
||||
});
|
||||
|
||||
const CopyButton: React.FC<{ textToCopy: string }> = ({ textToCopy }) => {
|
||||
const intl = useIntl();
|
||||
const [isCopied, setCopied] = useClipboard(textToCopy, {
|
||||
successDuration: 1000,
|
||||
});
|
||||
@@ -10,12 +16,12 @@ const CopyButton: React.FC<{ textToCopy: string }> = ({ textToCopy }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (isCopied) {
|
||||
addToast('Copied API key to clipboard', {
|
||||
addToast(intl.formatMessage(messages.copied), {
|
||||
appearance: 'info',
|
||||
autoDismiss: true,
|
||||
});
|
||||
}
|
||||
}, [isCopied, addToast]);
|
||||
}, [isCopied, addToast, intl]);
|
||||
|
||||
return (
|
||||
<button
|
||||
|
@@ -10,6 +10,10 @@ import * as Yup from 'yup';
|
||||
const messages = defineMessages({
|
||||
save: 'Save Changes',
|
||||
saving: 'Saving...',
|
||||
agentenabled: 'Agent Enabled',
|
||||
webhookUrl: 'Webhook URL',
|
||||
validationWebhookUrlRequired: 'You must provide a webhook URL',
|
||||
webhookUrlPlaceholder: 'Server Settings -> Integrations -> Webhooks',
|
||||
});
|
||||
|
||||
const NotificationsDiscord: React.FC = () => {
|
||||
@@ -19,7 +23,9 @@ const NotificationsDiscord: React.FC = () => {
|
||||
);
|
||||
|
||||
const NotificationsDiscordSchema = Yup.object().shape({
|
||||
webhookUrl: Yup.string().required('You must provide a webhook URL'),
|
||||
webhookUrl: Yup.string().required(
|
||||
intl.formatMessage(messages.validationWebhookUrlRequired)
|
||||
),
|
||||
});
|
||||
|
||||
if (!data && !error) {
|
||||
@@ -58,7 +64,7 @@ const NotificationsDiscord: React.FC = () => {
|
||||
htmlFor="isDefault"
|
||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
||||
>
|
||||
Agent Enabled
|
||||
{intl.formatMessage(messages.agentenabled)}
|
||||
</label>
|
||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<Field
|
||||
@@ -74,7 +80,7 @@ const NotificationsDiscord: React.FC = () => {
|
||||
htmlFor="name"
|
||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
||||
>
|
||||
Webhook URL
|
||||
{intl.formatMessage(messages.webhookUrl)}
|
||||
</label>
|
||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
||||
@@ -82,7 +88,9 @@ const NotificationsDiscord: React.FC = () => {
|
||||
id="webhookUrl"
|
||||
name="webhookUrl"
|
||||
type="text"
|
||||
placeholder="Server Settings -> Integrations -> Webhooks"
|
||||
placeholder={intl.formatMessage(
|
||||
messages.webhookUrlPlaceholder
|
||||
)}
|
||||
className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
|
||||
/>
|
||||
</div>
|
||||
|
@@ -10,6 +10,16 @@ import * as Yup from 'yup';
|
||||
const messages = defineMessages({
|
||||
save: 'Save Changes',
|
||||
saving: 'Saving...',
|
||||
validationFromRequired: 'You must provide an email sender address',
|
||||
validationSmtpHostRequired: 'You must provide an SMTP host',
|
||||
validationSmtpPortRequired: 'You must provide an SMTP port',
|
||||
agentenabled: 'Agent Enabled',
|
||||
emailsender: 'Email Sender Address',
|
||||
smtpHost: 'SMTP Host',
|
||||
smtpPort: 'SMTP Port',
|
||||
enableSsl: 'Enable SSL',
|
||||
authUser: 'Auth User',
|
||||
authPass: 'Auth Pass',
|
||||
});
|
||||
|
||||
const NotificationsEmail: React.FC = () => {
|
||||
@@ -20,10 +30,14 @@ const NotificationsEmail: React.FC = () => {
|
||||
|
||||
const NotificationsDiscordSchema = Yup.object().shape({
|
||||
emailFrom: Yup.string().required(
|
||||
'You must provide an email sender address'
|
||||
intl.formatMessage(messages.validationFromRequired)
|
||||
),
|
||||
smtpHost: Yup.string().required(
|
||||
intl.formatMessage(messages.validationSmtpHostRequired)
|
||||
),
|
||||
smtpPort: Yup.number().required(
|
||||
intl.formatMessage(messages.validationSmtpPortRequired)
|
||||
),
|
||||
smtpHost: Yup.string().required('You must provide an SMTP host'),
|
||||
smtpPort: Yup.number().required('You must provide an SMTP port'),
|
||||
});
|
||||
|
||||
if (!data && !error) {
|
||||
@@ -88,7 +102,7 @@ const NotificationsEmail: React.FC = () => {
|
||||
htmlFor="name"
|
||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
||||
>
|
||||
Email Sender Address
|
||||
{intl.formatMessage(messages.emailsender)}
|
||||
</label>
|
||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
||||
@@ -110,7 +124,7 @@ const NotificationsEmail: React.FC = () => {
|
||||
htmlFor="name"
|
||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
||||
>
|
||||
SMTP Host
|
||||
{intl.formatMessage(messages.smtpHost)}
|
||||
</label>
|
||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
||||
@@ -132,7 +146,7 @@ const NotificationsEmail: React.FC = () => {
|
||||
htmlFor="name"
|
||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
||||
>
|
||||
SMTP Port
|
||||
{intl.formatMessage(messages.smtpPort)}
|
||||
</label>
|
||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
||||
@@ -154,7 +168,7 @@ const NotificationsEmail: React.FC = () => {
|
||||
htmlFor="isDefault"
|
||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
||||
>
|
||||
Enable SSL
|
||||
{intl.formatMessage(messages.enableSsl)}
|
||||
</label>
|
||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<Field
|
||||
@@ -170,7 +184,7 @@ const NotificationsEmail: React.FC = () => {
|
||||
htmlFor="name"
|
||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
||||
>
|
||||
Auth User
|
||||
{intl.formatMessage(messages.authUser)}
|
||||
</label>
|
||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
||||
@@ -178,7 +192,6 @@ const NotificationsEmail: React.FC = () => {
|
||||
id="authUser"
|
||||
name="authUser"
|
||||
type="text"
|
||||
placeholder="localhost"
|
||||
className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
|
||||
/>
|
||||
</div>
|
||||
@@ -189,7 +202,7 @@ const NotificationsEmail: React.FC = () => {
|
||||
htmlFor="name"
|
||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
||||
>
|
||||
Auth Pass
|
||||
{intl.formatMessage(messages.authPass)}
|
||||
</label>
|
||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
||||
@@ -197,7 +210,6 @@ const NotificationsEmail: React.FC = () => {
|
||||
id="authPass"
|
||||
name="authPass"
|
||||
type="password"
|
||||
placeholder="localhost"
|
||||
className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
|
||||
/>
|
||||
</div>
|
||||
|
@@ -1,13 +1,17 @@
|
||||
import React from 'react';
|
||||
import useSWR from 'swr';
|
||||
import LoadingSpinner from '../Common/LoadingSpinner';
|
||||
import Badge from '../Common/Badge';
|
||||
import { FormattedDate, FormattedRelativeTime } from 'react-intl';
|
||||
import { FormattedRelativeTime, defineMessages, useIntl } from 'react-intl';
|
||||
import Button from '../Common/Button';
|
||||
import { hasPermission } from '../../../server/lib/permissions';
|
||||
import { Permission } from '../../hooks/useUser';
|
||||
|
||||
const messages = defineMessages({
|
||||
jobname: 'Job Name',
|
||||
nextexecution: 'Next Execution',
|
||||
runnow: 'Run Now',
|
||||
});
|
||||
|
||||
const SettingsJobs: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const { data, error } = useSWR<{ name: string; nextExecutionTime: string }[]>(
|
||||
'/api/v1/settings/jobs'
|
||||
);
|
||||
@@ -25,10 +29,10 @@ const SettingsJobs: React.FC = () => {
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider">
|
||||
Job Name
|
||||
{intl.formatMessage(messages.jobname)}
|
||||
</th>
|
||||
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider">
|
||||
Next Execution
|
||||
{intl.formatMessage(messages.nextexecution)}
|
||||
</th>
|
||||
<th className="px-6 py-3 bg-gray-500"></th>
|
||||
</tr>
|
||||
@@ -54,7 +58,9 @@ const SettingsJobs: React.FC = () => {
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm leading-5 font-medium">
|
||||
<Button buttonType="primary">Run Now</Button>
|
||||
<Button buttonType="primary">
|
||||
{intl.formatMessage(messages.runnow)}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
@@ -1,6 +1,17 @@
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
menuGeneralSettings: 'General Settings',
|
||||
menuPlexSettings: 'Plex',
|
||||
menuServices: 'Services',
|
||||
menuNotifications: 'Notifications',
|
||||
menuLogs: 'Logs',
|
||||
menuJobs: 'Jobs',
|
||||
menuAbout: 'About',
|
||||
});
|
||||
|
||||
interface SettingsRoute {
|
||||
text: string;
|
||||
@@ -8,46 +19,48 @@ interface SettingsRoute {
|
||||
regex: RegExp;
|
||||
}
|
||||
|
||||
const settingsRoutes: SettingsRoute[] = [
|
||||
{
|
||||
text: 'General Settings',
|
||||
route: '/settings/main',
|
||||
regex: /^\/settings(\/main)?$/,
|
||||
},
|
||||
{
|
||||
text: 'Plex',
|
||||
route: '/settings/plex',
|
||||
regex: /^\/settings\/plex/,
|
||||
},
|
||||
{
|
||||
text: 'Services',
|
||||
route: '/settings/services',
|
||||
regex: /^\/settings\/services/,
|
||||
},
|
||||
{
|
||||
text: 'Notifications',
|
||||
route: '/settings/notifications/email',
|
||||
regex: /^\/settings\/notifications/,
|
||||
},
|
||||
{
|
||||
text: 'Logs',
|
||||
route: '/settings/logs',
|
||||
regex: /^\/settings\/logs/,
|
||||
},
|
||||
{
|
||||
text: 'Jobs',
|
||||
route: '/settings/jobs',
|
||||
regex: /^\/settings\/jobs/,
|
||||
},
|
||||
{
|
||||
text: 'About',
|
||||
route: '/settings/about',
|
||||
regex: /^\/settings\/about/,
|
||||
},
|
||||
];
|
||||
|
||||
const SettingsLayout: React.FC = ({ children }) => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
|
||||
const settingsRoutes: SettingsRoute[] = [
|
||||
{
|
||||
text: intl.formatMessage(messages.menuGeneralSettings),
|
||||
route: '/settings/main',
|
||||
regex: /^\/settings(\/main)?$/,
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage(messages.menuPlexSettings),
|
||||
route: '/settings/plex',
|
||||
regex: /^\/settings\/plex/,
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage(messages.menuServices),
|
||||
route: '/settings/services',
|
||||
regex: /^\/settings\/services/,
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage(messages.menuNotifications),
|
||||
route: '/settings/notifications/email',
|
||||
regex: /^\/settings\/notifications/,
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage(messages.menuLogs),
|
||||
route: '/settings/logs',
|
||||
regex: /^\/settings\/logs/,
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage(messages.menuJobs),
|
||||
route: '/settings/jobs',
|
||||
regex: /^\/settings\/jobs/,
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage(messages.menuAbout),
|
||||
route: '/settings/about',
|
||||
regex: /^\/settings\/about/,
|
||||
},
|
||||
];
|
||||
|
||||
const activeLinkColor =
|
||||
'border-indigo-600 text-indigo-500 focus:outline-none focus:text-indigo-500 focus:border-indigo-500';
|
||||
|
||||
|
@@ -9,8 +9,13 @@ import Button from '../Common/Button';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
generalsettings: 'General Settings',
|
||||
generalsettingsDescription:
|
||||
'These are settings related to general Overseerr configuration.',
|
||||
save: 'Save Changes',
|
||||
saving: 'Saving...',
|
||||
apikey: 'API Key',
|
||||
applicationurl: 'Application URL',
|
||||
});
|
||||
|
||||
const SettingsMain: React.FC = () => {
|
||||
@@ -27,10 +32,10 @@ const SettingsMain: React.FC = () => {
|
||||
<>
|
||||
<div>
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-200">
|
||||
General Settings
|
||||
{intl.formatMessage(messages.generalsettings)}
|
||||
</h3>
|
||||
<p className="mt-1 max-w-2xl text-sm leading-5 text-gray-500">
|
||||
These are settings related to general Overseerr configuration.
|
||||
{intl.formatMessage(messages.generalsettingsDescription)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-6 sm:mt-5">
|
||||
@@ -59,7 +64,7 @@ const SettingsMain: React.FC = () => {
|
||||
htmlFor="username"
|
||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
||||
>
|
||||
API Key
|
||||
{intl.formatMessage(messages.apikey)}
|
||||
</label>
|
||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
||||
@@ -93,7 +98,7 @@ const SettingsMain: React.FC = () => {
|
||||
htmlFor="name"
|
||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
||||
>
|
||||
Application URL
|
||||
{intl.formatMessage(messages.applicationurl)}
|
||||
</label>
|
||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
||||
|
@@ -1,6 +1,13 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
notificationsettings: 'Notification Settings',
|
||||
notificationsettingsDescription:
|
||||
'Here you can pick and choose what types of notifications to send and through what types of services.',
|
||||
});
|
||||
|
||||
interface SettingsRoute {
|
||||
text: string;
|
||||
@@ -23,6 +30,7 @@ const settingsRoutes: SettingsRoute[] = [
|
||||
|
||||
const SettingsNotifications: React.FC = ({ children }) => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
|
||||
const activeLinkColor = 'bg-gray-700';
|
||||
|
||||
@@ -55,11 +63,10 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
||||
<>
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-200">
|
||||
Notification Settings
|
||||
{intl.formatMessage(messages.notificationsettings)}
|
||||
</h3>
|
||||
<p className="mt-1 max-w-2xl text-sm leading-5 text-gray-500">
|
||||
Here you can pick and choose what types of notifications to send and
|
||||
through what types of services.
|
||||
{intl.formatMessage(messages.notificationsettingsDescription)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
|
Reference in New Issue
Block a user