mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 20:03:12 +03:00
Added translation wrapper to public-facing strings in Portal
refs https://github.com/TryGhost/Ghost/issues/15502 - in order to use the translations, strings must be wrapped in the `t` function, which is passed through AppContext - whilst I haven't instrumented all public strings, the vast majority are done here and the strings have been brought into the JSON locale files using `yarn translate:generate`
This commit is contained in:
parent
f007094d4b
commit
acf2ab2d22
@ -1,3 +1,62 @@
|
|||||||
{
|
{
|
||||||
"Hello": "Hello"
|
"{{discount}}% discount": "",
|
||||||
|
"{{trialDays}} days free": "",
|
||||||
|
"A login link has been sent to your inbox. If it doesn't arrive in 3 minutes, be sure to check your spam folder.": "",
|
||||||
|
"Account": "",
|
||||||
|
"Account settings": "",
|
||||||
|
"After a free trial ends, you will be charged the regular price for the tier you've chosen. You can always cancel before then.": "",
|
||||||
|
"Already a member?": "",
|
||||||
|
"Back": "",
|
||||||
|
"Back to Log in": "",
|
||||||
|
"Cancel subscription": "",
|
||||||
|
"Cancellation reason": "",
|
||||||
|
"Choose a different plan": "",
|
||||||
|
"Choose your newsletters": "",
|
||||||
|
"Close": "",
|
||||||
|
"Comments": "",
|
||||||
|
"Confirm": "",
|
||||||
|
"Continue": "",
|
||||||
|
"Delete account": "",
|
||||||
|
"Don't have an account?": "",
|
||||||
|
"Email": "",
|
||||||
|
"Email preference updated.": "",
|
||||||
|
"Email preferences": "",
|
||||||
|
"Emails": "",
|
||||||
|
"Emails disabled": "",
|
||||||
|
"Get help": "",
|
||||||
|
"Get notified when someone replies to your comment": "",
|
||||||
|
"Give feedback on this post": "",
|
||||||
|
"Hello": "",
|
||||||
|
"Less like this": "",
|
||||||
|
"Manage": "",
|
||||||
|
"Monthly": "",
|
||||||
|
"More like this": "",
|
||||||
|
"Name": "",
|
||||||
|
"Not receiving emails?": "",
|
||||||
|
"Now check your email!": "",
|
||||||
|
"Powered by Ghost": "",
|
||||||
|
"Price": "",
|
||||||
|
"Re-enable emails": "",
|
||||||
|
"Retry": "",
|
||||||
|
"Save": "",
|
||||||
|
"Sending login link...": "",
|
||||||
|
"Sending...": "",
|
||||||
|
"Sign in": "",
|
||||||
|
"Sign up": "",
|
||||||
|
"Start {{amount}}-day free trial": "",
|
||||||
|
"Submit feedback": "",
|
||||||
|
"Successfully unsubscribed": "",
|
||||||
|
"Thanks for the feedback!": "",
|
||||||
|
"That didn't go to plan": "",
|
||||||
|
"This site is invite-only, contact the owner for access.": "",
|
||||||
|
"To complete signup, click the confirmation link in your inbox. If it doesn't arrive within 3 minutes, check your spam folder!": "",
|
||||||
|
"Unsubscribe from all emails": "",
|
||||||
|
"Unsubscribing from emails will not cancel your paid subscription to {{title}}": "",
|
||||||
|
"Update your preferences": "",
|
||||||
|
"We couldn't unsubscribe you as the email address was not found. Please contact the site owner.": "",
|
||||||
|
"Yearly": "",
|
||||||
|
"You have been successfully resubscribed": "",
|
||||||
|
"You're not receiving emails because you either marked a recent message as spam, or because messages could not be delivered to your provided email address.": "",
|
||||||
|
"Your account": "",
|
||||||
|
"Your input helps shape what gets published.": ""
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,62 @@
|
|||||||
{
|
{
|
||||||
"Hello": "Hallo"
|
"{{discount}}% discount": "",
|
||||||
|
"{{trialDays}} days free": "",
|
||||||
|
"A login link has been sent to your inbox. If it doesn't arrive in 3 minutes, be sure to check your spam folder.": "",
|
||||||
|
"Account": "",
|
||||||
|
"Account settings": "",
|
||||||
|
"After a free trial ends, you will be charged the regular price for the tier you've chosen. You can always cancel before then.": "",
|
||||||
|
"Already a member?": "",
|
||||||
|
"Back": "",
|
||||||
|
"Back to Log in": "",
|
||||||
|
"Cancel subscription": "",
|
||||||
|
"Cancellation reason": "",
|
||||||
|
"Choose a different plan": "",
|
||||||
|
"Choose your newsletters": "",
|
||||||
|
"Close": "",
|
||||||
|
"Comments": "",
|
||||||
|
"Confirm": "",
|
||||||
|
"Continue": "",
|
||||||
|
"Delete account": "",
|
||||||
|
"Don't have an account?": "",
|
||||||
|
"Email": "",
|
||||||
|
"Email preference updated.": "",
|
||||||
|
"Email preferences": "",
|
||||||
|
"Emails": "",
|
||||||
|
"Emails disabled": "",
|
||||||
|
"Get help": "",
|
||||||
|
"Get notified when someone replies to your comment": "",
|
||||||
|
"Give feedback on this post": "",
|
||||||
|
"Hello": "Hallo",
|
||||||
|
"Less like this": "",
|
||||||
|
"Manage": "",
|
||||||
|
"Monthly": "",
|
||||||
|
"More like this": "",
|
||||||
|
"Name": "",
|
||||||
|
"Not receiving emails?": "",
|
||||||
|
"Now check your email!": "",
|
||||||
|
"Powered by Ghost": "",
|
||||||
|
"Price": "",
|
||||||
|
"Re-enable emails": "",
|
||||||
|
"Retry": "",
|
||||||
|
"Save": "",
|
||||||
|
"Sending login link...": "",
|
||||||
|
"Sending...": "",
|
||||||
|
"Sign in": "",
|
||||||
|
"Sign up": "",
|
||||||
|
"Start {{amount}}-day free trial": "",
|
||||||
|
"Submit feedback": "",
|
||||||
|
"Successfully unsubscribed": "",
|
||||||
|
"Thanks for the feedback!": "",
|
||||||
|
"That didn't go to plan": "",
|
||||||
|
"This site is invite-only, contact the owner for access.": "",
|
||||||
|
"To complete signup, click the confirmation link in your inbox. If it doesn't arrive within 3 minutes, check your spam folder!": "",
|
||||||
|
"Unsubscribe from all emails": "",
|
||||||
|
"Unsubscribing from emails will not cancel your paid subscription to {{title}}": "",
|
||||||
|
"Update your preferences": "",
|
||||||
|
"We couldn't unsubscribe you as the email address was not found. Please contact the site owner.": "",
|
||||||
|
"Yearly": "",
|
||||||
|
"You have been successfully resubscribed": "",
|
||||||
|
"You're not receiving emails because you either marked a recent message as spam, or because messages could not be delivered to your provided email address.": "",
|
||||||
|
"Your account": "",
|
||||||
|
"Your input helps shape what gets published.": ""
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,13 @@ import {ReactComponent as CheckmarkIcon} from '../../images/icons/check-circle.s
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
function AccountHeader() {
|
function AccountHeader() {
|
||||||
const {brandColor, lastPage, onAction} = useContext(AppContext);
|
const {brandColor, lastPage, onAction, t} = useContext(AppContext);
|
||||||
return (
|
return (
|
||||||
<header className='gh-portal-detail-header'>
|
<header className='gh-portal-detail-header'>
|
||||||
<BackButton brandColor={brandColor} hidden={!lastPage} onClick={(e) => {
|
<BackButton brandColor={brandColor} hidden={!lastPage} onClick={(e) => {
|
||||||
onAction('back');
|
onAction('back');
|
||||||
}} />
|
}} />
|
||||||
<h3 className='gh-portal-main-title'>Email preferences</h3>
|
<h3 className='gh-portal-main-title'>{t('Email preferences')}</h3>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -86,6 +86,7 @@ function NewsletterPrefSection({newsletter, subscribedNewsletters, setSubscribed
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CommentsSection({updateCommentNotifications, isCommentsEnabled, enableCommentNotifications}) {
|
function CommentsSection({updateCommentNotifications, isCommentsEnabled, enableCommentNotifications}) {
|
||||||
|
const {t} = useContext(AppContext);
|
||||||
const isChecked = !!enableCommentNotifications;
|
const isChecked = !!enableCommentNotifications;
|
||||||
|
|
||||||
const [showUpdated, setShowUpdated] = useState(false);
|
const [showUpdated, setShowUpdated] = useState(false);
|
||||||
@ -98,8 +99,8 @@ function CommentsSection({updateCommentNotifications, isCommentsEnabled, enableC
|
|||||||
return (
|
return (
|
||||||
<section className='gh-portal-list-toggle-wrapper' data-test-toggle-wrapper>
|
<section className='gh-portal-list-toggle-wrapper' data-test-toggle-wrapper>
|
||||||
<div className='gh-portal-list-detail'>
|
<div className='gh-portal-list-detail'>
|
||||||
<h3>Comments</h3>
|
<h3>{t('Comments')}</h3>
|
||||||
<p>Get notified when someone replies to your comment</p>
|
<p>{t('Get notified when someone replies to your comment')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div style={{display: 'flex', alignItems: 'center'}}>
|
<div style={{display: 'flex', alignItems: 'center'}}>
|
||||||
<SuccessIcon show={showUpdated} checked={isChecked} />
|
<SuccessIcon show={showUpdated} checked={isChecked} />
|
||||||
@ -133,9 +134,11 @@ function NewsletterPrefs({subscribedNewsletters, setSubscribedNewsletters}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ShowPaidMemberMessage({site, isPaid}) {
|
function ShowPaidMemberMessage({site, isPaid}) {
|
||||||
|
const {t} = useContext(AppContext);
|
||||||
|
|
||||||
if (isPaid) {
|
if (isPaid) {
|
||||||
return (
|
return (
|
||||||
<p style={{textAlign: 'center', marginTop: '12px', marginBottom: '0', color: 'var(--grey6)'}}>Unsubscribing from emails will not cancel your paid subscription to {site?.title}</p>
|
<p style={{textAlign: 'center', marginTop: '12px', marginBottom: '0', color: 'var(--grey6)'}}>{t('Unsubscribing from emails will not cancel your paid subscription to {{title}}', {title: site?.title})}</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -151,7 +154,7 @@ export default function NewsletterManagement({
|
|||||||
isCommentsEnabled,
|
isCommentsEnabled,
|
||||||
enableCommentNotifications
|
enableCommentNotifications
|
||||||
}) {
|
}) {
|
||||||
const {brandColor, onAction, member, site} = useContext(AppContext);
|
const {brandColor, onAction, member, site, t} = useContext(AppContext);
|
||||||
const isDisabled = !subscribedNewsletters?.length && ((isCommentsEnabled && !enableCommentNotifications) || !isCommentsEnabled);
|
const isDisabled = !subscribedNewsletters?.length && ((isCommentsEnabled && !enableCommentNotifications) || !isCommentsEnabled);
|
||||||
const EmptyNotification = () => {
|
const EmptyNotification = () => {
|
||||||
return null;
|
return null;
|
||||||
@ -192,7 +195,7 @@ export default function NewsletterManagement({
|
|||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
brandColor={brandColor}
|
brandColor={brandColor}
|
||||||
isPrimary={false}
|
isPrimary={false}
|
||||||
label='Unsubscribe from all emails'
|
label={t('Unsubscribe from all emails')}
|
||||||
isDestructive={true}
|
isDestructive={true}
|
||||||
style={{width: '100%'}}
|
style={{width: '100%'}}
|
||||||
dataTestId="unsubscribe-from-all-emails"
|
dataTestId="unsubscribe-from-all-emails"
|
||||||
@ -201,12 +204,12 @@ export default function NewsletterManagement({
|
|||||||
</div>
|
</div>
|
||||||
{hasMemberGotEmailSuppression({member}) && !isDisabled &&
|
{hasMemberGotEmailSuppression({member}) && !isDisabled &&
|
||||||
<div className="gh-portal-footer-secondary">
|
<div className="gh-portal-footer-secondary">
|
||||||
<span className="gh-portal-footer-secondary-light">Not receiving emails?</span>
|
<span className="gh-portal-footer-secondary-light">{t('Not receiving emails?')}</span>
|
||||||
<button
|
<button
|
||||||
className="gh-portal-btn-text gh-email-faq-page-button"
|
className="gh-portal-btn-text gh-email-faq-page-button"
|
||||||
onClick={() => onAction('switchPage', {page: 'emailReceivingFAQ'})}
|
onClick={() => onAction('switchPage', {page: 'emailReceivingFAQ'})}
|
||||||
>
|
>
|
||||||
Get help →
|
{t('Get help')} →
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import AppContext from '../../AppContext';
|
||||||
import {ReactComponent as GhostLogo} from '../../images/ghost-logo-small.svg';
|
import {ReactComponent as GhostLogo} from '../../images/ghost-logo-small.svg';
|
||||||
|
|
||||||
export default class PoweredBy extends React.Component {
|
export default class PoweredBy extends React.Component {
|
||||||
|
static contextType = AppContext;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const {t} = this.context;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a href='https://ghost.org' target='_blank' rel='noopener noreferrer' onClick={() => {
|
<a href='https://ghost.org' target='_blank' rel='noopener noreferrer' onClick={() => {
|
||||||
window.open('https://ghost.org', '_blank');
|
window.open('https://ghost.org', '_blank');
|
||||||
}}>
|
}}>
|
||||||
<GhostLogo />
|
<GhostLogo />
|
||||||
Powered by Ghost
|
{t('Powered by Ghost')}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -547,12 +547,12 @@ function ProductCardAlternatePrice({price}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ProductCardTrialDays({trialDays, discount, selectedInterval}) {
|
function ProductCardTrialDays({trialDays, discount, selectedInterval}) {
|
||||||
const {site} = useContext(AppContext);
|
const {site, t} = useContext(AppContext);
|
||||||
|
|
||||||
if (hasFreeTrialTier({site})) {
|
if (hasFreeTrialTier({site})) {
|
||||||
if (trialDays) {
|
if (trialDays) {
|
||||||
return (
|
return (
|
||||||
<span className="gh-portal-discount-label">{trialDays} days free</span>
|
<span className="gh-portal-discount-label">{t('{{trialDays}} days free', {trialDays})}</span>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@ -561,7 +561,7 @@ function ProductCardTrialDays({trialDays, discount, selectedInterval}) {
|
|||||||
|
|
||||||
if (selectedInterval === 'year') {
|
if (selectedInterval === 'year') {
|
||||||
return (
|
return (
|
||||||
<span className="gh-portal-discount-label">{discount}% discount</span>
|
<span className="gh-portal-discount-label">{t('{{discount}}% discount', {discount})}</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -809,7 +809,7 @@ function YearlyDiscount({discount, trialDays}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ProductPriceSwitch({products, selectedInterval, setSelectedInterval}) {
|
function ProductPriceSwitch({products, selectedInterval, setSelectedInterval}) {
|
||||||
const {site} = useContext(AppContext);
|
const {site, t} = useContext(AppContext);
|
||||||
const {portal_plans: portalPlans} = site;
|
const {portal_plans: portalPlans} = site;
|
||||||
if (!portalPlans.includes('monthly') || !portalPlans.includes('yearly')) {
|
if (!portalPlans.includes('monthly') || !portalPlans.includes('yearly')) {
|
||||||
return null;
|
return null;
|
||||||
@ -825,7 +825,7 @@ function ProductPriceSwitch({products, selectedInterval, setSelectedInterval}) {
|
|||||||
setSelectedInterval('month');
|
setSelectedInterval('month');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Monthly
|
{t('Monthly')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
data-test-button='switch-yearly'
|
data-test-button='switch-yearly'
|
||||||
@ -834,7 +834,7 @@ function ProductPriceSwitch({products, selectedInterval, setSelectedInterval}) {
|
|||||||
setSelectedInterval('year');
|
setSelectedInterval('year');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Yearly
|
{t('Yearly')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@ export default class SiteTitleBackButton extends React.Component {
|
|||||||
static contextType = AppContext;
|
static contextType = AppContext;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// const {site} = this.context;
|
const {t} = this.context;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
@ -17,7 +17,7 @@ export default class SiteTitleBackButton extends React.Component {
|
|||||||
this.context.onAction('closePopup');
|
this.context.onAction('closePopup');
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<span>← </span> Back
|
<span>← </span> {t('Back')}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -6,7 +6,7 @@ import NewsletterManagement from '../common/NewsletterManagement';
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
export default function AccountEmailPage() {
|
export default function AccountEmailPage() {
|
||||||
const {member, onAction, site} = useContext(AppContext);
|
const {member, onAction, site, t} = useContext(AppContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!member) {
|
if (!member) {
|
||||||
@ -40,7 +40,7 @@ export default function AccountEmailPage() {
|
|||||||
setSubscribedNewsletters([]);
|
setSubscribedNewsletters([]);
|
||||||
onAction('showPopupNotification', {
|
onAction('showPopupNotification', {
|
||||||
action: 'updated:success',
|
action: 'updated:success',
|
||||||
message: `Email preference updated.`
|
message: t(`Email preference updated.`)
|
||||||
});
|
});
|
||||||
const data = {newsletters: []};
|
const data = {newsletters: []};
|
||||||
if (commentsEnabled) {
|
if (commentsEnabled) {
|
||||||
|
@ -4,14 +4,14 @@ import {isEmailSuppressed} from 'utils/helpers';
|
|||||||
import {ReactComponent as EmailDeliveryFailedIcon} from 'images/icons/email-delivery-failed.svg';
|
import {ReactComponent as EmailDeliveryFailedIcon} from 'images/icons/email-delivery-failed.svg';
|
||||||
|
|
||||||
function EmailPreferencesAction() {
|
function EmailPreferencesAction() {
|
||||||
const {onAction, member} = useContext(AppContext);
|
const {onAction, member, t} = useContext(AppContext);
|
||||||
const emailSuppressed = isEmailSuppressed({member});
|
const emailSuppressed = isEmailSuppressed({member});
|
||||||
const page = emailSuppressed ? 'emailSuppressed' : 'accountEmail';
|
const page = emailSuppressed ? 'emailSuppressed' : 'accountEmail';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<div className='gh-portal-list-detail'>
|
<div className='gh-portal-list-detail'>
|
||||||
<h3>Emails</h3>
|
<h3>{t('Emails')}</h3>
|
||||||
{
|
{
|
||||||
emailSuppressed
|
emailSuppressed
|
||||||
? (
|
? (
|
||||||
@ -20,7 +20,7 @@ function EmailPreferencesAction() {
|
|||||||
<span>You're <span className="gh-mobile-shortener">currently </span>not receiving emails</span>
|
<span>You're <span className="gh-mobile-shortener">currently </span>not receiving emails</span>
|
||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
: <p>Update your preferences</p>
|
: <p>{t('Update your preferences')}</p>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<button className='gh-portal-btn gh-portal-btn-list' onClick={(e) => {
|
<button className='gh-portal-btn gh-portal-btn-list' onClick={(e) => {
|
||||||
@ -29,7 +29,7 @@ function EmailPreferencesAction() {
|
|||||||
lastPage: 'accountHome'
|
lastPage: 'accountHome'
|
||||||
});
|
});
|
||||||
}} data-test-button='manage-newsletters'>
|
}} data-test-button='manage-newsletters'>
|
||||||
Manage
|
{t('Manage')}
|
||||||
</button>
|
</button>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
@ -3,12 +3,12 @@ import MemberAvatar from 'components/common/MemberGravatar';
|
|||||||
import React, {useContext} from 'react';
|
import React, {useContext} from 'react';
|
||||||
|
|
||||||
const UserHeader = () => {
|
const UserHeader = () => {
|
||||||
const {member, brandColor} = useContext(AppContext);
|
const {member, brandColor, t} = useContext(AppContext);
|
||||||
const avatar = member.avatar_image;
|
const avatar = member.avatar_image;
|
||||||
return (
|
return (
|
||||||
<header className='gh-portal-account-header'>
|
<header className='gh-portal-account-header'>
|
||||||
<MemberAvatar gravatar={avatar} style={{userIcon: {color: brandColor, width: '56px', height: '56px', padding: '2px'}}} />
|
<MemberAvatar gravatar={avatar} style={{userIcon: {color: brandColor, width: '56px', height: '56px', padding: '2px'}}} />
|
||||||
<h2 className="gh-portal-main-title">Your account</h2>
|
<h2 className="gh-portal-main-title">{t('Your account')}</h2>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -64,7 +64,7 @@ const Header = ({onBack, showConfirmation, confirmationType}) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CancelSubscriptionButton = ({member, onCancelSubscription, action, brandColor}) => {
|
const CancelSubscriptionButton = ({member, onCancelSubscription, action, brandColor}) => {
|
||||||
const {site} = useContext(AppContext);
|
const {site, t} = useContext(AppContext);
|
||||||
if (!member.paid) {
|
if (!member.paid) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ const CancelSubscriptionButton = ({member, onCancelSubscription, action, brandCo
|
|||||||
if (subscription.cancel_at_period_end) {
|
if (subscription.cancel_at_period_end) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const label = 'Cancel subscription';
|
const label = t('Cancel subscription');
|
||||||
const isRunning = ['cancelSubscription:running'].includes(action);
|
const isRunning = ['cancelSubscription:running'].includes(action);
|
||||||
const disabled = (isRunning) ? true : false;
|
const disabled = (isRunning) ? true : false;
|
||||||
const isPrimary = !!subscription.cancel_at_period_end;
|
const isPrimary = !!subscription.cancel_at_period_end;
|
||||||
@ -110,11 +110,11 @@ const CancelSubscriptionButton = ({member, onCancelSubscription, action, brandCo
|
|||||||
|
|
||||||
// For confirmation flows
|
// For confirmation flows
|
||||||
const PlanConfirmationSection = ({plan, type, onConfirm}) => {
|
const PlanConfirmationSection = ({plan, type, onConfirm}) => {
|
||||||
const {site, action, member, brandColor} = useContext(AppContext);
|
const {site, action, member, brandColor, t} = useContext(AppContext);
|
||||||
const [reason, setReason] = useState('');
|
const [reason, setReason] = useState('');
|
||||||
const subscription = getMemberSubscription({member});
|
const subscription = getMemberSubscription({member});
|
||||||
const isRunning = ['updateSubscription:running', 'checkoutPlan:running', 'cancelSubscription:running'].includes(action);
|
const isRunning = ['updateSubscription:running', 'checkoutPlan:running', 'cancelSubscription:running'].includes(action);
|
||||||
const label = 'Confirm';
|
const label = t('Confirm');
|
||||||
let planStartDate = getDateString(subscription.current_period_end);
|
let planStartDate = getDateString(subscription.current_period_end);
|
||||||
const currentActivePlan = getMemberActivePrice({member});
|
const currentActivePlan = getMemberActivePrice({member});
|
||||||
if (currentActivePlan.id !== plan.id) {
|
if (currentActivePlan.id !== plan.id) {
|
||||||
@ -123,14 +123,14 @@ const PlanConfirmationSection = ({plan, type, onConfirm}) => {
|
|||||||
const priceString = formatNumber(plan.price);
|
const priceString = formatNumber(plan.price);
|
||||||
const planStartMessage = `${plan.currency_symbol}${priceString}/${plan.interval} – Starting ${planStartDate}`;
|
const planStartMessage = `${plan.currency_symbol}${priceString}/${plan.interval} – Starting ${planStartDate}`;
|
||||||
const product = getProductFromPrice({site, priceId: plan?.id});
|
const product = getProductFromPrice({site, priceId: plan?.id});
|
||||||
const priceLabel = hasMultipleProductsFeature({site}) ? product?.name : 'Price';
|
const priceLabel = hasMultipleProductsFeature({site}) ? product?.name : t('Price');
|
||||||
if (type === 'changePlan') {
|
if (type === 'changePlan') {
|
||||||
return (
|
return (
|
||||||
<div className='gh-portal-logged-out-form-container'>
|
<div className='gh-portal-logged-out-form-container'>
|
||||||
<div className='gh-portal-list mb6'>
|
<div className='gh-portal-list mb6'>
|
||||||
<section>
|
<section>
|
||||||
<div className='gh-portal-list-detail'>
|
<div className='gh-portal-list-detail'>
|
||||||
<h3>Account</h3>
|
<h3>{t('Account')}</h3>
|
||||||
<p>{member.email}</p>
|
<p>{member.email}</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -161,7 +161,7 @@ const PlanConfirmationSection = ({plan, type, onConfirm}) => {
|
|||||||
<p>If you cancel your subscription now, you will continue to have access until <strong>{getDateString(subscription.current_period_end)}</strong>.</p>
|
<p>If you cancel your subscription now, you will continue to have access until <strong>{getDateString(subscription.current_period_end)}</strong>.</p>
|
||||||
<section className='gh-portal-input-section'>
|
<section className='gh-portal-input-section'>
|
||||||
<div className='gh-portal-input-labelcontainer'>
|
<div className='gh-portal-input-labelcontainer'>
|
||||||
<label className='gh-portal-input-label'>Cancellation reason</label>
|
<label className='gh-portal-input-label'>{t('Cancellation reason')}</label>
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
data-test-input='cancellation-reason'
|
data-test-input='cancellation-reason'
|
||||||
|
@ -55,10 +55,12 @@ export default class AccountProfilePage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderSaveButton() {
|
renderSaveButton() {
|
||||||
|
const {t} = this.context;
|
||||||
|
|
||||||
const isRunning = (this.context.action === 'updateProfile:running');
|
const isRunning = (this.context.action === 'updateProfile:running');
|
||||||
let label = 'Save';
|
let label = t('Save');
|
||||||
if (this.context.action === 'updateProfile:failed') {
|
if (this.context.action === 'updateProfile:failed') {
|
||||||
label = 'Retry';
|
label = t('Retry');
|
||||||
}
|
}
|
||||||
const disabled = isRunning ? true : false;
|
const disabled = isRunning ? true : false;
|
||||||
return (
|
return (
|
||||||
@ -75,8 +77,10 @@ export default class AccountProfilePage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderDeleteAccountButton() {
|
renderDeleteAccountButton() {
|
||||||
|
const {t} = this.context;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{cursor: 'pointer', color: 'red'}} role='button'>Delete account</div>
|
<div style={{cursor: 'pointer', color: 'red'}} role='button'>{t('Delete account')}</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,10 +93,12 @@ export default class AccountProfilePage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderHeader() {
|
renderHeader() {
|
||||||
|
const {t} = this.context;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className='gh-portal-detail-header'>
|
<header className='gh-portal-detail-header'>
|
||||||
<BackButton brandColor={this.context.brandColor} hidden={!this.context.lastPage} onClick={e => this.onBack(e)} />
|
<BackButton brandColor={this.context.brandColor} hidden={!this.context.lastPage} onClick={e => this.onBack(e)} />
|
||||||
<h3 className='gh-portal-main-title'>Account settings</h3>
|
<h3 className='gh-portal-main-title'>{t('Account settings')}</h3>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -129,13 +135,15 @@ export default class AccountProfilePage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getInputFields({state, fieldNames}) {
|
getInputFields({state, fieldNames}) {
|
||||||
|
const {t} = this.context;
|
||||||
|
|
||||||
const errors = state.errors || {};
|
const errors = state.errors || {};
|
||||||
const fields = [
|
const fields = [
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: state.name,
|
value: state.name,
|
||||||
placeholder: 'Jamie Larson',
|
placeholder: 'Jamie Larson',
|
||||||
label: 'Name',
|
label: t('Name'),
|
||||||
name: 'name',
|
name: 'name',
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: errors.name || ''
|
errorMessage: errors.name || ''
|
||||||
@ -144,7 +152,7 @@ export default class AccountProfilePage extends React.Component {
|
|||||||
type: 'email',
|
type: 'email',
|
||||||
value: state.email,
|
value: state.email,
|
||||||
placeholder: 'jamie@example.com',
|
placeholder: 'jamie@example.com',
|
||||||
label: 'Email',
|
label: t('Email'),
|
||||||
name: 'email',
|
name: 'email',
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: errors.email || ''
|
errorMessage: errors.email || ''
|
||||||
|
@ -7,7 +7,7 @@ import ActionButton from 'components/common/ActionButton';
|
|||||||
import {ReactComponent as EmailDeliveryFailedIcon} from 'images/icons/email-delivery-failed.svg';
|
import {ReactComponent as EmailDeliveryFailedIcon} from 'images/icons/email-delivery-failed.svg';
|
||||||
|
|
||||||
export default function EmailSuppressedPage() {
|
export default function EmailSuppressedPage() {
|
||||||
const {brandColor, lastPage, onAction, action, site} = useContext(AppContext);
|
const {brandColor, lastPage, onAction, action, site, t} = useContext(AppContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (['removeEmailFromSuppressionList:success'].includes(action)) {
|
if (['removeEmailFromSuppressionList:success'].includes(action)) {
|
||||||
@ -26,13 +26,13 @@ export default function EmailSuppressedPage() {
|
|||||||
lastPage: 'accountHome'
|
lastPage: 'accountHome'
|
||||||
});
|
});
|
||||||
onAction('showPopupNotification', {
|
onAction('showPopupNotification', {
|
||||||
message: 'You have been successfully resubscribed'
|
message: t('You have been successfully resubscribed')
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
onAction('back');
|
onAction('back');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [action, onAction, site]);
|
}, [action, onAction, site, t]);
|
||||||
|
|
||||||
const isRunning = ['removeEmailFromSuppressionList:running', 'refreshMemberData:running'].includes(action);
|
const isRunning = ['removeEmailFromSuppressionList:running', 'refreshMemberData:running'].includes(action);
|
||||||
|
|
||||||
@ -52,9 +52,9 @@ export default function EmailSuppressedPage() {
|
|||||||
<EmailDeliveryFailedIcon className="gh-email-suppressed-page-icon" />
|
<EmailDeliveryFailedIcon className="gh-email-suppressed-page-icon" />
|
||||||
|
|
||||||
<div className="gh-email-suppressed-page-text">
|
<div className="gh-email-suppressed-page-text">
|
||||||
<h3 className="gh-portal-main-title gh-email-suppressed-page-title">Emails disabled</h3>
|
<h3 className="gh-portal-main-title gh-email-suppressed-page-title">{t('Emails disabled')}</h3>
|
||||||
<p>
|
<p>
|
||||||
You're not receiving emails because you either marked a recent message as spam, or because messages could not be delivered to your provided email address.
|
{t('You\'re not receiving emails because you either marked a recent message as spam, or because messages could not be delivered to your provided email address.')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ export default function EmailSuppressedPage() {
|
|||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
disabled={isRunning}
|
disabled={isRunning}
|
||||||
brandColor={brandColor}
|
brandColor={brandColor}
|
||||||
label="Re-enable emails"
|
label={t('Re-enable emails')}
|
||||||
isRunning={isRunning}
|
isRunning={isRunning}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -196,7 +196,7 @@ function ErrorPage({error}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ConfirmDialog = ({onConfirm, loading, initialScore}) => {
|
const ConfirmDialog = ({onConfirm, loading, initialScore}) => {
|
||||||
const {onAction, brandColor} = useContext(AppContext);
|
const {onAction, brandColor, t} = useContext(AppContext);
|
||||||
const [score, setScore] = useState(initialScore);
|
const [score, setScore] = useState(initialScore);
|
||||||
|
|
||||||
const stopPropagation = (event) => {
|
const stopPropagation = (event) => {
|
||||||
@ -223,7 +223,7 @@ const ConfirmDialog = ({onConfirm, loading, initialScore}) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="gh-portal-confirm-dialog" onMouseDown={stopPropagation}>
|
<div className="gh-portal-confirm-dialog" onMouseDown={stopPropagation}>
|
||||||
<h1 className="gh-portal-confirm-title">Give feedback on this post</h1>
|
<h1 className="gh-portal-confirm-title">{t('Give feedback on this post')}</h1>
|
||||||
|
|
||||||
<div className="gh-feedback-buttons-group">
|
<div className="gh-feedback-buttons-group">
|
||||||
<button
|
<button
|
||||||
@ -232,7 +232,7 @@ const ConfirmDialog = ({onConfirm, loading, initialScore}) => {
|
|||||||
onClick={() => setScore(1)}
|
onClick={() => setScore(1)}
|
||||||
>
|
>
|
||||||
<ThumbUpIcon />
|
<ThumbUpIcon />
|
||||||
More like this
|
{t('More like this')}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@ -241,7 +241,7 @@ const ConfirmDialog = ({onConfirm, loading, initialScore}) => {
|
|||||||
onClick={() => setScore(0)}
|
onClick={() => setScore(0)}
|
||||||
>
|
>
|
||||||
<ThumbDownIcon />
|
<ThumbDownIcon />
|
||||||
Less like this
|
{t('Less like this')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ const ConfirmDialog = ({onConfirm, loading, initialScore}) => {
|
|||||||
onClick={submit}
|
onClick={submit}
|
||||||
disabled={false}
|
disabled={false}
|
||||||
brandColor={brandColor}
|
brandColor={brandColor}
|
||||||
label="Submit feedback"
|
label={t('Submit feedback')}
|
||||||
isRunning={loading}
|
isRunning={loading}
|
||||||
tabindex="3"
|
tabindex="3"
|
||||||
/>
|
/>
|
||||||
@ -274,7 +274,7 @@ const LoadingFeedbackView = ({action, score}) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ConfirmFeedback = ({positive}) => {
|
const ConfirmFeedback = ({positive}) => {
|
||||||
const {onAction, brandColor} = useContext(AppContext);
|
const {onAction, brandColor, t} = useContext(AppContext);
|
||||||
|
|
||||||
const icon = positive ? <ThumbUpIcon /> : <ThumbDownIcon />;
|
const icon = positive ? <ThumbUpIcon /> : <ThumbDownIcon />;
|
||||||
|
|
||||||
@ -285,15 +285,15 @@ const ConfirmFeedback = ({positive}) => {
|
|||||||
<div className="gh-feedback-icon">
|
<div className="gh-feedback-icon">
|
||||||
{icon}
|
{icon}
|
||||||
</div>
|
</div>
|
||||||
<h1 className="gh-portal-main-title">Thanks for the feedback!</h1>
|
<h1 className="gh-portal-main-title">{t('Thanks for the feedback!')}</h1>
|
||||||
<p className="gh-portal-text-center">Your input helps shape what gets published.</p>
|
<p className="gh-portal-text-center">{t('Your input helps shape what gets published.')}</p>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
style={{width: '100%'}}
|
style={{width: '100%'}}
|
||||||
retry={false}
|
retry={false}
|
||||||
onClick = {() => onAction('closePopup')}
|
onClick = {() => onAction('closePopup')}
|
||||||
disabled={false}
|
disabled={false}
|
||||||
brandColor={brandColor}
|
brandColor={brandColor}
|
||||||
label={'Close'}
|
label={t('Close')}
|
||||||
isRunning={false}
|
isRunning={false}
|
||||||
tabindex='3'
|
tabindex='3'
|
||||||
classes={'sticky bottom'}
|
classes={'sticky bottom'}
|
||||||
|
@ -27,12 +27,14 @@ export default class MagicLinkPage extends React.Component {
|
|||||||
static contextType = AppContext;
|
static contextType = AppContext;
|
||||||
|
|
||||||
renderFormHeader() {
|
renderFormHeader() {
|
||||||
let popupTitle = `Now check your email!`;
|
const {t} = this.context;
|
||||||
let popupDescription = `A login link has been sent to your inbox. If it doesn't arrive in 3 minutes, be sure to check your spam folder.`;
|
|
||||||
|
let popupTitle = t(`Now check your email!`);
|
||||||
|
let popupDescription = t(`A login link has been sent to your inbox. If it doesn't arrive in 3 minutes, be sure to check your spam folder.`);
|
||||||
|
|
||||||
if (this.context.lastPage === 'signup') {
|
if (this.context.lastPage === 'signup') {
|
||||||
popupTitle = `Now check your email!`;
|
popupTitle = t(`Now check your email!`);
|
||||||
popupDescription = `To complete signup, click the confirmation link in your inbox. If it doesn't arrive within 3 minutes, check your spam folder!`;
|
popupDescription = t(`To complete signup, click the confirmation link in your inbox. If it doesn't arrive within 3 minutes, check your spam folder!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -47,13 +49,15 @@ export default class MagicLinkPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderLoginMessage() {
|
renderLoginMessage() {
|
||||||
|
const {t} = this.context;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
style={{color: '#1d1d1d', fontWeight: 'bold', cursor: 'pointer'}}
|
style={{color: '#1d1d1d', fontWeight: 'bold', cursor: 'pointer'}}
|
||||||
onClick={() => this.context.onAction('switchPage', {page: 'signin'})}
|
onClick={() => this.context.onAction('switchPage', {page: 'signin'})}
|
||||||
>
|
>
|
||||||
Back to Log in
|
{t('Back to Log in')}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -64,7 +68,9 @@ export default class MagicLinkPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderCloseButton() {
|
renderCloseButton() {
|
||||||
const label = 'Close';
|
const {t} = this.context;
|
||||||
|
|
||||||
|
const label = t('Close');
|
||||||
return (
|
return (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
style={{width: '100%'}}
|
style={{width: '100%'}}
|
||||||
|
@ -65,7 +65,7 @@ function NewsletterPrefs({subscribedNewsletters, setSubscribedNewsletters}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function NewsletterSelectionPage({pageData, onBack}) {
|
export default function NewsletterSelectionPage({pageData, onBack}) {
|
||||||
const {brandColor, site, onAction, action} = useContext(AppContext);
|
const {brandColor, site, onAction, action, t} = useContext(AppContext);
|
||||||
const siteNewsletters = getSiteNewsletters({site});
|
const siteNewsletters = getSiteNewsletters({site});
|
||||||
const defaultNewsletters = siteNewsletters.filter((d) => {
|
const defaultNewsletters = siteNewsletters.filter((d) => {
|
||||||
return d.subscribe_on_signup;
|
return d.subscribe_on_signup;
|
||||||
@ -76,10 +76,10 @@ export default function NewsletterSelectionPage({pageData, onBack}) {
|
|||||||
if (action === 'signup:running') {
|
if (action === 'signup:running') {
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
}
|
}
|
||||||
let label = 'Continue';
|
let label = t('Continue');
|
||||||
let retry = false;
|
let retry = false;
|
||||||
if (action === 'signup:failed') {
|
if (action === 'signup:failed') {
|
||||||
label = 'Retry';
|
label = t('Retry');
|
||||||
retry = true;
|
retry = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ export default function NewsletterSelectionPage({pageData, onBack}) {
|
|||||||
const [subscribedNewsletters, setSubscribedNewsletters] = useState(defaultNewsletters);
|
const [subscribedNewsletters, setSubscribedNewsletters] = useState(defaultNewsletters);
|
||||||
return (
|
return (
|
||||||
<div className='gh-portal-content with-footer gh-portal-newsletter-selection'>
|
<div className='gh-portal-content with-footer gh-portal-newsletter-selection'>
|
||||||
<p className="gh-portal-text-center gh-portal-text-large">Choose your newsletters</p>
|
<p className="gh-portal-text-center gh-portal-text-large">{t('Choose your newsletters')}</p>
|
||||||
<div className='gh-portal-section'>
|
<div className='gh-portal-section'>
|
||||||
<div className='gh-portal-list'>
|
<div className='gh-portal-list'>
|
||||||
<NewsletterPrefs
|
<NewsletterPrefs
|
||||||
@ -124,7 +124,7 @@ export default function NewsletterSelectionPage({pageData, onBack}) {
|
|||||||
onClick = {() => {
|
onClick = {() => {
|
||||||
onBack();
|
onBack();
|
||||||
}}>
|
}}>
|
||||||
<span>Choose a different plan</span>
|
<span>{t('Choose a different plan')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -152,14 +152,14 @@ export default class OfferPage extends React.Component {
|
|||||||
|
|
||||||
getInputFields({state, fieldNames}) {
|
getInputFields({state, fieldNames}) {
|
||||||
const {portal_name: portalName} = this.context.site;
|
const {portal_name: portalName} = this.context.site;
|
||||||
const {member} = this.context;
|
const {member, t} = this.context;
|
||||||
const errors = state.errors || {};
|
const errors = state.errors || {};
|
||||||
const fields = [
|
const fields = [
|
||||||
{
|
{
|
||||||
type: 'email',
|
type: 'email',
|
||||||
value: member?.email || state.email,
|
value: member?.email || state.email,
|
||||||
placeholder: 'jamie@example.com',
|
placeholder: 'jamie@example.com',
|
||||||
label: 'Email',
|
label: t('Email'),
|
||||||
name: 'email',
|
name: 'email',
|
||||||
disabled: !!member,
|
disabled: !!member,
|
||||||
required: true,
|
required: true,
|
||||||
@ -181,7 +181,7 @@ export default class OfferPage extends React.Component {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
value: member?.name || state.name,
|
value: member?.name || state.name,
|
||||||
placeholder: 'Jamie Larson',
|
placeholder: 'Jamie Larson',
|
||||||
label: 'Name',
|
label: t('Name'),
|
||||||
name: 'name',
|
name: 'name',
|
||||||
disabled: !!member,
|
disabled: !!member,
|
||||||
required: true,
|
required: true,
|
||||||
@ -307,22 +307,22 @@ export default class OfferPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderSubmitButton() {
|
renderSubmitButton() {
|
||||||
const {action, brandColor} = this.context;
|
const {action, brandColor, t} = this.context;
|
||||||
const {pageData: offer} = this.context;
|
const {pageData: offer} = this.context;
|
||||||
let label = 'Continue';
|
let label = t('Continue');
|
||||||
|
|
||||||
if (offer.type === 'trial') {
|
if (offer.type === 'trial') {
|
||||||
label = 'Start ' + offer.amount + '-day free trial';
|
label = t('Start {{amount}}-day free trial', {amount: offer.amount});
|
||||||
}
|
}
|
||||||
|
|
||||||
let isRunning = false;
|
let isRunning = false;
|
||||||
if (action === 'signup:running') {
|
if (action === 'signup:running') {
|
||||||
label = 'Sending...';
|
label = t('Sending...');
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
}
|
}
|
||||||
let retry = false;
|
let retry = false;
|
||||||
if (action === 'signup:failed') {
|
if (action === 'signup:failed') {
|
||||||
label = 'Retry';
|
label = t('Retry');
|
||||||
retry = true;
|
retry = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,16 +347,16 @@ export default class OfferPage extends React.Component {
|
|||||||
if (member) {
|
if (member) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const {brandColor, onAction} = this.context;
|
const {brandColor, onAction, t} = this.context;
|
||||||
return (
|
return (
|
||||||
<div className='gh-portal-signup-message'>
|
<div className='gh-portal-signup-message'>
|
||||||
<div>Already a member?</div>
|
<div>{t('Already a member?')}</div>
|
||||||
<button
|
<button
|
||||||
className='gh-portal-btn gh-portal-btn-link'
|
className='gh-portal-btn gh-portal-btn-link'
|
||||||
style={{color: brandColor}}
|
style={{color: brandColor}}
|
||||||
onClick={() => onAction('switchPage', {page: 'signin'})}
|
onClick={() => onAction('switchPage', {page: 'signin'})}
|
||||||
>
|
>
|
||||||
<span>Sign in</span>
|
<span>{t('Sign in')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -477,15 +477,15 @@ export default class OfferPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderProductLabel({product, offer}) {
|
renderProductLabel({product, offer}) {
|
||||||
const {site} = this.context;
|
const {site, t} = this.context;
|
||||||
|
|
||||||
if (hasMultipleProductsFeature({site})) {
|
if (hasMultipleProductsFeature({site})) {
|
||||||
return (
|
return (
|
||||||
<h4 className="gh-portal-plan-name">{product.name} - {(offer.cadence === 'month' ? 'Monthly' : 'Yearly')}</h4>
|
<h4 className="gh-portal-plan-name">{product.name} - {(offer.cadence === 'month' ? t('Monthly') : t('Yearly'))}</h4>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<h4 className="gh-portal-plan-name">{(offer.cadence === 'month' ? 'Monthly' : 'Yearly')}</h4>
|
<h4 className="gh-portal-plan-name">{(offer.cadence === 'month' ? t('Monthly') : t('Yearly'))}</h4>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,6 +520,8 @@ export default class OfferPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderProductCard({product, offer, currencyClass, updatedPrice, price, benefits}) {
|
renderProductCard({product, offer, currencyClass, updatedPrice, price, benefits}) {
|
||||||
|
const {t} = this.context;
|
||||||
|
|
||||||
if (this.state.showNewsletterSelection) {
|
if (this.state.showNewsletterSelection) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -527,7 +529,7 @@ export default class OfferPage extends React.Component {
|
|||||||
<>
|
<>
|
||||||
<div className='gh-portal-product-card top'>
|
<div className='gh-portal-product-card top'>
|
||||||
<div className='gh-portal-product-card-header'>
|
<div className='gh-portal-product-card-header'>
|
||||||
<h4 className="gh-portal-product-name">{product.name} - {(offer.cadence === 'month' ? 'Monthly' : 'Yearly')}</h4>
|
<h4 className="gh-portal-product-name">{product.name} - {(offer.cadence === 'month' ? t('Monthly') : t('Yearly'))}</h4>
|
||||||
{this.renderOldTierPrice({offer, price})}
|
{this.renderOldTierPrice({offer, price})}
|
||||||
{this.renderUpdatedTierPrice({offer, currencyClass, updatedPrice, price})}
|
{this.renderUpdatedTierPrice({offer, currencyClass, updatedPrice, price})}
|
||||||
{this.renderOfferMessage({offer, product, price})}
|
{this.renderOfferMessage({offer, product, price})}
|
||||||
|
@ -56,13 +56,15 @@ export default class SigninPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getInputFields({state}) {
|
getInputFields({state}) {
|
||||||
|
const {t} = this.context;
|
||||||
|
|
||||||
const errors = state.errors || {};
|
const errors = state.errors || {};
|
||||||
const fields = [
|
const fields = [
|
||||||
{
|
{
|
||||||
type: 'email',
|
type: 'email',
|
||||||
value: state.email,
|
value: state.email,
|
||||||
placeholder: 'jamie@example.com',
|
placeholder: 'jamie@example.com',
|
||||||
label: 'Email',
|
label: t('Email'),
|
||||||
name: 'email',
|
name: 'email',
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: errors.email || '',
|
errorMessage: errors.email || '',
|
||||||
@ -73,13 +75,13 @@ export default class SigninPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderSubmitButton() {
|
renderSubmitButton() {
|
||||||
const {action} = this.context;
|
const {action, t} = this.context;
|
||||||
let retry = false;
|
let retry = false;
|
||||||
const isRunning = (action === 'signin:running');
|
const isRunning = (action === 'signin:running');
|
||||||
let label = isRunning ? 'Sending login link...' : 'Continue';
|
let label = isRunning ? t('Sending login link...') : t('Continue');
|
||||||
const disabled = isRunning ? true : false;
|
const disabled = isRunning ? true : false;
|
||||||
if (action === 'signin:failed') {
|
if (action === 'signin:failed') {
|
||||||
label = 'Retry';
|
label = t('Retry');
|
||||||
retry = true;
|
retry = true;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@ -97,17 +99,17 @@ export default class SigninPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderSignupMessage() {
|
renderSignupMessage() {
|
||||||
const brandColor = this.context.brandColor;
|
const {brandColor, t} = this.context;
|
||||||
return (
|
return (
|
||||||
<div className='gh-portal-signup-message'>
|
<div className='gh-portal-signup-message'>
|
||||||
<div>Don't have an account?</div>
|
<div>{t('Don\'t have an account?')}</div>
|
||||||
<button
|
<button
|
||||||
data-test-button='signup-switch'
|
data-test-button='signup-switch'
|
||||||
className='gh-portal-btn gh-portal-btn-link'
|
className='gh-portal-btn gh-portal-btn-link'
|
||||||
style={{color: brandColor}}
|
style={{color: brandColor}}
|
||||||
onClick={() => this.context.onAction('switchPage', {page: 'signup'})}
|
onClick={() => this.context.onAction('switchPage', {page: 'signup'})}
|
||||||
>
|
>
|
||||||
<span>Sign up</span>
|
<span>{t('Sign up')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -143,11 +145,12 @@ export default class SigninPage extends React.Component {
|
|||||||
|
|
||||||
renderFormHeader() {
|
renderFormHeader() {
|
||||||
// const siteTitle = this.context.site.title || 'Site Title';
|
// const siteTitle = this.context.site.title || 'Site Title';
|
||||||
|
const {t} = this.context;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className='gh-portal-signin-header'>
|
<header className='gh-portal-signin-header'>
|
||||||
{this.renderSiteLogo()}
|
{this.renderSiteLogo()}
|
||||||
<h1 className="gh-portal-main-title">Sign in</h1>
|
<h1 className="gh-portal-main-title">{t('Sign in')}</h1>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -361,7 +361,7 @@ class SignupPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getInputFields({state, fieldNames}) {
|
getInputFields({state, fieldNames}) {
|
||||||
const {portal_name: portalName} = this.context.site;
|
const {site: {portal_name: portalName}, t} = this.context;
|
||||||
|
|
||||||
const errors = state.errors || {};
|
const errors = state.errors || {};
|
||||||
const fields = [
|
const fields = [
|
||||||
@ -369,7 +369,7 @@ class SignupPage extends React.Component {
|
|||||||
type: 'email',
|
type: 'email',
|
||||||
value: state.email,
|
value: state.email,
|
||||||
placeholder: 'jamie@example.com',
|
placeholder: 'jamie@example.com',
|
||||||
label: 'Email',
|
label: t('Email'),
|
||||||
name: 'email',
|
name: 'email',
|
||||||
required: true,
|
required: true,
|
||||||
tabindex: 2,
|
tabindex: 2,
|
||||||
@ -383,7 +383,7 @@ class SignupPage extends React.Component {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
value: state.name,
|
value: state.name,
|
||||||
placeholder: 'Jamie Larson',
|
placeholder: 'Jamie Larson',
|
||||||
label: 'Name',
|
label: t('Name'),
|
||||||
name: 'name',
|
name: 'name',
|
||||||
required: true,
|
required: true,
|
||||||
tabindex: 1,
|
tabindex: 1,
|
||||||
@ -400,29 +400,29 @@ class SignupPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderSubmitButton() {
|
renderSubmitButton() {
|
||||||
const {action, site, brandColor, pageQuery} = this.context;
|
const {action, site, brandColor, pageQuery, t} = this.context;
|
||||||
|
|
||||||
if (isInviteOnlySite({site, pageQuery})) {
|
if (isInviteOnlySite({site, pageQuery})) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let label = 'Continue';
|
let label = t('Continue');
|
||||||
const showOnlyFree = pageQuery === 'free' && hasFreeProductPrice({site});
|
const showOnlyFree = pageQuery === 'free' && hasFreeProductPrice({site});
|
||||||
|
|
||||||
if (hasOnlyFreePlan({site}) || showOnlyFree) {
|
if (hasOnlyFreePlan({site}) || showOnlyFree) {
|
||||||
label = 'Sign up';
|
label = t('Sign up');
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let isRunning = false;
|
let isRunning = false;
|
||||||
if (action === 'signup:running') {
|
if (action === 'signup:running') {
|
||||||
label = 'Sending...';
|
label = t('Sending...');
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
}
|
}
|
||||||
let retry = false;
|
let retry = false;
|
||||||
if (action === 'signup:failed') {
|
if (action === 'signup:failed') {
|
||||||
label = 'Retry';
|
label = t('Retry');
|
||||||
retry = true;
|
retry = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,11 +456,11 @@ class SignupPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderFreeTrialMessage() {
|
renderFreeTrialMessage() {
|
||||||
const {site} = this.context;
|
const {site, t} = this.context;
|
||||||
if (hasFreeTrialTier({site}) && !isInviteOnlySite({site})) {
|
if (hasFreeTrialTier({site}) && !isInviteOnlySite({site})) {
|
||||||
return (
|
return (
|
||||||
<p className='gh-portal-free-trial-notification' data-testid="free-trial-notification-text">
|
<p className='gh-portal-free-trial-notification' data-testid="free-trial-notification-text">
|
||||||
After a free trial ends, you will be charged the regular price for the tier you’ve chosen. You can always cancel before then.
|
{t('After a free trial ends, you will be charged the regular price for the tier you\'ve chosen. You can always cancel before then.')}
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -468,19 +468,19 @@ class SignupPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderLoginMessage() {
|
renderLoginMessage() {
|
||||||
const {brandColor, onAction} = this.context;
|
const {brandColor, onAction, t} = this.context;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.renderFreeTrialMessage()}
|
{this.renderFreeTrialMessage()}
|
||||||
<div className='gh-portal-signup-message'>
|
<div className='gh-portal-signup-message'>
|
||||||
<div>Already a member?</div>
|
<div>{t('Already a member?')}</div>
|
||||||
<button
|
<button
|
||||||
data-test-button='signin-switch'
|
data-test-button='signin-switch'
|
||||||
className='gh-portal-btn gh-portal-btn-link'
|
className='gh-portal-btn gh-portal-btn-link'
|
||||||
style={{color: brandColor}}
|
style={{color: brandColor}}
|
||||||
onClick={() => onAction('switchPage', {page: 'signin'})}
|
onClick={() => onAction('switchPage', {page: 'signin'})}
|
||||||
>
|
>
|
||||||
<span>Sign in</span>
|
<span>{t('Sign in')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -489,7 +489,7 @@ class SignupPage extends React.Component {
|
|||||||
|
|
||||||
renderForm() {
|
renderForm() {
|
||||||
const fields = this.getInputFields({state: this.state});
|
const fields = this.getInputFields({state: this.state});
|
||||||
const {site, pageQuery} = this.context;
|
const {site, pageQuery, t} = this.context;
|
||||||
|
|
||||||
if (this.state.showNewsletterSelection) {
|
if (this.state.showNewsletterSelection) {
|
||||||
return (
|
return (
|
||||||
@ -512,7 +512,7 @@ class SignupPage extends React.Component {
|
|||||||
className='gh-portal-invite-only-notification'
|
className='gh-portal-invite-only-notification'
|
||||||
data-testid="invite-only-notification-text"
|
data-testid="invite-only-notification-text"
|
||||||
>
|
>
|
||||||
This site is invite-only, contact the owner for access.
|
{t('This site is invite-only, contact the owner for access.')}
|
||||||
</p>
|
</p>
|
||||||
{this.renderLoginMessage()}
|
{this.renderLoginMessage()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,7 +41,7 @@ async function updateMemberNewsletters({api, memberUuid, newsletters, enableComm
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function UnsubscribePage() {
|
export default function UnsubscribePage() {
|
||||||
const {site, pageData, onAction} = useContext(AppContext);
|
const {site, pageData, onAction, t} = useContext(AppContext);
|
||||||
const api = setupGhostApi({siteUrl: site.url});
|
const api = setupGhostApi({siteUrl: site.url});
|
||||||
const [member, setMember] = useState();
|
const [member, setMember] = useState();
|
||||||
const siteNewsletters = getSiteNewsletters({site});
|
const siteNewsletters = getSiteNewsletters({site});
|
||||||
@ -101,9 +101,9 @@ export default function UnsubscribePage() {
|
|||||||
<div class="gh-feedback-icon gh-feedback-icon-error">
|
<div class="gh-feedback-icon gh-feedback-icon-error">
|
||||||
<WarningIcon />
|
<WarningIcon />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="gh-portal-main-title">That didn't go to plan</h1>
|
<h1 className="gh-portal-main-title">{t('That didn\'t go to plan')}</h1>
|
||||||
<div>
|
<div>
|
||||||
<p className="gh-portal-text-center">We couldn't unsubscribe you as the email address was not found. Please contact the site owner.</p>
|
<p className="gh-portal-text-center">{t('We couldn\'t unsubscribe you as the email address was not found. Please contact the site owner.')}</p>
|
||||||
</div>
|
</div>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
style={{width: '100%'}}
|
style={{width: '100%'}}
|
||||||
@ -111,7 +111,7 @@ export default function UnsubscribePage() {
|
|||||||
onClick = {() => onAction('closePopup')}
|
onClick = {() => onAction('closePopup')}
|
||||||
disabled={false}
|
disabled={false}
|
||||||
brandColor='#000000'
|
brandColor='#000000'
|
||||||
label={'Close'}
|
label={t('Close')}
|
||||||
isRunning={false}
|
isRunning={false}
|
||||||
tabindex='3'
|
tabindex='3'
|
||||||
classes={'sticky bottom'}
|
classes={'sticky bottom'}
|
||||||
@ -126,7 +126,7 @@ export default function UnsubscribePage() {
|
|||||||
<div className='gh-portal-content gh-portal-unsubscribe with-footer'>
|
<div className='gh-portal-content gh-portal-unsubscribe with-footer'>
|
||||||
<CloseButton />
|
<CloseButton />
|
||||||
<AccountHeader />
|
<AccountHeader />
|
||||||
<h1 className="gh-portal-main-title">Successfully unsubscribed</h1>
|
<h1 className="gh-portal-main-title">{t('Successfully unsubscribed')}</h1>
|
||||||
<div>
|
<div>
|
||||||
<p className='gh-portal-text-center'><strong>{member?.email}</strong> will no longer receive this newsletter.</p>
|
<p className='gh-portal-text-center'><strong>{member?.email}</strong> will no longer receive this newsletter.</p>
|
||||||
<p className='gh-portal-text-center'>Didn't mean to do this? Manage your preferences
|
<p className='gh-portal-text-center'>Didn't mean to do this? Manage your preferences
|
||||||
@ -182,7 +182,7 @@ export default function UnsubscribePage() {
|
|||||||
setSubscribedNewsletters([]);
|
setSubscribedNewsletters([]);
|
||||||
onAction('showPopupNotification', {
|
onAction('showPopupNotification', {
|
||||||
action: 'updated:success',
|
action: 'updated:success',
|
||||||
message: `Email preference updated.`
|
message: t(`Email preference updated.`)
|
||||||
});
|
});
|
||||||
const updatedMember = await api.member.updateNewsletters({uuid: pageData.uuid, newsletters: [], enableCommentNotifications: false});
|
const updatedMember = await api.member.updateNewsletters({uuid: pageData.uuid, newsletters: [], enableCommentNotifications: false});
|
||||||
setMember(updatedMember);
|
setMember(updatedMember);
|
||||||
|
Loading…
Reference in New Issue
Block a user