diff --git a/.changeset/cold-turtles-press.md b/.changeset/cold-turtles-press.md new file mode 100644 index 00000000000..707ed6f7dfc --- /dev/null +++ b/.changeset/cold-turtles-press.md @@ -0,0 +1,5 @@ +--- +'@clerk/types': minor +--- + +Expose `__internal_LocalizationResource` which now includes metadata for which keys require interpolation. diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index 97bc5789f7d..bd7805ffc44 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -4,7 +4,7 @@ { "path": "./dist/clerk.browser.js", "maxSize": "70.16KB" }, { "path": "./dist/clerk.legacy.browser.js", "maxSize": "113KB" }, { "path": "./dist/clerk.headless*.js", "maxSize": "53.06KB" }, - { "path": "./dist/ui-common*.js", "maxSize": "108.36KB" }, + { "path": "./dist/ui-common*.js", "maxSize": "108.4KB" }, { "path": "./dist/vendors*.js", "maxSize": "40.2KB" }, { "path": "./dist/coinbase*.js", "maxSize": "38KB" }, { "path": "./dist/createorganization*.js", "maxSize": "5KB" }, diff --git a/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx b/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx index 56c0c2697b0..3cb67b93d49 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx @@ -15,7 +15,7 @@ type RevokeAPIKeyConfirmationModalProps = { onOpen: () => void; onClose: () => void; apiKeyId?: string; - apiKeyName?: string; + apiKeyName: string; modalRoot?: React.MutableRefObject; }; diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/ActionConfirmationPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/ActionConfirmationPage.tsx index e2961cc1c2a..cd6b22b00d9 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/ActionConfirmationPage.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/ActionConfirmationPage.tsx @@ -49,13 +49,13 @@ export const LeaveOrganizationForm = (props: LeaveOrganizationFormProps) => { return ( { return ( { diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainForm.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainForm.tsx index c74cb125586..8e595defaab 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainForm.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainForm.tsx @@ -129,10 +129,10 @@ export const VerifiedDomainForm = withCardStateProvider((props: VerifiedDomainFo }, [domain?.id]); const title = localizationKeys('organizationProfile.verifiedDomainPage.title', { - domain: domain?.name, + domain: domain?.name || '', }); const subtitle = localizationKeys('organizationProfile.verifiedDomainPage.subtitle', { - domain: domain?.name, + domain: domain?.name || '', }); const calloutLabel = useCalloutLabel(domain, { diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifyDomainForm.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifyDomainForm.tsx index 2d1671f9879..878d076801f 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifyDomainForm.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifyDomainForm.tsx @@ -50,7 +50,7 @@ export const VerifyDomainForm = withCardStateProvider((props: VerifyDomainFormPr const subtitleVerificationCodeScreen = localizationKeys( 'organizationProfile.verifyDomainPage.subtitleVerificationCodeScreen', { - emailAddress: affiliationEmailAddressRef.current, + emailAddress: affiliationEmailAddressRef.current || '', }, ); diff --git a/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx b/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx index 90439286110..fc47b2de578 100644 --- a/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx +++ b/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx @@ -247,11 +247,13 @@ function Card(props: CardProps) { order: ctaPosition === 'top' ? -1 : undefined, })} > - {shouldShowFooterNotice ? ( + {shouldShowFooterNotice && subscription ? ( ({ paddingBlock: t.space.$1x5, diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInFactorOneAlternativePhoneCodeCard.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInFactorOneAlternativePhoneCodeCard.tsx index 39135663a27..77953489ea2 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInFactorOneAlternativePhoneCodeCard.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInFactorOneAlternativePhoneCodeCard.tsx @@ -15,7 +15,7 @@ export const SignInFactorOneAlternativePhoneCodeCard = (props: SignInFactorOneAl { if (isAlternativePhoneCodeProvider) { const provider = getAlternativePhoneCodeProviderData(channel)?.name; - cardTitleKey = localizationKeys('signUp.alternativePhoneCodeProvider.title', { provider }); - cardSubtitleKey = localizationKeys('signUp.alternativePhoneCodeProvider.subtitle', { provider }); + cardTitleKey = localizationKeys('signUp.alternativePhoneCodeProvider.title', { provider: provider || '' }); + cardSubtitleKey = localizationKeys('signUp.alternativePhoneCodeProvider.subtitle', { + provider: provider || '', + }); resendButtonKey = localizationKeys('signUp.alternativePhoneCodeProvider.resendButton'); } diff --git a/packages/clerk-js/src/ui/components/SignUp/SignUpStartAlternativePhoneCodePhoneNumberCard.tsx b/packages/clerk-js/src/ui/components/SignUp/SignUpStartAlternativePhoneCodePhoneNumberCard.tsx index 1ba19fba423..40b4461a65d 100644 --- a/packages/clerk-js/src/ui/components/SignUp/SignUpStartAlternativePhoneCodePhoneNumberCard.tsx +++ b/packages/clerk-js/src/ui/components/SignUp/SignUpStartAlternativePhoneCodePhoneNumberCard.tsx @@ -75,7 +75,9 @@ export const SignUpStartAlternativePhoneCodePhoneNumberCard = (props: SignUpForm { {strategy === 'email_link' && ( diff --git a/packages/clerk-js/src/ui/components/UserProfile/MfaPhoneCodeScreen.tsx b/packages/clerk-js/src/ui/components/UserProfile/MfaPhoneCodeScreen.tsx index cc79d82b36d..32703649792 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/MfaPhoneCodeScreen.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/MfaPhoneCodeScreen.tsx @@ -151,7 +151,7 @@ export const MFAVerifyPhone = (props: MFAVerifyPhoneProps) => { { Promise.resolve(resource?.destroy())} onSuccess={onSuccess} @@ -212,7 +212,7 @@ export const RemovePasskeyForm = (props: RemovePasskeyFormProps) => { { diff --git a/packages/clerk-js/src/ui/elements/LegalConsentCheckbox.tsx b/packages/clerk-js/src/ui/elements/LegalConsentCheckbox.tsx index d26afd9f2f5..f4ae0de0597 100644 --- a/packages/clerk-js/src/ui/elements/LegalConsentCheckbox.tsx +++ b/packages/clerk-js/src/ui/elements/LegalConsentCheckbox.tsx @@ -23,16 +23,16 @@ const LegalCheckboxLabel = (props: { termsUrl?: string; privacyPolicyUrl?: strin if (termsUrl && privacyPolicyUrl) { localizationKey = localizationKeys('signUp.legalConsent.checkbox.label__termsOfServiceAndPrivacyPolicy', { - termsOfServiceLink: props.termsUrl, - privacyPolicyLink: props.privacyPolicyUrl, + termsOfServiceLink: termsUrl, + privacyPolicyLink: privacyPolicyUrl, }); } else if (termsUrl) { localizationKey = localizationKeys('signUp.legalConsent.checkbox.label__onlyTermsOfService', { - termsOfServiceLink: props.termsUrl, + termsOfServiceLink: termsUrl, }); } else if (privacyPolicyUrl) { localizationKey = localizationKeys('signUp.legalConsent.checkbox.label__onlyPrivacyPolicy', { - privacyPolicyLink: props.privacyPolicyUrl, + privacyPolicyLink: privacyPolicyUrl, }); } diff --git a/packages/clerk-js/src/ui/localization/localizationKeys.ts b/packages/clerk-js/src/ui/localization/localizationKeys.ts index 1fc8fd1d32f..934c97ed583 100644 --- a/packages/clerk-js/src/ui/localization/localizationKeys.ts +++ b/packages/clerk-js/src/ui/localization/localizationKeys.ts @@ -1,6 +1,4 @@ -import type { PathValue, RecordToPath } from '@clerk/types'; - -import type { defaultResource } from './defaultEnglishResource'; +import type { __internal_LocalizationResource, PathValue, RecordToPath } from '@clerk/types'; type Value = string | number | boolean | Date; type Whitespace = ' ' | '\t' | '\n' | '\r'; @@ -61,20 +59,19 @@ export type GetICUArgs> = T T extends readonly string[] ? TupleFindBlocks : FindBlocks >; -type DefaultLocalizationKey = RecordToPath; -type LocalizationKeyToValue

= PathValue; - -// @ts-ignore -type LocalizationKeyToParams

= GetICUArgs>; +type DefaultLocalizationKey = RecordToPath<__internal_LocalizationResource>; +type LocalizationKeyToValue

= PathValue<__internal_LocalizationResource, P>; export type LocalizationKey = { key: string; params: Record | undefined; }; -export const localizationKeys = >( +type ExtractArgsFromValue = Value extends { __params: any } ? [params: Value['__params']] : []; + +export const localizationKeys = >( key: Key, - params?: keyof Params extends never ? never : Params, + ...args: ExtractArgsFromValue ): LocalizationKey => { - return { key, params } as LocalizationKey; + return { key, params: args[0] } as LocalizationKey; }; diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts index 914aad282e2..37bbcb81e34 100644 --- a/packages/types/src/localization.ts +++ b/packages/types/src/localization.ts @@ -1,7 +1,56 @@ import type { FieldId } from './elementIds'; import type { CamelToSnake, DeepPartial } from './utils'; -export type LocalizationValue = string; +/** + * @internal + * @example + * type PageTitle = LocalizationValue<'name', 'greeting'>; + * // ?^ + * { + * name: string | number | boolean | Date; + * greeting: string | number | boolean | Date; + * } + */ +type UnionToRecordWithPrimitives = { + [K in T]: string | number | boolean | Date; +}; + +export type LocalizationValue = [T] extends [never] + ? Constraint + : Constraint & { __params: UnionToRecordWithPrimitives }; + +/** + * Recursively transforms a type by replacing all LocalizationValue types with their string representation. + * This is useful for creating type-safe localization objects where you want to ensure all values are strings. + * + * @example + * ```typescript + * type MyLocalization = { + * a: LocalizationValue; // becomes string + * b: LocalizationValue<'one'>; // becomes string + * c: { + * lala: LocalizationValue<'two' | 'three'>; // becomes string + * }; + * }; + * + * type StringifiedLocalization = DeepLocalizationWithoutObjects; + * // Result: + * // { + * // a: string; + * // b: string; + * // c: { + * // lala: string; + * // }; + * // } + * ``` + */ +type DeepLocalizationWithoutObjects = { + [K in keyof T]: T[K] extends LocalizationValue + ? T[K] + : T[K] extends object + ? DeepLocalizationWithoutObjects + : T[K]; +}; /** * A type containing all the possible localization keys the prebuilt Clerk components support. @@ -11,9 +60,9 @@ export type LocalizationValue = string; * the default english resource object from {@link https://github.com/clerk/javascript Clerk's open source repo} * as a starting point. */ -export type LocalizationResource = DeepPartial<_LocalizationResource>; +export type LocalizationResource = DeepPartial>; -type _LocalizationResource = { +export type __internal_LocalizationResource = { locale: string; maintenanceMode: LocalizationValue; /** @@ -23,13 +72,13 @@ type _LocalizationResource = { roles: { [r: string]: LocalizationValue; }; - socialButtonsBlockButton: LocalizationValue; + socialButtonsBlockButton: LocalizationValue<'provider'>; /** * It should be used to provide a shorter variation of `socialButtonsBlockButton`. * It is explicitly typed, in order to avoid contributions that use LLM tools to generate * translations that misinterpret the correct usage of this property. */ - socialButtonsBlockButtonManyInView: `${string}{{provider|titleize}}${string}`; + socialButtonsBlockButtonManyInView: LocalizationValue<'provider', `${string}{{provider|titleize}}${string}`>; dividerText: LocalizationValue; formFieldLabel__emailAddress: LocalizationValue; formFieldLabel__emailAddresses: LocalizationValue; @@ -98,11 +147,11 @@ type _LocalizationResource = { badge__currentPlan: LocalizationValue; badge__upcomingPlan: LocalizationValue; badge__activePlan: LocalizationValue; - badge__startsAt: LocalizationValue; + badge__startsAt: LocalizationValue<'date'>; badge__endsAt: LocalizationValue; badge__expired: LocalizationValue; - badge__canceledEndsAt: LocalizationValue; - badge__renewsAt: LocalizationValue; + badge__canceledEndsAt: LocalizationValue<'date'>; + badge__renewsAt: LocalizationValue<'date'>; footerPageLink__help: LocalizationValue; footerPageLink__privacy: LocalizationValue; footerPageLink__terms: LocalizationValue; @@ -143,10 +192,10 @@ type _LocalizationResource = { pastDue: LocalizationValue; paymentMethods: LocalizationValue; addPaymentMethod: LocalizationValue; - pay: LocalizationValue; - cancelSubscriptionTitle: LocalizationValue; + pay: LocalizationValue<'amount'>; + cancelSubscriptionTitle: LocalizationValue<'plan'>; cancelSubscriptionNoCharge: LocalizationValue; - cancelSubscriptionAccessUntil: LocalizationValue; + cancelSubscriptionAccessUntil: LocalizationValue<'plan' | 'date'>; popular: LocalizationValue; monthly: LocalizationValue; annually: LocalizationValue; @@ -202,9 +251,9 @@ type _LocalizationResource = { actionLink__use_email: LocalizationValue; alternativePhoneCodeProvider: { actionLink: LocalizationValue; - label: LocalizationValue; - subtitle: LocalizationValue; - title: LocalizationValue; + label: LocalizationValue<'provider'>; + subtitle: LocalizationValue<'provider'>; + title: LocalizationValue<'provider'>; }; }; emailLink: { @@ -247,8 +296,8 @@ type _LocalizationResource = { formSubtitle: LocalizationValue; formTitle: LocalizationValue; resendButton: LocalizationValue; - subtitle: LocalizationValue; - title: LocalizationValue; + subtitle: LocalizationValue<'provider'>; + title: LocalizationValue<'provider'>; }; continue: { title: LocalizationValue; @@ -271,9 +320,9 @@ type _LocalizationResource = { subtitle: LocalizationValue; }; checkbox: { - label__termsOfServiceAndPrivacyPolicy: LocalizationValue; - label__onlyPrivacyPolicy: LocalizationValue; - label__onlyTermsOfService: LocalizationValue; + label__termsOfServiceAndPrivacyPolicy: LocalizationValue<'termsOfServiceLink' | 'privacyPolicyLink'>; + label__onlyPrivacyPolicy: LocalizationValue<'privacyPolicyLink'>; + label__onlyTermsOfService: LocalizationValue<'termsOfServiceLink'>; }; }; }; @@ -294,9 +343,9 @@ type _LocalizationResource = { actionLink__join_waitlist: LocalizationValue; alternativePhoneCodeProvider: { actionLink: LocalizationValue; - label: LocalizationValue; - subtitle: LocalizationValue; - title: LocalizationValue; + label: LocalizationValue<'provider'>; + subtitle: LocalizationValue<'provider'>; + title: LocalizationValue<'provider'>; }; }; password: { @@ -384,7 +433,7 @@ type _LocalizationResource = { formTitle: LocalizationValue; resendButton: LocalizationValue; subtitle: LocalizationValue; - title: LocalizationValue; + title: LocalizationValue<'provider'>; }; phoneCodeMfa: { title: LocalizationValue; @@ -406,9 +455,9 @@ type _LocalizationResource = { subtitle: LocalizationValue; actionLink: LocalizationValue; actionText: LocalizationValue; - blockButton__emailLink: LocalizationValue; - blockButton__emailCode: LocalizationValue; - blockButton__phoneCode: LocalizationValue; + blockButton__emailLink: LocalizationValue<'identifier'>; + blockButton__emailCode: LocalizationValue<'identifier'>; + blockButton__phoneCode: LocalizationValue<'identifier'>; blockButton__password: LocalizationValue; blockButton__passkey: LocalizationValue; blockButton__totp: LocalizationValue; @@ -474,8 +523,8 @@ type _LocalizationResource = { subtitle: LocalizationValue; actionLink: LocalizationValue; actionText: LocalizationValue; - blockButton__emailCode: LocalizationValue; - blockButton__phoneCode: LocalizationValue; + blockButton__emailCode: LocalizationValue<'identifier'>; + blockButton__phoneCode: LocalizationValue<'identifier'>; blockButton__password: LocalizationValue; blockButton__totp: LocalizationValue; blockButton__passkey: LocalizationValue; @@ -622,7 +671,7 @@ type _LocalizationResource = { */ formHint: LocalizationValue; formTitle: LocalizationValue; - formSubtitle: LocalizationValue; + formSubtitle: LocalizationValue<'identifier'>; resendButton: LocalizationValue; successMessage: LocalizationValue; }; @@ -632,19 +681,19 @@ type _LocalizationResource = { */ formHint: LocalizationValue; formTitle: LocalizationValue; - formSubtitle: LocalizationValue; + formSubtitle: LocalizationValue<'identifier'>; resendButton: LocalizationValue; successMessage: LocalizationValue; }; enterpriseSSOLink: { - formSubtitle: LocalizationValue; + formSubtitle: LocalizationValue<'identifier'>; formButton: LocalizationValue; }; removeResource: { title: LocalizationValue; - messageLine1: LocalizationValue; + messageLine1: LocalizationValue<'identifier'>; messageLine2: LocalizationValue; - successMessage: LocalizationValue; + successMessage: LocalizationValue<'emailAddress'>; }; }; apiKeysPage: { @@ -656,46 +705,46 @@ type _LocalizationResource = { subtitle__rename: LocalizationValue; removeResource: { title: LocalizationValue; - messageLine1: LocalizationValue; + messageLine1: LocalizationValue<'name'>; }; }; phoneNumberPage: { title: LocalizationValue; verifyTitle: LocalizationValue; - verifySubtitle: LocalizationValue; + verifySubtitle: LocalizationValue<'identifier'>; successMessage: LocalizationValue; infoText: LocalizationValue; removeResource: { title: LocalizationValue; - messageLine1: LocalizationValue; + messageLine1: LocalizationValue<'identifier'>; messageLine2: LocalizationValue; - successMessage: LocalizationValue; + successMessage: LocalizationValue<'phoneNumber'>; }; }; connectedAccountPage: { title: LocalizationValue; formHint: LocalizationValue; formHint__noAccounts: LocalizationValue; - socialButtonsBlockButton: LocalizationValue; + socialButtonsBlockButton: LocalizationValue<'provider'>; successMessage: LocalizationValue; removeResource: { title: LocalizationValue; - messageLine1: LocalizationValue; + messageLine1: LocalizationValue<'identifier'>; messageLine2: LocalizationValue; - successMessage: LocalizationValue; + successMessage: LocalizationValue<'connectedAccount'>; }; }; web3WalletPage: { title: LocalizationValue; subtitle__availableWallets: LocalizationValue; subtitle__unavailableWallets: LocalizationValue; - web3WalletButtonsBlockButton: LocalizationValue; - successMessage: LocalizationValue; + web3WalletButtonsBlockButton: LocalizationValue<'provider'>; + successMessage: LocalizationValue<'web3Wallet'>; removeResource: { title: LocalizationValue; - messageLine1: LocalizationValue; + messageLine1: LocalizationValue<'identifier'>; messageLine2: LocalizationValue; - successMessage: LocalizationValue; + successMessage: LocalizationValue<'web3Wallet'>; }; }; passwordPage: { @@ -742,9 +791,9 @@ type _LocalizationResource = { successMessage2: LocalizationValue; removeResource: { title: LocalizationValue; - messageLine1: LocalizationValue; + messageLine1: LocalizationValue<'identifier'>; messageLine2: LocalizationValue; - successMessage: LocalizationValue; + successMessage: LocalizationValue<'mfaPhoneCode'>; }; }; backupCodePage: { @@ -815,9 +864,9 @@ type _LocalizationResource = { formButtonPrimary__pay: LocalizationValue; removeResource: { title: LocalizationValue; - messageLine1: LocalizationValue; + messageLine1: LocalizationValue<'identifier'>; messageLine2: LocalizationValue; - successMessage: LocalizationValue; + successMessage: LocalizationValue<'paymentSource'>; }; payWithTestCardButton: LocalizationValue; }; @@ -848,7 +897,7 @@ type _LocalizationResource = { suggestionsAcceptedLabel: LocalizationValue; }; impersonationFab: { - title: LocalizationValue; + title: LocalizationValue<'identifier'>; action__signOut: LocalizationValue; }; organizationProfile: { @@ -883,13 +932,13 @@ type _LocalizationResource = { messageLine1: LocalizationValue; messageLine2: LocalizationValue; successMessage: LocalizationValue; - actionDescription: LocalizationValue; + actionDescription: LocalizationValue<'organizationName'>; }; deleteOrganization: { title: LocalizationValue; messageLine1: LocalizationValue; messageLine2: LocalizationValue; - actionDescription: LocalizationValue; + actionDescription: LocalizationValue<'organizationName'>; successMessage: LocalizationValue; }; }; @@ -908,15 +957,15 @@ type _LocalizationResource = { }; verifyDomainPage: { title: LocalizationValue; - subtitle: LocalizationValue; - subtitleVerificationCodeScreen: LocalizationValue; + subtitle: LocalizationValue<'domainName'>; + subtitleVerificationCodeScreen: LocalizationValue<'emailAddress'>; formTitle: LocalizationValue; formSubtitle: LocalizationValue; resendButton: LocalizationValue; }; verifiedDomainPage: { - title: LocalizationValue; - subtitle: LocalizationValue; + title: LocalizationValue<'domain'>; + subtitle: LocalizationValue<'domain'>; start: { headerTitle__enrollment: LocalizationValue; headerTitle__danger: LocalizationValue; @@ -930,8 +979,8 @@ type _LocalizationResource = { automaticSuggestionOption__label: LocalizationValue; automaticSuggestionOption__description: LocalizationValue; calloutInfoLabel: LocalizationValue; - calloutInvitationCountLabel: LocalizationValue; - calloutSuggestionCountLabel: LocalizationValue; + calloutInvitationCountLabel: LocalizationValue<'count'>; + calloutSuggestionCountLabel: LocalizationValue<'count'>; }; dangerTab: { removeDomainTitle: LocalizationValue; @@ -944,13 +993,13 @@ type _LocalizationResource = { title: LocalizationValue; subtitle: LocalizationValue; successMessage: LocalizationValue; - detailsTitle__inviteFailed: LocalizationValue; + detailsTitle__inviteFailed: LocalizationValue<'email_addresses'>; formButtonPrimary__continue: LocalizationValue; selectDropdown__role: LocalizationValue; }; removeDomainPage: { title: LocalizationValue; - messageLine1: LocalizationValue; + messageLine1: LocalizationValue<'domain'>; messageLine2: LocalizationValue; successMessage: LocalizationValue; }; @@ -1004,9 +1053,9 @@ type _LocalizationResource = { }; statementsSection: { empty: LocalizationValue; - itemCaption__paidForPlan: LocalizationValue; + itemCaption__paidForPlan: LocalizationValue<'plan' | 'period'>; itemCaption__proratedCredit: LocalizationValue; - itemCaption__subscribedAndPaidForPlan: LocalizationValue; + itemCaption__subscribedAndPaidForPlan: LocalizationValue<'plan' | 'period'>; notFound: LocalizationValue; tableHeader__date: LocalizationValue; tableHeader__amount: LocalizationValue; @@ -1042,9 +1091,9 @@ type _LocalizationResource = { formButtonPrimary__pay: LocalizationValue; removeResource: { title: LocalizationValue; - messageLine1: LocalizationValue; + messageLine1: LocalizationValue<'identifier'>; messageLine2: LocalizationValue; - successMessage: LocalizationValue; + successMessage: LocalizationValue<'paymentSource'>; }; payWithTestCardButton: LocalizationValue; }; @@ -1072,9 +1121,9 @@ type _LocalizationResource = { }; organizationList: { createOrganization: LocalizationValue; - title: LocalizationValue; + title: LocalizationValue<'applicationName'>; titleWithoutPersonal: LocalizationValue; - subtitle: LocalizationValue; + subtitle: LocalizationValue<'applicationName'>; action__invitationAccept: LocalizationValue; invitationAcceptedLabel: LocalizationValue; action__suggestionsAccept: LocalizationValue; @@ -1083,12 +1132,12 @@ type _LocalizationResource = { }; unstable__errors: UnstableErrors; dates: { - previous6Days: LocalizationValue; - lastDay: LocalizationValue; - sameDay: LocalizationValue; - nextDay: LocalizationValue; - next6Days: LocalizationValue; - numeric: LocalizationValue; + previous6Days: LocalizationValue<'date'>; + lastDay: LocalizationValue<'date'>; + sameDay: LocalizationValue<'date'>; + nextDay: LocalizationValue<'date'>; + next6Days: LocalizationValue<'date'>; + numeric: LocalizationValue<'date'>; }; waitlist: { start: { @@ -1113,23 +1162,24 @@ type _LocalizationResource = { action__add: LocalizationValue; detailsTitle__emptyRow: LocalizationValue; revokeConfirmation: { - formTitle: LocalizationValue; + formTitle: LocalizationValue<'apiKeyName'>; formHint: LocalizationValue; formButtonPrimary__revoke: LocalizationValue; }; dates: { - lastUsed__seconds: LocalizationValue; - lastUsed__minutes: LocalizationValue; - lastUsed__hours: LocalizationValue; - lastUsed__days: LocalizationValue; - lastUsed__months: LocalizationValue; - lastUsed__years: LocalizationValue; + lastUsed__seconds: LocalizationValue<'seconds'>; + lastUsed__minutes: LocalizationValue<'minutes'>; + lastUsed__hours: LocalizationValue<'hours'>; + lastUsed__days: LocalizationValue<'days'>; + lastUsed__months: LocalizationValue<'months'>; + lastUsed__years: LocalizationValue<'years'>; }; }; }; type WithParamName = T & Partial>}`, LocalizationValue>>; + type UnstableErrors = WithParamName<{ external_account_not_found: LocalizationValue; identification_deletion_failed: LocalizationValue; @@ -1145,7 +1195,7 @@ type UnstableErrors = WithParamName<{ web3_missing_identifier: LocalizationValue; form_password_pwned: LocalizationValue; form_password_pwned__sign_in: LocalizationValue; - form_username_invalid_length: LocalizationValue; + form_username_invalid_length: LocalizationValue<'min_length' | 'max_length'>; form_username_invalid_character: LocalizationValue; form_param_format_invalid: LocalizationValue; form_param_format_invalid__email_address: LocalizationValue; @@ -1213,7 +1263,7 @@ type UnstableErrors = WithParamName<{ }; form_param_max_length_exceeded: LocalizationValue; organization_minimum_permissions_needed: LocalizationValue; - already_a_member_in_organization: LocalizationValue; + already_a_member_in_organization: LocalizationValue<'email'>; organization_domain_common: LocalizationValue; organization_domain_blocked: LocalizationValue; organization_domain_exists_for_enterprise_connection: LocalizationValue;