diff --git a/apps/portal/package.json b/apps/portal/package.json index c646934366c..67ed91c8689 100644 --- a/apps/portal/package.json +++ b/apps/portal/package.json @@ -1,6 +1,6 @@ { "name": "@tryghost/portal", - "version": "2.67.11", + "version": "2.67.12", "license": "MIT", "repository": "https://github.com/TryGhost/Ghost", "author": "Ghost Foundation", diff --git a/apps/portal/src/actions.js b/apps/portal/src/actions.js index 46d9e6b79a6..45d425b065d 100644 --- a/apps/portal/src/actions.js +++ b/apps/portal/src/actions.js @@ -1,6 +1,6 @@ import setupGhostApi from './utils/api'; import {chooseBestErrorMessage} from './utils/errors'; -import {createPopupNotification, getMemberEmail, getMemberName, getProductCadenceFromPrice, removePortalLinkFromUrl, getRefDomain} from './utils/helpers'; +import {createNotification, createPopupNotification, getMemberEmail, getMemberName, getProductCadenceFromPrice, removePortalLinkFromUrl, getRefDomain} from './utils/helpers'; import {t} from './utils/i18n'; function switchPage({data, state}) { @@ -49,16 +49,35 @@ function closePopup({state}) { }; } -function openNotification({data}) { +function openNotification({data, state}) { + const { + action = 'openNotification', + status = 'success', + autoHide = true, + closeable = true, + duration = 2600, + message = '' + } = data || {}; + + const notification = createNotification({ + type: action, + status, + autoHide, + closeable, + duration, + state, + message + }); + return { - showNotification: true, - ...data + notification, + notificationSequence: notification.count }; } function closeNotification() { return { - showNotification: false + notification: null }; } diff --git a/apps/portal/src/app.js b/apps/portal/src/app.js index cf3212eba27..57984cbba1e 100644 --- a/apps/portal/src/app.js +++ b/apps/portal/src/app.js @@ -13,11 +13,24 @@ import {hasMode} from './utils/check-mode'; import {transformPortalAnchorToRelative} from './utils/transform-portal-anchor-to-relative'; import {getActivePage, isAccountPage, isOfferPage} from './pages'; import ActionHandler from './actions'; +import {getGiftRedemptionErrorMessage} from './utils/gift-redemption-notification'; import './app.css'; -import {hasRecommendations, hasGiftSubscriptions, createPopupNotification, hasAvailablePrices, getCurrencySymbol, getFirstpromoterId, getPriceIdFromPageQuery, getProductCadenceFromPrice, getProductFromId, getQueryPrice, getSiteDomain, isActiveOffer, isRetentionOffer, isComplimentaryMember, isInviteOnly, isPaidMember, isRecentMember, isSentryEventAllowed, removePortalLinkFromUrl} from './utils/helpers'; +import {hasRecommendations, hasGiftSubscriptions, createNotification, createPopupNotification, hasAvailablePrices, getCurrencySymbol, getFirstpromoterId, getPriceIdFromPageQuery, getProductCadenceFromPrice, getProductFromId, getQueryPrice, getSiteDomain, isActiveOffer, isRetentionOffer, isComplimentaryMember, isInviteOnly, isPaidMember, isRecentMember, isSentryEventAllowed, removePortalLinkFromUrl} from './utils/helpers'; import {validateHexColor} from './utils/sanitize-html'; import {handleDataAttributes} from './data-attributes'; +const safeDecodeURIComponent = (value) => { + try { + return decodeURIComponent(value); + } catch (error) { + return null; + } +}; + +const staleGiftRedemptionRequestResult = { + staleGiftRedemptionRequest: true +}; + const DEV_MODE_DATA = { showPopup: true, site: Fixtures.site, @@ -59,10 +72,15 @@ export default class App extends React.Component { actionErrorMessage: null, initStatus: 'running', lastPage: null, + notification: null, + notificationSequence: -1, customSiteUrl: props.customSiteUrl, locale: props.locale, scrollbarWidth: 0 }; + + this._redemptionRequestId = 0; + this.currentRedemptionToken = null; } componentDidMount() { @@ -161,17 +179,39 @@ export default class App extends React.Component { /** Setup custom trigger buttons handling on page */ setupCustomTriggerButton() { // Handler for custom buttons - this.clickHandler = (event) => { + this.clickHandler = async (event) => { event.preventDefault(); const target = event.currentTarget; const pagePath = (target && target.dataset.portal); - const {page, pageQuery, pageData} = this.getPageFromLinkPath(pagePath) || {}; + const linkData = this.getPageFromLinkPath(pagePath); + if (!linkData) { + return; + } + + const {page, pageQuery, pageData} = linkData; if (this.state.initStatus === 'success') { if (page === 'gift' && !hasGiftSubscriptions({site: this.state.site})) { + this.invalidateGiftRedemptionRequest(); removePortalLinkFromUrl(); return; } + if (page === 'giftRedemption' && pageData?.token) { + const redemptionRequest = this.startGiftRedemptionRequest(pageData.token); + const giftLinkData = await this.fetchGiftRedemptionData({ + site: this.state.site, + token: pageData.token + }); + + if (!this.isCurrentGiftRedemptionRequest(redemptionRequest)) { + return; + } + + this.setState(giftLinkData); + return; + } + + this.invalidateGiftRedemptionRequest(); if (pageQuery && pageQuery !== 'free') { this.handleSignupQuery({site: this.state.site, pageQuery}); } else { @@ -202,11 +242,30 @@ export default class App extends React.Component { }); } + startGiftRedemptionRequest(token) { + this._redemptionRequestId += 1; + this.currentRedemptionToken = token; + + return { + requestId: this._redemptionRequestId, + token + }; + } + + invalidateGiftRedemptionRequest() { + this._redemptionRequestId += 1; + this.currentRedemptionToken = null; + } + + isCurrentGiftRedemptionRequest({requestId, token}) { + return this._redemptionRequestId === requestId && this.currentRedemptionToken === token; + } + /** Initialize portal setup on load, fetch data and setup state*/ async initSetup() { try { // Fetch data from API, links, preview, dev sources - const {site, member, offers, page, showPopup, popupNotification, lastPage, pageQuery, pageData} = await this.fetchData(); + const {site, member, offers, page, showPopup, popupNotification, notification, notificationSequence, lastPage, pageQuery, pageData} = await this.fetchData(); const i18nLanguage = this.props.siteI18nEnabled ? this.props.locale || site.locale || 'en' : 'en'; i18n.changeLanguage(i18nLanguage); @@ -220,6 +279,8 @@ export default class App extends React.Component { showPopup, pageData, popupNotification, + notification, + notificationSequence, dir: i18n.dir() || 'ltr', action: 'init:success', initStatus: 'success', @@ -266,7 +327,8 @@ export default class App extends React.Component { async fetchData() { const {site: apiSiteData, member, offers} = await this.fetchApiData(); const {site: devSiteData, ...restDevData} = this.fetchDevData(); - const {site: linkSiteData, ...restLinkData} = this.fetchLinkData(apiSiteData, member); + const linkData = await this.fetchLinkData(apiSiteData, member); + const {site: linkSiteData, ...restLinkData} = linkData?.staleGiftRedemptionRequest ? {} : linkData; const {site: previewSiteData, ...restPreviewData} = this.fetchPreviewData(); const {site: notificationSiteData, ...restNotificationData} = this.fetchNotificationData(); let page = ''; @@ -492,7 +554,49 @@ export default class App extends React.Component { } /** Fetch state from Portal Links */ - fetchLinkData(site, member) { + async fetchGiftRedemptionData({site, token}) { + if (!hasGiftSubscriptions({site})) { + removePortalLinkFromUrl(); + + return {}; + } + + try { + const response = await this.GhostApi.gift.fetchRedemptionData({token}); + + return { + showPopup: true, + notification: null, + page: 'giftRedemption', + pageData: { + token, + gift: response?.gift || null + } + }; + } catch (error) { + removePortalLinkFromUrl(); + + const notification = createNotification({ + type: 'giftRedemption:failed', + status: 'error', + autoHide: false, + closeable: true, + state: this.state, + message: getGiftRedemptionErrorMessage(error) + }); + + return { + showPopup: false, + pageData: null, + notification, + notificationSequence: notification.count + }; + } + } + + async fetchLinkData(site, member) { + this.invalidateGiftRedemptionRequest(); + const qParams = new URLSearchParams(window.location.search); if (qParams.get('stripe') === 'gift-purchase-success') { @@ -551,6 +655,7 @@ export default class App extends React.Component { const productMonthlyPriceQueryRegex = /^(?:(\w+?))?\/monthly$/; const productYearlyPriceQueryRegex = /^(?:(\w+?))?\/yearly$/; const offersRegex = /^offers\/(\w+?)\/?$/; + const giftRedemptionRegex = /^\/portal\/gift\/redeem\/([^/?#]+)\/?$/; const linkRegex = /^\/portal\/?(?:\/(\w+(?:\/\w+)*))?\/?$/; const feedbackRegex = /^\/feedback\/(\w+?)\/(\w+?)\/?$/; @@ -581,6 +686,25 @@ export default class App extends React.Component { } } } + if (path && giftRedemptionRegex.test(path)) { + const [, token] = path.match(giftRedemptionRegex); + const decodedToken = safeDecodeURIComponent(token); + if (!decodedToken) { + return {}; + } + + const redemptionRequest = this.startGiftRedemptionRequest(decodedToken); + const giftLinkData = await this.fetchGiftRedemptionData({ + site, + token: decodedToken + }); + + if (!this.isCurrentGiftRedemptionRequest(redemptionRequest)) { + return staleGiftRedemptionRequestResult; + } + + return giftLinkData; + } if (path && linkRegex.test(path)) { const [,pagePath] = path.match(linkRegex); const {page, pageQuery, pageData} = this.getPageFromLinkPath(pagePath, site) || {}; @@ -787,9 +911,14 @@ export default class App extends React.Component { } /**Handle state update for preview url and Portal Link changes */ - updateStateForPreviewLinks() { + async updateStateForPreviewLinks() { const {site: previewSite, ...restPreviewData} = this.fetchPreviewData(); - const {site: linkSite, ...restLinkData} = this.fetchLinkData(this.state.site, this.state.member); + const linkData = await this.fetchLinkData(this.state.site, this.state.member); + if (linkData?.staleGiftRedemptionRequest) { + return; + } + + const {site: linkSite, ...restLinkData} = linkData; const updatedState = { site: { @@ -891,11 +1020,25 @@ export default class App extends React.Component { const customMonthlyProductSignup = /^signup\/?(?:\/(\w+?))\/monthly\/?$/; const customYearlyProductSignup = /^signup\/?(?:\/(\w+?))\/yearly\/?$/; const customOfferRegex = /^offers\/(\w+?)\/?$/; + const giftRedemptionRegex = /^gift\/redeem\/([^/?#]+)\/?$/; if (path === undefined || path === '') { return { page: 'default' }; + } else if (giftRedemptionRegex.test(path)) { + const [, token] = path.match(giftRedemptionRegex); + const decodedToken = safeDecodeURIComponent(token); + if (!decodedToken) { + return null; + } + + return { + page: 'giftRedemption', + pageData: { + token: decodedToken + } + }; } else if (customOfferRegex.test(path)) { return { pageQuery: path @@ -1082,7 +1225,7 @@ export default class App extends React.Component { /**Get final App level context from App state*/ getContextFromState() { - const {site, member, offers, action, actionErrorMessage, page, lastPage, showPopup, pageQuery, pageData, popupNotification, customSiteUrl, dir, scrollbarWidth, otcRef, inboxLinks} = this.state; + const {site, member, offers, action, actionErrorMessage, page, lastPage, showPopup, pageQuery, pageData, popupNotification, notification, customSiteUrl, dir, scrollbarWidth, otcRef, inboxLinks} = this.state; const contextPage = this.getContextPage({site, page, member}); const contextMember = this.getContextMember({site, page: contextPage, member, offers, pageData, customSiteUrl}); return { @@ -1099,6 +1242,7 @@ export default class App extends React.Component { lastPage, showPopup, popupNotification, + notification, customSiteUrl, dir, scrollbarWidth, diff --git a/apps/portal/src/components/frame.styles.js b/apps/portal/src/components/frame.styles.js index 3b0c658bad6..743422c0bfb 100644 --- a/apps/portal/src/components/frame.styles.js +++ b/apps/portal/src/components/frame.styles.js @@ -21,6 +21,7 @@ import EmailSuppressedPage from './pages/email-suppressed-page.css?inline'; import EmailSuppressionFAQ from './pages/email-suppression-faq.css?inline'; import EmailReceivingFAQ from './pages/email-receiving-faq.css?inline'; import {TipsAndDonationsSuccessStyle} from './pages/support-success'; +import {GiftRedemptionStyles} from './pages/gift-redemption-page'; import {GiftSuccessStyle} from './pages/gift-success-page'; import {TipsAndDonationsErrorStyle} from './pages/support-error'; import {RecommendationsPageStyles} from './pages/recommendations-page'; @@ -1313,6 +1314,7 @@ export function getFrameStyles({site}) { EmailSuppressionFAQ + EmailReceivingFAQ + TipsAndDonationsSuccessStyle + + GiftRedemptionStyles + TipsAndDonationsErrorStyle + GiftSuccessStyle + RecommendationsPageStyles + diff --git a/apps/portal/src/components/notification.js b/apps/portal/src/components/notification.js index 530e6868f44..226e8f17c4b 100644 --- a/apps/portal/src/components/notification.js +++ b/apps/portal/src/components/notification.js @@ -27,10 +27,26 @@ const Styles = () => { }; }; -const NotificationText = ({type, status, context}) => { +const NotificationText = ({type, status, message, context}) => { const signinPortalLink = getPortalLink({page: 'signin', siteUrl: context.site.url}); const singupPortalLink = getPortalLink({page: 'signup', siteUrl: context.site.url}); + if (message) { + if (typeof message === 'object') { + return ( +

+ {message.title ? {message.title} : null} + {message.title && message.subtitle ?
: null} + {message.subtitle || null} +

+ ); + } + + return ( +

{message}

+ ); + } + if (type === 'signin' && status === 'success' && context.member) { const firstname = context.member.firstname || ''; return ( @@ -181,7 +197,7 @@ class NotificationContent extends React.Component { } render() { - const {type, status} = this.props; + const {type, status, message} = this.props; const {className = ''} = this.state; const statusClass = status ? ` ${status}` : ' neutral'; const slideClass = className ? ` ${className}` : ''; @@ -189,7 +205,7 @@ class NotificationContent extends React.Component {
this.onAnimationEnd(e)}> {(status === 'error' ? : )} - + this.onNotificationClose(e)} />
@@ -207,15 +223,20 @@ export default class Notification extends React.Component { active: true, type, status, + message: '', autoHide, duration, - className: '' + className: '', + source: type && status ? 'url' : null, + notificationCount: null }; } componentDidMount() { const {showPopup} = this.context; - if (showPopup) { + if (this.context.notification) { + this.showNotification(this.context.notification, 'state'); + } else if (showPopup) { // Don't show a notification if there is a popup visible on page load this.setState({ active: false @@ -223,18 +244,49 @@ export default class Notification extends React.Component { } } + componentDidUpdate() { + const {notification} = this.context; + + if (notification && notification.count !== this.state.notificationCount) { + this.showNotification(notification, 'state'); + } + } + + showNotification(notification, source) { + clearTimeout(this.timeoutId); + + this.setState({ + active: true, + className: '', + type: notification.type, + status: notification.status, + message: notification.message || '', + autoHide: notification.autoHide, + duration: notification.duration, + source, + notificationCount: notification.count || 0 + }); + } + onHideNotification() { - const type = this.state.type; - const deleteParams = []; - if (['signin', 'signup'].includes(type)) { - deleteParams.push('action', 'success'); - } else if (['stripe:checkout'].includes(type)) { - deleteParams.push('stripe'); + const {type, source} = this.state; + + if (source === 'url') { + const deleteParams = []; + if (['signin', 'signup'].includes(type)) { + deleteParams.push('action', 'success'); + } else if (['stripe:checkout'].includes(type)) { + deleteParams.push('stripe'); + } + clearURLParams(deleteParams); + this.context.doAction('refreshMemberData'); + } else if (source === 'state') { + this.context.doAction('closeNotification'); } - clearURLParams(deleteParams); - this.context.doAction('refreshMemberData'); + this.setState({ - active: false + active: false, + source: null }); } @@ -256,11 +308,11 @@ export default class Notification extends React.Component { if (!this.state.active) { return null; } - const {type, status, autoHide, duration} = this.state; + const {type, status, message, autoHide, duration, notificationCount} = this.state; if (type && status) { return ( - this.onHideNotification(e)} /> + this.onHideNotification(e)} /> ); } diff --git a/apps/portal/src/components/pages/gift-redemption-page.js b/apps/portal/src/components/pages/gift-redemption-page.js new file mode 100644 index 00000000000..6ba2858a3d0 --- /dev/null +++ b/apps/portal/src/components/pages/gift-redemption-page.js @@ -0,0 +1,323 @@ +import {useContext, useEffect, useState} from 'react'; +import AppContext from '../../app-context'; +import ActionButton from '../common/action-button'; +import CloseButton from '../common/close-button'; +import InputForm from '../common/input-form'; +import {ReactComponent as CheckmarkIcon} from '../../images/icons/checkmark.svg'; +import {ReactComponent as GiftIcon} from '../../images/icons/gift.svg'; +import {getGiftRedemptionErrorMessage} from '../../utils/gift-redemption-notification'; +import {t} from '../../utils/i18n'; +import {hasGiftSubscriptions, removePortalLinkFromUrl} from '../../utils/helpers'; + +export const GiftRedemptionStyles = ` + .gh-portal-popup-container.giftRedemption { + width: calc(100vw - 24px); + max-width: 452px; + padding: 0; + overflow: hidden; + } + + .gh-portal-popup-container.giftRedemption .gh-portal-closeicon-container { + position: absolute; + top: 16px; + right: 16px; + z-index: 5; + } + + html[dir="rtl"] .gh-portal-popup-container.giftRedemption .gh-portal-closeicon-container { + right: unset; + left: 18px; + } + + .gh-portal-popup-container.giftRedemption .gh-portal-closeicon { + color: rgba(24, 32, 38, 0.14); + } + + .gh-portal-popup-container.giftRedemption .gh-portal-closeicon:hover { + color: rgba(24, 32, 38, 0.28); + } + + .gh-portal-gift-redemption { + overflow: hidden; + } + + .gh-gift-redemption-panel { + position: relative; + background: var(--white); + } + + .gh-gift-redemption-summary { + padding: 32px 32px 28px; + text-align: center; + background: #fff5f5; + border-bottom: 1px solid #f1e7e4; + } + + .gh-gift-redemption-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 48px; + height: 48px; + color: var(--brandcolor); + } + + .gh-gift-redemption-icon svg { + width: 40px; + height: 40px; + } + + .gh-gift-redemption-kicker { + font-size: 1.15rem; + font-weight: 700; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--brandcolor); + } + + .gh-gift-redemption-title { + max-width: none; + margin: 16px auto 0; + font-size: 2.25rem; + font-weight: 800; + line-height: 1.08; + letter-spacing: -0.03em; + white-space: nowrap; + color: var(--grey0); + } + + .gh-gift-redemption-plan { + margin-top: 10px; + font-size: 1.65rem; + color: var(--grey2); + } + + .gh-gift-redemption-tier { + font-weight: 700; + } + + .gh-gift-redemption-cadence { + font-weight: 400; + } + + .gh-gift-redemption-benefits { + display: flex; + flex-direction: column; + gap: 8px; + max-width: 302px; + margin: 20px auto 0; + } + + .gh-gift-redemption-benefit { + display: flex; + align-items: flex-start; + justify-content: flex-start; + gap: 10px; + color: var(--grey2); + font-size: 1.45rem; + line-height: 1.35; + text-align: left; + } + + .gh-gift-redemption-benefit svg { + width: 14px; + height: 14px; + margin-top: 2px; + color: var(--grey1); + flex-shrink: 0; + } + + html[dir="rtl"] .gh-gift-redemption-benefit { + text-align: right; + flex-direction: row-reverse; + } + + .gh-gift-redemption-benefit span { + display: block; + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .gh-gift-redemption-form { + padding: 22px 28px 28px; + background: var(--white); + } + + .gh-gift-redemption-form .gh-portal-input-label { + margin-bottom: 5px; + font-size: 1.25rem; + font-weight: 700; + color: var(--grey1); + } + + .gh-gift-redemption-form .gh-portal-input { + margin-bottom: 14px; + } + + .gh-gift-redemption-submit { + width: 100%; + height: 44px; + margin-top: 22px; + font-size: 1.5rem; + font-weight: 600; + } + + @media (max-width: 480px) { + .gh-gift-redemption-summary { + padding: 28px 24px 24px; + } + + .gh-gift-redemption-title { + font-size: 2.1rem; + white-space: normal; + } + + .gh-gift-redemption-benefit { + font-size: 1.4rem; + } + + html[dir="rtl"] .gh-gift-redemption-benefit { + text-align: right; + } + + .gh-gift-redemption-form { + padding: 20px 20px 22px; + } + } +`; + +function getGiftCadenceLabel(gift) { + const {cadence, duration} = gift; + + if (cadence === 'year') { + return duration === 1 ? t('1 year') : t('{years} years', {years: duration}); + } + + return duration === 1 ? t('1 month') : t('{months} months', {months: duration}); +} + +// TODO: Add translation strings once copy has been finalised +const GiftRedemptionPage = () => { + const {brandColor, doAction, member, pageData, site} = useContext(AppContext); + const gift = pageData?.gift; + const giftSubscriptionsEnabled = hasGiftSubscriptions({site}); + const [name, setName] = useState(member?.name || ''); + const [email, setEmail] = useState(member?.email || ''); + + useEffect(() => { + setName(member?.name || ''); + setEmail(member?.email || ''); + }, [member?.email, member?.name]); + + useEffect(() => { + if (giftSubscriptionsEnabled) { + return; + } + + removePortalLinkFromUrl(); + doAction('closePopup'); + }, [doAction, giftSubscriptionsEnabled]); + + useEffect(() => { + if (!giftSubscriptionsEnabled || gift) { + return; + } + + doAction('openNotification', { + action: 'giftRedemption:failed', + status: 'error', + autoHide: false, + closeable: true, + message: getGiftRedemptionErrorMessage() + }); + doAction('closePopup'); + }, [doAction, gift, giftSubscriptionsEnabled]); + + if (!giftSubscriptionsEnabled || !gift) { + return null; + } + + const formFields = [ + { + type: 'text', + value: name, + placeholder: t('Jamie Larson'), + label: t('Your name'), + name: 'name', + required: true, + tabIndex: 1, + autoFocus: true + }, + { + type: 'email', + value: email, + placeholder: t('jamie@example.com'), + label: t('Your email'), + name: 'email', + required: true, + tabIndex: 2 + } + ]; + + const handleFieldChange = (event, field) => { + if (field.name === 'name') { + setName(event.target.value); + } + + if (field.name === 'email') { + setEmail(event.target.value); + } + }; + + const handleRedeemClick = (event) => { + event.preventDefault(); + }; + + return ( +
+ + +
+
+
+
{'Gift membership'}
+

{'You\'ve been gifted a membership'}

+ +
+ {gift.tier.name} +  ·  + {getGiftCadenceLabel(gift)} +
+ + {gift.tier.benefits.length > 0 && ( +
+ {gift.tier.benefits.map((benefit, index) => ( +
+ + {benefit.name} +
+ ))} +
+ )} + +
+ +
+ + +
+
+
+ ); +}; + +export default GiftRedemptionPage; diff --git a/apps/portal/src/components/pages/gift-success-page.js b/apps/portal/src/components/pages/gift-success-page.js index 88d4d192d04..956ac9bb397 100644 --- a/apps/portal/src/components/pages/gift-success-page.js +++ b/apps/portal/src/components/pages/gift-success-page.js @@ -112,7 +112,7 @@ const GiftSuccessPage = () => { const token = pageData?.token; const siteUrl = site?.url || ''; - const redeemUrl = `${siteUrl.replace(/\/$/, '')}/gift/${token}`; + const redeemUrl = `${siteUrl.replace(/\/$/, '')}/#/portal/gift/redeem/${token}`; const handleCopy = () => { copyTextToClipboard(redeemUrl); diff --git a/apps/portal/src/pages.js b/apps/portal/src/pages.js index ab85fe3722a..90bfc8151a6 100644 --- a/apps/portal/src/pages.js +++ b/apps/portal/src/pages.js @@ -18,6 +18,7 @@ import SupportSuccess from './components/pages/support-success'; import SupportError from './components/pages/support-error'; import RecommendationsPage from './components/pages/recommendations-page'; import GiftPage from './components/pages/gift-page'; +import GiftRedemptionPage from './components/pages/gift-redemption-page'; import GiftSuccessPage from './components/pages/gift-success-page'; /** List of all available pages in Portal, mapped to their UI component @@ -44,6 +45,7 @@ const Pages = { supportError: SupportError, recommendations: RecommendationsPage, gift: GiftPage, + giftRedemption: GiftRedemptionPage, giftSuccess: GiftSuccessPage }; diff --git a/apps/portal/src/utils/api.js b/apps/portal/src/utils/api.js index 2890c78bbac..67374e2e376 100644 --- a/apps/portal/src/utils/api.js +++ b/apps/portal/src/utils/api.js @@ -167,6 +167,68 @@ function setupGhostApi({siteUrl = window.location.origin, apiUrl, apiKey}) { } }; + api.gift = { + async fetchRedemptionData({token}) { + // Temporary: Mocked API response + return { + gift: { + token, + cadence: 'year', + duration: 1, + currency: 'EUR', + amount: 4000, + expires_at: '2027-04-06T10:09:30.000Z', + tier: { + id: '00000000703e0fb16598bca1', + name: 'Ultra', + description: 'Everything in Premium, but fancier', + benefits: [ + { + id: '69d39d43acb2b803745058a1', + name: 'Weekly round-up on Sunday' + }, + { + id: '69d39d43acb2b803745058a2', + name: 'Access to all podcasts and videos' + }, + { + id: '69d39d43acb2b803745058a3', + name: 'Five new stories per week' + } + ] + } + } + }; + + // Test out error cases: + // throw new HumanReadableError('This gift has already been redeemed.'); + // throw new HumanReadableError('This gift has expired.'); + // throw new HumanReadableError('This gift link is not valid.'); + + // TODO: Restore actual API code when ready to integrate + // const url = endpointFor({type: 'members', resource: `gifts/${encodeURIComponent(token)}`}); + // const res = await makeRequest({ + // url, + // method: 'GET', + // headers: { + // 'Content-Type': 'application/json' + // }, + // credentials: 'same-origin' + // }); + + // if (res.ok) { + // return res.json(); + // } + + // const humanError = await HumanReadableError.fromApiResponse(res); + // if (humanError) { + // throw humanError; + // } + + // throw new Error('Failed to load gift data'); + } + }; + api.recommendations = { trackClicked({recommendationId}) { let url = endpointFor({type: 'members', resource: 'recommendations/' + recommendationId + '/clicked'}); diff --git a/apps/portal/src/utils/gift-redemption-notification.js b/apps/portal/src/utils/gift-redemption-notification.js new file mode 100644 index 00000000000..d358f87bd0d --- /dev/null +++ b/apps/portal/src/utils/gift-redemption-notification.js @@ -0,0 +1,15 @@ +// TODO: Add translation strings once copy has been finalise + +const GIFT_REDEMPTION_ERROR_TITLE = 'Gift could not be redeemed'; +const INVALID_GIFT_LINK_MESSAGE = 'Gift link is not valid'; + +export function getGiftRedemptionErrorMessage(error) { + const subtitle = error?.message && error.message !== 'Failed to load gift data' + ? error.message + : INVALID_GIFT_LINK_MESSAGE; + + return { + title: GIFT_REDEMPTION_ERROR_TITLE, + subtitle + }; +} diff --git a/apps/portal/src/utils/helpers.js b/apps/portal/src/utils/helpers.js index d313622d94b..090e1b1bc8e 100644 --- a/apps/portal/src/utils/helpers.js +++ b/apps/portal/src/utils/helpers.js @@ -798,6 +798,23 @@ export const createPopupNotification = ({type, status, autoHide, duration = 2600 }; }; +export const createNotification = ({type, status, autoHide, duration = 2600, closeable, state, message}) => { + const previousCount = Number.isInteger(state?.notificationSequence) + ? state.notificationSequence + : state?.notification?.count; + const count = Number.isInteger(previousCount) ? previousCount + 1 : 0; + + return { + type, + status, + autoHide, + closeable, + duration, + message, + count + }; +}; + export function isSameCurrency(currency1, currency2) { return currency1?.toLowerCase() === currency2?.toLowerCase(); } @@ -1035,4 +1052,4 @@ export function translateCadence(cadence) { return t('year'); } return cadence; -} \ No newline at end of file +} diff --git a/apps/portal/test/actions.test.ts b/apps/portal/test/actions.test.ts index d246826f8a3..7b29ba7258d 100644 --- a/apps/portal/test/actions.test.ts +++ b/apps/portal/test/actions.test.ts @@ -88,6 +88,59 @@ describe('startSigninOTCFromCustomForm action', () => { }); }); +describe('notification actions', () => { + test('increments notification count after a notification is dismissed', async () => { + const firstNotification = await ActionHandler({ + action: 'openNotification', + data: { + action: 'giftRedemption:failed', + status: 'error', + autoHide: false, + message: 'Gift could not be redeemed' + }, + state: { + notification: null, + notificationSequence: -1 + }, + api: {} + }); + + expect(firstNotification.notification.count).toBe(0); + expect(firstNotification.notificationSequence).toBe(0); + + const dismissedNotification = await ActionHandler({ + action: 'closeNotification', + data: {}, + state: { + ...firstNotification + }, + api: {} + }); + + expect(dismissedNotification).toEqual({ + notification: null + }); + + const secondNotification = await ActionHandler({ + action: 'openNotification', + data: { + action: 'giftRedemption:failed', + status: 'error', + autoHide: false, + message: 'Gift could not be redeemed' + }, + state: { + ...firstNotification, + ...dismissedNotification + }, + api: {} + }); + + expect(secondNotification.notification.count).toBe(1); + expect(secondNotification.notificationSequence).toBe(1); + }); +}); + describe('continueSubscription action', () => { test('returns reloadOnPopupClose on success', async () => { const mockApi = { diff --git a/apps/portal/test/app.test.js b/apps/portal/test/app.test.js index 9cd0e51a8e2..eb4ed13de99 100644 --- a/apps/portal/test/app.test.js +++ b/apps/portal/test/app.test.js @@ -15,6 +15,18 @@ vi.mock('../src/utils/i18n', () => ({ t: vi.fn(str => str) })); +const createDeferred = () => { + let resolve; + const promise = new Promise((res) => { + resolve = res; + }); + + return { + promise, + resolve + }; +}; + describe('App', function () { beforeEach(function () { // Stub window.location with a URL object so we have an expected origin @@ -115,6 +127,140 @@ describe('App', function () { expect(window.location.reload).not.toHaveBeenCalled(); }); + test('ignores malformed gift redemption tokens in hash links', async () => { + window.location.hash = '#/portal/gift/redeem/%E0%A4%A'; + + const app = new App({siteUrl: 'http://example.com'}); + app.fetchGiftRedemptionData = vi.fn(); + + const result = await app.fetchLinkData(FixtureSite.singleTier.basic, FixtureMember.free); + + expect(result).toEqual({}); + expect(app.fetchGiftRedemptionData).not.toHaveBeenCalled(); + }); + + test('ignores malformed gift redemption tokens in trigger links', async () => { + const app = new App({siteUrl: 'http://example.com'}); + app.dispatchAction = vi.fn(); + app.fetchGiftRedemptionData = vi.fn(); + app.state = { + ...app.state, + initStatus: 'success', + site: {...FixtureSite.singleTier.basic, labs: {giftSubscriptions: true}} + }; + + await app.clickHandler({ + preventDefault: vi.fn(), + currentTarget: { + dataset: { + portal: 'gift/redeem/%E0%A4%A' + } + } + }); + + expect(app.fetchGiftRedemptionData).not.toHaveBeenCalled(); + expect(app.dispatchAction).not.toHaveBeenCalled(); + }); + + test('drops stale custom-trigger gift redemption responses', async () => { + const app = new App({siteUrl: 'http://example.com'}); + const firstRequest = createDeferred(); + const secondRequest = createDeferred(); + + app.setState = vi.fn((updatedState) => { + app.state = {...app.state, ...updatedState}; + }); + app.state = { + ...app.state, + initStatus: 'success', + site: {...FixtureSite.singleTier.basic, labs: {giftSubscriptions: true}} + }; + app.fetchGiftRedemptionData = vi.fn(({token}) => { + return token === 'first-token' ? firstRequest.promise : secondRequest.promise; + }); + + const firstClick = app.clickHandler({ + preventDefault: vi.fn(), + currentTarget: { + dataset: { + portal: 'gift/redeem/first-token' + } + } + }); + const secondClick = app.clickHandler({ + preventDefault: vi.fn(), + currentTarget: { + dataset: { + portal: 'gift/redeem/second-token' + } + } + }); + + secondRequest.resolve({ + page: 'giftRedemption', + pageData: { + token: 'second-token' + } + }); + await secondClick; + + firstRequest.resolve({ + page: 'giftRedemption', + pageData: { + token: 'first-token' + } + }); + await firstClick; + + expect(app.setState).toHaveBeenCalledTimes(1); + expect(app.state.pageData.token).toBe('second-token'); + }); + + test('drops stale hashchange gift redemption responses', async () => { + const app = new App({siteUrl: 'http://example.com'}); + const firstRequest = createDeferred(); + const secondRequest = createDeferred(); + + app.setState = vi.fn((updatedState) => { + app.state = {...app.state, ...updatedState}; + }); + app.state = { + ...app.state, + site: {...FixtureSite.singleTier.basic, labs: {giftSubscriptions: true}}, + member: FixtureMember.free + }; + app.fetchGiftRedemptionData = vi.fn(({token}) => { + return token === 'first-token' ? firstRequest.promise : secondRequest.promise; + }); + + window.location.hash = '#/portal/gift/redeem/first-token'; + const firstUpdate = app.updateStateForPreviewLinks(); + + window.location.hash = '#/portal/gift/redeem/second-token'; + const secondUpdate = app.updateStateForPreviewLinks(); + + secondRequest.resolve({ + showPopup: true, + page: 'giftRedemption', + pageData: { + token: 'second-token' + } + }); + await secondUpdate; + + firstRequest.resolve({ + showPopup: true, + page: 'giftRedemption', + pageData: { + token: 'first-token' + } + }); + await firstUpdate; + + expect(app.state.pageData.token).toBe('second-token'); + expect(app.setState).toHaveBeenCalledTimes(1); + }); + test('parses retention offer preview query data into account cancellation flow', () => { const app = new App({siteUrl: 'http://example.com'}); const previewData = app.fetchOfferQueryStrData('redemption_type=retention&display_title=Before%2520you%2520go&display_description=Please%2520stay&type=percent&amount=100&duration=repeating&duration_in_months=2&cadence=month&tier_id=product_123&enabled=false'); diff --git a/apps/portal/test/portal-links.test.js b/apps/portal/test/portal-links.test.js index 6214a062176..64d6319c106 100644 --- a/apps/portal/test/portal-links.test.js +++ b/apps/portal/test/portal-links.test.js @@ -3,7 +3,23 @@ import {site as FixtureSite, member as FixtureMember} from './utils/test-fixture import {appRender, fireEvent, waitFor, within} from './utils/test-utils'; import setupGhostApi from '../src/utils/api'; -const setup = async ({site, member = null, showPopup = true}) => { +const defaultGiftResponse = { + gift: { + token: 'gift-token-123', + cadence: 'year', + duration: 1, + tier: { + id: 'tier-gift', + name: 'Bronze', + benefits: [ + {id: 'benefit-1', name: 'Five great stories to read every day'}, + {id: 'benefit-2', name: 'Videos and podcasts to charm and delight you'} + ] + } + } +}; + +const setup = async ({site, member = null, showPopup = true, giftResponse = defaultGiftResponse, giftError = null}) => { const ghostApi = setupGhostApi({siteUrl: 'https://example.com'}); ghostApi.init = vi.fn(() => { @@ -25,6 +41,14 @@ const setup = async ({site, member = null, showPopup = true}) => { return Promise.resolve(); }); + ghostApi.gift.fetchRedemptionData = vi.fn(() => { + if (giftError) { + return Promise.reject(giftError); + } + + return Promise.resolve(giftResponse); + }); + const utils = appRender( ); @@ -379,6 +403,120 @@ describe('Portal Data links:', () => { }); }); + describe('#/portal/gift/redeem/', () => { + const giftRedemptionHash = '#/portal/gift/redeem/gift-token-123'; + + const setupGiftRedemption = async ({giftError = null, giftResponse = defaultGiftResponse} = {}) => { + window.location.hash = giftRedemptionHash; + + return setup({ + site: {...FixtureSite.singleTier.basic, labs: {giftSubscriptions: true}}, + member: FixtureMember.free, + showPopup: false, + giftError, + giftResponse + }); + }; + + const expectGiftRedemptionErrorToast = async ({utils, subtitle}) => { + const notificationFrame = await utils.findByTitle(/portal-notification/i); + expect(notificationFrame).toBeInTheDocument(); + expect(utils.queryByTitle(/portal-popup/i)).not.toBeInTheDocument(); + + const notificationIframeDocument = notificationFrame.contentDocument; + expect(await within(notificationIframeDocument).findByText(/Gift could not be redeemed/i)).toBeInTheDocument(); + expect(within(notificationIframeDocument).queryByText(subtitle)).toBeInTheDocument(); + }; + + test('renders a toast error when gift has expired', async () => { + let { + ghostApi, triggerButtonFrame, ...utils + } = await setupGiftRedemption({ + giftError: new Error('This gift has expired.') + }); + + expect(triggerButtonFrame).toBeInTheDocument(); + expect(ghostApi.gift.fetchRedemptionData).toHaveBeenCalledWith({token: 'gift-token-123'}); + + await expectGiftRedemptionErrorToast({ + utils, + subtitle: /This gift has expired\./i + }); + }); + + test('renders a toast error when gift has already been redeemed', async () => { + let { + ghostApi, triggerButtonFrame, ...utils + } = await setupGiftRedemption({ + giftError: new Error('This gift has already been redeemed.') + }); + + expect(triggerButtonFrame).toBeInTheDocument(); + expect(ghostApi.gift.fetchRedemptionData).toHaveBeenCalledWith({token: 'gift-token-123'}); + + await expectGiftRedemptionErrorToast({ + utils, + subtitle: /This gift has already been redeemed\./i + }); + }); + + test('renders a toast error when gift link is invalid', async () => { + let { + ghostApi, triggerButtonFrame, ...utils + } = await setupGiftRedemption({ + giftError: new Error('Failed to load gift data') + }); + + expect(triggerButtonFrame).toBeInTheDocument(); + expect(ghostApi.gift.fetchRedemptionData).toHaveBeenCalledWith({token: 'gift-token-123'}); + + await expectGiftRedemptionErrorToast({ + utils, + subtitle: /Gift link is not valid/i + }); + }); + + test('renders gift redemption popup when gift is valid', async () => { + let { + ghostApi, popupFrame, triggerButtonFrame, ...utils + } = await setupGiftRedemption(); + + expect(triggerButtonFrame).toBeInTheDocument(); + + popupFrame = await utils.findByTitle(/portal-popup/i); + expect(popupFrame).toBeInTheDocument(); + + const popupIframeDocument = popupFrame.contentDocument; + expect(await within(popupIframeDocument).findByText(/You've been gifted a membership/i)).toBeInTheDocument(); + expect(within(popupIframeDocument).queryByText(/Bronze/i)).toBeInTheDocument(); + expect(within(popupIframeDocument).queryByText(/1 year/i)).toBeInTheDocument(); + expect(within(popupIframeDocument).queryByText(/Five great stories to read every day/i)).toBeInTheDocument(); + + const nameInput = within(popupIframeDocument).getByLabelText(/your name/i); + const emailInput = within(popupIframeDocument).getByLabelText(/your email/i); + + expect(nameInput).toHaveValue('Jamie Larson'); + expect(emailInput).toHaveValue('jamie@example.com'); + expect(ghostApi.gift.fetchRedemptionData).toHaveBeenCalledWith({token: 'gift-token-123'}); + }); + + // TODO for GA: Remove test + test('does not open when giftSubscriptions labs flag is disabled', async () => { + window.location.hash = '#/portal/gift/redeem/gift-token-123'; + + let { + ghostApi, popupFrame, triggerButtonFrame + } = await setup({ + site: {...FixtureSite.singleTier.basic, labs: {}}, + showPopup: false + }); + + expect(triggerButtonFrame).toBeInTheDocument(); + expect(popupFrame).not.toBeInTheDocument(); + expect(ghostApi.gift.fetchRedemptionData).not.toHaveBeenCalled(); + }); + }); + describe('?stripe=gift-purchase-success', () => { test('opens gift success page when giftSubscriptions labs flag is enabled', async () => { window.location.href = 'https://portal.localhost/?stripe=gift-purchase-success&gift_token=abc123'; @@ -401,7 +539,7 @@ describe('Portal Data links:', () => { const giftTitle = within(popupFrame.contentDocument).queryByText(/gift ready to share/i); expect(giftTitle).toBeInTheDocument(); - const redeemUrl = within(popupFrame.contentDocument).queryByText(/\/gift\/abc123/); + const redeemUrl = within(popupFrame.contentDocument).queryByText(/#\/portal\/gift\/redeem\/abc123/); expect(redeemUrl).toBeInTheDocument(); }); diff --git a/apps/portal/test/unit/components/notification.test.js b/apps/portal/test/unit/components/notification.test.js new file mode 100644 index 00000000000..ff827267d08 --- /dev/null +++ b/apps/portal/test/unit/components/notification.test.js @@ -0,0 +1,88 @@ +import {act, render, waitFor} from '@testing-library/react'; +import {vi} from 'vitest'; +import Notification from '../../../src/components/notification'; +import AppContext from '../../../src/app-context'; + +vi.mock('../../../src/components/frame', () => ({ + default: ({children}) =>
{children}
+})); + +vi.mock('../../../src/utils/notifications', () => ({ + default: vi.fn(() => null), + clearURLParams: vi.fn() +})); + +describe('Notification', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.useRealTimers(); + vi.clearAllMocks(); + }); + + test('remounts notification content when notification count changes', async () => { + const doAction = vi.fn(); + const site = { + url: 'https://example.com', + title: 'Example Site' + }; + + const {container, getByText, rerender} = render( + + + + ); + + await waitFor(() => { + expect(getByText('First notification')).toBeInTheDocument(); + }); + + rerender( + + + + ); + + await waitFor(() => { + expect(getByText('Second notification')).toBeInTheDocument(); + }); + + await act(async () => { + vi.advanceTimersByTime(150); + }); + + expect(container.querySelector('.gh-portal-notification')).not.toHaveClass('slideout'); + }); +}); diff --git a/ghost/i18n/locales/af/portal.json b/ghost/i18n/locales/af/portal.json index 3a2a757a247..a0ffa190b06 100644 --- a/ghost/i18n/locales/af/portal.json +++ b/ghost/i18n/locales/af/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dae gratis", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Rekening", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "U het suksesvol aangemeld.", "You've successfully subscribed to {siteTitle}": "Jy het suksesvol ingeteken op {siteTitle}", "Your account": "U rekening", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "U insette help om te bepaal wat gepubliseer word.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "U intekening sal verval op {expiryDate}", "Your subscription will renew on {renewalDate}": "U intekening sal hernu op {renewalDate}", diff --git a/ghost/i18n/locales/ar/portal.json b/ghost/i18n/locales/ar/portal.json index 3ca4356c1c0..9899131e7f4 100644 --- a/ghost/i18n/locales/ar/portal.json +++ b/ghost/i18n/locales/ar/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} أيام مجانية", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "الحساب", "Account details updated successfully": ".تم تحديث تفاصيل الحساب بنجاح", @@ -244,9 +246,11 @@ "You've successfully signed in.": ".تم تسجيل الدخول بنجاح", "You've successfully subscribed to {siteTitle}": "تم الاشتراك بنجاح في {siteTitle}", "Your account": "حسابك", + "Your email": "", "Your email has failed to resubscribe, please try again": "بريدك الاكتروني لم ينجح في إعادة الاشتراك، رجاء المحاولة مرة أخرى", "your inbox": "بريدك الوارد", "Your input helps shape what gets published.": "آرائك تساهم في تحسين ما ينشر.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "{expiryDate} اشتراكك سوف ينتهي في", "Your subscription will renew on {renewalDate}": "{renewalDate} اشتراكك سوف يتجدد في", diff --git a/ghost/i18n/locales/bg/portal.json b/ghost/i18n/locales/bg/portal.json index 702b056d13b..bb64d839d5d 100644 --- a/ghost/i18n/locales/bg/portal.json +++ b/ghost/i18n/locales/bg/portal.json @@ -12,9 +12,11 @@ "{months} months": "{months} месеца", "{months} months free": "{months} месеца безплатно", "{trialDays} days free": "{trialDays} дни безплатно", + "{years} years": "", "+1 (123) 456-7890": "+359 88 123-4567", "1 month": "1 месец", "1 month free": "1 месец безплатно", + "1 year": "", "Access your RSS feeds": "Достъп до вашите RSS емисии", "Account": "Профил", "Account details updated successfully": "Настройките бяха успешно обновени", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Влязохте успешно.", "You've successfully subscribed to {siteTitle}": "Успешно се абонирахте за {siteTitle}", "Your account": "Вашият профил", + "Your email": "", "Your email has failed to resubscribe, please try again": "Неуспешно подновяване на абонамент с този имейл, опитайте отново", "your inbox": "вашия имейл", "Your input helps shape what gets published.": "Вашият принос помага за създаването на съдържанието.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "Абонаментът ви е прекратен и ще изтече на {expiryDate}.", "Your subscription will expire on {expiryDate}": "Абонаментът ви ще изтече на {expiryDate}", "Your subscription will renew on {renewalDate}": "Абонаментът ви ще се поднови на {renewalDate}", diff --git a/ghost/i18n/locales/bn/portal.json b/ghost/i18n/locales/bn/portal.json index 30ccdc82b58..4e62ad20f60 100644 --- a/ghost/i18n/locales/bn/portal.json +++ b/ghost/i18n/locales/bn/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} দিন ফ্রি", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "অ্যাকাউন্ট", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "আপনি সফলভাবে সাইন ইন করেছেন।", "You've successfully subscribed to {siteTitle}": "আপনি সফলভাবে সাবস্ক্রাইব করেছেন {siteTitle}", "Your account": "আপনার অ্যাকাউন্ট", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "আপনার ইনপুট প্রকাশিত হওয়ার বিষয়টি নির্ধারণ করতে সহায়তা করে।", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "আপনার সাবস্ক্রিপশনের মেয়াদ {expiryDate} এ শেষ হবে", "Your subscription will renew on {renewalDate}": "আপনার সাবস্ক্রিপশন {renewalDate} এ নবায়ন হবে", diff --git a/ghost/i18n/locales/bs/portal.json b/ghost/i18n/locales/bs/portal.json index 0364d48ccc6..c73d7d2aa30 100644 --- a/ghost/i18n/locales/bs/portal.json +++ b/ghost/i18n/locales/bs/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dana besplatno", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Račun", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Uspješna prijava.", "You've successfully subscribed to {siteTitle}": "Uspješna pretplata na {siteTitle}", "Your account": "Tvoj račun", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Tvoj feedback pomaže pri odabiru tema koje se objavljuju.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Tvoja pretplata istieče {expiryDate}", "Your subscription will renew on {renewalDate}": "Tvoja pretplata će se obnoviti {renewalDate}", diff --git a/ghost/i18n/locales/ca/portal.json b/ghost/i18n/locales/ca/portal.json index 50c687332be..6f390b73f57 100644 --- a/ghost/i18n/locales/ca/portal.json +++ b/ghost/i18n/locales/ca/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dies de prova", + "{years} years": "", "+1 (123) 456-7890": "+34 123 456 789", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Compte", "Account details updated successfully": "Detalls del compte actualitzats correctament", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Has iniciat la sessió correctament.", "You've successfully subscribed to {siteTitle}": "T'has subscrit correctament a {siteTitle}", "Your account": "El teu compte", + "Your email": "", "Your email has failed to resubscribe, please try again": "La teva adreça de correu no s'ha pogut tornar a subscriure - torna-ho a intentar", "your inbox": "", "Your input helps shape what gets published.": "La teva opinió ajuda a definir què es publica.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "La teva subscripció caducarà el {expiryDate}", "Your subscription will renew on {renewalDate}": "La teva subscripció es renovarà el {renewalDate}", diff --git a/ghost/i18n/locales/context.json b/ghost/i18n/locales/context.json index 22501ef1996..407669745d7 100644 --- a/ghost/i18n/locales/context.json +++ b/ghost/i18n/locales/context.json @@ -2,8 +2,9 @@ "(save {highestYearlyDiscount}%)": "Appears in portal next to the yearly plan selector", "+1 (123) 456-7890": "Placeholder for phone number input field", "1 comment": "Comment count displayed above the comments section in case there is only one", - "1 month": "Internal helper value passed as '{amountOff}' in Portal retention-offer logic for one-month free offers", - "1 month free": "Discount badge shown in Portal retention offer section when cancelling a subscription", + "1 month": "Duration label for a 1 month subscription", + "1 month free": "Retention offer discount badge shown during subscription cancellation", + "1 year": "Duration label for a 1 year subscription", "Access your RSS feeds": "Default description text for the Transistor podcast integration in Portal", "Account": "A label in Portal for your account area", "Account details updated successfully": "Popover message in Portal", @@ -365,10 +366,12 @@ "You've successfully signed in.": "A notification displayed when the user signs in", "You've successfully subscribed to {siteTitle}": "Notification displayed after a successful signup. The ... tag wraps the site name. {siteTitle} is the name of the publication", "Your account": "A label indicating member account details", + "Your email": "Form label for the email address input in e.g. signup forms", "Your email address": "Placeholder text in an input field", "Your email has failed to resubscribe, please try again": "error message in portal", "Your free trial ends on {date}, at which time you will be charged the regular price. You can always cancel before then.": "Newsletter text - shown to trialing members when receiving the newsletter (if subscription status is being shown)", "Your input helps shape what gets published.": "Descriptive text displayed on the member feedback UI, telling people how their feedback is used", + "Your name": "Form label for the name input in e.g. signup forms", "Your request will be sent to the owner of this site.": "Descriptive text displayed in the report comment modal", "Your subscription has been canceled and will expire on {date}. You can resume your subscription via your account settings.": "Shown in the newsletter footer when a member has cancelled their subscription but it hasn't expired yet.", "Your subscription has been canceled and will expire on {expiryDate}.": "Banner message shown on Portal account page when a subscription is canceled but still active until the period end", @@ -404,7 +407,8 @@ "{memberEmail} will no longer receive emails when someone replies to your comments.": "Shown when a member unsubscribes from comment replies", "{memberEmail} will no longer receive this newsletter.": "Shown when a member unsubscribes from a newsletter", "{memberEmail} will no longer receive {newsletterName} newsletter.": "Shown when a member unsubscribes from a newsletter", - "{months} months": "Internal helper value passed as '{amountOff}' in Portal retention-offer logic for multi-month free offers", - "{months} months free": "Discount badge in Portal retention offer section for multi-month free offers", - "{trialDays} days free": "Portal - label for a free trial that lasts a specific number of days" + "{months} months": "Duration label for a multiple-month subscription", + "{months} months free": "Retention offer discount badge shown during subscription cancellation", + "{trialDays} days free": "Portal - label for a free trial that lasts a specific number of days", + "{years} years": "Duration label for a multiple-year subscription" } \ No newline at end of file diff --git a/ghost/i18n/locales/cs/portal.json b/ghost/i18n/locales/cs/portal.json index bf780b5d13f..76f932125bb 100644 --- a/ghost/i18n/locales/cs/portal.json +++ b/ghost/i18n/locales/cs/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dní zdarma", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Účet", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Úspěšně jste se přihlásili.", "You've successfully subscribed to {siteTitle}": "Úspěšně jste se přihlásili k odběru {siteTitle}", "Your account": "Váš účet", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Vaše připomínky pomáhají ladit a tvořit obsah webu.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Vaše předplatné vyprší {expiryDate}", "Your subscription will renew on {renewalDate}": "Vaše předplatné se obnoví {renewalDate}", diff --git a/ghost/i18n/locales/da/portal.json b/ghost/i18n/locales/da/portal.json index 1f88c9dc1fd..2e8962056e8 100644 --- a/ghost/i18n/locales/da/portal.json +++ b/ghost/i18n/locales/da/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dage gratis", + "{years} years": "", "+1 (123) 456-7890": "+45 12 34 56 78", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Konto", "Account details updated successfully": "Kontooplysningerne blev opdateret", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Du er nu logget ind.", "You've successfully subscribed to {siteTitle}": "Du er nu tilmeldt til {siteTitle}", "Your account": "Din konto", + "Your email": "", "Your email has failed to resubscribe, please try again": "Din e-mail kunne ikke genabonneres, prøv venligst igen.", "your inbox": "", "Your input helps shape what gets published.": "Dit input hjælper med at forme det der bliver publiceret.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Dit abonnement udløber {expiryDate}", "Your subscription will renew on {renewalDate}": "Dit abonnement fornyes {renewalDate}", diff --git a/ghost/i18n/locales/de-CH/portal.json b/ghost/i18n/locales/de-CH/portal.json index ffdb33864d8..db0cdd40731 100644 --- a/ghost/i18n/locales/de-CH/portal.json +++ b/ghost/i18n/locales/de-CH/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} Tage kostenfrei", + "{years} years": "", "+1 (123) 456-7890": "+41 12 234 56 78", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Konto", "Account details updated successfully": "Kontodaten erfolgreich aktualisiert", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Sie haben sich erfolgreich angemeldet.", "You've successfully subscribed to {siteTitle}": "Sie haben sich erfolgreich abonniert bei {siteTitle}", "Your account": "Ihr Konto", + "Your email": "", "Your email has failed to resubscribe, please try again": "Ihre E-Mail-Adresse konnte nicht angemeldet werden. Bitte versuchen Sie es erneut", "your inbox": "Ihre Inbox", "Your input helps shape what gets published.": "Ihr Beitrag kann unsere Berichterstattung beeinflussen und mitprägen.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "Ihr Abo wurde gekündigt und läuft am {expiryDate} aus.", "Your subscription will expire on {expiryDate}": "Ihr Abo endet am {expiryDate}.", "Your subscription will renew on {renewalDate}": "Ihr Abo wird am {renewalDate} erneuert.", diff --git a/ghost/i18n/locales/de/portal.json b/ghost/i18n/locales/de/portal.json index 25be9f3a708..f92f923f528 100644 --- a/ghost/i18n/locales/de/portal.json +++ b/ghost/i18n/locales/de/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} Tage kostenfrei", + "{years} years": "", "+1 (123) 456-7890": "+49 123 4567890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Konto", "Account details updated successfully": "Kontodaten erfolgreich aktualisiert", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Du hast dich erfolgreich angemeldet.", "You've successfully subscribed to {siteTitle}": "Du hast dich erfolgreich angemeldet bei {siteTitle}", "Your account": "Dein Konto", + "Your email": "", "Your email has failed to resubscribe, please try again": "Deine E-Mailadresse konnte nicht angemeldet werden. Bitte versuche es noch einmal.", "your inbox": "Dein Posteingang", "Your input helps shape what gets published.": "Dein Beitrag trägt dazu bei, was veröffentlicht wird.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Dein Abonnement wird am {expiryDate} ablaufen.", "Your subscription will renew on {renewalDate}": "Dein Abonnement wird am {renewalDate} erneuert.", diff --git a/ghost/i18n/locales/el/portal.json b/ghost/i18n/locales/el/portal.json index c7561fd9ffb..7a972eebfa0 100644 --- a/ghost/i18n/locales/el/portal.json +++ b/ghost/i18n/locales/el/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} ημέρες δωρεάν", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Λογαριασμός", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Έχετε συνδεθεί επιτυχώς.", "You've successfully subscribed to {siteTitle}": "Έχετε εγγραφεί επιτυχώς στο {siteTitle}", "Your account": "Ο λογαριασμός σας", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Η συνεισφορά σας βοηθά να διαμορφωθεί το τι δημοσιεύεται.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Η συνδρομή σας θα λήξει στις {expiryDate}", "Your subscription will renew on {renewalDate}": "Η συνδρομή σας θα ανανεωθεί στις {renewalDate}", diff --git a/ghost/i18n/locales/en/portal.json b/ghost/i18n/locales/en/portal.json index 1d24e92b296..b48e74f3847 100644 --- a/ghost/i18n/locales/en/portal.json +++ b/ghost/i18n/locales/en/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "", "You've successfully subscribed to {siteTitle}": "", "Your account": "", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "", "Your subscription will renew on {renewalDate}": "", diff --git a/ghost/i18n/locales/eo/portal.json b/ghost/i18n/locales/eo/portal.json index 23093912766..bc69efdb6b8 100644 --- a/ghost/i18n/locales/eo/portal.json +++ b/ghost/i18n/locales/eo/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} tagoj senpagaj", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Konto", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "", "You've successfully subscribed to {siteTitle}": "", "Your account": "Via konto", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Via enigo helpas formi kio estas aperigita.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "", "Your subscription will renew on {renewalDate}": "", diff --git a/ghost/i18n/locales/es/portal.json b/ghost/i18n/locales/es/portal.json index d50841dc0ae..57c2f3ab342 100644 --- a/ghost/i18n/locales/es/portal.json +++ b/ghost/i18n/locales/es/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} días gratis", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Cuenta", "Account details updated successfully": "Los detalles de la cuenta se actualizaron con éxito", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Has iniciado sesión correctamente.", "You've successfully subscribed to {siteTitle}": "Te has suscrito correctamente a {siteTitle}", "Your account": "Tu cuenta", + "Your email": "", "Your email has failed to resubscribe, please try again": "No se ha podido volver a suscribir tu correo electrónico, inténtalo de nuevo por favor", "your inbox": "", "Your input helps shape what gets published.": "Tu opinión ayuda a definir lo que se publica.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Tu suscripción caducará el {expiryDate} ", "Your subscription will renew on {renewalDate}": "Tu suscripción se renovará el {renewalDate}", diff --git a/ghost/i18n/locales/et/portal.json b/ghost/i18n/locales/et/portal.json index 1d29bf4819b..b538bcd88ce 100644 --- a/ghost/i18n/locales/et/portal.json +++ b/ghost/i18n/locales/et/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} päeva tasuta", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Konto", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Olete edukalt sisse loginud.", "You've successfully subscribed to {siteTitle}": "Olete edukalt tellinud {siteTitle}", "Your account": "Teie konto", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Teie sisend aitab kujundada seda, mida avaldatakse.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Teie tellimus aegub {expiryDate}", "Your subscription will renew on {renewalDate}": "Teie tellimus uueneb {renewalDate}", diff --git a/ghost/i18n/locales/eu/portal.json b/ghost/i18n/locales/eu/portal.json index 22a44ae1be4..22e5c5ed1ba 100644 --- a/ghost/i18n/locales/eu/portal.json +++ b/ghost/i18n/locales/eu/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} egun debalde", + "{years} years": "", "+1 (123) 456-7890": "+3X 123 456 789", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Kontua", "Account details updated successfully": "Kontuaren xehetasunak eguneratu dira", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Saioa hasi duzu.", "You've successfully subscribed to {siteTitle}": "Honakora harpidetu zara: {siteTitle}", "Your account": "Zure kontua", + "Your email": "", "Your email has failed to resubscribe, please try again": "Ezin izan da zure ePosta berriro harpidetu, saiatu berriro", "your inbox": "", "Your input helps shape what gets published.": "Zure ekarpenak argitaratzen denari forma ematen laguntzen digu.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Zure harpidetza {expiryDate}(e)an iraungiko da", "Your subscription will renew on {renewalDate}": "Zure harpidetza {renewalDate}(e)an berrituko da", diff --git a/ghost/i18n/locales/fa/portal.json b/ghost/i18n/locales/fa/portal.json index 95eb3368ac7..25113889da9 100644 --- a/ghost/i18n/locales/fa/portal.json +++ b/ghost/i18n/locales/fa/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} روز رایگان", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "حساب کاربری", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "شما با موفقیت وارد شدید.", "You've successfully subscribed to {siteTitle}": "شما با موفقیت مشترک این موارد شدید: {siteTitle}", "Your account": "حساب کاربری شما", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "تلاش شما به آنچه که منتشر می\u200cشود، شکل می\u200cدهد.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "اشتراک شما در تاریخ {expiryDate} منقضی می\u200cشود", "Your subscription will renew on {renewalDate}": "اشتراک شما در تاریخ {renewalDate} تمدید می\u200cشود", diff --git a/ghost/i18n/locales/fi/portal.json b/ghost/i18n/locales/fi/portal.json index 6fa20a316f3..b86c0572b6f 100644 --- a/ghost/i18n/locales/fi/portal.json +++ b/ghost/i18n/locales/fi/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} päivää ilmaiseksi", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Oma tili", "Account details updated successfully": "Tilin lisätiedot on päivitetty", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Olet kirjautunut sisään onnistuneesti", "You've successfully subscribed to {siteTitle}": "Tilaus onnistui: {siteTitle}", "Your account": "Tilisi", + "Your email": "", "Your email has failed to resubscribe, please try again": "Tilauksen uusiminen epäonnistui sähköpostillesi. Yritäthän uudelleen.", "your inbox": "sähköpostilaatikkoosi", "Your input helps shape what gets published.": "Antamasi palautteen avulla muokataan julkaistavaa sisältöä", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Tilauksesi päättyy {expiryDate}", "Your subscription will renew on {renewalDate}": "Tilauksesi uusiutuu {renewalDate}", diff --git a/ghost/i18n/locales/fr/portal.json b/ghost/i18n/locales/fr/portal.json index 6c4e06472f6..21ebed05d12 100644 --- a/ghost/i18n/locales/fr/portal.json +++ b/ghost/i18n/locales/fr/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} jours gratuits", + "{years} years": "", "+1 (123) 456-7890": "+33 1 23 45 67 89", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Compte", "Account details updated successfully": "Mise à jour réussie des données du compte", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Vous vous êtes connecté avec succès.", "You've successfully subscribed to {siteTitle}": "Vous vous êtes abonné à {siteTitle}", "Your account": "Votre compte", + "Your email": "", "Your email has failed to resubscribe, please try again": "Votre e-mail n'a pas été pris en compte pour le réabonnement, veuillez réessayer", "your inbox": "votre boîte de réception", "Your input helps shape what gets published.": "Votre avis aide à améliorer ce qui est publié.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Votre abonnement expirera le {expiryDate}", "Your subscription will renew on {renewalDate}": "Votre abonnement sera renouvelé le {renewalDate}", diff --git a/ghost/i18n/locales/gd/portal.json b/ghost/i18n/locales/gd/portal.json index c44570a876d..3ffe18b1e13 100644 --- a/ghost/i18n/locales/gd/portal.json +++ b/ghost/i18n/locales/gd/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays}l an-asgaidh", + "{years} years": "", "+1 (123) 456-7890": "+44 1234 567890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Cunntas", "Account details updated successfully": "Chaidh roghainnean a' chunntais ùrachadh gu soirbheachail", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Chlàraich thu a-steach gu soirbheachail.", "You've successfully subscribed to {siteTitle}": "Fo-sgrìobh thu gu soirbheachail gu {siteTitle}", "Your account": "An cunntas agad", + "Your email": "", "Your email has failed to resubscribe, please try again": "Dh'fhàillig ath-nuadhachadh an fho-sgrìobhaidh, feuch a-rithist", "your inbox": "", "Your input helps shape what gets published.": "Bheir na beachdan agad buaidh air na foillseachaidhean ri teachd.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Falbhaidh an ùine air am fo-sgrìobhadh agad: {expiryDate}", "Your subscription will renew on {renewalDate}": "Ath-nuadhaichidh am fo-sgrìobhadh agad: {renewalDate}", diff --git a/ghost/i18n/locales/he/portal.json b/ghost/i18n/locales/he/portal.json index 728eaeec8ee..9688317c281 100644 --- a/ghost/i18n/locales/he/portal.json +++ b/ghost/i18n/locales/he/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} ימים חינם", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "חשבון", "Account details updated successfully": "פרטי החשבון עודכנו בהצלחה", @@ -244,9 +246,11 @@ "You've successfully signed in.": "נכנסתם בהצלחה.", "You've successfully subscribed to {siteTitle}": "נרשמתם בהצלחה ל {siteTitle}", "Your account": "החשבון שלך", + "Your email": "", "Your email has failed to resubscribe, please try again": "שגיאה בהרשמה מחדש עם כתובת המייל שלכם, נסו שוב", "your inbox": "", "Your input helps shape what gets published.": "המשוב שלכם עוזר לנו לעצב את התוכן שייפורסם.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "המינוי שלכם יפוג ב {expiryDate}", "Your subscription will renew on {renewalDate}": "המנוי שלכם יתחדש ב {renewalDate}", diff --git a/ghost/i18n/locales/hi/portal.json b/ghost/i18n/locales/hi/portal.json index bfcc09ddd0a..9ce406155fd 100644 --- a/ghost/i18n/locales/hi/portal.json +++ b/ghost/i18n/locales/hi/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} दिन मुफ्त", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "खाता", "Account details updated successfully": "खाते की जानकारी सफलतापूर्वक अपडेट की गई", @@ -244,9 +246,11 @@ "You've successfully signed in.": "आपने सफलतापूर्वक साइन इन कर लिया है।", "You've successfully subscribed to {siteTitle}": "आपने सफलतापूर्वक सदस्यता ली है {siteTitle}", "Your account": "आपका खाता", + "Your email": "", "Your email has failed to resubscribe, please try again": "आपका ईमेल पुनः सदस्यता लेने में विफल रहा, कृपया पुनः प्रयास करें", "your inbox": "", "Your input helps shape what gets published.": "आपका इनपुट प्रकाशित होने वाली चीज़ों को आकार देने में मदद करता है।", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "आपकी सदस्यता {expiryDate} को समाप्त हो जाएगी", "Your subscription will renew on {renewalDate}": "आपकी सदस्यता {renewalDate} को नवीनीकृत होगी", diff --git a/ghost/i18n/locales/hr/portal.json b/ghost/i18n/locales/hr/portal.json index 0e4b9c8a8ba..61f34f32016 100644 --- a/ghost/i18n/locales/hr/portal.json +++ b/ghost/i18n/locales/hr/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dana besplatno", + "{years} years": "", "+1 (123) 456-7890": "(+385 1) 234-5678", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Vaš račun", "Account details updated successfully": "Podaci računa su uspješno ažurirani", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Uspješno ste prijavljeni.", "You've successfully subscribed to {siteTitle}": "Uspješno ste pretplaćeni na {siteTitle}", "Your account": "Vaš korisnički račun", + "Your email": "", "Your email has failed to resubscribe, please try again": "Ponovna pretplata nije uspjela, pokušajte ponovno", "your inbox": "", "Your input helps shape what gets published.": "Vaš doprinos pomaže u oblikovanju sadržaja kojeg objavljujemo.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Vaša pretplata istječe {expiryDate}", "Your subscription will renew on {renewalDate}": "Vaša pretplata će se obnoviti {renewalDate}", diff --git a/ghost/i18n/locales/hu/portal.json b/ghost/i18n/locales/hu/portal.json index df86e9864b9..e5c209afd7b 100644 --- a/ghost/i18n/locales/hu/portal.json +++ b/ghost/i18n/locales/hu/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} nap ingyen", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Fiók", "Account details updated successfully": "A fiók adatai sikeresen frissültek.", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Sikeresen bejelentkeztél.", "You've successfully subscribed to {siteTitle}": "Sikeresen bejelentkeztél ide: {siteTitle}", "Your account": "Fiókod", + "Your email": "", "Your email has failed to resubscribe, please try again": "Az e-mail-címedet nem sikerült újra regisztrálni. Kérjük, próbáld újra", "your inbox": "", "Your input helps shape what gets published.": "A visszajelzésed segít abban, hogy miről írjunk", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Az előfizetésed lejár ekkor: {expiryDate}", "Your subscription will renew on {renewalDate}": "Az előfizetésed megújul ekkor: {renewalDate}", diff --git a/ghost/i18n/locales/id/portal.json b/ghost/i18n/locales/id/portal.json index bed53a35f7a..715a0eeeb7a 100644 --- a/ghost/i18n/locales/id/portal.json +++ b/ghost/i18n/locales/id/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "Gratis {trialDays} hari", + "{years} years": "", "+1 (123) 456-7890": "+62 123-4567-8900", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Akun", "Account details updated successfully": "Detail akun berhasil diperbarui", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Anda telah berhasil masuk.", "You've successfully subscribed to {siteTitle}": "Anda telah berhasil berlangganan ke {siteTitle}", "Your account": "Akun Anda", + "Your email": "", "Your email has failed to resubscribe, please try again": "Email Anda gagal berlangganan ulang, harap coba lagi", "your inbox": "", "Your input helps shape what gets published.": "Masukan Anda membantu membentuk apa yang dipublikasikan.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Langganan Anda akan berakhir pada {expiryDate}", "Your subscription will renew on {renewalDate}": "Langganan Anda akan diperpanjang pada {renewalDate}", diff --git a/ghost/i18n/locales/is/portal.json b/ghost/i18n/locales/is/portal.json index 34650d8c4e9..fa8fec3654e 100644 --- a/ghost/i18n/locales/is/portal.json +++ b/ghost/i18n/locales/is/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "dagar án endurgjalds", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Aðgangur", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Þér tókst að skrá þig inn", "You've successfully subscribed to {siteTitle}": "", "Your account": "Aðgangurinn þinn", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Áskrift þinni lýkur {expiryDate}", "Your subscription will renew on {renewalDate}": "Áskrift þín verður endurnýjuð {expiryDate}", diff --git a/ghost/i18n/locales/it/portal.json b/ghost/i18n/locales/it/portal.json index 0e9177b518b..b0556cbac72 100644 --- a/ghost/i18n/locales/it/portal.json +++ b/ghost/i18n/locales/it/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} giorni gratis", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Account", "Account details updated successfully": "Dettagli dell'account aggiornati con successo", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Accesso effettuato.", "You've successfully subscribed to {siteTitle}": "Iscrizione effettuata a {siteTitle}", "Your account": "Il tuo account", + "Your email": "", "Your email has failed to resubscribe, please try again": "La re-iscrizione della tua e-mail è fallita, per favore riprova", "your inbox": "la tua casella di posta", "Your input helps shape what gets published.": "Il tuo contributo aiuta a dare forma a ciò che viene pubblicato.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "Il tuo abbonamento è stato annullato e scadrà il {expiryDate}.", "Your subscription will expire on {expiryDate}": "Il tuo abbonamento scadrà il {expiryDate}", "Your subscription will renew on {renewalDate}": "Il tuo abbonamento verrà rinnovato il {renewalDate}", diff --git a/ghost/i18n/locales/ja/portal.json b/ghost/i18n/locales/ja/portal.json index 63a861d15db..b791ec9d336 100644 --- a/ghost/i18n/locales/ja/portal.json +++ b/ghost/i18n/locales/ja/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays}日間無料", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "アカウント", "Account details updated successfully": "アカウント詳細が更新されました", @@ -244,9 +246,11 @@ "You've successfully signed in.": "ログインに成功しました", "You've successfully subscribed to {siteTitle}": "の購読に成功しました {siteTitle}", "Your account": "あなたのアカウント", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "あなたの感想を今後の内容の参考にさせていただきます。", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "あなたの購読は{expiryDate}に期限切れになります。", "Your subscription will renew on {renewalDate}": "あなたの購読は{renewalDate}に更新されます。", diff --git a/ghost/i18n/locales/ko/portal.json b/ghost/i18n/locales/ko/portal.json index b3271ecad99..eb869c7a78a 100644 --- a/ghost/i18n/locales/ko/portal.json +++ b/ghost/i18n/locales/ko/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays}일 무료", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "계정", "Account details updated successfully": "계정 정보가 성공적으로 업데이트되었어요", @@ -244,9 +246,11 @@ "You've successfully signed in.": "성공적으로 로그인되었어요.", "You've successfully subscribed to {siteTitle}": "성공적으로 구독하셨어요: {siteTitle}", "Your account": "계정", + "Your email": "", "Your email has failed to resubscribe, please try again": "이메일 재구독에 실패했어요. 다시 시도해 주세요", "your inbox": "", "Your input helps shape what gets published.": "회원님의 의견은 게시물을 제작하는 것에 큰 도움이 돼요.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "회원님의 구독은 {expiryDate}에 만료돼요", "Your subscription will renew on {renewalDate}": "회원님의 구독은 {renewalDate}에 갱신돼요", diff --git a/ghost/i18n/locales/kz/portal.json b/ghost/i18n/locales/kz/portal.json index 17043429119..a4dfa7fb73c 100644 --- a/ghost/i18n/locales/kz/portal.json +++ b/ghost/i18n/locales/kz/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} күн тегін", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Аккаунт", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Кіру сәтті орындалды.", "You've successfully subscribed to {siteTitle}": "Жазылым сәтті орындалды {siteTitle}", "Your account": "Сіздің аккаунт", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Сіздің пікіріңіз жарияланатын мазмұнды қалыптастыруға көмектеседі.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Сіздің жазылым {expiryDate} күні аяқталады", "Your subscription will renew on {renewalDate}": "Сіздің жазылым {renewalDate} күні қайта жаңартылады", diff --git a/ghost/i18n/locales/lt/portal.json b/ghost/i18n/locales/lt/portal.json index 8c30ef68cfa..78786f50757 100644 --- a/ghost/i18n/locales/lt/portal.json +++ b/ghost/i18n/locales/lt/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} d. nemokamai", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Paskyra", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Sėkmingai prisijungėte.", "You've successfully subscribed to {siteTitle}": "Sėkmingai užsiprenumeravote {siteTitle}", "Your account": "Jūsų paskyra", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Jūsų indėlis padeda kurti tai, kas yra viešinama.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Jūsų prenumerata baigsis {expiryDate}", "Your subscription will renew on {renewalDate}": "Jūsų prenumerata atsinaujins {renewalDate}", diff --git a/ghost/i18n/locales/lv/portal.json b/ghost/i18n/locales/lv/portal.json index 137c0bc6949..6417dab6971 100644 --- a/ghost/i18n/locales/lv/portal.json +++ b/ghost/i18n/locales/lv/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays}\u00a0dienas bez maksas", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Konts", "Account details updated successfully": "Konta informācija ir veiksmīgi atjaunināta", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Jūs esat veiksmīgi pierakstījies.", "You've successfully subscribed to {siteTitle}": "Jūs esat veiksmīgi abonējis {siteTitle}", "Your account": "Jūsu konts", + "Your email": "", "Your email has failed to resubscribe, please try again": "Jūsu e-pasta abonēšana neizdevās atkārtoti. Lūdzu, mēģiniet vēlreiz", "your inbox": "", "Your input helps shape what gets published.": "Jūsu ieguldījums palīdz veidot publicējamo saturu.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Jūsu abonementa derīguma termiņš beigsies {expiryDate}", "Your subscription will renew on {renewalDate}": "Jūsu abonements tiks atjaunots šādā datumā: {renewalDate}", diff --git a/ghost/i18n/locales/mk/portal.json b/ghost/i18n/locales/mk/portal.json index 5a88c3587e5..63b2506c565 100644 --- a/ghost/i18n/locales/mk/portal.json +++ b/ghost/i18n/locales/mk/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} денови бесплатно", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Сметка", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Успешно се најавивте.", "You've successfully subscribed to {siteTitle}": "Успешно се претплативте на {siteTitle}", "Your account": "Вашата сметка", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Вашиот прилог помага да се оформи она кое ќе биде објавувано.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Вашата претплата ќе истече на {expiryDate}", "Your subscription will renew on {renewalDate}": "Вашата претплата ќе биде обновена на {renewalDate}", diff --git a/ghost/i18n/locales/mn/portal.json b/ghost/i18n/locales/mn/portal.json index 61429ac0674..6392c047654 100644 --- a/ghost/i18n/locales/mn/portal.json +++ b/ghost/i18n/locales/mn/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} өдөр үнэгүй", + "{years} years": "", "+1 (123) 456-7890": "+976 1234-5678", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Бүртгэл", "Account details updated successfully": "Бүртгэлийн мэдээлэл амжилттай шинэчлэгдлээ", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Та амжилттай нэвтэрлээ.", "You've successfully subscribed to {siteTitle}": "Таны захиалга амжилттай үүслээ. {siteTitle}", "Your account": "Таны бүртгэл", + "Your email": "", "Your email has failed to resubscribe, please try again": "Таны имэйлийг дахин захиалахад алдаа гарлаа, дахин оролдоно уу", "your inbox": "", "Your input helps shape what gets published.": "Таны санал дараа дараагийн нийтлэлийг илүү чанартай болгоход туслана", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Таны захиалга {expiryDate}-нд дуусна", "Your subscription will renew on {renewalDate}": "Таны захиалга {renewalDate}-нд шинэчлэгдэнэ", diff --git a/ghost/i18n/locales/ms/portal.json b/ghost/i18n/locales/ms/portal.json index 7c8ff05c770..3ed087f3adb 100644 --- a/ghost/i18n/locales/ms/portal.json +++ b/ghost/i18n/locales/ms/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "Percuma {trialDays} hari", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Akaun", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Anda telah berjaya log masuk.", "You've successfully subscribed to {siteTitle}": "", "Your account": "Akaun anda", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Input anda membantu membentuk apa yang diterbitkan.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Langganan anda akan tamat tempoh pada {expiryDate}", "Your subscription will renew on {renewalDate}": "Langganan anda akan diperbaharui pada {renewalDate}", diff --git a/ghost/i18n/locales/nb/portal.json b/ghost/i18n/locales/nb/portal.json index 45555cc8275..9a64a8a9465 100644 --- a/ghost/i18n/locales/nb/portal.json +++ b/ghost/i18n/locales/nb/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dager gratis", + "{years} years": "", "+1 (123) 456-7890": "+47 123 45 678", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Konto", "Account details updated successfully": "Kontodetaljer oppdatert", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Du har logget på.", "You've successfully subscribed to {siteTitle}": "Du har meldt deg på {siteTitle}", "Your account": "Din konto", + "Your email": "", "Your email has failed to resubscribe, please try again": "E-posten din kunne ikke bli meldt på igjen, vennligst prøv på nytt", "your inbox": "", "Your input helps shape what gets published.": "Din tilbakemelding bidrar til å påvirke hva som blir publisert.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Ditt abonnement vil avsluttes den {expiryDate}", "Your subscription will renew on {renewalDate}": "Ditt abonnement vil fornyes den {renewalDate}", diff --git a/ghost/i18n/locales/ne/portal.json b/ghost/i18n/locales/ne/portal.json index 1d24e92b296..b48e74f3847 100644 --- a/ghost/i18n/locales/ne/portal.json +++ b/ghost/i18n/locales/ne/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "", "You've successfully subscribed to {siteTitle}": "", "Your account": "", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "", "Your subscription will renew on {renewalDate}": "", diff --git a/ghost/i18n/locales/nl/portal.json b/ghost/i18n/locales/nl/portal.json index cc456243025..64c248bae7e 100644 --- a/ghost/i18n/locales/nl/portal.json +++ b/ghost/i18n/locales/nl/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dagen gratis", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Account", "Account details updated successfully": "Accountgegevens succesvol bijgewerkt", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Je bent succesvol ingelogd.", "You've successfully subscribed to {siteTitle}": "Je bent succesvol geabonneerd op {siteTitle}", "Your account": "Jouw account", + "Your email": "", "Your email has failed to resubscribe, please try again": "Je e-mail is niet opnieuw geabonneerd, probeer het opnieuw", "your inbox": "", "Your input helps shape what gets published.": "Jouw mening helpt bepalen wat er gepubliceerd wordt.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Je abonnement verloopt op {expiryDate}", "Your subscription will renew on {renewalDate}": "Je abonnement wordt verlengd op {renewalDate}", diff --git a/ghost/i18n/locales/nn/portal.json b/ghost/i18n/locales/nn/portal.json index 80e216f76d1..19cf54dd153 100644 --- a/ghost/i18n/locales/nn/portal.json +++ b/ghost/i18n/locales/nn/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dagar gratis", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Brukar", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Vellykka innlogging.", "You've successfully subscribed to {siteTitle}": "", "Your account": "Din brukar", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Dine tilbakemeldinger hjelper oss å forma tilbodet vårt.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Ditt abonnement går ut den {expiryDate}", "Your subscription will renew on {renewalDate}": "Ditt abonnement vil fornyast den {renewalDate}", diff --git a/ghost/i18n/locales/pa/portal.json b/ghost/i18n/locales/pa/portal.json index aa80da05986..6edabf3bd0c 100644 --- a/ghost/i18n/locales/pa/portal.json +++ b/ghost/i18n/locales/pa/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} ਦਿਨ ਮੁਫ਼ਤ", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "ਖਾਤਾ", "Account details updated successfully": "ਖਾਤੇ ਦੇ ਵੇਰਵੇ ਸਫਲਤਾਪੂਰਵਕ ਅੱਪਡੇਟ ਕੀਤੇ ਗਏ", @@ -244,9 +246,11 @@ "You've successfully signed in.": "ਤੁਸੀਂ ਸਫਲਤਾਪੂਰਵਕ ਸਾਈਨ ਇਨ ਕਰ ਲਿਆ ਹੈ।", "You've successfully subscribed to {siteTitle}": "ਤੁਸੀਂ ਸਫਲਤਾਪੂਰਵਕ ਸਦੱਸਤਾ ਲਈ ਹੈ: {siteTitle}", "Your account": "ਤੁਹਾਡਾ ਖਾਤਾ", + "Your email": "", "Your email has failed to resubscribe, please try again": "ਤੁਹਾਡੀ ਈਮੇਲ ਮੁੜ-ਸਦੱਸਤਾ ਲੈਣ ਵਿੱਚ ਅਸਫਲ ਰਹੀ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ", "your inbox": "", "Your input helps shape what gets published.": "ਤੁਹਾਡਾ ਇਨਪੁਟ ਇਹ ਆਕਾਰ ਦੇਣ ਵਿੱਚ ਮਦਦ ਕਰਦਾ ਹੈ ਕਿ ਕੀ ਪ੍ਰਕਾਸ਼ਿਤ ਹੁੰਦਾ ਹੈ।", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "ਤੁਹਾਡੀ ਸਦੱਸਤਾ {expiryDate} ਨੂੰ ਮਿਆਦ ਪੁੱਗ ਜਾਵੇਗੀ।", "Your subscription will renew on {renewalDate}": "ਤੁਹਾਡੀ ਸਦੱਸਤਾ {renewalDate} ਨੂੰ ਨਵਿਆਈ ਜਾਵੇਗੀ।", diff --git a/ghost/i18n/locales/pl/portal.json b/ghost/i18n/locales/pl/portal.json index 7dd55613715..1848f3d1f0f 100644 --- a/ghost/i18n/locales/pl/portal.json +++ b/ghost/i18n/locales/pl/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} darmowych dni", + "{years} years": "", "+1 (123) 456-7890": "+48 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Konto", "Account details updated successfully": "Szczegóły konta zostały pomyślnie zaktualizowane", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Logowanie powiodło się.", "You've successfully subscribed to {siteTitle}": "Pomyślnie zasubskrybowano {siteTitle}", "Your account": "Twoje konto", + "Your email": "", "Your email has failed to resubscribe, please try again": "Ponowna subskrypcja Twojego emaila nie powiodła się, spróbuj ponownie", "your inbox": "", "Your input helps shape what gets published.": "Twoja ocena pomoże nam lepiej kształtować nasze publikacje.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Subskrypcja wygaśnie w dniu {expiryDate}", "Your subscription will renew on {renewalDate}": "Subskrypcja zostanie odnowiona w dniu {renewalDate}", diff --git a/ghost/i18n/locales/pt-BR/portal.json b/ghost/i18n/locales/pt-BR/portal.json index 55d77e6f62e..8a16c18b340 100644 --- a/ghost/i18n/locales/pt-BR/portal.json +++ b/ghost/i18n/locales/pt-BR/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dias grátis", + "{years} years": "", "+1 (123) 456-7890": "+55 (00) 0 0000-0000", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Conta", "Account details updated successfully": "Detalhes da conta atualizados com sucesso", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Você entrou com sucesso.", "You've successfully subscribed to {siteTitle}": "Você se inscreveu com sucesso {siteTitle}", "Your account": "Sua conta", + "Your email": "", "Your email has failed to resubscribe, please try again": "Não foi possível reinscrever seu e-mail, por favor, tente novamente.", "your inbox": "sua caixa de entrada", "Your input helps shape what gets published.": "Sua resposta ajuda a definir o que será publicado.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Sua assinatura expirará em {expiryDate}", "Your subscription will renew on {renewalDate}": "Sua assinatura será renovada em {renewalDate}", diff --git a/ghost/i18n/locales/pt/portal.json b/ghost/i18n/locales/pt/portal.json index 003c6228e66..f61bfa1c233 100644 --- a/ghost/i18n/locales/pt/portal.json +++ b/ghost/i18n/locales/pt/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dias grátis", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Conta", "Account details updated successfully": "Detalhes da conta atualizados com sucesso", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Registou-se com sucesso.", "You've successfully subscribed to {siteTitle}": "Subscreveu com sucesso {siteTitle}", "Your account": "A sua conta", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "O seu feedback ajudará a decidir o conteúdo que será publicado no futuro.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "A sua assinatura expirará em {expiryDate}", "Your subscription will renew on {renewalDate}": "A sua assinatura será renovada em {renewalDate}", diff --git a/ghost/i18n/locales/ro/portal.json b/ghost/i18n/locales/ro/portal.json index 704886d04db..1970dcac8a6 100644 --- a/ghost/i18n/locales/ro/portal.json +++ b/ghost/i18n/locales/ro/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} zile gratuite", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Cont", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Te-ai autentificat cu succes.", "You've successfully subscribed to {siteTitle}": "Te-ai abonat cu succes la {siteTitle}", "Your account": "Contul tău", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Contribuția ta ajută la conturarea a ceea ce se publică.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Abonamentul tău va expira pe {expiryDate}", "Your subscription will renew on {renewalDate}": "Abonamentul tău se va reînnoi pe {renewalDate}", diff --git a/ghost/i18n/locales/ru/portal.json b/ghost/i18n/locales/ru/portal.json index 053badc0679..a10bdce2782 100644 --- a/ghost/i18n/locales/ru/portal.json +++ b/ghost/i18n/locales/ru/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} дня(ей) бесплатно", + "{years} years": "", "+1 (123) 456-7890": "+7 (987) 654-3210", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Аккаунт", "Account details updated successfully": "Данные учётной записи успешно обновлены", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Вы успешно вошли.", "You've successfully subscribed to {siteTitle}": "Вы успешно подписались на {siteTitle}", "Your account": "Ваш аккаунт", + "Your email": "", "Your email has failed to resubscribe, please try again": "Не удалось подписаться повторно используя ваш email, попробуйте ещё раз", "your inbox": "", "Your input helps shape what gets published.": "Ваш отзыв помогает формировать понимание, что вам интересно и что будет опубликовано в будущем.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Срок действия вашей подписки истекает {expiryDate}", "Your subscription will renew on {renewalDate}": "Ваша подписка будет продлена {renewalDate}", diff --git a/ghost/i18n/locales/si/portal.json b/ghost/i18n/locales/si/portal.json index 4e55ee51473..c0f69c8642f 100644 --- a/ghost/i18n/locales/si/portal.json +++ b/ghost/i18n/locales/si/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "දින {trialDays} ක් දක්වා නොමිලේ", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "ගිණුම", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "ඔබ සාර්ථකව sign in වන ලදී.", "You've successfully subscribed to {siteTitle}": "ඔබ සාර්ථකව subscribe ක\u200bර ඇත {siteTitle}", "Your account": "ඔබගේ ගිණුම", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "ඔබගේ අදහස් ඉදිරියේදී සිදු කරන පළකිරීම් වැඩිදියුණු කිරීමට උදව් කරනු ඇත.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "ඔබගේ subscription එක {expiryDate} වැනි දින කල් ඉකුත් වනු ඇත", "Your subscription will renew on {renewalDate}": "ඔබගේ subscription එක {expiryDate} වැනි දින renew වනු ඇත", diff --git a/ghost/i18n/locales/sk/portal.json b/ghost/i18n/locales/sk/portal.json index 1a060ea94d4..5ebf4df113e 100644 --- a/ghost/i18n/locales/sk/portal.json +++ b/ghost/i18n/locales/sk/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dní zdarma", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Účet", "Account details updated successfully": "Podrobnosti o účte boli úspešne aktualizované", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Úspešne ste sa prihlásili", "You've successfully subscribed to {siteTitle}": "Úspešne ste sa prihlásili na odber pre {siteTitle}", "Your account": "Váš účet", + "Your email": "", "Your email has failed to resubscribe, please try again": "Váš email zlyhal pri obnovení predplatného, prosím skúste to znova", "your inbox": "", "Your input helps shape what gets published.": "Vaše pripomienky pomáhajú spoluvytvárať obsah webu.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Váše predplatné expiruje {expiryDate}", "Your subscription will renew on {renewalDate}": "Váše predplatné bude obnovené {renewalDate}", diff --git a/ghost/i18n/locales/sl/portal.json b/ghost/i18n/locales/sl/portal.json index 248562cc708..cf160f10b71 100644 --- a/ghost/i18n/locales/sl/portal.json +++ b/ghost/i18n/locales/sl/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dni brezplačno", + "{years} years": "", "+1 (123) 456-7890": "+386 1 234 56 78", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Račun", "Account details updated successfully": "Podatki o računu so bili uspešno posodobljeni", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Uspešno ste se prijavili.", "You've successfully subscribed to {siteTitle}": "Uspešno ste se naročili na {siteTitle}", "Your account": "Vaš račun", + "Your email": "", "Your email has failed to resubscribe, please try again": "Vašega e-poštnega naslova nismo uspeli ponovno prijaviti, prosimo, poskusite znova", "your inbox": "", "Your input helps shape what gets published.": "Vaše mnenje nam pomaga pri izbiri objavljenih vsebin.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Vaša naročnina bo potekla dne {expiryDate}", "Your subscription will renew on {renewalDate}": "Vaša naročnina se bo obnovila dne {renewalDate}", diff --git a/ghost/i18n/locales/sq/portal.json b/ghost/i18n/locales/sq/portal.json index 01ca5168f15..ca1d6bdbfb8 100644 --- a/ghost/i18n/locales/sq/portal.json +++ b/ghost/i18n/locales/sq/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dite falas", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Llogaria", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Ju jeni identifikuar me sukses.", "You've successfully subscribed to {siteTitle}": "", "Your account": "Llogaria juar", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Të dhënat tuaja ndihmojnë në formimin e asaj që publikohet.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Abonimi juaj do te skadoje ne {expiryDate}", "Your subscription will renew on {renewalDate}": "Abonimi juaj to te rinovohet ne {renewalDate}", diff --git a/ghost/i18n/locales/sr-Cyrl/portal.json b/ghost/i18n/locales/sr-Cyrl/portal.json index a7f87fd7396..511df3bfe95 100644 --- a/ghost/i18n/locales/sr-Cyrl/portal.json +++ b/ghost/i18n/locales/sr-Cyrl/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} дана бесплатно", + "{years} years": "", "+1 (123) 456-7890": "+381 60 123-456", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Налог", "Account details updated successfully": "Детаљи налога су успешно ажурирани", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Успешно сте се пријавили.", "You've successfully subscribed to {siteTitle}": "Успешно сте се претплатили на {siteTitle}", "Your account": "Ваш налог", + "Your email": "", "Your email has failed to resubscribe, please try again": "Поновна претплата на Ваш email није успела, покушајте поново", "your inbox": "Ваш inbox", "Your input helps shape what gets published.": "Ваше учешће помаже у обликовању онога што ће бити објављено.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Ваша претплата истиче {expiryDate}", "Your subscription will renew on {renewalDate}": "Ваша претплата ће бити обновљена {renewalDate}", diff --git a/ghost/i18n/locales/sr/portal.json b/ghost/i18n/locales/sr/portal.json index c62d33f1bc2..63c06803f0c 100644 --- a/ghost/i18n/locales/sr/portal.json +++ b/ghost/i18n/locales/sr/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dana besplatno", + "{years} years": "", "+1 (123) 456-7890": "+381 60 123-456", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Nalog", "Account details updated successfully": "Detalji naloga su uspešno ažurirani", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Uspešno ste se prijavili.", "You've successfully subscribed to {siteTitle}": "Uspešno ste se pretplatili na {siteTitle}", "Your account": "Vaš nalog", + "Your email": "", "Your email has failed to resubscribe, please try again": "Ponovna pretplata na Vaš email nije uspela, pokušajte ponovo", "your inbox": "Vaš inbox", "Your input helps shape what gets published.": "Vaše učešće pomaže u oblikovanju onoga što će biti objavljeno.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Vaša pretplata ističe {expiryDate}", "Your subscription will renew on {renewalDate}": "Vaša pretplata će biti obnovljena {renewalDate}", diff --git a/ghost/i18n/locales/sv/portal.json b/ghost/i18n/locales/sv/portal.json index 5f370484cb8..0a1754d912d 100644 --- a/ghost/i18n/locales/sv/portal.json +++ b/ghost/i18n/locales/sv/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} dagar gratis", + "{years} years": "", "+1 (123) 456-7890": "+46 (0)78 901 23 45", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Konto", "Account details updated successfully": "Kontodetaljer uppdaterade framgångsrikt", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Du är nu inloggad.", "You've successfully subscribed to {siteTitle}": "Du är nu anmäld till {siteTitle}", "Your account": "Ditt konto", + "Your email": "", "Your email has failed to resubscribe, please try again": "Din e-postadress kunde inte återanmälas, vänligen försök igen", "your inbox": "din inkorg", "Your input helps shape what gets published.": "Din åsikt hjälper till att forma vad som publiceras.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Din prenumeration avslutas {expiryDate}", "Your subscription will renew on {renewalDate}": "Din prenumeration förnyas {renewalDate}", diff --git a/ghost/i18n/locales/sw/portal.json b/ghost/i18n/locales/sw/portal.json index 03e02a2edb3..ad425137bd4 100644 --- a/ghost/i18n/locales/sw/portal.json +++ b/ghost/i18n/locales/sw/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "siku {trialDays} bila malipo", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Akaunti", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Umeingia kwa mafanikio.", "You've successfully subscribed to {siteTitle}": "Umejiunga kwa mafanikio na {siteTitle}", "Your account": "Akaunti yako", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "Maoni yako yanasaidia kuunda yaliyochapishwa.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Usajili wako utaisha tarehe {expiryDate}", "Your subscription will renew on {renewalDate}": "Usajili wako utaongezwa tarehe {renewalDate}", diff --git a/ghost/i18n/locales/ta/portal.json b/ghost/i18n/locales/ta/portal.json index 9e33a0eaa9a..bc1031d6448 100644 --- a/ghost/i18n/locales/ta/portal.json +++ b/ghost/i18n/locales/ta/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} நாட்கள் இலவசம்", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "கணக்கு", "Account details updated successfully": "கணக்கு விவரங்கள் வெற்றிகரமாக புதுப்பிக்கப்பட்டன", @@ -244,9 +246,11 @@ "You've successfully signed in.": "நீங்கள் வெற்றிகரமாக உள்நுழைந்துள்ளீர்கள்.", "You've successfully subscribed to {siteTitle}": "நீங்கள் வெற்றிகரமாக சந்தா செய்துள்ளீர்கள் {siteTitle}", "Your account": "உங்கள் கணக்கு", + "Your email": "", "Your email has failed to resubscribe, please try again": "உங்கள் மின்னஞ்சல் மீண்டும் சந்தா செய்ய முடியவில்லை, தயவுசெய்து மீண்டும் முயற்சிக்கவும்", "your inbox": "", "Your input helps shape what gets published.": "உங்கள் உள்ளீடு வெளியிடப்படுவதை வடிவமைக்க உதவுகிறது.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "உங்கள் சந்தா {expiryDate} அன்று காலாவதியாகும்", "Your subscription will renew on {renewalDate}": "உங்கள் சந்தா {renewalDate} அன்று புதுப்பிக்கப்படும்", diff --git a/ghost/i18n/locales/th/portal.json b/ghost/i18n/locales/th/portal.json index 64e167aace7..8bffe422878 100644 --- a/ghost/i18n/locales/th/portal.json +++ b/ghost/i18n/locales/th/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "ฟรี {trialDays} วัน", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "บัญชี", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "คุณลงชื่อเข้าใช้สำเร็จแล้ว", "You've successfully subscribed to {siteTitle}": "คุณรับสมัครข้อมูลสำเร็จแล้ว {siteTitle}", "Your account": "บัญชีของคุณ", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "ข้อมูลของคุณ จะช่วยกำหนดสิ่งที่จะได้รับการเผยแพร่ในอนาคต", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "การรับสมัครข้อมูลของคุณจะหมดอายุในวันที่ {expiryDate}", "Your subscription will renew on {renewalDate}": "การรับสมัครข้อมูลของคุณจะต่ออายุในวันที่ {renewalDate}", diff --git a/ghost/i18n/locales/tr/portal.json b/ghost/i18n/locales/tr/portal.json index 69876ad81dc..33e36927764 100644 --- a/ghost/i18n/locales/tr/portal.json +++ b/ghost/i18n/locales/tr/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} gün ücretsiz", + "{years} years": "", "+1 (123) 456-7890": "+90 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Hesap", "Account details updated successfully": "Hesap bilgileri başarıyla güncellendi", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Başarıyla oturum açtınız.", "You've successfully subscribed to {siteTitle}": "Başarıyla abone oldunuz {siteTitle}", "Your account": "Hesabın", + "Your email": "", "Your email has failed to resubscribe, please try again": "E-postanız yeniden abone olmayı başaramadı, lütfen tekrar deneyin", "your inbox": "gelen kutunuz", "Your input helps shape what gets published.": "Yorumun yayımlanan içeriklerin şekillenmesine yardımcı olur.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Aboneliğiniz {expiryDate} tarihinde sona erecek", "Your subscription will renew on {renewalDate}": "Aboneliğiniz {renewalDate} tarihinde yenilenecek", diff --git a/ghost/i18n/locales/uk/portal.json b/ghost/i18n/locales/uk/portal.json index 6ea3f16f371..f8d723e8d74 100644 --- a/ghost/i18n/locales/uk/portal.json +++ b/ghost/i18n/locales/uk/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "Безплатно {trialDays} дні(-в)", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Oбліковий запис", "Account details updated successfully": "Дані облікового запису успішно оновлено", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Ви успішно увійшли.", "You've successfully subscribed to {siteTitle}": "Ви успішно підписалися на {siteTitle}", "Your account": "Ваш обліковий запис", + "Your email": "", "Your email has failed to resubscribe, please try again": "Не вдалося повторно підписатися за вашою електронну адресою, спробуйте ще раз", "your inbox": "", "Your input helps shape what gets published.": "Ваш відгук допомагає обирати що публікувати далі.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "Термін дії вашої підписки закінчується {expiryDate}", "Your subscription will renew on {renewalDate}": "Ваша підписка буде продовжена {renewalDate}", diff --git a/ghost/i18n/locales/ur/portal.json b/ghost/i18n/locales/ur/portal.json index e279fdd97b0..a9ef6e3c22c 100644 --- a/ghost/i18n/locales/ur/portal.json +++ b/ghost/i18n/locales/ur/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} دن مفت پڑھیں", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "اکاؤنٹ", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "آپ نے کامیابی سے سائن ان کیا ہے۔", "You've successfully subscribed to {siteTitle}": "", "Your account": "آپ کا اکاؤنٹ", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "آپ کا مدخلہ وہ چیزیں شکل دینے میں مدد فراہم کرتا ہے جو شائع ہوتی ہیں۔", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "آپ کی ہوشیاری {expiryDate} تک چلے گی", "Your subscription will renew on {renewalDate}": "آپ کی ہوشیاری {renewalDate} تک تجدید ہوگی", diff --git a/ghost/i18n/locales/uz/portal.json b/ghost/i18n/locales/uz/portal.json index 21a4bad653e..9e77c3dc902 100644 --- a/ghost/i18n/locales/uz/portal.json +++ b/ghost/i18n/locales/uz/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} kun bepul", + "{years} years": "", "+1 (123) 456-7890": "", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "Hisob", "Account details updated successfully": "", @@ -244,9 +246,11 @@ "You've successfully signed in.": "", "You've successfully subscribed to {siteTitle}": "", "Your account": "Sizning hisobingiz", + "Your email": "", "Your email has failed to resubscribe, please try again": "", "your inbox": "", "Your input helps shape what gets published.": "", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "", "Your subscription will expire on {expiryDate}": "", "Your subscription will renew on {renewalDate}": "", diff --git a/ghost/i18n/locales/vi/portal.json b/ghost/i18n/locales/vi/portal.json index 6656f0b27f9..93956b7564a 100644 --- a/ghost/i18n/locales/vi/portal.json +++ b/ghost/i18n/locales/vi/portal.json @@ -12,9 +12,11 @@ "{months} months": "{months} tháng", "{months} months free": "Miễn phí {months} tháng", "{trialDays} days free": "{trialDays} ngày đọc thử miễn phí", + "{years} years": "", "+1 (123) 456-7890": "+84 (987) 654-321", "1 month": "1 tháng", "1 month free": "Miễn phí 1 tháng", + "1 year": "", "Access your RSS feeds": "Truy cập các nguồn tin RSS của bạn", "Account": "Tài khoản", "Account details updated successfully": "Đã cập nhật chi tiết tài khoản thành công", @@ -244,9 +246,11 @@ "You've successfully signed in.": "Bạn đã đăng nhập thành công.", "You've successfully subscribed to {siteTitle}": "Bạn đã đăng ký gói thành công {siteTitle}", "Your account": "Tài khoản của bạn", + "Your email": "", "Your email has failed to resubscribe, please try again": "Không thể gia hạn với email của bạn, vui lòng thử lại", "your inbox": "hộp thư đến của bạn", "Your input helps shape what gets published.": "Thông tin bạn chọn giúp định hình nội dung được xuất bản.", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "Gói thành viên của bạn đã bị hủy và sẽ hết hạn vào {expiryDate}.", "Your subscription will expire on {expiryDate}": "Gói thành viên của bạn sẽ hết hạn vào {expiryDate}", "Your subscription will renew on {renewalDate}": "Gói thành viên của bạn sẽ tự động gia hạn vào {renewalDate}", diff --git a/ghost/i18n/locales/zh-Hant/portal.json b/ghost/i18n/locales/zh-Hant/portal.json index ea9aea043c7..f7f66847f5d 100644 --- a/ghost/i18n/locales/zh-Hant/portal.json +++ b/ghost/i18n/locales/zh-Hant/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} 天免費試用", + "{years} years": "", "+1 (123) 456-7890": "0912-123-123", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "帳號", "Account details updated successfully": "帳號資訊已更新成功", @@ -244,9 +246,11 @@ "You've successfully signed in.": "您已成功登入。", "You've successfully subscribed to {siteTitle}": "您已經成功訂閱 {siteTitle}", "Your account": "您的帳號", + "Your email": "", "Your email has failed to resubscribe, please try again": "您的電子郵件重新訂閱失敗,請再試一次", "your inbox": "您的收件匣", "Your input helps shape what gets published.": "您的建議將使我們變得更好。", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "您的訂閱已取消,將於 {expiryDate} 到期。", "Your subscription will expire on {expiryDate}": "您的訂閱將於 {expiryDate} 到期", "Your subscription will renew on {renewalDate}": "您的訂閱將於 {renewalDate} 自動續訂", diff --git a/ghost/i18n/locales/zh/portal.json b/ghost/i18n/locales/zh/portal.json index c63796632b8..c9948da1ba2 100644 --- a/ghost/i18n/locales/zh/portal.json +++ b/ghost/i18n/locales/zh/portal.json @@ -12,9 +12,11 @@ "{months} months": "", "{months} months free": "", "{trialDays} days free": "{trialDays} 天免费试用", + "{years} years": "", "+1 (123) 456-7890": "+1 (123) 456-7890", "1 month": "", "1 month free": "", + "1 year": "", "Access your RSS feeds": "", "Account": "账户", "Account details updated successfully": "账户信息更新成功", @@ -244,9 +246,11 @@ "You've successfully signed in.": "您已成功登录。", "You've successfully subscribed to {siteTitle}": "您已成功订阅 {siteTitle}", "Your account": "您的账户", + "Your email": "", "Your email has failed to resubscribe, please try again": "您的邮箱未能成功重新订阅,请重试。", "your inbox": "您的收件箱", "Your input helps shape what gets published.": "您的建议将使我们变得更好。", + "Your name": "", "Your subscription has been canceled and will expire on {expiryDate}.": "您的订阅已取消,将于 {expiryDate} 到期。", "Your subscription will expire on {expiryDate}": "您的订阅将在{expiryDate}到期", "Your subscription will renew on {renewalDate}": "您的订阅将在{renewalDate}续费",