From 736b250e0e603f57605b5e2ba0411a21b807eac3 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Thu, 29 Aug 2024 00:04:57 +0200 Subject: [PATCH 001/184] update TranslationBase and make few changes --- src/languages/en.ts | 6 ++- src/languages/es.ts | 6 ++- src/languages/translations.ts | 6 +-- src/languages/types.ts | 93 ++++++++++++++++++++++++++++++++--- src/libs/Localize/index.ts | 33 +++++++++++++ tests/unit/TranslateTest.ts | 40 ++++++++++++++- 6 files changed, 170 insertions(+), 14 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index f78b544fbfee..0dc28eb4e128 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -132,7 +132,7 @@ type States = Record; type AllCountries = Record; /* eslint-disable max-len */ -export default { +const translations = { common: { cancel: 'Cancel', dismiss: 'Dismiss', @@ -4488,4 +4488,6 @@ export default { updateRoomDescription: 'set the room description to:', clearRoomDescription: 'cleared the room description', }, -} satisfies TranslationBase; +}; + +export default translations satisfies TranslationBase; diff --git a/src/languages/es.ts b/src/languages/es.ts index aeb292b4723d..2558475d38f2 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -122,7 +122,7 @@ import type { } from './types'; /* eslint-disable max-len */ -export default { +const translations = { common: { cancel: 'Cancelar', dismiss: 'Descartar', @@ -5004,4 +5004,6 @@ export default { updateRoomDescription: 'establece la descripción de la sala a:', clearRoomDescription: 'la descripción de la habitación ha sido borrada', }, -} satisfies EnglishTranslation; +}; + +export default translations satisfies TranslationBase; diff --git a/src/languages/translations.ts b/src/languages/translations.ts index 4d89f1f529de..b0c0ad581b53 100644 --- a/src/languages/translations.ts +++ b/src/languages/translations.ts @@ -12,10 +12,10 @@ import type {TranslationBase, TranslationFlatObject} from './types'; */ // Necessary to export so that it is accessible to the unit tests // eslint-disable-next-line rulesdir/no-inline-named-export -export function flattenObject(obj: TranslationBase): TranslationFlatObject { +export function flattenObject(obj: TranslationBase): TranslationFlatObject { const result: Record = {}; - const recursive = (data: TranslationBase, key: string): void => { + const recursive = (data: TranslationBase, key: string): void => { // If the data is a function or not a object (eg. a string or array), // it's the final value for the key being built and there is no need // for more recursion @@ -27,7 +27,7 @@ export function flattenObject(obj: TranslationBase): TranslationFlatObject { // Recursive call to the keys and connect to the respective data Object.keys(data).forEach((k) => { isEmpty = false; - recursive(data[k] as TranslationBase, key ? `${key}.${k}` : k); + recursive(data[k] as TranslationBase, key ? `${key}.${k}` : k); }); // Check for when the object is empty but a key exists, so that diff --git a/src/languages/types.ts b/src/languages/types.ts index f3d6f5b677e6..4b4236341521 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -1,5 +1,5 @@ import type {OnyxInputOrEntry, ReportAction} from '@src/types/onyx'; -import type {Unit} from '@src/types/onyx/Policy'; +import type {ConnectionName, Unit} from '@src/types/onyx/Policy'; import type {ViolationDataType} from '@src/types/onyx/TransactionViolation'; import type en from './en'; @@ -11,6 +11,11 @@ type CharacterLimitParams = { limit: number; }; +type CharacterLengthLimitParams = { + limit: number; + length: number; +}; + type ZipCodeExampleFormatParams = { zipSampleFormat: string; }; @@ -253,17 +258,29 @@ type PaySomeoneParams = {name?: string}; type TaskCreatedActionParams = {title: string}; -/* Translation Object types */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type TranslationBaseValue = string | string[] | ((...args: any[]) => string); +type PluralizeValue = { + one: string; + other: string; + zero?: string; + two?: string; + few?: string; + many?: string; +}; -type TranslationBase = {[key: string]: TranslationBaseValue | TranslationBase}; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type FunctionArgumentType = T extends (arg: infer A, ...args: any[]) => string ? A : unknown; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type ValidTranslationBaseValue = string | string[] | ((arg: any, ...args: any[]) => string | PluralizeValue); +type TranslationBaseValue = string | string[] | ((arg: FunctionArgumentType extends string ? unknown : FunctionArgumentType, ...args: unknown[]) => string | PluralizeValue); +type TranslationBase = { + [K in keyof T]: T[K] extends ValidTranslationBaseValue ? TranslationBaseValue : TranslationBase; +}; /* Flat Translation Object types */ // Flattens an object and returns concatenations of all the keys of nested objects type FlattenObject = { // eslint-disable-next-line @typescript-eslint/no-explicit-any - [TKey in keyof TObject]: TObject[TKey] extends (...args: any[]) => any + [TKey in keyof TObject]: TObject[TKey] extends (args: any) => any ? `${TPrefix}${TKey & string}` : // eslint-disable-next-line @typescript-eslint/no-explicit-any TObject[TKey] extends any[] @@ -297,6 +314,8 @@ type ElectronicFundsParams = {percentage: string; amount: string}; type LogSizeParams = {size: number}; +type LogSizeAndDateParams = {size: number; date: string}; + type HeldRequestParams = {comment: string}; type DistanceRateOperationsParams = {count: number}; @@ -358,7 +377,53 @@ type ApprovalWorkflowErrorParams = { name2: string; }; +type ConnectionNameParams = { + connectionName: ConnectionName; +}; + +type LastSyncDateParams = { + connectionName: string; + formattedDate: string; +}; + +type CustomersOrJobsLabelParams = { + importFields: string[]; + importType: string; +}; + +type ExportAgainModalDescriptionParams = { + reportName: string; + connectionName: ConnectionName; +}; + +type IntegrationSyncFailedParams = {label: string; errorMessage: string}; + +type AddEmployeeParams = {email: string; role: string}; + +type UpdateRoleParams = {email: string; currentRole: string; newRole: string}; + +type RemoveMemberParams = {email: string; role: string}; + +type DateParams = {date?: string}; + +type AmountParams = {amount?: string}; + +type FiltersAmountBetweenParams = {greaterThan: string; lessThan: string}; + +type StatementPageTitleParams = {year: string | number; monthName: string}; + +type DisconnectPromptParams = {currentIntegration?: ConnectionName}; + +type DisconnectTitleParams = {integration?: ConnectionName}; + +type AmountWithCurrencyParams = {amountWithCurrency: string}; + +type LowerUpperParams = {lower: string; upper: string}; + export type { + AmountWithCurrencyParams, + LowerUpperParams, + LogSizeAndDateParams, AddressLineParams, AdminCanceledRequestParams, AlreadySignedInParams, @@ -481,4 +546,20 @@ export type { RemoveMembersWarningPrompt, DeleteExpenseTranslationParams, ApprovalWorkflowErrorParams, + PluralizeValue, + ConnectionNameParams, + LastSyncDateParams, + CustomersOrJobsLabelParams, + ExportAgainModalDescriptionParams, + IntegrationSyncFailedParams, + AddEmployeeParams, + UpdateRoleParams, + RemoveMemberParams, + DateParams, + AmountParams, + FiltersAmountBetweenParams, + StatementPageTitleParams, + DisconnectPromptParams, + DisconnectTitleParams, + CharacterLengthLimitParams, }; diff --git a/src/libs/Localize/index.ts b/src/libs/Localize/index.ts index c9eef3170245..cada825df854 100644 --- a/src/libs/Localize/index.ts +++ b/src/libs/Localize/index.ts @@ -70,6 +70,10 @@ const translationCache = new Map, Map, Map]>), ); +function isPlainObject(value: string): boolean { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + /** * Helper function to get the translated string for given * locale and phrase. This function is used to avoid @@ -110,6 +114,35 @@ function getTranslatedPhrase( if (translatedPhrase) { if (typeof translatedPhrase === 'function') { + /** + * + * is Plain object is for checking if the phraseTranslated output + * is an object then further check if it include the count param or not + * OR before checking the plain object output, we can check if we have the count + * param in phraseParameters + * + */ + + if (isPlainObject(translatedPhrase(...phraseParameters))) { + const phraseObject = {...(phraseParameters[0] as Record)}; + + if ('count' in phraseObject && typeof phraseObject.count === 'number') { + const pluralRule = new Intl.PluralRules(language).select(phraseObject.count); + const phraseTranslated = translatedPhrase(...phraseParameters); + + if (phraseTranslated && typeof phraseTranslated === 'object' && pluralRule in phraseTranslated) { + return phraseTranslated[pluralRule]; + } + + Log.alert(`Plural form ${pluralRule} is not found for ${phraseKey}, using 'other' form`); + // NOTEME fix ts error and lint error + + /* eslint-disable @typescript-eslint/no-unsafe-return */ + // @ts-expect-error Property 'other' does not exist on type 'string' + return phraseTranslated.other; + } + } + return translatedPhrase(...phraseParameters); } diff --git a/tests/unit/TranslateTest.ts b/tests/unit/TranslateTest.ts index 0be29a29cb12..ed63a334f119 100644 --- a/tests/unit/TranslateTest.ts +++ b/tests/unit/TranslateTest.ts @@ -15,12 +15,25 @@ asMutable(translations).default = { testKey2: 'Test Word 2', testKey3: 'Test Word 3', testKeyGroup: { - testFunction: ({testVariable}) => `With variable ${testVariable}`, + testFunction: ({testVariable}: {testVariable: string}) => `With variable ${testVariable}`, + }, + pluralisationGroup: { + countWithoutPluralRules: ({count}: {count: number}) => `Count value is ${count}`, + countWithNoCorrespondingRule: ({count}: {count: number}) => ({ + one: 'One file is being downloaded.', + other: `Other ${count} files are being downloaded.`, + }), }, }), [CONST.LOCALES.ES]: translations.flattenObject({ testKey1: 'Spanish', testKey2: 'Spanish Word 2', + pluralisationGroup: { + couthWithCorrespondingRule: ({count}: {count: number}) => ({ + one: 'Un artículo', + other: `${count} artículos`, + }), + }, }), [CONST.LOCALES.ES_ES]: translations.flattenObject({testKey1: 'Spanish ES'}), }; @@ -56,6 +69,31 @@ describe('translate', () => { // @ts-expect-error - TranslationPaths doesn't include testKeyGroup.testFunction as a valid key expect(Localize.translate(CONST.LOCALES.EN, 'testKeyGroup.testFunction' as TranslationPaths, {testVariable})).toBe(expectedValue); }); + + it('Test when count value passed to function but output is string', () => { + const expectedValue = 'Count value is 10'; + const count = 10; + // @ts-expect-error - TranslationPaths doesn't include pluralisationGroup.countWithoutPluralRules as a valid key + expect(Localize.translate(CONST.LOCALES.EN, 'pluralisationGroup.countWithoutPluralRules' as TranslationPaths, {count})).toBe(expectedValue); + }); + + it('Test when count value 2 passed to function but there is no rule for the key two', () => { + const expectedValue = 'Other 2 files are being downloaded.'; + const count = 2; + // @ts-expect-error - TranslationPaths doesn't include pluralisationGroup.countWithNoCorrespondingRule as a valid key + expect(Localize.translate(CONST.LOCALES.EN, 'pluralisationGroup.countWithNoCorrespondingRule' as TranslationPaths, {count})).toBe(expectedValue); + }); + + it('Test when count value 0, 1, 100 passed to function', () => { + // @ts-expect-error - TranslationPaths doesn't include pluralisationGroup.couthWithCorrespondingRule as a valid key + expect(Localize.translate(CONST.LOCALES.ES, 'pluralisationGroup.couthWithCorrespondingRule' as TranslationPaths, {count: 0})).toBe('0 artículos'); + + // @ts-expect-error - TranslationPaths doesn't include pluralisationGroup.couthWithCorrespondingRule as a valid key + expect(Localize.translate(CONST.LOCALES.ES, 'pluralisationGroup.couthWithCorrespondingRule' as TranslationPaths, {count: 1})).toBe('Un artículo'); + + // @ts-expect-error - TranslationPaths doesn't include pluralisationGroup.couthWithCorrespondingRule as a valid key + expect(Localize.translate(CONST.LOCALES.ES, 'pluralisationGroup.couthWithCorrespondingRule' as TranslationPaths, {count: 100})).toBe('100 artículos'); + }); }); describe('Translation Keys', () => { From e830bb57cb8feefd1f990a8ee7db01a8540f8b4f Mon Sep 17 00:00:00 2001 From: Yauheni Date: Thu, 29 Aug 2024 00:15:56 +0200 Subject: [PATCH 002/184] update TranslationBase --- src/languages/types.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/languages/types.ts b/src/languages/types.ts index 4b4236341521..2f4696524a66 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -271,7 +271,10 @@ type PluralizeValue = { type FunctionArgumentType = T extends (arg: infer A, ...args: any[]) => string ? A : unknown; // eslint-disable-next-line @typescript-eslint/no-explicit-any type ValidTranslationBaseValue = string | string[] | ((arg: any, ...args: any[]) => string | PluralizeValue); -type TranslationBaseValue = string | string[] | ((arg: FunctionArgumentType extends string ? unknown : FunctionArgumentType, ...args: unknown[]) => string | PluralizeValue); +type TranslationBaseValue = + | string + | string[] + | ((arg: FunctionArgumentType extends string ? Record : FunctionArgumentType, ...args: unknown[]) => string | PluralizeValue); type TranslationBase = { [K in keyof T]: T[K] extends ValidTranslationBaseValue ? TranslationBaseValue : TranslationBase; }; From 09f51d495672ed293bbae0bb9d2e9837e7f10a25 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Thu, 29 Aug 2024 12:25:59 +0200 Subject: [PATCH 003/184] update translations PART-1 --- src/languages/en.ts | 107 ++++++++++------- src/languages/es.ts | 109 +++++++++++------- src/languages/translations.ts | 2 +- src/languages/types.ts | 88 +++++++++++++- .../intacct/ExistingConnectionsPage.tsx | 7 +- .../NetSuiteExistingConnectionsPage.tsx | 7 +- tests/unit/TranslateTest.ts | 4 +- 7 files changed, 238 insertions(+), 86 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 892f7bda9b6f..ac135423ca00 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5,19 +5,35 @@ import type {Country} from '@src/CONST'; import type {DelegateRole} from '@src/types/onyx/Account'; import type {ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName} from '@src/types/onyx/Policy'; import type { + ActionsAreCurrentlyRestricted, AddressLineParams, AdminCanceledRequestParams, AlreadySignedInParams, ApprovalWorkflowErrorParams, ApprovedAmountParams, + BadgeFreeTrialParams, BeginningOfChatHistoryAdminRoomPartOneParams, BeginningOfChatHistoryAnnounceRoomPartOneParams, BeginningOfChatHistoryAnnounceRoomPartTwo, BeginningOfChatHistoryDomainRoomPartOneParams, + BillingBannerCardAuthenticationRequiredParams, + BillingBannerCardExpiredParams, + BillingBannerCardOnDisputeParams, + BillingBannerDisputePendingParams, + BillingBannerInsufficientFundsParams, + BillingBannerSubtitleWithDateParams, CanceledRequestParams, + CardEndingParams, + CardInfoParams, + CardNextPaymentParams, + CategoryNameParams, ChangeFieldParams, + ChangeOwnerDuplicateSubscriptionParams, + ChangeOwnerHasFailedSettlementsParams, + ChangeOwnerSubscriptionParams, ChangePolicyParams, ChangeTypeParams, + CharacterLengthLimitParams, CharacterLimitParams, ConfirmHoldExpenseParams, ConfirmThatParams, @@ -39,6 +55,7 @@ import type { GoToRoomParams, InstantSummaryParams, IssueVirtualCardParams, + LastSyncDateParams, LocalTimeParams, LoggedInAsParams, LogSizeParams, @@ -52,6 +69,7 @@ import type { OOOEventSummaryFullDayParams, OOOEventSummaryPartialDayParams, OurEmailProviderParams, + OwnerOwesAmountParams, PaidElsewhereWithAmountParams, PaidWithExpensifyWithAmountParams, ParentNavigationSummaryParams, @@ -65,16 +83,19 @@ import type { RemovedTheRequestParams, RemoveMembersWarningPrompt, RenamedRoomActionParams, + RenamedWorkspaceNameActionParams, ReportArchiveReasonsClosedParams, ReportArchiveReasonsMergedParams, - ReportArchiveReasonsPolicyDeletedParams, ReportArchiveReasonsRemovedFromPolicyParams, + ReportPolicyNameParams, RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, ResolutionConstraintsParams, RoomNameReservedErrorParams, RoomRenamedToParams, + SecondaryLoginParams, + SelectedNumberParams, SetTheDistanceParams, SetTheRequestParams, SettledAfterAddedBankAccountParams, @@ -83,15 +104,22 @@ import type { SignUpNewFaceCodeParams, SizeExceededParams, SplitAmountParams, + StatementTitleParams, StepCounterParams, StripePaidParams, + SubscriptionCommitmentParams, + SubscriptionSettingsRenewsOnParams, + SubscriptionSettingsSaveUpToParams, + SubscriptionSizeParams, TaskCreatedActionParams, + TaxAmountParams, TermsParams, ThreadRequestReportNameParams, ThreadSentMoneyReportNameParams, ToValidateLoginParams, TransferParams, TranslationBase, + TrialStartedTitleParams, UnapprovedParams, UnshareParams, UntilTimeParams, @@ -120,6 +148,8 @@ import type { WelcomeNoteParams, WelcomeToRoomParams, WeSentYouMagicSignInLinkParams, + WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams, + YourPlanPriceParams, ZipCodeExampleFormatParams, } from './types'; @@ -257,7 +287,7 @@ const translations = { fieldRequired: 'This field is required.', requestModified: 'This request is being modified by another member.', characterLimit: ({limit}: CharacterLimitParams) => `Exceeds the maximum length of ${limit} characters`, - characterLimitExceedCounter: ({length, limit}) => `Character limit exceeded (${length}/${limit})`, + characterLimitExceedCounter: ({length, limit}: CharacterLengthLimitParams) => `Character limit exceeded (${length}/${limit})`, dateInvalid: 'Please select a valid date.', invalidDateShouldBeFuture: 'Please choose today or a future date.', invalidTimeShouldBeFuture: 'Please choose a time at least one minute ahead.', @@ -628,8 +658,7 @@ const translations = { shouldUseYou ? `This chat is no longer active because you are no longer a member of the ${policyName} workspace.` : `This chat is no longer active because ${displayName} is no longer a member of the ${policyName} workspace.`, - [CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED]: ({policyName}: ReportArchiveReasonsPolicyDeletedParams) => - `This chat is no longer active because ${policyName} is no longer an active workspace.`, + [CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED]: ({policyName}: ReportPolicyNameParams) => `This chat is no longer active because ${policyName} is no longer an active workspace.`, [CONST.REPORT.ARCHIVE_REASON.BOOKING_END_DATE_HAS_PASSED]: 'This booking is archived.', }, writeCapabilityPage: { @@ -1469,7 +1498,7 @@ const translations = { }, }, reportDetailsPage: { - inWorkspace: ({policyName}) => `in ${policyName}`, + inWorkspace: ({policyName}: ReportPolicyNameParams) => `in ${policyName}`, }, reportDescriptionPage: { roomDescription: 'Room description', @@ -2143,7 +2172,7 @@ const translations = { testTransactions: 'Test transactions', issueAndManageCards: 'Issue and manage cards', reconcileCards: 'Reconcile cards', - selected: ({selectedNumber}) => `${selectedNumber} selected`, + selected: ({selectedNumber}: SelectedNumberParams) => `${selectedNumber} selected`, settlementFrequency: 'Settlement frequency', deleteConfirmation: 'Are you sure you want to delete this workspace?', unavailable: 'Unavailable workspace', @@ -2179,7 +2208,7 @@ const translations = { createNewConnection: 'Create new connection', reuseExistingConnection: 'Reuse existing connection', existingConnections: 'Existing connections', - lastSyncDate: (connectionName: string, formattedDate: string) => `${connectionName} - Last synced ${formattedDate}`, + lastSyncDate: ({connectionName, formattedDate}: LastSyncDateParams) => `${connectionName} - Last synced ${formattedDate}`, }, qbo: { importDescription: 'Choose which coding configurations to import from QuickBooks Online to Expensify.', @@ -2292,8 +2321,8 @@ const translations = { accountsSwitchDescription: 'Enabled categories will be available for members to select when creating their expenses.', trackingCategories: 'Tracking categories', trackingCategoriesDescription: 'Choose how to handle Xero tracking categories in Expensify.', - mapTrackingCategoryTo: ({categoryName}) => `Map Xero ${categoryName} to`, - mapTrackingCategoryToDescription: ({categoryName}) => `Choose where to map ${categoryName} when exporting to Xero.`, + mapTrackingCategoryTo: ({categoryName}: CategoryNameParams) => `Map Xero ${categoryName} to`, + mapTrackingCategoryToDescription: ({categoryName}: CategoryNameParams) => `Choose where to map ${categoryName} when exporting to Xero.`, customers: 'Re-bill customers', customersDescription: 'Choose whether to re-bill customers in Expensify. Your Xero customer contacts can be tagged to expenses, and will export to Xero as a sales invoice.', taxesDescription: 'Choose how to handle Xero taxes in Expensify.', @@ -3027,7 +3056,7 @@ const translations = { updateTaxClaimableFailureMessage: 'The reclaimable portion must be less than the distance rate amount.', }, deleteTaxConfirmation: 'Are you sure you want to delete this tax?', - deleteMultipleTaxConfirmation: ({taxAmount}) => `Are you sure you want to delete ${taxAmount} taxes?`, + deleteMultipleTaxConfirmation: ({taxAmount}: TaxAmountParams) => `Are you sure you want to delete ${taxAmount} taxes?`, actions: { delete: 'Delete rate', deleteMultiple: 'Delete rates', @@ -3081,7 +3110,7 @@ const translations = { genericRemove: 'There was a problem removing that workspace member.', }, addedWithPrimary: 'Some members were added with their primary logins.', - invitedBySecondaryLogin: ({secondaryLogin}) => `Added by secondary login ${secondaryLogin}.`, + invitedBySecondaryLogin: ({secondaryLogin}: SecondaryLoginParams) => `Added by secondary login ${secondaryLogin}.`, membersListTitle: 'Directory of all workspace members.', }, card: { @@ -3509,19 +3538,19 @@ const translations = { amountOwedText: 'This account has an outstanding balance from a previous month.\n\nDo you want to clear the balance and take over billing of this workspace?', ownerOwesAmountTitle: 'Outstanding balance', ownerOwesAmountButtonText: 'Transfer balance', - ownerOwesAmountText: ({email, amount}) => + ownerOwesAmountText: ({email, amount}: OwnerOwesAmountParams) => `The account owning this workspace (${email}) has an outstanding balance from a previous month.\n\nDo you want to transfer this amount (${amount}) in order to take over billing for this workspace? Your payment card will be charged immediately.`, subscriptionTitle: 'Take over annual subscription', subscriptionButtonText: 'Transfer subscription', - subscriptionText: ({usersCount, finalCount}) => + subscriptionText: ({usersCount, finalCount}: ChangeOwnerSubscriptionParams) => `Taking over this workspace will merge its annual subscription with your current subscription. This will increase your subscription size by ${usersCount} members making your new subscription size ${finalCount}. Would you like to continue?`, duplicateSubscriptionTitle: 'Duplicate subscription alert', duplicateSubscriptionButtonText: 'Continue', - duplicateSubscriptionText: ({email, workspaceName}) => + duplicateSubscriptionText: ({email, workspaceName}: ChangeOwnerDuplicateSubscriptionParams) => `It looks like you may be trying to take over billing for ${email}'s workspaces, but to do that, you need to be an admin on all their workspaces first.\n\nClick "Continue" if you only want to take over billing for the workspace ${workspaceName}.\n\nIf you want to take over billing for their entire subscription, please have them add you as an admin to all their workspaces first before taking over billing.`, hasFailedSettlementsTitle: 'Cannot transfer ownership', hasFailedSettlementsButtonText: 'Got it', - hasFailedSettlementsText: ({email}) => + hasFailedSettlementsText: ({email}: ChangeOwnerHasFailedSettlementsParams) => `You can't take over billing because ${email} has an overdue expensify Expensify Card settlement. Please ask them to reach out to concierge@expensify.com to resolve the issue. Then, you can take over billing for this workspace.`, failedToClearBalanceTitle: 'Failed to clear balance', failedToClearBalanceButtonText: 'OK', @@ -3606,8 +3635,8 @@ const translations = { }, restrictedAction: { restricted: 'Restricted', - actionsAreCurrentlyRestricted: ({workspaceName}) => `Actions on the ${workspaceName} workspace are currently restricted`, - workspaceOwnerWillNeedToAddOrUpdatePaymentCard: ({workspaceOwnerName}) => + actionsAreCurrentlyRestricted: ({workspaceName}: ActionsAreCurrentlyRestricted) => `Actions on the ${workspaceName} workspace are currently restricted`, + workspaceOwnerWillNeedToAddOrUpdatePaymentCard: ({workspaceOwnerName}: WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams) => `Workspace owner, ${workspaceOwnerName} will need to add or update the payment card on file to unlock new workspace activity.`, youWillNeedToAddOrUpdatePaymentCard: "You'll need to add or update the payment card on file to unlock new workspace activity.", addPaymentCardToUnlock: 'Add a payment card to unlock!', @@ -3698,7 +3727,7 @@ const translations = { }, }, workspaceActions: { - renamedWorkspaceNameAction: ({oldName, newName}) => `updated the name of this workspace from ${oldName} to ${newName}`, + renamedWorkspaceNameAction: ({oldName, newName}: RenamedWorkspaceNameActionParams) => `updated the name of this workspace from ${oldName} to ${newName}`, }, roomMembersPage: { memberNotFound: 'Member not found. To invite a new member to the room, please use the invite button above.', @@ -3738,7 +3767,7 @@ const translations = { deleteConfirmation: 'Are you sure you want to delete this task?', }, statementPage: { - title: (year, monthName) => `${monthName} ${year} statement`, + title: ({year, monthName}: StatementTitleParams) => `${monthName} ${year} statement`, generatingPDF: "We're generating your PDF right now. Please check back soon!", }, keyboardShortcutsPage: { @@ -4285,12 +4314,12 @@ const translations = { authenticatePaymentCard: 'Authenticate payment card', mobileReducedFunctionalityMessage: 'You can’t make changes to your subscription in the mobile app.', badge: { - freeTrial: ({numOfDays}) => `Free trial: ${numOfDays} ${numOfDays === 1 ? 'day' : 'days'} left`, + freeTrial: ({numOfDays}: BadgeFreeTrialParams) => `Free trial: ${numOfDays} ${numOfDays === 1 ? 'day' : 'days'} left`, }, billingBanner: { policyOwnerAmountOwed: { title: 'Your payment info is outdated', - subtitle: ({date}) => `Update your payment card by ${date} to continue using all of your favorite features.`, + subtitle: ({date}: BillingBannerSubtitleWithDateParams) => `Update your payment card by ${date} to continue using all of your favorite features.`, }, policyOwnerAmountOwedOverdue: { title: 'Your payment info is outdated', @@ -4298,7 +4327,7 @@ const translations = { }, policyOwnerUnderInvoicing: { title: 'Your payment info is outdated', - subtitle: ({date}) => `Your payment is past due. Please pay your invoice by ${date} to avoid service interruption.`, + subtitle: ({date}: BillingBannerSubtitleWithDateParams) => `Your payment is past due. Please pay your invoice by ${date} to avoid service interruption.`, }, policyOwnerUnderInvoicingOverdue: { title: 'Your payment info is outdated', @@ -4306,22 +4335,22 @@ const translations = { }, billingDisputePending: { title: 'Your card couldn’t be charged', - subtitle: ({amountOwed, cardEnding}) => + subtitle: ({amountOwed, cardEnding}: BillingBannerDisputePendingParams) => `You disputed the ${amountOwed} charge on the card ending in ${cardEnding}. Your account will be locked until the dispute is resolved with your bank.`, }, cardAuthenticationRequired: { title: 'Your card couldn’t be charged', - subtitle: ({cardEnding}) => + subtitle: ({cardEnding}: BillingBannerCardAuthenticationRequiredParams) => `Your payment card hasn’t been fully authenticated. Please complete the authentication process to activate your payment card ending in ${cardEnding}.`, }, insufficientFunds: { title: 'Your card couldn’t be charged', - subtitle: ({amountOwed}) => + subtitle: ({amountOwed}: BillingBannerInsufficientFundsParams) => `Your payment card was declined due to insufficient funds. Please retry or add a new payment card to clear your ${amountOwed} outstanding balance.`, }, cardExpired: { title: 'Your card couldn’t be charged', - subtitle: ({amountOwed}) => `Your payment card expired. Please add a new payment card to clear your ${amountOwed} outstanding balance.`, + subtitle: ({amountOwed}: BillingBannerCardExpiredParams) => `Your payment card expired. Please add a new payment card to clear your ${amountOwed} outstanding balance.`, }, cardExpireSoon: { title: 'Your card is expiring soon', @@ -4335,7 +4364,7 @@ const translations = { title: 'Your card couldn’t be charged', subtitle: 'Before retrying, please call your bank directly to authorize Expensify charges and remove any holds. Otherwise, try adding a different payment card.', }, - cardOnDispute: ({amountOwed, cardEnding}) => + cardOnDispute: ({amountOwed, cardEnding}: BillingBannerCardOnDisputeParams) => `You disputed the ${amountOwed} charge on the card ending in ${cardEnding}. Your account will be locked until the dispute is resolved with your bank.`, preTrial: { title: 'Start a free trial', @@ -4344,7 +4373,7 @@ const translations = { subtitleEnd: 'so your team can start expensing.', }, trialStarted: { - title: ({numOfDays}) => `Free trial: ${numOfDays} ${numOfDays === 1 ? 'day' : 'days'} left!`, + title: ({numOfDays}: TrialStartedTitleParams) => `Free trial: ${numOfDays} ${numOfDays === 1 ? 'day' : 'days'} left!`, subtitle: 'Add a payment card to continue using all of your favorite features.', }, trialEnded: { @@ -4356,9 +4385,9 @@ const translations = { title: 'Payment', subtitle: 'Add a card to pay for your Expensify subscription.', addCardButton: 'Add payment card', - cardNextPayment: ({nextPaymentDate}) => `Your next payment date is ${nextPaymentDate}.`, - cardEnding: ({cardNumber}) => `Card ending in ${cardNumber}`, - cardInfo: ({name, expiration, currency}) => `Name: ${name}, Expiration: ${expiration}, Currency: ${currency}`, + cardNextPayment: ({nextPaymentDate}: CardNextPaymentParams) => `Your next payment date is ${nextPaymentDate}.`, + cardEnding: ({cardNumber}: CardEndingParams) => `Card ending in ${cardNumber}`, + cardInfo: ({name, expiration, currency}: CardInfoParams) => `Name: ${name}, Expiration: ${expiration}, Currency: ${currency}`, changeCard: 'Change payment card', changeCurrency: 'Change payment currency', cardNotFound: 'No payment card added', @@ -4377,8 +4406,8 @@ const translations = { title: 'Your plan', collect: { title: 'Collect', - priceAnnual: ({lower, upper}) => `From ${lower}/active member with the Expensify Card, ${upper}/active member without the Expensify Card.`, - pricePayPerUse: ({lower, upper}) => `From ${lower}/active member with the Expensify Card, ${upper}/active member without the Expensify Card.`, + priceAnnual: ({lower, upper}: YourPlanPriceParams) => `From ${lower}/active member with the Expensify Card, ${upper}/active member without the Expensify Card.`, + pricePayPerUse: ({lower, upper}: YourPlanPriceParams) => `From ${lower}/active member with the Expensify Card, ${upper}/active member without the Expensify Card.`, benefit1: 'Unlimited SmartScans and distance tracking', benefit2: 'Expensify Cards with Smart Limits', benefit3: 'Bill pay and invoicing', @@ -4389,8 +4418,8 @@ const translations = { }, control: { title: 'Control', - priceAnnual: ({lower, upper}) => `From ${lower}/active member with the Expensify Card, ${upper}/active member without the Expensify Card.`, - pricePayPerUse: ({lower, upper}) => `From ${lower}/active member with the Expensify Card, ${upper}/active member without the Expensify Card.`, + priceAnnual: ({lower, upper}: YourPlanPriceParams) => `From ${lower}/active member with the Expensify Card, ${upper}/active member without the Expensify Card.`, + pricePayPerUse: ({lower, upper}: YourPlanPriceParams) => `From ${lower}/active member with the Expensify Card, ${upper}/active member without the Expensify Card.`, benefit1: 'Everything in Collect, plus:', benefit2: 'NetSuite and Sage Intacct integrations', benefit3: 'Certinia and Workday sync', @@ -4421,10 +4450,10 @@ const translations = { note: 'Note: An active member is anyone who has created, edited, submitted, approved, reimbursed, or exported expense data tied to your company workspace.', confirmDetails: 'Confirm your new annual subscription details:', subscriptionSize: 'Subscription size', - activeMembers: ({size}) => `${size} active members/month`, + activeMembers: ({size}: SubscriptionSizeParams) => `${size} active members/month`, subscriptionRenews: 'Subscription renews', youCantDowngrade: 'You can’t downgrade during your annual subscription.', - youAlreadyCommitted: ({size, date}) => + youAlreadyCommitted: ({size, date}: SubscriptionCommitmentParams) => `You already committed to an annual subscription size of ${size} active members per month until ${date}. You can switch to a pay-per-use subscription on ${date} by disabling auto-renew.`, error: { size: 'Please enter a valid subscription size.', @@ -4441,13 +4470,13 @@ const translations = { title: 'Subscription settings', autoRenew: 'Auto-renew', autoIncrease: 'Auto-increase annual seats', - saveUpTo: ({amountWithCurrency}) => `Save up to ${amountWithCurrency}/month per active member`, + saveUpTo: ({amountWithCurrency}: SubscriptionSettingsSaveUpToParams) => `Save up to ${amountWithCurrency}/month per active member`, automaticallyIncrease: 'Automatically increase your annual seats to accommodate for active members that exceed your subscription size. Note: This will extend your annual subscription end date.', disableAutoRenew: 'Disable auto-renew', helpUsImprove: 'Help us improve Expensify', whatsMainReason: "What's the main reason you're disabling auto-renew?", - renewsOn: ({date}) => `Renews on ${date}.`, + renewsOn: ({date}: SubscriptionSettingsRenewsOnParams) => `Renews on ${date}.`, }, requestEarlyCancellation: { title: 'Request early cancellation', diff --git a/src/languages/es.ts b/src/languages/es.ts index c65a1ede7ab7..70938e00e250 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3,19 +3,35 @@ import CONST from '@src/CONST'; import type {DelegateRole} from '@src/types/onyx/Account'; import type {ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName} from '@src/types/onyx/Policy'; import type { + ActionsAreCurrentlyRestricted, AddressLineParams, AdminCanceledRequestParams, AlreadySignedInParams, ApprovalWorkflowErrorParams, ApprovedAmountParams, + BadgeFreeTrialParams, BeginningOfChatHistoryAdminRoomPartOneParams, BeginningOfChatHistoryAnnounceRoomPartOneParams, BeginningOfChatHistoryAnnounceRoomPartTwo, BeginningOfChatHistoryDomainRoomPartOneParams, + BillingBannerCardAuthenticationRequiredParams, + BillingBannerCardExpiredParams, + BillingBannerCardOnDisputeParams, + BillingBannerDisputePendingParams, + BillingBannerInsufficientFundsParams, + BillingBannerSubtitleWithDateParams, CanceledRequestParams, + CardEndingParams, + CardInfoParams, + CardNextPaymentParams, + CategoryNameParams, ChangeFieldParams, + ChangeOwnerDuplicateSubscriptionParams, + ChangeOwnerHasFailedSettlementsParams, + ChangeOwnerSubscriptionParams, ChangePolicyParams, ChangeTypeParams, + CharacterLengthLimitParams, CharacterLimitParams, ConfirmHoldExpenseParams, ConfirmThatParams, @@ -29,7 +45,6 @@ import type { DistanceRateOperationsParams, EditActionParams, ElectronicFundsParams, - EnglishTranslation, EnterMagicCodeParams, ExportedToIntegrationParams, FormattedMaxLengthParams, @@ -38,6 +53,7 @@ import type { GoToRoomParams, InstantSummaryParams, IssueVirtualCardParams, + LastSyncDateParams, LocalTimeParams, LoggedInAsParams, LogSizeParams, @@ -51,6 +67,7 @@ import type { OOOEventSummaryFullDayParams, OOOEventSummaryPartialDayParams, OurEmailProviderParams, + OwnerOwesAmountParams, PaidElsewhereWithAmountParams, PaidWithExpensifyWithAmountParams, ParentNavigationSummaryParams, @@ -64,16 +81,19 @@ import type { RemovedTheRequestParams, RemoveMembersWarningPrompt, RenamedRoomActionParams, + RenamedWorkspaceNameActionParams, ReportArchiveReasonsClosedParams, ReportArchiveReasonsMergedParams, - ReportArchiveReasonsPolicyDeletedParams, ReportArchiveReasonsRemovedFromPolicyParams, + ReportPolicyNameParams, RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, ResolutionConstraintsParams, RoomNameReservedErrorParams, RoomRenamedToParams, + SecondaryLoginParams, + SelectedNumberParams, SetTheDistanceParams, SetTheRequestParams, SettledAfterAddedBankAccountParams, @@ -82,15 +102,22 @@ import type { SignUpNewFaceCodeParams, SizeExceededParams, SplitAmountParams, + StatementTitleParams, StepCounterParams, StripePaidParams, + SubscriptionCommitmentParams, + SubscriptionSettingsRenewsOnParams, + SubscriptionSettingsSaveUpToParams, + SubscriptionSizeParams, TaskCreatedActionParams, + TaxAmountParams, TermsParams, ThreadRequestReportNameParams, ThreadSentMoneyReportNameParams, ToValidateLoginParams, TransferParams, TranslationBase, + TrialStartedTitleParams, UnapprovedParams, UnshareParams, UntilTimeParams, @@ -120,6 +147,8 @@ import type { WelcomeNoteParams, WelcomeToRoomParams, WeSentYouMagicSignInLinkParams, + WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams, + YourPlanPriceParams, ZipCodeExampleFormatParams, } from './types'; @@ -248,7 +277,7 @@ const translations = { fieldRequired: 'Este campo es obligatorio.', requestModified: 'Esta solicitud está siendo modificada por otro miembro.', characterLimit: ({limit}: CharacterLimitParams) => `Supera el límite de ${limit} caracteres`, - characterLimitExceedCounter: ({length, limit}) => `Se superó el límite de caracteres (${length}/${limit})`, + characterLimitExceedCounter: ({length, limit}: CharacterLengthLimitParams) => `Se superó el límite de caracteres (${length}/${limit})`, dateInvalid: 'Por favor, selecciona una fecha válida.', invalidDateShouldBeFuture: 'Por favor, elige una fecha igual o posterior a hoy.', invalidTimeShouldBeFuture: 'Por favor, elige una hora al menos un minuto en el futuro.', @@ -622,8 +651,7 @@ const translations = { shouldUseYou ? `Este chat ya no está activo porque tu ya no eres miembro del espacio de trabajo ${policyName}.` : `Este chat está desactivado porque ${displayName} ha dejado de ser miembro del espacio de trabajo ${policyName}.`, - [CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED]: ({policyName}: ReportArchiveReasonsPolicyDeletedParams) => - `Este chat está desactivado porque el espacio de trabajo ${policyName} se ha eliminado.`, + [CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED]: ({policyName}: ReportPolicyNameParams) => `Este chat está desactivado porque el espacio de trabajo ${policyName} se ha eliminado.`, [CONST.REPORT.ARCHIVE_REASON.BOOKING_END_DATE_HAS_PASSED]: 'Esta reserva está archivada.', }, writeCapabilityPage: { @@ -1479,7 +1507,7 @@ const translations = { }, }, reportDetailsPage: { - inWorkspace: ({policyName}) => `en ${policyName}`, + inWorkspace: ({policyName}: ReportPolicyNameParams) => `en ${policyName}`, }, reportDescriptionPage: { roomDescription: 'Descripción de la sala de chat', @@ -2174,7 +2202,7 @@ const translations = { testTransactions: 'Transacciones de prueba', issueAndManageCards: 'Emitir y gestionar tarjetas', reconcileCards: 'Reconciliar tarjetas', - selected: ({selectedNumber}) => `${selectedNumber} seleccionados`, + selected: ({selectedNumber}: SelectedNumberParams) => `${selectedNumber} seleccionados`, settlementFrequency: 'Frecuencia de liquidación', deleteConfirmation: '¿Estás seguro de que quieres eliminar este espacio de trabajo?', unavailable: 'Espacio de trabajo no disponible', @@ -2210,7 +2238,7 @@ const translations = { createNewConnection: 'Crear una nueva conexión', reuseExistingConnection: 'Reutilizar la conexión existente', existingConnections: 'Conexiones existentes', - lastSyncDate: (connectionName: string, formattedDate: string) => `${connectionName} - Última sincronización ${formattedDate}`, + lastSyncDate: ({connectionName, formattedDate}: LastSyncDateParams) => `${connectionName} - Última sincronización ${formattedDate}`, topLevel: 'Nivel superior', }, qbo: { @@ -2330,8 +2358,8 @@ const translations = { accountsSwitchDescription: 'Las categorías activas estarán disponibles para ser escogidas cuando se crea un gasto.', trackingCategories: 'Categorías de seguimiento', trackingCategoriesDescription: 'Elige cómo gestionar categorías de seguimiento de Xero en Expensify.', - mapTrackingCategoryTo: ({categoryName}) => `Asignar ${categoryName} de Xero a`, - mapTrackingCategoryToDescription: ({categoryName}) => `Elige dónde mapear ${categoryName} al exportar a Xero.`, + mapTrackingCategoryTo: ({categoryName}: CategoryNameParams) => `Asignar ${categoryName} de Xero a`, + mapTrackingCategoryToDescription: ({categoryName}: CategoryNameParams) => `Elige dónde mapear ${categoryName} al exportar a Xero.`, customers: 'Volver a facturar a los clientes', customersDescription: 'Elige si quieres volver a facturar a los clientes en Expensify. Tus contactos de clientes de Xero se pueden etiquetar como gastos, y se exportarán a Xero como una factura de venta.', @@ -3076,7 +3104,7 @@ const translations = { updateTaxClaimableFailureMessage: 'La porción recuperable debe ser menor al monto del importe por distancia.', }, deleteTaxConfirmation: '¿Estás seguro de que quieres eliminar este impuesto?', - deleteMultipleTaxConfirmation: ({taxAmount}) => `¿Estás seguro de que quieres eliminar ${taxAmount} impuestos?`, + deleteMultipleTaxConfirmation: ({taxAmount}: TaxAmountParams) => `¿Estás seguro de que quieres eliminar ${taxAmount} impuestos?`, actions: { delete: 'Eliminar tasa', deleteMultiple: 'Eliminar tasas', @@ -3130,7 +3158,7 @@ const translations = { genericRemove: 'Ha ocurrido un problema al eliminar al miembro del espacio de trabajo.', }, addedWithPrimary: 'Se agregaron algunos miembros con sus nombres de usuario principales.', - invitedBySecondaryLogin: ({secondaryLogin}) => `Agregado por nombre de usuario secundario ${secondaryLogin}.`, + invitedBySecondaryLogin: ({secondaryLogin}: SecondaryLoginParams) => `Agregado por nombre de usuario secundario ${secondaryLogin}.`, membersListTitle: 'Directorio de todos los miembros del espacio de trabajo.', }, accounting: { @@ -3558,19 +3586,19 @@ const translations = { amountOwedText: 'Esta cuenta tiene un saldo pendiente de un mes anterior.\n\n¿Quiere liquidar el saldo y hacerse cargo de la facturación de este espacio de trabajo?', ownerOwesAmountTitle: 'Saldo pendiente', ownerOwesAmountButtonText: 'Transferir saldo', - ownerOwesAmountText: ({email, amount}) => + ownerOwesAmountText: ({email, amount}: OwnerOwesAmountParams) => `La cuenta propietaria de este espacio de trabajo (${email}) tiene un saldo pendiente de un mes anterior.\n\n¿Desea transferir este monto (${amount}) para hacerse cargo de la facturación de este espacio de trabajo? tu tarjeta de pago se cargará inmediatamente.`, subscriptionTitle: 'Asumir la suscripción anual', subscriptionButtonText: 'Transferir suscripción', - subscriptionText: ({usersCount, finalCount}) => + subscriptionText: ({usersCount, finalCount}: ChangeOwnerSubscriptionParams) => `Al hacerse cargo de este espacio de trabajo se fusionará tu suscripción anual asociada con tu suscripción actual. Esto aumentará el tamaño de tu suscripción en ${usersCount} miembros, lo que hará que tu nuevo tamaño de suscripción sea ${finalCount}. ¿Te gustaria continuar?`, duplicateSubscriptionTitle: 'Alerta de suscripción duplicada', duplicateSubscriptionButtonText: 'Continuar', - duplicateSubscriptionText: ({email, workspaceName}) => + duplicateSubscriptionText: ({email, workspaceName}: ChangeOwnerDuplicateSubscriptionParams) => `Parece que estás intentando hacerte cargo de la facturación de los espacios de trabajo de ${email}, pero para hacerlo, primero debes ser administrador de todos sus espacios de trabajo.\n\nHaz clic en "Continuar" si solo quieres tomar sobrefacturación para el espacio de trabajo ${workspaceName}.\n\nSi desea hacerse cargo de la facturación de toda tu suscripción, pídales que lo agreguen como administrador a todos sus espacios de trabajo antes de hacerse cargo de la facturación.`, hasFailedSettlementsTitle: 'No se puede transferir la propiedad', hasFailedSettlementsButtonText: 'Entiendo', - hasFailedSettlementsText: ({email}) => + hasFailedSettlementsText: ({email}: ChangeOwnerHasFailedSettlementsParams) => `No puede hacerse cargo de la facturación porque ${email} tiene una liquidación vencida de la tarjeta Expensify. Avíseles que se comuniquen con concierge@expensify.com para resolver el problema. Luego, podrá hacerse cargo de la facturación de este espacio de trabajo.`, failedToClearBalanceTitle: 'Fallo al liquidar el saldo', failedToClearBalanceButtonText: 'OK', @@ -3656,8 +3684,8 @@ const translations = { }, restrictedAction: { restricted: 'Restringido', - actionsAreCurrentlyRestricted: ({workspaceName}) => `Las acciones en el espacio de trabajo ${workspaceName} están actualmente restringidas`, - workspaceOwnerWillNeedToAddOrUpdatePaymentCard: ({workspaceOwnerName}) => + actionsAreCurrentlyRestricted: ({workspaceName}: ActionsAreCurrentlyRestricted) => `Las acciones en el espacio de trabajo ${workspaceName} están actualmente restringidas`, + workspaceOwnerWillNeedToAddOrUpdatePaymentCard: ({workspaceOwnerName}: WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams) => `El propietario del espacio de trabajo, ${workspaceOwnerName} tendrá que añadir o actualizar la tarjeta de pago registrada para desbloquear nueva actividad en el espacio de trabajo.`, youWillNeedToAddOrUpdatePaymentCard: 'Debes añadir o actualizar la tarjeta de pago registrada para desbloquear nueva actividad en el espacio de trabajo.', addPaymentCardToUnlock: 'Añade una tarjeta para desbloquearlo!', @@ -3749,7 +3777,7 @@ const translations = { }, }, workspaceActions: { - renamedWorkspaceNameAction: ({oldName, newName}) => `actualizó el nombre de este espacio de trabajo de ${oldName} a ${newName}`, + renamedWorkspaceNameAction: ({oldName, newName}: RenamedWorkspaceNameActionParams) => `actualizó el nombre de este espacio de trabajo de ${oldName} a ${newName}`, }, roomMembersPage: { memberNotFound: 'Miembro no encontrado. Para invitar a un nuevo miembro a la sala de chat, por favor, utiliza el botón invitar que está más arriba.', @@ -3789,7 +3817,7 @@ const translations = { deleteConfirmation: '¿Estás seguro de que quieres eliminar esta tarea?', }, statementPage: { - title: (year, monthName) => `Estado de cuenta de ${monthName} ${year}`, + title: ({year, monthName}: StatementTitleParams) => `Estado de cuenta de ${monthName} ${year}`, generatingPDF: 'Estamos generando tu PDF ahora mismo. ¡Por favor, vuelve más tarde!', }, keyboardShortcutsPage: { @@ -4800,12 +4828,12 @@ const translations = { authenticatePaymentCard: 'Autenticar tarjeta de pago', mobileReducedFunctionalityMessage: 'No puedes hacer cambios en tu suscripción en la aplicación móvil.', badge: { - freeTrial: ({numOfDays}) => `Prueba gratuita: ${numOfDays === 1 ? `queda 1 día` : `quedan ${numOfDays} días`}`, + freeTrial: ({numOfDays}: BadgeFreeTrialParams) => `Prueba gratuita: ${numOfDays === 1 ? `queda 1 día` : `quedan ${numOfDays} días`}`, }, billingBanner: { policyOwnerAmountOwed: { title: 'Tu información de pago está desactualizada', - subtitle: ({date}) => `Actualiza tu tarjeta de pago antes del ${date} para continuar utilizando todas tus herramientas favoritas`, + subtitle: ({date}: BillingBannerSubtitleWithDateParams) => `Actualiza tu tarjeta de pago antes del ${date} para continuar utilizando todas tus herramientas favoritas`, }, policyOwnerAmountOwedOverdue: { title: 'Tu información de pago está desactualizada', @@ -4813,7 +4841,7 @@ const translations = { }, policyOwnerUnderInvoicing: { title: 'Tu información de pago está desactualizada', - subtitle: ({date}) => `Tu pago está vencido. Por favor, paga tu factura antes del ${date} para evitar la interrupción del servicio.`, + subtitle: ({date}: BillingBannerSubtitleWithDateParams) => `Tu pago está vencido. Por favor, paga tu factura antes del ${date} para evitar la interrupción del servicio.`, }, policyOwnerUnderInvoicingOverdue: { title: 'Tu información de pago está desactualizada', @@ -4821,22 +4849,23 @@ const translations = { }, billingDisputePending: { title: 'No se ha podido realizar el cobro a tu tarjeta', - subtitle: ({amountOwed, cardEnding}) => + subtitle: ({amountOwed, cardEnding}: BillingBannerDisputePendingParams) => `Has impugnado el cargo ${amountOwed} en la tarjeta terminada en ${cardEnding}. Tu cuenta estará bloqueada hasta que se resuelva la disputa con tu banco.`, }, cardAuthenticationRequired: { title: 'No se ha podido realizar el cobro a tu tarjeta', - subtitle: ({cardEnding}) => + subtitle: ({cardEnding}: BillingBannerCardAuthenticationRequiredParams) => `Tu tarjeta de pago no ha sido autenticada completamente. Por favor, completa el proceso de autenticación para activar tu tarjeta de pago que termina en ${cardEnding}.`, }, insufficientFunds: { title: 'No se ha podido realizar el cobro a tu tarjeta', - subtitle: ({amountOwed}) => + subtitle: ({amountOwed}: BillingBannerInsufficientFundsParams) => `Tu tarjeta de pago fue rechazada por falta de fondos. Vuelve a intentarlo o añade una nueva tarjeta de pago para liquidar tu saldo pendiente de ${amountOwed}.`, }, cardExpired: { title: 'No se ha podido realizar el cobro a tu tarjeta', - subtitle: ({amountOwed}) => `Tu tarjeta de pago ha expirado. Por favor, añade una nueva tarjeta de pago para liquidar tu saldo pendiente de ${amountOwed}.`, + subtitle: ({amountOwed}: BillingBannerCardExpiredParams) => + `Tu tarjeta de pago ha expirado. Por favor, añade una nueva tarjeta de pago para liquidar tu saldo pendiente de ${amountOwed}.`, }, cardExpireSoon: { title: 'Tu tarjeta caducará pronto', @@ -4852,7 +4881,7 @@ const translations = { subtitle: 'Antes de volver a intentarlo, llama directamente a tu banco para que autorice los cargos de Expensify y elimine las retenciones. De lo contrario, añade una tarjeta de pago diferente.', }, - cardOnDispute: ({amountOwed, cardEnding}) => + cardOnDispute: ({amountOwed, cardEnding}: BillingBannerCardOnDisputeParams) => `Has impugnado el cargo ${amountOwed} en la tarjeta terminada en ${cardEnding}. Tu cuenta estará bloqueada hasta que se resuelva la disputa con tu banco.`, preTrial: { title: 'Iniciar una prueba gratuita', @@ -4861,7 +4890,7 @@ const translations = { subtitleEnd: 'para que tu equipo pueda empezar a enviar gastos.', }, trialStarted: { - title: ({numOfDays}) => `Prueba gratuita: ¡${numOfDays === 1 ? `queda 1 día` : `quedan ${numOfDays} días`}!`, + title: ({numOfDays}: TrialStartedTitleParams) => `Prueba gratuita: ¡${numOfDays === 1 ? `queda 1 día` : `quedan ${numOfDays} días`}!`, subtitle: 'Añade una tarjeta de pago para seguir utilizando tus funciones favoritas.', }, trialEnded: { @@ -4873,9 +4902,9 @@ const translations = { title: 'Pago', subtitle: 'Añade una tarjeta para pagar tu suscripción a Expensify.', addCardButton: 'Añade tarjeta de pago', - cardNextPayment: ({nextPaymentDate}) => `Tu próxima fecha de pago es ${nextPaymentDate}.`, - cardEnding: ({cardNumber}) => `Tarjeta terminada en ${cardNumber}`, - cardInfo: ({name, expiration, currency}) => `Nombre: ${name}, Expiración: ${expiration}, Moneda: ${currency}`, + cardNextPayment: ({nextPaymentDate}: CardNextPaymentParams) => `Tu próxima fecha de pago es ${nextPaymentDate}.`, + cardEnding: ({cardNumber}: CardEndingParams) => `Tarjeta terminada en ${cardNumber}`, + cardInfo: ({name, expiration, currency}: CardInfoParams) => `Nombre: ${name}, Expiración: ${expiration}, Moneda: ${currency}`, changeCard: 'Cambiar tarjeta de pago', changeCurrency: 'Cambiar moneda de pago', cardNotFound: 'No se ha añadido ninguna tarjeta de pago', @@ -4894,8 +4923,8 @@ const translations = { title: 'Tu plan', collect: { title: 'Recolectar', - priceAnnual: ({lower, upper}) => `Desde ${lower}/miembro activo con la Tarjeta Expensify, ${upper}/miembro activo sin la Tarjeta Expensify.`, - pricePayPerUse: ({lower, upper}) => `Desde ${lower}/miembro activo con la Tarjeta Expensify, ${upper}/miembro activo sin la Tarjeta Expensify.`, + priceAnnual: ({lower, upper}: YourPlanPriceParams) => `Desde ${lower}/miembro activo con la Tarjeta Expensify, ${upper}/miembro activo sin la Tarjeta Expensify.`, + pricePayPerUse: ({lower, upper}: YourPlanPriceParams) => `Desde ${lower}/miembro activo con la Tarjeta Expensify, ${upper}/miembro activo sin la Tarjeta Expensify.`, benefit1: 'SmartScans ilimitados y seguimiento de la distancia', benefit2: 'Tarjetas Expensify con Límites Inteligentes', benefit3: 'Pago de facturas y facturación', @@ -4906,8 +4935,8 @@ const translations = { }, control: { title: 'Control', - priceAnnual: ({lower, upper}) => `Desde ${lower}/miembro activo con la Tarjeta Expensify, ${upper}/miembro activo sin la Tarjeta Expensify.`, - pricePayPerUse: ({lower, upper}) => `Desde ${lower}/miembro activo con la Tarjeta Expensify, ${upper}/miembro activo sin la Tarjeta Expensify.`, + priceAnnual: ({lower, upper}: YourPlanPriceParams) => `Desde ${lower}/miembro activo con la Tarjeta Expensify, ${upper}/miembro activo sin la Tarjeta Expensify.`, + pricePayPerUse: ({lower, upper}: YourPlanPriceParams) => `Desde ${lower}/miembro activo con la Tarjeta Expensify, ${upper}/miembro activo sin la Tarjeta Expensify.`, benefit1: 'Todo en Recolectar, más:', benefit2: 'Integraciones con NetSuite y Sage Intacct', benefit3: 'Sincronización de Certinia y Workday', @@ -4938,10 +4967,10 @@ const translations = { note: 'Nota: Un miembro activo es cualquiera que haya creado, editado, enviado, aprobado, reembolsado, o exportado datos de gastos vinculados al espacio de trabajo de tu empresa.', confirmDetails: 'Confirma los datos de tu nueva suscripción anual:', subscriptionSize: 'Tamaño de suscripción', - activeMembers: ({size}) => `${size} miembros activos/mes`, + activeMembers: ({size}: SubscriptionSizeParams) => `${size} miembros activos/mes`, subscriptionRenews: 'Renovación de la suscripción', youCantDowngrade: 'No puedes bajar de categoría durante tu suscripción anual.', - youAlreadyCommitted: ({size, date}) => + youAlreadyCommitted: ({size, date}: SubscriptionCommitmentParams) => `Ya se ha comprometido a un tamaño de suscripción anual de ${size} miembros activos al mes hasta el ${date}. Puede cambiar a una suscripción de pago por uso en ${date} desactivando la auto-renovación.`, error: { size: 'Por favor ingrese un tamaño de suscripción valido.', @@ -4958,13 +4987,13 @@ const translations = { title: 'Configuración de suscripción', autoRenew: 'Auto-renovación', autoIncrease: 'Auto-incremento', - saveUpTo: ({amountWithCurrency}) => `Ahorre hasta ${amountWithCurrency} al mes por miembro activo`, + saveUpTo: ({amountWithCurrency}: SubscriptionSettingsSaveUpToParams) => `Ahorre hasta ${amountWithCurrency} al mes por miembro activo`, automaticallyIncrease: 'Aumenta automáticamente tus plazas anuales para dar lugar a los miembros activos que superen el tamaño de tu suscripción. Nota: Esto ampliará la fecha de finalización de tu suscripción anual.', disableAutoRenew: 'Desactivar auto-renovación', helpUsImprove: 'Ayúdanos a mejorar Expensify', whatsMainReason: '¿Cuál es la razón principal por la que deseas desactivar la auto-renovación?', - renewsOn: ({date}) => `Se renovará el ${date}.`, + renewsOn: ({date}: SubscriptionSettingsRenewsOnParams) => `Se renovará el ${date}.`, }, requestEarlyCancellation: { title: 'Solicitar cancelación anticipada', diff --git a/src/languages/translations.ts b/src/languages/translations.ts index b0c0ad581b53..02b7303f0b44 100644 --- a/src/languages/translations.ts +++ b/src/languages/translations.ts @@ -12,7 +12,7 @@ import type {TranslationBase, TranslationFlatObject} from './types'; */ // Necessary to export so that it is accessible to the unit tests // eslint-disable-next-line rulesdir/no-inline-named-export -export function flattenObject(obj: TranslationBase): TranslationFlatObject { +export function flattenObject(obj: TranslationBase): TranslationFlatObject { const result: Record = {}; const recursive = (data: TranslationBase, key: string): void => { diff --git a/src/languages/types.ts b/src/languages/types.ts index 2f4696524a66..a7d56c42fc95 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -96,7 +96,7 @@ type ReportArchiveReasonsRemovedFromPolicyParams = { shouldUseYou?: boolean; }; -type ReportArchiveReasonsPolicyDeletedParams = { +type ReportPolicyNameParams = { policyName: string; }; @@ -421,9 +421,93 @@ type DisconnectTitleParams = {integration?: ConnectionName}; type AmountWithCurrencyParams = {amountWithCurrency: string}; +type SelectedNumberParams = {selectedNumber: number}; + type LowerUpperParams = {lower: string; upper: string}; +type CategoryNameParams = {categoryName: string}; + +type TaxAmountParams = {taxAmount: number}; + +type SecondaryLoginParams = {secondaryLogin: string}; + +type OwnerOwesAmountParams = {amount: number; email: string}; + +type ChangeOwnerSubscriptionParams = {usersCount: number; finalCount: number}; + +type ChangeOwnerDuplicateSubscriptionParams = {email: string; workspaceName: string}; + +type ChangeOwnerHasFailedSettlementsParams = {email: string}; + +type ActionsAreCurrentlyRestricted = {workspaceName: string}; + +type WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams = {workspaceOwnerName: string}; + +type RenamedWorkspaceNameActionParams = {oldName: string; newName: string}; + +type StatementTitleParams = {year: number; monthName: string}; + +type BadgeFreeTrialParams = {numOfDays: number}; + +type BillingBannerSubtitleWithDateParams = {date: string}; + +type BillingBannerDisputePendingParams = {amountOwed: string; cardEnding: string}; + +type BillingBannerCardAuthenticationRequiredParams = {cardEnding: string}; + +type BillingBannerInsufficientFundsParams = {amountOwed: string}; + +type BillingBannerCardExpiredParams = {amountOwed: string}; + +type BillingBannerCardOnDisputeParams = {amountOwed: string; cardEnding: string}; + +type TrialStartedTitleParams = {numOfDays: number}; + +type CardNextPaymentParams = {nextPaymentDate: string}; + +type CardEndingParams = {cardNumber: string}; + +type CardInfoParams = {name: string; expiration: string; currency: string}; + +type YourPlanPriceParams = {lower: string; upper: string}; + +type SubscriptionSizeParams = {size: number}; + +type SubscriptionCommitmentParams = {size: number; date: string}; + +type SubscriptionSettingsSaveUpToParams = {amountWithCurrency: string}; + +type SubscriptionSettingsRenewsOnParams = {date: string}; + export type { + CardNextPaymentParams, + CardEndingParams, + CardInfoParams, + YourPlanPriceParams, + SubscriptionSizeParams, + SubscriptionCommitmentParams, + SubscriptionSettingsSaveUpToParams, + SubscriptionSettingsRenewsOnParams, + BadgeFreeTrialParams, + BillingBannerSubtitleWithDateParams, + BillingBannerDisputePendingParams, + BillingBannerCardAuthenticationRequiredParams, + BillingBannerInsufficientFundsParams, + BillingBannerCardExpiredParams, + BillingBannerCardOnDisputeParams, + TrialStartedTitleParams, + StatementTitleParams, + RenamedWorkspaceNameActionParams, + WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams, + ActionsAreCurrentlyRestricted, + ChangeOwnerHasFailedSettlementsParams, + OwnerOwesAmountParams, + ChangeOwnerDuplicateSubscriptionParams, + ChangeOwnerSubscriptionParams, + SecondaryLoginParams, + TaxAmountParams, + CategoryNameParams, + SelectedNumberParams, AmountWithCurrencyParams, LowerUpperParams, LogSizeAndDateParams, @@ -482,7 +566,7 @@ export type { RenamedRoomActionParams, ReportArchiveReasonsClosedParams, ReportArchiveReasonsMergedParams, - ReportArchiveReasonsPolicyDeletedParams, + ReportPolicyNameParams, ReportArchiveReasonsRemovedFromPolicyParams, RequestAmountParams, RequestCountParams, diff --git a/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx b/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx index d26626578bcb..a034699454ed 100644 --- a/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx +++ b/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx @@ -31,7 +31,12 @@ function ExistingConnectionsPage({route}: ExistingConnectionsPageProps) { avatarID: policy.id, icon: policy.avatarURL ? policy.avatarURL : ReportUtils.getDefaultWorkspaceAvatar(policy.name), iconType: policy.avatarURL ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_WORKSPACE, - description: date ? translate('workspace.common.lastSyncDate', CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY.intacct, date) : translate('workspace.accounting.intacct'), + description: date + ? translate('workspace.common.lastSyncDate', { + connectionName: CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY.intacct, + formattedDate: date, + }) + : translate('workspace.accounting.intacct'), onPress: () => { copyExistingPolicyConnection(policy.id, policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT); Navigation.dismissModal(); diff --git a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx index f6a57bcd0014..a83a5dab716e 100644 --- a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx +++ b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx @@ -31,7 +31,12 @@ function NetSuiteExistingConnectionsPage({route}: ExistingConnectionsPageProps) avatarID: policy.id, icon: policy.avatarURL ? policy.avatarURL : ReportUtils.getDefaultWorkspaceAvatar(policy.name), iconType: policy.avatarURL ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_WORKSPACE, - description: date ? translate('workspace.common.lastSyncDate', CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY.netsuite, date) : translate('workspace.accounting.netsuite'), + description: date + ? translate('workspace.common.lastSyncDate', { + connectionName: CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY.netsuite, + formattedDate: date, + }) + : translate('workspace.accounting.netsuite'), onPress: () => { copyExistingPolicyConnection(policy.id, policyID, CONST.POLICY.CONNECTIONS.NAME.NETSUITE); Navigation.goBack(ROUTES.WORKSPACE_ACCOUNTING.getRoute(policyID)); diff --git a/tests/unit/TranslateTest.ts b/tests/unit/TranslateTest.ts index ed63a334f119..f3563b322e4f 100644 --- a/tests/unit/TranslateTest.ts +++ b/tests/unit/TranslateTest.ts @@ -2,7 +2,7 @@ import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import * as translations from '@src/languages/translations'; -import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types'; +import type {TranslationBase, TranslationFlatObject, TranslationPaths} from '@src/languages/types'; import * as Localize from '@src/libs/Localize'; import asMutable from '@src/types/utils/asMutable'; import arrayDifference from '@src/utils/arrayDifference'; @@ -169,7 +169,7 @@ describe('flattenObject', () => { }, }; - const result = translations.flattenObject(simpleObject); + const result = translations.flattenObject(simpleObject as TranslationBase); expect(result).toStrictEqual({ 'common.yes': 'Yes', 'common.no': 'No', From a2378865fc4b759697ea8ac5875db0b010a0cb33 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Thu, 29 Aug 2024 13:38:28 +0200 Subject: [PATCH 004/184] update translations PART-2 --- src/languages/en.ts | 152 +++++++++++++++++++--------------- src/languages/es.ts | 140 ++++++++++++++++++------------- src/languages/translations.ts | 4 +- src/languages/types.ts | 58 +++++++++++-- 4 files changed, 223 insertions(+), 131 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index ac135423ca00..0973e3038813 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2,15 +2,16 @@ import {CONST as COMMON_CONST, Str} from 'expensify-common'; import {startCase} from 'lodash'; import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; -import type {DelegateRole} from '@src/types/onyx/Account'; -import type {ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName} from '@src/types/onyx/Policy'; import type { ActionsAreCurrentlyRestricted, + AddEmployeeParams, AddressLineParams, AdminCanceledRequestParams, + AgeParams, AlreadySignedInParams, ApprovalWorkflowErrorParams, ApprovedAmountParams, + AssigneeParams, BadgeFreeTrialParams, BeginningOfChatHistoryAdminRoomPartOneParams, BeginningOfChatHistoryAnnounceRoomPartOneParams, @@ -37,24 +38,38 @@ import type { CharacterLimitParams, ConfirmHoldExpenseParams, ConfirmThatParams, + ConnectionNameParams, + CustomersOrJobsLabelParams, + DateParams, DateShouldBeAfterParams, DateShouldBeBeforeParams, + DefaultVendorDescriptionParams, + DelegateRoleParams, DelegateSubmitParams, DeleteActionParams, DeleteConfirmationParams, DeleteExpenseTranslationParams, DidSplitAmountMessageParams, + DimensionsCountParams, DistanceRateOperationsParams, EditActionParams, ElectronicFundsParams, EnterMagicCodeParams, + ExportAgainModalDescriptionParams, ExportedToIntegrationParams, + ExportIntegrationSelectedParams, + FiltersAmountBetweenParams, FormattedMaxLengthParams, ForwardedAmountParams, GoBackMessageParams, GoToRoomParams, + ImportFieldParams, + IncorrectZipFormatParams, InstantSummaryParams, + IntacctMappingTitleParams, + IntegrationSyncFailedParams, IssueVirtualCardParams, + LastSyncAccountingParams, LastSyncDateParams, LocalTimeParams, LoggedInAsParams, @@ -79,8 +94,10 @@ import type { PayerPaidParams, PayerSettledParams, PaySomeoneParams, + ReconciliationWorksParams, ReimbursementRateParams, RemovedTheRequestParams, + RemoveMemberPromptParams, RemoveMembersWarningPrompt, RenamedRoomActionParams, RenamedWorkspaceNameActionParams, @@ -91,6 +108,7 @@ import type { RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, + RequiredFieldParams, ResolutionConstraintsParams, RoomNameReservedErrorParams, RoomRenamedToParams, @@ -111,6 +129,7 @@ import type { SubscriptionSettingsRenewsOnParams, SubscriptionSettingsSaveUpToParams, SubscriptionSizeParams, + SyncStageNameConnectionsParams, TaskCreatedActionParams, TaxAmountParams, TermsParams, @@ -121,10 +140,12 @@ import type { TranslationBase, TrialStartedTitleParams, UnapprovedParams, + UnapproveWithIntegrationWarningParams, UnshareParams, UntilTimeParams, UpdatedTheDistanceParams, UpdatedTheRequestParams, + UpdateRoleParams, UsePlusButtonParams, UserIsAlreadyMemberParams, UserSplitParams, @@ -720,7 +741,7 @@ const translations = { splitBill: 'Split expense', splitScan: 'Split receipt', splitDistance: 'Split distance', - paySomeone: (name: string) => `Pay ${name ?? 'someone'}`, + paySomeone: ({name}: PaySomeoneParams) => `Pay ${name ?? 'someone'}`, assignTask: 'Assign task', header: 'Quick action', trackManual: 'Track expense', @@ -766,7 +787,7 @@ const translations = { receiptScanning: 'Receipt scanning...', receiptScanInProgress: 'Receipt scan in progress', receiptScanInProgressDescription: 'Receipt scan in progress. Check back later or enter the details now.', - receiptIssuesFound: (count: number) => `${count === 1 ? 'Issue' : 'Issues'} found`, + receiptIssuesFound: ({count}: DistanceRateOperationsParams) => `${count === 1 ? 'Issue' : 'Issues'} found`, fieldPending: 'Pending...', defaultRate: 'Default rate', receiptMissingDetails: 'Receipt missing details', @@ -816,9 +837,9 @@ const translations = { yourSplit: ({amount}: UserSplitParams) => `Your split ${amount}`, payerOwesAmount: ({payer, amount, comment}: PayerOwesAmountParams) => `${payer} owes ${amount}${comment ? ` for ${comment}` : ''}`, payerOwes: ({payer}: PayerOwesParams) => `${payer} owes: `, - payerPaidAmount: ({payer, amount}: PayerPaidAmountParams): string => `${payer ? `${payer} ` : ''}paid ${amount}`, + payerPaidAmount: ({payer, amount}: PayerPaidAmountParams) => `${payer ? `${payer} ` : ''}paid ${amount}`, payerPaid: ({payer}: PayerPaidParams) => `${payer} paid: `, - payerSpentAmount: ({payer, amount}: PayerPaidAmountParams): string => `${payer} spent ${amount}`, + payerSpentAmount: ({payer, amount}: PayerPaidAmountParams) => `${payer} spent ${amount}`, payerSpent: ({payer}: PayerPaidParams) => `${payer} spent: `, managerApproved: ({manager}: ManagerApprovedParams) => `${manager} approved:`, managerApprovedAmount: ({manager, amount}: ManagerApprovedAmountParams) => `${manager} approved ${amount}`, @@ -913,7 +934,7 @@ const translations = { unapprove: 'Unapprove', unapproveReport: 'Unapprove report', headsUp: 'Heads up!', - unapproveWithIntegrationWarning: (accountingIntegration: string) => + unapproveWithIntegrationWarning: ({accountingIntegration}: UnapproveWithIntegrationWarningParams) => `This report has already been exported to ${accountingIntegration}. Changes to this report in Expensify may lead to data discrepancies and Expensify Card reconciliation issues. Are you sure you want to unapprove this report?`, reimbursable: 'reimbursable', nonReimbursable: 'non-reimbursable', @@ -1296,15 +1317,16 @@ const translations = { availableSpend: 'Remaining limit', smartLimit: { name: 'Smart limit', - title: (formattedLimit: string) => `You can spend up to ${formattedLimit} on this card, and the limit will reset as your submitted expenses are approved.`, + title: ({formattedLimit}: ViolationsOverLimitParams) => `You can spend up to ${formattedLimit} on this card, and the limit will reset as your submitted expenses are approved.`, }, fixedLimit: { name: 'Fixed limit', - title: (formattedLimit: string) => `You can spend up to ${formattedLimit} on this card, and then it will deactivate.`, + title: ({formattedLimit}: ViolationsOverLimitParams) => `You can spend up to ${formattedLimit} on this card, and then it will deactivate.`, }, monthlyLimit: { name: 'Monthly limit', - title: (formattedLimit: string) => `You can spend up to ${formattedLimit} on this card per month. The limit will reset on the 1st day of each calendar month.`, + title: ({formattedLimit}: ViolationsOverLimitParams) => + `You can spend up to ${formattedLimit} on this card per month. The limit will reset on the 1st day of each calendar month.`, }, virtualCardNumber: 'Virtual card number', physicalCardNumber: 'Physical card number', @@ -1512,7 +1534,7 @@ const translations = { groupMembersListTitle: 'Directory of all group members.', lastMemberTitle: 'Heads up!', lastMemberWarning: "Since you're the last person here, leaving will make this chat inaccessible to all users. Are you sure you want to leave?", - defaultReportName: ({displayName}: {displayName: string}) => `${displayName}'s group chat`, + defaultReportName: ({displayName}: ReportArchiveReasonsClosedParams) => `${displayName}'s group chat`, }, languagePage: { language: 'Language', @@ -1640,7 +1662,7 @@ const translations = { dateShouldBeBefore: ({dateString}: DateShouldBeBeforeParams) => `Date should be before ${dateString}.`, dateShouldBeAfter: ({dateString}: DateShouldBeAfterParams) => `Date should be after ${dateString}.`, hasInvalidCharacter: 'Name can only include Latin characters.', - incorrectZipFormat: (zipFormat?: string) => `Incorrect zip code format.${zipFormat ? ` Acceptable format: ${zipFormat}` : ''}`, + incorrectZipFormat: ({zipFormat}: IncorrectZipFormatParams) => `Incorrect zip code format.${zipFormat ? ` Acceptable format: ${zipFormat}` : ''}`, }, }, resendValidationForm: { @@ -1657,8 +1679,8 @@ const translations = { succesfullyUnlinkedLogin: 'Secondary login successfully unlinked!', }, emailDeliveryFailurePage: { - ourEmailProvider: (user: OurEmailProviderParams) => - `Our email provider has temporarily suspended emails to ${user.login} due to delivery issues. To unblock your login, please follow these steps:`, + ourEmailProvider: ({login}: OurEmailProviderParams) => + `Our email provider has temporarily suspended emails to ${login} due to delivery issues. To unblock your login, please follow these steps:`, confirmThat: ({login}: ConfirmThatParams) => `Confirm that ${login} is spelled correctly and is a real, deliverable email address. `, emailAliases: 'Email aliases such as "expenses@domain.com" must have access to their own email inbox for it to be a valid Expensify login.', ensureYourEmailClient: 'Ensure your email client allows expensify.com emails. ', @@ -2191,7 +2213,7 @@ const translations = { `You have been invited to ${workspaceName || 'a workspace'}! Download the Expensify mobile app at use.expensify.com/download to start tracking your expenses.`, subscription: 'Subscription', markAsExported: 'Mark as manually entered', - exportIntegrationSelected: (connectionName: ConnectionName) => `Export to ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]}`, + exportIntegrationSelected: ({connectionName}: ExportIntegrationSelectedParams) => `Export to ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]}`, letsDoubleCheck: "Let's double check that everything looks right.", lineItemLevel: 'Line-item level', reportLevel: 'Report level', @@ -2419,7 +2441,7 @@ const translations = { }, creditCardAccount: 'Credit card account', defaultVendor: 'Default vendor', - defaultVendorDescription: (isReimbursable: boolean): string => + defaultVendorDescription: ({isReimbursable}: DefaultVendorDescriptionParams) => `Set a default vendor that will apply to ${isReimbursable ? '' : 'non-'}reimbursable expenses that don't have a matching vendor in Sage Intacct.`, exportDescription: 'Configure how Expensify data exports to Sage Intacct.', exportPreferredExporterNote: @@ -2633,12 +2655,12 @@ const translations = { importJobs: 'Import projects', customers: 'customers', jobs: 'projects', - label: (importFields: string[], importType: string) => `${importFields.join(' and ')}, ${importType}`, + label: ({importFields, importType}: CustomersOrJobsLabelParams) => `${importFields.join(' and ')}, ${importType}`, }, importTaxDescription: 'Import tax groups from NetSuite.', importCustomFields: { chooseOptionBelow: 'Choose an option below:', - requiredFieldError: (fieldName: string) => `Please enter the ${fieldName}`, + requiredFieldError: ({fieldName}: RequiredFieldParams) => `Please enter the ${fieldName}`, customSegments: { title: 'Custom segments/records', addText: 'Add custom segment/record', @@ -2679,7 +2701,7 @@ const translations = { customRecordMappingTitle: 'How should this custom record be displayed in Expensify?', }, errors: { - uniqueFieldError: (fieldName: string) => `A custom segment/record with this ${fieldName?.toLowerCase()} already exists.`, + uniqueFieldError: ({fieldName}: RequiredFieldParams) => `A custom segment/record with this ${fieldName?.toLowerCase()} already exists.`, }, }, customLists: { @@ -2713,18 +2735,18 @@ const translations = { [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { label: 'NetSuite employee default', description: 'Not imported into Expensify, applied on export', - footerContent: (importField: string) => + footerContent: ({importField}: ImportFieldParams) => `If you use ${importField} in NetSuite, we'll apply the default set on the employee record upon export to Expense Report or Journal Entry.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { label: 'Tags', description: 'Line-item level', - footerContent: (importField: string) => `${startCase(importField)} will be selectable for each individual expense on an employee's report.`, + footerContent: ({importField}: ImportFieldParams) => `${startCase(importField)} will be selectable for each individual expense on an employee's report.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { label: 'Report fields', description: 'Report level', - footerContent: (importField: string) => `${startCase(importField)} selection will apply to all expense on an employee's report.`, + footerContent: ({importField}: ImportFieldParams) => `${startCase(importField)} selection will apply to all expense on an employee's report.`, }, }, }, @@ -2755,8 +2777,8 @@ const translations = { addAUserDefinedDimension: 'Add a user-defined dimension', detailedInstructionsLink: 'View detailed instructions', detailedInstructionsRestOfSentence: ' on adding user-defined dimensions.', - userDimensionsAdded: (dimensionsCount: number) => `${dimensionsCount} ${Str.pluralize('UDD', `UDDs`, dimensionsCount)} added`, - mappingTitle: (mappingName: SageIntacctMappingName): string => { + userDimensionsAdded: ({dimensionsCount}: DimensionsCountParams) => `${dimensionsCount} ${Str.pluralize('UDD', `UDDs`, dimensionsCount)} added`, + mappingTitle: ({mappingName}: IntacctMappingTitleParams) => { switch (mappingName) { case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS: return 'departments'; @@ -2825,20 +2847,21 @@ const translations = { deactivate: 'Deactivate card', changeCardLimit: 'Change card limit', changeLimit: 'Change limit', - smartLimitWarning: (limit: string) => `If you change this card’s limit to ${limit}, new transactions will be declined until you approve more expenses on the card.`, - monthlyLimitWarning: (limit: string) => `If you change this card’s limit to ${limit}, new transactions will be declined until next month.`, - fixedLimitWarning: (limit: string) => `If you change this card’s limit to ${limit}, new transactions will be declined.`, + smartLimitWarning: ({limit}: CharacterLimitParams) => + `If you change this card’s limit to ${limit}, new transactions will be declined until you approve more expenses on the card.`, + monthlyLimitWarning: ({limit}: CharacterLimitParams) => `If you change this card’s limit to ${limit}, new transactions will be declined until next month.`, + fixedLimitWarning: ({limit}: CharacterLimitParams) => `If you change this card’s limit to ${limit}, new transactions will be declined.`, changeCardLimitType: 'Change card limit type', changeLimitType: 'Change limit type', - changeCardSmartLimitTypeWarning: (limit: string) => + changeCardSmartLimitTypeWarning: ({limit}: CharacterLimitParams) => `If you change this card's limit type to Smart Limit, new transactions will be declined because the ${limit} unapproved limit has already been reached.`, - changeCardMonthlyLimitTypeWarning: (limit: string) => + changeCardMonthlyLimitTypeWarning: ({limit}: CharacterLimitParams) => `If you change this card's limit type to Monthly, new transactions will be declined because the ${limit} monthly limit has already been reached.`, addMailingAddress: 'Add mailing address', - issuedCard: (assignee: string) => `issued ${assignee} an Expensify Card! The card will arrive in 2-3 business days.`, - issuedCardNoMailingAddress: (assignee: string) => `issued ${assignee} an Expensify Card! The card will be shipped once a mailing address is added.`, + issuedCard: ({assignee}: AssigneeParams) => `issued ${assignee} an Expensify Card! The card will arrive in 2-3 business days.`, + issuedCardNoMailingAddress: ({assignee}: AssigneeParams) => `issued ${assignee} an Expensify Card! The card will be shipped once a mailing address is added.`, issuedCardVirtual: ({assignee, link}: IssueVirtualCardParams) => `issued ${assignee} a virtual ${link}! The card can be used right away.`, - addedAddress: (assignee: string) => `${assignee} added the address. Expensify Card will arrive in 2-3 business days.`, + addedAddress: ({assignee}: AssigneeParams) => `${assignee} added the address. Expensify Card will arrive in 2-3 business days.`, }, categories: { deleteCategories: 'Delete categories', @@ -3098,7 +3121,7 @@ const translations = { removeMembersTitle: 'Remove members', removeMemberButtonTitle: 'Remove from workspace', removeMemberGroupButtonTitle: 'Remove from group', - removeMemberPrompt: ({memberName}: {memberName: string}) => `Are you sure you want to remove ${memberName}?`, + removeMemberPrompt: ({memberName}: RemoveMemberPromptParams) => `Are you sure you want to remove ${memberName}?`, removeMemberTitle: 'Remove member', transferOwner: 'Transfer owner', makeMember: 'Make member', @@ -3188,8 +3211,8 @@ const translations = { xero: 'Xero', netsuite: 'NetSuite', intacct: 'Sage Intacct', - connectionName: (integration: ConnectionName) => { - switch (integration) { + connectionName: ({connectionName}: ConnectionNameParams) => { + switch (connectionName) { case CONST.POLICY.CONNECTIONS.NAME.QBO: return 'Quickbooks Online'; case CONST.POLICY.CONNECTIONS.NAME.XERO: @@ -3204,21 +3227,22 @@ const translations = { } }, setup: 'Connect', - lastSync: (relativeDate: string) => `Last synced ${relativeDate}`, + lastSync: ({relativeDate}: LastSyncAccountingParams) => `Last synced ${relativeDate}`, import: 'Import', export: 'Export', advanced: 'Advanced', other: 'Other integrations', syncNow: 'Sync now', disconnect: 'Disconnect', - disconnectTitle: (integration?: ConnectionName): string => { - const integrationName = integration && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integration] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integration] : 'integration'; + disconnectTitle: ({connectionName}: Partial) => { + const integrationName = + connectionName && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] : 'integration'; return `Disconnect ${integrationName}`; }, - connectTitle: (integrationToConnect: ConnectionName): string => `Connect ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] ?? 'accounting integration'}`, + connectTitle: ({connectionName}: ConnectionNameParams) => `Connect ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'accounting integration'}`, - syncError: (integration?: ConnectionName): string => { - switch (integration) { + syncError: ({connectionName}: ConnectionNameParams) => { + switch (connectionName) { case CONST.POLICY.CONNECTIONS.NAME.QBO: return "Can't connect to QuickBooks Online."; case CONST.POLICY.CONNECTIONS.NAME.XERO: @@ -3244,20 +3268,18 @@ const translations = { [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: 'Imported as report fields', [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: 'NetSuite employee default', }, - disconnectPrompt: (currentIntegration?: ConnectionName): string => { + disconnectPrompt: ({connectionName}: Partial) => { const integrationName = - currentIntegration && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[currentIntegration] - ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[currentIntegration] - : 'this integration'; + connectionName && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] : 'this integration'; return `Are you sure you want to disconnect ${integrationName}?`; }, - connectPrompt: (integrationToConnect: ConnectionName): string => + connectPrompt: ({connectionName}: ConnectionNameParams) => `Are you sure you want to connect ${ - CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] ?? 'this accounting integration' + CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'this accounting integration' }? This will remove any existing acounting connections.`, enterCredentials: 'Enter your credentials', connections: { - syncStageName: (stage: PolicyConnectionSyncStage) => { + syncStageName: ({stage}: SyncStageNameConnectionsParams) => { switch (stage) { case 'quickbooksOnlineImportCustomers': return 'Importing customers'; @@ -3390,7 +3412,7 @@ const translations = { chooseBankAccount: 'Choose the bank account that your Expensify Card payments will be reconciled against.', accountMatches: 'Make sure this account matches your ', settlementAccount: 'Expensify Card settlement account ', - reconciliationWorks: (lastFourPAN: string) => `(ending in ${lastFourPAN}) so Continuous Reconciliation works properly.`, + reconciliationWorks: ({lastFourPAN}: ReconciliationWorksParams) => `(ending in ${lastFourPAN}) so Continuous Reconciliation works properly.`, }, }, export: { @@ -3564,7 +3586,7 @@ const translations = { }, exportAgainModal: { title: 'Careful!', - description: (reportName: string, connectionName: ConnectionName) => + description: ({reportName, connectionName}: ExportAgainModalDescriptionParams) => `The following reports have already been exported to ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]}:\n\n${reportName}\n\nAre you sure you want to export them again?`, confirmText: 'Yes, export again', cancelText: 'Cancel', @@ -3627,7 +3649,7 @@ const translations = { upgradeToUnlock: 'Unlock this feature', completed: { headline: `You've upgraded your workspace!`, - successMessage: (policyName: string) => `You've successfully upgraded your ${policyName} workspace to the Control plan!`, + successMessage: ({policyName}: ReportPolicyNameParams) => `You've successfully upgraded your ${policyName} workspace to the Control plan!`, viewSubscription: 'View your subscription', moreDetails: 'for more details.', gotIt: 'Got it, thanks', @@ -3657,7 +3679,7 @@ const translations = { maxAge: 'Max age', maxExpenseAge: 'Max expense age', maxExpenseAgeDescription: 'Flag spend older than a specific number of days.', - maxExpenseAgeDays: (age: number) => `${age} ${Str.pluralize('day', 'days', age)}`, + maxExpenseAgeDays: ({age}: AgeParams) => `${age} ${Str.pluralize('day', 'days', age)}`, billableDefault: 'Billable default', billableDefaultDescription: 'Choose whether cash and credit card expenses should be billable by default. Billable expenses are enabled or disabled in', billable: 'Billable', @@ -3810,17 +3832,17 @@ const translations = { filtersHeader: 'Filters', filters: { date: { - before: (date?: string) => `Before ${date ?? ''}`, - after: (date?: string) => `After ${date ?? ''}`, + before: ({date}: Partial) => `Before ${date ?? ''}`, + after: ({date}: Partial) => `After ${date ?? ''}`, }, status: 'Status', keyword: 'Keyword', hasKeywords: 'Has keywords', currency: 'Currency', amount: { - lessThan: (amount?: string) => `Less than ${amount ?? ''}`, - greaterThan: (amount?: string) => `Greater than ${amount ?? ''}`, - between: (greaterThan: string, lessThan: string) => `Between ${greaterThan} and ${lessThan}`, + lessThan: ({amount}: Partial) => `Less than ${amount ?? ''}`, + greaterThan: ({amount}: Partial) => `Greater than ${amount ?? ''}`, + between: ({greaterThan, lessThan}: FiltersAmountBetweenParams) => `Between ${greaterThan} and ${lessThan}`, }, }, expenseType: 'Expense type', @@ -3938,7 +3960,7 @@ const translations = { nonReimbursableLink: 'View company card expenses.', pending: ({label}: ExportedToIntegrationParams) => `started exporting this report to ${label}...`, }, - integrationsMessage: (errorMessage: string, label: string) => `failed to export this report to ${label} ("${errorMessage}").`, + integrationsMessage: ({errorMessage, label}: IntegrationSyncFailedParams) => `failed to export this report to ${label} ("${errorMessage}").`, managerAttachReceipt: `added a receipt`, managerDetachReceipt: `removed a receipt`, markedReimbursed: ({amount, currency}: MarkedReimbursedParams) => `paid ${currency}${amount} elsewhere`, @@ -3955,10 +3977,10 @@ const translations = { stripePaid: ({amount, currency}: StripePaidParams) => `paid ${currency}${amount}`, takeControl: `took control`, unapproved: ({amount, currency}: UnapprovedParams) => `unapproved ${currency}${amount}`, - integrationSyncFailed: (label: string, errorMessage: string) => `failed to sync with ${label} ("${errorMessage}")`, - addEmployee: (email: string, role: string) => `added ${email} as ${role === 'user' ? 'member' : 'admin'}`, - updateRole: (email: string, currentRole: string, newRole: string) => `updated the role of ${email} from ${currentRole} to ${newRole}`, - removeMember: (email: string, role: string) => `removed ${role} ${email}`, + integrationSyncFailed: ({label, errorMessage}: IntegrationSyncFailedParams) => `failed to sync with ${label} ("${errorMessage}")`, + addEmployee: ({email, role}: AddEmployeeParams) => `added ${email} as ${role === 'user' ? 'member' : 'admin'}`, + updateRole: ({email, currentRole, newRole}: UpdateRoleParams) => `updated the role of ${email} from ${currentRole} to ${newRole}`, + removeMember: ({email, role}: AddEmployeeParams) => `removed ${role} ${email}`, }, }, }, @@ -4191,7 +4213,7 @@ const translations = { missingCategory: 'Missing category', missingComment: 'Description required for selected category', missingTag: ({tagName}: ViolationsMissingTagParams) => `Missing ${tagName ?? 'tag'}`, - modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams): string => { + modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => { switch (type) { case 'distance': return 'Amount differs from calculated distance'; @@ -4259,7 +4281,7 @@ const translations = { hold: 'Hold', }, reportViolations: { - [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: (fieldName: string) => `${fieldName} is required`, + [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `${fieldName} is required`, }, violationDismissal: { rter: { @@ -4520,7 +4542,7 @@ const translations = { }, delegate: { switchAccount: 'Switch accounts:', - role: (role: DelegateRole): string => { + role: ({role}: DelegateRoleParams) => { switch (role) { case CONST.DELEGATE_ROLE.ALL: return 'Full'; diff --git a/src/languages/es.ts b/src/languages/es.ts index 70938e00e250..25df635ee01d 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1,14 +1,15 @@ import {Str} from 'expensify-common'; import CONST from '@src/CONST'; -import type {DelegateRole} from '@src/types/onyx/Account'; -import type {ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName} from '@src/types/onyx/Policy'; import type { ActionsAreCurrentlyRestricted, + AddEmployeeParams, AddressLineParams, AdminCanceledRequestParams, + AgeParams, AlreadySignedInParams, ApprovalWorkflowErrorParams, ApprovedAmountParams, + AssigneeParams, BadgeFreeTrialParams, BeginningOfChatHistoryAdminRoomPartOneParams, BeginningOfChatHistoryAnnounceRoomPartOneParams, @@ -35,24 +36,38 @@ import type { CharacterLimitParams, ConfirmHoldExpenseParams, ConfirmThatParams, + ConnectionNameParams, + CustomersOrJobsLabelParams, + DateParams, DateShouldBeAfterParams, DateShouldBeBeforeParams, + DefaultVendorDescriptionParams, + DelegateRoleParams, DelegateSubmitParams, DeleteActionParams, DeleteConfirmationParams, DeleteExpenseTranslationParams, DidSplitAmountMessageParams, + DimensionsCountParams, DistanceRateOperationsParams, EditActionParams, ElectronicFundsParams, EnterMagicCodeParams, + ExportAgainModalDescriptionParams, ExportedToIntegrationParams, + ExportIntegrationSelectedParams, + FiltersAmountBetweenParams, FormattedMaxLengthParams, ForwardedAmountParams, GoBackMessageParams, GoToRoomParams, + ImportFieldParams, + IncorrectZipFormatParams, InstantSummaryParams, + IntacctMappingTitleParams, + IntegrationSyncFailedParams, IssueVirtualCardParams, + LastSyncAccountingParams, LastSyncDateParams, LocalTimeParams, LoggedInAsParams, @@ -77,8 +92,10 @@ import type { PayerPaidParams, PayerSettledParams, PaySomeoneParams, + ReconciliationWorksParams, ReimbursementRateParams, RemovedTheRequestParams, + RemoveMemberPromptParams, RemoveMembersWarningPrompt, RenamedRoomActionParams, RenamedWorkspaceNameActionParams, @@ -89,6 +106,7 @@ import type { RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, + RequiredFieldParams, ResolutionConstraintsParams, RoomNameReservedErrorParams, RoomRenamedToParams, @@ -109,6 +127,7 @@ import type { SubscriptionSettingsRenewsOnParams, SubscriptionSettingsSaveUpToParams, SubscriptionSizeParams, + SyncStageNameConnectionsParams, TaskCreatedActionParams, TaxAmountParams, TermsParams, @@ -119,10 +138,12 @@ import type { TranslationBase, TrialStartedTitleParams, UnapprovedParams, + UnapproveWithIntegrationWarningParams, UnshareParams, UntilTimeParams, UpdatedTheDistanceParams, UpdatedTheRequestParams, + UpdateRoleParams, UsePlusButtonParams, UserIsAlreadyMemberParams, UserSplitParams, @@ -713,7 +734,7 @@ const translations = { splitBill: 'Dividir gasto', splitScan: 'Dividir recibo', splitDistance: 'Dividir distancia', - paySomeone: (name: string) => `Pagar a ${name ?? 'alguien'}`, + paySomeone: ({name}: PaySomeoneParams) => `Pagar a ${name ?? 'alguien'}`, assignTask: 'Assignar tarea', header: 'Acción rápida', trackManual: 'Crear gasto', @@ -756,7 +777,7 @@ const translations = { pendingMatchWithCreditCardDescription: 'Recibo pendiente de adjuntar con la transacción de la tarjeta. Márcalo como efectivo para cancelar.', markAsCash: 'Marcar como efectivo', routePending: 'Ruta pendiente...', - receiptIssuesFound: (count: number) => `${count === 1 ? 'Problema encontrado' : 'Problemas encontrados'}`, + receiptIssuesFound: ({count}: DistanceRateOperationsParams) => `${count === 1 ? 'Problema encontrado' : 'Problemas encontrados'}`, fieldPending: 'Pendiente...', receiptScanning: 'Escaneando recibo...', receiptScanInProgress: 'Escaneado de recibo en proceso', @@ -811,7 +832,7 @@ const translations = { payerOwes: ({payer}: PayerOwesParams) => `${payer} debe: `, payerPaidAmount: ({payer, amount}: PayerPaidAmountParams) => `${payer ? `${payer} ` : ''}pagó ${amount}`, payerPaid: ({payer}: PayerPaidParams) => `${payer} pagó: `, - payerSpentAmount: ({payer, amount}: PayerPaidAmountParams): string => `${payer} gastó ${amount}`, + payerSpentAmount: ({payer, amount}: PayerPaidAmountParams) => `${payer} gastó ${amount}`, payerSpent: ({payer}: PayerPaidParams) => `${payer} gastó: `, managerApproved: ({manager}: ManagerApprovedParams) => `${manager} aprobó:`, managerApprovedAmount: ({manager, amount}: ManagerApprovedAmountParams) => `${manager} aprobó ${amount}`, @@ -916,7 +937,7 @@ const translations = { unapprove: 'Desaprobar', unapproveReport: 'Anular la aprobación del informe', headsUp: 'Atención!', - unapproveWithIntegrationWarning: (accountingIntegration: string) => + unapproveWithIntegrationWarning: ({accountingIntegration}: UnapproveWithIntegrationWarningParams) => `Este informe ya se ha exportado a ${accountingIntegration}. Los cambios realizados en este informe en Expensify pueden provocar discrepancias en los datos y problemas de conciliación de la tarjeta Expensify. ¿Está seguro de que desea anular la aprobación de este informe?`, reimbursable: 'reembolsable', nonReimbursable: 'no reembolsable', @@ -1302,15 +1323,15 @@ const translations = { availableSpend: 'Límite restante', smartLimit: { name: 'Límite inteligente', - title: (formattedLimit: string) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta al mes. El límite se restablecerá el primer día del mes.`, + title: ({formattedLimit}: ViolationsOverLimitParams) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta al mes. El límite se restablecerá el primer día del mes.`, }, fixedLimit: { name: 'Límite fijo', - title: (formattedLimit: string) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta, luego se desactivará.`, + title: ({formattedLimit}: ViolationsOverLimitParams) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta, luego se desactivará.`, }, monthlyLimit: { name: 'Límite mensual', - title: (formattedLimit: string) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta y el límite se restablecerá a medida que se aprueben tus gastos.`, + title: ({formattedLimit}: ViolationsOverLimitParams) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta y el límite se restablecerá a medida que se aprueben tus gastos.`, }, virtualCardNumber: 'Número de la tarjeta virtual', physicalCardNumber: 'Número de la tarjeta física', @@ -1521,7 +1542,7 @@ const translations = { groupMembersListTitle: 'Directorio de los miembros del grupo.', lastMemberTitle: '¡Atención!', lastMemberWarning: 'Ya que eres la última persona aquí, si te vas, este chat quedará inaccesible para todos los miembros. ¿Estás seguro de que quieres salir del chat?', - defaultReportName: ({displayName}: {displayName: string}) => `Chat de grupo de ${displayName}`, + defaultReportName: ({displayName}: ReportArchiveReasonsClosedParams) => `Chat de grupo de ${displayName}`, }, languagePage: { language: 'Idioma', @@ -1648,7 +1669,7 @@ const translations = { error: { dateShouldBeBefore: ({dateString}: DateShouldBeBeforeParams) => `La fecha debe ser anterior a ${dateString}.`, dateShouldBeAfter: ({dateString}: DateShouldBeAfterParams) => `La fecha debe ser posterior a ${dateString}.`, - incorrectZipFormat: (zipFormat?: string) => `Formato de código postal incorrecto.${zipFormat ? ` Formato aceptable: ${zipFormat}` : ''}`, + incorrectZipFormat: ({zipFormat}: IncorrectZipFormatParams) => `Formato de código postal incorrecto.${zipFormat ? ` Formato aceptable: ${zipFormat}` : ''}`, hasInvalidCharacter: 'El nombre sólo puede incluir caracteres latinos.', }, }, @@ -2221,7 +2242,7 @@ const translations = { `¡Has sido invitado a ${workspaceName}! Descargue la aplicación móvil Expensify en use.expensify.com/download para comenzar a rastrear sus gastos.`, subscription: 'Suscripción', markAsExported: 'Marcar como introducido manualmente', - exportIntegrationSelected: (connectionName: ConnectionName) => `Exportar a ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]}`, + exportIntegrationSelected: ({connectionName}: ExportIntegrationSelectedParams) => `Exportar a ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]}`, letsDoubleCheck: 'Verifiquemos que todo esté correcto', reportField: 'Campo del informe', lineItemLevel: 'Nivel de partida', @@ -2460,7 +2481,7 @@ const translations = { }, creditCardAccount: 'Cuenta de tarjeta de crédito', defaultVendor: 'Proveedor por defecto', - defaultVendorDescription: (isReimbursable: boolean): string => + defaultVendorDescription: ({isReimbursable}: DefaultVendorDescriptionParams) => `Establezca un proveedor predeterminado que se aplicará a los gastos ${isReimbursable ? '' : 'no '}reembolsables que no tienen un proveedor coincidente en Sage Intacct.`, exportDescription: 'Configure cómo se exportan los datos de Expensify a Sage Intacct.', exportPreferredExporterNote: @@ -2678,12 +2699,12 @@ const translations = { importJobs: 'Importar proyectos', customers: 'clientes', jobs: 'proyectos', - label: (importFields: string[], importType: string) => `${importFields.join(' y ')}, ${importType}`, + label: ({importFields, importType}: CustomersOrJobsLabelParams) => `${importFields.join(' y ')}, ${importType}`, }, importTaxDescription: 'Importar grupos de impuestos desde NetSuite.', importCustomFields: { chooseOptionBelow: 'Elija una de las opciones siguientes:', - requiredFieldError: (fieldName: string) => `Por favor, introduzca el ${fieldName}`, + requiredFieldError: ({fieldName}: RequiredFieldParams) => `Por favor, introduzca el ${fieldName}`, customSegments: { title: 'Segmentos/registros personalizados', addText: 'Añadir segmento/registro personalizado', @@ -2724,7 +2745,7 @@ const translations = { customRecordMappingTitle: '¿Cómo debería mostrarse este registro de segmento personalizado en Expensify?', }, errors: { - uniqueFieldError: (fieldName: string) => `Ya existe un segmento/registro personalizado con este ${fieldName?.toLowerCase()}.`, + uniqueFieldError: ({fieldName}: RequiredFieldParams) => `Ya existe un segmento/registro personalizado con este ${fieldName?.toLowerCase()}.`, }, }, customLists: { @@ -2758,18 +2779,18 @@ const translations = { [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { label: 'Predeterminado del empleado NetSuite', description: 'No importado a Expensify, aplicado en exportación', - footerContent: (importField: string) => + footerContent: ({importField}: ImportFieldParams) => `Si usa ${importField} en NetSuite, aplicaremos el conjunto predeterminado en el registro del empleado al exportarlo a Informe de gastos o Entrada de diario.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { label: 'Etiquetas', description: 'Nivel de línea de pedido', - footerContent: (importField: string) => `Se podrán seleccionar ${importField} para cada gasto individual en el informe de un empleado.`, + footerContent: ({importField}: ImportFieldParams) => `Se podrán seleccionar ${importField} para cada gasto individual en el informe de un empleado.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { label: 'Campos de informe', description: 'Nivel de informe', - footerContent: (importField: string) => `La selección de ${importField} se aplicará a todos los gastos en el informe de un empleado.`, + footerContent: ({importField}: ImportFieldParams) => `La selección de ${importField} se aplicará a todos los gastos en el informe de un empleado.`, }, }, }, @@ -2800,8 +2821,8 @@ const translations = { addAUserDefinedDimension: 'Añadir una dimensión definida por el usuario', detailedInstructionsLink: 'Ver instrucciones detalladas', detailedInstructionsRestOfSentence: ' para añadir dimensiones definidas por el usuario.', - userDimensionsAdded: (dimensionsCount: number) => `${dimensionsCount} ${Str.pluralize('UDD', `UDDs`, dimensionsCount)} añadido`, - mappingTitle: (mappingName: SageIntacctMappingName): string => { + userDimensionsAdded: ({dimensionsCount}: DimensionsCountParams) => `${dimensionsCount} ${Str.pluralize('UDD', `UDDs`, dimensionsCount)} añadido`, + mappingTitle: ({mappingName}: IntacctMappingTitleParams) => { switch (mappingName) { case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS: return 'departamentos'; @@ -2872,21 +2893,21 @@ const translations = { deactivate: 'Desactivar tarjeta', changeCardLimit: 'Modificar el límite de la tarjeta', changeLimit: 'Modificar límite', - smartLimitWarning: (limit: string) => + smartLimitWarning: ({limit}: CharacterLimitParams) => `Si cambias el límite de esta tarjeta a ${limit}, las nuevas transacciones serán rechazadas hasta que apruebes antiguos gastos de la tarjeta.`, - monthlyLimitWarning: (limit: string) => `Si cambias el límite de esta tarjeta a ${limit}, las nuevas transacciones serán rechazadas hasta el próximo mes.`, - fixedLimitWarning: (limit: string) => `Si cambias el límite de esta tarjeta a ${limit}, se rechazarán las nuevas transacciones.`, + monthlyLimitWarning: ({limit}: CharacterLimitParams) => `Si cambias el límite de esta tarjeta a ${limit}, las nuevas transacciones serán rechazadas hasta el próximo mes.`, + fixedLimitWarning: ({limit}: CharacterLimitParams) => `Si cambias el límite de esta tarjeta a ${limit}, se rechazarán las nuevas transacciones.`, changeCardLimitType: 'Modificar el tipo de límite de la tarjeta', changeLimitType: 'Modificar el tipo de límite', - changeCardSmartLimitTypeWarning: (limit: string) => + changeCardSmartLimitTypeWarning: ({limit}: CharacterLimitParams) => `Si cambias el tipo de límite de esta tarjeta a Límite inteligente, las nuevas transacciones serán rechazadas porque ya se ha alcanzado el límite de ${limit} no aprobado.`, - changeCardMonthlyLimitTypeWarning: (limit: string) => + changeCardMonthlyLimitTypeWarning: ({limit}: CharacterLimitParams) => `Si cambias el tipo de límite de esta tarjeta a Mensual, las nuevas transacciones serán rechazadas porque ya se ha alcanzado el límite de ${limit} mensual.`, addMailingAddress: 'Añadir dirección de postal', - issuedCard: (assignee: string) => `¡emitió a ${assignee} una Tarjeta Expensify! La tarjeta llegará en 2-3 días laborables.`, - issuedCardNoMailingAddress: (assignee: string) => `¡emitió a ${assignee} una Tarjeta Expensify! La tarjeta se enviará una vez que se añada una dirección postal.`, + issuedCard: ({assignee}: AssigneeParams) => `¡emitió a ${assignee} una Tarjeta Expensify! La tarjeta llegará en 2-3 días laborables.`, + issuedCardNoMailingAddress: ({assignee}: AssigneeParams) => `¡emitió a ${assignee} una Tarjeta Expensify! La tarjeta se enviará una vez que se añada una dirección postal.`, issuedCardVirtual: ({assignee, link}: IssueVirtualCardParams) => `¡emitió a ${assignee} una ${link} virtual! La tarjeta puede utilizarse inmediatamente.`, - addedAddress: (assignee: string) => `${assignee} ha añadido la dirección. Tarjeta Expensify llegará en 2-3 días hábiles.`, + addedAddress: ({assignee}: AssigneeParams) => `${assignee} ha añadido la dirección. Tarjeta Expensify llegará en 2-3 días hábiles.`, }, categories: { deleteCategories: 'Eliminar categorías', @@ -3146,7 +3167,7 @@ const translations = { removeMembersTitle: 'Eliminar miembros', removeMemberButtonTitle: 'Quitar del espacio de trabajo', removeMemberGroupButtonTitle: 'Quitar del grupo', - removeMemberPrompt: ({memberName}: {memberName: string}) => `¿Estás seguro de que deseas eliminar a ${memberName}?`, + removeMemberPrompt: ({memberName}: RemoveMemberPromptParams) => `¿Estás seguro de que deseas eliminar a ${memberName}?`, removeMemberTitle: 'Eliminar miembro', transferOwner: 'Transferir la propiedad', makeMember: 'Hacer miembro', @@ -3169,8 +3190,8 @@ const translations = { xero: 'Xero', netsuite: 'NetSuite', intacct: 'Sage Intacct', - connectionName: (integration: ConnectionName) => { - switch (integration) { + connectionName: ({connectionName}: ConnectionNameParams) => { + switch (connectionName) { case CONST.POLICY.CONNECTIONS.NAME.QBO: return 'Quickbooks Online'; case CONST.POLICY.CONNECTIONS.NAME.XERO: @@ -3185,20 +3206,21 @@ const translations = { } }, setup: 'Configurar', - lastSync: (relativeDate: string) => `Recién sincronizado ${relativeDate}`, + lastSync: ({relativeDate}: LastSyncAccountingParams) => `Recién sincronizado ${relativeDate}`, import: 'Importar', export: 'Exportar', advanced: 'Avanzado', other: 'Otras integraciones', syncNow: 'Sincronizar ahora', disconnect: 'Desconectar', - disconnectTitle: (integration?: ConnectionName): string => { - const integrationName = integration && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integration] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integration] : 'integración'; + disconnectTitle: ({connectionName}: Partial) => { + const integrationName = + connectionName && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] : 'integración'; return `Desconectar ${integrationName}`; }, - connectTitle: (integrationToConnect: ConnectionName): string => `Conectar ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] ?? 'accounting integration'}`, - syncError: (integration?: ConnectionName): string => { - switch (integration) { + connectTitle: ({connectionName}: ConnectionNameParams) => `Conectar ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'accounting integration'}`, + syncError: ({connectionName}: Partial) => { + switch (connectionName) { case CONST.POLICY.CONNECTIONS.NAME.QBO: return 'No se puede conectar a QuickBooks Online.'; case CONST.POLICY.CONNECTIONS.NAME.XERO: @@ -3224,18 +3246,18 @@ const translations = { [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: 'Importado como campos de informe', [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: 'Predeterminado del empleado NetSuite', }, - disconnectPrompt: (currentIntegration?: ConnectionName): string => { + disconnectPrompt: ({connectionName}: Partial) => { const integrationName = - currentIntegration && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[currentIntegration] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[currentIntegration] : 'integración'; + connectionName && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] : 'integración'; return `¿Estás seguro de que quieres desconectar ${integrationName}?`; }, - connectPrompt: (integrationToConnect: ConnectionName): string => + connectPrompt: ({connectionName}: ConnectionNameParams) => `¿Estás seguro de que quieres conectar a ${ - CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] ?? 'esta integración contable' + CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'esta integración contable' }? Esto eliminará cualquier conexión contable existente.`, enterCredentials: 'Ingresa tus credenciales', connections: { - syncStageName: (stage: PolicyConnectionSyncStage) => { + syncStageName: ({stage}: SyncStageNameConnectionsParams) => { switch (stage) { case 'quickbooksOnlineImportCustomers': return 'Importando clientes'; @@ -3368,7 +3390,7 @@ const translations = { chooseBankAccount: 'Elige la cuenta bancaria con la que se conciliarán los pagos de tu Tarjeta Expensify.', accountMatches: 'Asegúrate de que esta cuenta coincide con ', settlementAccount: 'la cuenta de liquidación de tu Tarjeta Expensify ', - reconciliationWorks: (lastFourPAN: string) => `(que termina en ${lastFourPAN}) para que la conciliación continua funcione correctamente.`, + reconciliationWorks: ({lastFourPAN}: ReconciliationWorksParams) => `(que termina en ${lastFourPAN}) para que la conciliación continua funcione correctamente.`, }, }, card: { @@ -3613,7 +3635,7 @@ const translations = { exportAgainModal: { title: '¡Cuidado!', - description: (reportName: string, connectionName: ConnectionName) => + description: ({reportName, connectionName}: ExportAgainModalDescriptionParams) => `Los siguientes informes ya se han exportado a ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]}:\n\n${reportName}\n\n¿Estás seguro de que deseas exportarlos de nuevo?`, confirmText: 'Sí, exportar de nuevo', cancelText: 'Cancelar', @@ -3676,7 +3698,7 @@ const translations = { upgradeToUnlock: 'Desbloquear esta función', completed: { headline: 'Has mejorado tu espacio de trabajo.', - successMessage: (policyName: string) => `Ha mejorado correctamente su espacio de trabajo ${policyName} al plan Control.`, + successMessage: ({policyName}: ReportPolicyNameParams) => `Ha mejorado correctamente su espacio de trabajo ${policyName} al plan Control.`, viewSubscription: 'Ver su suscripción', moreDetails: 'para obtener más información.', gotIt: 'Entendido, gracias.', @@ -3706,7 +3728,7 @@ const translations = { maxAge: 'Antigüedad máxima', maxExpenseAge: 'Antigüedad máxima de los gastos', maxExpenseAgeDescription: 'Marca los gastos de más de un número determinado de días.', - maxExpenseAgeDays: (age: number) => `${age} ${Str.pluralize('día', 'días', age)}`, + maxExpenseAgeDays: ({age}: AgeParams) => `${age} ${Str.pluralize('día', 'días', age)}`, billableDefault: 'Valor predeterminado facturable', billableDefaultDescription: 'Elige si los gastos en efectivo y con tarjeta de crédito deben ser facturables por defecto. Los gastos facturables se activan o desactivan en', billable: 'Facturable', @@ -3860,17 +3882,17 @@ const translations = { filtersHeader: 'Filtros', filters: { date: { - before: (date?: string) => `Antes de ${date ?? ''}`, - after: (date?: string) => `Después de ${date ?? ''}`, + before: ({date}: Partial) => `Antes de ${date ?? ''}`, + after: ({date}: Partial) => `Después de ${date ?? ''}`, }, status: 'Estado', keyword: 'Palabra clave', hasKeywords: 'Tiene palabras clave', currency: 'Divisa', amount: { - lessThan: (amount?: string) => `Menos de ${amount ?? ''}`, - greaterThan: (amount?: string) => `Más que ${amount ?? ''}`, - between: (greaterThan: string, lessThan: string) => `Entre ${greaterThan} y ${lessThan}`, + lessThan: ({amount}: Partial) => `Menos de ${amount ?? ''}`, + greaterThan: ({amount}: Partial) => `Más que ${amount ?? ''}`, + between: ({greaterThan, lessThan}: FiltersAmountBetweenParams) => `Entre ${greaterThan} y ${lessThan}`, }, }, expenseType: 'Tipo de gasto', @@ -3989,7 +4011,7 @@ const translations = { nonReimbursableLink: 'Ver los gastos de la tarjeta de empresa.', pending: ({label}: ExportedToIntegrationParams) => `comenzó a exportar este informe a ${label}...`, }, - integrationsMessage: (errorMessage: string, label: string) => `no se pudo exportar este informe a ${label} ("${errorMessage}").`, + integrationsMessage: ({label, errorMessage}: IntegrationSyncFailedParams) => `no se pudo exportar este informe a ${label} ("${errorMessage}").`, managerAttachReceipt: `agregó un recibo`, managerDetachReceipt: `quitó un recibo`, markedReimbursed: ({amount, currency}: MarkedReimbursedParams) => `pagó ${currency}${amount} en otro lugar`, @@ -4006,11 +4028,11 @@ const translations = { stripePaid: ({amount, currency}: StripePaidParams) => `pagado ${currency}${amount}`, takeControl: `tomó el control`, unapproved: ({amount, currency}: UnapprovedParams) => `no aprobado ${currency}${amount}`, - integrationSyncFailed: (label: string, errorMessage: string) => `no se pudo sincronizar con ${label} ("${errorMessage}")`, - addEmployee: (email: string, role: string) => `agregó a ${email} como ${role === 'user' ? 'miembro' : 'administrador'}`, - updateRole: (email: string, currentRole: string, newRole: string) => + integrationSyncFailed: ({label, errorMessage}: IntegrationSyncFailedParams) => `no se pudo sincronizar con ${label} ("${errorMessage}")`, + addEmployee: ({email, role}: AddEmployeeParams) => `agregó a ${email} como ${role === 'user' ? 'miembro' : 'administrador'}`, + updateRole: ({email, currentRole, newRole}: UpdateRoleParams) => `actualicé el rol ${email} de ${currentRole === 'user' ? 'miembro' : 'administrador'} a ${newRole === 'user' ? 'miembro' : 'administrador'}`, - removeMember: (email: string, role: string) => `eliminado ${role === 'user' ? 'miembro' : 'administrador'} ${email}`, + removeMember: ({email, role}: AddEmployeeParams) => `eliminado ${role === 'user' ? 'miembro' : 'administrador'} ${email}`, }, }, }, @@ -4773,7 +4795,7 @@ const translations = { hold: 'Bloqueado', }, reportViolations: { - [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: (fieldName: string) => `${fieldName} es obligatorio`, + [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `${fieldName} es obligatorio`, }, violationDismissal: { rter: { @@ -5037,7 +5059,7 @@ const translations = { }, delegate: { switchAccount: 'Cambiar de cuenta:', - role: (role: DelegateRole): string => { + role: ({role}: DelegateRoleParams) => { switch (role) { case CONST.DELEGATE_ROLE.ALL: return 'Completo'; diff --git a/src/languages/translations.ts b/src/languages/translations.ts index 02b7303f0b44..615a49e8accc 100644 --- a/src/languages/translations.ts +++ b/src/languages/translations.ts @@ -15,7 +15,7 @@ import type {TranslationBase, TranslationFlatObject} from './types'; export function flattenObject(obj: TranslationBase): TranslationFlatObject { const result: Record = {}; - const recursive = (data: TranslationBase, key: string): void => { + const recursive = (data: TranslationBase, key: string): void => { // If the data is a function or not a object (eg. a string or array), // it's the final value for the key being built and there is no need // for more recursion @@ -27,7 +27,7 @@ export function flattenObject(obj: TranslationBase): Translati // Recursive call to the keys and connect to the respective data Object.keys(data).forEach((k) => { isEmpty = false; - recursive(data[k] as TranslationBase, key ? `${key}.${k}` : k); + recursive(data[k] as TranslationBase, key ? `${key}.${k}` : k); }); // Check for when the object is empty but a key exists, so that diff --git a/src/languages/types.ts b/src/languages/types.ts index a7d56c42fc95..090d1ff35210 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -1,5 +1,6 @@ import type {OnyxInputOrEntry, ReportAction} from '@src/types/onyx'; -import type {ConnectionName, Unit} from '@src/types/onyx/Policy'; +import type {DelegateRole} from '@src/types/onyx/Account'; +import type {ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName, Unit} from '@src/types/onyx/Policy'; import type {ViolationDataType} from '@src/types/onyx/TransactionViolation'; import type en from './en'; @@ -11,6 +12,10 @@ type CharacterLimitParams = { limit: number; }; +type AssigneeParams = { + assignee: string; +}; + type CharacterLengthLimitParams = { limit: number; length: number; @@ -361,11 +366,16 @@ type UnshareParams = {to: string}; type StripePaidParams = {amount: string; currency: string}; type UnapprovedParams = {amount: string; currency: string}; + type RemoveMembersWarningPrompt = { memberName: string; ownerName: string; }; +type RemoveMemberPromptParams = { + memberName: string; +}; + type DeleteExpenseTranslationParams = { count: number; }; @@ -407,9 +417,7 @@ type UpdateRoleParams = {email: string; currentRole: string; newRole: string}; type RemoveMemberParams = {email: string; role: string}; -type DateParams = {date?: string}; - -type AmountParams = {amount?: string}; +type DateParams = {date: string}; type FiltersAmountBetweenParams = {greaterThan: string; lessThan: string}; @@ -479,7 +487,47 @@ type SubscriptionSettingsSaveUpToParams = {amountWithCurrency: string}; type SubscriptionSettingsRenewsOnParams = {date: string}; +type UnapproveWithIntegrationWarningParams = {accountingIntegration: string}; + +type IncorrectZipFormatParams = {zipFormat?: string}; + +type ExportIntegrationSelectedParams = {connectionName: ConnectionName}; + +type DefaultVendorDescriptionParams = {isReimbursable: boolean}; + +type RequiredFieldParams = {fieldName: string}; + +type ImportFieldParams = {importField: string}; + +type DimensionsCountParams = {dimensionsCount: number}; + +type IntacctMappingTitleParams = {mappingName: SageIntacctMappingName}; + +type AgeParams = {age: number}; + +type LastSyncAccountingParams = {relativeDate: string}; + +type SyncStageNameConnectionsParams = {stage: PolicyConnectionSyncStage}; + +type ReconciliationWorksParams = {lastFourPAN: string}; + +type DelegateRoleParams = {role: DelegateRole}; + export type { + DelegateRoleParams, + ReconciliationWorksParams, + LastSyncAccountingParams, + SyncStageNameConnectionsParams, + AgeParams, + RequiredFieldParams, + DimensionsCountParams, + IntacctMappingTitleParams, + ImportFieldParams, + AssigneeParams, + DefaultVendorDescriptionParams, + ExportIntegrationSelectedParams, + UnapproveWithIntegrationWarningParams, + IncorrectZipFormatParams, CardNextPaymentParams, CardEndingParams, CardInfoParams, @@ -496,6 +544,7 @@ export type { BillingBannerCardExpiredParams, BillingBannerCardOnDisputeParams, TrialStartedTitleParams, + RemoveMemberPromptParams, StatementTitleParams, RenamedWorkspaceNameActionParams, WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams, @@ -643,7 +692,6 @@ export type { UpdateRoleParams, RemoveMemberParams, DateParams, - AmountParams, FiltersAmountBetweenParams, StatementPageTitleParams, DisconnectPromptParams, From d0bf65b08a0cd7e04b8026ce5046dad3d379dfa8 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Thu, 29 Aug 2024 15:08:52 +0200 Subject: [PATCH 005/184] update translations PART-3 --- src/components/AccountSwitcher.tsx | 2 +- .../AccountingConnectionConfirmationModal.tsx | 4 +- src/components/AddressForm.tsx | 2 +- src/components/ReceiptAudit.tsx | 2 +- .../ExportWithDropdownMenu.tsx | 4 +- .../ReportActionItem/IssueCardMessage.tsx | 4 +- src/languages/en.ts | 29 +++++++------ src/languages/es.ts | 33 +++++++------- src/languages/translations.ts | 1 + src/languages/types.ts | 43 ++++++++++--------- src/libs/PolicyUtils.ts | 5 ++- src/libs/ReportActionsUtils.ts | 8 ++-- src/libs/ReportUtils.ts | 2 +- src/libs/Violations/ViolationsUtils.ts | 2 +- src/libs/WorkspacesSettingsUtils.ts | 6 +-- src/libs/actions/connections/index.ts | 2 +- src/pages/ReportDetailsPage.tsx | 5 ++- .../WorkspaceAdminRestrictedAction.tsx | 4 +- .../WorkspaceUserRestrictedAction.tsx | 2 +- src/pages/Search/AdvancedSearchFilters.tsx | 13 +++--- .../report/ContextMenu/ContextMenuActions.tsx | 2 +- src/pages/home/report/ReportActionItem.tsx | 2 +- .../home/report/ReportDetailsExportPage.tsx | 4 +- .../FloatingActionButtonAndPopover.tsx | 2 +- .../Subscription/CardSection/CardSection.tsx | 4 +- .../Subscription/CardSection/utils.ts | 4 +- .../settings/Wallet/ExpensifyCardPage.tsx | 2 +- src/pages/wallet/WalletStatementPage.tsx | 2 +- .../accounting/PolicyAccountingPage.tsx | 8 ++-- .../export/SageIntacctDefaultVendorPage.tsx | 2 +- ...SageIntacctNonReimbursableExpensesPage.tsx | 2 +- .../SageIntacctReimbursableExpensesPage.tsx | 2 +- .../intacct/import/SageIntacctImportPage.tsx | 4 +- .../import/SageIntacctToggleMappingsPage.tsx | 6 +-- .../import/NetSuiteImportCustomFieldEdit.tsx | 4 +- .../NetSuiteImportAddCustomListPage.tsx | 2 +- .../NetSuiteImportAddCustomSegmentPage.tsx | 17 ++++---- .../import/NetSuiteImportMappingPage.tsx | 2 +- .../ReconciliationAccountSettingsPage.tsx | 2 +- .../WorkspaceEditCardLimitPage.tsx | 2 +- .../WorkspaceEditCardLimitTypePage.tsx | 2 +- .../rules/IndividualExpenseRulesSection.tsx | 2 +- .../workspace/upgrade/UpgradeConfirmation.tsx | 2 +- 43 files changed, 133 insertions(+), 121 deletions(-) diff --git a/src/components/AccountSwitcher.tsx b/src/components/AccountSwitcher.tsx index ba30ea0062b9..08bb1862c393 100644 --- a/src/components/AccountSwitcher.tsx +++ b/src/components/AccountSwitcher.tsx @@ -102,7 +102,7 @@ function AccountSwitcher() { .map(({email, role, error}, index) => { const personalDetails = PersonalDetailsUtils.getPersonalDetailByEmail(email); return createBaseMenuItem(personalDetails, error, { - badgeText: translate('delegate.role', role), + badgeText: translate('delegate.role', {role}), onPress: () => { if (isOffline) { Modal.close(() => setShouldShowOfflineModal(true)); diff --git a/src/components/AccountingConnectionConfirmationModal.tsx b/src/components/AccountingConnectionConfirmationModal.tsx index c472f215b6df..bfacd8c0bf76 100644 --- a/src/components/AccountingConnectionConfirmationModal.tsx +++ b/src/components/AccountingConnectionConfirmationModal.tsx @@ -14,11 +14,11 @@ function AccountingConnectionConfirmationModal({integrationToConnect, onCancel, return ( 0 && shouldShowAuditResult) { - auditText = translate('iou.receiptIssuesFound', notes.length); + auditText = translate('iou.receiptIssuesFound', {count: notes.length}); } else if (!notes.length && shouldShowAuditResult) { auditText = translate('common.verified'); } diff --git a/src/components/ReportActionItem/ExportWithDropdownMenu.tsx b/src/components/ReportActionItem/ExportWithDropdownMenu.tsx index 6d73035fd8bb..4498c6599d27 100644 --- a/src/components/ReportActionItem/ExportWithDropdownMenu.tsx +++ b/src/components/ReportActionItem/ExportWithDropdownMenu.tsx @@ -59,7 +59,7 @@ function ExportWithDropdownMenu({ const options = [ { value: CONST.REPORT.EXPORT_OPTIONS.EXPORT_TO_INTEGRATION, - text: translate('workspace.common.exportIntegrationSelected', connectionName), + text: translate('workspace.common.exportIntegrationSelected', {connectionName}), ...optionTemplate, }, { @@ -126,7 +126,7 @@ function ExportWithDropdownMenu({ title={translate('workspace.exportAgainModal.title')} onConfirm={confirmExport} onCancel={() => setModalStatus(null)} - prompt={translate('workspace.exportAgainModal.description', report?.reportName ?? '', connectionName)} + prompt={translate('workspace.exportAgainModal.description', {connectionName, reportName: report?.reportName ?? ''})} confirmText={translate('workspace.exportAgainModal.confirmText')} cancelText={translate('workspace.exportAgainModal.cancelText')} isVisible={!!modalStatus} diff --git a/src/components/ReportActionItem/IssueCardMessage.tsx b/src/components/ReportActionItem/IssueCardMessage.tsx index 292b010cd851..7238810b0bcf 100644 --- a/src/components/ReportActionItem/IssueCardMessage.tsx +++ b/src/components/ReportActionItem/IssueCardMessage.tsx @@ -35,11 +35,11 @@ function IssueCardMessage({action}: IssueCardMessageProps) { const getTranslation = () => { switch (action?.actionName) { case CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED: - return translate('workspace.expensifyCard.issuedCard', assignee); + return translate('workspace.expensifyCard.issuedCard', {assignee}); case CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL: return translate('workspace.expensifyCard.issuedCardVirtual', {assignee, link}); case CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS: - return translate(`workspace.expensifyCard.${noMailingAddress ? 'issuedCardNoMailingAddress' : 'addedAddress'}`, assignee); + return translate(`workspace.expensifyCard.${noMailingAddress ? 'issuedCardNoMailingAddress' : 'addedAddress'}`, {assignee}); default: return ''; } diff --git a/src/languages/en.ts b/src/languages/en.ts index 0973e3038813..55bd0c06a02a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -83,6 +83,7 @@ import type { NotYouParams, OOOEventSummaryFullDayParams, OOOEventSummaryPartialDayParams, + OptionalParam, OurEmailProviderParams, OwnerOwesAmountParams, PaidElsewhereWithAmountParams, @@ -741,7 +742,7 @@ const translations = { splitBill: 'Split expense', splitScan: 'Split receipt', splitDistance: 'Split distance', - paySomeone: ({name}: PaySomeoneParams) => `Pay ${name ?? 'someone'}`, + paySomeone: ({name}: PaySomeoneParams = {}) => `Pay ${name ?? 'someone'}`, assignTask: 'Assign task', header: 'Quick action', trackManual: 'Track expense', @@ -765,7 +766,7 @@ const translations = { original: 'Original', split: 'Split', splitExpense: 'Split expense', - paySomeone: ({name}: PaySomeoneParams) => `Pay ${name ?? 'someone'}`, + paySomeone: ({name}: PaySomeoneParams = {}) => `Pay ${name ?? 'someone'}`, expense: 'Expense', categorize: 'Categorize', share: 'Share', @@ -1662,7 +1663,7 @@ const translations = { dateShouldBeBefore: ({dateString}: DateShouldBeBeforeParams) => `Date should be before ${dateString}.`, dateShouldBeAfter: ({dateString}: DateShouldBeAfterParams) => `Date should be after ${dateString}.`, hasInvalidCharacter: 'Name can only include Latin characters.', - incorrectZipFormat: ({zipFormat}: IncorrectZipFormatParams) => `Incorrect zip code format.${zipFormat ? ` Acceptable format: ${zipFormat}` : ''}`, + incorrectZipFormat: ({zipFormat}: IncorrectZipFormatParams = {}) => `Incorrect zip code format.${zipFormat ? ` Acceptable format: ${zipFormat}` : ''}`, }, }, resendValidationForm: { @@ -3234,7 +3235,7 @@ const translations = { other: 'Other integrations', syncNow: 'Sync now', disconnect: 'Disconnect', - disconnectTitle: ({connectionName}: Partial) => { + disconnectTitle: ({connectionName}: OptionalParam = {}) => { const integrationName = connectionName && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] : 'integration'; return `Disconnect ${integrationName}`; @@ -3268,7 +3269,7 @@ const translations = { [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: 'Imported as report fields', [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: 'NetSuite employee default', }, - disconnectPrompt: ({connectionName}: Partial) => { + disconnectPrompt: ({connectionName}: OptionalParam = {}) => { const integrationName = connectionName && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] : 'this integration'; return `Are you sure you want to disconnect ${integrationName}?`; @@ -3832,16 +3833,16 @@ const translations = { filtersHeader: 'Filters', filters: { date: { - before: ({date}: Partial) => `Before ${date ?? ''}`, - after: ({date}: Partial) => `After ${date ?? ''}`, + before: ({date}: OptionalParam = {}) => `Before ${date ?? ''}`, + after: ({date}: OptionalParam = {}) => `After ${date ?? ''}`, }, status: 'Status', keyword: 'Keyword', hasKeywords: 'Has keywords', currency: 'Currency', amount: { - lessThan: ({amount}: Partial) => `Less than ${amount ?? ''}`, - greaterThan: ({amount}: Partial) => `Greater than ${amount ?? ''}`, + lessThan: ({amount}: OptionalParam = {}) => `Less than ${amount ?? ''}`, + greaterThan: ({amount}: OptionalParam = {}) => `Greater than ${amount ?? ''}`, between: ({greaterThan, lessThan}: FiltersAmountBetweenParams) => `Between ${greaterThan} and ${lessThan}`, }, }, @@ -4201,7 +4202,7 @@ const translations = { allTagLevelsRequired: 'All tags required', autoReportedRejectedExpense: ({rejectReason, rejectedBy}: ViolationsAutoReportedRejectedExpenseParams) => `${rejectedBy} rejected this expense with the comment "${rejectReason}"`, billableExpense: 'Billable no longer valid', - cashExpenseWithNoReceipt: ({formattedLimit}: ViolationsCashExpenseWithNoReceiptParams) => `Receipt required${formattedLimit ? ` over ${formattedLimit}` : ''}`, + cashExpenseWithNoReceipt: ({formattedLimit}: ViolationsCashExpenseWithNoReceiptParams = {}) => `Receipt required${formattedLimit ? ` over ${formattedLimit}` : ''}`, categoryOutOfPolicy: 'Category no longer valid', conversionSurcharge: ({surcharge}: ViolationsConversionSurchargeParams) => `Applied ${surcharge}% conversion surcharge`, customUnitOutOfPolicy: 'Unit no longer valid', @@ -4212,7 +4213,7 @@ const translations = { maxAge: ({maxAge}: ViolationsMaxAgeParams) => `Date older than ${maxAge} days`, missingCategory: 'Missing category', missingComment: 'Description required for selected category', - missingTag: ({tagName}: ViolationsMissingTagParams) => `Missing ${tagName ?? 'tag'}`, + missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `Missing ${tagName ?? 'tag'}`, modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => { switch (type) { case 'distance': @@ -4261,10 +4262,10 @@ const translations = { return ''; }, smartscanFailed: 'Receipt scanning failed. Enter details manually.', - someTagLevelsRequired: ({tagName}: ViolationsTagOutOfPolicyParams) => `Missing ${tagName ?? 'Tag'}`, - tagOutOfPolicy: ({tagName}: ViolationsTagOutOfPolicyParams) => `${tagName ?? 'Tag'} no longer valid`, + someTagLevelsRequired: ({tagName}: ViolationsTagOutOfPolicyParams = {}) => `Missing ${tagName ?? 'Tag'}`, + tagOutOfPolicy: ({tagName}: ViolationsTagOutOfPolicyParams = {}) => `${tagName ?? 'Tag'} no longer valid`, taxAmountChanged: 'Tax amount was modified', - taxOutOfPolicy: ({taxName}: ViolationsTaxOutOfPolicyParams) => `${taxName ?? 'Tax'} no longer valid`, + taxOutOfPolicy: ({taxName}: ViolationsTaxOutOfPolicyParams = {}) => `${taxName ?? 'Tax'} no longer valid`, taxRateChanged: 'Tax rate was modified', taxRequired: 'Missing tax rate', none: 'None', diff --git a/src/languages/es.ts b/src/languages/es.ts index 25df635ee01d..955b38e4d8fe 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -81,6 +81,7 @@ import type { NotYouParams, OOOEventSummaryFullDayParams, OOOEventSummaryPartialDayParams, + OptionalParam, OurEmailProviderParams, OwnerOwesAmountParams, PaidElsewhereWithAmountParams, @@ -734,7 +735,7 @@ const translations = { splitBill: 'Dividir gasto', splitScan: 'Dividir recibo', splitDistance: 'Dividir distancia', - paySomeone: ({name}: PaySomeoneParams) => `Pagar a ${name ?? 'alguien'}`, + paySomeone: ({name}: PaySomeoneParams = {}) => `Pagar a ${name ?? 'alguien'}`, assignTask: 'Assignar tarea', header: 'Acción rápida', trackManual: 'Crear gasto', @@ -763,7 +764,7 @@ const translations = { share: 'Compartir', participants: 'Participantes', submitExpense: 'Presentar gasto', - paySomeone: ({name}: PaySomeoneParams) => `Pagar a ${name ?? 'alguien'}`, + paySomeone: ({name}: PaySomeoneParams = {}) => `Pagar a ${name ?? 'alguien'}`, trackExpense: 'Seguimiento de gastos', pay: 'Pagar', cancelPayment: 'Cancelar el pago', @@ -1669,7 +1670,7 @@ const translations = { error: { dateShouldBeBefore: ({dateString}: DateShouldBeBeforeParams) => `La fecha debe ser anterior a ${dateString}.`, dateShouldBeAfter: ({dateString}: DateShouldBeAfterParams) => `La fecha debe ser posterior a ${dateString}.`, - incorrectZipFormat: ({zipFormat}: IncorrectZipFormatParams) => `Formato de código postal incorrecto.${zipFormat ? ` Formato aceptable: ${zipFormat}` : ''}`, + incorrectZipFormat: ({zipFormat}: IncorrectZipFormatParams = {}) => `Formato de código postal incorrecto.${zipFormat ? ` Formato aceptable: ${zipFormat}` : ''}`, hasInvalidCharacter: 'El nombre sólo puede incluir caracteres latinos.', }, }, @@ -3213,13 +3214,13 @@ const translations = { other: 'Otras integraciones', syncNow: 'Sincronizar ahora', disconnect: 'Desconectar', - disconnectTitle: ({connectionName}: Partial) => { + disconnectTitle: ({connectionName}: OptionalParam = {}) => { const integrationName = connectionName && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] : 'integración'; return `Desconectar ${integrationName}`; }, connectTitle: ({connectionName}: ConnectionNameParams) => `Conectar ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ?? 'accounting integration'}`, - syncError: ({connectionName}: Partial) => { + syncError: ({connectionName}: OptionalParam = {}) => { switch (connectionName) { case CONST.POLICY.CONNECTIONS.NAME.QBO: return 'No se puede conectar a QuickBooks Online.'; @@ -3246,7 +3247,7 @@ const translations = { [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: 'Importado como campos de informe', [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: 'Predeterminado del empleado NetSuite', }, - disconnectPrompt: ({connectionName}: Partial) => { + disconnectPrompt: ({connectionName}: OptionalParam = {}) => { const integrationName = connectionName && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName] : 'integración'; return `¿Estás seguro de que quieres desconectar ${integrationName}?`; @@ -3882,16 +3883,16 @@ const translations = { filtersHeader: 'Filtros', filters: { date: { - before: ({date}: Partial) => `Antes de ${date ?? ''}`, - after: ({date}: Partial) => `Después de ${date ?? ''}`, + before: ({date}: OptionalParam = {}) => `Antes de ${date ?? ''}`, + after: ({date}: OptionalParam = {}) => `Después de ${date ?? ''}`, }, status: 'Estado', keyword: 'Palabra clave', hasKeywords: 'Tiene palabras clave', currency: 'Divisa', amount: { - lessThan: ({amount}: Partial) => `Menos de ${amount ?? ''}`, - greaterThan: ({amount}: Partial) => `Más que ${amount ?? ''}`, + lessThan: ({amount}: OptionalParam = {}) => `Menos de ${amount ?? ''}`, + greaterThan: ({amount}: OptionalParam = {}) => `Más que ${amount ?? ''}`, between: ({greaterThan, lessThan}: FiltersAmountBetweenParams) => `Entre ${greaterThan} y ${lessThan}`, }, }, @@ -4713,9 +4714,9 @@ const translations = { allTagLevelsRequired: 'Todas las etiquetas son obligatorias', autoReportedRejectedExpense: ({rejectedBy, rejectReason}: ViolationsAutoReportedRejectedExpenseParams) => `${rejectedBy} rechazó la solicitud y comentó "${rejectReason}"`, billableExpense: 'La opción facturable ya no es válida', - cashExpenseWithNoReceipt: ({formattedLimit}: ViolationsCashExpenseWithNoReceiptParams) => `Recibo obligatorio para cantidades mayores de ${formattedLimit}`, + cashExpenseWithNoReceipt: ({formattedLimit}: ViolationsCashExpenseWithNoReceiptParams = {}) => `Recibo obligatorio para cantidades mayores de ${formattedLimit}`, categoryOutOfPolicy: 'La categoría ya no es válida', - conversionSurcharge: ({surcharge}: ViolationsConversionSurchargeParams = {}) => `${surcharge}% de recargo aplicado`, + conversionSurcharge: ({surcharge}: ViolationsConversionSurchargeParams) => `${surcharge}% de recargo aplicado`, customUnitOutOfPolicy: 'La unidad ya no es válida', duplicatedTransaction: 'Duplicado', fieldRequired: 'Los campos del informe son obligatorios', @@ -4724,7 +4725,7 @@ const translations = { maxAge: ({maxAge}: ViolationsMaxAgeParams) => `Fecha de más de ${maxAge} días`, missingCategory: 'Falta categoría', missingComment: 'Descripción obligatoria para la categoría seleccionada', - missingTag: ({tagName}: ViolationsMissingTagParams) => `Falta ${tagName ?? 'etiqueta'}`, + missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `Falta ${tagName ?? 'etiqueta'}`, modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => { switch (type) { case 'distance': @@ -4775,10 +4776,10 @@ const translations = { return ''; }, smartscanFailed: 'No se pudo escanear el recibo. Introduce los datos manualmente', - someTagLevelsRequired: ({tagName}: ViolationsTagOutOfPolicyParams) => `Falta ${tagName ?? 'Tag'}`, - tagOutOfPolicy: ({tagName}: ViolationsTagOutOfPolicyParams) => `La etiqueta ${tagName ? `${tagName} ` : ''}ya no es válida`, + someTagLevelsRequired: ({tagName}: ViolationsTagOutOfPolicyParams = {}) => `Falta ${tagName ?? 'Tag'}`, + tagOutOfPolicy: ({tagName}: ViolationsTagOutOfPolicyParams = {}) => `La etiqueta ${tagName ? `${tagName} ` : ''}ya no es válida`, taxAmountChanged: 'El importe del impuesto fue modificado', - taxOutOfPolicy: ({taxName}: ViolationsTaxOutOfPolicyParams) => `${taxName ?? 'El impuesto'} ya no es válido`, + taxOutOfPolicy: ({taxName}: ViolationsTaxOutOfPolicyParams = {}) => `${taxName ?? 'El impuesto'} ya no es válido`, taxRateChanged: 'La tasa de impuesto fue modificada', taxRequired: 'Falta la tasa de impuesto', none: 'Ninguno', diff --git a/src/languages/translations.ts b/src/languages/translations.ts index 615a49e8accc..402641d1f4f3 100644 --- a/src/languages/translations.ts +++ b/src/languages/translations.ts @@ -27,6 +27,7 @@ export function flattenObject(obj: TranslationBase): Translati // Recursive call to the keys and connect to the respective data Object.keys(data).forEach((k) => { isEmpty = false; + // @ts-expect-error - The key is a string since forEach is always iterating over the keys like strings recursive(data[k] as TranslationBase, key ? `${key}.${k}` : k); }); diff --git a/src/languages/types.ts b/src/languages/types.ts index 090d1ff35210..fb724a764eb2 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -9,7 +9,7 @@ type AddressLineParams = { }; type CharacterLimitParams = { - limit: number; + limit: number | string; }; type AssigneeParams = { @@ -225,25 +225,25 @@ type WalletProgramParams = {walletProgram: string}; type ViolationsAutoReportedRejectedExpenseParams = {rejectedBy: string; rejectReason: string}; -type ViolationsCashExpenseWithNoReceiptParams = {formattedLimit?: string}; +type ViolationsCashExpenseWithNoReceiptParams = {formattedLimit?: string} | undefined; -type ViolationsConversionSurchargeParams = {surcharge?: number}; +type ViolationsConversionSurchargeParams = {surcharge: number}; -type ViolationsInvoiceMarkupParams = {invoiceMarkup?: number}; +type ViolationsInvoiceMarkupParams = {invoiceMarkup: number}; type ViolationsMaxAgeParams = {maxAge: number}; -type ViolationsMissingTagParams = {tagName?: string}; +type ViolationsMissingTagParams = {tagName?: string} | undefined; type ViolationsModifiedAmountParams = {type?: ViolationDataType; displayPercentVariance?: number}; -type ViolationsOverAutoApprovalLimitParams = {formattedLimit?: string}; +type ViolationsOverAutoApprovalLimitParams = {formattedLimit: string}; -type ViolationsOverCategoryLimitParams = {formattedLimit?: string}; +type ViolationsOverCategoryLimitParams = {formattedLimit: string}; -type ViolationsOverLimitParams = {formattedLimit?: string}; +type ViolationsOverLimitParams = {formattedLimit: string}; -type ViolationsPerDayLimitParams = {formattedLimit?: string}; +type ViolationsPerDayLimitParams = {formattedLimit: string}; type ViolationsReceiptRequiredParams = {formattedLimit?: string; category?: string}; @@ -255,11 +255,11 @@ type ViolationsRterParams = { member?: string; }; -type ViolationsTagOutOfPolicyParams = {tagName?: string}; +type ViolationsTagOutOfPolicyParams = {tagName?: string} | undefined; -type ViolationsTaxOutOfPolicyParams = {taxName?: string}; +type ViolationsTaxOutOfPolicyParams = {taxName?: string} | undefined; -type PaySomeoneParams = {name?: string}; +type PaySomeoneParams = {name?: string} | undefined; type TaskCreatedActionParams = {title: string}; @@ -316,6 +316,8 @@ type TranslationFlatObject = { [TKey in TranslationPaths]: TranslateType; }; +type OptionalParam = Partial | undefined; + type TermsParams = {amount: string}; type ElectronicFundsParams = {percentage: string; amount: string}; @@ -423,9 +425,9 @@ type FiltersAmountBetweenParams = {greaterThan: string; lessThan: string}; type StatementPageTitleParams = {year: string | number; monthName: string}; -type DisconnectPromptParams = {currentIntegration?: ConnectionName}; +type DisconnectPromptParams = {currentIntegration?: ConnectionName} | undefined; -type DisconnectTitleParams = {integration?: ConnectionName}; +type DisconnectTitleParams = {integration?: ConnectionName} | undefined; type AmountWithCurrencyParams = {amountWithCurrency: string}; @@ -439,7 +441,7 @@ type TaxAmountParams = {taxAmount: number}; type SecondaryLoginParams = {secondaryLogin: string}; -type OwnerOwesAmountParams = {amount: number; email: string}; +type OwnerOwesAmountParams = {amount: string; email: string}; type ChangeOwnerSubscriptionParams = {usersCount: number; finalCount: number}; @@ -453,19 +455,19 @@ type WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams = {workspaceOwnerName: type RenamedWorkspaceNameActionParams = {oldName: string; newName: string}; -type StatementTitleParams = {year: number; monthName: string}; +type StatementTitleParams = {year: number | string; monthName: string}; type BadgeFreeTrialParams = {numOfDays: number}; type BillingBannerSubtitleWithDateParams = {date: string}; -type BillingBannerDisputePendingParams = {amountOwed: string; cardEnding: string}; +type BillingBannerDisputePendingParams = {amountOwed: number; cardEnding: string}; type BillingBannerCardAuthenticationRequiredParams = {cardEnding: string}; -type BillingBannerInsufficientFundsParams = {amountOwed: string}; +type BillingBannerInsufficientFundsParams = {amountOwed: number}; -type BillingBannerCardExpiredParams = {amountOwed: string}; +type BillingBannerCardExpiredParams = {amountOwed: number}; type BillingBannerCardOnDisputeParams = {amountOwed: string; cardEnding: string}; @@ -489,7 +491,7 @@ type SubscriptionSettingsRenewsOnParams = {date: string}; type UnapproveWithIntegrationWarningParams = {accountingIntegration: string}; -type IncorrectZipFormatParams = {zipFormat?: string}; +type IncorrectZipFormatParams = {zipFormat?: string} | undefined; type ExportIntegrationSelectedParams = {connectionName: ConnectionName}; @@ -697,4 +699,5 @@ export type { DisconnectPromptParams, DisconnectTitleParams, CharacterLengthLimitParams, + OptionalParam, }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 2ed6443b243e..66bc82992cb1 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -812,7 +812,10 @@ function getCustomersOrJobsLabelNetSuite(policy: Policy | undefined, translate: importFields.push(translate('workspace.netsuite.import.customersOrJobs.jobs')); } - const importedValueLabel = translate(`workspace.netsuite.import.customersOrJobs.label`, importFields, translate(`workspace.accounting.importTypes.${importedValue}`).toLowerCase()); + const importedValueLabel = translate(`workspace.netsuite.import.customersOrJobs.label`, { + importFields, + importType: translate(`workspace.accounting.importTypes.${importedValue}`).toLowerCase(), + }); return importedValueLabel.charAt(0).toUpperCase() + importedValueLabel.slice(1); } diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 4d126cf9cbf4..2b3dbbb62ae5 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1236,7 +1236,7 @@ function getMessageOfOldDotReportAction(oldDotAction: PartialReportAction | OldD case CONST.REPORT.ACTIONS.TYPE.INTEGRATIONS_MESSAGE: { const {result, label} = originalMessage; const errorMessage = result?.messages?.join(', ') ?? ''; - return Localize.translateLocal('report.actions.type.integrationsMessage', errorMessage, label); + return Localize.translateLocal('report.actions.type.integrationsMessage', {errorMessage, label}); } case CONST.REPORT.ACTIONS.TYPE.MANAGER_ATTACH_RECEIPT: return Localize.translateLocal('report.actions.type.managerAttachReceipt'); @@ -1613,7 +1613,7 @@ function getPolicyChangeLogAddEmployeeMessage(reportAction: OnyxInputOrEntry): reportAction is ReportAction { @@ -1628,7 +1628,7 @@ function getPolicyChangeLogChangeRoleMessage(reportAction: OnyxInputOrEntry>) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f8f85beff4ad..2aceede6bfe8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -7617,7 +7617,7 @@ function getFieldViolationTranslation(reportField: PolicyReportField, violation? switch (violation) { case 'fieldRequired': - return Localize.translateLocal('reportViolations.fieldRequired', reportField.name); + return Localize.translateLocal('reportViolations.fieldRequired', {fieldName: reportField.name}); default: return ''; } diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 3ac01c704443..906437ac7170 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -245,7 +245,7 @@ const ViolationsUtils = { category, rejectedBy = '', rejectReason = '', - formattedLimit, + formattedLimit = '', surcharge = 0, invoiceMarkup = 0, maxAge = 0, diff --git a/src/libs/WorkspacesSettingsUtils.ts b/src/libs/WorkspacesSettingsUtils.ts index 9b96c8404bf6..6dec630d7184 100644 --- a/src/libs/WorkspacesSettingsUtils.ts +++ b/src/libs/WorkspacesSettingsUtils.ts @@ -288,14 +288,14 @@ function getOwnershipChecksDisplayText( case CONST.POLICY.OWNERSHIP_ERRORS.DUPLICATE_SUBSCRIPTION: title = translate('workspace.changeOwner.duplicateSubscriptionTitle'); text = translate('workspace.changeOwner.duplicateSubscriptionText', { - email: changeOwner?.duplicateSubscription, - workspaceName: policy?.name, + email: changeOwner?.duplicateSubscription ?? '', + workspaceName: policy?.name ?? '', }); buttonText = translate('workspace.changeOwner.duplicateSubscriptionButtonText'); break; case CONST.POLICY.OWNERSHIP_ERRORS.HAS_FAILED_SETTLEMENTS: title = translate('workspace.changeOwner.hasFailedSettlementsTitle'); - text = translate('workspace.changeOwner.hasFailedSettlementsText', {email: accountLogin}); + text = translate('workspace.changeOwner.hasFailedSettlementsText', {email: accountLogin ?? ''}); buttonText = translate('workspace.changeOwner.hasFailedSettlementsButtonText'); break; case CONST.POLICY.OWNERSHIP_ERRORS.FAILED_TO_CLEAR_BALANCE: diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts index fa2e274204a2..158f5391a793 100644 --- a/src/libs/actions/connections/index.ts +++ b/src/libs/actions/connections/index.ts @@ -370,7 +370,7 @@ function updateManyPolicyConnectionConfigs, connectionName: PolicyConnectionName, isSyncInProgress: boolean): string | undefined { - const syncError = Localize.translateLocal('workspace.accounting.syncError', connectionName); + const syncError = Localize.translateLocal('workspace.accounting.syncError', {connectionName}); // NetSuite does not use the conventional lastSync object, so we need to check for lastErrorSyncDate if (connectionName === CONST.POLICY.CONNECTIONS.NAME.NETSUITE) { if ( diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index de93ed7a3ced..c3b0feb01aac 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -474,10 +474,11 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD /> ) : null; - const connectedIntegrationName = connectedIntegration ? translate('workspace.accounting.connectionName', connectedIntegration) : ''; + const connectedIntegrationName = connectedIntegration ? translate('workspace.accounting.connectionName', {connectionName: connectedIntegration}) : ''; const unapproveWarningText = ( - {translate('iou.headsUp')} {translate('iou.unapproveWithIntegrationWarning', connectedIntegrationName)} + {translate('iou.headsUp')}{' '} + {translate('iou.unapproveWithIntegrationWarning', {accountingIntegration: connectedIntegrationName})} ); diff --git a/src/pages/RestrictedAction/Workspace/WorkspaceAdminRestrictedAction.tsx b/src/pages/RestrictedAction/Workspace/WorkspaceAdminRestrictedAction.tsx index 89b5dcdd8a2b..342cd4ce5e6e 100644 --- a/src/pages/RestrictedAction/Workspace/WorkspaceAdminRestrictedAction.tsx +++ b/src/pages/RestrictedAction/Workspace/WorkspaceAdminRestrictedAction.tsx @@ -50,10 +50,10 @@ function WorkspaceAdminRestrictedAction({policyID}: WorkspaceAdminRestrictedActi height={variables.restrictedActionIllustrationHeight} /> - {translate('workspace.restrictedAction.actionsAreCurrentlyRestricted', {workspaceName: policy?.name})} + {translate('workspace.restrictedAction.actionsAreCurrentlyRestricted', {workspaceName: policy?.name ?? ''})} - {translate('workspace.restrictedAction.workspaceOwnerWillNeedToAddOrUpdatePaymentCard', {workspaceOwnerName: policy?.owner})} + {translate('workspace.restrictedAction.workspaceOwnerWillNeedToAddOrUpdatePaymentCard', {workspaceOwnerName: policy?.owner ?? ''})}