Skip to content

Commit 5491491

Browse files
chore(clerk-js,types,localizations): Finish adding billing localizations (#5922)
1 parent 4282bfa commit 5491491

Some content is hidden

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

50 files changed

+2397
-88
lines changed

.changeset/ready-places-arrive.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/localizations': minor
3+
'@clerk/clerk-js': minor
4+
'@clerk/types': minor
5+
---
6+
7+
Replaces strings with localizations throughout billing components.

packages/clerk-js/bundlewatch.config.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"files": [
3-
{ "path": "./dist/clerk.js", "maxSize": "595.5kB" },
4-
{ "path": "./dist/clerk.browser.js", "maxSize": "68.4KB" },
3+
{ "path": "./dist/clerk.js", "maxSize": "595.7kB" },
4+
{ "path": "./dist/clerk.browser.js", "maxSize": "68.5KB" },
55
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "110KB" },
66
{ "path": "./dist/clerk.headless*.js", "maxSize": "52KB" },
7-
{ "path": "./dist/ui-common*.js", "maxSize": "104.7KB" },
7+
{ "path": "./dist/ui-common*.js", "maxSize": "105.1KB" },
88
{ "path": "./dist/vendors*.js", "maxSize": "39.5KB" },
99
{ "path": "./dist/coinbase*.js", "maxSize": "38KB" },
1010
{ "path": "./dist/createorganization*.js", "maxSize": "5KB" },

packages/clerk-js/src/ui/components/Checkout/Checkout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { __internal_CheckoutProps } from '@clerk/types';
22

33
import { CheckoutContext, SubscriberTypeContext } from '../../contexts';
4-
import { Flow } from '../../customizables';
4+
import { Flow, localizationKeys } from '../../customizables';
55
import { Drawer } from '../../elements';
66
import { CheckoutPage } from './CheckoutPage';
77

@@ -17,7 +17,7 @@ export const Checkout = (props: __internal_CheckoutProps) => {
1717
}}
1818
>
1919
<Drawer.Content>
20-
<Drawer.Header title='Checkout' />
20+
<Drawer.Header title={localizationKeys('commerce.checkout.title')} />
2121
<CheckoutPage {...props} />
2222
</Drawer.Content>
2323
</CheckoutContext.Provider>

packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ export const CheckoutForm = ({
5151
title={plan.name}
5252
description={planPeriod === 'annual' ? localizationKeys('commerce.billedAnnually') : undefined}
5353
/>
54-
{/* TODO(@Commerce): needs localization */}
5554
<LineItems.Description
5655
prefix={planPeriod === 'annual' ? 'x12' : undefined}
5756
text={`${plan.currencySymbol}${planPeriod === 'month' ? plan.amountFormatted : plan.annualMonthlyAmountFormatted}`}
@@ -62,28 +61,22 @@ export const CheckoutForm = ({
6261
borderTop
6362
variant='tertiary'
6463
>
65-
{/* TODO(@Commerce): needs localization */}
66-
<LineItems.Title title='Subtotal' />
64+
<LineItems.Title title={localizationKeys('commerce.subtotal')} />
6765
<LineItems.Description text={`${totals.subtotal.currencySymbol}${totals.subtotal.amountFormatted}`} />
6866
</LineItems.Group>
6967
{showCredits && (
7068
<LineItems.Group variant='tertiary'>
71-
{/* TODO(@Commerce): needs localization */}
72-
<LineItems.Title title={'Credit for the remainder of your current subscription.'} />
73-
{/* TODO(@Commerce): needs localization */}
74-
{/* TODO(@Commerce): Replace client-side calculation with server-side calculation once data are available in the response */}
69+
<LineItems.Title title={localizationKeys('commerce.creditRemainder')} />
7570
<LineItems.Description text={`- ${totals.credit?.currencySymbol}${totals.credit?.amountFormatted}`} />
7671
</LineItems.Group>
7772
)}
7873
<LineItems.Group borderTop>
79-
{/* TODO(@Commerce): needs localization */}
80-
<LineItems.Title title={`Total Due Today`} />
74+
<LineItems.Title title={localizationKeys('commerce.totalDueToday')} />
8175
<LineItems.Description text={`${totals.totalDueNow.currencySymbol}${totals.totalDueNow.amountFormatted}`} />
8276
</LineItems.Group>
8377
</LineItems.Root>
8478
</Box>
8579

86-
{/* TODO(@Commerce): needs localization */}
8780
{showDowngradeInfo && (
8881
<Box
8982
elementDescriptor={descriptors.checkoutFormLineItemsRoot}
@@ -201,13 +194,11 @@ const CheckoutFormElements = ({
201194
>
202195
<SegmentedControl.Button
203196
value='existing'
204-
// TODO(@Commerce): needs localization
205-
text='Payment Methods'
197+
text={localizationKeys('commerce.paymentMethods')}
206198
/>
207199
<SegmentedControl.Button
208200
value='new'
209-
// TODO(@Commerce): needs localization
210-
text='Add payment method'
201+
text={localizationKeys('commerce.addPaymentMethod')}
211202
/>
212203
</SegmentedControl.Root>
213204
)}
@@ -228,13 +219,12 @@ const CheckoutFormElements = ({
228219
checkout={checkout}
229220
onSuccess={onAddPaymentSourceSuccess}
230221
onPayWithTestPaymentSourceSuccess={onPayWithTestPaymentSourceSuccess}
231-
// @ts-ignore TODO(@COMMERCE): needs localization
232222
submitLabel={
233223
checkout.totals.totalDueNow.amount > 0
234224
? localizationKeys('userProfile.billingPage.paymentSourcesSection.formButtonPrimary__pay', {
235225
amount: `${checkout.totals.totalDueNow.currencySymbol}${checkout.totals.totalDueNow.amountFormatted}`,
236226
})
237-
: 'Subscribe'
227+
: localizationKeys('commerce.subscribe')
238228
}
239229
submitError={submitError}
240230
setSubmitError={setSubmitError}
@@ -351,15 +341,15 @@ const ExistingPaymentSourceForm = ({
351341
}}
352342
isLoading={isSubmitting}
353343
>
354-
{totalDueNow.amount > 0 ? (
355-
<>
356-
{/* TODO(@COMMERCE): needs localization */}
357-
Pay {totalDueNow.currencySymbol}
358-
{totalDueNow.amountFormatted}
359-
</>
360-
) : (
361-
'Subscribe'
362-
)}
344+
<Text
345+
localizationKey={
346+
totalDueNow.amount > 0
347+
? localizationKeys('commerce.pay', {
348+
amount: `${totalDueNow.currencySymbol}${totalDueNow.amountFormatted}`,
349+
})
350+
: localizationKeys('commerce.subscribe')
351+
}
352+
/>
363353
</Button>
364354
</Form>
365355
);

packages/clerk-js/src/ui/components/Checkout/CheckoutPage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { useCheckout } from './useCheckout';
2020

2121
export const CheckoutPage = (props: __internal_CheckoutProps) => {
2222
const { translateError } = useLocalizations();
23+
const { t } = useLocalizations();
2324
const { planId, planPeriod, subscriberType, onSubscriptionComplete } = props;
2425
const { setIsOpen, isOpen } = useDrawerContext();
2526
const prefersReducedMotion = usePrefersReducedMotion();
@@ -129,11 +130,10 @@ export const CheckoutPage = (props: __internal_CheckoutProps) => {
129130
</LineItems.Root>
130131
</Box>
131132
<Box sx={t => ({ padding: t.space.$4 })}>
132-
{/* TODO(@Commerce): needs localization */}
133133
<Alert
134134
variant='info'
135135
colorScheme='info'
136-
title={`You cannot subscribe to this plan by paying monthly. To subscribe to this plan, you need to choose to pay annually.`}
136+
title={localizationKeys('commerce.cannotSubscribeMonthly')}
137137
/>
138138
</Box>
139139
</Flex>
@@ -156,7 +156,7 @@ export const CheckoutPage = (props: __internal_CheckoutProps) => {
156156
variant='danger'
157157
colorScheme='danger'
158158
>
159-
{errors ? translateError(errors[0]) : 'There was a problem, please try again later.'}
159+
{errors ? translateError(errors[0]) : t(localizationKeys('unstable__errors.form_param_value_invalid'))}
160160
</Alert>
161161
</Flex>
162162
</Drawer.Body>

packages/clerk-js/src/ui/components/PaymentSources/AddPaymentSource.tsx

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,17 @@ import { useEffect, useRef, useState } from 'react';
77

88
import { clerkUnsupportedEnvironmentWarning } from '../../../core/errors';
99
import { useEnvironment, useSubscriberTypeContext } from '../../contexts';
10-
import { Box, Button, descriptors, Flex, localizationKeys, Spinner, Text, useAppearance } from '../../customizables';
10+
import {
11+
Box,
12+
Button,
13+
descriptors,
14+
Flex,
15+
localizationKeys,
16+
Spinner,
17+
Text,
18+
useAppearance,
19+
useLocalizations,
20+
} from '../../customizables';
1121
import { Alert, Form, FormButtons, FormContainer, LineItems, withCardStateProvider } from '../../elements';
1222
import { useFetch } from '../../hooks/useFetch';
1323
import type { LocalizationKey } from '../../localization';
@@ -171,6 +181,7 @@ const AddPaymentSourceForm = withCardStateProvider(
171181
const stripe = useStripe();
172182
const elements = useElements();
173183
const { displayConfig } = useEnvironment();
184+
const { t } = useLocalizations();
174185

175186
const subscriberType = useSubscriberTypeContext();
176187

@@ -264,7 +275,7 @@ const AddPaymentSourceForm = withCardStateProvider(
264275
variant='caption'
265276
colorScheme='body'
266277
>
267-
Test card information
278+
<Text localizationKey={localizationKeys('commerce.paymentSource.dev.testCardInfo')} />
268279
</Text>
269280
<Text
270281
variant='caption'
@@ -273,21 +284,21 @@ const AddPaymentSourceForm = withCardStateProvider(
273284
fontWeight: t.fontWeights.$semibold,
274285
})}
275286
>
276-
Development mode
287+
<Text localizationKey={localizationKeys('commerce.paymentSource.dev.developmentMode')} />
277288
</Text>
278289
</Box>
279290
<LineItems.Root>
280291
<LineItems.Group variant='tertiary'>
281-
<LineItems.Title title={'Card number'} />
292+
<LineItems.Title title={localizationKeys('commerce.paymentSource.dev.cardNumber')} />
282293
<LineItems.Description text={'4242 4242 4242 4242'} />
283294
</LineItems.Group>
284295
<LineItems.Group variant='tertiary'>
285-
<LineItems.Title title={'Expiration date'} />
296+
<LineItems.Title title={localizationKeys('commerce.paymentSource.dev.expirationDate')} />
286297
<LineItems.Description text={'11/44'} />
287298
</LineItems.Group>
288299
<LineItems.Group variant='tertiary'>
289-
<LineItems.Title title={'CVC, ZIP'} />
290-
<LineItems.Description text={'Any numbers'} />
300+
<LineItems.Title title={localizationKeys('commerce.paymentSource.dev.cvcZip')} />
301+
<LineItems.Description text={t(localizationKeys('commerce.paymentSource.dev.anyNumbers'))} />
291302
</LineItems.Group>
292303
</LineItems.Root>
293304
</Box>
@@ -303,7 +314,7 @@ const AddPaymentSourceForm = withCardStateProvider(
303314
applePay: checkout
304315
? {
305316
recurringPaymentRequest: {
306-
paymentDescription: `${checkout.planPeriod === 'month' ? 'Monthly' : 'Annual'} payment`,
317+
paymentDescription: `${t(localizationKeys(checkout.planPeriod === 'month' ? 'commerce.paymentSource.applePayDescription.monthly' : 'commerce.paymentSource.applePayDescription.annual'))}`,
307318
managementURL: displayConfig.homeUrl, // TODO(@COMMERCE): is this the right URL?
308319
regularBilling: {
309320
amount: checkout.totals.totalDueNow?.amount || checkout.totals.grandTotal.amount,
@@ -393,7 +404,7 @@ const PayWithTestPaymentSource = withCardStateProvider(
393404
fontWeight: t.fontWeights.$semibold,
394405
})}
395406
>
396-
Development mode
407+
<Text localizationKey={localizationKeys('commerce.paymentSource.dev.developmentMode')} />
397408
</Text>
398409
<Button
399410
type='button'

packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { useProtect } from '../../common';
1414
import { PlansContextProvider, SubscriberTypeContext, usePlansContext, useSubscriberTypeContext } from '../../contexts';
1515
import { Badge, Box, Button, Col, descriptors, Flex, Heading, localizationKeys, Span, Text } from '../../customizables';
1616
import { Alert, Avatar, Drawer, Switch, useDrawerContext } from '../../elements';
17-
import { formatDate, handleError } from '../../utils';
17+
import { handleError } from '../../utils';
1818

1919
export const PlanDetails = (props: __internal_PlanDetailsProps) => {
2020
return (
@@ -295,27 +295,23 @@ const PlanDetailsInternal = ({
295295
elementDescriptor={descriptors.drawerConfirmationTitle}
296296
as='h2'
297297
textVariant='h3'
298-
>
299-
{/* TODO(@COMMERCE): needs localization */}
300-
Cancel {subscription.status === 'upcoming' ? 'upcoming ' : ''}
301-
{subscription.plan.name} Subscription?
302-
</Heading>
298+
localizationKey={localizationKeys('commerce.cancelSubscriptionTitle', {
299+
plan: `${subscription.status === 'upcoming' ? 'upcoming ' : ''}${subscription.plan.name}`,
300+
})}
301+
/>
303302
<Text
304303
elementDescriptor={descriptors.drawerConfirmationDescription}
305304
colorScheme='secondary'
306-
>
307-
{/* TODO(@COMMERCE): needs localization */}
308-
{subscription.status === 'upcoming' ? (
309-
<>You will not be charged for this subscription.</>
310-
) : (
311-
<>
312-
You can keep using &ldquo;{subscription.plan.name}&rdquo; features until{' '}
313-
{formatDate(new Date(subscription.periodEnd))}, after which you will no longer have access.
314-
</>
315-
)}
316-
</Text>
305+
localizationKey={
306+
subscription.status === 'upcoming'
307+
? localizationKeys('commerce.cancelSubscriptionNoCharge')
308+
: localizationKeys('commerce.cancelSubscriptionAccessUntil', {
309+
plan: subscription.plan.name,
310+
date: subscription.periodEnd,
311+
})
312+
}
313+
/>
317314
{cancelError && (
318-
// TODO(@COMMERCE): needs localization
319315
<Alert colorScheme='danger'>{typeof cancelError === 'string' ? cancelError : cancelError.message}</Alert>
320316
)}
321317
</Drawer.Confirmation>

packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
Span,
1515
Text,
1616
useAppearance,
17+
useLocalizations,
1718
} from '../../customizables';
1819
import { Avatar, SegmentedControl } from '../../elements';
1920
import { usePrefersReducedMotion } from '../../hooks';
@@ -43,6 +44,7 @@ export function PricingTableMatrix({
4344
const segmentedControlId = `${pricingTableMatrixId}-segmented-control`;
4445

4546
const { buttonPropsForPlan } = usePlansContext();
47+
const { t } = useLocalizations();
4648

4749
const feePeriodNoticeAnimation: ThemableCssProp = t => ({
4850
transition: isMotionSafe
@@ -129,6 +131,7 @@ export function PricingTableMatrix({
129131
id={segmentedControlId}
130132
colorScheme='secondary'
131133
variant='caption'
134+
localizationKey={localizationKeys('commerce.pricingTable.billingCycle')}
132135
>
133136
Billing cycle
134137
</Text>
@@ -139,13 +142,11 @@ export function PricingTableMatrix({
139142
>
140143
<SegmentedControl.Button
141144
value='month'
142-
// TODO(@Commerce): needs localization
143-
text='Monthly'
145+
text={localizationKeys('commerce.monthly')}
144146
/>
145147
<SegmentedControl.Button
146148
value='annual'
147-
// TODO(@Commerce): needs localization
148-
text='Annually'
149+
text={localizationKeys('commerce.annually')}
149150
/>
150151
</SegmentedControl.Root>
151152
</>
@@ -204,14 +205,12 @@ export function PricingTableMatrix({
204205
imageUrl={plan.avatarUrl}
205206
/>
206207
) : null}
207-
{/* TODO(@Commerce): needs localization */}
208208
{highlight ? (
209209
<Badge
210210
elementDescriptor={descriptors.pricingTableMatrixBadge}
211211
colorScheme='secondary'
212-
>
213-
Popular
214-
</Badge>
212+
localizationKey={localizationKeys('commerce.popular')}
213+
/>
215214
) : null}
216215
</Span>
217216
) : null}
@@ -397,7 +396,7 @@ export function PricingTableMatrix({
397396
icon={Check}
398397
colorScheme='neutral'
399398
size='sm'
400-
aria-label='Included'
399+
aria-label={t(localizationKeys('commerce.pricingTable.included'))}
401400
/>
402401
)}
403402
</Box>

packages/clerk-js/src/ui/localization/localizationModifiers.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,22 @@ const shortDate = (val: Date | string | number, locale?: string) => {
3737
}
3838
};
3939

40+
/**
41+
* Returns a long date string with the year.
42+
*/
43+
const longDate = (val: Date | string | number, locale?: string) => {
44+
try {
45+
return new Intl.DateTimeFormat(locale || 'en-US', {
46+
month: 'long',
47+
day: 'numeric',
48+
year: 'numeric',
49+
}).format(normalizeDate(val));
50+
} catch (e) {
51+
console.warn(e);
52+
return '';
53+
}
54+
};
55+
4056
const numeric = (val: Date | number | string, locale?: string) => {
4157
try {
4258
return new Intl.DateTimeFormat(locale || 'en-US').format(normalizeDate(val));
@@ -57,4 +73,5 @@ export const MODIFIERS = {
5773
numeric,
5874
link,
5975
shortDate,
76+
longDate,
6077
} as const;

0 commit comments

Comments
 (0)