0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

Added more string translations

refs: https://github.com/TryGhost/Ghost/issues/16628
This commit is contained in:
Sam Lord 2023-05-24 17:40:27 +01:00 committed by Sam Lord
parent 67c81dbd6c
commit 5816217019
6 changed files with 87 additions and 44 deletions

View file

@ -6,6 +6,8 @@ import BackButton from '../common/BackButton';
import {MultipleProductsPlansSection} from '../common/PlansSection';
import {getDateString} from '../../utils/date-time';
import {allowCompMemberUpgrade, formatNumber, getAvailablePrices, getFilteredPrices, getMemberActivePrice, getMemberSubscription, getPriceFromSubscription, getProductFromPrice, getSubscriptionFromId, getUpgradeProducts, hasMultipleProductsFeature, isComplimentaryMember, isPaidMember} from '../../utils/helpers';
import Interpolate from '@doist/react-interpolate';
import {SYNTAX_I18NEXT} from '@doist/react-interpolate';
export const AccountPlanPageStyles = `
.account-plan.full-size .gh-portal-main-title {
@ -38,21 +40,21 @@ export const AccountPlanPageStyles = `
}
`;
function getConfirmationPageTitle({confirmationType}) {
function getConfirmationPageTitle({confirmationType, t}) {
if (confirmationType === 'changePlan') {
return 'Confirm subscription';
return t('Confirm subscription');
} else if (confirmationType === 'cancel') {
return 'Cancel subscription';
return t('Cancel subscription');
} else if (confirmationType === 'subscribe') {
return 'Subscribe';
return t('Subscribe');
}
}
const Header = ({onBack, showConfirmation, confirmationType}) => {
const {member} = useContext(AppContext);
const {member, t} = useContext(AppContext);
let title = isPaidMember({member}) ? 'Change plan' : 'Choose a plan';
if (showConfirmation) {
title = getConfirmationPageTitle({confirmationType});
title = getConfirmationPageTitle({confirmationType, t});
}
return (
<header className='gh-portal-detail-header'>
@ -113,13 +115,14 @@ const PlanConfirmationSection = ({plan, type, onConfirm}) => {
const subscription = getMemberSubscription({member});
const isRunning = ['updateSubscription:running', 'checkoutPlan:running', 'cancelSubscription:running'].includes(action);
const label = t('Confirm');
let planStartDate = getDateString(subscription.current_period_end);
const planStartDate = getDateString(subscription.current_period_end);
const currentActivePlan = getMemberActivePrice({member});
let planStartingMessage = t('Starting {{startDate}}', {startDate: planStartDate});
if (currentActivePlan.id !== plan.id) {
planStartDate = 'today';
planStartingMessage = t('Starting today');
}
const priceString = formatNumber(plan.price);
const planStartMessage = `${plan.currency_symbol}${priceString}/${plan.interval} Starting ${planStartDate}`;
const planStartMessage = `${plan.currency_symbol}${priceString}/${plan.interval} ${planStartingMessage}`;
const product = getProductFromPrice({site, priceId: plan?.id});
const priceLabel = hasMultipleProductsFeature({site}) ? product?.name : t('Price');
if (type === 'changePlan') {
@ -156,7 +159,15 @@ const PlanConfirmationSection = ({plan, type, onConfirm}) => {
} else {
return (
<div className="gh-portal-logged-out-form-container gh-portal-cancellation-form">
<p>If you cancel your subscription now, you will continue to have access until <strong>{getDateString(subscription.current_period_end)}</strong>.</p>
<p>
<Interpolate
syntax={SYNTAX_I18NEXT}
string={t(`If you cancel your subscription now, you will continue to have access until {{periodEnd}}.`)}
mapping={{
periodEnd: <strong>{getDateString(subscription.current_period_end)}</strong>
}}
/>
</p>
<section className='gh-portal-input-section'>
<div className='gh-portal-input-labelcontainer'>
<label className='gh-portal-input-label'>{t('Cancellation reason')}</label>
@ -181,7 +192,7 @@ const PlanConfirmationSection = ({plan, type, onConfirm}) => {
isRunning={isRunning}
isPrimary={true}
brandColor={brandColor}
label={label + ' cancellation'}
label={t('Confirm cancellation')}
style={{
width: '100%',
height: '40px'

View file

@ -5,7 +5,7 @@ import CloseButton from '../../components/common/CloseButton';
import {getSupportAddress} from '../../utils/helpers';
export default function EmailSuppressedPage() {
const {brandColor, onAction, site} = useContext(AppContext);
const {brandColor, onAction, site, t} = useContext(AppContext);
const supportAddress = `mailto:${getSupportAddress({site})}`;
@ -19,19 +19,19 @@ export default function EmailSuppressedPage() {
</header>
<div class="gh-longform">
<h3>Why has my email been disabled?</h3>
<p>Newsletters can be disabled on your account for two reasons: A previous email was marked as spam, or attempting to send an email resulted in a permanent failure (bounce).</p>
<h4>Spam complaints</h4>
<p>If a newsletter is flagged as spam, emails are automatically disabled for that address to make sure you no longer receive any unwanted messages.</p>
<p>If the spam complaint was accidental, or you would like to begin receiving emails again, you can resubscribe to emails by clicking the button on the previous screen.</p>
<p>Once resubscribed, if you still don't see emails in your inbox, check your spam folder. Some inbox providers keep a record of previous spam complaints and will continue to flag emails. If this happens, mark the latest newsletter as 'Not spam' to move it back to your primary inbox.</p>
<h4>Permanent failure (bounce)</h4>
<p>When an inbox fails to accept an email it is commonly called a bounce. In many cases, this can be temporary. However, in some cases, a bounced email can be returned as a permanent failure when an email address is invalid or non-existent.</p>
<p>In the event a permanent failure is received when attempting to send a newsletter, emails will be disabled on the account.</p>
<p>If you would like to start receiving emails again, the best next steps are to check your email address on file for any issues and then click resubscribe on the previous screen.</p>
<h3>{t('Why has my email been disabled?')}</h3>
<p>{t('Newsletters can be disabled on your account for two reasons: A previous email was marked as spam, or attempting to send an email resulted in a permanent failure (bounce).')}</p>
<h4>{t('Spam complaints')}</h4>
<p>{t('If a newsletter is flagged as spam, emails are automatically disabled for that address to make sure you no longer receive any unwanted messages.')}</p>
<p>{t('If the spam complaint was accidental, or you would like to begin receiving emails again, you can resubscribe to emails by clicking the button on the previous screen.')}</p>
<p>{t('Once resubscribed, if you still don\'t see emails in your inbox, check your spam folder. Some inbox providers keep a record of previous spam complaints and will continue to flag emails. If this happens, mark the latest newsletter as \'Not spam\' to move it back to your primary inbox.')}</p>
<h4>{t('Permanent failure (bounce)')}</h4>
<p>{t('When an inbox fails to accept an email it is commonly called a bounce. In many cases, this can be temporary. However, in some cases, a bounced email can be returned as a permanent failure when an email address is invalid or non-existent.')}</p>
<p>{t('In the event a permanent failure is received when attempting to send a newsletter, emails will be disabled on the account.')}</p>
<p>{t('If you would like to start receiving emails again, the best next steps are to check your email address on file for any issues and then click resubscribe on the previous screen.')}</p>
<p><a className='gh-portal-btn gh-portal-btn-branded' href={supportAddress} onClick={() => {
supportAddress && window.open(supportAddress);
}}>Need more help? Contact support</a></p>
}}>{t('Need more help? Contact support')}</a></p>
</div>
</div>
);

View file

@ -166,7 +166,7 @@ export const FeedbackPageStyles = `
`;
function ErrorPage({error}) {
const {onAction} = useContext(AppContext);
const {onAction, t} = useContext(AppContext);
return (
<div className='gh-portal-content gh-portal-feedback with-footer'>
@ -174,7 +174,7 @@ function ErrorPage({error}) {
<div class="gh-feedback-icon gh-feedback-icon-error">
<ThumbErrorIcon />
</div>
<h1 className="gh-portal-main-title">Sorry, that didnt work.</h1>
<h1 className="gh-portal-main-title">{t('Sorry, that didnt work.')}</h1>
<div>
<p className="gh-portal-text-center">{error}</p>
</div>
@ -184,7 +184,7 @@ function ErrorPage({error}) {
onClick = {() => onAction('closePopup')}
disabled={false}
brandColor='#000000'
label={'Close'}
label={t('Close')}
isRunning={false}
tabindex='3'
classes={'sticky bottom'}
@ -301,7 +301,7 @@ const ConfirmFeedback = ({positive}) => {
};
export default function FeedbackPage() {
const {site, pageData, member} = useContext(AppContext);
const {site, pageData, member, t} = useContext(AppContext);
const {uuid, postId, score: initialScore} = pageData;
const [score, setScore] = useState(initialScore);
const positive = score === 1;
@ -318,7 +318,7 @@ export default function FeedbackPage() {
await sendFeedback({siteUrl: site.url, uuid, postId, score: selectedScore});
setScore(selectedScore);
} catch (e) {
const text = HumanReadableError.getMessageFromError(e, 'There was a problem submitting your feedback. Please try again a little later.');
const text = HumanReadableError.getMessageFromError(e, t('There was a problem submitting your feedback. Please try again a little later.'));
setError(text);
}
setLoading(false);

View file

@ -5,7 +5,7 @@ import {getSiteNewsletters, hasOnlyFreePlan} from '../../utils/helpers';
import ActionButton from '../common/ActionButton';
import {ReactComponent as LockIcon} from '../../images/icons/lock.svg';
function NewsletterPrefSection({newsletter, subscribedNewsletters, setSubscribedNewsletters}) {
function NewsletterPrefSection({newsletter, subscribedNewsletters, setSubscribedNewsletters, t}) {
const isChecked = subscribedNewsletters.some((d) => {
return d.id === newsletter?.id;
});
@ -17,7 +17,7 @@ function NewsletterPrefSection({newsletter, subscribedNewsletters, setSubscribed
<p>{newsletter.description}</p>
</div>
<div class="gh-portal-lock-icon-container">
<LockIcon className='gh-portal-lock-icon' alt='' title="Unlock access to all newsletters by becoming a paid subscriber." />
<LockIcon className='gh-portal-lock-icon' alt='' title={t('Unlock access to all newsletters by becoming a paid subscriber.')} />
</div>
</section>
);
@ -48,7 +48,7 @@ function NewsletterPrefSection({newsletter, subscribedNewsletters, setSubscribed
}
function NewsletterPrefs({subscribedNewsletters, setSubscribedNewsletters}) {
const {site} = useContext(AppContext);
const {site, t} = useContext(AppContext);
const newsletters = getSiteNewsletters({site});
return newsletters.map((newsletter) => {
return (
@ -57,6 +57,7 @@ function NewsletterPrefs({subscribedNewsletters, setSubscribedNewsletters}) {
newsletter={newsletter}
subscribedNewsletters={subscribedNewsletters}
setSubscribedNewsletters={setSubscribedNewsletters}
t={t}
/>
);
});

View file

@ -583,14 +583,14 @@ class SignupPage extends React.Component {
}
renderProducts() {
const {site, pageQuery} = this.context;
const {site, pageQuery, t} = this.context;
const products = getSiteProducts({site, pageQuery});
const errors = this.state.errors || {};
const priceErrors = {};
// If we have at least one error, set an error message for the current selected plan
if (Object.keys(errors).length > 0 && this.state.plan) {
priceErrors[this.state.plan] = 'Please fill in required fields';
priceErrors[this.state.plan] = t('Please fill in required fields');
}
return (

View file

@ -6,6 +6,8 @@ import setupGhostApi from '../../utils/api';
import NewsletterManagement from '../common/NewsletterManagement';
import CloseButton from '../common/CloseButton';
import {ReactComponent as WarningIcon} from '../../images/icons/warning-fill.svg';
import Interpolate from '@doist/react-interpolate';
import {SYNTAX_I18NEXT} from '@doist/react-interpolate';
function SiteLogo() {
const {site} = useContext(AppContext);
@ -126,16 +128,28 @@ export default function UnsubscribePage() {
<AccountHeader />
<h1 className="gh-portal-main-title">{t('Successfully unsubscribed')}</h1>
<div>
<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
<button
className="gh-portal-btn-link gh-portal-btn-branded gh-portal-btn-inline"
onClick={() => {
setShowPrefs(true);
<p className='gh-portal-text-center'>
<Interpolate
syntax={SYNTAX_I18NEXT}
string={t('{{memberEmail}} will no longer receive this newsletter.')}
mapping={{
memberEmail: <strong>{member?.email}</strong>
}}
>
here
</button>.
/>
</p>
<p className='gh-portal-text-center'>
<Interpolate
syntax={SYNTAX_I18NEXT}
string={t('Didn\'t mean to do this? Manage your preferences <button>here</button>.')}
mapping={{
button: <button
className="gh-portal-btn-link gh-portal-btn-branded gh-portal-btn-inline"
onClick={() => {
setShowPrefs(true);
}}
/>
}}
/>
</p>
</div>
</div>
@ -147,7 +161,15 @@ export default function UnsubscribePage() {
const hideClassName = hasInteracted ? 'gh-portal-hide' : '';
return (
<>
<p className={`gh-portal-text-center gh-portal-header-message ${hideClassName}`}><strong>{member?.email}</strong> will no longer receive emails when someone replies to your comments.</p>
<p className={`gh-portal-text-center gh-portal-header-message ${hideClassName}`}>
<Interpolate
syntax={SYNTAX_I18NEXT}
string={t('{{memberEmail}} will no longer receive emails when someone replies to your comments.')}
mapping={{
memberEmail: <strong>{member?.email}</strong>
}}
/>
</p>
</>
);
}
@ -157,7 +179,16 @@ export default function UnsubscribePage() {
const hideClassName = hasInteracted ? 'gh-portal-hide' : '';
return (
<>
<p className={`gh-portal-text-center gh-portal-header-message ${hideClassName}`}><strong>{member?.email}</strong> will no longer receive <strong>{unsubscribedNewsletter?.name}</strong> newsletter.</p>
<p className={`gh-portal-text-center gh-portal-header-message ${hideClassName}`}>
<Interpolate
syntax={SYNTAX_I18NEXT}
string={t('{{memberEmail}} will no longer receive {{newsletterName}} newsletter.')}
mapping={{
memberEmail: <strong>{member?.email}</strong>,
newsletterName: <strong>{unsubscribedNewsletter?.name}</strong>
}}
/>
</p>
</>
);
};