From e69dd460c76176e324a374bf6905fafa18f2d5cc Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jul 2024 16:09:51 +0530 Subject: [PATCH 001/386] Added pluralization system for lang translations --- src/components/MoneyReportHeader.tsx | 4 +- src/components/ProcessMoneyReportHoldMenu.tsx | 2 +- src/components/ReceiptAudit.tsx | 2 +- .../ExportWithDropdownMenu.tsx | 2 +- .../ReportActionItem/ReportPreview.tsx | 13 +- .../Search/SearchListWithHeader.tsx | 4 +- src/components/Search/SearchPageHeader.tsx | 2 +- src/languages/en.ts | 108 ++++++++++------ src/languages/es.ts | 115 +++++++++++------- src/languages/types.ts | 63 ++++++++-- src/libs/Localize/index.ts | 37 +++++- src/libs/PolicyUtils.ts | 5 +- src/libs/ReportActionsUtils.ts | 2 +- src/pages/ReportDetailsPage.tsx | 4 +- src/pages/ReportParticipantsPage.tsx | 2 +- src/pages/Search/SearchSelectedNarrow.tsx | 2 +- .../ReportActionCompose.tsx | 12 +- .../home/report/ReportDetailsExportPage.tsx | 2 +- .../FloatingActionButtonAndPopover.tsx | 2 +- src/pages/wallet/WalletStatementPage.tsx | 2 +- src/pages/workspace/WorkspaceMembersPage.tsx | 2 +- .../intacct/ExistingConnectionsPage.tsx | 4 +- .../intacct/import/SageIntacctImportPage.tsx | 2 +- .../NetSuiteExistingConnectionsPage.tsx | 4 +- .../categories/WorkspaceCategoriesPage.tsx | 2 +- .../PolicyDistanceRateDetailsPage.tsx | 2 +- .../distanceRates/PolicyDistanceRatesPage.tsx | 10 +- .../ReportFieldsListValuesPage.tsx | 2 +- .../WorkspaceReportFieldsPage.tsx | 2 +- .../workspace/tags/WorkspaceTagsPage.tsx | 2 +- .../workspace/tags/WorkspaceViewTagsPage.tsx | 2 +- .../workspace/taxes/WorkspaceTaxesPage.tsx | 2 +- 32 files changed, 273 insertions(+), 148 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 1acee187c67b..18a8c0b9e1a1 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -387,12 +387,12 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea /> )} setIsDeleteRequestModalVisible(false)} onModalHide={() => ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current)} - prompt={translate('iou.deleteConfirmation')} + prompt={translate('iou.deleteConfirmation', undefined, 1)} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger diff --git a/src/components/ProcessMoneyReportHoldMenu.tsx b/src/components/ProcessMoneyReportHoldMenu.tsx index 872464d8a5b0..ffc1150a454b 100644 --- a/src/components/ProcessMoneyReportHoldMenu.tsx +++ b/src/components/ProcessMoneyReportHoldMenu.tsx @@ -76,7 +76,7 @@ function ProcessMoneyReportHoldMenu({ if (nonHeldAmount) { return translate(isApprove ? 'iou.confirmApprovalAmount' : 'iou.confirmPayAmount'); } - return translate(isApprove ? 'iou.confirmApprovalAllHoldAmount' : 'iou.confirmPayAllHoldAmount', {transactionCount}); + return translate(isApprove ? 'iou.confirmApprovalAllHoldAmount' : 'iou.confirmPayAllHoldAmount', undefined, transactionCount); }, [nonHeldAmount, transactionCount, translate, isApprove]); return ( diff --git a/src/components/ReceiptAudit.tsx b/src/components/ReceiptAudit.tsx index bb704def1836..1057736621cd 100644 --- a/src/components/ReceiptAudit.tsx +++ b/src/components/ReceiptAudit.tsx @@ -22,7 +22,7 @@ function ReceiptAudit({notes, shouldShowAuditResult}: ReceiptAuditProps) { let auditText = ''; if (notes.length > 0 && shouldShowAuditResult) { - auditText = translate('iou.receiptIssuesFound', notes.length); + auditText = translate('iou.receiptIssuesFound', undefined, 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 a13c0a266689..2906bee5758f 100644 --- a/src/components/ReportActionItem/ExportWithDropdownMenu.tsx +++ b/src/components/ReportActionItem/ExportWithDropdownMenu.tsx @@ -118,7 +118,7 @@ function ExportWithDropdownMenu({policy, report, connectionName}: ExportWithDrop title={translate('workspace.exportAgainModal.title')} onConfirm={confirmExport} onCancel={() => setModalStatus(null)} - prompt={translate('workspace.exportAgainModal.description', report?.reportName ?? '', connectionName)} + prompt={translate('workspace.exportAgainModal.description', {reportName: report?.reportName ?? '', connectionName})} confirmText={translate('workspace.exportAgainModal.confirmText')} cancelText={translate('workspace.exportAgainModal.cancelText')} isVisible={!!modalStatus} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index ae6ace23c64e..c794113d0f50 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -328,11 +328,14 @@ function ReportPreview({ return {supportText: formattedMerchant}; } return { - supportText: translate('iou.expenseCount', { - count: numberOfRequests - numberOfScanningReceipts - numberOfPendingRequests, - scanningReceipts: numberOfScanningReceipts, - pendingReceipts: numberOfPendingRequests, - }), + supportText: translate( + 'iou.expenseCount', + { + scanningReceipts: numberOfScanningReceipts, + pendingReceipts: numberOfPendingRequests, + }, + numberOfRequests - numberOfScanningReceipts - numberOfPendingRequests, + ), }; }, [formattedMerchant, formattedDescription, moneyRequestComment, translate, numberOfRequests, numberOfScanningReceipts, numberOfPendingRequests]); diff --git a/src/components/Search/SearchListWithHeader.tsx b/src/components/Search/SearchListWithHeader.tsx index bd4b843bbd60..edfa55a53b26 100644 --- a/src/components/Search/SearchListWithHeader.tsx +++ b/src/components/Search/SearchListWithHeader.tsx @@ -213,8 +213,8 @@ function SearchListWithHeader( isVisible={deleteExpensesConfirmModalVisible} onConfirm={handleDeleteExpenses} onCancel={handleOnCancelConfirmModal} - title={translate('iou.deleteExpense', {count: selectedTransactionsToDelete.length})} - prompt={translate('iou.deleteConfirmation', {count: selectedTransactionsToDelete.length})} + title={translate('iou.deleteExpense', undefined, selectedTransactionsToDelete.length)} + prompt={translate('iou.deleteConfirmation', undefined, selectedTransactionsToDelete.length)} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index bd1e053b1c7e..0f4843b52a58 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -221,7 +221,7 @@ function SearchPageHeader({ shouldAlwaysShowDropdownMenu pressOnEnter buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedTransactionsKeys.length})} + customText={translate('workspace.common.selected', undefined, selectedTransactionsKeys.length)} options={headerButtonsOptions} isSplitButton={false} /> diff --git a/src/languages/en.ts b/src/languages/en.ts index b49ad50421a4..5e3b9241aae9 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1,4 +1,4 @@ -import {CONST as COMMON_CONST, Str} from 'expensify-common'; +import {CONST as COMMON_CONST} from 'expensify-common'; import {startCase} from 'lodash'; import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; @@ -17,25 +17,25 @@ import type { ChangePolicyParams, ChangeTypeParams, CharacterLimitParams, - ConfirmHoldExpenseParams, ConfirmThatParams, + CustomersOrJobsLabelTranslationParams, DateShouldBeAfterParams, DateShouldBeBeforeParams, DelegateSubmitParams, DeleteActionParams, DeleteConfirmationParams, - DeleteExpenseTranslationParams, DidSplitAmountMessageParams, - DistanceRateOperationsParams, EditActionParams, ElectronicFundsParams, EnterMagicCodeParams, + ExportAgainModalDescriptionTranslationParams, ExportedToIntegrationParams, FormattedMaxLengthParams, ForwardedParams, GoBackMessageParams, GoToRoomParams, InstantSummaryParams, + LastSyncDateTranslationParams, LocalTimeParams, LoggedInAsParams, LogSizeParams, @@ -66,6 +66,7 @@ import type { ReportArchiveReasonsMergedParams, ReportArchiveReasonsPolicyDeletedParams, ReportArchiveReasonsRemovedFromPolicyParams, + ReportIntegrationMessageTranslationParams, RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, @@ -80,6 +81,7 @@ import type { SignUpNewFaceCodeParams, SizeExceededParams, SplitAmountParams, + StatementPageTitleTranslationParams, StepCounterParams, StripePaidParams, TaskCreatedActionParams, @@ -496,16 +498,14 @@ export default { sendAttachment: 'Send attachment', addAttachment: 'Add attachment', writeSomething: 'Write something...', - conciergePlaceholderOptions: [ - 'Ask for help!', - 'Ask me anything!', - 'Ask me to book travel!', - 'Ask me what I can do!', - 'Ask me how to pay people!', - 'Ask me how to send an invoice!', - 'Ask me how to scan a receipt!', - 'Ask me how to get a free corporate card!', - ], + conciergePlaceholderOptions: (isSmallScreenWidth: boolean) => { + // If we are on a small width device then don't show last 3 items from conciergePlaceholderOptions + const options = ['Ask for help!', 'Ask me anything!', 'Ask me to book travel!', 'Ask me what I can do!', 'Ask me how to pay people!']; + if (!isSmallScreenWidth) { + options.push('Ask me how to send an invoice!', 'Ask me how to scan a receipt!', 'Ask me how to get a free corporate card!'); + } + return options[Math.floor(Math.random() * options.length)]; + }, blockedFromConcierge: 'Communication is barred', fileUploadFailed: 'Upload failed. File is not supported.', localTime: ({user, time}: LocalTimeParams) => `It's ${time} for ${user}`, @@ -654,7 +654,7 @@ export default { 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', @@ -700,7 +700,10 @@ export default { 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: () => ({ + one: `Issue found`, + other: () => `Issues found`, + }), fieldPending: 'Pending...', defaultRate: 'Default rate', receiptMissingDetails: 'Receipt missing details', @@ -710,12 +713,19 @@ export default { receiptStatusText: "Only you can see this receipt when it's scanning. Check back later or enter the details now.", receiptScanningFailed: 'Receipt scanning failed. Please enter the details manually.', transactionPendingDescription: 'Transaction pending. It may take a few days to post.', - expenseCount: ({count, scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => - `${count} ${Str.pluralize('expense', 'expenses', count)}${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${ - pendingReceipts > 0 ? `, ${pendingReceipts} pending` : '' - }`, - deleteExpense: ({count}: DeleteExpenseTranslationParams = {count: 1}) => `Delete ${Str.pluralize('expense', 'expenses', count)}`, - deleteConfirmation: ({count}: DeleteExpenseTranslationParams = {count: 1}) => `Are you sure that you want to delete ${Str.pluralize('this expense', 'these expenses', count)}?`, + expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => ({ + zero: `0 expenses${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, + one: `1 expense${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, + other: (count: number) => `${count} expenses${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, + }), + deleteExpense: () => ({ + one: `Delete expense`, + other: () => `Delete expenses`, + }), + deleteConfirmation: () => ({ + one: `Are you sure that you want to delete this expense?`, + other: () => `Are you sure that you want to delete these expenses?`, + }), settledExpensify: 'Paid', settledElsewhere: 'Paid elsewhere', individual: 'Individual', @@ -808,12 +818,18 @@ export default { keepAll: 'Keep all', confirmApprove: 'Confirm approval amount', confirmApprovalAmount: 'Approve only compliant expenses, or approve the entire report.', - confirmApprovalAllHoldAmount: ({transactionCount}: ConfirmHoldExpenseParams) => - `${Str.pluralize('This expense is', 'These expenses are', transactionCount)} on hold. Do you want to approve anyway?`, + confirmApprovalAllHoldAmount: () => ({ + zero: `This expense is on hold. Do you want to approve anyway?`, + one: `This expense is on hold. Do you want to approve anyway?`, + other: () => `These expenses are on hold. Do you want to approve anyway?`, + }), confirmPay: 'Confirm payment amount', confirmPayAmount: "Pay what's not on hold, or pay the entire report.", - confirmPayAllHoldAmount: ({transactionCount}: ConfirmHoldExpenseParams) => - `${Str.pluralize('This expense is', 'These expenses are', transactionCount)} on hold. Do you want to pay anyway?`, + confirmPayAllHoldAmount: () => ({ + zero: `This expense is on hold. Do you want to pay anyway?`, + one: `This expense is on hold. Do you want to pay anyway?`, + other: () => `These expenses are on hold. Do you want to pay anyway?`, + }), payOnly: 'Pay only', approveOnly: 'Approve only', holdEducationalTitle: 'This expense is on', @@ -2040,7 +2056,10 @@ export default { testTransactions: 'Test transactions', issueAndManageCards: 'Issue and manage cards', reconcileCards: 'Reconcile cards', - selected: ({selectedNumber}) => `${selectedNumber} selected`, + selected: () => ({ + one: `1 selected`, + other: (count: number) => `${count} selected`, + }), settlementFrequency: 'Settlement frequency', deleteConfirmation: 'Are you sure you want to delete this workspace?', unavailable: 'Unavailable workspace', @@ -2075,7 +2094,7 @@ export default { createNewConnection: 'Create new connection', reuseExistingConnection: 'Reuse existing connection', existingConnections: 'Existing connections', - lastSyncDate: (connectionName: string, formattedDate: string) => `${connectionName} - Last synced ${formattedDate}`, + lastSyncDate: ({connectionName, formattedDate}: LastSyncDateTranslationParams) => `${connectionName} - Last synced ${formattedDate}`, }, qbo: { importDescription: 'Choose which coding configurations to import from QuickBooks Online to Expensify.', @@ -2507,7 +2526,7 @@ export default { importJobs: 'Import projects', customers: 'customers', jobs: 'projects', - label: (importFields: string[], importType: string) => `${importFields.join(' and ')}, ${importType}`, + label: ({importFields, importType}: CustomersOrJobsLabelTranslationParams) => `${importFields.join(' and ')}, ${importType}`, }, importTaxDescription: 'Import tax groups from NetSuite.', importCustomFields: { @@ -2629,7 +2648,10 @@ export default { 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`, + userDimensionsAdded: () => ({ + one: `1 UDD added`, + other: (count: number) => `${count} UDDs added`, + }), mappingTitle: (mappingName: SageIntacctMappingName): string => { switch (mappingName) { case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS: @@ -3279,9 +3301,18 @@ export default { rate: 'Rate', addRate: 'Add rate', trackTax: 'Track tax', - deleteRates: ({count}: DistanceRateOperationsParams) => `Delete ${Str.pluralize('rate', 'rates', count)}`, - enableRates: ({count}: DistanceRateOperationsParams) => `Enable ${Str.pluralize('rate', 'rates', count)}`, - disableRates: ({count}: DistanceRateOperationsParams) => `Disable ${Str.pluralize('rate', 'rates', count)}`, + deleteRates: () => ({ + one: `Delete 1 rate`, + other: (count: number) => `Delete ${count} rates`, + }), + enableRates: () => ({ + one: `Enable 1 rate`, + other: (count: number) => `Enable ${count} rates`, + }), + disableRates: () => ({ + one: `Disable 1 rate`, + other: (count: number) => `Disable ${count} rates`, + }), enableRate: 'Enable rate', status: 'Status', unit: 'Unit', @@ -3289,7 +3320,10 @@ export default { changePromptMessage: ' to make that change.', defaultCategory: 'Default category', deleteDistanceRate: 'Delete distance rate', - areYouSureDelete: ({count}: DistanceRateOperationsParams) => `Are you sure you want to delete ${Str.pluralize('this rate', 'these rates', count)}?`, + areYouSureDelete: () => ({ + one: `Are you sure you want to delete 1 rate?`, + other: (count: number) => `Are you sure you want to delete ${count} rates?`, + }), }, editor: { descriptionInputLabel: 'Description', @@ -3376,7 +3410,7 @@ export default { }, exportAgainModal: { title: 'Careful!', - description: (reportName: string, connectionName: ConnectionName) => + description: ({reportName, connectionName}: ExportAgainModalDescriptionTranslationParams) => `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', @@ -3530,7 +3564,7 @@ export default { deleteConfirmation: 'Are you sure you want to delete this task?', }, statementPage: { - title: (year, monthName) => `${monthName} ${year} statement`, + title: ({year, monthName}: StatementPageTitleTranslationParams) => `${monthName} ${year} statement`, generatingPDF: "We're generating your PDF right now. Please check back soon!", }, keyboardShortcutsPage: { @@ -3688,7 +3722,7 @@ export default { pending: ({label}: ExportedToIntegrationParams) => `started exporting this report to ${label}...`, }, forwarded: ({amount, currency}: ForwardedParams) => `approved ${currency}${amount}`, - integrationsMessage: (errorMessage: string, label: string) => `failed to export this report to ${label} ("${errorMessage}").`, + integrationsMessage: ({errorMessage, label}: ReportIntegrationMessageTranslationParams) => `failed to export this report to ${label} ("${errorMessage}").`, managerAttachReceipt: `added a receipt`, managerDetachReceipt: `removed a receipt`, markedReimbursed: ({amount, currency}: MarkedReimbursedParams) => `paid ${currency}${amount} elsewhere`, diff --git a/src/languages/es.ts b/src/languages/es.ts index ff06d4f4f97e..1ce4ac35586b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1,4 +1,3 @@ -import {Str} from 'expensify-common'; import CONST from '@src/CONST'; import type {ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName} from '@src/types/onyx/Policy'; import type { @@ -15,26 +14,26 @@ import type { ChangePolicyParams, ChangeTypeParams, CharacterLimitParams, - ConfirmHoldExpenseParams, ConfirmThatParams, + CustomersOrJobsLabelTranslationParams, DateShouldBeAfterParams, DateShouldBeBeforeParams, DelegateSubmitParams, DeleteActionParams, DeleteConfirmationParams, - DeleteExpenseTranslationParams, DidSplitAmountMessageParams, - DistanceRateOperationsParams, EditActionParams, ElectronicFundsParams, EnglishTranslation, EnterMagicCodeParams, + ExportAgainModalDescriptionTranslationParams, ExportedToIntegrationParams, FormattedMaxLengthParams, ForwardedParams, GoBackMessageParams, GoToRoomParams, InstantSummaryParams, + LastSyncDateTranslationParams, LocalTimeParams, LoggedInAsParams, LogSizeParams, @@ -65,6 +64,7 @@ import type { ReportArchiveReasonsMergedParams, ReportArchiveReasonsPolicyDeletedParams, ReportArchiveReasonsRemovedFromPolicyParams, + ReportIntegrationMessageTranslationParams, RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, @@ -79,6 +79,7 @@ import type { SignUpNewFaceCodeParams, SizeExceededParams, SplitAmountParams, + StatementPageTitleTranslationParams, StepCounterParams, StripePaidParams, TaskCreatedActionParams, @@ -487,16 +488,13 @@ export default { sendAttachment: 'Enviar adjunto', addAttachment: 'Añadir archivo adjunto', writeSomething: 'Escribe algo...', - conciergePlaceholderOptions: [ - '¡Pide ayuda!', - '¡Pregúntame lo que sea!', - '¡Pídeme que te reserve un viaje!', - '¡Pregúntame qué puedo hacer!', - '¡Pregúntame cómo pagar a la gente!', - '¡Pregúntame cómo enviar una factura!', - '¡Pregúntame cómo escanear un recibo!', - '¡Pregúntame cómo obtener una tarjeta de crédito corporativa gratis!', - ], + conciergePlaceholderOptions: (isSmallScreenWidth: boolean) => { + const options = ['¡Pide ayuda!', '¡Pregúntame lo que sea!', '¡Pídeme que te reserve un viaje!', '¡Pregúntame qué puedo hacer!', '¡Pregúntame cómo pagar a la gente!']; + if (!isSmallScreenWidth) { + options.push('¡Pregúntame cómo enviar una factura!', '¡Pregúntame cómo escanear un recibo!', '¡Pregúntame cómo obtener una tarjeta de crédito corporativa gratis!'); + } + return options[Math.floor(Math.random() * options.length)]; + }, blockedFromConcierge: 'Comunicación no permitida', fileUploadFailed: 'Subida fallida. El archivo no es compatible.', localTime: ({user, time}: LocalTimeParams) => `Son las ${time} para ${user}`, @@ -649,7 +647,7 @@ export default { 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', @@ -692,7 +690,10 @@ export default { 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: () => ({ + one: `Problema encontrado`, + other: () => `Problemas encontrados`, + }), fieldPending: 'Pendiente...', receiptScanning: 'Escaneando recibo...', receiptScanInProgress: 'Escaneado de recibo en proceso', @@ -705,13 +706,19 @@ export default { receiptStatusText: 'Solo tú puedes ver este recibo cuando se está escaneando. Vuelve más tarde o introduce los detalles ahora.', receiptScanningFailed: 'El escaneo de recibo ha fallado. Introduce los detalles manualmente.', transactionPendingDescription: 'Transacción pendiente. Puede tardar unos días en contabilizarse.', - expenseCount: ({count, scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => - `${count} ${Str.pluralize('gasto', 'gastos', count)}${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${ - pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : '' - }`, - - deleteExpense: ({count}: DeleteExpenseTranslationParams = {count: 1}) => `Eliminar ${Str.pluralize('gasto', 'gastos', count)}`, - deleteConfirmation: ({count}: DeleteExpenseTranslationParams = {count: 1}) => `¿Estás seguro de que quieres eliminar ${Str.pluralize('esta solicitud', 'estas solicitudes', count)}?`, + expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => ({ + zero: `0 gasto${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, + one: `1 gasto${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, + other: (count: number) => `${count} gastos${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, + }), + deleteExpense: () => ({ + one: `Eliminar gasto`, + other: () => `Eliminar gastos`, + }), + deleteConfirmation: () => ({ + one: `¿Estás seguro de que quieres eliminar esta solicitud?`, + other: () => `¿Estás seguro de que quieres eliminar estas solicitudes?`, + }), settledExpensify: 'Pagado', settledElsewhere: 'Pagado de otra forma', individual: 'Individual', @@ -804,20 +811,18 @@ export default { keepAll: 'Mantener todos', confirmApprove: 'Confirmar importe a aprobar', confirmApprovalAmount: 'Aprueba sólo los gastos conformes, o aprueba todo el informe.', - confirmApprovalAllHoldAmount: ({transactionCount}: ConfirmHoldExpenseParams) => - `${Str.pluralize('Este gasto está bloqueado', 'Estos gastos están bloqueados', transactionCount)}. ¿Quieres ${Str.pluralize( - 'aprobar', - 'aprobarlos', - transactionCount, - )} de todos modos?`, + confirmApprovalAllHoldAmount: () => ({ + zero: `Ningún gasto está bloqueado. ¿Quieres aprobar todo el informe?`, + one: `Este gasto está bloqueado. ¿Quieres aprobarlo de todos modos?`, + other: () => `Estos gastos están bloqueados. ¿Quieres aprobarlos de todos modos?`, + }), confirmPay: 'Confirmar importe de pago', confirmPayAmount: 'Paga lo que no está bloqueado, o paga el informe completo.', - confirmPayAllHoldAmount: ({transactionCount}: ConfirmHoldExpenseParams) => - `${Str.pluralize('Este gasto está bloqueado', 'Estos gastos están bloqueados', transactionCount)}. ¿Quieres ${Str.pluralize( - 'pagar', - 'pagarlo', - transactionCount, - )} de todos modos?`, + confirmPayAllHoldAmount: () => ({ + zero: `Ningún gasto está bloqueado. ¿Quieres pagar todo el informe?`, + one: `Este gasto está bloqueado. ¿Quieres pagarlo de todos modos?`, + other: () => `Estos gastos están bloqueados. ¿Quieres pagarlos de todos modos?`, + }), payOnly: 'Solo pagar', approveOnly: 'Solo aprobar', hold: 'Bloquear', @@ -2074,7 +2079,10 @@ export default { testTransactions: 'Transacciones de prueba', issueAndManageCards: 'Emitir y gestionar tarjetas', reconcileCards: 'Reconciliar tarjetas', - selected: ({selectedNumber}) => `${selectedNumber} seleccionados`, + selected: () => ({ + one: `1 seleccionado`, + other: (count: number) => `${count} seleccionados`, + }), settlementFrequency: 'Frecuencia de liquidación', deleteConfirmation: '¿Estás seguro de que quieres eliminar este espacio de trabajo?', unavailable: 'Espacio de trabajo no disponible', @@ -2110,7 +2118,7 @@ export default { 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}: LastSyncDateTranslationParams) => `${connectionName} - Última sincronización ${formattedDate}`, }, qbo: { importDescription: 'Elige que configuraciónes de codificación son importadas desde QuickBooks Online a Expensify.', @@ -2556,7 +2564,7 @@ export default { importJobs: 'Importar proyectos', customers: 'clientes', jobs: 'proyectos', - label: (importFields: string[], importType: string) => `${importFields.join(' y ')}, ${importType}`, + label: ({importFields, importType}: CustomersOrJobsLabelTranslationParams) => `${importFields.join(' y ')}, ${importType}`, }, importTaxDescription: 'Importar grupos de impuestos desde NetSuite.', importCustomFields: { @@ -2678,7 +2686,10 @@ export default { 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`, + userDimensionsAdded: () => ({ + one: `1 UDD añadido`, + other: (count: number) => `${count} UDDs añadido`, + }), mappingTitle: (mappingName: SageIntacctMappingName): string => { switch (mappingName) { case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS: @@ -3333,9 +3344,18 @@ export default { rate: 'Tasa', addRate: 'Agregar tasa', trackTax: 'Impuesto de seguimiento', - deleteRates: ({count}: DistanceRateOperationsParams) => `Eliminar ${Str.pluralize('tasa', 'tasas', count)}`, - enableRates: ({count}: DistanceRateOperationsParams) => `Activar ${Str.pluralize('tasa', 'tasas', count)}`, - disableRates: ({count}: DistanceRateOperationsParams) => `Desactivar ${Str.pluralize('tasa', 'tasas', count)}`, + deleteRates: () => ({ + one: `Eliminar 1 tasa`, + other: (count: number) => `Eliminar ${count} tasas`, + }), + enableRates: () => ({ + one: `Activar 1 tasa`, + other: (count: number) => `Activar ${count} tasas`, + }), + disableRates: () => ({ + one: `Desactivar 1 tasa`, + other: (count: number) => `Desactivar ${count} tasas`, + }), enableRate: 'Activar tasa', status: 'Estado', unit: 'Unidad', @@ -3343,7 +3363,10 @@ export default { changePromptMessage: ' para hacer ese cambio.', defaultCategory: 'Categoría predeterminada', deleteDistanceRate: 'Eliminar tasa de distancia', - areYouSureDelete: ({count}: DistanceRateOperationsParams) => `¿Estás seguro de que quieres eliminar ${Str.pluralize('esta tasa', 'estas tasas', count)}?`, + areYouSureDelete: () => ({ + one: `¿Estás seguro de que quieres eliminar 1 tasa?`, + other: (count: number) => `¿Estás seguro de que quieres eliminar ${count} tasas?`, + }), }, editor: { nameInputLabel: 'Nombre', @@ -3432,7 +3455,7 @@ export default { exportAgainModal: { title: '¡Cuidado!', - description: (reportName: string, connectionName: ConnectionName) => + description: ({reportName, connectionName}: ExportAgainModalDescriptionTranslationParams) => `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', @@ -3587,7 +3610,7 @@ export default { deleteConfirmation: '¿Estás seguro de que quieres eliminar esta tarea?', }, statementPage: { - title: (year, monthName) => `Estado de cuenta de ${monthName} ${year}`, + title: ({year, monthName}: StatementPageTitleTranslationParams) => `Estado de cuenta de ${monthName} ${year}`, generatingPDF: 'Estamos generando tu PDF ahora mismo. ¡Por favor, vuelve más tarde!', }, keyboardShortcutsPage: { @@ -3746,7 +3769,7 @@ export default { pending: ({label}: ExportedToIntegrationParams) => `comenzó a exportar este informe a ${label}...`, }, forwarded: ({amount, currency}: ForwardedParams) => `aprobado ${currency}${amount}`, - integrationsMessage: (errorMessage: string, label: string) => `no se pudo exportar este informe a ${label} ("${errorMessage}").`, + integrationsMessage: ({errorMessage, label}: ReportIntegrationMessageTranslationParams) => `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`, diff --git a/src/languages/types.ts b/src/languages/types.ts index 24117f257d8f..62c96beb323e 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'; @@ -96,7 +96,6 @@ type ReportArchiveReasonsPolicyDeletedParams = { }; type RequestCountParams = { - count: number; scanningReceipts: number; pendingReceipts: number; }; @@ -251,9 +250,21 @@ type PaySomeoneParams = {name?: string}; type TaskCreatedActionParams = {title: string}; +type PluralFormPhase = { + zero?: string; + one: string; + two?: string; + few?: (count: number) => string; + many?: (count: number) => string; + other: (count: number) => string; +}; + /* Translation Object types */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -type TranslationBaseValue = string | string[] | ((...args: any[]) => string); +type TranslationPluralPhaseValue = (arg?: any) => PluralFormPhase; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type TranslationSingularFunctionValue = (arg?: any) => string; +type TranslationBaseValue = string | TranslationPluralPhaseValue | TranslationSingularFunctionValue; type TranslationBase = {[key: string]: TranslationBaseValue | TranslationBase}; @@ -261,7 +272,7 @@ type TranslationBase = {[key: string]: TranslationBaseValue | TranslationBase}; // 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 (arg?: any) => any ? `${TPrefix}${TKey & string}` : // eslint-disable-next-line @typescript-eslint/no-explicit-any TObject[TKey] extends any[] @@ -289,6 +300,11 @@ type TranslationFlatObject = { [TKey in TranslationPaths]: TranslateType; }; +type PluralTranslationFlatObject = Pick< + TranslationFlatObject, + {[K in keyof TranslationFlatObject]: TranslationFlatObject[K] extends TranslationPluralPhaseValue ? K : never}[keyof TranslationFlatObject] +>; + type TermsParams = {amount: string}; type ElectronicFundsParams = {percentage: string; amount: string}; @@ -297,12 +313,8 @@ type LogSizeParams = {size: number}; type HeldRequestParams = {comment: string}; -type DistanceRateOperationsParams = {count: number}; - type ReimbursementRateParams = {unit: Unit}; -type ConfirmHoldExpenseParams = {transactionCount: number}; - type ChangeFieldParams = {oldValue?: string; newValue: string; fieldName: string}; type ChangePolicyParams = {fromPolicy: string; toPolicy: string}; @@ -344,8 +356,29 @@ type RemoveMembersWarningPrompt = { ownerName: string; }; -type DeleteExpenseTranslationParams = { - count: number; +type LastSyncDateTranslationParams = { + connectionName: string; + formattedDate: string; +}; + +type CustomersOrJobsLabelTranslationParams = { + importFields: string[]; + importType: string; +}; + +type ExportAgainModalDescriptionTranslationParams = { + reportName: string; + connectionName: ConnectionName; +}; + +type StatementPageTitleTranslationParams = { + year: string | number; + monthName: string; +}; + +type ReportIntegrationMessageTranslationParams = { + errorMessage: string; + label: string; }; export type { @@ -359,14 +392,12 @@ export type { BeginningOfChatHistoryDomainRoomPartOneParams, CanceledRequestParams, CharacterLimitParams, - ConfirmHoldExpenseParams, ConfirmThatParams, DateShouldBeAfterParams, DateShouldBeBeforeParams, DeleteActionParams, DeleteConfirmationParams, DidSplitAmountMessageParams, - DistanceRateOperationsParams, EditActionParams, ElectronicFundsParams, EnglishTranslation, @@ -423,9 +454,11 @@ export type { ThreadSentMoneyReportNameParams, ToValidateLoginParams, TransferParams, + PluralFormPhase, TranslationBase, TranslationFlatObject, TranslationPaths, + PluralTranslationFlatObject, UntilTimeParams, UpdatedTheDistanceParams, UpdatedTheRequestParams, @@ -468,5 +501,9 @@ export type { StripePaidParams, UnapprovedParams, RemoveMembersWarningPrompt, - DeleteExpenseTranslationParams, + LastSyncDateTranslationParams, + CustomersOrJobsLabelTranslationParams, + ExportAgainModalDescriptionTranslationParams, + StatementPageTitleTranslationParams, + ReportIntegrationMessageTranslationParams, }; diff --git a/src/libs/Localize/index.ts b/src/libs/Localize/index.ts index c9eef3170245..f1b705398067 100644 --- a/src/libs/Localize/index.ts +++ b/src/libs/Localize/index.ts @@ -6,7 +6,7 @@ import type {MessageElementBase, MessageTextElement} from '@libs/MessageElement' import Config from '@src/CONFIG'; import CONST from '@src/CONST'; import translations from '@src/languages/translations'; -import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types'; +import type {PluralFormPhase, TranslationFlatObject, TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Locale} from '@src/types/onyx'; import LocaleListener from './LocaleListener'; @@ -45,8 +45,8 @@ function init() { }, {}); } -type PhraseParameters = T extends (...args: infer A) => string ? A : never[]; -type Phrase = TranslationFlatObject[TKey] extends (...args: infer A) => unknown ? (...args: A) => string : string; +type PhraseParameters = T extends (arg: infer A) => string ? [A] : T extends (arg: infer A) => PluralFormPhase ? [A, number] : never[]; +type Phrase = TranslationFlatObject[TKey]; /** * Map to store translated values for each locale. @@ -70,6 +70,23 @@ const translationCache = new Map, Map, Map]>), ); +function handlePluralForm(translatedPhrase: PluralFormPhase, pluralForm: Intl.LDMLPluralRule, count: number) { + switch (pluralForm) { + case 'zero': + return translatedPhrase.zero; + case 'one': + return translatedPhrase.one; + case 'two': + return translatedPhrase.two; + case 'few': + return translatedPhrase.few?.(count); + case 'many': + return translatedPhrase.many?.(count); + default: + return translatedPhrase.other(count); + } +} + /** * Helper function to get the translated string for given * locale and phrase. This function is used to avoid @@ -106,11 +123,21 @@ function getTranslatedPhrase( return valueFromCache; } - const translatedPhrase = translations?.[language]?.[phraseKey] as Phrase; + const translatedPhrase = translations?.[language]?.[phraseKey]; if (translatedPhrase) { if (typeof translatedPhrase === 'function') { - return translatedPhrase(...phraseParameters); + const calledTranslatedPhrase = translatedPhrase(phraseParameters[0]); + if (typeof calledTranslatedPhrase === 'string') { + return calledTranslatedPhrase; + } + const count = phraseParameters[1] ?? 0; + const pluralForm = new Intl.PluralRules(language, {type: 'ordinal'}).select(count); + if (pluralForm in calledTranslatedPhrase) { + return handlePluralForm(calledTranslatedPhrase, pluralForm, count) ?? ''; + } + Log.alert(`Plural form ${pluralForm} is not found for ${phraseKey}, using 'other' form`); + return calledTranslatedPhrase.other(count); } // We set the translated value in the cache only for the phrases without parameters. diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index fe53a7bcd5ce..c3410fbd6516 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -649,7 +649,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 73784873201a..f4683abf6100 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1248,7 +1248,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'); diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 4e82900372b9..5e8c311a79e4 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -763,7 +763,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD shouldEnableNewFocusManagement /> setIsDeleteModalVisible(false)} @@ -779,7 +779,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current, true); } }} - prompt={caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation')} + prompt={caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation', undefined, 1)} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index 15dd063ec6ee..300122db9be5 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -268,7 +268,7 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic shouldAlwaysShowDropdownMenu pressOnEnter - customText={translate('workspace.common.selected', {selectedNumber: selectedMembers.length})} + customText={translate('workspace.common.selected', undefined, selectedMembers.length)} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} onPress={() => null} options={bulkActionsButtonOptions} diff --git a/src/pages/Search/SearchSelectedNarrow.tsx b/src/pages/Search/SearchSelectedNarrow.tsx index 1cc34d6bf53a..039c54dc609a 100644 --- a/src/pages/Search/SearchSelectedNarrow.tsx +++ b/src/pages/Search/SearchSelectedNarrow.tsx @@ -50,7 +50,7 @@ function SearchSelectedNarrow({options, itemsLength}: SearchSelectedNarrowProps) onPress={openMenu} ref={buttonRef} style={[styles.w100, styles.ph5]} - text={translate('workspace.common.selected', {selectedNumber: itemsLength})} + text={translate('workspace.common.selected', undefined, itemsLength)} isContentCentered iconRight={Expensicons.DownArrow} isDisabled={options.length === 0} diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 6ff163f6ec37..403b7864102f 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -195,13 +195,6 @@ function ReportActionCompose({ const userBlockedFromConcierge = useMemo(() => User.isBlockedFromConcierge(blockedFromConcierge), [blockedFromConcierge]); const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]); - // If we are on a small width device then don't show last 3 items from conciergePlaceholderOptions - const conciergePlaceholderRandomIndex = useMemo( - () => Math.floor(Math.random() * (translate('reportActionCompose.conciergePlaceholderOptions').length - (isSmallScreenWidth ? 4 : 1) + 1)), - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - [], - ); - // Placeholder to display in the chat input. const inputPlaceholder = useMemo(() => { if (includesConcierge) { @@ -209,11 +202,12 @@ function ReportActionCompose({ return translate('reportActionCompose.blockedFromConcierge'); } - return translate('reportActionCompose.conciergePlaceholderOptions')[conciergePlaceholderRandomIndex]; + return translate('reportActionCompose.conciergePlaceholderOptions', isSmallScreenWidth); } return translate('reportActionCompose.writeSomething'); - }, [includesConcierge, translate, userBlockedFromConcierge, conciergePlaceholderRandomIndex]); + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, [includesConcierge, translate, userBlockedFromConcierge]); const focus = () => { if (composerRef.current === null) { diff --git a/src/pages/home/report/ReportDetailsExportPage.tsx b/src/pages/home/report/ReportDetailsExportPage.tsx index 99b7305cc7a9..d6861bd54dd0 100644 --- a/src/pages/home/report/ReportDetailsExportPage.tsx +++ b/src/pages/home/report/ReportDetailsExportPage.tsx @@ -120,7 +120,7 @@ function ReportDetailsExportPage({route}: ReportDetailsExportPageProps) { title={translate('workspace.exportAgainModal.title')} onConfirm={confirmExport} onCancel={() => setModalStatus(null)} - prompt={translate('workspace.exportAgainModal.description', report?.reportName ?? '', connectionName)} + prompt={translate('workspace.exportAgainModal.description', {reportName: report?.reportName ?? '', connectionName})} confirmText={translate('workspace.exportAgainModal.confirmText')} cancelText={translate('workspace.exportAgainModal.cancelText')} isVisible={!!modalStatus} diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index cde3750fc37f..ab18013d6b23 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -212,7 +212,7 @@ function FloatingActionButtonAndPopover( } if (quickAction?.action === CONST.QUICK_ACTIONS.SEND_MONEY && quickActionAvatars.length > 0) { const name: string = ReportUtils.getDisplayNameForParticipant(+(quickActionAvatars[0]?.id ?? -1), true) ?? ''; - return translate('quickAction.paySomeone', name); + return translate('quickAction.paySomeone', {name}); } const titleKey = getQuickActionTitle(quickAction?.action ?? ('' as QuickActionName)); return titleKey ? translate(titleKey) : ''; diff --git a/src/pages/wallet/WalletStatementPage.tsx b/src/pages/wallet/WalletStatementPage.tsx index 0db72877bd2e..54b601a00350 100644 --- a/src/pages/wallet/WalletStatementPage.tsx +++ b/src/pages/wallet/WalletStatementPage.tsx @@ -70,7 +70,7 @@ function WalletStatementPage({walletStatement, route}: WalletStatementPageProps) const year = yearMonth?.substring(0, 4) || getYear(new Date()); const month = yearMonth?.substring(4) || getMonth(new Date()); const monthName = format(new Date(Number(year), Number(month) - 1), CONST.DATE.MONTH_FORMAT); - const title = translate('statementPage.title', year, monthName); + const title = translate('statementPage.title', {year, monthName}); const url = `${CONFIG.EXPENSIFY.EXPENSIFY_URL}statement.php?period=${yearMonth}${themePreference === CONST.THEME.DARK ? '&isDarkMode=true' : ''}`; return ( diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index adbf5a664c82..8b2ca4e09ca1 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -503,7 +503,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, shouldAlwaysShowDropdownMenu pressOnEnter - customText={translate('workspace.common.selected', {selectedNumber: selectedEmployees.length})} + customText={translate('workspace.common.selected', undefined, selectedEmployees.length)} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} onPress={() => null} options={getBulkActionsButtonOptions()} diff --git a/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx b/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx index c55e46083470..3475d726ba84 100644 --- a/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx +++ b/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx @@ -30,7 +30,9 @@ function ExistingConnectionsPage({route}: ExistingConnectionsPageProps) { key: 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/intacct/import/SageIntacctImportPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx index 543d3f4d0154..958e35c5b572 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx @@ -128,7 +128,7 @@ function SageIntacctImportPage({policy}: WithPolicyProps) { 0 - ? translate('workspace.intacct.userDimensionsAdded', sageIntacctConfig?.mappings?.dimensions?.length) + ? translate('workspace.intacct.userDimensionsAdded', undefined, sageIntacctConfig?.mappings?.dimensions?.length) : undefined } description={translate('workspace.intacct.userDefinedDimensions')} diff --git a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx index ef98625a64e2..5fdca30600c2 100644 --- a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx +++ b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx @@ -30,7 +30,9 @@ function NetSuiteExistingConnectionsPage({route}: ExistingConnectionsPageProps) key: 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/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 174251a80d5f..12f76fe15abb 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -224,7 +224,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { shouldAlwaysShowDropdownMenu pressOnEnter buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedCategoriesArray.length})} + customText={translate('workspace.common.selected', undefined, selectedCategoriesArray.length)} options={options} isSplitButton={false} style={[shouldUseNarrowLayout && styles.flexGrow1, shouldUseNarrowLayout && styles.mb3]} diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx index 00204d1e40c7..85e602228149 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx @@ -194,7 +194,7 @@ function PolicyDistanceRateDetailsPage({policy, route}: PolicyDistanceRateDetail isVisible={isDeleteModalVisible} onConfirm={deleteRate} onCancel={() => setIsDeleteModalVisible(false)} - prompt={translate('workspace.distanceRates.areYouSureDelete', {count: 1})} + prompt={translate('workspace.distanceRates.areYouSureDelete', undefined, 1)} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx index 1ef1319973a1..4749c3483571 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx @@ -200,7 +200,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) const getBulkActionsButtonOptions = () => { const options: Array> = [ { - text: translate('workspace.distanceRates.deleteRates', {count: selectedDistanceRates.length}), + text: translate('workspace.distanceRates.deleteRates', undefined, selectedDistanceRates.length), value: CONST.POLICY.BULK_ACTION_TYPES.DELETE, icon: Expensicons.Trashcan, onSelected: () => (canDisableOrDeleteSelectedRates ? setIsDeleteModalVisible(true) : setIsWarningModalVisible(true)), @@ -210,7 +210,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) const enabledRates = selectedDistanceRates.filter((rate) => rate.enabled); if (enabledRates.length > 0) { options.push({ - text: translate('workspace.distanceRates.disableRates', {count: enabledRates.length}), + text: translate('workspace.distanceRates.disableRates', undefined, enabledRates.length), value: CONST.POLICY.BULK_ACTION_TYPES.DISABLE, icon: Expensicons.DocumentSlash, onSelected: () => (canDisableOrDeleteSelectedRates ? disableRates() : setIsWarningModalVisible(true)), @@ -220,7 +220,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) const disabledRates = selectedDistanceRates.filter((rate) => !rate.enabled); if (disabledRates.length > 0) { options.push({ - text: translate('workspace.distanceRates.enableRates', {count: disabledRates.length}), + text: translate('workspace.distanceRates.enableRates', undefined, disabledRates.length), value: CONST.POLICY.BULK_ACTION_TYPES.ENABLE, icon: Expensicons.Document, onSelected: enableRates, @@ -257,7 +257,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) shouldAlwaysShowDropdownMenu pressOnEnter - customText={translate('workspace.common.selected', {selectedNumber: selectedDistanceRates.length})} + customText={translate('workspace.common.selected', undefined, selectedDistanceRates.length)} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} onPress={() => null} options={getBulkActionsButtonOptions()} @@ -332,7 +332,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) isVisible={isDeleteModalVisible} onConfirm={deleteRates} onCancel={() => setIsDeleteModalVisible(false)} - prompt={translate('workspace.distanceRates.areYouSureDelete', {count: selectedDistanceRates.length})} + prompt={translate('workspace.distanceRates.areYouSureDelete', undefined, selectedDistanceRates.length)} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger diff --git a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx index adbe33397950..0d5a6e617aec 100644 --- a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx +++ b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx @@ -249,7 +249,7 @@ function ReportFieldsListValuesPage({ shouldAlwaysShowDropdownMenu pressOnEnter buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedValuesArray.length})} + customText={translate('workspace.common.selected', undefined, selectedValuesArray.length)} options={options} isSplitButton={false} style={[isSmallScreenWidth && styles.flexGrow1, isSmallScreenWidth && styles.mb3]} diff --git a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx index f4e3b01145da..4b231f1f2c5f 100644 --- a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx +++ b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx @@ -164,7 +164,7 @@ function WorkspaceReportFieldsPage({ shouldAlwaysShowDropdownMenu pressOnEnter buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedReportFields.length})} + customText={translate('workspace.common.selected', undefined, selectedReportFields.length)} options={options} isSplitButton={false} style={[shouldUseNarrowLayout && styles.flexGrow1, shouldUseNarrowLayout && styles.mb3]} diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx index cf9952720fc9..dc0dab1634b0 100644 --- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx @@ -276,7 +276,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) { pressOnEnter isSplitButton={false} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedTagsArray.length})} + customText={translate('workspace.common.selected', undefined, selectedTagsArray.length)} options={options} style={[isSmallScreenWidth && styles.flexGrow1, isSmallScreenWidth && styles.mb3]} /> diff --git a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx index 9ac1fc7583ae..02a9ba0fe0c2 100644 --- a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx @@ -202,7 +202,7 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) { pressOnEnter isSplitButton={false} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedTagsArray.length})} + customText={translate('workspace.common.selected', undefined, selectedTagsArray.length)} options={options} style={[isSmallScreenWidth && styles.flexGrow1, isSmallScreenWidth && styles.mb3]} /> diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx index edd2ef4e3a65..f40b2fc054b9 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx @@ -235,7 +235,7 @@ function WorkspaceTaxesPage({ onPress={() => {}} options={dropdownMenuOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedTaxesIDs.length})} + customText={translate('workspace.common.selected', undefined, selectedTaxesIDs.length)} shouldAlwaysShowDropdownMenu pressOnEnter isSplitButton={false} From e7df141dd5b4a843ddd128da78d06085a67c28be Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jul 2024 16:54:25 +0530 Subject: [PATCH 002/386] Fix lint --- src/components/AddressForm.tsx | 2 +- src/components/ArchivedReportFooter.tsx | 37 +++++++++++++++++++------ tests/unit/TranslateTest.ts | 11 ++------ 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/components/AddressForm.tsx b/src/components/AddressForm.tsx index 7ca4cc3273ca..d6ba5813a101 100644 --- a/src/components/AddressForm.tsx +++ b/src/components/AddressForm.tsx @@ -126,7 +126,7 @@ function AddressForm({ } } } else if (!CONST.GENERIC_ZIP_CODE_REGEX.test(values?.zipPostCode?.trim()?.toUpperCase() ?? '')) { - errors.zipPostCode = translate('privatePersonalDetails.error.incorrectZipFormat'); + errors.zipPostCode = translate('privatePersonalDetails.error.incorrectZipFormat', undefined); } return errors; diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx index 35f5aeecb5a4..7e0121a3ae4f 100644 --- a/src/components/ArchivedReportFooter.tsx +++ b/src/components/ArchivedReportFooter.tsx @@ -53,14 +53,35 @@ function ArchivedReportFooter({report, reportClosedAction, personalDetails = {}} policyName = lodashEscape(policyName); } - const text = shouldRenderHTML - ? translate(`reportArchiveReasons.${archiveReason}`, { - displayName: `${displayName}`, - oldDisplayName: `${oldDisplayName}`, - policyName: `${policyName}`, - shouldUseYou: actorPersonalDetails?.accountID === getCurrentUserAccountID(), - }) - : translate(`reportArchiveReasons.${archiveReason}`); + let text; + switch (archiveReason) { + case CONST.REPORT.ARCHIVE_REASON.ACCOUNT_CLOSED: + text = translate(`reportArchiveReasons.${archiveReason}`, { + displayName: `${displayName}`, + }); + break; + case CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED: + text = translate(`reportArchiveReasons.${archiveReason}`, { + displayName: `${displayName}`, + oldDisplayName: `${oldDisplayName}`, + }); + break; + case CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY: + text = translate(`reportArchiveReasons.${archiveReason}`, { + displayName: `${displayName}`, + policyName: `${policyName}`, + shouldUseYou: actorPersonalDetails?.accountID === getCurrentUserAccountID(), + }); + break; + case CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED: + text = translate(`reportArchiveReasons.${archiveReason}`, { + policyName: `${policyName}`, + }); + break; + default: + text = translate(`reportArchiveReasons.${archiveReason}`); + break; + } return ( { expect(Localize.translate(CONST.LOCALES.ES_ES, 'testKey4' as TranslationPaths)).toBe('testKey4'); asMutable(CONFIG).IS_IN_PRODUCTION = ORIGINAL_IS_IN_PRODUCTION; }); - - it('Test when translation value is a function', () => { - const expectedValue = 'With variable Test Variable'; - const testVariable = 'Test Variable'; - // @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); - }); }); describe('Translation Keys', () => { @@ -126,7 +119,7 @@ describe('flattenObject', () => { none: 'No description', }, content: func, - messages: ['Hello', 'Hi', 'Sup!'], + messages: 'Hello!', }, }, }; @@ -141,7 +134,7 @@ describe('flattenObject', () => { 'complex.report.title.task': 'Task', 'complex.report.description.none': 'No description', 'complex.report.content': func, - 'complex.report.messages': ['Hello', 'Hi', 'Sup!'], + 'complex.report.messages': 'Hello!', }); }); }); From dbc34e6ee66d91bd0c9835a8099315d41827ca18 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:10:43 +0530 Subject: [PATCH 003/386] Apply suggestions from code review Co-authored-by: Jayesh Mangwani <35371050+jayeshmangwani@users.noreply.github.com> --- src/languages/en.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 5e3b9241aae9..80e37460ac05 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3302,16 +3302,16 @@ export default { addRate: 'Add rate', trackTax: 'Track tax', deleteRates: () => ({ - one: `Delete 1 rate`, - other: (count: number) => `Delete ${count} rates`, + one: 'Delete rate', + other: () => 'Delete rates', }), enableRates: () => ({ - one: `Enable 1 rate`, - other: (count: number) => `Enable ${count} rates`, + one: 'Enable rate', + other: () => 'Enable rates', }), disableRates: () => ({ - one: `Disable 1 rate`, - other: (count: number) => `Disable ${count} rates`, + one: 'Disable rate', + other: () => 'Disable rates', }), enableRate: 'Enable rate', status: 'Status', @@ -3321,8 +3321,8 @@ export default { defaultCategory: 'Default category', deleteDistanceRate: 'Delete distance rate', areYouSureDelete: () => ({ - one: `Are you sure you want to delete 1 rate?`, - other: (count: number) => `Are you sure you want to delete ${count} rates?`, + one: 'Are you sure you want to delete this rate?', + other: () => 'Are you sure you want to delete these rates?', }), }, editor: { From 7f91f9be8ce90c02b2980225267ed120f45cce7c Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:14:16 +0530 Subject: [PATCH 004/386] Apply suggestions from code review --- src/languages/es.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 1ce4ac35586b..0ae384b5b075 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3345,16 +3345,16 @@ export default { addRate: 'Agregar tasa', trackTax: 'Impuesto de seguimiento', deleteRates: () => ({ - one: `Eliminar 1 tasa`, - other: (count: number) => `Eliminar ${count} tasas`, + one: 'Eliminar tasa', + other: () => 'Eliminar tasas', }), enableRates: () => ({ - one: `Activar 1 tasa`, - other: (count: number) => `Activar ${count} tasas`, + one: 'Activar 1 tasa', + other: () => 'Activar tasas', }), disableRates: () => ({ - one: `Desactivar 1 tasa`, - other: (count: number) => `Desactivar ${count} tasas`, + one: 'Desactivar tasa', + other: () => 'Desactivar tasas', }), enableRate: 'Activar tasa', status: 'Estado', @@ -3364,8 +3364,8 @@ export default { defaultCategory: 'Categoría predeterminada', deleteDistanceRate: 'Eliminar tasa de distancia', areYouSureDelete: () => ({ - one: `¿Estás seguro de que quieres eliminar 1 tasa?`, - other: (count: number) => `¿Estás seguro de que quieres eliminar ${count} tasas?`, + one: '¿Estás seguro de que quieres eliminar esta tasa?', + other: () => '¿Estás seguro de que quieres eliminar estas tasas?', }), }, editor: { From c8dd8d1e696031b4c438c7ac607776c62f3a19ad Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:19:44 +0530 Subject: [PATCH 005/386] Apply suggestions from code review --- .../home/report/ReportActionCompose/ReportActionCompose.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 77e8f366d866..275fe000fe79 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -202,7 +202,7 @@ function ReportActionCompose({ return translate('reportActionCompose.blockedFromConcierge'); } - return translate('reportActionCompose.conciergePlaceholderOptions', isSmallScreenWidth); + return translate('reportActionCompose.conciergePlaceholderOptions', shouldUseNarrowLayout); } return translate('reportActionCompose.writeSomething'); From 3a15ab360099f7427680f2ca6327a84338eea4f2 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 30 Jul 2024 18:11:49 +0530 Subject: [PATCH 006/386] Fixed unused type --- src/languages/types.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/languages/types.ts b/src/languages/types.ts index 62c96beb323e..611deac1e854 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -300,11 +300,6 @@ type TranslationFlatObject = { [TKey in TranslationPaths]: TranslateType; }; -type PluralTranslationFlatObject = Pick< - TranslationFlatObject, - {[K in keyof TranslationFlatObject]: TranslationFlatObject[K] extends TranslationPluralPhaseValue ? K : never}[keyof TranslationFlatObject] ->; - type TermsParams = {amount: string}; type ElectronicFundsParams = {percentage: string; amount: string}; @@ -458,7 +453,6 @@ export type { TranslationBase, TranslationFlatObject, TranslationPaths, - PluralTranslationFlatObject, UntilTimeParams, UpdatedTheDistanceParams, UpdatedTheRequestParams, From fd0b5e7175b1be67111891cc67130ffd3e536419 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 30 Jul 2024 18:27:46 +0530 Subject: [PATCH 007/386] Fixed lint --- src/pages/Search/SearchFiltersDatePage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Search/SearchFiltersDatePage.tsx b/src/pages/Search/SearchFiltersDatePage.tsx index 9ac9973c8ca2..ae11e972dc20 100644 --- a/src/pages/Search/SearchFiltersDatePage.tsx +++ b/src/pages/Search/SearchFiltersDatePage.tsx @@ -47,7 +47,7 @@ function SearchFiltersDatePage() { Date: Tue, 30 Jul 2024 19:19:32 +0530 Subject: [PATCH 008/386] Fixed lint --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index e87a5bb71e3d..f1961dd7b449 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -35,8 +35,8 @@ import type { GoBackMessageParams, GoToRoomParams, InstantSummaryParams, - LastSyncDateTranslationParams, IssueVirtualCardParams, + LastSyncDateTranslationParams, LocalTimeParams, LoggedInAsParams, LogSizeParams, diff --git a/src/languages/es.ts b/src/languages/es.ts index 488cbbca626a..f44850067036 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -33,8 +33,8 @@ import type { GoBackMessageParams, GoToRoomParams, InstantSummaryParams, - LastSyncDateTranslationParams, IssueVirtualCardParams, + LastSyncDateTranslationParams, LocalTimeParams, LoggedInAsParams, LogSizeParams, From 5eed7b5482abfeef2dc723ec9797c04944ce688d Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Thu, 1 Aug 2024 18:00:10 +0530 Subject: [PATCH 009/386] Apply suggestions --- src/languages/en.ts | 1 - src/languages/es.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index bc91fa1ab428..ec03c97d8948 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -823,7 +823,6 @@ export default { confirmApprove: 'Confirm approval amount', confirmApprovalAmount: 'Approve only compliant expenses, or approve the entire report.', confirmApprovalAllHoldAmount: () => ({ - zero: `This expense is on hold. Do you want to approve anyway?`, one: `This expense is on hold. Do you want to approve anyway?`, other: () => `These expenses are on hold. Do you want to approve anyway?`, }), diff --git a/src/languages/es.ts b/src/languages/es.ts index 3133d6446db2..4a0b72ad237b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -816,7 +816,6 @@ export default { confirmApprove: 'Confirmar importe a aprobar', confirmApprovalAmount: 'Aprueba sólo los gastos conformes, o aprueba todo el informe.', confirmApprovalAllHoldAmount: () => ({ - zero: `Ningún gasto está bloqueado. ¿Quieres aprobar todo el informe?`, one: `Este gasto está bloqueado. ¿Quieres aprobarlo de todos modos?`, other: () => `Estos gastos están bloqueados. ¿Quieres aprobarlos de todos modos?`, }), From 426d50e80e2809e4b6e0b42a643574bf0e51eb53 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Thu, 1 Aug 2024 18:01:53 +0530 Subject: [PATCH 010/386] Apply suggestions --- src/components/ArchivedReportFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx index 7e0121a3ae4f..eeee6c68b73f 100644 --- a/src/components/ArchivedReportFooter.tsx +++ b/src/components/ArchivedReportFooter.tsx @@ -53,7 +53,7 @@ function ArchivedReportFooter({report, reportClosedAction, personalDetails = {}} policyName = lodashEscape(policyName); } - let text; + let text: string; switch (archiveReason) { case CONST.REPORT.ARCHIVE_REASON.ACCOUNT_CLOSED: text = translate(`reportArchiveReasons.${archiveReason}`, { From 158b4c9bd0a56333bf6134d5772468447fd37fe0 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 5 Aug 2024 22:45:35 +0530 Subject: [PATCH 011/386] Polyfill plural rules for hermes --- src/languages/es.ts | 2 +- src/libs/IntlPolyfill/index.android.ts | 4 ++++ src/libs/IntlPolyfill/index.ios.ts | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 27d4b40380ed..a2d51f58cd89 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3388,7 +3388,7 @@ export default { other: () => 'Eliminar tasas', }), enableRates: () => ({ - one: 'Activar 1 tasa', + one: 'Activar tasa', other: () => 'Activar tasas', }), disableRates: () => ({ diff --git a/src/libs/IntlPolyfill/index.android.ts b/src/libs/IntlPolyfill/index.android.ts index 7a21ae26bfa4..0f852457e3db 100644 --- a/src/libs/IntlPolyfill/index.android.ts +++ b/src/libs/IntlPolyfill/index.android.ts @@ -11,6 +11,10 @@ const intlPolyfill: IntlPolyfill = () => { require('@formatjs/intl-locale/polyfill'); + require('@formatjs/intl-pluralrules/polyfill'); + require('@formatjs/intl-pluralrules/locale-data/en'); + require('@formatjs/intl-pluralrules/locale-data/es'); + polyfillListFormat(); }; diff --git a/src/libs/IntlPolyfill/index.ios.ts b/src/libs/IntlPolyfill/index.ios.ts index 569b666eb434..0ee189b4d329 100644 --- a/src/libs/IntlPolyfill/index.ios.ts +++ b/src/libs/IntlPolyfill/index.ios.ts @@ -16,6 +16,8 @@ const intlPolyfill: IntlPolyfill = () => { // Required to polyfill NumberFormat on iOS // see: https://github.com/facebook/hermes/issues/1172#issuecomment-1776156538 require('@formatjs/intl-pluralrules/polyfill'); + require('@formatjs/intl-pluralrules/locale-data/en'); + require('@formatjs/intl-pluralrules/locale-data/es'); polyfillNumberFormat(); // Required to polyfill DateTimeFormat on iOS From 2c7a93778e27c7e6faa9b5357abbfc87801a8528 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 5 Aug 2024 22:51:47 +0530 Subject: [PATCH 012/386] Adjusting position of code --- src/libs/IntlPolyfill/index.ios.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/IntlPolyfill/index.ios.ts b/src/libs/IntlPolyfill/index.ios.ts index 0ee189b4d329..3a41790aa8b6 100644 --- a/src/libs/IntlPolyfill/index.ios.ts +++ b/src/libs/IntlPolyfill/index.ios.ts @@ -13,11 +13,12 @@ const intlPolyfill: IntlPolyfill = () => { require('@formatjs/intl-locale/polyfill'); - // Required to polyfill NumberFormat on iOS - // see: https://github.com/facebook/hermes/issues/1172#issuecomment-1776156538 require('@formatjs/intl-pluralrules/polyfill'); require('@formatjs/intl-pluralrules/locale-data/en'); require('@formatjs/intl-pluralrules/locale-data/es'); + + // Required to polyfill NumberFormat on iOS + // see: https://github.com/facebook/hermes/issues/1172#issuecomment-1776156538 polyfillNumberFormat(); // Required to polyfill DateTimeFormat on iOS From 2428dfac03e32a62f735c6d829072764e361d4e1 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Thu, 8 Aug 2024 16:54:27 +0530 Subject: [PATCH 013/386] Applying suggestion --- src/components/AddressForm.tsx | 2 +- src/libs/Localize/index.ts | 2 +- src/pages/Search/SearchFiltersDatePage.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/AddressForm.tsx b/src/components/AddressForm.tsx index d6ba5813a101..7ca4cc3273ca 100644 --- a/src/components/AddressForm.tsx +++ b/src/components/AddressForm.tsx @@ -126,7 +126,7 @@ function AddressForm({ } } } else if (!CONST.GENERIC_ZIP_CODE_REGEX.test(values?.zipPostCode?.trim()?.toUpperCase() ?? '')) { - errors.zipPostCode = translate('privatePersonalDetails.error.incorrectZipFormat', undefined); + errors.zipPostCode = translate('privatePersonalDetails.error.incorrectZipFormat'); } return errors; diff --git a/src/libs/Localize/index.ts b/src/libs/Localize/index.ts index f1b705398067..70db13c32851 100644 --- a/src/libs/Localize/index.ts +++ b/src/libs/Localize/index.ts @@ -45,7 +45,7 @@ function init() { }, {}); } -type PhraseParameters = T extends (arg: infer A) => string ? [A] : T extends (arg: infer A) => PluralFormPhase ? [A, number] : never[]; +type PhraseParameters = T extends (arg?: infer A) => string ? [A?] : T extends (arg: infer A) => string ? [A] : T extends (arg: infer A) => PluralFormPhase ? [A, number] : never[]; type Phrase = TranslationFlatObject[TKey]; /** diff --git a/src/pages/Search/SearchFiltersDatePage.tsx b/src/pages/Search/SearchFiltersDatePage.tsx index e2a465f6fa9e..4bc95aa21351 100644 --- a/src/pages/Search/SearchFiltersDatePage.tsx +++ b/src/pages/Search/SearchFiltersDatePage.tsx @@ -53,7 +53,7 @@ function SearchFiltersDatePage() { Date: Thu, 8 Aug 2024 21:12:35 +0530 Subject: [PATCH 014/386] Adjusting code --- src/languages/en.ts | 1 - src/languages/es.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 5bcdfdc1cb14..bccd8bdfba18 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -731,7 +731,6 @@ export default { invalidDomainError: 'You have entered an invalid domain. To continue, please enter a valid domain.', publicDomainError: 'You have entered a public domain. To continue, please enter a private domain.', expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => ({ - zero: `0 expenses${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, one: `1 expense${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, other: (count: number) => `${count} expenses${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, }), diff --git a/src/languages/es.ts b/src/languages/es.ts index bfab43641f85..61cd8baba543 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -724,7 +724,6 @@ export default { invalidDomainError: 'Ha introducido un dominio no válido. Para continuar, introduzca un dominio válido.', publicDomainError: 'Ha introducido un dominio público. Para continuar, introduzca un dominio privado.', expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => ({ - zero: `0 gasto${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, one: `1 gasto${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, other: (count: number) => `${count} gastos${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, }), From b38931602767fe3bcb2d222957343aefb02886a6 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:10:34 +0530 Subject: [PATCH 015/386] Apply suggestions from code review --- src/libs/IntlPolyfill/index.android.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/IntlPolyfill/index.android.ts b/src/libs/IntlPolyfill/index.android.ts index 84761382b3fd..e6ab02d15c25 100644 --- a/src/libs/IntlPolyfill/index.android.ts +++ b/src/libs/IntlPolyfill/index.android.ts @@ -11,7 +11,7 @@ const intlPolyfill: IntlPolyfill = () => { require('@formatjs/intl-locale/polyfill-force'); - require('@formatjs/intl-pluralrules/polyfill'); + require('@formatjs/intl-pluralrules/polyfill-force'); require('@formatjs/intl-pluralrules/locale-data/en'); require('@formatjs/intl-pluralrules/locale-data/es'); From 6dfb0cd941be2203e0a1b6fc1ae6e04c6439b95b Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:16:54 +0530 Subject: [PATCH 016/386] Apply suggestions from code review --- src/languages/en.ts | 1 - src/languages/es.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 495bec63a5d7..71a26d93c623 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -866,7 +866,6 @@ export default { confirmPay: 'Confirm payment amount', confirmPayAmount: "Pay what's not on hold, or pay the entire report.", confirmPayAllHoldAmount: () => ({ - zero: `This expense is on hold. Do you want to pay anyway?`, one: `This expense is on hold. Do you want to pay anyway?`, other: () => `These expenses are on hold. Do you want to pay anyway?`, }), diff --git a/src/languages/es.ts b/src/languages/es.ts index e1187603afd8..92a23c0afb49 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -859,7 +859,6 @@ export default { confirmPay: 'Confirmar importe de pago', confirmPayAmount: 'Paga lo que no está bloqueado, o paga el informe completo.', confirmPayAllHoldAmount: () => ({ - zero: `Ningún gasto está bloqueado. ¿Quieres pagar todo el informe?`, one: `Este gasto está bloqueado. ¿Quieres pagarlo de todos modos?`, other: () => `Estos gastos están bloqueados. ¿Quieres pagarlos de todos modos?`, }), From 895108439ea8f158373287d987b978fa37e5a4ca Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Fri, 23 Aug 2024 08:41:56 +0530 Subject: [PATCH 017/386] Apply suggestions from code review --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 16571e7e35e8..4a6b86830587 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -754,7 +754,7 @@ export default { invalidDomainError: 'You have entered an invalid domain. To continue, please enter a valid domain.', publicDomainError: 'You have entered a public domain. To continue, please enter a private domain.', expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => { - const statusText = []; + const statusText: string[] = []; if (scanningReceipts > 0) { statusText.push(`${scanningReceipts} scanning`); } diff --git a/src/languages/es.ts b/src/languages/es.ts index e670a003648a..5f753962ff34 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -745,7 +745,7 @@ export default { invalidDomainError: 'Ha introducido un dominio no válido. Para continuar, introduzca un dominio válido.', publicDomainError: 'Ha introducido un dominio público. Para continuar, introduzca un dominio privado.', expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => { - const statusText = []; + const statusText: string[] = []; if (scanningReceipts > 0) { statusText.push(`${scanningReceipts} escaneando`); } From 29124ea861c38f311a46da8b8f79b19ecafefedd Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Fri, 23 Aug 2024 09:22:10 +0530 Subject: [PATCH 018/386] Post merge fixes --- src/languages/en.ts | 17 ++++++++++------- src/languages/es.ts | 17 ++++++++++------- src/languages/types.ts | 19 +++++++++++++++++++ src/libs/ReportActionsUtils.ts | 6 +++--- src/pages/Search/AdvancedSearchFilters.tsx | 5 ++++- .../report/ContextMenu/ContextMenuActions.tsx | 2 +- src/pages/home/report/ReportActionItem.tsx | 2 +- 7 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 4a6b86830587..6bd72a3790f7 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -69,12 +69,14 @@ import type { ReportArchiveReasonsPolicyDeletedParams, ReportArchiveReasonsRemovedFromPolicyParams, ReportIntegrationMessageTranslationParams, + ReportMemberRoleParams, RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, ResolutionConstraintsParams, RoomNameReservedErrorParams, RoomRenamedToParams, + SearchFilterAmountBetweenParams, SetTheDistanceParams, SetTheRequestParams, SettledAfterAddedBankAccountParams, @@ -98,6 +100,7 @@ import type { UntilTimeParams, UpdatedTheDistanceParams, UpdatedTheRequestParams, + UpdateReportMemberRoleParams, UsePlusButtonParams, UserIsAlreadyMemberParams, UserSplitParams, @@ -763,8 +766,8 @@ export default { } return { one: statusText.length > 0 ? `1 expense (${statusText.join(', ')})` : `1 expense`, - other: (count: number) => statusText.length > 0 ? `${count} expenses (${statusText.join(', ')})` : `${count} expenses`, - } + other: (count: number) => (statusText.length > 0 ? `${count} expenses (${statusText.join(', ')})` : `${count} expenses`), + }; }, deleteExpense: () => ({ one: `Delete expense`, @@ -3786,7 +3789,7 @@ export default { amount: { lessThan: (amount?: string) => `Less than ${amount ?? ''}`, greaterThan: (amount?: string) => `Greater than ${amount ?? ''}`, - between: (greaterThan: string, lessThan: string) => `Between ${greaterThan} and ${lessThan}`, + between: ({greaterThan, lessThan}: SearchFilterAmountBetweenParams) => `Between ${greaterThan} and ${lessThan}`, }, }, expenseType: 'Expense type', @@ -3921,10 +3924,10 @@ export default { 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}: ReportIntegrationMessageTranslationParams) => `failed to sync with ${label} ("${errorMessage}")`, + addEmployee: ({email, role}: ReportMemberRoleParams) => `added ${email} as ${role === 'user' ? 'member' : 'admin'}`, + updateRole: ({email, currentRole, newRole}: UpdateReportMemberRoleParams) => `updated the role of ${email} from ${currentRole} to ${newRole}`, + removeMember: ({email, role}: ReportMemberRoleParams) => `removed ${role} ${email}`, }, }, }, diff --git a/src/languages/es.ts b/src/languages/es.ts index 5f753962ff34..a12fd53009c0 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -67,12 +67,14 @@ import type { ReportArchiveReasonsPolicyDeletedParams, ReportArchiveReasonsRemovedFromPolicyParams, ReportIntegrationMessageTranslationParams, + ReportMemberRoleParams, RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, ResolutionConstraintsParams, RoomNameReservedErrorParams, RoomRenamedToParams, + SearchFilterAmountBetweenParams, SetTheDistanceParams, SetTheRequestParams, SettledAfterAddedBankAccountParams, @@ -95,6 +97,7 @@ import type { UntilTimeParams, UpdatedTheDistanceParams, UpdatedTheRequestParams, + UpdateReportMemberRoleParams, UsePlusButtonParams, UserIsAlreadyMemberParams, UserSplitParams, @@ -754,8 +757,8 @@ export default { } return { one: statusText.length > 0 ? `1 gasto (${statusText.join(', ')})` : `1 gasto`, - other: (count: number) => statusText.length > 0 ? `${count} gastos (${statusText.join(', ')})` : `${count} gastos`, - } + other: (count: number) => (statusText.length > 0 ? `${count} gastos (${statusText.join(', ')})` : `${count} gastos`), + }; }, deleteExpense: () => ({ one: `Eliminar gasto`, @@ -3826,7 +3829,7 @@ export default { amount: { lessThan: (amount?: string) => `Menos de ${amount ?? ''}`, greaterThan: (amount?: string) => `Más que ${amount ?? ''}`, - between: (greaterThan: string, lessThan: string) => `Entre ${greaterThan} y ${lessThan}`, + between: ({greaterThan, lessThan}: SearchFilterAmountBetweenParams) => `Entre ${greaterThan} y ${lessThan}`, }, }, expenseType: 'Tipo de gasto', @@ -3962,11 +3965,11 @@ export default { 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}: ReportIntegrationMessageTranslationParams) => `no se pudo sincronizar con ${label} ("${errorMessage}")`, + addEmployee: ({email, role}: ReportMemberRoleParams) => `agregó a ${email} como ${role === 'user' ? 'miembro' : 'administrador'}`, + updateRole: ({email, currentRole, newRole}: UpdateReportMemberRoleParams) => `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}: ReportMemberRoleParams) => `eliminado ${role === 'user' ? 'miembro' : 'administrador'} ${email}`, }, }, }, diff --git a/src/languages/types.ts b/src/languages/types.ts index 444a97d26249..b95122979df8 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -386,6 +386,22 @@ type ApprovalWorkflowErrorParams = { name2: string; }; +type SearchFilterAmountBetweenParams = { + greaterThan: string; + lessThan: string; +}; + +type ReportMemberRoleParams = { + email: string; + role: string; +}; + +type UpdateReportMemberRoleParams = { + email: string; + currentRole: string; + newRole: string; +}; + export type { AddressLineParams, AdminCanceledRequestParams, @@ -512,4 +528,7 @@ export type { StatementPageTitleTranslationParams, ReportIntegrationMessageTranslationParams, ApprovalWorkflowErrorParams, + SearchFilterAmountBetweenParams, + ReportMemberRoleParams, + UpdateReportMemberRoleParams, }; diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 5fdfec3fd96b..008a5a4be225 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1613,7 +1613,7 @@ function getPolicyChangeLogAddEmployeeMessage(reportAction: OnyxInputOrEntry): reportAction is ReportAction { @@ -1628,7 +1628,7 @@ function getPolicyChangeLogChangeRoleMessage(reportAction: OnyxInputOrEntry>) { diff --git a/src/pages/Search/AdvancedSearchFilters.tsx b/src/pages/Search/AdvancedSearchFilters.tsx index 5143a2d70008..d0929e33db52 100644 --- a/src/pages/Search/AdvancedSearchFilters.tsx +++ b/src/pages/Search/AdvancedSearchFilters.tsx @@ -69,7 +69,10 @@ function getFilterDisplayTitle(filters: Partial, fiel if (fieldName === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) { const {lessThan, greaterThan} = filters; if (lessThan && greaterThan) { - return translate('search.filters.amount.between', convertToDisplayStringWithoutCurrency(Number(greaterThan)), convertToDisplayStringWithoutCurrency(Number(lessThan))); + return translate('search.filters.amount.between', { + greaterThan: convertToDisplayStringWithoutCurrency(Number(greaterThan)), + lessThan: convertToDisplayStringWithoutCurrency(Number(lessThan)), + }); } if (lessThan) { return translate('search.filters.amount.lessThan', convertToDisplayStringWithoutCurrency(Number(lessThan))); diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index db7e482a0457..4174557c2e05 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -472,7 +472,7 @@ const ContextMenuActions: ContextMenuAction[] = [ setClipboardMessage(ReportActionsUtils.getPolicyChangeLogDeleteMemberMessage(reportAction)); } else if (ReportActionsUtils.isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.INTEGRATION_SYNC_FAILED)) { const {label, errorMessage} = ReportActionsUtils.getOriginalMessage(reportAction) ?? {label: '', errorMessage: ''}; - setClipboardMessage(Localize.translateLocal('report.actions.type.integrationSyncFailed', label, errorMessage)); + setClipboardMessage(Localize.translateLocal('report.actions.type.integrationSyncFailed', {label, errorMessage})); } else if (content) { setClipboardMessage( content.replace(/()(.*?)(<\/mention-user>)/gi, (match, openTag: string, innerContent: string, closeTag: string): string => { diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index e4dab8518eb2..012fb84b5bc5 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -675,7 +675,7 @@ function ReportActionItem({ children = ; } else if (ReportActionsUtils.isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.INTEGRATION_SYNC_FAILED)) { const {label, errorMessage} = ReportActionsUtils.getOriginalMessage(action) ?? {label: '', errorMessage: ''}; - children = ; + children = ; } else { const hasBeenFlagged = ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) && From 736b250e0e603f57605b5e2ba0411a21b807eac3 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Thu, 29 Aug 2024 00:04:57 +0200 Subject: [PATCH 019/386] 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 020/386] 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 021/386] 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 022/386] 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 023/386] 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 ?? ''})}