Skip to content

Commit 4f3f33a

Browse files
authored
Wrapped gift subscription strings in Portal with t() (TryGhost#27833)
ref https://linear.app/ghost/issue/BER-3529/ When building gift subscriptions, several strings in Portal were intentionally left as English literals because copy was still being reviewed. Those screens were marked with `// TODO: Add translation strings once copy has been finalised` (and `/* eslint-disable i18next/no-literal-string */` in a few files). Copy has now been reviewed for the most part, so we're wrapping English literals into translatable strings.
1 parent 67a4b51 commit 4f3f33a

72 files changed

Lines changed: 1354 additions & 44 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/portal/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tryghost/portal",
3-
"version": "2.68.43",
3+
"version": "2.68.44",
44
"license": "MIT",
55
"repository": "https://github.com/TryGhost/Ghost",
66
"author": "Ghost Foundation",

apps/portal/src/components/common/gift-card.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// TODO: wrap strings with t() once copy is finalised
2-
/* eslint-disable i18next/no-literal-string */
1+
import {t} from '../../utils/i18n';
32

43
const GiftCard = ({cardRef, duration, tierName, name, giftValue, siteIcon, siteTitle}) => {
54
const hasMeta = duration && tierName;
@@ -12,20 +11,20 @@ const GiftCard = ({cardRef, duration, tierName, name, giftValue, siteIcon, siteT
1211
{hasMeta && (
1312
<div className='gh-portal-gift-checkout-card-meta'>
1413
<div className='gh-portal-gift-checkout-card-duration'>{duration}</div>
15-
<div className='gh-portal-gift-checkout-card-tier'>{`${tierName} membership`}</div>
14+
<div className='gh-portal-gift-checkout-card-tier'>{t('{tierName} membership', {tierName})}</div>
1615
</div>
1716
)}
1817
{hasDetails && (
1918
<div className='gh-portal-gift-checkout-card-details'>
2019
{name && (
2120
<div className='gh-portal-gift-checkout-card-detail'>
22-
<div className='gh-portal-gift-checkout-card-detail-label'>Name</div>
21+
<div className='gh-portal-gift-checkout-card-detail-label'>{t('Name')}</div>
2322
<div className='gh-portal-gift-checkout-card-detail-value'>{name}</div>
2423
</div>
2524
)}
2625
{giftValue && (
2726
<div className='gh-portal-gift-checkout-card-detail'>
28-
<div className='gh-portal-gift-checkout-card-detail-label'>Gift value</div>
27+
<div className='gh-portal-gift-checkout-card-detail-label'>{t('Gift value')}</div>
2928
<div className='gh-portal-gift-checkout-card-detail-value'>{giftValue}</div>
3029
</div>
3130
)}

apps/portal/src/components/common/gift-details-toggle.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import {ReactComponent as CheckmarkIcon} from '../../images/icons/checkmark.svg';
2-
3-
// TODO: wrap strings with t() once copy is finalised
4-
/* eslint-disable i18next/no-literal-string */
2+
import {t} from '../../utils/i18n';
53

64
const ChevronIcon = () => (
75
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
@@ -56,7 +54,7 @@ const GiftDetailsToggle = ({description, benefits, showDetails, onToggle}) => {
5654
onClick={onToggle}
5755
aria-expanded={showDetails}
5856
>
59-
{showDetails ? 'Hide details' : 'Gift details'}
57+
{showDetails ? t('Hide details') : t('Gift details')}
6058
<ChevronIcon />
6159
</button>
6260
</>

apps/portal/src/components/notification.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,8 @@ const NotificationText = ({type, status, message, context}) => {
108108
</p>
109109
);
110110
} else if (type === 'giftRedeem' && status === 'success') {
111-
// TODO: Add translation strings once copy has been finalised
112111
const successMessage = getGiftRedemptionSuccessMessage({member: context.member})
113-
|| 'Gift redeemed! You\'re all set.';
112+
|| t('Gift redeemed! You\'re all set.');
114113

115114
return (
116115
<p>

apps/portal/src/components/pages/AccountHomePage/components/continue-gift-subscription-banner.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import AppContext from '../../../../app-context';
22
import ActionButton from '../../../common/action-button';
33
import {getSubscriptionExpiry, isArchivedTier, isGiftMember, isStripeConfigured} from '../../../../utils/helpers';
4+
import {t} from '../../../../utils/i18n';
45
import {useContext} from 'react';
56

67
const ContinueGiftSubscriptionBanner = () => {
@@ -18,21 +19,19 @@ const ContinueGiftSubscriptionBanner = () => {
1819

1920
const isRunning = action === 'continueGiftSubscription:running';
2021

21-
// TODO: Add translation strings once copy has been finalised
22-
/* eslint-disable i18next/no-literal-string */
2322
return (
2423
<div className='gh-portal-cancelcontinue-container'>
2524
<div className='gh-portal-cancel-banner'>
2625
<p style={{maxWidth: 'none', margin: '0 0 16px', textAlign: 'center', textWrap: 'pretty'}}>
27-
Continue with a paid subscription anytime. Your remaining gift period will be added as a free trial.
26+
{t('Continue with a paid subscription anytime. Your remaining gift period will be added as a free trial.')}
2827
</p>
2928
<ActionButton
3029
onClick={() => doAction('continueGiftSubscription')}
3130
isRunning={isRunning}
3231
disabled={isRunning}
3332
isPrimary={true}
3433
brandColor={brandColor}
35-
label='Continue subscription'
34+
label={t('Continue subscription')}
3635
style={{
3736
width: '100%'
3837
}}

apps/portal/src/components/pages/gift-page.js

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ import giftCardNoiseUrl from '../../images/gift-card-noise.webp';
99
import giftCardOrbUrl from '../../images/gift-card-orb.webp';
1010
import {getAvailableProducts, getCurrencySymbol, formatNumber, getStripeAmount, isCookiesDisabled, getActiveInterval} from '../../utils/helpers';
1111
import {getGiftDurationLabel} from '../../utils/gift-redemption-notification';
12+
import {t} from '../../utils/i18n';
1213
import useCardTilt from '../../utils/use-card-tilt';
1314

14-
// TODO: wrap strings with t() once copy is finalised
15-
/* eslint-disable i18next/no-literal-string */
16-
1715
export const GiftPageStyles = `
1816
@property --shine-angle {
1917
syntax: '<angle>';
@@ -627,14 +625,14 @@ function GiftPriceSwitch({selectedInterval, setSelectedInterval}) {
627625
className={'gh-portal-btn' + (selectedInterval === 'month' ? ' active' : '')}
628626
onClick={() => setSelectedInterval('month')}
629627
>
630-
1 month
628+
{t('1 month')}
631629
</button>
632630
<button
633631
data-test-button='switch-yearly'
634632
className={'gh-portal-btn' + (selectedInterval === 'year' ? ' active' : '')}
635633
onClick={() => setSelectedInterval('year')}
636634
>
637-
1 year
635+
{t('1 year')}
638636
</button>
639637
</div>
640638
);
@@ -719,9 +717,9 @@ const GiftPage = () => {
719717
<div className='gh-portal-gift-checkout-bg' aria-hidden='true' />
720718
<div className='gh-portal-gift-checkout-inner'>
721719
<header className='gh-portal-gift-checkout-header'>
722-
<h1 className='gh-portal-main-title'>Gift a membership</h1>
720+
<h1 className='gh-portal-main-title'>{t('Gift a membership')}</h1>
723721
<p className='gh-portal-gift-checkout-subtitle'>
724-
Gift subscriptions are not available right now.
722+
{t('Gift subscriptions are not available right now.')}
725723
</p>
726724
</header>
727725
</div>
@@ -756,9 +754,9 @@ const GiftPage = () => {
756754
<div className='gh-portal-gift-checkout-bg' aria-hidden='true' />
757755
<div className='gh-portal-gift-checkout-inner' ref={innerRef}>
758756
<header className='gh-portal-gift-checkout-header'>
759-
<h1 className='gh-portal-main-title'>Gift a membership</h1>
757+
<h1 className='gh-portal-main-title'>{t('Gift a membership')}</h1>
760758
<p className='gh-portal-gift-checkout-subtitle'>
761-
Share a full membership to {siteTitle} with a friend or colleague
759+
{t('Share a full membership to {siteTitle} with a friend or colleague', {siteTitle})}
762760
</p>
763761
</header>
764762

@@ -770,11 +768,11 @@ const GiftPage = () => {
770768
</div>
771769

772770
<div className='gh-portal-gift-checkout-section'>
773-
<div className='gh-portal-gift-checkout-label'>{isSingleTier ? 'Membership details' : 'Tier'}</div>
771+
<div className='gh-portal-gift-checkout-label'>{isSingleTier ? t('Membership details') : t('Tier')}</div>
774772
<div
775773
className={'gh-portal-gift-checkout-tiers' + (isSingleTier ? ' single' : '')}
776774
role={isSingleTier ? undefined : 'radiogroup'}
777-
aria-label={isSingleTier ? undefined : 'Tier'}
775+
aria-label={isSingleTier ? undefined : t('Tier')}
778776
>
779777
{products.map((product) => {
780778
const isSelected = product.id === activeProduct.id;
@@ -835,7 +833,7 @@ const GiftPage = () => {
835833
<div className='gh-portal-gift-checkout-cta-wrapper'>
836834
<ActionButton
837835
dataTestId='purchase-gift'
838-
label='Continue'
836+
label={t('Continue')}
839837
onClick={handlePurchase}
840838
disabled={isDisabled}
841839
isRunning={isPurchasing}

apps/portal/src/components/pages/gift-redemption-page.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export const GiftRedemptionStyles = `
2222
}
2323
`;
2424

25-
// TODO: Add translation strings once copy has been finalised
2625
const GiftRedemptionPage = () => {
2726
const {action, brandColor, doAction, member, pageData, site} = useContext(AppContext);
2827
const gift = pageData?.gift;
@@ -150,13 +149,13 @@ const GiftRedemptionPage = () => {
150149

151150
const isRedeeming = action === 'redeemGift:running';
152151
const buttonLabel = isRedeeming
153-
? 'Redeeming gift...' // TODO: Add translation strings once copy has been finalised
154-
: 'Redeem your membership'; // TODO: Add translation strings once copy has been finalised
152+
? t('Redeeming...')
153+
: t('Redeem your membership');
155154
const siteIcon = site?.icon;
156155
const siteTitle = site?.title || '';
157156
const headerText = siteTitle
158-
? `You've been gifted a membership to ${siteTitle}`
159-
: 'You\'ve been gifted a membership';
157+
? t('You\'ve been gifted a membership to {siteTitle}', {siteTitle})
158+
: t('You\'ve been gifted a membership');
160159
const benefits = gift.tier.benefits || [];
161160
const tierDescription = gift.tier.description || '';
162161

@@ -169,8 +168,7 @@ const GiftRedemptionPage = () => {
169168
<div className='gh-portal-gift-checkout-bg' aria-hidden='true' />
170169
<div className='gh-portal-gift-checkout-inner'>
171170
<header className='gh-portal-gift-checkout-header'>
172-
{/* eslint-disable-next-line i18next/no-literal-string -- copy not yet finalised */}
173-
<h1 className='gh-portal-main-title'>A gift, just for you</h1>
171+
<h1 className='gh-portal-main-title'>{t('A gift, just for you')}</h1>
174172
<p className='gh-portal-gift-checkout-subtitle'>{headerText}</p>
175173
</header>
176174

apps/portal/src/components/pages/gift-success-page.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ import GiftDetailsToggle from '../common/gift-details-toggle';
66
import copyTextToClipboard from '../../utils/copy-to-clipboard';
77
import {getAvailableProducts} from '../../utils/helpers';
88
import {getGiftDurationLabel} from '../../utils/gift-redemption-notification';
9+
import {t} from '../../utils/i18n';
910
import useCardTilt from '../../utils/use-card-tilt';
1011
import {formatGiftValue} from './gift-page';
1112

12-
// TODO: wrap strings with t() once copy is finalised
13-
/* eslint-disable i18next/no-literal-string */
14-
1513
export const GiftSuccessStyle = `
1614
.gh-portal-gift-success-link {
1715
display: flex;
@@ -115,9 +113,9 @@ const GiftSuccessPage = () => {
115113
<div className='gh-portal-gift-checkout-bg' aria-hidden='true' />
116114
<div className='gh-portal-gift-checkout-inner'>
117115
<header className='gh-portal-gift-checkout-header'>
118-
<h1 className='gh-portal-main-title'>Your gift is ready!</h1>
116+
<h1 className='gh-portal-main-title'>{t('Your gift is ready!')}</h1>
119117
<p className='gh-portal-gift-checkout-subtitle'>
120-
Send the link below to share it with whoever you&apos;d like.
118+
{t('Send the link below to share it with whoever you\'d like.')}
121119
</p>
122120
</header>
123121

@@ -126,13 +124,13 @@ const GiftSuccessPage = () => {
126124
<span className='gh-portal-gift-success-link-url'>{redeemUrl}</span>
127125
<button className='gh-portal-gift-success-copy' onClick={handleCopy} type='button'>
128126
{copied ? <CheckIcon /> : <CopyIcon />}
129-
{copied ? 'Copied' : 'Copy'}
127+
{copied ? t('Copied') : t('Copy')}
130128
</button>
131129
</div>
132130
</div>
133131

134132
<p className='gh-portal-gift-success-footer'>
135-
Not ready to share? We&apos;ve also emailed a copy to your inbox.
133+
{t('Not ready to share? We\'ve also emailed a copy to your inbox.')}
136134
</p>
137135
</div>
138136
</div>

apps/portal/src/utils/gift-redemption-notification.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ export function getGiftRedemptionSuccessMessage({member} = {}) {
1919
if (!tierName || !expiryDate) {
2020
return null;
2121
}
22-
// TODO: Add translation strings once copy has been finalised
23-
return `You now have access to ${tierName} until ${expiryDate}. Enjoy!`;
22+
return t('You now have access to {tierName} until {expiryDate}. Enjoy!', {tierName, expiryDate});
2423
}
2524

2625
export function getGiftRedemptionErrorMessage(error) {

0 commit comments

Comments
 (0)