Add localization support for FollowModal component (#4558)
* Initial plan * Add localization support for FollowModal component Co-authored-by: gabek <414923+gabek@users.noreply.github.com> * Update web/components/modals/FollowModal/FollowModal.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update web/components/modals/FollowModal/FollowForm.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: gabek <414923+gabek@users.noreply.github.com> Co-authored-by: Gabe Kangas <gabek@real-ity.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
gabek
Copilot
copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Gabe Kangas
parent
aa6ab61cbc
commit
51ae467a5a
@@ -1,8 +1,11 @@
|
|||||||
/* eslint-disable react/no-unescaped-entities */
|
/* eslint-disable react/no-unescaped-entities */
|
||||||
import { Input, Button, Alert, Spin, Space } from 'antd';
|
|
||||||
import { FC, useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
|
import { Input, Button, Alert, Spin, Space } from 'antd';
|
||||||
|
import { useTranslation } from 'next-export-i18n';
|
||||||
import styles from './FollowModal.module.scss';
|
import styles from './FollowModal.module.scss';
|
||||||
import { isValidFediverseAccount } from '../../../utils/validators';
|
import { isValidFediverseAccount } from '../../../utils/validators';
|
||||||
|
import { Translation } from '../../ui/Translation/Translation';
|
||||||
|
import { Localization } from '../../../types/localization';
|
||||||
|
|
||||||
const ENDPOINT = '/api/remotefollow';
|
const ENDPOINT = '/api/remotefollow';
|
||||||
|
|
||||||
@@ -15,6 +18,7 @@ export const FollowForm: FC<FollowFormProps> = ({ handleClose }: FollowFormProps
|
|||||||
const [valid, setValid] = useState(false);
|
const [valid, setValid] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [errorMessage, setErrorMessage] = useState(null);
|
const [errorMessage, setErrorMessage] = useState(null);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleAccountChange = a => {
|
const handleAccountChange = a => {
|
||||||
setRemoteAccount(a);
|
setRemoteAccount(a);
|
||||||
@@ -55,7 +59,7 @@ export const FollowForm: FC<FollowFormProps> = ({ handleClose }: FollowFormProps
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!result.redirectUrl) {
|
if (!result.redirectUrl) {
|
||||||
setErrorMessage('Unable to follow.');
|
setErrorMessage(t(Localization.Frontend.FollowModal.unableToFollow) || 'Unable to follow.');
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -69,7 +73,12 @@ export const FollowForm: FC<FollowFormProps> = ({ handleClose }: FollowFormProps
|
|||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<Alert
|
<Alert
|
||||||
message="Follow Error"
|
message={
|
||||||
|
<Translation
|
||||||
|
translationKey={Localization.Frontend.FollowModal.followError}
|
||||||
|
defaultText="Follow Error"
|
||||||
|
/>
|
||||||
|
}
|
||||||
description={errorMessage}
|
description={errorMessage}
|
||||||
type="error"
|
type="error"
|
||||||
closable
|
closable
|
||||||
@@ -78,24 +87,41 @@ export const FollowForm: FC<FollowFormProps> = ({ handleClose }: FollowFormProps
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
<div className={styles.instructions}>Enter your username @server to follow</div>
|
<div className={styles.instructions}>
|
||||||
|
<Translation
|
||||||
|
translationKey={Localization.Frontend.FollowModal.instructions}
|
||||||
|
defaultText="Enter your username @server to follow"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<Input
|
<Input
|
||||||
value={remoteAccount}
|
value={remoteAccount}
|
||||||
size="large"
|
size="large"
|
||||||
onChange={e => handleAccountChange(e.target.value)}
|
onChange={e => handleAccountChange(e.target.value)}
|
||||||
placeholder="Your fediverse account @account@server"
|
placeholder={
|
||||||
|
t(Localization.Frontend.FollowModal.placeholder) ||
|
||||||
|
'Your fediverse account @account@server'
|
||||||
|
}
|
||||||
defaultValue={remoteAccount}
|
defaultValue={remoteAccount}
|
||||||
/>
|
/>
|
||||||
<div className={styles.footer}>
|
<div className={styles.footer}>
|
||||||
You'll be redirected to your Fediverse server and asked to confirm the action.
|
<Translation
|
||||||
|
translationKey={Localization.Frontend.FollowModal.redirectMessage}
|
||||||
|
defaultText="You'll be redirected to your Fediverse server and asked to confirm the action."
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Space className={styles.buttons}>
|
<Space className={styles.buttons}>
|
||||||
<Button onClick={joinButtonPressed} type="text">
|
<Button onClick={joinButtonPressed} type="text">
|
||||||
Join the Fediverse
|
<Translation
|
||||||
|
translationKey={Localization.Frontend.FollowModal.joinFediverse}
|
||||||
|
defaultText="Join the Fediverse"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button disabled={!valid} type="primary" onClick={remoteFollowButtonPressed}>
|
<Button disabled={!valid} type="primary" onClick={remoteFollowButtonPressed}>
|
||||||
Follow
|
<Translation
|
||||||
|
translationKey={Localization.Frontend.FollowModal.followButton}
|
||||||
|
defaultText="Follow"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</Spin>
|
</Spin>
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ const meta = {
|
|||||||
- Validate the input to make sure it's a valid looking account.
|
- Validate the input to make sure it's a valid looking account.
|
||||||
- Handle errors that come back from the server.
|
- Handle errors that come back from the server.
|
||||||
- Perform the redirect to the remote server when the backend response is received.
|
- Perform the redirect to the remote server when the backend response is received.
|
||||||
|
|
||||||
|
## Localization Support
|
||||||
|
This component now supports localization and all user-facing strings can be translated. To test with different languages, add '?lang=de' or '?lang=fr' to the URL to see translated versions where available.
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -37,3 +40,15 @@ const Template: StoryFn<typeof FollowModal> = () => <Example />;
|
|||||||
export const Basic = {
|
export const Basic = {
|
||||||
render: Template,
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const LocalizationExample = {
|
||||||
|
render: Template,
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
'This story demonstrates the localization support. All text strings are now translatable. To test different languages, add "?lang=XX" to the URL with a supported language code like "de" for German.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
/* eslint-disable react/no-unescaped-entities */
|
/* eslint-disable react/no-unescaped-entities */
|
||||||
import { Space } from 'antd';
|
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
|
import { Space } from 'antd';
|
||||||
import styles from './FollowModal.module.scss';
|
import styles from './FollowModal.module.scss';
|
||||||
import { FollowForm } from './FollowForm';
|
import { FollowForm } from './FollowForm';
|
||||||
|
import { Translation } from '../../ui/Translation/Translation';
|
||||||
|
import { Localization } from '../../../types/localization';
|
||||||
|
|
||||||
export type FollowModalProps = {
|
export type FollowModalProps = {
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
@@ -13,12 +15,22 @@ export type FollowModalProps = {
|
|||||||
export const FollowModal: FC<FollowModalProps> = ({ handleClose, account, name }) => (
|
export const FollowModal: FC<FollowModalProps> = ({ handleClose, account, name }) => (
|
||||||
<Space direction="vertical" id="follow-modal">
|
<Space direction="vertical" id="follow-modal">
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
By following this stream you'll get notified on the Fediverse when it goes live. Now is a
|
<Translation
|
||||||
great time to
|
translationKey={Localization.Frontend.FollowModal.description}
|
||||||
|
defaultText="By following this stream you'll get notified on the Fediverse when it goes live. Now is a great time to"
|
||||||
|
/>
|
||||||
<a href="https://owncast.online/join-fediverse" target="_blank" rel="noreferrer">
|
<a href="https://owncast.online/join-fediverse" target="_blank" rel="noreferrer">
|
||||||
learn about the Fediverse
|
|
||||||
|
<Translation
|
||||||
|
translationKey={Localization.Frontend.FollowModal.learnFediverse}
|
||||||
|
defaultText="learn about the Fediverse"
|
||||||
|
/>
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
if it's new to you.
|
<Translation
|
||||||
|
translationKey={Localization.Frontend.FollowModal.newToYou}
|
||||||
|
defaultText="if it's new to you."
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.account}>
|
<div className={styles.account}>
|
||||||
<img src="/logo" alt="logo" className={styles.logo} />
|
<img src="/logo" alt="logo" className={styles.logo} />
|
||||||
|
|||||||
@@ -118,6 +118,18 @@
|
|||||||
"contribute": "Contribute",
|
"contribute": "Contribute",
|
||||||
"source": "Source"
|
"source": "Source"
|
||||||
},
|
},
|
||||||
|
"FollowModal": {
|
||||||
|
"description": "By following this stream you'll get notified on the Fediverse when it goes live. Now is a great time to",
|
||||||
|
"learnFediverse": "learn about the Fediverse",
|
||||||
|
"newToYou": "if it's new to you.",
|
||||||
|
"instructions": "Enter your username @server to follow",
|
||||||
|
"placeholder": "Your fediverse account @account@server",
|
||||||
|
"redirectMessage": "You'll be redirected to your Fediverse server and asked to confirm the action.",
|
||||||
|
"joinFediverse": "Join the Fediverse",
|
||||||
|
"followButton": "Follow",
|
||||||
|
"followError": "Follow Error",
|
||||||
|
"unableToFollow": "Unable to follow."
|
||||||
|
},
|
||||||
"Header": {
|
"Header": {
|
||||||
"skipToPlayer": "Skip to player",
|
"skipToPlayer": "Skip to player",
|
||||||
"skipToOfflineMessage": "Skip to offline message",
|
"skipToOfflineMessage": "Skip to offline message",
|
||||||
|
|||||||
@@ -100,6 +100,20 @@ export const Localization = {
|
|||||||
source: 'Frontend.Footer.source',
|
source: 'Frontend.Footer.source',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Follow modal component
|
||||||
|
FollowModal: {
|
||||||
|
description: 'Frontend.FollowModal.description',
|
||||||
|
learnFediverse: 'Frontend.FollowModal.learnFediverse',
|
||||||
|
newToYou: 'Frontend.FollowModal.newToYou',
|
||||||
|
instructions: 'Frontend.FollowModal.instructions',
|
||||||
|
placeholder: 'Frontend.FollowModal.placeholder',
|
||||||
|
redirectMessage: 'Frontend.FollowModal.redirectMessage',
|
||||||
|
joinFediverse: 'Frontend.FollowModal.joinFediverse',
|
||||||
|
followButton: 'Frontend.FollowModal.followButton',
|
||||||
|
followError: 'Frontend.FollowModal.followError',
|
||||||
|
unableToFollow: 'Frontend.FollowModal.unableToFollow',
|
||||||
|
},
|
||||||
|
|
||||||
// Offline banner messages
|
// Offline banner messages
|
||||||
offlineBasic: 'Frontend.offlineBasic',
|
offlineBasic: 'Frontend.offlineBasic',
|
||||||
offlineNotifyOnly: 'Frontend.offlineNotifyOnly',
|
offlineNotifyOnly: 'Frontend.offlineNotifyOnly',
|
||||||
|
|||||||
Reference in New Issue
Block a user